diff --git a/.github/workflows/release-cli-npm.yml b/.github/workflows/release-cli-npm.yml new file mode 100644 index 00000000..56dab62b --- /dev/null +++ b/.github/workflows/release-cli-npm.yml @@ -0,0 +1,162 @@ +name: Release CLI to NPM + +on: + push: + tags: [yaak-cli-*] + workflow_dispatch: + inputs: + version: + description: CLI version to publish (for example 0.4.0 or v0.4.0) + required: true + type: string + +permissions: + contents: read + +jobs: + build-binaries: + name: Build ${{ matrix.pkg }} + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - pkg: cli-darwin-arm64 + runner: macos-latest + target: aarch64-apple-darwin + binary: yaak + - pkg: cli-darwin-x64 + runner: macos-latest + target: x86_64-apple-darwin + binary: yaak + - pkg: cli-linux-arm64 + runner: ubuntu-22.04-arm + target: aarch64-unknown-linux-gnu + binary: yaak + - pkg: cli-linux-x64 + runner: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + binary: yaak + - pkg: cli-win32-arm64 + runner: windows-latest + target: aarch64-pc-windows-msvc + binary: yaak.exe + - pkg: cli-win32-x64 + runner: windows-latest + target: x86_64-pc-windows-msvc + binary: yaak.exe + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Restore Rust cache + uses: Swatinem/rust-cache@v2 + with: + shared-key: release-cli-npm + cache-on-failure: true + + - name: Build yaak + run: cargo build --locked --release -p yaak-cli --bin yaak --target ${{ matrix.target }} + + - name: Stage binary artifact + shell: bash + run: | + set -euo pipefail + mkdir -p "npm/dist/${{ matrix.pkg }}" + cp "target/${{ matrix.target }}/release/${{ matrix.binary }}" "npm/dist/${{ matrix.pkg }}/${{ matrix.binary }}" + + - name: Upload binary artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.pkg }} + path: npm/dist/${{ matrix.pkg }}/${{ matrix.binary }} + if-no-files-found: error + + publish-npm: + name: Publish @yaakapp/cli packages + needs: build-binaries + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: lts/* + registry-url: https://registry.npmjs.org + + - name: Download binary artifacts + uses: actions/download-artifact@v4 + with: + pattern: cli-* + path: npm/dist + merge-multiple: false + + - name: Prepare npm packages + shell: bash + env: + WORKFLOW_VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="$WORKFLOW_VERSION" + else + VERSION="${GITHUB_REF_NAME#yaak-cli-}" + fi + echo "Preparing CLI npm packages for version: $VERSION" + YAAK_CLI_VERSION="$VERSION" node npm/prepare-publish.js + + - name: Ensure NPM token exists + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + if [ -z "$NODE_AUTH_TOKEN" ]; then + echo "NPM_TOKEN is not configured" + exit 1 + fi + + - name: Publish @yaakapp/cli-darwin-arm64 + run: npm publish --provenance --access public + working-directory: npm/cli-darwin-arm64 + env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" } + + - name: Publish @yaakapp/cli-darwin-x64 + run: npm publish --provenance --access public + working-directory: npm/cli-darwin-x64 + env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" } + + - name: Publish @yaakapp/cli-linux-arm64 + run: npm publish --provenance --access public + working-directory: npm/cli-linux-arm64 + env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" } + + - name: Publish @yaakapp/cli-linux-x64 + run: npm publish --provenance --access public + working-directory: npm/cli-linux-x64 + env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" } + + - name: Publish @yaakapp/cli-win32-arm64 + run: npm publish --provenance --access public + working-directory: npm/cli-win32-arm64 + env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" } + + - name: Publish @yaakapp/cli-win32-x64 + run: npm publish --provenance --access public + working-directory: npm/cli-win32-x64 + env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" } + + - name: Publish @yaakapp/cli + run: npm publish --provenance --access public + working-directory: npm/cli + env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" } diff --git a/Cargo.lock b/Cargo.lock index 8ae3b89b..d27fe7b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android_log-sys" version = "0.3.2" @@ -167,6 +173,12 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +[[package]] +name = "append-only-vec" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114736faba96bcd79595c700d03183f61357b9fbce14852515e59f3bee4ed4a" + [[package]] name = "arbitrary" version = "1.4.1" @@ -197,6 +209,18 @@ dependencies = [ "x11rb", ] +[[package]] +name = "arcstr" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -350,6 +374,17 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "async-scoped" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4042078ea593edffc452eef14e99fdb2b120caa4ad9618bcdeabc4a023b98740" +dependencies = [ + "futures", + "pin-project", + "tokio", +] + [[package]] name = "async-signal" version = "0.2.11" @@ -489,6 +524,12 @@ dependencies = [ "tower-service", ] +[[package]] +name = "base-encode" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17bd29f7c70f32e9387f4d4acfa5ea7b7749ef784fb78cf382df97069337b8c" + [[package]] name = "base32" version = "0.5.1" @@ -507,6 +548,31 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -515,11 +581,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -534,6 +600,20 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq 0.4.2", + "cpufeatures", +] + [[package]] name = "block" version = "0.1.6" @@ -667,9 +747,12 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +dependencies = [ + "allocator-api2", +] [[package]] name = "byte-unit" @@ -756,7 +839,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cairo-sys-rs", "glib", "libc", @@ -817,6 +900,15 @@ dependencies = [ "toml 0.8.23", ] +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + [[package]] name = "cbc" version = "0.1.2" @@ -993,13 +1085,22 @@ dependencies = [ "cc", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.17", +] + [[package]] name = "cocoa" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad36507aeb7e16159dfe68db81ccc27571c3ccd4b76fb2fb72fc59e7a4b1b64c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block", "cocoa-foundation", "core-foundation 0.10.1", @@ -1015,7 +1116,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block", "core-foundation 0.10.1", "core-graphics-types 0.2.0", @@ -1048,6 +1149,30 @@ dependencies = [ "memchr", ] +[[package]] +name = "commondir" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab552acb7c0a751c75c3dd4f9b95d31ed85c985ce5c70232a2952ffbe7ecfda5" +dependencies = [ + "thiserror 1.0.69", +] + +[[package]] +name = "compact_str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "serde", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1057,6 +1182,28 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "concurrent_lru" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feb5cb312f774e8a24540e27206db4e890f7d488563671d24a16389cf4c2e4e" +dependencies = [ + "once_cell", +] + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + [[package]] name = "const-random" version = "0.1.18" @@ -1083,12 +1230,27 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.18.1" @@ -1144,7 +1306,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -1168,7 +1330,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "core-foundation 0.10.1", "libc", ] @@ -1185,6 +1347,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cow-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -1212,6 +1380,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -1235,6 +1422,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "css-module-lexer" +version = "0.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b51940c54c6ca015d3add383571ec5610114466eb67aa0a27096e1dcf3c9e29" +dependencies = [ + "smallvec", +] + [[package]] name = "csscolorparser" version = "0.7.2" @@ -1316,6 +1512,20 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -1376,13 +1586,36 @@ version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ - "convert_case", + "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", "syn 2.0.101", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case 0.10.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.101", + "unicode-xid", +] + [[package]] name = "des" version = "0.8.1" @@ -1442,7 +1675,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block2 0.6.1", "libc", "objc2 0.6.1", @@ -1454,7 +1687,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "objc2 0.6.1", ] @@ -1516,6 +1749,12 @@ dependencies = [ "serde", ] +[[package]] +name = "dragonbox_ecma" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d742b56656e8b14d63e7ea9806597b1849ae25412584c8adf78c0f67bd985e66" + [[package]] name = "dtoa" version = "1.0.10" @@ -1569,6 +1808,24 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -1584,6 +1841,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +[[package]] +name = "endian-type" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869b0adbda23651a9c5c0c3d270aac9fcb52e8622a8f2b17e57802d7791962f2" + [[package]] name = "enumflags2" version = "0.7.11" @@ -1712,6 +1975,26 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fancy-regex" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax 0.8.5", +] + +[[package]] +name = "fast-glob" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d26eec0ae9682c457cb0f85de67ad417b716ae852736a5d94c2ad6e92a997c9" +dependencies = [ + "arrayvec", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1765,6 +2048,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.1" @@ -1791,6 +2080,18 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "font-loader" version = "0.11.0" @@ -2207,7 +2508,7 @@ version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "libc", "libgit2-sys", "log 0.4.29", @@ -2222,7 +2523,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "futures-channel", "futures-core", "futures-executor", @@ -2351,6 +2652,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "halfbrown" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed2f2edad8a14c8186b847909a41fbb9c3eafa44f88bd891114ed5019da09" +dependencies = [ + "hashbrown 0.16.1", +] + [[package]] name = "handlebars" version = "0.29.1" @@ -2389,6 +2699,20 @@ name = "hashbrown" version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "hashlink" @@ -2601,7 +2925,7 @@ dependencies = [ "js-sys", "log 0.4.29", "wasm-bindgen", - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -2816,7 +3140,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "inotify-sys", "libc", ] @@ -3007,6 +3331,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json-escape-simd" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c2a6c0b4b5637c41719973ef40c6a1cf564f9db6958350de6193fbee9c23f5" + [[package]] name = "json-patch" version = "3.0.1" @@ -3019,6 +3349,15 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "json-strip-comments" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25376d12b2f6ae53f986f86e2a808a56af03d72284ae24fc35a2e290d09ee3c3" +dependencies = [ + "memchr", +] + [[package]] name = "jsonptr" version = "0.6.3" @@ -3035,7 +3374,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "serde", "unicode-segmentation", ] @@ -3188,7 +3527,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "libc", "redox_syscall", ] @@ -3357,9 +3696,9 @@ checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" -version = "2.7.4" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" @@ -3482,7 +3821,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "jni-sys", "log 0.4.29", "ndk-sys", @@ -3512,6 +3851,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + [[package]] name = "nix" version = "0.23.2" @@ -3531,13 +3879,19 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", "memoffset 0.9.1", ] +[[package]] +name = "nodejs-built-in-modules" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5eb86a92577833b75522336f210c49d9ebd7dd55a44d80a92e68c668a75f27c" + [[package]] name = "nodrop" version = "0.1.14" @@ -3554,6 +3908,21 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -3566,7 +3935,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "filetime", "fsevent-sys", "inotify", @@ -3585,12 +3954,40 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3671,7 +4068,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block2 0.6.1", "libc", "objc2 0.6.1", @@ -3690,7 +4087,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "objc2 0.6.1", "objc2-foundation 0.3.1", ] @@ -3701,7 +4098,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "objc2 0.6.1", "objc2-foundation 0.3.1", ] @@ -3712,7 +4109,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "dispatch2 0.3.0", "objc2 0.6.1", ] @@ -3723,7 +4120,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "dispatch2 0.3.0", "objc2 0.6.1", "objc2-core-foundation", @@ -3761,7 +4158,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -3773,7 +4170,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block2 0.6.1", "libc", "objc2 0.6.1", @@ -3786,7 +4183,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "objc2 0.6.1", "objc2-core-foundation", ] @@ -3807,7 +4204,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3819,7 +4216,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26bb88504b5a050dbba515d2414607bf5e57dd56b107bc5f0351197a3e7bdc5d" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "objc2 0.6.1", "objc2-app-kit", "objc2-foundation 0.3.1", @@ -3831,7 +4228,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3844,7 +4241,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "objc2 0.6.1", "objc2-foundation 0.3.1", ] @@ -3855,7 +4252,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "objc2 0.6.1", "objc2-core-foundation", ] @@ -3866,7 +4263,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "objc2 0.6.1", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -3878,7 +4275,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block2 0.6.1", "objc2 0.6.1", "objc2-app-kit", @@ -3924,7 +4321,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -4042,6 +4439,506 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "owo-colors" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" + +[[package]] +name = "oxc" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca2b36d3a7519a1aa606ecdb2d48f01bcdaba810d0a74881c9aebb41d8bec42" +dependencies = [ + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_cfg", + "oxc_codegen", + "oxc_diagnostics", + "oxc_isolated_declarations", + "oxc_mangler", + "oxc_minifier", + "oxc_parser", + "oxc_regular_expression", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "oxc_transformer", + "oxc_transformer_plugins", +] + +[[package]] +name = "oxc-browserslist" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b1853bc34cadaa90aa09f95713d8b77ec0c0d3e2d90ccf7a74216f40d20850" +dependencies = [ + "flate2", + "postcard", + "rustc-hash", + "serde", + "serde_json", + "thiserror 2.0.17", +] + +[[package]] +name = "oxc-miette" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a7ba54c704edefead1f44e9ef09c43e5cfae666bdc33516b066011f0e6ebf7" +dependencies = [ + "cfg-if", + "owo-colors", + "oxc-miette-derive", + "textwrap", + "thiserror 2.0.17", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "oxc-miette-derive" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4faecb54d0971f948fbc1918df69b26007e6f279a204793669542e1e8b75eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "oxc_allocator" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433214c659b860685d987ca25a523a544d35ebf87ee3658a942fd1c664cfa49b" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.16.1", + "oxc_data_structures", + "oxc_estree", + "rustc-hash", + "serde", +] + +[[package]] +name = "oxc_ast" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e78ea25d23521092edcd509198635dd0bd96df7fb71349bcd8087bb8f6a615d" +dependencies = [ + "bitflags 2.11.0", + "oxc_allocator", + "oxc_ast_macros", + "oxc_data_structures", + "oxc_diagnostics", + "oxc_estree", + "oxc_regular_expression", + "oxc_span", + "oxc_syntax", +] + +[[package]] +name = "oxc_ast_macros" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3f29bf83925451a7ebbd30d3ff449414cc230c9b63aba70487c2ca74ea1e3ed" +dependencies = [ + "phf 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "oxc_ast_visit" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808862f42c9331954cf187261d6a48dbf471ccbe60b6a35f6b1056c11f32e95b" +dependencies = [ + "oxc_allocator", + "oxc_ast", + "oxc_span", + "oxc_syntax", +] + +[[package]] +name = "oxc_cfg" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c88db454a235f30f3c9730e5a9926c64794864a9e560190094fb40937654b71" +dependencies = [ + "bitflags 2.11.0", + "itertools", + "oxc_index", + "oxc_syntax", + "petgraph 0.8.3", + "rustc-hash", +] + +[[package]] +name = "oxc_codegen" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480bab938439c921d34750708abf15aacf253ea11e2f348e4b085cbf697f4ea2" +dependencies = [ + "bitflags 2.11.0", + "cow-utils", + "dragonbox_ecma", + "itoa", + "oxc_allocator", + "oxc_ast", + "oxc_data_structures", + "oxc_index", + "oxc_semantic", + "oxc_sourcemap", + "oxc_span", + "oxc_syntax", + "rustc-hash", +] + +[[package]] +name = "oxc_compat" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd06b5cb59ece933be410adc6f811108a10c567fa2d105321f8d0f01cc4ab0c6" +dependencies = [ + "cow-utils", + "oxc-browserslist", + "oxc_syntax", + "rustc-hash", + "serde", +] + +[[package]] +name = "oxc_data_structures" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd090988aa69c1e394f424289abb9bb1281448a072419ca556a07228ad36b854" +dependencies = [ + "ropey", +] + +[[package]] +name = "oxc_diagnostics" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b8a09558d38ee7f23faf0249cdb37b5b26f53a7375941f8636c41c661b283ce" +dependencies = [ + "cow-utils", + "oxc-miette", + "percent-encoding", +] + +[[package]] +name = "oxc_ecmascript" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0002ece1cc266aa6654913d39cf552b0cf501afce9d953b87c283dfcd307266" +dependencies = [ + "cow-utils", + "num-bigint", + "num-traits", + "oxc_allocator", + "oxc_ast", + "oxc_span", + "oxc_syntax", +] + +[[package]] +name = "oxc_estree" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af85b24b7e10bf0ccb252f16d38492f51236c1ba146f62013993667cbf7fa649" +dependencies = [ + "dragonbox_ecma", + "itoa", + "oxc_data_structures", +] + +[[package]] +name = "oxc_index" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3e6120999627ec9703025eab7c9f410ebb7e95557632a8902ca48210416c2b" +dependencies = [ + "nonmax", + "rayon", + "serde", +] + +[[package]] +name = "oxc_isolated_declarations" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82e443dba19b89e943d874fbd75fc5adbe996796d6930b891d7b63084e4b926" +dependencies = [ + "bitflags 2.11.0", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_diagnostics", + "oxc_ecmascript", + "oxc_span", + "oxc_syntax", + "rustc-hash", +] + +[[package]] +name = "oxc_mangler" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb751e1971f0809547347edfebe3755d5bed354aacf3ca7fe7216b901a11dd1d" +dependencies = [ + "itertools", + "oxc_allocator", + "oxc_ast", + "oxc_data_structures", + "oxc_index", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "rustc-hash", +] + +[[package]] +name = "oxc_minifier" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e836caa36befa6f9603eebf56b0c08d1e164ae31160c8e99ab3f535203065a4c" +dependencies = [ + "cow-utils", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_codegen", + "oxc_compat", + "oxc_data_structures", + "oxc_ecmascript", + "oxc_index", + "oxc_mangler", + "oxc_parser", + "oxc_regular_expression", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "oxc_traverse", + "rustc-hash", +] + +[[package]] +name = "oxc_parser" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb730ab93ff23bbc471ef7109f847afa709bb284dd52a5d3366283d724858158" +dependencies = [ + "bitflags 2.11.0", + "cow-utils", + "memchr", + "num-bigint", + "num-traits", + "oxc_allocator", + "oxc_ast", + "oxc_data_structures", + "oxc_diagnostics", + "oxc_ecmascript", + "oxc_regular_expression", + "oxc_span", + "oxc_syntax", + "rustc-hash", + "seq-macro", +] + +[[package]] +name = "oxc_regular_expression" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bafc3035e3b0fe555ae56f7bbc108a7ee520b0858250ba16d197e44ca83746" +dependencies = [ + "bitflags 2.11.0", + "oxc_allocator", + "oxc_ast_macros", + "oxc_diagnostics", + "oxc_span", + "phf 0.13.1", + "rustc-hash", + "unicode-id-start", +] + +[[package]] +name = "oxc_resolver" +version = "11.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec96245d28afd809ea0d830d0cdedb372907d5f6d9c8fac2683888f14dfd62c" +dependencies = [ + "cfg-if", + "indexmap 2.9.0", + "json-strip-comments", + "libc", + "once_cell", + "papaya", + "pnp", + "rustc-hash", + "self_cell", + "serde", + "serde_json", + "simd-json", + "simdutf8", + "thiserror 2.0.17", + "tracing", + "url", + "windows 0.62.0", +] + +[[package]] +name = "oxc_semantic" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578e5fa0b975a28e1d1ffa6c81c8df094545fe3c3225956d4e93ffe1ade3dae0" +dependencies = [ + "itertools", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_cfg", + "oxc_data_structures", + "oxc_diagnostics", + "oxc_ecmascript", + "oxc_index", + "oxc_span", + "oxc_syntax", + "phf 0.13.1", + "rustc-hash", + "self_cell", +] + +[[package]] +name = "oxc_sourcemap" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f89482522f3cd820817d48ee4ade5b10822060d6e5e4d419f05f6d8bd29d70" +dependencies = [ + "base64-simd", + "json-escape-simd", + "rustc-hash", + "serde", + "serde_json", +] + +[[package]] +name = "oxc_span" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71243fe1ece27f71a6be5e2ab11d051792eeb578f3b056d7c9bbe82ec8043bf3" +dependencies = [ + "compact_str", + "oxc-miette", + "oxc_allocator", + "oxc_ast_macros", + "oxc_estree", + "serde", +] + +[[package]] +name = "oxc_syntax" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c99e555ed497111004a71fb2aa6f8fb90b0d3e2733aceeb035e24701d69634" +dependencies = [ + "bitflags 2.11.0", + "cow-utils", + "dragonbox_ecma", + "nonmax", + "oxc_allocator", + "oxc_ast_macros", + "oxc_data_structures", + "oxc_estree", + "oxc_index", + "oxc_span", + "phf 0.13.1", + "serde", + "unicode-id-start", +] + +[[package]] +name = "oxc_transformer" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d55a4cd5291f307ae9138e30de09b6b3004b07001df7b70b9d9d72b87593d07" +dependencies = [ + "base64 0.22.1", + "compact_str", + "indexmap 2.9.0", + "itoa", + "memchr", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_compat", + "oxc_data_structures", + "oxc_diagnostics", + "oxc_ecmascript", + "oxc_parser", + "oxc_regular_expression", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "oxc_traverse", + "rustc-hash", + "serde", + "serde_json", + "sha1", +] + +[[package]] +name = "oxc_transformer_plugins" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcec20f3427b1b1fd2bc5a67c1a22aac31d5157ee0c85eed7105a4289bfd9e86" +dependencies = [ + "cow-utils", + "itoa", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_diagnostics", + "oxc_ecmascript", + "oxc_parser", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "oxc_transformer", + "oxc_traverse", + "rustc-hash", +] + +[[package]] +name = "oxc_traverse" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68d00264248ef474988ea02bfa44ccacd9a1d8716f0efb33a9cd6f992cf07b7b" +dependencies = [ + "itoa", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_data_structures", + "oxc_ecmascript", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "rustc-hash", +] + [[package]] name = "p12" version = "0.6.3" @@ -4084,6 +4981,16 @@ dependencies = [ "system-deps", ] +[[package]] +name = "papaya" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92dd0b07c53a0a0c764db2ace8c541dc47320dad97c2200c2a637ab9dd2328f" +dependencies = [ + "equivalent", + "seize", +] + [[package]] name = "parking" version = "2.2.1" @@ -4137,9 +5044,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" @@ -4153,10 +5060,22 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "fixedbitset", + "fixedbitset 0.4.2", "indexmap 2.9.0", ] +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset 0.5.7", + "hashbrown 0.15.3", + "indexmap 2.9.0", + "serde", +] + [[package]] name = "phf" version = "0.8.0" @@ -4187,6 +5106,17 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + [[package]] name = "phf_codegen" version = "0.8.0" @@ -4237,6 +5167,16 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.10.0" @@ -4264,6 +5204,19 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "phf_shared" version = "0.8.0" @@ -4291,6 +5244,15 @@ dependencies = [ "siphasher 1.0.1", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -4366,6 +5328,25 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "pnp" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e38320d5a8e386647f622067588bdb338c9e6e43eb32cf6f8991dd0e8f0046" +dependencies = [ + "byteorder", + "concurrent_lru", + "fancy-regex", + "flate2", + "nodejs-built-in-modules", + "pathdiff", + "radix_trie", + "rustc-hash", + "serde", + "serde_json", + "thiserror 2.0.17", +] + [[package]] name = "polling" version = "3.8.0" @@ -4407,6 +5388,18 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.2" @@ -4734,6 +5727,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "radix_trie" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4431027dcd37fc2a73ef740b5f233aa805897935b8bce0195e41bbf9a3289a" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.7.3" @@ -4850,6 +5853,26 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "rc2" version = "0.8.1" @@ -4865,7 +5888,7 @@ version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", ] [[package]] @@ -4879,6 +5902,26 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "regex" version = "0.2.11" @@ -4888,7 +5931,7 @@ dependencies = [ "aho-corasick 0.6.10", "memchr", "regex-syntax 0.5.6", - "thread_local", + "thread_local 0.3.6", "utf8-ranges", ] @@ -4930,6 +5973,16 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "regress" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" +dependencies = [ + "hashbrown 0.16.1", + "memchr", +] + [[package]] name = "rend" version = "0.4.2" @@ -5056,13 +6109,430 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rolldown" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab66da287f0b268d5ba628ed75a2562b249ab1635eac2645450fc2c7cb242ae5" +dependencies = [ + "anyhow", + "append-only-vec", + "arcstr", + "bitflags 2.11.0", + "commondir", + "css-module-lexer", + "derive_more 2.1.1", + "dunce", + "futures", + "indexmap 2.9.0", + "itertools", + "itoa", + "json-escape-simd", + "memchr", + "oxc", + "oxc_allocator", + "oxc_ecmascript", + "oxc_index", + "oxc_traverse", + "petgraph 0.8.3", + "rayon", + "rolldown-notify", + "rolldown_common", + "rolldown_debug", + "rolldown_ecmascript", + "rolldown_ecmascript_utils", + "rolldown_error", + "rolldown_fs", + "rolldown_plugin", + "rolldown_plugin_chunk_import_map", + "rolldown_plugin_data_uri", + "rolldown_plugin_hmr", + "rolldown_plugin_oxc_runtime", + "rolldown_resolver", + "rolldown_sourcemap", + "rolldown_std_utils", + "rolldown_tracing", + "rolldown_utils", + "rolldown_watcher", + "rustc-hash", + "serde", + "serde_json", + "string_wizard", + "sugar_path", + "tokio", + "tracing", + "url", + "xxhash-rust", +] + +[[package]] +name = "rolldown-ariadne" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77dff57c9de498bb1eb5b1ce682c2e3a0ae956b266fa0933c3e151b87b078967" +dependencies = [ + "unicode-width", + "yansi", +] + +[[package]] +name = "rolldown-file-id" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77087e8de5a5a65ea239ed3a5b182dfc27807a8fca4d0c2c0e693eb528b322b7" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "rolldown-notify" +version = "8.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9122f313b71e7632260ee45f068ff70239bff876997fe78aecf2eabaf4fd709" +dependencies = [ + "bitflags 2.11.0", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log 0.4.29", + "mio", + "rolldown-notify-types", + "walkdir", + "windows-sys 0.61.2", +] + +[[package]] +name = "rolldown-notify-debouncer-full" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69128ae9ebf6d65638157e79dc9430e9341179aed9d85dde315ce6a702dba7e1" +dependencies = [ + "log 0.4.29", + "rolldown-file-id", + "rolldown-notify", + "rolldown-notify-types", + "walkdir", +] + +[[package]] +name = "rolldown-notify-types" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1931923a28e14c01a27ca56669669eb3e3de4068859c34e17b96c93ba3a61afe" + +[[package]] +name = "rolldown_common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52908c2df0f1caf4883b858a0327284f8208016f47c02c2359a1eb8a7dd48a03" +dependencies = [ + "anyhow", + "arcstr", + "bitflags 2.11.0", + "dashmap", + "derive_more 2.1.1", + "fast-glob", + "itertools", + "num-bigint", + "oxc", + "oxc_ecmascript", + "oxc_index", + "oxc_resolver", + "rolldown_ecmascript", + "rolldown_error", + "rolldown_sourcemap", + "rolldown_std_utils", + "rolldown_utils", + "rustc-hash", + "serde", + "serde_json", + "simdutf8", + "string_wizard", + "sugar_path", + "tokio", +] + +[[package]] +name = "rolldown_debug" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff3c020f3689c76ba711b8244df0c5b356b51a3661ef1eab12cd81abaeabb468" +dependencies = [ + "blake3", + "dashmap", + "rolldown_debug_action", + "rustc-hash", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "rolldown_debug_action" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a6e4decc9842a6638f85d95c25c6ad95e65367a9ad8845b7cae7241a7d9aa1" +dependencies = [ + "serde", + "ts-rs", +] + +[[package]] +name = "rolldown_ecmascript" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff517120f92e3135809afeb404de52c12008a294b8d3a42f204f8a6f0d1edbf2" +dependencies = [ + "arcstr", + "oxc", + "oxc_sourcemap", + "rolldown_error", + "self_cell", +] + +[[package]] +name = "rolldown_ecmascript_utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea28f9d1db8bd99bc42d9795a73feb00d1fb113e462a0e7faccd608c2765371" +dependencies = [ + "oxc", + "rolldown_common", + "smallvec", +] + +[[package]] +name = "rolldown_error" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d0919fac41eadf256b01515113bf719132d745b05fd0796e6a8893f07d0367" +dependencies = [ + "anyhow", + "arcstr", + "bitflags 2.11.0", + "derive_more 2.1.1", + "heck 0.5.0", + "oxc", + "oxc_resolver", + "rolldown-ariadne", + "rolldown_utils", + "ropey", + "rustc-hash", + "sugar_path", +] + +[[package]] +name = "rolldown_fs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "689f6d8749193f57e534d0377e0532e1a773e6b9eec57d8732598fe920a8ad55" +dependencies = [ + "oxc_resolver", + "vfs", +] + +[[package]] +name = "rolldown_plugin" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a71784af5ac2505d5c2e43f91a33a10b18dd0305f169666ffb5e1c673f9c0a4" +dependencies = [ + "anyhow", + "arcstr", + "async-trait", + "bitflags 2.11.0", + "dashmap", + "derive_more 2.1.1", + "oxc_index", + "rolldown_common", + "rolldown_debug", + "rolldown_ecmascript", + "rolldown_error", + "rolldown_resolver", + "rolldown_sourcemap", + "rolldown_utils", + "rustc-hash", + "serde", + "serde_json", + "string_wizard", + "sugar_path", + "tokio", + "tracing", + "typedmap", +] + +[[package]] +name = "rolldown_plugin_chunk_import_map" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40021d98db701111ba3adff9c9469ddd18b4c81ad653a1626300c505714ecc94" +dependencies = [ + "arcstr", + "rolldown_common", + "rolldown_plugin", + "rolldown_utils", + "rustc-hash", + "serde_json", + "xxhash-rust", +] + +[[package]] +name = "rolldown_plugin_data_uri" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3f700d3a8c5fa2e8a78ae6b36e268fbb831ad88e2832c42264c954918fe191" +dependencies = [ + "arcstr", + "base64-simd", + "rolldown_common", + "rolldown_plugin", + "rolldown_utils", + "simdutf8", + "urlencoding", +] + +[[package]] +name = "rolldown_plugin_hmr" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6908f9e06606be7b00e4f00c51ef0eaf4905845d0ccf3441d4dfec4a1c02bd11" +dependencies = [ + "arcstr", + "oxc", + "rolldown_common", + "rolldown_plugin", +] + +[[package]] +name = "rolldown_plugin_oxc_runtime" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8434a88468c4807d61a076bf422aadd05208cbff4799815100d17d8061e5041" +dependencies = [ + "arcstr", + "phf 0.13.1", + "rolldown_plugin", + "rolldown_utils", +] + +[[package]] +name = "rolldown_resolver" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88ed5e49376de5f5789c55840ef0eea23b0d95a2c31346d27e5df2dfbe41753" +dependencies = [ + "anyhow", + "arcstr", + "dashmap", + "itertools", + "oxc_resolver", + "rolldown_common", + "rolldown_fs", + "rolldown_utils", + "sugar_path", +] + +[[package]] +name = "rolldown_sourcemap" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee286104242208a64dba055eceeabce5d0e5d3c67a219e8b86d3f937631a9169" +dependencies = [ + "memchr", + "oxc", + "oxc_sourcemap", + "rolldown_utils", + "rustc-hash", +] + +[[package]] +name = "rolldown_std_utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfe1535df7c67d246e3394196a6b89050ef63cb0a6c0d1c70bb32d747be94d6" +dependencies = [ + "regex 1.11.1", +] + +[[package]] +name = "rolldown_tracing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6f0098a2f15319bb7b8257701e667367386480e7c7eeafd5cc832e3f29a487" +dependencies = [ + "tracing", + "tracing-chrome", + "tracing-subscriber", +] + +[[package]] +name = "rolldown_utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6252c045eb9a2b3b12039ce01ae5e31d17425ead895ce1d423ad1e6b8b5e8795" +dependencies = [ + "anyhow", + "arcstr", + "async-scoped", + "base-encode", + "base64-simd", + "cow-utils", + "dashmap", + "fast-glob", + "form_urlencoded", + "futures", + "indexmap 2.9.0", + "infer", + "itoa", + "memchr", + "mime", + "nom 8.0.0", + "oxc", + "oxc_index", + "phf 0.13.1", + "rayon", + "regex 1.11.1", + "regress", + "rolldown_std_utils", + "rustc-hash", + "serde_json", + "simdutf8", + "sugar_path", + "tokio", + "uuid", + "xxhash-rust", +] + +[[package]] +name = "rolldown_watcher" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01832a5170dca8727de5bb19bf8171dc8022cca54743f7a1f9fe25c581cb247d" +dependencies = [ + "rolldown-notify", + "rolldown-notify-debouncer-full", + "rolldown_error", +] + +[[package]] +name = "ropey" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" +dependencies = [ + "smallvec", + "str_indices", +] + [[package]] name = "rusqlite" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "chrono", "fallible-iterator", "fallible-streaming-iterator", @@ -5119,7 +6589,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5132,7 +6602,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.9.4", @@ -5342,7 +6812,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5355,7 +6825,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5372,6 +6842,16 @@ dependencies = [ "libc", ] +[[package]] +name = "seize" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "selectors" version = "0.24.0" @@ -5380,7 +6860,7 @@ checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", "cssparser", - "derive_more", + "derive_more 0.99.20", "fxhash", "log 0.4.29", "phf 0.8.0", @@ -5390,6 +6870,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "self_cell" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" + [[package]] name = "semver" version = "1.0.26" @@ -5399,6 +6885,12 @@ dependencies = [ "serde", ] +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + [[package]] name = "serde" version = "1.0.228" @@ -5478,6 +6970,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ + "indexmap 2.9.0", "itoa", "memchr", "ryu", @@ -5655,6 +7148,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static 1.5.0", +] + [[package]] name = "shared_child" version = "1.0.2" @@ -5686,6 +7188,18 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd-json" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4255126f310d2ba20048db6321c81ab376f6a6735608bf11f0785c41f01f64e3" +dependencies = [ + "halfbrown", + "ref-cast", + "simdutf8", + "value-trait", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -5719,6 +7233,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "socket2" version = "0.5.10" @@ -5799,6 +7319,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str_indices" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" + [[package]] name = "string_cache" version = "0.8.9" @@ -5824,6 +7350,19 @@ dependencies = [ "quote", ] +[[package]] +name = "string_wizard" +version = "0.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49077a960de0ac40075720160a3afb2bfac54ee2b744f245730b811c6fc95ed7" +dependencies = [ + "memchr", + "oxc_index", + "oxc_sourcemap", + "rustc-hash", + "serde", +] + [[package]] name = "strsim" version = "0.11.1" @@ -5836,6 +7375,15 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "sugar_path" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48abcb2199ce37819c20dc7a72dc09e3263a00e598ff5089fe5fda92e0f63c37" +dependencies = [ + "smallvec", +] + [[package]] name = "swift-rs" version = "1.0.7" @@ -5916,7 +7464,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5950,7 +7498,7 @@ version = "0.34.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "block2 0.6.1", "core-foundation 0.10.1", "core-graphics 0.24.0", @@ -5978,8 +7526,8 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows", - "windows-core", + "windows 0.61.1", + "windows-core 0.61.2", "windows-version", "x11-dl", ] @@ -6067,7 +7615,7 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows", + "windows 0.61.1", ] [[package]] @@ -6183,7 +7731,7 @@ dependencies = [ "tracing", "url", "windows-registry", - "windows-result", + "windows-result 0.3.4", ] [[package]] @@ -6266,7 +7814,7 @@ dependencies = [ "tauri-plugin", "thiserror 2.0.17", "url", - "windows", + "windows 0.61.1", "zbus", ] @@ -6363,7 +7911,7 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73736611e14142408d15353e21e3cca2f12a3cfb523ad0ce85999b6d2ef1a704" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "log 0.4.29", "serde", "serde_json", @@ -6394,7 +7942,7 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.1", ] [[package]] @@ -6420,7 +7968,7 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.1", "wry", ] @@ -6512,6 +8060,17 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -6561,6 +8120,15 @@ dependencies = [ "lazy_static 1.5.0", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "tiff" version = "0.9.1" @@ -6913,7 +8481,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "bytes", "futures-util", "http", @@ -6960,12 +8528,62 @@ dependencies = [ ] [[package]] -name = "tracing-core" -version = "0.1.34" +name = "tracing-chrome" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "bf0a738ed5d6450a9fb96e86a23ad808de2b727fd1394585da5cdd6788ffe724" +dependencies = [ + "serde_json", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log 0.4.29", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "nu-ansi-term", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local 1.1.9", + "tracing-core", + "tracing-log", + "tracing-serde", ] [[package]] @@ -6998,9 +8616,9 @@ checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63" dependencies = [ "fnv", "memchr", - "nom", + "nom 7.1.3", "once_cell", - "petgraph", + "petgraph 0.6.5", ] [[package]] @@ -7058,6 +8676,15 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typedmap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63278e72ed4f207eb3216c944cbafb35bdb656d2eab97ef73c0c165a1cd3e319" +dependencies = [ + "dashmap", +] + [[package]] name = "typeid" version = "1.0.3" @@ -7134,18 +8761,42 @@ version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +[[package]] +name = "unicode-id-start" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-segmentation" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.5.1" @@ -7241,12 +8892,30 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "value-bag" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" +[[package]] +name = "value-trait" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e80f0c733af0720a501b3905d22e2f97662d8eacfe082a75ed7ffb5ab08cb59" +dependencies = [ + "float-cmp", + "halfbrown", + "itoa", + "ryu", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -7265,6 +8934,21 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vfs" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e723b9e1c02a3cf9f9d0de6a4ddb8cdc1df859078902fe0ae0589d615711ae6" +dependencies = [ + "filetime", +] + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "vswhom" version = "0.1.0" @@ -7439,7 +9123,7 @@ version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "rustix 0.38.44", "wayland-backend", "wayland-scanner", @@ -7451,7 +9135,7 @@ version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7463,7 +9147,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7510,6 +9194,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webbrowser" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f00bb839c1cf1e3036066614cbdcd035ecf215206691ea646aa3c60a24f68f2" +dependencies = [ + "core-foundation 0.10.1", + "jni", + "log 0.4.29", + "ndk-context", + "objc2 0.6.1", + "objc2-foundation 0.3.1", + "url", + "web-sys", +] + [[package]] name = "webkit2gtk" version = "2.0.1" @@ -7580,8 +9280,8 @@ checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", - "windows-core", + "windows 0.61.1", + "windows-core 0.61.2", "windows-implement", "windows-interface", ] @@ -7604,8 +9304,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" dependencies = [ "thiserror 2.0.17", - "windows", - "windows-core", + "windows 0.61.1", + "windows-core 0.61.2", ] [[package]] @@ -7666,11 +9366,24 @@ version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ - "windows-collections", - "windows-core", - "windows-future", + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", "windows-link 0.1.1", - "windows-numerics", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9579d0e6970fd5250aa29aba5994052385ff55cf7b28a059e484bb79ea842e42" +dependencies = [ + "windows-collections 0.3.0", + "windows-core 0.62.0", + "windows-future 0.3.0", + "windows-link 0.2.1", + "windows-numerics 0.3.0", ] [[package]] @@ -7679,7 +9392,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core", + "windows-core 0.61.2", +] + +[[package]] +name = "windows-collections" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90dd7a7b86859ec4cdf864658b311545ef19dbcf17a672b52ab7cefe80c336f" +dependencies = [ + "windows-core 0.62.0", ] [[package]] @@ -7691,8 +9413,21 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link 0.1.1", - "windows-result", - "windows-strings", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -7701,9 +9436,20 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link 0.1.1", - "windows-threading", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2194dee901458cb79e1148a4e9aac2b164cc95fa431891e7b296ff0b2f1d8a6" +dependencies = [ + "windows-core 0.62.0", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -7746,10 +9492,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link 0.1.1", ] +[[package]] +name = "windows-numerics" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce3498fe0aba81e62e477408383196b4b0363db5e0c27646f932676283b43d8" +dependencies = [ + "windows-core 0.62.0", + "windows-link 0.2.1", +] + [[package]] name = "windows-registry" version = "0.5.2" @@ -7757,8 +9513,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" dependencies = [ "windows-link 0.1.1", - "windows-result", - "windows-strings", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -7770,6 +9526,15 @@ dependencies = [ "windows-link 0.1.1", ] +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -7779,6 +9544,15 @@ dependencies = [ "windows-link 0.1.1", ] +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -7895,6 +9669,15 @@ dependencies = [ "windows-link 0.1.1", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-version" version = "0.1.4" @@ -8127,7 +9910,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", ] [[package]] @@ -8194,8 +9977,8 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", - "windows-core", + "windows 0.61.1", + "windows-core 0.61.2", "windows-version", "x11-dl", ] @@ -8257,6 +10040,12 @@ dependencies = [ "rustix 1.0.7", ] +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yaak" version = "0.1.0" @@ -8353,23 +10142,35 @@ name = "yaak-cli" version = "0.1.0" dependencies = [ "assert_cmd", + "base64 0.22.1", "clap", + "console", "dirs", "env_logger", "futures", + "hex", + "keyring", "log 0.4.29", + "oxc_resolver", "predicates", + "rand 0.8.5", + "reqwest", + "rolldown", "schemars", "serde", "serde_json", + "sha2", "tempfile", "tokio", + "walkdir", + "webbrowser", "yaak", "yaak-crypto", "yaak-http", "yaak-models", "yaak-plugins", "yaak-templates", + "zip", ] [[package]] @@ -8658,6 +10459,12 @@ dependencies = [ "yaak-tls", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yasna" version = "0.5.2" @@ -8852,7 +10659,7 @@ dependencies = [ "aes", "arbitrary", "bzip2", - "constant_time_eq", + "constant_time_eq 0.3.1", "crc32fast", "deflate64", "flate2", diff --git a/crates-cli/yaak-cli/Cargo.toml b/crates-cli/yaak-cli/Cargo.toml index 54e7385e..6ef74cb8 100644 --- a/crates-cli/yaak-cli/Cargo.toml +++ b/crates-cli/yaak-cli/Cargo.toml @@ -5,19 +5,31 @@ edition = "2024" publish = false [[bin]] -name = "yaakcli" +name = "yaak" path = "src/main.rs" [dependencies] +base64 = "0.22" clap = { version = "4", features = ["derive"] } +console = "0.15" dirs = "6" env_logger = "0.11" futures = "0.3" +hex = { workspace = true } +keyring = { workspace = true, features = ["apple-native", "windows-native", "sync-secret-service"] } log = { workspace = true } +rand = "0.8" +reqwest = { workspace = true } +rolldown = "0.1.0" +oxc_resolver = "=11.10.0" schemars = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } +sha2 = { workspace = true } +tokio = { workspace = true, features = ["rt-multi-thread", "macros", "io-util", "net", "signal", "time"] } +walkdir = "2" +webbrowser = "1" +zip = "4" yaak = { workspace = true } yaak-crypto = { workspace = true } yaak-http = { workspace = true } diff --git a/crates-cli/yaak-cli/src/cli.rs b/crates-cli/yaak-cli/src/cli.rs index 2b74ca9b..5bc43efe 100644 --- a/crates-cli/yaak-cli/src/cli.rs +++ b/crates-cli/yaak-cli/src/cli.rs @@ -2,7 +2,7 @@ use clap::{Args, Parser, Subcommand, ValueEnum}; use std::path::PathBuf; #[derive(Parser)] -#[command(name = "yaakcli")] +#[command(name = "yaak")] #[command(about = "Yaak CLI - API client from the command line")] pub struct Cli { /// Use a custom data directory @@ -23,6 +23,18 @@ pub struct Cli { #[derive(Subcommand)] pub enum Commands { + /// Authentication commands + Auth(AuthArgs), + + /// Plugin development and publishing commands + Plugin(PluginArgs), + + #[command(hide = true)] + Build(PluginPathArg), + + #[command(hide = true)] + Dev(PluginPathArg), + /// Send a request, folder, or workspace by ID Send(SendArgs), @@ -305,3 +317,59 @@ pub enum EnvironmentCommands { yes: bool, }, } + +#[derive(Args)] +pub struct AuthArgs { + #[command(subcommand)] + pub command: AuthCommands, +} + +#[derive(Subcommand)] +pub enum AuthCommands { + /// Login to Yaak via web browser + Login, + + /// Sign out of the Yaak CLI + Logout, + + /// Print the current logged-in user's info + Whoami, +} + +#[derive(Args)] +pub struct PluginArgs { + #[command(subcommand)] + pub command: PluginCommands, +} + +#[derive(Subcommand)] +pub enum PluginCommands { + /// Transpile code into a runnable plugin bundle + Build(PluginPathArg), + + /// Build plugin bundle continuously when the filesystem changes + Dev(PluginPathArg), + + /// Generate a "Hello World" Yaak plugin + Generate(GenerateArgs), + + /// Publish a Yaak plugin version to the plugin registry + Publish(PluginPathArg), +} + +#[derive(Args, Clone)] +pub struct PluginPathArg { + /// Path to plugin directory (defaults to current working directory) + pub path: Option, +} + +#[derive(Args, Clone)] +pub struct GenerateArgs { + /// Plugin name (defaults to a generated name in interactive mode) + #[arg(long)] + pub name: Option, + + /// Output directory for the generated plugin (defaults to ./ in interactive mode) + #[arg(long)] + pub dir: Option, +} diff --git a/crates-cli/yaak-cli/src/commands/auth.rs b/crates-cli/yaak-cli/src/commands/auth.rs new file mode 100644 index 00000000..50259397 --- /dev/null +++ b/crates-cli/yaak-cli/src/commands/auth.rs @@ -0,0 +1,556 @@ +use crate::cli::{AuthArgs, AuthCommands}; +use crate::ui; +use base64::Engine as _; +use keyring::Entry; +use rand::RngCore; +use rand::rngs::OsRng; +use reqwest::Url; +use serde_json::Value; +use sha2::{Digest, Sha256}; +use std::io::{self, IsTerminal, Write}; +use std::time::Duration; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::{TcpListener, TcpStream}; + +const OAUTH_CLIENT_ID: &str = "a1fe44800c2d7e803cad1b4bf07a291c"; +const KEYRING_USER: &str = "yaak"; +const AUTH_TIMEOUT: Duration = Duration::from_secs(300); +const MAX_REQUEST_BYTES: usize = 16 * 1024; + +type CommandResult = std::result::Result; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Environment { + Production, + Staging, + Development, +} + +impl Environment { + fn app_base_url(self) -> &'static str { + match self { + Environment::Production => "https://yaak.app", + Environment::Staging => "https://todo.yaak.app", + Environment::Development => "http://localhost:9444", + } + } + + fn api_base_url(self) -> &'static str { + match self { + Environment::Production => "https://api.yaak.app", + Environment::Staging => "https://todo.yaak.app", + Environment::Development => "http://localhost:9444", + } + } + + fn keyring_service(self) -> &'static str { + match self { + Environment::Production => "app.yaak.cli.Token", + Environment::Staging => "app.yaak.cli.staging.Token", + Environment::Development => "app.yaak.cli.dev.Token", + } + } +} + +struct OAuthFlow { + app_base_url: String, + auth_url: Url, + token_url: String, + redirect_url: String, + state: String, + code_verifier: String, +} + +pub async fn run(args: AuthArgs) -> i32 { + let result = match args.command { + AuthCommands::Login => login().await, + AuthCommands::Logout => logout(), + AuthCommands::Whoami => whoami().await, + }; + + match result { + Ok(()) => 0, + Err(error) => { + ui::error(&error); + 1 + } + } +} + +async fn login() -> CommandResult { + let environment = current_environment(); + + let listener = TcpListener::bind("127.0.0.1:0") + .await + .map_err(|e| format!("Failed to start OAuth callback server: {e}"))?; + let port = listener + .local_addr() + .map_err(|e| format!("Failed to determine callback server port: {e}"))? + .port(); + + let oauth = build_oauth_flow(environment, port)?; + + ui::info(&format!("Initiating login to {}", oauth.auth_url)); + if !confirm_open_browser()? { + ui::info("Login canceled"); + return Ok(()); + } + + if let Err(err) = webbrowser::open(oauth.auth_url.as_ref()) { + ui::warning(&format!("Failed to open browser: {err}")); + ui::info(&format!("Open this URL manually:\n{}", oauth.auth_url)); + } + ui::info("Waiting for authentication..."); + + let code = tokio::select! { + result = receive_oauth_code(listener, &oauth.state, &oauth.app_base_url) => result?, + _ = tokio::signal::ctrl_c() => { + return Err("Interrupted by user".to_string()); + } + _ = tokio::time::sleep(AUTH_TIMEOUT) => { + return Err("Timeout waiting for authentication".to_string()); + } + }; + + let token = exchange_access_token(&oauth, &code).await?; + store_auth_token(environment, &token)?; + ui::success("Authentication successful!"); + Ok(()) +} + +fn logout() -> CommandResult { + delete_auth_token(current_environment())?; + ui::success("Signed out of Yaak"); + Ok(()) +} + +async fn whoami() -> CommandResult { + let environment = current_environment(); + let token = match get_auth_token(environment)? { + Some(token) => token, + None => { + ui::warning("Not logged in"); + ui::info("Please run `yaak auth login`"); + return Ok(()); + } + }; + + let url = format!("{}/api/v1/whoami", environment.api_base_url()); + let response = reqwest::Client::new() + .get(url) + .header("X-Yaak-Session", token) + .header(reqwest::header::USER_AGENT, user_agent()) + .send() + .await + .map_err(|e| format!("Failed to call whoami endpoint: {e}"))?; + + let status = response.status(); + let body = + response.text().await.map_err(|e| format!("Failed to read whoami response body: {e}"))?; + + if !status.is_success() { + if status.as_u16() == 401 { + let _ = delete_auth_token(environment); + return Err( + "Unauthorized to access CLI. Run `yaak auth login` to refresh credentials." + .to_string(), + ); + } + return Err(parse_api_error(status.as_u16(), &body)); + } + + println!("{body}"); + Ok(()) +} + +fn current_environment() -> Environment { + let value = std::env::var("ENVIRONMENT").ok(); + parse_environment(value.as_deref()) +} + +fn parse_environment(value: Option<&str>) -> Environment { + match value { + Some("staging") => Environment::Staging, + Some("development") => Environment::Development, + _ => Environment::Production, + } +} + +fn build_oauth_flow(environment: Environment, callback_port: u16) -> CommandResult { + let code_verifier = random_hex(32); + let state = random_hex(24); + let redirect_url = format!("http://127.0.0.1:{callback_port}/oauth/callback"); + + let code_challenge = base64::engine::general_purpose::URL_SAFE_NO_PAD + .encode(Sha256::digest(code_verifier.as_bytes())); + + let mut auth_url = Url::parse(&format!("{}/login/oauth/authorize", environment.app_base_url())) + .map_err(|e| format!("Failed to build OAuth authorize URL: {e}"))?; + auth_url + .query_pairs_mut() + .append_pair("response_type", "code") + .append_pair("client_id", OAUTH_CLIENT_ID) + .append_pair("redirect_uri", &redirect_url) + .append_pair("state", &state) + .append_pair("code_challenge_method", "S256") + .append_pair("code_challenge", &code_challenge); + + Ok(OAuthFlow { + app_base_url: environment.app_base_url().to_string(), + auth_url, + token_url: format!("{}/login/oauth/access_token", environment.app_base_url()), + redirect_url, + state, + code_verifier, + }) +} + +async fn receive_oauth_code( + listener: TcpListener, + expected_state: &str, + app_base_url: &str, +) -> CommandResult { + loop { + let (mut stream, _) = listener + .accept() + .await + .map_err(|e| format!("OAuth callback server accept error: {e}"))?; + + match parse_callback_request(&mut stream).await { + Ok((state, code)) => { + if state != expected_state { + let _ = write_bad_request(&mut stream, "Invalid OAuth state").await; + continue; + } + + let success_redirect = format!("{app_base_url}/login/oauth/success"); + write_redirect(&mut stream, &success_redirect) + .await + .map_err(|e| format!("Failed responding to OAuth callback: {e}"))?; + return Ok(code); + } + Err(error) => { + let _ = write_bad_request(&mut stream, &error).await; + if error.starts_with("OAuth provider returned error:") { + return Err(error); + } + } + } + } +} + +async fn parse_callback_request(stream: &mut TcpStream) -> CommandResult<(String, String)> { + let target = read_http_target(stream).await?; + if !target.starts_with("/oauth/callback") { + return Err("Expected /oauth/callback path".to_string()); + } + + let url = Url::parse(&format!("http://127.0.0.1{target}")) + .map_err(|e| format!("Failed to parse callback URL: {e}"))?; + let mut state: Option = None; + let mut code: Option = None; + let mut oauth_error: Option = None; + let mut oauth_error_description: Option = None; + + for (k, v) in url.query_pairs() { + if k == "state" { + state = Some(v.into_owned()); + } else if k == "code" { + code = Some(v.into_owned()); + } else if k == "error" { + oauth_error = Some(v.into_owned()); + } else if k == "error_description" { + oauth_error_description = Some(v.into_owned()); + } + } + + if let Some(error) = oauth_error { + let mut message = format!("OAuth provider returned error: {error}"); + if let Some(description) = oauth_error_description.filter(|d| !d.is_empty()) { + message.push_str(&format!(" ({description})")); + } + return Err(message); + } + + let state = state.ok_or_else(|| "Missing 'state' query parameter".to_string())?; + let code = code.ok_or_else(|| "Missing 'code' query parameter".to_string())?; + + if code.is_empty() { + return Err("Missing 'code' query parameter".to_string()); + } + + Ok((state, code)) +} + +async fn read_http_target(stream: &mut TcpStream) -> CommandResult { + let mut buf = vec![0_u8; MAX_REQUEST_BYTES]; + let mut total_read = 0_usize; + + loop { + let n = stream + .read(&mut buf[total_read..]) + .await + .map_err(|e| format!("Failed reading callback request: {e}"))?; + if n == 0 { + break; + } + total_read += n; + + if buf[..total_read].windows(4).any(|w| w == b"\r\n\r\n") { + break; + } + + if total_read == MAX_REQUEST_BYTES { + return Err("OAuth callback request too large".to_string()); + } + } + + let req = String::from_utf8_lossy(&buf[..total_read]); + let request_line = + req.lines().next().ok_or_else(|| "Invalid callback request line".to_string())?; + let mut parts = request_line.split_whitespace(); + let method = parts.next().unwrap_or_default(); + let target = parts.next().unwrap_or_default(); + + if method != "GET" { + return Err(format!("Expected GET callback request, got '{method}'")); + } + if target.is_empty() { + return Err("Missing callback request target".to_string()); + } + + Ok(target.to_string()) +} + +async fn write_bad_request(stream: &mut TcpStream, message: &str) -> std::io::Result<()> { + let body = format!("Failed to authenticate: {message}"); + let response = format!( + "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", + body.len(), + body + ); + stream.write_all(response.as_bytes()).await?; + stream.shutdown().await +} + +async fn write_redirect(stream: &mut TcpStream, location: &str) -> std::io::Result<()> { + let response = format!( + "HTTP/1.1 302 Found\r\nLocation: {location}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + ); + stream.write_all(response.as_bytes()).await?; + stream.shutdown().await +} + +async fn exchange_access_token(oauth: &OAuthFlow, code: &str) -> CommandResult { + let response = reqwest::Client::new() + .post(&oauth.token_url) + .header(reqwest::header::USER_AGENT, user_agent()) + .form(&[ + ("grant_type", "authorization_code"), + ("client_id", OAUTH_CLIENT_ID), + ("code", code), + ("redirect_uri", oauth.redirect_url.as_str()), + ("code_verifier", oauth.code_verifier.as_str()), + ]) + .send() + .await + .map_err(|e| format!("Failed to exchange OAuth code for access token: {e}"))?; + + let status = response.status(); + let body = + response.text().await.map_err(|e| format!("Failed to read token response body: {e}"))?; + + if !status.is_success() { + return Err(format!( + "Failed to fetch access token: status={} body={}", + status.as_u16(), + body + )); + } + + let parsed: Value = + serde_json::from_str(&body).map_err(|e| format!("Invalid token response JSON: {e}"))?; + let token = parsed + .get("access_token") + .and_then(Value::as_str) + .filter(|s| !s.is_empty()) + .ok_or_else(|| format!("Token response missing access_token: {body}"))?; + + Ok(token.to_string()) +} + +fn keyring_entry(environment: Environment) -> CommandResult { + Entry::new(environment.keyring_service(), KEYRING_USER) + .map_err(|e| format!("Failed to initialize auth keyring entry: {e}")) +} + +fn get_auth_token(environment: Environment) -> CommandResult> { + let entry = keyring_entry(environment)?; + match entry.get_password() { + Ok(token) => Ok(Some(token)), + Err(keyring::Error::NoEntry) => Ok(None), + Err(err) => Err(format!("Failed to read auth token: {err}")), + } +} + +fn store_auth_token(environment: Environment, token: &str) -> CommandResult { + let entry = keyring_entry(environment)?; + entry.set_password(token).map_err(|e| format!("Failed to store auth token: {e}")) +} + +fn delete_auth_token(environment: Environment) -> CommandResult { + let entry = keyring_entry(environment)?; + match entry.delete_credential() { + Ok(()) | Err(keyring::Error::NoEntry) => Ok(()), + Err(err) => Err(format!("Failed to delete auth token: {err}")), + } +} + +fn parse_api_error(status: u16, body: &str) -> String { + if let Ok(value) = serde_json::from_str::(body) { + if let Some(message) = value.get("message").and_then(Value::as_str) { + return message.to_string(); + } + if let Some(error) = value.get("error").and_then(Value::as_str) { + return error.to_string(); + } + } + + format!("API error {status}: {body}") +} + +fn random_hex(bytes: usize) -> String { + let mut data = vec![0_u8; bytes]; + OsRng.fill_bytes(&mut data); + hex::encode(data) +} + +fn user_agent() -> String { + format!("YaakCli/{} ({})", env!("CARGO_PKG_VERSION"), ua_platform()) +} + +fn ua_platform() -> &'static str { + match std::env::consts::OS { + "windows" => "Win", + "darwin" => "Mac", + "linux" => "Linux", + _ => "Unknown", + } +} + +fn confirm_open_browser() -> CommandResult { + if !io::stdin().is_terminal() { + return Ok(true); + } + + loop { + print!("Open default browser? [Y/n]: "); + io::stdout().flush().map_err(|e| format!("Failed to flush stdout: {e}"))?; + + let mut input = String::new(); + io::stdin().read_line(&mut input).map_err(|e| format!("Failed to read input: {e}"))?; + + match input.trim().to_ascii_lowercase().as_str() { + "" | "y" | "yes" => return Ok(true), + "n" | "no" => return Ok(false), + _ => ui::warning("Please answer y or n"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn environment_mapping() { + assert_eq!(parse_environment(Some("staging")), Environment::Staging); + assert_eq!(parse_environment(Some("development")), Environment::Development); + assert_eq!(parse_environment(Some("production")), Environment::Production); + assert_eq!(parse_environment(None), Environment::Production); + } + + #[tokio::test] + async fn parses_callback_request() { + let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind"); + let addr = listener.local_addr().expect("local addr"); + + let server = tokio::spawn(async move { + let (mut stream, _) = listener.accept().await.expect("accept"); + parse_callback_request(&mut stream).await + }); + + let mut client = TcpStream::connect(addr).await.expect("connect"); + client + .write_all( + b"GET /oauth/callback?code=abc123&state=xyz HTTP/1.1\r\nHost: localhost\r\n\r\n", + ) + .await + .expect("write"); + + let parsed = server.await.expect("join").expect("parse"); + assert_eq!(parsed.0, "xyz"); + assert_eq!(parsed.1, "abc123"); + } + + #[tokio::test] + async fn parse_callback_request_oauth_error() { + let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind"); + let addr = listener.local_addr().expect("local addr"); + + let server = tokio::spawn(async move { + let (mut stream, _) = listener.accept().await.expect("accept"); + parse_callback_request(&mut stream).await + }); + + let mut client = TcpStream::connect(addr).await.expect("connect"); + client + .write_all( + b"GET /oauth/callback?error=access_denied&error_description=User%20denied&state=xyz HTTP/1.1\r\nHost: localhost\r\n\r\n", + ) + .await + .expect("write"); + + let err = server.await.expect("join").expect_err("should fail"); + assert!(err.contains("OAuth provider returned error: access_denied")); + assert!(err.contains("User denied")); + } + + #[tokio::test] + async fn receive_oauth_code_fails_fast_on_provider_error() { + let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind"); + let addr = listener.local_addr().expect("local addr"); + + let server = tokio::spawn(async move { + receive_oauth_code(listener, "expected-state", "http://localhost:9444").await + }); + + let mut client = TcpStream::connect(addr).await.expect("connect"); + client + .write_all( + b"GET /oauth/callback?error=access_denied&state=expected-state HTTP/1.1\r\nHost: localhost\r\n\r\n", + ) + .await + .expect("write"); + + let result = tokio::time::timeout(std::time::Duration::from_secs(2), server) + .await + .expect("should not timeout") + .expect("join"); + let err = result.expect_err("should return oauth error"); + assert!(err.contains("OAuth provider returned error: access_denied")); + } + + #[test] + fn builds_oauth_flow_with_pkce() { + let flow = build_oauth_flow(Environment::Development, 8080).expect("flow"); + assert!(flow.auth_url.as_str().contains("code_challenge_method=S256")); + assert!( + flow.auth_url + .as_str() + .contains("redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Foauth%2Fcallback") + ); + assert_eq!(flow.redirect_url, "http://127.0.0.1:8080/oauth/callback"); + assert_eq!(flow.token_url, "http://localhost:9444/login/oauth/access_token"); + } +} diff --git a/crates-cli/yaak-cli/src/commands/mod.rs b/crates-cli/yaak-cli/src/commands/mod.rs index 76c1ac0a..b2a0bf23 100644 --- a/crates-cli/yaak-cli/src/commands/mod.rs +++ b/crates-cli/yaak-cli/src/commands/mod.rs @@ -1,5 +1,7 @@ +pub mod auth; pub mod environment; pub mod folder; +pub mod plugin; pub mod request; pub mod send; pub mod workspace; diff --git a/crates-cli/yaak-cli/src/commands/plugin.rs b/crates-cli/yaak-cli/src/commands/plugin.rs new file mode 100644 index 00000000..b78d4645 --- /dev/null +++ b/crates-cli/yaak-cli/src/commands/plugin.rs @@ -0,0 +1,553 @@ +use crate::cli::{GenerateArgs, PluginArgs, PluginCommands, PluginPathArg}; +use crate::ui; +use keyring::Entry; +use rand::Rng; +use rolldown::{ + Bundler, BundlerOptions, ExperimentalOptions, InputItem, LogLevel, OutputFormat, Platform, + WatchOption, Watcher, +}; +use serde::Deserialize; +use serde_json::Value; +use std::collections::HashSet; +use std::fs; +use std::io::{self, IsTerminal, Read, Write}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use tokio::sync::Mutex; +use walkdir::WalkDir; +use zip::CompressionMethod; +use zip::write::SimpleFileOptions; + +type CommandResult = std::result::Result; + +const KEYRING_USER: &str = "yaak"; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Environment { + Production, + Staging, + Development, +} + +impl Environment { + fn api_base_url(self) -> &'static str { + match self { + Environment::Production => "https://api.yaak.app", + Environment::Staging => "https://todo.yaak.app", + Environment::Development => "http://localhost:9444", + } + } + + fn keyring_service(self) -> &'static str { + match self { + Environment::Production => "app.yaak.cli.Token", + Environment::Staging => "app.yaak.cli.staging.Token", + Environment::Development => "app.yaak.cli.dev.Token", + } + } +} + +pub async fn run_build(args: PluginPathArg) -> i32 { + match build(args).await { + Ok(()) => 0, + Err(error) => { + ui::error(&error); + 1 + } + } +} + +pub async fn run(args: PluginArgs) -> i32 { + match args.command { + PluginCommands::Build(args) => run_build(args).await, + PluginCommands::Dev(args) => run_dev(args).await, + PluginCommands::Generate(args) => run_generate(args).await, + PluginCommands::Publish(args) => run_publish(args).await, + } +} + +pub async fn run_dev(args: PluginPathArg) -> i32 { + match dev(args).await { + Ok(()) => 0, + Err(error) => { + ui::error(&error); + 1 + } + } +} + +pub async fn run_generate(args: GenerateArgs) -> i32 { + match generate(args) { + Ok(()) => 0, + Err(error) => { + ui::error(&error); + 1 + } + } +} + +pub async fn run_publish(args: PluginPathArg) -> i32 { + match publish(args).await { + Ok(()) => 0, + Err(error) => { + ui::error(&error); + 1 + } + } +} + +async fn build(args: PluginPathArg) -> CommandResult { + let plugin_dir = resolve_plugin_dir(args.path)?; + ensure_plugin_build_inputs(&plugin_dir)?; + + ui::info(&format!("Building plugin {}...", plugin_dir.display())); + let warnings = build_plugin_bundle(&plugin_dir).await?; + for warning in warnings { + ui::warning(&warning); + } + ui::success(&format!("Built plugin bundle at {}", plugin_dir.join("build/index.js").display())); + Ok(()) +} + +async fn dev(args: PluginPathArg) -> CommandResult { + let plugin_dir = resolve_plugin_dir(args.path)?; + ensure_plugin_build_inputs(&plugin_dir)?; + + ui::info(&format!("Watching plugin {}...", plugin_dir.display())); + ui::info("Press Ctrl-C to stop"); + + let bundler = Bundler::new(bundler_options(&plugin_dir, true)) + .map_err(|err| format!("Failed to initialize Rolldown watcher: {err}"))?; + let watcher = Watcher::new(vec![Arc::new(Mutex::new(bundler))], None) + .map_err(|err| format!("Failed to start Rolldown watcher: {err}"))?; + + watcher.start().await; + Ok(()) +} + +fn generate(args: GenerateArgs) -> CommandResult { + let default_name = random_name(); + let name = match args.name { + Some(name) => name, + None => prompt_with_default("Plugin name", &default_name)?, + }; + + let default_dir = format!("./{name}"); + let output_dir = match args.dir { + Some(dir) => dir, + None => PathBuf::from(prompt_with_default("Plugin dir", &default_dir)?), + }; + + if output_dir.exists() { + return Err(format!("Plugin directory already exists: {}", output_dir.display())); + } + + ui::info(&format!("Generating plugin in {}", output_dir.display())); + fs::create_dir_all(output_dir.join("src")) + .map_err(|e| format!("Failed creating plugin directory {}: {e}", output_dir.display()))?; + + write_file(&output_dir.join(".gitignore"), TEMPLATE_GITIGNORE)?; + write_file( + &output_dir.join("package.json"), + &TEMPLATE_PACKAGE_JSON.replace("yaak-plugin-name", &name), + )?; + write_file(&output_dir.join("tsconfig.json"), TEMPLATE_TSCONFIG)?; + write_file(&output_dir.join("README.md"), &TEMPLATE_README.replace("yaak-plugin-name", &name))?; + write_file( + &output_dir.join("src/index.ts"), + &TEMPLATE_INDEX_TS.replace("yaak-plugin-name", &name), + )?; + write_file(&output_dir.join("src/index.test.ts"), TEMPLATE_INDEX_TEST_TS)?; + + ui::success("Plugin scaffold generated"); + ui::info("Next steps:"); + println!(" 1. cd {}", output_dir.display()); + println!(" 2. npm install"); + println!(" 3. yaak plugin build"); + Ok(()) +} + +async fn publish(args: PluginPathArg) -> CommandResult { + let plugin_dir = resolve_plugin_dir(args.path)?; + ensure_plugin_build_inputs(&plugin_dir)?; + + let environment = current_environment(); + let token = get_auth_token(environment)? + .ok_or_else(|| "Not logged in. Run `yaak auth login`.".to_string())?; + + ui::info(&format!("Building plugin {}...", plugin_dir.display())); + let warnings = build_plugin_bundle(&plugin_dir).await?; + for warning in warnings { + ui::warning(&warning); + } + + ui::info("Archiving plugin"); + let archive = create_publish_archive(&plugin_dir)?; + + ui::info("Uploading plugin"); + let url = format!("{}/api/v1/plugins/publish", environment.api_base_url()); + let response = reqwest::Client::new() + .post(url) + .header("X-Yaak-Session", token) + .header(reqwest::header::USER_AGENT, user_agent()) + .header(reqwest::header::CONTENT_TYPE, "application/zip") + .body(archive) + .send() + .await + .map_err(|e| format!("Failed to upload plugin: {e}"))?; + + let status = response.status(); + let body = + response.text().await.map_err(|e| format!("Failed reading publish response body: {e}"))?; + + if !status.is_success() { + return Err(parse_api_error(status.as_u16(), &body)); + } + + let published: PublishResponse = serde_json::from_str(&body) + .map_err(|e| format!("Failed parsing publish response JSON: {e}\nResponse: {body}"))?; + ui::success(&format!("Plugin published {}", published.version)); + println!(" -> {}", published.url); + Ok(()) +} + +#[derive(Deserialize)] +struct PublishResponse { + version: String, + url: String, +} + +async fn build_plugin_bundle(plugin_dir: &Path) -> CommandResult> { + prepare_build_output_dir(plugin_dir)?; + let mut bundler = Bundler::new(bundler_options(plugin_dir, false)) + .map_err(|err| format!("Failed to initialize Rolldown: {err}"))?; + let output = bundler.write().await.map_err(|err| format!("Plugin build failed:\n{err}"))?; + + Ok(output.warnings.into_iter().map(|w| w.to_string()).collect()) +} + +fn prepare_build_output_dir(plugin_dir: &Path) -> CommandResult { + let build_dir = plugin_dir.join("build"); + if build_dir.exists() { + fs::remove_dir_all(&build_dir) + .map_err(|e| format!("Failed to clean build directory {}: {e}", build_dir.display()))?; + } + fs::create_dir_all(&build_dir) + .map_err(|e| format!("Failed to create build directory {}: {e}", build_dir.display())) +} + +fn bundler_options(plugin_dir: &Path, watch: bool) -> BundlerOptions { + BundlerOptions { + input: Some(vec![InputItem { import: "./src/index.ts".to_string(), ..Default::default() }]), + cwd: Some(plugin_dir.to_path_buf()), + file: Some("build/index.js".to_string()), + format: Some(OutputFormat::Cjs), + platform: Some(Platform::Node), + log_level: Some(LogLevel::Info), + experimental: watch + .then_some(ExperimentalOptions { incremental_build: Some(true), ..Default::default() }), + watch: watch.then_some(WatchOption::default()), + ..Default::default() + } +} + +fn resolve_plugin_dir(path: Option) -> CommandResult { + let cwd = + std::env::current_dir().map_err(|e| format!("Failed to read current directory: {e}"))?; + let candidate = match path { + Some(path) if path.is_absolute() => path, + Some(path) => cwd.join(path), + None => cwd, + }; + + if !candidate.exists() { + return Err(format!("Plugin directory does not exist: {}", candidate.display())); + } + if !candidate.is_dir() { + return Err(format!("Plugin path is not a directory: {}", candidate.display())); + } + + candidate + .canonicalize() + .map_err(|e| format!("Failed to resolve plugin directory {}: {e}", candidate.display())) +} + +fn ensure_plugin_build_inputs(plugin_dir: &Path) -> CommandResult { + let package_json = plugin_dir.join("package.json"); + if !package_json.is_file() { + return Err(format!( + "{} does not exist. Ensure that you are in a plugin directory.", + package_json.display() + )); + } + + let entry = plugin_dir.join("src/index.ts"); + if !entry.is_file() { + return Err(format!("Required entrypoint missing: {}", entry.display())); + } + + Ok(()) +} + +fn create_publish_archive(plugin_dir: &Path) -> CommandResult> { + let required_files = [ + "README.md", + "package.json", + "build/index.js", + "src/index.ts", + ]; + let optional_files = ["package-lock.json"]; + + let mut selected = HashSet::new(); + for required in required_files { + let required_path = plugin_dir.join(required); + if !required_path.is_file() { + return Err(format!("Missing required file: {required}")); + } + selected.insert(required.to_string()); + } + for optional in optional_files { + selected.insert(optional.to_string()); + } + + let cursor = std::io::Cursor::new(Vec::new()); + let mut zip = zip::ZipWriter::new(cursor); + let options = SimpleFileOptions::default().compression_method(CompressionMethod::Deflated); + + for entry in WalkDir::new(plugin_dir) { + let entry = entry.map_err(|e| format!("Failed walking plugin directory: {e}"))?; + if !entry.file_type().is_file() { + continue; + } + + let path = entry.path(); + let rel = path + .strip_prefix(plugin_dir) + .map_err(|e| format!("Failed deriving relative path for {}: {e}", path.display()))?; + let rel = rel.to_string_lossy().replace('\\', "/"); + + let keep = rel.starts_with("src/") || rel.starts_with("build/") || selected.contains(&rel); + if !keep { + continue; + } + + zip.start_file(rel, options).map_err(|e| format!("Failed adding file to archive: {e}"))?; + let mut file = fs::File::open(path) + .map_err(|e| format!("Failed opening file {}: {e}", path.display()))?; + let mut contents = Vec::new(); + file.read_to_end(&mut contents) + .map_err(|e| format!("Failed reading file {}: {e}", path.display()))?; + zip.write_all(&contents).map_err(|e| format!("Failed writing archive contents: {e}"))?; + } + + let cursor = zip.finish().map_err(|e| format!("Failed finalizing plugin archive: {e}"))?; + Ok(cursor.into_inner()) +} + +fn write_file(path: &Path, contents: &str) -> CommandResult { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent) + .map_err(|e| format!("Failed creating directory {}: {e}", parent.display()))?; + } + fs::write(path, contents).map_err(|e| format!("Failed writing file {}: {e}", path.display())) +} + +fn prompt_with_default(label: &str, default: &str) -> CommandResult { + if !io::stdin().is_terminal() { + return Ok(default.to_string()); + } + + print!("{label} [{default}]: "); + io::stdout().flush().map_err(|e| format!("Failed to flush stdout: {e}"))?; + + let mut input = String::new(); + io::stdin().read_line(&mut input).map_err(|e| format!("Failed to read input: {e}"))?; + let trimmed = input.trim(); + + if trimmed.is_empty() { Ok(default.to_string()) } else { Ok(trimmed.to_string()) } +} + +fn current_environment() -> Environment { + match std::env::var("ENVIRONMENT").as_deref() { + Ok("staging") => Environment::Staging, + Ok("development") => Environment::Development, + _ => Environment::Production, + } +} + +fn keyring_entry(environment: Environment) -> CommandResult { + Entry::new(environment.keyring_service(), KEYRING_USER) + .map_err(|e| format!("Failed to initialize auth keyring entry: {e}")) +} + +fn get_auth_token(environment: Environment) -> CommandResult> { + let entry = keyring_entry(environment)?; + match entry.get_password() { + Ok(token) => Ok(Some(token)), + Err(keyring::Error::NoEntry) => Ok(None), + Err(err) => Err(format!("Failed to read auth token: {err}")), + } +} + +fn parse_api_error(status: u16, body: &str) -> String { + if let Ok(value) = serde_json::from_str::(body) { + if let Some(message) = value.get("message").and_then(Value::as_str) { + return message.to_string(); + } + if let Some(error) = value.get("error").and_then(Value::as_str) { + return error.to_string(); + } + } + + format!("API error {status}: {body}") +} + +fn user_agent() -> String { + format!("YaakCli/{} ({})", env!("CARGO_PKG_VERSION"), ua_platform()) +} + +fn ua_platform() -> &'static str { + match std::env::consts::OS { + "windows" => "Win", + "darwin" => "Mac", + "linux" => "Linux", + _ => "Unknown", + } +} + +fn random_name() -> String { + const ADJECTIVES: &[&str] = &[ + "young", "youthful", "yellow", "yielding", "yappy", "yawning", "yummy", "yucky", "yearly", + "yester", "yeasty", "yelling", + ]; + const NOUNS: &[&str] = &[ + "yak", "yarn", "year", "yell", "yoke", "yoga", "yam", "yacht", "yodel", + ]; + + let mut rng = rand::thread_rng(); + let adjective = ADJECTIVES[rng.gen_range(0..ADJECTIVES.len())]; + let noun = NOUNS[rng.gen_range(0..NOUNS.len())]; + format!("{adjective}-{noun}") +} + +const TEMPLATE_GITIGNORE: &str = "node_modules\n"; + +const TEMPLATE_PACKAGE_JSON: &str = r#"{ + "name": "yaak-plugin-name", + "private": true, + "version": "0.0.1", + "scripts": { + "build": "yaak plugin build", + "dev": "yaak plugin dev" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "typescript": "^5.9.3", + "vitest": "^4.0.14" + }, + "dependencies": { + "@yaakapp/api": "^0.7.0" + } +} +"#; + +const TEMPLATE_TSCONFIG: &str = r#"{ + "compilerOptions": { + "target": "es2021", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "useDefineForClassFields": true, + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"] +} +"#; + +const TEMPLATE_README: &str = r#"# yaak-plugin-name + +Describe what your plugin does. +"#; + +const TEMPLATE_INDEX_TS: &str = r#"import type { PluginDefinition } from "@yaakapp/api"; + +export const plugin: PluginDefinition = { + httpRequestActions: [ + { + label: "Hello, From Plugin", + icon: "info", + async onSelect(ctx, args) { + await ctx.toast.show({ + color: "success", + message: `You clicked the request ${args.httpRequest.id}`, + }); + }, + }, + ], +}; +"#; + +const TEMPLATE_INDEX_TEST_TS: &str = r#"import { describe, expect, test } from "vitest"; +import { plugin } from "./index"; + +describe("Example Plugin", () => { + test("Exports plugin object", () => { + expect(plugin).toBeTypeOf("object"); + }); +}); +"#; + +#[cfg(test)] +mod tests { + use super::create_publish_archive; + use std::collections::HashSet; + use std::fs; + use std::io::Cursor; + use tempfile::TempDir; + use zip::ZipArchive; + + #[test] + fn publish_archive_includes_required_and_optional_files() { + let dir = TempDir::new().expect("temp dir"); + let root = dir.path(); + + fs::create_dir_all(root.join("src")).expect("create src"); + fs::create_dir_all(root.join("build")).expect("create build"); + fs::create_dir_all(root.join("ignored")).expect("create ignored"); + + fs::write(root.join("README.md"), "# Demo\n").expect("write README"); + fs::write(root.join("package.json"), "{}").expect("write package.json"); + fs::write(root.join("package-lock.json"), "{}").expect("write package-lock.json"); + fs::write(root.join("src/index.ts"), "export const plugin = {};\n") + .expect("write src/index.ts"); + fs::write(root.join("build/index.js"), "exports.plugin = {};\n") + .expect("write build/index.js"); + fs::write(root.join("ignored/secret.txt"), "do-not-ship").expect("write ignored file"); + + let archive = create_publish_archive(root).expect("create archive"); + let mut zip = ZipArchive::new(Cursor::new(archive)).expect("open zip"); + + let mut names = HashSet::new(); + for i in 0..zip.len() { + let file = zip.by_index(i).expect("zip entry"); + names.insert(file.name().to_string()); + } + + assert!(names.contains("README.md")); + assert!(names.contains("package.json")); + assert!(names.contains("package-lock.json")); + assert!(names.contains("src/index.ts")); + assert!(names.contains("build/index.js")); + assert!(!names.contains("ignored/secret.txt")); + } +} diff --git a/crates-cli/yaak-cli/src/main.rs b/crates-cli/yaak-cli/src/main.rs index 58b83bb1..afafc069 100644 --- a/crates-cli/yaak-cli/src/main.rs +++ b/crates-cli/yaak-cli/src/main.rs @@ -2,6 +2,7 @@ mod cli; mod commands; mod context; mod plugin_events; +mod ui; mod utils; use clap::Parser; @@ -22,6 +23,15 @@ async fn main() { dirs::data_dir().expect("Could not determine data directory").join(app_id) }); + let needs_context = matches!( + &command, + Commands::Send(_) + | Commands::Workspace(_) + | Commands::Request(_) + | Commands::Folder(_) + | Commands::Environment(_) + ); + let needs_plugins = matches!( &command, Commands::Send(_) @@ -30,21 +40,51 @@ async fn main() { }) ); - let context = CliContext::initialize(data_dir, app_id, needs_plugins).await; - - let exit_code = match command { - Commands::Send(args) => { - commands::send::run(&context, args, environment.as_deref(), verbose).await - } - Commands::Workspace(args) => commands::workspace::run(&context, args), - Commands::Request(args) => { - commands::request::run(&context, args, environment.as_deref(), verbose).await - } - Commands::Folder(args) => commands::folder::run(&context, args), - Commands::Environment(args) => commands::environment::run(&context, args), + let context = if needs_context { + Some(CliContext::initialize(data_dir, app_id, needs_plugins).await) + } else { + None }; - context.shutdown().await; + let exit_code = match command { + Commands::Auth(args) => commands::auth::run(args).await, + Commands::Plugin(args) => commands::plugin::run(args).await, + Commands::Build(args) => commands::plugin::run_build(args).await, + Commands::Dev(args) => commands::plugin::run_dev(args).await, + Commands::Send(args) => { + commands::send::run( + context.as_ref().expect("context initialized for send"), + args, + environment.as_deref(), + verbose, + ) + .await + } + Commands::Workspace(args) => commands::workspace::run( + context.as_ref().expect("context initialized for workspace"), + args, + ), + Commands::Request(args) => { + commands::request::run( + context.as_ref().expect("context initialized for request"), + args, + environment.as_deref(), + verbose, + ) + .await + } + Commands::Folder(args) => { + commands::folder::run(context.as_ref().expect("context initialized for folder"), args) + } + Commands::Environment(args) => commands::environment::run( + context.as_ref().expect("context initialized for environment"), + args, + ), + }; + + if let Some(context) = &context { + context.shutdown().await; + } if exit_code != 0 { std::process::exit(exit_code); diff --git a/crates-cli/yaak-cli/src/ui.rs b/crates-cli/yaak-cli/src/ui.rs new file mode 100644 index 00000000..18a48bb1 --- /dev/null +++ b/crates-cli/yaak-cli/src/ui.rs @@ -0,0 +1,34 @@ +use console::style; +use std::io::{self, IsTerminal}; + +pub fn info(message: &str) { + if io::stdout().is_terminal() { + println!("{:<8} {}", style("INFO").cyan().bold(), style(message).cyan()); + } else { + println!("INFO {message}"); + } +} + +pub fn warning(message: &str) { + if io::stdout().is_terminal() { + println!("{:<8} {}", style("WARNING").yellow().bold(), style(message).yellow()); + } else { + println!("WARNING {message}"); + } +} + +pub fn success(message: &str) { + if io::stdout().is_terminal() { + println!("{:<8} {}", style("SUCCESS").green().bold(), style(message).green()); + } else { + println!("SUCCESS {message}"); + } +} + +pub fn error(message: &str) { + if io::stderr().is_terminal() { + eprintln!("{:<8} {}", style("ERROR").red().bold(), style(message).red()); + } else { + eprintln!("Error: {message}"); + } +} diff --git a/crates-cli/yaak-cli/tests/common/mod.rs b/crates-cli/yaak-cli/tests/common/mod.rs index 9b61c850..2a874939 100644 --- a/crates-cli/yaak-cli/tests/common/mod.rs +++ b/crates-cli/yaak-cli/tests/common/mod.rs @@ -10,7 +10,7 @@ use yaak_models::query_manager::QueryManager; use yaak_models::util::UpdateSource; pub fn cli_cmd(data_dir: &Path) -> Command { - let mut cmd = cargo_bin_cmd!("yaakcli"); + let mut cmd = cargo_bin_cmd!("yaak"); cmd.arg("--data-dir").arg(data_dir); cmd } diff --git a/crates/yaak-grpc/src/any.rs b/crates/yaak-grpc/src/any.rs index ed41fbfd..54b523b6 100644 --- a/crates/yaak-grpc/src/any.rs +++ b/crates/yaak-grpc/src/any.rs @@ -55,6 +55,7 @@ mod tests { let mut out = Vec::new(); super::collect_any_types(json, &mut out); + out.sort(); assert_eq!(out, vec!["foo.bar", "mount_source.MountSourceRBDVolume"]); } } diff --git a/npm/README.md b/npm/README.md new file mode 100644 index 00000000..e605cd9f --- /dev/null +++ b/npm/README.md @@ -0,0 +1,7 @@ +# Yaak CLI NPM Packages + +The Rust `yaak` CLI binary is published to NPM with a meta package (`@yaakapp/cli`) and +platform-specific optional dependency packages. The package exposes both `yaak` and `yaakcli` +commands for compatibility. + +This follows the same strategy previously used in the standalone `yaak-cli` repo. diff --git a/npm/cli-darwin-arm64/bin/.gitkeep b/npm/cli-darwin-arm64/bin/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/npm/cli-darwin-arm64/package.json b/npm/cli-darwin-arm64/package.json new file mode 100644 index 00000000..e8ca680b --- /dev/null +++ b/npm/cli-darwin-arm64/package.json @@ -0,0 +1,10 @@ +{ + "name": "@yaakapp/cli-darwin-arm64", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git+https://github.com/mountain-loop/yaak.git" + }, + "os": ["darwin"], + "cpu": ["arm64"] +} diff --git a/npm/cli-darwin-x64/bin/.gitkeep b/npm/cli-darwin-x64/bin/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/npm/cli-darwin-x64/package.json b/npm/cli-darwin-x64/package.json new file mode 100644 index 00000000..a7e34777 --- /dev/null +++ b/npm/cli-darwin-x64/package.json @@ -0,0 +1,10 @@ +{ + "name": "@yaakapp/cli-darwin-x64", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git+https://github.com/mountain-loop/yaak.git" + }, + "os": ["darwin"], + "cpu": ["x64"] +} diff --git a/npm/cli-linux-arm64/bin/.gitkeep b/npm/cli-linux-arm64/bin/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/npm/cli-linux-arm64/package.json b/npm/cli-linux-arm64/package.json new file mode 100644 index 00000000..21ed08a8 --- /dev/null +++ b/npm/cli-linux-arm64/package.json @@ -0,0 +1,10 @@ +{ + "name": "@yaakapp/cli-linux-arm64", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git+https://github.com/mountain-loop/yaak.git" + }, + "os": ["linux"], + "cpu": ["arm64"] +} diff --git a/npm/cli-linux-x64/bin/.gitkeep b/npm/cli-linux-x64/bin/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/npm/cli-linux-x64/package.json b/npm/cli-linux-x64/package.json new file mode 100644 index 00000000..72b7df70 --- /dev/null +++ b/npm/cli-linux-x64/package.json @@ -0,0 +1,10 @@ +{ + "name": "@yaakapp/cli-linux-x64", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git+https://github.com/mountain-loop/yaak.git" + }, + "os": ["linux"], + "cpu": ["x64"] +} diff --git a/npm/cli-win32-arm64/bin/.gitkeep b/npm/cli-win32-arm64/bin/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/npm/cli-win32-arm64/package.json b/npm/cli-win32-arm64/package.json new file mode 100644 index 00000000..fdfe1451 --- /dev/null +++ b/npm/cli-win32-arm64/package.json @@ -0,0 +1,10 @@ +{ + "name": "@yaakapp/cli-win32-arm64", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git+https://github.com/mountain-loop/yaak.git" + }, + "os": ["win32"], + "cpu": ["arm64"] +} diff --git a/npm/cli-win32-x64/bin/.gitkeep b/npm/cli-win32-x64/bin/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/npm/cli-win32-x64/package.json b/npm/cli-win32-x64/package.json new file mode 100644 index 00000000..5f615e82 --- /dev/null +++ b/npm/cli-win32-x64/package.json @@ -0,0 +1,10 @@ +{ + "name": "@yaakapp/cli-win32-x64", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git+https://github.com/mountain-loop/yaak.git" + }, + "os": ["win32"], + "cpu": ["x64"] +} diff --git a/npm/cli/.gitignore b/npm/cli/.gitignore new file mode 100644 index 00000000..c3ac0f65 --- /dev/null +++ b/npm/cli/.gitignore @@ -0,0 +1,2 @@ +yaak +yaak.exe diff --git a/npm/cli/bin/cli.js b/npm/cli/bin/cli.js new file mode 100755 index 00000000..e0fff298 --- /dev/null +++ b/npm/cli/bin/cli.js @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +const path = require("path"); +const childProcess = require("child_process"); +const { BINARY_NAME, PLATFORM_SPECIFIC_PACKAGE_NAME } = require("../common"); + +function getBinaryPath() { + try { + if (!PLATFORM_SPECIFIC_PACKAGE_NAME) { + throw new Error("unsupported platform"); + } + return require.resolve(`${PLATFORM_SPECIFIC_PACKAGE_NAME}/bin/${BINARY_NAME}`); + } catch (_) { + return path.join(__dirname, "..", BINARY_NAME); + } +} + +const result = childProcess.spawnSync(getBinaryPath(), process.argv.slice(2), { + stdio: "inherit" +}); + +if (result.error) { + throw result.error; +} + +if (result.signal) { + process.kill(process.pid, result.signal); +} + +process.exit(result.status ?? 1); diff --git a/npm/cli/common.js b/npm/cli/common.js new file mode 100644 index 00000000..21794886 --- /dev/null +++ b/npm/cli/common.js @@ -0,0 +1,20 @@ +const BINARY_DISTRIBUTION_PACKAGES = { + darwin_arm64: "@yaakapp/cli-darwin-arm64", + darwin_x64: "@yaakapp/cli-darwin-x64", + linux_arm64: "@yaakapp/cli-linux-arm64", + linux_x64: "@yaakapp/cli-linux-x64", + win32_x64: "@yaakapp/cli-win32-x64", + win32_arm64: "@yaakapp/cli-win32-arm64" +}; + +const BINARY_DISTRIBUTION_VERSION = require("./package.json").version; +const BINARY_NAME = process.platform === "win32" ? "yaak.exe" : "yaak"; +const PLATFORM_SPECIFIC_PACKAGE_NAME = + BINARY_DISTRIBUTION_PACKAGES[`${process.platform}_${process.arch}`]; + +module.exports = { + BINARY_DISTRIBUTION_PACKAGES, + BINARY_DISTRIBUTION_VERSION, + BINARY_NAME, + PLATFORM_SPECIFIC_PACKAGE_NAME +}; diff --git a/npm/cli/index.js b/npm/cli/index.js new file mode 100644 index 00000000..888c76ca --- /dev/null +++ b/npm/cli/index.js @@ -0,0 +1,20 @@ +const path = require("path"); +const childProcess = require("child_process"); +const { PLATFORM_SPECIFIC_PACKAGE_NAME, BINARY_NAME } = require("./common"); + +function getBinaryPath() { + try { + if (!PLATFORM_SPECIFIC_PACKAGE_NAME) { + throw new Error("unsupported platform"); + } + return require.resolve(`${PLATFORM_SPECIFIC_PACKAGE_NAME}/bin/${BINARY_NAME}`); + } catch (_) { + return path.join(__dirname, BINARY_NAME); + } +} + +module.exports.runBinary = function runBinary(...args) { + childProcess.execFileSync(getBinaryPath(), args, { + stdio: "inherit" + }); +}; diff --git a/npm/cli/install.js b/npm/cli/install.js new file mode 100644 index 00000000..ef8f1b59 --- /dev/null +++ b/npm/cli/install.js @@ -0,0 +1,97 @@ +const fs = require("node:fs"); +const path = require("node:path"); +const zlib = require("node:zlib"); +const https = require("node:https"); +const { + BINARY_DISTRIBUTION_VERSION, + BINARY_NAME, + PLATFORM_SPECIFIC_PACKAGE_NAME +} = require("./common"); + +const fallbackBinaryPath = path.join(__dirname, BINARY_NAME); + +function makeRequest(url) { + return new Promise((resolve, reject) => { + https + .get(url, (response) => { + if (response.statusCode >= 200 && response.statusCode < 300) { + const chunks = []; + response.on("data", (chunk) => chunks.push(chunk)); + response.on("end", () => resolve(Buffer.concat(chunks))); + } else if ( + response.statusCode >= 300 && + response.statusCode < 400 && + response.headers.location + ) { + makeRequest(response.headers.location).then(resolve, reject); + } else { + reject( + new Error( + `npm responded with status code ${response.statusCode} when downloading package ${url}` + ) + ); + } + }) + .on("error", (error) => reject(error)); + }); +} + +function extractFileFromTarball(tarballBuffer, filepath) { + let offset = 0; + while (offset < tarballBuffer.length) { + const header = tarballBuffer.subarray(offset, offset + 512); + offset += 512; + + const fileName = header.toString("utf-8", 0, 100).replace(/\0.*/g, ""); + const fileSize = parseInt(header.toString("utf-8", 124, 136).replace(/\0.*/g, ""), 8); + + if (fileName === filepath) { + return tarballBuffer.subarray(offset, offset + fileSize); + } + + offset = (offset + fileSize + 511) & ~511; + } + + return null; +} + +async function downloadBinaryFromNpm() { + if (!PLATFORM_SPECIFIC_PACKAGE_NAME) { + throw new Error(`Unsupported platform: ${process.platform}/${process.arch}`); + } + + const packageNameWithoutScope = PLATFORM_SPECIFIC_PACKAGE_NAME.split("/")[1]; + const tarballUrl = `https://registry.npmjs.org/${PLATFORM_SPECIFIC_PACKAGE_NAME}/-/${packageNameWithoutScope}-${BINARY_DISTRIBUTION_VERSION}.tgz`; + const tarballDownloadBuffer = await makeRequest(tarballUrl); + const tarballBuffer = zlib.unzipSync(tarballDownloadBuffer); + + const binary = extractFileFromTarball(tarballBuffer, `package/bin/${BINARY_NAME}`); + if (!binary) { + throw new Error(`Could not find package/bin/${BINARY_NAME} in tarball`); + } + + fs.writeFileSync(fallbackBinaryPath, binary); + fs.chmodSync(fallbackBinaryPath, "755"); +} + +function isPlatformSpecificPackageInstalled() { + try { + if (!PLATFORM_SPECIFIC_PACKAGE_NAME) { + return false; + } + require.resolve(`${PLATFORM_SPECIFIC_PACKAGE_NAME}/bin/${BINARY_NAME}`); + return true; + } catch (_) { + return false; + } +} + +if (!isPlatformSpecificPackageInstalled()) { + console.log("Platform package missing. Downloading Yaak CLI binary from npm..."); + downloadBinaryFromNpm().catch((err) => { + console.error("Failed to install Yaak CLI binary:", err); + process.exitCode = 1; + }); +} else { + console.log("Platform package present. Using bundled Yaak CLI binary."); +} diff --git a/npm/cli/package.json b/npm/cli/package.json new file mode 100644 index 00000000..f000e55b --- /dev/null +++ b/npm/cli/package.json @@ -0,0 +1,25 @@ +{ + "name": "@yaakapp/cli", + "version": "0.0.1", + "main": "./index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/mountain-loop/yaak.git" + }, + "scripts": { + "postinstall": "node ./install.js", + "prepublishOnly": "node ./prepublish.js" + }, + "bin": { + "yaak": "bin/cli.js", + "yaakcli": "bin/cli.js" + }, + "optionalDependencies": { + "@yaakapp/cli-darwin-x64": "0.0.1", + "@yaakapp/cli-darwin-arm64": "0.0.1", + "@yaakapp/cli-linux-arm64": "0.0.1", + "@yaakapp/cli-linux-x64": "0.0.1", + "@yaakapp/cli-win32-x64": "0.0.1", + "@yaakapp/cli-win32-arm64": "0.0.1" + } +} diff --git a/npm/cli/prepublish.js b/npm/cli/prepublish.js new file mode 100644 index 00000000..cddc3052 --- /dev/null +++ b/npm/cli/prepublish.js @@ -0,0 +1,5 @@ +const fs = require("node:fs"); +const path = require("node:path"); + +const readme = path.join(__dirname, "..", "..", "README.md"); +fs.copyFileSync(readme, path.join(__dirname, "README.md")); diff --git a/npm/prepare-publish.js b/npm/prepare-publish.js new file mode 100644 index 00000000..e5759e9e --- /dev/null +++ b/npm/prepare-publish.js @@ -0,0 +1,74 @@ +const { copyFileSync, existsSync, readFileSync, writeFileSync } = require("node:fs"); +const { join } = require("node:path"); + +const version = process.env.YAAK_CLI_VERSION?.replace(/^v/, ""); +if (!version) { + console.error("YAAK_CLI_VERSION is not set"); + process.exit(1); +} + +const packages = [ + "cli", + "cli-darwin-arm64", + "cli-darwin-x64", + "cli-linux-arm64", + "cli-linux-x64", + "cli-win32-arm64", + "cli-win32-x64" +]; + +const binaries = [ + { + src: join(__dirname, "dist", "cli-darwin-arm64", "yaak"), + dest: join(__dirname, "cli-darwin-arm64", "bin", "yaak") + }, + { + src: join(__dirname, "dist", "cli-darwin-x64", "yaak"), + dest: join(__dirname, "cli-darwin-x64", "bin", "yaak") + }, + { + src: join(__dirname, "dist", "cli-linux-arm64", "yaak"), + dest: join(__dirname, "cli-linux-arm64", "bin", "yaak") + }, + { + src: join(__dirname, "dist", "cli-linux-x64", "yaak"), + dest: join(__dirname, "cli-linux-x64", "bin", "yaak") + }, + { + src: join(__dirname, "dist", "cli-win32-arm64", "yaak.exe"), + dest: join(__dirname, "cli-win32-arm64", "bin", "yaak.exe") + }, + { + src: join(__dirname, "dist", "cli-win32-x64", "yaak.exe"), + dest: join(__dirname, "cli-win32-x64", "bin", "yaak.exe") + } +]; + +for (const { src, dest } of binaries) { + if (!existsSync(src)) { + console.error(`Missing binary artifact: ${src}`); + process.exit(1); + } + copyFileSync(src, dest); +} + +for (const pkg of packages) { + const filepath = join(__dirname, pkg, "package.json"); + const json = JSON.parse(readFileSync(filepath, "utf-8")); + json.version = version; + + if (json.name === "@yaakapp/cli") { + json.optionalDependencies = { + "@yaakapp/cli-darwin-x64": version, + "@yaakapp/cli-darwin-arm64": version, + "@yaakapp/cli-linux-arm64": version, + "@yaakapp/cli-linux-x64": version, + "@yaakapp/cli-win32-x64": version, + "@yaakapp/cli-win32-arm64": version + }; + } + + writeFileSync(filepath, `${JSON.stringify(json, null, 2)}\n`); +} + +console.log(`Prepared @yaakapp/cli npm packages for ${version}`);