diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b01f824..861d1e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - node-version: [20, 22, 24] + node-version: [22, 24] runs-on: ${{ matrix.os }} permissions: contents: read @@ -30,7 +30,7 @@ jobs: - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable with: - targets: wasm32-unknown-unknown + targets: wasm32-unknown-unknown,wasm32-wasip2 - name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 diff --git a/.gitignore b/.gitignore index fbc185c..836d812 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,9 @@ typings/ # Typescript Output dist/ +# Rust build artifacts +samples/target/ + # VSCode extension development .vscode-test/ *.vsix @@ -80,3 +83,6 @@ dist/ types/ out/ tmp/ + +# Test coverage +coverage/ diff --git a/.prettierignore b/.prettierignore index c2f3de8..a283028 100644 --- a/.prettierignore +++ b/.prettierignore @@ -16,6 +16,7 @@ dist/ *.vsix wit-bindgen-wasm/pkg/ wit-bindgen-wasm/target/ +samples/target/ # Test snapshots (these are auto-generated and shouldn't be manually formatted) *.snap diff --git a/.vscode/launch.json b/.vscode/launch.json index a36bfed..ab6a573 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "version": "0.2.0", "configurations": [ { - "name": "Extension", + "name": "default", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", @@ -15,6 +15,17 @@ "--extensionDevelopmentPath=${workspaceFolder}", "${workspaceRoot}/tests/grammar" ] + }, + { + "name": "samples", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}", + "${workspaceRoot}/samples" + ] } ] } diff --git a/README.md b/README.md index ca9cbda..2eea1d8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ _A comprehensive Visual Studio Code extension for WebAssembly Interface Type (WI - 🎨 Full syntax highlighting and code completion - ✅ Real-time syntax validation with detailed error diagnostics - 🔧 Automatic code formatting -- 🌐 Generate bindings for Rust, C, C++, C#, Go, MoonBit, and Markdown +- 🌐 Generate guest bindings for Rust, C, C++, C#, Go, MoonBit, and JavaScript +- 🧱 Generate JavaScript host bindings from WIT or `.wasm` components +- 📚 Generate Markdown documentation from WIT definitions - 🧩 WebAssembly component detection and WIT extraction - 📝 Context menu integration for quick access to tools @@ -50,19 +52,27 @@ Format WIT files with a single command: Generate language bindings directly from WIT files or WebAssembly components: -#### Supported Languages +#### Guest Bindings - **Rust**: Generate idiomatic Rust bindings with `wit-bindgen` - **C**: Generate C bindings for C projects - **C++**: Generate C++ bindings - **C#**: Generate C# bindings for .NET projects - **Go**: Generate Go bindings - **MoonBit**: Generate MoonBit bindings +- **JavaScript**: Generate JavaScript/TypeScript guest-side bindings + +#### Host Bindings +- **JavaScript**: Generate host-side JavaScript output + - For `.wit` files, generates host-oriented TypeScript declarations + - For `.wasm` components, transpiles to runnable JavaScript host modules + +#### Documentation Output - **Markdown**: Generate documentation in Markdown format #### Binding Generation Features -- **Context Menu Integration**: Right-click on `.wit` or `.wasm` files to generate bindings -- **Multiple Targets**: Generate bindings for multiple languages at once -- **Output to Folder**: Automatically creates language-specific output directories +- **Context Menu Integration**: Right-click on `.wit` or `.wasm` files to generate outputs +- **Structured Menus**: Separate submenus for guest bindings, host bindings, and documentation +- **Output to Folder**: Preserves generated relative folder layout safely - **Progress Feedback**: Visual feedback during generation process ### WebAssembly Component Support @@ -88,12 +98,16 @@ Right-click on files in the editor or Explorer for quick access to: **For `.wit` files:** - Check WIT Syntax - Format Document -- Generate Bindings (with language submenu) +- Generate Guest Bindings (submenu) +- Generate Host Bindings (submenu) +- Generate Documentation (submenu) **For `.wasm` component files:** - Extract WIT - Extract Core Wasm -- Generate Bindings (with language submenu) +- Generate Guest Bindings (submenu) +- Generate Host Bindings (submenu) +- Generate Documentation (submenu) ## Available Commands @@ -115,31 +129,36 @@ Access these commands via the Command Palette (`Ctrl+Shift+P` or `Cmd+Shift+P`): - Applies consistent styling and indentation ### Binding Generation Commands -- **WIT: Generate Language Bindings** - - Opens a language selection menu - - Available for `.wit` files and WebAssembly components - - **WIT: Generate Rust Bindings** - - Generates Rust bindings using `wit-bindgen` + - Generates Rust guest bindings using `wit-bindgen` - **WIT: Generate C Bindings** - - Generates C bindings for C projects + - Generates C guest bindings for C projects - **WIT: Generate C++ Bindings** - - Generates C++ bindings + - Generates C++ guest bindings - **WIT: Generate C# Bindings** - - Generates C# bindings for .NET projects + - Generates C# guest bindings for .NET projects - **WIT: Generate Go Bindings** - - Generates Go bindings + - Generates Go guest bindings - **WIT: Generate MoonBit Bindings** - - Generates MoonBit bindings + - Generates MoonBit guest bindings + +- **WIT: Generate JavaScript Bindings (Guest)** + - Generates JavaScript/TypeScript guest bindings from `.wit` or extracted component WIT + +- **WIT: Generate JavaScript Bindings (Host)** + - Generates host-side JavaScript output + - Uses host type generation for `.wit`, and component transpilation for `.wasm` - **WIT: Generate Markdown Documentation** - Generates Markdown documentation from WIT definitions +> Note: legacy command IDs for `wit-idl.generateBindings*` are still supported as deprecated aliases for backward compatibility. + ### WebAssembly Component Commands - **WIT: Extract WIT** - Extracts WIT definitions from a WebAssembly component file @@ -248,6 +267,9 @@ npm test # Run unit tests only npm run test-unit +# Run unit tests with coverage report +npm run test-unit-coverage + # Run tests in watch mode npm run test-unit-watch ``` diff --git a/images/component-view-menu.png b/images/component-view-menu.png index 75f025f..51d20d5 100644 Binary files a/images/component-view-menu.png and b/images/component-view-menu.png differ diff --git a/images/editor-menu.png b/images/editor-menu.png index 36dc6c7..189b9eb 100644 Binary files a/images/editor-menu.png and b/images/editor-menu.png differ diff --git a/package-lock.json b/package-lock.json index 043428a..eef288b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,22 +9,23 @@ "version": "0.3.27", "license": "Apache-2.0 WITH LLVM-exception", "dependencies": { + "@bytecodealliance/jco": "1.17.0", "wit-bindgen-wasm": "file:wit-bindgen-wasm/pkg" }, "devDependencies": { "@eslint/css": "0.14.1", "@eslint/js": "10.0.1", - "@eslint/markdown": "7.5.1", - "@types/node": "24.10.13", + "@types/node": "25.3.3", "@types/vscode": "1.99.0", "@typescript-eslint/parser": "8.56.1", + "@vitest/coverage-v8": "^4.0.18", "@vitest/ui": "4.0.18", "@vscode/vsce": "3.7.1", "esbuild": "0.27.3", "esbuild-node-externals": "1.20.1", "eslint": "10.0.2", "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", + "globals": "17.4.0", "npm-run-all": "4.1.5", "ovsx": "0.10.9", "prettier": "3.8.1", @@ -240,6 +241,16 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", @@ -250,11 +261,231 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@bytecodealliance/componentize-js": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@bytecodealliance/componentize-js/-/componentize-js-0.19.3.tgz", + "integrity": "sha512-ju7Y4WeF0B9uMkSPHJgmT6ouEfSwbe9M1uR/YOnYZjBpxJjH9qzxIkJg/kf8NycVDyFJ2/lscmJ1E1uPiDQVRQ==", + "workspaces": [ + "." + ], + "dependencies": { + "@bytecodealliance/jco": "^1.15.1", + "@bytecodealliance/wizer": "^10.0.0", + "es-module-lexer": "^1.6.0", + "oxc-parser": "^0.76.0" + }, + "bin": { + "componentize-js": "src/cli.js" + } + }, + "node_modules/@bytecodealliance/jco": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.17.0.tgz", + "integrity": "sha512-+8cLL6p++K+KKJiG+xqRDyKcjoWvAB1cwH+diIvuUf8O0AEN+QfzW7GdZJ0zzvfpCfGYX+uO7ylmPFvn+pWxCA==", + "license": "(Apache-2.0 WITH LLVM-exception)", + "dependencies": { + "@bytecodealliance/componentize-js": "^0.19.3", + "@bytecodealliance/preview2-shim": "^0.17.3", + "binaryen": "^123.0.0", + "commander": "^14", + "mkdirp": "^3", + "ora": "^8", + "terser": "^5" + }, + "bin": { + "jco": "src/jco.js" + } + }, + "node_modules/@bytecodealliance/jco/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@bytecodealliance/jco/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@bytecodealliance/preview2-shim": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.17.8.tgz", + "integrity": "sha512-wS5kg8u0KCML1UeHQPJ1IuOI24x/XLentCzsqPER1+gDNC5Cz2hG4G2blLOZap+3CEGhIhnJ9mmZYj6a2W0Lww==", + "license": "(Apache-2.0 WITH LLVM-exception)" + }, + "node_modules/@bytecodealliance/wizer": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer/-/wizer-10.0.0.tgz", + "integrity": "sha512-ziWmovyu1jQl9TsKlfC2bwuUZwxVPFHlX4fOqTzxhgS76jITIo45nzODEwPgU+jjmOr8F3YX2V2wAChC5NKujg==", + "license": "Apache-2.0", + "bin": { + "wizer": "wizer.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@bytecodealliance/wizer-darwin-arm64": "10.0.0", + "@bytecodealliance/wizer-darwin-x64": "10.0.0", + "@bytecodealliance/wizer-linux-arm64": "10.0.0", + "@bytecodealliance/wizer-linux-s390x": "10.0.0", + "@bytecodealliance/wizer-linux-x64": "10.0.0", + "@bytecodealliance/wizer-win32-x64": "10.0.0" + } + }, + "node_modules/@bytecodealliance/wizer-darwin-arm64": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-darwin-arm64/-/wizer-darwin-arm64-10.0.0.tgz", + "integrity": "sha512-dhZTWel+xccGTKSJtI9A7oM4yyP20FWflsT+AoqkOqkCY7kCNrj4tmMtZ6GXZFRDkrPY5+EnOh62sfShEibAMA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "bin": { + "wizer-darwin-arm64": "wizer" + } + }, + "node_modules/@bytecodealliance/wizer-darwin-x64": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-darwin-x64/-/wizer-darwin-x64-10.0.0.tgz", + "integrity": "sha512-r/LUIZw6Q3Hf4htd46mD+EBxfwjBkxVIrTM1r+B2pTCddoBYQnKVdVsI4UFyy7NoBxzEg8F8BwmTNoSLmFRjpw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "bin": { + "wizer-darwin-x64": "wizer" + } + }, + "node_modules/@bytecodealliance/wizer-linux-arm64": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-arm64/-/wizer-linux-arm64-10.0.0.tgz", + "integrity": "sha512-pGSfFWXzeTqHm6z1PtVaEn+7Fm3QGC8YnHrzBV4sQDVS3N1NwmuHZAc8kslmlFPNdu61ycEvdOsSgCny8JPQvg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "bin": { + "wizer-linux-arm64": "wizer" + } + }, + "node_modules/@bytecodealliance/wizer-linux-s390x": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-s390x/-/wizer-linux-s390x-10.0.0.tgz", + "integrity": "sha512-O8vHxRTAdb1lUnVXMIMTcp/9q4pq1D4iIKigJCipg2JN15taV9uFAWh0fO88wylXwuSlO7dOE1AwQl54fMKXQg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "bin": { + "wizer-linux-s390x": "wizer" + } + }, + "node_modules/@bytecodealliance/wizer-linux-x64": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-x64/-/wizer-linux-x64-10.0.0.tgz", + "integrity": "sha512-fJtM1sy43FBMnp+xpapFX6U1YdTBKA/1T4CYfG/qeE8jn0SXk2EuiYoY/EnC2uyNy9hjTrvfdYO5n4MXW0EIdQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "bin": { + "wizer-linux-x64": "wizer" + } + }, + "node_modules/@bytecodealliance/wizer-win32-x64": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-win32-x64/-/wizer-win32-x64-10.0.0.tgz", + "integrity": "sha512-55BPLfGT7iT7gH5M69NpTM16QknJZ7OxJ0z73VOEoeGA9CT8QPKMRzFKsPIvLs+W8G28fdudFA94nElrdkp3Kg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "bin": { + "wizer-win32-x64": "wizer" + } + }, "node_modules/@emnapi/core": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -266,7 +497,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -277,7 +507,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -872,30 +1101,6 @@ } } }, - "node_modules/@eslint/markdown": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@eslint/markdown/-/markdown-7.5.1.tgz", - "integrity": "sha512-R8uZemG9dKTbru/DQRPblbJyXpObwKzo8rv1KYGGuPUPtjM4LXBYM9q5CIZAComzZupws3tWbDwam5AFpPLyJQ==", - "dev": true, - "license": "MIT", - "workspaces": [ - "examples/*" - ], - "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" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@eslint/object-schema": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz", @@ -982,18 +1187,55 @@ "node": ">=18" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1308,112 +1550,361 @@ "node": ">= 8" } }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "node_modules/@oxc-parser/binding-android-arm64": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.76.0.tgz", + "integrity": "sha512-1XJW/16CDmF5bHE7LAyPPmEEVnxSadDgdJz+xiLqBrmC4lfAeuAfRw3HlOygcPGr+AJsbD4Z5sFJMkwjbSZlQg==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": ">=20.0.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "node_modules/@oxc-parser/binding-darwin-arm64": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.76.0.tgz", + "integrity": "sha512-yoQwSom8xsB+JdGsPUU0xxmxLKiF2kdlrK7I56WtGKZilixuBf/TmOwNYJYLRWkBoW5l2/pDZOhBm2luwmLiLw==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">=20.0.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "node_modules/@oxc-parser/binding-darwin-x64": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.76.0.tgz", + "integrity": "sha512-uRIopPLvr3pf2Xj7f5LKyCuqzIU6zOS+zEIR8UDYhcgJyZHnvBkfrYnfcztyIcrGdQehrFUi3uplmI09E7RdiQ==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">=20.0.0" + } }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "node_modules/@oxc-parser/binding-freebsd-x64": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.76.0.tgz", + "integrity": "sha512-a0EOFvnOd2FqmDSvH6uWLROSlU6KV/JDKbsYDA/zRLyKcG6HCsmFnPsp8iV7/xr9WMbNgyJi6R5IMpePQlUq7Q==", "cpu": [ - "arm64" + "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" - ] + ], + "engines": { + "node": ">=20.0.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.76.0.tgz", + "integrity": "sha512-ikRYDHL3fOdZwfJKmcdqjlLgkeNZ3Ez0qM8wAev5zlHZ+lY/Ig7qG5SCqPlvuTu+nNQ6zrFFaKvvt69EBKXU/g==", "cpu": [ - "x64" + "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.76.0.tgz", + "integrity": "sha512-dtRv5J5MRCLR7x39K8ufIIW4svIc7gYFUaI0YFXmmeOBhK/K2t/CkguPnDroKtsmXIPHDRtmJ1JJYzNcgJl6Wg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-gnu": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.76.0.tgz", + "integrity": "sha512-IE4iiiggFH2snagQxHrY5bv6dDpRMMat+vdlMN/ibonA65eOmRLp8VLTXnDiNrcla/itJ1L9qGABHNKU+SnE8g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-musl": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.76.0.tgz", + "integrity": "sha512-wi9zQPMDHrBuRuT7Iurfidc9qlZh7cKa5vfYzOWNBCaqJdgxmNOFzvYen02wVUxSWGKhpiPHxrPX0jdRyJ8Npg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.76.0.tgz", + "integrity": "sha512-0tqqu1pqPee2lLGY8vtYlX1L415fFn89e0a3yp4q5N9f03j1rRs0R31qesTm3bt/UK8HYjECZ+56FCVPs2MEMQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-s390x-gnu": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.76.0.tgz", + "integrity": "sha512-y36Hh1a5TA+oIGtlc8lT7N9vdHXBlhBetQJW0p457KbiVQ7jF7AZkaPWhESkjHWAsTVKD2OjCa9ZqfaqhSI0FQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-gnu": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.76.0.tgz", + "integrity": "sha512-7/acaG9htovp3gp/J0kHgbItQTuHctl+rbqPPqZ9DRBYTz8iV8kv3QN8t8Or8i/hOmOjfZp9McDoSU1duoR4/A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-musl": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.76.0.tgz", + "integrity": "sha512-AxFt0reY6Q2rfudABmMTFGR8tFFr58NlH2rRBQgcj+F+iEwgJ+jMwAPhXd2y1I2zaI8GspuahedUYQinqxWqjA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.76.0.tgz", + "integrity": "sha512-wHdkHdhf6AWBoO8vs5cpoR6zEFY1rB+fXWtq6j/xb9j/lu1evlujRVMkh8IM/M/pOUIrNkna3nzST/mRImiveQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-parser/binding-win32-arm64-msvc": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.76.0.tgz", + "integrity": "sha512-G7ZlEWcb2hNwCK3qalzqJoyB6HaTigQ/GEa7CU8sAJ/WwMdG/NnPqiC9IqpEAEy1ARSo4XMALfKbKNuqbSs5mg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-win32-x64-msvc": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.76.0.tgz", + "integrity": "sha512-0jLzzmnu8/mqNhKBnNS2lFUbPEzRdj5ReiZwHGHpjma0+ullmmwP2AqSEqx3ssHDK9CpcEMdKOK2LsbCfhHKIA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.76.0.tgz", + "integrity": "sha512-CH3THIrSViKal8yV/Wh3FK0pFhp40nzW1MUDCik9fNuid2D/7JJXKJnfFOAvMxInGXDlvmgT6ACAzrl47TqzkQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "cpu": [ @@ -1960,7 +2451,6 @@ "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1978,16 +2468,6 @@ "assertion-error": "^2.0.1" } }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -2016,31 +2496,14 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { - "version": "24.10.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.13.tgz", - "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==", + "version": "25.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", + "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/normalize-package-data": { @@ -2057,13 +2520,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/vscode": { "version": "1.99.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.99.0.tgz", @@ -2316,7 +2772,38 @@ "node": ">=20.0.0" } }, - "node_modules/@vitest/expect": { + "node_modules/@vitest/coverage-v8": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz", + "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.0.18", + "ast-v8-to-istanbul": "^0.3.10", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.1", + "obug": "^2.1.1", + "std-env": "^3.10.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.0.18", + "vitest": "4.0.18" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { "version": "4.0.18", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", @@ -2676,7 +3163,6 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2742,7 +3228,6 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2823,6 +3308,25 @@ "node": ">=12" } }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz", + "integrity": "sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -3005,6 +3509,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/binaryen": { + "version": "123.0.0", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-123.0.0.tgz", + "integrity": "sha512-/hls/a309aZCc0itqP6uhoR+5DsKSlJVfB8Opd2BY9Ndghs84IScTunlyidyF4r2Xe3lQttnfBNIDjaNpj6mTw==", + "license": "Apache-2.0", + "bin": { + "wasm-as": "bin/wasm-as", + "wasm-ctor-eval": "bin/wasm-ctor-eval", + "wasm-dis": "bin/wasm-dis", + "wasm-merge": "bin/wasm-merge", + "wasm-metadce": "bin/wasm-metadce", + "wasm-opt": "bin/wasm-opt", + "wasm-reduce": "bin/wasm-reduce", + "wasm-shell": "bin/wasm-shell", + "wasm2js": "bin/wasm2js" + } + }, "node_modules/binaryextensions": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", @@ -3124,6 +3645,12 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -3190,17 +3717,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -3228,17 +3744,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/cheerio": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", @@ -3298,6 +3803,33 @@ "dev": true, "license": "MIT" }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cockatiel": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", @@ -3475,20 +4007,6 @@ } } }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -3613,16 +4131,6 @@ "node": ">=0.4.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -3634,20 +4142,6 @@ "node": ">=8" } }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/diff": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", @@ -3920,7 +4414,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { @@ -4391,20 +4884,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -4557,15 +5036,6 @@ "node": ">= 6" } }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -4688,6 +5158,18 @@ "node": ">= 0.4" } }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -4753,13 +5235,6 @@ "license": "MIT", "optional": true }, - "node_modules/github-slugger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", - "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", - "dev": true, - "license": "ISC" - }, "node_modules/glob": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", @@ -4799,9 +5274,9 @@ } }, "node_modules/globals": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", - "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", "dev": true, "license": "MIT", "engines": { @@ -4999,6 +5474,13 @@ "node": ">=10" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/htmlparser2": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", @@ -5429,6 +5911,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-it-type": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/is-it-type/-/is-it-type-5.1.3.tgz", @@ -5594,6 +6088,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -5670,6 +6176,45 @@ "dev": true, "license": "ISC" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/istextorbinary": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", @@ -5983,101 +6528,38 @@ "dev": true, "license": "MIT" }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lru-cache": { + "node_modules/log-symbols": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" + "node": ">=18" }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "dev": true, - "license": "MIT", "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, + "node_modules/log-symbols/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "dev": true, - "license": "MIT", - "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" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "license": "MIT", "engines": { "node": ">=12" @@ -6086,862 +6568,118 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "license": "MIT", + "license": "ISC", "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" + "yallist": "^4.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=10" } }, - "node_modules/mdast-util-frontmatter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", - "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.23.0.tgz", - "integrity": "sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true, - "license": "MIT" - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/micromark-extension-frontmatter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", - "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fault": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "dev": true, - "license": "MIT", - "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" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-symbol": "^2.0.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-types": "^2.0.0" + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" } }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" } }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "node_modules/mdn-data": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.23.0.tgz", + "integrity": "sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT" }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -6992,6 +6730,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -7508,6 +7258,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/open": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", @@ -7545,6 +7310,64 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ovsx": { "version": "0.10.9", "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.10.9.tgz", @@ -7596,6 +7419,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oxc-parser": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.76.0.tgz", + "integrity": "sha512-l98B2e9evuhES7zN99rb1QGhbzx25829TJFaKi2j0ib3/K/G5z1FdGYz6HZkrU3U8jdH7v2FC8mX1j2l9JrOUg==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "^0.76.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-parser/binding-android-arm64": "0.76.0", + "@oxc-parser/binding-darwin-arm64": "0.76.0", + "@oxc-parser/binding-darwin-x64": "0.76.0", + "@oxc-parser/binding-freebsd-x64": "0.76.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.76.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.76.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.76.0", + "@oxc-parser/binding-linux-arm64-musl": "0.76.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.76.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.76.0", + "@oxc-parser/binding-linux-x64-gnu": "0.76.0", + "@oxc-parser/binding-linux-x64-musl": "0.76.0", + "@oxc-parser/binding-wasm32-wasi": "0.76.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.76.0", + "@oxc-parser/binding-win32-x64-msvc": "0.76.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8204,6 +8059,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -8721,7 +8592,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -8835,6 +8705,15 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -8845,6 +8724,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -8895,6 +8784,18 @@ "dev": true, "license": "MIT" }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -9040,7 +8941,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -9270,6 +9170,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/terser": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -9418,7 +9342,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "devOptional": true, "license": "0BSD" }, "node_modules/tunnel": { @@ -9643,9 +9567,9 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, @@ -9662,65 +9586,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -10439,17 +10304,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "wit-bindgen-wasm/pkg": { "name": "wit-bindgen-wasm", "version": "0.1.0", diff --git a/package.json b/package.json index 4c10777..a9bdee2 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,9 @@ "scripts": { "clean": "rimraf out dist types && rimraf --glob \"*.vsix\"", "clean-wasm": "rimraf wit-bindgen-wasm/pkg wit-bindgen-wasm/target", + "clean-samples": "rimraf samples/target samples/tmp/*", "clean-deps": "rimraf node_modules package-lock.json", - "clean-all": "npm run clean && npm run clean-wasm", + "clean-all": "npm run clean && npm run clean-wasm && npm run clean-samples", "clean-full": "npm run clean-all && npm run clean-deps", "setup-wasm": "./scripts/setup-wasm.sh", "install-extension": "npm run package && code --install-extension wit-idl.vsix", @@ -33,6 +34,7 @@ "test-unit": "vitest run", "test-unit-watch": "vitest", "test-unit-ui": "vitest --ui", + "test-unit-coverage": "vitest run --coverage", "test-build-resources": "vitest run tests/build-resources.test.ts", "test-navigator-polyfill": "vitest run tests/navigator-polyfill.test.ts", "gen-types": "tsc --project tsconfig.json --emitDeclarationOnly", @@ -42,14 +44,16 @@ "build-extension-prod": "node esbuild.mts -- --production", "build-extension": "node esbuild.mts", "build-extension-watch": "node esbuild.mts --watch", + "build-samples": "cd samples && ./build.sh", "build": "run-s build-wasm build-extension", + "build-with-samples": "run-s build build-samples", "vscode:prepublish": "run-s build-wasm-prod build-extension-prod", "package": "npx -y @vscode/vsce package -o wit-idl.vsix", "fmt": "prettier . --write", "fmt-check": "prettier --check .", "lint": "eslint src --ext ts ./*.m?s", "lint-fix": "eslint src --ext ts ./*.m?s --fix", - "test": "run-s lint fmt-check build package test-grammar test-unit", + "test": "run-s lint fmt-check build package gen-types test-grammar test-unit", "test-wasm": "cd wit-bindgen-wasm && cargo test", "check-wasm": "cd wit-bindgen-wasm && cargo check", "verify-wasm": "node -e \"console.log('Verifying WASM build...'); const fs = require('fs'); const path = 'wit-bindgen-wasm/pkg/wit_bindgen_wasm_bg.wasm'; if (fs.existsSync(path)) { console.log('✅ WASM file exists:', path); const size = Math.round(fs.statSync(path).size / 1024); console.log('📏 Size:', size, 'KB'); try { const fd = fs.openSync(path, 'r'); const header = Buffer.alloc(8); fs.readSync(fd, header, 0, 8, 0); fs.closeSync(fd); const magicOk = header[0] === 0x00 && header[1] === 0x61 && header[2] === 0x73 && header[3] === 0x6D; if (!magicOk) { console.warn('⚠️ Not a WebAssembly binary (bad magic).'); process.exit(1); } const version = header.readUInt32LE(4); if (version === 1) { console.log('🧩 Type: Core WebAssembly module (vanilla wasm)'); } else if (version === 0x0A) { console.log('🧩 Type: WebAssembly component'); } else { console.log('🧩 Type: Unknown/other (version ' + version + ')'); } } catch (e) { console.error('❌ Error reading header:', e.message); process.exit(1); } } else { console.error('❌ WASM file not found:', path); process.exit(1); }\"", @@ -65,22 +69,23 @@ "update-major": "run-s update-npm-major update-cargo-major" }, "dependencies": { + "@bytecodealliance/jco": "1.17.0", "wit-bindgen-wasm": "file:wit-bindgen-wasm/pkg" }, "devDependencies": { "@eslint/css": "0.14.1", "@eslint/js": "10.0.1", - "@eslint/markdown": "7.5.1", - "@types/node": "24.10.13", + "@types/node": "25.3.3", "@types/vscode": "1.99.0", "@typescript-eslint/parser": "8.56.1", + "@vitest/coverage-v8": "4.0.18", "@vitest/ui": "4.0.18", "@vscode/vsce": "3.7.1", "esbuild": "0.27.3", "esbuild-node-externals": "1.20.1", "eslint": "10.0.2", "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", + "globals": "17.4.0", "npm-run-all": "4.1.5", "ovsx": "0.10.9", "prettier": "3.8.1", @@ -147,45 +152,50 @@ "category": "WIT" }, { - "command": "wit-idl.generateBindings", - "title": "Generate Language Bindings", - "category": "WIT" - }, - { - "command": "wit-idl.generateBindingsRust", + "command": "wit-idl.generateGuestBindingsRust", "title": "Generate Rust Bindings", "category": "WIT" }, { - "command": "wit-idl.generateBindingsC", + "command": "wit-idl.generateGuestBindingsC", "title": "Generate C Bindings", "category": "WIT" }, { - "command": "wit-idl.generateBindingsCpp", + "command": "wit-idl.generateGuestBindingsCpp", "title": "Generate C++ Bindings", "category": "WIT" }, { - "command": "wit-idl.generateBindingsCSharp", + "command": "wit-idl.generateGuestBindingsCSharp", "title": "Generate C# Bindings", "category": "WIT" }, { - "command": "wit-idl.generateBindingsGo", + "command": "wit-idl.generateGuestBindingsGo", "title": "Generate Go Bindings", "category": "WIT" }, { - "command": "wit-idl.generateBindingsMoonBit", + "command": "wit-idl.generateGuestBindingsMoonBit", "title": "Generate MoonBit Bindings", "category": "WIT" }, { - "command": "wit-idl.generateBindingsMarkdown", + "command": "wit-idl.generateGuestBindingsJavaScript", + "title": "Generate JavaScript Bindings", + "category": "WIT" + }, + { + "command": "wit-idl.generateDocMarkdown", "title": "Generate Markdown Documentation", "category": "WIT" }, + { + "command": "wit-idl.generateHostBindingsJavaScript", + "title": "Generate JavaScript Bindings", + "category": "WIT" + }, { "command": "wit-idl.extractWit", "title": "Extract WIT", @@ -205,7 +215,15 @@ "submenus": [ { "id": "wit-idl.generateBindings.submenu", - "label": "Generate Bindings" + "label": "Generate Guest Bindings" + }, + { + "id": "wit-idl.generateDocs.submenu", + "label": "Generate Documentation" + }, + { + "id": "wit-idl.generateHostBindings.submenu", + "label": "Generate Host Bindings" } ], "menus": { @@ -234,6 +252,16 @@ "submenu": "wit-idl.generateBindings.submenu", "when": "resourceExtname == .wit || witIdl.isWasmComponent", "group": "4_witIdl@14" + }, + { + "submenu": "wit-idl.generateHostBindings.submenu", + "when": "resourceExtname == .wit || witIdl.isWasmComponent", + "group": "4_witIdl@15" + }, + { + "submenu": "wit-idl.generateDocs.submenu", + "when": "resourceExtname == .wit || witIdl.isWasmComponent", + "group": "4_witIdl@16" } ], "explorer/context": [ @@ -261,29 +289,49 @@ "submenu": "wit-idl.generateBindings.submenu", "when": "resourceExtname == .wit || witIdl.isWasmComponent", "group": "4_witIdl@14" + }, + { + "submenu": "wit-idl.generateHostBindings.submenu", + "when": "resourceExtname == .wit || witIdl.isWasmComponent", + "group": "4_witIdl@15" + }, + { + "submenu": "wit-idl.generateDocs.submenu", + "when": "resourceExtname == .wit || witIdl.isWasmComponent", + "group": "4_witIdl@16" } ], "wit-idl.generateBindings.submenu": [ { - "command": "wit-idl.generateBindingsRust" + "command": "wit-idl.generateGuestBindingsRust" }, { - "command": "wit-idl.generateBindingsC" + "command": "wit-idl.generateGuestBindingsC" }, { - "command": "wit-idl.generateBindingsCpp" + "command": "wit-idl.generateGuestBindingsCpp" }, { - "command": "wit-idl.generateBindingsCSharp" + "command": "wit-idl.generateGuestBindingsCSharp" }, { - "command": "wit-idl.generateBindingsGo" + "command": "wit-idl.generateGuestBindingsGo" }, { - "command": "wit-idl.generateBindingsMoonBit" + "command": "wit-idl.generateGuestBindingsMoonBit" }, { - "command": "wit-idl.generateBindingsMarkdown" + "command": "wit-idl.generateGuestBindingsJavaScript" + } + ], + "wit-idl.generateDocs.submenu": [ + { + "command": "wit-idl.generateDocMarkdown" + } + ], + "wit-idl.generateHostBindings.submenu": [ + { + "command": "wit-idl.generateHostBindingsJavaScript" } ], "commandPalette": [ @@ -298,31 +346,39 @@ "command": "wit-idl.showVersion" }, { - "command": "wit-idl.generateBindingsRust", + "command": "wit-idl.generateGuestBindingsRust", + "when": "editorLangId == wit || witIdl.isWasmComponent" + }, + { + "command": "wit-idl.generateGuestBindingsC", + "when": "editorLangId == wit || witIdl.isWasmComponent" + }, + { + "command": "wit-idl.generateGuestBindingsCpp", "when": "editorLangId == wit || witIdl.isWasmComponent" }, { - "command": "wit-idl.generateBindingsC", + "command": "wit-idl.generateGuestBindingsCSharp", "when": "editorLangId == wit || witIdl.isWasmComponent" }, { - "command": "wit-idl.generateBindingsCpp", + "command": "wit-idl.generateGuestBindingsGo", "when": "editorLangId == wit || witIdl.isWasmComponent" }, { - "command": "wit-idl.generateBindingsCSharp", + "command": "wit-idl.generateGuestBindingsMoonBit", "when": "editorLangId == wit || witIdl.isWasmComponent" }, { - "command": "wit-idl.generateBindingsGo", + "command": "wit-idl.generateGuestBindingsJavaScript", "when": "editorLangId == wit || witIdl.isWasmComponent" }, { - "command": "wit-idl.generateBindingsMoonBit", + "command": "wit-idl.generateDocMarkdown", "when": "editorLangId == wit || witIdl.isWasmComponent" }, { - "command": "wit-idl.generateBindingsMarkdown", + "command": "wit-idl.generateHostBindingsJavaScript", "when": "editorLangId == wit || witIdl.isWasmComponent" }, { diff --git a/samples/.gitignore b/samples/.gitignore new file mode 100644 index 0000000..7930e61 --- /dev/null +++ b/samples/.gitignore @@ -0,0 +1,2 @@ +/target/ +/tmp/ \ No newline at end of file diff --git a/samples/Cargo.lock b/samples/Cargo.lock new file mode 100644 index 0000000..eb9deb5 --- /dev/null +++ b/samples/Cargo.lock @@ -0,0 +1,819 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adder" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "auditable-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7bf8143dfc3c0258df908843e169b5cc5fcf76c7718bd66135ef4a9cd558c5" +dependencies = [ + "semver", + "serde", + "serde_json", + "topological-sort", +] + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "counter" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "greeter" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "spdx" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3" +dependencies = [ + "smallvec", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "topological-sort" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "wasm-encoder" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d" +dependencies = [ + "anyhow", + "auditable-serde", + "flate2", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "url", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10fb6648689b3929d56bbc7eb1acf70c9a42a29eb5358c67c10f54dbd5d695de" +dependencies = [ + "wit-bindgen-rt", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92fa781d4f2ff6d3f27f3cc9b74a73327b31ca0dc4a3ef25a0ce2983e0e5af9b" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621" +dependencies = [ + "bitflags", + "futures", + "once_cell", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0809dc5ba19e2e98661bf32fc0addc5a3ca5bf3a6a7083aa6ba484085ff3ce" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad19eec017904e04c60719592a803ee5da76cb51c81e3f6fbf9457f59db49799" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/samples/Cargo.toml b/samples/Cargo.toml new file mode 100644 index 0000000..8f5299e --- /dev/null +++ b/samples/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "adder", + "greeter", + "counter", +] +resolver = "2" diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 0000000..f903af0 --- /dev/null +++ b/samples/README.md @@ -0,0 +1,121 @@ +# WIT Component Samples + +Sample [WebAssembly Interface Types (WIT)](https://component-model.bytecodealliance.org/design/wit.html) projects that compile to WebAssembly components. These can be used to test the **vscode-wit** extension features such as WIT validation, binding generation, and WIT extraction from `.wasm` components. + +## Samples + +| Sample | Description | WIT Features | +|--------|-------------|--------------| +| [adder](adder/) | Adds two numbers | functions, `u32` type | +| [greeter](greeter/) | Greets a person by name, with multi-language support | strings, enums, multiple interfaces | +| [counter](counter/) | Stateful counter | resources, constructors, methods | + +## Prerequisites + +```bash +# Rust toolchain with the WASI P2 target +rustup target add wasm32-wasip2 + +# (Optional) wasm-tools for inspecting built components +cargo install wasm-tools +``` + +## Building + +Build all samples at once: + +```bash +# Debug build +./build.sh + +# Release build (smaller binaries) +./build.sh --release +``` + +Or build individually: + +```bash +cd adder +cargo build --target wasm32-wasip2 +``` + +Built `.wasm` component files are output to `target/wasm32-wasip2/{debug,release}/`. + +## Inspecting Components + +After building, you can extract the WIT interface from any component: + +```bash +wasm-tools component wit target/wasm32-wasip2/release/adder.wasm +``` + +Or use the **vscode-wit** extension: open a `.wasm` file and run the **Extract WIT** command. + +## Sample Details + +### adder + +A minimal component based on the [official Component Model tutorial](https://component-model.bytecodealliance.org/language-support/building-a-simple-component/rust.html). Exports an `add` interface with a single function. + +```wit +package docs:adder@0.1.0; + +interface add { + add: func(x: u32, y: u32) -> u32; +} + +world adder { + export add; +} +``` + +### greeter + +Demonstrates string handling and enums. Exports two interfaces: a simple `greet` and a `multi-greet` that supports multiple languages. + +```wit +package docs:greeter@0.1.0; + +interface greet { + greet: func(name: string) -> string; +} + +interface multi-greet { + enum language { english, spanish, french, german, japanese } + greet-in: func(name: string, lang: language) -> string; +} + +world greeter { + export greet; + export multi-greet; +} +``` + +### counter + +Demonstrates WIT resources — object-like types with constructors and methods. + +```wit +package docs:counter@0.1.0; + +interface types { + resource counter { + constructor(initial: s32); + increment-by: func(amount: s32) -> s32; + decrement-by: func(amount: s32) -> s32; + get-value: func() -> s32; + reset: func() -> s32; + } +} + +world counter { + export types; +} +``` + +## References + +- [Component Model Book](https://component-model.bytecodealliance.org/) +- [WIT specification](https://component-model.bytecodealliance.org/design/wit.html) +- [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen) +- [wasm-tools](https://github.com/bytecodealliance/wasm-tools) diff --git a/samples/adder/Cargo.toml b/samples/adder/Cargo.toml new file mode 100644 index 0000000..b1194e8 --- /dev/null +++ b/samples/adder/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "adder" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = "0.41" diff --git a/samples/adder/src/lib.rs b/samples/adder/src/lib.rs new file mode 100644 index 0000000..e641219 --- /dev/null +++ b/samples/adder/src/lib.rs @@ -0,0 +1,19 @@ +mod bindings { + //! Generated code for implementing the `adder` world in `wit/world.wit`. + wit_bindgen::generate!({ + path: "wit/world.wit", + }); + + use super::AdderComponent; + export!(AdderComponent); +} + +/// Component that implements the `docs:adder/add` interface. +struct AdderComponent; + +impl bindings::exports::docs::adder::add::Guest for AdderComponent { + /// Add two unsigned 32-bit integers. + fn add(x: u32, y: u32) -> u32 { + x + y + } +} diff --git a/samples/adder/wit/world.wit b/samples/adder/wit/world.wit new file mode 100644 index 0000000..d36bd9d --- /dev/null +++ b/samples/adder/wit/world.wit @@ -0,0 +1,10 @@ +package docs:adder@0.1.0; + +interface add { + /// Add two unsigned 32-bit integers. + add: func(x: u32, y: u32) -> u32; +} + +world adder { + export add; +} diff --git a/samples/build.sh b/samples/build.sh new file mode 100755 index 0000000..5c54089 --- /dev/null +++ b/samples/build.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -euo pipefail + +# Build all sample WebAssembly components +# Usage: ./build.sh [--release] +# +# Prerequisites: +# rustup target add wasm32-wasip2 +# cargo install wasm-tools (optional, for inspection) + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" + +PROFILE="debug" +CARGO_FLAGS="" +if [[ "${1:-}" == "--release" ]]; then + PROFILE="release" + CARGO_FLAGS="--release" +fi + +echo "=== Building sample WebAssembly components ($PROFILE) ===" +echo "" + +# Build all workspace members +cargo build --target wasm32-wasip2 $CARGO_FLAGS + +echo "" +echo "=== Built components ===" + +COMPONENTS=(adder greeter counter) +for name in "${COMPONENTS[@]}"; do + WASM="target/wasm32-wasip2/$PROFILE/$name.wasm" + if [[ -f "$WASM" ]]; then + SIZE=$(du -h "$WASM" | cut -f1) + echo " ✅ $name ($SIZE) -> $WASM" + + # If wasm-tools is available, show the WIT interface + if command -v wasm-tools &>/dev/null; then + echo " WIT interface:" + wasm-tools component wit "$WASM" 2>/dev/null | sed 's/^/ /' || true + echo "" + fi + else + echo " ❌ $name - build output not found at $WASM" + fi +done + +echo "" +echo "=== Done ===" +echo "" +echo "Component .wasm files are in: $SCRIPT_DIR/target/wasm32-wasip2/$PROFILE/" +echo "" +echo "To inspect a component: wasm-tools component wit " +echo "To validate a component: wasm-tools validate " diff --git a/samples/counter/Cargo.toml b/samples/counter/Cargo.toml new file mode 100644 index 0000000..16bc730 --- /dev/null +++ b/samples/counter/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "counter" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = "0.41" diff --git a/samples/counter/src/lib.rs b/samples/counter/src/lib.rs new file mode 100644 index 0000000..360b55f --- /dev/null +++ b/samples/counter/src/lib.rs @@ -0,0 +1,60 @@ +use std::cell::Cell; + +mod bindings { + //! Generated code for implementing the `counter` world in `wit/world.wit`. + wit_bindgen::generate!({ + path: "wit/world.wit", + }); + + use super::CounterResource; + export!(CounterResource); +} + +use bindings::exports::docs::counter::types::{Guest, GuestCounter}; + +/// Top-level component export. +struct CounterResource; + +impl Guest for CounterResource { + type Counter = Counter; +} + +/// A counter resource that tracks a running total. +pub struct Counter { + value: Cell, +} + +impl GuestCounter for Counter { + /// Create a new counter with an initial value. + fn new(initial: i32) -> Self { + Counter { + value: Cell::new(initial), + } + } + + /// Increment the counter by a given amount and return the new value. + fn increment_by(&self, amount: i32) -> i32 { + let new_val = self.value.get() + amount; + self.value.set(new_val); + new_val + } + + /// Decrement the counter by a given amount and return the new value. + fn decrement_by(&self, amount: i32) -> i32 { + let new_val = self.value.get() - amount; + self.value.set(new_val); + new_val + } + + /// Get the current value of the counter. + fn get_value(&self) -> i32 { + self.value.get() + } + + /// Reset the counter to zero and return the old value. + fn reset(&self) -> i32 { + let old = self.value.get(); + self.value.set(0); + old + } +} diff --git a/samples/counter/wit/world.wit b/samples/counter/wit/world.wit new file mode 100644 index 0000000..c20bc3d --- /dev/null +++ b/samples/counter/wit/world.wit @@ -0,0 +1,25 @@ +package docs:counter@0.1.0; + +interface types { + /// A counter resource that tracks a running total. + resource counter { + /// Create a new counter with an initial value. + constructor(initial: s32); + + /// Increment the counter by a given amount and return the new value. + increment-by: func(amount: s32) -> s32; + + /// Decrement the counter by a given amount and return the new value. + decrement-by: func(amount: s32) -> s32; + + /// Get the current value of the counter. + get-value: func() -> s32; + + /// Reset the counter to zero and return the old value. + reset: func() -> s32; + } +} + +world counter { + export types; +} diff --git a/samples/greeter/Cargo.toml b/samples/greeter/Cargo.toml new file mode 100644 index 0000000..91fef75 --- /dev/null +++ b/samples/greeter/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "greeter" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = "0.41" diff --git a/samples/greeter/src/lib.rs b/samples/greeter/src/lib.rs new file mode 100644 index 0000000..981b742 --- /dev/null +++ b/samples/greeter/src/lib.rs @@ -0,0 +1,34 @@ +mod bindings { + //! Generated code for implementing the `greeter` world in `wit/world.wit`. + wit_bindgen::generate!({ + path: "wit/world.wit", + }); + + use super::GreeterComponent; + export!(GreeterComponent); +} + +use bindings::exports::docs::greeter::multi_greet::Language; + +/// Component that implements the `docs:greeter` interfaces. +struct GreeterComponent; + +impl bindings::exports::docs::greeter::greet::Guest for GreeterComponent { + /// Greet someone by name in English. + fn greet(name: String) -> String { + format!("Hello, {name}!") + } +} + +impl bindings::exports::docs::greeter::multi_greet::Guest for GreeterComponent { + /// Greet someone in a specific language. + fn greet_in(name: String, lang: Language) -> String { + match lang { + Language::English => format!("Hello, {name}!"), + Language::Spanish => format!("¡Hola, {name}!"), + Language::French => format!("Bonjour, {name}!"), + Language::German => format!("Hallo, {name}!"), + Language::Japanese => format!("こんにちは、{name}!"), + } + } +} diff --git a/samples/greeter/wit/world.wit b/samples/greeter/wit/world.wit new file mode 100644 index 0000000..6dfa917 --- /dev/null +++ b/samples/greeter/wit/world.wit @@ -0,0 +1,25 @@ +package docs:greeter@0.1.0; + +interface greet { + /// Greet someone by name. + greet: func(name: string) -> string; +} + +interface multi-greet { + /// Supported greeting languages. + enum language { + english, + spanish, + french, + german, + japanese, + } + + /// Greet someone in a specific language. + greet-in: func(name: string, lang: language) -> string; +} + +world greeter { + export greet; + export multi-greet; +} diff --git a/src/bindingsSource.ts b/src/bindingsSource.ts new file mode 100644 index 0000000..d04cc3b --- /dev/null +++ b/src/bindingsSource.ts @@ -0,0 +1,37 @@ +import * as fs from "node:fs"; + +/** + * Minimal URI shape used for deciding whether WIT generation can use a file path. + */ +export interface UriLike { + scheme: string; + fsPath: string; +} + +/** + * Returns true when a URI points to an existing, file-backed `.wit` document. + * + * Path-based generation is preferred for real `.wit` files because it allows + * `deps/` resolution. Virtual WIT views (for example `wit-extract:`) and + * unsaved editors must use in-memory content generation. + */ +export function canUseWitPathGeneration(uri: UriLike): boolean { + if (uri.scheme === "wit-extract") { + return false; + } + + const fsPath: string = uri.fsPath; + if (!fsPath || !fsPath.toLowerCase().endsWith(".wit")) { + return false; + } + + return fs.existsSync(fsPath); +} + +/** + * Returns true when a URI represents the virtual extracted-WIT editor for a + * backing `.wasm` component file. + */ +export function isExtractedWitFromWasm(uri: UriLike): boolean { + return uri.scheme === "wit-extract" && uri.fsPath.toLowerCase().endsWith(".wasm"); +} diff --git a/src/extension.ts b/src/extension.ts index c618dcf..2e97b15 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -10,6 +10,14 @@ import { generateBindingsFromWasm, extractCoreWasmFromComponent, } from "./wasmUtils.js"; +import { + generateJsBindingsFromPath, + generateJsBindings, + transpileJsHostFromPath, + type JsBindingsMode, +} from "./jsBindings.js"; +import { determineHostJsGenerationStrategy } from "./hostBindingsRouting.js"; +import { canUseWitPathGeneration } from "./bindingsSource.js"; class WitExtractContentProvider implements vscode.TextDocumentContentProvider, vscode.Disposable { private readonly _onDidChangeEmitter = new vscode.EventEmitter(); @@ -191,6 +199,11 @@ const staticCompletions = new vscode.CompletionList( true ); +/** + * Activate the WIT extension. + * Registers all commands, providers, and event listeners. + * @param context - The extension context for managing subscriptions + */ export function activate(context: vscode.ExtensionContext) { const validator = new WitSyntaxValidator(); const witExtractProvider = new WitExtractContentProvider(); @@ -474,203 +487,525 @@ export function activate(context: vscode.ExtensionContext) { } ); - const createGenerateBindingsCommand = (language: string, languageLabel: string) => { - return vscode.commands.registerCommand( - `wit-idl.generateBindings${languageLabel}`, - async (resource?: vscode.Uri) => { - try { - const active = vscode.window.activeTextEditor; - const targetUri: vscode.Uri | undefined = resource ?? active?.document.uri; - if (!targetUri) { - vscode.window.showErrorMessage("No file selected. Open a .wit or .wasm component file."); - return; - } + const getGuestGenerationLabel = (languageLabel: string, isDocumentationGeneration: boolean): string => { + if (isDocumentationGeneration) { + return `${languageLabel} documentation`; + } + return `${languageLabel} guest bindings`; + }; - let witContent: string; - let diagDoc: vscode.TextDocument | undefined; - - const isWitDoc = - active && - active.document.uri.toString() === targetUri.toString() && - active.document.languageId === "wit"; - if (isWitDoc) { - witContent = active!.document.getText(); - diagDoc = active!.document; - if (!witContent.trim()) { - vscode.window.showWarningMessage("The .wit file is empty."); - return; - } - } else if (targetUri.fsPath.toLowerCase().endsWith(".wasm")) { - const comp = await isWasmComponentFile(targetUri.fsPath); - if (!comp) { - vscode.window.showWarningMessage("The selected .wasm is not a WebAssembly component."); - return; - } - const bytes = await vscode.workspace.fs.readFile(targetUri); - const extracted = await extractWitFromComponent(bytes); - if (!extracted.trim()) { - vscode.window.showWarningMessage("No WIT could be extracted from this component."); - return; - } - witContent = extracted; - const os = await import("os"); - const tmpDir = os.tmpdir(); - const base = path.basename(targetUri.fsPath, ".wasm"); - const tmpPath = path.join(tmpDir, `wit-idl-${base}-${Date.now()}.wit`); - fs.writeFileSync(tmpPath, witContent, "utf8"); - diagDoc = await vscode.workspace.openTextDocument(tmpPath); - } else { - vscode.window.showErrorMessage("Unsupported file type. Select a .wit or .wasm component file."); - return; - } + const resolveSafeGeneratedOutputPath = (outputRoot: string, generatedName: string): string | undefined => { + if (path.isAbsolute(generatedName)) { + return undefined; + } - const worldMatch = witContent.match(/world\s+([a-zA-Z][a-zA-Z0-9-_]*)/g); - const selectedWorld = undefined; + const normalized = path.posix.normalize(generatedName.replace(/\\/g, "/")); + if (normalized === "." || normalized.startsWith("../") || normalized.includes("/../")) { + return undefined; + } - const workspaceFolders = vscode.workspace.workspaceFolders; - const defaultUri = workspaceFolders ? workspaceFolders[0].uri : undefined; + const pathSegments = normalized.split("/").filter((segment) => segment.length > 0); + if (pathSegments.length === 0) { + return undefined; + } - const outputUri = await vscode.window.showOpenDialog({ - canSelectFiles: false, - canSelectFolders: true, - canSelectMany: false, - defaultUri, - openLabel: "Select Output Directory", - title: `Select directory for ${languageLabel} bindings`, - }); + const outputPath = path.join(outputRoot, ...pathSegments); + const relative = path.relative(outputRoot, outputPath); + if (relative.startsWith("..") || path.isAbsolute(relative)) { + return undefined; + } - if (!outputUri || outputUri.length === 0) { + return outputPath; + }; + + const createGenerateBindingsCommand = (language: string, commandId: string) => { + const languageLabels: Record = { + rust: "Rust", + c: "C", + cpp: "C++", + csharp: "C#", + go: "Go", + moonbit: "MoonBit", + markdown: "Markdown", + }; + const languageLabel = languageLabels[language] ?? language; + const isDocumentationGeneration = language === "markdown"; + const generationLabel = getGuestGenerationLabel(languageLabel, isDocumentationGeneration); + return vscode.commands.registerCommand(commandId, async (resource?: vscode.Uri) => { + try { + const active = vscode.window.activeTextEditor; + const targetUri: vscode.Uri | undefined = resource ?? active?.document.uri; + if (!targetUri) { + vscode.window.showErrorMessage("No file selected. Open a .wit or .wasm component file."); + return; + } + + let witContent: string; + let diagDoc: vscode.TextDocument | undefined; + + const isWitDoc = + active && + active.document.uri.toString() === targetUri.toString() && + active.document.languageId === "wit"; + if (isWitDoc) { + witContent = active!.document.getText(); + diagDoc = active!.document; + if (!witContent.trim()) { + vscode.window.showWarningMessage("The .wit file is empty."); return; } - - const outputPath = outputUri[0].fsPath; - - const bindingFiles = await generateBindingsFromWasm(witContent, language, selectedWorld); - - const fileEntries = Object.entries(bindingFiles); - const errorFile = fileEntries.find(([filename]) => filename === "error.txt"); - - if (errorFile) { - const errorMessage = errorFile[1]; - const docPath = diagDoc?.uri.fsPath ?? targetUri.fsPath; - const parsedError = validator.parseWitBindgenError(errorMessage, docPath); - - if (parsedError && diagDoc) { - const diagnostic = validator.createDiagnosticFromError(parsedError, diagDoc); - validator.getDiagnosticCollection().set(diagDoc.uri, [diagnostic]); - } else { - const cleanMessage = errorMessage.replace(/^\/\/\s*/, "").replace(/\n\/\/\s*/g, "\n"); - const diagnostic = new vscode.Diagnostic( - new vscode.Range(0, 0, 0, 1), - `Failed to generate ${languageLabel} bindings: ${cleanMessage}`, - vscode.DiagnosticSeverity.Error - ); - diagnostic.source = "wit-bindgen"; - diagnostic.code = "binding-generation-error"; - const uriForDiag = diagDoc?.uri ?? targetUri; - validator.getDiagnosticCollection().set(uriForDiag, [diagnostic]); - } + } else if (targetUri.fsPath.toLowerCase().endsWith(".wasm")) { + const comp = await isWasmComponentFile(targetUri.fsPath); + if (!comp) { + vscode.window.showWarningMessage("The selected .wasm is not a WebAssembly component."); return; } - - if (fileEntries.length === 0) { - const errorMessage = - "No files were generated. This may be due to invalid WIT syntax or unsupported features."; - const docPath = diagDoc?.uri.fsPath ?? targetUri.fsPath; - const parsedError = validator.parseWitBindgenError(errorMessage, docPath); - - if (parsedError && diagDoc) { - const diagnostic = validator.createDiagnosticFromError(parsedError, diagDoc); - validator.getDiagnosticCollection().set(diagDoc.uri, [diagnostic]); - } else { - const diagnostic = new vscode.Diagnostic( - new vscode.Range(0, 0, 0, 1), - `Failed to generate ${languageLabel} bindings: ${errorMessage}`, - vscode.DiagnosticSeverity.Error - ); - diagnostic.source = "wit-bindgen"; - diagnostic.code = "binding-generation-error"; - const uriForDiag = diagDoc?.uri ?? targetUri; - validator.getDiagnosticCollection().set(uriForDiag, [diagnostic]); - } + const bytes = await vscode.workspace.fs.readFile(targetUri); + const extracted = await extractWitFromComponent(bytes); + if (!extracted.trim()) { + vscode.window.showWarningMessage("No WIT could be extracted from this component."); return; } + witContent = extracted; + const os = await import("os"); + const tmpDir = os.tmpdir(); + const base = path.basename(targetUri.fsPath, ".wasm"); + const tmpPath = path.join(tmpDir, `wit-idl-${base}-${Date.now()}.wit`); + fs.writeFileSync(tmpPath, witContent, "utf8"); + diagDoc = await vscode.workspace.openTextDocument(tmpPath); + } else { + vscode.window.showErrorMessage("Unsupported file type. Select a .wit or .wasm component file."); + return; + } - if (diagDoc) { - const existingDiagnostics = validator.getDiagnosticCollection().get(diagDoc.uri) || []; - const filteredDiagnostics = existingDiagnostics.filter((d) => d.source !== "wit-bindgen"); - validator.getDiagnosticCollection().set(diagDoc.uri, filteredDiagnostics); - } + const workspaceFolders = vscode.workspace.workspaceFolders; + const defaultUri = workspaceFolders ? workspaceFolders[0].uri : undefined; - const writtenFiles: string[] = []; - for (const [filename, fileContent] of fileEntries) { - const sanitizedFilename = path.basename(filename); - const filePath = path.join(outputPath, sanitizedFilename); + const outputUri = await vscode.window.showOpenDialog({ + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + defaultUri, + openLabel: "Select Output Directory", + title: `Select directory for ${generationLabel}`, + }); - const dir = path.dirname(filePath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } + if (!outputUri || outputUri.length === 0) { + return; + } - const binaryData = Buffer.from(fileContent, "latin1"); - fs.writeFileSync(filePath, binaryData); - writtenFiles.push(sanitizedFilename); - } + const outputPath = outputUri[0].fsPath; - const fileCount = writtenFiles.length; - vscode.window.showInformationMessage( - `${languageLabel} bindings generated successfully! ${fileCount} file${fileCount > 1 ? "s" : ""} written to ${outputPath}` - ); + const bindingFiles = await generateBindingsFromWasm(witContent, language); - const mainFiles = ["src/lib.rs", "main.c", "Program.cs", "main.go", "main.mbt"]; - const mainFile = mainFiles.find((f) => writtenFiles.includes(f)) || writtenFiles[0]; + const fileEntries = Object.entries(bindingFiles); + const errorFile = fileEntries.find(([filename]) => filename === "error.txt"); - if (mainFile) { - const mainFilePath = path.join(outputPath, mainFile); - const doc = await vscode.workspace.openTextDocument(mainFilePath); - await vscode.window.showTextDocument(doc); - } - } catch (error) { - console.error("Failed to generate bindings:", error); - const errorMessage = error instanceof Error ? error.message : String(error); - const uriForDiag = (vscode.window.activeTextEditor?.document ?? undefined)?.uri; - const docPath = uriForDiag?.fsPath ?? ""; + if (errorFile) { + const errorMessage = errorFile[1]; + const docPath = diagDoc?.uri.fsPath ?? targetUri.fsPath; const parsedError = validator.parseWitBindgenError(errorMessage, docPath); - if (parsedError && vscode.window.activeTextEditor) { - const diagnostic = validator.createDiagnosticFromError( - parsedError, - vscode.window.activeTextEditor.document + if (parsedError && diagDoc) { + const diagnostic = validator.createDiagnosticFromError(parsedError, diagDoc); + validator.getDiagnosticCollection().set(diagDoc.uri, [diagnostic]); + } else { + const cleanMessage = errorMessage.replace(/^\/\/\s*/, "").replace(/\n\/\/\s*/g, "\n"); + const diagnostic = new vscode.Diagnostic( + new vscode.Range(0, 0, 0, 1), + `Failed to generate ${generationLabel}: ${cleanMessage}`, + vscode.DiagnosticSeverity.Error ); - validator - .getDiagnosticCollection() - .set(vscode.window.activeTextEditor.document.uri, [diagnostic]); + diagnostic.source = "wit-bindgen"; + diagnostic.code = "binding-generation-error"; + const uriForDiag = diagDoc?.uri ?? targetUri; + validator.getDiagnosticCollection().set(uriForDiag, [diagnostic]); + } + return; + } + + if (fileEntries.length === 0) { + const errorMessage = + "No files were generated. This may be due to invalid WIT syntax or unsupported features."; + const docPath = diagDoc?.uri.fsPath ?? targetUri.fsPath; + const parsedError = validator.parseWitBindgenError(errorMessage, docPath); + + if (parsedError && diagDoc) { + const diagnostic = validator.createDiagnosticFromError(parsedError, diagDoc); + validator.getDiagnosticCollection().set(diagDoc.uri, [diagnostic]); } else { const diagnostic = new vscode.Diagnostic( new vscode.Range(0, 0, 0, 1), - `Failed to generate bindings: ${errorMessage}`, + `Failed to generate ${generationLabel}: ${errorMessage}`, vscode.DiagnosticSeverity.Error ); diagnostic.source = "wit-bindgen"; diagnostic.code = "binding-generation-error"; - const uriForSet = uriForDiag ?? resource ?? vscode.window.activeTextEditor?.document.uri; - if (uriForSet) { - validator.getDiagnosticCollection().set(uriForSet, [diagnostic]); - } + const uriForDiag = diagDoc?.uri ?? targetUri; + validator.getDiagnosticCollection().set(uriForDiag, [diagnostic]); + } + return; + } + + if (diagDoc) { + const existingDiagnostics = validator.getDiagnosticCollection().get(diagDoc.uri) || []; + const filteredDiagnostics = existingDiagnostics.filter((d) => d.source !== "wit-bindgen"); + validator.getDiagnosticCollection().set(diagDoc.uri, filteredDiagnostics); + } + + const writtenFiles: string[] = []; + const skippedFiles: string[] = []; + for (const [filename, fileContent] of fileEntries) { + const filePath = resolveSafeGeneratedOutputPath(outputPath, filename); + if (!filePath) { + skippedFiles.push(filename); + continue; + } + + const dir = path.dirname(filePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + + const binaryData = Buffer.from(fileContent, "latin1"); + fs.writeFileSync(filePath, binaryData); + const relativeFilePath = path.relative(outputPath, filePath).replace(/\\/g, "/"); + writtenFiles.push(relativeFilePath); + } + + if (writtenFiles.length === 0) { + const skippedDetails = skippedFiles.length > 0 ? ` Skipped: ${skippedFiles.join(", ")}` : ""; + vscode.window.showErrorMessage(`No files could be written to disk.${skippedDetails}`); + return; + } + + const fileCount = writtenFiles.length; + vscode.window.showInformationMessage( + `${generationLabel} generated successfully! ${fileCount} file${fileCount > 1 ? "s" : ""} written to ${outputPath}` + ); + + const mainFiles = ["src/lib.rs", "main.c", "Program.cs", "main.go", "main.mbt"]; + const mainFile = mainFiles.find((f) => writtenFiles.includes(f)) || writtenFiles[0]; + + if (mainFile) { + const mainFilePath = path.join(outputPath, mainFile); + const doc = await vscode.workspace.openTextDocument(mainFilePath); + await vscode.window.showTextDocument(doc); + } + } catch (error) { + console.error("Failed to generate guest bindings:", error); + const errorMessage = error instanceof Error ? error.message : String(error); + const uriForDiag = (vscode.window.activeTextEditor?.document ?? undefined)?.uri; + const docPath = uriForDiag?.fsPath ?? ""; + const parsedError = validator.parseWitBindgenError(errorMessage, docPath); + + if (parsedError && vscode.window.activeTextEditor) { + const diagnostic = validator.createDiagnosticFromError( + parsedError, + vscode.window.activeTextEditor.document + ); + validator.getDiagnosticCollection().set(vscode.window.activeTextEditor.document.uri, [diagnostic]); + } else { + const diagnostic = new vscode.Diagnostic( + new vscode.Range(0, 0, 0, 1), + `Failed to generate ${generationLabel}: ${errorMessage}`, + vscode.DiagnosticSeverity.Error + ); + diagnostic.source = "wit-bindgen"; + diagnostic.code = "binding-generation-error"; + const uriForSet = uriForDiag ?? resource ?? vscode.window.activeTextEditor?.document.uri; + if (uriForSet) { + validator.getDiagnosticCollection().set(uriForSet, [diagnostic]); } } } - ); + }); }; - // Create individual language binding commands - const generateRustBindingsCommand = createGenerateBindingsCommand("rust", "Rust"); - const generateCBindingsCommand = createGenerateBindingsCommand("c", "C"); - const generateCppBindingsCommand = createGenerateBindingsCommand("cpp", "Cpp"); - const generateCSharpBindingsCommand = createGenerateBindingsCommand("csharp", "CSharp"); - const generateGoBindingsCommand = createGenerateBindingsCommand("go", "Go"); - const generateMoonBitBindingsCommand = createGenerateBindingsCommand("moonbit", "MoonBit"); - const generateMarkdownBindingsCommand = createGenerateBindingsCommand("markdown", "Markdown"); + // Create individual language guest binding commands + const generateGuestRustCommand = createGenerateBindingsCommand("rust", "wit-idl.generateGuestBindingsRust"); + const generateGuestCCommand = createGenerateBindingsCommand("c", "wit-idl.generateGuestBindingsC"); + const generateGuestCppCommand = createGenerateBindingsCommand("cpp", "wit-idl.generateGuestBindingsCpp"); + const generateGuestCSharpCommand = createGenerateBindingsCommand("csharp", "wit-idl.generateGuestBindingsCSharp"); + const generateGuestGoCommand = createGenerateBindingsCommand("go", "wit-idl.generateGuestBindingsGo"); + const generateGuestMoonBitCommand = createGenerateBindingsCommand( + "moonbit", + "wit-idl.generateGuestBindingsMoonBit" + ); + + // Documentation generation commands + const generateDocMarkdownCommand = createGenerateBindingsCommand("markdown", "wit-idl.generateDocMarkdown"); + + const deprecatedGenerateBindingsAliases: Array<{ alias: string; target: string }> = [ + { alias: "wit-idl.generateBindingsRust", target: "wit-idl.generateGuestBindingsRust" }, + { alias: "wit-idl.generateBindingsC", target: "wit-idl.generateGuestBindingsC" }, + { alias: "wit-idl.generateBindingsCpp", target: "wit-idl.generateGuestBindingsCpp" }, + { alias: "wit-idl.generateBindingsCSharp", target: "wit-idl.generateGuestBindingsCSharp" }, + { alias: "wit-idl.generateBindingsGo", target: "wit-idl.generateGuestBindingsGo" }, + { alias: "wit-idl.generateBindingsMoonBit", target: "wit-idl.generateGuestBindingsMoonBit" }, + { alias: "wit-idl.generateBindingsMarkdown", target: "wit-idl.generateDocMarkdown" }, + ]; + + const deprecatedAliasCommands = deprecatedGenerateBindingsAliases.map(({ alias, target }) => + vscode.commands.registerCommand(alias, async (resource?: vscode.Uri) => { + await vscode.commands.executeCommand(target, resource); + }) + ); + + const generateBindingsAliasCommand = vscode.commands.registerCommand( + "wit-idl.generateBindings", + async (resource?: vscode.Uri) => { + const picks: Array<{ label: string; commandId: string }> = [ + { label: "Rust", commandId: "wit-idl.generateGuestBindingsRust" }, + { label: "C", commandId: "wit-idl.generateGuestBindingsC" }, + { label: "C++", commandId: "wit-idl.generateGuestBindingsCpp" }, + { label: "C#", commandId: "wit-idl.generateGuestBindingsCSharp" }, + { label: "Go", commandId: "wit-idl.generateGuestBindingsGo" }, + { label: "MoonBit", commandId: "wit-idl.generateGuestBindingsMoonBit" }, + { label: "JavaScript", commandId: "wit-idl.generateGuestBindingsJavaScript" }, + { label: "Markdown", commandId: "wit-idl.generateDocMarkdown" }, + ]; + + const choice = await vscode.window.showQuickPick( + picks.map((pick) => ({ label: pick.label, commandId: pick.commandId })), + { + title: "Generate Bindings (Deprecated Alias)", + placeHolder: "Select a generator", + } + ); + + if (!choice) { + return; + } + + await vscode.commands.executeCommand(choice.commandId, resource); + } + ); + + /** + * Factory for JavaScript binding commands (guest or host). + * Uses jco's typesComponent under the hood. + */ + const createJsBindingsCommand = (commandId: string, mode: JsBindingsMode): vscode.Disposable => { + return vscode.commands.registerCommand(commandId, async (resource?: vscode.Uri) => { + try { + const active = vscode.window.activeTextEditor; + const targetUri: vscode.Uri | undefined = resource ?? active?.document.uri; + if (!targetUri) { + vscode.window.showErrorMessage("No file selected. Open a .wit or .wasm component file."); + return; + } + + let witContent: string | undefined; + let witFilePath: string | undefined; + + const isWitDoc = + active && + active.document.uri.toString() === targetUri.toString() && + active.document.languageId === "wit"; + + if (isWitDoc) { + witContent = active!.document.getText(); + witFilePath = canUseWitPathGeneration(targetUri) ? targetUri.fsPath : undefined; + if (!witContent.trim()) { + vscode.window.showWarningMessage("The .wit file is empty."); + return; + } + } else if (targetUri.fsPath.toLowerCase().endsWith(".wasm")) { + const comp = await isWasmComponentFile(targetUri.fsPath); + if (!comp) { + vscode.window.showWarningMessage("The selected .wasm is not a WebAssembly component."); + return; + } + const bytes = await vscode.workspace.fs.readFile(targetUri); + const extracted = await extractWitFromComponent(bytes); + if (!extracted.trim()) { + vscode.window.showWarningMessage("No WIT could be extracted from this component."); + return; + } + witContent = extracted; + } else { + vscode.window.showErrorMessage("Unsupported file type. Select a .wit or .wasm component file."); + return; + } + + const workspaceFolders = vscode.workspace.workspaceFolders; + const defaultUri = workspaceFolders ? workspaceFolders[0].uri : undefined; + + const outputUri = await vscode.window.showOpenDialog({ + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + defaultUri, + openLabel: "Select Output Directory", + title: `Select directory for JavaScript ${mode} bindings`, + }); + + if (!outputUri || outputUri.length === 0) { + return; + } + + const outputPath = outputUri[0].fsPath; + + // Use the file path directly when we have a .wit file on disk, + // so that deps/ directories are resolved correctly. + // For .wasm components, pass the extracted WIT content instead. + const result = witFilePath + ? await generateJsBindingsFromPath(witFilePath, mode) + : await generateJsBindings({ witContent: witContent! }, mode); + + const fileEntries = Object.entries(result.files); + if (fileEntries.length === 0) { + vscode.window.showWarningMessage( + "No files were generated. This may be due to invalid WIT syntax or unsupported features." + ); + return; + } + + const writtenFiles: string[] = []; + for (const [filename, fileContent] of fileEntries) { + const filePath = path.join(outputPath, filename); + const dir = path.dirname(filePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(filePath, fileContent); + writtenFiles.push(filename); + } + + const fileCount = writtenFiles.length; + vscode.window.showInformationMessage( + `JavaScript ${mode} bindings generated successfully! ${fileCount} file${fileCount > 1 ? "s" : ""} written to ${outputPath}` + ); + + const mainFile = writtenFiles.find((f) => f.endsWith(".d.ts")) || writtenFiles[0]; + if (mainFile) { + const mainFilePath = path.join(outputPath, mainFile); + const doc = await vscode.workspace.openTextDocument(mainFilePath); + await vscode.window.showTextDocument(doc); + } + } catch (error) { + console.error(`Failed to generate JavaScript ${mode} bindings:`, error); + vscode.window.showErrorMessage( + `Failed to generate JavaScript ${mode} bindings: ${error instanceof Error ? error.message : String(error)}` + ); + } + }); + }; + + // Host bindings commands — uses jco transpile for .wasm, types for .wit + const generateHostJavaScriptCommand = vscode.commands.registerCommand( + "wit-idl.generateHostBindingsJavaScript", + async (resource?: vscode.Uri) => { + try { + const active = vscode.window.activeTextEditor; + const targetUri: vscode.Uri | undefined = resource ?? active?.document.uri; + if (!targetUri) { + vscode.window.showErrorMessage("No file selected. Open a .wit or .wasm component file."); + return; + } + + const workspaceFolders = vscode.workspace.workspaceFolders; + const defaultUri = workspaceFolders ? workspaceFolders[0].uri : undefined; + + const outputUri = await vscode.window.showOpenDialog({ + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + defaultUri, + openLabel: "Select Output Directory", + title: "Select directory for JavaScript host bindings", + }); + + if (!outputUri || outputUri.length === 0) { + return; + } + + const outputPath = outputUri[0].fsPath; + let resultFiles: Record; + + const isWitDoc = + active && + active.document.uri.toString() === targetUri.toString() && + active.document.languageId === "wit"; + + if (isWitDoc) { + const witContent = active!.document.getText(); + if (!witContent.trim()) { + vscode.window.showWarningMessage("The .wit file is empty."); + return; + } + } + + const strategy = determineHostJsGenerationStrategy({ targetUri, isWitDoc: Boolean(isWitDoc) }); + + if (strategy === "transpile-wasm-path") { + const comp = await isWasmComponentFile(targetUri.fsPath); + if (!comp) { + const message = isWitDoc + ? "The source .wasm for this extracted WIT view is not a WebAssembly component." + : "The selected .wasm is not a WebAssembly component."; + vscode.window.showWarningMessage(message); + return; + } + const result = await transpileJsHostFromPath(targetUri.fsPath); + resultFiles = result.files; + } else if (strategy === "types-wit-path") { + const result = await generateJsBindingsFromPath(targetUri.fsPath, "host"); + resultFiles = result.files; + } else if (strategy === "types-wit-content") { + const witContent = active!.document.getText(); + const result = await generateJsBindings({ witContent }, "host"); + resultFiles = result.files; + } else { + vscode.window.showErrorMessage("Unsupported file type. Select a .wit or .wasm component file."); + return; + } + + const fileEntries = Object.entries(resultFiles); + if (fileEntries.length === 0) { + vscode.window.showWarningMessage( + "No files were generated. This may be due to invalid WIT syntax or unsupported features." + ); + return; + } + + const writtenFiles: string[] = []; + for (const [filename, fileContent] of fileEntries) { + const filePath = path.join(outputPath, filename); + const dir = path.dirname(filePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(filePath, fileContent); + writtenFiles.push(filename); + } + + const fileCount = writtenFiles.length; + vscode.window.showInformationMessage( + `JavaScript host bindings generated successfully! ${fileCount} file${fileCount > 1 ? "s" : ""} written to ${outputPath}` + ); + + // Open the main JS or .d.ts file + const mainFile = + writtenFiles.find((f) => f.endsWith(".js") || f.endsWith(".mjs")) || + writtenFiles.find((f) => f.endsWith(".d.ts")) || + writtenFiles[0]; + if (mainFile) { + const mainFilePath = path.join(outputPath, mainFile); + const doc = await vscode.workspace.openTextDocument(mainFilePath); + await vscode.window.showTextDocument(doc); + } + } catch (error) { + console.error("Failed to generate JavaScript host bindings:", error); + vscode.window.showErrorMessage( + `Failed to generate JavaScript host bindings: ${error instanceof Error ? error.message : String(error)}` + ); + } + } + ); + + const generateGuestJavaScriptCommand = createJsBindingsCommand("wit-idl.generateGuestBindingsJavaScript", "guest"); const onSaveListener = vscode.workspace.onDidSaveTextDocument(async (document) => { if (document.languageId === "wit") { @@ -716,13 +1051,17 @@ export function activate(context: vscode.ExtensionContext) { formatDocumentCommand, extractWitCommand, extractCoreWasmCommand, - generateRustBindingsCommand, - generateCBindingsCommand, - generateCppBindingsCommand, - generateCSharpBindingsCommand, - generateGoBindingsCommand, - generateMoonBitBindingsCommand, - generateMarkdownBindingsCommand, + generateGuestRustCommand, + generateGuestCCommand, + generateGuestCppCommand, + generateGuestCSharpCommand, + generateGuestGoCommand, + generateGuestMoonBitCommand, + generateDocMarkdownCommand, + generateGuestJavaScriptCommand, + generateHostJavaScriptCommand, + generateBindingsAliasCommand, + ...deprecatedAliasCommands, wasmToWitProvider, onSaveListener, onOpenListener, @@ -735,4 +1074,8 @@ export function activate(context: vscode.ExtensionContext) { ); } +/** + * Deactivate the WIT extension. + * Resources are cleaned up via context.subscriptions disposal. + */ export function deactivate() {} diff --git a/src/formatter.ts b/src/formatter.ts index 7e264dd..0d7429e 100644 --- a/src/formatter.ts +++ b/src/formatter.ts @@ -41,8 +41,7 @@ export class WitFormatter implements vscode.DocumentFormattingEditProvider { let aliasGenericDepth = 0; let inFuncParams = false; for (let i = 0; i < lines.length; i++) { - const raw = lines[i]; - const trimmed = raw.trim(); + const trimmed = lines[i].trim(); if (trimmed === "") { out.push(""); continue; diff --git a/src/hostBindingsRouting.ts b/src/hostBindingsRouting.ts new file mode 100644 index 0000000..2536c44 --- /dev/null +++ b/src/hostBindingsRouting.ts @@ -0,0 +1,30 @@ +import type { UriLike } from "./bindingsSource.js"; +import { canUseWitPathGeneration, isExtractedWitFromWasm } from "./bindingsSource.js"; + +export type HostJsGenerationStrategy = "transpile-wasm-path" | "types-wit-path" | "types-wit-content" | "unsupported"; + +export interface HostJsRoutingInput { + targetUri: UriLike; + isWitDoc: boolean; +} + +/** + * Determines which generation strategy should be used for host-side JavaScript output. + */ +export function determineHostJsGenerationStrategy(input: HostJsRoutingInput): HostJsGenerationStrategy { + const { targetUri, isWitDoc } = input; + + if (isWitDoc) { + if (isExtractedWitFromWasm(targetUri)) { + return "transpile-wasm-path"; + } + + return canUseWitPathGeneration(targetUri) ? "types-wit-path" : "types-wit-content"; + } + + if (targetUri.fsPath.toLowerCase().endsWith(".wasm")) { + return "transpile-wasm-path"; + } + + return "unsupported"; +} diff --git a/src/jsBindings.ts b/src/jsBindings.ts new file mode 100644 index 0000000..2ef7d2d --- /dev/null +++ b/src/jsBindings.ts @@ -0,0 +1,251 @@ +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; + +/** + * Binding mode: "guest" generates guest-side types, "host" generates host-side types. + */ +export type JsBindingsMode = "guest" | "host"; + +/** + * Options for generating JavaScript bindings from WIT + */ +export interface JsBindingsOptions { + /** WIT content as a string */ + witContent: string; + /** Optional world name to target */ + worldName?: string; +} + +/** + * Result of JavaScript binding generation + */ +export interface JsBindingsResult { + /** Map of relative file paths to file contents (as Uint8Array) */ + files: Record; +} + +/** + * Result of JavaScript host transpilation from a .wasm component + */ +export interface JsTranspileResult { + /** Map of relative file paths to file contents (as Uint8Array) */ + files: Record; + /** Import specifiers the generated JS needs at runtime */ + imports: string[]; + /** Exported items: [name, kind] tuples */ + exports: [string, "function" | "instance"][]; +} + +/** + * Dynamically import jco's typesComponent function. + * + * The function is re-exported from `@bytecodealliance/jco` as `types`. + * We use a dynamic import to avoid loading the module eagerly at startup. + */ +async function getTypesComponent(): Promise< + (witPath: string, opts: { worldName?: string; guest?: boolean }) => Promise> +> { + const jco = (await import("@bytecodealliance/jco")) as { + types: (witPath: string, opts: { worldName?: string; guest?: boolean }) => Promise>; + }; + return jco.types; +} + +/** + * Dynamically import jco's transpileComponent function. + * + * The function is re-exported from `@bytecodealliance/jco` as `transpile`. + * It takes raw WebAssembly component bytes and produces the full JavaScript + * host runtime (JS modules, TypeScript declarations, and embedded core .wasm files). + */ +async function getTranspileComponent(): Promise< + ( + component: Uint8Array, + opts?: { + name?: string; + instantiation?: "async" | "sync"; + map?: Record; + validLiftingOptimization?: boolean; + tracing?: boolean; + nodejsCompat?: boolean; + tlaCompat?: boolean; + base64Cutoff?: boolean; + js?: boolean; + minify?: boolean; + optimize?: boolean; + namespacedExports?: boolean; + multiMemory?: boolean; + } + ) => Promise<{ + files: Record; + imports: string[]; + exports: [string, "function" | "instance"][]; + }> +> { + const jco = (await import("@bytecodealliance/jco")) as { + transpile: ( + component: Uint8Array, + opts?: Record + ) => Promise<{ + files: Record; + imports: string[]; + exports: [string, "function" | "instance"][]; + }>; + }; + return jco.transpile; +} + +/** + * Generate JavaScript/TypeScript bindings from WIT content. + * + * Uses `@bytecodealliance/jco` `typesComponent` to produce TypeScript + * declaration files (.d.ts). In "guest" mode, generates guest module types; + * in "host" mode, generates host-side types describing the component's + * imports and exports from the host perspective. + * + * @param options - Options for generation + * @param mode - "guest" or "host" (defaults to "guest") + * @returns Map of filename to file content + */ +export async function generateJsBindings( + options: JsBindingsOptions, + mode: JsBindingsMode = "guest" +): Promise { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `wit-idl-js-${mode}-`)); + const witFilePath = path.join(tmpDir, "input.wit"); + + try { + fs.writeFileSync(witFilePath, options.witContent, "utf8"); + + const typesComponent = await getTypesComponent(); + const files = await typesComponent(witFilePath, { + worldName: options.worldName, + ...(mode === "guest" ? { guest: true } : {}), + }); + + return { files }; + } finally { + try { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } catch { + // Ignore cleanup errors + } + } +} + +/** + * Generate JavaScript/TypeScript bindings from a WIT file path. + * + * Passes the path directly to jco without copying to a temp directory, + * which allows resolution of dependencies in `deps/` directories. + * + * @param witPath - Absolute path to the WIT file or directory + * @param mode - "guest" or "host" (defaults to "guest") + * @param worldName - Optional world name to target + * @returns Map of filename to file content + */ +export async function generateJsBindingsFromPath( + witPath: string, + mode: JsBindingsMode = "guest", + worldName?: string +): Promise { + const typesComponent = await getTypesComponent(); + const files = await typesComponent(witPath, { + worldName, + ...(mode === "guest" ? { guest: true } : {}), + }); + + return { files }; +} + +// ── Convenience aliases (preserve backward-compatible named exports) ── + +/** @deprecated Use `generateJsBindings(options, "guest")` instead */ +export const generateJsGuestBindings = (options: JsBindingsOptions): Promise => + generateJsBindings(options, "guest"); + +/** @deprecated Use `generateJsBindingsFromPath(path, "guest")` instead */ +export const generateJsGuestBindingsFromPath = (witPath: string, worldName?: string): Promise => + generateJsBindingsFromPath(witPath, "guest", worldName); + +/** @deprecated Use `generateJsBindings(options, "host")` instead */ +export const generateJsHostBindings = (options: JsBindingsOptions): Promise => + generateJsBindings(options, "host"); + +/** @deprecated Use `generateJsBindingsFromPath(path, "host")` instead */ +export const generateJsHostBindingsFromPath = (witPath: string, worldName?: string): Promise => + generateJsBindingsFromPath(witPath, "host", worldName); + +// ── Full host transpilation (from .wasm component) ── + +/** + * Options for transpiling a WebAssembly component into a JavaScript host module. + */ +export interface JsTranspileOptions { + /** Name for the generated module (derived from filename if omitted) */ + name?: string; + /** Whether to generate an instantiation API ("async" | "sync") instead of a direct module */ + instantiation?: "async" | "sync"; + /** Enable Node.js compatibility mode (e.g. for fs/path shims) */ + nodejsCompat?: boolean; + /** Enable tracing of component calls */ + tracing?: boolean; + /** Generate .mjs instead of .js for broader ESM compat */ + tlaCompat?: boolean; +} + +/** + * Transpile a WebAssembly component into a full JavaScript host module. + * + * Uses `@bytecodealliance/jco` `transpileComponent` to produce: + * - JavaScript ES module(s) that instantiate the component + * - TypeScript declaration files (.d.ts) for type-safe usage + * - Embedded core WebAssembly module(s) referenced by the JS + * + * This is the host-side counterpart to guest binding generation. + * The output can be used directly from Node.js or bundled for the browser. + * + * @param componentBytes - Raw bytes of a WebAssembly component (.wasm) + * @param options - Transpilation options + * @returns Generated files, import specifiers, and export descriptors + */ +export async function transpileJsHost( + componentBytes: Uint8Array, + options?: JsTranspileOptions +): Promise { + const transpileComponent = await getTranspileComponent(); + + const name = options?.name ?? "component"; + const result = await transpileComponent(componentBytes, { + name, + instantiation: options?.instantiation, + nodejsCompat: options?.nodejsCompat, + tracing: options?.tracing, + tlaCompat: options?.tlaCompat, + }); + + return { + files: result.files, + imports: result.imports, + exports: result.exports, + }; +} + +/** + * Transpile a WebAssembly component file into a full JavaScript host module. + * + * Reads the file from disk and delegates to {@link transpileJsHost}. + * + * @param wasmPath - Absolute path to the .wasm component file + * @param options - Transpilation options (name defaults to the file's base name) + * @returns Generated files, import specifiers, and export descriptors + */ +export async function transpileJsHostFromPath( + wasmPath: string, + options?: JsTranspileOptions +): Promise { + const componentBytes = fs.readFileSync(wasmPath); + const name = options?.name ?? path.basename(wasmPath, ".wasm"); + return transpileJsHost(new Uint8Array(componentBytes), { ...options, name }); +} diff --git a/tests/bindingsSource.test.ts b/tests/bindingsSource.test.ts new file mode 100644 index 0000000..0ac5ee1 --- /dev/null +++ b/tests/bindingsSource.test.ts @@ -0,0 +1,78 @@ +import { describe, expect, it } from "vitest"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { canUseWitPathGeneration, isExtractedWitFromWasm, type UriLike } from "../src/bindingsSource.js"; + +describe("canUseWitPathGeneration", () => { + it("returns true for existing file-backed .wit paths", () => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "wit-idl-source-test-")); + const witPath = path.join(tempDir, "world.wit"); + fs.writeFileSync(witPath, "package example:test; world w {}", "utf8"); + + try { + const uri: UriLike = { scheme: "file", fsPath: witPath }; + expect(canUseWitPathGeneration(uri)).toBe(true); + } finally { + fs.rmSync(tempDir, { recursive: true, force: true }); + } + }); + + it("returns false for virtual extracted WIT views", () => { + const uri: UriLike = { scheme: "wit-extract", fsPath: "/tmp/component.wasm" }; + expect(canUseWitPathGeneration(uri)).toBe(false); + }); + + it("returns false for wit-extract even with a valid .wit fsPath", () => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "wit-idl-source-test-")); + const witPath = path.join(tempDir, "world.wit"); + fs.writeFileSync(witPath, "package example:test; world w {}", "utf8"); + + try { + const uri: UriLike = { scheme: "wit-extract", fsPath: witPath }; + expect(canUseWitPathGeneration(uri)).toBe(false); + } finally { + fs.rmSync(tempDir, { recursive: true, force: true }); + } + }); + + it("returns true for existing .wit paths from non-file remote schemes", () => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "wit-idl-source-test-")); + const witPath = path.join(tempDir, "world.wit"); + fs.writeFileSync(witPath, "package example:test; world w {}", "utf8"); + + try { + const uri: UriLike = { scheme: "vscode-remote", fsPath: witPath }; + expect(canUseWitPathGeneration(uri)).toBe(true); + } finally { + fs.rmSync(tempDir, { recursive: true, force: true }); + } + }); + + it("returns false for non-.wit file paths", () => { + const uri: UriLike = { scheme: "file", fsPath: "/tmp/component.wasm" }; + expect(canUseWitPathGeneration(uri)).toBe(false); + }); + + it("returns false for missing .wit paths", () => { + const uri: UriLike = { scheme: "file", fsPath: path.join(os.tmpdir(), "missing-world.wit") }; + expect(canUseWitPathGeneration(uri)).toBe(false); + }); +}); + +describe("isExtractedWitFromWasm", () => { + it("returns true for virtual wit-extract URIs backed by .wasm", () => { + const uri: UriLike = { scheme: "wit-extract", fsPath: "/tmp/component.wasm" }; + expect(isExtractedWitFromWasm(uri)).toBe(true); + }); + + it("returns false for file-backed .wit URIs", () => { + const uri: UriLike = { scheme: "file", fsPath: "/tmp/world.wit" }; + expect(isExtractedWitFromWasm(uri)).toBe(false); + }); + + it("returns false for wit-extract URIs not ending in .wasm", () => { + const uri: UriLike = { scheme: "wit-extract", fsPath: "/tmp/world.wit" }; + expect(isExtractedWitFromWasm(uri)).toBe(false); + }); +}); diff --git a/tests/formatter-edge.test.ts b/tests/formatter-edge.test.ts new file mode 100644 index 0000000..3f028b0 --- /dev/null +++ b/tests/formatter-edge.test.ts @@ -0,0 +1,174 @@ +import { describe, it, expect, vi } from "vitest"; +import { WitFormatter } from "../src/formatter.js"; + +vi.mock("vscode", () => ({})); + +describe("WitFormatter — edge cases", () => { + const formatter = new WitFormatter(); + const defaultOptions = { insertSpaces: true, tabSize: 4 }; + + describe("empty and whitespace input", () => { + it("should handle empty string", () => { + expect(formatter.formatWitContent("", defaultOptions)).toBe(""); + }); + + it("should preserve blank lines", () => { + const input = "\n\n\n"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toBe("\n\n\n"); + }); + + it("should handle single blank line", () => { + const input = "\n"; + expect(formatter.formatWitContent(input, defaultOptions)).toBe("\n"); + }); + }); + + describe("tab indentation", () => { + it("should use tabs when insertSpaces is false", () => { + const input = "package foo:bar;\n\ninterface test {\nmy-func: func();\n}"; + const result = formatter.formatWitContent(input, { insertSpaces: false, tabSize: 4 }); + expect(result).toContain("\tmy-func: func();"); + }); + + it("should respect tabSize setting", () => { + const input = "package foo:bar;\n\ninterface test {\nmy-func: func();\n}"; + const result2 = formatter.formatWitContent(input, { insertSpaces: true, tabSize: 2 }); + expect(result2).toContain(" my-func: func();"); + + const result4 = formatter.formatWitContent(input, { insertSpaces: true, tabSize: 4 }); + expect(result4).toContain(" my-func: func();"); + }); + }); + + describe("nested structures", () => { + it("should handle deeply nested braces", () => { + const input = + "package foo:bar;\n\nworld my-world {\nexport run: func();\nimport log: func(msg: string);\n}"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toContain(" export run: func();"); + expect(result).toContain(" import log: func(msg: string);"); + }); + + it("should format resource with methods", () => { + const input = + "package foo:bar;\n\ninterface api {\nresource my-res {\nconstructor(name: string);\nget-name: func() -> string;\n}\n}"; + const result = formatter.formatWitContent(input, defaultOptions); + // Resource body should be indented within interface + const lines = result.split("\n"); + const constructorLine = lines.find((l: string) => l.includes("constructor")); + expect(constructorLine).toBeDefined(); + expect(constructorLine!.startsWith(" ")).toBe(true); // 8 spaces (2 levels) + }); + }); + + describe("comment handling", () => { + it("should preserve single-line comments at correct indentation", () => { + const input = "package foo:bar;\n\ninterface test {\n// A comment\nmy-func: func();\n}"; + const result = formatter.formatWitContent(input, defaultOptions); + const lines = result.split("\n"); + const commentLine = lines.find((l: string) => l.includes("// A comment")); + expect(commentLine).toBeDefined(); + expect(commentLine!.startsWith(" ")).toBe(true); + }); + + it("should handle doc comments (///)", () => { + const input = "package foo:bar;\n\ninterface test {\n/// Doc comment\nmy-func: func();\n}"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toContain("/// Doc comment"); + }); + }); + + describe("type alias formatting", () => { + it("should format simple type aliases", () => { + const input = "type my-type = u32 ;"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toBe("type my-type = u32;"); + }); + + it("should format type alias with complex type", () => { + const input = "type my-type = list ;"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toBe("type my-type = list;"); + }); + }); + + describe("use statement formatting", () => { + it("should format use statements", () => { + const input = "use my-interface.{type1, type2} ;"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toContain("use my-interface"); + expect(result.endsWith(";")).toBe(true); + }); + + it("should format use with as clause", () => { + const input = "use my-interface.{type1} as alias-name ;"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toContain(" as "); + }); + }); + + describe("function formatting edge cases", () => { + it("should format function with no parameters", () => { + const input = "my-func:func();"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toBe("my-func: func();"); + }); + + it("should format function with return type", () => { + const input = "my-func:func()->string;"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toBe("my-func: func() -> string;"); + }); + + it("should format function with multiple parameters", () => { + const input = "my-func:func(a:u32,b:string)->bool;"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toBe("my-func: func(a: u32, b: string) -> bool;"); + }); + }); + + describe("enum and flags formatting", () => { + it("should format enum with cases", () => { + const input = "package foo:bar;\n\nenum my-enum {\ncase-a,\ncase-b,\n}"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toContain("enum my-enum {"); + expect(result).toContain(" case-a,"); + }); + + it("should format flags with members", () => { + const input = "package foo:bar;\n\nflags my-flags {\nflag-a,\nflag-b,\n}"; + const result = formatter.formatWitContent(input, defaultOptions); + expect(result).toContain("flags my-flags {"); + expect(result).toContain(" flag-a,"); + }); + }); + + describe("provideDocumentFormattingEdits", () => { + it("should return empty array when content is unchanged", () => { + const mockDocument = { + getText: () => "package foo:bar;", + positionAt: (offset: number) => ({ line: 0, character: offset }), + } as unknown as import("vscode").TextDocument; + + const result = formatter.provideDocumentFormattingEdits(mockDocument, defaultOptions); + expect(result).toEqual([]); + }); + }); + + describe("multiline function parameters", () => { + it("should indent continuation lines of func parameters", () => { + const input = "package foo:bar;\n\ninterface test {\nmy-func: func(\na: u32,\nb: string,\n) -> bool;\n}"; + const result = formatter.formatWitContent(input, defaultOptions); + const lines = result.split("\n"); + const paramLine = lines.find((l: string) => l.includes("a: u32")); + expect(paramLine).toBeDefined(); + // Should be indented more than the func line + const funcLine = lines.find((l: string) => l.includes("my-func")); + expect(funcLine).toBeDefined(); + const funcIndent = funcLine!.length - funcLine!.trimStart().length; + const paramIndent = paramLine!.length - paramLine!.trimStart().length; + expect(paramIndent).toBeGreaterThan(funcIndent); + }); + }); +}); diff --git a/tests/hostBindingsRouting.test.ts b/tests/hostBindingsRouting.test.ts new file mode 100644 index 0000000..adaacbb --- /dev/null +++ b/tests/hostBindingsRouting.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from "vitest"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { determineHostJsGenerationStrategy, type HostJsGenerationStrategy } from "../src/hostBindingsRouting.js"; +import type { UriLike } from "../src/bindingsSource.js"; + +function strategyFor(targetUri: UriLike, isWitDoc: boolean): HostJsGenerationStrategy { + return determineHostJsGenerationStrategy({ targetUri, isWitDoc }); +} + +describe("determineHostJsGenerationStrategy", () => { + it("uses transpile path for extracted WIT views backed by wasm", () => { + const uri: UriLike = { scheme: "wit-extract", fsPath: "/tmp/component.wasm" }; + expect(strategyFor(uri, true)).toBe("transpile-wasm-path"); + }); + + it("uses transpile path for non-WIT wasm targets", () => { + const uri: UriLike = { scheme: "file", fsPath: "/tmp/component.wasm" }; + expect(strategyFor(uri, false)).toBe("transpile-wasm-path"); + }); + + it("uses path-based type generation for file-backed wit docs", () => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "wit-idl-routing-test-")); + const witPath = path.join(tempDir, "world.wit"); + fs.writeFileSync(witPath, "package example:test; world w {}", "utf8"); + + try { + const uri: UriLike = { scheme: "file", fsPath: witPath }; + expect(strategyFor(uri, true)).toBe("types-wit-path"); + } finally { + fs.rmSync(tempDir, { recursive: true, force: true }); + } + }); + + it("uses content-based type generation for virtual wit docs", () => { + const uri: UriLike = { scheme: "untitled", fsPath: "/tmp/world.wit" }; + expect(strategyFor(uri, true)).toBe("types-wit-content"); + }); + + it("returns unsupported for non-wit, non-wasm targets", () => { + const uri: UriLike = { scheme: "file", fsPath: "/tmp/notes.txt" }; + expect(strategyFor(uri, false)).toBe("unsupported"); + }); +}); diff --git a/tests/js-bindings.test.ts b/tests/js-bindings.test.ts new file mode 100644 index 0000000..c2417ee --- /dev/null +++ b/tests/js-bindings.test.ts @@ -0,0 +1,470 @@ +import { describe, it, expect } from "vitest"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { + generateJsBindings, + generateJsBindingsFromPath, + generateJsGuestBindings, + generateJsGuestBindingsFromPath, + generateJsHostBindings, + generateJsHostBindingsFromPath, + transpileJsHost, + transpileJsHostFromPath, +} from "../src/jsBindings.js"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; + +/** + * Build a minimal WebAssembly component from inline WIT source + * using jco's componentEmbed + componentNew. + */ +async function buildComponentFromWit(witSource: string): Promise { + const jco = (await import("@bytecodealliance/jco")) as { + componentEmbed: (opts: { witSource: string; dummy: boolean }) => Promise; + componentNew: (binary: Uint8Array, adapters: unknown[]) => Promise; + }; + const embedded = await jco.componentEmbed({ witSource, dummy: true }); + return jco.componentNew(embedded, []); +} + +const TEST_WIT = `package example:test; + +world test-world { + export greet: func(name: string) -> string; +} +`; + +const COMPLEX_WIT = `package example:complex; + +interface logger { + enum level { + debug, + info, + warn, + error, + } + + record entry { + level: level, + message: string, + } + + log: func(entry: entry); +} + +world app { + import logger; + export run: func() -> result<_, string>; +} +`; + +describe("JavaScript Guest Bindings Generation", () => { + it("should generate .d.ts files from WIT content", async () => { + const result = await generateJsBindings({ witContent: TEST_WIT }, "guest"); + + expect(result.files).toBeDefined(); + const filenames = Object.keys(result.files); + expect(filenames.length).toBeGreaterThan(0); + + const hasDtsFile = filenames.some((f) => f.endsWith(".d.ts")); + expect(hasDtsFile).toBe(true); + + console.log(`✅ JS guest bindings: Generated ${filenames.length} file(s): ${filenames.join(", ")}`); + }); + + it("should generate files containing TypeScript type declarations", async () => { + const result = await generateJsBindings({ witContent: TEST_WIT }, "guest"); + const filenames = Object.keys(result.files); + + const dtsFile = filenames.find((f) => f.endsWith(".d.ts")); + expect(dtsFile).toBeDefined(); + + if (dtsFile) { + const content = new TextDecoder().decode(result.files[dtsFile]); + expect(content.length).toBeGreaterThan(0); + expect(content).toMatch(/export|import|interface|type|declare/); + + console.log(`✅ JS guest bindings: ${dtsFile} contains TypeScript declarations (${content.length} bytes)`); + } + }); + + it("should handle complex WIT definitions with interfaces and records", async () => { + const result = await generateJsBindings({ witContent: COMPLEX_WIT }, "guest"); + + expect(result.files).toBeDefined(); + const filenames = Object.keys(result.files); + expect(filenames.length).toBeGreaterThan(0); + + const hasDtsFile = filenames.some((f) => f.endsWith(".d.ts")); + expect(hasDtsFile).toBe(true); + + console.log(`✅ JS guest bindings: Complex WIT generated ${filenames.length} file(s): ${filenames.join(", ")}`); + }); + + it("should generate files from a WIT file path", async () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "wit-idl-test-")); + const witFile = path.join(tmpDir, "test.wit"); + fs.writeFileSync(witFile, TEST_WIT, "utf8"); + + try { + const result = await generateJsBindingsFromPath(witFile, "guest"); + + expect(result.files).toBeDefined(); + const filenames = Object.keys(result.files); + expect(filenames.length).toBeGreaterThan(0); + + const hasDtsFile = filenames.some((f) => f.endsWith(".d.ts")); + expect(hasDtsFile).toBe(true); + + console.log(`✅ JS guest bindings from path: Generated ${filenames.length} file(s)`); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + }); + + it("should produce different output than wit-bindgen languages", async () => { + const result = await generateJsBindings({ witContent: TEST_WIT }, "guest"); + const filenames = Object.keys(result.files); + + const hasRust = filenames.some((f) => f.endsWith(".rs")); + const hasC = filenames.some((f) => f.endsWith(".h") || f.endsWith(".c")); + const hasGo = filenames.some((f) => f.endsWith(".go")); + + expect(hasRust).toBe(false); + expect(hasC).toBe(false); + expect(hasGo).toBe(false); + + const hasDts = filenames.some((f) => f.endsWith(".d.ts")); + expect(hasDts).toBe(true); + }); + + it("should reject empty WIT content", async () => { + await expect(generateJsBindings({ witContent: "" }, "guest")).rejects.toThrow(); + }); + + it("convenience alias works the same as core function", async () => { + const aliasResult = await generateJsGuestBindings({ witContent: TEST_WIT }); + const coreResult = await generateJsBindings({ witContent: TEST_WIT }, "guest"); + + expect(Object.keys(aliasResult.files).sort()).toEqual(Object.keys(coreResult.files).sort()); + }); + + it("path alias works the same as core path function", async () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "wit-idl-test-guest-alias-")); + const witFile = path.join(tmpDir, "test.wit"); + fs.writeFileSync(witFile, TEST_WIT, "utf8"); + + try { + const aliasResult = await generateJsGuestBindingsFromPath(witFile); + const coreResult = await generateJsBindingsFromPath(witFile, "guest"); + expect(Object.keys(aliasResult.files).sort()).toEqual(Object.keys(coreResult.files).sort()); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + }); +}); + +describe("JavaScript Host Bindings Generation", () => { + it("should generate .d.ts files from WIT content", async () => { + const result = await generateJsBindings({ witContent: TEST_WIT }, "host"); + + expect(result.files).toBeDefined(); + const filenames = Object.keys(result.files); + expect(filenames.length).toBeGreaterThan(0); + + const hasDtsFile = filenames.some((f) => f.endsWith(".d.ts")); + expect(hasDtsFile).toBe(true); + + console.log(`✅ JS host bindings: Generated ${filenames.length} file(s): ${filenames.join(", ")}`); + }); + + it("should generate host-side type declarations", async () => { + const result = await generateJsBindings({ witContent: TEST_WIT }, "host"); + const filenames = Object.keys(result.files); + + const dtsFile = filenames.find((f) => f.endsWith(".d.ts")); + expect(dtsFile).toBeDefined(); + + if (dtsFile) { + const content = new TextDecoder().decode(result.files[dtsFile]); + expect(content.length).toBeGreaterThan(0); + expect(content).toMatch(/export|import|interface|type|declare/); + + console.log(`✅ JS host bindings: ${dtsFile} contains TypeScript declarations (${content.length} bytes)`); + } + }); + + it("should produce different output than guest bindings", async () => { + const guestResult = await generateJsBindings({ witContent: TEST_WIT }, "guest"); + const hostResult = await generateJsBindings({ witContent: TEST_WIT }, "host"); + + const guestFiles = Object.keys(guestResult.files); + const hostFiles = Object.keys(hostResult.files); + + expect(guestFiles.length).toBeGreaterThan(0); + expect(hostFiles.length).toBeGreaterThan(0); + + const guestDts = guestFiles.find((f) => f.endsWith(".d.ts")); + const hostDts = hostFiles.find((f) => f.endsWith(".d.ts")); + if (guestDts && hostDts) { + const guestContent = new TextDecoder().decode(guestResult.files[guestDts]); + const hostContent = new TextDecoder().decode(hostResult.files[hostDts]); + expect(guestContent).not.toBe(hostContent); + } + + console.log(`✅ JS host vs guest: guest=${guestFiles.length} files, host=${hostFiles.length} files`); + }); + + it("should handle complex WIT definitions", async () => { + const result = await generateJsBindings({ witContent: COMPLEX_WIT }, "host"); + + expect(result.files).toBeDefined(); + const filenames = Object.keys(result.files); + expect(filenames.length).toBeGreaterThan(0); + + console.log(`✅ JS host bindings: Complex WIT generated ${filenames.length} file(s): ${filenames.join(", ")}`); + }); + + it("should generate files from a WIT file path", async () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "wit-idl-test-host-")); + const witFile = path.join(tmpDir, "test.wit"); + fs.writeFileSync(witFile, TEST_WIT, "utf8"); + + try { + const result = await generateJsBindingsFromPath(witFile, "host"); + + expect(result.files).toBeDefined(); + const filenames = Object.keys(result.files); + expect(filenames.length).toBeGreaterThan(0); + + const hasDtsFile = filenames.some((f) => f.endsWith(".d.ts")); + expect(hasDtsFile).toBe(true); + + console.log(`✅ JS host bindings from path: Generated ${filenames.length} file(s)`); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + }); + + it("should reject empty WIT content", async () => { + await expect(generateJsBindings({ witContent: "" }, "host")).rejects.toThrow(); + }); + + it("convenience alias works the same as core function", async () => { + const aliasResult = await generateJsHostBindings({ witContent: TEST_WIT }); + const coreResult = await generateJsBindings({ witContent: TEST_WIT }, "host"); + + expect(Object.keys(aliasResult.files).sort()).toEqual(Object.keys(coreResult.files).sort()); + }); + + it("path alias works the same as core path function", async () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "wit-idl-test-host-alias-")); + const witFile = path.join(tmpDir, "test.wit"); + fs.writeFileSync(witFile, TEST_WIT, "utf8"); + + try { + const aliasResult = await generateJsHostBindingsFromPath(witFile); + const coreResult = await generateJsBindingsFromPath(witFile, "host"); + expect(Object.keys(aliasResult.files).sort()).toEqual(Object.keys(coreResult.files).sort()); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + }); +}); + +describe("JavaScript bindings command contributions", () => { + const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8")) as { + contributes: { + commands: { command: string; title: string }[]; + menus: Record; + submenus: { id: string; label: string }[]; + }; + }; + + it("includes Generate JavaScript Bindings command for guests", () => { + const cmd = pkg.contributes.commands.find((c) => c.command === "wit-idl.generateGuestBindingsJavaScript"); + expect(cmd).toBeTruthy(); + expect(cmd?.title).toBe("Generate JavaScript Bindings"); + }); + + it("includes Generate JavaScript Bindings command for hosts", () => { + const cmd = pkg.contributes.commands.find((c) => c.command === "wit-idl.generateHostBindingsJavaScript"); + expect(cmd).toBeTruthy(); + expect(cmd?.title).toBe("Generate JavaScript Bindings"); + }); + + it("all guest binding commands use generateGuestBindings prefix", () => { + const guestCommands = pkg.contributes.commands.filter((c) => + c.command.startsWith("wit-idl.generateGuestBindings") + ); + expect(guestCommands.length).toBeGreaterThanOrEqual(7); + for (const cmd of guestCommands) { + expect(cmd.title).toMatch(/^Generate .+ Bindings$/); + } + }); + + it("guest JS command is in the generate guest bindings submenu", () => { + const submenu = pkg.contributes.menus["wit-idl.generateBindings.submenu"] || []; + expect(submenu.some((m) => m.command === "wit-idl.generateGuestBindingsJavaScript")).toBe(true); + }); + + it("host JS command is in the generate host bindings submenu", () => { + const submenu = pkg.contributes.menus["wit-idl.generateHostBindings.submenu"] || []; + expect(submenu.some((m) => m.command === "wit-idl.generateHostBindingsJavaScript")).toBe(true); + }); + + it("guest bindings submenu label includes 'Guest'", () => { + const submenu = pkg.contributes.submenus.find((s) => s.id === "wit-idl.generateBindings.submenu"); + expect(submenu).toBeTruthy(); + expect(submenu?.label).toContain("Guest"); + }); + + it("host bindings submenu label includes 'Host'", () => { + const submenu = pkg.contributes.submenus.find((s) => s.id === "wit-idl.generateHostBindings.submenu"); + expect(submenu).toBeTruthy(); + expect(submenu?.label).toContain("Host"); + }); + + it("documentation submenu exists with markdown command", () => { + const submenu = pkg.contributes.submenus.find((s) => s.id === "wit-idl.generateDocs.submenu"); + expect(submenu).toBeTruthy(); + expect(submenu?.label).toContain("Documentation"); + const docsMenu = pkg.contributes.menus["wit-idl.generateDocs.submenu"] || []; + expect(docsMenu.some((m) => m.command === "wit-idl.generateDocMarkdown")).toBe(true); + }); + + it("guest JS is in the command palette with correct when clause", () => { + const palette = pkg.contributes.menus.commandPalette || []; + const entry = palette.find((m) => m.command === "wit-idl.generateGuestBindingsJavaScript"); + expect(entry).toBeTruthy(); + expect(entry?.when).toMatch(/editorLangId == wit|witIdl\.isWasmComponent/); + }); + + it("host JS is in the command palette with correct when clause", () => { + const palette = pkg.contributes.menus.commandPalette || []; + const entry = palette.find((m) => m.command === "wit-idl.generateHostBindingsJavaScript"); + expect(entry).toBeTruthy(); + expect(entry?.when).toMatch(/editorLangId == wit|witIdl\.isWasmComponent/); + }); +}); + +describe("JavaScript Host Transpilation (from .wasm component)", () => { + it("should transpile a simple component into JS + .d.ts files", async () => { + const component = await buildComponentFromWit(TEST_WIT); + const result = await transpileJsHost(component, { name: "test-world" }); + + expect(result.files).toBeDefined(); + const filenames = Object.keys(result.files); + expect(filenames.length).toBeGreaterThan(0); + + const hasJs = filenames.some((f) => f.endsWith(".js") || f.endsWith(".mjs")); + const hasDts = filenames.some((f) => f.endsWith(".d.ts")); + expect(hasJs).toBe(true); + expect(hasDts).toBe(true); + + console.log(`✅ Host transpile: Generated ${filenames.length} file(s): ${filenames.join(", ")}`); + }); + + it("should produce a runnable JS module with imports/exports metadata", async () => { + const component = await buildComponentFromWit(TEST_WIT); + const result = await transpileJsHost(component, { name: "test-world" }); + + // The test WIT exports greet + expect(result.exports.length).toBeGreaterThan(0); + expect(result.exports).toContainEqual(["greet", "function"]); + + // No imports in this simple world + expect(result.imports).toEqual([]); + + // The JS file should contain executable module code (not just types) + const jsFile = Object.keys(result.files).find((f) => f.endsWith(".js") || f.endsWith(".mjs")); + expect(jsFile).toBeDefined(); + if (jsFile) { + const jsContent = new TextDecoder().decode(result.files[jsFile]); + expect(jsContent.length).toBeGreaterThan(100); + } + }); + + it("should handle complex WIT with imports and interfaces", async () => { + const component = await buildComponentFromWit(COMPLEX_WIT); + const result = await transpileJsHost(component, { name: "app" }); + + const filenames = Object.keys(result.files); + expect(filenames.length).toBeGreaterThan(0); + + // The complex WIT imports logger + expect(result.imports.length).toBeGreaterThan(0); + + // Exports run + expect(result.exports).toContainEqual(["run", "function"]); + + console.log( + `✅ Host transpile complex: ${filenames.length} file(s), ` + + `${result.imports.length} import(s), ${result.exports.length} export(s)` + ); + }); + + it("should produce substantially more content than type-only generation", async () => { + const component = await buildComponentFromWit(TEST_WIT); + const transpileResult = await transpileJsHost(component, { name: "test-world" }); + const typesResult = await generateJsBindings({ witContent: TEST_WIT }, "host"); + + // Full transpilation produces JS runtime code, which is much larger + const transpileSize = Object.values(transpileResult.files).reduce((sum, f) => sum + f.length, 0); + const typesSize = Object.values(typesResult.files).reduce((sum, f) => sum + f.length, 0); + + expect(transpileSize).toBeGreaterThan(typesSize); + + console.log(`✅ Transpile size: ${transpileSize} bytes vs types-only: ${typesSize} bytes`); + }); + + it("should transpile from a .wasm file path", async () => { + const component = await buildComponentFromWit(TEST_WIT); + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "wit-idl-transpile-")); + const wasmFile = path.join(tmpDir, "test-world.wasm"); + fs.writeFileSync(wasmFile, component); + + try { + const result = await transpileJsHostFromPath(wasmFile); + + expect(result.files).toBeDefined(); + const filenames = Object.keys(result.files); + expect(filenames.length).toBeGreaterThan(0); + + const hasJs = filenames.some((f) => f.endsWith(".js") || f.endsWith(".mjs")); + expect(hasJs).toBe(true); + + // Name should be derived from filename + const mainJs = filenames.find((f) => f.endsWith(".js") || f.endsWith(".mjs")); + expect(mainJs).toContain("test-world"); + + console.log(`✅ Host transpile from path: Generated ${filenames.length} file(s)`); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + }); + + it("should use custom name when provided", async () => { + const component = await buildComponentFromWit(TEST_WIT); + const result = await transpileJsHost(component, { name: "my-custom-name" }); + + const filenames = Object.keys(result.files); + const mainJs = filenames.find((f) => f.endsWith(".js") || f.endsWith(".mjs")); + expect(mainJs).toContain("my-custom-name"); + }); + + it("should default name to 'component' when no name given", async () => { + const component = await buildComponentFromWit(TEST_WIT); + const result = await transpileJsHost(component); + + const filenames = Object.keys(result.files); + const mainJs = filenames.find((f) => f.endsWith(".js") || f.endsWith(".mjs")); + expect(mainJs).toContain("component"); + }); + + it("should reject invalid wasm bytes", async () => { + const badBytes = new Uint8Array([0, 1, 2, 3]); + await expect(transpileJsHost(badBytes)).rejects.toThrow(); + }); +}); diff --git a/tests/test-wit-validator.js b/tests/test-wit-validator.js deleted file mode 100644 index e69de29..0000000 diff --git a/tests/validator-methods.test.ts b/tests/validator-methods.test.ts new file mode 100644 index 0000000..49831cb --- /dev/null +++ b/tests/validator-methods.test.ts @@ -0,0 +1,318 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; + +// Mock VS Code API +vi.mock("vscode", () => { + class MockPosition { + constructor( + public line: number, + public character: number + ) {} + } + class MockRange { + constructor( + public start: MockPosition, + public end: MockPosition + ) {} + } + class MockLocation { + constructor( + public uri: unknown, + public range: unknown + ) {} + } + class MockDiagnostic { + source: string | undefined; + code: string | undefined; + relatedInformation: unknown[] | undefined; + constructor( + public range: unknown, + public message: string, + public severity: number + ) {} + } + class MockDiagnosticRelatedInformation { + constructor( + public location: unknown, + public message: string + ) {} + } + return { + languages: { + createDiagnosticCollection: vi.fn(() => ({ + delete: vi.fn(), + clear: vi.fn(), + set: vi.fn(), + dispose: vi.fn(), + get: vi.fn(() => []), + })), + }, + DiagnosticCollection: vi.fn(), + DiagnosticSeverity: { Error: 0, Warning: 1, Information: 2, Hint: 3 }, + Diagnostic: MockDiagnostic, + Range: MockRange, + Position: MockPosition, + Location: MockLocation, + DiagnosticRelatedInformation: MockDiagnosticRelatedInformation, + extensions: { + getExtension: vi.fn(() => ({ + extensionPath: "/mock/extension/path", + })), + }, + }; +}); + +// Mock the WASM utilities +vi.mock("../src/wasmUtils.js", () => ({ + validateWitSyntaxDetailedFromWasm: vi.fn(), +})); + +import { WitSyntaxValidator } from "../src/validator.js"; + +describe("WitSyntaxValidator — parseWitBindgenError", () => { + let validator: WitSyntaxValidator; + + beforeEach(() => { + validator = new WitSyntaxValidator(); + }); + + afterEach(() => { + validator.dispose(); + }); + + describe("parseWitBindgenError", () => { + it("should parse error with location information", () => { + const errorMessage = `// failed to parse WIT +// +// Caused by: +// expected '{', found keyword \`interface\` +// --> /test/file.wit:3:1 +// | +// 3 | interface test { +// | ^`; + + const result = validator.parseWitBindgenError(errorMessage, "/test/file.wit"); + expect(result).not.toBeNull(); + expect(result!.mainError).toBe("WIT binding generation error"); + expect(result!.row).toBe(3); + expect(result!.column).toBe(1); + }); + + it("should parse undefined type errors", () => { + const errorMessage = "// undefined type `MyType` in generated bindings"; + + const result = validator.parseWitBindgenError(errorMessage, "/test/file.wit"); + expect(result).not.toBeNull(); + expect(result!.mainError).toBe("Undefined type in bindings"); + expect(result!.detailedError).toContain("MyType"); + }); + + it("should parse failed to resolve errors", () => { + const errorMessage = "failed to resolve interface 'my-interface'"; + + const result = validator.parseWitBindgenError(errorMessage, "/test/file.wit"); + expect(result).not.toBeNull(); + expect(result!.mainError).toBe("Binding resolution error"); + expect(result!.detailedError).toContain("failed to resolve"); + }); + + it("should parse expected/found syntax errors", () => { + const errorMessage = "expected '{' found '}'"; + + const result = validator.parseWitBindgenError(errorMessage, "/test/file.wit"); + expect(result).not.toBeNull(); + expect(result!.mainError).toBe("Binding syntax error"); + }); + + it("should parse unsupported feature errors", () => { + const errorMessage = "unsupported feature: async functions"; + + const result = validator.parseWitBindgenError(errorMessage, "/test/file.wit"); + expect(result).not.toBeNull(); + expect(result!.mainError).toBe("Unsupported feature"); + }); + + it("should handle generic errors as fallback", () => { + const errorMessage = "something went wrong with binding generation"; + + const result = validator.parseWitBindgenError(errorMessage, "/test/file.wit"); + expect(result).not.toBeNull(); + expect(result!.mainError).toBe("Binding generation failed"); + expect(result!.detailedError).toBe("something went wrong with binding generation"); + expect(result!.filePath).toBe("/test/file.wit"); + expect(result!.row).toBe(1); + expect(result!.column).toBe(1); + }); + + it("should strip comment markers from error messages", () => { + const errorMessage = "// Error in binding\n// generation process"; + + const result = validator.parseWitBindgenError(errorMessage, "/test/file.wit"); + expect(result).not.toBeNull(); + expect(result!.detailedError).not.toContain("//"); + }); + + it("should handle empty error message", () => { + const result = validator.parseWitBindgenError("", "/test/file.wit"); + expect(result).not.toBeNull(); + expect(result!.mainError).toBe("Binding generation failed"); + }); + }); + + describe("createDiagnosticFromError", () => { + it("should create a diagnostic with correct range", () => { + const errorInfo = { + mainError: "Test error", + detailedError: "Detailed test error", + filePath: "/test/file.wit", + row: 5, + column: 10, + }; + + const mockDocument = { + lineAt: vi.fn(() => ({ text: "some wit content here" })), + lineCount: 100, + uri: { fsPath: "/test/file.wit" }, + } as unknown as import("vscode").TextDocument; + + const diagnostic = validator.createDiagnosticFromError(errorInfo, mockDocument); + + expect(diagnostic).toBeDefined(); + expect(diagnostic.message).toBe("Detailed test error"); + expect(diagnostic.source).toBe("wit-syntax"); + expect(diagnostic.code).toBe("wit-parse-error"); + }); + + it("should use mainError as message when detailedError is absent", () => { + const errorInfo = { + mainError: "Main error only", + filePath: "/test/file.wit", + row: 1, + column: 1, + }; + + const mockDocument = { + lineAt: vi.fn(() => ({ text: "content" })), + lineCount: 10, + uri: { fsPath: "/test/file.wit" }, + } as unknown as import("vscode").TextDocument; + + const diagnostic = validator.createDiagnosticFromError(errorInfo, mockDocument); + expect(diagnostic.message).toBe("Main error only"); + }); + + it("should add related information when both mainError and detailedError exist", () => { + const errorInfo = { + mainError: "Context info", + detailedError: "The detailed message", + filePath: "/test/file.wit", + row: 2, + column: 3, + }; + + const mockDocument = { + lineAt: vi.fn(() => ({ text: "a line of wit" })), + lineCount: 50, + uri: { fsPath: "/test/file.wit" }, + } as unknown as import("vscode").TextDocument; + + const diagnostic = validator.createDiagnosticFromError(errorInfo, mockDocument); + expect(diagnostic.relatedInformation).toBeDefined(); + expect(diagnostic.relatedInformation).toHaveLength(1); + }); + + it("should clamp row and column to valid range", () => { + const errorInfo = { + mainError: "Error", + detailedError: "Details", + filePath: "/test/file.wit", + row: 0, // below 1 + column: 0, // below 1 + }; + + const mockDocument = { + lineAt: vi.fn(() => ({ text: "content" })), + lineCount: 5, + uri: { fsPath: "/test/file.wit" }, + } as unknown as import("vscode").TextDocument; + + const diagnostic = validator.createDiagnosticFromError(errorInfo, mockDocument); + // Should not throw even with row/column 0 + expect(diagnostic).toBeDefined(); + }); + + it("should handle row exceeding document line count", () => { + const errorInfo = { + mainError: "Error", + detailedError: "Details", + filePath: "/test/file.wit", + row: 999, + column: 1, + }; + + const mockDocument = { + lineAt: vi.fn(() => ({ text: "last line" })), + lineCount: 5, + uri: { fsPath: "/test/file.wit" }, + } as unknown as import("vscode").TextDocument; + + const diagnostic = validator.createDiagnosticFromError(errorInfo, mockDocument); + expect(diagnostic).toBeDefined(); + // lineAt should be called with clamped value + expect(mockDocument.lineAt).toHaveBeenCalledWith(4); // min(998, 4) + }); + }); + + describe("validate — parseWitParserError paths", () => { + it("should parse wit-parser error with location info", async () => { + const { validateWitSyntaxDetailedFromWasm } = await import("../src/wasmUtils.js"); + vi.mocked(validateWitSyntaxDetailedFromWasm).mockResolvedValueOnce({ + valid: false, + error: "expected '}'\n --> /test/file.wit:10:5\n |\n10 | bad syntax", + errorType: "parsing", + }); + + const result = await validator.validate("/test/file.wit", "bad content"); + expect(result).not.toBeNull(); + expect(result!.row).toBe(10); + expect(result!.column).toBe(5); + }); + + it("should handle validation result with no error message", async () => { + const { validateWitSyntaxDetailedFromWasm } = await import("../src/wasmUtils.js"); + vi.mocked(validateWitSyntaxDetailedFromWasm).mockResolvedValueOnce({ + valid: false, + error: undefined, + errorType: undefined, + }); + + const result = await validator.validate("/test/file.wit", "bad content"); + expect(result).not.toBeNull(); + expect(result!.mainError).toBe("WIT syntax validation failed"); + }); + + it("should handle thrown error without stack", async () => { + const { validateWitSyntaxDetailedFromWasm } = await import("../src/wasmUtils.js"); + vi.mocked(validateWitSyntaxDetailedFromWasm).mockRejectedValueOnce("string error"); + + const result = await validator.validate("/test/file.wit", "bad content"); + expect(result).not.toBeNull(); + expect(result!.mainError).toBe("WIT validation error"); + expect(result!.detailedError).toBe("string error"); + }); + }); + + describe("workspaceCheck flag", () => { + it("should prevent clearing diagnostics during workspace check", () => { + const collection = validator.getDiagnosticCollection(); + const mockUri = {} as import("vscode").Uri; + + validator.workspaceCheck = true; + validator.clearDiagnostics(mockUri); + expect(collection.delete).not.toHaveBeenCalled(); + + validator.workspaceCheck = false; + validator.clearDiagnostics(mockUri); + expect(collection.delete).toHaveBeenCalledWith(mockUri); + }); + }); +}); diff --git a/tests/wasmDetection-fs.test.ts b/tests/wasmDetection-fs.test.ts new file mode 100644 index 0000000..b8d70fc --- /dev/null +++ b/tests/wasmDetection-fs.test.ts @@ -0,0 +1,125 @@ +import { describe, it, expect, afterEach } from "vitest"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { readWasmHeader, isWasmComponentFile, isWasmComponentVersion } from "../src/wasmDetection.js"; + +/** + * Build an 8-byte WASM header buffer with the given version u32 (LE). + */ +function buildWasmHeader(version: number): Buffer { + const buf = Buffer.alloc(8); + // magic: \0asm + buf[0] = 0x00; + buf[1] = 0x61; + buf[2] = 0x73; + buf[3] = 0x6d; + buf.writeUInt32LE(version, 4); + return buf; +} + +describe("wasmDetection — file-based tests", () => { + const tmpFiles: string[] = []; + + function writeTmpWasm(content: Buffer): string { + const tmpDir = os.tmpdir(); + const filePath = path.join(tmpDir, `test-${Date.now()}-${Math.random().toString(36).slice(2)}.wasm`); + fs.writeFileSync(filePath, content); + tmpFiles.push(filePath); + return filePath; + } + + afterEach(() => { + for (const f of tmpFiles) { + try { + fs.unlinkSync(f); + } catch { + // ignore + } + } + tmpFiles.length = 0; + }); + + describe("readWasmHeader", () => { + it("should read a valid core module header (version 1)", async () => { + const filePath = writeTmpWasm(buildWasmHeader(1)); + const result = await readWasmHeader(filePath); + expect(result.magicOk).toBe(true); + expect(result.version).toBe(1); + }); + + it("should read a valid component header (version 65549)", async () => { + const filePath = writeTmpWasm(buildWasmHeader(65549)); + const result = await readWasmHeader(filePath); + expect(result.magicOk).toBe(true); + expect(result.version).toBe(65549); + }); + + it("should return magicOk=false for non-WASM file", async () => { + const filePath = writeTmpWasm(Buffer.from("not a wasm file!")); + const result = await readWasmHeader(filePath); + expect(result.magicOk).toBe(false); + }); + + it("should return magicOk=false for file shorter than 8 bytes", async () => { + const filePath = writeTmpWasm(Buffer.from([0x00, 0x61, 0x73])); + const result = await readWasmHeader(filePath); + expect(result.magicOk).toBe(false); + expect(result.version).toBe(0); + }); + + it("should return magicOk=false for non-existent file", async () => { + const result = await readWasmHeader("/nonexistent/path/to/file.wasm"); + expect(result.magicOk).toBe(false); + expect(result.version).toBe(0); + }); + + it("should handle empty file", async () => { + const filePath = writeTmpWasm(Buffer.alloc(0)); + const result = await readWasmHeader(filePath); + expect(result.magicOk).toBe(false); + expect(result.version).toBe(0); + }); + }); + + describe("isWasmComponentFile", () => { + it("should return true for a component WASM file", async () => { + const filePath = writeTmpWasm(buildWasmHeader(65549)); + expect(await isWasmComponentFile(filePath)).toBe(true); + }); + + it("should return false for a core WASM module", async () => { + const filePath = writeTmpWasm(buildWasmHeader(1)); + expect(await isWasmComponentFile(filePath)).toBe(false); + }); + + it("should return false for a non-WASM file", async () => { + const filePath = writeTmpWasm(Buffer.from("hello world")); + expect(await isWasmComponentFile(filePath)).toBe(false); + }); + + it("should return false for non-existent file", async () => { + expect(await isWasmComponentFile("/does/not/exist.wasm")).toBe(false); + }); + }); + + describe("isWasmComponentVersion — additional edge cases", () => { + it("should return false for version 0", () => { + expect(isWasmComponentVersion(0)).toBe(false); + }); + + it("should return true for a version with layer=1, version=13 (0x0001000d = 65549)", () => { + expect(isWasmComponentVersion(0x0001_000d)).toBe(true); + }); + + it("should return false for large version with layer=0", () => { + // layer=0, version=0xFFFF + expect(isWasmComponentVersion(0x0000_ffff)).toBe(false); + }); + + it("should return true for hypothetical future component versions (layer=1)", () => { + // layer=1, version=0x0100 + expect(isWasmComponentVersion(0x0001_0100)).toBe(true); + }); + }); +}); diff --git a/tests/wasmUtils.test.ts b/tests/wasmUtils.test.ts new file mode 100644 index 0000000..a666371 --- /dev/null +++ b/tests/wasmUtils.test.ts @@ -0,0 +1,168 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +// Mock vscode before importing wasmUtils +vi.mock("vscode", () => ({ + extensions: { + getExtension: vi.fn(() => ({ + extensionUri: { fsPath: "/mock/extension" }, + })), + }, + workspace: { + fs: { + stat: vi.fn(), + readFile: vi.fn(), + }, + }, + Uri: { + joinPath: vi.fn((_base: unknown, ...segments: string[]) => ({ + fsPath: `/mock/extension/${segments.join("/")}`, + })), + }, +})); + +// Mock the wit-bindgen-wasm module +vi.mock("wit-bindgen-wasm", () => { + class MockWitBindgen { + version(): string { + return "0.42.0-mock"; + } + validateWitSyntax(content: string): boolean { + return content.includes("package"); + } + validateWitSyntaxDetailed(content: string): string { + if (content.includes("package")) { + return JSON.stringify({ valid: true }); + } + return JSON.stringify({ valid: false, error: "expected package", errorType: "parsing" }); + } + extractInterfaces(): string { + return "my-interface, another-interface"; + } + generateBindings(): string { + return JSON.stringify({ "lib.rs": "// generated" }); + } + free(): void {} + } + return { + default: vi.fn(), + WitBindgen: MockWitBindgen, + }; +}); + +import { + isWitFileExtensionFromWasm, + validateWitSyntaxFromWasm, + getWitBindgenVersionFromWasm, + createWitBindgenInstance, + extractInterfacesFromWasm, + generateBindingsFromWasm, + validateWitSyntaxDetailedFromWasm, + initializeWasm, +} from "../src/wasmUtils.js"; + +describe("wasmUtils", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("isWitFileExtensionFromWasm", () => { + it("should return true for .wit files", async () => { + expect(await isWitFileExtensionFromWasm("test.wit")).toBe(true); + }); + + it("should return true for .WIT files (case-insensitive)", async () => { + expect(await isWitFileExtensionFromWasm("test.WIT")).toBe(true); + }); + + it("should return false for non-.wit files", async () => { + expect(await isWitFileExtensionFromWasm("test.rs")).toBe(false); + }); + + it("should return false for files ending with .wits", async () => { + expect(await isWitFileExtensionFromWasm("test.wits")).toBe(false); + }); + + it("should return false for empty string", async () => { + expect(await isWitFileExtensionFromWasm("")).toBe(false); + }); + + it("should return true for paths with directories", async () => { + expect(await isWitFileExtensionFromWasm("/path/to/file.wit")).toBe(true); + }); + }); + + describe("initializeWasm", () => { + it("should initialize the WASM module", async () => { + await expect(initializeWasm()).resolves.toBeUndefined(); + }); + + it("should not re-initialize if already initialized", async () => { + await initializeWasm(); + await initializeWasm(); + // Should not throw on double init + }); + }); + + describe("getWitBindgenVersionFromWasm", () => { + it("should return a version string", async () => { + const version = await getWitBindgenVersionFromWasm(); + expect(typeof version).toBe("string"); + expect(version).toBe("0.42.0-mock"); + }); + }); + + describe("createWitBindgenInstance", () => { + it("should return a WitBindgen instance", async () => { + const instance = await createWitBindgenInstance(); + expect(instance).toBeDefined(); + expect(typeof instance.version).toBe("function"); + }); + }); + + describe("validateWitSyntaxFromWasm", () => { + it("should return true for valid WIT content", async () => { + const result = await validateWitSyntaxFromWasm("package foo:bar;"); + expect(result).toBe(true); + }); + + it("should return false for invalid WIT content", async () => { + const result = await validateWitSyntaxFromWasm("not valid wit"); + expect(result).toBe(false); + }); + }); + + describe("validateWitSyntaxDetailedFromWasm", () => { + it("should return valid result for valid content", async () => { + const result = await validateWitSyntaxDetailedFromWasm("package foo:bar;"); + expect(result.valid).toBe(true); + }); + + it("should return error details for invalid content", async () => { + const result = await validateWitSyntaxDetailedFromWasm("not valid wit"); + expect(result.valid).toBe(false); + expect(result.error).toBeDefined(); + expect(result.errorType).toBe("parsing"); + }); + }); + + describe("extractInterfacesFromWasm", () => { + it("should return comma-separated interface names", async () => { + const result = await extractInterfacesFromWasm("package foo:bar; interface my-interface {}"); + expect(result).toBe("my-interface, another-interface"); + }); + }); + + describe("generateBindingsFromWasm", () => { + it("should return a map of generated files", async () => { + const result = await generateBindingsFromWasm("package foo:bar; world w {}", "rust"); + expect(result).toBeDefined(); + expect(typeof result).toBe("object"); + expect(result["lib.rs"]).toBe("// generated"); + }); + + it("should accept optional world name", async () => { + const result = await generateBindingsFromWasm("package foo:bar; world w {}", "rust", "w"); + expect(result).toBeDefined(); + }); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index f59171d..08eec59 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,6 +7,13 @@ export default defineConfig({ environment: "node", include: ["src/**/*.{test,spec}.{js,ts}", "tests/**/*.{test,spec}.{js,ts}"], exclude: ["node_modules", "dist", "out", "*.vsix"], + coverage: { + provider: "v8", + reporter: ["text", "lcov", "json-summary"], + reportsDirectory: "./coverage", + include: ["src/**/*.ts"], + exclude: ["src/**/*.test.ts", "src/**/*.spec.ts"], + }, }, resolve: { alias: { diff --git a/wit-bindgen-wasm/Cargo.lock b/wit-bindgen-wasm/Cargo.lock index fa650dc..6bff5f5 100644 --- a/wit-bindgen-wasm/Cargo.lock +++ b/wit-bindgen-wasm/Cargo.lock @@ -91,9 +91,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.90" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dc6f6450b3f6d4ed5b16327f38fed626d375a886159ca555bd7822c0c3a5a6" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -295,9 +295,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "wasm-bindgen" -version = "0.2.113" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60722a937f594b7fde9adb894d7c092fc1bb6612897c46368d18e7a20208eff2" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -308,9 +308,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.113" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac8c6395094b6b91c4af293f4c79371c163f9a6f56184d2c9a85f5a95f3950" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -318,9 +318,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.113" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3fabce6159dc20728033842636887e4877688ae94382766e00b180abac9d60" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", @@ -331,9 +331,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.113" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0e091bdb824da87dc01d967388880d017a0a9bc4f3bdc0d86ee9f9336e3bb5" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] @@ -375,9 +375,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.90" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705eceb4ce901230f8625bd1d665128056ccbe4b7408faa625eec1ba80f59a97" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/wit-bindgen-wasm/Cargo.toml b/wit-bindgen-wasm/Cargo.toml index 007d1e0..60c5f2c 100644 --- a/wit-bindgen-wasm/Cargo.toml +++ b/wit-bindgen-wasm/Cargo.toml @@ -13,7 +13,7 @@ default = ["console_error_panic_hook"] console_error_panic_hook = ["dep:console_error_panic_hook"] [dependencies] -wasm-bindgen = "0.2.113" +wasm-bindgen = "0.2.114" console_error_panic_hook = { version = "0.1.7", optional = true } serde_json = "1.0" wasmparser = { version = "0.245", features = ["component-model"] } @@ -30,7 +30,7 @@ wit-component = "0.245" anyhow = "1.0.102" [dependencies.web-sys] -version = "0.3.90" +version = "0.3.91" features = [ "console", ]