diff --git a/README.md b/README.md index 72feeef..790990b 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,18 @@ # LeetCode MCP Server [![NPM Version](https://img.shields.io/npm/v/@jinzcdev/leetcode-mcp-server.svg)](https://www.npmjs.com/package/@jinzcdev/leetcode-mcp-server) +[![Chinese Doc](https://img.shields.io/badge/docs-中文-blue)](README_zh-CN.md) [![NPM Downloads](https://img.shields.io/npm/dm/@jinzcdev/leetcode-mcp-server.svg)](https://www.npmjs.com/package/@jinzcdev/leetcode-mcp-server) [![GitHub License](https://img.shields.io/github/license/jinzcdev/leetcode-mcp-server.svg)](./LICENSE) -[![Chinese Doc](https://img.shields.io/badge/简体中文-查看-orange)](README_zh-CN.md) +[![LeetCode MCP Server on Glama](https://glama.ai/mcp/servers/jinzcdev/leetcode-mcp-server/badges/score.svg)](https://glama.ai/mcp/servers/jinzcdev/leetcode-mcp-server) [![Stars](https://img.shields.io/github/stars/jinzcdev/leetcode-mcp-server)](https://github.com/jinzcdev/leetcode-mcp-server) The LeetCode MCP Server is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that provides seamless integration with LeetCode APIs, enabling advanced automation and intelligent interaction with LeetCode's programming problems, contests, solutions, and user data. - - LeetCode Server MCP server - - ## Features - 🌐 **Multi-site Support**: Support​ both leetcode.com (Global) and leetcode.cn (China) platforms +- 🔌 **Dual Transport Modes**: Run as a stdio process (default) or as a [Streamable HTTP](https://modelcontextprotocol.io/docs/concepts/transports) server for web-based integrations - 📊 **Problem Data Retrieval**: Obtain detailed problem descriptions, constraints, examples, official editorials, and ​user-submitted solutions - 👤 **User Data Access**: Retrieve user profiles, submission history, and contest performance - 🔒 **​Private Data Access**: Create and query user notes, track problem-solving progress, and analyze submission details (AC/WA analysis) @@ -28,25 +26,18 @@ The LeetCode MCP Server is a [Model Context Protocol (MCP)](https://modelcontext ## Installation -### Installing via Smithery - -To install leetcode-mcp-server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@jinzcdev/leetcode-mcp-server): - -```bash -npx -y @smithery/cli install @jinzcdev/leetcode-mcp-server --client claude -``` - -### Manual Installation - ```bash # Install from npm npm install @jinzcdev/leetcode-mcp-server -g -# Or run with Global site configuration +# Run with Global site configuration (stdio transport, default) npx -y @jinzcdev/leetcode-mcp-server --site global # Run with authentication (for accessing private data) npx -y @jinzcdev/leetcode-mcp-server --site global --session + +# Run as a Streamable HTTP server +npx -y @jinzcdev/leetcode-mcp-server --transport http --port 3000 --site global ``` Alternatively, you can clone the repository and run it locally: @@ -61,30 +52,48 @@ cd leetcode-mcp-server # Build the project npm install && npm run build -# Run the server +# Run the server (stdio transport) node build/index.js --site global + +# Or run as a Streamable HTTP server +node build/index.js --transport http --port 3000 --site global ``` ## Usage -### Visual Studio Code Integration +The server supports two transport modes: -Add the following JSON configuration to your User Settings (JSON) file. Access this by pressing `Ctrl/Cmd + Shift + P` and searching for `Preferences: Open User Settings (JSON)`. +| Transport | Description | +| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `stdio` (default) | Standard input/output transport for local MCP clients | +| `http` | [Streamable HTTP](https://modelcontextprotocol.io/docs/concepts/transports) transport for web-based integrations and remote access | + +### Command-Line Options + +| Option | Alias | Default | Description | +| ------------- | ----- | ----------- | ---------------------------------------------------------------- | +| `--site` | `-s` | `global` | LeetCode API site: `global` (leetcode.com) or `cn` (leetcode.cn) | +| `--session` | `-c` | — | LeetCode session cookie for authenticated requests | +| `--transport` | `-t` | `stdio` | Transport mode: `stdio` or `http` | +| `--port` | — | `3000` | HTTP server port (Streamable HTTP only) | +| `--host` | — | `127.0.0.1` | HTTP server host (Streamable HTTP only) | +| `--endpoint` | — | `/mcp` | HTTP endpoint path (Streamable HTTP only) | + +### MCP Client Configuration (stdio) + +Add the following server entry to your MCP client configuration file: #### Option 1: Using Environment Variables ```json { - "mcp": { - "servers": { - "leetcode": { - "type": "stdio", - "command": "npx", - "args": ["-y", "@jinzcdev/leetcode-mcp-server"], - "env": { - "LEETCODE_SITE": "global", - "LEETCODE_SESSION": "" - } + "mcpServers": { + "leetcode": { + "command": "npx", + "args": ["-y", "@jinzcdev/leetcode-mcp-server"], + "env": { + "LEETCODE_SITE": "global", + "LEETCODE_SESSION": "" } } } @@ -95,20 +104,17 @@ Add the following JSON configuration to your User Settings (JSON) file. Access t ```json { - "mcp": { - "servers": { - "leetcode": { - "type": "stdio", - "command": "npx", - "args": [ - "-y", - "@jinzcdev/leetcode-mcp-server", - "--site", - "global", - "--session", - "" - ] - } + "mcpServers": { + "leetcode": { + "command": "npx", + "args": [ + "-y", + "@jinzcdev/leetcode-mcp-server", + "--site", + "global", + "--session", + "" + ] } } } @@ -116,12 +122,51 @@ Add the following JSON configuration to your User Settings (JSON) file. Access t For LeetCode China site, modify the `--site` parameter to `cn`. +> [!NOTE] +> +> The exact configuration file location and JSON structure may vary by MCP client. Some clients use an `mcp.servers` wrapper or a `type` field — refer to your client's documentation and adapt the example accordingly. + +### MCP Client Configuration (Streamable HTTP) + +First, start the server in HTTP mode: + +```bash +npx -y @jinzcdev/leetcode-mcp-server --transport http --port 3000 --site global +``` + +Then connect your MCP client to the running server: + +```json +{ + "mcpServers": { + "leetcode": { + "type": "http", + "url": "http://127.0.0.1:3000/mcp" + } + } +} +``` + +To use authenticated endpoints, pass the session cookie when starting the server: + +```bash +npx -y @jinzcdev/leetcode-mcp-server --transport http --port 3000 --site global --session +``` + +> [!NOTE] + +> Some MCP clients use `"type": "http"` or `"type": "streamableHttp"` alongside the `url` field. Refer to your client's documentation for the exact HTTP transport configuration format. + > [!TIP] > > The server supports the following optional environment variables: > -> - `LEETCODE_SITE`: LeetCode API endpoint ('global' or 'cn', default: 'global') +> - `LEETCODE_SITE`: LeetCode API endpoint (`global` or `cn`, default: `global`) > - `LEETCODE_SESSION`: LeetCode session cookie for authenticated API access (default: empty) +> - `LEETCODE_TRANSPORT`: Transport mode (`stdio` or `http`, default: `stdio`) +> - `LEETCODE_HTTP_PORT`: HTTP server port when using Streamable HTTP (default: `3000`) +> - `LEETCODE_HTTP_HOST`: HTTP server host when using Streamable HTTP (default: `127.0.0.1`) +> - `LEETCODE_HTTP_ENDPOINT`: HTTP endpoint path when using Streamable HTTP (default: `/mcp`) > > **Priority Note**: > Command-line arguments take precedence over environment variables when both are specified. For example: diff --git a/README_zh-CN.md b/README_zh-CN.md index b940a90..afca84e 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -1,20 +1,18 @@ # LeetCode MCP 服务器 [![NPM Version](https://img.shields.io/npm/v/@jinzcdev/leetcode-mcp-server.svg)](https://www.npmjs.com/package/@jinzcdev/leetcode-mcp-server) +[![English Doc](https://img.shields.io/badge/docs-English-blue)](README.md) [![NPM Downloads](https://img.shields.io/npm/dm/@jinzcdev/leetcode-mcp-server.svg)](https://www.npmjs.com/package/@jinzcdev/leetcode-mcp-server) [![GitHub License](https://img.shields.io/github/license/jinzcdev/leetcode-mcp-server.svg)](./LICENSE) -[![English Doc](https://img.shields.io/badge/English-View-orange)](README.md) +[![LeetCode MCP Server on Glama](https://glama.ai/mcp/servers/jinzcdev/leetcode-mcp-server/badges/score.svg)](https://glama.ai/mcp/servers/jinzcdev/leetcode-mcp-server) [![Stars](https://img.shields.io/github/stars/jinzcdev/leetcode-mcp-server)](https://github.com/jinzcdev/leetcode-mcp-server) LeetCode MCP Server 是一个基于 [模型上下文协议 (MCP)](https://modelcontextprotocol.io/introduction) 的服务,提供与 LeetCode API 的无缝集成,实现与 LeetCode 编程题目、竞赛、题解和用户数据的高级自动化和智能交互。 - - LeetCode Server MCP server - - ## 特性 - 🌐 **多站点支持**:同时支持 leetcode.com(全球)和 leetcode.cn(中国)站点 +- 🔌 **双传输模式**:默认以 stdio 进程运行,或以 [Streamable HTTP](https://modelcontextprotocol.io/docs/concepts/transports) 服务器模式运行,适用于 Web 集成场景 - 📊 ​**题目数据获取**:获取详细的题目描述、约束条件、示例、官方题解和用户提交的解答 - 👤 **用户数据访问**:检索用户资料、提交历史和竞赛表现 - 🔒 **私有数据访问**:创建和查询用户笔记,跟踪题目解答进度,分析提交详情(AC/WA 报告) @@ -28,25 +26,18 @@ LeetCode MCP Server 是一个基于 [模型上下文协议 (MCP)](https://modelc ## 安装 -### 通过 Smithery 安装 - -使用 [Smithery](https://smithery.ai/server/@jinzcdev/leetcode-mcp-server) 为 Claude Desktop 自动安装 leetcode-mcp-server: - -```bash -npx -y @smithery/cli install @jinzcdev/leetcode-mcp-server --client claude -``` - -### 手动安装 - ```bash # 从 npm 安装 npm install @jinzcdev/leetcode-mcp-server -g -# 使用中国站点配置运行 +# 使用中国站点配置运行(stdio 传输,默认) npx -y @jinzcdev/leetcode-mcp-server --site cn # 使用认证运行(访问私有数据) npx -y @jinzcdev/leetcode-mcp-server --site cn --session <您的 LEETCODE 会话 COOKIE> + +# 以 Streamable HTTP 服务器模式运行 +npx -y @jinzcdev/leetcode-mcp-server --transport http --port 3000 --site cn ``` 或者,您可以克隆仓库并在本地运行: @@ -61,30 +52,48 @@ cd leetcode-mcp-server # 构建项目 npm install && npm run build -# 运行服务器 +# 运行服务器(stdio 传输) node build/index.js --site cn + +# 或以 Streamable HTTP 服务器模式运行 +node build/index.js --transport http --port 3000 --site cn ``` ## 使用方法 -### Visual Studio Code 集成 +服务器支持两种传输模式: -在您的用户设置 (JSON) 文件中添加以下配置。通过按 `Ctrl/Cmd + Shift + P` 并搜索 `Preferences: Open User Settings (JSON)` 来访问此文件。 +| 传输模式 | 说明 | +| --------------- | ----------------------------------------------------------------------------------------------------------- | +| `stdio`(默认) | 标准输入/输出传输,适用于本地 MCP 客户端 | +| `http` | [Streamable HTTP](https://modelcontextprotocol.io/docs/concepts/transports) 传输,适用于 Web 集成和远程访问 | + +### 命令行参数 + +| 参数 | 别名 | 默认值 | 说明 | +| ------------- | ---- | ----------- | ----------------------------------------------------------------- | +| `--site` | `-s` | `global` | LeetCode API 站点:`global`(leetcode.com)或 `cn`(leetcode.cn) | +| `--session` | `-c` | — | LeetCode 会话 cookie,用于授权访问 | +| `--transport` | `-t` | `stdio` | 传输模式:`stdio` 或 `http` | +| `--port` | — | `3000` | HTTP 服务端口(仅 Streamable HTTP) | +| `--host` | — | `127.0.0.1` | HTTP 服务主机(仅 Streamable HTTP) | +| `--endpoint` | — | `/mcp` | HTTP 端点路径(仅 Streamable HTTP) | + +### MCP 客户端配置(stdio) + +将以下服务器配置添加到 MCP 客户端的配置文件中: #### 方式一:使用环境变量 ```json { - "mcp": { - "servers": { - "leetcode": { - "type": "stdio", - "command": "npx", - "args": ["-y", "@jinzcdev/leetcode-mcp-server"], - "env": { - "LEETCODE_SITE": "cn", - "LEETCODE_SESSION": "<您的 LEETCODE 会话 COOKIE>" - } + "mcpServers": { + "leetcode": { + "command": "npx", + "args": ["-y", "@jinzcdev/leetcode-mcp-server"], + "env": { + "LEETCODE_SITE": "cn", + "LEETCODE_SESSION": "<您的 LEETCODE 会话 COOKIE>" } } } @@ -95,20 +104,17 @@ node build/index.js --site cn ```json { - "mcp": { - "servers": { - "leetcode": { - "type": "stdio", - "command": "npx", - "args": [ - "-y", - "@jinzcdev/leetcode-mcp-server", - "--site", - "cn", - "--session", - "<您的 LEETCODE 会话 COOKIE>" - ] - } + "mcpServers": { + "leetcode": { + "command": "npx", + "args": [ + "-y", + "@jinzcdev/leetcode-mcp-server", + "--site", + "cn", + "--session", + "<您的 LEETCODE 会话 COOKIE>" + ] } } } @@ -116,12 +122,51 @@ node build/index.js --site cn 对于 LeetCode 全球站点,请将 `--site` 参数修改为 `global`。 +> [!NOTE] +> +> 不同 MCP 客户端的配置文件位置和 JSON 结构可能有所不同。部分客户端使用 `mcp.servers` 包装层或 `type` 字段,请参考所使用客户端的文档并据此调整示例。 + +### MCP 客户端配置(Streamable HTTP) + +首先以 HTTP 模式启动服务器: + +```bash +npx -y @jinzcdev/leetcode-mcp-server --transport http --port 3000 --site cn +``` + +然后在 MCP 客户端中连接到该服务器: + +```json +{ + "mcpServers": { + "leetcode": { + "type": "http", + "url": "http://127.0.0.1:3000/mcp" + } + } +} +``` + +如需访问需要认证的接口,启动服务器时传入会话 cookie: + +```bash +npx -y @jinzcdev/leetcode-mcp-server --transport http --port 3000 --site cn --session <您的 LEETCODE 会话 COOKIE> +``` + +> [!NOTE] + +> 部分 MCP 客户端需要在 `url` 字段之外额外指定 `"type": "http"` 或 `"type": "streamableHttp"`,请参考所使用客户端的文档以确认 HTTP 传输的配置格式。 + > [!TIP] > > 服务支持以下可选的环境变量: > -> - `LEETCODE_SITE`:LeetCode API 端点('global' 或 'cn',默认为 'global') +> - `LEETCODE_SITE`:LeetCode API 端点(`global` 或 `cn`,默认为 `global`) > - `LEETCODE_SESSION`:用于授权 API 访问的 LeetCode 会话 cookie(默认为空) +> - `LEETCODE_TRANSPORT`:传输模式(`stdio` 或 `http`,默认为 `stdio`) +> - `LEETCODE_HTTP_PORT`:Streamable HTTP 服务端口(默认为 `3000`) +> - `LEETCODE_HTTP_HOST`:Streamable HTTP 服务主机(默认为 `127.0.0.1`) +> - `LEETCODE_HTTP_ENDPOINT`:Streamable HTTP 端点路径(默认为 `/mcp`) > > **优先级说明**: > diff --git a/package-lock.json b/package-lock.json index f41de6e..03ae50d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "1.3.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.8.0", - "leetcode-query": "^2.0.0", + "@modelcontextprotocol/sdk": "^1.29.0", + "leetcode-query": "^2.0.1", "minimist": "^1.2.8", "pino": "^9.6.0", "ts-node": "^10.9.2", @@ -40,7 +40,7 @@ "vitest": "^3.1.3" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "^4.40.1" + "@rollup/rollup-linux-x64-gnu": "^4.62.2" } }, "node_modules/@cspotcode/source-map-support": { @@ -621,19 +621,32 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@fetch-impl/cross-fetch": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@fetch-impl/cross-fetch/-/cross-fetch-1.0.0.tgz", @@ -650,6 +663,18 @@ "integrity": "sha512-UPUN9Yfjnk513Vc08iNW8/9L1nSwQMsTx6nOvmjPNfU2Rtbew/2KgAbQDPuoL6PrNgEmEmmyeM29BkcVBpt3gQ==", "license": "MIT" }, + "node_modules/@hono/node-server": { + "version": "1.19.14", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", + "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -726,9 +751,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -742,27 +767,67 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.0.tgz", - "integrity": "sha512-m//7RlINx1F3sz3KqwY1WWzVgTcYX52HYk4bJ1hkBXV3zccAEth+jRvG8DBRrdaQuRsPAJOx2MH3zaHNCKL7Zg==", + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", "dependencies": { - "ajv": "^6.12.6", + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -815,9 +880,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz", - "integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", "cpu": [ "arm" ], @@ -829,9 +894,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz", - "integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", "cpu": [ "arm64" ], @@ -843,9 +908,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz", - "integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", "cpu": [ "arm64" ], @@ -857,9 +922,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz", - "integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", "cpu": [ "x64" ], @@ -871,9 +936,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz", - "integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", "cpu": [ "arm64" ], @@ -885,9 +950,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz", - "integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", "cpu": [ "x64" ], @@ -899,13 +964,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz", - "integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -913,13 +981,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz", - "integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -927,13 +998,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz", - "integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -941,41 +1015,84 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz", - "integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz", - "integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz", - "integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -983,13 +1100,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz", - "integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -997,13 +1117,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz", - "integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1011,13 +1134,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz", - "integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1025,12 +1151,15 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", - "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1038,23 +1167,54 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", - "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz", - "integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", "cpu": [ "arm64" ], @@ -1066,9 +1226,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz", - "integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", "cpu": [ "ia32" ], @@ -1079,10 +1239,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz", - "integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", "cpu": [ "x64" ], @@ -1117,10 +1291,28 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -1334,9 +1526,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -1344,13 +1536,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -1402,14 +1594,15 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz", - "integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.4", - "@vitest/utils": "3.1.4", + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -1418,13 +1611,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz", - "integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.4", + "@vitest/spy": "3.2.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1433,7 +1626,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -1445,9 +1638,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz", - "integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", "dev": true, "license": "MIT", "dependencies": { @@ -1458,27 +1651,28 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz", - "integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.4", - "pathe": "^2.0.3" + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz", - "integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.4", + "@vitest/pretty-format": "3.2.7", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -1487,27 +1681,27 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz", - "integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^3.0.2" + "tinyspy": "^4.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz", - "integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.4", - "loupe": "^3.1.3", + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" }, "funding": { @@ -1562,9 +1756,10 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -1577,6 +1772,45 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/ansi-escapes": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", @@ -1662,29 +1896,46 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1764,9 +2015,9 @@ } }, "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", "dependencies": { @@ -1777,7 +2028,7 @@ "pathval": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/chalk": { @@ -1798,9 +2049,9 @@ } }, "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", "dev": true, "license": "MIT", "engines": { @@ -1885,15 +2136,16 @@ "license": "MIT" }, "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { @@ -1976,9 +2228,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2019,9 +2271,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -2119,9 +2371,9 @@ "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2489,18 +2741,19 @@ } }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -2531,10 +2784,13 @@ } }, "node_modules/express-rate-limit": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", - "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", "license": "MIT", + "dependencies": { + "ip-address": "^10.2.0" + }, "engines": { "node": ">= 16" }, @@ -2542,7 +2798,7 @@ "url": "https://github.com/sponsors/express-rate-limit" }, "peerDependencies": { - "express": "^4.11 || 5 || ^5.0.0-beta.1" + "express": ">= 4.11" } }, "node_modules/fast-copy": { @@ -2599,6 +2855,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -2624,6 +2881,22 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", + "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -2661,9 +2934,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -2674,7 +2947,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/find-up": { @@ -2709,9 +2986,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -2895,9 +3172,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2913,20 +3190,33 @@ "dev": true, "license": "MIT" }, + "node_modules/hono": { + "version": "4.12.29", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.29.tgz", + "integrity": "sha512-1hNiRjawYrLq/4m3DQQjPGFg0VZkk4RjQJDff/excI6Dm9BiL75qxGrd7/c6YOxPdq6AscP3LiXhQ6fKFC1Waw==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/human-signals": { @@ -2956,15 +3246,19 @@ } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ignore": { @@ -3010,6 +3304,15 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3090,6 +3393,15 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/joycon": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", @@ -3100,11 +3412,28 @@ "node": ">=10" } }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -3124,8 +3453,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, "license": "MIT" }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -3144,9 +3480,9 @@ } }, "node_modules/leetcode-query": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/leetcode-query/-/leetcode-query-2.0.0.tgz", - "integrity": "sha512-kIX2YZq/XTRx53VObr4D39u9nSEnBUl3aCoYQEzTusuo97Oo3EfjryvyN6esqwqtfYa2AeSy/glsKVVxyTAcXw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/leetcode-query/-/leetcode-query-2.0.1.tgz", + "integrity": "sha512-zvVp5T5C69pmvgaaxIP8OFBfhzIw57TAVPFDK5y2DYDNPW2A5sBSFHAUDgFpbZylO3VeHJWlCBAEgrhs2PHR9Q==", "license": "MIT", "dependencies": { "@fetch-impl/cross-fetch": "^1.0.0", @@ -3331,20 +3667,20 @@ } }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "dev": true, "license": "MIT" }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/make-error": { @@ -3430,15 +3766,19 @@ } }, "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-fn": { @@ -3468,9 +3808,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -3496,9 +3836,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", "dev": true, "funding": [ { @@ -3745,12 +4085,13 @@ } }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", - "engines": { - "node": ">=16" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/pathe": { @@ -3761,9 +4102,9 @@ "license": "MIT" }, "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { @@ -3791,9 +4132,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -3888,9 +4229,9 @@ } }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", "dev": true, "funding": [ { @@ -3908,7 +4249,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4032,18 +4373,20 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -4080,27 +4423,31 @@ "license": "MIT" }, "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, "node_modules/real-require": { @@ -4112,6 +4459,15 @@ "node": ">= 12.13.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4174,13 +4530,13 @@ "license": "MIT" }, "node_modules/rollup": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", - "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -4190,26 +4546,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.41.1", - "@rollup/rollup-android-arm64": "4.41.1", - "@rollup/rollup-darwin-arm64": "4.41.1", - "@rollup/rollup-darwin-x64": "4.41.1", - "@rollup/rollup-freebsd-arm64": "4.41.1", - "@rollup/rollup-freebsd-x64": "4.41.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", - "@rollup/rollup-linux-arm-musleabihf": "4.41.1", - "@rollup/rollup-linux-arm64-gnu": "4.41.1", - "@rollup/rollup-linux-arm64-musl": "4.41.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", - "@rollup/rollup-linux-riscv64-gnu": "4.41.1", - "@rollup/rollup-linux-riscv64-musl": "4.41.1", - "@rollup/rollup-linux-s390x-gnu": "4.41.1", - "@rollup/rollup-linux-x64-gnu": "4.41.1", - "@rollup/rollup-linux-x64-musl": "4.41.1", - "@rollup/rollup-win32-arm64-msvc": "4.41.1", - "@rollup/rollup-win32-ia32-msvc": "4.41.1", - "@rollup/rollup-win32-x64-msvc": "4.41.1", + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" } }, @@ -4253,26 +4614,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safe-stable-stringify": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", @@ -4309,31 +4650,35 @@ } }, "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "statuses": "^2.0.2" }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -4343,6 +4688,10 @@ }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/setprototypeof": { @@ -4373,14 +4722,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -4392,13 +4741,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -4543,9 +4892,9 @@ "license": "MIT" }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4638,6 +4987,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4730,9 +5092,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -4743,9 +5105,9 @@ } }, "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -4763,9 +5125,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", "dev": true, "license": "MIT", "engines": { @@ -4892,17 +5254,34 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typescript": { @@ -4960,6 +5339,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -4981,9 +5361,9 @@ } }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "dev": true, "license": "MIT", "dependencies": { @@ -5056,17 +5436,17 @@ } }, "node_modules/vite-node": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz", - "integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.4.0", + "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" @@ -5094,9 +5474,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -5107,32 +5487,34 @@ } }, "node_modules/vitest": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz", - "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.4", - "@vitest/mocker": "3.1.4", - "@vitest/pretty-format": "^3.1.4", - "@vitest/runner": "3.1.4", - "@vitest/snapshot": "3.1.4", - "@vitest/spy": "3.1.4", - "@vitest/utils": "3.1.4", + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", "chai": "^5.2.0", - "debug": "^4.4.0", + "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", + "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.13", - "tinypool": "^1.0.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.4", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -5148,8 +5530,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.4", - "@vitest/ui": "3.1.4", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", "happy-dom": "*", "jsdom": "*" }, @@ -5177,6 +5559,19 @@ } } }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -5273,9 +5668,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "dev": true, "license": "ISC", "bin": { @@ -5283,6 +5678,9 @@ }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yn": { @@ -5317,12 +5715,12 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", "license": "ISC", "peerDependencies": { - "zod": "^3.24.1" + "zod": "^3.25.28 || ^4" } } } diff --git a/package.json b/package.json index 68ea88b..6ae3206 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "start": "node build/index.js", "dev": "tsc-watch --onSuccess \"node build/index.js\"", "format": "prettier --write .", - "prepare": "husky" + "prepare": "husky", + "lint": "eslint .", + "lint:fix": "eslint . --fix" }, "bin": { "leetcode-mcp-server": "build/index.js" @@ -42,8 +44,8 @@ ] }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.8.0", - "leetcode-query": "^2.0.0", + "@modelcontextprotocol/sdk": "^1.29.0", + "leetcode-query": "^2.0.1", "minimist": "^1.2.8", "pino": "^9.6.0", "ts-node": "^10.9.2", @@ -70,6 +72,6 @@ "pino-pretty": "^13.0.0" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "^4.40.1" + "@rollup/rollup-linux-x64-gnu": "^4.62.2" } } diff --git a/smithery.yaml b/smithery.yaml deleted file mode 100644 index c90eb87..0000000 --- a/smithery.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml - -startCommand: - type: stdio - configSchema: - # JSON Schema defining the configuration options for the MCP. - type: object - properties: - site: - type: string - default: global - description: "LeetCode site to target: 'global' for leetcode.com, or 'cn' for leetcode.cn" - session: - type: string - description: Optional session cookie to authenticate with LeetCode API - commandFunction: - # A JS function that produces the CLI command based on the given config to start the MCP on stdio. - |- - (config) => { - const args = ['build/index.js', '--site', config.site]; - if (config.session && config.session.trim() !== '') { - args.push('--session', config.session); - } - return { - command: 'node', - args - }; - } - exampleConfig: - site: global - session: leetcode_session_cookie_value diff --git a/src/index.ts b/src/index.ts index 1be2d6e..31d53dd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,21 +15,28 @@ import { registerProblemTools } from "./mcp/tools/problem-tools.js"; import { registerSolutionTools } from "./mcp/tools/solution-tools.js"; import { registerSubmissionTools } from "./mcp/tools/submission-tools.js"; import { registerUserTools } from "./mcp/tools/user-tools.js"; +import { startStreamableHttpTransport } from "./transport/streamable-http.js"; import logger from "./utils/logger.js"; +type TransportMode = "stdio" | "http"; + /** * Parses and validates command line arguments for the LeetCode MCP Server. * - * @returns Configuration object with site and optional session information + * @returns Configuration object with site, transport, and optional session information */ function parseArgs() { const args = minimist(process.argv.slice(2), { - string: ["site", "session"], + string: ["site", "session", "transport", "host", "endpoint"], boolean: ["help"], alias: { s: "site", c: "session", - h: "help" + h: "help", + t: "transport" + }, + default: { + port: 3000 } }); @@ -39,15 +46,35 @@ function parseArgs() { Usage: leetcode-mcp-server [options] Options: - --site, -s LeetCode API site: 'global' (leetcode.com) or 'cn' (leetcode.cn), default is 'global' - --session, -c Optional LeetCode session cookie for authenticated requests - --help, -h Show this help message`); + --site, -s LeetCode API site: 'global' (leetcode.com) or 'cn' (leetcode.cn), default is 'global' + --session, -c Optional LeetCode session cookie for authenticated requests + --transport, -t Transport mode: 'stdio' (default) or 'http' (Streamable HTTP) + --port HTTP server port when using Streamable HTTP transport (default: 3000) + --host HTTP server host when using Streamable HTTP transport (default: 127.0.0.1) + --endpoint HTTP endpoint path when using Streamable HTTP transport (default: /mcp) + --help, -h Show this help message + + Environment variables: + LEETCODE_SITE Same as --site + LEETCODE_SESSION Same as --session + LEETCODE_TRANSPORT Same as --transport + LEETCODE_HTTP_PORT Same as --port + LEETCODE_HTTP_HOST Same as --host + LEETCODE_HTTP_ENDPOINT Same as --endpoint`); process.exit(0); } + const transport = (args.transport || + process.env.LEETCODE_TRANSPORT || + "stdio") as TransportMode; + const options = { site: args.site || process.env.LEETCODE_SITE || "global", - session: args.session || process.env.LEETCODE_SESSION + session: args.session || process.env.LEETCODE_SESSION, + transport, + port: Number(args.port || process.env.LEETCODE_HTTP_PORT || 3000), + host: args.host || process.env.LEETCODE_HTTP_HOST || "127.0.0.1", + endpoint: args.endpoint || process.env.LEETCODE_HTTP_ENDPOINT || "/mcp" }; if (options.site !== "global" && options.site !== "cn") { @@ -55,6 +82,25 @@ function parseArgs() { process.exit(1); } + if (options.transport !== "stdio" && options.transport !== "http") { + logger.error("The transport must be either 'stdio' or 'http'"); + process.exit(1); + } + + if ( + !Number.isInteger(options.port) || + options.port < 1 || + options.port > 65535 + ) { + logger.error("The port must be an integer between 1 and 65535"); + process.exit(1); + } + + if (!options.endpoint.startsWith("/")) { + logger.error("The endpoint must start with '/'"); + process.exit(1); + } + return options; } @@ -72,12 +118,9 @@ function getPackageJson() { } /** - * Main function that initializes and starts the LeetCode MCP Server. - * This function creates the server, sets up the LeetCode service, - * registers all tools and resources, and connects the server to the stdio transport. + * Creates and configures the LeetCode MCP Server instance. */ -async function main() { - const options = parseArgs(); +function createMcpServer(leetcodeService: LeetCodeBaseService): McpServer { const packageJSON = getPackageJson(); const server = new McpServer({ @@ -85,12 +128,6 @@ async function main() { version: packageJSON.version }); - const leetcodeService: LeetCodeBaseService = - await LeetCodeServiceFactory.createService( - options.site, - options.session - ); - registerProblemTools(server, leetcodeService); registerUserTools(server, leetcodeService); registerContestTools(server, leetcodeService); @@ -101,6 +138,32 @@ async function main() { registerProblemResources(server, leetcodeService); registerSolutionResources(server, leetcodeService); + return server; +} + +/** + * Main function that initializes and starts the LeetCode MCP Server. + */ +async function main() { + const options = parseArgs(); + + const leetcodeService: LeetCodeBaseService = + await LeetCodeServiceFactory.createService( + options.site, + options.session + ); + + if (options.transport === "http") { + await startStreamableHttpTransport({ + host: options.host, + port: options.port, + endpoint: options.endpoint, + createServer: () => createMcpServer(leetcodeService) + }); + return; + } + + const server = createMcpServer(leetcodeService); const transport = new StdioServerTransport(); await server.connect(transport); } diff --git a/src/mcp/tools/contest-tools.ts b/src/mcp/tools/contest-tools.ts index a4a5c8c..43499d8 100644 --- a/src/mcp/tools/contest-tools.ts +++ b/src/mcp/tools/contest-tools.ts @@ -12,19 +12,19 @@ export class ContestToolRegistry extends ToolRegistry { // User contest ranking tool this.server.tool( "get_user_contest_ranking", - "Retrieves a user's contest ranking information on LeetCode, including overall ranking, participation history, and performance metrics across contests", + "Retrieves a user's contest ranking and participation history (read-only, no auth). Returns overall rating, ranking, and per-contest performance as JSON. Use get_user_profile for general stats and ranking; use this specifically for contest performance data.", { username: z .string() .describe( - "LeetCode username to retrieve contest ranking information for" + "LeetCode username whose contest ranking to fetch" ), attended: z .boolean() .optional() .default(true) .describe( - "Whether to include only the contests the user has participated in (true) or all contests (false); defaults to true" + "true = only contests the user attended (default); false = include all contests in history" ) }, async ({ username, attended = true }) => { diff --git a/src/mcp/tools/note-tools.ts b/src/mcp/tools/note-tools.ts index 6ef5ba4..1859aba 100644 --- a/src/mcp/tools/note-tools.ts +++ b/src/mcp/tools/note-tools.ts @@ -12,34 +12,30 @@ export class NoteToolRegistry extends ToolRegistry { // Notes search tool (CN-specific) this.server.tool( "search_notes", - "Searches for user notes on LeetCode with filtering options, returning note content and associated problem information (requires authentication)", + "Searches the authenticated user's personal notes across all problems (read-only, requires auth, CN only). Keyword search with pagination. Use get_note when you know the specific questionId; use this to find notes by keyword across problems.", { keyword: z .string() .optional() .describe( - "Optional search term to filter notes by content or title" + "Search term to filter notes by content or title. Omit to list all notes." ), limit: z .number() .optional() .default(10) - .describe( - "Maximum number of notes to return per request (defaults to 10)" - ), + .describe("Max notes per page (default: 10)"), skip: z .number() .optional() .default(0) - .describe( - "Number of notes to skip before returning results (for pagination)" - ), + .describe("Notes to skip for pagination (default: 0)"), orderBy: z .enum(["ASCENDING", "DESCENDING"]) .optional() .default("DESCENDING") .describe( - "Sort order for returned notes: 'DESCENDING' (newest first) or 'ASCENDING' (oldest first)" + "'DESCENDING' = newest first (default); 'ASCENDING' = oldest first" ) }, async ({ keyword, limit, skip, orderBy }) => { @@ -90,27 +86,23 @@ export class NoteToolRegistry extends ToolRegistry { // Notes detail tool (CN-specific, requires authentication) this.server.tool( "get_note", - "Retrieves user notes for a specific LeetCode problem by its question ID, returning the complete note content and metadata (requires authentication)", + "Retrieves personal notes for a specific problem by questionId (read-only, requires auth, CN only). Use search_notes to find notes across problems by keyword when you don't know the questionId.", { questionId: z .string() .describe( - "The question ID of the LeetCode problem to get notes for (e.g., '42' for 'Trapping Rain Water')" + "Numeric problem ID (e.g., '42' for Trapping Rain Water)—not the titleSlug" ), limit: z .number() .optional() .default(10) - .describe( - "Maximum number of notes to return per request (defaults to 10)" - ), + .describe("Max notes per page (default: 10)"), skip: z .number() .optional() .default(0) - .describe( - "Number of notes to skip before returning results (for pagination)" - ) + .describe("Notes to skip for pagination (default: 0)") }, async ({ questionId, limit = 20, skip = 0 }) => { try { @@ -153,23 +145,19 @@ export class NoteToolRegistry extends ToolRegistry { // Note creation tool (CN-specific, requires authentication) this.server.tool( "create_note", - "Creates a new note for a specific LeetCode problem, allowing users to save personal comments and observations for future reference (requires authentication, CN only)", + "Creates a new personal note for a problem (write operation, requires auth, CN only). Supports markdown content. Returns { success, note } as JSON. Use update_note to modify an existing note by noteId.", { questionId: z .string() .describe( - "The question ID of the LeetCode problem to create a note for (e.g., '42' for 'Trapping Rain Water')" - ), - content: z - .string() - .describe( - "The content of the note (supports markdown format)" + "Numeric problem ID (e.g., '42')—not the titleSlug" ), + content: z.string().describe("Note body (markdown supported)"), title: z .string() .optional() .default("") - .describe("An short title or summary for the note") + .describe("Optional short title or summary") }, async ({ questionId, content, title = "" }) => { try { @@ -210,19 +198,19 @@ export class NoteToolRegistry extends ToolRegistry { // Note update tool (CN-specific, requires authentication) this.server.tool( "update_note", - "Updates an existing note with new content or title, allowing users to refine their saved observations (requires authentication, CN only)", + "Updates an existing personal note by noteId (write operation, requires auth, CN only). Use create_note for new notes; use get_note or search_notes first to find the noteId.", { - noteId: z.string().describe("The ID of the note to update"), + noteId: z + .string() + .describe("Note ID from get_note or search_notes response"), content: z .string() .default("") - .describe( - "The new content for the note (supports markdown format)" - ), + .describe("New note body (markdown supported)"), title: z .string() .default("") - .describe("An new short title or summary for the note") + .describe("New short title or summary") }, async ({ noteId, content, title = "" }) => { try { diff --git a/src/mcp/tools/problem-tools.ts b/src/mcp/tools/problem-tools.ts index bb5c3f2..becc2c5 100644 --- a/src/mcp/tools/problem-tools.ts +++ b/src/mcp/tools/problem-tools.ts @@ -13,7 +13,7 @@ export class ProblemToolRegistry extends ToolRegistry { // Daily challenge tool this.server.tool( "get_daily_challenge", - "Retrieves today's LeetCode Daily Challenge problem with complete details, including problem description, constraints, and examples", + "Retrieves today's Daily Challenge problem with full description (read-only, no auth). Returns problem details for the current date as JSON. Use get_problem when you know a specific titleSlug; use search_problems to discover problems by filters.", {}, async () => { const data = await this.leetcodeService.fetchDailyChallenge(); @@ -34,12 +34,12 @@ export class ProblemToolRegistry extends ToolRegistry { // Problem details tool this.server.tool( "get_problem", - "Retrieves details about a specific LeetCode problem, including its description, examples, constraints, and related information", + "Retrieves a single LeetCode problem by titleSlug (read-only, no auth). Returns description, examples, constraints, and metadata as JSON. Use search_problems to find problems by keyword/tag/difficulty; use get_daily_challenge for today's featured problem.", { titleSlug: z .string() .describe( - "The URL slug/identifier of the problem (e.g., 'two-sum', 'add-two-numbers') as it appears in the LeetCode URL" + "Problem URL slug (e.g., 'two-sum', 'add-two-numbers')—the path segment after /problems/" ) }, async ({ titleSlug }) => { @@ -64,43 +64,39 @@ export class ProblemToolRegistry extends ToolRegistry { // Search problems tool this.server.tool( "search_problems", - "Searches for LeetCode problems based on multiple filter criteria including categories, tags, difficulty levels, and keywords, with pagination support", + "Searches LeetCode problems by category, tags, difficulty, and keywords (read-only, no auth). Supports pagination via limit/offset. Returns matching problem list as JSON. Use get_problem when you already know the titleSlug; use this to browse or discover problems.", { category: z .enum(PROBLEM_CATEGORIES as [string]) .default("all-code-essentials") .describe( - "Problem category filter (e.g., 'algorithms', 'database', 'shell') to narrow down the problem domain" + "Problem set category (e.g., 'algorithms', 'database'). Default: 'all-code-essentials'." ), tags: z .array(z.enum(PROBLEM_TAGS as [string])) .optional() .describe( - "List of topic tags to filter problems by (e.g., ['array', 'dynamic-programming', 'tree'])" + "Topic tags to filter by, e.g. ['array', 'dynamic-programming']. Omit for no tag filter." ), difficulty: z .enum(["EASY", "MEDIUM", "HARD"]) .optional() - .describe( - "Problem difficulty level filter to show only problems of a specific difficulty" - ), + .describe("Difficulty filter. Omit for all levels."), searchKeywords: z .string() .optional() .describe( - "Keywords to search in problem titles and descriptions" + "Keyword search in problem titles and descriptions" ), limit: z .number() .optional() .default(10) - .describe( - "Maximum number of problems to return in a single request (for pagination)" - ), + .describe("Max results per page (default: 10)"), offset: z .number() .optional() - .describe("Number of problems to skip (for pagination)") + .describe("Results to skip for pagination (default: 0)") }, async ({ category, diff --git a/src/mcp/tools/solution-tools.ts b/src/mcp/tools/solution-tools.ts index ac001b4..ddaf3e2 100644 --- a/src/mcp/tools/solution-tools.ts +++ b/src/mcp/tools/solution-tools.ts @@ -12,45 +12,41 @@ export class SolutionToolRegistry extends ToolRegistry { // Problem solutions listing tool (Global-specific) this.server.tool( "list_problem_solutions", - "Retrieves a list of community solutions for a specific LeetCode problem, including only metadata like topicId. To view the full content of a solution, use the 'get_problem_solution' tool with the topicId returned by this tool.", + "Lists community solution articles for a problem (read-only, no auth). Returns metadata only (topicId)—not full content. Use get_problem_solution with the returned topicId to read the full article. Do not call this when you already have a topicId and need the solution text.", { questionSlug: z .string() .describe( - "The URL slug/identifier of the problem to retrieve solutions for (e.g., 'two-sum', 'add-two-numbers'). This is the same string that appears in the LeetCode problem URL after '/problems/'" + "Problem URL slug (e.g., 'two-sum')—same as in /problems/{slug}" ), limit: z .number() .optional() .default(10) - .describe( - "Maximum number of solutions to return per request. Used for pagination and controlling response size. Default is 20 if not specified. Must be a positive integer." - ), + .describe("Max solutions per page (default: 10)"), skip: z .number() .optional() - .describe( - "Number of solutions to skip before starting to collect results. Used in conjunction with 'limit' for implementing pagination. Default is 0 if not specified. Must be a non-negative integer." - ), + .describe("Solutions to skip for pagination (default: 0)"), orderBy: z .enum(["HOT", " MOST_RECENT", "MOST_VOTES"]) .default("HOT") .optional() .describe( - "Sorting criteria for the returned solutions. 'DEFAULT' sorts by LeetCode's default algorithm (typically a combination of recency and popularity), 'MOST_VOTES' sorts by the number of upvotes (highest first), and 'MOST_RECENT' sorts by publication date (newest first)." + "Sort order: 'HOT' (default), 'MOST_VOTES', or 'MOST_RECENT'" ), userInput: z .string() .optional() .describe( - "Search term to filter solutions by title, content, or author name. Case insensitive. Useful for finding specific approaches or algorithms mentioned in solutions." + "Keyword filter on solution title, content, or author (case-insensitive)" ), tagSlugs: z .array(z.string()) .optional() .default([]) .describe( - "Array of tag identifiers to filter solutions by programming languages (e.g., 'python', 'java') or problem algorithm/data-structure tags (e.g., 'dynamic-programming', 'recursion'). Only solutions tagged with at least one of the specified tags will be returned." + "Filter by language or algorithm tags, e.g. ['python', 'dynamic-programming']" ) }, async ({ @@ -106,12 +102,12 @@ export class SolutionToolRegistry extends ToolRegistry { // Solution article detail tool (Global-specific) this.server.tool( "get_problem_solution", - "Retrieves the complete content and metadata of a specific solution, including the full article text, author information, and related navigation links", + "Retrieves full content of a community solution article (read-only, no auth). Requires topicId from list_problem_solutions. Returns article text, author, and metadata as JSON. Use list_problem_solutions first to discover solutions and obtain topicId.", { topicId: z .string() .describe( - "The unique topic ID of the solution to retrieve. This ID can be obtained from the 'topicId' field in the response of the 'list_problem_solutions' tool. Format is typically a string of numbers and letters that uniquely identifies the solution in LeetCode's database." + "Solution topicId from list_problem_solutions response" ) }, async ({ topicId }) => { @@ -153,27 +149,23 @@ export class SolutionToolRegistry extends ToolRegistry { // Problem solutions listing tool (CN-specific) this.server.tool( "list_problem_solutions", - "Retrieves a list of community solutions for a specific LeetCode problem, including only metadata like article slug. To view the full content of a solution, use the 'get_problem_solution' tool with the slug returned by this tool.", + "Lists community solution articles for a problem on LeetCode CN (read-only, no auth). Returns metadata only (slug)—not full content. Use get_problem_solution with the returned slug to read the full article.", { questionSlug: z .string() .describe( - "The URL slug/identifier of the problem to retrieve solutions for (e.g., 'two-sum', 'add-two-numbers'). This is the same string that appears in the LeetCode problem URL after '/problems/'" + "Problem URL slug (e.g., 'two-sum')—same as in /problems/{slug}" ), limit: z .number() .min(1) .optional() .default(10) - .describe( - "Maximum number of solutions to return per request. Used for pagination and controlling response size. Default is 20 if not specified. Must be a positive integer. If not provided or set to a very large number, the system may still apply internal limits." - ), + .describe("Max solutions per page (default: 10)"), skip: z .number() .optional() - .describe( - "Number of solutions to skip before starting to collect results. Used in conjunction with 'limit' for implementing pagination. Default is 0 if not specified. Must be a non-negative integer." - ), + .describe("Solutions to skip for pagination (default: 0)"), orderBy: z .enum([ "DEFAULT", @@ -185,20 +177,20 @@ export class SolutionToolRegistry extends ToolRegistry { .default("DEFAULT") .optional() .describe( - "Sorting criteria for the returned solutions. 'DEFAULT' uses the default algorithm, 'MOST_UPVOTE' sorts by the number of upvotes (highest first), 'HOT' prioritizes trending solutions with recent engagement, 'NEWEST_TO_OLDEST' sorts by publication date (newest first), and 'OLDEST_TO_NEWEST' sorts by publication date (oldest first)." + "Sort order: 'DEFAULT' (default), 'MOST_UPVOTE', 'HOT', 'NEWEST_TO_OLDEST', or 'OLDEST_TO_NEWEST'" ), userInput: z .string() .optional() .describe( - "Search term to filter solutions by title, content, or author name. Case insensitive. Useful for finding specific approaches or algorithms mentioned in solutions." + "Keyword filter on solution title, content, or author (case-insensitive)" ), tagSlugs: z .array(z.string()) .optional() .default([]) .describe( - "Array of tag identifiers to filter solutions by programming languages (e.g., 'python', 'java') or problem algorithm/data-structure approaches (e.g., 'dynamic-programming', 'recursion'). Only solutions tagged with at least one of the specified tags will be returned." + "Filter by language or algorithm tags, e.g. ['python', 'dynamic-programming']" ) }, async ({ @@ -254,12 +246,12 @@ export class SolutionToolRegistry extends ToolRegistry { // Solution article detail tool (CN-specific) this.server.tool( "get_problem_solution", - "Retrieves the complete content and metadata of a specific solution, including the full article text, author information, and related navigation links", + "Retrieves full content of a community solution article on LeetCode CN (read-only, no auth). Requires slug from list_problem_solutions (node.slug field). Returns article text, author, and metadata as JSON.", { slug: z .string() .describe( - "The unique slug/identifier of the solution to retrieve. This slug can be obtained from the 'node.slug' field in the response of the 'list_problem_solutions' tool. A URL-friendly slug string to identify solutions." + "Solution slug from list_problem_solutions response (node.slug field)" ) }, async ({ slug }) => { diff --git a/src/mcp/tools/submission-tools.ts b/src/mcp/tools/submission-tools.ts index d7b913f..7f756a9 100644 --- a/src/mcp/tools/submission-tools.ts +++ b/src/mcp/tools/submission-tools.ts @@ -8,26 +8,24 @@ export class SubmissionToolRegistry extends ToolRegistry { protected registerAuthenticatedCommon(): void { this.server.tool( "run_code", - "Runs code for a LeetCode problem (requires authentication). Starts an interpret request then polls the /check/ endpoint until finished, returning both the start response and final check JSON.", + "Runs code against test cases without submitting (side effect: creates interpret request on LeetCode, requires auth). Polls /check/ until finished; returns start response and final check JSON. Supports custom input via dataInput. Use submit_solution for official judging; use run_code for testing/debugging only.", { titleSlug: z .string() .min(1) - .describe( - "The URL slug/identifier of the problem (e.g., 'two-sum')" - ), + .describe("Problem URL slug (e.g., 'two-sum')"), lang: z .enum(PROGRAMMING_LANGS as [string]) - .describe("Programming language (e.g., 'python3', 'cpp')"), + .describe("Language slug (e.g., 'python3', 'cpp', 'java')"), typedCode: z .string() .min(1) - .describe("Source code to run (will not be logged)"), + .describe("Source code to run (not logged by this server)"), dataInput: z .string() .optional() .describe( - "Custom test input for run (optional). If omitted, LeetCode may use default test cases." + "Custom test input. Omit to use LeetCode default test cases." ), timeoutMs: z .number() @@ -35,18 +33,14 @@ export class SubmissionToolRegistry extends ToolRegistry { .positive() .optional() .default(120000) - .describe( - "Polling timeout in milliseconds (optional, default: 120000)" - ), + .describe("Max wait for result in ms (default: 120000)"), pollIntervalMs: z .number() .int() .positive() .optional() .default(1500) - .describe( - "Polling interval in milliseconds (optional, default: 1500)" - ) + .describe("Polling interval in ms (default: 1500)") }, async ({ titleSlug, @@ -111,39 +105,35 @@ export class SubmissionToolRegistry extends ToolRegistry { this.server.tool( "submit_solution", - "Submits code for a LeetCode problem (requires authentication). Starts a submission then polls the /check/ endpoint until finished, returning both the start response and final check JSON.", + "Submits code for official judging (side effect: creates submission on LeetCode, requires auth). Polls /check/ until finished; returns start response and final check JSON. Counts toward submission history. Use run_code for dry-run testing with optional custom input; use submit_solution when ready for final judging.", { titleSlug: z .string() .min(1) - .describe( - "The URL slug/identifier of the problem (e.g., 'two-sum')" - ), + .describe("Problem URL slug (e.g., 'two-sum')"), lang: z .enum(PROGRAMMING_LANGS as [string]) - .describe("Programming language (e.g., 'python3', 'cpp')"), + .describe("Language slug (e.g., 'python3', 'cpp', 'java')"), typedCode: z .string() .min(1) - .describe("Source code to submit (will not be logged)"), + .describe( + "Source code to submit (not logged by this server)" + ), timeoutMs: z .number() .int() .positive() .optional() .default(120000) - .describe( - "Polling timeout in milliseconds (optional, default: 120000)" - ), + .describe("Max wait for result in ms (default: 120000)"), pollIntervalMs: z .number() .int() .positive() .optional() .default(1500) - .describe( - "Polling interval in milliseconds (optional, default: 1500)" - ) + .describe("Polling interval in ms (default: 1500)") }, async ({ titleSlug, diff --git a/src/mcp/tools/user-tools.ts b/src/mcp/tools/user-tools.ts index f126d15..b367d0c 100644 --- a/src/mcp/tools/user-tools.ts +++ b/src/mcp/tools/user-tools.ts @@ -13,12 +13,12 @@ export class UserToolRegistry extends ToolRegistry { // User profile tool this.server.tool( "get_user_profile", - "Retrieves profile information about a LeetCode user, including user stats, solved problems, and profile details", + "Retrieves any user's public profile by username (read-only, no auth). Returns ranking, avatar, bio, submission stats, and platform-specific progress. Use this to look up other users or public stats. Use get_user_status (requires auth) instead to verify the current session user's login state—not for looking up arbitrary users.", { username: z .string() .describe( - "LeetCode username to retrieve profile information for" + "LeetCode username (case-sensitive). Public profile lookup; no authentication required." ) }, async ({ username }) => { @@ -43,19 +43,19 @@ export class UserToolRegistry extends ToolRegistry { // Recent submissions tool (Global-specific) this.server.tool( "get_recent_submissions", - "Retrieves a user's recent submissions on LeetCode Global, including both accepted and failed submissions with detailed metadata", + "Retrieves a user's recent submission history on LeetCode Global (read-only, no auth). Includes both accepted and failed submissions. Global only—not available on CN. Use get_recent_ac_submissions for accepted-only results, or get_all_submissions (auth, current user) for paginated full history with filters.", { username: z .string() .describe( - "LeetCode username to retrieve recent submissions for" + "LeetCode username whose public recent submissions to fetch" ), limit: z .number() .optional() .default(10) .describe( - "Maximum number of submissions to return (optional, defaults to server-defined limit)" + "Max submissions to return (default: 10). Increase for more history within the recent window." ) }, async ({ username, limit }) => { @@ -95,20 +95,18 @@ export class UserToolRegistry extends ToolRegistry { // Recent accepted submissions tool (Global-specific) this.server.tool( "get_recent_ac_submissions", - "Retrieves a user's recent accepted (AC) submissions on LeetCode Global, focusing only on successfully completed problems", + "Retrieves a user's recent accepted (AC) submissions (read-only, no auth). Available on both Global and CN. Use get_recent_submissions (Global only) to include failed attempts, or get_all_submissions (auth, current user) for full paginated history.", { username: z .string() .describe( - "LeetCode username to retrieve recent accepted submissions for" + "LeetCode username whose public recent AC submissions to fetch" ), limit: z .number() .optional() .default(10) - .describe( - "Maximum number of accepted submissions to return (optional, defaults to server-defined limit)" - ) + .describe("Max AC submissions to return (default: 10)") }, async ({ username, limit }) => { try { @@ -149,20 +147,18 @@ export class UserToolRegistry extends ToolRegistry { // User recent AC submissions tool (CN-specific) this.server.tool( "get_recent_ac_submissions", - "Retrieves a user's recent accepted (AC) submissions on LeetCode China, with details about each successfully solved problem", + "Retrieves a user's recent accepted (AC) submissions on LeetCode CN (read-only, no auth). Use get_all_submissions (auth, current user) for full paginated history with lang/status filters.", { username: z .string() .describe( - "LeetCode China username to retrieve recent accepted submissions for" + "LeetCode CN username whose public recent AC submissions to fetch" ), limit: z .number() .optional() .default(10) - .describe( - "Maximum number of accepted submissions to return (optional, defaults to server-defined limit)" - ) + .describe("Max AC submissions to return (default: 10)") }, async ({ username, limit }) => { try { @@ -206,7 +202,7 @@ export class UserToolRegistry extends ToolRegistry { // User status tool (requires authentication) this.server.tool( "get_user_status", - "Retrieves the current user's status on LeetCode, including login status, premium membership details, and user information (requires authentication)", + "Checks the authenticated user's LeetCode login session (read-only, requires auth). Returns isSignedIn, username, avatar, and admin flag as JSON. Only available when credentials are configured. Use this to verify login state or identify the current user—not to look up other users (use get_user_profile for that). Errors return { error: message } if credentials are missing or invalid.", async () => { try { const status = await this.leetcodeService.fetchUserStatus(); @@ -236,12 +232,12 @@ export class UserToolRegistry extends ToolRegistry { // Submission detail tool (requires authentication) this.server.tool( "get_problem_submission_report", - "Retrieves detailed information about a specific LeetCode submission by its ID, including source code, runtime stats, and test results (requires authentication)", + "Retrieves full details for one submission by ID (read-only, requires auth). Returns source code, runtime, memory, and test results as JSON. Use when you have a submission ID from get_all_submissions or get_recent_submissions. Use get_all_submissions to browse/list submissions; do not use it when you only need one submission's code and results.", { id: z .number() .describe( - "The numerical submission ID to retrieve detailed information for" + "Numeric submission ID (e.g., from get_all_submissions or get_recent_submissions response)" ) }, async ({ id }) => { @@ -277,31 +273,29 @@ export class UserToolRegistry extends ToolRegistry { // User progress questions tool (requires authentication) this.server.tool( "get_problem_progress", - "Retrieves the current user's problem-solving status with filtering options, including detailed solution history for attempted or solved questions (requires authentication)", + "Retrieves the authenticated user's per-problem solving progress (read-only, requires auth). Filter by ATTEMPTED/SOLVED status and difficulty; supports pagination. Use for tracking which problems the current user has tried or solved. Use get_all_submissions for individual submission records with code/runtime, or get_recent_ac_submissions for another user's public AC list.", { offset: z .number() .default(0) .describe( - "The number of questions to skip for pagination purposes" + "Pagination offset—number of questions to skip (default: 0)" ), limit: z .number() .default(100) - .describe( - "The maximum number of questions to return in a single request" - ), + .describe("Max questions per page (default: 100)"), questionStatus: z .enum(["ATTEMPTED", "SOLVED"]) .optional() .describe( - "Filter by question status: 'ATTEMPTED' for questions that have been tried but not necessarily solved, 'SOLVED' for questions that have been successfully completed" + "'ATTEMPTED' = tried but not necessarily solved; 'SOLVED' = successfully completed. Omit to return all." ), difficulty: z .array(z.string()) .optional() .describe( - "Filter by difficulty levels as an array (e.g., ['EASY', 'MEDIUM', 'HARD']); if not provided, questions of all difficulty levels will be returned" + "Filter by difficulty, e.g. ['EASY', 'MEDIUM', 'HARD']. Omit for all levels." ) }, async ({ offset, limit, questionStatus, difficulty }) => { @@ -352,25 +346,23 @@ export class UserToolRegistry extends ToolRegistry { // Global user submissions tool (requires authentication) this.server.tool( "get_all_submissions", - "Retrieves a paginated list of the current user's submissions for a specific problem or all problems on LeetCode Global, with detailed submission metadata (requires authentication)", + "Retrieves paginated submission history for the authenticated user on LeetCode Global (read-only, requires auth). Optionally filter by problem slug. Use get_recent_submissions/get_recent_ac_submissions to view any user's public recent history. Use get_problem_submission_report when you have a specific submission ID and need source code and test results.", { limit: z .number() .default(20) - .describe( - "Maximum number of submissions to return per page (typically defaults to 20 if not specified)" - ), + .describe("Submissions per page (default: 20)"), offset: z .number() .default(0) .describe( - "Number of submissions to skip for pagination purposes" + "Pagination offset—submissions to skip (default: 0)" ), questionSlug: z .string() .optional() .describe( - "Optional problem identifier (slug) to filter submissions for a specific problem (e.g., 'two-sum'); if omitted, returns submissions across all problems" + "Filter by problem slug (e.g., 'two-sum'). Omit for all problems." ) }, async ({ questionSlug, limit, offset }) => { @@ -416,43 +408,41 @@ export class UserToolRegistry extends ToolRegistry { // China user submissions tool (requires authentication, enhanced version with more parameters) this.server.tool( "get_all_submissions", - "Retrieves a list of the current user's submissions on LeetCode China with extensive filtering options, including pagination support via lastKey parameter (requires authentication)", + "Retrieves paginated submission history for the authenticated user on LeetCode CN (read-only, requires auth). Supports filtering by problem slug, language, and AC/WA status; uses lastKey for cursor pagination. Use get_recent_ac_submissions for another user's public AC list. Use get_problem_submission_report for full details of a single submission by ID.", { limit: z .number() .default(20) - .describe( - "Maximum number of submissions to return per page (typically defaults to 20 if not specified)" - ), + .describe("Submissions per page (default: 20)"), offset: z .number() .default(0) .describe( - "Number of submissions to skip for pagination purposes" + "Pagination offset—submissions to skip (default: 0)" ), questionSlug: z .string() .optional() .describe( - "Optional problem identifier (slug) to filter submissions for a specific problem (e.g., 'two-sum'); if omitted, returns submissions across all problems" + "Filter by problem slug (e.g., 'two-sum'). Omit for all problems." ), lang: z .enum(PROGRAMMING_LANGS as [string]) .optional() .describe( - "Programming language filter to show only submissions in a specific language (e.g., 'python3', 'java', 'cpp')" + "Filter by language (e.g., 'python3', 'java', 'cpp'). Omit for all languages." ), status: z .enum(["AC", "WA"]) .optional() .describe( - "Submission status filter (e.g., 'AC' for Accepted, 'WA' for Wrong Answer) to show only submissions with that status; if omitted, returns all submissions" + "'AC' = accepted, 'WA' = wrong answer. Omit for all statuses." ), lastKey: z .string() .optional() .describe( - "Pagination token from a previous request used to retrieve the next page of results" + "Cursor token from previous response's lastKey for next page" ) }, async ({ questionSlug, limit, offset, lang, status, lastKey }) => { diff --git a/src/transport/streamable-http.ts b/src/transport/streamable-http.ts new file mode 100644 index 0000000..e7e6022 --- /dev/null +++ b/src/transport/streamable-http.ts @@ -0,0 +1,173 @@ +import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; +import { randomUUID } from "node:crypto"; +import type { IncomingMessage, ServerResponse } from "node:http"; +import logger from "../utils/logger.js"; + +type McpRequest = IncomingMessage & { body?: unknown }; +type McpResponse = ServerResponse & { + status(code: number): McpResponse; + json(body: unknown): void; + send(body: string): void; +}; + +export interface StreamableHttpOptions { + host: string; + port: number; + endpoint: string; + createServer: () => Promise | McpServer; +} + +/** + * Starts the MCP server using the Streamable HTTP transport. + * + * Implements stateful session management with POST, GET (SSE), and DELETE endpoints + * as defined in the MCP Streamable HTTP specification. + */ +export async function startStreamableHttpTransport( + options: StreamableHttpOptions +): Promise { + const { host, port, endpoint, createServer } = options; + const app = createMcpExpressApp({ host }); + const transports: Record = {}; + + const mcpPostHandler = async (req: McpRequest, res: McpResponse) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + + try { + let transport: StreamableHTTPServerTransport; + + if (sessionId && transports[sessionId]) { + transport = transports[sessionId]; + } else if (!sessionId && isInitializeRequest(req.body)) { + transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + onsessioninitialized: (id) => { + transports[id] = transport; + logger.info("MCP session initialized: %s", id); + } + }); + + transport.onclose = () => { + const sid = transport.sessionId; + if (sid && transports[sid]) { + logger.info("MCP session closed: %s", sid); + delete transports[sid]; + } + }; + + const server = await createServer(); + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + return; + } else { + res.status(400).json({ + jsonrpc: "2.0", + error: { + code: -32000, + message: "Bad Request: No valid session ID provided" + }, + id: null + }); + return; + } + + await transport.handleRequest(req, res, req.body); + } catch (error) { + logger.error("Error handling MCP POST request: %s", error); + if (!res.headersSent) { + res.status(500).json({ + jsonrpc: "2.0", + error: { + code: -32603, + message: "Internal server error" + }, + id: null + }); + } + } + }; + + const mcpGetHandler = async (req: McpRequest, res: McpResponse) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + + if (!sessionId || !transports[sessionId]) { + res.status(400).send("Invalid or missing session ID"); + return; + } + + const transport = transports[sessionId]; + await transport.handleRequest(req, res); + }; + + const mcpDeleteHandler = async (req: McpRequest, res: McpResponse) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + + if (!sessionId || !transports[sessionId]) { + res.status(400).send("Invalid or missing session ID"); + return; + } + + logger.info("MCP session termination requested: %s", sessionId); + + try { + const transport = transports[sessionId]; + await transport.handleRequest(req, res); + } catch (error) { + logger.error("Error handling MCP session termination: %s", error); + if (!res.headersSent) { + res.status(500).send("Error processing session termination"); + } + } + }; + + app.post(endpoint, mcpPostHandler); + app.get(endpoint, mcpGetHandler); + app.delete(endpoint, mcpDeleteHandler); + + const closeAllTransports = async () => { + for (const sessionId of Object.keys(transports)) { + try { + await transports[sessionId].close(); + delete transports[sessionId]; + } catch (error) { + logger.error( + "Error closing transport for session %s: %s", + sessionId, + error + ); + } + } + }; + + process.on("SIGINT", async () => { + logger.info("Shutting down Streamable HTTP server..."); + await closeAllTransports(); + process.exit(0); + }); + + process.on("SIGTERM", async () => { + logger.info("Shutting down Streamable HTTP server..."); + await closeAllTransports(); + process.exit(0); + }); + + await new Promise((resolve, reject) => { + app.listen(port, host, (error?: Error) => { + if (error) { + reject(error); + return; + } + + logger.info( + "LeetCode MCP Server (Streamable HTTP) listening on http://%s:%d%s", + host, + port, + endpoint + ); + resolve(); + }); + }); +}