├── .gitignore ├── README.md ├── package.json ├── pnpm-lock.yaml ├── src ├── blog.ts ├── hello.ts └── weather.ts ├── test ├── mcp-client.ts ├── mock-server.test.ts ├── mock-server.ts ├── server-tools.test.ts ├── server.test.ts └── utils.ts ├── tsconfig.json └── vitest.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # vitepress build output 108 | **/.vitepress/dist 109 | 110 | # vitepress cache directory 111 | **/.vitepress/cache 112 | 113 | # Docusaurus cache and generated files 114 | .docusaurus 115 | 116 | # Serverless directories 117 | .serverless/ 118 | 119 | # FuseBox cache 120 | .fusebox/ 121 | 122 | # DynamoDB Local files 123 | .dynamodb/ 124 | 125 | # TernJS port file 126 | .tern-port 127 | 128 | # Stores VSCode versions used for testing VSCode extensions 129 | .vscode-test 130 | 131 | # yarn v2 132 | .yarn/cache 133 | .yarn/unplugged 134 | .yarn/build-state.yml 135 | .yarn/install-state.gz 136 | .pnp.* 137 | 138 | # Compiled binaries 139 | /bin/ 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MCP Tools 2 | 3 | This is a modular toolkit for creating MCP (Model Context Protocol) tools. Each tool is a standalone executable that can be used independently. 4 | 5 | https://github.com/modelcontextprotocol/typescript-sdk 6 | 7 | ## Prerequisites 8 | 9 | - Node.js 23+ (or Bun/Deno/Anything that supports running .ts files) 10 | - Bun (for building executables) 11 | 12 | ## Installing Bun 13 | 14 | If you don't have Bun installed, you can install it using one of the following methods: 15 | 16 | ### macOS and Linux 17 | 18 | ```bash 19 | # Using curl (recommended) 20 | curl -fsSL https://bun.sh/install | bash 21 | 22 | # Using Homebrew 23 | brew install oven-sh/bun/bun 24 | 25 | # Using npm 26 | npm install -g bun 27 | ``` 28 | 29 | ### Windows 30 | 31 | ```bash 32 | # Using PowerShell 33 | powershell -c "irm bun.sh/install.ps1|iex" 34 | 35 | # Using npm 36 | npm install -g bun 37 | 38 | # Using Scoop 39 | scoop install bun 40 | ``` 41 | 42 | Verify your installation by running: 43 | 44 | ```bash 45 | bun --version 46 | ``` 47 | 48 | ## Installation 49 | 50 | ```bash 51 | pnpm install 52 | ``` 53 | 54 | ## Project Structure 55 | 56 | This project demonstrates a modular approach to building MCP tools: 57 | 58 | - Each tool is defined in its own TypeScript file in the `src` directory 59 | - Each tool can be built into a standalone executable in the `bin` directory 60 | - The main `index.ts` provides a simple "Hello World" example 61 | 62 | ### Available Tools 63 | 64 | 1. **Hello** (`src/index.ts`): A simple greeting tool 65 | 2. **Weather** (`src/weather.ts`): Get weather alerts for a state 66 | 3. **Blog** (`src/blog.ts`): Tools for blog post creation and frontmatter generation 67 | 68 | ## Creating Your Own Tool 69 | 70 | To create a new tool: 71 | 72 | 1. Create a new TypeScript file in the `src` directory (e.g., `src/mytool.ts`) 73 | 2. Use the existing tools as templates 74 | 3. Add a build script to `package.json`: 75 | 76 | ```json 77 | "build:mytool": "mkdir -p bin && bun build src/mytool.ts --compile --minify --sourcemap --outfile bin/mcp-mytool" 78 | ``` 79 | 80 | 4. Update the `build:all` script to include your new tool 81 | 82 | ## Usage 83 | 84 | ### Building Executables 85 | 86 | ```bash 87 | # Build all tools 88 | pnpm build 89 | 90 | # Build a specific tool 91 | pnpm build:hello 92 | pnpm build:weather 93 | pnpm build:blog 94 | ``` 95 | 96 | The resulting executables will be in the `bin` directory and can be run directly: 97 | 98 | ```bash 99 | ./bin/mcp-hello 100 | ./bin/mcp-weather 101 | ./bin/mcp-blog 102 | ``` 103 | 104 | ## Cursor Notes 105 | 106 | When using these tools with Cursor, always use the full path to the executable: 107 | 108 | ``` 109 | /path/to/your/project/bin/mcp-hello 110 | /path/to/your/project/bin/mcp-weather 111 | /path/to/your/project/bin/mcp-blog 112 | ``` 113 | 114 | Alternatively, you can run the TypeScript files directly with Node: 115 | 116 | ``` 117 | /path/to/node ~/path/to/project/src/index.ts 118 | ``` 119 | 120 | ## Testing 121 | 122 | ```bash 123 | # Run all tests 124 | pnpm test 125 | 126 | # Run tests in watch mode 127 | pnpm test:watch 128 | 129 | # Run tests with coverage 130 | pnpm test:coverage 131 | ``` 132 | 133 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mcp-tools", 3 | "type": "module", 4 | "dependencies": { 5 | "@modelcontextprotocol/sdk": "^1.4.0", 6 | "zod": "^3.24.1" 7 | }, 8 | "devDependencies": { 9 | "@types/node": "^22.10.0", 10 | "@vitest/coverage-v8": "^3.0.7", 11 | "onchange": "^7.1.0", 12 | "typescript": "^5.7.3", 13 | "vite-tsconfig-paths": "^5.1.4", 14 | "vitest": "^3.0.7" 15 | }, 16 | "scripts": { 17 | "build": "pnpm build:all", 18 | "build:all": "pnpm build:hello && pnpm build:weather && pnpm build:blog", 19 | "build:hello": "mkdir -p bin && bun build src/hello.ts --compile --minify --sourcemap --outfile bin/mcp-hello", 20 | "build:weather": "mkdir -p bin && bun build src/weather.ts --compile --minify --sourcemap --outfile bin/mcp-weather", 21 | "build:blog": "mkdir -p bin && bun build src/blog.ts --compile --minify --sourcemap --outfile bin/mcp-blog", 22 | "watch:hello": "onchange 'src/hello.ts' -- pnpm build:hello", 23 | "watch:weather": "onchange 'src/weather.ts' -- pnpm build:weather", 24 | "watch:blog": "onchange 'src/blog.ts' -- pnpm build:blog", 25 | "watch:rules": "onchange 'src/rules.ts' -- pnpm build:rules", 26 | "watch:all": "onchange 'src/*.ts' -- pnpm build:all", 27 | "test": "vitest run", 28 | "test:watch": "vitest", 29 | "test:coverage": "vitest run --coverage" 30 | } 31 | } -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@modelcontextprotocol/sdk': 12 | specifier: ^1.4.0 13 | version: 1.6.1 14 | zod: 15 | specifier: ^3.24.1 16 | version: 3.24.2 17 | devDependencies: 18 | '@types/node': 19 | specifier: ^22.10.0 20 | version: 22.13.8 21 | '@vitest/coverage-v8': 22 | specifier: ^3.0.7 23 | version: 3.0.7(vitest@3.0.7(@types/node@22.13.8)) 24 | onchange: 25 | specifier: ^7.1.0 26 | version: 7.1.0 27 | typescript: 28 | specifier: ^5.7.3 29 | version: 5.8.2 30 | vite-tsconfig-paths: 31 | specifier: ^5.1.4 32 | version: 5.1.4(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.8)) 33 | vitest: 34 | specifier: ^3.0.7 35 | version: 3.0.7(@types/node@22.13.8) 36 | 37 | packages: 38 | 39 | '@ampproject/remapping@2.3.0': 40 | resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 41 | engines: {node: '>=6.0.0'} 42 | 43 | '@babel/helper-string-parser@7.25.9': 44 | resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} 45 | engines: {node: '>=6.9.0'} 46 | 47 | '@babel/helper-validator-identifier@7.25.9': 48 | resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} 49 | engines: {node: '>=6.9.0'} 50 | 51 | '@babel/parser@7.26.9': 52 | resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} 53 | engines: {node: '>=6.0.0'} 54 | hasBin: true 55 | 56 | '@babel/types@7.26.9': 57 | resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} 58 | engines: {node: '>=6.9.0'} 59 | 60 | '@bcoe/v8-coverage@1.0.2': 61 | resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} 62 | engines: {node: '>=18'} 63 | 64 | '@blakeembrey/deque@1.0.5': 65 | resolution: {integrity: sha512-6xnwtvp9DY1EINIKdTfvfeAtCYw4OqBZJhtiqkT3ivjnEfa25VQ3TsKvaFfKm8MyGIEfE95qLe+bNEt3nB0Ylg==} 66 | 67 | '@blakeembrey/template@1.2.0': 68 | resolution: {integrity: sha512-w/63nURdkRPpg3AXbNr7lPv6HgOuVDyefTumiXsbXxtIwcuk5EXayWR5OpSwDjsQPgaYsfUSedMduaNOjAYY8A==} 69 | 70 | '@esbuild/aix-ppc64@0.25.0': 71 | resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} 72 | engines: {node: '>=18'} 73 | cpu: [ppc64] 74 | os: [aix] 75 | 76 | '@esbuild/android-arm64@0.25.0': 77 | resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} 78 | engines: {node: '>=18'} 79 | cpu: [arm64] 80 | os: [android] 81 | 82 | '@esbuild/android-arm@0.25.0': 83 | resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} 84 | engines: {node: '>=18'} 85 | cpu: [arm] 86 | os: [android] 87 | 88 | '@esbuild/android-x64@0.25.0': 89 | resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} 90 | engines: {node: '>=18'} 91 | cpu: [x64] 92 | os: [android] 93 | 94 | '@esbuild/darwin-arm64@0.25.0': 95 | resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} 96 | engines: {node: '>=18'} 97 | cpu: [arm64] 98 | os: [darwin] 99 | 100 | '@esbuild/darwin-x64@0.25.0': 101 | resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} 102 | engines: {node: '>=18'} 103 | cpu: [x64] 104 | os: [darwin] 105 | 106 | '@esbuild/freebsd-arm64@0.25.0': 107 | resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} 108 | engines: {node: '>=18'} 109 | cpu: [arm64] 110 | os: [freebsd] 111 | 112 | '@esbuild/freebsd-x64@0.25.0': 113 | resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} 114 | engines: {node: '>=18'} 115 | cpu: [x64] 116 | os: [freebsd] 117 | 118 | '@esbuild/linux-arm64@0.25.0': 119 | resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} 120 | engines: {node: '>=18'} 121 | cpu: [arm64] 122 | os: [linux] 123 | 124 | '@esbuild/linux-arm@0.25.0': 125 | resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} 126 | engines: {node: '>=18'} 127 | cpu: [arm] 128 | os: [linux] 129 | 130 | '@esbuild/linux-ia32@0.25.0': 131 | resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} 132 | engines: {node: '>=18'} 133 | cpu: [ia32] 134 | os: [linux] 135 | 136 | '@esbuild/linux-loong64@0.25.0': 137 | resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} 138 | engines: {node: '>=18'} 139 | cpu: [loong64] 140 | os: [linux] 141 | 142 | '@esbuild/linux-mips64el@0.25.0': 143 | resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} 144 | engines: {node: '>=18'} 145 | cpu: [mips64el] 146 | os: [linux] 147 | 148 | '@esbuild/linux-ppc64@0.25.0': 149 | resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} 150 | engines: {node: '>=18'} 151 | cpu: [ppc64] 152 | os: [linux] 153 | 154 | '@esbuild/linux-riscv64@0.25.0': 155 | resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} 156 | engines: {node: '>=18'} 157 | cpu: [riscv64] 158 | os: [linux] 159 | 160 | '@esbuild/linux-s390x@0.25.0': 161 | resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} 162 | engines: {node: '>=18'} 163 | cpu: [s390x] 164 | os: [linux] 165 | 166 | '@esbuild/linux-x64@0.25.0': 167 | resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} 168 | engines: {node: '>=18'} 169 | cpu: [x64] 170 | os: [linux] 171 | 172 | '@esbuild/netbsd-arm64@0.25.0': 173 | resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} 174 | engines: {node: '>=18'} 175 | cpu: [arm64] 176 | os: [netbsd] 177 | 178 | '@esbuild/netbsd-x64@0.25.0': 179 | resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} 180 | engines: {node: '>=18'} 181 | cpu: [x64] 182 | os: [netbsd] 183 | 184 | '@esbuild/openbsd-arm64@0.25.0': 185 | resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} 186 | engines: {node: '>=18'} 187 | cpu: [arm64] 188 | os: [openbsd] 189 | 190 | '@esbuild/openbsd-x64@0.25.0': 191 | resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} 192 | engines: {node: '>=18'} 193 | cpu: [x64] 194 | os: [openbsd] 195 | 196 | '@esbuild/sunos-x64@0.25.0': 197 | resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} 198 | engines: {node: '>=18'} 199 | cpu: [x64] 200 | os: [sunos] 201 | 202 | '@esbuild/win32-arm64@0.25.0': 203 | resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} 204 | engines: {node: '>=18'} 205 | cpu: [arm64] 206 | os: [win32] 207 | 208 | '@esbuild/win32-ia32@0.25.0': 209 | resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} 210 | engines: {node: '>=18'} 211 | cpu: [ia32] 212 | os: [win32] 213 | 214 | '@esbuild/win32-x64@0.25.0': 215 | resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} 216 | engines: {node: '>=18'} 217 | cpu: [x64] 218 | os: [win32] 219 | 220 | '@isaacs/cliui@8.0.2': 221 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 222 | engines: {node: '>=12'} 223 | 224 | '@istanbuljs/schema@0.1.3': 225 | resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} 226 | engines: {node: '>=8'} 227 | 228 | '@jridgewell/gen-mapping@0.3.8': 229 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 230 | engines: {node: '>=6.0.0'} 231 | 232 | '@jridgewell/resolve-uri@3.1.2': 233 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 234 | engines: {node: '>=6.0.0'} 235 | 236 | '@jridgewell/set-array@1.2.1': 237 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 238 | engines: {node: '>=6.0.0'} 239 | 240 | '@jridgewell/sourcemap-codec@1.5.0': 241 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 242 | 243 | '@jridgewell/trace-mapping@0.3.25': 244 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 245 | 246 | '@modelcontextprotocol/sdk@1.6.1': 247 | resolution: {integrity: sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA==} 248 | engines: {node: '>=18'} 249 | 250 | '@pkgjs/parseargs@0.11.0': 251 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 252 | engines: {node: '>=14'} 253 | 254 | '@rollup/rollup-android-arm-eabi@4.34.9': 255 | resolution: {integrity: sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==} 256 | cpu: [arm] 257 | os: [android] 258 | 259 | '@rollup/rollup-android-arm64@4.34.9': 260 | resolution: {integrity: sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==} 261 | cpu: [arm64] 262 | os: [android] 263 | 264 | '@rollup/rollup-darwin-arm64@4.34.9': 265 | resolution: {integrity: sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==} 266 | cpu: [arm64] 267 | os: [darwin] 268 | 269 | '@rollup/rollup-darwin-x64@4.34.9': 270 | resolution: {integrity: sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==} 271 | cpu: [x64] 272 | os: [darwin] 273 | 274 | '@rollup/rollup-freebsd-arm64@4.34.9': 275 | resolution: {integrity: sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==} 276 | cpu: [arm64] 277 | os: [freebsd] 278 | 279 | '@rollup/rollup-freebsd-x64@4.34.9': 280 | resolution: {integrity: sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==} 281 | cpu: [x64] 282 | os: [freebsd] 283 | 284 | '@rollup/rollup-linux-arm-gnueabihf@4.34.9': 285 | resolution: {integrity: sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==} 286 | cpu: [arm] 287 | os: [linux] 288 | 289 | '@rollup/rollup-linux-arm-musleabihf@4.34.9': 290 | resolution: {integrity: sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==} 291 | cpu: [arm] 292 | os: [linux] 293 | 294 | '@rollup/rollup-linux-arm64-gnu@4.34.9': 295 | resolution: {integrity: sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==} 296 | cpu: [arm64] 297 | os: [linux] 298 | 299 | '@rollup/rollup-linux-arm64-musl@4.34.9': 300 | resolution: {integrity: sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==} 301 | cpu: [arm64] 302 | os: [linux] 303 | 304 | '@rollup/rollup-linux-loongarch64-gnu@4.34.9': 305 | resolution: {integrity: sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==} 306 | cpu: [loong64] 307 | os: [linux] 308 | 309 | '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': 310 | resolution: {integrity: sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==} 311 | cpu: [ppc64] 312 | os: [linux] 313 | 314 | '@rollup/rollup-linux-riscv64-gnu@4.34.9': 315 | resolution: {integrity: sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==} 316 | cpu: [riscv64] 317 | os: [linux] 318 | 319 | '@rollup/rollup-linux-s390x-gnu@4.34.9': 320 | resolution: {integrity: sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==} 321 | cpu: [s390x] 322 | os: [linux] 323 | 324 | '@rollup/rollup-linux-x64-gnu@4.34.9': 325 | resolution: {integrity: sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==} 326 | cpu: [x64] 327 | os: [linux] 328 | 329 | '@rollup/rollup-linux-x64-musl@4.34.9': 330 | resolution: {integrity: sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==} 331 | cpu: [x64] 332 | os: [linux] 333 | 334 | '@rollup/rollup-win32-arm64-msvc@4.34.9': 335 | resolution: {integrity: sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==} 336 | cpu: [arm64] 337 | os: [win32] 338 | 339 | '@rollup/rollup-win32-ia32-msvc@4.34.9': 340 | resolution: {integrity: sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==} 341 | cpu: [ia32] 342 | os: [win32] 343 | 344 | '@rollup/rollup-win32-x64-msvc@4.34.9': 345 | resolution: {integrity: sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==} 346 | cpu: [x64] 347 | os: [win32] 348 | 349 | '@types/estree@1.0.6': 350 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 351 | 352 | '@types/node@22.13.8': 353 | resolution: {integrity: sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==} 354 | 355 | '@vitest/coverage-v8@3.0.7': 356 | resolution: {integrity: sha512-Av8WgBJLTrfLOer0uy3CxjlVuWK4CzcLBndW1Nm2vI+3hZ2ozHututkfc7Blu1u6waeQ7J8gzPK/AsBRnWA5mQ==} 357 | peerDependencies: 358 | '@vitest/browser': 3.0.7 359 | vitest: 3.0.7 360 | peerDependenciesMeta: 361 | '@vitest/browser': 362 | optional: true 363 | 364 | '@vitest/expect@3.0.7': 365 | resolution: {integrity: sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==} 366 | 367 | '@vitest/mocker@3.0.7': 368 | resolution: {integrity: sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==} 369 | peerDependencies: 370 | msw: ^2.4.9 371 | vite: ^5.0.0 || ^6.0.0 372 | peerDependenciesMeta: 373 | msw: 374 | optional: true 375 | vite: 376 | optional: true 377 | 378 | '@vitest/pretty-format@3.0.7': 379 | resolution: {integrity: sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==} 380 | 381 | '@vitest/runner@3.0.7': 382 | resolution: {integrity: sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==} 383 | 384 | '@vitest/snapshot@3.0.7': 385 | resolution: {integrity: sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==} 386 | 387 | '@vitest/spy@3.0.7': 388 | resolution: {integrity: sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==} 389 | 390 | '@vitest/utils@3.0.7': 391 | resolution: {integrity: sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==} 392 | 393 | accepts@2.0.0: 394 | resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} 395 | engines: {node: '>= 0.6'} 396 | 397 | ansi-regex@5.0.1: 398 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 399 | engines: {node: '>=8'} 400 | 401 | ansi-regex@6.1.0: 402 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 403 | engines: {node: '>=12'} 404 | 405 | ansi-styles@4.3.0: 406 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 407 | engines: {node: '>=8'} 408 | 409 | ansi-styles@6.2.1: 410 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 411 | engines: {node: '>=12'} 412 | 413 | anymatch@3.1.3: 414 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 415 | engines: {node: '>= 8'} 416 | 417 | arg@4.1.3: 418 | resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} 419 | 420 | assertion-error@2.0.1: 421 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 422 | engines: {node: '>=12'} 423 | 424 | balanced-match@1.0.2: 425 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 426 | 427 | binary-extensions@2.3.0: 428 | resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} 429 | engines: {node: '>=8'} 430 | 431 | body-parser@2.1.0: 432 | resolution: {integrity: sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==} 433 | engines: {node: '>=18'} 434 | 435 | brace-expansion@2.0.1: 436 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 437 | 438 | braces@3.0.3: 439 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 440 | engines: {node: '>=8'} 441 | 442 | bytes@3.1.2: 443 | resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} 444 | engines: {node: '>= 0.8'} 445 | 446 | cac@6.7.14: 447 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 448 | engines: {node: '>=8'} 449 | 450 | call-bind-apply-helpers@1.0.2: 451 | resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} 452 | engines: {node: '>= 0.4'} 453 | 454 | call-bound@1.0.3: 455 | resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} 456 | engines: {node: '>= 0.4'} 457 | 458 | chai@5.2.0: 459 | resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} 460 | engines: {node: '>=12'} 461 | 462 | check-error@2.1.1: 463 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 464 | engines: {node: '>= 16'} 465 | 466 | chokidar@3.6.0: 467 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} 468 | engines: {node: '>= 8.10.0'} 469 | 470 | color-convert@2.0.1: 471 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 472 | engines: {node: '>=7.0.0'} 473 | 474 | color-name@1.1.4: 475 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 476 | 477 | content-disposition@1.0.0: 478 | resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} 479 | engines: {node: '>= 0.6'} 480 | 481 | content-type@1.0.5: 482 | resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} 483 | engines: {node: '>= 0.6'} 484 | 485 | cookie-signature@1.2.2: 486 | resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} 487 | engines: {node: '>=6.6.0'} 488 | 489 | cookie@0.7.1: 490 | resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} 491 | engines: {node: '>= 0.6'} 492 | 493 | cors@2.8.5: 494 | resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} 495 | engines: {node: '>= 0.10'} 496 | 497 | cross-spawn@7.0.6: 498 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 499 | engines: {node: '>= 8'} 500 | 501 | debug@2.6.9: 502 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} 503 | peerDependencies: 504 | supports-color: '*' 505 | peerDependenciesMeta: 506 | supports-color: 507 | optional: true 508 | 509 | debug@4.3.6: 510 | resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} 511 | engines: {node: '>=6.0'} 512 | peerDependencies: 513 | supports-color: '*' 514 | peerDependenciesMeta: 515 | supports-color: 516 | optional: true 517 | 518 | debug@4.4.0: 519 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 520 | engines: {node: '>=6.0'} 521 | peerDependencies: 522 | supports-color: '*' 523 | peerDependenciesMeta: 524 | supports-color: 525 | optional: true 526 | 527 | deep-eql@5.0.2: 528 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 529 | engines: {node: '>=6'} 530 | 531 | depd@2.0.0: 532 | resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 533 | engines: {node: '>= 0.8'} 534 | 535 | destroy@1.2.0: 536 | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} 537 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 538 | 539 | dunder-proto@1.0.1: 540 | resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 541 | engines: {node: '>= 0.4'} 542 | 543 | eastasianwidth@0.2.0: 544 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 545 | 546 | ee-first@1.1.1: 547 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} 548 | 549 | emoji-regex@8.0.0: 550 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 551 | 552 | emoji-regex@9.2.2: 553 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 554 | 555 | encodeurl@1.0.2: 556 | resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} 557 | engines: {node: '>= 0.8'} 558 | 559 | encodeurl@2.0.0: 560 | resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} 561 | engines: {node: '>= 0.8'} 562 | 563 | es-define-property@1.0.1: 564 | resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 565 | engines: {node: '>= 0.4'} 566 | 567 | es-errors@1.3.0: 568 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 569 | engines: {node: '>= 0.4'} 570 | 571 | es-module-lexer@1.6.0: 572 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} 573 | 574 | es-object-atoms@1.1.1: 575 | resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} 576 | engines: {node: '>= 0.4'} 577 | 578 | esbuild@0.25.0: 579 | resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} 580 | engines: {node: '>=18'} 581 | hasBin: true 582 | 583 | escape-html@1.0.3: 584 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 585 | 586 | estree-walker@3.0.3: 587 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 588 | 589 | etag@1.8.1: 590 | resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} 591 | engines: {node: '>= 0.6'} 592 | 593 | eventsource-parser@3.0.0: 594 | resolution: {integrity: sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==} 595 | engines: {node: '>=18.0.0'} 596 | 597 | eventsource@3.0.5: 598 | resolution: {integrity: sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==} 599 | engines: {node: '>=18.0.0'} 600 | 601 | expect-type@1.2.0: 602 | resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==} 603 | engines: {node: '>=12.0.0'} 604 | 605 | express-rate-limit@7.5.0: 606 | resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} 607 | engines: {node: '>= 16'} 608 | peerDependencies: 609 | express: ^4.11 || 5 || ^5.0.0-beta.1 610 | 611 | express@5.0.1: 612 | resolution: {integrity: sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==} 613 | engines: {node: '>= 18'} 614 | 615 | fill-range@7.1.1: 616 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 617 | engines: {node: '>=8'} 618 | 619 | finalhandler@2.0.0: 620 | resolution: {integrity: sha512-MX6Zo2adDViYh+GcxxB1dpO43eypOGUOL12rLCOTMQv/DfIbpSJUy4oQIIZhVZkH9e+bZWKMon0XHFEju16tkQ==} 621 | engines: {node: '>= 0.8'} 622 | 623 | foreground-child@3.3.1: 624 | resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 625 | engines: {node: '>=14'} 626 | 627 | forwarded@0.2.0: 628 | resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} 629 | engines: {node: '>= 0.6'} 630 | 631 | fresh@0.5.2: 632 | resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} 633 | engines: {node: '>= 0.6'} 634 | 635 | fresh@2.0.0: 636 | resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} 637 | engines: {node: '>= 0.8'} 638 | 639 | fsevents@2.3.3: 640 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 641 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 642 | os: [darwin] 643 | 644 | function-bind@1.1.2: 645 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 646 | 647 | get-intrinsic@1.3.0: 648 | resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} 649 | engines: {node: '>= 0.4'} 650 | 651 | get-proto@1.0.1: 652 | resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 653 | engines: {node: '>= 0.4'} 654 | 655 | glob-parent@5.1.2: 656 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 657 | engines: {node: '>= 6'} 658 | 659 | glob@10.4.5: 660 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 661 | hasBin: true 662 | 663 | globrex@0.1.2: 664 | resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} 665 | 666 | gopd@1.2.0: 667 | resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 668 | engines: {node: '>= 0.4'} 669 | 670 | has-flag@4.0.0: 671 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 672 | engines: {node: '>=8'} 673 | 674 | has-symbols@1.1.0: 675 | resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 676 | engines: {node: '>= 0.4'} 677 | 678 | hasown@2.0.2: 679 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 680 | engines: {node: '>= 0.4'} 681 | 682 | html-escaper@2.0.2: 683 | resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} 684 | 685 | http-errors@2.0.0: 686 | resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} 687 | engines: {node: '>= 0.8'} 688 | 689 | iconv-lite@0.5.2: 690 | resolution: {integrity: sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==} 691 | engines: {node: '>=0.10.0'} 692 | 693 | iconv-lite@0.6.3: 694 | resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} 695 | engines: {node: '>=0.10.0'} 696 | 697 | ignore@5.3.2: 698 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 699 | engines: {node: '>= 4'} 700 | 701 | inherits@2.0.4: 702 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 703 | 704 | ipaddr.js@1.9.1: 705 | resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} 706 | engines: {node: '>= 0.10'} 707 | 708 | is-binary-path@2.1.0: 709 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 710 | engines: {node: '>=8'} 711 | 712 | is-extglob@2.1.1: 713 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 714 | engines: {node: '>=0.10.0'} 715 | 716 | is-fullwidth-code-point@3.0.0: 717 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 718 | engines: {node: '>=8'} 719 | 720 | is-glob@4.0.3: 721 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 722 | engines: {node: '>=0.10.0'} 723 | 724 | is-number@7.0.0: 725 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 726 | engines: {node: '>=0.12.0'} 727 | 728 | is-promise@4.0.0: 729 | resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} 730 | 731 | isexe@2.0.0: 732 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 733 | 734 | istanbul-lib-coverage@3.2.2: 735 | resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} 736 | engines: {node: '>=8'} 737 | 738 | istanbul-lib-report@3.0.1: 739 | resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} 740 | engines: {node: '>=10'} 741 | 742 | istanbul-lib-source-maps@5.0.6: 743 | resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} 744 | engines: {node: '>=10'} 745 | 746 | istanbul-reports@3.1.7: 747 | resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} 748 | engines: {node: '>=8'} 749 | 750 | jackspeak@3.4.3: 751 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 752 | 753 | loupe@3.1.3: 754 | resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} 755 | 756 | lru-cache@10.4.3: 757 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 758 | 759 | magic-string@0.30.17: 760 | resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 761 | 762 | magicast@0.3.5: 763 | resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} 764 | 765 | make-dir@4.0.0: 766 | resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} 767 | engines: {node: '>=10'} 768 | 769 | math-intrinsics@1.1.0: 770 | resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} 771 | engines: {node: '>= 0.4'} 772 | 773 | media-typer@1.1.0: 774 | resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} 775 | engines: {node: '>= 0.8'} 776 | 777 | merge-descriptors@2.0.0: 778 | resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} 779 | engines: {node: '>=18'} 780 | 781 | methods@1.1.2: 782 | resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} 783 | engines: {node: '>= 0.6'} 784 | 785 | mime-db@1.52.0: 786 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 787 | engines: {node: '>= 0.6'} 788 | 789 | mime-db@1.53.0: 790 | resolution: {integrity: sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==} 791 | engines: {node: '>= 0.6'} 792 | 793 | mime-types@2.1.35: 794 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 795 | engines: {node: '>= 0.6'} 796 | 797 | mime-types@3.0.0: 798 | resolution: {integrity: sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==} 799 | engines: {node: '>= 0.6'} 800 | 801 | minimatch@9.0.5: 802 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 803 | engines: {node: '>=16 || 14 >=14.17'} 804 | 805 | minipass@7.1.2: 806 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 807 | engines: {node: '>=16 || 14 >=14.17'} 808 | 809 | ms@2.0.0: 810 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} 811 | 812 | ms@2.1.2: 813 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 814 | 815 | ms@2.1.3: 816 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 817 | 818 | nanoid@3.3.8: 819 | resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} 820 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 821 | hasBin: true 822 | 823 | negotiator@1.0.0: 824 | resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} 825 | engines: {node: '>= 0.6'} 826 | 827 | normalize-path@3.0.0: 828 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 829 | engines: {node: '>=0.10.0'} 830 | 831 | object-assign@4.1.1: 832 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 833 | engines: {node: '>=0.10.0'} 834 | 835 | object-inspect@1.13.4: 836 | resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} 837 | engines: {node: '>= 0.4'} 838 | 839 | on-finished@2.4.1: 840 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 841 | engines: {node: '>= 0.8'} 842 | 843 | once@1.4.0: 844 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 845 | 846 | onchange@7.1.0: 847 | resolution: {integrity: sha512-ZJcqsPiWUAUpvmnJri5TPBooqJOPmC0ttN65juhN15Q8xA+Nbg3BaxBHXQ45EistKKlKElb0edmbPWnKSBkvMg==} 848 | hasBin: true 849 | 850 | package-json-from-dist@1.0.1: 851 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 852 | 853 | parseurl@1.3.3: 854 | resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 855 | engines: {node: '>= 0.8'} 856 | 857 | path-key@3.1.1: 858 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 859 | engines: {node: '>=8'} 860 | 861 | path-scurry@1.11.1: 862 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 863 | engines: {node: '>=16 || 14 >=14.18'} 864 | 865 | path-to-regexp@8.2.0: 866 | resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} 867 | engines: {node: '>=16'} 868 | 869 | pathe@2.0.3: 870 | resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} 871 | 872 | pathval@2.0.0: 873 | resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} 874 | engines: {node: '>= 14.16'} 875 | 876 | picocolors@1.1.1: 877 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 878 | 879 | picomatch@2.3.1: 880 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 881 | engines: {node: '>=8.6'} 882 | 883 | pkce-challenge@4.1.0: 884 | resolution: {integrity: sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==} 885 | engines: {node: '>=16.20.0'} 886 | 887 | postcss@8.5.3: 888 | resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} 889 | engines: {node: ^10 || ^12 || >=14} 890 | 891 | proxy-addr@2.0.7: 892 | resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} 893 | engines: {node: '>= 0.10'} 894 | 895 | qs@6.13.0: 896 | resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} 897 | engines: {node: '>=0.6'} 898 | 899 | qs@6.14.0: 900 | resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} 901 | engines: {node: '>=0.6'} 902 | 903 | range-parser@1.2.1: 904 | resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} 905 | engines: {node: '>= 0.6'} 906 | 907 | raw-body@3.0.0: 908 | resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} 909 | engines: {node: '>= 0.8'} 910 | 911 | readdirp@3.6.0: 912 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 913 | engines: {node: '>=8.10.0'} 914 | 915 | rollup@4.34.9: 916 | resolution: {integrity: sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==} 917 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 918 | hasBin: true 919 | 920 | router@2.1.0: 921 | resolution: {integrity: sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==} 922 | engines: {node: '>= 18'} 923 | 924 | safe-buffer@5.2.1: 925 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 926 | 927 | safer-buffer@2.1.2: 928 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 929 | 930 | semver@7.7.1: 931 | resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} 932 | engines: {node: '>=10'} 933 | hasBin: true 934 | 935 | send@1.1.0: 936 | resolution: {integrity: sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==} 937 | engines: {node: '>= 18'} 938 | 939 | serve-static@2.1.0: 940 | resolution: {integrity: sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==} 941 | engines: {node: '>= 18'} 942 | 943 | setprototypeof@1.2.0: 944 | resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} 945 | 946 | shebang-command@2.0.0: 947 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 948 | engines: {node: '>=8'} 949 | 950 | shebang-regex@3.0.0: 951 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 952 | engines: {node: '>=8'} 953 | 954 | side-channel-list@1.0.0: 955 | resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} 956 | engines: {node: '>= 0.4'} 957 | 958 | side-channel-map@1.0.1: 959 | resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} 960 | engines: {node: '>= 0.4'} 961 | 962 | side-channel-weakmap@1.0.2: 963 | resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} 964 | engines: {node: '>= 0.4'} 965 | 966 | side-channel@1.1.0: 967 | resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} 968 | engines: {node: '>= 0.4'} 969 | 970 | siginfo@2.0.0: 971 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 972 | 973 | signal-exit@4.1.0: 974 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 975 | engines: {node: '>=14'} 976 | 977 | source-map-js@1.2.1: 978 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 979 | engines: {node: '>=0.10.0'} 980 | 981 | stackback@0.0.2: 982 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 983 | 984 | statuses@2.0.1: 985 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} 986 | engines: {node: '>= 0.8'} 987 | 988 | std-env@3.8.1: 989 | resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==} 990 | 991 | string-width@4.2.3: 992 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 993 | engines: {node: '>=8'} 994 | 995 | string-width@5.1.2: 996 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 997 | engines: {node: '>=12'} 998 | 999 | strip-ansi@6.0.1: 1000 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1001 | engines: {node: '>=8'} 1002 | 1003 | strip-ansi@7.1.0: 1004 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 1005 | engines: {node: '>=12'} 1006 | 1007 | supports-color@7.2.0: 1008 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1009 | engines: {node: '>=8'} 1010 | 1011 | test-exclude@7.0.1: 1012 | resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} 1013 | engines: {node: '>=18'} 1014 | 1015 | tinybench@2.9.0: 1016 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 1017 | 1018 | tinyexec@0.3.2: 1019 | resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 1020 | 1021 | tinypool@1.0.2: 1022 | resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} 1023 | engines: {node: ^18.0.0 || >=20.0.0} 1024 | 1025 | tinyrainbow@2.0.0: 1026 | resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} 1027 | engines: {node: '>=14.0.0'} 1028 | 1029 | tinyspy@3.0.2: 1030 | resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} 1031 | engines: {node: '>=14.0.0'} 1032 | 1033 | to-regex-range@5.0.1: 1034 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1035 | engines: {node: '>=8.0'} 1036 | 1037 | toidentifier@1.0.1: 1038 | resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} 1039 | engines: {node: '>=0.6'} 1040 | 1041 | tree-kill@1.2.2: 1042 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 1043 | hasBin: true 1044 | 1045 | tsconfck@3.1.5: 1046 | resolution: {integrity: sha512-CLDfGgUp7XPswWnezWwsCRxNmgQjhYq3VXHM0/XIRxhVrKw0M1if9agzryh1QS3nxjCROvV+xWxoJO1YctzzWg==} 1047 | engines: {node: ^18 || >=20} 1048 | hasBin: true 1049 | peerDependencies: 1050 | typescript: ^5.0.0 1051 | peerDependenciesMeta: 1052 | typescript: 1053 | optional: true 1054 | 1055 | type-is@2.0.0: 1056 | resolution: {integrity: sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==} 1057 | engines: {node: '>= 0.6'} 1058 | 1059 | typescript@5.8.2: 1060 | resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} 1061 | engines: {node: '>=14.17'} 1062 | hasBin: true 1063 | 1064 | undici-types@6.20.0: 1065 | resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 1066 | 1067 | unpipe@1.0.0: 1068 | resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 1069 | engines: {node: '>= 0.8'} 1070 | 1071 | utils-merge@1.0.1: 1072 | resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} 1073 | engines: {node: '>= 0.4.0'} 1074 | 1075 | vary@1.1.2: 1076 | resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} 1077 | engines: {node: '>= 0.8'} 1078 | 1079 | vite-node@3.0.7: 1080 | resolution: {integrity: sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==} 1081 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1082 | hasBin: true 1083 | 1084 | vite-tsconfig-paths@5.1.4: 1085 | resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} 1086 | peerDependencies: 1087 | vite: '*' 1088 | peerDependenciesMeta: 1089 | vite: 1090 | optional: true 1091 | 1092 | vite@6.2.0: 1093 | resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==} 1094 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1095 | hasBin: true 1096 | peerDependencies: 1097 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 1098 | jiti: '>=1.21.0' 1099 | less: '*' 1100 | lightningcss: ^1.21.0 1101 | sass: '*' 1102 | sass-embedded: '*' 1103 | stylus: '*' 1104 | sugarss: '*' 1105 | terser: ^5.16.0 1106 | tsx: ^4.8.1 1107 | yaml: ^2.4.2 1108 | peerDependenciesMeta: 1109 | '@types/node': 1110 | optional: true 1111 | jiti: 1112 | optional: true 1113 | less: 1114 | optional: true 1115 | lightningcss: 1116 | optional: true 1117 | sass: 1118 | optional: true 1119 | sass-embedded: 1120 | optional: true 1121 | stylus: 1122 | optional: true 1123 | sugarss: 1124 | optional: true 1125 | terser: 1126 | optional: true 1127 | tsx: 1128 | optional: true 1129 | yaml: 1130 | optional: true 1131 | 1132 | vitest@3.0.7: 1133 | resolution: {integrity: sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==} 1134 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1135 | hasBin: true 1136 | peerDependencies: 1137 | '@edge-runtime/vm': '*' 1138 | '@types/debug': ^4.1.12 1139 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 1140 | '@vitest/browser': 3.0.7 1141 | '@vitest/ui': 3.0.7 1142 | happy-dom: '*' 1143 | jsdom: '*' 1144 | peerDependenciesMeta: 1145 | '@edge-runtime/vm': 1146 | optional: true 1147 | '@types/debug': 1148 | optional: true 1149 | '@types/node': 1150 | optional: true 1151 | '@vitest/browser': 1152 | optional: true 1153 | '@vitest/ui': 1154 | optional: true 1155 | happy-dom: 1156 | optional: true 1157 | jsdom: 1158 | optional: true 1159 | 1160 | which@2.0.2: 1161 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1162 | engines: {node: '>= 8'} 1163 | hasBin: true 1164 | 1165 | why-is-node-running@2.3.0: 1166 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 1167 | engines: {node: '>=8'} 1168 | hasBin: true 1169 | 1170 | wrap-ansi@7.0.0: 1171 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1172 | engines: {node: '>=10'} 1173 | 1174 | wrap-ansi@8.1.0: 1175 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1176 | engines: {node: '>=12'} 1177 | 1178 | wrappy@1.0.2: 1179 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 1180 | 1181 | zod-to-json-schema@3.24.3: 1182 | resolution: {integrity: sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==} 1183 | peerDependencies: 1184 | zod: ^3.24.1 1185 | 1186 | zod@3.24.2: 1187 | resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} 1188 | 1189 | snapshots: 1190 | 1191 | '@ampproject/remapping@2.3.0': 1192 | dependencies: 1193 | '@jridgewell/gen-mapping': 0.3.8 1194 | '@jridgewell/trace-mapping': 0.3.25 1195 | 1196 | '@babel/helper-string-parser@7.25.9': {} 1197 | 1198 | '@babel/helper-validator-identifier@7.25.9': {} 1199 | 1200 | '@babel/parser@7.26.9': 1201 | dependencies: 1202 | '@babel/types': 7.26.9 1203 | 1204 | '@babel/types@7.26.9': 1205 | dependencies: 1206 | '@babel/helper-string-parser': 7.25.9 1207 | '@babel/helper-validator-identifier': 7.25.9 1208 | 1209 | '@bcoe/v8-coverage@1.0.2': {} 1210 | 1211 | '@blakeembrey/deque@1.0.5': {} 1212 | 1213 | '@blakeembrey/template@1.2.0': {} 1214 | 1215 | '@esbuild/aix-ppc64@0.25.0': 1216 | optional: true 1217 | 1218 | '@esbuild/android-arm64@0.25.0': 1219 | optional: true 1220 | 1221 | '@esbuild/android-arm@0.25.0': 1222 | optional: true 1223 | 1224 | '@esbuild/android-x64@0.25.0': 1225 | optional: true 1226 | 1227 | '@esbuild/darwin-arm64@0.25.0': 1228 | optional: true 1229 | 1230 | '@esbuild/darwin-x64@0.25.0': 1231 | optional: true 1232 | 1233 | '@esbuild/freebsd-arm64@0.25.0': 1234 | optional: true 1235 | 1236 | '@esbuild/freebsd-x64@0.25.0': 1237 | optional: true 1238 | 1239 | '@esbuild/linux-arm64@0.25.0': 1240 | optional: true 1241 | 1242 | '@esbuild/linux-arm@0.25.0': 1243 | optional: true 1244 | 1245 | '@esbuild/linux-ia32@0.25.0': 1246 | optional: true 1247 | 1248 | '@esbuild/linux-loong64@0.25.0': 1249 | optional: true 1250 | 1251 | '@esbuild/linux-mips64el@0.25.0': 1252 | optional: true 1253 | 1254 | '@esbuild/linux-ppc64@0.25.0': 1255 | optional: true 1256 | 1257 | '@esbuild/linux-riscv64@0.25.0': 1258 | optional: true 1259 | 1260 | '@esbuild/linux-s390x@0.25.0': 1261 | optional: true 1262 | 1263 | '@esbuild/linux-x64@0.25.0': 1264 | optional: true 1265 | 1266 | '@esbuild/netbsd-arm64@0.25.0': 1267 | optional: true 1268 | 1269 | '@esbuild/netbsd-x64@0.25.0': 1270 | optional: true 1271 | 1272 | '@esbuild/openbsd-arm64@0.25.0': 1273 | optional: true 1274 | 1275 | '@esbuild/openbsd-x64@0.25.0': 1276 | optional: true 1277 | 1278 | '@esbuild/sunos-x64@0.25.0': 1279 | optional: true 1280 | 1281 | '@esbuild/win32-arm64@0.25.0': 1282 | optional: true 1283 | 1284 | '@esbuild/win32-ia32@0.25.0': 1285 | optional: true 1286 | 1287 | '@esbuild/win32-x64@0.25.0': 1288 | optional: true 1289 | 1290 | '@isaacs/cliui@8.0.2': 1291 | dependencies: 1292 | string-width: 5.1.2 1293 | string-width-cjs: string-width@4.2.3 1294 | strip-ansi: 7.1.0 1295 | strip-ansi-cjs: strip-ansi@6.0.1 1296 | wrap-ansi: 8.1.0 1297 | wrap-ansi-cjs: wrap-ansi@7.0.0 1298 | 1299 | '@istanbuljs/schema@0.1.3': {} 1300 | 1301 | '@jridgewell/gen-mapping@0.3.8': 1302 | dependencies: 1303 | '@jridgewell/set-array': 1.2.1 1304 | '@jridgewell/sourcemap-codec': 1.5.0 1305 | '@jridgewell/trace-mapping': 0.3.25 1306 | 1307 | '@jridgewell/resolve-uri@3.1.2': {} 1308 | 1309 | '@jridgewell/set-array@1.2.1': {} 1310 | 1311 | '@jridgewell/sourcemap-codec@1.5.0': {} 1312 | 1313 | '@jridgewell/trace-mapping@0.3.25': 1314 | dependencies: 1315 | '@jridgewell/resolve-uri': 3.1.2 1316 | '@jridgewell/sourcemap-codec': 1.5.0 1317 | 1318 | '@modelcontextprotocol/sdk@1.6.1': 1319 | dependencies: 1320 | content-type: 1.0.5 1321 | cors: 2.8.5 1322 | eventsource: 3.0.5 1323 | express: 5.0.1 1324 | express-rate-limit: 7.5.0(express@5.0.1) 1325 | pkce-challenge: 4.1.0 1326 | raw-body: 3.0.0 1327 | zod: 3.24.2 1328 | zod-to-json-schema: 3.24.3(zod@3.24.2) 1329 | transitivePeerDependencies: 1330 | - supports-color 1331 | 1332 | '@pkgjs/parseargs@0.11.0': 1333 | optional: true 1334 | 1335 | '@rollup/rollup-android-arm-eabi@4.34.9': 1336 | optional: true 1337 | 1338 | '@rollup/rollup-android-arm64@4.34.9': 1339 | optional: true 1340 | 1341 | '@rollup/rollup-darwin-arm64@4.34.9': 1342 | optional: true 1343 | 1344 | '@rollup/rollup-darwin-x64@4.34.9': 1345 | optional: true 1346 | 1347 | '@rollup/rollup-freebsd-arm64@4.34.9': 1348 | optional: true 1349 | 1350 | '@rollup/rollup-freebsd-x64@4.34.9': 1351 | optional: true 1352 | 1353 | '@rollup/rollup-linux-arm-gnueabihf@4.34.9': 1354 | optional: true 1355 | 1356 | '@rollup/rollup-linux-arm-musleabihf@4.34.9': 1357 | optional: true 1358 | 1359 | '@rollup/rollup-linux-arm64-gnu@4.34.9': 1360 | optional: true 1361 | 1362 | '@rollup/rollup-linux-arm64-musl@4.34.9': 1363 | optional: true 1364 | 1365 | '@rollup/rollup-linux-loongarch64-gnu@4.34.9': 1366 | optional: true 1367 | 1368 | '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': 1369 | optional: true 1370 | 1371 | '@rollup/rollup-linux-riscv64-gnu@4.34.9': 1372 | optional: true 1373 | 1374 | '@rollup/rollup-linux-s390x-gnu@4.34.9': 1375 | optional: true 1376 | 1377 | '@rollup/rollup-linux-x64-gnu@4.34.9': 1378 | optional: true 1379 | 1380 | '@rollup/rollup-linux-x64-musl@4.34.9': 1381 | optional: true 1382 | 1383 | '@rollup/rollup-win32-arm64-msvc@4.34.9': 1384 | optional: true 1385 | 1386 | '@rollup/rollup-win32-ia32-msvc@4.34.9': 1387 | optional: true 1388 | 1389 | '@rollup/rollup-win32-x64-msvc@4.34.9': 1390 | optional: true 1391 | 1392 | '@types/estree@1.0.6': {} 1393 | 1394 | '@types/node@22.13.8': 1395 | dependencies: 1396 | undici-types: 6.20.0 1397 | 1398 | '@vitest/coverage-v8@3.0.7(vitest@3.0.7(@types/node@22.13.8))': 1399 | dependencies: 1400 | '@ampproject/remapping': 2.3.0 1401 | '@bcoe/v8-coverage': 1.0.2 1402 | debug: 4.4.0 1403 | istanbul-lib-coverage: 3.2.2 1404 | istanbul-lib-report: 3.0.1 1405 | istanbul-lib-source-maps: 5.0.6 1406 | istanbul-reports: 3.1.7 1407 | magic-string: 0.30.17 1408 | magicast: 0.3.5 1409 | std-env: 3.8.1 1410 | test-exclude: 7.0.1 1411 | tinyrainbow: 2.0.0 1412 | vitest: 3.0.7(@types/node@22.13.8) 1413 | transitivePeerDependencies: 1414 | - supports-color 1415 | 1416 | '@vitest/expect@3.0.7': 1417 | dependencies: 1418 | '@vitest/spy': 3.0.7 1419 | '@vitest/utils': 3.0.7 1420 | chai: 5.2.0 1421 | tinyrainbow: 2.0.0 1422 | 1423 | '@vitest/mocker@3.0.7(vite@6.2.0(@types/node@22.13.8))': 1424 | dependencies: 1425 | '@vitest/spy': 3.0.7 1426 | estree-walker: 3.0.3 1427 | magic-string: 0.30.17 1428 | optionalDependencies: 1429 | vite: 6.2.0(@types/node@22.13.8) 1430 | 1431 | '@vitest/pretty-format@3.0.7': 1432 | dependencies: 1433 | tinyrainbow: 2.0.0 1434 | 1435 | '@vitest/runner@3.0.7': 1436 | dependencies: 1437 | '@vitest/utils': 3.0.7 1438 | pathe: 2.0.3 1439 | 1440 | '@vitest/snapshot@3.0.7': 1441 | dependencies: 1442 | '@vitest/pretty-format': 3.0.7 1443 | magic-string: 0.30.17 1444 | pathe: 2.0.3 1445 | 1446 | '@vitest/spy@3.0.7': 1447 | dependencies: 1448 | tinyspy: 3.0.2 1449 | 1450 | '@vitest/utils@3.0.7': 1451 | dependencies: 1452 | '@vitest/pretty-format': 3.0.7 1453 | loupe: 3.1.3 1454 | tinyrainbow: 2.0.0 1455 | 1456 | accepts@2.0.0: 1457 | dependencies: 1458 | mime-types: 3.0.0 1459 | negotiator: 1.0.0 1460 | 1461 | ansi-regex@5.0.1: {} 1462 | 1463 | ansi-regex@6.1.0: {} 1464 | 1465 | ansi-styles@4.3.0: 1466 | dependencies: 1467 | color-convert: 2.0.1 1468 | 1469 | ansi-styles@6.2.1: {} 1470 | 1471 | anymatch@3.1.3: 1472 | dependencies: 1473 | normalize-path: 3.0.0 1474 | picomatch: 2.3.1 1475 | 1476 | arg@4.1.3: {} 1477 | 1478 | assertion-error@2.0.1: {} 1479 | 1480 | balanced-match@1.0.2: {} 1481 | 1482 | binary-extensions@2.3.0: {} 1483 | 1484 | body-parser@2.1.0: 1485 | dependencies: 1486 | bytes: 3.1.2 1487 | content-type: 1.0.5 1488 | debug: 4.4.0 1489 | http-errors: 2.0.0 1490 | iconv-lite: 0.5.2 1491 | on-finished: 2.4.1 1492 | qs: 6.14.0 1493 | raw-body: 3.0.0 1494 | type-is: 2.0.0 1495 | transitivePeerDependencies: 1496 | - supports-color 1497 | 1498 | brace-expansion@2.0.1: 1499 | dependencies: 1500 | balanced-match: 1.0.2 1501 | 1502 | braces@3.0.3: 1503 | dependencies: 1504 | fill-range: 7.1.1 1505 | 1506 | bytes@3.1.2: {} 1507 | 1508 | cac@6.7.14: {} 1509 | 1510 | call-bind-apply-helpers@1.0.2: 1511 | dependencies: 1512 | es-errors: 1.3.0 1513 | function-bind: 1.1.2 1514 | 1515 | call-bound@1.0.3: 1516 | dependencies: 1517 | call-bind-apply-helpers: 1.0.2 1518 | get-intrinsic: 1.3.0 1519 | 1520 | chai@5.2.0: 1521 | dependencies: 1522 | assertion-error: 2.0.1 1523 | check-error: 2.1.1 1524 | deep-eql: 5.0.2 1525 | loupe: 3.1.3 1526 | pathval: 2.0.0 1527 | 1528 | check-error@2.1.1: {} 1529 | 1530 | chokidar@3.6.0: 1531 | dependencies: 1532 | anymatch: 3.1.3 1533 | braces: 3.0.3 1534 | glob-parent: 5.1.2 1535 | is-binary-path: 2.1.0 1536 | is-glob: 4.0.3 1537 | normalize-path: 3.0.0 1538 | readdirp: 3.6.0 1539 | optionalDependencies: 1540 | fsevents: 2.3.3 1541 | 1542 | color-convert@2.0.1: 1543 | dependencies: 1544 | color-name: 1.1.4 1545 | 1546 | color-name@1.1.4: {} 1547 | 1548 | content-disposition@1.0.0: 1549 | dependencies: 1550 | safe-buffer: 5.2.1 1551 | 1552 | content-type@1.0.5: {} 1553 | 1554 | cookie-signature@1.2.2: {} 1555 | 1556 | cookie@0.7.1: {} 1557 | 1558 | cors@2.8.5: 1559 | dependencies: 1560 | object-assign: 4.1.1 1561 | vary: 1.1.2 1562 | 1563 | cross-spawn@7.0.6: 1564 | dependencies: 1565 | path-key: 3.1.1 1566 | shebang-command: 2.0.0 1567 | which: 2.0.2 1568 | 1569 | debug@2.6.9: 1570 | dependencies: 1571 | ms: 2.0.0 1572 | 1573 | debug@4.3.6: 1574 | dependencies: 1575 | ms: 2.1.2 1576 | 1577 | debug@4.4.0: 1578 | dependencies: 1579 | ms: 2.1.3 1580 | 1581 | deep-eql@5.0.2: {} 1582 | 1583 | depd@2.0.0: {} 1584 | 1585 | destroy@1.2.0: {} 1586 | 1587 | dunder-proto@1.0.1: 1588 | dependencies: 1589 | call-bind-apply-helpers: 1.0.2 1590 | es-errors: 1.3.0 1591 | gopd: 1.2.0 1592 | 1593 | eastasianwidth@0.2.0: {} 1594 | 1595 | ee-first@1.1.1: {} 1596 | 1597 | emoji-regex@8.0.0: {} 1598 | 1599 | emoji-regex@9.2.2: {} 1600 | 1601 | encodeurl@1.0.2: {} 1602 | 1603 | encodeurl@2.0.0: {} 1604 | 1605 | es-define-property@1.0.1: {} 1606 | 1607 | es-errors@1.3.0: {} 1608 | 1609 | es-module-lexer@1.6.0: {} 1610 | 1611 | es-object-atoms@1.1.1: 1612 | dependencies: 1613 | es-errors: 1.3.0 1614 | 1615 | esbuild@0.25.0: 1616 | optionalDependencies: 1617 | '@esbuild/aix-ppc64': 0.25.0 1618 | '@esbuild/android-arm': 0.25.0 1619 | '@esbuild/android-arm64': 0.25.0 1620 | '@esbuild/android-x64': 0.25.0 1621 | '@esbuild/darwin-arm64': 0.25.0 1622 | '@esbuild/darwin-x64': 0.25.0 1623 | '@esbuild/freebsd-arm64': 0.25.0 1624 | '@esbuild/freebsd-x64': 0.25.0 1625 | '@esbuild/linux-arm': 0.25.0 1626 | '@esbuild/linux-arm64': 0.25.0 1627 | '@esbuild/linux-ia32': 0.25.0 1628 | '@esbuild/linux-loong64': 0.25.0 1629 | '@esbuild/linux-mips64el': 0.25.0 1630 | '@esbuild/linux-ppc64': 0.25.0 1631 | '@esbuild/linux-riscv64': 0.25.0 1632 | '@esbuild/linux-s390x': 0.25.0 1633 | '@esbuild/linux-x64': 0.25.0 1634 | '@esbuild/netbsd-arm64': 0.25.0 1635 | '@esbuild/netbsd-x64': 0.25.0 1636 | '@esbuild/openbsd-arm64': 0.25.0 1637 | '@esbuild/openbsd-x64': 0.25.0 1638 | '@esbuild/sunos-x64': 0.25.0 1639 | '@esbuild/win32-arm64': 0.25.0 1640 | '@esbuild/win32-ia32': 0.25.0 1641 | '@esbuild/win32-x64': 0.25.0 1642 | 1643 | escape-html@1.0.3: {} 1644 | 1645 | estree-walker@3.0.3: 1646 | dependencies: 1647 | '@types/estree': 1.0.6 1648 | 1649 | etag@1.8.1: {} 1650 | 1651 | eventsource-parser@3.0.0: {} 1652 | 1653 | eventsource@3.0.5: 1654 | dependencies: 1655 | eventsource-parser: 3.0.0 1656 | 1657 | expect-type@1.2.0: {} 1658 | 1659 | express-rate-limit@7.5.0(express@5.0.1): 1660 | dependencies: 1661 | express: 5.0.1 1662 | 1663 | express@5.0.1: 1664 | dependencies: 1665 | accepts: 2.0.0 1666 | body-parser: 2.1.0 1667 | content-disposition: 1.0.0 1668 | content-type: 1.0.5 1669 | cookie: 0.7.1 1670 | cookie-signature: 1.2.2 1671 | debug: 4.3.6 1672 | depd: 2.0.0 1673 | encodeurl: 2.0.0 1674 | escape-html: 1.0.3 1675 | etag: 1.8.1 1676 | finalhandler: 2.0.0 1677 | fresh: 2.0.0 1678 | http-errors: 2.0.0 1679 | merge-descriptors: 2.0.0 1680 | methods: 1.1.2 1681 | mime-types: 3.0.0 1682 | on-finished: 2.4.1 1683 | once: 1.4.0 1684 | parseurl: 1.3.3 1685 | proxy-addr: 2.0.7 1686 | qs: 6.13.0 1687 | range-parser: 1.2.1 1688 | router: 2.1.0 1689 | safe-buffer: 5.2.1 1690 | send: 1.1.0 1691 | serve-static: 2.1.0 1692 | setprototypeof: 1.2.0 1693 | statuses: 2.0.1 1694 | type-is: 2.0.0 1695 | utils-merge: 1.0.1 1696 | vary: 1.1.2 1697 | transitivePeerDependencies: 1698 | - supports-color 1699 | 1700 | fill-range@7.1.1: 1701 | dependencies: 1702 | to-regex-range: 5.0.1 1703 | 1704 | finalhandler@2.0.0: 1705 | dependencies: 1706 | debug: 2.6.9 1707 | encodeurl: 1.0.2 1708 | escape-html: 1.0.3 1709 | on-finished: 2.4.1 1710 | parseurl: 1.3.3 1711 | statuses: 2.0.1 1712 | unpipe: 1.0.0 1713 | transitivePeerDependencies: 1714 | - supports-color 1715 | 1716 | foreground-child@3.3.1: 1717 | dependencies: 1718 | cross-spawn: 7.0.6 1719 | signal-exit: 4.1.0 1720 | 1721 | forwarded@0.2.0: {} 1722 | 1723 | fresh@0.5.2: {} 1724 | 1725 | fresh@2.0.0: {} 1726 | 1727 | fsevents@2.3.3: 1728 | optional: true 1729 | 1730 | function-bind@1.1.2: {} 1731 | 1732 | get-intrinsic@1.3.0: 1733 | dependencies: 1734 | call-bind-apply-helpers: 1.0.2 1735 | es-define-property: 1.0.1 1736 | es-errors: 1.3.0 1737 | es-object-atoms: 1.1.1 1738 | function-bind: 1.1.2 1739 | get-proto: 1.0.1 1740 | gopd: 1.2.0 1741 | has-symbols: 1.1.0 1742 | hasown: 2.0.2 1743 | math-intrinsics: 1.1.0 1744 | 1745 | get-proto@1.0.1: 1746 | dependencies: 1747 | dunder-proto: 1.0.1 1748 | es-object-atoms: 1.1.1 1749 | 1750 | glob-parent@5.1.2: 1751 | dependencies: 1752 | is-glob: 4.0.3 1753 | 1754 | glob@10.4.5: 1755 | dependencies: 1756 | foreground-child: 3.3.1 1757 | jackspeak: 3.4.3 1758 | minimatch: 9.0.5 1759 | minipass: 7.1.2 1760 | package-json-from-dist: 1.0.1 1761 | path-scurry: 1.11.1 1762 | 1763 | globrex@0.1.2: {} 1764 | 1765 | gopd@1.2.0: {} 1766 | 1767 | has-flag@4.0.0: {} 1768 | 1769 | has-symbols@1.1.0: {} 1770 | 1771 | hasown@2.0.2: 1772 | dependencies: 1773 | function-bind: 1.1.2 1774 | 1775 | html-escaper@2.0.2: {} 1776 | 1777 | http-errors@2.0.0: 1778 | dependencies: 1779 | depd: 2.0.0 1780 | inherits: 2.0.4 1781 | setprototypeof: 1.2.0 1782 | statuses: 2.0.1 1783 | toidentifier: 1.0.1 1784 | 1785 | iconv-lite@0.5.2: 1786 | dependencies: 1787 | safer-buffer: 2.1.2 1788 | 1789 | iconv-lite@0.6.3: 1790 | dependencies: 1791 | safer-buffer: 2.1.2 1792 | 1793 | ignore@5.3.2: {} 1794 | 1795 | inherits@2.0.4: {} 1796 | 1797 | ipaddr.js@1.9.1: {} 1798 | 1799 | is-binary-path@2.1.0: 1800 | dependencies: 1801 | binary-extensions: 2.3.0 1802 | 1803 | is-extglob@2.1.1: {} 1804 | 1805 | is-fullwidth-code-point@3.0.0: {} 1806 | 1807 | is-glob@4.0.3: 1808 | dependencies: 1809 | is-extglob: 2.1.1 1810 | 1811 | is-number@7.0.0: {} 1812 | 1813 | is-promise@4.0.0: {} 1814 | 1815 | isexe@2.0.0: {} 1816 | 1817 | istanbul-lib-coverage@3.2.2: {} 1818 | 1819 | istanbul-lib-report@3.0.1: 1820 | dependencies: 1821 | istanbul-lib-coverage: 3.2.2 1822 | make-dir: 4.0.0 1823 | supports-color: 7.2.0 1824 | 1825 | istanbul-lib-source-maps@5.0.6: 1826 | dependencies: 1827 | '@jridgewell/trace-mapping': 0.3.25 1828 | debug: 4.4.0 1829 | istanbul-lib-coverage: 3.2.2 1830 | transitivePeerDependencies: 1831 | - supports-color 1832 | 1833 | istanbul-reports@3.1.7: 1834 | dependencies: 1835 | html-escaper: 2.0.2 1836 | istanbul-lib-report: 3.0.1 1837 | 1838 | jackspeak@3.4.3: 1839 | dependencies: 1840 | '@isaacs/cliui': 8.0.2 1841 | optionalDependencies: 1842 | '@pkgjs/parseargs': 0.11.0 1843 | 1844 | loupe@3.1.3: {} 1845 | 1846 | lru-cache@10.4.3: {} 1847 | 1848 | magic-string@0.30.17: 1849 | dependencies: 1850 | '@jridgewell/sourcemap-codec': 1.5.0 1851 | 1852 | magicast@0.3.5: 1853 | dependencies: 1854 | '@babel/parser': 7.26.9 1855 | '@babel/types': 7.26.9 1856 | source-map-js: 1.2.1 1857 | 1858 | make-dir@4.0.0: 1859 | dependencies: 1860 | semver: 7.7.1 1861 | 1862 | math-intrinsics@1.1.0: {} 1863 | 1864 | media-typer@1.1.0: {} 1865 | 1866 | merge-descriptors@2.0.0: {} 1867 | 1868 | methods@1.1.2: {} 1869 | 1870 | mime-db@1.52.0: {} 1871 | 1872 | mime-db@1.53.0: {} 1873 | 1874 | mime-types@2.1.35: 1875 | dependencies: 1876 | mime-db: 1.52.0 1877 | 1878 | mime-types@3.0.0: 1879 | dependencies: 1880 | mime-db: 1.53.0 1881 | 1882 | minimatch@9.0.5: 1883 | dependencies: 1884 | brace-expansion: 2.0.1 1885 | 1886 | minipass@7.1.2: {} 1887 | 1888 | ms@2.0.0: {} 1889 | 1890 | ms@2.1.2: {} 1891 | 1892 | ms@2.1.3: {} 1893 | 1894 | nanoid@3.3.8: {} 1895 | 1896 | negotiator@1.0.0: {} 1897 | 1898 | normalize-path@3.0.0: {} 1899 | 1900 | object-assign@4.1.1: {} 1901 | 1902 | object-inspect@1.13.4: {} 1903 | 1904 | on-finished@2.4.1: 1905 | dependencies: 1906 | ee-first: 1.1.1 1907 | 1908 | once@1.4.0: 1909 | dependencies: 1910 | wrappy: 1.0.2 1911 | 1912 | onchange@7.1.0: 1913 | dependencies: 1914 | '@blakeembrey/deque': 1.0.5 1915 | '@blakeembrey/template': 1.2.0 1916 | arg: 4.1.3 1917 | chokidar: 3.6.0 1918 | cross-spawn: 7.0.6 1919 | ignore: 5.3.2 1920 | tree-kill: 1.2.2 1921 | 1922 | package-json-from-dist@1.0.1: {} 1923 | 1924 | parseurl@1.3.3: {} 1925 | 1926 | path-key@3.1.1: {} 1927 | 1928 | path-scurry@1.11.1: 1929 | dependencies: 1930 | lru-cache: 10.4.3 1931 | minipass: 7.1.2 1932 | 1933 | path-to-regexp@8.2.0: {} 1934 | 1935 | pathe@2.0.3: {} 1936 | 1937 | pathval@2.0.0: {} 1938 | 1939 | picocolors@1.1.1: {} 1940 | 1941 | picomatch@2.3.1: {} 1942 | 1943 | pkce-challenge@4.1.0: {} 1944 | 1945 | postcss@8.5.3: 1946 | dependencies: 1947 | nanoid: 3.3.8 1948 | picocolors: 1.1.1 1949 | source-map-js: 1.2.1 1950 | 1951 | proxy-addr@2.0.7: 1952 | dependencies: 1953 | forwarded: 0.2.0 1954 | ipaddr.js: 1.9.1 1955 | 1956 | qs@6.13.0: 1957 | dependencies: 1958 | side-channel: 1.1.0 1959 | 1960 | qs@6.14.0: 1961 | dependencies: 1962 | side-channel: 1.1.0 1963 | 1964 | range-parser@1.2.1: {} 1965 | 1966 | raw-body@3.0.0: 1967 | dependencies: 1968 | bytes: 3.1.2 1969 | http-errors: 2.0.0 1970 | iconv-lite: 0.6.3 1971 | unpipe: 1.0.0 1972 | 1973 | readdirp@3.6.0: 1974 | dependencies: 1975 | picomatch: 2.3.1 1976 | 1977 | rollup@4.34.9: 1978 | dependencies: 1979 | '@types/estree': 1.0.6 1980 | optionalDependencies: 1981 | '@rollup/rollup-android-arm-eabi': 4.34.9 1982 | '@rollup/rollup-android-arm64': 4.34.9 1983 | '@rollup/rollup-darwin-arm64': 4.34.9 1984 | '@rollup/rollup-darwin-x64': 4.34.9 1985 | '@rollup/rollup-freebsd-arm64': 4.34.9 1986 | '@rollup/rollup-freebsd-x64': 4.34.9 1987 | '@rollup/rollup-linux-arm-gnueabihf': 4.34.9 1988 | '@rollup/rollup-linux-arm-musleabihf': 4.34.9 1989 | '@rollup/rollup-linux-arm64-gnu': 4.34.9 1990 | '@rollup/rollup-linux-arm64-musl': 4.34.9 1991 | '@rollup/rollup-linux-loongarch64-gnu': 4.34.9 1992 | '@rollup/rollup-linux-powerpc64le-gnu': 4.34.9 1993 | '@rollup/rollup-linux-riscv64-gnu': 4.34.9 1994 | '@rollup/rollup-linux-s390x-gnu': 4.34.9 1995 | '@rollup/rollup-linux-x64-gnu': 4.34.9 1996 | '@rollup/rollup-linux-x64-musl': 4.34.9 1997 | '@rollup/rollup-win32-arm64-msvc': 4.34.9 1998 | '@rollup/rollup-win32-ia32-msvc': 4.34.9 1999 | '@rollup/rollup-win32-x64-msvc': 4.34.9 2000 | fsevents: 2.3.3 2001 | 2002 | router@2.1.0: 2003 | dependencies: 2004 | is-promise: 4.0.0 2005 | parseurl: 1.3.3 2006 | path-to-regexp: 8.2.0 2007 | 2008 | safe-buffer@5.2.1: {} 2009 | 2010 | safer-buffer@2.1.2: {} 2011 | 2012 | semver@7.7.1: {} 2013 | 2014 | send@1.1.0: 2015 | dependencies: 2016 | debug: 4.3.6 2017 | destroy: 1.2.0 2018 | encodeurl: 2.0.0 2019 | escape-html: 1.0.3 2020 | etag: 1.8.1 2021 | fresh: 0.5.2 2022 | http-errors: 2.0.0 2023 | mime-types: 2.1.35 2024 | ms: 2.1.3 2025 | on-finished: 2.4.1 2026 | range-parser: 1.2.1 2027 | statuses: 2.0.1 2028 | transitivePeerDependencies: 2029 | - supports-color 2030 | 2031 | serve-static@2.1.0: 2032 | dependencies: 2033 | encodeurl: 2.0.0 2034 | escape-html: 1.0.3 2035 | parseurl: 1.3.3 2036 | send: 1.1.0 2037 | transitivePeerDependencies: 2038 | - supports-color 2039 | 2040 | setprototypeof@1.2.0: {} 2041 | 2042 | shebang-command@2.0.0: 2043 | dependencies: 2044 | shebang-regex: 3.0.0 2045 | 2046 | shebang-regex@3.0.0: {} 2047 | 2048 | side-channel-list@1.0.0: 2049 | dependencies: 2050 | es-errors: 1.3.0 2051 | object-inspect: 1.13.4 2052 | 2053 | side-channel-map@1.0.1: 2054 | dependencies: 2055 | call-bound: 1.0.3 2056 | es-errors: 1.3.0 2057 | get-intrinsic: 1.3.0 2058 | object-inspect: 1.13.4 2059 | 2060 | side-channel-weakmap@1.0.2: 2061 | dependencies: 2062 | call-bound: 1.0.3 2063 | es-errors: 1.3.0 2064 | get-intrinsic: 1.3.0 2065 | object-inspect: 1.13.4 2066 | side-channel-map: 1.0.1 2067 | 2068 | side-channel@1.1.0: 2069 | dependencies: 2070 | es-errors: 1.3.0 2071 | object-inspect: 1.13.4 2072 | side-channel-list: 1.0.0 2073 | side-channel-map: 1.0.1 2074 | side-channel-weakmap: 1.0.2 2075 | 2076 | siginfo@2.0.0: {} 2077 | 2078 | signal-exit@4.1.0: {} 2079 | 2080 | source-map-js@1.2.1: {} 2081 | 2082 | stackback@0.0.2: {} 2083 | 2084 | statuses@2.0.1: {} 2085 | 2086 | std-env@3.8.1: {} 2087 | 2088 | string-width@4.2.3: 2089 | dependencies: 2090 | emoji-regex: 8.0.0 2091 | is-fullwidth-code-point: 3.0.0 2092 | strip-ansi: 6.0.1 2093 | 2094 | string-width@5.1.2: 2095 | dependencies: 2096 | eastasianwidth: 0.2.0 2097 | emoji-regex: 9.2.2 2098 | strip-ansi: 7.1.0 2099 | 2100 | strip-ansi@6.0.1: 2101 | dependencies: 2102 | ansi-regex: 5.0.1 2103 | 2104 | strip-ansi@7.1.0: 2105 | dependencies: 2106 | ansi-regex: 6.1.0 2107 | 2108 | supports-color@7.2.0: 2109 | dependencies: 2110 | has-flag: 4.0.0 2111 | 2112 | test-exclude@7.0.1: 2113 | dependencies: 2114 | '@istanbuljs/schema': 0.1.3 2115 | glob: 10.4.5 2116 | minimatch: 9.0.5 2117 | 2118 | tinybench@2.9.0: {} 2119 | 2120 | tinyexec@0.3.2: {} 2121 | 2122 | tinypool@1.0.2: {} 2123 | 2124 | tinyrainbow@2.0.0: {} 2125 | 2126 | tinyspy@3.0.2: {} 2127 | 2128 | to-regex-range@5.0.1: 2129 | dependencies: 2130 | is-number: 7.0.0 2131 | 2132 | toidentifier@1.0.1: {} 2133 | 2134 | tree-kill@1.2.2: {} 2135 | 2136 | tsconfck@3.1.5(typescript@5.8.2): 2137 | optionalDependencies: 2138 | typescript: 5.8.2 2139 | 2140 | type-is@2.0.0: 2141 | dependencies: 2142 | content-type: 1.0.5 2143 | media-typer: 1.1.0 2144 | mime-types: 3.0.0 2145 | 2146 | typescript@5.8.2: {} 2147 | 2148 | undici-types@6.20.0: {} 2149 | 2150 | unpipe@1.0.0: {} 2151 | 2152 | utils-merge@1.0.1: {} 2153 | 2154 | vary@1.1.2: {} 2155 | 2156 | vite-node@3.0.7(@types/node@22.13.8): 2157 | dependencies: 2158 | cac: 6.7.14 2159 | debug: 4.4.0 2160 | es-module-lexer: 1.6.0 2161 | pathe: 2.0.3 2162 | vite: 6.2.0(@types/node@22.13.8) 2163 | transitivePeerDependencies: 2164 | - '@types/node' 2165 | - jiti 2166 | - less 2167 | - lightningcss 2168 | - sass 2169 | - sass-embedded 2170 | - stylus 2171 | - sugarss 2172 | - supports-color 2173 | - terser 2174 | - tsx 2175 | - yaml 2176 | 2177 | vite-tsconfig-paths@5.1.4(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.8)): 2178 | dependencies: 2179 | debug: 4.4.0 2180 | globrex: 0.1.2 2181 | tsconfck: 3.1.5(typescript@5.8.2) 2182 | optionalDependencies: 2183 | vite: 6.2.0(@types/node@22.13.8) 2184 | transitivePeerDependencies: 2185 | - supports-color 2186 | - typescript 2187 | 2188 | vite@6.2.0(@types/node@22.13.8): 2189 | dependencies: 2190 | esbuild: 0.25.0 2191 | postcss: 8.5.3 2192 | rollup: 4.34.9 2193 | optionalDependencies: 2194 | '@types/node': 22.13.8 2195 | fsevents: 2.3.3 2196 | 2197 | vitest@3.0.7(@types/node@22.13.8): 2198 | dependencies: 2199 | '@vitest/expect': 3.0.7 2200 | '@vitest/mocker': 3.0.7(vite@6.2.0(@types/node@22.13.8)) 2201 | '@vitest/pretty-format': 3.0.7 2202 | '@vitest/runner': 3.0.7 2203 | '@vitest/snapshot': 3.0.7 2204 | '@vitest/spy': 3.0.7 2205 | '@vitest/utils': 3.0.7 2206 | chai: 5.2.0 2207 | debug: 4.4.0 2208 | expect-type: 1.2.0 2209 | magic-string: 0.30.17 2210 | pathe: 2.0.3 2211 | std-env: 3.8.1 2212 | tinybench: 2.9.0 2213 | tinyexec: 0.3.2 2214 | tinypool: 1.0.2 2215 | tinyrainbow: 2.0.0 2216 | vite: 6.2.0(@types/node@22.13.8) 2217 | vite-node: 3.0.7(@types/node@22.13.8) 2218 | why-is-node-running: 2.3.0 2219 | optionalDependencies: 2220 | '@types/node': 22.13.8 2221 | transitivePeerDependencies: 2222 | - jiti 2223 | - less 2224 | - lightningcss 2225 | - msw 2226 | - sass 2227 | - sass-embedded 2228 | - stylus 2229 | - sugarss 2230 | - supports-color 2231 | - terser 2232 | - tsx 2233 | - yaml 2234 | 2235 | which@2.0.2: 2236 | dependencies: 2237 | isexe: 2.0.0 2238 | 2239 | why-is-node-running@2.3.0: 2240 | dependencies: 2241 | siginfo: 2.0.0 2242 | stackback: 0.0.2 2243 | 2244 | wrap-ansi@7.0.0: 2245 | dependencies: 2246 | ansi-styles: 4.3.0 2247 | string-width: 4.2.3 2248 | strip-ansi: 6.0.1 2249 | 2250 | wrap-ansi@8.1.0: 2251 | dependencies: 2252 | ansi-styles: 6.2.1 2253 | string-width: 5.1.2 2254 | strip-ansi: 7.1.0 2255 | 2256 | wrappy@1.0.2: {} 2257 | 2258 | zod-to-json-schema@3.24.3(zod@3.24.2): 2259 | dependencies: 2260 | zod: 3.24.2 2261 | 2262 | zod@3.24.2: {} 2263 | -------------------------------------------------------------------------------- /src/blog.ts: -------------------------------------------------------------------------------- 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 3 | import { z } from "zod"; 4 | 5 | // Create an MCP server instance 6 | const server = new McpServer({ 7 | name: "blog-tool", 8 | version: "1.0.0", 9 | }); 10 | 11 | // Register a frontmatter generation tool 12 | server.tool( 13 | "get_frontmatter", 14 | "Generate frontmatter for a blog post", 15 | { 16 | content: z.string().describe("The content of the blog post"), 17 | title: z.string().optional().describe("The title of the blog post"), 18 | author: z.string().optional().describe("The author of the blog post"), 19 | tags: z.array(z.string()).optional().describe("Tags for the blog post"), 20 | }, 21 | async ({ content, title, author, tags }) => { 22 | // Extract a title from the content if not provided 23 | const extractedTitle = title || content.split('\n')[0].replace(/^#\s*/, ''); 24 | 25 | // Generate frontmatter 26 | const frontmatter = [ 27 | '---', 28 | `title: "${extractedTitle}"`, 29 | author ? `author: "${author}"` : 'author: "Anonymous"', 30 | `date: "${new Date().toISOString().split('T')[0]}"`, 31 | tags && tags.length > 0 ? `tags: [${tags.map(tag => `"${tag}"`).join(', ')}]` : 'tags: []', 32 | '---', 33 | ].join('\n'); 34 | 35 | return { 36 | content: [ 37 | { 38 | type: "text", 39 | text: `${frontmatter}\n\n${content}`, 40 | }, 41 | ], 42 | }; 43 | } 44 | ); 45 | 46 | // Register a blog post creation tool 47 | server.tool( 48 | "create_blog_post", 49 | "Create a new blog post file with proper frontmatter", 50 | { 51 | cwd: z.string().min(1).describe("The root directory of the blog project"), 52 | title: z.string().min(1).describe("The title of the blog post"), 53 | content: z.string().describe("The content of the blog post"), 54 | author: z.string().optional().describe("The author of the blog post"), 55 | tags: z.array(z.string()).optional().describe("Tags for the blog post"), 56 | postsDirectory: z.string().default("posts").describe("Directory where posts are stored"), 57 | }, 58 | async ({ cwd, title, content, author, tags, postsDirectory }) => { 59 | // In a real implementation, this would create a file 60 | // For this example, we'll just return the content that would be written 61 | 62 | // Generate slug from title 63 | const slug = title 64 | .toLowerCase() 65 | .replace(/[^\w\s]/g, '') 66 | .replace(/\s+/g, '-'); 67 | 68 | const date = new Date().toISOString().split('T')[0]; 69 | const filename = `${date}-${slug}.md`; 70 | const filepath = `${postsDirectory}/${filename}`; 71 | 72 | // Generate frontmatter 73 | const frontmatter = [ 74 | '---', 75 | `title: "${title}"`, 76 | author ? `author: "${author}"` : 'author: "Anonymous"', 77 | `date: "${date}"`, 78 | tags && tags.length > 0 ? `tags: [${tags.map(tag => `"${tag}"`).join(', ')}]` : 'tags: []', 79 | '---', 80 | ].join('\n'); 81 | 82 | const fileContent = `${frontmatter}\n\n${content}`; 83 | 84 | return { 85 | content: [ 86 | { 87 | type: "text", 88 | text: `Blog post would be created at: ${filepath}\n\nContent:\n${fileContent}`, 89 | }, 90 | ], 91 | }; 92 | } 93 | ); 94 | 95 | // Start the server using stdio transport 96 | async function main() { 97 | const transport = new StdioServerTransport(); 98 | await server.connect(transport); 99 | console.error("Blog MCP Tool running on stdio"); 100 | } 101 | 102 | main().catch((error) => { 103 | console.error("Fatal error:", error); 104 | process.exit(1); 105 | }); -------------------------------------------------------------------------------- /src/hello.ts: -------------------------------------------------------------------------------- 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 3 | import { z } from "zod"; 4 | 5 | // Create an MCP server instance 6 | const server = new McpServer({ 7 | name: "mcp-tools", 8 | version: "1.0.0", 9 | }); 10 | 11 | // Register a simple Hello tool 12 | server.tool( 13 | "Hello", 14 | "Get a greeting with your name", 15 | { 16 | name: z.string().describe("Your name"), 17 | }, 18 | async ({ name }) => { 19 | return { 20 | content: [ 21 | { 22 | type: "text", 23 | text: `Hello, ${name}! Welcome to MCP Tools.`, 24 | }, 25 | ], 26 | }; 27 | } 28 | ); 29 | 30 | // Start the server using stdio transport 31 | async function main() { 32 | const transport = new StdioServerTransport(); 33 | await server.connect(transport); 34 | console.error("MCP Server running on stdio"); 35 | } 36 | 37 | main().catch((error) => { 38 | console.error("Fatal error:", error); 39 | process.exit(1); 40 | }); 41 | -------------------------------------------------------------------------------- /src/weather.ts: -------------------------------------------------------------------------------- 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 3 | import { z } from "zod"; 4 | 5 | // Create an MCP server instance 6 | const server = new McpServer({ 7 | name: "weather-tool", 8 | version: "1.0.0", 9 | }); 10 | 11 | // Register a weather alerts tool 12 | server.tool( 13 | "get_alerts", 14 | "Get weather alerts for a state", 15 | { 16 | state: z.string().min(2).max(2).describe("Two-letter state code (e.g. CA, NY)"), 17 | }, 18 | async ({ state }) => { 19 | // In a real implementation, this would call a weather API 20 | // For this example, we'll just return mock data 21 | const mockAlerts = { 22 | "CA": ["Wildfire warning in Northern California", "Heat advisory in Southern California"], 23 | "NY": ["Flood warning in Western New York", "Thunderstorm watch in NYC metro area"], 24 | "FL": ["Hurricane watch along the coast", "Flood warning in South Florida"], 25 | }; 26 | 27 | const alerts = (mockAlerts as Record)[state] || ["No current alerts for this state"]; 28 | 29 | return { 30 | content: [ 31 | { 32 | type: "text", 33 | text: `Weather Alerts for ${state}:\n${alerts.map(alert => `- ${alert}`).join('\n')}`, 34 | }, 35 | ], 36 | }; 37 | } 38 | ); 39 | 40 | // Start the server using stdio transport 41 | async function main() { 42 | const transport = new StdioServerTransport(); 43 | await server.connect(transport); 44 | console.error("Weather MCP Tool running on stdio"); 45 | } 46 | 47 | main().catch((error) => { 48 | console.error("Fatal error:", error); 49 | process.exit(1); 50 | }); -------------------------------------------------------------------------------- /test/mcp-client.ts: -------------------------------------------------------------------------------- 1 | import { Client } from '@modelcontextprotocol/sdk/client/index.js'; 2 | import { spawn } from 'node:child_process'; 3 | import { afterAll, beforeAll } from 'vitest'; 4 | import { join } from 'node:path'; 5 | import { Readable, Writable } from 'node:stream'; 6 | 7 | /** 8 | * A custom transport for testing MCP servers 9 | */ 10 | class TestClientTransport { 11 | private process: ReturnType | null = null; 12 | private serverPath: string; 13 | private messageHandlers: Map void> = new Map(); 14 | private nextId = 1; 15 | 16 | constructor(serverPath: string) { 17 | this.serverPath = serverPath; 18 | } 19 | 20 | async connect(): Promise { 21 | this.process = spawn('node', [this.serverPath], { 22 | stdio: ['pipe', 'pipe', 'pipe'], 23 | }); 24 | 25 | if (!this.process.stdin || !this.process.stdout) { 26 | throw new Error('Failed to create process streams'); 27 | } 28 | 29 | // Set up error handling 30 | this.process.on('error', (err) => { 31 | console.error('Server process error:', err); 32 | }); 33 | 34 | this.process.stderr?.on('data', (data) => { 35 | console.error(`Server stderr: ${data.toString()}`); 36 | }); 37 | 38 | // Set up message handling 39 | this.process.stdout.on('data', (data) => { 40 | try { 41 | const message = JSON.parse(data.toString()); 42 | const handler = this.messageHandlers.get(message.id); 43 | if (handler) { 44 | handler(message); 45 | this.messageHandlers.delete(message.id); 46 | } 47 | } catch (error) { 48 | console.error('Error parsing message:', error); 49 | } 50 | }); 51 | 52 | // Wait a bit for the server to start 53 | await new Promise((resolve) => setTimeout(resolve, 500)); 54 | } 55 | 56 | async disconnect(): Promise { 57 | if (this.process) { 58 | this.process.kill(); 59 | this.process = null; 60 | } 61 | } 62 | 63 | async sendRequest(method: string, params: any): Promise { 64 | if (!this.process?.stdin) { 65 | throw new Error('Transport not connected'); 66 | } 67 | 68 | const id = String(this.nextId++); 69 | const request = { 70 | jsonrpc: '2.0', 71 | id, 72 | method, 73 | params, 74 | }; 75 | 76 | return new Promise((resolve, reject) => { 77 | this.messageHandlers.set(id, (response) => { 78 | if (response.error) { 79 | reject(new Error(response.error.message)); 80 | } else { 81 | resolve(response.result); 82 | } 83 | }); 84 | 85 | this.process!.stdin!.write(JSON.stringify(request) + '\n'); 86 | }); 87 | } 88 | } 89 | 90 | /** 91 | * Creates an MCP client for testing that connects to a server via child process 92 | * 93 | * @param serverPath Path to the server script 94 | * @returns A setup object with the client and cleanup function 95 | */ 96 | export async function createMcpClient(serverPath: string) { 97 | const absoluteServerPath = join(process.cwd(), serverPath); 98 | const transport = new TestClientTransport(absoluteServerPath); 99 | 100 | await transport.connect(); 101 | 102 | // Create a simplified client that uses our custom transport 103 | const client = { 104 | async listTools() { 105 | return transport.sendRequest('tools/list', {}); 106 | }, 107 | 108 | async callTool({ name, arguments: args }: { name: string, arguments: Record }) { 109 | return transport.sendRequest('tools/call', { 110 | name, 111 | arguments: args 112 | }); 113 | }, 114 | 115 | async disconnect() { 116 | await transport.disconnect(); 117 | } 118 | }; 119 | 120 | const cleanup = async () => { 121 | await client.disconnect(); 122 | }; 123 | 124 | return { client, cleanup }; 125 | } 126 | 127 | /** 128 | * Test fixture for MCP server tests using our custom client 129 | */ 130 | export function setupMcpClientTest(serverPath: string) { 131 | let client: any; 132 | let cleanup: () => Promise; 133 | 134 | beforeAll(async () => { 135 | const setup = await createMcpClient(serverPath); 136 | client = setup.client; 137 | cleanup = setup.cleanup; 138 | }); 139 | 140 | afterAll(async () => { 141 | await cleanup(); 142 | }); 143 | 144 | return () => client; 145 | } -------------------------------------------------------------------------------- /test/mock-server.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { setupMcpClientTest } from './mcp-client'; 3 | 4 | describe('Mock MCP Server', () => { 5 | // Set up the client that connects to our mock server 6 | const getClient = setupMcpClientTest('test/mock-server.ts'); 7 | 8 | it('should list available tools', async () => { 9 | const client = getClient(); 10 | const tools = await client.listTools(); 11 | 12 | expect(tools).toBeDefined(); 13 | expect(tools.tools).toBeInstanceOf(Array); 14 | expect(tools.tools.length).toBeGreaterThanOrEqual(3); 15 | 16 | // Check if our tools are in the list 17 | const toolNames = tools.tools.map((tool: any) => tool.name); 18 | expect(toolNames).toContain('Hello'); 19 | expect(toolNames).toContain('get_forecast'); 20 | expect(toolNames).toContain('get_alerts'); 21 | }); 22 | 23 | it('should call the Hello tool', async () => { 24 | const client = getClient(); 25 | const result = await client.callTool({ 26 | name: 'Hello', 27 | arguments: { 28 | name: 'Test User', 29 | }, 30 | }); 31 | 32 | expect(result).toBeDefined(); 33 | expect(result.content).toBeInstanceOf(Array); 34 | expect(result.content.length).toBeGreaterThanOrEqual(1); 35 | expect(result.content[0].type).toBe('text'); 36 | expect(result.content[0].text).toBe('Hello, Test User!'); 37 | }); 38 | 39 | it('should call the get_forecast tool', async () => { 40 | const client = getClient(); 41 | const result = await client.callTool({ 42 | name: 'get_forecast', 43 | arguments: { 44 | latitude: 37.7749, 45 | longitude: -122.4194, 46 | }, 47 | }); 48 | 49 | expect(result).toBeDefined(); 50 | expect(result.content).toBeInstanceOf(Array); 51 | expect(result.content.length).toBeGreaterThanOrEqual(1); 52 | expect(result.content[0].type).toBe('text'); 53 | expect(result.content[0].text).toContain('Weather forecast for location'); 54 | expect(result.content[0].text).toContain('37.7749'); 55 | expect(result.content[0].text).toContain('-122.4194'); 56 | }); 57 | 58 | it('should call the get_alerts tool', async () => { 59 | const client = getClient(); 60 | const result = await client.callTool({ 61 | name: 'get_alerts', 62 | arguments: { 63 | state: 'CA', 64 | }, 65 | }); 66 | 67 | expect(result).toBeDefined(); 68 | expect(result.content).toBeInstanceOf(Array); 69 | expect(result.content.length).toBeGreaterThanOrEqual(1); 70 | expect(result.content[0].type).toBe('text'); 71 | expect(result.content[0].text).toContain('Weather alerts for CA'); 72 | expect(result.content[0].text).toContain('Heat advisory'); 73 | }); 74 | 75 | it('should handle errors for invalid tool arguments', async () => { 76 | const client = getClient(); 77 | 78 | await expect(client.callTool({ 79 | name: 'get_forecast', 80 | arguments: { 81 | latitude: 100, // Invalid latitude (> 90) 82 | longitude: -122.4194, 83 | }, 84 | })).rejects.toThrow(); 85 | }); 86 | }); -------------------------------------------------------------------------------- /test/mock-server.ts: -------------------------------------------------------------------------------- 1 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; 2 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 3 | import { z } from 'zod'; 4 | 5 | // Create a mock MCP server for testing 6 | const server = new McpServer({ 7 | name: 'mock-server', 8 | version: '1.0.0', 9 | }); 10 | 11 | // Register a simple "hello" tool 12 | server.tool( 13 | 'Hello', 14 | 'Get a greeting with your name', 15 | { 16 | name: z.string().describe('Your name'), 17 | }, 18 | async ({ name }) => { 19 | return { 20 | content: [ 21 | { 22 | type: 'text', 23 | text: `Hello, ${name}!`, 24 | }, 25 | ], 26 | }; 27 | } 28 | ); 29 | 30 | // Register a "get_forecast" tool 31 | server.tool( 32 | 'get_forecast', 33 | 'Get weather forecast for a location', 34 | { 35 | latitude: z.number().min(-90).max(90).describe('Latitude of the location'), 36 | longitude: z.number().min(-180).max(180).describe('Longitude of the location'), 37 | }, 38 | async ({ latitude, longitude }) => { 39 | // This is a mock implementation 40 | return { 41 | content: [ 42 | { 43 | type: 'text', 44 | text: `Weather forecast for location (${latitude}, ${longitude}): Sunny, 75°F`, 45 | }, 46 | ], 47 | }; 48 | } 49 | ); 50 | 51 | // Register a "get_alerts" tool 52 | server.tool( 53 | 'get_alerts', 54 | 'Get weather alerts for a state', 55 | { 56 | state: z.string().min(2).max(2).describe('Two-letter state code (e.g. CA, NY)'), 57 | }, 58 | async ({ state }) => { 59 | // This is a mock implementation 60 | const alerts = state === 'CA' 61 | ? ['Heat advisory in effect until 8 PM', 'Air quality alert until tomorrow morning'] 62 | : ['No alerts for this state']; 63 | 64 | return { 65 | content: [ 66 | { 67 | type: 'text', 68 | text: `Weather alerts for ${state}: ${alerts.join(', ')}`, 69 | }, 70 | ], 71 | }; 72 | } 73 | ); 74 | 75 | // Start the server using stdio transport 76 | async function main() { 77 | const transport = new StdioServerTransport(); 78 | await server.connect(transport); 79 | console.error('Mock MCP Server running on stdio'); 80 | } 81 | 82 | main().catch((error) => { 83 | console.error('Fatal error:', error); 84 | process.exit(1); 85 | }); -------------------------------------------------------------------------------- /test/server-tools.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; 2 | import { setupMcpClientTest } from './mcp-client.js'; 3 | 4 | describe('MCP Server Tools', () => { 5 | describe('Hello tool', () => { 6 | // Set up the client that connects to our server with the Hello tool 7 | const getClient = setupMcpClientTest('src/hello.ts'); 8 | 9 | it('should return a greeting with the provided name', async () => { 10 | const client = getClient(); 11 | 12 | const result = await client.callTool({ 13 | name: 'Hello', 14 | arguments: { 15 | name: 'Test User', 16 | }, 17 | }); 18 | 19 | // Verify the response content 20 | expect(result).toBeDefined(); 21 | expect(result.content).toBeInstanceOf(Array); 22 | expect(result.content.length).toBe(1); 23 | expect(result.content[0].type).toBe('text'); 24 | expect(result.content[0].text).toBe('Hello, Test User! Welcome to MCP Tools.'); 25 | }); 26 | }); 27 | 28 | describe('get_alerts tool', () => { 29 | // Set up the client that connects to our weather server 30 | const getClient = setupMcpClientTest('src/weather.ts'); 31 | 32 | it('should return alerts for a valid state', async () => { 33 | const client = getClient(); 34 | 35 | const result = await client.callTool({ 36 | name: 'get_alerts', 37 | arguments: { 38 | state: 'CA', 39 | }, 40 | }); 41 | 42 | // Verify the response content 43 | expect(result).toBeDefined(); 44 | expect(result.content).toBeInstanceOf(Array); 45 | expect(result.content.length).toBe(1); 46 | expect(result.content[0].type).toBe('text'); 47 | expect(result.content[0].text).toContain('Weather Alerts for CA:'); 48 | expect(result.content[0].text).toContain('Wildfire warning in Northern California'); 49 | expect(result.content[0].text).toContain('Heat advisory in Southern California'); 50 | }); 51 | 52 | it('should handle unknown states', async () => { 53 | const client = getClient(); 54 | 55 | const result = await client.callTool({ 56 | name: 'get_alerts', 57 | arguments: { 58 | state: 'XX', // Non-existent state 59 | }, 60 | }); 61 | 62 | expect(result).toBeDefined(); 63 | expect(result.content).toBeInstanceOf(Array); 64 | expect(result.content.length).toBe(1); 65 | expect(result.content[0].type).toBe('text'); 66 | expect(result.content[0].text).toContain('Weather Alerts for XX:'); 67 | expect(result.content[0].text).toContain('No current alerts for this state'); 68 | }); 69 | }); 70 | 71 | describe('get_frontmatter tool', () => { 72 | // Set up the client that connects to our blog server 73 | const getClient = setupMcpClientTest('src/blog.ts'); 74 | 75 | // Mock date for consistent testing 76 | beforeEach(() => { 77 | vi.useFakeTimers(); 78 | vi.setSystemTime(new Date('2023-01-01')); 79 | }); 80 | 81 | afterEach(() => { 82 | vi.useRealTimers(); 83 | }); 84 | 85 | it('should generate frontmatter with minimal parameters', async () => { 86 | const client = getClient(); 87 | 88 | const result = await client.callTool({ 89 | name: 'get_frontmatter', 90 | arguments: { 91 | content: '# Test Post\n\nThis is a test post.', 92 | }, 93 | }); 94 | 95 | expect(result).toBeDefined(); 96 | expect(result.content).toBeInstanceOf(Array); 97 | expect(result.content.length).toBe(1); 98 | expect(result.content[0].type).toBe('text'); 99 | expect(result.content[0].text).toContain('title: "Test Post"'); 100 | expect(result.content[0].text).toContain('author: "Anonymous"'); 101 | expect(result.content[0].text).toMatch(/date: "\d{4}-\d{2}-\d{2}"/); 102 | expect(result.content[0].text).toContain('tags: []'); 103 | expect(result.content[0].text).toContain('# Test Post'); 104 | }); 105 | 106 | it('should generate frontmatter with all parameters', async () => { 107 | const client = getClient(); 108 | 109 | const result = await client.callTool({ 110 | name: 'get_frontmatter', 111 | arguments: { 112 | content: '# Custom Title in Content\n\nThis is a test post.', 113 | title: 'Explicit Title', 114 | author: 'Test Author', 115 | tags: ['test', 'example'], 116 | }, 117 | }); 118 | 119 | expect(result).toBeDefined(); 120 | expect(result.content).toBeInstanceOf(Array); 121 | expect(result.content.length).toBe(1); 122 | expect(result.content[0].type).toBe('text'); 123 | expect(result.content[0].text).toContain('title: "Explicit Title"'); 124 | expect(result.content[0].text).toContain('author: "Test Author"'); 125 | expect(result.content[0].text).toMatch(/date: "\d{4}-\d{2}-\d{2}"/); 126 | expect(result.content[0].text).toContain('tags: ["test", "example"]'); 127 | expect(result.content[0].text).toContain('# Custom Title in Content'); 128 | }); 129 | }); 130 | }); -------------------------------------------------------------------------------- /test/server.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; 2 | import { setupMcpClientTest } from './mcp-client.js'; 3 | describe('MCP Tools Server', () => { 4 | // Set up the client that connects to our server 5 | const getClient = setupMcpClientTest('src/hello.ts'); 6 | 7 | it('should list available tools', async () => { 8 | const client = getClient(); 9 | const tools = await client.listTools(); 10 | 11 | expect(tools).toBeDefined(); 12 | expect(tools.tools).toBeInstanceOf(Array); 13 | 14 | // Check if our tools are in the list 15 | const toolNames = tools.tools.map((tool: any) => tool.name); 16 | expect(toolNames).toContain('Hello'); 17 | }); 18 | 19 | describe('Hello tool', () => { 20 | it('should validate required parameters', async () => { 21 | const client = getClient(); 22 | 23 | // Missing required parameters should throw 24 | await expect(client.callTool({ 25 | name: 'Hello', 26 | arguments: { 27 | // Missing name 28 | }, 29 | })).rejects.toThrow(); 30 | }); 31 | 32 | it('should return a greeting with the provided name', async () => { 33 | const client = getClient(); 34 | 35 | const result = await client.callTool({ 36 | name: 'Hello', 37 | arguments: { 38 | name: 'John', 39 | }, 40 | }); 41 | 42 | expect(result).toBeDefined(); 43 | expect(result.content).toBeInstanceOf(Array); 44 | expect(result.content.length).toBe(1); 45 | expect(result.content[0].type).toBe('text'); 46 | expect(result.content[0].text).toBe('Hello, John! Welcome to MCP Tools.'); 47 | }); 48 | }); 49 | }); 50 | 51 | describe('Weather MCP Tool Server', () => { 52 | // Set up the client that connects to our weather server 53 | const getClient = setupMcpClientTest('src/weather.ts'); 54 | 55 | it('should list available tools', async () => { 56 | const client = getClient(); 57 | const tools = await client.listTools(); 58 | 59 | expect(tools).toBeDefined(); 60 | expect(tools.tools).toBeInstanceOf(Array); 61 | 62 | // Check if our tools are in the list 63 | const toolNames = tools.tools.map((tool: any) => tool.name); 64 | expect(toolNames).toContain('get_alerts'); 65 | }); 66 | 67 | describe('get_alerts tool', () => { 68 | it('should validate required parameters', async () => { 69 | const client = getClient(); 70 | 71 | // Missing required parameters should throw 72 | await expect(client.callTool({ 73 | name: 'get_alerts', 74 | arguments: { 75 | // Missing state 76 | }, 77 | })).rejects.toThrow(); 78 | }); 79 | 80 | it('should return alerts for a valid state', async () => { 81 | const client = getClient(); 82 | 83 | const result = await client.callTool({ 84 | name: 'get_alerts', 85 | arguments: { 86 | state: 'CA', 87 | }, 88 | }); 89 | 90 | expect(result).toBeDefined(); 91 | expect(result.content).toBeInstanceOf(Array); 92 | expect(result.content.length).toBe(1); 93 | expect(result.content[0].type).toBe('text'); 94 | expect(result.content[0].text).toContain('Weather Alerts for CA:'); 95 | expect(result.content[0].text).toContain('Wildfire warning'); 96 | expect(result.content[0].text).toContain('Heat advisory'); 97 | }); 98 | 99 | it('should handle unknown states', async () => { 100 | const client = getClient(); 101 | 102 | const result = await client.callTool({ 103 | name: 'get_alerts', 104 | arguments: { 105 | state: 'XX', // Non-existent state 106 | }, 107 | }); 108 | 109 | expect(result).toBeDefined(); 110 | expect(result.content).toBeInstanceOf(Array); 111 | expect(result.content.length).toBe(1); 112 | expect(result.content[0].type).toBe('text'); 113 | expect(result.content[0].text).toContain('Weather Alerts for XX:'); 114 | expect(result.content[0].text).toContain('No current alerts for this state'); 115 | }); 116 | }); 117 | }); 118 | 119 | describe('Blog MCP Tool Server', () => { 120 | // Set up the client that connects to our blog server 121 | const getClient = setupMcpClientTest('src/blog.ts'); 122 | 123 | it('should list available tools', async () => { 124 | const client = getClient(); 125 | const tools = await client.listTools(); 126 | 127 | expect(tools).toBeDefined(); 128 | expect(tools.tools).toBeInstanceOf(Array); 129 | 130 | // Check if our tools are in the list 131 | const toolNames = tools.tools.map((tool: any) => tool.name); 132 | expect(toolNames).toContain('get_frontmatter'); 133 | expect(toolNames).toContain('create_blog_post'); 134 | }); 135 | 136 | describe('get_frontmatter tool', () => { 137 | // Mock date for consistent testing 138 | beforeEach(() => { 139 | vi.useFakeTimers(); 140 | vi.setSystemTime(new Date('2023-01-01')); 141 | }); 142 | 143 | afterEach(() => { 144 | vi.useRealTimers(); 145 | }); 146 | 147 | it('should validate required parameters', async () => { 148 | const client = getClient(); 149 | 150 | // Missing required parameters should throw 151 | await expect(client.callTool({ 152 | name: 'get_frontmatter', 153 | arguments: { 154 | // Missing content 155 | }, 156 | })).rejects.toThrow(); 157 | }); 158 | 159 | it('should generate frontmatter with minimal parameters', async () => { 160 | const client = getClient(); 161 | 162 | const result = await client.callTool({ 163 | name: 'get_frontmatter', 164 | arguments: { 165 | content: '# Test Post\n\nThis is a test post.', 166 | }, 167 | }); 168 | 169 | expect(result).toBeDefined(); 170 | expect(result.content).toBeInstanceOf(Array); 171 | expect(result.content.length).toBe(1); 172 | expect(result.content[0].type).toBe('text'); 173 | expect(result.content[0].text).toContain('title: "Test Post"'); 174 | expect(result.content[0].text).toContain('author: "Anonymous"'); 175 | // Check for date in ISO format (YYYY-MM-DD) instead of specific date 176 | expect(result.content[0].text).toMatch(/date: "\d{4}-\d{2}-\d{2}"/); 177 | expect(result.content[0].text).toContain('tags: []'); 178 | expect(result.content[0].text).toContain('# Test Post'); 179 | }); 180 | 181 | it('should generate frontmatter with all parameters', async () => { 182 | const client = getClient(); 183 | 184 | const result = await client.callTool({ 185 | name: 'get_frontmatter', 186 | arguments: { 187 | content: '# Custom Title in Content\n\nThis is a test post.', 188 | title: 'Explicit Title', 189 | author: 'Test Author', 190 | tags: ['test', 'example'], 191 | }, 192 | }); 193 | 194 | expect(result).toBeDefined(); 195 | expect(result.content).toBeInstanceOf(Array); 196 | expect(result.content.length).toBe(1); 197 | expect(result.content[0].type).toBe('text'); 198 | expect(result.content[0].text).toContain('title: "Explicit Title"'); 199 | expect(result.content[0].text).toContain('author: "Test Author"'); 200 | // Check for date in ISO format (YYYY-MM-DD) instead of specific date 201 | expect(result.content[0].text).toMatch(/date: "\d{4}-\d{2}-\d{2}"/); 202 | expect(result.content[0].text).toContain('tags: ["test", "example"]'); 203 | expect(result.content[0].text).toContain('# Custom Title in Content'); 204 | }); 205 | }); 206 | }); -------------------------------------------------------------------------------- /test/utils.ts: -------------------------------------------------------------------------------- 1 | import { Client } from '@modelcontextprotocol/sdk/client/index.js'; 2 | import { ChildProcess, spawn } from 'node:child_process'; 3 | import { join } from 'node:path'; 4 | import { afterAll, beforeAll } from 'vitest'; 5 | import { Readable, Writable } from 'node:stream'; 6 | 7 | /** 8 | * A custom transport for testing MCP servers that uses direct stream connections 9 | * instead of spawning a child process. 10 | */ 11 | export class TestTransport { 12 | private stdin: Writable; 13 | private stdout: Readable; 14 | private process: ChildProcess | null = null; 15 | private serverPath: string; 16 | 17 | constructor(serverPath: string) { 18 | this.serverPath = serverPath; 19 | } 20 | 21 | async connect(): Promise { 22 | // Spawn the server process 23 | this.process = spawn('node', [this.serverPath], { 24 | stdio: ['pipe', 'pipe', 'pipe'], 25 | }); 26 | 27 | if (!this.process.stdin || !this.process.stdout) { 28 | throw new Error('Failed to create process streams'); 29 | } 30 | 31 | this.stdin = this.process.stdin; 32 | this.stdout = this.process.stdout; 33 | 34 | // Set up error handling 35 | this.process.on('error', (err) => { 36 | console.error('Server process error:', err); 37 | }); 38 | 39 | this.process.stderr?.on('data', (data) => { 40 | console.error(`Server stderr: ${data.toString()}`); 41 | }); 42 | 43 | // Wait a bit for the server to start 44 | await new Promise((resolve) => setTimeout(resolve, 500)); 45 | } 46 | 47 | async disconnect(): Promise { 48 | if (this.process) { 49 | this.process.kill(); 50 | this.process = null; 51 | } 52 | } 53 | 54 | async sendMessage(message: any): Promise { 55 | const messageStr = JSON.stringify(message) + '\n'; 56 | return new Promise((resolve, reject) => { 57 | this.stdin.write(messageStr, (err) => { 58 | if (err) reject(err); 59 | else resolve(); 60 | }); 61 | }); 62 | } 63 | 64 | async receiveMessage(): Promise { 65 | return new Promise((resolve) => { 66 | const onData = (data: Buffer) => { 67 | const message = JSON.parse(data.toString()); 68 | this.stdout.removeListener('data', onData); 69 | resolve(message); 70 | }; 71 | 72 | this.stdout.on('data', onData); 73 | }); 74 | } 75 | } 76 | 77 | /** 78 | * Creates a test client for an MCP server. 79 | * 80 | * @param serverPath Path to the server script 81 | * @returns A setup object with the client and cleanup function 82 | */ 83 | export async function createTestClient(serverPath: string) { 84 | const absoluteServerPath = join(process.cwd(), serverPath); 85 | const transport = new TestTransport(absoluteServerPath); 86 | 87 | await transport.connect(); 88 | 89 | const client = { 90 | async callTool(name: string, args: Record) { 91 | await transport.sendMessage({ 92 | jsonrpc: '2.0', 93 | id: '1', 94 | method: 'tools/call', 95 | params: { 96 | name, 97 | arguments: args 98 | } 99 | }); 100 | 101 | return await transport.receiveMessage(); 102 | }, 103 | 104 | async listTools() { 105 | await transport.sendMessage({ 106 | jsonrpc: '2.0', 107 | id: '1', 108 | method: 'tools/list', 109 | params: {} 110 | }); 111 | 112 | return await transport.receiveMessage(); 113 | } 114 | }; 115 | 116 | const cleanup = async () => { 117 | await transport.disconnect(); 118 | }; 119 | 120 | return { client, cleanup }; 121 | } 122 | 123 | /** 124 | * Test fixture for MCP server tests 125 | */ 126 | export function setupMcpServerTest(serverPath: string) { 127 | let client: any; 128 | let cleanup: () => Promise; 129 | 130 | beforeAll(async () => { 131 | const setup = await createTestClient(serverPath); 132 | client = setup.client; 133 | cleanup = setup.cleanup; 134 | }); 135 | 136 | afterAll(async () => { 137 | await cleanup(); 138 | }); 139 | 140 | return () => client; 141 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "esModuleInterop": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "outDir": "dist", 11 | "declaration": true, 12 | "sourceMap": true 13 | }, 14 | "include": ["src/**/*", "test/**/*"], 15 | "exclude": ["node_modules", "dist"] 16 | } 17 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | import tsconfigPaths from 'vite-tsconfig-paths'; 3 | 4 | export default defineConfig({ 5 | plugins: [tsconfigPaths()], 6 | test: { 7 | environment: 'node', 8 | include: ['test/**/*.test.ts'], 9 | coverage: { 10 | provider: 'v8', 11 | reporter: ['text', 'json', 'html'], 12 | }, 13 | }, 14 | }); --------------------------------------------------------------------------------