diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 3496523..2831d4a 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -6,4 +6,4 @@ "author": { "name": "PassionFactory" } -} \ No newline at end of file +} diff --git a/.claude/rules/reference.md b/.claude/rules/reference.md index fee79a0..f7e4611 100644 --- a/.claude/rules/reference.md +++ b/.claude/rules/reference.md @@ -2,4 +2,4 @@ https://opencode.ai/docs/formatters/ https://code.claude.com/docs/en/hooks https://code.claude.com/docs/en/hooks-guide https://biomejs.dev/guides/editors/create-an-extension/#use-the-lsp-proxy -https://github.com/oxc-project/oxc/tree/main/crates/oxc_language_server \ No newline at end of file +https://github.com/oxc-project/oxc/tree/main/crates/oxc_language_server diff --git a/.claude/settings.json b/.claude/settings.json index 509e295..33f37b5 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -4,7 +4,8 @@ "Bash(bun run build)", "Bash(bun run test)", "Bash(bun run lint)", - "Bash(bun run lint:fix:*)" + "Bash(bun run lint:fix:*)", + "mcp__github__issue_write" ], "deny": [], "ask": [] @@ -23,4 +24,4 @@ } ] } -} \ No newline at end of file +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7162d2f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install + + - name: Lint + run: bun run lint + + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install + + - name: Typecheck + run: bun run typecheck + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install + + - name: Test with coverage + run: bun test --coverage --coverage-reporter=lcov + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage/lcov.info + fail_ci_if_error: false diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 4fb457e..3289c2a 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -46,8 +46,8 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: "22" - registry-url: "https://registry.npmjs.org" + node-version: '22' + registry-url: 'https://registry.npmjs.org' - name: Install dependencies run: bun install diff --git a/.gitignore b/.gitignore index e00e9ad..45ce1cc 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,9 @@ bun.lockb Thumbs.db .turbo/ +# Lint +.eslintcache + +# Coverage +coverage/ + diff --git a/.mcp.json b/.mcp.json index b48968b..fb4c67c 100644 --- a/.mcp.json +++ b/.mcp.json @@ -9,4 +9,4 @@ "url": "https://mcp.exa.ai/mcp?tools=web_search_exa" } } -} \ No newline at end of file +} diff --git a/.please/memory/tasklist.json b/.please/memory/tasklist.json new file mode 100644 index 0000000..6558c52 --- /dev/null +++ b/.please/memory/tasklist.json @@ -0,0 +1,19 @@ +{ + "session_id": "20251217-oxc-formatter-support", + "feature_name": "Add oxc formatter (oxfmt) support to packages/format", + "created_at": "2025-12-17T10:00:00Z", + "updated_at": "2025-12-17T10:00:00Z", + "status": "in_progress", + "current_phase": 1, + "phases": [ + { "number": 1, "name": "Discovery", "status": "in_progress", "started_at": "2025-12-17T10:00:00Z" }, + { "number": 2, "name": "Codebase Exploration", "status": "pending" }, + { "number": 3, "name": "Clarifying Questions", "status": "pending" }, + { "number": 4, "name": "Architecture Design", "status": "pending" }, + { "number": 5, "name": "GitHub Issue & PR", "status": "pending" }, + { "number": 6, "name": "Implementation", "status": "pending" }, + { "number": 7, "name": "Quality Review", "status": "pending" }, + { "number": 8, "name": "PR Finalization", "status": "pending" } + ], + "tasks": [] +} diff --git a/bun.lock b/bun.lock index 1ab1ead..051bc81 100644 --- a/bun.lock +++ b/bun.lock @@ -12,7 +12,9 @@ "zod": "^3.25.0", }, "devDependencies": { + "@antfu/eslint-config": "^6.7.1", "@types/bun": "latest", + "eslint": "^9.39.2", "turbo": "^2.6.3", "typescript": "^5.7.0", }, @@ -74,10 +76,66 @@ }, }, "packages": { + "@antfu/eslint-config": ["@antfu/eslint-config@6.7.1", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@clack/prompts": "^0.11.0", "@eslint-community/eslint-plugin-eslint-comments": "^4.5.0", "@eslint/markdown": "^7.5.1", "@stylistic/eslint-plugin": "^5.6.1", "@typescript-eslint/eslint-plugin": "^8.49.0", "@typescript-eslint/parser": "^8.49.0", "@vitest/eslint-plugin": "^1.5.2", "ansis": "^4.2.0", "cac": "^6.7.14", "eslint-config-flat-gitignore": "^2.1.0", "eslint-flat-config-utils": "^2.1.4", "eslint-merge-processors": "^2.0.0", "eslint-plugin-antfu": "^3.1.1", "eslint-plugin-command": "^3.4.0", "eslint-plugin-import-lite": "^0.3.0", "eslint-plugin-jsdoc": "^61.5.0", "eslint-plugin-jsonc": "^2.21.0", "eslint-plugin-n": "^17.23.1", "eslint-plugin-no-only-tests": "^3.3.0", "eslint-plugin-perfectionist": "^4.15.1", "eslint-plugin-pnpm": "^1.4.3", "eslint-plugin-regexp": "^2.10.0", "eslint-plugin-toml": "^0.12.0", "eslint-plugin-unicorn": "^62.0.0", "eslint-plugin-unused-imports": "^4.3.0", "eslint-plugin-vue": "^10.6.2", "eslint-plugin-yml": "^1.19.0", "eslint-processor-vue-blocks": "^2.0.0", "globals": "^16.5.0", "jsonc-eslint-parser": "^2.4.2", "local-pkg": "^1.1.2", "parse-gitignore": "^2.0.0", "toml-eslint-parser": "^0.10.1", "vue-eslint-parser": "^10.2.0", "yaml-eslint-parser": "^1.3.2" }, "peerDependencies": { "@eslint-react/eslint-plugin": "^2.0.1", "@next/eslint-plugin-next": ">=15.0.0", "@prettier/plugin-xml": "^3.4.1", "@unocss/eslint-plugin": ">=0.50.0", "astro-eslint-parser": "^1.0.2", "eslint": "^9.10.0", "eslint-plugin-astro": "^1.2.0", "eslint-plugin-format": ">=0.1.0", "eslint-plugin-jsx-a11y": ">=6.10.2", "eslint-plugin-react-hooks": "^7.0.0", "eslint-plugin-react-refresh": "^0.4.19", "eslint-plugin-solid": "^0.14.3", "eslint-plugin-svelte": ">=2.35.1", "eslint-plugin-vuejs-accessibility": "^2.4.1", "prettier-plugin-astro": "^0.14.0", "prettier-plugin-slidev": "^1.0.5", "svelte-eslint-parser": ">=0.37.0" }, "optionalPeers": ["@eslint-react/eslint-plugin", "@next/eslint-plugin-next", "@prettier/plugin-xml", "@unocss/eslint-plugin", "astro-eslint-parser", "eslint-plugin-astro", "eslint-plugin-format", "eslint-plugin-jsx-a11y", "eslint-plugin-react-hooks", "eslint-plugin-react-refresh", "eslint-plugin-solid", "eslint-plugin-svelte", "eslint-plugin-vuejs-accessibility", "prettier-plugin-astro", "prettier-plugin-slidev", "svelte-eslint-parser"], "bin": { "eslint-config": "bin/index.mjs" } }, "sha512-+8GIMmOfrtAVXoqVK9sfovAlHPkp35ilntqZ6XloO/Rty36gOxaa8dvwCh8/eqwwIsloA/hDJo3Ef95TRbdyEg=="], + + "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], + + "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], + + "@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="], + + "@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="], + + "@es-joy/jsdoccomment": ["@es-joy/jsdoccomment@0.78.0", "", { "dependencies": { "@types/estree": "^1.0.8", "@typescript-eslint/types": "^8.46.4", "comment-parser": "1.4.1", "esquery": "^1.6.0", "jsdoc-type-pratt-parser": "~7.0.0" } }, "sha512-rQkU5u8hNAq2NVRzHnIUUvR6arbO0b6AOlvpTNS48CkiKSn/xtNfOzBK23JE4SiW89DgvU7GtxLVgV4Vn2HBAw=="], + + "@es-joy/resolve.exports": ["@es-joy/resolve.exports@1.2.0", "", {}, "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g=="], + + "@eslint-community/eslint-plugin-eslint-comments": ["@eslint-community/eslint-plugin-eslint-comments@4.5.0", "", { "dependencies": { "escape-string-regexp": "^4.0.0", "ignore": "^5.2.4" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/compat": ["@eslint/compat@1.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0" }, "peerDependencies": { "eslint": "^8.40 || 9" }, "optionalPeers": ["eslint"] }, "sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="], + + "@eslint/js": ["@eslint/js@9.39.2", "", {}, "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA=="], + + "@eslint/markdown": ["@eslint/markdown@7.5.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "@eslint/plugin-kit": "^0.4.1", "github-slugger": "^2.0.0", "mdast-util-from-markdown": "^2.0.2", "mdast-util-frontmatter": "^2.0.1", "mdast-util-gfm": "^3.1.0", "micromark-extension-frontmatter": "^2.0.0", "micromark-extension-gfm": "^3.0.0", "micromark-util-normalize-identifier": "^2.0.1" } }, "sha512-R8uZemG9dKTbru/DQRPblbJyXpObwKzo8rv1KYGGuPUPtjM4LXBYM9q5CIZAComzZupws3tWbDwam5AFpPLyJQ=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + "@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="], + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.0", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-z0Zhn/LmQ3yz91dEfd5QgS7DpSjA4pk+3z2++zKgn5L6iDFM9QapsVoAQSbKLvlrFsZk9+ru6yHHWNq2lCYJKQ=="], + "@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="], + "@pleaseai/code": ["@pleaseai/code@workspace:packages/code"], "@pleaseai/code-format": ["@pleaseai/code-format@workspace:packages/format"], @@ -86,26 +144,126 @@ "@pleaseai/dora": ["@pleaseai/dora@workspace:packages/dora"], + "@sindresorhus/base62": ["@sindresorhus/base62@1.0.0", "", {}, "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA=="], + + "@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@5.6.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.0", "@typescript-eslint/types": "^8.47.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", "picomatch": "^4.0.3" }, "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-JCs+MqoXfXrRPGbGmho/zGS/jMcn3ieKl/A8YImqib76C8kjgZwq5uUFzc30lJkMvcchuRn6/v8IApLxli3Jyw=="], + "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + "@types/node": ["@types/node@25.0.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA=="], + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.50.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/type-utils": "8.50.0", "@typescript-eslint/utils": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.50.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.50.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.50.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.50.0", "@typescript-eslint/types": "^8.50.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.50.0", "", { "dependencies": { "@typescript-eslint/types": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0" } }, "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.50.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.50.0", "", { "dependencies": { "@typescript-eslint/types": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0", "@typescript-eslint/utils": "8.50.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.50.0", "", {}, "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.50.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.50.0", "@typescript-eslint/tsconfig-utils": "8.50.0", "@typescript-eslint/types": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.50.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.50.0", "", { "dependencies": { "@typescript-eslint/types": "8.50.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q=="], + + "@vitest/eslint-plugin": ["@vitest/eslint-plugin@1.5.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "^8.46.1", "@typescript-eslint/utils": "^8.46.1" }, "peerDependencies": { "eslint": ">=8.57.0", "typescript": ">=5.0.0", "vitest": "*" }, "optionalPeers": ["typescript", "vitest"] }, "sha512-2t1F2iecXB/b1Ox4U137lhD3chihEE3dRVtu3qMD35tc6UqUjg1VGRJoS1AkFKwpT8zv8OQInzPQO06hrRkeqw=="], + + "@vue/compiler-core": ["@vue/compiler-core@3.5.25", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/shared": "3.5.25", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw=="], + + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.25", "", { "dependencies": { "@vue/compiler-core": "3.5.25", "@vue/shared": "3.5.25" } }, "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q=="], + + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.25", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/compiler-core": "3.5.25", "@vue/compiler-dom": "3.5.25", "@vue/compiler-ssr": "3.5.25", "@vue/shared": "3.5.25", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag=="], + + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.25", "", { "dependencies": { "@vue/compiler-dom": "3.5.25", "@vue/shared": "3.5.25" } }, "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A=="], + + "@vue/shared": ["@vue/shared@3.5.25", "", {}, "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], - "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="], + + "are-docs-informative": ["are-docs-informative@0.0.2", "", {}, "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.9", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-V8fbOCSeOFvlDj7LLChUcqbZrdKD9RU/VR260piF1790vT0mfLSwGc/Qzxv3IqiTukOpNtItePa0HBpMAj7MDg=="], + "body-parser": ["body-parser@2.2.1", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw=="], + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + + "builtin-modules": ["builtin-modules@5.0.0", "", {}, "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="], + "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001760", "", {}, "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "change-case": ["change-case@5.4.4", "", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], + + "clean-regexp": ["clean-regexp@1.0.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "comment-parser": ["comment-parser@1.4.1", "", {}, "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], @@ -114,28 +272,116 @@ "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + "core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="], + "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "diff-sequences": ["diff-sequences@27.5.1", "", {}, "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ=="], + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "electron-to-chromium": ["electron-to-chromium@1.5.267", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="], + + "empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="], + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], + + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="], + + "eslint-compat-utils": ["eslint-compat-utils@0.6.5", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ=="], + + "eslint-config-flat-gitignore": ["eslint-config-flat-gitignore@2.1.0", "", { "dependencies": { "@eslint/compat": "^1.2.5" }, "peerDependencies": { "eslint": "^9.5.0" } }, "sha512-cJzNJ7L+psWp5mXM7jBX+fjHtBvvh06RBlcweMhKD8jWqQw0G78hOW5tpVALGHGFPsBV+ot2H+pdDGJy6CV8pA=="], + + "eslint-flat-config-utils": ["eslint-flat-config-utils@2.1.4", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-bEnmU5gqzS+4O+id9vrbP43vByjF+8KOs+QuuV4OlqAuXmnRW2zfI/Rza1fQvdihQ5h4DUo0NqFAiViD4mSrzQ=="], + + "eslint-json-compat-utils": ["eslint-json-compat-utils@0.2.1", "", { "dependencies": { "esquery": "^1.6.0" }, "peerDependencies": { "eslint": "*", "jsonc-eslint-parser": "^2.4.0" } }, "sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg=="], + + "eslint-merge-processors": ["eslint-merge-processors@2.0.0", "", { "peerDependencies": { "eslint": "*" } }, "sha512-sUuhSf3IrJdGooquEUB5TNpGNpBoQccbnaLHsb1XkBLUPPqCNivCpY05ZcpCOiV9uHwO2yxXEWVczVclzMxYlA=="], + + "eslint-plugin-antfu": ["eslint-plugin-antfu@3.1.1", "", { "peerDependencies": { "eslint": "*" } }, "sha512-7Q+NhwLfHJFvopI2HBZbSxWXngTwBLKxW1AGXLr2lEGxcEIK/AsDs8pn8fvIizl5aZjBbVbVK5ujmMpBe4Tvdg=="], + + "eslint-plugin-command": ["eslint-plugin-command@3.4.0", "", { "dependencies": { "@es-joy/jsdoccomment": "^0.78.0" }, "peerDependencies": { "eslint": "*" } }, "sha512-EW4eg/a7TKEhG0s5IEti72kh3YOTlnhfFNuctq5WnB1fst37/IHTd5OkD+vnlRf3opTvUcSRihAateP6bT5ZcA=="], + + "eslint-plugin-es-x": ["eslint-plugin-es-x@7.8.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.1.2", "@eslint-community/regexpp": "^4.11.0", "eslint-compat-utils": "^0.5.1" }, "peerDependencies": { "eslint": ">=8" } }, "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ=="], + + "eslint-plugin-import-lite": ["eslint-plugin-import-lite@0.3.1", "", { "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=4.5" }, "optionalPeers": ["typescript"] }, "sha512-9+EByHZatvWFn/lRsUja5pwah0U5lhOA6SXqTI/iIzoIJHMgmsHUHEaTlLzKU/ukyCRwKEU5E92aUURPgVWq0A=="], + + "eslint-plugin-jsdoc": ["eslint-plugin-jsdoc@61.5.0", "", { "dependencies": { "@es-joy/jsdoccomment": "~0.76.0", "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.1", "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", "espree": "^10.4.0", "esquery": "^1.6.0", "html-entities": "^2.6.0", "object-deep-merge": "^2.0.0", "parse-imports-exports": "^0.2.4", "semver": "^7.7.3", "spdx-expression-parse": "^4.0.0", "to-valid-identifier": "^1.0.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-PR81eOGq4S7diVnV9xzFSBE4CDENRQGP0Lckkek8AdHtbj+6Bm0cItwlFnxsLFriJHspiE3mpu8U20eODyToIg=="], + + "eslint-plugin-jsonc": ["eslint-plugin-jsonc@2.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.5.1", "diff-sequences": "^27.5.1", "eslint-compat-utils": "^0.6.4", "eslint-json-compat-utils": "^0.2.1", "espree": "^9.6.1 || ^10.3.0", "graphemer": "^1.4.0", "jsonc-eslint-parser": "^2.4.0", "natural-compare": "^1.4.0", "synckit": "^0.6.2 || ^0.7.3 || ^0.11.5" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-HttlxdNG5ly3YjP1cFMP62R4qKLxJURfBZo2gnMY+yQojZxkLyOpY1H1KRTKBmvQeSG9pIpSGEhDjE17vvYosg=="], + + "eslint-plugin-n": ["eslint-plugin-n@17.23.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.5.0", "enhanced-resolve": "^5.17.1", "eslint-plugin-es-x": "^7.8.0", "get-tsconfig": "^4.8.1", "globals": "^15.11.0", "globrex": "^0.1.2", "ignore": "^5.3.2", "semver": "^7.6.3", "ts-declaration-location": "^1.0.6" }, "peerDependencies": { "eslint": ">=8.23.0" } }, "sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A=="], + + "eslint-plugin-no-only-tests": ["eslint-plugin-no-only-tests@3.3.0", "", {}, "sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q=="], + + "eslint-plugin-perfectionist": ["eslint-plugin-perfectionist@4.15.1", "", { "dependencies": { "@typescript-eslint/types": "^8.38.0", "@typescript-eslint/utils": "^8.38.0", "natural-orderby": "^5.0.0" }, "peerDependencies": { "eslint": ">=8.45.0" } }, "sha512-MHF0cBoOG0XyBf7G0EAFCuJJu4I18wy0zAoT1OHfx2o6EOx1EFTIzr2HGeuZa1kDcusoX0xJ9V7oZmaeFd773Q=="], + + "eslint-plugin-pnpm": ["eslint-plugin-pnpm@1.4.3", "", { "dependencies": { "empathic": "^2.0.0", "jsonc-eslint-parser": "^2.4.2", "pathe": "^2.0.3", "pnpm-workspace-yaml": "1.4.3", "tinyglobby": "^0.2.15", "yaml": "^2.8.2", "yaml-eslint-parser": "^1.3.2" }, "peerDependencies": { "eslint": "^9.0.0" } }, "sha512-wdWrkWN5mxRgEADkQvxwv0xA+0++/hYDD5OyXTL6UqPLUPdcCFQJO61NO7IKhEqb3GclWs02OoFs1METN+a3zQ=="], + + "eslint-plugin-regexp": ["eslint-plugin-regexp@2.10.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", "comment-parser": "^1.4.0", "jsdoc-type-pratt-parser": "^4.0.0", "refa": "^0.12.1", "regexp-ast-analysis": "^0.7.1", "scslre": "^0.3.0" }, "peerDependencies": { "eslint": ">=8.44.0" } }, "sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng=="], + + "eslint-plugin-toml": ["eslint-plugin-toml@0.12.0", "", { "dependencies": { "debug": "^4.1.1", "eslint-compat-utils": "^0.6.0", "lodash": "^4.17.19", "toml-eslint-parser": "^0.10.0" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-+/wVObA9DVhwZB1nG83D2OAQRrcQZXy+drqUnFJKymqnmbnbfg/UPmEMCKrJNcEboUGxUjYrJlgy+/Y930mURQ=="], + + "eslint-plugin-unicorn": ["eslint-plugin-unicorn@62.0.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "@eslint-community/eslint-utils": "^4.9.0", "@eslint/plugin-kit": "^0.4.0", "change-case": "^5.4.4", "ci-info": "^4.3.1", "clean-regexp": "^1.0.0", "core-js-compat": "^3.46.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", "globals": "^16.4.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.13.0", "semver": "^7.7.3", "strip-indent": "^4.1.1" }, "peerDependencies": { "eslint": ">=9.38.0" } }, "sha512-HIlIkGLkvf29YEiS/ImuDZQbP12gWyx5i3C6XrRxMvVdqMroCI9qoVYCoIl17ChN+U89pn9sVwLxhIWj5nEc7g=="], + + "eslint-plugin-unused-imports": ["eslint-plugin-unused-imports@4.3.0", "", { "peerDependencies": { "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", "eslint": "^9.0.0 || ^8.0.0" }, "optionalPeers": ["@typescript-eslint/eslint-plugin"] }, "sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA=="], + + "eslint-plugin-vue": ["eslint-plugin-vue@10.6.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^7.1.0", "semver": "^7.6.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "vue-eslint-parser": "^10.0.0" }, "optionalPeers": ["@stylistic/eslint-plugin", "@typescript-eslint/parser"] }, "sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw=="], + + "eslint-plugin-yml": ["eslint-plugin-yml@1.19.1", "", { "dependencies": { "debug": "^4.3.2", "diff-sequences": "^27.5.1", "escape-string-regexp": "4.0.0", "eslint-compat-utils": "^0.6.0", "natural-compare": "^1.4.0", "yaml-eslint-parser": "^1.2.1" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-bYkOxyEiXh9WxUhVYPELdSHxGG5pOjCSeJOVkfdIyj6tuiHDxrES2WAW1dBxn3iaZQey57XflwLtCYRcNPOiOg=="], + + "eslint-processor-vue-blocks": ["eslint-processor-vue-blocks@2.0.0", "", { "peerDependencies": { "@vue/compiler-sfc": "^3.3.0", "eslint": ">=9.0.0" } }, "sha512-u4W0CJwGoWY3bjXAuFpc/b6eK3NQEI8MoeW7ritKj3G3z/WtHrKjkqf+wk8mPEy5rlMGS+k6AZYOw2XBoN/02Q=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], @@ -146,12 +392,34 @@ "express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="], + "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fault": ["fault@2.0.1", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "find-up-simple": ["find-up-simple@1.0.1", "", {}, "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], @@ -162,76 +430,294 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], + + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="], + + "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], "hono": ["hono@4.11.1", "", {}, "sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg=="], + "html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], "iconv-lite": ["iconv-lite@0.7.1", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw=="], + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "is-builtin-module": ["is-builtin-module@5.0.0", "", { "dependencies": { "builtin-modules": "^5.0.0" } }, "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], - "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@4.8.0", "", {}, "sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "jsonc-eslint-parser": ["jsonc-eslint-parser@2.4.2", "", { "dependencies": { "acorn": "^8.5.0", "eslint-visitor-keys": "^3.0.0", "espree": "^9.0.0", "semver": "^7.3.5" } }, "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="], + + "mdast-util-frontmatter": ["mdast-util-frontmatter@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "escape-string-regexp": "^5.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0" } }, "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-frontmatter": ["micromark-extension-frontmatter@2.0.0", "", { "dependencies": { "fault": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "natural-orderby": ["natural-orderby@5.0.0", "", {}, "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg=="], + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], + + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "object-deep-merge": ["object-deep-merge@2.0.0", "", {}, "sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-gitignore": ["parse-gitignore@2.0.0", "", {}, "sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog=="], + + "parse-imports-exports": ["parse-imports-exports@0.2.4", "", { "dependencies": { "parse-statements": "1.0.11" } }, "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ=="], + + "parse-statements": ["parse-statements@1.0.11", "", {}, "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], + + "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], + + "pnpm-workspace-yaml": ["pnpm-workspace-yaml@1.4.3", "", { "dependencies": { "yaml": "^2.8.2" } }, "sha512-Q8B3SWuuISy/Ciag4DFP7MCrJX07wfaekcqD2o/msdIj4x8Ql3bZ/NEKOXV7mTVh7m1YdiFWiMi9xH+0zuEGHw=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "refa": ["refa@0.12.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.8.0" } }, "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g=="], + + "regexp-ast-analysis": ["regexp-ast-analysis@0.7.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.8.0", "refa": "^0.12.1" } }, "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A=="], + + "regexp-tree": ["regexp-tree@0.1.27", "", { "bin": { "regexp-tree": "bin/regexp-tree" } }, "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA=="], + + "regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + "reserved-identifiers": ["reserved-identifiers@1.2.0", "", {}, "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "scslre": ["scslre@0.3.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.8.0", "refa": "^0.12.0", "regexp-ast-analysis": "^0.7.0" } }, "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], @@ -250,10 +736,42 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], + + "spdx-expression-parse": ["spdx-expression-parse@4.0.0", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ=="], + + "spdx-license-ids": ["spdx-license-ids@3.0.22", "", {}, "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "strip-indent": ["strip-indent@4.1.1", "", {}, "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="], + + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "to-valid-identifier": ["to-valid-identifier@1.0.0", "", { "dependencies": { "@sindresorhus/base62": "^1.0.0", "reserved-identifiers": "^1.0.0" } }, "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "toml-eslint-parser": ["toml-eslint-parser@0.10.1", "", { "dependencies": { "eslint-visitor-keys": "^3.0.0" } }, "sha512-9mjy3frhioGIVGcwamlVlUyJ9x+WHw/TXiz9R4YOlmsIuBN43r9Dp8HZ35SF9EKjHrn3BUZj04CF+YqZ2oJ+7w=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "ts-declaration-location": ["ts-declaration-location@1.0.7", "", { "dependencies": { "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": ">=4.0.0" } }, "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA=="], + "turbo": ["turbo@2.6.3", "", { "optionalDependencies": { "turbo-darwin-64": "2.6.3", "turbo-darwin-arm64": "2.6.3", "turbo-linux-64": "2.6.3", "turbo-linux-arm64": "2.6.3", "turbo-windows-64": "2.6.3", "turbo-windows-arm64": "2.6.3" }, "bin": { "turbo": "bin/turbo" } }, "sha512-bf6YKUv11l5Xfcmg76PyWoy/e2vbkkxFNBGJSnfdSXQC33ZiUfutYh6IXidc5MhsnrFkWfdNNLyaRk+kHMLlwA=="], "turbo-darwin-64": ["turbo-darwin-64@2.6.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlJJDc1CQ7SK5Y5qnl7AzpkvKSnpkfPmnA+HeU/sgny3oHZckPV2776ebO2M33CYDSor7+8HQwaodY++IINhYg=="], @@ -268,28 +786,104 @@ "turbo-windows-arm64": ["turbo-windows-arm64@2.6.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-dDHVKpSeukah3VsI/xMEKeTnV9V9cjlpFSUs4bmsUiLu3Yv2ENlgVEZv65wxbeE0bh0jjpmElDT+P1KaCxArQQ=="], + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], "vscode-jsonrpc": ["vscode-jsonrpc@8.2.1", "", {}, "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ=="], "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + "vue-eslint-parser": ["vue-eslint-parser@10.2.0", "", { "dependencies": { "debug": "^4.4.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.6.0", "semver": "^7.6.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "xml-name-validator": ["xml-name-validator@4.0.0", "", {}, "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="], + "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + "yaml-eslint-parser": ["yaml-eslint-parser@1.3.2", "", { "dependencies": { "eslint-visitor-keys": "^3.0.0", "yaml": "^2.0.0" } }, "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@es-joy/jsdoccomment/jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@7.0.0", "", {}, "sha512-c7YbokssPOSHmqTbSAmTtnVgAVa/7lumWNYqomgd5KOMyPrRve2anx6lonfOsXEQacqF9FKVUj7bLg4vRSvdYA=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "ajv-formats/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "clean-regexp/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "eslint-plugin-es-x/eslint-compat-utils": ["eslint-compat-utils@0.5.1", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q=="], + + "eslint-plugin-jsdoc/@es-joy/jsdoccomment": ["@es-joy/jsdoccomment@0.76.0", "", { "dependencies": { "@types/estree": "^1.0.8", "@typescript-eslint/types": "^8.46.0", "comment-parser": "1.4.1", "esquery": "^1.6.0", "jsdoc-type-pratt-parser": "~6.10.0" } }, "sha512-g+RihtzFgGTx2WYCuTHbdOXJeAlGnROws0TeALx9ow/ZmOROOZkVg5wp/B44n0WJgI4SQFP1eWM2iRPlU2Y14w=="], + + "eslint-plugin-n/globals": ["globals@15.15.0", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="], + + "jsonc-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "jsonc-eslint-parser/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + + "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "mdast-util-frontmatter/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + + "toml-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "yaml-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "eslint-plugin-jsdoc/@es-joy/jsdoccomment/jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@6.10.0", "", {}, "sha512-+LexoTRyYui5iOhJGn13N9ZazL23nAHGkXsa1p/C8yeq79WRfLBag6ZZ0FQG2aRoc9yfo59JT9EYCQonOkHKkQ=="], + + "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], } } diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..b01dac4 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,10 @@ +import antfu from '@antfu/eslint-config' + +export default antfu({ + typescript: true, + ignores: [ + 'dist', + 'npm', + 'node_modules', + ], +}) diff --git a/package.json b/package.json index 33090e5..0a24105 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,20 @@ { "name": "code-please", + "type": "module", "version": "0.1.2", + "packageManager": "bun@1.3.3", "description": "Auto-format and type-check hooks for AI coding", - "type": "module", "workspaces": [ "packages/*" ], + "engines": { + "bun": ">=1.0.0" + }, "scripts": { "start": "bun run packages/code/src/cli.ts", "dev": "turbo dev", + "lint": "eslint --cache .", + "lint:fix": "eslint --cache --fix .", "typecheck": "turbo typecheck", "test": "turbo test", "build": "turbo build", @@ -23,12 +29,10 @@ "zod": "^3.25.0" }, "devDependencies": { + "@antfu/eslint-config": "^6.7.1", "@types/bun": "latest", + "eslint": "^9.39.2", "turbo": "^2.6.3", "typescript": "^5.7.0" - }, - "packageManager": "bun@1.3.3", - "engines": { - "bun": ">=1.0.0" } } diff --git a/packages/code/package.json b/packages/code/package.json index e4ab366..543694e 100644 --- a/packages/code/package.json +++ b/packages/code/package.json @@ -1,12 +1,29 @@ { "name": "@pleaseai/code", + "type": "module", "version": "0.1.1", "description": "Auto-format and type-check hooks for AI coding", - "type": "module", + "author": "Minsu Lee", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/pleaseai/code" + }, + "keywords": [ + "claude-code", + "hooks", + "formatter", + "lsp", + "ai-coding" + ], "main": "src/index.ts", "bin": { "code": "src/cli.ts" }, + "files": [ + "hooks", + "src" + ], "scripts": { "dev": "bun --watch run src/cli.ts", "build": "echo 'Build handled by root build:npm'", @@ -20,22 +37,5 @@ "devDependencies": { "@types/bun": "latest", "typescript": "^5.7.0" - }, - "files": [ - "src", - "hooks" - ], - "keywords": [ - "claude-code", - "hooks", - "formatter", - "lsp", - "ai-coding" - ], - "author": "Minsu Lee", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pleaseai/code" } } diff --git a/packages/code/src/__tests__/cli.test.ts b/packages/code/src/__tests__/cli.test.ts index 4a9ecfd..6986eaa 100644 --- a/packages/code/src/__tests__/cli.test.ts +++ b/packages/code/src/__tests__/cli.test.ts @@ -1,40 +1,40 @@ -import { describe, test, expect } from "bun:test" -import { $ } from "bun" -import path from "node:path" +import path from 'node:path' +import { $ } from 'bun' +import { describe, expect, test } from 'bun:test' -const CLI_PATH = path.resolve(import.meta.dir, "../cli.ts") -const PROJECT_DIR = path.resolve(import.meta.dir, "../../../..") +const CLI_PATH = path.resolve(import.meta.dir, '../cli.ts') +const PROJECT_DIR = path.resolve(import.meta.dir, '../../../..') -describe("CLI", () => { - describe("version", () => { - test("prints version", async () => { +describe('CLI', () => { + describe('version', () => { + test('prints version', async () => { const result = await $`bun run ${CLI_PATH} version`.text() expect(result).toMatch(/code \d+\.\d+\.\d+/) }) }) - describe("help", () => { - test("prints help", async () => { + describe('help', () => { + test('prints help', async () => { const result = await $`bun run ${CLI_PATH} help`.text() - expect(result).toContain("Usage:") - expect(result).toContain("format") - expect(result).toContain("lsp") + expect(result).toContain('Usage:') + expect(result).toContain('format') + expect(result).toContain('lsp') }) }) - describe("format --stdin", () => { - test("handles valid JSON input", async () => { + describe('format --stdin', () => { + test('handles valid JSON input', async () => { const input = JSON.stringify({ tool_input: { - file_path: "/tmp/test.ts", - file_contents: "const x = 1", + file_path: '/tmp/test.ts', + file_contents: 'const x = 1', }, }) - const proc = Bun.spawn(["bun", "run", CLI_PATH, "format", "--stdin"], { - stdin: "pipe", - stdout: "pipe", - stderr: "pipe", + const proc = Bun.spawn(['bun', 'run', CLI_PATH, 'format', '--stdin'], { + stdin: 'pipe', + stdout: 'pipe', + stderr: 'pipe', cwd: PROJECT_DIR, }) @@ -46,15 +46,15 @@ describe("CLI", () => { expect([0, 1]).toContain(exitCode) }) - test("handles missing file_path", async () => { + test('handles missing file_path', async () => { const input = JSON.stringify({ tool_input: {}, }) - const proc = Bun.spawn(["bun", "run", CLI_PATH, "format", "--stdin"], { - stdin: "pipe", - stdout: "pipe", - stderr: "pipe", + const proc = Bun.spawn(['bun', 'run', CLI_PATH, 'format', '--stdin'], { + stdin: 'pipe', + stdout: 'pipe', + stderr: 'pipe', }) proc.stdin.write(input) @@ -64,17 +64,17 @@ describe("CLI", () => { const stderr = await new Response(proc.stderr).text() expect(exitCode).toBe(1) - expect(stderr).toContain("file_path") + expect(stderr).toContain('file_path') }) - test("handles invalid JSON input", async () => { - const proc = Bun.spawn(["bun", "run", CLI_PATH, "format", "--stdin"], { - stdin: "pipe", - stdout: "pipe", - stderr: "pipe", + test('handles invalid JSON input', async () => { + const proc = Bun.spawn(['bun', 'run', CLI_PATH, 'format', '--stdin'], { + stdin: 'pipe', + stdout: 'pipe', + stderr: 'pipe', }) - proc.stdin.write("not valid json") + proc.stdin.write('not valid json') proc.stdin.end() const exitCode = await proc.exited @@ -82,18 +82,18 @@ describe("CLI", () => { }) }) - describe("lsp --stdin", () => { - test("handles valid JSON input", async () => { + describe('lsp --stdin', () => { + test('handles valid JSON input', async () => { const input = JSON.stringify({ tool_input: { - file_path: path.join(PROJECT_DIR, "packages/code/src/cli.ts"), + file_path: path.join(PROJECT_DIR, 'packages/code/src/cli.ts'), }, }) - const proc = Bun.spawn(["bun", "run", CLI_PATH, "lsp", "--stdin"], { - stdin: "pipe", - stdout: "pipe", - stderr: "pipe", + const proc = Bun.spawn(['bun', 'run', CLI_PATH, 'lsp', '--stdin'], { + stdin: 'pipe', + stdout: 'pipe', + stderr: 'pipe', cwd: PROJECT_DIR, }) @@ -108,18 +108,18 @@ describe("CLI", () => { // May exit with various codes depending on LSP availability // 0 = success, non-zero = error or killed - expect(typeof exitCode).toBe("number") + expect(typeof exitCode).toBe('number') }, 10000) // Increase test timeout - test("handles missing file_path", async () => { + test('handles missing file_path', async () => { const input = JSON.stringify({ tool_input: {}, }) - const proc = Bun.spawn(["bun", "run", CLI_PATH, "lsp", "--stdin"], { - stdin: "pipe", - stdout: "pipe", - stderr: "pipe", + const proc = Bun.spawn(['bun', 'run', CLI_PATH, 'lsp', '--stdin'], { + stdin: 'pipe', + stdout: 'pipe', + stderr: 'pipe', }) proc.stdin.write(input) @@ -129,22 +129,22 @@ describe("CLI", () => { const stderr = await new Response(proc.stderr).text() expect(exitCode).toBe(1) - expect(stderr).toContain("file_path") + expect(stderr).toContain('file_path') }) }) - describe("unknown command", () => { - test("prints error for unknown command", async () => { - const proc = Bun.spawn(["bun", "run", CLI_PATH, "unknown"], { - stdout: "pipe", - stderr: "pipe", + describe('unknown command', () => { + test('prints error for unknown command', async () => { + const proc = Bun.spawn(['bun', 'run', CLI_PATH, 'unknown'], { + stdout: 'pipe', + stderr: 'pipe', }) const exitCode = await proc.exited const stderr = await new Response(proc.stderr).text() expect(exitCode).toBe(1) - expect(stderr).toContain("Unknown command") + expect(stderr).toContain('Unknown command') }) }) }) diff --git a/packages/code/src/cli.ts b/packages/code/src/cli.ts index cb956fe..a1d4d11 100644 --- a/packages/code/src/cli.ts +++ b/packages/code/src/cli.ts @@ -12,9 +12,11 @@ * code lsp --stdin Read hook input from stdin */ -import { Format } from "@pleaseai/code-format" -import { runLSPDiagnostics } from "./hooks/lsp" -import pkg from "../package.json" +import { Buffer } from 'node:buffer' +import process from 'node:process' +import { Format } from '@pleaseai/code-format' +import pkg from '../package.json' +import { runLSPDiagnostics } from './hooks/lsp' const VERSION = pkg.version @@ -42,18 +44,20 @@ function parseArgs(argv: string[]): ParsedArgs { for (let i = 0; i < args.length; i++) { const arg = args[i]! - if (arg.startsWith("--")) { - const [key, value] = arg.slice(2).split("=") + if (arg.startsWith('--')) { + const [key, value] = arg.slice(2).split('=') flags[key!] = value ?? true - } else if (arg.startsWith("-")) { + } + else if (arg.startsWith('-')) { flags[arg.slice(1)] = true - } else { + } + else { positional.push(arg) } } return { - command: positional[0] ?? "help", + command: positional[0] ?? 'help', args: positional.slice(1), flags, } @@ -64,16 +68,17 @@ async function readStdinJson(): Promise { for await (const chunk of Bun.stdin.stream()) { chunks.push(chunk as Buffer) } - const text = Buffer.concat(chunks).toString("utf-8").trim() + const text = Buffer.concat(chunks).toString('utf-8').trim() if (!text) { - throw new Error("No input received from stdin") + throw new Error('No input received from stdin') } try { return JSON.parse(text) - } catch { - throw new Error("Invalid JSON input from stdin") + } + catch { + throw new Error('Invalid JSON input from stdin') } } @@ -86,12 +91,14 @@ async function formatCommand(filePath: string, projectDir: string, isHookMode: b if (success) { console.log(JSON.stringify({ suppressOutput: true })) } - } else { + } + else { // CLI mode: human-readable output if (success) { console.log(JSON.stringify({ success: true, file: filePath })) - } else { - console.log(JSON.stringify({ success: false, file: filePath, reason: "no formatter" })) + } + else { + console.log(JSON.stringify({ success: false, file: filePath, reason: 'no formatter' })) } } } @@ -104,11 +111,12 @@ async function lspCommand(filePath: string, projectDir: string, isHookMode: bool // Hook mode: output as additionalContext for Claude Code console.log(JSON.stringify({ hookSpecificOutput: { - hookEventName: "PostToolUse", + hookEventName: 'PostToolUse', additionalContext: `[code lsp]: ${report}`, }, })) - } else { + } + else { // CLI mode: direct output console.log(report) process.exit(1) @@ -149,65 +157,65 @@ Environment: async function main(): Promise { const { command, args, flags } = parseArgs(process.argv) - const projectDir = - (flags["project"] as string) ?? - process.env["CODE_PROJECT_PATH"] ?? - process.cwd() + const projectDir + = (flags.project as string) + ?? process.env.CODE_PROJECT_PATH + ?? process.cwd() switch (command) { - case "format": { - const isHookMode = flags["stdin"] === true + case 'format': { + const isHookMode = flags.stdin === true let file = args[0] let dir = projectDir if (isHookMode) { const input = await readStdinJson() if (!input?.tool_input?.file_path) { - throw new Error("Missing file_path in tool_input") + throw new Error('Missing file_path in tool_input') } file = input.tool_input.file_path - dir = process.env["CLAUDE_PROJECT_DIR"] || input.cwd || projectDir + dir = process.env.CLAUDE_PROJECT_DIR || input.cwd || projectDir } if (!file) { - console.error("Usage: code format | code format --stdin") + console.error('Usage: code format | code format --stdin') process.exit(1) } await formatCommand(file, dir, isHookMode) break } - case "lsp": { - const isHookMode = flags["stdin"] === true + case 'lsp': { + const isHookMode = flags.stdin === true let file = args[0] let dir = projectDir if (isHookMode) { const input = await readStdinJson() if (!input?.tool_input?.file_path) { - throw new Error("Missing file_path in tool_input") + throw new Error('Missing file_path in tool_input') } file = input.tool_input.file_path - dir = process.env["CLAUDE_PROJECT_DIR"] || input.cwd || projectDir + dir = process.env.CLAUDE_PROJECT_DIR || input.cwd || projectDir } if (!file) { - console.error("Usage: code lsp | code lsp --stdin") + console.error('Usage: code lsp | code lsp --stdin') process.exit(1) } await lspCommand(file, dir, isHookMode) break } - case "version": - case "-v": - case "--version": + case 'version': + case '-v': + case '--version': versionCommand() break - case "help": - case "-h": - case "--help": + case 'help': + case '-h': + case '--help': helpCommand() break @@ -219,6 +227,6 @@ async function main(): Promise { } main().catch((error) => { - console.error("[code] Error:", error) + console.error('[code] Error:', error) process.exit(1) }) diff --git a/packages/code/src/hooks/index.ts b/packages/code/src/hooks/index.ts index 013b100..0d3688e 100644 --- a/packages/code/src/hooks/index.ts +++ b/packages/code/src/hooks/index.ts @@ -11,9 +11,9 @@ * This module re-exports utilities for use in hooks. */ -export { Format, Formatter } from "@pleaseai/code-format" export { - runLSPDiagnostics, - getDiagnostics, formatDiagnosticsReport, -} from "./lsp" + getDiagnostics, + runLSPDiagnostics, +} from './lsp' +export { Format, Formatter } from '@pleaseai/code-format' diff --git a/packages/code/src/hooks/lsp.ts b/packages/code/src/hooks/lsp.ts index d435f66..74c4c82 100644 --- a/packages/code/src/hooks/lsp.ts +++ b/packages/code/src/hooks/lsp.ts @@ -5,8 +5,9 @@ * Based on OpenCode LSP integration approach. */ -import path from "path" -import { LSPManager, type Diagnostic } from "@pleaseai/code-lsp" +import type { Diagnostic } from '@pleaseai/code-lsp' +import path from 'node:path' +import { LSPManager } from '@pleaseai/code-lsp' const DIAGNOSTICS_MAX_DISPLAY = 5 const DIAGNOSTICS_WAIT_MS = 1500 @@ -14,11 +15,11 @@ const DIAGNOSTICS_WAIT_MS = 1500 /** * Diagnostic severity mapping */ -const SEVERITY_MAP: Record = { - 1: { icon: "✗", name: "error" }, - 2: { icon: "⚠", name: "warning" }, - 3: { icon: "ℹ", name: "info" }, - 4: { icon: "💡", name: "hint" }, +const SEVERITY_MAP: Record = { + 1: { icon: '✗', name: 'error' }, + 2: { icon: '⚠', name: 'warning' }, + 3: { icon: 'ℹ', name: 'info' }, + 4: { icon: '💡', name: 'hint' }, } /** @@ -27,7 +28,7 @@ const SEVERITY_MAP: Record = { function formatDiagnostic( diagnostic: Diagnostic, filePath: string, - projectDir: string + projectDir: string, ): string { const severity = diagnostic.severity ?? 1 const { icon } = SEVERITY_MAP[severity] ?? SEVERITY_MAP[1]! @@ -35,8 +36,8 @@ function formatDiagnostic( const line = diagnostic.range.start.line + 1 const col = diagnostic.range.start.character + 1 - const code = diagnostic.code ? ` [${diagnostic.code}]` : "" - const message = diagnostic.message.split("\n")[0] // First line only + const code = diagnostic.code ? ` [${diagnostic.code}]` : '' + const message = diagnostic.message.split('\n')[0] // First line only return ` ${icon} ${relativePath}:${line}:${col}${code}: ${message}` } @@ -46,7 +47,7 @@ function formatDiagnostic( */ export function formatDiagnosticsReport( diagnostics: Record, - projectDir: string + projectDir: string, ): string | null { let errorCount = 0 let warningCount = 0 @@ -55,8 +56,10 @@ export function formatDiagnosticsReport( for (const [filePath, diags] of Object.entries(diagnostics)) { for (const diag of diags) { const severity = diag.severity ?? 1 - if (severity === 1) errorCount++ - if (severity === 2) warningCount++ + if (severity === 1) + errorCount++ + if (severity === 2) + warningCount++ if (lines.length < DIAGNOSTICS_MAX_DISPLAY) { lines.push(formatDiagnostic(diag, filePath, projectDir)) @@ -65,13 +68,16 @@ export function formatDiagnosticsReport( } const total = errorCount + warningCount - if (total === 0) return null + if (total === 0) + return null // Build summary line const parts: string[] = [] - if (errorCount > 0) parts.push(`${errorCount} error${errorCount > 1 ? "s" : ""}`) - if (warningCount > 0) parts.push(`${warningCount} warning${warningCount > 1 ? "s" : ""}`) - const summary = `✗ ${parts.join(", ")} found` + if (errorCount > 0) + parts.push(`${errorCount} error${errorCount > 1 ? 's' : ''}`) + if (warningCount > 0) + parts.push(`${warningCount} warning${warningCount > 1 ? 's' : ''}`) + const summary = `✗ ${parts.join(', ')} found` // Add overflow indicator const overflow = total - lines.length @@ -79,7 +85,7 @@ export function formatDiagnosticsReport( lines.push(` ... and ${overflow} more`) } - return [summary, ...lines].join("\n") + return [summary, ...lines].join('\n') } /** @@ -87,7 +93,7 @@ export function formatDiagnosticsReport( */ export async function getDiagnostics( filePath: string, - projectDir: string + projectDir: string, ): Promise> { const manager = new LSPManager(projectDir) @@ -96,10 +102,11 @@ export async function getDiagnostics( await manager.touchFile(filePath, true) // Small additional delay for diagnostics to settle - await new Promise((resolve) => setTimeout(resolve, DIAGNOSTICS_WAIT_MS)) + await new Promise(resolve => setTimeout(resolve, DIAGNOSTICS_WAIT_MS)) return await manager.diagnostics() - } finally { + } + finally { await manager.shutdown() } } @@ -109,8 +116,8 @@ export async function getDiagnostics( */ export async function runLSPDiagnostics( filePath: string, - projectDir: string + projectDir: string, ): Promise { const diagnostics = await getDiagnostics(filePath, projectDir) return formatDiagnosticsReport(diagnostics, projectDir) -} \ No newline at end of file +} diff --git a/packages/code/src/index.ts b/packages/code/src/index.ts index 457a27b..9661a31 100644 --- a/packages/code/src/index.ts +++ b/packages/code/src/index.ts @@ -4,5 +4,5 @@ * Provides auto-formatting and LSP diagnostics for Claude Code hooks. */ -export { Format, Formatter, loadConfig, type Config, type FormatterConfig } from "@pleaseai/code-format" -export { LSPManager, formatDiagnostic } from "@pleaseai/code-lsp" +export { type Config, Format, Formatter, type FormatterConfig, loadConfig } from '@pleaseai/code-format' +export { formatDiagnostic, LSPManager } from '@pleaseai/code-lsp' diff --git a/packages/code/src/launcher/cli.ts b/packages/code/src/launcher/cli.ts index 5373257..afa6ea8 100644 --- a/packages/code/src/launcher/cli.ts +++ b/packages/code/src/launcher/cli.ts @@ -5,16 +5,17 @@ * It detects the current platform and executes the appropriate native binary. */ -import { execFileSync, execSync } from "node:child_process" -import path from "node:path" -import fs from "node:fs" -import { fileURLToPath } from "node:url" +import { execFileSync, execSync } from 'node:child_process' +import fs from 'node:fs' +import path from 'node:path' +import process from 'node:process' +import { fileURLToPath } from 'node:url' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) -const PACKAGE_SCOPE = "@pleaseai" -const PACKAGE_NAME = "code" +const PACKAGE_SCOPE = '@pleaseai' +const PACKAGE_NAME = 'code' /** * Detect if running on musl libc (Alpine Linux, etc.) @@ -23,11 +24,12 @@ const PACKAGE_NAME = "code" function isMusl(): boolean { // Method 1: Check /usr/bin/ldd for musl try { - const lddContent = fs.readFileSync("/usr/bin/ldd", "utf-8") - if (lddContent.includes("musl")) { + const lddContent = fs.readFileSync('/usr/bin/ldd', 'utf-8') + if (lddContent.includes('musl')) { return true } - } catch { + } + catch { // File doesn't exist or can't be read } @@ -37,17 +39,19 @@ function isMusl(): boolean { if (report?.header?.glibcVersionRuntime) { return false // Has glibc, not musl } - } catch { + } + catch { // process.report not available } // Method 3: Run ldd --version and check output try { - const output = execSync("ldd --version 2>&1", { encoding: "utf-8" }) - if (output.includes("musl")) { + const output = execSync('ldd --version 2>&1', { encoding: 'utf-8' }) + if (output.includes('musl')) { return true } - } catch { + } + catch { // ldd not available or failed } @@ -58,16 +62,16 @@ function getTarget(): string { const platform = process.platform const arch = process.arch - if (platform === "darwin") { + if (platform === 'darwin') { return `darwin-${arch}` } - if (platform === "win32") { + if (platform === 'win32') { return `win32-${arch}` } - if (platform === "linux") { - const libc = isMusl() ? "musl" : "glibc" + if (platform === 'linux') { + const libc = isMusl() ? 'musl' : 'glibc' return `linux-${arch}-${libc}` } @@ -77,16 +81,16 @@ function getTarget(): string { function getBinaryPath(): string { const target = getTarget() const packageName = `${PACKAGE_SCOPE}/${PACKAGE_NAME}-${target}` - const binaryName = process.platform === "win32" ? `${PACKAGE_NAME}.exe` : PACKAGE_NAME + const binaryName = process.platform === 'win32' ? `${PACKAGE_NAME}.exe` : PACKAGE_NAME // Try to find the binary in node_modules const paths = [ // Hoisted (npm/yarn) - path.join(__dirname, "..", "..", packageName.replace("/", path.sep), binaryName), + path.join(__dirname, '..', '..', packageName.replace('/', path.sep), binaryName), // Not hoisted - path.join(__dirname, "..", "node_modules", packageName.replace("/", path.sep), binaryName), + path.join(__dirname, '..', 'node_modules', packageName.replace('/', path.sep), binaryName), // pnpm - path.join(__dirname, "..", "..", "..", packageName.replace("/", path.sep), binaryName), + path.join(__dirname, '..', '..', '..', packageName.replace('/', path.sep), binaryName), ] for (const p of paths) { @@ -96,19 +100,20 @@ function getBinaryPath(): string { } throw new Error( - `Could not find ${PACKAGE_NAME} binary for ${target}.\n` + - `Tried: ${paths.join(", ")}\n` + - `Please ensure the optional dependency ${packageName} is installed.`, + `Could not find ${PACKAGE_NAME} binary for ${target}.\n` + + `Tried: ${paths.join(', ')}\n` + + `Please ensure the optional dependency ${packageName} is installed.`, ) } try { const binaryPath = getBinaryPath() execFileSync(binaryPath, process.argv.slice(2), { - stdio: "inherit", + stdio: 'inherit', }) -} catch (error: unknown) { - if (error && typeof error === "object" && "status" in error && typeof error.status === "number") { +} +catch (error: unknown) { + if (error && typeof error === 'object' && 'status' in error && typeof error.status === 'number') { process.exit(error.status) } if (error instanceof Error) { diff --git a/packages/code/src/providers/index.ts b/packages/code/src/providers/index.ts index 77ebf5a..d4996e3 100644 --- a/packages/code/src/providers/index.ts +++ b/packages/code/src/providers/index.ts @@ -2,5 +2,5 @@ * Providers module exports */ -export * from "./provider" -export * from "./lsp" +export * from './lsp' +export * from './provider' diff --git a/packages/code/src/providers/lsp/__tests__/index.test.ts b/packages/code/src/providers/lsp/__tests__/index.test.ts index 9d22bb1..7a2207d 100644 --- a/packages/code/src/providers/lsp/__tests__/index.test.ts +++ b/packages/code/src/providers/lsp/__tests__/index.test.ts @@ -1,87 +1,87 @@ -import { describe, expect, test, afterEach } from "bun:test"; -import { LSPProvider, createLSPProvider } from "../index"; +import { afterEach, describe, expect, test } from 'bun:test' +import { createLSPProvider, LSPProvider } from '../index' -describe("LSPProvider", () => { - let provider: LSPProvider | null = null; +describe('LSPProvider', () => { + let provider: LSPProvider | null = null afterEach(async () => { if (provider) { - await provider.disconnect(); - provider = null; + await provider.disconnect() + provider = null } - }); - - test("has correct name", () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - expect(provider.name).toBe("lsp"); - }); - - test("starts disconnected", () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - expect(provider.isConnected()).toBe(false); - }); - - test("connects successfully", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - await provider.connect(); - expect(provider.isConnected()).toBe(true); - }); - - test("disconnect sets connected to false", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - await provider.connect(); - expect(provider.isConnected()).toBe(true); - - await provider.disconnect(); - expect(provider.isConnected()).toBe(false); - provider = null; // Prevent double disconnect - }); - - test("lists LSP tools", () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - const tools = provider.listTools(); - - expect(tools.length).toBeGreaterThan(0); - - const toolNames = tools.map((t) => t.name); - expect(toolNames).toContain("lsp_diagnostics"); - expect(toolNames).toContain("lsp_hover"); - expect(toolNames).toContain("lsp_workspace_symbol"); - expect(toolNames).toContain("lsp_document_symbol"); - expect(toolNames).toContain("lsp_status"); - }); - - test("returns error when calling tool without connecting", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - - const result = await provider.callTool("lsp_status", {}); - expect(result.isError).toBe(true); - expect(result.content[0]!.text).toContain("not connected"); - }); - - test("lsp_status returns no servers when none connected", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - await provider.connect(); - - const result = await provider.callTool("lsp_status", {}); - expect(result.isError).toBeFalsy(); - expect(result.content[0]!.text).toContain("No LSP servers connected"); - }); - - test("returns error for unknown tool", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - await provider.connect(); - - const result = await provider.callTool("unknown_tool", {}); - expect(result.isError).toBe(true); - expect(result.content[0]!.text).toContain("Unknown tool"); - }); -}); - -describe("createLSPProvider", () => { - test("creates provider instance", () => { - const provider = createLSPProvider({ projectPath: process.cwd() }); - expect(provider).toBeInstanceOf(LSPProvider); - expect(provider.name).toBe("lsp"); - }); -}); + }) + + test('has correct name', () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + expect(provider.name).toBe('lsp') + }) + + test('starts disconnected', () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + expect(provider.isConnected()).toBe(false) + }) + + test('connects successfully', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + await provider.connect() + expect(provider.isConnected()).toBe(true) + }) + + test('disconnect sets connected to false', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + await provider.connect() + expect(provider.isConnected()).toBe(true) + + await provider.disconnect() + expect(provider.isConnected()).toBe(false) + provider = null // Prevent double disconnect + }) + + test('lists LSP tools', () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + const tools = provider.listTools() + + expect(tools.length).toBeGreaterThan(0) + + const toolNames = tools.map(t => t.name) + expect(toolNames).toContain('lsp_diagnostics') + expect(toolNames).toContain('lsp_hover') + expect(toolNames).toContain('lsp_workspace_symbol') + expect(toolNames).toContain('lsp_document_symbol') + expect(toolNames).toContain('lsp_status') + }) + + test('returns error when calling tool without connecting', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + + const result = await provider.callTool('lsp_status', {}) + expect(result.isError).toBe(true) + expect(result.content[0]!.text).toContain('not connected') + }) + + test('lsp_status returns no servers when none connected', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + await provider.connect() + + const result = await provider.callTool('lsp_status', {}) + expect(result.isError).toBeFalsy() + expect(result.content[0]!.text).toContain('No LSP servers connected') + }) + + test('returns error for unknown tool', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + await provider.connect() + + const result = await provider.callTool('unknown_tool', {}) + expect(result.isError).toBe(true) + expect(result.content[0]!.text).toContain('Unknown tool') + }) +}) + +describe('createLSPProvider', () => { + test('creates provider instance', () => { + const provider = createLSPProvider({ projectPath: process.cwd() }) + expect(provider).toBeInstanceOf(LSPProvider) + expect(provider.name).toBe('lsp') + }) +}) diff --git a/packages/code/src/providers/lsp/index.ts b/packages/code/src/providers/lsp/index.ts index 64c733e..8c0da0f 100644 --- a/packages/code/src/providers/lsp/index.ts +++ b/packages/code/src/providers/lsp/index.ts @@ -4,16 +4,16 @@ * Provides language intelligence tools via Language Server Protocol */ -import { z } from "zod"; -import path from "path"; -import type { Provider, ToolDefinition, ToolResult } from "../provider"; -import { LSPManager, formatDiagnostic } from "@pleaseai/code-lsp"; +import type { Provider, ToolDefinition, ToolResult } from '../provider' +import path from 'node:path' +import { formatDiagnostic, LSPManager } from '@pleaseai/code-lsp' +import { z } from 'zod' /** * LSP Provider configuration */ export interface LSPProviderConfig { - projectPath: string; + projectPath: string } /** @@ -21,150 +21,152 @@ export interface LSPProviderConfig { */ const LSP_TOOLS: ToolDefinition[] = [ { - name: "lsp_diagnostics", + name: 'lsp_diagnostics', description: - "Get diagnostics (errors, warnings) for a file from language servers. " + - "Returns type errors, syntax errors, and other issues detected by LSP.", + 'Get diagnostics (errors, warnings) for a file from language servers. ' + + 'Returns type errors, syntax errors, and other issues detected by LSP.', inputSchema: z.object({ path: z .string() - .describe("Path to the file to get diagnostics for (relative or absolute)"), + .describe('Path to the file to get diagnostics for (relative or absolute)'), }), }, { - name: "lsp_hover", + name: 'lsp_hover', description: - "Get hover information (type, documentation) at a specific position in a file. " + - "Useful for understanding types, function signatures, and documentation.", + 'Get hover information (type, documentation) at a specific position in a file. ' + + 'Useful for understanding types, function signatures, and documentation.', inputSchema: z.object({ - file: z.string().describe("Path to the file"), - line: z.number().describe("Line number (0-indexed)"), - character: z.number().describe("Character position (0-indexed)"), + file: z.string().describe('Path to the file'), + line: z.number().describe('Line number (0-indexed)'), + character: z.number().describe('Character position (0-indexed)'), }), }, { - name: "lsp_workspace_symbol", + name: 'lsp_workspace_symbol', description: - "Search for symbols (functions, classes, variables) across the workspace. " + - "Returns matching symbols with their locations.", + 'Search for symbols (functions, classes, variables) across the workspace. ' + + 'Returns matching symbols with their locations.', inputSchema: z.object({ - query: z.string().describe("Search query for symbol name"), + query: z.string().describe('Search query for symbol name'), }), }, { - name: "lsp_document_symbol", + name: 'lsp_document_symbol', description: - "Get all symbols defined in a document. " + - "Returns the structure of a file including functions, classes, and variables.", + 'Get all symbols defined in a document. ' + + 'Returns the structure of a file including functions, classes, and variables.', inputSchema: z.object({ - file: z.string().describe("Path to the file to analyze"), + file: z.string().describe('Path to the file to analyze'), }), }, { - name: "lsp_status", + name: 'lsp_status', description: - "Get the status of connected LSP servers. " + - "Shows which language servers are running and their connection status.", + 'Get the status of connected LSP servers. ' + + 'Shows which language servers are running and their connection status.', inputSchema: z.object({}), }, -]; +] /** * LSP Provider implementation */ export class LSPProvider implements Provider { - readonly name = "lsp"; + readonly name = 'lsp' - private manager: LSPManager | null = null; - private config: LSPProviderConfig; - private connected = false; + private manager: LSPManager | null = null + private config: LSPProviderConfig + private connected = false constructor(config: LSPProviderConfig) { - this.config = config; + this.config = config } async connect(): Promise { - if (this.connected) return; + if (this.connected) + return - this.manager = new LSPManager(this.config.projectPath); - this.connected = true; + this.manager = new LSPManager(this.config.projectPath) + this.connected = true } async disconnect(): Promise { if (this.manager) { - await this.manager.shutdown(); - this.manager = null; + await this.manager.shutdown() + this.manager = null } - this.connected = false; + this.connected = false } isConnected(): boolean { - return this.connected; + return this.connected } listTools(): ToolDefinition[] { - return LSP_TOOLS; + return LSP_TOOLS } async callTool(name: string, args: unknown): Promise { if (!this.manager) { return { - content: [{ type: "text", text: "LSP provider not connected" }], + content: [{ type: 'text', text: 'LSP provider not connected' }], isError: true, - }; + } } try { switch (name) { - case "lsp_diagnostics": - return await this.handleDiagnostics(args); - case "lsp_hover": - return await this.handleHover(args); - case "lsp_workspace_symbol": - return await this.handleWorkspaceSymbol(args); - case "lsp_document_symbol": - return await this.handleDocumentSymbol(args); - case "lsp_status": - return await this.handleStatus(); + case 'lsp_diagnostics': + return await this.handleDiagnostics(args) + case 'lsp_hover': + return await this.handleHover(args) + case 'lsp_workspace_symbol': + return await this.handleWorkspaceSymbol(args) + case 'lsp_document_symbol': + return await this.handleDocumentSymbol(args) + case 'lsp_status': + return await this.handleStatus() default: return { - content: [{ type: "text", text: `Unknown tool: ${name}` }], + content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true, - }; + } } - } catch (error) { + } + catch (error) { return { content: [ { - type: "text", + type: 'text', text: `LSP error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, - }; + } } } private async handleDiagnostics(args: unknown): Promise { - const parsed = z.object({ path: z.string() }).parse(args); + const parsed = z.object({ path: z.string() }).parse(args) const filePath = path.isAbsolute(parsed.path) ? parsed.path - : path.join(this.config.projectPath, parsed.path); + : path.join(this.config.projectPath, parsed.path) - await this.manager!.touchFile(filePath, true); - const allDiagnostics = await this.manager!.diagnostics(); - const fileDiagnostics = allDiagnostics[filePath]; + await this.manager!.touchFile(filePath, true) + const allDiagnostics = await this.manager!.diagnostics() + const fileDiagnostics = allDiagnostics[filePath] if (!fileDiagnostics?.length) { return { - content: [{ type: "text", text: "No errors found" }], - }; + content: [{ type: 'text', text: 'No errors found' }], + } } - const formatted = fileDiagnostics.map(formatDiagnostic).join("\n"); + const formatted = fileDiagnostics.map(formatDiagnostic).join('\n') return { - content: [{ type: "text", text: formatted }], - }; + content: [{ type: 'text', text: formatted }], + } } private async handleHover(args: unknown): Promise { @@ -174,87 +176,87 @@ export class LSPProvider implements Provider { line: z.number(), character: z.number(), }) - .parse(args); + .parse(args) const filePath = path.isAbsolute(parsed.file) ? parsed.file - : path.join(this.config.projectPath, parsed.file); + : path.join(this.config.projectPath, parsed.file) - await this.manager!.touchFile(filePath, true); + await this.manager!.touchFile(filePath, true) const results = await this.manager!.hover({ file: filePath, line: parsed.line, character: parsed.character, - }); + }) - const validResults = results.filter(Boolean); + const validResults = results.filter(Boolean) if (!validResults.length) { return { content: [ - { type: "text", text: "No hover information available at this position" }, + { type: 'text', text: 'No hover information available at this position' }, ], - }; + } } return { content: [ - { type: "text", text: JSON.stringify(validResults, null, 2) }, + { type: 'text', text: JSON.stringify(validResults, null, 2) }, ], - }; + } } private async handleWorkspaceSymbol(args: unknown): Promise { - const parsed = z.object({ query: z.string() }).parse(args); - const symbols = await this.manager!.workspaceSymbol(parsed.query); + const parsed = z.object({ query: z.string() }).parse(args) + const symbols = await this.manager!.workspaceSymbol(parsed.query) if (!symbols.length) { return { - content: [{ type: "text", text: `No symbols found matching "${parsed.query}"` }], - }; + content: [{ type: 'text', text: `No symbols found matching "${parsed.query}"` }], + } } return { - content: [{ type: "text", text: JSON.stringify(symbols, null, 2) }], - }; + content: [{ type: 'text', text: JSON.stringify(symbols, null, 2) }], + } } private async handleDocumentSymbol(args: unknown): Promise { - const parsed = z.object({ file: z.string() }).parse(args); + const parsed = z.object({ file: z.string() }).parse(args) const filePath = path.isAbsolute(parsed.file) ? parsed.file - : path.join(this.config.projectPath, parsed.file); + : path.join(this.config.projectPath, parsed.file) - await this.manager!.touchFile(filePath, false); - const uri = `file://${filePath}`; - const symbols = await this.manager!.documentSymbol(uri); + await this.manager!.touchFile(filePath, false) + const uri = `file://${filePath}` + const symbols = await this.manager!.documentSymbol(uri) if (!symbols.length) { return { - content: [{ type: "text", text: "No symbols found in this document" }], - }; + content: [{ type: 'text', text: 'No symbols found in this document' }], + } } return { - content: [{ type: "text", text: JSON.stringify(symbols, null, 2) }], - }; + content: [{ type: 'text', text: JSON.stringify(symbols, null, 2) }], + } } private async handleStatus(): Promise { - const status = await this.manager!.status(); + const status = await this.manager!.status() if (!status.length) { return { - content: [{ type: "text", text: "No LSP servers connected" }], - }; + content: [{ type: 'text', text: 'No LSP servers connected' }], + } } const formatted = status - .map((s) => `${s.name}: ${s.status} (root: ${s.root})`) - .join("\n"); + .map(s => `${s.name}: ${s.status} (root: ${s.root})`) + .join('\n') return { - content: [{ type: "text", text: formatted }], - }; + content: [{ type: 'text', text: formatted }], + } } } @@ -262,5 +264,5 @@ export class LSPProvider implements Provider { * Factory function for creating LSPProvider */ export function createLSPProvider(config: LSPProviderConfig): LSPProvider { - return new LSPProvider(config); + return new LSPProvider(config) } diff --git a/packages/code/src/providers/provider.ts b/packages/code/src/providers/provider.ts index 39f0363..77a7340 100644 --- a/packages/code/src/providers/provider.ts +++ b/packages/code/src/providers/provider.ts @@ -2,24 +2,24 @@ * Provider interface for tool backends (LSP, etc.) */ -import type { z } from "zod"; +import type { z } from 'zod' /** * Tool definition exposed by a provider */ export interface ToolDefinition { - name: string; - description: string; - inputSchema: z.ZodType; + name: string + description: string + inputSchema: z.ZodType } /** * Result of a tool call */ export interface ToolResult { - [key: string]: unknown; - content: Array<{ type: "text"; text: string }>; - isError?: boolean; + [key: string]: unknown + content: Array<{ type: 'text', text: string }> + isError?: boolean } /** @@ -27,19 +27,19 @@ export interface ToolResult { */ export interface Provider { /** Provider name for identification */ - readonly name: string; + readonly name: string /** Connect to the backend */ - connect(): Promise; + connect: () => Promise /** Disconnect from the backend */ - disconnect(): Promise; + disconnect: () => Promise /** Check if currently connected */ - isConnected(): boolean; + isConnected: () => boolean /** Get list of available tools */ - listTools(): ToolDefinition[]; + listTools: () => ToolDefinition[] /** * Call a tool with given arguments @@ -47,10 +47,10 @@ export interface Provider { * @param args Tool arguments * @returns Tool result */ - callTool(name: string, args: unknown): Promise; + callTool: (name: string, args: unknown) => Promise } /** * Provider type enumeration */ -export type ProviderType = "lsp" +export type ProviderType = 'lsp' diff --git a/packages/dora/package.json b/packages/dora/package.json index 30dee66..4bc6720 100644 --- a/packages/dora/package.json +++ b/packages/dora/package.json @@ -1,12 +1,27 @@ { "name": "@pleaseai/dora", + "type": "module", "version": "0.1.0", "description": "MCP server for JetBrains IDE integration", - "type": "module", + "author": "Minsu Lee", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/chatbot-pf/code-please" + }, + "keywords": [ + "mcp", + "jetbrains", + "ide", + "ai-coding" + ], "main": "src/index.ts", "bin": { "dora": "src/cli.ts" }, + "files": [ + "src" + ], "scripts": { "dev": "bun --watch run src/cli.ts", "build": "echo 'Build handled by root build:npm'", @@ -21,20 +36,5 @@ "devDependencies": { "@types/bun": "latest", "typescript": "^5.7.0" - }, - "files": [ - "src" - ], - "keywords": [ - "mcp", - "jetbrains", - "ide", - "ai-coding" - ], - "author": "Minsu Lee", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/chatbot-pf/code-please" } } diff --git a/packages/dora/src/cli.ts b/packages/dora/src/cli.ts index a568455..3cb517d 100644 --- a/packages/dora/src/cli.ts +++ b/packages/dora/src/cli.ts @@ -8,9 +8,10 @@ * dora help Show help */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" -import { createDoraServer } from "./server" -import pkg from "../package.json" +import process from 'node:process' +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' +import pkg from '../package.json' +import { createDoraServer } from './server' const VERSION = pkg.version @@ -27,18 +28,20 @@ function parseArgs(argv: string[]): ParsedArgs { for (let i = 0; i < args.length; i++) { const arg = args[i]! - if (arg.startsWith("--")) { - const [key, value] = arg.slice(2).split("=") + if (arg.startsWith('--')) { + const [key, value] = arg.slice(2).split('=') flags[key!] = value ?? true - } else if (arg.startsWith("-")) { + } + else if (arg.startsWith('-')) { flags[arg.slice(1)] = true - } else { + } + else { positional.push(arg) } } return { - command: positional[0] ?? "serve", + command: positional[0] ?? 'serve', args: positional.slice(1), flags, } @@ -51,7 +54,7 @@ async function serveCommand(projectPath: string, timeout: number): Promise const transport = new StdioServerTransport() await server.connect(transport) - console.error("[dora] MCP server connected and ready") + console.error('[dora] MCP server connected and ready') } function versionCommand(): void { @@ -83,31 +86,31 @@ Environment: async function main(): Promise { const { command, flags } = parseArgs(process.argv) - const projectDir = - (flags["project"] as string) ?? - process.env["DORA_PROJECT_PATH"] ?? - process.cwd() + const projectDir + = (flags.project as string) + ?? process.env.DORA_PROJECT_PATH + ?? process.cwd() - const timeout = flags["timeout"] - ? parseInt(flags["timeout"] as string, 10) - : process.env["DORA_TIMEOUT"] - ? parseInt(process.env["DORA_TIMEOUT"], 10) + const timeout = flags.timeout + ? Number.parseInt(flags.timeout as string, 10) + : process.env.DORA_TIMEOUT + ? Number.parseInt(process.env.DORA_TIMEOUT, 10) : 30000 switch (command) { - case "serve": + case 'serve': await serveCommand(projectDir, timeout) break - case "version": - case "-v": - case "--version": + case 'version': + case '-v': + case '--version': versionCommand() break - case "help": - case "-h": - case "--help": + case 'help': + case '-h': + case '--help': helpCommand() break @@ -119,6 +122,6 @@ async function main(): Promise { } main().catch((error) => { - console.error("[dora] Error:", error) + console.error('[dora] Error:', error) process.exit(1) }) diff --git a/packages/dora/src/client/index.ts b/packages/dora/src/client/index.ts index b76e19b..2a81238 100644 --- a/packages/dora/src/client/index.ts +++ b/packages/dora/src/client/index.ts @@ -1,3 +1,3 @@ -export { JetBrainsClient } from "./jetbrains-client"; -export { discoverPort, clearPortCache } from "./port-discovery"; -export { transformResponse, toSnakeCase } from "./response-transformer"; +export { JetBrainsClient } from './jetbrains-client' +export { clearPortCache, discoverPort } from './port-discovery' +export { toSnakeCase, transformResponse } from './response-transformer' diff --git a/packages/dora/src/client/jetbrains-client.ts b/packages/dora/src/client/jetbrains-client.ts index 7152bcf..dc1dfb4 100644 --- a/packages/dora/src/client/jetbrains-client.ts +++ b/packages/dora/src/client/jetbrains-client.ts @@ -2,77 +2,80 @@ * HTTP client for JetBrains plugin communication */ -import { ApiError, ConnectionError, TimeoutError } from "../errors"; -import { transformResponse } from "./response-transformer"; import type { - FindSymbolRequest, - FindSymbolResponse, FindReferencesRequest, FindReferencesResponse, + FindSymbolRequest, + FindSymbolResponse, GetSymbolsOverviewRequest, GetSymbolsOverviewResponse, -} from "../types/api"; +} from '../types/api' +import { ApiError, ConnectionError, TimeoutError } from '../errors' +import { transformResponse } from './response-transformer' export class JetBrainsClient { - private readonly baseUrl: string; - private readonly timeout: number; + private readonly baseUrl: string + private readonly timeout: number constructor(port: number, timeout: number = 30000) { - this.baseUrl = `http://127.0.0.1:${port}`; - this.timeout = timeout; + this.baseUrl = `http://127.0.0.1:${port}` + this.timeout = timeout } /** * Makes an HTTP request to the JetBrains plugin service */ private async request( - method: "GET" | "POST", + method: 'GET' | 'POST', endpoint: string, - data?: unknown + data?: unknown, ): Promise { - const url = `${this.baseUrl}${endpoint}`; - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), this.timeout); + const url = `${this.baseUrl}${endpoint}` + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), this.timeout) try { const options: RequestInit = { method, headers: { - "Content-Type": "application/json", - Accept: "application/json", + 'Content-Type': 'application/json', + 'Accept': 'application/json', }, signal: controller.signal, - }; + } - if (method === "POST" && data) { - options.body = JSON.stringify(data); + if (method === 'POST' && data) { + options.body = JSON.stringify(data) } - const response = await fetch(url, options); + const response = await fetch(url, options) if (!response.ok) { - const text = await response.text(); + const text = await response.text() throw new ApiError( `API request failed with status ${response.status}: ${text}`, - response.status - ); + response.status, + ) } - const json = await response.json(); - return transformResponse(json); - } catch (error) { - if (error instanceof ApiError) throw error; + const json = await response.json() + return transformResponse(json) + } + catch (error) { + if (error instanceof ApiError) + throw error - if (error instanceof DOMException && error.name === "AbortError") { - throw new TimeoutError(url, this.timeout); + if (error instanceof DOMException && error.name === 'AbortError') { + throw new TimeoutError(url, this.timeout) } throw new ConnectionError( `Failed to connect to JetBrains service at ${url}`, - error - ); - } finally { - clearTimeout(timeoutId); + error, + ) + } + finally { + clearTimeout(timeoutId) } } @@ -81,22 +84,22 @@ export class JetBrainsClient { */ async getProjectRoot(): Promise { const response = await this.request<{ project_root: string }>( - "GET", - "/status" - ); - return response.project_root; + 'GET', + '/status', + ) + return response.project_root } /** * Finds symbols by name path pattern */ async findSymbol(params: { - namePath: string; - relativePath?: string | null; - includeBody: boolean; - depth: number; - includeLocation: boolean; - searchDeps: boolean; + namePath: string + relativePath?: string | null + includeBody: boolean + depth: number + includeLocation: boolean + searchDeps: boolean }): Promise { const request: FindSymbolRequest = { namePath: params.namePath, @@ -105,41 +108,41 @@ export class JetBrainsClient { depth: params.depth, includeLocation: params.includeLocation, searchDeps: params.searchDeps, - }; - return this.request("POST", "/findSymbol", request); + } + return this.request('POST', '/findSymbol', request) } /** * Finds references to a symbol */ async findReferences(params: { - namePath: string; - relativePath: string; + namePath: string + relativePath: string }): Promise { const request: FindReferencesRequest = { namePath: params.namePath, relativePath: params.relativePath, - }; + } return this.request( - "POST", - "/findReferences", - request - ); + 'POST', + '/findReferences', + request, + ) } /** * Gets an overview of top-level symbols in a file */ async getSymbolsOverview(params: { - relativePath: string; + relativePath: string }): Promise { const request: GetSymbolsOverviewRequest = { relativePath: params.relativePath, - }; + } return this.request( - "POST", - "/getSymbolsOverview", - request - ); + 'POST', + '/getSymbolsOverview', + request, + ) } } diff --git a/packages/dora/src/client/port-discovery.ts b/packages/dora/src/client/port-discovery.ts index f8def46..b19c190 100644 --- a/packages/dora/src/client/port-discovery.ts +++ b/packages/dora/src/client/port-discovery.ts @@ -3,22 +3,22 @@ * Scans ports 24226-24245 to find the running IDE instance */ -import { ServerNotFoundError } from "../errors"; -import { transformResponse } from "./response-transformer"; +import { ServerNotFoundError } from '../errors' +import { transformResponse } from './response-transformer' -const BASE_PORT = 0x5ea2; // 24226 -const PORT_RANGE = 20; -const DISCOVERY_TIMEOUT = 1000; // 1 second for port discovery +const BASE_PORT = 0x5EA2 // 24226 +const PORT_RANGE = 20 +const DISCOVERY_TIMEOUT = 1000 // 1 second for port discovery interface PortCache { - port: number | null; - projectPath: string | null; + port: number | null + projectPath: string | null } -const cache: PortCache = { port: null, projectPath: null }; +const cache: PortCache = { port: null, projectPath: null } interface StatusResponse { - project_root: string; + project_root: string } /** @@ -26,30 +26,32 @@ interface StatusResponse { */ async function checkPort( port: number, - expectedPath: string + expectedPath: string, ): Promise { try { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT); + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT) const response = await fetch(`http://127.0.0.1:${port}/status`, { signal: controller.signal, headers: { - Accept: "application/json", + Accept: 'application/json', }, - }); + }) - clearTimeout(timeoutId); + clearTimeout(timeoutId) - if (!response.ok) return false; + if (!response.ok) + return false - const data = await response.json(); - const transformed = transformResponse(data); - const projectRoot = transformed.project_root; + const data = await response.json() + const transformed = transformResponse(data) + const projectRoot = transformed.project_root - return projectRoot === expectedPath; - } catch { - return false; + return projectRoot === expectedPath + } + catch { + return false } } @@ -63,27 +65,27 @@ export async function discoverPort(projectPath: string): Promise { // Try cached port first for performance if (cache.port !== null && cache.projectPath === projectPath) { if (await checkPort(cache.port, projectPath)) { - return cache.port; + return cache.port } } // Scan port range for (let port = BASE_PORT; port < BASE_PORT + PORT_RANGE; port++) { if (await checkPort(port, projectPath)) { - cache.port = port; - cache.projectPath = projectPath; - console.error(`[dora] Found JetBrains IDE service at port ${port}`); - return port; + cache.port = port + cache.projectPath = projectPath + console.error(`[dora] Found JetBrains IDE service at port ${port}`) + return port } } - throw new ServerNotFoundError(projectPath); + throw new ServerNotFoundError(projectPath) } /** * Clears the port cache (useful for testing or reconnection) */ export function clearPortCache(): void { - cache.port = null; - cache.projectPath = null; + cache.port = null + cache.projectPath = null } diff --git a/packages/dora/src/client/response-transformer.ts b/packages/dora/src/client/response-transformer.ts index 89d538c..7b8f7e9 100644 --- a/packages/dora/src/client/response-transformer.ts +++ b/packages/dora/src/client/response-transformer.ts @@ -6,7 +6,7 @@ * Converts a string from camelCase to snake_case */ export function toSnakeCase(str: string): string { - return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`); + return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`) } /** @@ -14,20 +14,20 @@ export function toSnakeCase(str: string): string { */ export function transformResponse(response: unknown): T { if (response === null || response === undefined) { - return response as T; + return response as T } if (Array.isArray(response)) { - return response.map((item) => transformResponse(item)) as T; + return response.map(item => transformResponse(item)) as T } - if (typeof response === "object") { - const transformed: Record = {}; + if (typeof response === 'object') { + const transformed: Record = {} for (const [key, value] of Object.entries(response)) { - transformed[toSnakeCase(key)] = transformResponse(value); + transformed[toSnakeCase(key)] = transformResponse(value) } - return transformed as T; + return transformed as T } - return response as T; + return response as T } diff --git a/packages/dora/src/errors/index.ts b/packages/dora/src/errors/index.ts index a01276f..d28fdce 100644 --- a/packages/dora/src/errors/index.ts +++ b/packages/dora/src/errors/index.ts @@ -5,17 +5,17 @@ export class DoraError extends Error { constructor( message: string, - public readonly cause?: unknown + public readonly cause?: unknown, ) { - super(message); - this.name = "DoraError"; + super(message) + this.name = 'DoraError' } } export class ConnectionError extends DoraError { constructor(message: string, cause?: unknown) { - super(message, cause); - this.name = "ConnectionError"; + super(message, cause) + this.name = 'ConnectionError' } } @@ -23,26 +23,26 @@ export class ApiError extends DoraError { constructor( message: string, public readonly statusCode?: number, - cause?: unknown + cause?: unknown, ) { - super(message, cause); - this.name = "ApiError"; + super(message, cause) + this.name = 'ApiError' } } export class ServerNotFoundError extends DoraError { constructor(projectPath: string) { super( - `Found no JetBrains IDE service for the project at ${projectPath}. ` + - "Ensure the Serena plugin is installed and the project is open." - ); - this.name = "ServerNotFoundError"; + `Found no JetBrains IDE service for the project at ${projectPath}. ` + + 'Ensure the Serena plugin is installed and the project is open.', + ) + this.name = 'ServerNotFoundError' } } export class TimeoutError extends DoraError { constructor(url: string, timeout: number) { - super(`Request to ${url} timed out after ${timeout}ms`); - this.name = "TimeoutError"; + super(`Request to ${url} timed out after ${timeout}ms`) + this.name = 'TimeoutError' } } diff --git a/packages/dora/src/index.ts b/packages/dora/src/index.ts index d7899e8..c15c834 100644 --- a/packages/dora/src/index.ts +++ b/packages/dora/src/index.ts @@ -5,6 +5,6 @@ * providing symbol finding and navigation tools. */ -export { createDoraServer, type ServerConfig } from "./server" -export * from "./providers" -export * from "./errors" +export * from './errors' +export * from './providers' +export { createDoraServer, type ServerConfig } from './server' diff --git a/packages/dora/src/providers/index.ts b/packages/dora/src/providers/index.ts index 178c1fd..bd45704 100644 --- a/packages/dora/src/providers/index.ts +++ b/packages/dora/src/providers/index.ts @@ -2,7 +2,7 @@ * Providers module exports */ -export * from "./provider"; -export * from "./registry"; -export * from "./jetbrains"; -export * from "./lsp"; +export * from './jetbrains' +export * from './lsp' +export * from './provider' +export * from './registry' diff --git a/packages/dora/src/providers/jetbrains/discovery.ts b/packages/dora/src/providers/jetbrains/discovery.ts index 012b183..cd473ad 100644 --- a/packages/dora/src/providers/jetbrains/discovery.ts +++ b/packages/dora/src/providers/jetbrains/discovery.ts @@ -5,37 +5,38 @@ * This module discovers the port by checking known port ranges. */ -const DEFAULT_PORT = 64342; -const PORT_RANGE_START = 63342; -const PORT_RANGE_END = 63352; -const DISCOVERY_TIMEOUT = 2000; +const DEFAULT_PORT = 64342 +const PORT_RANGE_START = 63342 +const PORT_RANGE_END = 63352 +const DISCOVERY_TIMEOUT = 2000 interface DiscoveryResult { - port: number; - url: string; + port: number + url: string } /** * Check if JetBrains MCP is available on a given port */ async function checkPort(port: number, projectPath: string): Promise { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT); + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT) try { // JetBrains MCP uses SSE, but we can check if the endpoint responds const response = await fetch(`http://127.0.0.1:${port}/mcp`, { - method: "GET", + method: 'GET', headers: { IJ_MCP_SERVER_PROJECT_PATH: projectPath, }, signal: controller.signal, - }); - clearTimeout(timeoutId); - return response.ok || response.status === 405; // 405 = method not allowed, but server is there - } catch { - clearTimeout(timeoutId); - return false; + }) + clearTimeout(timeoutId) + return response.ok || response.status === 405 // 405 = method not allowed, but server is there + } + catch { + clearTimeout(timeoutId) + return false } } @@ -47,14 +48,14 @@ async function checkPort(port: number, projectPath: string): Promise { * @throws Error if no MCP server found */ export async function discoverJetBrainsMcp( - projectPath: string + projectPath: string, ): Promise { // First try the default port if (await checkPort(DEFAULT_PORT, projectPath)) { return { port: DEFAULT_PORT, url: `http://127.0.0.1:${DEFAULT_PORT}/sse`, - }; + } } // Scan port range @@ -63,12 +64,12 @@ export async function discoverJetBrainsMcp( return { port, url: `http://127.0.0.1:${port}/sse`, - }; + } } } throw new Error( - `JetBrains MCP server not found. Ensure JetBrains IDE 2025.2+ is running ` + - `and MCP Server is enabled in Settings | Tools | MCP Server.` - ); + `JetBrains MCP server not found. Ensure JetBrains IDE 2025.2+ is running ` + + `and MCP Server is enabled in Settings | Tools | MCP Server.`, + ) } diff --git a/packages/dora/src/providers/jetbrains/index.ts b/packages/dora/src/providers/jetbrains/index.ts index e57380e..18c285e 100644 --- a/packages/dora/src/providers/jetbrains/index.ts +++ b/packages/dora/src/providers/jetbrains/index.ts @@ -4,33 +4,34 @@ * Connects to JetBrains IDE's built-in MCP server via SSE transport. */ -import { Client } from "@modelcontextprotocol/sdk/client/index.js"; -import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; -import type { Provider, ToolDefinition, ToolResult } from "../provider"; -import type { RegistryConfig } from "../registry"; -import { discoverJetBrainsMcp } from "./discovery"; -import { getDoraToolDefinitions, findMapping } from "./tool-mapping"; +import type { Provider, ToolDefinition, ToolResult } from '../provider' +import type { RegistryConfig } from '../registry' +import { Client } from '@modelcontextprotocol/sdk/client/index.js' +import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js' +import { discoverJetBrainsMcp } from './discovery' +import { findMapping, getDoraToolDefinitions } from './tool-mapping' /** * JetBrains MCP Provider implementation */ export class JetBrainsMcpProvider implements Provider { - readonly name = "jetbrains-mcp"; + readonly name = 'jetbrains-mcp' - private client: Client | null = null; - private transport: SSEClientTransport | null = null; - private config: RegistryConfig; - private connected = false; + private client: Client | null = null + private transport: SSEClientTransport | null = null + private config: RegistryConfig + private connected = false constructor(config: RegistryConfig) { - this.config = config; + this.config = config } async connect(): Promise { - if (this.connected) return; + if (this.connected) + return // Discover JetBrains MCP endpoint - const discovery = await discoverJetBrainsMcp(this.config.projectPath); + const discovery = await discoverJetBrainsMcp(this.config.projectPath) // Create SSE transport with project path header this.transport = new SSEClientTransport(new URL(discovery.url), { @@ -39,99 +40,101 @@ export class JetBrainsMcpProvider implements Provider { IJ_MCP_SERVER_PROJECT_PATH: this.config.projectPath, }, }, - }); + }) // Create MCP client this.client = new Client( - { name: "dora", version: "0.1.0" }, - { capabilities: {} } - ); + { name: 'dora', version: '0.1.0' }, + { capabilities: {} }, + ) // Connect - await this.client.connect(this.transport); - this.connected = true; + await this.client.connect(this.transport) + this.connected = true } async disconnect(): Promise { if (this.client) { - await this.client.close(); - this.client = null; + await this.client.close() + this.client = null } if (this.transport) { - await this.transport.close(); - this.transport = null; + await this.transport.close() + this.transport = null } - this.connected = false; + this.connected = false } isConnected(): boolean { - return this.connected; + return this.connected } listTools(): ToolDefinition[] { - return getDoraToolDefinitions(); + return getDoraToolDefinitions() } async callTool(name: string, args: unknown): Promise { if (!this.client) { - throw new Error("Not connected to JetBrains MCP"); + throw new Error('Not connected to JetBrains MCP') } // Find mapping for this tool - const mapping = findMapping(name); + const mapping = findMapping(name) if (!mapping) { return { - content: [{ type: "text", text: `Unknown tool: ${name}` }], + content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true, - }; + } } // Transform args for JetBrains - const jetbrainsArgs = mapping.transformArgs(args) as Record; + const jetbrainsArgs = mapping.transformArgs(args) as Record // Set project path - jetbrainsArgs.projectPath = this.config.projectPath; + jetbrainsArgs.projectPath = this.config.projectPath try { // Call JetBrains MCP tool const result = await this.client.callTool({ name: mapping.jetbrainsName, arguments: jetbrainsArgs, - }); + }) // Extract text content from result - const content = result.content as Array<{ type: string; text?: string }>; + const content = result.content as Array<{ type: string, text?: string }> const textContent = content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join("\n"); + .filter((c): c is { type: 'text', text: string } => c.type === 'text') + .map(c => c.text) + .join('\n') // Apply result transformation if defined - let finalText = textContent; + let finalText = textContent if (mapping.transformResult) { try { - const parsed = JSON.parse(textContent); - const transformed = mapping.transformResult(parsed); - finalText = JSON.stringify(transformed, null, 2); - } catch { + const parsed = JSON.parse(textContent) + const transformed = mapping.transformResult(parsed) + finalText = JSON.stringify(transformed, null, 2) + } + catch { // Keep original text if transformation fails } } return { - content: [{ type: "text", text: finalText }], + content: [{ type: 'text', text: finalText }], isError: Boolean(result.isError), - }; - } catch (error) { + } + } + catch (error) { return { content: [ { - type: "text", + type: 'text', text: `Error calling JetBrains MCP: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, - }; + } } } } @@ -140,7 +143,7 @@ export class JetBrainsMcpProvider implements Provider { * Factory function for creating JetBrainsMcpProvider */ export function createJetBrainsMcpProvider( - config: RegistryConfig + config: RegistryConfig, ): JetBrainsMcpProvider { - return new JetBrainsMcpProvider(config); + return new JetBrainsMcpProvider(config) } diff --git a/packages/dora/src/providers/jetbrains/tool-mapping.ts b/packages/dora/src/providers/jetbrains/tool-mapping.ts index 3c84bb7..b37bc87 100644 --- a/packages/dora/src/providers/jetbrains/tool-mapping.ts +++ b/packages/dora/src/providers/jetbrains/tool-mapping.ts @@ -2,34 +2,34 @@ * Tool mapping from Dora unified API to JetBrains MCP tools */ -import { z } from "zod"; -import type { ToolDefinition } from "../provider"; +import type { z } from 'zod' +import type { ToolDefinition } from '../provider' import { - FindSymbolParams, FindReferencingSymbolsParams, + FindSymbolParams, GetSymbolsOverviewParams, -} from "../../types/tool-params"; +} from '../../types/tool-params' /** * Mapping entry from Dora tool to JetBrains tool */ interface ToolMapping { /** Dora tool name */ - doraName: string; + doraName: string /** JetBrains MCP tool name */ - jetbrainsName: string; + jetbrainsName: string /** Description for the Dora tool */ - description: string; + description: string /** Input schema for the Dora tool */ - inputSchema: z.ZodType; + inputSchema: z.ZodType /** * Transform Dora args to JetBrains args */ - transformArgs: (args: unknown) => unknown; + transformArgs: (args: unknown) => unknown /** * Transform JetBrains result to Dora result */ - transformResult?: (result: unknown) => unknown; + transformResult?: (result: unknown) => unknown } /** @@ -37,8 +37,8 @@ interface ToolMapping { */ export const TOOL_MAPPINGS: ToolMapping[] = [ { - doraName: "find_symbol", - jetbrainsName: "get_symbol", + doraName: 'find_symbol', + jetbrainsName: 'get_symbol', description: `Find symbols by name pattern in the codebase. A name path is a path in the symbol tree within a source file. @@ -53,64 +53,64 @@ Pattern types: Returns JSON list of matching symbols with locations.`, inputSchema: FindSymbolParams, transformArgs: (args: unknown) => { - const params = args as z.infer; + const params = args as z.infer return { - projectPath: "", // Will be set by provider + projectPath: '', // Will be set by provider symbol: params.name_path_pattern, filePath: params.relative_path ?? undefined, includeSource: params.include_body, - }; + } }, }, { - doraName: "find_references", - jetbrainsName: "find_usages", + doraName: 'find_references', + jetbrainsName: 'find_usages', description: `Find all references to a symbol in the codebase. Provide the name path of the symbol and the file containing it. Returns a list of locations where the symbol is referenced.`, inputSchema: FindReferencingSymbolsParams, transformArgs: (args: unknown) => { - const params = args as z.infer; + const params = args as z.infer return { - projectPath: "", // Will be set by provider + projectPath: '', // Will be set by provider symbol: params.name_path, filePath: params.relative_path, - }; + } }, }, { - doraName: "get_symbols_overview", - jetbrainsName: "get_file_symbols", + doraName: 'get_symbols_overview', + jetbrainsName: 'get_file_symbols', description: `Get an overview of top-level symbols in a file. Useful for understanding file structure before targeted operations. Returns JSON object containing the symbols in the file.`, inputSchema: GetSymbolsOverviewParams, transformArgs: (args: unknown) => { - const params = args as z.infer; + const params = args as z.infer return { - projectPath: "", // Will be set by provider + projectPath: '', // Will be set by provider filePath: params.relative_path, - }; + } }, }, -]; +] /** * Get Dora tool definitions from mappings */ export function getDoraToolDefinitions(): ToolDefinition[] { - return TOOL_MAPPINGS.map((mapping) => ({ + return TOOL_MAPPINGS.map(mapping => ({ name: mapping.doraName, description: mapping.description, inputSchema: mapping.inputSchema, - })); + })) } /** * Find mapping by Dora tool name */ export function findMapping(doraToolName: string): ToolMapping | undefined { - return TOOL_MAPPINGS.find((m) => m.doraName === doraToolName); + return TOOL_MAPPINGS.find(m => m.doraName === doraToolName) } diff --git a/packages/dora/src/providers/lsp/__tests__/index.test.ts b/packages/dora/src/providers/lsp/__tests__/index.test.ts index 9d22bb1..7a2207d 100644 --- a/packages/dora/src/providers/lsp/__tests__/index.test.ts +++ b/packages/dora/src/providers/lsp/__tests__/index.test.ts @@ -1,87 +1,87 @@ -import { describe, expect, test, afterEach } from "bun:test"; -import { LSPProvider, createLSPProvider } from "../index"; +import { afterEach, describe, expect, test } from 'bun:test' +import { createLSPProvider, LSPProvider } from '../index' -describe("LSPProvider", () => { - let provider: LSPProvider | null = null; +describe('LSPProvider', () => { + let provider: LSPProvider | null = null afterEach(async () => { if (provider) { - await provider.disconnect(); - provider = null; + await provider.disconnect() + provider = null } - }); - - test("has correct name", () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - expect(provider.name).toBe("lsp"); - }); - - test("starts disconnected", () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - expect(provider.isConnected()).toBe(false); - }); - - test("connects successfully", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - await provider.connect(); - expect(provider.isConnected()).toBe(true); - }); - - test("disconnect sets connected to false", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - await provider.connect(); - expect(provider.isConnected()).toBe(true); - - await provider.disconnect(); - expect(provider.isConnected()).toBe(false); - provider = null; // Prevent double disconnect - }); - - test("lists LSP tools", () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - const tools = provider.listTools(); - - expect(tools.length).toBeGreaterThan(0); - - const toolNames = tools.map((t) => t.name); - expect(toolNames).toContain("lsp_diagnostics"); - expect(toolNames).toContain("lsp_hover"); - expect(toolNames).toContain("lsp_workspace_symbol"); - expect(toolNames).toContain("lsp_document_symbol"); - expect(toolNames).toContain("lsp_status"); - }); - - test("returns error when calling tool without connecting", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - - const result = await provider.callTool("lsp_status", {}); - expect(result.isError).toBe(true); - expect(result.content[0]!.text).toContain("not connected"); - }); - - test("lsp_status returns no servers when none connected", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - await provider.connect(); - - const result = await provider.callTool("lsp_status", {}); - expect(result.isError).toBeFalsy(); - expect(result.content[0]!.text).toContain("No LSP servers connected"); - }); - - test("returns error for unknown tool", async () => { - provider = new LSPProvider({ projectPath: process.cwd() }); - await provider.connect(); - - const result = await provider.callTool("unknown_tool", {}); - expect(result.isError).toBe(true); - expect(result.content[0]!.text).toContain("Unknown tool"); - }); -}); - -describe("createLSPProvider", () => { - test("creates provider instance", () => { - const provider = createLSPProvider({ projectPath: process.cwd() }); - expect(provider).toBeInstanceOf(LSPProvider); - expect(provider.name).toBe("lsp"); - }); -}); + }) + + test('has correct name', () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + expect(provider.name).toBe('lsp') + }) + + test('starts disconnected', () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + expect(provider.isConnected()).toBe(false) + }) + + test('connects successfully', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + await provider.connect() + expect(provider.isConnected()).toBe(true) + }) + + test('disconnect sets connected to false', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + await provider.connect() + expect(provider.isConnected()).toBe(true) + + await provider.disconnect() + expect(provider.isConnected()).toBe(false) + provider = null // Prevent double disconnect + }) + + test('lists LSP tools', () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + const tools = provider.listTools() + + expect(tools.length).toBeGreaterThan(0) + + const toolNames = tools.map(t => t.name) + expect(toolNames).toContain('lsp_diagnostics') + expect(toolNames).toContain('lsp_hover') + expect(toolNames).toContain('lsp_workspace_symbol') + expect(toolNames).toContain('lsp_document_symbol') + expect(toolNames).toContain('lsp_status') + }) + + test('returns error when calling tool without connecting', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + + const result = await provider.callTool('lsp_status', {}) + expect(result.isError).toBe(true) + expect(result.content[0]!.text).toContain('not connected') + }) + + test('lsp_status returns no servers when none connected', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + await provider.connect() + + const result = await provider.callTool('lsp_status', {}) + expect(result.isError).toBeFalsy() + expect(result.content[0]!.text).toContain('No LSP servers connected') + }) + + test('returns error for unknown tool', async () => { + provider = new LSPProvider({ projectPath: process.cwd() }) + await provider.connect() + + const result = await provider.callTool('unknown_tool', {}) + expect(result.isError).toBe(true) + expect(result.content[0]!.text).toContain('Unknown tool') + }) +}) + +describe('createLSPProvider', () => { + test('creates provider instance', () => { + const provider = createLSPProvider({ projectPath: process.cwd() }) + expect(provider).toBeInstanceOf(LSPProvider) + expect(provider.name).toBe('lsp') + }) +}) diff --git a/packages/dora/src/providers/lsp/index.ts b/packages/dora/src/providers/lsp/index.ts index b98dbe0..027bed1 100644 --- a/packages/dora/src/providers/lsp/index.ts +++ b/packages/dora/src/providers/lsp/index.ts @@ -4,161 +4,163 @@ * Provides language intelligence tools via Language Server Protocol */ -import { z } from "zod"; -import path from "path"; -import type { Provider, ToolDefinition, ToolResult } from "../provider"; -import type { RegistryConfig } from "../registry"; -import { LSPManager, formatDiagnostic } from "@pleaseai/code-lsp"; +import type { Provider, ToolDefinition, ToolResult } from '../provider' +import type { RegistryConfig } from '../registry' +import path from 'node:path' +import { formatDiagnostic, LSPManager } from '@pleaseai/code-lsp' +import { z } from 'zod' /** * Tool definitions for LSP provider */ const LSP_TOOLS: ToolDefinition[] = [ { - name: "lsp_diagnostics", + name: 'lsp_diagnostics', description: - "Get diagnostics (errors, warnings) for a file from language servers. " + - "Returns type errors, syntax errors, and other issues detected by LSP.", + 'Get diagnostics (errors, warnings) for a file from language servers. ' + + 'Returns type errors, syntax errors, and other issues detected by LSP.', inputSchema: z.object({ path: z .string() - .describe("Path to the file to get diagnostics for (relative or absolute)"), + .describe('Path to the file to get diagnostics for (relative or absolute)'), }), }, { - name: "lsp_hover", + name: 'lsp_hover', description: - "Get hover information (type, documentation) at a specific position in a file. " + - "Useful for understanding types, function signatures, and documentation.", + 'Get hover information (type, documentation) at a specific position in a file. ' + + 'Useful for understanding types, function signatures, and documentation.', inputSchema: z.object({ - file: z.string().describe("Path to the file"), - line: z.number().describe("Line number (0-indexed)"), - character: z.number().describe("Character position (0-indexed)"), + file: z.string().describe('Path to the file'), + line: z.number().describe('Line number (0-indexed)'), + character: z.number().describe('Character position (0-indexed)'), }), }, { - name: "lsp_workspace_symbol", + name: 'lsp_workspace_symbol', description: - "Search for symbols (functions, classes, variables) across the workspace. " + - "Returns matching symbols with their locations.", + 'Search for symbols (functions, classes, variables) across the workspace. ' + + 'Returns matching symbols with their locations.', inputSchema: z.object({ - query: z.string().describe("Search query for symbol name"), + query: z.string().describe('Search query for symbol name'), }), }, { - name: "lsp_document_symbol", + name: 'lsp_document_symbol', description: - "Get all symbols defined in a document. " + - "Returns the structure of a file including functions, classes, and variables.", + 'Get all symbols defined in a document. ' + + 'Returns the structure of a file including functions, classes, and variables.', inputSchema: z.object({ - file: z.string().describe("Path to the file to analyze"), + file: z.string().describe('Path to the file to analyze'), }), }, { - name: "lsp_status", + name: 'lsp_status', description: - "Get the status of connected LSP servers. " + - "Shows which language servers are running and their connection status.", + 'Get the status of connected LSP servers. ' + + 'Shows which language servers are running and their connection status.', inputSchema: z.object({}), }, -]; +] /** * LSP Provider implementation */ export class LSPProvider implements Provider { - readonly name = "lsp"; + readonly name = 'lsp' - private manager: LSPManager | null = null; - private config: RegistryConfig; - private connected = false; + private manager: LSPManager | null = null + private config: RegistryConfig + private connected = false constructor(config: RegistryConfig) { - this.config = config; + this.config = config } async connect(): Promise { - if (this.connected) return; + if (this.connected) + return - this.manager = new LSPManager(this.config.projectPath); - this.connected = true; + this.manager = new LSPManager(this.config.projectPath) + this.connected = true } async disconnect(): Promise { if (this.manager) { - await this.manager.shutdown(); - this.manager = null; + await this.manager.shutdown() + this.manager = null } - this.connected = false; + this.connected = false } isConnected(): boolean { - return this.connected; + return this.connected } listTools(): ToolDefinition[] { - return LSP_TOOLS; + return LSP_TOOLS } async callTool(name: string, args: unknown): Promise { if (!this.manager) { return { - content: [{ type: "text", text: "LSP provider not connected" }], + content: [{ type: 'text', text: 'LSP provider not connected' }], isError: true, - }; + } } try { switch (name) { - case "lsp_diagnostics": - return await this.handleDiagnostics(args); - case "lsp_hover": - return await this.handleHover(args); - case "lsp_workspace_symbol": - return await this.handleWorkspaceSymbol(args); - case "lsp_document_symbol": - return await this.handleDocumentSymbol(args); - case "lsp_status": - return await this.handleStatus(); + case 'lsp_diagnostics': + return await this.handleDiagnostics(args) + case 'lsp_hover': + return await this.handleHover(args) + case 'lsp_workspace_symbol': + return await this.handleWorkspaceSymbol(args) + case 'lsp_document_symbol': + return await this.handleDocumentSymbol(args) + case 'lsp_status': + return await this.handleStatus() default: return { - content: [{ type: "text", text: `Unknown tool: ${name}` }], + content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true, - }; + } } - } catch (error) { + } + catch (error) { return { content: [ { - type: "text", + type: 'text', text: `LSP error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, - }; + } } } private async handleDiagnostics(args: unknown): Promise { - const parsed = z.object({ path: z.string() }).parse(args); + const parsed = z.object({ path: z.string() }).parse(args) const filePath = path.isAbsolute(parsed.path) ? parsed.path - : path.join(this.config.projectPath, parsed.path); + : path.join(this.config.projectPath, parsed.path) - await this.manager!.touchFile(filePath, true); - const allDiagnostics = await this.manager!.diagnostics(); - const fileDiagnostics = allDiagnostics[filePath]; + await this.manager!.touchFile(filePath, true) + const allDiagnostics = await this.manager!.diagnostics() + const fileDiagnostics = allDiagnostics[filePath] if (!fileDiagnostics?.length) { return { - content: [{ type: "text", text: "No errors found" }], - }; + content: [{ type: 'text', text: 'No errors found' }], + } } - const formatted = fileDiagnostics.map(formatDiagnostic).join("\n"); + const formatted = fileDiagnostics.map(formatDiagnostic).join('\n') return { - content: [{ type: "text", text: formatted }], - }; + content: [{ type: 'text', text: formatted }], + } } private async handleHover(args: unknown): Promise { @@ -168,87 +170,87 @@ export class LSPProvider implements Provider { line: z.number(), character: z.number(), }) - .parse(args); + .parse(args) const filePath = path.isAbsolute(parsed.file) ? parsed.file - : path.join(this.config.projectPath, parsed.file); + : path.join(this.config.projectPath, parsed.file) - await this.manager!.touchFile(filePath, true); + await this.manager!.touchFile(filePath, true) const results = await this.manager!.hover({ file: filePath, line: parsed.line, character: parsed.character, - }); + }) - const validResults = results.filter(Boolean); + const validResults = results.filter(Boolean) if (!validResults.length) { return { content: [ - { type: "text", text: "No hover information available at this position" }, + { type: 'text', text: 'No hover information available at this position' }, ], - }; + } } return { content: [ - { type: "text", text: JSON.stringify(validResults, null, 2) }, + { type: 'text', text: JSON.stringify(validResults, null, 2) }, ], - }; + } } private async handleWorkspaceSymbol(args: unknown): Promise { - const parsed = z.object({ query: z.string() }).parse(args); - const symbols = await this.manager!.workspaceSymbol(parsed.query); + const parsed = z.object({ query: z.string() }).parse(args) + const symbols = await this.manager!.workspaceSymbol(parsed.query) if (!symbols.length) { return { - content: [{ type: "text", text: `No symbols found matching "${parsed.query}"` }], - }; + content: [{ type: 'text', text: `No symbols found matching "${parsed.query}"` }], + } } return { - content: [{ type: "text", text: JSON.stringify(symbols, null, 2) }], - }; + content: [{ type: 'text', text: JSON.stringify(symbols, null, 2) }], + } } private async handleDocumentSymbol(args: unknown): Promise { - const parsed = z.object({ file: z.string() }).parse(args); + const parsed = z.object({ file: z.string() }).parse(args) const filePath = path.isAbsolute(parsed.file) ? parsed.file - : path.join(this.config.projectPath, parsed.file); + : path.join(this.config.projectPath, parsed.file) - await this.manager!.touchFile(filePath, false); - const uri = `file://${filePath}`; - const symbols = await this.manager!.documentSymbol(uri); + await this.manager!.touchFile(filePath, false) + const uri = `file://${filePath}` + const symbols = await this.manager!.documentSymbol(uri) if (!symbols.length) { return { - content: [{ type: "text", text: "No symbols found in this document" }], - }; + content: [{ type: 'text', text: 'No symbols found in this document' }], + } } return { - content: [{ type: "text", text: JSON.stringify(symbols, null, 2) }], - }; + content: [{ type: 'text', text: JSON.stringify(symbols, null, 2) }], + } } private async handleStatus(): Promise { - const status = await this.manager!.status(); + const status = await this.manager!.status() if (!status.length) { return { - content: [{ type: "text", text: "No LSP servers connected" }], - }; + content: [{ type: 'text', text: 'No LSP servers connected' }], + } } const formatted = status - .map((s) => `${s.name}: ${s.status} (root: ${s.root})`) - .join("\n"); + .map(s => `${s.name}: ${s.status} (root: ${s.root})`) + .join('\n') return { - content: [{ type: "text", text: formatted }], - }; + content: [{ type: 'text', text: formatted }], + } } } @@ -256,5 +258,5 @@ export class LSPProvider implements Provider { * Factory function for creating LSPProvider */ export function createLSPProvider(config: RegistryConfig): LSPProvider { - return new LSPProvider(config); + return new LSPProvider(config) } diff --git a/packages/dora/src/providers/provider.ts b/packages/dora/src/providers/provider.ts index 8ea3504..1c4c0cb 100644 --- a/packages/dora/src/providers/provider.ts +++ b/packages/dora/src/providers/provider.ts @@ -2,24 +2,24 @@ * Provider interface for tool backends (JetBrains MCP, LSP, etc.) */ -import type { z } from "zod"; +import type { z } from 'zod' /** * Tool definition exposed by a provider */ export interface ToolDefinition { - name: string; - description: string; - inputSchema: z.ZodType; + name: string + description: string + inputSchema: z.ZodType } /** * Result of a tool call */ export interface ToolResult { - [key: string]: unknown; - content: Array<{ type: "text"; text: string }>; - isError?: boolean; + [key: string]: unknown + content: Array<{ type: 'text', text: string }> + isError?: boolean } /** @@ -27,19 +27,19 @@ export interface ToolResult { */ export interface Provider { /** Provider name for identification */ - readonly name: string; + readonly name: string /** Connect to the backend */ - connect(): Promise; + connect: () => Promise /** Disconnect from the backend */ - disconnect(): Promise; + disconnect: () => Promise /** Check if currently connected */ - isConnected(): boolean; + isConnected: () => boolean /** Get list of available tools */ - listTools(): ToolDefinition[]; + listTools: () => ToolDefinition[] /** * Call a tool with given arguments @@ -47,10 +47,10 @@ export interface Provider { * @param args Tool arguments * @returns Tool result */ - callTool(name: string, args: unknown): Promise; + callTool: (name: string, args: unknown) => Promise } /** * Provider type enumeration */ -export type ProviderType = "jetbrains-mcp" | "lsp"; +export type ProviderType = 'jetbrains-mcp' | 'lsp' diff --git a/packages/dora/src/providers/registry.ts b/packages/dora/src/providers/registry.ts index 716cb8a..799ea77 100644 --- a/packages/dora/src/providers/registry.ts +++ b/packages/dora/src/providers/registry.ts @@ -2,38 +2,38 @@ * Provider registry for managing and selecting backends */ -import type { Provider, ProviderType } from "./provider"; +import type { Provider, ProviderType } from './provider' /** * Registry configuration */ export interface RegistryConfig { - projectPath: string; - timeout?: number; + projectPath: string + timeout?: number } /** * Provider factory function type */ -export type ProviderFactory = (config: RegistryConfig) => Provider; +export type ProviderFactory = (config: RegistryConfig) => Provider /** * Registry for managing provider instances */ export class ProviderRegistry { - private factories = new Map(); - private activeProvider: Provider | null = null; - private config: RegistryConfig; + private factories = new Map() + private activeProvider: Provider | null = null + private config: RegistryConfig constructor(config: RegistryConfig) { - this.config = config; + this.config = config } /** * Register a provider factory */ register(type: ProviderType, factory: ProviderFactory): void { - this.factories.set(type, factory); + this.factories.set(type, factory) } /** @@ -42,28 +42,30 @@ export class ProviderRegistry { */ async getProvider(): Promise { if (this.activeProvider?.isConnected()) { - return this.activeProvider; + return this.activeProvider } // Try providers in order of preference - const preferredOrder: ProviderType[] = ["jetbrains-mcp", "lsp"]; + const preferredOrder: ProviderType[] = ['jetbrains-mcp', 'lsp'] for (const type of preferredOrder) { - const factory = this.factories.get(type); - if (!factory) continue; + const factory = this.factories.get(type) + if (!factory) + continue - const provider = factory(this.config); + const provider = factory(this.config) try { - await provider.connect(); - this.activeProvider = provider; - return provider; - } catch { + await provider.connect() + this.activeProvider = provider + return provider + } + catch { // Try next provider - continue; + continue } } - throw new Error("No provider available. Ensure JetBrains IDE is running."); + throw new Error('No provider available. Ensure JetBrains IDE is running.') } /** @@ -71,8 +73,8 @@ export class ProviderRegistry { */ async disconnect(): Promise { if (this.activeProvider) { - await this.activeProvider.disconnect(); - this.activeProvider = null; + await this.activeProvider.disconnect() + this.activeProvider = null } } @@ -80,6 +82,6 @@ export class ProviderRegistry { * Get registered provider types */ getRegisteredTypes(): ProviderType[] { - return Array.from(this.factories.keys()); + return Array.from(this.factories.keys()) } } diff --git a/packages/dora/src/server.ts b/packages/dora/src/server.ts index 7ad103d..673f67a 100644 --- a/packages/dora/src/server.ts +++ b/packages/dora/src/server.ts @@ -2,61 +2,62 @@ * MCP server setup and tool registration */ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { Provider } from './providers' +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import { - ProviderRegistry, createJetBrainsMcpProvider, createLSPProvider, - type Provider, -} from "./providers"; + + ProviderRegistry, +} from './providers' export interface ServerConfig { - projectPath: string; - timeout?: number; + projectPath: string + timeout?: number } /** * Creates and configures the Dora MCP server */ export async function createDoraServer( - config: ServerConfig + config: ServerConfig, ): Promise { const server = new McpServer({ - name: "dora", - version: "0.1.0", - }); + name: 'dora', + version: '0.1.0', + }) // Create provider registry and register providers - const registry = new ProviderRegistry(config); - registry.register("jetbrains-mcp", createJetBrainsMcpProvider); - registry.register("lsp", createLSPProvider); + const registry = new ProviderRegistry(config) + registry.register('jetbrains-mcp', createJetBrainsMcpProvider) + registry.register('lsp', createLSPProvider) // Initialize providers - const jetbrainsProvider = createJetBrainsMcpProvider(config); - const lspProvider = createLSPProvider(config); + const jetbrainsProvider = createJetBrainsMcpProvider(config) + const lspProvider = createLSPProvider(config) // Connect LSP provider immediately (it doesn't require external service) - await lspProvider.connect(); + await lspProvider.connect() // Lazy JetBrains provider initialization - let activeJetbrainsProvider: Provider | null = null; + let activeJetbrainsProvider: Provider | null = null async function getJetbrainsProvider(): Promise { if (activeJetbrainsProvider === null) { - await jetbrainsProvider.connect(); - activeJetbrainsProvider = jetbrainsProvider; + await jetbrainsProvider.connect() + activeJetbrainsProvider = jetbrainsProvider } - return activeJetbrainsProvider; + return activeJetbrainsProvider } // Collect tools from all providers - const jetbrainsTools = jetbrainsProvider.listTools(); - const lspTools = lspProvider.listTools(); - const allTools = [...jetbrainsTools, ...lspTools]; + const jetbrainsTools = jetbrainsProvider.listTools() + const lspTools = lspProvider.listTools() + const allTools = [...jetbrainsTools, ...lspTools] // Map tool names to their providers - const jetbrainsToolNames = new Set(jetbrainsTools.map((t) => t.name)); - const lspToolNames = new Set(lspTools.map((t) => t.name)); + const jetbrainsToolNames = new Set(jetbrainsTools.map(t => t.name)) + const lspToolNames = new Set(lspTools.map(t => t.name)) // Register each tool dynamically for (const tool of allTools) { @@ -70,30 +71,32 @@ export async function createDoraServer( try { // Route to appropriate provider if (lspToolNames.has(tool.name)) { - return await lspProvider.callTool(tool.name, params); - } else if (jetbrainsToolNames.has(tool.name)) { - const provider = await getJetbrainsProvider(); - return await provider.callTool(tool.name, params); + return await lspProvider.callTool(tool.name, params) + } + else if (jetbrainsToolNames.has(tool.name)) { + const provider = await getJetbrainsProvider() + return await provider.callTool(tool.name, params) } return { - content: [{ type: "text", text: `Unknown tool: ${tool.name}` }], + content: [{ type: 'text', text: `Unknown tool: ${tool.name}` }], isError: true, - }; - } catch (error) { + } + } + catch (error) { return { content: [ { - type: "text", + type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, - }; + } } - } - ); + }, + ) } - return server; + return server } diff --git a/packages/dora/src/tools/find-referencing.ts b/packages/dora/src/tools/find-referencing.ts index bb8b02d..fe7cc0d 100644 --- a/packages/dora/src/tools/find-referencing.ts +++ b/packages/dora/src/tools/find-referencing.ts @@ -2,27 +2,27 @@ * jet_brains_find_referencing_symbols tool implementation */ -import type { JetBrainsClient } from "../client/jetbrains-client"; -import type { FindReferencingSymbolsParamsType } from "../types/tool-params"; -import { limitLength } from "./utils"; +import type { JetBrainsClient } from '../client/jetbrains-client' +import type { FindReferencingSymbolsParamsType } from '../types/tool-params' +import { limitLength } from './utils' export const FIND_REFERENCING_DESCRIPTION = `Finds symbols that reference the given symbol using the JetBrains backend. The result contains metadata about the referencing symbols including their locations. -Use this tool to understand code dependencies and find all usages of a symbol.`; +Use this tool to understand code dependencies and find all usages of a symbol.` /** * Executes the find_referencing_symbols tool */ export async function executeFindReferencingSymbols( client: JetBrainsClient, - params: FindReferencingSymbolsParamsType + params: FindReferencingSymbolsParamsType, ): Promise { const response = await client.findReferences({ namePath: params.name_path, relativePath: params.relative_path, - }); + }) - const result = JSON.stringify(response, null, 2); - return limitLength(result, params.max_answer_chars); + const result = JSON.stringify(response, null, 2) + return limitLength(result, params.max_answer_chars) } diff --git a/packages/dora/src/tools/find-symbol.ts b/packages/dora/src/tools/find-symbol.ts index bf046eb..19d0700 100644 --- a/packages/dora/src/tools/find-symbol.ts +++ b/packages/dora/src/tools/find-symbol.ts @@ -2,9 +2,9 @@ * jet_brains_find_symbol tool implementation */ -import type { JetBrainsClient } from "../client/jetbrains-client"; -import type { FindSymbolParamsType } from "../types/tool-params"; -import { limitLength } from "./utils"; +import type { JetBrainsClient } from '../client/jetbrains-client' +import type { FindSymbolParamsType } from '../types/tool-params' +import { limitLength } from './utils' export const FIND_SYMBOL_DESCRIPTION = `Retrieves information on all symbols/code entities (classes, methods, etc.) based on the given name path pattern. The returned symbol information can be used for edits or further queries. @@ -19,14 +19,14 @@ To search for a symbol, provide a name path pattern: - A relative path like "class/method" matches any symbol with that name path suffix - An absolute path "/class/method" requires exact match of the full name path -Returns a JSON list of symbols with locations matching the pattern.`; +Returns a JSON list of symbols with locations matching the pattern.` /** * Executes the find_symbol tool */ export async function executeFindSymbol( client: JetBrainsClient, - params: FindSymbolParamsType + params: FindSymbolParamsType, ): Promise { const response = await client.findSymbol({ namePath: params.name_path_pattern, @@ -35,8 +35,8 @@ export async function executeFindSymbol( depth: params.depth, includeLocation: true, // Always include location for useful output searchDeps: params.search_deps, - }); + }) - const result = JSON.stringify(response, null, 2); - return limitLength(result, params.max_answer_chars); + const result = JSON.stringify(response, null, 2) + return limitLength(result, params.max_answer_chars) } diff --git a/packages/dora/src/tools/get-symbols-overview.ts b/packages/dora/src/tools/get-symbols-overview.ts index 1dc8070..85152d8 100644 --- a/packages/dora/src/tools/get-symbols-overview.ts +++ b/packages/dora/src/tools/get-symbols-overview.ts @@ -2,9 +2,9 @@ * jet_brains_get_symbols_overview tool implementation */ -import type { JetBrainsClient } from "../client/jetbrains-client"; -import type { GetSymbolsOverviewParamsType } from "../types/tool-params"; -import { limitLength } from "./utils"; +import type { JetBrainsClient } from '../client/jetbrains-client' +import type { GetSymbolsOverviewParamsType } from '../types/tool-params' +import { limitLength } from './utils' export const GET_SYMBOLS_OVERVIEW_DESCRIPTION = `Gets an overview of the top-level symbols in the given file using the JetBrains backend. Calling this is often a good idea before more targeted reading, searching or editing operations. @@ -12,19 +12,19 @@ Calling this is often a good idea before more targeted reading, searching or edi Before requesting a symbol overview, narrow down the scope by first understanding the basic directory structure of the repository. -Returns a JSON object containing the symbols in the file.`; +Returns a JSON object containing the symbols in the file.` /** * Executes the get_symbols_overview tool */ export async function executeGetSymbolsOverview( client: JetBrainsClient, - params: GetSymbolsOverviewParamsType + params: GetSymbolsOverviewParamsType, ): Promise { const response = await client.getSymbolsOverview({ relativePath: params.relative_path, - }); + }) - const result = JSON.stringify(response, null, 2); - return limitLength(result, params.max_answer_chars); + const result = JSON.stringify(response, null, 2) + return limitLength(result, params.max_answer_chars) } diff --git a/packages/dora/src/tools/index.ts b/packages/dora/src/tools/index.ts index 750d6fb..3d1e97f 100644 --- a/packages/dora/src/tools/index.ts +++ b/packages/dora/src/tools/index.ts @@ -1,13 +1,13 @@ export { - FIND_SYMBOL_DESCRIPTION, - executeFindSymbol, -} from "./find-symbol"; -export { - FIND_REFERENCING_DESCRIPTION, executeFindReferencingSymbols, -} from "./find-referencing"; + FIND_REFERENCING_DESCRIPTION, +} from './find-referencing' +export { + executeFindSymbol, + FIND_SYMBOL_DESCRIPTION, +} from './find-symbol' export { - GET_SYMBOLS_OVERVIEW_DESCRIPTION, executeGetSymbolsOverview, -} from "./get-symbols-overview"; -export { limitLength } from "./utils"; + GET_SYMBOLS_OVERVIEW_DESCRIPTION, +} from './get-symbols-overview' +export { limitLength } from './utils' diff --git a/packages/dora/src/tools/utils.ts b/packages/dora/src/tools/utils.ts index 3f72849..5624842 100644 --- a/packages/dora/src/tools/utils.ts +++ b/packages/dora/src/tools/utils.ts @@ -9,18 +9,19 @@ * @returns The original string or a truncation message */ export function limitLength(result: string, maxChars: number): string { - if (maxChars === -1) return result; + if (maxChars === -1) + return result if (maxChars <= 0) { - throw new Error(`max_answer_chars must be positive or -1, got: ${maxChars}`); + throw new Error(`max_answer_chars must be positive or -1, got: ${maxChars}`) } if (result.length > maxChars) { return ( - `The answer is too long (${result.length} characters). ` + - `Please try a more specific tool query or raise the max_answer_chars parameter.` - ); + `The answer is too long (${result.length} characters). ` + + `Please try a more specific tool query or raise the max_answer_chars parameter.` + ) } - return result; + return result } diff --git a/packages/dora/src/types/api.ts b/packages/dora/src/types/api.ts index aed394f..ec15e6f 100644 --- a/packages/dora/src/types/api.ts +++ b/packages/dora/src/types/api.ts @@ -6,69 +6,69 @@ * Symbol location in source file */ export interface SymbolLocation { - start_line: number; - end_line: number; - start_column: number; - end_column: number; + start_line: number + end_line: number + start_column: number + end_column: number } /** * Symbol information returned by the JetBrains plugin */ export interface SymbolInfo { - name: string; - name_path: string; - kind: string; - relative_path: string; - location?: SymbolLocation; - body?: string; - children?: SymbolInfo[]; + name: string + name_path: string + kind: string + relative_path: string + location?: SymbolLocation + body?: string + children?: SymbolInfo[] } /** * Request for POST /findSymbol */ export interface FindSymbolRequest { - namePath: string; - relativePath?: string | null; - includeBody: boolean; - depth: number; - includeLocation: boolean; - searchDeps: boolean; + namePath: string + relativePath?: string | null + includeBody: boolean + depth: number + includeLocation: boolean + searchDeps: boolean } /** * Response from POST /findSymbol */ export interface FindSymbolResponse { - symbols: SymbolInfo[]; + symbols: SymbolInfo[] } /** * Request for POST /findReferences */ export interface FindReferencesRequest { - namePath: string; - relativePath: string; + namePath: string + relativePath: string } /** * Response from POST /findReferences */ export interface FindReferencesResponse { - symbols: SymbolInfo[]; + symbols: SymbolInfo[] } /** * Request for POST /getSymbolsOverview */ export interface GetSymbolsOverviewRequest { - relativePath: string; + relativePath: string } /** * Response from POST /getSymbolsOverview */ export interface GetSymbolsOverviewResponse { - symbols: SymbolInfo[]; + symbols: SymbolInfo[] } diff --git a/packages/dora/src/types/index.ts b/packages/dora/src/types/index.ts index 8052ab4..9e371c3 100644 --- a/packages/dora/src/types/index.ts +++ b/packages/dora/src/types/index.ts @@ -1,2 +1,2 @@ -export * from "./api"; -export * from "./tool-params"; +export * from './api' +export * from './tool-params' diff --git a/packages/dora/src/types/tool-params.ts b/packages/dora/src/types/tool-params.ts index 872ea9b..f98d1e4 100644 --- a/packages/dora/src/types/tool-params.ts +++ b/packages/dora/src/types/tool-params.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from 'zod' /** * Schema for jet_brains_find_symbol tool parameters @@ -7,11 +7,11 @@ export const FindSymbolParams = z.object({ name_path_pattern: z .string() .describe( - "The name path matching pattern. Can be: " + - '(1) a simple name like "method" matching any symbol with that name, ' + - '(2) a relative path like "class/method" matching name path suffixes, or ' + - '(3) an absolute path like "/class/method" requiring exact match. ' + - 'Append [i] to match a specific overload (e.g., "MyClass/method[1]").' + 'The name path matching pattern. Can be: ' + + '(1) a simple name like "method" matching any symbol with that name, ' + + '(2) a relative path like "class/method" matching name path suffixes, or ' + + '(3) an absolute path like "/class/method" requiring exact match. ' + + 'Append [i] to match a specific overload (e.g., "MyClass/method[1]").', ), depth: z .number() @@ -19,35 +19,35 @@ export const FindSymbolParams = z.object({ .min(0) .default(0) .describe( - "Depth up to which descendants shall be retrieved. " + - "Use 1 to also retrieve immediate children (e.g., methods of a class). Default 0." + 'Depth up to which descendants shall be retrieved. ' + + 'Use 1 to also retrieve immediate children (e.g., methods of a class). Default 0.', ), relative_path: z .string() .nullable() .optional() .describe( - "Optional. Restrict search to this file or directory. " + - "If None, searches entire codebase. Using this significantly speeds up the search." + 'Optional. Restrict search to this file or directory. ' + + 'If None, searches entire codebase. Using this significantly speeds up the search.', ), include_body: z .boolean() .default(false) - .describe("If true, include the symbol source code. Use judiciously."), + .describe('If true, include the symbol source code. Use judiciously.'), search_deps: z .boolean() .default(false) .describe( - "If true, also search in project dependencies (e.g., libraries)." + 'If true, also search in project dependencies (e.g., libraries).', ), max_answer_chars: z .number() .int() .default(-1) - .describe("Max characters for the JSON result. -1 means no limit."), -}); + .describe('Max characters for the JSON result. -1 means no limit.'), +}) -export type FindSymbolParamsType = z.infer; +export type FindSymbolParamsType = z.infer /** * Schema for jet_brains_find_referencing_symbols tool parameters @@ -56,25 +56,25 @@ export const FindReferencingSymbolsParams = z.object({ name_path: z .string() .describe( - "Name path of the symbol for which to find references. " + - "Uses same matching logic as find_symbol tool." + 'Name path of the symbol for which to find references. ' + + 'Uses same matching logic as find_symbol tool.', ), relative_path: z .string() .describe( - "The relative path to the file containing the symbol. " + - "Must be a file path, not a directory." + 'The relative path to the file containing the symbol. ' + + 'Must be a file path, not a directory.', ), max_answer_chars: z .number() .int() .default(-1) - .describe("Max characters for the JSON result. -1 means no limit."), -}); + .describe('Max characters for the JSON result. -1 means no limit.'), +}) export type FindReferencingSymbolsParamsType = z.infer< typeof FindReferencingSymbolsParams ->; +> /** * Schema for jet_brains_get_symbols_overview tool parameters @@ -82,14 +82,14 @@ export type FindReferencingSymbolsParamsType = z.infer< export const GetSymbolsOverviewParams = z.object({ relative_path: z .string() - .describe("The relative path to the file to get the overview of."), + .describe('The relative path to the file to get the overview of.'), max_answer_chars: z .number() .int() .default(-1) - .describe("Max characters for the JSON result. -1 means no limit."), -}); + .describe('Max characters for the JSON result. -1 means no limit.'), +}) export type GetSymbolsOverviewParamsType = z.infer< typeof GetSymbolsOverviewParams ->; +> diff --git a/packages/format/package.json b/packages/format/package.json index 34bee56..3b88eb9 100644 --- a/packages/format/package.json +++ b/packages/format/package.json @@ -1,12 +1,27 @@ { "name": "@pleaseai/code-format", + "type": "module", "version": "0.1.0", "description": "Auto-format hooks for AI coding", - "type": "module", - "main": "src/index.ts", + "author": "Minsu Lee", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/chatbot-pf/code-please" + }, + "keywords": [ + "formatter", + "biome", + "prettier", + "ai-coding" + ], "exports": { ".": "./src/index.ts" }, + "main": "src/index.ts", + "files": [ + "src" + ], "scripts": { "typecheck": "tsc -p tsconfig.json --noEmit", "test": "echo 'No tests yet'" @@ -17,20 +32,5 @@ "devDependencies": { "@types/bun": "latest", "typescript": "^5.7.0" - }, - "files": [ - "src" - ], - "keywords": [ - "formatter", - "biome", - "prettier", - "ai-coding" - ], - "author": "Minsu Lee", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/chatbot-pf/code-please" } } diff --git a/packages/format/src/config/index.ts b/packages/format/src/config/index.ts index 68ed581..39f3773 100644 --- a/packages/format/src/config/index.ts +++ b/packages/format/src/config/index.ts @@ -1,2 +1,2 @@ -export * from "./schema" -export * from "./loader" +export * from './loader' +export * from './schema' diff --git a/packages/format/src/config/loader.ts b/packages/format/src/config/loader.ts index e9ac472..0adc2d5 100644 --- a/packages/format/src/config/loader.ts +++ b/packages/format/src/config/loader.ts @@ -1,17 +1,18 @@ -import path from "path" -import YAML from "yaml" -import { ConfigSchema, defaultConfig, type Config } from "./schema" +import type { Config } from './schema' +import path from 'node:path' +import YAML from 'yaml' +import { ConfigSchema, defaultConfig } from './schema' /** * Configuration file names to search for (in order of priority) */ const CONFIG_FILES = [ - "opencode.json", - "dora.json", - ".please/config.yml", - ".please/config.yaml", - ".dora/config.yml", - ".dora/config.yaml", + 'opencode.json', + 'dora.json', + '.please/config.yml', + '.please/config.yaml', + '.dora/config.yml', + '.dora/config.yaml', ] /** @@ -19,7 +20,7 @@ const CONFIG_FILES = [ */ async function findConfigFile( startDir: string, - stopDir?: string + stopDir?: string, ): Promise { let currentDir = startDir const root = stopDir ?? path.parse(startDir).root @@ -43,16 +44,17 @@ async function findConfigFile( */ function parseConfigContent(content: string, filePath: string): unknown { const ext = path.extname(filePath).toLowerCase() - if (ext === ".json") { + if (ext === '.json') { return JSON.parse(content) } - if (ext === ".yml" || ext === ".yaml") { + if (ext === '.yml' || ext === '.yaml') { return YAML.parse(content) } // Try JSON first, then YAML try { return JSON.parse(content) - } catch { + } + catch { return YAML.parse(content) } } @@ -100,9 +102,11 @@ export function mergeConfig(base: Config, source: Partial): Config { if (source.formatter !== undefined) { if (source.formatter === false) { merged.formatter = false - } else if (base.formatter === false) { + } + else if (base.formatter === false) { merged.formatter = source.formatter - } else { + } + else { merged.formatter = { ...(base.formatter ?? {}), ...source.formatter, diff --git a/packages/format/src/config/schema.ts b/packages/format/src/config/schema.ts index 26e690a..1a67a5f 100644 --- a/packages/format/src/config/schema.ts +++ b/packages/format/src/config/schema.ts @@ -1,4 +1,4 @@ -import { z } from "zod" +import { z } from 'zod' /** * Formatter configuration schema diff --git a/packages/format/src/formatter.ts b/packages/format/src/formatter.ts index fcaa215..2e76b9f 100644 --- a/packages/format/src/formatter.ts +++ b/packages/format/src/formatter.ts @@ -1,11 +1,12 @@ -import path from "path" +import path from 'node:path' +import process from 'node:process' export interface Info { name: string command: string[] environment?: Record extensions: string[] - enabled(projectDir: string): Promise + enabled: (projectDir: string) => Promise } /** @@ -14,7 +15,7 @@ export interface Info { async function findUp( filename: string, startDir: string, - stopDir?: string + stopDir?: string, ): Promise { const results: string[] = [] let currentDir = startDir @@ -33,108 +34,110 @@ async function findUp( } function bunExecutable(): string { - return process.execPath ?? "bun" + return process.execPath ?? 'bun' } export const gofmt: Info = { - name: "gofmt", - command: ["gofmt", "-w", "$FILE"], - extensions: [".go"], + name: 'gofmt', + command: ['gofmt', '-w', '$FILE'], + extensions: ['.go'], async enabled() { - return Bun.which("gofmt") !== null + return Bun.which('gofmt') !== null }, } export const mix: Info = { - name: "mix", - command: ["mix", "format", "$FILE"], - extensions: [".ex", ".exs", ".eex", ".heex", ".leex", ".neex", ".sface"], + name: 'mix', + command: ['mix', 'format', '$FILE'], + extensions: ['.ex', '.exs', '.eex', '.heex', '.leex', '.neex', '.sface'], async enabled() { - return Bun.which("mix") !== null + return Bun.which('mix') !== null }, } export const prettier: Info = { - name: "prettier", - command: [bunExecutable(), "x", "prettier", "--write", "$FILE"], + name: 'prettier', + command: [bunExecutable(), 'x', 'prettier', '--write', '$FILE'], environment: { - BUN_BE_BUN: "1", + BUN_BE_BUN: '1', }, extensions: [ - ".js", - ".jsx", - ".mjs", - ".cjs", - ".ts", - ".tsx", - ".mts", - ".cts", - ".html", - ".htm", - ".css", - ".scss", - ".sass", - ".less", - ".vue", - ".svelte", - ".json", - ".jsonc", - ".yaml", - ".yml", - ".toml", - ".xml", - ".md", - ".mdx", - ".graphql", - ".gql", + '.js', + '.jsx', + '.mjs', + '.cjs', + '.ts', + '.tsx', + '.mts', + '.cts', + '.html', + '.htm', + '.css', + '.scss', + '.sass', + '.less', + '.vue', + '.svelte', + '.json', + '.jsonc', + '.yaml', + '.yml', + '.toml', + '.xml', + '.md', + '.mdx', + '.graphql', + '.gql', ], async enabled(projectDir: string) { - const items = await findUp("package.json", projectDir) + const items = await findUp('package.json', projectDir) for (const item of items) { const json = await Bun.file(item).json() - if (json.dependencies?.prettier) return true - if (json.devDependencies?.prettier) return true + if (json.dependencies?.prettier) + return true + if (json.devDependencies?.prettier) + return true } return false }, } export const biome: Info = { - name: "biome", - command: [bunExecutable(), "x", "@biomejs/biome", "format", "--write", "$FILE"], + name: 'biome', + command: [bunExecutable(), 'x', '@biomejs/biome', 'format', '--write', '$FILE'], environment: { - BUN_BE_BUN: "1", + BUN_BE_BUN: '1', }, extensions: [ - ".js", - ".jsx", - ".mjs", - ".cjs", - ".ts", - ".tsx", - ".mts", - ".cts", - ".html", - ".htm", - ".css", - ".scss", - ".sass", - ".less", - ".vue", - ".svelte", - ".json", - ".jsonc", - ".yaml", - ".yml", - ".toml", - ".xml", - ".md", - ".mdx", - ".graphql", - ".gql", + '.js', + '.jsx', + '.mjs', + '.cjs', + '.ts', + '.tsx', + '.mts', + '.cts', + '.html', + '.htm', + '.css', + '.scss', + '.sass', + '.less', + '.vue', + '.svelte', + '.json', + '.jsonc', + '.yaml', + '.yml', + '.toml', + '.xml', + '.md', + '.mdx', + '.graphql', + '.gql', ], async enabled(projectDir: string) { - const configs = ["biome.json", "biome.jsonc"] + const configs = ['biome.json', 'biome.jsonc'] for (const config of configs) { const found = await findUp(config, projectDir) if (found.length > 0) { @@ -146,59 +149,63 @@ export const biome: Info = { } export const zig: Info = { - name: "zig", - command: ["zig", "fmt", "$FILE"], - extensions: [".zig", ".zon"], + name: 'zig', + command: ['zig', 'fmt', '$FILE'], + extensions: ['.zig', '.zon'], async enabled() { - return Bun.which("zig") !== null + return Bun.which('zig') !== null }, } export const clang: Info = { - name: "clang-format", - command: ["clang-format", "-i", "$FILE"], - extensions: [".c", ".cc", ".cpp", ".cxx", ".c++", ".h", ".hh", ".hpp", ".hxx", ".h++", ".ino", ".C", ".H"], + name: 'clang-format', + command: ['clang-format', '-i', '$FILE'], + extensions: ['.c', '.cc', '.cpp', '.cxx', '.c++', '.h', '.hh', '.hpp', '.hxx', '.h++', '.ino', '.C', '.H'], async enabled(projectDir: string) { - const items = await findUp(".clang-format", projectDir) + const items = await findUp('.clang-format', projectDir) return items.length > 0 }, } export const ktlint: Info = { - name: "ktlint", - command: ["ktlint", "-F", "$FILE"], - extensions: [".kt", ".kts"], + name: 'ktlint', + command: ['ktlint', '-F', '$FILE'], + extensions: ['.kt', '.kts'], async enabled() { - return Bun.which("ktlint") !== null + return Bun.which('ktlint') !== null }, } export const ruff: Info = { - name: "ruff", - command: ["ruff", "format", "$FILE"], - extensions: [".py", ".pyi"], + name: 'ruff', + command: ['ruff', 'format', '$FILE'], + extensions: ['.py', '.pyi'], async enabled(projectDir: string) { - if (!Bun.which("ruff")) return false - const configs = ["pyproject.toml", "ruff.toml", ".ruff.toml"] + if (!Bun.which('ruff')) + return false + const configs = ['pyproject.toml', 'ruff.toml', '.ruff.toml'] for (const config of configs) { const found = await findUp(config, projectDir) const firstFound = found[0] if (firstFound) { - if (config === "pyproject.toml") { + if (config === 'pyproject.toml') { const content = await Bun.file(firstFound).text() - if (content.includes("[tool.ruff]")) return true - } else { + if (content.includes('[tool.ruff]')) + return true + } + else { return true } } } - const deps = ["requirements.txt", "pyproject.toml", "Pipfile"] + const deps = ['requirements.txt', 'pyproject.toml', 'Pipfile'] for (const dep of deps) { const found = await findUp(dep, projectDir) const firstFound = found[0] if (firstFound) { const content = await Bun.file(firstFound).text() - if (content.includes("ruff")) return true + if (content.includes('ruff')) + return true } } return false @@ -206,40 +213,43 @@ export const ruff: Info = { } export const rlang: Info = { - name: "air", - command: ["air", "format", "$FILE"], - extensions: [".R"], + name: 'air', + command: ['air', 'format', '$FILE'], + extensions: ['.R'], async enabled() { - const airPath = Bun.which("air") - if (airPath == null) return false + const airPath = Bun.which('air') + if (airPath == null) + return false try { - const proc = Bun.spawn(["air", "--help"], { - stdout: "pipe", - stderr: "pipe", + const proc = Bun.spawn(['air', '--help'], { + stdout: 'pipe', + stderr: 'pipe', }) await proc.exited const output = await new Response(proc.stdout).text() // Check for "Air: An R language server and formatter" - const firstLine = output.split("\n")[0] ?? "" - const hasR = firstLine.includes("R language") - const hasFormatter = firstLine.includes("formatter") + const firstLine = output.split('\n')[0] ?? '' + const hasR = firstLine.includes('R language') + const hasFormatter = firstLine.includes('formatter') return hasR && hasFormatter - } catch { + } + catch { return false } }, } export const uvformat: Info = { - name: "uv format", - command: ["uv", "format", "--", "$FILE"], - extensions: [".py", ".pyi"], + name: 'uv format', + command: ['uv', 'format', '--', '$FILE'], + extensions: ['.py', '.pyi'], async enabled(projectDir: string) { - if (await ruff.enabled(projectDir)) return false - if (Bun.which("uv") !== null) { - const proc = Bun.spawn(["uv", "format", "--help"], { stderr: "pipe", stdout: "pipe" }) + if (await ruff.enabled(projectDir)) + return false + if (Bun.which('uv') !== null) { + const proc = Bun.spawn(['uv', 'format', '--help'], { stderr: 'pipe', stdout: 'pipe' }) const code = await proc.exited return code === 0 } @@ -248,75 +258,76 @@ export const uvformat: Info = { } export const rubocop: Info = { - name: "rubocop", - command: ["rubocop", "--autocorrect", "$FILE"], - extensions: [".rb", ".rake", ".gemspec", ".ru"], + name: 'rubocop', + command: ['rubocop', '--autocorrect', '$FILE'], + extensions: ['.rb', '.rake', '.gemspec', '.ru'], async enabled() { - return Bun.which("rubocop") !== null + return Bun.which('rubocop') !== null }, } export const standardrb: Info = { - name: "standardrb", - command: ["standardrb", "--fix", "$FILE"], - extensions: [".rb", ".rake", ".gemspec", ".ru"], + name: 'standardrb', + command: ['standardrb', '--fix', '$FILE'], + extensions: ['.rb', '.rake', '.gemspec', '.ru'], async enabled() { - return Bun.which("standardrb") !== null + return Bun.which('standardrb') !== null }, } export const htmlbeautifier: Info = { - name: "htmlbeautifier", - command: ["htmlbeautifier", "$FILE"], - extensions: [".erb", ".html.erb"], + name: 'htmlbeautifier', + command: ['htmlbeautifier', '$FILE'], + extensions: ['.erb', '.html.erb'], async enabled() { - return Bun.which("htmlbeautifier") !== null + return Bun.which('htmlbeautifier') !== null }, } export const dart: Info = { - name: "dart", - command: ["dart", "format", "$FILE"], - extensions: [".dart"], + name: 'dart', + command: ['dart', 'format', '$FILE'], + extensions: ['.dart'], async enabled() { - return Bun.which("dart") !== null + return Bun.which('dart') !== null }, } export const ocamlformat: Info = { - name: "ocamlformat", - command: ["ocamlformat", "-i", "$FILE"], - extensions: [".ml", ".mli"], + name: 'ocamlformat', + command: ['ocamlformat', '-i', '$FILE'], + extensions: ['.ml', '.mli'], async enabled(projectDir: string) { - if (!Bun.which("ocamlformat")) return false - const items = await findUp(".ocamlformat", projectDir) + if (!Bun.which('ocamlformat')) + return false + const items = await findUp('.ocamlformat', projectDir) return items.length > 0 }, } export const terraform: Info = { - name: "terraform", - command: ["terraform", "fmt", "$FILE"], - extensions: [".tf", ".tfvars"], + name: 'terraform', + command: ['terraform', 'fmt', '$FILE'], + extensions: ['.tf', '.tfvars'], async enabled() { - return Bun.which("terraform") !== null + return Bun.which('terraform') !== null }, } export const latexindent: Info = { - name: "latexindent", - command: ["latexindent", "-w", "-s", "$FILE"], - extensions: [".tex"], + name: 'latexindent', + command: ['latexindent', '-w', '-s', '$FILE'], + extensions: ['.tex'], async enabled() { - return Bun.which("latexindent") !== null + return Bun.which('latexindent') !== null }, } export const gleam: Info = { - name: "gleam", - command: ["gleam", "format", "$FILE"], - extensions: [".gleam"], + name: 'gleam', + command: ['gleam', 'format', '$FILE'], + extensions: ['.gleam'], async enabled() { - return Bun.which("gleam") !== null + return Bun.which('gleam') !== null }, -} \ No newline at end of file +} diff --git a/packages/format/src/index.ts b/packages/format/src/index.ts index 27031d2..81d17b0 100644 --- a/packages/format/src/index.ts +++ b/packages/format/src/index.ts @@ -1,192 +1,210 @@ -import path from "path" -import z from "zod" +import type { FormatterConfig } from './config' +import path from 'node:path' +import process from 'node:process' -import * as Formatter from "./formatter" -import { loadConfig, type FormatterConfig } from "./config" +import z from 'zod' +import { loadConfig } from './config' +import * as Formatter from './formatter' -export namespace Format { - const log = { - info: (...args: unknown[]) => console.error("[format]", ...args), - error: (...args: unknown[]) => console.error("[format:error]", ...args), - } +const log = { + info: (...args: unknown[]) => console.error('[format]', ...args), + error: (...args: unknown[]) => console.error('[format:error]', ...args), +} - export const Status = z - .object({ - name: z.string(), - extensions: z.string().array(), - enabled: z.boolean(), - }) - export type Status = z.infer +export const FormatStatusSchema = z + .object({ + name: z.string(), + extensions: z.string().array(), + enabled: z.boolean(), + }) +export type FormatStatus = z.infer + +export interface FormatConfig { + projectDir: string + formatter?: FormatterConfig +} + +interface State { + enabled: Record + formatters: Record + projectDir: string +} - export interface Config { - projectDir: string - formatter?: FormatterConfig +let cachedState: State | null = null + +/** + * Initialize formatters from project configuration files + * Searches for opencode.json, dora.json, or .please/config.yml + */ +async function initFromProject(projectDir: string): Promise { + const config = await loadConfig(projectDir) + const formatConfig: FormatConfig = { projectDir } + if (config.formatter !== undefined) { + formatConfig.formatter = config.formatter } + init(formatConfig) +} + +function init(config: FormatConfig): void { + const enabled: Record = {} + const formatters: Record = {} - interface State { - enabled: Record - formatters: Record - projectDir: string + if (config.formatter === false) { + log.info('all formatters are disabled') + cachedState = { enabled, formatters, projectDir: config.projectDir } + return } - let cachedState: State | null = null - - /** - * Initialize formatters from project configuration files - * Searches for opencode.json, dora.json, or .please/config.yml - */ - export async function initFromProject(projectDir: string): Promise { - const config = await loadConfig(projectDir) - const formatConfig: Config = { projectDir } - if (config.formatter !== undefined) { - formatConfig.formatter = config.formatter + // Register built-in formatters + for (const item of Object.values(Formatter)) { + if (typeof item === 'object' && 'name' in item && 'command' in item) { + formatters[item.name] = item as Formatter.Info } - init(formatConfig) } - export function init(config: Config): void { - const enabled: Record = {} - const formatters: Record = {} - - if (config.formatter === false) { - log.info("all formatters are disabled") - cachedState = { enabled, formatters, projectDir: config.projectDir } - return + // Apply user configuration overrides + for (const [name, item] of Object.entries(config.formatter ?? {})) { + if (item.disabled) { + delete formatters[name] + continue } - // Register built-in formatters - for (const item of Object.values(Formatter)) { - if (typeof item === "object" && "name" in item && "command" in item) { - formatters[item.name] = item as Formatter.Info + const existing = formatters[name] + if (existing) { + // Merge existing formatter with config overrides + const env = item.environment ?? existing.environment + const merged: Formatter.Info = { + ...existing, + command: item.command ?? existing.command, + extensions: item.extensions ?? existing.extensions, } + if (env) + merged.environment = env + formatters[name] = merged } - - // Apply user configuration overrides - for (const [name, item] of Object.entries(config.formatter ?? {})) { - if (item.disabled) { - delete formatters[name] - continue - } - - const existing = formatters[name] - if (existing) { - // Merge existing formatter with config overrides - const env = item.environment ?? existing.environment - const merged: Formatter.Info = { - ...existing, - command: item.command ?? existing.command, - extensions: item.extensions ?? existing.extensions, - } - if (env) merged.environment = env - formatters[name] = merged - } else if (item.command && item.command.length > 0) { - // Create new formatter from config - const newFormatter: Formatter.Info = { - name, - command: item.command, - extensions: item.extensions ?? [], - enabled: async () => true, - } - if (item.environment) newFormatter.environment = item.environment - formatters[name] = newFormatter + else if (item.command && item.command.length > 0) { + // Create new formatter from config + const newFormatter: Formatter.Info = { + name, + command: item.command, + extensions: item.extensions ?? [], + enabled: async () => true, } + if (item.environment) + newFormatter.environment = item.environment + formatters[name] = newFormatter } - - cachedState = { enabled, formatters, projectDir: config.projectDir } - log.info("initialized with", Object.keys(formatters).length, "formatters") } - function getState(): State { - if (!cachedState) { - throw new Error("Format not initialized. Call Format.init() first.") - } - return cachedState + cachedState = { enabled, formatters, projectDir: config.projectDir } + log.info('initialized with', Object.keys(formatters).length, 'formatters') +} + +function getState(): State { + if (!cachedState) { + throw new Error('Format not initialized. Call Format.init() first.') } + return cachedState +} - async function isEnabled(item: Formatter.Info): Promise { - const s = getState() - let status = s.enabled[item.name] - if (status === undefined) { - status = await item.enabled(s.projectDir) - s.enabled[item.name] = status - } - return status +async function isEnabled(item: Formatter.Info): Promise { + const s = getState() + let status = s.enabled[item.name] + if (status === undefined) { + status = await item.enabled(s.projectDir) + s.enabled[item.name] = status } + return status +} - async function getFormatter(ext: string): Promise { - const s = getState() - const result: Formatter.Info[] = [] - for (const item of Object.values(s.formatters)) { - log.info("checking", { name: item.name, ext }) - if (!item.extensions.includes(ext)) continue - if (!(await isEnabled(item))) continue - log.info("enabled", { name: item.name, ext }) - result.push(item) - } - return result +async function getFormatter(ext: string): Promise { + const s = getState() + const result: Formatter.Info[] = [] + for (const item of Object.values(s.formatters)) { + log.info('checking', { name: item.name, ext }) + if (!item.extensions.includes(ext)) + continue + if (!(await isEnabled(item))) + continue + log.info('enabled', { name: item.name, ext }) + result.push(item) } + return result +} - export async function status(): Promise { - const s = getState() - const result: Status[] = [] - for (const formatter of Object.values(s.formatters)) { - const enabled = await isEnabled(formatter) - result.push({ - name: formatter.name, - extensions: formatter.extensions, - enabled, - }) - } - return result +async function status(): Promise { + const s = getState() + const result: FormatStatus[] = [] + for (const formatter of Object.values(s.formatters)) { + const enabled = await isEnabled(formatter) + result.push({ + name: formatter.name, + extensions: formatter.extensions, + enabled, + }) } + return result +} - /** - * Format a file using the appropriate formatter based on file extension - */ - export async function formatFile(file: string): Promise { - const s = getState() - const ext = path.extname(file) - log.info("formatting", { file, ext }) - - const formatters = await getFormatter(ext) - if (formatters.length === 0) { - log.info("no formatter found for", ext) - return false - } +/** + * Format a file using the appropriate formatter based on file extension + */ +async function formatFile(file: string): Promise { + const s = getState() + const ext = path.extname(file) + log.info('formatting', { file, ext }) + + const formatters = await getFormatter(ext) + if (formatters.length === 0) { + log.info('no formatter found for', ext) + return false + } - let success = true - for (const item of formatters) { - log.info("running", { command: item.command }) - try { - const proc = Bun.spawn({ - cmd: item.command.map((x) => x.replace("$FILE", file)), - cwd: s.projectDir, - env: { ...process.env, ...item.environment }, - stdout: "ignore", - stderr: "ignore", - }) - const exit = await proc.exited - if (exit !== 0) { - log.error("failed", { - command: item.command, - ...item.environment, - }) - success = false - } - } catch (error) { - log.error("failed to format file", { - error, + let success = true + for (const item of formatters) { + log.info('running', { command: item.command }) + try { + const proc = Bun.spawn({ + cmd: item.command.map(x => x.replace('$FILE', file)), + cwd: s.projectDir, + env: { ...process.env, ...item.environment }, + stdout: 'ignore', + stderr: 'ignore', + }) + const exit = await proc.exited + if (exit !== 0) { + log.error('failed', { command: item.command, ...item.environment, - file, }) success = false } } - - return success + catch (error) { + log.error('failed to format file', { + error, + command: item.command, + ...item.environment, + file, + }) + success = false + } } + + return success } -export * as Formatter from "./formatter" -export { loadConfig, type Config, type FormatterConfig } from "./config" +// Export as object to maintain backward compatibility +export const Format = { + Status: FormatStatusSchema, + initFromProject, + init, + status, + formatFile, +} as const + +// Type exports for backward compatibility +export type { FormatConfig as Config } + +export { type Config as ConfigType, type FormatterConfig, loadConfig } from './config' +export * as Formatter from './formatter' diff --git a/packages/lsp/package.json b/packages/lsp/package.json index b138171..21574d8 100644 --- a/packages/lsp/package.json +++ b/packages/lsp/package.json @@ -1,12 +1,28 @@ { "name": "@pleaseai/code-lsp", + "type": "module", "version": "0.1.0", "description": "LSP client for AI coding tools", - "type": "module", - "main": "src/index.ts", + "author": "Minsu Lee", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/chatbot-pf/code-please" + }, + "keywords": [ + "lsp", + "language-server", + "typescript", + "python", + "ai-coding" + ], "exports": { ".": "./src/index.ts" }, + "main": "src/index.ts", + "files": [ + "src" + ], "scripts": { "typecheck": "tsc -p tsconfig.json --noEmit", "test": "bun test ./src" @@ -19,21 +35,5 @@ "devDependencies": { "@types/bun": "latest", "typescript": "^5.7.0" - }, - "files": [ - "src" - ], - "keywords": [ - "lsp", - "language-server", - "typescript", - "python", - "ai-coding" - ], - "author": "Minsu Lee", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/chatbot-pf/code-please" } } diff --git a/packages/lsp/src/__tests__/client.test.ts b/packages/lsp/src/__tests__/client.test.ts index 5405a8c..dff7494 100644 --- a/packages/lsp/src/__tests__/client.test.ts +++ b/packages/lsp/src/__tests__/client.test.ts @@ -1,199 +1,200 @@ -import { describe, expect, test, afterEach } from "bun:test"; -import { spawn } from "child_process"; -import path from "path"; -import { createLSPClient, type LSPClientInfo } from "../client"; +import type { LSPClientInfo } from '../client' +import { spawn } from 'node:child_process' +import path from 'node:path' +import { afterEach, describe, expect, test } from 'bun:test' +import { createLSPClient } from '../client' function spawnFakeServer() { - const serverPath = path.join(__dirname, "fixture/fake-lsp-server.js"); + const serverPath = path.join(__dirname, 'fixture/fake-lsp-server.js') return { process: spawn(process.execPath, [serverPath], { - stdio: "pipe", + stdio: 'pipe', }), - }; + } } -describe("LSPClient", () => { - let client: LSPClientInfo | null = null; +describe('LSPClient', () => { + let client: LSPClientInfo | null = null afterEach(async () => { if (client) { - await client.shutdown(); - client = null; + await client.shutdown() + client = null } - }); + }) - test("creates client and initializes connection", async () => { - const handle = spawnFakeServer(); + test('creates client and initializes connection', async () => { + const handle = spawnFakeServer() client = await createLSPClient({ - serverID: "fake", + serverID: 'fake', server: handle, root: process.cwd(), projectPath: process.cwd(), - }); + }) - expect(client).toBeDefined(); - expect(client.serverID).toBe("fake"); - expect(client.root).toBe(process.cwd()); - expect(client.connection).toBeDefined(); - }); + expect(client).toBeDefined() + expect(client.serverID).toBe('fake') + expect(client.root).toBe(process.cwd()) + expect(client.connection).toBeDefined() + }) - test("handles workspace/workspaceFolders request", async () => { - const handle = spawnFakeServer(); + test('handles workspace/workspaceFolders request', async () => { + const handle = spawnFakeServer() client = await createLSPClient({ - serverID: "fake", + serverID: 'fake', server: handle, root: process.cwd(), projectPath: process.cwd(), - }); + }) // Trigger the request via notification - await client.connection.sendNotification("test/trigger", { - method: "workspace/workspaceFolders", - }); + await client.connection.sendNotification('test/trigger', { + method: 'workspace/workspaceFolders', + }) - await new Promise((r) => setTimeout(r, 100)); + await new Promise(r => setTimeout(r, 100)) - expect(client.connection).toBeDefined(); - }); + expect(client.connection).toBeDefined() + }) - test("handles client/registerCapability request", async () => { - const handle = spawnFakeServer(); + test('handles client/registerCapability request', async () => { + const handle = spawnFakeServer() client = await createLSPClient({ - serverID: "fake", + serverID: 'fake', server: handle, root: process.cwd(), projectPath: process.cwd(), - }); + }) - await client.connection.sendNotification("test/trigger", { - method: "client/registerCapability", - }); + await client.connection.sendNotification('test/trigger', { + method: 'client/registerCapability', + }) - await new Promise((r) => setTimeout(r, 100)); + await new Promise(r => setTimeout(r, 100)) - expect(client.connection).toBeDefined(); - }); + expect(client.connection).toBeDefined() + }) - test("handles client/unregisterCapability request", async () => { - const handle = spawnFakeServer(); + test('handles client/unregisterCapability request', async () => { + const handle = spawnFakeServer() client = await createLSPClient({ - serverID: "fake", + serverID: 'fake', server: handle, root: process.cwd(), projectPath: process.cwd(), - }); + }) - await client.connection.sendNotification("test/trigger", { - method: "client/unregisterCapability", - }); + await client.connection.sendNotification('test/trigger', { + method: 'client/unregisterCapability', + }) - await new Promise((r) => setTimeout(r, 100)); + await new Promise(r => setTimeout(r, 100)) - expect(client.connection).toBeDefined(); - }); + expect(client.connection).toBeDefined() + }) - test("opens file and receives diagnostics", async () => { - const handle = spawnFakeServer(); + test('opens file and receives diagnostics', async () => { + const handle = spawnFakeServer() client = await createLSPClient({ - serverID: "fake", + serverID: 'fake', server: handle, root: process.cwd(), projectPath: process.cwd(), - }); + }) // Create a temporary test file - const testFile = path.join(process.cwd(), "package.json"); - await client.notify.open({ path: testFile }); + const testFile = path.join(process.cwd(), 'package.json') + await client.notify.open({ path: testFile }) // Wait for diagnostics - await new Promise((r) => setTimeout(r, 200)); + await new Promise(r => setTimeout(r, 200)) - expect(client.diagnostics.size).toBeGreaterThan(0); - }); + expect(client.diagnostics.size).toBeGreaterThan(0) + }) - test("sends hover request", async () => { - const handle = spawnFakeServer(); + test('sends hover request', async () => { + const handle = spawnFakeServer() client = await createLSPClient({ - serverID: "fake", + serverID: 'fake', server: handle, root: process.cwd(), projectPath: process.cwd(), - }); + }) - const result = await client.connection.sendRequest("textDocument/hover", { - textDocument: { uri: "file:///test.ts" }, + const result = await client.connection.sendRequest('textDocument/hover', { + textDocument: { uri: 'file:///test.ts' }, position: { line: 0, character: 5 }, - }); + }) - expect(result).toBeDefined(); + expect(result).toBeDefined() expect((result as { contents: { value: string } }).contents.value).toContain( - "Test hover" - ); - }); + 'Test hover', + ) + }) - test("sends workspace symbol request", async () => { - const handle = spawnFakeServer(); + test('sends workspace symbol request', async () => { + const handle = spawnFakeServer() client = await createLSPClient({ - serverID: "fake", + serverID: 'fake', server: handle, root: process.cwd(), projectPath: process.cwd(), - }); + }) - const result = (await client.connection.sendRequest("workspace/symbol", { - query: "test", - })) as Array<{ name: string }>; + const result = (await client.connection.sendRequest('workspace/symbol', { + query: 'test', + })) as Array<{ name: string }> - expect(result).toBeDefined(); - expect(Array.isArray(result)).toBe(true); - expect(result.length).toBeGreaterThan(0); - expect(result[0]!.name).toBe("testFunction"); - }); + expect(result).toBeDefined() + expect(Array.isArray(result)).toBe(true) + expect(result.length).toBeGreaterThan(0) + expect(result[0]!.name).toBe('testFunction') + }) - test("sends document symbol request", async () => { - const handle = spawnFakeServer(); + test('sends document symbol request', async () => { + const handle = spawnFakeServer() client = await createLSPClient({ - serverID: "fake", + serverID: 'fake', server: handle, root: process.cwd(), projectPath: process.cwd(), - }); + }) const result = (await client.connection.sendRequest( - "textDocument/documentSymbol", + 'textDocument/documentSymbol', { - textDocument: { uri: "file:///test.ts" }, - } - )) as Array<{ name: string }>; + textDocument: { uri: 'file:///test.ts' }, + }, + )) as Array<{ name: string }> - expect(result).toBeDefined(); - expect(Array.isArray(result)).toBe(true); - expect(result.length).toBeGreaterThan(0); - expect(result[0]!.name).toBe("TestClass"); - }); + expect(result).toBeDefined() + expect(Array.isArray(result)).toBe(true) + expect(result.length).toBeGreaterThan(0) + expect(result[0]!.name).toBe('TestClass') + }) - test("shuts down cleanly", async () => { - const handle = spawnFakeServer(); + test('shuts down cleanly', async () => { + const handle = spawnFakeServer() client = await createLSPClient({ - serverID: "fake", + serverID: 'fake', server: handle, root: process.cwd(), projectPath: process.cwd(), - }); + }) - await client.shutdown(); - client = null; // Prevent double shutdown in afterEach + await client.shutdown() + client = null // Prevent double shutdown in afterEach // Process should be killed - expect(handle.process.killed).toBe(true); - }); -}); + expect(handle.process.killed).toBe(true) + }) +}) diff --git a/packages/lsp/src/__tests__/fixture/fake-lsp-server.js b/packages/lsp/src/__tests__/fixture/fake-lsp-server.js index 9ba1455..7b84336 100644 --- a/packages/lsp/src/__tests__/fixture/fake-lsp-server.js +++ b/packages/lsp/src/__tests__/fixture/fake-lsp-server.js @@ -1,69 +1,75 @@ // Simple JSON-RPC 2.0 LSP-like fake server over stdio // Implements a minimal LSP handshake and publishes diagnostics -let nextId = 1; +import { Buffer } from 'node:buffer' +import process from 'node:process' + +let nextId = 1 function encode(message) { - const json = JSON.stringify(message); - const header = `Content-Length: ${Buffer.byteLength(json, "utf8")}\r\n\r\n`; + const json = JSON.stringify(message) + const header = `Content-Length: ${Buffer.byteLength(json, 'utf8')}\r\n\r\n` return Buffer.concat([ - Buffer.from(header, "utf8"), - Buffer.from(json, "utf8"), - ]); + Buffer.from(header, 'utf8'), + Buffer.from(json, 'utf8'), + ]) } function decodeFrames(buffer) { - const results = []; - let idx; - while ((idx = buffer.indexOf("\r\n\r\n")) !== -1) { - const header = buffer.slice(0, idx).toString("utf8"); - const m = /Content-Length:\s*(\d+)/i.exec(header); - const len = m ? parseInt(m[1], 10) : 0; - const bodyStart = idx + 4; - const bodyEnd = bodyStart + len; - if (buffer.length < bodyEnd) break; - const body = buffer.slice(bodyStart, bodyEnd).toString("utf8"); - results.push(body); - buffer = buffer.slice(bodyEnd); + const results = [] + let idx = buffer.indexOf('\r\n\r\n') + while (idx !== -1) { + const header = buffer.slice(0, idx).toString('utf8') + const m = /Content-Length:\s*(\d+)/i.exec(header) + const len = m ? Number.parseInt(m[1], 10) : 0 + const bodyStart = idx + 4 + const bodyEnd = bodyStart + len + if (buffer.length < bodyEnd) + break + const body = buffer.slice(bodyStart, bodyEnd).toString('utf8') + results.push(body) + buffer = buffer.slice(bodyEnd) + idx = buffer.indexOf('\r\n\r\n') } - return { messages: results, rest: buffer }; + return { messages: results, rest: buffer } } -let readBuffer = Buffer.alloc(0); +let readBuffer = Buffer.alloc(0) -process.stdin.on("data", (chunk) => { - readBuffer = Buffer.concat([readBuffer, chunk]); - const { messages, rest } = decodeFrames(readBuffer); - readBuffer = rest; - for (const m of messages) handle(m); -}); +process.stdin.on('data', (chunk) => { + readBuffer = Buffer.concat([readBuffer, chunk]) + const { messages, rest } = decodeFrames(readBuffer) + readBuffer = rest + for (const m of messages) handle(m) +}) function send(msg) { - process.stdout.write(encode(msg)); + process.stdout.write(encode(msg)) } function sendNotification(method, params) { - send({ jsonrpc: "2.0", method, params }); + send({ jsonrpc: '2.0', method, params }) } function sendRequest(method, params) { - const id = nextId++; - send({ jsonrpc: "2.0", id, method, params }); - return id; + const id = nextId++ + send({ jsonrpc: '2.0', id, method, params }) + return id } function handle(raw) { - let data; + let data try { - data = JSON.parse(raw); - } catch { - return; + data = JSON.parse(raw) + } + catch { + return } // Initialize request - if (data.method === "initialize") { + if (data.method === 'initialize') { send({ - jsonrpc: "2.0", + jsonrpc: '2.0', id: data.id, result: { capabilities: { @@ -73,26 +79,26 @@ function handle(raw) { documentSymbolProvider: true, }, }, - }); - return; + }) + return } // Initialized notification - if (data.method === "initialized") { - return; + if (data.method === 'initialized') { + return } // Configuration change - if (data.method === "workspace/didChangeConfiguration") { - return; + if (data.method === 'workspace/didChangeConfiguration') { + return } // Document open - send fake diagnostics - if (data.method === "textDocument/didOpen") { - const uri = data.params?.textDocument?.uri; + if (data.method === 'textDocument/didOpen') { + const uri = data.params?.textDocument?.uri if (uri) { setTimeout(() => { - sendNotification("textDocument/publishDiagnostics", { + sendNotification('textDocument/publishDiagnostics', { uri, diagnostics: [ { @@ -101,50 +107,50 @@ function handle(raw) { end: { line: 0, character: 5 }, }, severity: 1, // Error - message: "Test error from fake LSP server", + message: 'Test error from fake LSP server', }, ], - }); - }, 50); + }) + }, 50) } - return; + return } // Document change - if (data.method === "textDocument/didChange") { - return; + if (data.method === 'textDocument/didChange') { + return } // Hover request - if (data.method === "textDocument/hover") { + if (data.method === 'textDocument/hover') { send({ - jsonrpc: "2.0", + jsonrpc: '2.0', id: data.id, result: { contents: { - kind: "markdown", - value: "**Test hover info**\n\nThis is test hover content.", + kind: 'markdown', + value: '**Test hover info**\n\nThis is test hover content.', }, range: { start: { line: data.params.position.line, character: 0 }, end: { line: data.params.position.line, character: 10 }, }, }, - }); - return; + }) + return } // Workspace symbol request - if (data.method === "workspace/symbol") { + if (data.method === 'workspace/symbol') { send({ - jsonrpc: "2.0", + jsonrpc: '2.0', id: data.id, result: [ { - name: "testFunction", + name: 'testFunction', kind: 12, // Function location: { - uri: "file:///test.ts", + uri: 'file:///test.ts', range: { start: { line: 0, character: 0 }, end: { line: 5, character: 1 }, @@ -152,18 +158,18 @@ function handle(raw) { }, }, ], - }); - return; + }) + return } // Document symbol request - if (data.method === "textDocument/documentSymbol") { + if (data.method === 'textDocument/documentSymbol') { send({ - jsonrpc: "2.0", + jsonrpc: '2.0', id: data.id, result: [ { - name: "TestClass", + name: 'TestClass', kind: 5, // Class range: { start: { line: 0, character: 0 }, @@ -175,20 +181,20 @@ function handle(raw) { }, }, ], - }); - return; + }) + return } // Test trigger for capability requests - if (data.method === "test/trigger") { - const method = data.params?.method; - if (method) sendRequest(method, {}); - return; + if (data.method === 'test/trigger') { + const method = data.params?.method + if (method) + sendRequest(method, {}) + return } // Respond OK to any other request - if (typeof data.id !== "undefined") { - send({ jsonrpc: "2.0", id: data.id, result: null }); - return; + if (typeof data.id !== 'undefined') { + send({ jsonrpc: '2.0', id: data.id, result: null }) } } diff --git a/packages/lsp/src/__tests__/index.test.ts b/packages/lsp/src/__tests__/index.test.ts index 83317cf..18c5b28 100644 --- a/packages/lsp/src/__tests__/index.test.ts +++ b/packages/lsp/src/__tests__/index.test.ts @@ -1,176 +1,176 @@ -import { describe, expect, test } from "bun:test"; +import type { Diagnostic } from 'vscode-languageserver-types' +import { describe, expect, test } from 'bun:test' import { - LSPManager, formatDiagnostic, - SymbolKind, getLanguageId, LANGUAGE_EXTENSIONS, -} from "../index"; -import type { Diagnostic } from "vscode-languageserver-types"; - -describe("LSPManager", () => { - test("creates manager with project path", () => { - const manager = new LSPManager("/test/project"); - expect(manager).toBeDefined(); - }); - - test("creates disabled manager", () => { - const manager = new LSPManager("/test/project", { enabled: false }); - expect(manager).toBeDefined(); - }); - - test("returns empty status when no clients connected", async () => { - const manager = new LSPManager("/test/project"); - const status = await manager.status(); - expect(status).toEqual([]); - }); - - test("returns empty diagnostics when no clients connected", async () => { - const manager = new LSPManager("/test/project"); - const diagnostics = await manager.diagnostics(); - expect(diagnostics).toEqual({}); - }); - - test("shuts down cleanly with no clients", async () => { - const manager = new LSPManager("/test/project"); - await manager.shutdown(); + LSPManager, + SymbolKind, +} from '../index' + +describe('LSPManager', () => { + test('creates manager with project path', () => { + const manager = new LSPManager('/test/project') + expect(manager).toBeDefined() + }) + + test('creates disabled manager', () => { + const manager = new LSPManager('/test/project', { enabled: false }) + expect(manager).toBeDefined() + }) + + test('returns empty status when no clients connected', async () => { + const manager = new LSPManager('/test/project') + const status = await manager.status() + expect(status).toEqual([]) + }) + + test('returns empty diagnostics when no clients connected', async () => { + const manager = new LSPManager('/test/project') + const diagnostics = await manager.diagnostics() + expect(diagnostics).toEqual({}) + }) + + test('shuts down cleanly with no clients', async () => { + const manager = new LSPManager('/test/project') + await manager.shutdown() // Should not throw - }); -}); + }) +}) -describe("formatDiagnostic", () => { - test("formats error diagnostic", () => { +describe('formatDiagnostic', () => { + test('formats error diagnostic', () => { const diagnostic: Diagnostic = { range: { start: { line: 0, character: 5 }, end: { line: 0, character: 10 }, }, - message: "Test error message", + message: 'Test error message', severity: 1, - }; + } - const result = formatDiagnostic(diagnostic); - expect(result).toBe("ERROR [1:6] Test error message"); - }); + const result = formatDiagnostic(diagnostic) + expect(result).toBe('ERROR [1:6] Test error message') + }) - test("formats warning diagnostic", () => { + test('formats warning diagnostic', () => { const diagnostic: Diagnostic = { range: { start: { line: 4, character: 0 }, end: { line: 4, character: 10 }, }, - message: "Test warning", + message: 'Test warning', severity: 2, - }; + } - const result = formatDiagnostic(diagnostic); - expect(result).toBe("WARN [5:1] Test warning"); - }); + const result = formatDiagnostic(diagnostic) + expect(result).toBe('WARN [5:1] Test warning') + }) - test("formats info diagnostic", () => { + test('formats info diagnostic', () => { const diagnostic: Diagnostic = { range: { start: { line: 9, character: 3 }, end: { line: 9, character: 8 }, }, - message: "Test info", + message: 'Test info', severity: 3, - }; + } - const result = formatDiagnostic(diagnostic); - expect(result).toBe("INFO [10:4] Test info"); - }); + const result = formatDiagnostic(diagnostic) + expect(result).toBe('INFO [10:4] Test info') + }) - test("formats hint diagnostic", () => { + test('formats hint diagnostic', () => { const diagnostic: Diagnostic = { range: { start: { line: 0, character: 0 }, end: { line: 0, character: 5 }, }, - message: "Test hint", + message: 'Test hint', severity: 4, - }; + } - const result = formatDiagnostic(diagnostic); - expect(result).toBe("HINT [1:1] Test hint"); - }); + const result = formatDiagnostic(diagnostic) + expect(result).toBe('HINT [1:1] Test hint') + }) - test("defaults to ERROR when severity not specified", () => { + test('defaults to ERROR when severity not specified', () => { const diagnostic: Diagnostic = { range: { start: { line: 0, character: 0 }, end: { line: 0, character: 5 }, }, - message: "No severity", - }; - - const result = formatDiagnostic(diagnostic); - expect(result).toBe("ERROR [1:1] No severity"); - }); -}); - -describe("SymbolKind", () => { - test("has correct enum values", () => { - expect(SymbolKind.File).toBe(1); - expect(SymbolKind.Module).toBe(2); - expect(SymbolKind.Class).toBe(5); - expect(SymbolKind.Method).toBe(6); - expect(SymbolKind.Function).toBe(12); - expect(SymbolKind.Variable).toBe(13); - }); -}); - -describe("getLanguageId", () => { - test("returns correct language for TypeScript", () => { - expect(getLanguageId(".ts")).toBe("typescript"); - }); - - test("returns correct language for TypeScript React", () => { - expect(getLanguageId(".tsx")).toBe("typescriptreact"); - }); - - test("returns correct language for JavaScript", () => { - expect(getLanguageId(".js")).toBe("javascript"); - }); - - test("returns correct language for Python", () => { - expect(getLanguageId(".py")).toBe("python"); - }); - - test("returns correct language for Go", () => { - expect(getLanguageId(".go")).toBe("go"); - }); - - test("returns correct language for Rust", () => { - expect(getLanguageId(".rs")).toBe("rust"); - }); - - test("returns plaintext for unknown extensions", () => { - expect(getLanguageId(".unknown")).toBe("plaintext"); - }); -}); - -describe("LANGUAGE_EXTENSIONS", () => { - test("contains common languages", () => { - expect(LANGUAGE_EXTENSIONS[".ts"]).toBe("typescript"); - expect(LANGUAGE_EXTENSIONS[".js"]).toBe("javascript"); - expect(LANGUAGE_EXTENSIONS[".py"]).toBe("python"); - expect(LANGUAGE_EXTENSIONS[".go"]).toBe("go"); - expect(LANGUAGE_EXTENSIONS[".rs"]).toBe("rust"); - expect(LANGUAGE_EXTENSIONS[".java"]).toBe("java"); - expect(LANGUAGE_EXTENSIONS[".kt"]).toBe("kotlin"); - }); - - test("contains web languages", () => { - expect(LANGUAGE_EXTENSIONS[".html"]).toBe("html"); - expect(LANGUAGE_EXTENSIONS[".css"]).toBe("css"); - expect(LANGUAGE_EXTENSIONS[".vue"]).toBe("vue"); - expect(LANGUAGE_EXTENSIONS[".svelte"]).toBe("svelte"); - }); - - test("contains config files", () => { - expect(LANGUAGE_EXTENSIONS[".json"]).toBe("json"); - expect(LANGUAGE_EXTENSIONS[".yaml"]).toBe("yaml"); - expect(LANGUAGE_EXTENSIONS[".yml"]).toBe("yaml"); - }); -}); + message: 'No severity', + } + + const result = formatDiagnostic(diagnostic) + expect(result).toBe('ERROR [1:1] No severity') + }) +}) + +describe('SymbolKind', () => { + test('has correct enum values', () => { + expect(SymbolKind.File).toBe(1) + expect(SymbolKind.Module).toBe(2) + expect(SymbolKind.Class).toBe(5) + expect(SymbolKind.Method).toBe(6) + expect(SymbolKind.Function).toBe(12) + expect(SymbolKind.Variable).toBe(13) + }) +}) + +describe('getLanguageId', () => { + test('returns correct language for TypeScript', () => { + expect(getLanguageId('.ts')).toBe('typescript') + }) + + test('returns correct language for TypeScript React', () => { + expect(getLanguageId('.tsx')).toBe('typescriptreact') + }) + + test('returns correct language for JavaScript', () => { + expect(getLanguageId('.js')).toBe('javascript') + }) + + test('returns correct language for Python', () => { + expect(getLanguageId('.py')).toBe('python') + }) + + test('returns correct language for Go', () => { + expect(getLanguageId('.go')).toBe('go') + }) + + test('returns correct language for Rust', () => { + expect(getLanguageId('.rs')).toBe('rust') + }) + + test('returns plaintext for unknown extensions', () => { + expect(getLanguageId('.unknown')).toBe('plaintext') + }) +}) + +describe('LANGUAGE_EXTENSIONS', () => { + test('contains common languages', () => { + expect(LANGUAGE_EXTENSIONS['.ts']).toBe('typescript') + expect(LANGUAGE_EXTENSIONS['.js']).toBe('javascript') + expect(LANGUAGE_EXTENSIONS['.py']).toBe('python') + expect(LANGUAGE_EXTENSIONS['.go']).toBe('go') + expect(LANGUAGE_EXTENSIONS['.rs']).toBe('rust') + expect(LANGUAGE_EXTENSIONS['.java']).toBe('java') + expect(LANGUAGE_EXTENSIONS['.kt']).toBe('kotlin') + }) + + test('contains web languages', () => { + expect(LANGUAGE_EXTENSIONS['.html']).toBe('html') + expect(LANGUAGE_EXTENSIONS['.css']).toBe('css') + expect(LANGUAGE_EXTENSIONS['.vue']).toBe('vue') + expect(LANGUAGE_EXTENSIONS['.svelte']).toBe('svelte') + }) + + test('contains config files', () => { + expect(LANGUAGE_EXTENSIONS['.json']).toBe('json') + expect(LANGUAGE_EXTENSIONS['.yaml']).toBe('yaml') + expect(LANGUAGE_EXTENSIONS['.yml']).toBe('yaml') + }) +}) diff --git a/packages/lsp/src/__tests__/server.test.ts b/packages/lsp/src/__tests__/server.test.ts index b99c258..b2e9254 100644 --- a/packages/lsp/src/__tests__/server.test.ts +++ b/packages/lsp/src/__tests__/server.test.ts @@ -1,165 +1,165 @@ -import { describe, expect, test } from "bun:test"; +import { describe, expect, test } from 'bun:test' import { - LSP_SERVERS, - TypescriptServer, DenoServer, + getServerById, + getServersForExtension, + GoplsServer, + LSP_SERVERS, OxlintServer, PyrightServer, - GoplsServer, RustAnalyzerServer, - getServerById, - getServersForExtension, -} from "../server"; - -describe("LSP_SERVERS", () => { - test("contains expected servers", () => { - expect(LSP_SERVERS.length).toBeGreaterThan(0); - - const serverIds = LSP_SERVERS.map((s) => s.id); - expect(serverIds).toContain("typescript"); - expect(serverIds).toContain("deno"); - expect(serverIds).toContain("oxlint"); - expect(serverIds).toContain("pyright"); - expect(serverIds).toContain("gopls"); - expect(serverIds).toContain("rust-analyzer"); - }); -}); - -describe("TypescriptServer", () => { - test("has correct id", () => { - expect(TypescriptServer.id).toBe("typescript"); - }); - - test("supports TypeScript extensions", () => { - expect(TypescriptServer.extensions).toContain(".ts"); - expect(TypescriptServer.extensions).toContain(".tsx"); - expect(TypescriptServer.extensions).toContain(".js"); - expect(TypescriptServer.extensions).toContain(".jsx"); - }); - - test("has root function", () => { - expect(typeof TypescriptServer.root).toBe("function"); - }); - - test("has spawn function", () => { - expect(typeof TypescriptServer.spawn).toBe("function"); - }); -}); - -describe("DenoServer", () => { - test("has correct id", () => { - expect(DenoServer.id).toBe("deno"); - }); - - test("supports TypeScript extensions", () => { - expect(DenoServer.extensions).toContain(".ts"); - expect(DenoServer.extensions).toContain(".tsx"); - expect(DenoServer.extensions).toContain(".js"); - }); -}); - -describe("OxlintServer", () => { - test("has correct id", () => { - expect(OxlintServer.id).toBe("oxlint"); - }); - - test("supports JavaScript/TypeScript extensions", () => { - expect(OxlintServer.extensions).toContain(".ts"); - expect(OxlintServer.extensions).toContain(".tsx"); - expect(OxlintServer.extensions).toContain(".js"); - expect(OxlintServer.extensions).toContain(".jsx"); - expect(OxlintServer.extensions).toContain(".mjs"); - expect(OxlintServer.extensions).toContain(".cjs"); - expect(OxlintServer.extensions).toContain(".mts"); - expect(OxlintServer.extensions).toContain(".cts"); - }); - - test("supports framework-specific extensions", () => { - expect(OxlintServer.extensions).toContain(".vue"); - expect(OxlintServer.extensions).toContain(".astro"); - expect(OxlintServer.extensions).toContain(".svelte"); - }); - - test("has root function", () => { - expect(typeof OxlintServer.root).toBe("function"); - }); - - test("has spawn function", () => { - expect(typeof OxlintServer.spawn).toBe("function"); - }); -}); - -describe("PyrightServer", () => { - test("has correct id", () => { - expect(PyrightServer.id).toBe("pyright"); - }); - - test("supports Python extensions", () => { - expect(PyrightServer.extensions).toContain(".py"); - expect(PyrightServer.extensions).toContain(".pyi"); - }); -}); - -describe("GoplsServer", () => { - test("has correct id", () => { - expect(GoplsServer.id).toBe("gopls"); - }); - - test("supports Go extension", () => { - expect(GoplsServer.extensions).toContain(".go"); - }); -}); - -describe("RustAnalyzerServer", () => { - test("has correct id", () => { - expect(RustAnalyzerServer.id).toBe("rust-analyzer"); - }); - - test("supports Rust extension", () => { - expect(RustAnalyzerServer.extensions).toContain(".rs"); - }); -}); - -describe("getServerById", () => { - test("returns typescript server", () => { - const server = getServerById("typescript"); - expect(server).toBeDefined(); - expect(server?.id).toBe("typescript"); - }); - - test("returns undefined for unknown id", () => { - const server = getServerById("unknown"); - expect(server).toBeUndefined(); - }); -}); - -describe("getServersForExtension", () => { - test("returns servers for .ts extension", () => { - const servers = getServersForExtension(".ts"); - expect(servers.length).toBeGreaterThan(0); - - const serverIds = servers.map((s) => s.id); - expect(serverIds).toContain("typescript"); - }); - - test("returns servers for .py extension", () => { - const servers = getServersForExtension(".py"); - expect(servers.length).toBeGreaterThan(0); - - const serverIds = servers.map((s) => s.id); - expect(serverIds).toContain("pyright"); - }); - - test("returns servers for .go extension", () => { - const servers = getServersForExtension(".go"); - expect(servers.length).toBeGreaterThan(0); - - const serverIds = servers.map((s) => s.id); - expect(serverIds).toContain("gopls"); - }); - - test("returns empty array for unknown extension", () => { - const servers = getServersForExtension(".unknown"); - expect(servers).toEqual([]); - }); -}); + TypescriptServer, +} from '../server' + +describe('LSP_SERVERS', () => { + test('contains expected servers', () => { + expect(LSP_SERVERS.length).toBeGreaterThan(0) + + const serverIds = LSP_SERVERS.map(s => s.id) + expect(serverIds).toContain('typescript') + expect(serverIds).toContain('deno') + expect(serverIds).toContain('oxlint') + expect(serverIds).toContain('pyright') + expect(serverIds).toContain('gopls') + expect(serverIds).toContain('rust-analyzer') + }) +}) + +describe('TypescriptServer', () => { + test('has correct id', () => { + expect(TypescriptServer.id).toBe('typescript') + }) + + test('supports TypeScript extensions', () => { + expect(TypescriptServer.extensions).toContain('.ts') + expect(TypescriptServer.extensions).toContain('.tsx') + expect(TypescriptServer.extensions).toContain('.js') + expect(TypescriptServer.extensions).toContain('.jsx') + }) + + test('has root function', () => { + expect(typeof TypescriptServer.root).toBe('function') + }) + + test('has spawn function', () => { + expect(typeof TypescriptServer.spawn).toBe('function') + }) +}) + +describe('DenoServer', () => { + test('has correct id', () => { + expect(DenoServer.id).toBe('deno') + }) + + test('supports TypeScript extensions', () => { + expect(DenoServer.extensions).toContain('.ts') + expect(DenoServer.extensions).toContain('.tsx') + expect(DenoServer.extensions).toContain('.js') + }) +}) + +describe('OxlintServer', () => { + test('has correct id', () => { + expect(OxlintServer.id).toBe('oxlint') + }) + + test('supports JavaScript/TypeScript extensions', () => { + expect(OxlintServer.extensions).toContain('.ts') + expect(OxlintServer.extensions).toContain('.tsx') + expect(OxlintServer.extensions).toContain('.js') + expect(OxlintServer.extensions).toContain('.jsx') + expect(OxlintServer.extensions).toContain('.mjs') + expect(OxlintServer.extensions).toContain('.cjs') + expect(OxlintServer.extensions).toContain('.mts') + expect(OxlintServer.extensions).toContain('.cts') + }) + + test('supports framework-specific extensions', () => { + expect(OxlintServer.extensions).toContain('.vue') + expect(OxlintServer.extensions).toContain('.astro') + expect(OxlintServer.extensions).toContain('.svelte') + }) + + test('has root function', () => { + expect(typeof OxlintServer.root).toBe('function') + }) + + test('has spawn function', () => { + expect(typeof OxlintServer.spawn).toBe('function') + }) +}) + +describe('PyrightServer', () => { + test('has correct id', () => { + expect(PyrightServer.id).toBe('pyright') + }) + + test('supports Python extensions', () => { + expect(PyrightServer.extensions).toContain('.py') + expect(PyrightServer.extensions).toContain('.pyi') + }) +}) + +describe('GoplsServer', () => { + test('has correct id', () => { + expect(GoplsServer.id).toBe('gopls') + }) + + test('supports Go extension', () => { + expect(GoplsServer.extensions).toContain('.go') + }) +}) + +describe('RustAnalyzerServer', () => { + test('has correct id', () => { + expect(RustAnalyzerServer.id).toBe('rust-analyzer') + }) + + test('supports Rust extension', () => { + expect(RustAnalyzerServer.extensions).toContain('.rs') + }) +}) + +describe('getServerById', () => { + test('returns typescript server', () => { + const server = getServerById('typescript') + expect(server).toBeDefined() + expect(server?.id).toBe('typescript') + }) + + test('returns undefined for unknown id', () => { + const server = getServerById('unknown') + expect(server).toBeUndefined() + }) +}) + +describe('getServersForExtension', () => { + test('returns servers for .ts extension', () => { + const servers = getServersForExtension('.ts') + expect(servers.length).toBeGreaterThan(0) + + const serverIds = servers.map(s => s.id) + expect(serverIds).toContain('typescript') + }) + + test('returns servers for .py extension', () => { + const servers = getServersForExtension('.py') + expect(servers.length).toBeGreaterThan(0) + + const serverIds = servers.map(s => s.id) + expect(serverIds).toContain('pyright') + }) + + test('returns servers for .go extension', () => { + const servers = getServersForExtension('.go') + expect(servers.length).toBeGreaterThan(0) + + const serverIds = servers.map(s => s.id) + expect(serverIds).toContain('gopls') + }) + + test('returns empty array for unknown extension', () => { + const servers = getServersForExtension('.unknown') + expect(servers).toEqual([]) + }) +}) diff --git a/packages/lsp/src/client.ts b/packages/lsp/src/client.ts index 35f92fe..9135e99 100644 --- a/packages/lsp/src/client.ts +++ b/packages/lsp/src/client.ts @@ -3,116 +3,118 @@ * Based on opencode reference implementation */ -import path from "path"; -import { pathToFileURL, fileURLToPath } from "url"; +import type { ChildProcess } from 'node:child_process' +import type { MessageConnection } from 'vscode-jsonrpc/node' +import type { Diagnostic } from 'vscode-languageserver-types' +import path from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' import { createMessageConnection, + StreamMessageReader, StreamMessageWriter, - type MessageConnection, -} from "vscode-jsonrpc/node"; -import type { Diagnostic } from "vscode-languageserver-types"; -import type { ChildProcess } from "child_process"; -import { getLanguageId } from "./language"; +} from 'vscode-jsonrpc/node' +import { getLanguageId } from './language' -const DIAGNOSTICS_DEBOUNCE_MS = 150; -const INITIALIZE_TIMEOUT_MS = 45_000; -const DIAGNOSTICS_WAIT_TIMEOUT_MS = 3_000; +const DIAGNOSTICS_DEBOUNCE_MS = 150 +const INITIALIZE_TIMEOUT_MS = 45_000 +const DIAGNOSTICS_WAIT_TIMEOUT_MS = 3_000 export interface LSPServerHandle { - process: ChildProcess; - initialization?: Record; + process: ChildProcess + initialization?: Record } export interface LSPClientInfo { - serverID: string; - root: string; - connection: MessageConnection; - diagnostics: Map; + serverID: string + root: string + connection: MessageConnection + diagnostics: Map notify: { - open(input: { path: string }): Promise; - }; - waitForDiagnostics(input: { path: string }): Promise; - shutdown(): Promise; + open: (input: { path: string }) => Promise + } + waitForDiagnostics: (input: { path: string }) => Promise + shutdown: () => Promise } -type DiagnosticsCallback = (path: string, serverID: string) => void; +type DiagnosticsCallback = (path: string, serverID: string) => void /** * Create an LSP client from a server handle */ export async function createLSPClient(input: { - serverID: string; - server: LSPServerHandle; - root: string; - projectPath: string; - onDiagnostics?: DiagnosticsCallback; + serverID: string + server: LSPServerHandle + root: string + projectPath: string + onDiagnostics?: DiagnosticsCallback }): Promise { - const { stdout, stdin } = input.server.process; + const { stdout, stdin } = input.server.process if (!stdout || !stdin) { - throw new Error(`LSP server ${input.serverID} has no stdio`); + throw new Error(`LSP server ${input.serverID} has no stdio`) } const connection = createMessageConnection( new StreamMessageReader(stdout), - new StreamMessageWriter(stdin) - ); + new StreamMessageWriter(stdin), + ) - const diagnostics = new Map(); - const files: Record = {}; + const diagnostics = new Map() + const files: Record = {} let diagnosticsListeners: Array<{ - path: string; - resolve: () => void; - timer?: ReturnType; - }> = []; + path: string + resolve: () => void + timer?: ReturnType + }> = [] // Handle diagnostics notifications connection.onNotification( - "textDocument/publishDiagnostics", - (params: { uri: string; diagnostics: Diagnostic[] }) => { - const filePath = normalizePath(fileURLToPath(params.uri)); - diagnostics.set(filePath, params.diagnostics); + 'textDocument/publishDiagnostics', + (params: { uri: string, diagnostics: Diagnostic[] }) => { + const filePath = normalizePath(fileURLToPath(params.uri)) + diagnostics.set(filePath, params.diagnostics) // Notify listeners with debounce - const listener = diagnosticsListeners.find((l) => l.path === filePath); + const listener = diagnosticsListeners.find(l => l.path === filePath) if (listener) { - if (listener.timer) clearTimeout(listener.timer); + if (listener.timer) + clearTimeout(listener.timer) listener.timer = setTimeout(() => { diagnosticsListeners = diagnosticsListeners.filter( - (l) => l !== listener - ); - listener.resolve(); - }, DIAGNOSTICS_DEBOUNCE_MS); + l => l !== listener, + ) + listener.resolve() + }, DIAGNOSTICS_DEBOUNCE_MS) } - input.onDiagnostics?.(filePath, input.serverID); - } - ); + input.onDiagnostics?.(filePath, input.serverID) + }, + ) // Handle capability requests - connection.onRequest("window/workDoneProgress/create", () => null); - connection.onRequest("workspace/configuration", () => [ + connection.onRequest('window/workDoneProgress/create', () => null) + connection.onRequest('workspace/configuration', () => [ input.server.initialization ?? {}, - ]); - connection.onRequest("client/registerCapability", () => {}); - connection.onRequest("client/unregisterCapability", () => {}); - connection.onRequest("workspace/workspaceFolders", () => [ + ]) + connection.onRequest('client/registerCapability', () => {}) + connection.onRequest('client/unregisterCapability', () => {}) + connection.onRequest('workspace/workspaceFolders', () => [ { - name: "workspace", + name: 'workspace', uri: pathToFileURL(input.root).href, }, - ]); + ]) - connection.listen(); + connection.listen() // Initialize LSP connection await withTimeout( - connection.sendRequest("initialize", { + connection.sendRequest('initialize', { rootUri: pathToFileURL(input.root).href, processId: input.server.process.pid, workspaceFolders: [ { - name: "workspace", + name: 'workspace', uri: pathToFileURL(input.root).href, }, ], @@ -126,15 +128,15 @@ export async function createLSPClient(input: { }, }, }), - INITIALIZE_TIMEOUT_MS - ); + INITIALIZE_TIMEOUT_MS, + ) - await connection.sendNotification("initialized", {}); + await connection.sendNotification('initialized', {}) if (input.server.initialization) { - await connection.sendNotification("workspace/didChangeConfiguration", { + await connection.sendNotification('workspace/didChangeConfiguration', { settings: input.server.initialization, - }); + }) } const client: LSPClientInfo = { @@ -147,37 +149,37 @@ export async function createLSPClient(input: { async open(fileInput: { path: string }) { const filePath = path.isAbsolute(fileInput.path) ? fileInput.path - : path.resolve(input.projectPath, fileInput.path); + : path.resolve(input.projectPath, fileInput.path) - const file = Bun.file(filePath); - const text = await file.text(); - const extension = path.extname(filePath); - const languageId = getLanguageId(extension); + const file = Bun.file(filePath) + const text = await file.text() + const extension = path.extname(filePath) + const languageId = getLanguageId(extension) - const version = files[filePath]; + const version = files[filePath] if (version !== undefined) { - const next = version + 1; - files[filePath] = next; - await connection.sendNotification("textDocument/didChange", { + const next = version + 1 + files[filePath] = next + await connection.sendNotification('textDocument/didChange', { textDocument: { uri: pathToFileURL(filePath).href, version: next, }, contentChanges: [{ text }], - }); - return; + }) + return } - diagnostics.delete(filePath); - await connection.sendNotification("textDocument/didOpen", { + diagnostics.delete(filePath) + await connection.sendNotification('textDocument/didOpen', { textDocument: { uri: pathToFileURL(filePath).href, languageId, version: 0, text, }, - }); - files[filePath] = 0; + }) + files[filePath] = 0 }, }, @@ -185,37 +187,37 @@ export async function createLSPClient(input: { const normalizedPath = normalizePath( path.isAbsolute(fileInput.path) ? fileInput.path - : path.resolve(input.projectPath, fileInput.path) - ); + : path.resolve(input.projectPath, fileInput.path), + ) await withTimeout( new Promise((resolve) => { - diagnosticsListeners.push({ path: normalizedPath, resolve }); + diagnosticsListeners.push({ path: normalizedPath, resolve }) }), - DIAGNOSTICS_WAIT_TIMEOUT_MS + DIAGNOSTICS_WAIT_TIMEOUT_MS, ).catch(() => { // Timeout - remove listener diagnosticsListeners = diagnosticsListeners.filter( - (l) => l.path !== normalizedPath - ); - }); + l => l.path !== normalizedPath, + ) + }) }, async shutdown() { - connection.end(); - connection.dispose(); - input.server.process.kill(); + connection.end() + connection.dispose() + input.server.process.kill() }, - }; + } - return client; + return client } /** * Normalize path for consistent lookups */ function normalizePath(p: string): string { - return path.normalize(p); + return path.normalize(p) } /** @@ -225,7 +227,7 @@ function withTimeout(promise: Promise, ms: number): Promise { return Promise.race([ promise, new Promise((_, reject) => - setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms) + setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms), ), - ]); + ]) } diff --git a/packages/lsp/src/index.ts b/packages/lsp/src/index.ts index 3e3c7c3..93465c9 100644 --- a/packages/lsp/src/index.ts +++ b/packages/lsp/src/index.ts @@ -5,18 +5,19 @@ * Based on opencode reference implementation */ -import path from "path"; -import { pathToFileURL } from "url"; -import { z } from "zod"; +import type { Diagnostic as VSCodeDiagnostic } from 'vscode-languageserver-types' +import type { LSPClientInfo, LSPServerHandle } from './client' +import type { LSPServerInfo } from './server' +import path from 'node:path' +import { pathToFileURL } from 'node:url' +import { z } from 'zod' import { createLSPClient, - type LSPClientInfo, - type LSPServerHandle, -} from "./client"; -import { LSP_SERVERS, type LSPServerInfo } from "./server"; -import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types"; -export type Diagnostic = VSCodeDiagnostic; +} from './client' +import { LSP_SERVERS } from './server' + +export type Diagnostic = VSCodeDiagnostic /** * LSP Range schema @@ -30,8 +31,8 @@ export const RangeSchema = z.object({ line: z.number(), character: z.number(), }), -}); -export type Range = z.infer; +}) +export type Range = z.infer /** * LSP Symbol schema @@ -43,8 +44,8 @@ export const SymbolSchema = z.object({ uri: z.string(), range: RangeSchema, }), -}); -export type Symbol = z.infer; +}) +export type Symbol = z.infer /** * LSP Document Symbol schema @@ -55,17 +56,17 @@ export const DocumentSymbolSchema = z.object({ kind: z.number(), range: RangeSchema, selectionRange: RangeSchema, -}); -export type DocumentSymbol = z.infer; +}) +export type DocumentSymbol = z.infer /** * LSP Status */ export interface LSPStatus { - id: string; - name: string; - root: string; - status: "connected" | "error"; + id: string + name: string + root: string + status: 'connected' | 'error' } /** @@ -104,21 +105,22 @@ export enum SymbolKind { * LSP Manager - handles client lifecycle and operations */ export class LSPManager { - private clients: LSPClientInfo[] = []; - private servers: Map = new Map(); - private broken: Set = new Set(); - private spawning: Map> = - new Map(); - private projectPath: string; - private enabled: boolean = true; + private clients: LSPClientInfo[] = [] + private servers: Map = new Map() + private broken: Set = new Set() + private spawning: Map> + = new Map() + + private projectPath: string + private enabled: boolean = true constructor(projectPath: string, options?: { enabled?: boolean }) { - this.projectPath = projectPath; - this.enabled = options?.enabled ?? true; + this.projectPath = projectPath + this.enabled = options?.enabled ?? true // Register servers for (const server of LSP_SERVERS) { - this.servers.set(server.id, server); + this.servers.set(server.id, server) } } @@ -126,40 +128,42 @@ export class LSPManager { * Get status of connected LSP servers */ async status(): Promise { - return this.clients.map((client) => ({ + return this.clients.map(client => ({ id: client.serverID, name: this.servers.get(client.serverID)?.id ?? client.serverID, root: path.relative(this.projectPath, client.root), - status: "connected" as const, - })); + status: 'connected' as const, + })) } /** * Get clients for a file */ private async getClients(file: string): Promise { - if (!this.enabled) return []; + if (!this.enabled) + return [] - const extension = path.extname(file) || file; - const result: LSPClientInfo[] = []; + const extension = path.extname(file) || file + const result: LSPClientInfo[] = [] const schedule = async ( server: LSPServerInfo, root: string, - key: string + key: string, ): Promise => { - let handle: LSPServerHandle | undefined; + let handle: LSPServerHandle | undefined try { - handle = await server.spawn(root); + handle = await server.spawn(root) if (!handle) { - this.broken.add(key); - return undefined; + this.broken.add(key) + return undefined } - } catch (err) { - this.broken.add(key); - console.error(`Failed to spawn LSP server ${server.id}:`, err); - return undefined; + } + catch (err) { + this.broken.add(key) + console.error(`Failed to spawn LSP server ${server.id}:`, err) + return undefined } try { @@ -168,73 +172,78 @@ export class LSPManager { server: handle, root, projectPath: this.projectPath, - }); + }) // Check if another client was created in parallel const existing = this.clients.find( - (x) => x.root === root && x.serverID === server.id - ); + x => x.root === root && x.serverID === server.id, + ) if (existing) { - handle.process.kill(); - return existing; + handle.process.kill() + return existing } - this.clients.push(client); - return client; - } catch (err) { - this.broken.add(key); - handle.process.kill(); - console.error(`Failed to initialize LSP client ${server.id}:`, err); - return undefined; + this.clients.push(client) + return client } - }; + catch (err) { + this.broken.add(key) + handle.process.kill() + console.error(`Failed to initialize LSP client ${server.id}:`, err) + return undefined + } + } for (const server of this.servers.values()) { if ( - server.extensions.length && - !server.extensions.includes(extension) + server.extensions.length + && !server.extensions.includes(extension) ) { - continue; + continue } - const root = await server.root(file, this.projectPath); - if (!root) continue; + const root = await server.root(file, this.projectPath) + if (!root) + continue - const key = root + server.id; - if (this.broken.has(key)) continue; + const key = root + server.id + if (this.broken.has(key)) + continue // Check for existing client const match = this.clients.find( - (x) => x.root === root && x.serverID === server.id - ); + x => x.root === root && x.serverID === server.id, + ) if (match) { - result.push(match); - continue; + result.push(match) + continue } // Check for in-flight spawn - const inflight = this.spawning.get(key); + const inflight = this.spawning.get(key) if (inflight) { - const client = await inflight; - if (client) result.push(client); - continue; + const client = await inflight + if (client) + result.push(client) + continue } // Schedule new spawn - const task = schedule(server, root, key); - this.spawning.set(key, task); + const task = schedule(server, root, key) + this.spawning.set(key, task) task.finally(() => { if (this.spawning.get(key) === task) { - this.spawning.delete(key); + this.spawning.delete(key) } - }); + }) - const client = await task; - if (client) result.push(client); + const client = await task + if (client) + result.push(client) } - return result; + return result } /** @@ -242,54 +251,54 @@ export class LSPManager { */ async touchFile( file: string, - waitForDiagnostics?: boolean + waitForDiagnostics?: boolean, ): Promise { - const clients = await this.getClients(file); + const clients = await this.getClients(file) await Promise.all( clients.map(async (client) => { const wait = waitForDiagnostics ? client.waitForDiagnostics({ path: file }) - : Promise.resolve(); - await client.notify.open({ path: file }); - return wait; - }) + : Promise.resolve() + await client.notify.open({ path: file }) + return wait + }), ).catch((err) => { - console.error("Failed to touch file:", err); - }); + console.error('Failed to touch file:', err) + }) } /** * Get diagnostics from all clients */ async diagnostics(): Promise> { - const results: Record = {}; + const results: Record = {} for (const client of this.clients) { for (const [filePath, diags] of client.diagnostics.entries()) { - const arr = results[filePath] || []; - arr.push(...diags); - results[filePath] = arr; + const arr = results[filePath] || [] + arr.push(...diags) + results[filePath] = arr } } - return results; + return results } /** * Get hover information */ async hover(input: { - file: string; - line: number; - character: number; + file: string + line: number + character: number }): Promise { - const clients = await this.getClients(input.file); + const clients = await this.getClients(input.file) return Promise.all( - clients.map((client) => + clients.map(client => client.connection - .sendRequest("textDocument/hover", { + .sendRequest('textDocument/hover', { textDocument: { uri: pathToFileURL(input.file).href, }, @@ -298,9 +307,9 @@ export class LSPManager { character: input.character, }, }) - .catch(() => null) - ) - ); + .catch(() => null), + ), + ) } /** @@ -316,23 +325,23 @@ export class LSPManager { SymbolKind.Constant, SymbolKind.Struct, SymbolKind.Enum, - ]; + ] const results = await Promise.all( - this.clients.map((client) => + this.clients.map(client => client.connection - .sendRequest("workspace/symbol", { query }) + .sendRequest('workspace/symbol', { query }) .then((result: unknown) => { - const symbols = result as Symbol[]; + const symbols = result as Symbol[] return symbols - .filter((x) => relevantKinds.includes(x.kind)) - .slice(0, 10); + .filter(x => relevantKinds.includes(x.kind)) + .slice(0, 10) }) - .catch(() => []) - ) - ); + .catch(() => []), + ), + ) - return results.flat(); + return results.flat() } /** @@ -340,24 +349,24 @@ export class LSPManager { */ async documentSymbol(uri: string): Promise<(DocumentSymbol | Symbol)[]> { const results = await Promise.all( - this.clients.map((client) => + this.clients.map(client => client.connection - .sendRequest("textDocument/documentSymbol", { + .sendRequest('textDocument/documentSymbol', { textDocument: { uri }, }) - .catch(() => []) - ) - ); + .catch(() => []), + ), + ) - return results.flat().filter(Boolean) as (DocumentSymbol | Symbol)[]; + return results.flat().filter(Boolean) as (DocumentSymbol | Symbol)[] } /** * Shutdown all clients */ async shutdown(): Promise { - await Promise.all(this.clients.map((client) => client.shutdown())); - this.clients = []; + await Promise.all(this.clients.map(client => client.shutdown())) + this.clients = [] } } @@ -366,32 +375,32 @@ export class LSPManager { */ export function formatDiagnostic(diagnostic: Diagnostic): string { const severityMap: Record = { - 1: "ERROR", - 2: "WARN", - 3: "INFO", - 4: "HINT", - }; + 1: 'ERROR', + 2: 'WARN', + 3: 'INFO', + 4: 'HINT', + } - const severity = severityMap[diagnostic.severity || 1]; - const line = diagnostic.range.start.line + 1; - const col = diagnostic.range.start.character + 1; + const severity = severityMap[diagnostic.severity || 1] + const line = diagnostic.range.start.line + 1 + const col = diagnostic.range.start.character + 1 - return `${severity} [${line}:${col}] ${diagnostic.message}`; + return `${severity} [${line}:${col}] ${diagnostic.message}` } +export { createLSPClient, type LSPClientInfo } from './client' // Re-export specific items to avoid conflicts -export { getLanguageId, LANGUAGE_EXTENSIONS } from "./language"; +export { getLanguageId, LANGUAGE_EXTENSIONS } from './language' export { - LSP_SERVERS, - TypescriptServer, DenoServer, - OxlintServer, - PyrightServer, - GoplsServer, - RustAnalyzerServer, getServerById, getServersForExtension, - type LSPServerInfo, + GoplsServer, + LSP_SERVERS, type LSPServerHandle, -} from "./server"; -export { createLSPClient, type LSPClientInfo } from "./client"; + type LSPServerInfo, + OxlintServer, + PyrightServer, + RustAnalyzerServer, + TypescriptServer, +} from './server' diff --git a/packages/lsp/src/language.ts b/packages/lsp/src/language.ts index e5cc29d..a2966c7 100644 --- a/packages/lsp/src/language.ts +++ b/packages/lsp/src/language.ts @@ -4,123 +4,123 @@ */ export const LANGUAGE_EXTENSIONS: Record = { - ".abap": "abap", - ".bat": "bat", - ".bib": "bibtex", - ".bibtex": "bibtex", - ".clj": "clojure", - ".cljs": "clojure", - ".cljc": "clojure", - ".edn": "clojure", - ".coffee": "coffeescript", - ".c": "c", - ".cpp": "cpp", - ".cxx": "cpp", - ".cc": "cpp", - ".c++": "cpp", - ".cs": "csharp", - ".css": "css", - ".d": "d", - ".pas": "pascal", - ".pascal": "pascal", - ".diff": "diff", - ".patch": "diff", - ".dart": "dart", - ".dockerfile": "dockerfile", - ".ex": "elixir", - ".exs": "elixir", - ".erl": "erlang", - ".hrl": "erlang", - ".fs": "fsharp", - ".fsi": "fsharp", - ".fsx": "fsharp", - ".fsscript": "fsharp", - ".gitcommit": "git-commit", - ".gitrebase": "git-rebase", - ".go": "go", - ".groovy": "groovy", - ".gleam": "gleam", - ".hbs": "handlebars", - ".handlebars": "handlebars", - ".hs": "haskell", - ".html": "html", - ".htm": "html", - ".ini": "ini", - ".java": "java", - ".js": "javascript", - ".jsx": "javascriptreact", - ".json": "json", - ".tex": "latex", - ".latex": "latex", - ".less": "less", - ".lua": "lua", - ".makefile": "makefile", - makefile: "makefile", - ".md": "markdown", - ".markdown": "markdown", - ".m": "objective-c", - ".mm": "objective-cpp", - ".pl": "perl", - ".pm": "perl", - ".pm6": "perl6", - ".php": "php", - ".ps1": "powershell", - ".psm1": "powershell", - ".pug": "jade", - ".jade": "jade", - ".py": "python", - ".r": "r", - ".cshtml": "razor", - ".razor": "razor", - ".rb": "ruby", - ".rake": "ruby", - ".gemspec": "ruby", - ".ru": "ruby", - ".erb": "erb", - ".html.erb": "erb", - ".js.erb": "erb", - ".css.erb": "erb", - ".json.erb": "erb", - ".rs": "rust", - ".scss": "scss", - ".sass": "sass", - ".scala": "scala", - ".shader": "shaderlab", - ".sh": "shellscript", - ".bash": "shellscript", - ".zsh": "shellscript", - ".ksh": "shellscript", - ".sql": "sql", - ".svelte": "svelte", - ".swift": "swift", - ".ts": "typescript", - ".tsx": "typescriptreact", - ".mts": "typescript", - ".cts": "typescript", - ".mtsx": "typescriptreact", - ".ctsx": "typescriptreact", - ".xml": "xml", - ".xsl": "xsl", - ".yaml": "yaml", - ".yml": "yaml", - ".mjs": "javascript", - ".cjs": "javascript", - ".vue": "vue", - ".zig": "zig", - ".zon": "zig", - ".astro": "astro", - ".ml": "ocaml", - ".mli": "ocaml", - ".tf": "terraform", - ".tfvars": "terraform-vars", - ".hcl": "hcl", - ".kt": "kotlin", - ".kts": "kotlin", -} as const; + '.abap': 'abap', + '.bat': 'bat', + '.bib': 'bibtex', + '.bibtex': 'bibtex', + '.clj': 'clojure', + '.cljs': 'clojure', + '.cljc': 'clojure', + '.edn': 'clojure', + '.coffee': 'coffeescript', + '.c': 'c', + '.cpp': 'cpp', + '.cxx': 'cpp', + '.cc': 'cpp', + '.c++': 'cpp', + '.cs': 'csharp', + '.css': 'css', + '.d': 'd', + '.pas': 'pascal', + '.pascal': 'pascal', + '.diff': 'diff', + '.patch': 'diff', + '.dart': 'dart', + '.dockerfile': 'dockerfile', + '.ex': 'elixir', + '.exs': 'elixir', + '.erl': 'erlang', + '.hrl': 'erlang', + '.fs': 'fsharp', + '.fsi': 'fsharp', + '.fsx': 'fsharp', + '.fsscript': 'fsharp', + '.gitcommit': 'git-commit', + '.gitrebase': 'git-rebase', + '.go': 'go', + '.groovy': 'groovy', + '.gleam': 'gleam', + '.hbs': 'handlebars', + '.handlebars': 'handlebars', + '.hs': 'haskell', + '.html': 'html', + '.htm': 'html', + '.ini': 'ini', + '.java': 'java', + '.js': 'javascript', + '.jsx': 'javascriptreact', + '.json': 'json', + '.tex': 'latex', + '.latex': 'latex', + '.less': 'less', + '.lua': 'lua', + '.makefile': 'makefile', + 'makefile': 'makefile', + '.md': 'markdown', + '.markdown': 'markdown', + '.m': 'objective-c', + '.mm': 'objective-cpp', + '.pl': 'perl', + '.pm': 'perl', + '.pm6': 'perl6', + '.php': 'php', + '.ps1': 'powershell', + '.psm1': 'powershell', + '.pug': 'jade', + '.jade': 'jade', + '.py': 'python', + '.r': 'r', + '.cshtml': 'razor', + '.razor': 'razor', + '.rb': 'ruby', + '.rake': 'ruby', + '.gemspec': 'ruby', + '.ru': 'ruby', + '.erb': 'erb', + '.html.erb': 'erb', + '.js.erb': 'erb', + '.css.erb': 'erb', + '.json.erb': 'erb', + '.rs': 'rust', + '.scss': 'scss', + '.sass': 'sass', + '.scala': 'scala', + '.shader': 'shaderlab', + '.sh': 'shellscript', + '.bash': 'shellscript', + '.zsh': 'shellscript', + '.ksh': 'shellscript', + '.sql': 'sql', + '.svelte': 'svelte', + '.swift': 'swift', + '.ts': 'typescript', + '.tsx': 'typescriptreact', + '.mts': 'typescript', + '.cts': 'typescript', + '.mtsx': 'typescriptreact', + '.ctsx': 'typescriptreact', + '.xml': 'xml', + '.xsl': 'xsl', + '.yaml': 'yaml', + '.yml': 'yaml', + '.mjs': 'javascript', + '.cjs': 'javascript', + '.vue': 'vue', + '.zig': 'zig', + '.zon': 'zig', + '.astro': 'astro', + '.ml': 'ocaml', + '.mli': 'ocaml', + '.tf': 'terraform', + '.tfvars': 'terraform-vars', + '.hcl': 'hcl', + '.kt': 'kotlin', + '.kts': 'kotlin', +} as const /** * Get the language ID for a file extension */ export function getLanguageId(extension: string): string { - return LANGUAGE_EXTENSIONS[extension] ?? "plaintext"; + return LANGUAGE_EXTENSIONS[extension] ?? 'plaintext' } diff --git a/packages/lsp/src/server.ts b/packages/lsp/src/server.ts index 7aa1753..25a6d26 100644 --- a/packages/lsp/src/server.ts +++ b/packages/lsp/src/server.ts @@ -3,25 +3,27 @@ * Based on opencode reference implementation */ -import { spawn, type ChildProcessWithoutNullStreams } from "child_process"; -import path from "path"; -import fs from "fs/promises"; +import type { ChildProcessWithoutNullStreams } from 'node:child_process' +import { spawn } from 'node:child_process' +import fs from 'node:fs/promises' +import path from 'node:path' +import process from 'node:process' export interface LSPServerHandle { - process: ChildProcessWithoutNullStreams; - initialization?: Record; + process: ChildProcessWithoutNullStreams + initialization?: Record } type RootFunction = ( file: string, - projectPath: string -) => Promise; + projectPath: string, +) => Promise export interface LSPServerInfo { - id: string; - extensions: string[]; - root: RootFunction; - spawn(root: string): Promise; + id: string + extensions: string[] + root: RootFunction + spawn: (root: string) => Promise } /** @@ -29,290 +31,303 @@ export interface LSPServerInfo { */ function nearestRoot( includePatterns: string[], - excludePatterns?: string[] + excludePatterns?: string[], ): RootFunction { return async (file, projectPath) => { - let current = path.dirname(file); + let current = path.dirname(file) // Check exclusions first if (excludePatterns) { - let checkDir = current; + let checkDir = current while (checkDir.startsWith(projectPath) || checkDir === projectPath) { for (const pattern of excludePatterns) { - const target = path.join(checkDir, pattern); + const target = path.join(checkDir, pattern) try { - await fs.access(target); - return undefined; // Excluded - } catch { + await fs.access(target) + return undefined // Excluded + } + catch { // Not found, continue } } - const parent = path.dirname(checkDir); - if (parent === checkDir) break; - checkDir = parent; + const parent = path.dirname(checkDir) + if (parent === checkDir) + break + checkDir = parent } } // Find nearest matching file while (current.startsWith(projectPath) || current === projectPath) { for (const pattern of includePatterns) { - const target = path.join(current, pattern); + const target = path.join(current, pattern) try { - await fs.access(target); - return current; - } catch { + await fs.access(target) + return current + } + catch { // Not found, continue } } - const parent = path.dirname(current); - if (parent === current) break; - current = parent; + const parent = path.dirname(current) + if (parent === current) + break + current = parent } - return projectPath; - }; + return projectPath + } } /** * TypeScript Language Server */ export const TypescriptServer: LSPServerInfo = { - id: "typescript", - extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"], + id: 'typescript', + extensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts'], root: nearestRoot( [ - "package-lock.json", - "bun.lockb", - "bun.lock", - "pnpm-lock.yaml", - "yarn.lock", + 'package-lock.json', + 'bun.lockb', + 'bun.lock', + 'pnpm-lock.yaml', + 'yarn.lock', ], - ["deno.json", "deno.jsonc"] + ['deno.json', 'deno.jsonc'], ), async spawn(root) { - const tsserver = Bun.which("typescript-language-server"); + const tsserver = Bun.which('typescript-language-server') if (!tsserver) { // Try via bunx - const proc = spawn("bunx", ["typescript-language-server", "--stdio"], { + const proc = spawn('bunx', ['typescript-language-server', '--stdio'], { cwd: root, - env: { ...process.env, BUN_BE_BUN: "1" }, - }); - return { process: proc }; + env: { ...process.env, BUN_BE_BUN: '1' }, + }) + return { process: proc } } - const proc = spawn(tsserver, ["--stdio"], { + const proc = spawn(tsserver, ['--stdio'], { cwd: root, - env: { ...process.env, BUN_BE_BUN: "1" }, - }); - return { process: proc }; + env: { ...process.env, BUN_BE_BUN: '1' }, + }) + return { process: proc } }, -}; +} /** * Deno Language Server */ export const DenoServer: LSPServerInfo = { - id: "deno", - extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs"], + id: 'deno', + extensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs'], root: async (file, projectPath) => { - let current = path.dirname(file); + let current = path.dirname(file) while (current.startsWith(projectPath) || current === projectPath) { - for (const pattern of ["deno.json", "deno.jsonc"]) { - const target = path.join(current, pattern); + for (const pattern of ['deno.json', 'deno.jsonc']) { + const target = path.join(current, pattern) try { - await fs.access(target); - return current; - } catch { + await fs.access(target) + return current + } + catch { // Not found } } - const parent = path.dirname(current); - if (parent === current) break; - current = parent; + const parent = path.dirname(current) + if (parent === current) + break + current = parent } - return undefined; + return undefined }, async spawn(root) { - const deno = Bun.which("deno"); - if (!deno) return undefined; + const deno = Bun.which('deno') + if (!deno) + return undefined - const proc = spawn(deno, ["lsp"], { + const proc = spawn(deno, ['lsp'], { cwd: root, - }); - return { process: proc }; + }) + return { process: proc } }, -}; +} /** * Python Language Server (Pyright) */ export const PyrightServer: LSPServerInfo = { - id: "pyright", - extensions: [".py", ".pyi"], + id: 'pyright', + extensions: ['.py', '.pyi'], root: nearestRoot([ - "pyproject.toml", - "setup.py", - "requirements.txt", - "pyrightconfig.json", + 'pyproject.toml', + 'setup.py', + 'requirements.txt', + 'pyrightconfig.json', ]), async spawn(root) { - const pyright = Bun.which("pyright-langserver"); + const pyright = Bun.which('pyright-langserver') if (!pyright) { // Try via bunx/npx - const proc = spawn("bunx", ["pyright-langserver", "--stdio"], { + const proc = spawn('bunx', ['pyright-langserver', '--stdio'], { cwd: root, - }); - return { process: proc }; + }) + return { process: proc } } - const proc = spawn(pyright, ["--stdio"], { + const proc = spawn(pyright, ['--stdio'], { cwd: root, - }); - return { process: proc }; + }) + return { process: proc } }, -}; +} /** * Go Language Server (gopls) */ export const GoplsServer: LSPServerInfo = { - id: "gopls", - extensions: [".go"], + id: 'gopls', + extensions: ['.go'], root: async (file, projectPath) => { // Check for go.work first - const workRoot = await nearestRoot(["go.work"])(file, projectPath); - if (workRoot) return workRoot; - return nearestRoot(["go.mod", "go.sum"])(file, projectPath); + const workRoot = await nearestRoot(['go.work'])(file, projectPath) + if (workRoot) + return workRoot + return nearestRoot(['go.mod', 'go.sum'])(file, projectPath) }, async spawn(root) { - const gopls = Bun.which("gopls"); - if (!gopls) return undefined; + const gopls = Bun.which('gopls') + if (!gopls) + return undefined - const proc = spawn(gopls, ["serve"], { + const proc = spawn(gopls, ['serve'], { cwd: root, - }); - return { process: proc }; + }) + return { process: proc } }, -}; +} /** * Rust Analyzer */ export const RustAnalyzerServer: LSPServerInfo = { - id: "rust-analyzer", - extensions: [".rs"], - root: nearestRoot(["Cargo.toml", "Cargo.lock"]), + id: 'rust-analyzer', + extensions: ['.rs'], + root: nearestRoot(['Cargo.toml', 'Cargo.lock']), async spawn(root) { - const rustAnalyzer = Bun.which("rust-analyzer"); - if (!rustAnalyzer) return undefined; + const rustAnalyzer = Bun.which('rust-analyzer') + if (!rustAnalyzer) + return undefined const proc = spawn(rustAnalyzer, [], { cwd: root, - }); - return { process: proc }; + }) + return { process: proc } }, -}; +} /** * Oxlint Language Server * Based on opencode PR #5570 */ export const OxlintServer: LSPServerInfo = { - id: "oxlint", + id: 'oxlint', extensions: [ - ".ts", - ".tsx", - ".js", - ".jsx", - ".mjs", - ".cjs", - ".mts", - ".cts", - ".vue", - ".astro", - ".svelte", + '.ts', + '.tsx', + '.js', + '.jsx', + '.mjs', + '.cjs', + '.mts', + '.cts', + '.vue', + '.astro', + '.svelte', ], root: nearestRoot([ - ".oxlintrc.json", - "package-lock.json", - "bun.lockb", - "bun.lock", - "pnpm-lock.yaml", - "yarn.lock", - "package.json", + '.oxlintrc.json', + 'package-lock.json', + 'bun.lockb', + 'bun.lock', + 'pnpm-lock.yaml', + 'yarn.lock', + 'package.json', ]), async spawn(root) { - const ext = process.platform === "win32" ? ".cmd" : ""; + const ext = process.platform === 'win32' ? '.cmd' : '' const resolveBin = async ( - binName: string + binName: string, ): Promise => { - const localBin = path.join(root, "node_modules", ".bin", binName + ext); + const localBin = path.join(root, 'node_modules', '.bin', binName + ext) try { - await fs.access(localBin); - return localBin; - } catch (err: unknown) { + await fs.access(localBin) + return localBin + } + catch (err: unknown) { // Only ignore ENOENT (file not found), log other errors - const isNotFound = - err instanceof Error && - "code" in err && - (err as NodeJS.ErrnoException).code === "ENOENT"; + const isNotFound + = err instanceof Error + && 'code' in err + && (err as NodeJS.ErrnoException).code === 'ENOENT' if (!isNotFound) { - console.error(`[oxlint] Cannot access local binary at ${localBin}:`, err); + console.error(`[oxlint] Cannot access local binary at ${localBin}:`, err) } } // Check global PATH - const globalBin = Bun.which(binName); - if (globalBin) return globalBin; + const globalBin = Bun.which(binName) + if (globalBin) + return globalBin - return undefined; - }; + return undefined + } // Try oxlint with --lsp flag first - const lintBin = await resolveBin("oxlint"); + const lintBin = await resolveBin('oxlint') if (lintBin) { - const proc = Bun.spawn([lintBin, "--help"], { - stdout: "pipe", - stderr: "pipe", - }); - const exitCode = await proc.exited; + const proc = Bun.spawn([lintBin, '--help'], { + stdout: 'pipe', + stderr: 'pipe', + }) + const exitCode = await proc.exited if (exitCode !== 0) { - const stderr = await new Response(proc.stderr).text(); + const stderr = await new Response(proc.stderr).text() console.warn( - `[oxlint] Binary at ${lintBin} failed --help (exit ${exitCode}): ${stderr.slice(0, 200)}` - ); - } else { - const help = await new Response(proc.stdout).text(); - if (help.includes("--lsp")) { + `[oxlint] Binary at ${lintBin} failed --help (exit ${exitCode}): ${stderr.slice(0, 200)}`, + ) + } + else { + const help = await new Response(proc.stdout).text() + if (help.includes('--lsp')) { return { - process: spawn(lintBin, ["--lsp"], { + process: spawn(lintBin, ['--lsp'], { cwd: root, }), - }; + } } } } // Fallback to oxc_language_server - const serverBin = await resolveBin("oxc_language_server"); + const serverBin = await resolveBin('oxc_language_server') if (serverBin) { return { process: spawn(serverBin, [], { cwd: root, }), - }; + } } // Neither found - log diagnostic console.warn( - `[oxlint] Could not start oxlint LSP server for ${root}. ` + - `Install with: npm install -D oxlint` - ); - return undefined; + `[oxlint] Could not start oxlint LSP server for ${root}. ` + + `Install with: npm install -D oxlint`, + ) + return undefined }, -}; +} /** * All available LSP servers @@ -324,18 +339,18 @@ export const LSP_SERVERS: LSPServerInfo[] = [ PyrightServer, GoplsServer, RustAnalyzerServer, -]; +] /** * Get server info by ID */ export function getServerById(id: string): LSPServerInfo | undefined { - return LSP_SERVERS.find((s) => s.id === id); + return LSP_SERVERS.find(s => s.id === id) } /** * Get servers that support a file extension */ export function getServersForExtension(extension: string): LSPServerInfo[] { - return LSP_SERVERS.filter((s) => s.extensions.includes(extension)); + return LSP_SERVERS.filter(s => s.extensions.includes(extension)) } diff --git a/scripts/generate-packages.ts b/scripts/generate-packages.ts index 961148d..6eced95 100644 --- a/scripts/generate-packages.ts +++ b/scripts/generate-packages.ts @@ -13,38 +13,39 @@ * npm/@dora/win32-x64/ */ -import * as fs from "node:fs" -import * as path from "node:path" +import * as fs from 'node:fs' +import * as path from 'node:path' +import process from 'node:process' -const PACKAGE_NAME = "code" -const PACKAGE_SCOPE = "@pleaseai" +const PACKAGE_NAME = 'code' +const PACKAGE_SCOPE = '@pleaseai' -const ROOT_DIR = path.resolve(import.meta.dir, "..") -const NPM_DIR = path.resolve(ROOT_DIR, "npm") -const CODE_PKG_DIR = path.resolve(ROOT_DIR, "packages/code") -const MANIFEST_PATH = path.resolve(CODE_PKG_DIR, "package.json") +const ROOT_DIR = path.resolve(import.meta.dir, '..') +const NPM_DIR = path.resolve(ROOT_DIR, 'npm') +const CODE_PKG_DIR = path.resolve(ROOT_DIR, 'packages/code') +const MANIFEST_PATH = path.resolve(CODE_PKG_DIR, 'package.json') interface Target { platform: string arch: string bunTarget: string - libc?: "glibc" | "musl" + libc?: 'glibc' | 'musl' } // Supported targets from https://bun.sh/docs/bundler/executables#supported-targets // Using -modern for x64 (AVX2, 2013+ CPUs) for better performance const TARGETS: Target[] = [ // macOS - { platform: "darwin", arch: "arm64", bunTarget: "bun-darwin-arm64" }, - { platform: "darwin", arch: "x64", bunTarget: "bun-darwin-x64-modern" }, + { platform: 'darwin', arch: 'arm64', bunTarget: 'bun-darwin-arm64' }, + { platform: 'darwin', arch: 'x64', bunTarget: 'bun-darwin-x64-modern' }, // Linux glibc - { platform: "linux", arch: "x64", bunTarget: "bun-linux-x64-modern", libc: "glibc" }, - { platform: "linux", arch: "arm64", bunTarget: "bun-linux-arm64", libc: "glibc" }, + { platform: 'linux', arch: 'x64', bunTarget: 'bun-linux-x64-modern', libc: 'glibc' }, + { platform: 'linux', arch: 'arm64', bunTarget: 'bun-linux-arm64', libc: 'glibc' }, // Linux musl (Alpine) - { platform: "linux", arch: "x64", bunTarget: "bun-linux-x64-modern-musl", libc: "musl" }, - { platform: "linux", arch: "arm64", bunTarget: "bun-linux-arm64-musl", libc: "musl" }, + { platform: 'linux', arch: 'x64', bunTarget: 'bun-linux-x64-modern-musl', libc: 'musl' }, + { platform: 'linux', arch: 'arm64', bunTarget: 'bun-linux-arm64-musl', libc: 'musl' }, // Windows (arm64 not supported) - { platform: "win32", arch: "x64", bunTarget: "bun-windows-x64-modern" }, + { platform: 'win32', arch: 'x64', bunTarget: 'bun-windows-x64-modern' }, ] function getTargetName(target: Target): string { @@ -55,7 +56,7 @@ function getTargetName(target: Target): string { } function getBinaryName(target: Target): string { - const ext = target.platform === "win32" ? ".exe" : "" + const ext = target.platform === 'win32' ? '.exe' : '' return `${PACKAGE_NAME}${ext}` } @@ -69,7 +70,7 @@ async function compileForTarget(target: Target): Promise { try { const result = await Bun.build({ - entrypoints: [path.resolve(CODE_PKG_DIR, "src/cli.ts")], + entrypoints: [path.resolve(CODE_PKG_DIR, 'src/cli.ts')], // @ts-expect-error - compile option for single executable compile: { target: target.bunTarget, @@ -82,7 +83,8 @@ async function compileForTarget(target: Target): Promise { } console.log(` Compiled: ${outFile}`) return outFile - } catch (error) { + } + catch (error) { console.error(` Failed to compile for ${targetName}:`, error) return null } @@ -111,7 +113,7 @@ async function generateNativePackage(target: Target, rootManifest: Record): Promise { @@ -168,7 +170,7 @@ async function generateMainPackage(rootManifest: Record): Promi name: `${PACKAGE_SCOPE}/${PACKAGE_NAME}`, version, description, - type: "module", + type: 'module', bin: { [PACKAGE_NAME]: `bin/${PACKAGE_NAME}`, }, @@ -178,33 +180,33 @@ async function generateMainPackage(rootManifest: Record): Promi bugs, repository, optionalDependencies, - files: ["bin", "dist"], + files: ['bin', 'dist'], } - const manifestPath = path.resolve(packageRoot, "package.json") + const manifestPath = path.resolve(packageRoot, 'package.json') fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)) console.log(`Created main package: ${manifestPath}`) // Compile launcher from packages/code/src/launcher/cli.ts - const launcherSrc = path.resolve(CODE_PKG_DIR, "src/launcher/cli.ts") - const distDir = path.resolve(packageRoot, "dist") + const launcherSrc = path.resolve(CODE_PKG_DIR, 'src/launcher/cli.ts') + const distDir = path.resolve(packageRoot, 'dist') fs.mkdirSync(distDir, { recursive: true }) const result = await Bun.build({ entrypoints: [launcherSrc], outdir: distDir, - target: "node", - format: "esm", + target: 'node', + format: 'esm', }) if (!result.success) { - console.error("Failed to compile launcher:", result.logs) - throw new Error("Launcher compilation failed") + console.error('Failed to compile launcher:', result.logs) + throw new Error('Launcher compilation failed') } - console.log(`Compiled launcher: ${path.resolve(distDir, "cli.js")}`) + console.log(`Compiled launcher: ${path.resolve(distDir, 'cli.js')}`) // Create bin directory with minimal entry point - const binDir = path.resolve(packageRoot, "bin") + const binDir = path.resolve(packageRoot, 'bin') fs.mkdirSync(binDir, { recursive: true }) const binScript = `#!/usr/bin/env node @@ -218,32 +220,32 @@ import "../dist/cli.js"; } async function main(): Promise { - console.log("Generating platform-specific packages for dora...\n") + console.log('Generating platform-specific packages for dora...\n') // Read root manifest - const rootManifest = JSON.parse(fs.readFileSync(MANIFEST_PATH, "utf-8")) + const rootManifest = JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf-8')) // Ensure npm directory exists fs.mkdirSync(NPM_DIR, { recursive: true }) // Generate native packages for each target - console.log("=== Compiling native binaries ===\n") + console.log('=== Compiling native binaries ===\n') for (const target of TARGETS) { await generateNativePackage(target, rootManifest) console.log() } // Generate main package with launcher - console.log("=== Generating main package ===\n") + console.log('=== Generating main package ===\n') await generateMainPackage(rootManifest) - console.log("\n=== Done ===") + console.log('\n=== Done ===') console.log(`\nGenerated packages in: ${NPM_DIR}`) - console.log("\nTo publish all packages:") - console.log(" bun publish --access public") + console.log('\nTo publish all packages:') + console.log(' bun publish --access public') } main().catch((error) => { - console.error("Error:", error) + console.error('Error:', error) process.exit(1) }) diff --git a/tsconfig.json b/tsconfig.json index 75b3d8d..fc0470f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,23 +3,23 @@ "target": "ESNext", "module": "ESNext", "moduleResolution": "bundler", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, + "types": ["bun"], + "strict": true, + "exactOptionalPropertyTypes": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, "declaration": true, "declarationMap": true, - "outDir": "./dist", - "types": ["bun"], "noEmit": true, + "outDir": "./dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, "isolatedModules": true, "verbatimModuleSyntax": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "exactOptionalPropertyTypes": true + "skipLibCheck": true }, "include": ["packages/code/src/**/*", "scripts/**/*"], "exclude": ["node_modules", "dist", "ref"]