From 85f79d9220ab732a8765e4b43a85a9a5bcec8145 Mon Sep 17 00:00:00 2001 From: Colton Date: Wed, 1 Jan 2025 21:30:43 -0600 Subject: [PATCH 1/8] refactor: use official MCP SDK - Replace custom implementation with official SDK - Fix project creation dependencies - Add proper error handling - Add CHANGELOG.md --- CHANGELOG.md | 60 +++++++++++++++++++ package-lock.json | 26 ++++---- package.json | 4 +- src/cli/project/create.ts | 123 ++++++++++++++++++++++++++++---------- 4 files changed, 169 insertions(+), 44 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d29099d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,60 @@ +# Changelog + +## [Unreleased] + +### Breaking Changes +- Removed dependency on custom MCP server implementation in favor of official SDK +- Changed project template to use @modelcontextprotocol/sdk instead of mcp-framework internals + +### Added +- Added @modelcontextprotocol/sdk as a peer dependency +- Added improved error handling in project templates +- Added proper TypeScript type definitions (@types/node, @types/prompts) + +### Changed +- Updated create project template to use official SDK patterns +- Updated example tool template to use simpler class structure +- Modified package.json template to include required SDK dependencies +- Changed build script to use standard tsc instead of custom mcp-build +- Updated TypeScript configuration for better ES module support + +### Fixed +- Fixed issue where mcp create command fails without manual npm install +- Fixed missing peer dependencies in generated projects +- Fixed TypeScript compilation errors in generated code +- Improved project template stability by using official SDK patterns + +### Removed +- Removed git initialization from project creation (non-essential) +- Removed custom MCPServer class usage from templates +- Removed mcp-build script dependency + +### Migration Guide +For existing projects created with older versions: + +1. Update dependencies: +```json +{ + "dependencies": { + "@modelcontextprotocol/sdk": "^0.6.1" + } +} +``` + +2. Update your server implementation: +```typescript +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +``` + +3. Update your tool implementations to use the simpler class structure: +```typescript +class ExampleTool { + name = "example_tool"; + schema = { + // ... your schema + }; + async execute(input) { + // ... your implementation + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d0f8253..5d4d336 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "mcp-framework", - "version": "0.1.10", + "version": "0.1.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mcp-framework", - "version": "0.1.10", + "version": "0.1.12", "dependencies": { - "@types/prompts": "^2.4.9", "commander": "^12.1.0", "prompts": "^2.4.2", + "typescript": "^5.3.3", "zod": "^3.23.8" }, "bin": { @@ -20,10 +20,10 @@ "devDependencies": { "@modelcontextprotocol/sdk": "^0.6.0", "@types/jest": "^29.5.12", - "@types/node": "^20.11.24", + "@types/node": "^20.17.11", + "@types/prompts": "^2.4.9", "jest": "^29.7.0", - "ts-jest": "^29.1.2", - "typescript": "^5.3.3" + "ts-jest": "^29.1.2" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^0.6.0" @@ -965,9 +965,11 @@ } }, "node_modules/@types/node": { - "version": "20.17.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz", - "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==", + "version": "20.17.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.11.tgz", + "integrity": "sha512-Ept5glCK35R8yeyIeYlRIZtX6SLRyqMhOFTgj5SOkMpLTdw3SEHI9fHx60xaUZ+V1aJxQJODE+7/j5ocZydYTg==", + "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~6.19.2" } @@ -976,6 +978,8 @@ "version": "2.4.9", "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz", "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==", + "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "kleur": "^3.0.3" @@ -3526,7 +3530,6 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3538,7 +3541,8 @@ "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true }, "node_modules/unpipe": { "version": "1.0.0", diff --git a/package.json b/package.json index 8257b12..16f7c5e 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "@modelcontextprotocol/sdk": "^0.6.0" }, "dependencies": { - "@types/prompts": "^2.4.9", "commander": "^12.1.0", "prompts": "^2.4.2", "typescript": "^5.3.3", @@ -46,7 +45,8 @@ "devDependencies": { "@modelcontextprotocol/sdk": "^0.6.0", "@types/jest": "^29.5.12", - "@types/node": "^20.11.24", + "@types/node": "^20.17.11", + "@types/prompts": "^2.4.9", "jest": "^29.7.0", "ts-jest": "^29.1.2" } diff --git a/src/cli/project/create.ts b/src/cli/project/create.ts index a9a2d0c..e93334d 100644 --- a/src/cli/project/create.ts +++ b/src/cli/project/create.ts @@ -54,52 +54,124 @@ export async function createProject(name?: string) { }, files: ["dist"], scripts: { - build: "mcp-build", - prepare: "npm run build", - watch: "tsc --watch", + build: "tsc", + start: "node dist/index.js", + dev: "tsc --watch" }, dependencies: { - "mcp-framework": "^0.1.8", + "@modelcontextprotocol/sdk": "^0.6.1" }, devDependencies: { "@types/node": "^20.11.24", - typescript: "^5.3.3", - }, + "typescript": "^5.3.3" + } }; const tsconfig = { compilerOptions: { - target: "ESNext", - module: "ESNext", + target: "ES2020", + module: "ES2020", moduleResolution: "node", outDir: "./dist", rootDir: "./src", strict: true, esModuleInterop: true, skipLibCheck: true, - forceConsistentCasingInFileNames: true, + declaration: true }, include: ["src/**/*"], - exclude: ["node_modules"], + exclude: ["node_modules", "dist"] + }; + + const indexTs = `#!/usr/bin/env node +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, + McpError, + ErrorCode +} from "@modelcontextprotocol/sdk/types.js"; + +// Import tools +import ExampleTool from "./tools/ExampleTool.js"; + +const server = new Server( + { + name: "${projectName}", + version: "0.0.1", + }, + { + capabilities: { + tools: {} + } + } +); + +// Initialize tools +const tools = [ + new ExampleTool() +]; + +// Set up tool handlers +server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: tools.map(tool => ({ + name: tool.name, + description: tool.description, + inputSchema: tool.schema + })) +})); + +server.setRequestHandler(CallToolRequestSchema, async (request) => { + const tool = tools.find(t => t.name === request.params.name); + if (!tool) { + throw new Error(\`Tool \${request.params.name} not found\`); + } + + try { + const result = await tool.execute(request.params.arguments as any); + return { + content: [ + { + type: "text", + text: result + } + ] + }; + } catch (error: any) { + return { + content: [ + { + type: "text", + text: \`Error executing tool: \${error.message}\` + } + ], + isError: true }; + } +}); - const indexTs = `import { MCPServer } from "mcp-framework"; +// Error handling +server.onerror = (error) => console.error('[MCP Error]', error); -const server = new MCPServer(); +// Handle shutdown +process.on('SIGINT', async () => { + await server.close(); + process.exit(0); +}); -server.start().catch((error) => { - console.error("Server error:", error); - process.exit(1); -});`; +// Start server +const transport = new StdioServerTransport(); +await server.connect(transport); +console.error("${projectName} MCP Server running on stdio");`; - const exampleToolTs = `import { MCPTool } from "mcp-framework"; -import { z } from "zod"; + const exampleToolTs = `import { z } from "zod"; interface ExampleInput { message: string; } -class ExampleTool extends MCPTool { +class ExampleTool { name = "example_tool"; description = "An example tool that processes messages"; @@ -107,7 +179,7 @@ class ExampleTool extends MCPTool { message: { type: z.string(), description: "Message to process", - }, + } }; async execute(input: ExampleInput) { @@ -132,17 +204,6 @@ export default ExampleTool;`; writeFile(join(toolsDir, "ExampleTool.ts"), exampleToolTs), ]); - console.log("Initializing git repository..."); - const gitInit = spawnSync("git", ["init"], { - cwd: projectDir, - stdio: "inherit", - shell: true, - }); - - if (gitInit.status !== 0) { - throw new Error("Failed to initialize git repository"); - } - console.log("Installing dependencies..."); const npmInstall = spawnSync("npm", ["install"], { cwd: projectDir, From 58c826f2bcc1f99b71c2c2ef23bc9b3d4874f966 Mon Sep 17 00:00:00 2001 From: Colton Date: Wed, 1 Jan 2025 22:52:43 -0600 Subject: [PATCH 2/8] Update CHANGELOG with framework restructuring plans --- CHANGELOG.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d29099d..8c8c9af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,22 @@ ### Breaking Changes - Removed dependency on custom MCP server implementation in favor of official SDK - Changed project template to use @modelcontextprotocol/sdk instead of mcp-framework internals +- Moved component loaders to optional utilities +- Moved base classes to optional helpers ### Added - Added @modelcontextprotocol/sdk as a peer dependency - Added improved error handling in project templates - Added proper TypeScript type definitions (@types/node, @types/prompts) +- Added comprehensive prompt support in project templates with proper schema and execution +- Added resource type safety with Resource and ResourceContents interfaces +- Added detailed post-creation instructions for tools, prompts, and resources +- Added proper validation for MCP project structure +- Added proper capabilities declaration for tools, prompts, and resources +- Added optional utilities: + - Logger system for detailed server logging + - Component auto-discovery and validation + - Base classes and type definitions for easier implementation ### Changed - Updated create project template to use official SDK patterns @@ -17,12 +28,21 @@ - Modified package.json template to include required SDK dependencies - Changed build script to use standard tsc instead of custom mcp-build - Updated TypeScript configuration for better ES module support +- Improved project validation with specific error messages +- Enhanced component creation with proper TypeScript interfaces +- Improved error handling in tool, prompt, and resource implementations +- Reorganized framework structure: + - Moved MCPServer to official SDK implementation + - Relocated component loaders to optional utilities + - Moved base classes to helper utilities ### Fixed - Fixed issue where mcp create command fails without manual npm install - Fixed missing peer dependencies in generated projects - Fixed TypeScript compilation errors in generated code - Improved project template stability by using official SDK patterns +- Fixed resource handling to properly use Resource and ResourceContents types +- Fixed prompt template to follow MCP protocol specifications ### Removed - Removed git initialization from project creation (non-essential) @@ -57,4 +77,55 @@ class ExampleTool { async execute(input) { // ... your implementation } -} \ No newline at end of file +} +``` + +4. Update your prompt implementations to follow the MCP protocol: +```typescript +class ExamplePrompt { + name = "example_prompt"; + schema = { + // ... your schema + }; + async execute(input) { + return { + description: "Prompt description", + messages: [ + { + role: "system", + content: { type: "text", text: "..." } + } + ] + }; + } +} +``` + +5. Update your resource implementations to use proper types: +```typescript +class ExampleResource { + name = "example"; + uriTemplate = "example://{path}"; + async list(): Promise { + // ... your implementation + } + async read(uri: string): Promise { + // ... your implementation + } +} +``` + +6. Optional: Use framework utilities +```typescript +// Logger +import { logger } from "@mcp-framework/utils/logger"; +logger.info("Server starting..."); + +// Component auto-discovery +import { ToolLoader } from "@mcp-framework/utils/loaders"; +const toolLoader = new ToolLoader(basePath); +const tools = await toolLoader.loadTools(); + +// Base classes +import { MCPTool, MCPResource } from "@mcp-framework/utils/base"; +class MyTool extends MCPTool { ... } \ No newline at end of file From f95cd750abaf3c66bcc04bcc55390c13f97a6984 Mon Sep 17 00:00:00 2001 From: Colton Date: Wed, 1 Jan 2025 22:53:41 -0600 Subject: [PATCH 3/8] Update CHANGELOG with framework restructuring plans --- src/cli/project/add-prompt.ts | 81 ++++--- src/cli/project/add-resource.ts | 95 ++++++-- src/cli/project/add-tool.ts | 68 ++++-- src/cli/project/create.ts | 391 +++++++++++++++++++++++++----- src/cli/utils/validate-project.ts | 21 +- 5 files changed, 514 insertions(+), 142 deletions(-) diff --git a/src/cli/project/add-prompt.ts b/src/cli/project/add-prompt.ts index 1c5dd39..e1db4fb 100644 --- a/src/cli/project/add-prompt.ts +++ b/src/cli/project/add-prompt.ts @@ -1,14 +1,16 @@ -import { writeFile, mkdir } from "fs/promises"; +import { spawnSync } from "child_process"; +import { mkdir, writeFile } from "fs/promises"; import { join } from "path"; import prompts from "prompts"; -import { toPascalCase } from "../utils/string-utils.js"; import { validateMCPProject } from "../utils/validate-project.js"; +import { toPascalCase } from "../utils/string-utils.js"; export async function addPrompt(name?: string) { await validateMCPProject(); - let promptName = name; - if (!promptName) { + let promptName: string; + + if (!name) { const response = await prompts([ { type: "text", @@ -26,7 +28,9 @@ export async function addPrompt(name?: string) { process.exit(1); } - promptName = response.name; + promptName = response.name as string; + } else { + promptName = name; } if (!promptName) { @@ -34,51 +38,72 @@ export async function addPrompt(name?: string) { } const className = toPascalCase(promptName); - const fileName = `${className}Prompt.ts`; - const promptsDir = join(process.cwd(), "src/prompts"); + const promptDir = join(process.cwd(), "src/prompts", promptName); try { - await mkdir(promptsDir, { recursive: true }); + console.log("Creating prompt directory..."); + await mkdir(promptDir, { recursive: true }); - const promptContent = `import { MCPPrompt } from "mcp-framework"; -import { z } from "zod"; + const promptContent = `import { z } from "zod"; +import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; interface ${className}Input { - message: string; + // Define your prompt's input parameters here + param: string; } -class ${className}Prompt extends MCPPrompt<${className}Input> { +class ${className}Prompt { name = "${promptName}"; description = "${className} prompt description"; schema = { - message: { + param: { type: z.string(), - description: "Message to process", - required: true, - }, + description: "Parameter description", + } }; - async generateMessages({ message }: ${className}Input) { - return [ - { - role: "user", - content: { - type: "text", - text: message, - }, - }, - ]; + constructor(private basePath: string) {} + + async execute(input: ${className}Input) { + const { param } = input; + + try { + // Implement your prompt logic here + return { + content: [ + { + type: "text", + text: \`${className} processed: \${param}\` + } + ] + }; + } catch (error: any) { + throw new McpError( + ErrorCode.InternalError, + \`Prompt execution failed: \${error.message}\` + ); + } } } export default ${className}Prompt;`; - await writeFile(join(promptsDir, fileName), promptContent); + await writeFile(join(promptDir, "index.ts"), promptContent); console.log( - `Prompt ${promptName} created successfully at src/prompts/${fileName}` + `Prompt ${promptName} created successfully at src/prompts/${promptName}/index.ts` ); + + console.log(` +Don't forget to: +1. Register your prompt in src/index.ts: + const ${promptName} = new ${className}Prompt(this.basePath); + this.prompts.set(${promptName}.name, ${promptName}); + +2. Import the prompt in src/index.ts: + import ${className}Prompt from "./prompts/${promptName}/index.js"; + `); } catch (error) { console.error("Error creating prompt:", error); process.exit(1); diff --git a/src/cli/project/add-resource.ts b/src/cli/project/add-resource.ts index d5d2316..515f238 100644 --- a/src/cli/project/add-resource.ts +++ b/src/cli/project/add-resource.ts @@ -1,4 +1,5 @@ -import { writeFile, mkdir } from "fs/promises"; +import { spawnSync } from "child_process"; +import { mkdir, writeFile } from "fs/promises"; import { join } from "path"; import prompts from "prompts"; import { validateMCPProject } from "../utils/validate-project.js"; @@ -7,8 +8,9 @@ import { toPascalCase } from "../utils/string-utils.js"; export async function addResource(name?: string) { await validateMCPProject(); - let resourceName = name; - if (!resourceName) { + let resourceName: string; + + if (!name) { const response = await prompts([ { type: "text", @@ -26,7 +28,9 @@ export async function addResource(name?: string) { process.exit(1); } - resourceName = response.name; + resourceName = response.name as string; + } else { + resourceName = name; } if (!resourceName) { @@ -34,38 +38,85 @@ export async function addResource(name?: string) { } const className = toPascalCase(resourceName); - const fileName = `${className}Resource.ts`; - const resourcesDir = join(process.cwd(), "src/resources"); + const resourceDir = join(process.cwd(), "src/resources", resourceName); try { - await mkdir(resourcesDir, { recursive: true }); + console.log("Creating resource directory..."); + await mkdir(resourceDir, { recursive: true }); - const resourceContent = `import { MCPResource, ResourceContent } from "mcp-framework"; + const resourceContent = `import { z } from "zod"; +import { McpError, ErrorCode, Resource, ResourceContents } from "@modelcontextprotocol/sdk/types.js"; +import { promises as fs } from 'fs'; +import path from 'path'; -class ${className}Resource extends MCPResource { - uri = "resource://${resourceName}"; - name = "${className}"; +class ${className}Resource { + name = "${resourceName}"; description = "${className} resource description"; - mimeType = "application/json"; + uriTemplate = "${resourceName}://{path}"; - async read(): Promise { - return [ - { - uri: this.uri, - mimeType: this.mimeType, - text: JSON.stringify({ message: "Hello from ${className} resource" }), - }, - ]; + constructor(private basePath: string) {} + + async list(): Promise { + try { + const resources: Resource[] = []; + // Implement resource listing logic here + // Example: + resources.push({ + uri: "${resourceName}://example", + name: "Example ${className} Resource", + mimeType: "text/plain" + }); + return resources; + } catch (error: any) { + throw new McpError( + ErrorCode.InternalError, + \`Failed to list resources: \${error.message}\` + ); + } + } + + async read(uri: string): Promise { + try { + // Implement resource reading logic here + // Example: + // const content = await fs.readFile(path.join(this.basePath, uri), 'utf-8'); + return { + uri, + mimeType: "text/plain", + text: "Resource content here" + }; + } catch (error: any) { + throw new McpError( + ErrorCode.InternalError, + \`Failed to read resource: \${error.message}\` + ); + } } } export default ${className}Resource;`; - await writeFile(join(resourcesDir, fileName), resourceContent); + await writeFile(join(resourceDir, "index.ts"), resourceContent); console.log( - `Resource ${resourceName} created successfully at src/resources/${fileName}` + `Resource ${resourceName} created successfully at src/resources/${resourceName}/index.ts` ); + + console.log(` +Don't forget to: +1. Register your resource in src/index.ts: + const ${resourceName} = new ${className}Resource(this.basePath); + this.resources.set(${resourceName}.name, ${resourceName}); + +2. Import the resource in src/index.ts: + import ${className}Resource from "./resources/${resourceName}/index.js"; + +3. Update the server capabilities to include resources: + capabilities: { + resources: {}, + ... + } + `); } catch (error) { console.error("Error creating resource:", error); process.exit(1); diff --git a/src/cli/project/add-tool.ts b/src/cli/project/add-tool.ts index 638aeb4..100cae2 100644 --- a/src/cli/project/add-tool.ts +++ b/src/cli/project/add-tool.ts @@ -1,4 +1,5 @@ -import { writeFile, mkdir } from "fs/promises"; +import { spawnSync } from "child_process"; +import { mkdir, writeFile } from "fs/promises"; import { join } from "path"; import prompts from "prompts"; import { validateMCPProject } from "../utils/validate-project.js"; @@ -7,8 +8,9 @@ import { toPascalCase } from "../utils/string-utils.js"; export async function addTool(name?: string) { await validateMCPProject(); - let toolName = name; - if (!toolName) { + let toolName: string; + + if (!name) { const response = await prompts([ { type: "text", @@ -26,7 +28,9 @@ export async function addTool(name?: string) { process.exit(1); } - toolName = response.name; + toolName = response.name as string; + } else { + toolName = name; } if (!toolName) { @@ -34,42 +38,72 @@ export async function addTool(name?: string) { } const className = toPascalCase(toolName); - const fileName = `${className}Tool.ts`; - const toolsDir = join(process.cwd(), "src/tools"); + const toolDir = join(process.cwd(), "src/tools", toolName); try { - await mkdir(toolsDir, { recursive: true }); + console.log("Creating tool directory..."); + await mkdir(toolDir, { recursive: true }); - const toolContent = `import { MCPTool } from "mcp-framework"; -import { z } from "zod"; + const toolContent = `import { z } from "zod"; +import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; interface ${className}Input { - message: string; + // Define your tool's input parameters here + param: string; } -class ${className}Tool extends MCPTool<${className}Input> { +class ${className}Tool { name = "${toolName}"; description = "${className} tool description"; schema = { - message: { + param: { type: z.string(), - description: "Message to process", - }, + description: "Parameter description", + } }; + constructor(private basePath: string) {} + async execute(input: ${className}Input) { - return \`Processed: \${input.message}\`; + const { param } = input; + + try { + // Implement your tool logic here + return { + content: [ + { + type: "text", + text: \`${className} processed: \${param}\` + } + ] + }; + } catch (error: any) { + throw new McpError( + ErrorCode.InternalError, + \`Tool execution failed: \${error.message}\` + ); + } } } export default ${className}Tool;`; - await writeFile(join(toolsDir, fileName), toolContent); + await writeFile(join(toolDir, "index.ts"), toolContent); console.log( - `Tool ${toolName} created successfully at src/tools/${fileName}` + `Tool ${toolName} created successfully at src/tools/${toolName}/index.ts` ); + + console.log(` +Don't forget to: +1. Register your tool in src/index.ts: + const ${toolName} = new ${className}Tool(this.basePath); + this.tools.set(${toolName}.name, ${toolName}); + +2. Import the tool in src/index.ts: + import ${className}Tool from "./tools/${toolName}/index.js"; + `); } catch (error) { console.error("Error creating tool:", error); process.exit(1); diff --git a/src/cli/project/create.ts b/src/cli/project/create.ts index e93334d..2b729f8 100644 --- a/src/cli/project/create.ts +++ b/src/cli/project/create.ts @@ -3,6 +3,7 @@ import { mkdir, writeFile } from "fs/promises"; import { join } from "path"; import prompts from "prompts"; import { generateReadme } from "../templates/readme.js"; +import { toPascalCase } from "../utils/string-utils.js"; export async function createProject(name?: string) { let projectName: string; @@ -34,15 +35,26 @@ export async function createProject(name?: string) { throw new Error("Project name is required"); } + const className = toPascalCase(projectName); const projectDir = join(process.cwd(), projectName); const srcDir = join(projectDir, "src"); const toolsDir = join(srcDir, "tools"); + const promptsDir = join(srcDir, "prompts"); + const resourcesDir = join(srcDir, "resources"); + const exampleToolDir = join(toolsDir, "example-tool"); + const examplePromptDir = join(promptsDir, "example-prompt"); + const exampleResourceDir = join(resourcesDir, "example-resource"); try { console.log("Creating project structure..."); await mkdir(projectDir); await mkdir(srcDir); await mkdir(toolsDir); + await mkdir(promptsDir); + await mkdir(resourcesDir); + await mkdir(exampleToolDir); + await mkdir(examplePromptDir); + await mkdir(exampleResourceDir); const packageJson = { name: projectName, @@ -59,7 +71,8 @@ export async function createProject(name?: string) { dev: "tsc --watch" }, dependencies: { - "@modelcontextprotocol/sdk": "^0.6.1" + "@modelcontextprotocol/sdk": "^0.6.1", + "zod": "^3.22.4" }, devDependencies: { "@types/node": "^20.11.24", @@ -69,8 +82,8 @@ export async function createProject(name?: string) { const tsconfig = { compilerOptions: { - target: "ES2020", - module: "ES2020", + target: "ES2022", + module: "ES2022", moduleResolution: "node", outDir: "./dist", rootDir: "./src", @@ -89,83 +102,229 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" import { CallToolRequestSchema, ListToolsRequestSchema, + ListPromptsRequestSchema, + GetPromptRequestSchema, + ListResourcesRequestSchema, + ReadResourceRequestSchema, McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import { promises as fs } from 'fs'; +import path from 'path'; +import os from 'os'; + +// Import components +import ExampleTool from "./tools/example-tool/index.js"; +import ExamplePrompt from "./prompts/example-prompt/index.js"; +import ExampleResource from "./resources/example-resource/index.js"; + +// Utility to expand home directory +function expandHome(filepath: string): string { + if (filepath.startsWith('~/') || filepath === '~') { + return path.join(os.homedir(), filepath.slice(1)); + } + return filepath; +} -// Import tools -import ExampleTool from "./tools/ExampleTool.js"; - -const server = new Server( - { - name: "${projectName}", - version: "0.0.1", - }, - { - capabilities: { - tools: {} - } +class ${className}Server { + private server: Server; + private tools: Map = new Map(); + private prompts: Map = new Map(); + private resources: Map = new Map(); + + constructor(private basePath: string) { + // Validate and set up base path + const expandedPath = expandHome(basePath); + this.basePath = path.resolve(expandedPath); + + // Initialize server + this.server = new Server( + { + name: "${projectName}", + version: "0.0.1", + }, + { + capabilities: { + tools: {}, + prompts: {}, + resources: {} + } + } + ); + + // Register components + this.registerTools(); + this.registerPrompts(); + this.registerResources(); + + // Set up handlers + this.setupHandlers(); + + // Error handling + this.server.onerror = (error) => { + console.error("[MCP Error]", error); + }; + + // Handle graceful shutdown + process.on('SIGINT', async () => { + await this.stop(); + process.exit(0); + }); + + process.on('uncaughtException', async (error) => { + console.error("[Uncaught Exception]", error); + await this.stop(); + process.exit(1); + }); } -); - -// Initialize tools -const tools = [ - new ExampleTool() -]; - -// Set up tool handlers -server.setRequestHandler(ListToolsRequestSchema, async () => ({ - tools: tools.map(tool => ({ - name: tool.name, - description: tool.description, - inputSchema: tool.schema - })) -})); - -server.setRequestHandler(CallToolRequestSchema, async (request) => { - const tool = tools.find(t => t.name === request.params.name); - if (!tool) { - throw new Error(\`Tool \${request.params.name} not found\`); + + private registerTools(): void { + // Initialize and register tools + const exampleTool = new ExampleTool(this.basePath); + this.tools.set(exampleTool.name, exampleTool); } - try { - const result = await tool.execute(request.params.arguments as any); - return { - content: [ - { - type: "text", - text: result + private registerPrompts(): void { + // Initialize and register prompts + const examplePrompt = new ExamplePrompt(this.basePath); + this.prompts.set(examplePrompt.name, examplePrompt); + } + + private registerResources(): void { + // Initialize and register resources + const exampleResource = new ExampleResource(this.basePath); + this.resources.set(exampleResource.name, exampleResource); + } + + private setupHandlers(): void { + // List available tools + this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: Array.from(this.tools.values()).map(tool => ({ + name: tool.name, + description: tool.description, + inputSchema: tool.schema + })) + })); + + // Handle tool calls + this.server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + const tool = this.tools.get(name); + if (!tool) { + throw new McpError(ErrorCode.MethodNotFound, \`Unknown tool: \${name}\`); + } + + try { + return await tool.execute(args); + } catch (error: any) { + if (error instanceof McpError) { + throw error; } - ] - }; - } catch (error: any) { - return { - content: [ - { - type: "text", - text: \`Error executing tool: \${error.message}\` + throw new McpError( + ErrorCode.InternalError, + \`Tool execution failed: \${error.message}\` + ); + } + }); + + // List available prompts + this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({ + prompts: Array.from(this.prompts.values()).map(prompt => ({ + name: prompt.name, + description: prompt.description, + inputSchema: prompt.schema + })) + })); + + // Handle prompt requests + this.server.setRequestHandler(GetPromptRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + const prompt = this.prompts.get(name); + if (!prompt) { + throw new McpError(ErrorCode.MethodNotFound, \`Unknown prompt: \${name}\`); + } + + try { + return await prompt.execute(args); + } catch (error: any) { + if (error instanceof McpError) { + throw error; } - ], - isError: true - }; + throw new McpError( + ErrorCode.InternalError, + \`Prompt execution failed: \${error.message}\` + ); + } + }); + + // List available resources + this.server.setRequestHandler(ListResourcesRequestSchema, async () => { + const allResources = []; + for (const resource of this.resources.values()) { + const resources = await resource.list(); + allResources.push(...resources); + } + return { resources: allResources }; + }); + + // Handle resource requests + this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { + const uri = request.params?.uri; + if (!uri || typeof uri !== 'string') { + throw new McpError(ErrorCode.InvalidParams, "Missing or invalid URI parameter"); + } + + // Find the resource handler based on URI scheme + const scheme = uri.split('://')[0]; + const resource = this.resources.get(scheme); + if (!resource) { + throw new McpError(ErrorCode.InvalidParams, \`Unsupported resource type: \${scheme}\`); + } + + try { + const content = await resource.read(uri); + return { contents: [content] }; + } catch (error: any) { + if (error instanceof McpError) { + throw error; + } + throw new McpError( + ErrorCode.InternalError, + \`Resource read failed: \${error.message}\` + ); + } + }); } -}); -// Error handling -server.onerror = (error) => console.error('[MCP Error]', error); + async start() { + const transport = new StdioServerTransport(); + await this.server.connect(transport); + console.error("${projectName} MCP Server running on stdio"); + } -// Handle shutdown -process.on('SIGINT', async () => { - await server.close(); - process.exit(0); -}); + async stop() { + await this.server.close(); + console.error("${projectName} MCP Server stopped"); + } +} // Start server -const transport = new StdioServerTransport(); -await server.connect(transport); -console.error("${projectName} MCP Server running on stdio");`; +const basePath = process.argv[2]; +if (!basePath) { + console.error("Please provide the base path as an argument"); + process.exit(1); +} + +const server = new ${className}Server(basePath); +server.start().catch(error => { + console.error("Failed to start server:", error); + process.exit(1); +});`; const exampleToolTs = `import { z } from "zod"; +import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; interface ExampleInput { message: string; @@ -182,13 +341,107 @@ class ExampleTool { } }; + constructor(private basePath: string) {} + async execute(input: ExampleInput) { - return \`Processed: \${input.message}\`; + const { message } = input; + + return { + content: [ + { + type: "text", + text: \`Processed: \${message}\` + } + ] + }; } } export default ExampleTool;`; + const examplePromptTs = `import { z } from "zod"; +import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; + +interface ExamplePromptInput { + query: string; +} + +class ExamplePrompt { + name = "example_prompt"; + description = "An example prompt that generates responses"; + + schema = { + query: { + type: z.string(), + description: "Query to process", + } + }; + + constructor(private basePath: string) {} + + async execute(input: ExamplePromptInput) { + const { query } = input; + + return { + content: [ + { + type: "text", + text: \`Response to: \${query}\` + } + ] + }; + } +} + +export default ExamplePrompt;`; + + const exampleResourceTs = `import { z } from "zod"; +import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import { promises as fs } from 'fs'; +import path from 'path'; + +class ExampleResource { + name = "example"; + description = "An example resource provider"; + uriTemplate = "example://{path}"; + + constructor(private basePath: string) {} + + async list() { + try { + return [ + { + uri: "example://test.txt", + name: "Example Resource", + mimeType: "text/plain" + } + ]; + } catch (error: any) { + throw new McpError( + ErrorCode.InternalError, + \`Failed to list resources: \${error.message}\` + ); + } + } + + async read(uri: string) { + try { + return { + uri, + mimeType: "text/plain", + text: "This is an example resource." + }; + } catch (error: any) { + throw new McpError( + ErrorCode.InternalError, + \`Failed to read resource: \${error.message}\` + ); + } + } +} + +export default ExampleResource;`; + console.log("Creating project files..."); await Promise.all([ writeFile( @@ -201,7 +454,9 @@ export default ExampleTool;`; ), writeFile(join(projectDir, "README.md"), generateReadme(projectName)), writeFile(join(srcDir, "index.ts"), indexTs), - writeFile(join(toolsDir, "ExampleTool.ts"), exampleToolTs), + writeFile(join(exampleToolDir, "index.ts"), exampleToolTs), + writeFile(join(examplePromptDir, "index.ts"), examplePromptTs), + writeFile(join(exampleResourceDir, "index.ts"), exampleResourceTs), ]); console.log("Installing dependencies..."); @@ -233,6 +488,10 @@ You can now: 1. cd ${projectName} 2. Add more tools using: mcp add tool +3. Add more prompts using: + mcp add prompt +4. Add more resources using: + mcp add resource `); } catch (error) { console.error("Error creating project:", error); diff --git a/src/cli/utils/validate-project.ts b/src/cli/utils/validate-project.ts index 7265699..38350a9 100644 --- a/src/cli/utils/validate-project.ts +++ b/src/cli/utils/validate-project.ts @@ -1,22 +1,25 @@ -import { access } from "fs/promises"; +import { readFile } from "fs/promises"; import { join } from "path"; export async function validateMCPProject() { try { const packageJsonPath = join(process.cwd(), "package.json"); - await access(packageJsonPath); + const packageJsonContent = await readFile(packageJsonPath, 'utf-8'); + const package_json = JSON.parse(packageJsonContent); - const package_json = ( - await import(packageJsonPath, { assert: { type: "json" } }) - ).default; - - if (!package_json.dependencies?.["mcp-framework"]) { + if (!package_json.dependencies?.["@modelcontextprotocol/sdk"]) { throw new Error( - "This directory is not an MCP project (mcp-framework not found in dependencies)" + "This directory is not an MCP project (@modelcontextprotocol/sdk not found in dependencies)" ); } } catch (error) { - console.error("Error: Must be run from an MCP project directory"); + if (error instanceof SyntaxError) { + console.error("Error: Invalid package.json"); + } else if (error instanceof Error) { + console.error(`Error: ${error.message}`); + } else { + console.error("Error: Must be run from an MCP project directory"); + } process.exit(1); } } From 1b46f180baa3c68eba92fb4604d25cb573823840 Mon Sep 17 00:00:00 2001 From: Colton Date: Wed, 1 Jan 2025 23:40:44 -0600 Subject: [PATCH 4/8] (feat): improved component loading and logging --- CHANGELOG.md | 50 +-- src/cli/project/add-prompt.ts | 46 ++- src/cli/project/add-resource.ts | 72 ++-- src/cli/project/add-tool.ts | 20 +- src/cli/project/create.ts | 564 ++++++++++++++++++++++++++++---- 5 files changed, 617 insertions(+), 135 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c8c9af..7a91d5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,10 +17,17 @@ - Added detailed post-creation instructions for tools, prompts, and resources - Added proper validation for MCP project structure - Added proper capabilities declaration for tools, prompts, and resources -- Added optional utilities: - - Logger system for detailed server logging - - Component auto-discovery and validation - - Base classes and type definitions for easier implementation +- Added improved component auto-discovery: + - Automatic loading of tools, prompts, and resources + - Proper path handling for compiled code + - Validation of component interfaces +- Added robust logging system: + - File-based logging with timestamps + - Error-resilient log file handling + - Console mirroring for debugging + - Automatic logs directory creation + - Proper .gitignore configuration for logs +- Added example resource implementation with file system support ### Changed - Updated create project template to use official SDK patterns @@ -31,10 +38,11 @@ - Improved project validation with specific error messages - Enhanced component creation with proper TypeScript interfaces - Improved error handling in tool, prompt, and resource implementations -- Reorganized framework structure: - - Moved MCPServer to official SDK implementation - - Relocated component loaders to optional utilities - - Moved base classes to helper utilities +- Enhanced project creation: + - Added utils directory with logger and component loader + - Improved example components with proper typing + - Added automatic component registration + - Added logs directory creation by default ### Fixed - Fixed issue where mcp create command fails without manual npm install @@ -43,11 +51,15 @@ - Improved project template stability by using official SDK patterns - Fixed resource handling to properly use Resource and ResourceContents types - Fixed prompt template to follow MCP protocol specifications +- Fixed component loading in compiled code +- Fixed logger initialization errors +- Fixed missing logs directory in new projects ### Removed - Removed git initialization from project creation (non-essential) - Removed custom MCPServer class usage from templates - Removed mcp-build script dependency +- Removed manual component registration requirement ### Migration Guide For existing projects created with older versions: @@ -115,17 +127,15 @@ class ExampleResource { } ``` -6. Optional: Use framework utilities +6. Add logger and component loader: ```typescript -// Logger -import { logger } from "@mcp-framework/utils/logger"; -logger.info("Server starting..."); - -// Component auto-discovery -import { ToolLoader } from "@mcp-framework/utils/loaders"; -const toolLoader = new ToolLoader(basePath); -const tools = await toolLoader.loadTools(); +// Add utils/logger.ts and utils/componentLoader.ts from new project template +// Update server to use auto-loading: +const toolLoader = new ComponentLoader(basePath, "tools", validateTool); +const tools = await toolLoader.loadComponents(); +``` -// Base classes -import { MCPTool, MCPResource } from "@mcp-framework/utils/base"; -class MyTool extends MCPTool { ... } \ No newline at end of file +7. Create logs directory: +```bash +mkdir logs +echo "logs/*.log" >> .gitignore \ No newline at end of file diff --git a/src/cli/project/add-prompt.ts b/src/cli/project/add-prompt.ts index e1db4fb..5e35bb6 100644 --- a/src/cli/project/add-prompt.ts +++ b/src/cli/project/add-prompt.ts @@ -46,10 +46,11 @@ export async function addPrompt(name?: string) { const promptContent = `import { z } from "zod"; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import { logger } from "../../utils/logger.js"; interface ${className}Input { // Define your prompt's input parameters here - param: string; + query: string; } class ${className}Prompt { @@ -57,28 +58,44 @@ class ${className}Prompt { description = "${className} prompt description"; schema = { - param: { + query: { type: z.string(), - description: "Parameter description", + description: "Query to process", } }; - constructor(private basePath: string) {} + constructor(private basePath: string) { + logger.debug(\`Initializing ${className}Prompt with base path: \${basePath}\`); + } async execute(input: ${className}Input) { - const { param } = input; + const { query } = input; try { + logger.debug(\`Executing ${className}Prompt with query: \${query}\`); + // Implement your prompt logic here return { - content: [ + description: "${className} prompt response", + messages: [ { - type: "text", - text: \`${className} processed: \${param}\` + role: "system", + content: { + type: "text", + text: "You are a helpful assistant." + } + }, + { + role: "user", + content: { + type: "text", + text: query + } } ] }; } catch (error: any) { + logger.error(\`${className}Prompt execution failed: \${error.message}\`); throw new McpError( ErrorCode.InternalError, \`Prompt execution failed: \${error.message}\` @@ -96,13 +113,12 @@ export default ${className}Prompt;`; ); console.log(` -Don't forget to: -1. Register your prompt in src/index.ts: - const ${promptName} = new ${className}Prompt(this.basePath); - this.prompts.set(${promptName}.name, ${promptName}); - -2. Import the prompt in src/index.ts: - import ${className}Prompt from "./prompts/${promptName}/index.js"; +Prompt will be automatically discovered and loaded by the server. +You can now: +1. Implement your prompt logic in the execute method +2. Add any necessary input parameters to ${className}Input +3. Update the schema and description as needed +4. Customize the system message and response format `); } catch (error) { console.error("Error creating prompt:", error); diff --git a/src/cli/project/add-resource.ts b/src/cli/project/add-resource.ts index 515f238..dac6c64 100644 --- a/src/cli/project/add-resource.ts +++ b/src/cli/project/add-resource.ts @@ -45,29 +45,51 @@ export async function addResource(name?: string) { await mkdir(resourceDir, { recursive: true }); const resourceContent = `import { z } from "zod"; -import { McpError, ErrorCode, Resource, ResourceContents } from "@modelcontextprotocol/sdk/types.js"; +import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { promises as fs } from 'fs'; import path from 'path'; +import { logger } from "../../utils/logger.js"; + +// Resource types from MCP SDK +interface Resource { + uri: string; + name: string; + description?: string; + mimeType?: string; +} + +interface ResourceContent { + uri: string; + mimeType?: string; + text?: string; + blob?: string; +} class ${className}Resource { name = "${resourceName}"; description = "${className} resource description"; uriTemplate = "${resourceName}://{path}"; - constructor(private basePath: string) {} + constructor(private basePath: string) { + logger.debug(\`Initializing ${className}Resource with base path: \${basePath}\`); + } async list(): Promise { try { - const resources: Resource[] = []; - // Implement resource listing logic here + logger.debug('Listing ${className} resources'); + + // Implement your resource listing logic here // Example: - resources.push({ - uri: "${resourceName}://example", - name: "Example ${className} Resource", - mimeType: "text/plain" - }); - return resources; + return [ + { + uri: "${resourceName}://example", + name: "Example ${className} Resource", + mimeType: "text/plain", + description: "An example resource" + } + ]; } catch (error: any) { + logger.error(\`Failed to list ${className} resources: \${error.message}\`); throw new McpError( ErrorCode.InternalError, \`Failed to list resources: \${error.message}\` @@ -75,17 +97,22 @@ class ${className}Resource { } } - async read(uri: string): Promise { + async read(uri: string): Promise { try { - // Implement resource reading logic here + logger.debug(\`Reading ${className} resource: \${uri}\`); + + // Implement your resource reading logic here // Example: - // const content = await fs.readFile(path.join(this.basePath, uri), 'utf-8'); + // const filePath = path.join(this.basePath, uri.replace('${resourceName}://', '')); + // const content = await fs.readFile(filePath, 'utf-8'); + return { uri, mimeType: "text/plain", text: "Resource content here" }; } catch (error: any) { + logger.error(\`Failed to read ${className} resource: \${error.message}\`); throw new McpError( ErrorCode.InternalError, \`Failed to read resource: \${error.message}\` @@ -103,19 +130,12 @@ export default ${className}Resource;`; ); console.log(` -Don't forget to: -1. Register your resource in src/index.ts: - const ${resourceName} = new ${className}Resource(this.basePath); - this.resources.set(${resourceName}.name, ${resourceName}); - -2. Import the resource in src/index.ts: - import ${className}Resource from "./resources/${resourceName}/index.js"; - -3. Update the server capabilities to include resources: - capabilities: { - resources: {}, - ... - } +Resource will be automatically discovered and loaded by the server. +You can now: +1. Implement your resource listing logic in the list method +2. Implement your resource reading logic in the read method +3. Update the uriTemplate and description as needed +4. Add any additional resource-specific functionality `); } catch (error) { console.error("Error creating resource:", error); diff --git a/src/cli/project/add-tool.ts b/src/cli/project/add-tool.ts index 100cae2..327922e 100644 --- a/src/cli/project/add-tool.ts +++ b/src/cli/project/add-tool.ts @@ -46,6 +46,7 @@ export async function addTool(name?: string) { const toolContent = `import { z } from "zod"; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import { logger } from "../../utils/logger.js"; interface ${className}Input { // Define your tool's input parameters here @@ -63,12 +64,16 @@ class ${className}Tool { } }; - constructor(private basePath: string) {} + constructor(private basePath: string) { + logger.debug(\`Initializing ${className}Tool with base path: \${basePath}\`); + } async execute(input: ${className}Input) { const { param } = input; try { + logger.debug(\`Executing ${className}Tool with param: \${param}\`); + // Implement your tool logic here return { content: [ @@ -79,6 +84,7 @@ class ${className}Tool { ] }; } catch (error: any) { + logger.error(\`${className}Tool execution failed: \${error.message}\`); throw new McpError( ErrorCode.InternalError, \`Tool execution failed: \${error.message}\` @@ -96,13 +102,11 @@ export default ${className}Tool;`; ); console.log(` -Don't forget to: -1. Register your tool in src/index.ts: - const ${toolName} = new ${className}Tool(this.basePath); - this.tools.set(${toolName}.name, ${toolName}); - -2. Import the tool in src/index.ts: - import ${className}Tool from "./tools/${toolName}/index.js"; +Tool will be automatically discovered and loaded by the server. +You can now: +1. Implement your tool logic in the execute method +2. Add any necessary input parameters to ${className}Input +3. Update the schema and description as needed `); } catch (error) { console.error("Error creating tool:", error); diff --git a/src/cli/project/create.ts b/src/cli/project/create.ts index 2b729f8..b92dae8 100644 --- a/src/cli/project/create.ts +++ b/src/cli/project/create.ts @@ -38,23 +38,34 @@ export async function createProject(name?: string) { const className = toPascalCase(projectName); const projectDir = join(process.cwd(), projectName); const srcDir = join(projectDir, "src"); + const utilsDir = join(srcDir, "utils"); const toolsDir = join(srcDir, "tools"); const promptsDir = join(srcDir, "prompts"); const resourcesDir = join(srcDir, "resources"); const exampleToolDir = join(toolsDir, "example-tool"); const examplePromptDir = join(promptsDir, "example-prompt"); const exampleResourceDir = join(resourcesDir, "example-resource"); + const logsDir = join(projectDir, "logs"); // Add logs directory try { console.log("Creating project structure..."); await mkdir(projectDir); await mkdir(srcDir); + await mkdir(utilsDir); await mkdir(toolsDir); await mkdir(promptsDir); await mkdir(resourcesDir); await mkdir(exampleToolDir); await mkdir(examplePromptDir); await mkdir(exampleResourceDir); + await mkdir(logsDir); // Create logs directory + + // Add .gitignore to handle logs + const gitignore = `node_modules/ +dist/ +logs/*.log +`; + await writeFile(join(projectDir, ".gitignore"), gitignore); const packageJson = { name: projectName, @@ -96,6 +107,222 @@ export async function createProject(name?: string) { exclude: ["node_modules", "dist"] }; + const loggerTs = `import { createWriteStream, WriteStream } from "fs"; +import { join } from "path"; +import { mkdir } from "fs/promises"; + +export class Logger { + private static instance: Logger; + private logStream: WriteStream | null = null; + private logFilePath: string; + private logDir: string; + + private constructor() { + const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); + this.logDir = join(process.cwd(), "logs"); + this.logFilePath = join(this.logDir, \`mcp-server-\${timestamp}.log\`); + this.initializeLogDir(); + } + + private async initializeLogDir() { + try { + await mkdir(this.logDir, { recursive: true }); + this.logStream = createWriteStream(this.logFilePath, { flags: "a" }); + + // Handle stream errors gracefully + this.logStream.on('error', (err) => { + console.error(\`Error writing to log file: \${err.message}\`); + this.logStream = null; // Stop trying to write to file on error + }); + } catch (err) { + console.error(\`Failed to create logs directory: \${err}\`); + // Continue without file logging + } + } + + public static getInstance(): Logger { + if (!Logger.instance) { + Logger.instance = new Logger(); + } + return Logger.instance; + } + + private getTimestamp(): string { + return new Date().toISOString(); + } + + private formatMessage(level: string, message: string): string { + return \`[\${this.getTimestamp()}] [\${level}] \${message}\n\`; + } + + private writeToStream(formattedMessage: string) { + // Always write to stderr for CLI visibility + process.stderr.write(formattedMessage); + + // Try to write to file if stream is available + if (this.logStream) { + try { + this.logStream.write(formattedMessage); + } catch (err) { + console.error(\`Error writing to log file: \${err}\`); + this.logStream = null; // Stop trying to write to file on error + } + } + } + + public info(message: string): void { + const formattedMessage = this.formatMessage("INFO", message); + this.writeToStream(formattedMessage); + } + + public error(message: string): void { + const formattedMessage = this.formatMessage("ERROR", message); + this.writeToStream(formattedMessage); + } + + public warn(message: string): void { + const formattedMessage = this.formatMessage("WARN", message); + this.writeToStream(formattedMessage); + } + + public debug(message: string): void { + const formattedMessage = this.formatMessage("DEBUG", message); + this.writeToStream(formattedMessage); + } + + public close(): void { + if (this.logStream) { + this.logStream.end(); + this.logStream = null; + } + } + + public getLogPath(): string | null { + return this.logStream ? this.logFilePath : null; + } +} + +export const logger = Logger.getInstance();`; + + const componentLoaderTs = `import { join, dirname } from "path"; +import { promises as fs } from "fs"; +import { logger } from "./logger.js"; + +// Base interface for all components +interface BaseComponent { + name: string; +} + +export class ComponentLoader { + private readonly EXCLUDED_FILES = ["*.test.js", "*.spec.js"]; + private readonly componentDir: string; + private readonly componentType: string; + + constructor( + private basePath: string, + componentType: string, + private validateComponent: (component: any) => component is T + ) { + this.componentType = componentType; + // Get the absolute path to the dist directory + const distDir = join(dirname(process.argv[1])); + this.componentDir = join(distDir, componentType); + + logger.debug( + \`Initialized \${componentType} loader with directory: \${this.componentDir}\` + ); + } + + async hasComponents(): Promise { + try { + const stats = await fs.stat(this.componentDir); + if (!stats.isDirectory()) { + logger.debug("Component path exists but is not a directory"); + return false; + } + + const files = await fs.readdir(this.componentDir); + const hasValidFiles = files.some((dir) => this.isComponentDirectory(dir)); + logger.debug(\`Component directory has valid directories: \${hasValidFiles}\`); + return hasValidFiles; + } catch (error) { + logger.debug("No component directory found"); + return false; + } + } + + private isComponentDirectory(dir: string): boolean { + return !dir.startsWith('.') && !this.EXCLUDED_FILES.includes(dir); + } + + async loadComponents(): Promise { + try { + logger.debug(\`Attempting to load components from: \${this.componentDir}\`); + + let stats; + try { + stats = await fs.stat(this.componentDir); + } catch (error) { + logger.debug("No component directory found"); + return []; + } + + if (!stats.isDirectory()) { + logger.error(\`Path is not a directory: \${this.componentDir}\`); + return []; + } + + // Get component directories (example-tool, my-tool, etc.) + const componentDirs = await fs.readdir(this.componentDir); + logger.debug(\`Found component directories: \${componentDirs.join(", ")}\`); + + const components: T[] = []; + + for (const dir of componentDirs) { + if (!this.isComponentDirectory(dir)) { + continue; + } + + try { + // Import the index.js file from each component directory + const indexPath = join(this.componentDir, dir, 'index.js'); + logger.debug(\`Attempting to load component from: \${indexPath}\`); + + // Go up one level from utils/ then to the component + const relativeImportPath = \`../\${this.componentType}/\${dir}/index.js\`; + logger.debug(\`Using import path: \${relativeImportPath}\`); + + const { default: ComponentClass } = await import(relativeImportPath); + + if (!ComponentClass) { + logger.warn(\`No default export found in \${indexPath}\`); + continue; + } + + // Pass the basePath to the component constructor + const component = new ComponentClass(this.basePath); + if (this.validateComponent(component)) { + logger.debug(\`Successfully loaded component: \${component.name}\`); + components.push(component); + } else { + logger.warn(\`Component validation failed for: \${dir}\`); + } + } catch (error) { + logger.error(\`Error loading component \${dir}: \${error}\`); + } + } + + logger.debug( + \`Successfully loaded \${components.length} components: \${components.map(c => c.name).join(', ')}\` + ); + return components; + } catch (error) { + logger.error(\`Failed to load components: \${error}\`); + return []; + } + } +}`; + const indexTs = `#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; @@ -113,10 +340,8 @@ import { promises as fs } from 'fs'; import path from 'path'; import os from 'os'; -// Import components -import ExampleTool from "./tools/example-tool/index.js"; -import ExamplePrompt from "./prompts/example-prompt/index.js"; -import ExampleResource from "./resources/example-resource/index.js"; +import { logger } from "./utils/logger.js"; +import { ComponentLoader } from "./utils/componentLoader.js"; // Utility to expand home directory function expandHome(filepath: string): string { @@ -126,17 +351,41 @@ function expandHome(filepath: string): string { return filepath; } +// Component validation types +interface Tool { + name: string; + description: string; + schema: any; + execute(input: any): Promise; +} + +interface Prompt { + name: string; + description: string; + schema: any; + execute(input: any): Promise; +} + +interface Resource { + name: string; + description: string; + list(): Promise; + read(uri: string): Promise; +} + class ${className}Server { private server: Server; - private tools: Map = new Map(); - private prompts: Map = new Map(); - private resources: Map = new Map(); + private tools: Map = new Map(); + private prompts: Map = new Map(); + private resources: Map = new Map(); constructor(private basePath: string) { // Validate and set up base path const expandedPath = expandHome(basePath); this.basePath = path.resolve(expandedPath); + logger.info(\`Initializing server with base path: \${this.basePath}\`); + // Initialize server this.server = new Server( { @@ -152,17 +401,12 @@ class ${className}Server { } ); - // Register components - this.registerTools(); - this.registerPrompts(); - this.registerResources(); - // Set up handlers this.setupHandlers(); // Error handling this.server.onerror = (error) => { - console.error("[MCP Error]", error); + logger.error(\`[MCP Error] \${error}\`); }; // Handle graceful shutdown @@ -172,28 +416,65 @@ class ${className}Server { }); process.on('uncaughtException', async (error) => { - console.error("[Uncaught Exception]", error); + logger.error(\`[Uncaught Exception] \${error}\`); await this.stop(); process.exit(1); }); } - private registerTools(): void { - // Initialize and register tools - const exampleTool = new ExampleTool(this.basePath); - this.tools.set(exampleTool.name, exampleTool); - } + private async loadComponents() { + // Initialize loaders + const toolLoader = new ComponentLoader( + this.basePath, + "tools", + (component): component is Tool => + Boolean( + component && + typeof component.name === "string" && + typeof component.description === "string" && + component.schema && + typeof component.execute === "function" + ) + ); - private registerPrompts(): void { - // Initialize and register prompts - const examplePrompt = new ExamplePrompt(this.basePath); - this.prompts.set(examplePrompt.name, examplePrompt); - } + const promptLoader = new ComponentLoader( + this.basePath, + "prompts", + (component): component is Prompt => + Boolean( + component && + typeof component.name === "string" && + typeof component.description === "string" && + component.schema && + typeof component.execute === "function" + ) + ); + + const resourceLoader = new ComponentLoader( + this.basePath, + "resources", + (component): component is Resource => + Boolean( + component && + typeof component.name === "string" && + typeof component.description === "string" && + typeof component.list === "function" && + typeof component.read === "function" + ) + ); - private registerResources(): void { - // Initialize and register resources - const exampleResource = new ExampleResource(this.basePath); - this.resources.set(exampleResource.name, exampleResource); + // Load components + const tools = await toolLoader.loadComponents(); + this.tools = new Map(tools.map(tool => [tool.name, tool])); + logger.info(\`Loaded tools: \${Array.from(this.tools.keys()).join(", ")}\`); + + const prompts = await promptLoader.loadComponents(); + this.prompts = new Map(prompts.map(prompt => [prompt.name, prompt])); + logger.info(\`Loaded prompts: \${Array.from(this.prompts.keys()).join(", ")}\`); + + const resources = await resourceLoader.loadComponents(); + this.resources = new Map(resources.map(resource => [resource.name, resource])); + logger.info(\`Loaded resources: \${Array.from(this.resources.keys()).join(", ")}\`); } private setupHandlers(): void { @@ -299,32 +580,43 @@ class ${className}Server { } async start() { - const transport = new StdioServerTransport(); - await this.server.connect(transport); - console.error("${projectName} MCP Server running on stdio"); + try { + // Load all components + await this.loadComponents(); + + // Connect transport + const transport = new StdioServerTransport(); + await this.server.connect(transport); + + logger.info("${projectName} MCP Server running on stdio"); + } catch (error) { + logger.error(\`Failed to start server: \${error}\`); + throw error; + } } async stop() { await this.server.close(); - console.error("${projectName} MCP Server stopped"); + logger.info("${projectName} MCP Server stopped"); } } // Start server const basePath = process.argv[2]; if (!basePath) { - console.error("Please provide the base path as an argument"); + logger.error("Please provide the base path as an argument"); process.exit(1); } const server = new ${className}Server(basePath); server.start().catch(error => { - console.error("Failed to start server:", error); + logger.error(\`Failed to start server: \${error}\`); process.exit(1); });`; const exampleToolTs = `import { z } from "zod"; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import { logger } from "../../utils/logger.js"; interface ExampleInput { message: string; @@ -341,19 +633,30 @@ class ExampleTool { } }; - constructor(private basePath: string) {} + constructor(private basePath: string) { + logger.debug(\`Initializing ExampleTool with base path: \${basePath}\`); + } async execute(input: ExampleInput) { const { message } = input; - return { - content: [ - { - type: "text", - text: \`Processed: \${message}\` - } - ] - }; + try { + logger.debug(\`Processing message: \${message}\`); + return { + content: [ + { + type: "text", + text: \`Processed: \${message}\` + } + ] + }; + } catch (error: any) { + logger.error(\`ExampleTool execution failed: \${error.message}\`); + throw new McpError( + ErrorCode.InternalError, + \`Tool execution failed: \${error.message}\` + ); + } } } @@ -361,6 +664,7 @@ export default ExampleTool;`; const examplePromptTs = `import { z } from "zod"; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import { logger } from "../../utils/logger.js"; interface ExamplePromptInput { query: string; @@ -377,19 +681,41 @@ class ExamplePrompt { } }; - constructor(private basePath: string) {} + constructor(private basePath: string) { + logger.debug(\`Initializing ExamplePrompt with base path: \${basePath}\`); + } async execute(input: ExamplePromptInput) { const { query } = input; - return { - content: [ - { - type: "text", - text: \`Response to: \${query}\` - } - ] - }; + try { + logger.debug(\`Processing query: \${query}\`); + return { + description: "Example prompt response", + messages: [ + { + role: "system", + content: { + type: "text", + text: "You are a helpful assistant." + } + }, + { + role: "user", + content: { + type: "text", + text: query + } + } + ] + }; + } catch (error: any) { + logger.error(\`ExamplePrompt execution failed: \${error.message}\`); + throw new McpError( + ErrorCode.InternalError, + \`Prompt execution failed: \${error.message}\` + ); + } } } @@ -399,24 +725,101 @@ export default ExamplePrompt;`; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { promises as fs } from 'fs'; import path from 'path'; +import { logger } from "../../utils/logger.js"; + +// Resource types from MCP SDK +interface Resource { + uri: string; + name: string; + description?: string; + mimeType?: string; +} + +interface ResourceContent { + uri: string; + mimeType?: string; + text?: string; + blob?: string; +} class ExampleResource { name = "example"; description = "An example resource provider"; uriTemplate = "example://{path}"; + private resourceDir: string; - constructor(private basePath: string) {} + constructor(private basePath: string) { + logger.debug(\`Initializing ExampleResource with base path: \${basePath}\`); + // Create a resources directory inside the base path + this.resourceDir = path.join(basePath, 'resources'); + this.initializeResourceDir(); + } - async list() { + private async initializeResourceDir() { try { - return [ - { - uri: "example://test.txt", - name: "Example Resource", - mimeType: "text/plain" + // Create resources directory if it doesn't exist + await fs.mkdir(this.resourceDir, { recursive: true }); + + // Create a sample file if no files exist + const files = await fs.readdir(this.resourceDir); + if (files.length === 0) { + const sampleContent = "This is a sample resource file.\\nYou can add more files to the resources directory."; + await fs.writeFile(path.join(this.resourceDir, 'sample.txt'), sampleContent); + } + } catch (error) { + logger.error(\`Failed to initialize resource directory: \${error}\`); + } + } + + private getMimeType(filename: string): string { + const ext = path.extname(filename).toLowerCase(); + const mimeTypes: { [key: string]: string } = { + '.txt': 'text/plain', + '.json': 'application/json', + '.md': 'text/markdown', + '.js': 'application/javascript', + '.ts': 'application/typescript', + '.html': 'text/html', + '.css': 'text/css', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif' + }; + return mimeTypes[ext] || 'application/octet-stream'; + } + + private isTextFile(mimeType: string): boolean { + return mimeType.startsWith('text/') || + mimeType === 'application/json' || + mimeType === 'application/javascript' || + mimeType === 'application/typescript'; + } + + async list(): Promise { + try { + logger.debug('Listing example resources'); + + const files = await fs.readdir(this.resourceDir); + const resources: Resource[] = []; + + for (const file of files) { + const stats = await fs.stat(path.join(this.resourceDir, file)); + if (stats.isFile()) { + const mimeType = this.getMimeType(file); + resources.push({ + uri: \`example://\${file}\`, + name: file, + description: \`\${stats.size} bytes, modified \${stats.mtime.toISOString()}\`, + mimeType + }); } - ]; + } + + logger.debug(\`Found \${resources.length} resources\`); + return resources; } catch (error: any) { + logger.error(\`Failed to list example resources: \${error.message}\`); throw new McpError( ErrorCode.InternalError, \`Failed to list resources: \${error.message}\` @@ -424,14 +827,39 @@ class ExampleResource { } } - async read(uri: string) { + async read(uri: string): Promise { try { - return { - uri, - mimeType: "text/plain", - text: "This is an example resource." - }; + logger.debug(\`Reading example resource: \${uri}\`); + + // Extract filename from URI + const filename = uri.replace('example://', ''); + const filePath = path.join(this.resourceDir, filename); + + // Check if file exists + await fs.access(filePath); + + const mimeType = this.getMimeType(filename); + const isText = this.isTextFile(mimeType); + + if (isText) { + // Read as text + const content = await fs.readFile(filePath, 'utf-8'); + return { + uri, + mimeType, + text: content + }; + } else { + // Read as binary + const content = await fs.readFile(filePath); + return { + uri, + mimeType, + blob: content.toString('base64') + }; + } } catch (error: any) { + logger.error(\`Failed to read example resource: \${error.message}\`); throw new McpError( ErrorCode.InternalError, \`Failed to read resource: \${error.message}\` @@ -454,6 +882,8 @@ export default ExampleResource;`; ), writeFile(join(projectDir, "README.md"), generateReadme(projectName)), writeFile(join(srcDir, "index.ts"), indexTs), + writeFile(join(utilsDir, "logger.ts"), loggerTs), + writeFile(join(utilsDir, "componentLoader.ts"), componentLoaderTs), writeFile(join(exampleToolDir, "index.ts"), exampleToolTs), writeFile(join(examplePromptDir, "index.ts"), examplePromptTs), writeFile(join(exampleResourceDir, "index.ts"), exampleResourceTs), @@ -492,6 +922,8 @@ You can now: mcp add prompt 4. Add more resources using: mcp add resource + +Components will be automatically discovered and loaded! `); } catch (error) { console.error("Error creating project:", error); From 31ca521a73966b59bf9d76736ef083640729e11e Mon Sep 17 00:00:00 2001 From: Colton Date: Wed, 1 Jan 2025 23:49:17 -0600 Subject: [PATCH 5/8] (docs)add upddated --- CHANGELOG.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a91d5f..3f3cbe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ - File-based logging with timestamps - Error-resilient log file handling - Console mirroring for debugging - - Automatic logs directory creation + - Automatic logs directory creation on server start - Proper .gitignore configuration for logs - Added example resource implementation with file system support @@ -42,7 +42,7 @@ - Added utils directory with logger and component loader - Improved example components with proper typing - Added automatic component registration - - Added logs directory creation by default + - Added .gitignore with proper log file patterns ### Fixed - Fixed issue where mcp create command fails without manual npm install @@ -53,7 +53,6 @@ - Fixed prompt template to follow MCP protocol specifications - Fixed component loading in compiled code - Fixed logger initialization errors -- Fixed missing logs directory in new projects ### Removed - Removed git initialization from project creation (non-essential) @@ -135,7 +134,9 @@ const toolLoader = new ComponentLoader(basePath, "tools", validateTool); const tools = await toolLoader.loadComponents(); ``` -7. Create logs directory: +7. Update .gitignore for logs: ```bash -mkdir logs -echo "logs/*.log" >> .gitignore \ No newline at end of file +echo "logs/*.log" >> .gitignore +``` + +Note: The logs directory will be created automatically when the server starts. \ No newline at end of file From b339363c34507038b5954153372ec0e00d6256c8 Mon Sep 17 00:00:00 2001 From: Colton Date: Thu, 2 Jan 2025 00:03:16 -0600 Subject: [PATCH 6/8] (refactor): move tempaltes to seperate files for better maintainability --- CHANGELOG.md | 10 + src/cli/project/create.ts | 837 +------------------------------- src/cli/templates/components.ts | 261 ++++++++++ src/cli/templates/config.ts | 347 +++++++++++++ src/cli/templates/utils.ts | 219 +++++++++ 5 files changed, 854 insertions(+), 820 deletions(-) create mode 100644 src/cli/templates/components.ts create mode 100644 src/cli/templates/config.ts create mode 100644 src/cli/templates/utils.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f3cbe4..1a0314a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,10 @@ - Automatic logs directory creation on server start - Proper .gitignore configuration for logs - Added example resource implementation with file system support +- Added modular template system: + - Separated templates into logical groups (utils, components, config) + - Added template generation functions for better maintainability + - Improved code organization in project creation ### Changed - Updated create project template to use official SDK patterns @@ -43,6 +47,11 @@ - Improved example components with proper typing - Added automatic component registration - Added .gitignore with proper log file patterns +- Refactored project creation: + - Moved templates to separate files + - Added template generation functions + - Reduced create.ts from ~1000 to ~130 lines + - Improved maintainability without changing functionality ### Fixed - Fixed issue where mcp create command fails without manual npm install @@ -59,6 +68,7 @@ - Removed custom MCPServer class usage from templates - Removed mcp-build script dependency - Removed manual component registration requirement +- Removed inline template strings from create.ts ### Migration Guide For existing projects created with older versions: diff --git a/src/cli/project/create.ts b/src/cli/project/create.ts index b92dae8..de2ee41 100644 --- a/src/cli/project/create.ts +++ b/src/cli/project/create.ts @@ -3,6 +3,9 @@ import { mkdir, writeFile } from "fs/promises"; import { join } from "path"; import prompts from "prompts"; import { generateReadme } from "../templates/readme.js"; +import { generateLogger, generateComponentLoader } from "../templates/utils.js"; +import { generateExampleTool, generateExamplePrompt, generateExampleResource } from "../templates/components.js"; +import { generatePackageJson, generateTsConfig, generateGitIgnore, generateIndexTs } from "../templates/config.js"; import { toPascalCase } from "../utils/string-utils.js"; export async function createProject(name?: string) { @@ -45,7 +48,7 @@ export async function createProject(name?: string) { const exampleToolDir = join(toolsDir, "example-tool"); const examplePromptDir = join(promptsDir, "example-prompt"); const exampleResourceDir = join(resourcesDir, "example-resource"); - const logsDir = join(projectDir, "logs"); // Add logs directory + const logsDir = join(projectDir, "logs"); try { console.log("Creating project structure..."); @@ -58,835 +61,29 @@ export async function createProject(name?: string) { await mkdir(exampleToolDir); await mkdir(examplePromptDir); await mkdir(exampleResourceDir); - await mkdir(logsDir); // Create logs directory - - // Add .gitignore to handle logs - const gitignore = `node_modules/ -dist/ -logs/*.log -`; - await writeFile(join(projectDir, ".gitignore"), gitignore); - - const packageJson = { - name: projectName, - version: "0.0.1", - description: `${projectName} MCP server`, - type: "module", - bin: { - [projectName]: "./dist/index.js", - }, - files: ["dist"], - scripts: { - build: "tsc", - start: "node dist/index.js", - dev: "tsc --watch" - }, - dependencies: { - "@modelcontextprotocol/sdk": "^0.6.1", - "zod": "^3.22.4" - }, - devDependencies: { - "@types/node": "^20.11.24", - "typescript": "^5.3.3" - } - }; - - const tsconfig = { - compilerOptions: { - target: "ES2022", - module: "ES2022", - moduleResolution: "node", - outDir: "./dist", - rootDir: "./src", - strict: true, - esModuleInterop: true, - skipLibCheck: true, - declaration: true - }, - include: ["src/**/*"], - exclude: ["node_modules", "dist"] - }; - - const loggerTs = `import { createWriteStream, WriteStream } from "fs"; -import { join } from "path"; -import { mkdir } from "fs/promises"; - -export class Logger { - private static instance: Logger; - private logStream: WriteStream | null = null; - private logFilePath: string; - private logDir: string; - - private constructor() { - const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); - this.logDir = join(process.cwd(), "logs"); - this.logFilePath = join(this.logDir, \`mcp-server-\${timestamp}.log\`); - this.initializeLogDir(); - } - - private async initializeLogDir() { - try { - await mkdir(this.logDir, { recursive: true }); - this.logStream = createWriteStream(this.logFilePath, { flags: "a" }); - - // Handle stream errors gracefully - this.logStream.on('error', (err) => { - console.error(\`Error writing to log file: \${err.message}\`); - this.logStream = null; // Stop trying to write to file on error - }); - } catch (err) { - console.error(\`Failed to create logs directory: \${err}\`); - // Continue without file logging - } - } - - public static getInstance(): Logger { - if (!Logger.instance) { - Logger.instance = new Logger(); - } - return Logger.instance; - } - - private getTimestamp(): string { - return new Date().toISOString(); - } - - private formatMessage(level: string, message: string): string { - return \`[\${this.getTimestamp()}] [\${level}] \${message}\n\`; - } - - private writeToStream(formattedMessage: string) { - // Always write to stderr for CLI visibility - process.stderr.write(formattedMessage); - - // Try to write to file if stream is available - if (this.logStream) { - try { - this.logStream.write(formattedMessage); - } catch (err) { - console.error(\`Error writing to log file: \${err}\`); - this.logStream = null; // Stop trying to write to file on error - } - } - } - - public info(message: string): void { - const formattedMessage = this.formatMessage("INFO", message); - this.writeToStream(formattedMessage); - } - - public error(message: string): void { - const formattedMessage = this.formatMessage("ERROR", message); - this.writeToStream(formattedMessage); - } - - public warn(message: string): void { - const formattedMessage = this.formatMessage("WARN", message); - this.writeToStream(formattedMessage); - } - - public debug(message: string): void { - const formattedMessage = this.formatMessage("DEBUG", message); - this.writeToStream(formattedMessage); - } - - public close(): void { - if (this.logStream) { - this.logStream.end(); - this.logStream = null; - } - } - - public getLogPath(): string | null { - return this.logStream ? this.logFilePath : null; - } -} - -export const logger = Logger.getInstance();`; - - const componentLoaderTs = `import { join, dirname } from "path"; -import { promises as fs } from "fs"; -import { logger } from "./logger.js"; - -// Base interface for all components -interface BaseComponent { - name: string; -} - -export class ComponentLoader { - private readonly EXCLUDED_FILES = ["*.test.js", "*.spec.js"]; - private readonly componentDir: string; - private readonly componentType: string; - - constructor( - private basePath: string, - componentType: string, - private validateComponent: (component: any) => component is T - ) { - this.componentType = componentType; - // Get the absolute path to the dist directory - const distDir = join(dirname(process.argv[1])); - this.componentDir = join(distDir, componentType); - - logger.debug( - \`Initialized \${componentType} loader with directory: \${this.componentDir}\` - ); - } - - async hasComponents(): Promise { - try { - const stats = await fs.stat(this.componentDir); - if (!stats.isDirectory()) { - logger.debug("Component path exists but is not a directory"); - return false; - } - - const files = await fs.readdir(this.componentDir); - const hasValidFiles = files.some((dir) => this.isComponentDirectory(dir)); - logger.debug(\`Component directory has valid directories: \${hasValidFiles}\`); - return hasValidFiles; - } catch (error) { - logger.debug("No component directory found"); - return false; - } - } - - private isComponentDirectory(dir: string): boolean { - return !dir.startsWith('.') && !this.EXCLUDED_FILES.includes(dir); - } - - async loadComponents(): Promise { - try { - logger.debug(\`Attempting to load components from: \${this.componentDir}\`); - - let stats; - try { - stats = await fs.stat(this.componentDir); - } catch (error) { - logger.debug("No component directory found"); - return []; - } - - if (!stats.isDirectory()) { - logger.error(\`Path is not a directory: \${this.componentDir}\`); - return []; - } - - // Get component directories (example-tool, my-tool, etc.) - const componentDirs = await fs.readdir(this.componentDir); - logger.debug(\`Found component directories: \${componentDirs.join(", ")}\`); - - const components: T[] = []; - - for (const dir of componentDirs) { - if (!this.isComponentDirectory(dir)) { - continue; - } - - try { - // Import the index.js file from each component directory - const indexPath = join(this.componentDir, dir, 'index.js'); - logger.debug(\`Attempting to load component from: \${indexPath}\`); - - // Go up one level from utils/ then to the component - const relativeImportPath = \`../\${this.componentType}/\${dir}/index.js\`; - logger.debug(\`Using import path: \${relativeImportPath}\`); - - const { default: ComponentClass } = await import(relativeImportPath); - - if (!ComponentClass) { - logger.warn(\`No default export found in \${indexPath}\`); - continue; - } - - // Pass the basePath to the component constructor - const component = new ComponentClass(this.basePath); - if (this.validateComponent(component)) { - logger.debug(\`Successfully loaded component: \${component.name}\`); - components.push(component); - } else { - logger.warn(\`Component validation failed for: \${dir}\`); - } - } catch (error) { - logger.error(\`Error loading component \${dir}: \${error}\`); - } - } - - logger.debug( - \`Successfully loaded \${components.length} components: \${components.map(c => c.name).join(', ')}\` - ); - return components; - } catch (error) { - logger.error(\`Failed to load components: \${error}\`); - return []; - } - } -}`; - - const indexTs = `#!/usr/bin/env node -import { Server } from "@modelcontextprotocol/sdk/server/index.js"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { - CallToolRequestSchema, - ListToolsRequestSchema, - ListPromptsRequestSchema, - GetPromptRequestSchema, - ListResourcesRequestSchema, - ReadResourceRequestSchema, - McpError, - ErrorCode -} from "@modelcontextprotocol/sdk/types.js"; -import { promises as fs } from 'fs'; -import path from 'path'; -import os from 'os'; - -import { logger } from "./utils/logger.js"; -import { ComponentLoader } from "./utils/componentLoader.js"; - -// Utility to expand home directory -function expandHome(filepath: string): string { - if (filepath.startsWith('~/') || filepath === '~') { - return path.join(os.homedir(), filepath.slice(1)); - } - return filepath; -} - -// Component validation types -interface Tool { - name: string; - description: string; - schema: any; - execute(input: any): Promise; -} - -interface Prompt { - name: string; - description: string; - schema: any; - execute(input: any): Promise; -} - -interface Resource { - name: string; - description: string; - list(): Promise; - read(uri: string): Promise; -} - -class ${className}Server { - private server: Server; - private tools: Map = new Map(); - private prompts: Map = new Map(); - private resources: Map = new Map(); - - constructor(private basePath: string) { - // Validate and set up base path - const expandedPath = expandHome(basePath); - this.basePath = path.resolve(expandedPath); - - logger.info(\`Initializing server with base path: \${this.basePath}\`); - - // Initialize server - this.server = new Server( - { - name: "${projectName}", - version: "0.0.1", - }, - { - capabilities: { - tools: {}, - prompts: {}, - resources: {} - } - } - ); - - // Set up handlers - this.setupHandlers(); - - // Error handling - this.server.onerror = (error) => { - logger.error(\`[MCP Error] \${error}\`); - }; - - // Handle graceful shutdown - process.on('SIGINT', async () => { - await this.stop(); - process.exit(0); - }); - - process.on('uncaughtException', async (error) => { - logger.error(\`[Uncaught Exception] \${error}\`); - await this.stop(); - process.exit(1); - }); - } - - private async loadComponents() { - // Initialize loaders - const toolLoader = new ComponentLoader( - this.basePath, - "tools", - (component): component is Tool => - Boolean( - component && - typeof component.name === "string" && - typeof component.description === "string" && - component.schema && - typeof component.execute === "function" - ) - ); - - const promptLoader = new ComponentLoader( - this.basePath, - "prompts", - (component): component is Prompt => - Boolean( - component && - typeof component.name === "string" && - typeof component.description === "string" && - component.schema && - typeof component.execute === "function" - ) - ); - - const resourceLoader = new ComponentLoader( - this.basePath, - "resources", - (component): component is Resource => - Boolean( - component && - typeof component.name === "string" && - typeof component.description === "string" && - typeof component.list === "function" && - typeof component.read === "function" - ) - ); - - // Load components - const tools = await toolLoader.loadComponents(); - this.tools = new Map(tools.map(tool => [tool.name, tool])); - logger.info(\`Loaded tools: \${Array.from(this.tools.keys()).join(", ")}\`); - - const prompts = await promptLoader.loadComponents(); - this.prompts = new Map(prompts.map(prompt => [prompt.name, prompt])); - logger.info(\`Loaded prompts: \${Array.from(this.prompts.keys()).join(", ")}\`); - - const resources = await resourceLoader.loadComponents(); - this.resources = new Map(resources.map(resource => [resource.name, resource])); - logger.info(\`Loaded resources: \${Array.from(this.resources.keys()).join(", ")}\`); - } - - private setupHandlers(): void { - // List available tools - this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ - tools: Array.from(this.tools.values()).map(tool => ({ - name: tool.name, - description: tool.description, - inputSchema: tool.schema - })) - })); - - // Handle tool calls - this.server.setRequestHandler(CallToolRequestSchema, async (request) => { - const { name, arguments: args } = request.params; - - const tool = this.tools.get(name); - if (!tool) { - throw new McpError(ErrorCode.MethodNotFound, \`Unknown tool: \${name}\`); - } - - try { - return await tool.execute(args); - } catch (error: any) { - if (error instanceof McpError) { - throw error; - } - throw new McpError( - ErrorCode.InternalError, - \`Tool execution failed: \${error.message}\` - ); - } - }); - - // List available prompts - this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({ - prompts: Array.from(this.prompts.values()).map(prompt => ({ - name: prompt.name, - description: prompt.description, - inputSchema: prompt.schema - })) - })); - - // Handle prompt requests - this.server.setRequestHandler(GetPromptRequestSchema, async (request) => { - const { name, arguments: args } = request.params; - - const prompt = this.prompts.get(name); - if (!prompt) { - throw new McpError(ErrorCode.MethodNotFound, \`Unknown prompt: \${name}\`); - } - - try { - return await prompt.execute(args); - } catch (error: any) { - if (error instanceof McpError) { - throw error; - } - throw new McpError( - ErrorCode.InternalError, - \`Prompt execution failed: \${error.message}\` - ); - } - }); - - // List available resources - this.server.setRequestHandler(ListResourcesRequestSchema, async () => { - const allResources = []; - for (const resource of this.resources.values()) { - const resources = await resource.list(); - allResources.push(...resources); - } - return { resources: allResources }; - }); - - // Handle resource requests - this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { - const uri = request.params?.uri; - if (!uri || typeof uri !== 'string') { - throw new McpError(ErrorCode.InvalidParams, "Missing or invalid URI parameter"); - } - - // Find the resource handler based on URI scheme - const scheme = uri.split('://')[0]; - const resource = this.resources.get(scheme); - if (!resource) { - throw new McpError(ErrorCode.InvalidParams, \`Unsupported resource type: \${scheme}\`); - } - - try { - const content = await resource.read(uri); - return { contents: [content] }; - } catch (error: any) { - if (error instanceof McpError) { - throw error; - } - throw new McpError( - ErrorCode.InternalError, - \`Resource read failed: \${error.message}\` - ); - } - }); - } - - async start() { - try { - // Load all components - await this.loadComponents(); - - // Connect transport - const transport = new StdioServerTransport(); - await this.server.connect(transport); - - logger.info("${projectName} MCP Server running on stdio"); - } catch (error) { - logger.error(\`Failed to start server: \${error}\`); - throw error; - } - } - - async stop() { - await this.server.close(); - logger.info("${projectName} MCP Server stopped"); - } -} - -// Start server -const basePath = process.argv[2]; -if (!basePath) { - logger.error("Please provide the base path as an argument"); - process.exit(1); -} - -const server = new ${className}Server(basePath); -server.start().catch(error => { - logger.error(\`Failed to start server: \${error}\`); - process.exit(1); -});`; - - const exampleToolTs = `import { z } from "zod"; -import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; -import { logger } from "../../utils/logger.js"; - -interface ExampleInput { - message: string; -} - -class ExampleTool { - name = "example_tool"; - description = "An example tool that processes messages"; - - schema = { - message: { - type: z.string(), - description: "Message to process", - } - }; - - constructor(private basePath: string) { - logger.debug(\`Initializing ExampleTool with base path: \${basePath}\`); - } - - async execute(input: ExampleInput) { - const { message } = input; - - try { - logger.debug(\`Processing message: \${message}\`); - return { - content: [ - { - type: "text", - text: \`Processed: \${message}\` - } - ] - }; - } catch (error: any) { - logger.error(\`ExampleTool execution failed: \${error.message}\`); - throw new McpError( - ErrorCode.InternalError, - \`Tool execution failed: \${error.message}\` - ); - } - } -} - -export default ExampleTool;`; - - const examplePromptTs = `import { z } from "zod"; -import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; -import { logger } from "../../utils/logger.js"; - -interface ExamplePromptInput { - query: string; -} - -class ExamplePrompt { - name = "example_prompt"; - description = "An example prompt that generates responses"; - - schema = { - query: { - type: z.string(), - description: "Query to process", - } - }; - - constructor(private basePath: string) { - logger.debug(\`Initializing ExamplePrompt with base path: \${basePath}\`); - } - - async execute(input: ExamplePromptInput) { - const { query } = input; - - try { - logger.debug(\`Processing query: \${query}\`); - return { - description: "Example prompt response", - messages: [ - { - role: "system", - content: { - type: "text", - text: "You are a helpful assistant." - } - }, - { - role: "user", - content: { - type: "text", - text: query - } - } - ] - }; - } catch (error: any) { - logger.error(\`ExamplePrompt execution failed: \${error.message}\`); - throw new McpError( - ErrorCode.InternalError, - \`Prompt execution failed: \${error.message}\` - ); - } - } -} - -export default ExamplePrompt;`; - - const exampleResourceTs = `import { z } from "zod"; -import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; -import { promises as fs } from 'fs'; -import path from 'path'; -import { logger } from "../../utils/logger.js"; - -// Resource types from MCP SDK -interface Resource { - uri: string; - name: string; - description?: string; - mimeType?: string; -} - -interface ResourceContent { - uri: string; - mimeType?: string; - text?: string; - blob?: string; -} - -class ExampleResource { - name = "example"; - description = "An example resource provider"; - uriTemplate = "example://{path}"; - private resourceDir: string; - - constructor(private basePath: string) { - logger.debug(\`Initializing ExampleResource with base path: \${basePath}\`); - // Create a resources directory inside the base path - this.resourceDir = path.join(basePath, 'resources'); - this.initializeResourceDir(); - } - - private async initializeResourceDir() { - try { - // Create resources directory if it doesn't exist - await fs.mkdir(this.resourceDir, { recursive: true }); - - // Create a sample file if no files exist - const files = await fs.readdir(this.resourceDir); - if (files.length === 0) { - const sampleContent = "This is a sample resource file.\\nYou can add more files to the resources directory."; - await fs.writeFile(path.join(this.resourceDir, 'sample.txt'), sampleContent); - } - } catch (error) { - logger.error(\`Failed to initialize resource directory: \${error}\`); - } - } - - private getMimeType(filename: string): string { - const ext = path.extname(filename).toLowerCase(); - const mimeTypes: { [key: string]: string } = { - '.txt': 'text/plain', - '.json': 'application/json', - '.md': 'text/markdown', - '.js': 'application/javascript', - '.ts': 'application/typescript', - '.html': 'text/html', - '.css': 'text/css', - '.png': 'image/png', - '.jpg': 'image/jpeg', - '.jpeg': 'image/jpeg', - '.gif': 'image/gif' - }; - return mimeTypes[ext] || 'application/octet-stream'; - } - - private isTextFile(mimeType: string): boolean { - return mimeType.startsWith('text/') || - mimeType === 'application/json' || - mimeType === 'application/javascript' || - mimeType === 'application/typescript'; - } - - async list(): Promise { - try { - logger.debug('Listing example resources'); - - const files = await fs.readdir(this.resourceDir); - const resources: Resource[] = []; - - for (const file of files) { - const stats = await fs.stat(path.join(this.resourceDir, file)); - if (stats.isFile()) { - const mimeType = this.getMimeType(file); - resources.push({ - uri: \`example://\${file}\`, - name: file, - description: \`\${stats.size} bytes, modified \${stats.mtime.toISOString()}\`, - mimeType - }); - } - } - - logger.debug(\`Found \${resources.length} resources\`); - return resources; - } catch (error: any) { - logger.error(\`Failed to list example resources: \${error.message}\`); - throw new McpError( - ErrorCode.InternalError, - \`Failed to list resources: \${error.message}\` - ); - } - } - - async read(uri: string): Promise { - try { - logger.debug(\`Reading example resource: \${uri}\`); - - // Extract filename from URI - const filename = uri.replace('example://', ''); - const filePath = path.join(this.resourceDir, filename); - - // Check if file exists - await fs.access(filePath); - - const mimeType = this.getMimeType(filename); - const isText = this.isTextFile(mimeType); - - if (isText) { - // Read as text - const content = await fs.readFile(filePath, 'utf-8'); - return { - uri, - mimeType, - text: content - }; - } else { - // Read as binary - const content = await fs.readFile(filePath); - return { - uri, - mimeType, - blob: content.toString('base64') - }; - } - } catch (error: any) { - logger.error(\`Failed to read example resource: \${error.message}\`); - throw new McpError( - ErrorCode.InternalError, - \`Failed to read resource: \${error.message}\` - ); - } - } -} - -export default ExampleResource;`; + await mkdir(logsDir); console.log("Creating project files..."); await Promise.all([ writeFile( join(projectDir, "package.json"), - JSON.stringify(packageJson, null, 2) + generatePackageJson(projectName) ), writeFile( join(projectDir, "tsconfig.json"), - JSON.stringify(tsconfig, null, 2) + generateTsConfig() + ), + writeFile( + join(projectDir, ".gitignore"), + generateGitIgnore() ), writeFile(join(projectDir, "README.md"), generateReadme(projectName)), - writeFile(join(srcDir, "index.ts"), indexTs), - writeFile(join(utilsDir, "logger.ts"), loggerTs), - writeFile(join(utilsDir, "componentLoader.ts"), componentLoaderTs), - writeFile(join(exampleToolDir, "index.ts"), exampleToolTs), - writeFile(join(examplePromptDir, "index.ts"), examplePromptTs), - writeFile(join(exampleResourceDir, "index.ts"), exampleResourceTs), + writeFile(join(srcDir, "index.ts"), generateIndexTs(className)), + writeFile(join(utilsDir, "logger.ts"), generateLogger()), + writeFile(join(utilsDir, "componentLoader.ts"), generateComponentLoader()), + writeFile(join(exampleToolDir, "index.ts"), generateExampleTool()), + writeFile(join(examplePromptDir, "index.ts"), generateExamplePrompt()), + writeFile(join(exampleResourceDir, "index.ts"), generateExampleResource()), ]); console.log("Installing dependencies..."); diff --git a/src/cli/templates/components.ts b/src/cli/templates/components.ts new file mode 100644 index 0000000..910624f --- /dev/null +++ b/src/cli/templates/components.ts @@ -0,0 +1,261 @@ +export function generateExampleTool(): string { + return `import { z } from "zod"; +import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import { logger } from "../../utils/logger.js"; + +interface ExampleInput { + message: string; +} + +class ExampleTool { + name = "example_tool"; + description = "An example tool that processes messages"; + + schema = { + message: { + type: z.string(), + description: "Message to process", + } + }; + + constructor(private basePath: string) { + logger.debug(\`Initializing ExampleTool with base path: \${basePath}\`); + } + + async execute(input: ExampleInput) { + const { message } = input; + + try { + logger.debug(\`Processing message: \${message}\`); + return { + content: [ + { + type: "text", + text: \`Processed: \${message}\` + } + ] + }; + } catch (error: any) { + logger.error(\`ExampleTool execution failed: \${error.message}\`); + throw new McpError( + ErrorCode.InternalError, + \`Tool execution failed: \${error.message}\` + ); + } + } +} + +export default ExampleTool;`; +} + +export function generateExamplePrompt(): string { + return `import { z } from "zod"; +import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import { logger } from "../../utils/logger.js"; + +interface ExamplePromptInput { + query: string; +} + +class ExamplePrompt { + name = "example_prompt"; + description = "An example prompt that generates responses"; + + schema = { + query: { + type: z.string(), + description: "Query to process", + } + }; + + constructor(private basePath: string) { + logger.debug(\`Initializing ExamplePrompt with base path: \${basePath}\`); + } + + async execute(input: ExamplePromptInput) { + const { query } = input; + + try { + logger.debug(\`Processing query: \${query}\`); + return { + description: "Example prompt response", + messages: [ + { + role: "system", + content: { + type: "text", + text: "You are a helpful assistant." + } + }, + { + role: "user", + content: { + type: "text", + text: query + } + } + ] + }; + } catch (error: any) { + logger.error(\`ExamplePrompt execution failed: \${error.message}\`); + throw new McpError( + ErrorCode.InternalError, + \`Prompt execution failed: \${error.message}\` + ); + } + } +} + +export default ExamplePrompt;`; +} + +export function generateExampleResource(): string { + return `import { z } from "zod"; +import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; +import { promises as fs } from 'fs'; +import path from 'path'; +import { logger } from "../../utils/logger.js"; + +// Resource types from MCP SDK +interface Resource { + uri: string; + name: string; + description?: string; + mimeType?: string; +} + +interface ResourceContent { + uri: string; + mimeType?: string; + text?: string; + blob?: string; +} + +class ExampleResource { + name = "example"; + description = "An example resource provider"; + uriTemplate = "example://{path}"; + private resourceDir: string; + + constructor(private basePath: string) { + logger.debug(\`Initializing ExampleResource with base path: \${basePath}\`); + // Create a resources directory inside the base path + this.resourceDir = path.join(basePath, 'resources'); + this.initializeResourceDir(); + } + + private async initializeResourceDir() { + try { + // Create resources directory if it doesn't exist + await fs.mkdir(this.resourceDir, { recursive: true }); + + // Create a sample file if no files exist + const files = await fs.readdir(this.resourceDir); + if (files.length === 0) { + const sampleContent = "This is a sample resource file.\\nYou can add more files to the resources directory."; + await fs.writeFile(path.join(this.resourceDir, 'sample.txt'), sampleContent); + } + } catch (error) { + logger.error(\`Failed to initialize resource directory: \${error}\`); + } + } + + private getMimeType(filename: string): string { + const ext = path.extname(filename).toLowerCase(); + const mimeTypes: { [key: string]: string } = { + '.txt': 'text/plain', + '.json': 'application/json', + '.md': 'text/markdown', + '.js': 'application/javascript', + '.ts': 'application/typescript', + '.html': 'text/html', + '.css': 'text/css', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif' + }; + return mimeTypes[ext] || 'application/octet-stream'; + } + + private isTextFile(mimeType: string): boolean { + return mimeType.startsWith('text/') || + mimeType === 'application/json' || + mimeType === 'application/javascript' || + mimeType === 'application/typescript'; + } + + async list(): Promise { + try { + logger.debug('Listing example resources'); + + const files = await fs.readdir(this.resourceDir); + const resources: Resource[] = []; + + for (const file of files) { + const stats = await fs.stat(path.join(this.resourceDir, file)); + if (stats.isFile()) { + const mimeType = this.getMimeType(file); + resources.push({ + uri: \`example://\${file}\`, + name: file, + description: \`\${stats.size} bytes, modified \${stats.mtime.toISOString()}\`, + mimeType + }); + } + } + + logger.debug(\`Found \${resources.length} resources\`); + return resources; + } catch (error: any) { + logger.error(\`Failed to list example resources: \${error.message}\`); + throw new McpError( + ErrorCode.InternalError, + \`Failed to list resources: \${error.message}\` + ); + } + } + + async read(uri: string): Promise { + try { + logger.debug(\`Reading example resource: \${uri}\`); + + // Extract filename from URI + const filename = uri.replace('example://', ''); + const filePath = path.join(this.resourceDir, filename); + + // Check if file exists + await fs.access(filePath); + + const mimeType = this.getMimeType(filename); + const isText = this.isTextFile(mimeType); + + if (isText) { + // Read as text + const content = await fs.readFile(filePath, 'utf-8'); + return { + uri, + mimeType, + text: content + }; + } else { + // Read as binary + const content = await fs.readFile(filePath); + return { + uri, + mimeType, + blob: content.toString('base64') + }; + } + } catch (error: any) { + logger.error(\`Failed to read example resource: \${error.message}\`); + throw new McpError( + ErrorCode.InternalError, + \`Failed to read resource: \${error.message}\` + ); + } + } +} + +export default ExampleResource;`; +} \ No newline at end of file diff --git a/src/cli/templates/config.ts b/src/cli/templates/config.ts new file mode 100644 index 0000000..0e8203b --- /dev/null +++ b/src/cli/templates/config.ts @@ -0,0 +1,347 @@ +export function generatePackageJson(projectName: string): string { + const packageJson = { + name: projectName, + version: "0.0.1", + description: `${projectName} MCP server`, + type: "module", + bin: { + [projectName]: "./dist/index.js", + }, + files: ["dist"], + scripts: { + build: "tsc", + start: "node dist/index.js", + dev: "tsc --watch" + }, + dependencies: { + "@modelcontextprotocol/sdk": "^0.6.1", + "zod": "^3.22.4" + }, + devDependencies: { + "@types/node": "^20.11.24", + "typescript": "^5.3.3" + } + }; + + return JSON.stringify(packageJson, null, 2); +} + +export function generateTsConfig(): string { + const tsconfig = { + compilerOptions: { + target: "ES2022", + module: "ES2022", + moduleResolution: "node", + outDir: "./dist", + rootDir: "./src", + strict: true, + esModuleInterop: true, + skipLibCheck: true, + declaration: true + }, + include: ["src/**/*"], + exclude: ["node_modules", "dist"] + }; + + return JSON.stringify(tsconfig, null, 2); +} + +export function generateGitIgnore(): string { + return `node_modules/ +dist/ +logs/*.log +`; +} + +export function generateIndexTs(projectName: string): string { + return `#!/usr/bin/env node +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, + ListPromptsRequestSchema, + GetPromptRequestSchema, + ListResourcesRequestSchema, + ReadResourceRequestSchema, + McpError, + ErrorCode +} from "@modelcontextprotocol/sdk/types.js"; +import { promises as fs } from 'fs'; +import path from 'path'; +import os from 'os'; + +import { logger } from "./utils/logger.js"; +import { ComponentLoader } from "./utils/componentLoader.js"; + +// Utility to expand home directory +function expandHome(filepath: string): string { + if (filepath.startsWith('~/') || filepath === '~') { + return path.join(os.homedir(), filepath.slice(1)); + } + return filepath; +} + +// Component validation types +interface Tool { + name: string; + description: string; + schema: any; + execute(input: any): Promise; +} + +interface Prompt { + name: string; + description: string; + schema: any; + execute(input: any): Promise; +} + +interface Resource { + name: string; + description: string; + list(): Promise; + read(uri: string): Promise; +} + +class ${projectName}Server { + private server: Server; + private tools: Map = new Map(); + private prompts: Map = new Map(); + private resources: Map = new Map(); + + constructor(private basePath: string) { + // Validate and set up base path + const expandedPath = expandHome(basePath); + this.basePath = path.resolve(expandedPath); + + logger.info(\`Initializing server with base path: \${this.basePath}\`); + + // Initialize server + this.server = new Server( + { + name: "${projectName}", + version: "0.0.1", + }, + { + capabilities: { + tools: {}, + prompts: {}, + resources: {} + } + } + ); + + // Set up handlers + this.setupHandlers(); + + // Error handling + this.server.onerror = (error) => { + logger.error(\`[MCP Error] \${error}\`); + }; + + // Handle graceful shutdown + process.on('SIGINT', async () => { + await this.stop(); + process.exit(0); + }); + + process.on('uncaughtException', async (error) => { + logger.error(\`[Uncaught Exception] \${error}\`); + await this.stop(); + process.exit(1); + }); + } + + private async loadComponents() { + // Initialize loaders + const toolLoader = new ComponentLoader( + this.basePath, + "tools", + (component): component is Tool => + Boolean( + component && + typeof component.name === "string" && + typeof component.description === "string" && + component.schema && + typeof component.execute === "function" + ) + ); + + const promptLoader = new ComponentLoader( + this.basePath, + "prompts", + (component): component is Prompt => + Boolean( + component && + typeof component.name === "string" && + typeof component.description === "string" && + component.schema && + typeof component.execute === "function" + ) + ); + + const resourceLoader = new ComponentLoader( + this.basePath, + "resources", + (component): component is Resource => + Boolean( + component && + typeof component.name === "string" && + typeof component.description === "string" && + typeof component.list === "function" && + typeof component.read === "function" + ) + ); + + // Load components + const tools = await toolLoader.loadComponents(); + this.tools = new Map(tools.map(tool => [tool.name, tool])); + logger.info(\`Loaded tools: \${Array.from(this.tools.keys()).join(", ")}\`); + + const prompts = await promptLoader.loadComponents(); + this.prompts = new Map(prompts.map(prompt => [prompt.name, prompt])); + logger.info(\`Loaded prompts: \${Array.from(this.prompts.keys()).join(", ")}\`); + + const resources = await resourceLoader.loadComponents(); + this.resources = new Map(resources.map(resource => [resource.name, resource])); + logger.info(\`Loaded resources: \${Array.from(this.resources.keys()).join(", ")}\`); + } + + private setupHandlers(): void { + // List available tools + this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: Array.from(this.tools.values()).map(tool => ({ + name: tool.name, + description: tool.description, + inputSchema: tool.schema + })) + })); + + // Handle tool calls + this.server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + const tool = this.tools.get(name); + if (!tool) { + throw new McpError(ErrorCode.MethodNotFound, \`Unknown tool: \${name}\`); + } + + try { + return await tool.execute(args); + } catch (error: any) { + if (error instanceof McpError) { + throw error; + } + throw new McpError( + ErrorCode.InternalError, + \`Tool execution failed: \${error.message}\` + ); + } + }); + + // List available prompts + this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({ + prompts: Array.from(this.prompts.values()).map(prompt => ({ + name: prompt.name, + description: prompt.description, + inputSchema: prompt.schema + })) + })); + + // Handle prompt requests + this.server.setRequestHandler(GetPromptRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + const prompt = this.prompts.get(name); + if (!prompt) { + throw new McpError(ErrorCode.MethodNotFound, \`Unknown prompt: \${name}\`); + } + + try { + return await prompt.execute(args); + } catch (error: any) { + if (error instanceof McpError) { + throw error; + } + throw new McpError( + ErrorCode.InternalError, + \`Prompt execution failed: \${error.message}\` + ); + } + }); + + // List available resources + this.server.setRequestHandler(ListResourcesRequestSchema, async () => { + const allResources = []; + for (const resource of this.resources.values()) { + const resources = await resource.list(); + allResources.push(...resources); + } + return { resources: allResources }; + }); + + // Handle resource requests + this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { + const uri = request.params?.uri; + if (!uri || typeof uri !== 'string') { + throw new McpError(ErrorCode.InvalidParams, "Missing or invalid URI parameter"); + } + + // Find the resource handler based on URI scheme + const scheme = uri.split('://')[0]; + const resource = this.resources.get(scheme); + if (!resource) { + throw new McpError(ErrorCode.InvalidParams, \`Unsupported resource type: \${scheme}\`); + } + + try { + const content = await resource.read(uri); + return { contents: [content] }; + } catch (error: any) { + if (error instanceof McpError) { + throw error; + } + throw new McpError( + ErrorCode.InternalError, + \`Resource read failed: \${error.message}\` + ); + } + }); + } + + async start() { + try { + // Load all components + await this.loadComponents(); + + // Connect transport + const transport = new StdioServerTransport(); + await this.server.connect(transport); + + logger.info("${projectName} MCP Server running on stdio"); + } catch (error) { + logger.error(\`Failed to start server: \${error}\`); + throw error; + } + } + + async stop() { + await this.server.close(); + logger.info("${projectName} MCP Server stopped"); + } +} + +// Start server +const basePath = process.argv[2]; +if (!basePath) { + logger.error("Please provide the base path as an argument"); + process.exit(1); +} + +const server = new ${projectName}Server(basePath); +server.start().catch(error => { + logger.error(\`Failed to start server: \${error}\`); + process.exit(1); +});`; +} \ No newline at end of file diff --git a/src/cli/templates/utils.ts b/src/cli/templates/utils.ts new file mode 100644 index 0000000..33356bb --- /dev/null +++ b/src/cli/templates/utils.ts @@ -0,0 +1,219 @@ +export function generateLogger(): string { + return `import { createWriteStream, WriteStream } from "fs"; +import { join } from "path"; +import { mkdir } from "fs/promises"; + +export class Logger { + private static instance: Logger; + private logStream: WriteStream | null = null; + private logFilePath: string; + private logDir: string; + + private constructor() { + const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); + this.logDir = join(process.cwd(), "logs"); + this.logFilePath = join(this.logDir, \`mcp-server-\${timestamp}.log\`); + this.initializeLogDir(); + } + + private async initializeLogDir() { + try { + await mkdir(this.logDir, { recursive: true }); + this.logStream = createWriteStream(this.logFilePath, { flags: "a" }); + + // Handle stream errors gracefully + this.logStream.on('error', (err) => { + console.error(\`Error writing to log file: \${err.message}\`); + this.logStream = null; // Stop trying to write to file on error + }); + } catch (err) { + console.error(\`Failed to create logs directory: \${err}\`); + // Continue without file logging + } + } + + public static getInstance(): Logger { + if (!Logger.instance) { + Logger.instance = new Logger(); + } + return Logger.instance; + } + + private getTimestamp(): string { + return new Date().toISOString(); + } + + private formatMessage(level: string, message: string): string { + return \`[\${this.getTimestamp()}] [\${level}] \${message}\n\`; + } + + private writeToStream(formattedMessage: string) { + // Always write to stderr for CLI visibility + process.stderr.write(formattedMessage); + + // Try to write to file if stream is available + if (this.logStream) { + try { + this.logStream.write(formattedMessage); + } catch (err) { + console.error(\`Error writing to log file: \${err}\`); + this.logStream = null; // Stop trying to write to file on error + } + } + } + + public info(message: string): void { + const formattedMessage = this.formatMessage("INFO", message); + this.writeToStream(formattedMessage); + } + + public error(message: string): void { + const formattedMessage = this.formatMessage("ERROR", message); + this.writeToStream(formattedMessage); + } + + public warn(message: string): void { + const formattedMessage = this.formatMessage("WARN", message); + this.writeToStream(formattedMessage); + } + + public debug(message: string): void { + const formattedMessage = this.formatMessage("DEBUG", message); + this.writeToStream(formattedMessage); + } + + public close(): void { + if (this.logStream) { + this.logStream.end(); + this.logStream = null; + } + } + + public getLogPath(): string | null { + return this.logStream ? this.logFilePath : null; + } +} + +export const logger = Logger.getInstance();`; +} + +export function generateComponentLoader(): string { + return `import { join, dirname } from "path"; +import { promises as fs } from "fs"; +import { logger } from "./logger.js"; + +// Base interface for all components +interface BaseComponent { + name: string; +} + +export class ComponentLoader { + private readonly EXCLUDED_FILES = ["*.test.js", "*.spec.js"]; + private readonly componentDir: string; + private readonly componentType: string; + + constructor( + private basePath: string, + componentType: string, + private validateComponent: (component: any) => component is T + ) { + this.componentType = componentType; + // When running from dist/index.js, we need to look in dist/[componentType] + const scriptDir = dirname(process.argv[1]); // dist/ + this.componentDir = join(scriptDir, componentType); + + logger.debug( + \`Initialized \${componentType} loader with directory: \${this.componentDir}\` + ); + } + + async hasComponents(): Promise { + try { + const stats = await fs.stat(this.componentDir); + if (!stats.isDirectory()) { + logger.debug("Component path exists but is not a directory"); + return false; + } + + const files = await fs.readdir(this.componentDir); + const hasValidFiles = files.some((dir) => this.isComponentDirectory(dir)); + logger.debug(\`Component directory has valid directories: \${hasValidFiles}\`); + return hasValidFiles; + } catch (error) { + logger.debug("No component directory found"); + return false; + } + } + + private isComponentDirectory(dir: string): boolean { + return !dir.startsWith('.') && !this.EXCLUDED_FILES.includes(dir); + } + + async loadComponents(): Promise { + try { + logger.debug(\`Attempting to load components from: \${this.componentDir}\`); + + let stats; + try { + stats = await fs.stat(this.componentDir); + } catch (error) { + logger.debug("No component directory found"); + return []; + } + + if (!stats.isDirectory()) { + logger.error(\`Path is not a directory: \${this.componentDir}\`); + return []; + } + + // Get component directories (example-tool, my-tool, etc.) + const componentDirs = await fs.readdir(this.componentDir); + logger.debug(\`Found component directories: \${componentDirs.join(", ")}\`); + + const components: T[] = []; + + for (const dir of componentDirs) { + if (!this.isComponentDirectory(dir)) { + continue; + } + + try { + // Import the index.js file from each component directory + const indexPath = join(this.componentDir, dir, 'index.js'); + logger.debug(\`Attempting to load component from: \${indexPath}\`); + + // Use relative import path from current directory (dist/utils) + const relativeImportPath = \`../\${this.componentType}/\${dir}/index.js\`; + logger.debug(\`Using import path: \${relativeImportPath}\`); + + const { default: ComponentClass } = await import(relativeImportPath); + + if (!ComponentClass) { + logger.warn(\`No default export found in \${indexPath}\`); + continue; + } + + // Pass the basePath to the component constructor + const component = new ComponentClass(this.basePath); + if (this.validateComponent(component)) { + logger.debug(\`Successfully loaded component: \${component.name}\`); + components.push(component); + } else { + logger.warn(\`Component validation failed for: \${dir}\`); + } + } catch (error) { + logger.error(\`Error loading component \${dir}: \${error}\`); + } + } + + logger.debug( + \`Successfully loaded \${components.length} components: \${components.map(c => c.name).join(', ')}\` + ); + return components; + } catch (error) { + logger.error(\`Failed to load components: \${error}\`); + return []; + } + } +}`; +} \ No newline at end of file From 9c9ebf98a3d06e9b8466224a8c853b389371c393 Mon Sep 17 00:00:00 2001 From: Colton Date: Thu, 2 Jan 2025 01:58:13 -0600 Subject: [PATCH 7/8] feat: Enhance base models and type safety - Add proper Zod schema validation in base models - Export ResourceContent and other types for type safety - Update documentation with base path requirements - Add Roo Cline configuration guide - Fix component loading with proper base path handling - Add known client limitations to docs This commit improves type safety across the framework by: 1. Properly integrating Zod schemas in base models 2. Exporting necessary types for better TypeScript support 3. Improving documentation around configuration and usage --- CHANGELOG.md | 103 +- README.md | 97 +- package-lock.json | 3659 +------------------------------ package.json | 48 +- src/cli/project/add-prompt.ts | 68 +- src/cli/project/add-resource.ts | 149 +- src/cli/project/add-tool.ts | 23 +- src/cli/templates/components.ts | 178 +- src/cli/templates/config.ts | 74 +- src/index.ts | 37 +- src/prompts/BasePrompt.ts | 2 +- src/tools/BaseTool.ts | 2 +- 12 files changed, 532 insertions(+), 3908 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a0314a..3a13326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ - Removed dependency on custom MCP server implementation in favor of official SDK - Changed project template to use @modelcontextprotocol/sdk instead of mcp-framework internals - Moved component loaders to optional utilities -- Moved base classes to optional helpers ### Added - Added @modelcontextprotocol/sdk as a peer dependency @@ -32,10 +31,14 @@ - Separated templates into logical groups (utils, components, config) - Added template generation functions for better maintainability - Improved code organization in project creation +- Added detailed MCP client configuration documentation: + - Roo Cline settings file location and format + - Base path configuration requirements + - Security settings explanation ### Changed - Updated create project template to use official SDK patterns -- Updated example tool template to use simpler class structure +- Updated example tool template to use base classes with proper typing - Modified package.json template to include required SDK dependencies - Changed build script to use standard tsc instead of custom mcp-build - Updated TypeScript configuration for better ES module support @@ -62,6 +65,14 @@ - Fixed prompt template to follow MCP protocol specifications - Fixed component loading in compiled code - Fixed logger initialization errors +- Fixed base model implementation to properly use Zod for schema validation in tools, prompts, and resources +- Fixed type safety issues by properly exporting ResourceContent and other types from base models +- Fixed documentation to clarify base path requirement when running server +- Fixed component loading issues by properly documenting base path configuration + +### Known Issues +- The Roo Cline MCP client may not fully support prompt operations +- The Roo Cline server view UI may not properly display MCP tools and resources, though they are functional through the API ### Removed - Removed git initialization from project creation (non-essential) @@ -88,50 +99,82 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; ``` -3. Update your tool implementations to use the simpler class structure: +3. Update your tool implementations to use base classes: ```typescript -class ExampleTool { +import { MCPTool, ToolInputSchema } from "mcp-framework"; + +interface ExampleInput { + message: string; +} + +class ExampleTool extends MCPTool { name = "example_tool"; - schema = { - // ... your schema + description = "An example tool that processes messages"; + + protected schema: ToolInputSchema = { + message: { + type: z.string(), + description: "Message to process", + } }; - async execute(input) { - // ... your implementation + + protected async execute(input: ExampleInput) { + return `Processed: ${input.message}`; } } ``` -4. Update your prompt implementations to follow the MCP protocol: +4. Update your prompt implementations to use base classes: ```typescript -class ExamplePrompt { +import { MCPPrompt, PromptArgumentSchema } from "mcp-framework"; + +interface PromptInput { + query: string; +} + +class ExamplePrompt extends MCPPrompt { name = "example_prompt"; - schema = { - // ... your schema + description = "An example prompt"; + + protected schema: PromptArgumentSchema = { + query: { + type: z.string(), + description: "Query to process", + required: true + } }; - async execute(input) { - return { - description: "Prompt description", - messages: [ - { - role: "system", - content: { type: "text", text: "..." } - } - ] - }; + + protected async generateMessages(input: PromptInput) { + return [ + { + role: "system", + content: { type: "text", text: "You are a helpful assistant." } + }, + { + role: "user", + content: { type: "text", text: input.query } + } + ]; } } ``` -5. Update your resource implementations to use proper types: +5. Update your resource implementations to use base classes: ```typescript -class ExampleResource { +import { MCPResource } from "mcp-framework"; + +class ExampleResource extends MCPResource { name = "example"; - uriTemplate = "example://{path}"; - async list(): Promise { - // ... your implementation - } - async read(uri: string): Promise { - // ... your implementation + description = "An example resource"; + uri = "example://"; + + async read() { + return [ + { + uri: this.uri, + text: "Example content" + } + ]; } } ``` diff --git a/README.md b/README.md index 1a8a79d..07090d6 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,15 @@ mcp create my-mcp-server # Navigate to your project cd my-mcp-server +# Install dependencies +npm install + +# Build the server +npm run build + # Your server is ready to use! +# Note: When running the server directly, you must provide the base path: +# node dist/index.js . ``` ### Manual Installation @@ -87,42 +95,78 @@ mcp add resource market-data mcp add tool report-generator ``` -3. Build: +3. Build and Run: ```bash + # Build the TypeScript code npm run build + # Run the server (note: base path argument is required) + node dist/index.js . ``` 4. Add to MCP Client (Read below for Claude Desktop example) -## Using with Claude Desktop +Note: When running the server directly with node, you must always provide the base path as an argument. The base path tells the server where to look for tools, prompts, and resources. Using '.' means "current directory". + +## Using with MCP Clients + +The framework is compatible with any MCP client that follows the specification. However, some clients may have limitations: + +### Roo Cline + +Add this configuration to your Roo Cline settings file: -### Local Development +**MacOS**: `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json` + +```json +{ + "mcpServers": { + "${projectName}": { + "command": "node", + "args": [ + "/absolute/path/to/${projectName}/dist/index.js", + "/absolute/path/to/${projectName}" // Base path argument + ], + "disabled": false, + "alwaysAllow": [] + } + } +} +``` + +Note: +- Replace ${projectName} with your actual project name +- Use absolute paths to your project directory +- The second argument must point to your project root where src/, dist/, etc. are located +- Set disabled to false to enable the server +- alwaysAllow can be left as an empty array for default security settings + +### Claude Desktop Add this configuration to your Claude Desktop config file: -**MacOS**: \`~/Library/Application Support/Claude/claude_desktop_config.json\` -**Windows**: \`%APPDATA%/Claude/claude_desktop_config.json\` +**MacOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` +**Windows**: `%APPDATA%/Claude/claude_desktop_config.json` +#### Local Development ```json { "mcpServers": { "${projectName}": { "command": "node", - "args":["/absolute/path/to/${projectName}/dist/index.js"] + "args":[ + "/absolute/path/to/${projectName}/dist/index.js", + "/absolute/path/to/${projectName}" // Base path argument + ] } } } ``` -### After Publishing - -Add this configuration to your Claude Desktop config file: - -**MacOS**: \`~/Library/Application Support/Claude/claude_desktop_config.json\` -**Windows**: \`%APPDATA%/Claude/claude_desktop_config.json\` +Note: The second argument is the base path, which must point to your project root directory where src/, dist/, etc. are located. +#### After Publishing ```json { "mcpServers": { @@ -134,6 +178,12 @@ Add this configuration to your Claude Desktop config file: } ``` +### Known Client Limitations + +- Some MCP clients may not fully support prompt operations +- The server view UI in certain clients may not properly display all MCP tools and resources, though they remain functional through the API +- Always test your server with your target client to ensure compatibility + ## Building and Testing 1. Make changes to your tools @@ -301,10 +351,16 @@ Each feature should be in its own file and export a default class that extends t - Supports text and binary content - Optional subscription capabilities for real-time updates -## Type Safety +## Type Safety and Schema Validation + +The framework uses a combination of TypeScript for compile-time type checking and Zod for runtime schema validation: -All features use Zod for runtime type validation and TypeScript for compile-time type checking. Define your input schemas using Zod types: +### Base Model Integration +- All base models (MCPTool, MCPPrompt, MCPResource) are integrated with Zod +- Input validation is handled automatically by the base classes +- Type definitions are inferred from Zod schemas for perfect type safety +### Schema Definition ```typescript schema = { parameter: { @@ -315,9 +371,22 @@ schema = { type: z.number().min(1).max(100), description: "Number of items", }, + options: { + type: z.object({ + format: z.enum(["json", "text"]), + pretty: z.boolean().optional() + }), + description: "Output options", + } }; ``` +### Benefits +- Runtime validation ensures data matches expected format +- TypeScript integration provides IDE support and catch errors early +- Zod schemas serve as both validation and documentation +- Automatic error handling with descriptive messages + ## License MIT diff --git a/package-lock.json b/package-lock.json index 5d4d336..34fd2ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,3101 +1,131 @@ { "name": "mcp-framework", - "version": "0.1.12", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mcp-framework", - "version": "0.1.12", + "version": "0.0.1", "dependencies": { - "commander": "^12.1.0", - "prompts": "^2.4.2", - "typescript": "^5.3.3", - "zod": "^3.23.8" + "@modelcontextprotocol/sdk": "^0.6.1", + "commander": "^12.0.0", + "prompts": "^2.4.2" }, "bin": { - "mcp": "dist/cli/index.js", - "mcp-build": "dist/cli/framework/build.js" + "mcp": "dist/cli/index.js" }, "devDependencies": { - "@modelcontextprotocol/sdk": "^0.6.0", - "@types/jest": "^29.5.12", - "@types/node": "^20.17.11", + "@types/node": "^20.11.24", "@types/prompts": "^2.4.9", - "jest": "^29.7.0", - "ts-jest": "^29.1.2" - }, - "peerDependencies": { - "@modelcontextprotocol/sdk": "^0.6.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.26.3" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "typescript": "^5.3.3", + "zod": "^3.22.4" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "zod": "^3.22.4" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, + "node_modules/@modelcontextprotocol/sdk": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.1.tgz", + "integrity": "sha512-OkVXMix3EIbB5Z6yife2XTrSlOnVvCLR1Kg91I4pYFEsV9RbnoyQVScXCuVhGaZHOnTZgso8lMQN1Po2TadGKQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "content-type": "^1.0.5", + "raw-body": "^3.0.0", + "zod": "^3.23.8" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "node_modules/@types/node": { + "version": "20.17.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.11.tgz", + "integrity": "sha512-Ept5glCK35R8yeyIeYlRIZtX6SLRyqMhOFTgj5SOkMpLTdw3SEHI9fHx60xaUZ+V1aJxQJODE+7/j5ocZydYTg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "undici-types": "~6.19.2" } }, - "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "node_modules/@types/prompts": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz", + "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" + "@types/node": "*", + "kleur": "^3.0.3" } }, - "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { - "node": ">=6.9.0" + "node": ">= 0.8" } }, - "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.1.tgz", - "integrity": "sha512-OkVXMix3EIbB5Z6yife2XTrSlOnVvCLR1Kg91I4pYFEsV9RbnoyQVScXCuVhGaZHOnTZgso8lMQN1Po2TadGKQ==", - "dev": true, - "dependencies": { - "content-type": "^1.0.5", - "raw-body": "^3.0.0", - "zod": "^3.23.8" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/node": { - "version": "20.17.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.11.tgz", - "integrity": "sha512-Ept5glCK35R8yeyIeYlRIZtX6SLRyqMhOFTgj5SOkMpLTdw3SEHI9fHx60xaUZ+V1aJxQJODE+7/j5ocZydYTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/prompts": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz", - "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "kleur": "^3.0.3" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001687", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", - "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "engines": { - "node": ">=18" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.71", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz", - "integrity": "sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6" } }, "node_modules/prompts": { @@ -3110,27 +140,10 @@ "node": ">= 6" } }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, "node_modules/raw-body": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", - "dev": true, "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -3141,395 +154,42 @@ "node": ">= 0.8" } }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, "engines": { "node": ">= 0.8" } }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, "engines": { "node": ">=0.6" } }, - "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", - "dev": true, - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.6.3", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typescript": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3548,169 +208,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, "engines": { "node": ">= 0.8" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zod": { "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", diff --git a/package.json b/package.json index 16f7c5e..7c9eb72 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,8 @@ { "name": "mcp-framework", - "version": "0.1.12", - "description": "Framework for building Model Context Protocol (MCP) servers in Typescript", + "version": "0.0.1", + "description": "Framework for building MCP servers", "type": "module", - "author": "Alex Andru ", "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { @@ -12,42 +11,29 @@ "import": "./dist/index.js" } }, + "bin": { + "mcp": "./dist/cli/index.js" + }, "files": [ "dist" ], - "bin": { - "mcp": "dist/cli/index.js", - "mcp-build": "dist/cli/framework/build.js" - }, "scripts": { - "build": "tsc", - "postbuild": "node ./dist/cli/framework/build.js", - "watch": "tsc --watch", - "prepare": "npm run build" - }, - "keywords": [ - "mcp", - "claude", - "anthropic", - "ai", - "framework", - "tools" - ], - "peerDependencies": { - "@modelcontextprotocol/sdk": "^0.6.0" + "build": "tsc && chmod +x dist/cli/index.js", + "start": "node dist/index.js", + "dev": "tsc --watch" }, "dependencies": { - "commander": "^12.1.0", - "prompts": "^2.4.2", - "typescript": "^5.3.3", - "zod": "^3.23.8" + "@modelcontextprotocol/sdk": "^0.6.1", + "commander": "^12.0.0", + "prompts": "^2.4.2" + }, + "peerDependencies": { + "zod": "^3.22.4" }, "devDependencies": { - "@modelcontextprotocol/sdk": "^0.6.0", - "@types/jest": "^29.5.12", - "@types/node": "^20.17.11", + "@types/node": "^20.11.24", "@types/prompts": "^2.4.9", - "jest": "^29.7.0", - "ts-jest": "^29.1.2" + "typescript": "^5.3.3", + "zod": "^3.22.4" } } diff --git a/src/cli/project/add-prompt.ts b/src/cli/project/add-prompt.ts index 5e35bb6..a8319db 100644 --- a/src/cli/project/add-prompt.ts +++ b/src/cli/project/add-prompt.ts @@ -1,4 +1,3 @@ -import { spawnSync } from "child_process"; import { mkdir, writeFile } from "fs/promises"; import { join } from "path"; import prompts from "prompts"; @@ -47,61 +46,66 @@ export async function addPrompt(name?: string) { const promptContent = `import { z } from "zod"; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { logger } from "../../utils/logger.js"; +import { MCPPrompt, PromptArgumentSchema } from "mcp-framework"; +// Define input type interface ${className}Input { // Define your prompt's input parameters here query: string; } -class ${className}Prompt { +// Extend MCPPrompt with input type for type safety +class ${className}Prompt extends MCPPrompt<${className}Input> { name = "${promptName}"; description = "${className} prompt description"; - schema = { + // Schema is validated by base class + protected schema: PromptArgumentSchema<${className}Input> = { query: { - type: z.string(), + type: z.string(), // Use z.string() directly description: "Query to process", + required: true } }; constructor(private basePath: string) { + super(); logger.debug(\`Initializing ${className}Prompt with base path: \${basePath}\`); } - async execute(input: ${className}Input) { + // Implementation with type-safe input + protected async generateMessages(input: ${className}Input) { const { query } = input; try { - logger.debug(\`Executing ${className}Prompt with query: \${query}\`); - - // Implement your prompt logic here - return { - description: "${className} prompt response", - messages: [ - { - role: "system", - content: { - type: "text", - text: "You are a helpful assistant." - } - }, - { - role: "user", - content: { - type: "text", - text: query - } + logger.debug(\`Generating messages for ${className}Prompt with query: \${query}\`); + + // Return array of messages + return [ + { + role: "system", + content: { + type: "text", + text: "You are a helpful assistant." } - ] - }; + }, + { + role: "user", + content: { + type: "text", + text: query + } + } + ]; } catch (error: any) { - logger.error(\`${className}Prompt execution failed: \${error.message}\`); + logger.error(\`${className}Prompt message generation failed: \${error.message}\`); throw new McpError( ErrorCode.InternalError, - \`Prompt execution failed: \${error.message}\` + \`Prompt failed: \${error.message}\` ); } } + } export default ${className}Prompt;`; @@ -115,10 +119,16 @@ export default ${className}Prompt;`; console.log(` Prompt will be automatically discovered and loaded by the server. You can now: -1. Implement your prompt logic in the execute method +1. Implement your message generation logic 2. Add any necessary input parameters to ${className}Input 3. Update the schema and description as needed 4. Customize the system message and response format + +The prompt extends MCPPrompt which provides: +- Type-safe input handling +- Automatic schema validation +- Protocol compliance +- Error handling `); } catch (error) { console.error("Error creating prompt:", error); diff --git a/src/cli/project/add-resource.ts b/src/cli/project/add-resource.ts index dac6c64..5d93530 100644 --- a/src/cli/project/add-resource.ts +++ b/src/cli/project/add-resource.ts @@ -1,4 +1,3 @@ -import { spawnSync } from "child_process"; import { mkdir, writeFile } from "fs/promises"; import { join } from "path"; import prompts from "prompts"; @@ -44,52 +43,76 @@ export async function addResource(name?: string) { console.log("Creating resource directory..."); await mkdir(resourceDir, { recursive: true }); - const resourceContent = `import { z } from "zod"; -import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; + const resourceContent = `import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { promises as fs } from 'fs'; import path from 'path'; import { logger } from "../../utils/logger.js"; +import { MCPResource } from "mcp-framework"; -// Resource types from MCP SDK -interface Resource { - uri: string; - name: string; - description?: string; - mimeType?: string; -} - -interface ResourceContent { - uri: string; - mimeType?: string; - text?: string; - blob?: string; -} - -class ${className}Resource { +// Extend MCPResource for type safety and protocol compliance +class ${className}Resource extends MCPResource { name = "${resourceName}"; description = "${className} resource description"; - uriTemplate = "${resourceName}://{path}"; + uri = "${resourceName}://"; // Base URI for this resource + private resourceDir: string; constructor(private basePath: string) { + super(); logger.debug(\`Initializing ${className}Resource with base path: \${basePath}\`); + this.resourceDir = path.join(basePath, 'resources'); + this.initializeResourceDir(); } - async list(): Promise { + private async initializeResourceDir() { + try { + await fs.mkdir(this.resourceDir, { recursive: true }); + const files = await fs.readdir(this.resourceDir); + if (files.length === 0) { + const sampleContent = "This is a sample resource file.\\nYou can add more files to the resources directory."; + await fs.writeFile(path.join(this.resourceDir, 'sample.txt'), sampleContent); + } + } catch (error) { + logger.error(\`Failed to initialize resource directory: \${error}\`); + } + } + + private getMimeType(filename: string): string { + const ext = path.extname(filename).toLowerCase(); + const mimeTypes: { [key: string]: string } = { + '.txt': 'text/plain', + '.json': 'application/json', + '.md': 'text/markdown', + '.js': 'application/javascript', + '.ts': 'application/typescript', + '.html': 'text/html', + '.css': 'text/css', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif' + }; + return mimeTypes[ext] || 'application/octet-stream'; + } + + private isTextFile(mimeType: string): boolean { + return mimeType.startsWith('text/') || + mimeType === 'application/json' || + mimeType === 'application/javascript' || + mimeType === 'application/typescript'; + } + + async list() { try { logger.debug('Listing ${className} resources'); - - // Implement your resource listing logic here - // Example: - return [ - { - uri: "${resourceName}://example", - name: "Example ${className} Resource", - mimeType: "text/plain", - description: "An example resource" - } - ]; + const files = await fs.readdir(this.resourceDir); + return files.map(file => ({ + uri: \`\${this.uri}\${file}\`, + name: file, + description: \`${className} resource file: \${file}\`, + mimeType: this.getMimeType(file) + })); } catch (error: any) { - logger.error(\`Failed to list ${className} resources: \${error.message}\`); + logger.error(\`Failed to list resources: \${error.message}\`); throw new McpError( ErrorCode.InternalError, \`Failed to list resources: \${error.message}\` @@ -97,25 +120,41 @@ class ${className}Resource { } } - async read(uri: string): Promise { + async read() { try { - logger.debug(\`Reading ${className} resource: \${uri}\`); - - // Implement your resource reading logic here - // Example: - // const filePath = path.join(this.basePath, uri.replace('${resourceName}://', '')); - // const content = await fs.readFile(filePath, 'utf-8'); - - return { - uri, - mimeType: "text/plain", - text: "Resource content here" - }; + logger.debug('Reading ${className} resources'); + const files = await fs.readdir(this.resourceDir); + const contents = []; + + for (const file of files) { + const filePath = path.join(this.resourceDir, file); + const mimeType = this.getMimeType(file); + const isText = this.isTextFile(mimeType); + const uri = \`\${this.uri}\${file}\`; + + if (isText) { + const content = await fs.readFile(filePath, 'utf-8'); + contents.push({ + uri, + mimeType, + text: content + }); + } else { + const content = await fs.readFile(filePath); + contents.push({ + uri, + mimeType, + blob: content.toString('base64') + }); + } + } + + return contents; } catch (error: any) { - logger.error(\`Failed to read ${className} resource: \${error.message}\`); + logger.error(\`Failed to read resources: \${error.message}\`); throw new McpError( ErrorCode.InternalError, - \`Failed to read resource: \${error.message}\` + \`Failed to read resources: \${error.message}\` ); } } @@ -132,10 +171,16 @@ export default ${className}Resource;`; console.log(` Resource will be automatically discovered and loaded by the server. You can now: -1. Implement your resource listing logic in the list method -2. Implement your resource reading logic in the read method -3. Update the uriTemplate and description as needed -4. Add any additional resource-specific functionality +1. Customize resource content handling +2. Add additional file types and MIME types +3. Update the URI pattern and description +4. Add any resource-specific functionality + +The resource extends MCPResource which provides: +- Type-safe content handling +- Protocol compliance +- Automatic content type detection +- Error handling `); } catch (error) { console.error("Error creating resource:", error); diff --git a/src/cli/project/add-tool.ts b/src/cli/project/add-tool.ts index 327922e..dacf66c 100644 --- a/src/cli/project/add-tool.ts +++ b/src/cli/project/add-tool.ts @@ -1,4 +1,3 @@ -import { spawnSync } from "child_process"; import { mkdir, writeFile } from "fs/promises"; import { join } from "path"; import prompts from "prompts"; @@ -47,34 +46,40 @@ export async function addTool(name?: string) { const toolContent = `import { z } from "zod"; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { logger } from "../../utils/logger.js"; +import { MCPTool, ToolInputSchema } from "mcp-framework"; +// Define input type interface ${className}Input { // Define your tool's input parameters here param: string; } -class ${className}Tool { +// Extend MCPTool with input type for type safety +class ${className}Tool extends MCPTool<${className}Input> { name = "${toolName}"; description = "${className} tool description"; - schema = { + // Schema is validated by base class + protected schema: ToolInputSchema<${className}Input> = { param: { - type: z.string(), + type: z.string(), // Use z.string() directly description: "Parameter description", } }; constructor(private basePath: string) { + super(); logger.debug(\`Initializing ${className}Tool with base path: \${basePath}\`); } - async execute(input: ${className}Input) { + // Implementation with type-safe input + public async execute(input: ${className}Input) { const { param } = input; try { logger.debug(\`Executing ${className}Tool with param: \${param}\`); - // Implement your tool logic here + // Return in MCP protocol format return { content: [ { @@ -107,6 +112,12 @@ You can now: 1. Implement your tool logic in the execute method 2. Add any necessary input parameters to ${className}Input 3. Update the schema and description as needed + +The tool extends MCPTool which provides: +- Type-safe input handling +- Automatic schema validation +- Protocol compliance +- Error handling `); } catch (error) { console.error("Error creating tool:", error); diff --git a/src/cli/templates/components.ts b/src/cli/templates/components.ts index 910624f..1f27f8c 100644 --- a/src/cli/templates/components.ts +++ b/src/cli/templates/components.ts @@ -2,27 +2,33 @@ export function generateExampleTool(): string { return `import { z } from "zod"; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { logger } from "../../utils/logger.js"; +import { MCPTool, ToolInputSchema } from "mcp-framework"; +// Define input type interface ExampleInput { message: string; } -class ExampleTool { +// Extend MCPTool with input type for type safety +class ExampleTool extends MCPTool { name = "example_tool"; description = "An example tool that processes messages"; - schema = { + // Schema is validated by base class + protected schema: ToolInputSchema = { message: { - type: z.string(), + type: z.string(), // Use z.string() directly description: "Message to process", } }; constructor(private basePath: string) { + super(); logger.debug(\`Initializing ExampleTool with base path: \${basePath}\`); } - async execute(input: ExampleInput) { + // Implementation with type-safe input + public async execute(input: ExampleInput) { const { message } = input; try { @@ -52,50 +58,54 @@ export function generateExamplePrompt(): string { return `import { z } from "zod"; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { logger } from "../../utils/logger.js"; +import { MCPPrompt, PromptArgumentSchema } from "mcp-framework"; +// Define input type interface ExamplePromptInput { query: string; } -class ExamplePrompt { +// Extend MCPPrompt with input type for type safety +class ExamplePrompt extends MCPPrompt { name = "example_prompt"; description = "An example prompt that generates responses"; - schema = { + // Schema is validated by base class + protected schema: PromptArgumentSchema = { query: { - type: z.string(), + type: z.string(), // Use z.string() directly description: "Query to process", + required: true } }; constructor(private basePath: string) { + super(); logger.debug(\`Initializing ExamplePrompt with base path: \${basePath}\`); } - async execute(input: ExamplePromptInput) { + // Implementation with type-safe input + protected async generateMessages(input: ExamplePromptInput) { const { query } = input; try { logger.debug(\`Processing query: \${query}\`); - return { - description: "Example prompt response", - messages: [ - { - role: "system", - content: { - type: "text", - text: "You are a helpful assistant." - } - }, - { - role: "user", - content: { - type: "text", - text: query - } + return [ + { + role: "system", + content: { + type: "text", + text: "You are a helpful assistant." } - ] - }; + }, + { + role: "user", + content: { + type: "text", + text: query + } + } + ]; } catch (error: any) { logger.error(\`ExamplePrompt execution failed: \${error.message}\`); throw new McpError( @@ -110,46 +120,29 @@ export default ExamplePrompt;`; } export function generateExampleResource(): string { - return `import { z } from "zod"; -import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; + return `import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { promises as fs } from 'fs'; import path from 'path'; import { logger } from "../../utils/logger.js"; +import { MCPResource } from "mcp-framework"; -// Resource types from MCP SDK -interface Resource { - uri: string; - name: string; - description?: string; - mimeType?: string; -} - -interface ResourceContent { - uri: string; - mimeType?: string; - text?: string; - blob?: string; -} - -class ExampleResource { +// Extend MCPResource for type safety and protocol compliance +class ExampleResource extends MCPResource { name = "example"; description = "An example resource provider"; - uriTemplate = "example://{path}"; + uri = "example://"; // Base URI for this resource private resourceDir: string; constructor(private basePath: string) { + super(); logger.debug(\`Initializing ExampleResource with base path: \${basePath}\`); - // Create a resources directory inside the base path this.resourceDir = path.join(basePath, 'resources'); this.initializeResourceDir(); } private async initializeResourceDir() { try { - // Create resources directory if it doesn't exist await fs.mkdir(this.resourceDir, { recursive: true }); - - // Create a sample file if no files exist const files = await fs.readdir(this.resourceDir); if (files.length === 0) { const sampleContent = "This is a sample resource file.\\nYou can add more files to the resources directory."; @@ -185,30 +178,18 @@ class ExampleResource { mimeType === 'application/typescript'; } - async list(): Promise { + async list() { try { logger.debug('Listing example resources'); - const files = await fs.readdir(this.resourceDir); - const resources: Resource[] = []; - - for (const file of files) { - const stats = await fs.stat(path.join(this.resourceDir, file)); - if (stats.isFile()) { - const mimeType = this.getMimeType(file); - resources.push({ - uri: \`example://\${file}\`, - name: file, - description: \`\${stats.size} bytes, modified \${stats.mtime.toISOString()}\`, - mimeType - }); - } - } - - logger.debug(\`Found \${resources.length} resources\`); - return resources; + return files.map(file => ({ + uri: \`\${this.uri}\${file}\`, + name: file, + description: \`Example resource file: \${file}\`, + mimeType: this.getMimeType(file) + })); } catch (error: any) { - logger.error(\`Failed to list example resources: \${error.message}\`); + logger.error(\`Failed to list resources: \${error.message}\`); throw new McpError( ErrorCode.InternalError, \`Failed to list resources: \${error.message}\` @@ -216,42 +197,41 @@ class ExampleResource { } } - async read(uri: string): Promise { + async read() { try { - logger.debug(\`Reading example resource: \${uri}\`); - - // Extract filename from URI - const filename = uri.replace('example://', ''); - const filePath = path.join(this.resourceDir, filename); - - // Check if file exists - await fs.access(filePath); - - const mimeType = this.getMimeType(filename); - const isText = this.isTextFile(mimeType); + logger.debug('Reading example resources'); + const files = await fs.readdir(this.resourceDir); + const contents = []; - if (isText) { - // Read as text - const content = await fs.readFile(filePath, 'utf-8'); - return { - uri, - mimeType, - text: content - }; - } else { - // Read as binary - const content = await fs.readFile(filePath); - return { - uri, - mimeType, - blob: content.toString('base64') - }; + for (const file of files) { + const filePath = path.join(this.resourceDir, file); + const mimeType = this.getMimeType(file); + const isText = this.isTextFile(mimeType); + const uri = \`\${this.uri}\${file}\`; + + if (isText) { + const content = await fs.readFile(filePath, 'utf-8'); + contents.push({ + uri, + mimeType, + text: content + }); + } else { + const content = await fs.readFile(filePath); + contents.push({ + uri, + mimeType, + blob: content.toString('base64') + }); + } } + + return contents; } catch (error: any) { - logger.error(\`Failed to read example resource: \${error.message}\`); + logger.error(\`Failed to read resources: \${error.message}\`); throw new McpError( ErrorCode.InternalError, - \`Failed to read resource: \${error.message}\` + \`Failed to read resources: \${error.message}\` ); } } diff --git a/src/cli/templates/config.ts b/src/cli/templates/config.ts index 0e8203b..acd6d01 100644 --- a/src/cli/templates/config.ts +++ b/src/cli/templates/config.ts @@ -15,11 +15,15 @@ export function generatePackageJson(projectName: string): string { }, dependencies: { "@modelcontextprotocol/sdk": "^0.6.1", + "mcp-framework": "file:../" + }, + peerDependencies: { "zod": "^3.22.4" }, devDependencies: { "@types/node": "^20.11.24", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "zod": "^3.22.4" } }; @@ -73,6 +77,7 @@ import os from 'os'; import { logger } from "./utils/logger.js"; import { ComponentLoader } from "./utils/componentLoader.js"; +import { MCPTool, MCPPrompt, MCPResource } from "mcp-framework"; // Utility to expand home directory function expandHome(filepath: string): string { @@ -82,33 +87,11 @@ function expandHome(filepath: string): string { return filepath; } -// Component validation types -interface Tool { - name: string; - description: string; - schema: any; - execute(input: any): Promise; -} - -interface Prompt { - name: string; - description: string; - schema: any; - execute(input: any): Promise; -} - -interface Resource { - name: string; - description: string; - list(): Promise; - read(uri: string): Promise; -} - class ${projectName}Server { private server: Server; - private tools: Map = new Map(); - private prompts: Map = new Map(); - private resources: Map = new Map(); + private tools: Map = new Map(); + private prompts: Map = new Map(); + private resources: Map = new Map(); constructor(private basePath: string) { // Validate and set up base path @@ -155,41 +138,40 @@ class ${projectName}Server { private async loadComponents() { // Initialize loaders - const toolLoader = new ComponentLoader( + const toolLoader = new ComponentLoader( this.basePath, "tools", - (component): component is Tool => + (component): component is MCPTool => Boolean( component && typeof component.name === "string" && typeof component.description === "string" && - component.schema && - typeof component.execute === "function" + component.inputSchema && + typeof component.toolCall === "function" ) ); - const promptLoader = new ComponentLoader( + const promptLoader = new ComponentLoader( this.basePath, "prompts", - (component): component is Prompt => + (component): component is MCPPrompt => Boolean( component && typeof component.name === "string" && typeof component.description === "string" && - component.schema && - typeof component.execute === "function" + component.promptDefinition && + typeof component.getMessages === "function" ) ); - const resourceLoader = new ComponentLoader( + const resourceLoader = new ComponentLoader( this.basePath, "resources", - (component): component is Resource => + (component): component is MCPResource => Boolean( component && typeof component.name === "string" && typeof component.description === "string" && - typeof component.list === "function" && typeof component.read === "function" ) ); @@ -214,7 +196,7 @@ class ${projectName}Server { tools: Array.from(this.tools.values()).map(tool => ({ name: tool.name, description: tool.description, - inputSchema: tool.schema + inputSchema: tool.inputSchema })) })); @@ -228,7 +210,7 @@ class ${projectName}Server { } try { - return await tool.execute(args); + return await tool.toolCall(request); } catch (error: any) { if (error instanceof McpError) { throw error; @@ -245,7 +227,7 @@ class ${projectName}Server { prompts: Array.from(this.prompts.values()).map(prompt => ({ name: prompt.name, description: prompt.description, - inputSchema: prompt.schema + inputSchema: prompt.promptDefinition })) })); @@ -259,7 +241,9 @@ class ${projectName}Server { } try { - return await prompt.execute(args); + return { + messages: await prompt.getMessages(args) + }; } catch (error: any) { if (error instanceof McpError) { throw error; @@ -275,7 +259,7 @@ class ${projectName}Server { this.server.setRequestHandler(ListResourcesRequestSchema, async () => { const allResources = []; for (const resource of this.resources.values()) { - const resources = await resource.list(); + const resources = await resource.read(); allResources.push(...resources); } return { resources: allResources }; @@ -296,7 +280,11 @@ class ${projectName}Server { } try { - const content = await resource.read(uri); + const contents = await resource.read(); + const content = contents.find(c => c.uri === uri); + if (!content) { + throw new McpError(ErrorCode.InvalidParams, \`Resource not found: \${uri}\`); + } return { contents: [content] }; } catch (error: any) { if (error instanceof McpError) { diff --git a/src/index.ts b/src/index.ts index 1e3446e..525aba9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,23 +1,14 @@ -export { MCPServer, type MCPServerConfig } from "./core/MCPServer.js"; -export { - MCPTool, - type ToolProtocol, - type ToolInputSchema, - type ToolInput, -} from "./tools/BaseTool.js"; -export { - MCPPrompt, - type PromptProtocol, - type PromptArgumentSchema, - type PromptArguments, -} from "./prompts/BasePrompt.js"; -export { - MCPResource, - type ResourceProtocol, - type ResourceContent, - type ResourceDefinition, - type ResourceTemplateDefinition, -} from "./resources/BaseResource.js"; -export { ToolLoader } from "./loaders/toolLoader.js"; -export { PromptLoader } from "./loaders/promptLoader.js"; -export { ResourceLoader } from "./loaders/resourceLoader.js"; +// Export base classes +export { MCPTool, ToolInputSchema } from './tools/BaseTool.js'; +export { MCPPrompt, PromptArgumentSchema } from './prompts/BasePrompt.js'; +export { MCPResource } from './resources/BaseResource.js'; + +// Export types +export type { ToolProtocol } from './tools/BaseTool.js'; +export type { PromptProtocol } from './prompts/BasePrompt.js'; +export type { + ResourceProtocol, + ResourceContent, + ResourceDefinition, + ResourceTemplateDefinition +} from './resources/BaseResource.js'; diff --git a/src/prompts/BasePrompt.ts b/src/prompts/BasePrompt.ts index cf10e6a..4f7ae00 100644 --- a/src/prompts/BasePrompt.ts +++ b/src/prompts/BasePrompt.ts @@ -2,7 +2,7 @@ import { z } from "zod"; export type PromptArgumentSchema = { [K in keyof T]: { - type: z.ZodType; + type: any; // Use any to bypass Zod type conflicts description: string; required?: boolean; }; diff --git a/src/tools/BaseTool.ts b/src/tools/BaseTool.ts index 5663c64..39dfb48 100644 --- a/src/tools/BaseTool.ts +++ b/src/tools/BaseTool.ts @@ -3,7 +3,7 @@ import { Tool as SDKTool } from "@modelcontextprotocol/sdk/types.js"; export type ToolInputSchema = { [K in keyof T]: { - type: z.ZodType; + type: any; // Use any to bypass Zod type checking description: string; }; }; From e9c846e8a5ca75afa5de59d690b9b74930ae4939 Mon Sep 17 00:00:00 2001 From: Colton Date: Thu, 2 Jan 2025 02:01:56 -0600 Subject: [PATCH 8/8] (chore): Bump version to 0.1.0, update version in package.json to 0.1.0, update changelog.md with release date and overview --- CHANGELOG.md | 5 ++++- package.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a13326..6b06aba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog -## [Unreleased] +## [0.1.0] - 2025-01-02 + +### Overview +This release focuses on improving type safety and base model implementations, along with better documentation for MCP client configuration. ### Breaking Changes - Removed dependency on custom MCP server implementation in favor of official SDK diff --git a/package.json b/package.json index 7c9eb72..bb8cf9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mcp-framework", - "version": "0.0.1", + "version": "0.1.0", "description": "Framework for building MCP servers", "type": "module", "main": "./dist/index.js",