diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a823829419..4e7a8d7618 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,9 +1,8 @@ FROM mcr.microsoft.com/vscode/devcontainers/rust:1-bullseye -# Install cmake and protobuf-compiler +# Install cmake RUN apt-get update \ && apt-get install -y cmake \ - && apt-get install -y protobuf-compiler \ && rm -rf /var/lib/apt/lists/* # Install Deno diff --git a/.github/workflows/cargo_publish.yml b/.github/workflows/cargo_publish.yml index 45f075b579..f77286c7d4 100644 --- a/.github/workflows/cargo_publish.yml +++ b/.github/workflows/cargo_publish.yml @@ -32,12 +32,6 @@ jobs: with: deno-version: v1.x - - name: Install protoc - uses: arduino/setup-protoc@v3 - with: - version: '21.12' - repo-token: '${{ secrets.GITHUB_TOKEN }}' - - name: Publish env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index f1fa7edf65..c03f10bc92 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -5,7 +5,7 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify"; // Bump this number when you want to purge the cache. // Note: the tools/release/01_bump_crate_versions.ts script will update this version // automatically via regex, so ensure that this line maintains this format. -const cacheVersion = 16; +const cacheVersion = 18; const ubuntuX86Runner = "ubuntu-22.04"; const ubuntuX86XlRunner = "ubuntu-22.04-xl"; @@ -191,11 +191,6 @@ const installNodeStep = { uses: "actions/setup-node@v4", with: { "node-version": 18 }, }; -const installProtocStep = { - name: "Install protoc", - uses: "arduino/setup-protoc@v3", - with: { "version": "21.12", "repo-token": "${{ secrets.GITHUB_TOKEN }}" }, -}; const installDenoStep = { name: "Install Deno", uses: "denoland/setup-deno@v1", @@ -494,7 +489,6 @@ const ci = { if: "matrix.job == 'bench' || matrix.job == 'test'", ...installNodeStep, }, - installProtocStep, { if: [ "matrix.profile == 'release' &&", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 728d1deb21..701d67f93c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -199,12 +199,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: 18 - - name: Install protoc - uses: arduino/setup-protoc@v3 - with: - version: '21.12' - repo-token: '${{ secrets.GITHUB_TOKEN }}' - if: '!(matrix.skip)' - if: |- !(matrix.skip) && (matrix.profile == 'release' && matrix.job == 'test' && @@ -367,8 +361,8 @@ jobs: path: |- ~/.cargo/registry/index ~/.cargo/registry/cache - key: '16-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '16-cargo-home-${{ matrix.os }}-${{ matrix.arch }}' + key: '18-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '18-cargo-home-${{ matrix.os }}-${{ matrix.arch }}' if: '!(matrix.skip)' - name: Restore cache build output (PR) uses: actions/cache/restore@v4 @@ -381,7 +375,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '16-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '18-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' - name: Apply and update mtime cache if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))' uses: ./.github/mtime_cache @@ -691,7 +685,7 @@ jobs: !./target/*/*.zip !./target/*/*.sha256sum !./target/*/*.tar.gz - key: '16-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '18-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-22.04 diff --git a/Cargo.lock b/Cargo.lock index 0ab2d96eaa..c559611cd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,19 +128,6 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" -[[package]] -name = "ammonia" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e6d1c7838db705c9b756557ee27c384ce695a1c51a6fe528784cb1c6840170" -dependencies = [ - "html5ever", - "maplit", - "once_cell", - "tendril", - "url", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -471,6 +458,26 @@ dependencies = [ "which 4.4.2", ] +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease 0.2.17", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.72", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -631,12 +638,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.10" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" +checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -688,7 +696,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.8.3", + "libloading 0.8.5", ] [[package]] @@ -840,9 +848,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "comrak" -version = "0.26.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ab67843c57df5a4ee29d610740828dbc928cc64ecf0f2a1d5cd0e98e107a9" +checksum = "c93ab3577cca16b4a1d80a88c2e0cd8b6e969e51696f0bbb0d1dcb0157109832" dependencies = [ "caseless", "derive_builder", @@ -1040,7 +1048,7 @@ dependencies = [ "cpufeatures", "curve25519-dalek-derive", "digest", - "fiat-crypto", + "fiat-crypto 0.2.7", "rustc_version 0.4.0", "subtle", "zeroize", @@ -1064,7 +1072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" dependencies = [ "bitflags 2.6.0", - "libloading 0.8.3", + "libloading 0.8.5", "winapi", ] @@ -1146,7 +1154,7 @@ dependencies = [ [[package]] name = "deno" -version = "2.0.0-rc.9" +version = "2.0.0" dependencies = [ "anstream", "async-trait", @@ -1205,6 +1213,7 @@ dependencies = [ "lazy-regex", "libc", "libsui", + "libuv-sys-lite", "libz-sys", "log", "lsp-types", @@ -1221,7 +1230,7 @@ dependencies = [ "p256", "pathdiff", "percent-encoding", - "phf 0.11.2", + "phf", "pretty_assertions", "pretty_yaml", "quick-junit", @@ -1253,6 +1262,7 @@ dependencies = [ "walkdir", "which 4.4.2", "winapi", + "windows-sys 0.52.0", "winres", "yoke", "zeromq", @@ -1273,9 +1283,9 @@ dependencies = [ [[package]] name = "deno_ast" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ea2fd038c9c7e3e87e624fd708303cd33f39c33707f6c48fa9a65d65fefc47" +checksum = "b2b9d03b1bbeeecdac54367f075d572131736d06c5be3bc49037855bc5ab1bbb" dependencies = [ "base64 0.21.7", "deno_media_type", @@ -1317,7 +1327,7 @@ dependencies = [ [[package]] name = "deno_bench_util" -version = "0.163.0" +version = "0.165.0" dependencies = [ "bencher", "deno_core", @@ -1326,7 +1336,7 @@ dependencies = [ [[package]] name = "deno_broadcast_channel" -version = "0.163.0" +version = "0.165.0" dependencies = [ "async-trait", "deno_core", @@ -1336,7 +1346,7 @@ dependencies = [ [[package]] name = "deno_cache" -version = "0.101.0" +version = "0.103.0" dependencies = [ "async-trait", "deno_core", @@ -1368,7 +1378,7 @@ dependencies = [ [[package]] name = "deno_canvas" -version = "0.38.0" +version = "0.40.0" dependencies = [ "deno_core", "deno_webgpu", @@ -1378,12 +1388,13 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.35.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "105864a9e0a7fbc22f1106784b2d263f402f157be1c3e1a9905f53d182700c9f" +checksum = "3cb7a1723676fba5964f8d7441d8b53748f9e74d6d4241be7de9730da021859a" dependencies = [ "anyhow", "deno_package_json", + "deno_path_util", "deno_semver", "glob", "ignore", @@ -1392,7 +1403,7 @@ dependencies = [ "jsonc-parser", "log", "percent-encoding", - "phf 0.11.2", + "phf", "serde", "serde_json", "thiserror", @@ -1401,7 +1412,7 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.169.0" +version = "0.171.0" dependencies = [ "deno_core", ] @@ -1446,7 +1457,7 @@ checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1" [[package]] name = "deno_cron" -version = "0.49.0" +version = "0.51.0" dependencies = [ "anyhow", "async-trait", @@ -1458,7 +1469,7 @@ dependencies = [ [[package]] name = "deno_crypto" -version = "0.183.0" +version = "0.185.0" dependencies = [ "aes", "aes-gcm", @@ -1470,6 +1481,7 @@ dependencies = [ "curve25519-dalek", "deno_core", "deno_web", + "ed448-goldilocks", "elliptic-curve", "num-traits", "once_cell", @@ -1492,11 +1504,10 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.150.1" +version = "0.152.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0841188bc852535b76e53be6c3d13c61cfc6751a731969b8959fe31fa696c73f" +checksum = "a8d237256ad99d6064b271324485120028e843329fd0fa0e93175d5e98f17033" dependencies = [ - "ammonia", "anyhow", "cfg-if", "comrak", @@ -1512,13 +1523,23 @@ dependencies = [ "regex", "serde", "serde_json", - "syntect", "termcolor", + "tree-sitter-bash", + "tree-sitter-css", + "tree-sitter-highlight", + "tree-sitter-html", + "tree-sitter-javascript", + "tree-sitter-json", + "tree-sitter-md", + "tree-sitter-regex", + "tree-sitter-rust", + "tree-sitter-typescript", + "tree-sitter-xml", ] [[package]] name = "deno_fetch" -version = "0.193.0" +version = "0.195.0" dependencies = [ "base64 0.21.7", "bytes", @@ -1550,7 +1571,7 @@ dependencies = [ [[package]] name = "deno_ffi" -version = "0.156.0" +version = "0.158.0" dependencies = [ "deno_core", "deno_permissions", @@ -1567,7 +1588,7 @@ dependencies = [ [[package]] name = "deno_fs" -version = "0.79.0" +version = "0.81.0" dependencies = [ "async-trait", "base32", @@ -1588,9 +1609,9 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.83.0" +version = "0.83.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20088a4497b1a212482883dc7b0365e99f703d575fb512d4a793531cdc92ea76" +checksum = "77163c46755676d8f793fc19e365537ba660a8db173cd1e02d21eb010c0b3cef" dependencies = [ "anyhow", "async-trait", @@ -1617,7 +1638,7 @@ dependencies = [ [[package]] name = "deno_http" -version = "0.167.0" +version = "0.169.0" dependencies = [ "async-compression", "async-trait", @@ -1642,7 +1663,7 @@ dependencies = [ "mime", "once_cell", "percent-encoding", - "phf 0.11.2", + "phf", "pin-project", "rand", "ring", @@ -1656,7 +1677,7 @@ dependencies = [ [[package]] name = "deno_io" -version = "0.79.0" +version = "0.81.0" dependencies = [ "async-trait", "deno_core", @@ -1677,7 +1698,7 @@ dependencies = [ [[package]] name = "deno_kv" -version = "0.77.0" +version = "0.79.0" dependencies = [ "anyhow", "async-trait", @@ -1717,7 +1738,7 @@ dependencies = [ "if_chain", "log", "once_cell", - "phf 0.11.2", + "phf", "regex", "serde", "serde_json", @@ -1748,7 +1769,7 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.100.0" +version = "0.102.0" dependencies = [ "deno_core", "deno_permissions", @@ -1770,7 +1791,7 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.161.0" +version = "0.163.0" dependencies = [ "deno_core", "deno_permissions", @@ -1786,7 +1807,7 @@ dependencies = [ [[package]] name = "deno_node" -version = "0.106.0" +version = "0.108.0" dependencies = [ "aead-gcm-stream", "aes", @@ -1876,9 +1897,9 @@ dependencies = [ [[package]] name = "deno_npm" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1809e2d77d8a06bc2800dc10c1d4acb664197e518e289a86e336411c1feba785" +checksum = "8050bcc2513046cbc0134ae1bc0f3b251a58b95012f3b81e0ea09a7f069c301b" dependencies = [ "anyhow", "async-trait", @@ -1910,9 +1931,9 @@ dependencies = [ [[package]] name = "deno_package_json" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38cf6ea5cc98ea7ad58b0e84593773bea03fc0431071a296017bed4151e3dc1d" +checksum = "6cbc4c4d3eb0960b58e8f43f9fc2d3f620fcac9a03cd85203e08db5b04e83c1f" dependencies = [ "deno_semver", "indexmap", @@ -1935,7 +1956,7 @@ dependencies = [ [[package]] name = "deno_permissions" -version = "0.29.0" +version = "0.31.0" dependencies = [ "deno_core", "deno_path_util", @@ -1952,7 +1973,7 @@ dependencies = [ [[package]] name = "deno_resolver" -version = "0.1.0" +version = "0.3.0" dependencies = [ "anyhow", "base32", @@ -1962,12 +1983,13 @@ dependencies = [ "deno_semver", "node_resolver", "test_server", + "thiserror", "url", ] [[package]] name = "deno_runtime" -version = "0.178.0" +version = "0.180.0" dependencies = [ "deno_ast", "deno_broadcast_channel", @@ -2045,9 +2067,9 @@ dependencies = [ [[package]] name = "deno_task_shell" -version = "0.17.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6413ffc1654cad015edb5c4ab574069acdc929a6efafed23bc947901bcff1a" +checksum = "4f444918f7102c1a5a143e9d57809e499fb4d365070519bf2e8bdb16d586af2a" dependencies = [ "anyhow", "futures", @@ -2082,7 +2104,7 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.156.0" +version = "0.158.0" dependencies = [ "deno_core", "deno_native_certs", @@ -2130,7 +2152,7 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.169.0" +version = "0.171.0" dependencies = [ "deno_bench_util", "deno_console", @@ -2141,7 +2163,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.200.0" +version = "0.202.0" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -2162,7 +2184,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.136.0" +version = "0.138.0" dependencies = [ "deno_core", "raw-window-handle", @@ -2174,7 +2196,7 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.169.0" +version = "0.171.0" dependencies = [ "deno_bench_util", "deno_core", @@ -2182,7 +2204,7 @@ dependencies = [ [[package]] name = "deno_websocket" -version = "0.174.0" +version = "0.176.0" dependencies = [ "bytes", "deno_core", @@ -2203,7 +2225,7 @@ dependencies = [ [[package]] name = "deno_webstorage" -version = "0.164.0" +version = "0.166.0" dependencies = [ "deno_core", "deno_web", @@ -2692,6 +2714,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed448-goldilocks" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06924531e9e90130842b012e447f85bdaf9161bc8a0f8092be8cb70b01ebe092" +dependencies = [ + "fiat-crypto 0.1.20", + "hex", + "subtle", + "zeroize", +] + [[package]] name = "editpe" version = "0.1.0" @@ -2954,6 +2988,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "fiat-crypto" version = "0.2.7" @@ -3137,16 +3177,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - [[package]] name = "futures" version = "0.3.30" @@ -3445,9 +3475,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "5.1.2" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b" +checksum = "ce25b617d1375ef96eeb920ae717e3da34a02fc979fe632c75128350f9e1f74a" dependencies = [ "heck 0.5.0", "log", @@ -3563,7 +3593,7 @@ dependencies = [ "hashbrown", "new_debug_unreachable", "once_cell", - "phf 0.11.2", + "phf", "rustc-hash 1.1.0", "triomphe", ] @@ -3577,20 +3607,6 @@ dependencies = [ "utf8-width", ] -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "http" version = "0.2.12" @@ -4038,7 +4054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.3", + "libloading 0.8.5", "pkg-config", ] @@ -4143,9 +4159,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", "windows-targets 0.52.4", @@ -4169,9 +4185,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.30.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +checksum = "6b694a822684ddb75df4d657029161431bcb4a85c1856952f845b76912bc6fec" dependencies = [ "cc", "pkg-config", @@ -4192,10 +4208,20 @@ dependencies = [ ] [[package]] -name = "libz-sys" -version = "1.1.16" +name = "libuv-sys-lite" +version = "1.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "ca8dfd1a173826d193e3b955e07c22765829890f62c677a59c4a410cb4f47c01" +dependencies = [ + "bindgen 0.70.1", + "libloading 0.8.5", +] + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "cmake", @@ -4263,12 +4289,6 @@ dependencies = [ "serde_repr", ] -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - [[package]] name = "malloc_buf" version = "0.0.6" @@ -4291,26 +4311,6 @@ dependencies = [ "tiny_pretty", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen", - "string_cache", - "string_cache_codegen", - "tendril", -] - [[package]] name = "markup_fmt" version = "0.13.1" @@ -4488,7 +4488,7 @@ dependencies = [ [[package]] name = "napi_sym" -version = "0.99.0" +version = "0.101.0" dependencies = [ "quote", "serde", @@ -4557,7 +4557,7 @@ dependencies = [ [[package]] name = "node_resolver" -version = "0.8.0" +version = "0.10.0" dependencies = [ "anyhow", "async-trait", @@ -4733,28 +4733,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "onig" -version = "6.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" -dependencies = [ - "bitflags 1.3.2", - "libc", - "once_cell", - "onig_sys", -] - -[[package]] -name = "onig_sys" -version = "69.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "opaque-debug" version = "0.3.1" @@ -5006,15 +4984,6 @@ dependencies = [ "indexmap", ] -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - [[package]] name = "phf" version = "0.11.2" @@ -5022,27 +4991,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ "phf_macros", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand", + "phf_shared", ] [[package]] @@ -5051,7 +5000,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "phf_shared 0.11.2", + "phf_shared", "rand", ] @@ -5061,22 +5010,13 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", + "phf_generator", + "phf_shared", "proc-macro2", "quote", "syn 2.0.72", ] -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - [[package]] name = "phf_shared" version = "0.11.2" @@ -5199,12 +5139,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "pretty_assertions" version = "1.4.0" @@ -5838,9 +5772,9 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.32.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +checksum = "1cdbe9230a57259b37f7257d0aff38b8c9dbda3513edba2105e59b130189d82f" dependencies = [ "bitflags 2.6.0", "fallible-iterator", @@ -6535,32 +6469,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - [[package]] name = "string_enum" version = "0.4.4" @@ -6747,7 +6655,7 @@ dependencies = [ "bitflags 2.6.0", "is-macro", "num-bigint", - "phf 0.11.2", + "phf", "scoped-tls", "serde", "string_enum", @@ -6811,7 +6719,7 @@ dependencies = [ "new_debug_unreachable", "num-bigint", "num-traits", - "phf 0.11.2", + "phf", "serde", "smallvec", "smartstring", @@ -6833,7 +6741,7 @@ dependencies = [ "bitflags 2.6.0", "indexmap", "once_cell", - "phf 0.11.2", + "phf", "rustc-hash 1.1.0", "serde", "smallvec", @@ -7113,26 +7021,6 @@ dependencies = [ "syn 2.0.72", ] -[[package]] -name = "syntect" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" -dependencies = [ - "bincode", - "bitflags 1.3.2", - "flate2", - "fnv", - "once_cell", - "onig", - "regex-syntax", - "serde", - "serde_derive", - "serde_json", - "thiserror", - "walkdir", -] - [[package]] name = "tap" version = "1.0.1" @@ -7162,17 +7050,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - [[package]] name = "termcolor" version = "1.4.1" @@ -7204,6 +7081,7 @@ dependencies = [ name = "test_napi" version = "0.1.0" dependencies = [ + "libuv-sys-lite", "napi-build", "napi-sys", "test_server", @@ -7574,6 +7452,128 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tree-sitter" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca" +dependencies = [ + "cc", + "regex", +] + +[[package]] +name = "tree-sitter-bash" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5244703ad2e08a616d859a0557d7aa290adcd5e0990188a692e628ffe9dce40" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-css" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e08e324b1cf60fd3291774b49724c66de2ce8fcf4d358d0b4b82e37b41b1c9b" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-highlight" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaca0fe34fa96eec6aaa8e63308dbe1bafe65a6317487c287f93938959b21907" +dependencies = [ + "lazy_static", + "regex", + "thiserror", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-html" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8766b5ad3721517f8259e6394aefda9c686aebf7a8c74ab8624f2c3b46902fd5" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-javascript" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8710a71bc6779e33811a8067bdda3ed08bed1733296ff915e44faf60f8c533d7" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-json" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b737dcb73c35d74b7d64a5f3dde158113c86a012bf3cee2bfdf2150d23b05db" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-md" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c3cfd068f2527250bbd8ff407431164e12b17863e7eafb76e311dd3f96965a" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-regex" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ff1286fe9651b2797484839ffa37aa76c8618d4ccb6836d7e31765dfd60c0d5" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-rust" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "277690f420bf90741dea984f3da038ace46c4fe6047cba57a66822226cde1c93" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-typescript" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb35d98a688378e56c18c9c159824fd16f730ccbea19aacf4f206e5d5438ed9" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-xml" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c3a1b08e9842143f84fde1a18ac40ee77ca80a80b14077e4ca67a3b4808b8b" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "triomphe" version = "0.1.13" @@ -7890,7 +7890,7 @@ version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a381badc47c6f15acb5fe0b5b40234162349ed9d4e4fd7c83a7f5547c0fc69c5" dependencies = [ - "bindgen", + "bindgen 0.69.4", "bitflags 2.6.0", "fslock", "gzip-header", @@ -8157,7 +8157,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.3", + "libloading 0.8.5", "log", "metal", "naga", diff --git a/Cargo.toml b/Cargo.toml index 26ef3be19b..badc84992f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,19 +45,19 @@ license = "MIT" repository = "https://github.com/denoland/deno" [workspace.dependencies] -deno_ast = { version = "=0.42.1", features = ["transpiling"] } +deno_ast = { version = "=0.42.2", features = ["transpiling"] } deno_core = { version = "0.311.0" } -deno_bench_util = { version = "0.163.0", path = "./bench_util" } +deno_bench_util = { version = "0.165.0", path = "./bench_util" } deno_lockfile = "=0.23.1" deno_media_type = { version = "0.1.4", features = ["module_specifier"] } -deno_npm = "=0.25.2" +deno_npm = "=0.25.3" deno_path_util = "=0.2.0" -deno_permissions = { version = "0.29.0", path = "./runtime/permissions" } -deno_runtime = { version = "0.178.0", path = "./runtime" } +deno_permissions = { version = "0.31.0", path = "./runtime/permissions" } +deno_runtime = { version = "0.180.0", path = "./runtime" } deno_semver = "=0.5.14" deno_terminal = "0.2.0" -napi_sym = { version = "0.99.0", path = "./cli/napi/sym" } +napi_sym = { version = "0.101.0", path = "./cli/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } denokv_proto = "0.8.1" @@ -66,32 +66,32 @@ denokv_remote = "0.8.1" denokv_sqlite = { default-features = false, version = "0.8.2" } # exts -deno_broadcast_channel = { version = "0.163.0", path = "./ext/broadcast_channel" } -deno_cache = { version = "0.101.0", path = "./ext/cache" } -deno_canvas = { version = "0.38.0", path = "./ext/canvas" } -deno_console = { version = "0.169.0", path = "./ext/console" } -deno_cron = { version = "0.49.0", path = "./ext/cron" } -deno_crypto = { version = "0.183.0", path = "./ext/crypto" } -deno_fetch = { version = "0.193.0", path = "./ext/fetch" } -deno_ffi = { version = "0.156.0", path = "./ext/ffi" } -deno_fs = { version = "0.79.0", path = "./ext/fs" } -deno_http = { version = "0.167.0", path = "./ext/http" } -deno_io = { version = "0.79.0", path = "./ext/io" } -deno_kv = { version = "0.77.0", path = "./ext/kv" } -deno_napi = { version = "0.100.0", path = "./ext/napi" } -deno_net = { version = "0.161.0", path = "./ext/net" } -deno_node = { version = "0.106.0", path = "./ext/node" } -deno_tls = { version = "0.156.0", path = "./ext/tls" } -deno_url = { version = "0.169.0", path = "./ext/url" } -deno_web = { version = "0.200.0", path = "./ext/web" } -deno_webgpu = { version = "0.136.0", path = "./ext/webgpu" } -deno_webidl = { version = "0.169.0", path = "./ext/webidl" } -deno_websocket = { version = "0.174.0", path = "./ext/websocket" } -deno_webstorage = { version = "0.164.0", path = "./ext/webstorage" } +deno_broadcast_channel = { version = "0.165.0", path = "./ext/broadcast_channel" } +deno_cache = { version = "0.103.0", path = "./ext/cache" } +deno_canvas = { version = "0.40.0", path = "./ext/canvas" } +deno_console = { version = "0.171.0", path = "./ext/console" } +deno_cron = { version = "0.51.0", path = "./ext/cron" } +deno_crypto = { version = "0.185.0", path = "./ext/crypto" } +deno_fetch = { version = "0.195.0", path = "./ext/fetch" } +deno_ffi = { version = "0.158.0", path = "./ext/ffi" } +deno_fs = { version = "0.81.0", path = "./ext/fs" } +deno_http = { version = "0.169.0", path = "./ext/http" } +deno_io = { version = "0.81.0", path = "./ext/io" } +deno_kv = { version = "0.79.0", path = "./ext/kv" } +deno_napi = { version = "0.102.0", path = "./ext/napi" } +deno_net = { version = "0.163.0", path = "./ext/net" } +deno_node = { version = "0.108.0", path = "./ext/node" } +deno_tls = { version = "0.158.0", path = "./ext/tls" } +deno_url = { version = "0.171.0", path = "./ext/url" } +deno_web = { version = "0.202.0", path = "./ext/web" } +deno_webgpu = { version = "0.138.0", path = "./ext/webgpu" } +deno_webidl = { version = "0.171.0", path = "./ext/webidl" } +deno_websocket = { version = "0.176.0", path = "./ext/websocket" } +deno_webstorage = { version = "0.166.0", path = "./ext/webstorage" } # resolvers -deno_resolver = { version = "0.1.0", path = "./resolvers/deno" } -node_resolver = { version = "0.8.0", path = "./resolvers/node" } +deno_resolver = { version = "0.3.0", path = "./resolvers/deno" } +node_resolver = { version = "0.10.0", path = "./resolvers/node" } aes = "=0.8.3" anyhow = "1.0.57" @@ -111,7 +111,7 @@ dashmap = "5.5.3" data-encoding = "2.3.3" data-url = "=0.3.0" deno_cache_dir = "=0.13.0" -deno_package_json = { version = "0.1.1", default-features = false } +deno_package_json = { version = "0.1.2", default-features = false } dlopen2 = "0.6.1" ecb = "=0.1.2" elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem", "jwk"] } @@ -139,7 +139,7 @@ ipnet = "2.3" jsonc-parser = { version = "=0.23.0", features = ["serde"] } lazy-regex = "3" libc = "0.2.126" -libz-sys = { version = "1.1", default-features = false } +libz-sys = { version = "1.1.20", default-features = false } log = "0.4.20" lsp-types = "=0.97.0" # used by tower-lsp and "proposed" feature is unstable in patch releases memmem = "0.1.1" @@ -225,10 +225,9 @@ nix = "=0.26.2" # windows deps junction = "=0.2.0" winapi = "=0.3.9" -windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry"] } +windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_Security", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry", "Win32_System_Kernel"] } winres = "=0.1.12" -# NB: the `bench` and `release` profiles must remain EXACTLY the same. [profile.release] codegen-units = 1 incremental = true @@ -246,13 +245,6 @@ inherits = "release" codegen-units = 128 lto = "thin" -# NB: the `bench` and `release` profiles must remain EXACTLY the same. -[profile.bench] -codegen-units = 1 -incremental = true -lto = true -opt-level = 'z' # Optimize for size - # Key generation is too slow on `debug` [profile.dev.package.num-bigint-dig] opt-level = 3 @@ -261,80 +253,6 @@ opt-level = 3 [profile.dev.package.v8] opt-level = 1 -# Optimize these packages for performance. -# NB: the `bench` and `release` profiles must remain EXACTLY the same. -[profile.bench.package.async-compression] -opt-level = 3 -[profile.bench.package.base64-simd] -opt-level = 3 -[profile.bench.package.brotli] -opt-level = 3 -[profile.bench.package.brotli-decompressor] -opt-level = 3 -[profile.bench.package.bytes] -opt-level = 3 -[profile.bench.package.deno_bench_util] -opt-level = 3 -[profile.bench.package.deno_broadcast_channel] -opt-level = 3 -[profile.bench.package.deno_core] -opt-level = 3 -[profile.bench.package.deno_crypto] -opt-level = 3 -[profile.bench.package.deno_fetch] -opt-level = 3 -[profile.bench.package.deno_ffi] -opt-level = 3 -[profile.bench.package.deno_http] -opt-level = 3 -[profile.bench.package.deno_napi] -opt-level = 3 -[profile.bench.package.deno_net] -opt-level = 3 -[profile.bench.package.deno_node] -opt-level = 3 -[profile.bench.package.deno_runtime] -opt-level = 3 -[profile.bench.package.deno_tls] -opt-level = 3 -[profile.bench.package.deno_url] -opt-level = 3 -[profile.bench.package.deno_web] -opt-level = 3 -[profile.bench.package.deno_websocket] -opt-level = 3 -[profile.bench.package.fastwebsockets] -opt-level = 3 -[profile.bench.package.flate2] -opt-level = 3 -[profile.bench.package.futures-util] -opt-level = 3 -[profile.bench.package.hyper] -opt-level = 3 -[profile.bench.package.miniz_oxide] -opt-level = 3 -[profile.bench.package.num-bigint-dig] -opt-level = 3 -[profile.bench.package.rand] -opt-level = 3 -[profile.bench.package.serde] -opt-level = 3 -[profile.bench.package.serde_v8] -opt-level = 3 -[profile.bench.package.test_napi] -opt-level = 3 -[profile.bench.package.tokio] -opt-level = 3 -[profile.bench.package.url] -opt-level = 3 -[profile.bench.package.v8] -opt-level = 3 -[profile.bench.package.zstd] -opt-level = 3 -[profile.bench.package.zstd-sys] -opt-level = 3 - -# NB: the `bench` and `release` profiles must remain EXACTLY the same. [profile.release.package.async-compression] opt-level = 3 [profile.release.package.base64-simd] @@ -393,6 +311,8 @@ opt-level = 3 opt-level = 3 [profile.release.package.serde_v8] opt-level = 3 +[profile.release.package.libsui] +opt-level = 3 [profile.release.package.test_napi] opt-level = 3 [profile.release.package.tokio] diff --git a/Releases.md b/Releases.md index a6e4d84e7b..80fdc7fb8d 100644 --- a/Releases.md +++ b/Releases.md @@ -6,6 +6,349 @@ https://github.com/denoland/deno/releases We also have one-line install commands at: https://github.com/denoland/deno_install +### 2.0.0 / 2024.10.09 + +Read announcement blog post at: https://deno.com/blog/v2 + +- BREAKING: `DENO_FUTURE=1` by default, or welcome to Deno 2.0 (#25213) +- BREAKING: disallow `new Deno.FsFile()` (#25478) +- BREAKING: drop support for Deno.run.{clearEnv,gid,uid} (#25371) +- BREAKING: improve types for `Deno.serve` (#25369) +- BREAKING: improved error code accuracy (#25383) +- BREAKING: make supported compilerOptions an allow list (#25432) +- BREAKING: move `width` and `height` options to `UnsafeWindowSurface` + constructor (#24200) +- BREAKING: remove --allow-hrtime (#25367) +- BREAKING: remove "emit" and "map" from deno info output (#25468) +- BREAKING: remove `--allow-none` flag (#25337) +- BREAKING: remove `--jobs` flag (#25336) +- BREAKING: remove `--trace-ops` (#25344) +- BREAKING: remove `--ts` flag (#25338) +- BREAKING: remove `--unstable` flag (#25522) +- BREAKING: remove `deno bundle` (#25339) +- BREAKING: remove `deno vendor` (#25343) +- BREAKING: remove `Deno.[Tls]Listener.prototype.rid` (#25556) +- BREAKING: remove `Deno.{Conn,TlsConn,TcpConn,UnixConn}.prototype.rid` (#25446) +- BREAKING: remove `Deno.{Reader,Writer}[Sync]` and `Deno.Closer` (#25524) +- BREAKING: remove `Deno.Buffer` (#25441) +- BREAKING: remove `Deno.close()` (#25347) +- BREAKING: remove `Deno.ConnectTlsOptions.{certChain,certFile,privateKey}` and + `Deno.ListenTlsOptions.certChain,certFile,keyFile}` (#25525) +- BREAKING: remove `Deno.copy()` (#25345) +- BREAKING: remove `Deno.customInspect` (#25348) +- BREAKING: remove `Deno.fdatasync[Sync]()` (#25520) +- BREAKING: remove `Deno.File` (#25447) +- BREAKING: remove `Deno.flock[Sync]()` (#25350) +- BREAKING: remove `Deno.FsFile.prototype.rid` (#25499) +- BREAKING: remove `Deno.fstat[Sync]()` (#25351) +- BREAKING: remove `Deno.FsWatcher.prototype.rid` (#25444) +- BREAKING: remove `Deno.fsync[Sync]()` (#25448) +- BREAKING: remove `Deno.ftruncate[Sync]()` (#25412) +- BREAKING: remove `Deno.funlock[Sync]()` (#25442) +- BREAKING: remove `Deno.futime[Sync]()` (#25252) +- BREAKING: remove `Deno.iter[Sync]()` (#25346) +- BREAKING: remove `Deno.read[Sync]()` (#25409) +- BREAKING: remove `Deno.readAll[Sync]()` (#25386) +- BREAKING: remove `Deno.seek[Sync]()` (#25449) +- BREAKING: remove `Deno.Seeker[Sync]` (#25551) +- BREAKING: remove `Deno.shutdown()` (#25253) +- BREAKING: remove `Deno.write[Sync]()` (#25408) +- BREAKING: remove `Deno.writeAll[Sync]()` (#25407) +- BREAKING: remove deprecated `UnsafeFnPointer` constructor type with untyped + `Deno.PointerObject` parameter (#25577) +- BREAKING: remove deprecated files config (#25535) +- BREAKING: Remove obsoleted Temporal APIs part 2 (#25505) +- BREAKING: remove remaining web types for compatibility (#25334) +- BREAKING: remove support for remote import maps in deno.json (#25836) +- BREAKING: rename "deps" remote cache folder to "remote" (#25969) +- BREAKING: soft-remove `Deno.isatty()` (#25410) +- BREAKING: soft-remove `Deno.run()` (#25403) +- BREAKING: soft-remove `Deno.serveHttp()` (#25451) +- BREAKING: undeprecate `Deno.FsWatcher.prototype.return()` (#25623) +- feat: add `--allow-import` flag (#25469) +- feat: Add a hint on error about 'Relative import path ... not prefixed with + ...' (#25430) +- feat: Add better error messages for unstable APIs (#25519) +- feat: Add suggestion for packages using Node-API addons (#25975) +- feat: Allow importing .cjs files (#25426) +- feat: default to TS for file extension and support ext flag in more scenarios + (#25472) +- feat: deprecate import assertions (#25281) +- feat: Don't warn about --allow-script when using esbuild (#25894) +- feat: hide several --unstable-* flags (#25378) +- feat: improve lockfile v4 to store normalized version constraints and be more + terse (#25247) +- feat: improve warnings for deprecations and lifecycle script for npm packages + (#25694) +- feat: include version number in all --json based outputs (#25335) +- feat: lockfile v4 by default (#25165) +- feat: make 'globalThis.location' a configurable property (#25812) +- feat: print `Listening on` messages on stderr instead of stdout (#25491) +- feat: remove `--lock-write` flag (#25214) +- feat: require jsr prefix for `deno install` and `deno add` (#25698) +- feat: require(esm) (#25501) +- feat: Show hints when using `window` global (#25805) +- feat: stabilize `Deno.createHttpClient()` (#25569) +- feat: suggest `deno install --entrypoint` instead of `deno cache` (#25228) +- feat: support DENO_LOG env var instead of RUST_LOG (#25356) +- feat: TypeScript 5.6 and `npm:@types/node@22` (#25614) +- feat: Update no-window lint rule (#25486) +- feat: update warning message for --allow-run with no list (#25693) +- feat: warn when using `--allow-run` with no allow list (#25215) +- feat(add): Add npm packages to package.json if present (#25477) +- feat(add): strip package subpath when adding a package (#25419) +- feat(add/install): Flag to add dev dependency to package.json (#25495) +- feat(byonm): support `deno run npm:` when package is not in + package.json (#25981) +- feat(check): turn on noImplicitOverride (#25695) +- feat(check): turn on useUnknownInCatchVariables (#25465) +- feat(cli): evaluate code snippets in JSDoc and markdown (#25220) +- feat(cli): give access to `process` global everywhere (#25291) +- feat(cli): use NotCapable error for permission errors (#25431) +- feat(config): Node modules option for 2.0 (#25299) +- feat(ext/crypto): import and export p521 keys (#25789) +- feat(ext/crypto): X448 support (#26043) +- feat(ext/kv): configurable limit params (#25174) +- feat(ext/node): add abort helpers, process & streams fix (#25262) +- feat(ext/node): add rootCertificates to node:tls (#25707) +- feat(ext/node): buffer.transcode() (#25972) +- feat(ext/node): export 'promises' symbol from 'node:timers' (#25589) +- feat(ext/node): export missing constants from 'zlib' module (#25584) +- feat(ext/node): export missing symbols from domain, puncode, repl, tls + (#25585) +- feat(ext/node): export more symbols from streams and timers/promises (#25582) +- feat(ext/node): expose ES modules for _ modules (#25588) +- feat(flags): allow double commas to escape values in path based flags (#25453) +- feat(flags): support user provided args in repl subcommand (#25605) +- feat(fmt): better error on malfored HTML files (#25853) +- feat(fmt): stabilize CSS, HTML and YAML formatters (#25753) +- feat(fmt): support vto and njk extensions (#25831) +- feat(fmt): upgrade markup_fmt (#25768) +- feat(install): deno install with entrypoint (#25411) +- feat(install): warn repeatedly about not-run lifecycle scripts on explicit + installs (#25878) +- feat(lint): add `no-process-global` lint rule (#25709) +- feat(lsp): add a message when someone runs 'deno lsp' manually (#26051) +- feat(lsp): auto-import types with 'import type' (#25662) +- feat(lsp): html/css/yaml file formatting (#25353) +- feat(lsp): quick fix for @deno-types="npm:@types/*" (#25954) +- feat(lsp): turn on useUnknownInCatchVariables (#25474) +- feat(lsp): unstable setting as list (#25552) +- feat(permissions): `Deno.mainModule` doesn't require permissions (#25667) +- feat(permissions): allow importing from cdn.jsdelivr.net by default (#26013) +- feat(serve): Support second parameter in deno serve (#25606) +- feat(tools/doc): display subitems in symbol overviews where applicable + (#25885) +- feat(uninstall): alias to 'deno remove' if -g flag missing (#25461) +- feat(upgrade): better error message on failure (#25503) +- feat(upgrade): print info links for Deno 2 RC releases (#25225) +- feat(upgrade): support LTS release channel (#25123) +- fix: add link to env var docs (#25557) +- fix: add suggestion how to fix importing CJS module (#21764) +- fix: add test ensuring als works across dynamic import (#25593) +- fix: better error for Deno.UnsafeWindowSurface, correct HttpClient name, + cleanup unused code (#25833) +- fix: cjs resolution cases (#25739) +- fix: consistent with deno_config and treat `"experimentalDecorators"` as + deprecated (#25735) +- fix: delete old Deno 1.x headers file when loading cache (#25283) +- fix: do not panic running invalid file specifier (#25530) +- fix: don't include extensionless files in file collection for lint & fmt by + default (#25721) +- fix: don't prompt when using `Deno.permissions.request` with `--no-prompt` + (#25811) +- fix: eagerly error for specifier with empty version constraint (#25944) +- fix: enable `Win32_Security` feature in `windows-sys` (#26007) +- fix: error on unsupported compiler options (#25714) +- fix: error out if a valid flag is passed before a subcommand (#25830) +- fix: fix jupyter display function type (#25326) +- fix: Float16Array type (#25506) +- fix: handle showing warnings while the progress bar is shown (#25187) +- fix: Hide 'deno cache' from help output (#25960) +- fix: invalid ipv6 hostname on `deno serve` (#25482) +- fix: linux canonicalization checks (#24641) +- fix: lock down allow-run permissions more (#25370) +- fix: make some warnings more standard (#25324) +- fix: no cmd prefix in help output go links (#25459) +- fix: only enable byonm if workspace root has pkg json (#25379) +- fix: panic when require(esm) (#25769) +- fix: precompile preserve SVG camelCase attributes (#25945) +- fix: reland async context (#25140) +- fix: remove --allow-run warning when using deno without args or subcommand + (#25684) +- fix: remove entrypoint hack for Deno 2.0 (#25332) +- fix: remove recently added deno.json node_modules aliasing (#25542) +- fix: remove the typo in the help message (#25962) +- fix: removed unstable-htttp from deno help (#25216) +- fix: replace `npm install` hint with `deno install` hint (#25244) +- fix: trim space around DENO_AUTH_TOKENS (#25147) +- fix: update deno_doc (#25290) +- fix: Update deno_npm to fix `deno install` with crossws (#25837) +- fix: update hint for `deno add ` (#25455) +- fix: update malva in deno to support astro css comments (#25553) +- fix: update nodeModulesDir config JSON schema (#25653) +- fix: update patchver to 0.2 (#25952) +- fix: update sui to 0.4 (#25942) +- fix: upgrade deno_ast 0.42 (#25313) +- fix: upgrade deno_core to 0.307.0 (#25287) +- fix(add/install): default to "latest" tag for npm packages in + `deno add npm:pkg` (#25858) +- fix(bench): Fix table column alignments and NO_COLOR=1 (#25190) +- fix(BREAKING): make dns record types have consistent naming (#25357) +- fix(byonm): resolve npm deps of jsr deps (#25399) +- fix(check): ignore noImplicitOverrides in remote modules (#25854) +- fix(check): move is cjs check from resolving to loading (#25597) +- fix(check): properly surface dependency errors in types file of js file + (#25860) +- fix(cli): `deno task` exit with status 0 (#25637) +- fix(cli): Default to auto with --node-modules-dir flag (#25772) +- fix(cli): handle edge cases around `export`s in doc tests and default export + (#25720) +- fix(cli): Map error kind to `PermissionDenied` when symlinking fails due to + permissions (#25398) +- fix(cli): Only set allow net flag for deno serve if not already allowed all + (#25743) +- fix(cli): Warn on not-run lifecycle scripts with global cache (#25786) +- fix(cli/tools): correct `deno init --serve` template behavior (#25318) +- fix(compile): support 'deno compile' in RC and LTS releases (#25875) +- fix(config): validate export names (#25436) +- fix(coverage): ignore urls from doc testing (#25736) +- fix(doc): surface graph errors as warnings (#25888) +- fix(dts): stabilize `fetch` declaration for use with `Deno.HttpClient` + (#25683) +- fix(ext/console): more precision in console.time (#25723) +- fix(ext/console): prevent duplicate error printing when the cause is assigned + (#25327) +- fix(ext/crypto): ensure EC public keys are exported uncompressed (#25766) +- fix(ext/crypto): fix identity test for x25519 derive bits (#26011) +- fix(ext/crypto): reject empty usages in SubtleCrypto#importKey (#25759) +- fix(ext/crypto): support md4 digest algorithm (#25656) +- fix(ext/crypto): throw DataError for invalid EC key import (#25181) +- fix(ext/fetch): fix lowercase http_proxy classified as https (#25686) +- fix(ext/fetch): percent decode userinfo when parsing proxies (#25229) +- fix(ext/http): do not set localhost to hostname unnecessarily (#24777) +- fix(ext/http): gracefully handle Response.error responses (#25712) +- fix(ext/node): add `FileHandle#writeFile` (#25555) +- fix(ext/node): add `vm.constants` (#25630) +- fix(ext/node): Add missing `node:path` exports (#25567) +- fix(ext/node): Add missing node:fs and node:constants exports (#25568) +- fix(ext/node): add stubs for `node:trace_events` (#25628) +- fix(ext/node): attach console stream properties (#25617) +- fix(ext/node): avoid showing `UNKNOWN` error from TCP handle (#25550) +- fix(ext/node): close upgraded socket when the underlying http connection is + closed (#25387) +- fix(ext/node): delay accept() call 2 ticks in net.Server#listen (#25481) +- fix(ext/node): don't throw error for unsupported signal binding on windows + (#25699) +- fix(ext/node): emit `online` event after worker thread is initialized (#25243) +- fix(ext/node): export `process.allowedNodeEnvironmentFlags` (#25629) +- fix(ext/node): export JWK public key (#25239) +- fix(ext/node): export request and response clases from `http2` module (#25592) +- fix(ext/node): fix `Cipheriv#update(string, undefined)` (#25571) +- fix(ext/node): fix Decipheriv when autoPadding disabled (#25598) +- fix(ext/node): fix process.stdin.pause() (#25864) +- fix(ext/node): Fix vm sandbox object panic (#24985) +- fix(ext/node): http2session ready state (#25143) +- fix(ext/node): Implement detached option in `child_process` (#25218) +- fix(ext/node): import EC JWK keys (#25266) +- fix(ext/node): import JWK octet key pairs (#25180) +- fix(ext/node): import RSA JWK keys (#25267) +- fix(ext/node): register `node:wasi` built-in (#25134) +- fix(ext/node): remove unimplemented promiseHook stubs (#25979) +- fix(ext/node): report freemem() on Linux in bytes (#25511) +- fix(ext/node): Rewrite `node:v8` serialize/deserialize (#25439) +- fix(ext/node): session close during stream setup (#25170) +- fix(ext/node): Stream should be instance of EventEmitter (#25527) +- fix(ext/node): stub `inspector/promises` (#25635) +- fix(ext/node): stub `process.cpuUsage()` (#25462) +- fix(ext/node): stub cpu_info() for OpenBSD (#25807) +- fix(ext/node): support x509 certificates in `createPublicKey` (#25731) +- fix(ext/node): throw when loading `cpu-features` module (#25257) +- fix(ext/node): update aead-gcm-stream to 0.3 (#25261) +- fix(ext/node): use primordials in `ext/node/polyfills/console.ts` (#25572) +- fix(ext/node): use primordials in ext/node/polyfills/wasi.ts (#25608) +- fix(ext/node): validate input lengths in `Cipheriv` and `Decipheriv` (#25570) +- fix(ext/web): don't ignore capture in EventTarget.removeEventListener (#25788) +- fix(ext/webgpu): allow to build on unsupported platforms (#25202) +- fix(ext/webgpu): sync category comment (#25580) +- fix(ext/webstorage): make `getOwnPropertyDescriptor` with symbol return + `undefined` (#13348) +- fix(flags): --allow-all should conflict with lower permissions (#25909) +- fix(flags): don't treat empty run command as task subcommand (#25708) +- fix(flags): move some content from docs.deno.com into help output (#25951) +- fix(flags): properly error out for urls (#25770) +- fix(flags): require global flag for permission flags in install subcommand + (#25391) +- fix(fmt): --check was broken for CSS, YAML and HTML (#25848) +- fix(fmt): fix incorrect quotes in components (#25249) +- fix(fmt): fix tabs in YAML (#25536) +- fix(fmt/markdown): fix regression with multi-line footnotes and inline math + (#25222) +- fix(info): error instead of panic for npm specifiers when using byonm (#25947) +- fix(info): move "version" field to top of json output (#25890) +- fix(inspector): Fix panic when re-entering runtime ops (#25537) +- fix(install): compare versions directly to decide whether to create a child + node_modules dir for a workspace member (#26001) +- fix(install): Make sure target node_modules exists when symlinking (#25494) +- fix(install): recommend using `deno install -g` when using a single http url + (#25388) +- fix(install): store tags associated with package in node_modules dir (#26000) +- fix(install): surface package.json dependency errors (#26023) +- fix(install): Use relative symlinks in deno install (#25164) +- fix(installl): make bin entries executable even if not put in + `node_modules/.bin` (#25873) +- fix(jupyter): allow unstable flags (#25483) +- fix(lint): correctly handle old jsx in linter (#25902) +- fix(lint): support linting jsr pkg without version field (#25230) +- fix(lockfile): use loose deserialization for version constraints (#25660) +- fix(lsp): encode url parts before parsing as uri (#25509) +- fix(lsp): exclude missing import quick fixes with bad resolutions (#26025) +- fix(lsp): panic on url_to_uri() (#25238) +- fix(lsp): properly resolve jsxImportSource for caching (#25688) +- fix(lsp): update diagnostics on npm install (#25352) +- fix(napi): Don't run microtasks in napi_resolve_deferred (#25246) +- fix(napi): Fix worker threads importing already-loaded NAPI addon (#25245) +- fix(no-slow-types): better `override` handling (#25989) +- fix(node): Don't error out if we fail to statically analyze CJS re-export + (#25748) +- fix(node): fix worker_threads issues blocking Angular support (#26024) +- fix(node): implement libuv APIs needed to support `npm:sqlite3` (#25893) +- fix(node): Include "node" condition during CJS re-export analysis (#25785) +- fix(node): Pass NPM_PROCESS_STATE to subprocesses via temp file instead of env + var (#25896) +- fix(node/byonm): do not accidentally resolve bare node built-ins (#25543) +- fix(node/cluster): improve stubs to make log4js work (#25146) +- fix(npm): better error handling for remote npm deps (#25670) +- fix(npm): root package has peer dependency on itself (#26022) +- fix(permissions): disallow any `LD_` or `DYLD_` prefixed env var without full + --allow-run permissions (#25271) +- fix(permissions): disallow launching subprocess with LD_PRELOAD env var + without full run permissions (#25221) +- fix(publish): ensure provenance is spec compliant (#25200) +- fix(regression): do not expose resolved path in Deno.Command permission denied + error (#25434) +- fix(runtime): don't error `child.output()` on consumed stream (#25657) +- fix(runtime): use more null proto objects again (#25040) +- fix(runtime/web_worker): populate `SnapshotOptions` for `WebWorker` when + instantiated without snapshot (#25280) +- fix(task): correct name for scoped npm package binaries (#25390) +- fix(task): support tasks with colons in name in `deno run` (#25233) +- fix(task): use current executable for deno even when not named deno (#26019) +- fix(types): simplify mtls related types (#25658) +- fix(upgrade): more informative information on invalid version (#25319) +- fix(windows): Deno.Command - align binary resolution with linux and mac + (#25429) +- fix(workspace): handle when config has members when specified via --config + (#25988) +- perf: fast path for cached dyn imports (#25636) +- perf: Use -O3 for sui in release builds (#26010) +- perf(cache): single cache file for remote modules (#24983) +- perf(cache): single cache file for typescript emit (#24994) +- perf(ext/fetch): improve decompression throughput by upgrading `tower_http` + (#25806) +- perf(ext/node): reduce some allocations in require (#25197) +- perf(ext/web): optimize performance.measure() (#25774) + ### 1.46.3 / 2024.09.04 - feat(upgrade): print info links for Deno 2 RC releases (#25225) diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index d668d3a944..4b886a2907 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_bench_util" -version = "0.163.0" +version = "0.165.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2cc32c880b..ae57df4f7c 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno" -version = "2.0.0-rc.9" +version = "2.0.0" authors.workspace = true default-run = "deno" edition.workspace = true @@ -65,10 +65,10 @@ winres.workspace = true [dependencies] deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] } deno_cache_dir = { workspace = true } -deno_config = { version = "=0.35.0", features = ["workspace", "sync"] } +deno_config = { version = "=0.37.1", features = ["workspace", "sync"] } deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } -deno_doc = { version = "0.150.1", features = ["html", "syntect"] } -deno_graph = { version = "=0.83.0" } +deno_doc = { version = "0.152.0", features = ["html"] } +deno_graph = { version = "=0.83.3" } deno_lint = { version = "=0.67.0", features = ["docs"] } deno_lockfile.workspace = true deno_npm.workspace = true @@ -77,7 +77,7 @@ deno_path_util.workspace = true deno_resolver.workspace = true deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_semver.workspace = true -deno_task_shell = "=0.17.0" +deno_task_shell = "=0.18.1" deno_terminal.workspace = true eszip = "=0.79.1" libsui = "0.4.0" @@ -170,12 +170,14 @@ zstd.workspace = true [target.'cfg(windows)'.dependencies] junction.workspace = true winapi = { workspace = true, features = ["knownfolders", "mswsock", "objbase", "shlobj", "tlhelp32", "winbase", "winerror", "winsock2"] } +windows-sys.workspace = true [target.'cfg(unix)'.dependencies] nix.workspace = true [dev-dependencies] deno_bench_util.workspace = true +libuv-sys-lite = "=1.48.2" pretty_assertions.workspace = true test_util.workspace = true diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 6caef29d9f..258712ca92 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -580,6 +580,15 @@ pub struct UnstableConfig { pub features: Vec, // --unstabe-kv --unstable-cron } +#[derive(Clone, Debug, Eq, PartialEq, Default)] +pub struct InternalFlags { + /// Used when the language server is configured with an + /// explicit cache option. + pub cache_path: Option, + /// Only reads to the lockfile instead of writing to it. + pub lockfile_skip_write: bool, +} + #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct Flags { /// Vector of CLI arguments - these are user script arguments, all Deno @@ -591,9 +600,6 @@ pub struct Flags { pub ca_stores: Option>, pub ca_data: Option, pub cache_blocklist: Vec, - /// This is not exposed as an option in the CLI, it is used internally when - /// the language server is configured with an explicit cache option. - pub cache_path: Option, pub cached_only: bool, pub type_check_mode: TypeCheckMode, pub config_flag: ConfigFlag, @@ -602,6 +608,8 @@ pub struct Flags { pub enable_op_summary_metrics: bool, pub enable_testing_features: bool, pub ext: Option, + /// Flags that aren't exposed in the CLI, but are used internally. + pub internal: InternalFlags, pub ignore: Vec, pub import_map_path: Option, pub env_file: Option, @@ -688,9 +696,10 @@ impl PermissionFlags { } let builtin_allowed_import_hosts = [ + "jsr.io:443", "deno.land:443", "esm.sh:443", - "jsr.io:443", + "cdn.jsdelivr.net:443", "raw.githubusercontent.com:443", "gist.githubusercontent.com:443", ]; @@ -2901,6 +2910,7 @@ List all available tasks: .help("Specify the directory to run the task in") .value_hint(ValueHint::DirPath), ) + .arg(node_modules_dir_arg()) }) } @@ -3253,7 +3263,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { -W, --allow-write[=<...] Allow file system write access. Optionally specify allowed paths. --allow-write | --allow-write="/etc,/var/log.txt" -I, --allow-import[=<...] Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. - Default value: deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443 + Default value: deno.land:443,jsr.io:443,esm.sh:443,cdn.jsdelivr.net:443,raw.githubusercontent.com:443,user.githubusercontent.com:443 --allow-import | --allow-import="example.com,github.com" -N, --allow-net[=<...] Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary. --allow-net | --allow-net="localhost:8080,deno.land" @@ -3663,7 +3673,7 @@ fn allow_import_arg() -> Arg { .require_equals(true) .value_name("IP_OR_HOSTNAME") .help(cstr!( - "Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. Default value: deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443" + "Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. Default value: deno.land:443,jsr.io:443,esm.sh:443,cdn.jsdelivr.net:443,raw.githubusercontent.com:443,user.githubusercontent.com:443" )) .value_parser(flags_net::validator) } @@ -4965,6 +4975,7 @@ fn task_parse(flags: &mut Flags, matches: &mut ArgMatches) { .unwrap_or(ConfigFlag::Discover); unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); + node_modules_arg_parse(flags, matches); let mut task_flags = TaskFlags { cwd: matches.remove_one::("cwd"), diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index 59ec7f0ef5..1805d26426 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -24,11 +24,20 @@ use crate::args::InstallKind; use deno_lockfile::Lockfile; +#[derive(Debug)] +pub struct CliLockfileReadFromPathOptions { + pub file_path: PathBuf, + pub frozen: bool, + /// Causes the lockfile to only be read from, but not written to. + pub skip_write: bool, +} + #[derive(Debug)] pub struct CliLockfile { lockfile: Mutex, pub filename: PathBuf, - pub frozen: bool, + frozen: bool, + skip_write: bool, } pub struct Guard<'a, T> { @@ -50,15 +59,6 @@ impl<'a, T> std::ops::DerefMut for Guard<'a, T> { } impl CliLockfile { - pub fn new(lockfile: Lockfile, frozen: bool) -> Self { - let filename = lockfile.filename.clone(); - Self { - lockfile: Mutex::new(lockfile), - filename, - frozen, - } - } - /// Get the inner deno_lockfile::Lockfile. pub fn lock(&self) -> Guard { Guard { @@ -78,6 +78,10 @@ impl CliLockfile { } pub fn write_if_changed(&self) -> Result<(), AnyError> { + if self.skip_write { + return Ok(()); + } + self.error_if_changed()?; let mut lockfile = self.lockfile.lock(); let Some(bytes) = lockfile.resolve_write_bytes() else { @@ -142,7 +146,7 @@ impl CliLockfile { return Ok(None); } - let filename = match flags.lock { + let file_path = match flags.lock { Some(ref lock) => PathBuf::from(lock), None => match workspace.resolve_lockfile_path()? { Some(path) => path, @@ -160,7 +164,11 @@ impl CliLockfile { .unwrap_or(false) }); - let lockfile = Self::read_from_path(filename, frozen)?; + let lockfile = Self::read_from_path(CliLockfileReadFromPathOptions { + file_path, + frozen, + skip_write: flags.internal.lockfile_skip_write, + })?; // initialize the lockfile with the workspace's configuration let root_url = workspace.root_dir(); @@ -212,25 +220,29 @@ impl CliLockfile { } pub fn read_from_path( - file_path: PathBuf, - frozen: bool, + opts: CliLockfileReadFromPathOptions, ) -> Result { - match std::fs::read_to_string(&file_path) { - Ok(text) => Ok(CliLockfile::new( - Lockfile::new(deno_lockfile::NewLockfileOptions { - file_path, - content: &text, - overwrite: false, - })?, - frozen, - )), - Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok( - CliLockfile::new(Lockfile::new_empty(file_path, false), frozen), - ), - Err(err) => Err(err).with_context(|| { - format!("Failed reading lockfile '{}'", file_path.display()) - }), - } + let lockfile = match std::fs::read_to_string(&opts.file_path) { + Ok(text) => Lockfile::new(deno_lockfile::NewLockfileOptions { + file_path: opts.file_path, + content: &text, + overwrite: false, + })?, + Err(err) if err.kind() == std::io::ErrorKind::NotFound => { + Lockfile::new_empty(opts.file_path, false) + } + Err(err) => { + return Err(err).with_context(|| { + format!("Failed reading lockfile '{}'", opts.file_path.display()) + }); + } + }; + Ok(CliLockfile { + filename: lockfile.filename.clone(), + lockfile: Mutex::new(lockfile), + frozen: opts.frozen, + skip_write: opts.skip_write, + }) } pub fn error_if_changed(&self) -> Result<(), AnyError> { diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 2ae7098da5..07906a86ac 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -44,6 +44,7 @@ pub use deno_config::glob::FilePatterns; pub use deno_json::check_warn_tsconfig; pub use flags::*; pub use lockfile::CliLockfile; +pub use lockfile::CliLockfileReadFromPathOptions; pub use package_json::NpmInstallDepsProvider; use deno_ast::ModuleSpecifier; @@ -824,11 +825,9 @@ impl CliOptions { } } - warn_insecure_allow_run_flags(&flags); - let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache); let deno_dir_provider = - Arc::new(DenoDirProvider::new(flags.cache_path.clone())); + Arc::new(DenoDirProvider::new(flags.internal.cache_path.clone())); let maybe_node_modules_folder = resolve_node_modules_folder( &initial_cwd, &flags, @@ -1710,27 +1709,6 @@ impl CliOptions { } } -/// Warns for specific uses of `--allow-run`. This function is not -/// intended to catch every single possible insecure use of `--allow-run`, -/// but is just an attempt to discourage some common pitfalls. -fn warn_insecure_allow_run_flags(flags: &Flags) { - let permissions = &flags.permissions; - if permissions.allow_all { - return; - } - let Some(allow_run_list) = permissions.allow_run.as_ref() else { - return; - }; - - // discourage using --allow-run without an allow list - if allow_run_list.is_empty() { - log::warn!( - "{} --allow-run without an allow list is susceptible to exploits. Prefer specifying an allow list (https://docs.deno.com/runtime/fundamentals/security/#running-subprocesses)", - colors::yellow("Warning") - ); - } -} - /// Resolves the path to use for a local node_modules folder. fn resolve_node_modules_folder( cwd: &Path, diff --git a/cli/args/package_json.rs b/cli/args/package_json.rs index 2529e54fdf..2ef39a30d2 100644 --- a/cli/args/package_json.rs +++ b/cli/args/package_json.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use deno_config::workspace::Workspace; use deno_core::serde_json; use deno_package_json::PackageJsonDepValue; +use deno_package_json::PackageJsonDepValueParseError; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; @@ -26,6 +27,7 @@ pub struct InstallNpmWorkspacePkg { pub struct NpmInstallDepsProvider { remote_pkgs: Vec, workspace_pkgs: Vec, + pkg_json_dep_errors: Vec, } impl NpmInstallDepsProvider { @@ -37,6 +39,7 @@ impl NpmInstallDepsProvider { // todo(dsherret): estimate capacity? let mut workspace_pkgs = Vec::new(); let mut remote_pkgs = Vec::new(); + let mut pkg_json_dep_errors = Vec::new(); let workspace_npm_pkgs = workspace.npm_packages(); for (_, folder) in workspace.config_folders() { @@ -83,8 +86,12 @@ impl NpmInstallDepsProvider { let deps = pkg_json.resolve_local_package_json_deps(); let mut pkg_pkgs = Vec::with_capacity(deps.len()); for (alias, dep) in deps { - let Ok(dep) = dep else { - continue; + let dep = match dep { + Ok(dep) => dep, + Err(err) => { + pkg_json_dep_errors.push(err); + continue; + } }; match dep { PackageJsonDepValue::Req(pkg_req) => { @@ -131,14 +138,19 @@ impl NpmInstallDepsProvider { Self { remote_pkgs, workspace_pkgs, + pkg_json_dep_errors, } } - pub fn remote_pkgs(&self) -> &Vec { + pub fn remote_pkgs(&self) -> &[InstallNpmRemotePkg] { &self.remote_pkgs } - pub fn workspace_pkgs(&self) -> &Vec { + pub fn workspace_pkgs(&self) -> &[InstallNpmWorkspacePkg] { &self.workspace_pkgs } + + pub fn pkg_json_dep_errors(&self) -> &[PackageJsonDepValueParseError] { + &self.pkg_json_dep_errors + } } diff --git a/cli/build.rs b/cli/build.rs index c3b6f8b045..aa5d3d18c6 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -387,6 +387,8 @@ fn main() { "Missing symbols list! Generate using tools/napi/generate_symbols_lists.js", ); + println!("cargo:rustc-rerun-if-changed={}", symbols_path.display()); + #[cfg(target_os = "windows")] println!( "cargo:rustc-link-arg-bin=deno=/DEF:{}", diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index d189ab2534..8c2e8bb1dc 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -44,6 +44,7 @@ use std::cmp::Ordering; use std::collections::HashMap; use std::collections::HashSet; use std::path::Path; +use text_lines::LineAndColumnIndex; use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types::Position; use tower_lsp::lsp_types::Range; @@ -228,6 +229,7 @@ pub struct TsResponseImportMapper<'a> { documents: &'a Documents, maybe_import_map: Option<&'a ImportMap>, resolver: &'a LspResolver, + file_referrer: ModuleSpecifier, } impl<'a> TsResponseImportMapper<'a> { @@ -235,11 +237,13 @@ impl<'a> TsResponseImportMapper<'a> { documents: &'a Documents, maybe_import_map: Option<&'a ImportMap>, resolver: &'a LspResolver, + file_referrer: &ModuleSpecifier, ) -> Self { Self { documents, maybe_import_map, resolver, + file_referrer: file_referrer.clone(), } } @@ -260,8 +264,6 @@ impl<'a> TsResponseImportMapper<'a> { } } - let file_referrer = self.documents.get_file_referrer(referrer); - if let Some(jsr_path) = specifier.as_str().strip_prefix(jsr_url().as_str()) { let mut segments = jsr_path.split('/'); @@ -276,7 +278,7 @@ impl<'a> TsResponseImportMapper<'a> { let export = self.resolver.jsr_lookup_export_for_path( &nv, &path, - file_referrer.as_deref(), + Some(&self.file_referrer), )?; let sub_path = (export != ".").then_some(export); let mut req = None; @@ -302,7 +304,7 @@ impl<'a> TsResponseImportMapper<'a> { req = req.or_else(|| { self .resolver - .jsr_lookup_req_for_nv(&nv, file_referrer.as_deref()) + .jsr_lookup_req_for_nv(&nv, Some(&self.file_referrer)) }); let spec_str = if let Some(req) = req { let req_ref = PackageReqReference { req, sub_path }; @@ -332,7 +334,7 @@ impl<'a> TsResponseImportMapper<'a> { if let Some(npm_resolver) = self .resolver - .maybe_managed_npm_resolver(file_referrer.as_deref()) + .maybe_managed_npm_resolver(Some(&self.file_referrer)) { if npm_resolver.in_npm_package(specifier) { if let Ok(Some(pkg_id)) = @@ -468,6 +470,26 @@ impl<'a> TsResponseImportMapper<'a> { } None } + + pub fn is_valid_import( + &self, + specifier_text: &str, + referrer: &ModuleSpecifier, + ) -> bool { + self + .resolver + .as_graph_resolver(Some(&self.file_referrer)) + .resolve( + specifier_text, + &deno_graph::Range { + specifier: referrer.clone(), + start: deno_graph::Position::zeroed(), + end: deno_graph::Position::zeroed(), + }, + deno_graph::source::ResolutionMode::Types, + ) + .is_ok() + } } fn try_reverse_map_package_json_exports( @@ -580,7 +602,7 @@ fn fix_ts_import_action( referrer: &ModuleSpecifier, action: &tsc::CodeFixAction, import_mapper: &TsResponseImportMapper, -) -> Result { +) -> Result, AnyError> { if matches!( action.fix_name.as_str(), "import" | "fixMissingFunctionDeclaration" @@ -623,19 +645,21 @@ fn fix_ts_import_action( }) .collect(); - return Ok(tsc::CodeFixAction { + return Ok(Some(tsc::CodeFixAction { description, changes, commands: None, fix_name: action.fix_name.clone(), fix_id: None, fix_all_description: None, - }); + })); + } else if !import_mapper.is_valid_import(specifier, referrer) { + return Ok(None); } } } - Ok(action.clone()) + Ok(Some(action.clone())) } /// Determines if two TypeScript diagnostic codes are effectively equivalent. @@ -976,11 +1000,14 @@ impl CodeActionCollection { "The action returned from TypeScript is unsupported.", )); } - let action = fix_ts_import_action( + let Some(action) = fix_ts_import_action( specifier, action, &language_server.get_ts_response_import_mapper(specifier), - )?; + )? + else { + return Ok(()); + }; let edit = ts_changes_to_edit(&action.changes, language_server)?; let code_action = lsp::CodeAction { title: action.description.clone(), @@ -1161,6 +1188,34 @@ impl CodeActionCollection { range: &lsp::Range, language_server: &language_server::Inner, ) { + fn import_start_from_specifier( + document: &Document, + import: &deno_graph::Import, + ) -> Option { + // find the top level statement that contains the specifier + let parsed_source = document.maybe_parsed_source()?.as_ref().ok()?; + let text_info = parsed_source.text_info_lazy(); + let specifier_range = SourceRange::new( + text_info.loc_to_source_pos(LineAndColumnIndex { + line_index: import.specifier_range.start.line, + column_index: import.specifier_range.start.character, + }), + text_info.loc_to_source_pos(LineAndColumnIndex { + line_index: import.specifier_range.end.line, + column_index: import.specifier_range.end.character, + }), + ); + + match parsed_source.program_ref() { + deno_ast::swc::ast::Program::Module(module) => module + .body + .iter() + .find(|i| i.range().contains(&specifier_range)) + .map(|i| text_info.line_and_column_index(i.range().start)), + deno_ast::swc::ast::Program::Script(_) => None, + } + } + async fn deno_types_for_npm_action( document: &Document, range: &lsp::Range, @@ -1181,14 +1236,15 @@ impl CodeActionCollection { range.end.line as usize, range.end.character as usize, ); - let import_range = dependency.imports.iter().find_map(|i| { + let import_start = dependency.imports.iter().find_map(|i| { if json!(i.kind) != json!("es") && json!(i.kind) != json!("tsType") { return None; } if !i.specifier_range.includes(&position) { return None; } - i.full_range.as_ref() + + import_start_from_specifier(document, i) })?; let referrer = document.specifier(); let file_referrer = document.file_referrer(); @@ -1275,8 +1331,8 @@ impl CodeActionCollection { .specifier_to_uri(referrer, file_referrer) .ok()?; let position = lsp::Position { - line: import_range.start.line as u32, - character: import_range.start.character as u32, + line: import_start.line_index as u32, + character: import_start.column_index as u32, }; let new_text = format!( "{}// @deno-types=\"{}\"\n", diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index c54de3a235..07fdd3c65a 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -53,6 +53,7 @@ use super::logging::lsp_log; use crate::args::discover_npmrc_from_workspace; use crate::args::has_flag_env_var; use crate::args::CliLockfile; +use crate::args::CliLockfileReadFromPathOptions; use crate::args::ConfigFile; use crate::args::LintFlags; use crate::args::LintOptions; @@ -1931,7 +1932,11 @@ fn resolve_lockfile_from_path( lockfile_path: PathBuf, frozen: bool, ) -> Option { - match CliLockfile::read_from_path(lockfile_path, frozen) { + match CliLockfile::read_from_path(CliLockfileReadFromPathOptions { + file_path: lockfile_path, + frozen, + skip_write: false, + }) { Ok(value) => { if value.filename.exists() { if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename) diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 6ddbb1a514..8269dc8515 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -96,6 +96,7 @@ use crate::args::CaData; use crate::args::CacheSetting; use crate::args::CliOptions; use crate::args::Flags; +use crate::args::InternalFlags; use crate::args::UnstableFmtOptions; use crate::factory::CliFactory; use crate::file_fetcher::FileFetcher; @@ -1917,6 +1918,7 @@ impl Inner { // as the import map is an implementation detail .and_then(|d| d.resolver.maybe_import_map()), self.resolver.as_ref(), + file_referrer, ) } @@ -3605,7 +3607,10 @@ impl Inner { }; let cli_options = CliOptions::new( Arc::new(Flags { - cache_path: Some(self.cache.deno_dir().root.clone()), + internal: InternalFlags { + cache_path: Some(self.cache.deno_dir().root.clone()), + ..Default::default() + }, ca_stores: workspace_settings.certificate_stores.clone(), ca_data: workspace_settings.tls_certificate.clone().map(CaData::File), unsafely_ignore_certificate_errors: workspace_settings diff --git a/cli/main.rs b/cli/main.rs index 31bebc882f..ddb6078af4 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -37,6 +37,7 @@ use crate::util::v8::get_v8_flags_from_env; use crate::util::v8::init_v8_flags; use args::TaskFlags; +use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; use deno_runtime::WorkerExecutionMode; pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; @@ -51,10 +52,12 @@ use deno_runtime::fmt_errors::FixSuggestion; use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; use deno_terminal::colors; use factory::CliFactory; +use npm::ResolvePkgFolderFromDenoReqError; use standalone::MODULE_NOT_FOUND; use standalone::UNSUPPORTED_SCHEME; use std::env; use std::future::Future; +use std::io::IsTerminal; use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; @@ -159,7 +162,19 @@ async fn run_subcommand(flags: Arc) -> Result { DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async { tools::installer::uninstall(flags, uninstall_flags).await }), - DenoSubcommand::Lsp => spawn_subcommand(async { lsp::start().await }), + DenoSubcommand::Lsp => spawn_subcommand(async { + if std::io::stderr().is_terminal() { + log::warn!( + "{} command is intended to be run by text editors and IDEs and shouldn't be run manually. + + Visit https://docs.deno.com/runtime/getting_started/setup_your_environment/ for instruction + how to setup your favorite text editor. + + Press Ctrl+C to exit. + ", colors::cyan("deno lsp")); + } + lsp::start().await + }), DenoSubcommand::Lint(lint_flags) => spawn_subcommand(async { if lint_flags.rules { tools::lint::print_rules_list( @@ -182,6 +197,21 @@ async fn run_subcommand(flags: Arc) -> Result { match result { Ok(v) => Ok(v), Err(script_err) => { + if let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = script_err.downcast_ref::() { + if flags.node_modules_dir.is_none() { + let mut flags = flags.deref().clone(); + let watch = match &flags.subcommand { + DenoSubcommand::Run(run_flags) => run_flags.watch.clone(), + _ => unreachable!(), + }; + flags.node_modules_dir = Some(deno_config::deno_json::NodeModulesDirMode::None); + // use the current lockfile, but don't write it out + if flags.frozen_lockfile.is_none() { + flags.internal.lockfile_skip_write = true; + } + return tools::run::run_script(WorkerExecutionMode::Run, Arc::new(flags), watch).await; + } + } let script_err_msg = script_err.to_string(); if script_err_msg.starts_with(MODULE_NOT_FOUND) || script_err_msg.starts_with(UNSUPPORTED_SCHEME) { if run_flags.bare { diff --git a/cli/napi/generated_symbol_exports_list_linux.def b/cli/napi/generated_symbol_exports_list_linux.def index 06e94f05bb..614880ebfa 100644 --- a/cli/napi/generated_symbol_exports_list_linux.def +++ b/cli/napi/generated_symbol_exports_list_linux.def @@ -1 +1 @@ -{ "node_api_create_syntax_error"; "napi_make_callback"; "napi_has_named_property"; "napi_async_destroy"; "napi_coerce_to_object"; "napi_get_arraybuffer_info"; "napi_detach_arraybuffer"; "napi_get_undefined"; "napi_reference_unref"; "napi_fatal_error"; "napi_open_callback_scope"; "napi_close_callback_scope"; "napi_get_value_uint32"; "napi_create_function"; "napi_create_arraybuffer"; "napi_get_value_int64"; "napi_get_all_property_names"; "napi_resolve_deferred"; "napi_is_detached_arraybuffer"; "napi_create_string_utf8"; "napi_create_threadsafe_function"; "node_api_throw_syntax_error"; "napi_create_bigint_int64"; "napi_wrap"; "napi_set_property"; "napi_get_value_bigint_int64"; "napi_open_handle_scope"; "napi_create_error"; "napi_create_buffer"; "napi_cancel_async_work"; "napi_is_exception_pending"; "napi_acquire_threadsafe_function"; "napi_create_external"; "napi_get_threadsafe_function_context"; "napi_get_null"; "napi_create_string_utf16"; "node_api_create_external_string_utf16"; "napi_get_value_bigint_uint64"; "napi_module_register"; "napi_is_typedarray"; "napi_create_external_buffer"; "napi_get_new_target"; "napi_get_instance_data"; "napi_close_handle_scope"; "napi_get_value_string_utf16"; "napi_get_property_names"; "napi_is_arraybuffer"; "napi_get_cb_info"; "napi_define_properties"; "napi_add_env_cleanup_hook"; "node_api_get_module_file_name"; "napi_get_node_version"; "napi_create_int64"; "napi_create_double"; "napi_get_and_clear_last_exception"; "napi_create_reference"; "napi_get_typedarray_info"; "napi_call_threadsafe_function"; "napi_get_last_error_info"; "napi_create_array_with_length"; "napi_coerce_to_number"; "napi_get_global"; "napi_is_error"; "napi_set_instance_data"; "napi_create_typedarray"; "napi_throw_type_error"; "napi_has_property"; "napi_get_value_external"; "napi_create_range_error"; "napi_typeof"; "napi_ref_threadsafe_function"; "napi_create_bigint_uint64"; "napi_get_prototype"; "napi_adjust_external_memory"; "napi_release_threadsafe_function"; "napi_delete_async_work"; "napi_create_string_latin1"; "node_api_create_external_string_latin1"; "napi_is_array"; "napi_unref_threadsafe_function"; "napi_throw_error"; "napi_has_own_property"; "napi_get_reference_value"; "napi_remove_env_cleanup_hook"; "napi_get_value_string_utf8"; "napi_is_promise"; "napi_get_boolean"; "napi_run_script"; "napi_get_element"; "napi_get_named_property"; "napi_get_buffer_info"; "napi_get_value_bool"; "napi_reference_ref"; "napi_create_object"; "napi_create_promise"; "napi_create_int32"; "napi_escape_handle"; "napi_open_escapable_handle_scope"; "napi_throw"; "napi_get_value_double"; "napi_set_named_property"; "napi_call_function"; "napi_create_date"; "napi_object_freeze"; "napi_get_uv_event_loop"; "napi_get_value_string_latin1"; "napi_reject_deferred"; "napi_add_finalizer"; "napi_create_array"; "napi_delete_reference"; "napi_get_date_value"; "napi_create_dataview"; "napi_get_version"; "napi_define_class"; "napi_is_date"; "napi_remove_wrap"; "napi_delete_property"; "napi_instanceof"; "napi_create_buffer_copy"; "napi_delete_element"; "napi_object_seal"; "napi_queue_async_work"; "napi_get_value_bigint_words"; "napi_is_buffer"; "napi_get_array_length"; "napi_get_property"; "napi_new_instance"; "napi_set_element"; "napi_create_bigint_words"; "napi_strict_equals"; "napi_is_dataview"; "napi_close_escapable_handle_scope"; "napi_get_dataview_info"; "napi_get_value_int32"; "napi_unwrap"; "napi_throw_range_error"; "napi_coerce_to_bool"; "napi_create_uint32"; "napi_has_element"; "napi_create_external_arraybuffer"; "napi_create_symbol"; "node_api_symbol_for"; "napi_coerce_to_string"; "napi_create_type_error"; "napi_fatal_exception"; "napi_create_async_work"; "napi_async_init"; "node_api_create_property_key_utf16"; "napi_type_tag_object"; "napi_check_object_type_tag"; "node_api_post_finalizer"; "napi_add_async_cleanup_hook"; "napi_remove_async_cleanup_hook"; }; \ No newline at end of file +{ "node_api_create_syntax_error"; "napi_make_callback"; "napi_has_named_property"; "napi_async_destroy"; "napi_coerce_to_object"; "napi_get_arraybuffer_info"; "napi_detach_arraybuffer"; "napi_get_undefined"; "napi_reference_unref"; "napi_fatal_error"; "napi_open_callback_scope"; "napi_close_callback_scope"; "napi_get_value_uint32"; "napi_create_function"; "napi_create_arraybuffer"; "napi_get_value_int64"; "napi_get_all_property_names"; "napi_resolve_deferred"; "napi_is_detached_arraybuffer"; "napi_create_string_utf8"; "napi_create_threadsafe_function"; "node_api_throw_syntax_error"; "napi_create_bigint_int64"; "napi_wrap"; "napi_set_property"; "napi_get_value_bigint_int64"; "napi_open_handle_scope"; "napi_create_error"; "napi_create_buffer"; "napi_cancel_async_work"; "napi_is_exception_pending"; "napi_acquire_threadsafe_function"; "napi_create_external"; "napi_get_threadsafe_function_context"; "napi_get_null"; "napi_create_string_utf16"; "node_api_create_external_string_utf16"; "napi_get_value_bigint_uint64"; "napi_module_register"; "napi_is_typedarray"; "napi_create_external_buffer"; "napi_get_new_target"; "napi_get_instance_data"; "napi_close_handle_scope"; "napi_get_value_string_utf16"; "napi_get_property_names"; "napi_is_arraybuffer"; "napi_get_cb_info"; "napi_define_properties"; "napi_add_env_cleanup_hook"; "node_api_get_module_file_name"; "napi_get_node_version"; "napi_create_int64"; "napi_create_double"; "napi_get_and_clear_last_exception"; "napi_create_reference"; "napi_get_typedarray_info"; "napi_call_threadsafe_function"; "napi_get_last_error_info"; "napi_create_array_with_length"; "napi_coerce_to_number"; "napi_get_global"; "napi_is_error"; "napi_set_instance_data"; "napi_create_typedarray"; "napi_throw_type_error"; "napi_has_property"; "napi_get_value_external"; "napi_create_range_error"; "napi_typeof"; "napi_ref_threadsafe_function"; "napi_create_bigint_uint64"; "napi_get_prototype"; "napi_adjust_external_memory"; "napi_release_threadsafe_function"; "napi_delete_async_work"; "napi_create_string_latin1"; "node_api_create_external_string_latin1"; "napi_is_array"; "napi_unref_threadsafe_function"; "napi_throw_error"; "napi_has_own_property"; "napi_get_reference_value"; "napi_remove_env_cleanup_hook"; "napi_get_value_string_utf8"; "napi_is_promise"; "napi_get_boolean"; "napi_run_script"; "napi_get_element"; "napi_get_named_property"; "napi_get_buffer_info"; "napi_get_value_bool"; "napi_reference_ref"; "napi_create_object"; "napi_create_promise"; "napi_create_int32"; "napi_escape_handle"; "napi_open_escapable_handle_scope"; "napi_throw"; "napi_get_value_double"; "napi_set_named_property"; "napi_call_function"; "napi_create_date"; "napi_object_freeze"; "napi_get_uv_event_loop"; "napi_get_value_string_latin1"; "napi_reject_deferred"; "napi_add_finalizer"; "napi_create_array"; "napi_delete_reference"; "napi_get_date_value"; "napi_create_dataview"; "napi_get_version"; "napi_define_class"; "napi_is_date"; "napi_remove_wrap"; "napi_delete_property"; "napi_instanceof"; "napi_create_buffer_copy"; "napi_delete_element"; "napi_object_seal"; "napi_queue_async_work"; "napi_get_value_bigint_words"; "napi_is_buffer"; "napi_get_array_length"; "napi_get_property"; "napi_new_instance"; "napi_set_element"; "napi_create_bigint_words"; "napi_strict_equals"; "napi_is_dataview"; "napi_close_escapable_handle_scope"; "napi_get_dataview_info"; "napi_get_value_int32"; "napi_unwrap"; "napi_throw_range_error"; "napi_coerce_to_bool"; "napi_create_uint32"; "napi_has_element"; "napi_create_external_arraybuffer"; "napi_create_symbol"; "node_api_symbol_for"; "napi_coerce_to_string"; "napi_create_type_error"; "napi_fatal_exception"; "napi_create_async_work"; "napi_async_init"; "node_api_create_property_key_utf16"; "napi_type_tag_object"; "napi_check_object_type_tag"; "node_api_post_finalizer"; "napi_add_async_cleanup_hook"; "napi_remove_async_cleanup_hook"; "uv_mutex_init"; "uv_mutex_lock"; "uv_mutex_unlock"; "uv_mutex_destroy"; "uv_async_init"; "uv_async_send"; "uv_close"; }; \ No newline at end of file diff --git a/cli/napi/generated_symbol_exports_list_macos.def b/cli/napi/generated_symbol_exports_list_macos.def index cac7100c6f..36b2f37fa5 100644 --- a/cli/napi/generated_symbol_exports_list_macos.def +++ b/cli/napi/generated_symbol_exports_list_macos.def @@ -150,4 +150,11 @@ _napi_type_tag_object _napi_check_object_type_tag _node_api_post_finalizer _napi_add_async_cleanup_hook -_napi_remove_async_cleanup_hook \ No newline at end of file +_napi_remove_async_cleanup_hook +_uv_mutex_init +_uv_mutex_lock +_uv_mutex_unlock +_uv_mutex_destroy +_uv_async_init +_uv_async_send +_uv_close \ No newline at end of file diff --git a/cli/napi/generated_symbol_exports_list_windows.def b/cli/napi/generated_symbol_exports_list_windows.def index 5386b46e54..b7355112e7 100644 --- a/cli/napi/generated_symbol_exports_list_windows.def +++ b/cli/napi/generated_symbol_exports_list_windows.def @@ -152,4 +152,11 @@ EXPORTS napi_check_object_type_tag node_api_post_finalizer napi_add_async_cleanup_hook - napi_remove_async_cleanup_hook \ No newline at end of file + napi_remove_async_cleanup_hook + uv_mutex_init + uv_mutex_lock + uv_mutex_unlock + uv_mutex_destroy + uv_async_init + uv_async_send + uv_close \ No newline at end of file diff --git a/cli/napi/mod.rs b/cli/napi/mod.rs index 122d2ff060..811efb1ecc 100644 --- a/cli/napi/mod.rs +++ b/cli/napi/mod.rs @@ -18,3 +18,4 @@ pub mod js_native_api; pub mod node_api; pub mod util; +pub mod uv; diff --git a/cli/napi/node_api.rs b/cli/napi/node_api.rs index 81cb800a7d..2efb71c267 100644 --- a/cli/napi/node_api.rs +++ b/cli/napi/node_api.rs @@ -547,11 +547,16 @@ fn napi_delete_async_work(env: *mut Env, work: napi_async_work) -> napi_status { } #[napi_sym] -fn napi_get_uv_event_loop(env: *mut Env, uv_loop: *mut *mut ()) -> napi_status { - let env = check_env!(env); +fn napi_get_uv_event_loop( + env_ptr: *mut Env, + uv_loop: *mut *mut (), +) -> napi_status { + let env = check_env!(env_ptr); check_arg!(env, uv_loop); - // There is no uv_loop in Deno - napi_set_last_error(env, napi_generic_failure) + unsafe { + *uv_loop = env_ptr.cast(); + } + 0 } #[napi_sym] diff --git a/cli/napi/sym/Cargo.toml b/cli/napi/sym/Cargo.toml index 849d3cc87f..fef749fc46 100644 --- a/cli/napi/sym/Cargo.toml +++ b/cli/napi/sym/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "napi_sym" -version = "0.99.0" +version = "0.101.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/cli/napi/sym/symbol_exports.json b/cli/napi/sym/symbol_exports.json index 64b548d496..00946b8ed7 100644 --- a/cli/napi/sym/symbol_exports.json +++ b/cli/napi/sym/symbol_exports.json @@ -152,6 +152,13 @@ "napi_check_object_type_tag", "node_api_post_finalizer", "napi_add_async_cleanup_hook", - "napi_remove_async_cleanup_hook" + "napi_remove_async_cleanup_hook", + "uv_mutex_init", + "uv_mutex_lock", + "uv_mutex_unlock", + "uv_mutex_destroy", + "uv_async_init", + "uv_async_send", + "uv_close" ] } diff --git a/cli/napi/uv.rs b/cli/napi/uv.rs new file mode 100644 index 0000000000..d4cb5c0b36 --- /dev/null +++ b/cli/napi/uv.rs @@ -0,0 +1,231 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use deno_core::parking_lot::Mutex; +use deno_runtime::deno_napi::*; +use std::mem::MaybeUninit; +use std::ptr::addr_of_mut; + +#[allow(clippy::print_stderr)] +fn assert_ok(res: c_int) -> c_int { + if res != 0 { + eprintln!("bad result in uv polyfill: {res}"); + // don't panic because that might unwind into + // c/c++ + std::process::abort(); + } + res +} + +use crate::napi::js_native_api::napi_create_string_utf8; +use crate::napi::node_api::napi_create_async_work; +use crate::napi::node_api::napi_delete_async_work; +use crate::napi::node_api::napi_queue_async_work; +use std::ffi::c_int; + +const UV_MUTEX_SIZE: usize = { + #[cfg(unix)] + { + std::mem::size_of::() + } + #[cfg(windows)] + { + std::mem::size_of::( + ) + } +}; + +#[repr(C)] +struct uv_mutex_t { + mutex: Mutex<()>, + _padding: [MaybeUninit; const { + (UV_MUTEX_SIZE - size_of::>()) / size_of::() + }], +} + +#[no_mangle] +unsafe extern "C" fn uv_mutex_init(lock: *mut uv_mutex_t) -> c_int { + unsafe { + addr_of_mut!((*lock).mutex).write(Mutex::new(())); + 0 + } +} + +#[no_mangle] +unsafe extern "C" fn uv_mutex_lock(lock: *mut uv_mutex_t) { + unsafe { + let guard = (*lock).mutex.lock(); + // forget the guard so it doesn't unlock when it goes out of scope. + // we're going to unlock it manually + std::mem::forget(guard); + } +} + +#[no_mangle] +unsafe extern "C" fn uv_mutex_unlock(lock: *mut uv_mutex_t) { + unsafe { + (*lock).mutex.force_unlock(); + } +} + +#[no_mangle] +unsafe extern "C" fn uv_mutex_destroy(_lock: *mut uv_mutex_t) { + // no cleanup required +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +#[allow(dead_code)] +enum uv_handle_type { + UV_UNKNOWN_HANDLE = 0, + UV_ASYNC, + UV_CHECK, + UV_FS_EVENT, + UV_FS_POLL, + UV_HANDLE, + UV_IDLE, + UV_NAMED_PIPE, + UV_POLL, + UV_PREPARE, + UV_PROCESS, + UV_STREAM, + UV_TCP, + UV_TIMER, + UV_TTY, + UV_UDP, + UV_SIGNAL, + UV_FILE, + UV_HANDLE_TYPE_MAX, +} + +const UV_HANDLE_SIZE: usize = 96; + +#[repr(C)] +struct uv_handle_t { + // public members + pub data: *mut c_void, + pub r#loop: *mut uv_loop_t, + pub r#type: uv_handle_type, + + _padding: [MaybeUninit; const { + (UV_HANDLE_SIZE + - size_of::<*mut c_void>() + - size_of::<*mut uv_loop_t>() + - size_of::()) + / size_of::() + }], +} + +#[cfg(unix)] +const UV_ASYNC_SIZE: usize = 128; + +#[cfg(windows)] +const UV_ASYNC_SIZE: usize = 224; + +#[repr(C)] +struct uv_async_t { + // public members + pub data: *mut c_void, + pub r#loop: *mut uv_loop_t, + pub r#type: uv_handle_type, + // private + async_cb: uv_async_cb, + work: napi_async_work, + _padding: [MaybeUninit; const { + (UV_ASYNC_SIZE + - size_of::<*mut c_void>() + - size_of::<*mut uv_loop_t>() + - size_of::() + - size_of::() + - size_of::()) + / size_of::() + }], +} + +type uv_loop_t = Env; +type uv_async_cb = extern "C" fn(handle: *mut uv_async_t); +#[no_mangle] +unsafe extern "C" fn uv_async_init( + r#loop: *mut uv_loop_t, + // probably uninitialized + r#async: *mut uv_async_t, + async_cb: uv_async_cb, +) -> c_int { + unsafe { + addr_of_mut!((*r#async).r#loop).write(r#loop); + addr_of_mut!((*r#async).r#type).write(uv_handle_type::UV_ASYNC); + addr_of_mut!((*r#async).async_cb).write(async_cb); + + let mut resource_name: MaybeUninit = MaybeUninit::uninit(); + assert_ok(napi_create_string_utf8( + r#loop, + c"uv_async".as_ptr(), + usize::MAX, + resource_name.as_mut_ptr(), + )); + let resource_name = resource_name.assume_init(); + + let res = napi_create_async_work( + r#loop, + None::>.into(), + resource_name, + Some(async_exec_wrap), + None, + r#async.cast(), + addr_of_mut!((*r#async).work), + ); + -res + } +} + +#[no_mangle] +unsafe extern "C" fn uv_async_send(handle: *mut uv_async_t) -> c_int { + unsafe { -napi_queue_async_work((*handle).r#loop, (*handle).work) } +} + +type uv_close_cb = unsafe extern "C" fn(*mut uv_handle_t); + +#[no_mangle] +unsafe extern "C" fn uv_close(handle: *mut uv_handle_t, close: uv_close_cb) { + unsafe { + if handle.is_null() { + close(handle); + return; + } + if let uv_handle_type::UV_ASYNC = (*handle).r#type { + let handle: *mut uv_async_t = handle.cast(); + napi_delete_async_work((*handle).r#loop, (*handle).work); + } + close(handle); + } +} + +unsafe extern "C" fn async_exec_wrap(_env: napi_env, data: *mut c_void) { + let data: *mut uv_async_t = data.cast(); + unsafe { + ((*data).async_cb)(data); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sizes() { + assert_eq!( + std::mem::size_of::(), + UV_MUTEX_SIZE + ); + assert_eq!( + std::mem::size_of::(), + UV_HANDLE_SIZE + ); + assert_eq!( + std::mem::size_of::(), + UV_ASYNC_SIZE + ); + assert_eq!(std::mem::size_of::(), UV_MUTEX_SIZE); + assert_eq!(std::mem::size_of::(), UV_HANDLE_SIZE); + assert_eq!(std::mem::size_of::(), UV_ASYNC_SIZE); + } +} diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index ceef68135a..fc095ab16f 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -21,6 +22,7 @@ use crate::resolver::CliDenoResolverFs; use super::CliNpmResolver; use super::InnerCliNpmResolverRef; +use super::ResolvePkgFolderFromDenoReqError; pub type CliByonmNpmResolverCreateOptions = ByonmNpmResolverCreateOptions; @@ -31,18 +33,19 @@ pub type CliByonmNpmResolver = ByonmNpmResolver; struct CliByonmWrapper(Arc); impl NodeRequireResolver for CliByonmWrapper { - fn ensure_read_permission( + fn ensure_read_permission<'a>( &self, permissions: &mut dyn NodePermissions, - path: &Path, - ) -> Result<(), AnyError> { + path: &'a Path, + ) -> Result, AnyError> { if !path .components() .any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules") { - _ = permissions.check_read_path(path)?; + permissions.check_read_path(path) + } else { + Ok(Cow::Borrowed(path)) } - Ok(()) } } @@ -90,10 +93,11 @@ impl CliNpmResolver for CliByonmNpmResolver { &self, req: &PackageReq, referrer: &Url, - ) -> Result { + ) -> Result { ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req( self, req, referrer, ) + .map_err(ResolvePkgFolderFromDenoReqError::Byonm) } fn check_state_hash(&self) -> Option { diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 6f6fa4bf87..ec50a9c65a 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -20,6 +21,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; +use deno_runtime::colors; use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodeRequireResolver; @@ -51,6 +53,7 @@ use self::resolvers::NpmPackageFsResolver; use super::CliNpmResolver; use super::InnerCliNpmResolverRef; +use super::ResolvePkgFolderFromDenoReqError; mod cache; mod registry; @@ -477,6 +480,25 @@ impl ManagedCliNpmResolver { self.resolution.resolve_pkg_id_from_pkg_req(req) } + pub fn ensure_no_pkg_json_dep_errors(&self) -> Result<(), AnyError> { + for err in self.npm_install_deps_provider.pkg_json_dep_errors() { + match err { + deno_package_json::PackageJsonDepValueParseError::VersionReq(_) => { + return Err( + AnyError::from(err.clone()) + .context("Failed to install from package.json"), + ); + } + deno_package_json::PackageJsonDepValueParseError::Unsupported { + .. + } => { + log::warn!("{} {} in package.json", colors::yellow("Warning"), err) + } + } + } + Ok(()) + } + /// Ensures that the top level `package.json` dependencies are installed. /// This may set up the `node_modules` directory. /// @@ -488,6 +510,7 @@ impl ManagedCliNpmResolver { if !self.top_level_install_flag.raise() { return Ok(false); // already did this } + let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); if pkg_json_remote_pkgs.is_empty() { return Ok(false); @@ -571,11 +594,11 @@ impl NpmResolver for ManagedCliNpmResolver { } impl NodeRequireResolver for ManagedCliNpmResolver { - fn ensure_read_permission( + fn ensure_read_permission<'a>( &self, permissions: &mut dyn NodePermissions, - path: &Path, - ) -> Result<(), AnyError> { + path: &'a Path, + ) -> Result, AnyError> { self.fs_resolver.ensure_read_permission(permissions, path) } } @@ -649,9 +672,13 @@ impl CliNpmResolver for ManagedCliNpmResolver { &self, req: &PackageReq, _referrer: &ModuleSpecifier, - ) -> Result { - let pkg_id = self.resolve_pkg_id_from_pkg_req(req)?; - self.resolve_pkg_folder_from_pkg_id(&pkg_id) + ) -> Result { + let pkg_id = self + .resolve_pkg_id_from_pkg_req(req) + .map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(err.into()))?; + self + .resolve_pkg_folder_from_pkg_id(&pkg_id) + .map_err(ResolvePkgFolderFromDenoReqError::Managed) } fn check_state_hash(&self) -> Option { diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs index 8df4debc5c..867bb4168a 100644 --- a/cli/npm/managed/resolvers/common.rs +++ b/cli/npm/managed/resolvers/common.rs @@ -3,6 +3,7 @@ pub mod bin_entries; pub mod lifecycle_scripts; +use std::borrow::Cow; use std::collections::HashMap; use std::io::ErrorKind; use std::path::Path; @@ -62,11 +63,12 @@ pub trait NpmPackageFsResolver: Send + Sync { async fn cache_packages(&self) -> Result<(), AnyError>; - fn ensure_read_permission( + #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] + fn ensure_read_permission<'a>( &self, permissions: &mut dyn NodePermissions, - path: &Path, - ) -> Result<(), AnyError>; + path: &'a Path, + ) -> Result, AnyError>; } #[derive(Debug)] @@ -85,11 +87,15 @@ impl RegistryReadPermissionChecker { } } - pub fn ensure_registry_read_permission( + pub fn ensure_registry_read_permission<'a>( &self, permissions: &mut dyn NodePermissions, - path: &Path, - ) -> Result<(), AnyError> { + path: &'a Path, + ) -> Result, AnyError> { + if permissions.query_read_all() { + return Ok(Cow::Borrowed(path)); // skip permissions checks below + } + // allow reading if it's in the node_modules let is_path_in_node_modules = path.starts_with(&self.registry_path) && path @@ -118,20 +124,20 @@ impl RegistryReadPermissionChecker { }, } }; - let Some(registry_path_canon) = canonicalize(&self.registry_path)? else { - return Ok(()); // not exists, allow reading - }; - let Some(path_canon) = canonicalize(path)? else { - return Ok(()); // not exists, allow reading - }; - - if path_canon.starts_with(registry_path_canon) { - return Ok(()); + if let Some(registry_path_canon) = canonicalize(&self.registry_path)? { + if let Some(path_canon) = canonicalize(path)? { + if path_canon.starts_with(registry_path_canon) { + return Ok(Cow::Owned(path_canon)); + } + } else if path.starts_with(registry_path_canon) + || path.starts_with(&self.registry_path) + { + return Ok(Cow::Borrowed(path)); + } } } - _ = permissions.check_read_path(path)?; - Ok(()) + permissions.check_read_path(path) } } diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs index ca5722c869..5be315e992 100644 --- a/cli/npm/managed/resolvers/global.rs +++ b/cli/npm/managed/resolvers/global.rs @@ -183,11 +183,11 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { Ok(()) } - fn ensure_read_permission( + fn ensure_read_permission<'a>( &self, permissions: &mut dyn NodePermissions, - path: &Path, - ) -> Result<(), AnyError> { + path: &'a Path, + ) -> Result, AnyError> { self .registry_read_permission_checker .ensure_registry_read_permission(permissions, path) diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 59ba27d059..63a972a431 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -257,11 +257,11 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { .await } - fn ensure_read_permission( + fn ensure_read_permission<'a>( &self, permissions: &mut dyn NodePermissions, - path: &Path, - ) -> Result<(), AnyError> { + path: &'a Path, + ) -> Result, AnyError> { self .registry_read_permission_checker .ensure_registry_read_permission(permissions, path) @@ -343,6 +343,14 @@ async fn sync_resolution_with_fs( }, ); let packages_with_deprecation_warnings = Arc::new(Mutex::new(Vec::new())); + + let mut package_tags: HashMap<&PackageNv, Vec<&str>> = HashMap::new(); + for (package_req, package_nv) in snapshot.package_reqs() { + if let Some(tag) = package_req.version_req.tag() { + package_tags.entry(package_nv).or_default().push(tag); + } + } + for package in &package_partitions.packages { if let Some(current_pkg) = newest_packages_by_name.get_mut(&package.id.nv.name) @@ -357,11 +365,29 @@ async fn sync_resolution_with_fs( let package_folder_name = get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); let folder_path = deno_local_registry_dir.join(&package_folder_name); + let tags = package_tags + .get(&package.id.nv) + .map(|tags| tags.join(",")) + .unwrap_or_default(); + enum PackageFolderState { + UpToDate, + Uninitialized, + TagsOutdated, + } let initialized_file = folder_path.join(".initialized"); + let package_state = std::fs::read_to_string(&initialized_file) + .map(|s| { + if s != tags { + PackageFolderState::TagsOutdated + } else { + PackageFolderState::UpToDate + } + }) + .unwrap_or(PackageFolderState::Uninitialized); if !cache .cache_setting() .should_use_for_npm_package(&package.id.nv.name) - || !initialized_file.exists() + || matches!(package_state, PackageFolderState::Uninitialized) { // cache bust the dep from the dep setup cache so the symlinks // are forced to be recreated @@ -371,6 +397,7 @@ async fn sync_resolution_with_fs( let bin_entries_to_setup = bin_entries.clone(); let packages_with_deprecation_warnings = packages_with_deprecation_warnings.clone(); + cache_futures.push(async move { tarball_cache .ensure_package(&package.id.nv, &package.dist) @@ -389,7 +416,7 @@ async fn sync_resolution_with_fs( move || { clone_dir_recursive(&cache_folder, &package_path)?; // write out a file that indicates this folder has been initialized - fs::write(initialized_file, "")?; + fs::write(initialized_file, tags)?; Ok::<_, AnyError>(()) } @@ -410,6 +437,8 @@ async fn sync_resolution_with_fs( drop(pb_guard); // explicit for clarity Ok::<_, AnyError>(()) }); + } else if matches!(package_state, PackageFolderState::TagsOutdated) { + fs::write(initialized_file, tags)?; } let sub_node_modules = folder_path.join("node_modules"); @@ -518,9 +547,9 @@ async fn sync_resolution_with_fs( // linked into the root match found_names.entry(remote_alias) { Entry::Occupied(nv) => { - alias_clashes - || remote.req.name != nv.get().name // alias to a different package (in case of duplicate aliases) - || !remote.req.version_req.matches(&nv.get().version) // incompatible version + // alias to a different package (in case of duplicate aliases) + // or the version doesn't match the version in the root node_modules + alias_clashes || &remote_pkg.id.nv != *nv.get() } Entry::Vacant(entry) => { entry.insert(&remote_pkg.id.nv); diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 1e3c752aee..53baaf77b4 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -14,11 +14,13 @@ use deno_core::error::AnyError; use deno_core::serde_json; use deno_npm::registry::NpmPackageInfo; use deno_resolver::npm::ByonmNpmResolver; +use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; use deno_runtime::deno_node::NodeRequireResolver; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use node_resolver::NpmResolver; +use thiserror::Error; use crate::args::npm_registry_url; use crate::file_fetcher::FileFetcher; @@ -29,6 +31,14 @@ pub use self::managed::CliNpmResolverManagedCreateOptions; pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::ManagedCliNpmResolver; +#[derive(Debug, Error)] +pub enum ResolvePkgFolderFromDenoReqError { + #[error(transparent)] + Managed(deno_core::error::AnyError), + #[error(transparent)] + Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError), +} + pub enum CliNpmResolverCreateOptions { Managed(CliNpmResolverManagedCreateOptions), Byonm(CliByonmNpmResolverCreateOptions), @@ -93,7 +103,7 @@ pub trait CliNpmResolver: NpmResolver { &self, req: &PackageReq, referrer: &ModuleSpecifier, - ) -> Result; + ) -> Result; /// Returns a hash returning the state of the npm resolver /// or `None` if the state currently can't be determined. diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 8f37632c84..5e18546a28 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -195,7 +195,7 @@ pub async fn doc( kind_with_drilldown: deno_doc::html::DocNodeKindWithDrilldown::Other(node.kind()), inner: Rc::new(node), - drilldown_parent_kind: None, + drilldown_name: None, parent: None, }) .collect::>(), @@ -262,7 +262,7 @@ pub async fn doc( } struct DocResolver { - deno_ns: std::collections::HashSet>, + deno_ns: std::collections::HashMap, Option>>, strip_trailing_html: bool, } @@ -286,7 +286,7 @@ impl deno_doc::html::HrefResolver for DocResolver { } fn resolve_global_symbol(&self, symbol: &[String]) -> Option { - if self.deno_ns.contains(symbol) { + if self.deno_ns.contains_key(symbol) { Some(format!( "https://deno.land/api@v{}?s={}", env!("CARGO_PKG_VERSION"), @@ -455,7 +455,7 @@ impl deno_doc::html::HrefResolver for NodeDocResolver { fn generate_docs_directory( doc_nodes_by_url: IndexMap>, html_options: &DocHtmlFlag, - deno_ns: std::collections::HashSet>, + deno_ns: std::collections::HashMap, Option>>, rewrite_map: Option>, ) -> Result<(), AnyError> { let cwd = std::env::current_dir().context("Failed to get CWD")?; @@ -513,7 +513,6 @@ fn generate_docs_directory( rewrite_map, href_resolver, usage_composer: None, - composable_output: false, category_docs, disable_search: internal_env.is_some(), symbol_redirect_map, diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index bc8769de1c..ed86e86c79 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -298,6 +298,10 @@ async fn install_local( } InstallFlagsLocal::TopLevel => { let factory = CliFactory::from_flags(flags); + // surface any errors in the package.json + if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() { + npm_resolver.ensure_no_pkg_json_dep_errors()?; + } crate::tools::registry::cache_top_level_deps(&factory, None).await?; if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() { diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs index 71e947dddb..0ffd0da1ee 100644 --- a/cli/tools/jupyter/mod.rs +++ b/cli/tools/jupyter/mod.rs @@ -357,56 +357,74 @@ pub struct JupyterReplSession { impl JupyterReplSession { pub async fn start(&mut self) { + let mut poll_worker = true; loop { - let Some(msg) = self.rx.recv().await else { - break; - }; - let resp = match msg { - JupyterReplRequest::LspCompletions { - line_text, - position, - } => JupyterReplResponse::LspCompletions( - self.lsp_completions(&line_text, position).await, - ), - JupyterReplRequest::JsGetProperties { object_id } => { - JupyterReplResponse::JsGetProperties( - self.get_properties(object_id).await, - ) - } - JupyterReplRequest::JsEvaluate { expr } => { - JupyterReplResponse::JsEvaluate(self.evaluate(expr).await) - } - JupyterReplRequest::JsGlobalLexicalScopeNames => { - JupyterReplResponse::JsGlobalLexicalScopeNames( - self.global_lexical_scope_names().await, - ) - } - JupyterReplRequest::JsEvaluateLineWithObjectWrapping { line } => { - JupyterReplResponse::JsEvaluateLineWithObjectWrapping( - self.evaluate_line_with_object_wrapping(&line).await, - ) - } - JupyterReplRequest::JsCallFunctionOnArgs { - function_declaration, - args, - } => JupyterReplResponse::JsCallFunctionOnArgs( - self - .call_function_on_args(function_declaration, &args) - .await, - ), - JupyterReplRequest::JsCallFunctionOn { arg0, arg1 } => { - JupyterReplResponse::JsCallFunctionOn( - self.call_function_on(arg0, arg1).await, - ) - } - }; + tokio::select! { + biased; - let Ok(()) = self.tx.send(resp) else { - break; - }; + maybe_message = self.rx.recv() => { + let Some(msg) = maybe_message else { + break; + }; + if self.handle_message(msg).await.is_err() { + break; + } + poll_worker = true; + }, + _ = self.repl_session.run_event_loop(), if poll_worker => { + poll_worker = false; + } + } } } + async fn handle_message( + &mut self, + msg: JupyterReplRequest, + ) -> Result<(), AnyError> { + let resp = match msg { + JupyterReplRequest::LspCompletions { + line_text, + position, + } => JupyterReplResponse::LspCompletions( + self.lsp_completions(&line_text, position).await, + ), + JupyterReplRequest::JsGetProperties { object_id } => { + JupyterReplResponse::JsGetProperties( + self.get_properties(object_id).await, + ) + } + JupyterReplRequest::JsEvaluate { expr } => { + JupyterReplResponse::JsEvaluate(self.evaluate(expr).await) + } + JupyterReplRequest::JsGlobalLexicalScopeNames => { + JupyterReplResponse::JsGlobalLexicalScopeNames( + self.global_lexical_scope_names().await, + ) + } + JupyterReplRequest::JsEvaluateLineWithObjectWrapping { line } => { + JupyterReplResponse::JsEvaluateLineWithObjectWrapping( + self.evaluate_line_with_object_wrapping(&line).await, + ) + } + JupyterReplRequest::JsCallFunctionOnArgs { + function_declaration, + args, + } => JupyterReplResponse::JsCallFunctionOnArgs( + self + .call_function_on_args(function_declaration, &args) + .await, + ), + JupyterReplRequest::JsCallFunctionOn { arg0, arg1 } => { + JupyterReplResponse::JsCallFunctionOn( + self.call_function_on(arg0, arg1).await, + ) + } + }; + + self.tx.send(resp).map_err(|e| e.into()) + } + pub async fn lsp_completions( &mut self, line_text: &str, diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index c92710f460..f56774e8eb 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -558,12 +558,7 @@ pub async fn add( result.context("Failed to update configuration file")?; } - // clear the previously cached package.json from memory before reloading it - node_resolver::PackageJsonThreadLocalCache::clear(); - // make a new CliFactory to pick up the updated config file - let cli_factory = CliFactory::from_flags(flags); - // cache deps - cache_deps::cache_top_level_deps(&cli_factory, Some(jsr_resolver)).await?; + npm_install_after_modification(flags, Some(jsr_resolver)).await?; Ok(()) } @@ -786,15 +781,33 @@ pub async fn remove( config.commit().await?; } - // Update deno.lock - node_resolver::PackageJsonThreadLocalCache::clear(); - let cli_factory = CliFactory::from_flags(flags); - cache_deps::cache_top_level_deps(&cli_factory, None).await?; + npm_install_after_modification(flags, None).await?; } Ok(()) } +async fn npm_install_after_modification( + flags: Arc, + // explicitly provided to prevent redownloading + jsr_resolver: Option>, +) -> Result<(), AnyError> { + // clear the previously cached package.json from memory before reloading it + node_resolver::PackageJsonThreadLocalCache::clear(); + + // make a new CliFactory to pick up the updated config file + let cli_factory = CliFactory::from_flags(flags); + // surface any errors in the package.json + let npm_resolver = cli_factory.npm_resolver().await?; + if let Some(npm_resolver) = npm_resolver.as_managed() { + npm_resolver.ensure_no_pkg_json_dep_errors()?; + } + // npm install + cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).await?; + + Ok(()) +} + fn update_config_file_content< I: IntoIterator)>, >( diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index 7d1773b343..c8258e6009 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -11,6 +11,7 @@ use deno_core::futures::StreamExt; use deno_semver::package::PackageReq; pub async fn cache_top_level_deps( + // todo(dsherret): don't pass the factory into this function. Instead use ctor deps factory: &CliFactory, jsr_resolver: Option>, ) -> Result<(), AnyError> { diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index da8cef30bd..3caf7b9efd 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_broadcast_channel" -version = "0.163.0" +version = "0.165.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index 225870944a..71ca34380c 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cache" -version = "0.101.0" +version = "0.103.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index db59822049..47c37560ec 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_canvas" -version = "0.38.0" +version = "0.40.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/console/01_console.js b/ext/console/01_console.js index 1ca23d5a49..d9acc958a0 100644 --- a/ext/console/01_console.js +++ b/ext/console/01_console.js @@ -1301,7 +1301,9 @@ function getKeys(value, showHidden) { ArrayPrototypePushApply(keys, ArrayPrototypeFilter(symbols, filter)); } } - keys = ArrayPrototypeFilter(keys, (key) => key !== "cause"); + if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, value)) { + keys = ArrayPrototypeFilter(keys, (key) => key !== "cause"); + } return keys; } diff --git a/ext/console/Cargo.toml b/ext/console/Cargo.toml index d2125bbcc1..5c143ca18c 100644 --- a/ext/console/Cargo.toml +++ b/ext/console/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_console" -version = "0.169.0" +version = "0.171.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index 9d3fa20092..a773521f5b 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cron" -version = "0.49.0" +version = "0.51.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 8e43b76f7c..63b1905145 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -18,21 +18,27 @@ import { op_crypto_decrypt, op_crypto_derive_bits, op_crypto_derive_bits_x25519, + op_crypto_derive_bits_x448, op_crypto_encrypt, op_crypto_export_key, op_crypto_export_pkcs8_ed25519, op_crypto_export_pkcs8_x25519, + op_crypto_export_pkcs8_x448, op_crypto_export_spki_ed25519, op_crypto_export_spki_x25519, + op_crypto_export_spki_x448, op_crypto_generate_ed25519_keypair, op_crypto_generate_key, op_crypto_generate_x25519_keypair, + op_crypto_generate_x448_keypair, op_crypto_get_random_values, op_crypto_import_key, op_crypto_import_pkcs8_ed25519, op_crypto_import_pkcs8_x25519, + op_crypto_import_pkcs8_x448, op_crypto_import_spki_ed25519, op_crypto_import_spki_x25519, + op_crypto_import_spki_x448, op_crypto_jwk_x_ed25519, op_crypto_random_uuid, op_crypto_sign_ed25519, @@ -134,6 +140,7 @@ const supportedAlgorithms = { "AES-KW": "AesKeyGenParams", "HMAC": "HmacKeyGenParams", "X25519": null, + "X448": null, "Ed25519": null, }, "sign": { @@ -165,12 +172,14 @@ const supportedAlgorithms = { "AES-KW": null, "Ed25519": null, "X25519": null, + "X448": null, }, "deriveBits": { "HKDF": "HkdfParams", "PBKDF2": "Pbkdf2Params", "ECDH": "EcdhKeyDeriveParams", "X25519": "EcdhKeyDeriveParams", + "X448": "EcdhKeyDeriveParams", }, "encrypt": { "RSA-OAEP": "RsaOaepParams", @@ -1037,6 +1046,10 @@ class SubtleCrypto { result = exportKeyEd25519(format, key, innerKey); break; } + case "X448": { + result = exportKeyX448(format, key, innerKey); + break; + } case "X25519": { result = exportKeyX25519(format, key, innerKey); break; @@ -1954,6 +1967,48 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { return generateKeyAES(normalizedAlgorithm, extractable, usages); } + case "X448": { + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + const privateKeyData = new Uint8Array(56); + const publicKeyData = new Uint8Array(56); + + op_crypto_generate_x448_keypair(privateKeyData, publicKeyData); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const publicHandle = {}; + WeakMapPrototypeSet(KEY_STORE, publicHandle, publicKeyData); + + const algorithm = { + name: algorithmName, + }; + + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, []), + algorithm, + publicHandle, + ); + + const privateKey = constructKey( + "private", + extractable, + usageIntersection(usages, ["deriveKey", "deriveBits"]), + algorithm, + handle, + ); + + return { publicKey, privateKey }; + } case "X25519": { if ( ArrayPrototypeFind( @@ -2100,6 +2155,211 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { } } +function importKeyX448( + format, + keyData, + extractable, + keyUsages, +) { + switch (format) { + case "raw": { + // 1. + if (keyUsages.length > 0) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, keyData); + + // 2-3. + const algorithm = { + name: "X448", + }; + + // 4-6. + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "spki": { + // 1. + if (keyUsages.length > 0) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + + const publicKeyData = new Uint8Array(56); + if (!op_crypto_import_spki_x448(keyData, publicKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: "X448", + }; + + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + + const privateKeyData = new Uint8Array(32); + if (!op_crypto_import_pkcs8_x448(keyData, privateKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const algorithm = { + name: "X448", + }; + + return constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + } + case "jwk": { + // 1. + const jwk = keyData; + + // 2. + if (jwk.d !== undefined) { + if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + ["deriveKey", "deriveBits"], + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + } + + // 3. + if (jwk.d === undefined && keyUsages.length > 0) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + + // 4. + if (jwk.kty !== "OKP") { + throw new DOMException("Invalid key type", "DataError"); + } + + // 5. + if (jwk.crv !== "X448") { + throw new DOMException("Invalid curve", "DataError"); + } + + // 6. + if (keyUsages.length > 0 && jwk.use !== undefined) { + if (jwk.use !== "enc") { + throw new DOMException("Invalid key use", "DataError"); + } + } + + // 7. + if (jwk.key_ops !== undefined) { + if ( + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); + } + + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); + } + } + + // 8. + if (jwk.ext !== undefined && jwk.ext === false && extractable) { + throw new DOMException("Invalid key extractability", "DataError"); + } + + // 9. + if (jwk.d !== undefined) { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const privateKeyData = op_crypto_base64url_decode(jwk.d); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const algorithm = { + name: "X448", + }; + + return constructKey( + "private", + extractable, + usageIntersection(keyUsages, ["deriveKey", "deriveBits"]), + algorithm, + handle, + ); + } else { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const publicKeyData = op_crypto_base64url_decode(jwk.x); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: "X448", + }; + + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} + function importKeyEd25519( format, keyData, @@ -3358,6 +3618,14 @@ async function importKeyInner( ["wrapKey", "unwrapKey"], ); } + case "X448": { + return importKeyX448( + format, + keyData, + extractable, + keyUsages, + ); + } case "X25519": { return importKeyX25519( format, @@ -4162,6 +4430,66 @@ function exportKeyEd25519(format, key, innerKey) { } } +function exportKeyX448(format, key, innerKey) { + switch (format) { + case "raw": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + // 2-3. + return TypedArrayPrototypeGetBuffer(innerKey); + } + case "spki": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + const spkiDer = op_crypto_export_spki_x448(innerKey); + return TypedArrayPrototypeGetBuffer(spkiDer); + } + case "pkcs8": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key is not a private key", + "InvalidAccessError", + ); + } + + const pkcs8Der = op_crypto_export_pkcs8_x448( + new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]), + ); + pkcs8Der[15] = 0x20; + return TypedArrayPrototypeGetBuffer(pkcs8Der); + } + case "jwk": { + if (key[_type] === "private") { + throw new DOMException("Not implemented", "NotSupportedError"); + } + const x = op_crypto_base64url_encode(innerKey); + const jwk = { + kty: "OKP", + crv: "X448", + x, + "key_ops": key.usages, + ext: key[_extractable], + }; + return jwk; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} + function exportKeyX25519(format, key, innerKey) { switch (format) { case "raw": { @@ -4519,6 +4847,55 @@ async function deriveBits(normalizedAlgorithm, baseKey, length) { return TypedArrayPrototypeGetBuffer(buf); } + case "X448": { + // 1. + if (baseKey[_type] !== "private") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 2. + const publicKey = normalizedAlgorithm.public; + // 3. + if (publicKey[_type] !== "public") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 4. + if (publicKey[_algorithm].name !== baseKey[_algorithm].name) { + throw new DOMException( + "Algorithm mismatch", + "InvalidAccessError", + ); + } + + // 5. + const kHandle = baseKey[_handle]; + const k = WeakMapPrototypeGet(KEY_STORE, kHandle); + + const uHandle = publicKey[_handle]; + const u = WeakMapPrototypeGet(KEY_STORE, uHandle); + + const secret = new Uint8Array(56); + const isIdentity = op_crypto_derive_bits_x448(k, u, secret); + + // 6. + if (isIdentity) { + throw new DOMException("Invalid key", "OperationError"); + } + + // 7. + if (length === null) { + return TypedArrayPrototypeGetBuffer(secret); + } else if ( + TypedArrayPrototypeGetByteLength(secret) * 8 < length + ) { + throw new DOMException("Invalid length", "OperationError"); + } else { + return ArrayBufferPrototypeSlice( + TypedArrayPrototypeGetBuffer(secret), + 0, + MathCeil(length / 8), + ); + } + } case "X25519": { // 1. if (baseKey[_type] !== "private") { diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 5c0865e20a..c81c8f6a70 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_crypto" -version = "0.183.0" +version = "0.185.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -24,6 +24,7 @@ ctr = "0.9.1" curve25519-dalek = "4.1.3" deno_core.workspace = true deno_web.workspace = true +ed448-goldilocks = { version = "0.8.3", features = ["zeroize"] } elliptic-curve = { version = "0.13.1", features = ["std", "pem"] } num-traits = "0.2.14" once_cell.workspace = true diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 7aa3462c76..c96029bf4a 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -64,6 +64,7 @@ mod import_key; mod key; mod shared; mod x25519; +mod x448; pub use crate::decrypt::op_crypto_decrypt; pub use crate::encrypt::op_crypto_encrypt; @@ -98,6 +99,14 @@ deno_core::extension!(deno_crypto, x25519::op_crypto_derive_bits_x25519, x25519::op_crypto_import_spki_x25519, x25519::op_crypto_import_pkcs8_x25519, + x25519::op_crypto_export_spki_x25519, + x25519::op_crypto_export_pkcs8_x25519, + x448::op_crypto_generate_x448_keypair, + x448::op_crypto_derive_bits_x448, + x448::op_crypto_import_spki_x448, + x448::op_crypto_import_pkcs8_x448, + x448::op_crypto_export_spki_x448, + x448::op_crypto_export_pkcs8_x448, ed25519::op_crypto_generate_ed25519_keypair, ed25519::op_crypto_import_spki_ed25519, ed25519::op_crypto_import_pkcs8_ed25519, @@ -106,8 +115,6 @@ deno_core::extension!(deno_crypto, ed25519::op_crypto_export_spki_ed25519, ed25519::op_crypto_export_pkcs8_ed25519, ed25519::op_crypto_jwk_x_ed25519, - x25519::op_crypto_export_spki_x25519, - x25519::op_crypto_export_pkcs8_x25519, ], esm = [ "00_crypto.js" ], options = { diff --git a/ext/crypto/x25519.rs b/ext/crypto/x25519.rs index 8fcad3ef21..cdbd1d7c8f 100644 --- a/ext/crypto/x25519.rs +++ b/ext/crypto/x25519.rs @@ -47,10 +47,10 @@ pub fn op_crypto_derive_bits_x25519( let sh_sec = x25519_dalek::x25519(k, u); let point = MontgomeryPoint(sh_sec); if point.ct_eq(&MONTGOMERY_IDENTITY).unwrap_u8() == 1 { - return false; + return true; } secret.copy_from_slice(&sh_sec); - true + false } // id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 } diff --git a/ext/crypto/x448.rs b/ext/crypto/x448.rs new file mode 100644 index 0000000000..3c8f24c319 --- /dev/null +++ b/ext/crypto/x448.rs @@ -0,0 +1,147 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_core::error::custom_error; +use deno_core::error::AnyError; +use deno_core::op2; +use deno_core::ToJsBuffer; +use ed448_goldilocks::curve::MontgomeryPoint; +use ed448_goldilocks::Scalar; +use elliptic_curve::pkcs8::PrivateKeyInfo; +use elliptic_curve::subtle::ConstantTimeEq; +use rand::rngs::OsRng; +use rand::RngCore; +use spki::der::asn1::BitString; +use spki::der::Decode; +use spki::der::Encode; + +#[op2(fast)] +pub fn op_crypto_generate_x448_keypair( + #[buffer] pkey: &mut [u8], + #[buffer] pubkey: &mut [u8], +) { + let mut rng = OsRng; + rng.fill_bytes(pkey); + + // x448(pkey, 5) + let point = &MontgomeryPoint::generator() + * &Scalar::from_bytes(pkey.try_into().unwrap()); + pubkey.copy_from_slice(&point.0); +} + +const MONTGOMERY_IDENTITY: MontgomeryPoint = MontgomeryPoint([0; 56]); + +#[op2(fast)] +pub fn op_crypto_derive_bits_x448( + #[buffer] k: &[u8], + #[buffer] u: &[u8], + #[buffer] secret: &mut [u8], +) -> bool { + let k: [u8; 56] = k.try_into().expect("Expected byteLength 56"); + let u: [u8; 56] = u.try_into().expect("Expected byteLength 56"); + + // x448(k, u) + let point = &MontgomeryPoint(u) * &Scalar::from_bytes(k); + if point.ct_eq(&MONTGOMERY_IDENTITY).unwrap_u8() == 1 { + return true; + } + + secret.copy_from_slice(&point.0); + false +} + +// id-X448 OBJECT IDENTIFIER ::= { 1 3 101 111 } +const X448_OID: const_oid::ObjectIdentifier = + const_oid::ObjectIdentifier::new_unwrap("1.3.101.111"); + +#[op2] +#[serde] +pub fn op_crypto_export_spki_x448( + #[buffer] pubkey: &[u8], +) -> Result { + let key_info = spki::SubjectPublicKeyInfo { + algorithm: spki::AlgorithmIdentifierRef { + oid: X448_OID, + parameters: None, + }, + subject_public_key: BitString::from_bytes(pubkey)?, + }; + Ok( + key_info + .to_der() + .map_err(|_| { + custom_error("DOMExceptionOperationError", "Failed to export key") + })? + .into(), + ) +} + +#[op2] +#[serde] +pub fn op_crypto_export_pkcs8_x448( + #[buffer] pkey: &[u8], +) -> Result { + use rsa::pkcs1::der::Encode; + + let pk_info = rsa::pkcs8::PrivateKeyInfo { + public_key: None, + algorithm: rsa::pkcs8::AlgorithmIdentifierRef { + oid: X448_OID, + parameters: None, + }, + private_key: pkey, // OCTET STRING + }; + + let mut buf = Vec::new(); + pk_info.encode_to_vec(&mut buf)?; + Ok(buf.into()) +} + +#[op2(fast)] +pub fn op_crypto_import_spki_x448( + #[buffer] key_data: &[u8], + #[buffer] out: &mut [u8], +) -> bool { + // 2-3. + let pk_info = match spki::SubjectPublicKeyInfoRef::try_from(key_data) { + Ok(pk_info) => pk_info, + Err(_) => return false, + }; + // 4. + let alg = pk_info.algorithm.oid; + if alg != X448_OID { + return false; + } + // 5. + if pk_info.algorithm.parameters.is_some() { + return false; + } + out.copy_from_slice(pk_info.subject_public_key.raw_bytes()); + true +} + +#[op2(fast)] +pub fn op_crypto_import_pkcs8_x448( + #[buffer] key_data: &[u8], + #[buffer] out: &mut [u8], +) -> bool { + // 2-3. + let pk_info = match PrivateKeyInfo::from_der(key_data) { + Ok(pk_info) => pk_info, + Err(_) => return false, + }; + // 4. + let alg = pk_info.algorithm.oid; + if alg != X448_OID { + return false; + } + // 5. + if pk_info.algorithm.parameters.is_some() { + return false; + } + // 6. + // CurvePrivateKey ::= OCTET STRING + if pk_info.private_key.len() != 56 { + return false; + } + out.copy_from_slice(&pk_info.private_key[2..]); + true +} diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index 732bf47c27..cc9e4f03d3 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fetch" -version = "0.193.0" +version = "0.195.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index 158ab1b489..23a2aee1cd 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_ffi" -version = "0.156.0" +version = "0.158.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index fc223e1dc8..606c00ad8c 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fs" -version = "0.79.0" +version = "0.81.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index 2c47903f01..b7637bec33 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_http" -version = "0.167.0" +version = "0.169.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 72cdfcc7e3..d45834a8fe 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_io" -version = "0.79.0" +version = "0.81.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index 7d002aa122..14c3514ffd 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_kv" -version = "0.77.0" +version = "0.79.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index 9f3a2951b2..ade789ff80 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_napi" -version = "0.100.0" +version = "0.102.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index c75367a4b1..9f72456b90 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_net" -version = "0.161.0" +version = "0.163.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 29f7722999..c5f07210b2 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_node" -version = "0.106.0" +version = "0.108.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node/lib.rs b/ext/node/lib.rs index d23c072042..03462f36fa 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -66,6 +66,7 @@ pub trait NodePermissions { &mut self, path: &'a Path, ) -> Result, AnyError>; + fn query_read_all(&mut self) -> bool; fn check_sys(&mut self, kind: &str, api_name: &str) -> Result<(), AnyError>; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_write_with_api_name( @@ -103,6 +104,10 @@ impl NodePermissions for deno_permissions::PermissionsContainer { deno_permissions::PermissionsContainer::check_read_path(self, path, None) } + fn query_read_all(&mut self) -> bool { + deno_permissions::PermissionsContainer::query_read_all(self) + } + #[inline(always)] fn check_write_with_api_name( &mut self, @@ -124,11 +129,12 @@ pub type NodeRequireResolverRc = deno_fs::sync::MaybeArc; pub trait NodeRequireResolver: std::fmt::Debug + MaybeSend + MaybeSync { - fn ensure_read_permission( + #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] + fn ensure_read_permission<'a>( &self, permissions: &mut dyn NodePermissions, - path: &Path, - ) -> Result<(), AnyError>; + path: &'a Path, + ) -> Result, AnyError>; } pub static NODE_ENV_VAR_ALLOWLIST: Lazy> = Lazy::new(|| { diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs index 15667aae73..5473369815 100644 --- a/ext/node/ops/require.rs +++ b/ext/node/ops/require.rs @@ -15,6 +15,7 @@ use deno_path_util::normalize_path; use node_resolver::NodeModuleKind; use node_resolver::NodeResolutionMode; use node_resolver::REQUIRE_CONDITIONS; +use std::borrow::Cow; use std::cell::RefCell; use std::path::Path; use std::path::PathBuf; @@ -25,10 +26,11 @@ use crate::NodeRequireResolverRc; use crate::NodeResolverRc; use crate::NpmResolverRc; -fn ensure_read_permission

( +#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] +fn ensure_read_permission<'a, P>( state: &mut OpState, - file_path: &Path, -) -> Result<(), AnyError> + file_path: &'a Path, +) -> Result, AnyError> where P: NodePermissions + 'static, { @@ -107,7 +109,7 @@ where deno_path_util::normalize_path(current_dir.join(from)) }; - ensure_read_permission::

(state, &from)?; + let from = ensure_read_permission::

(state, &from)?; if cfg!(windows) { // return root node_modules when path is 'D:\\'. @@ -129,7 +131,7 @@ where } let mut paths = Vec::with_capacity(from.components().count()); - let mut current_path = from.as_path(); + let mut current_path = from.as_ref(); let mut maybe_parent = Some(current_path); while let Some(parent) = maybe_parent { if !parent.ends_with("node_modules") { @@ -267,7 +269,7 @@ where P: NodePermissions + 'static, { let path = PathBuf::from(path); - ensure_read_permission::

(state, &path)?; + let path = ensure_read_permission::

(state, &path)?; let fs = state.borrow::(); if let Ok(metadata) = fs.stat_sync(&path) { if metadata.is_file { @@ -290,7 +292,7 @@ where P: NodePermissions + 'static, { let path = PathBuf::from(request); - ensure_read_permission::

(state, &path)?; + let path = ensure_read_permission::

(state, &path)?; let fs = state.borrow::(); let canonicalized_path = deno_core::strip_unc_prefix(fs.realpath_sync(&path)?); @@ -362,7 +364,7 @@ where if parent_id == "" || parent_id == "internal/preload" { let fs = state.borrow::(); if let Ok(cwd) = fs.cwd() { - ensure_read_permission::

(state, &cwd)?; + let cwd = ensure_read_permission::

(state, &cwd)?; return Ok(Some(cwd.to_string_lossy().into_owned())); } } @@ -443,7 +445,7 @@ where P: NodePermissions + 'static, { let file_path = PathBuf::from(file_path); - ensure_read_permission::

(state, &file_path)?; + let file_path = ensure_read_permission::

(state, &file_path)?; let fs = state.borrow::(); Ok(fs.read_text_file_lossy_sync(&file_path, None)?) } @@ -528,7 +530,7 @@ where P: NodePermissions + 'static, { let filename = PathBuf::from(filename); - ensure_read_permission::

(state, filename.parent().unwrap())?; + // permissions: allow reading the closest package.json files let node_resolver = state.borrow::().clone(); node_resolver .get_closest_package_json_from_path(&filename) @@ -567,7 +569,7 @@ where P: NodePermissions + 'static, { let referrer_path = PathBuf::from(&referrer_filename); - ensure_read_permission::

(state, &referrer_path)?; + let referrer_path = ensure_read_permission::

(state, &referrer_path)?; let node_resolver = state.borrow::(); let Some(pkg) = node_resolver.get_closest_package_json_from_path(&referrer_path)? diff --git a/ext/node/ops/worker_threads.rs b/ext/node/ops/worker_threads.rs index c7ea4c52c2..4c50092f28 100644 --- a/ext/node/ops/worker_threads.rs +++ b/ext/node/ops/worker_threads.rs @@ -7,6 +7,7 @@ use deno_core::url::Url; use deno_core::OpState; use deno_fs::FileSystemRc; use node_resolver::NodeResolution; +use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; @@ -14,10 +15,11 @@ use crate::NodePermissions; use crate::NodeRequireResolverRc; use crate::NodeResolverRc; -fn ensure_read_permission

( +#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] +fn ensure_read_permission<'a, P>( state: &mut OpState, - file_path: &Path, -) -> Result<(), AnyError> + file_path: &'a Path, +) -> Result, AnyError> where P: NodePermissions + 'static, { @@ -47,7 +49,7 @@ where "Relative path entries must start with '.' or '..'", )); } - ensure_read_permission::

(state, &path)?; + let path = ensure_read_permission::

(state, &path)?; let fs = state.borrow::(); let canonicalized_path = deno_core::strip_unc_prefix(fs.realpath_sync(&path)?); @@ -57,7 +59,7 @@ where let url_path = url .to_file_path() .map_err(|e| generic_error(format!("URL to Path-String: {:#?}", e)))?; - ensure_read_permission::

(state, &url_path)?; + let url_path = ensure_read_permission::

(state, &url_path)?; let fs = state.borrow::(); if !fs.exists_sync(&url_path) { return Err(generic_error(format!("File not found [{:?}]", url_path))); diff --git a/ext/node/polyfills/_fs/_fs_read.ts b/ext/node/polyfills/_fs/_fs_read.ts index dec3a8bbdf..df4f5e375d 100644 --- a/ext/node/polyfills/_fs/_fs_read.ts +++ b/ext/node/polyfills/_fs/_fs_read.ts @@ -173,7 +173,7 @@ export function readSync( validateBuffer(buffer); if (length == null) { - length = 0; + length = buffer.byteLength; } if (typeof offsetOrOpt === "number") { diff --git a/ext/node/polyfills/worker_threads.ts b/ext/node/polyfills/worker_threads.ts index 5ff4446f73..d4b75fb30c 100644 --- a/ext/node/polyfills/worker_threads.ts +++ b/ext/node/polyfills/worker_threads.ts @@ -302,8 +302,8 @@ class NodeWorker extends EventEmitter { if (this.#status !== "TERMINATED") { this.#status = "TERMINATED"; op_host_terminate_worker(this.#id); + this.emit("exit", 0); } - this.emit("exit", 0); return PromiseResolve(0); } @@ -422,7 +422,11 @@ internals.__initWorkerThreads = ( parentPort.once = function (this: ParentPort, name, listener) { // deno-lint-ignore no-explicit-any - const _listener = (ev: any) => listener(ev.data); + const _listener = (ev: any) => { + const message = ev.data; + patchMessagePortIfFound(message); + return listener(message); + }; listeners.set(listener, _listener); this.addEventListener(name, _listener); return this; @@ -494,7 +498,9 @@ export function receiveMessageOnPort(port: MessagePort): object | undefined { port[MessagePortReceiveMessageOnPortSymbol] = true; const data = op_message_port_recv_message_sync(port[MessagePortIdSymbol]); if (data === null) return undefined; - return { message: deserializeJsMessageData(data)[0] }; + const message = deserializeJsMessageData(data)[0]; + patchMessagePortIfFound(message); + return { message }; } class NodeMessageChannel { diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index fac2f691ee..6c1554241e 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_tls" -version = "0.156.0" +version = "0.158.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/url/01_urlpattern.js b/ext/url/01_urlpattern.js index 58a6d6bce0..6e27563089 100644 --- a/ext/url/01_urlpattern.js +++ b/ext/url/01_urlpattern.js @@ -31,6 +31,7 @@ import * as webidl from "ext:deno_webidl/00_webidl.js"; import { createFilteredInspectProxy } from "ext:deno_console/01_console.js"; const _components = Symbol("components"); +const urlPatternSettings = { groupStringFallback: false }; /** * @typedef Components @@ -349,7 +350,11 @@ class URLPattern { const groups = res.groups; for (let i = 0; i < groupList.length; ++i) { // TODO(lucacasonato): this is vulnerable to override mistake - groups[groupList[i]] = match[i + 1] ?? ""; // TODO(@crowlKats): remove fallback for 2.0 + if (urlPatternSettings.groupStringFallback) { + groups[groupList[i]] = match[i + 1] ?? ""; + } else { + groups[groupList[i]] = match[i + 1]; + } } break; } @@ -422,4 +427,4 @@ webidl.converters.URLPatternOptions = webidl }, ]); -export { URLPattern }; +export { URLPattern, urlPatternSettings }; diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index cced5cae59..13aca99539 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_url" -version = "0.169.0" +version = "0.171.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/web/13_message_port.js b/ext/web/13_message_port.js index 04697d6aa8..cf72c43e6f 100644 --- a/ext/web/13_message_port.js +++ b/ext/web/13_message_port.js @@ -22,6 +22,7 @@ const { Symbol, SymbolFor, SymbolIterator, + PromiseResolve, SafeArrayIterator, TypeError, } = primordials; @@ -41,7 +42,10 @@ import { import { isDetachedBuffer } from "./06_streams.js"; import { DOMException } from "./01_dom_exception.js"; -let messageEventListenerCount = 0; +// counter of how many message ports are actively refed +// either due to the existence of "message" event listeners or +// explicit calls to ref/unref (in the case of node message ports) +let refedMessagePortsCount = 0; class MessageChannel { /** @type {MessagePort} */ @@ -93,6 +97,7 @@ const MessagePortReceiveMessageOnPortSymbol = Symbol( ); const _enabled = Symbol("enabled"); const _refed = Symbol("refed"); +const _messageEventListenerCount = Symbol("messageEventListenerCount"); const nodeWorkerThreadCloseCb = Symbol("nodeWorkerThreadCloseCb"); const nodeWorkerThreadCloseCbInvoked = Symbol("nodeWorkerThreadCloseCbInvoked"); export const refMessagePort = Symbol("refMessagePort"); @@ -109,6 +114,9 @@ function createMessagePort(id) { port[core.hostObjectBrand] = core.hostObjectBrand; setEventTargetData(port); port[_id] = id; + port[_enabled] = false; + port[_messageEventListenerCount] = 0; + port[_refed] = false; return port; } @@ -122,12 +130,18 @@ function nodeWorkerThreadMaybeInvokeCloseCb(port) { } } +const _isRefed = Symbol("isRefed"); +const _dataPromise = Symbol("dataPromise"); + class MessagePort extends EventTarget { /** @type {number | null} */ [_id] = null; /** @type {boolean} */ [_enabled] = false; [_refed] = false; + /** @type {Promise | undefined} */ + [_dataPromise] = undefined; + [_messageEventListenerCount] = 0; constructor() { super(); @@ -193,24 +207,21 @@ class MessagePort extends EventTarget { this[_enabled] = true; while (true) { if (this[_id] === null) break; - // Exit if no message event listeners are present in Node compat mode. - if ( - typeof this[nodeWorkerThreadCloseCb] == "function" && - messageEventListenerCount === 0 - ) break; let data; try { - data = await op_message_port_recv_message( + this[_dataPromise] = op_message_port_recv_message( this[_id], ); + if ( + typeof this[nodeWorkerThreadCloseCb] === "function" && + !this[_refed] + ) { + core.unrefOpPromise(this[_dataPromise]); + } + data = await this[_dataPromise]; + this[_dataPromise] = undefined; } catch (err) { if (ObjectPrototypeIsPrototypeOf(InterruptedPrototype, err)) { - // If we were interrupted, check if the interruption is coming - // from `receiveMessageOnPort` API from Node compat, if so, continue. - if (this[MessagePortReceiveMessageOnPortSymbol]) { - this[MessagePortReceiveMessageOnPortSymbol] = false; - continue; - } break; } nodeWorkerThreadMaybeInvokeCloseCb(this); @@ -246,12 +257,26 @@ class MessagePort extends EventTarget { } [refMessagePort](ref) { - if (ref && !this[_refed]) { - this[_refed] = true; - messageEventListenerCount++; - } else if (!ref && this[_refed]) { - this[_refed] = false; - messageEventListenerCount = 0; + if (ref) { + if (!this[_refed]) { + refedMessagePortsCount++; + if ( + this[_dataPromise] + ) { + core.refOpPromise(this[_dataPromise]); + } + this[_refed] = true; + } + } else if (!ref) { + if (this[_refed]) { + refedMessagePortsCount--; + if ( + this[_dataPromise] + ) { + core.unrefOpPromise(this[_dataPromise]); + } + this[_refed] = false; + } } } @@ -266,15 +291,20 @@ class MessagePort extends EventTarget { removeEventListener(...args) { if (args[0] == "message") { - messageEventListenerCount--; + if (--this[_messageEventListenerCount] === 0 && this[_refed]) { + refedMessagePortsCount--; + this[_refed] = false; + } } super.removeEventListener(...new SafeArrayIterator(args)); } addEventListener(...args) { if (args[0] == "message") { - messageEventListenerCount++; - if (!this[_refed]) this[_refed] = true; + if (++this[_messageEventListenerCount] === 1 && !this[_refed]) { + refedMessagePortsCount++; + this[_refed] = true; + } } super.addEventListener(...new SafeArrayIterator(args)); } @@ -295,7 +325,17 @@ class MessagePort extends EventTarget { } defineEventHandler(MessagePort.prototype, "message", function (self) { - self.start(); + if (self[nodeWorkerThreadCloseCb]) { + (async () => { + // delay `start()` until he end of this event loop turn, to give `receiveMessageOnPort` + // a chance to receive a message first. this is primarily to resolve an issue with + // a pattern used in `npm:piscina` that results in an indefinite hang + await PromiseResolve(); + self.start(); + })(); + } else { + self.start(); + } }); defineEventHandler(MessagePort.prototype, "messageerror"); @@ -463,12 +503,12 @@ function structuredClone(value, options) { export { deserializeJsMessageData, MessageChannel, - messageEventListenerCount, MessagePort, MessagePortIdSymbol, MessagePortPrototype, MessagePortReceiveMessageOnPortSymbol, nodeWorkerThreadCloseCb, + refedMessagePortsCount, serializeJsMessageData, structuredClone, }; diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index b2b7e7084a..4120e978e0 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_web" -version = "0.200.0" +version = "0.202.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/web/message_port.rs b/ext/web/message_port.rs index c069037f81..fa299475d9 100644 --- a/ext/web/message_port.rs +++ b/ext/web/message_port.rs @@ -239,7 +239,6 @@ pub fn op_message_port_recv_message_sync( #[smi] rid: ResourceId, ) -> Result, AnyError> { let resource = state.resource_table.get::(rid)?; - resource.cancel.cancel(); let mut rx = resource.port.rx.borrow_mut(); match rx.try_recv() { diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 17d8cbe580..4c709b9c30 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.136.0" +version = "0.138.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" diff --git a/ext/webidl/Cargo.toml b/ext/webidl/Cargo.toml index 5ff37d2be4..8a25be3662 100644 --- a/ext/webidl/Cargo.toml +++ b/ext/webidl/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webidl" -version = "0.169.0" +version = "0.171.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index 49d059771f..6bef387b44 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_websocket" -version = "0.174.0" +version = "0.176.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webstorage/01_webstorage.js b/ext/webstorage/01_webstorage.js index 3cbdd708dd..9e86366563 100644 --- a/ext/webstorage/01_webstorage.js +++ b/ext/webstorage/01_webstorage.js @@ -143,6 +143,9 @@ function createStorage(persistent) { if (ReflectHas(target, key)) { return undefined; } + if (typeof key === "symbol") { + return undefined; + } const value = target.getItem(key); if (value === null) { return undefined; diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index 3c78fb49ac..08ed8b0f29 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webstorage" -version = "0.164.0" +version = "0.166.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 70f95c43c8..62504c8303 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_resolver" -version = "0.1.0" +version = "0.3.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -22,6 +22,7 @@ deno_package_json.features = ["sync"] deno_path_util.workspace = true deno_semver.workspace = true node_resolver.workspace = true +thiserror.workspace = true url.workspace = true [dev-dependencies] diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index c847cee0f2..3394b3e501 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -5,8 +5,6 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use anyhow::bail; -use anyhow::Error as AnyError; use deno_package_json::PackageJson; use deno_package_json::PackageJsonDepValue; use deno_path_util::url_to_file_path; @@ -18,6 +16,7 @@ use node_resolver::errors::PackageJsonLoadError; use node_resolver::errors::PackageNotFoundError; use node_resolver::load_pkg_json; use node_resolver::NpmResolver; +use thiserror::Error; use url::Url; use crate::fs::DenoPkgJsonFsAdapter; @@ -25,6 +24,18 @@ use crate::fs::DenoResolverFs; use super::local::normalize_pkg_name_for_node_modules_deno_folder; +#[derive(Debug, Error)] +pub enum ByonmResolvePkgFolderFromDenoReqError { + #[error("Could not find \"{}\" in a node_modules folder. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", .0)] + MissingAlias(String), + #[error(transparent)] + PackageJson(#[from] PackageJsonLoadError), + #[error("Could not find a matching package for 'npm:{}' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `\"nodeModulesDir\": \"auto\"` in your deno.json file.", .0)] + UnmatchedReq(PackageReq), + #[error(transparent)] + Io(#[from] std::io::Error), +} + pub struct ByonmNpmResolverCreateOptions { pub fs: Fs, // todo(dsherret): investigate removing this @@ -100,12 +111,12 @@ impl ByonmNpmResolver { &self, req: &PackageReq, referrer: &Url, - ) -> Result { + ) -> Result { fn node_resolve_dir( fs: &Fs, alias: &str, start_dir: &Path, - ) -> Result, AnyError> { + ) -> std::io::Result> { for ancestor in start_dir.ancestors() { let node_modules_folder = ancestor.join("node_modules"); let sub_dir = join_package_name(&node_modules_folder, alias); @@ -131,14 +142,7 @@ impl ByonmNpmResolver { return Ok(resolved); } - bail!( - concat!( - "Could not find \"{}\" in a node_modules folder. ", - "Deno expects the node_modules/ directory to be up to date. ", - "Did you forget to run `deno install`?" - ), - alias, - ); + Err(ByonmResolvePkgFolderFromDenoReqError::MissingAlias(alias)) } None => { // now check if node_modules/.deno/ matches this constraint @@ -146,16 +150,9 @@ impl ByonmNpmResolver { return Ok(folder); } - bail!( - concat!( - "Could not find a matching package for 'npm:{}' in the node_modules ", - "directory. Ensure you have all your JSR and npm dependencies listed ", - "in your deno.json or package.json, then run `deno install`. Alternatively, ", - r#"turn on auto-install by specifying `"nodeModulesDir": "auto"` in your "#, - "deno.json file." - ), - req, - ); + Err(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq( + req.clone(), + )) } } } @@ -164,7 +161,7 @@ impl ByonmNpmResolver { &self, req: &PackageReq, referrer: &Url, - ) -> Result, String)>, AnyError> { + ) -> Result, String)>, PackageJsonLoadError> { fn resolve_alias_from_pkg_json( req: &PackageReq, pkg_json: &PackageJson, @@ -256,7 +253,24 @@ impl ByonmNpmResolver { let Ok(version) = Version::parse_from_npm(version) else { continue; }; - if req.version_req.matches(&version) { + if let Some(tag) = req.version_req.tag() { + let initialized_file = + node_modules_deno_dir.join(&entry.name).join(".initialized"); + let Ok(contents) = self.fs.read_to_string_lossy(&initialized_file) + else { + continue; + }; + let mut tags = contents.split(',').map(str::trim); + if tags.any(|t| t == tag) { + if let Some((best_version_version, _)) = &best_version { + if version > *best_version_version { + best_version = Some((version, entry.name)); + } + } else { + best_version = Some((version, entry.name)); + } + } + } else if req.version_req.matches(&version) { if let Some((best_version_version, _)) = &best_version { if version > *best_version_version { best_version = Some((version, entry.name)); diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 2e24144cd6..9d885cad31 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -5,4 +5,5 @@ mod local; pub use byonm::ByonmNpmResolver; pub use byonm::ByonmNpmResolverCreateOptions; +pub use byonm::ByonmResolvePkgFolderFromDenoReqError; pub use local::normalize_pkg_name_for_node_modules_deno_folder; diff --git a/resolvers/node/Cargo.toml b/resolvers/node/Cargo.toml index 153b4a38d4..f05b209fdb 100644 --- a/resolvers/node/Cargo.toml +++ b/resolvers/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "node_resolver" -version = "0.8.0" +version = "0.10.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 5bffd7980e..ba9dc6243f 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_runtime" -version = "0.178.0" +version = "0.180.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 0da2072b84..56a5b411bb 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -169,8 +169,11 @@ let isClosing = false; let globalDispatchEvent; function hasMessageEventListener() { + // the function name is kind of a misnomer, but we want to behave + // as if we have message event listeners if a node message port is explicitly + // refed (and the inverse as well) return event.listenerCount(globalThis, "message") > 0 || - messagePort.messageEventListenerCount > 0; + messagePort.refedMessagePortsCount > 0; } async function pollForMessages() { diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index 29ef9db134..8f0b0dd3de 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_permissions" -version = "0.29.0" +version = "0.31.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 77038ff2fa..2904242dae 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -476,6 +476,9 @@ impl UnaryPermission { if state != PermissionState::Prompt { return state; } + if !self.prompt { + return PermissionState::Denied; + } let mut message = String::with_capacity(40); message.push_str(&format!("{} access", TQuery::flag_name())); if let Some(desc) = desc { @@ -2282,6 +2285,11 @@ impl PermissionsContainer { self.inner.lock().read.check_all(Some(api_name)) } + #[inline(always)] + pub fn query_read_all(&self) -> bool { + self.inner.lock().read.query(None) == PermissionState::Granted + } + #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] #[inline(always)] pub fn check_write( @@ -2611,8 +2619,13 @@ impl PermissionsContainer { &self, path: Option<&str>, ) -> Result { + let inner = self.inner.lock(); + let permission = &inner.read; + if permission.is_allow_all() { + return Ok(PermissionState::Granted); + } Ok( - self.inner.lock().read.query( + permission.query( path .map(|path| { Result::<_, AnyError>::Ok( @@ -2630,8 +2643,13 @@ impl PermissionsContainer { &self, path: Option<&str>, ) -> Result { + let inner = self.inner.lock(); + let permission = &inner.write; + if permission.is_allow_all() { + return Ok(PermissionState::Granted); + } Ok( - self.inner.lock().write.query( + permission.query( path .map(|path| { Result::<_, AnyError>::Ok( @@ -2649,8 +2667,13 @@ impl PermissionsContainer { &self, host: Option<&str>, ) -> Result { + let inner = self.inner.lock(); + let permission = &inner.net; + if permission.is_allow_all() { + return Ok(PermissionState::Granted); + } Ok( - self.inner.lock().net.query( + permission.query( match host { None => None, Some(h) => Some(self.descriptor_parser.parse_net_descriptor(h)?), @@ -2662,7 +2685,12 @@ impl PermissionsContainer { #[inline(always)] pub fn query_env(&self, var: Option<&str>) -> PermissionState { - self.inner.lock().env.query(var) + let inner = self.inner.lock(); + let permission = &inner.env; + if permission.is_allow_all() { + return PermissionState::Granted; + } + permission.query(var) } #[inline(always)] @@ -2670,8 +2698,13 @@ impl PermissionsContainer { &self, kind: Option<&str>, ) -> Result { + let inner = self.inner.lock(); + let permission = &inner.sys; + if permission.is_allow_all() { + return Ok(PermissionState::Granted); + } Ok( - self.inner.lock().sys.query( + permission.query( kind .map(|kind| self.descriptor_parser.parse_sys_descriptor(kind)) .transpose()? @@ -2685,8 +2718,13 @@ impl PermissionsContainer { &self, cmd: Option<&str>, ) -> Result { + let inner = self.inner.lock(); + let permission = &inner.run; + if permission.is_allow_all() { + return Ok(PermissionState::Granted); + } Ok( - self.inner.lock().run.query( + permission.query( cmd .map(|request| self.descriptor_parser.parse_run_query(request)) .transpose()? @@ -2700,8 +2738,13 @@ impl PermissionsContainer { &self, path: Option<&str>, ) -> Result { + let inner = self.inner.lock(); + let permission = &inner.ffi; + if permission.is_allow_all() { + return Ok(PermissionState::Granted); + } Ok( - self.inner.lock().ffi.query( + permission.query( path .map(|path| { Result::<_, AnyError>::Ok( @@ -3906,7 +3949,8 @@ mod tests { fn test_request() { set_prompter(Box::new(TestPrompter)); let parser = TestPermissionDescriptorParser; - let mut perms: Permissions = Permissions::none_without_prompt(); + let mut perms: Permissions = Permissions::none_with_prompt(); + let mut perms_no_prompt: Permissions = Permissions::none_without_prompt(); let read_query = |path: &str| parser.parse_path_query(path).unwrap().into_read(); let write_query = @@ -3955,6 +3999,7 @@ mod tests { assert_eq!(perms.run.query(None), PermissionState::Prompt); prompt_value.set(false); assert_eq!(perms.run.request(Some(&run_query)), PermissionState::Granted); + assert_eq!(perms_no_prompt.read.request(Some(&read_query("/foo"))), PermissionState::Denied); }; } diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index 456810e6a6..041132f971 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -97,6 +97,9 @@ impl deno_node::NodePermissions for Permissions { ) -> Result { unreachable!("snapshotting!") } + fn query_read_all(&mut self) -> bool { + unreachable!("snapshotting!") + } fn check_write_with_api_name( &mut self, _p: &str, diff --git a/tests/integration/jupyter_tests.rs b/tests/integration/jupyter_tests.rs index 1b2c213118..e99780a276 100644 --- a/tests/integration/jupyter_tests.rs +++ b/tests/integration/jupyter_tests.rs @@ -628,3 +628,37 @@ async fn jupyter_store_history_false() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn jupyter_http_server() -> Result<()> { + let (_ctx, client, _process) = setup().await; + client + .send( + Shell, + "execute_request", + json!({ + "silent": false, + "store_history": false, + "code": r#"Deno.serve({ port: 10234 }, (req) => Response.json({ hello: "world" }))"#, + }), + ) + .await?; + + let reply = client.recv(Shell).await?; + assert_eq!(reply.header.msg_type, "execute_reply"); + assert_json_subset( + reply.content, + json!({ + "status": "ok", + "execution_count": 0, + }), + ); + + for _ in 0..3 { + let resp = reqwest::get("http://localhost:10234").await.unwrap(); + let text: serde_json::Value = resp.json().await.unwrap(); + assert_eq!(text, json!({ "hello": "world" })); + } + + Ok(()) +} diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index fc54375947..19ea89a54b 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -8504,6 +8504,74 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() { client.shutdown(); } +// Regression test for https://github.com/denoland/deno/issues/25775. +#[test] +fn lsp_quick_fix_missing_import_exclude_bare_node_builtins() { + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .add_npm_env_vars() + .build(); + let temp_dir = context.temp_dir(); + temp_dir.write( + "package.json", + json!({ + "dependencies": { + "@types/node": "*", + }, + }) + .to_string(), + ); + context.run_npm("install"); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + let diagnostics = client.did_open(json!({ + "textDocument": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "languageId": "typescript", + "version": 1, + // Include node:buffer import to ensure @types/node is in the dep graph. + "text": "import \"node:buffer\";\nassert();\n", + }, + })); + let diagnostic = diagnostics + .all() + .into_iter() + .find(|d| d.message == "Cannot find name 'assert'.") + .unwrap(); + let res = client.write_request( + "textDocument/codeAction", + json!({ + "textDocument": { + "uri": temp_dir.url().join("file.ts").unwrap(), + }, + "range": { + "start": { "line": 1, "character": 0 }, + "end": { "line": 1, "character": 6 }, + }, + "context": { + "diagnostics": [&diagnostic], + "only": ["quickfix"], + }, + }), + ); + let code_actions = + serde_json::from_value::>(res).unwrap(); + let titles = code_actions + .iter() + .map(|a| a.title.clone()) + .collect::>(); + assert_eq!( + json!(titles), + json!([ + "Add import from \"node:assert\"", + "Add import from \"node:console\"", + "Add missing function declaration 'assert'", + ]), + ); + client.shutdown(); +} + #[test] fn lsp_completions_snippet() { let context = TestContextBuilder::new().use_temp_cwd().build(); diff --git a/tests/integration/repl_tests.rs b/tests/integration/repl_tests.rs index 3f137ce7cb..4e00398ce6 100644 --- a/tests/integration/repl_tests.rs +++ b/tests/integration/repl_tests.rs @@ -1136,3 +1136,22 @@ fn eval_file_promise_error() { assert_contains!(out, "Uncaught undefined"); assert!(err.is_empty()); } + +#[test] +fn repl_json_imports() { + let context = TestContextBuilder::default().use_temp_cwd().build(); + let temp_dir = context.temp_dir(); + temp_dir.write("./data.json", r#"{"hello": "world"}"#); + context + .new_command() + .env("NO_COLOR", "1") + .args_vec(["repl", "-A"]) + .with_pty(|mut console| { + console.write_line_raw( + "import data from './data.json' with { type: 'json' };", + ); + console.expect("undefined"); + console.write_line_raw("data"); + console.expect(r#"{ hello: "world" }"#); + }); +} diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index 1e1d6ed61b..db83acb19d 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -926,9 +926,7 @@ fn lock_redirects() { ); } -// TODO(2.0): this should be rewritten to a spec test and first run `deno install` #[test] -#[ignore] fn lock_deno_json_package_json_deps() { let context = TestContextBuilder::new() .use_temp_cwd() @@ -942,6 +940,7 @@ fn lock_deno_json_package_json_deps() { // add a jsr and npm dependency deno_json.write_json(&json!({ + "nodeModulesDir": "auto", "imports": { "esm-basic": "npm:@denotest/esm-basic", "module_graph": "jsr:@denotest/module-graph@1.4", @@ -984,6 +983,7 @@ fn lock_deno_json_package_json_deps() { // now remove the npm dependency from the deno.json and move // it to a package.json that uses an alias deno_json.write_json(&json!({ + "nodeModulesDir": "auto", "imports": { "module_graph": "jsr:@denotest/module-graph@1.4", } @@ -1060,7 +1060,9 @@ fn lock_deno_json_package_json_deps() { })); // now remove the deps from the deno.json - deno_json.write("{}"); + deno_json.write_json(&json!({ + "nodeModulesDir": "auto" + })); main_ts.write(""); context .new_command() @@ -3512,6 +3514,22 @@ itest!(no_prompt_flag { output_str: Some(""), }); +#[test] +fn permission_request_with_no_prompt() { + TestContext::default() + .new_command() + .env("NO_COLOR", "1") + .args_vec([ + "run", + "--quiet", + "--no-prompt", + "run/permission_request_no_prompt.ts", + ]) + .with_pty(|mut console| { + console.expect("PermissionStatus { state: \"denied\", onchange: null }"); + }); +} + #[test] fn deno_no_prompt_environment_variable() { let output = util::deno_cmd() diff --git a/tests/integration/task_tests.rs b/tests/integration/task_tests.rs index d198a3be6f..56bab2970d 100644 --- a/tests/integration/task_tests.rs +++ b/tests/integration/task_tests.rs @@ -18,19 +18,6 @@ // http_server: true, // }); -// TODO(2.0): decide what to do with this test -// should not auto-install the packages in the package.json -// when using nodeModulesDir: false -// itest!(task_package_json_node_modules_dir_false { -// args: "task echo", -// cwd: Some("task/package_json_node_modules_dir_false/"), -// output: "task/package_json_node_modules_dir_false/bin.out", -// copy_temp_dir: Some("task/package_json_node_modules_dir_false/"), -// envs: env_vars_for_npm_tests(), -// exit_code: 0, -// http_server: true, -// }); - // TODO(2.0): not entirely clear what's wrong with this test but it hangs for more than 60s // itest!(task_npx_on_own { // args: "task on-own", diff --git a/tests/napi/Cargo.toml b/tests/napi/Cargo.toml index 611d6d5507..e3de253683 100644 --- a/tests/napi/Cargo.toml +++ b/tests/napi/Cargo.toml @@ -13,6 +13,7 @@ repository.workspace = true crate-type = ["cdylib"] [dependencies] +libuv-sys-lite = "=1.48.2" napi-sys = { version = "=2.2.2", default-features = false, features = ["napi7"] } [dev-dependencies] diff --git a/tests/napi/src/lib.rs b/tests/napi/src/lib.rs index f6fe6e189a..8c6190ad3e 100644 --- a/tests/napi/src/lib.rs +++ b/tests/napi/src/lib.rs @@ -31,6 +31,7 @@ pub mod strings; pub mod symbol; pub mod tsfn; pub mod typedarray; +pub mod uv; #[macro_export] macro_rules! cstr { @@ -138,6 +139,7 @@ unsafe extern "C" fn napi_register_module_v1( #[cfg(windows)] { napi_sys::setup(); + libuv_sys_lite::setup(); } // We create a fresh exports object and leave the passed @@ -169,6 +171,7 @@ unsafe extern "C" fn napi_register_module_v1( symbol::init(env, exports); make_callback::init(env, exports); object::init(env, exports); + uv::init(env, exports); init_cleanup_hook(env, exports); diff --git a/tests/napi/src/uv.rs b/tests/napi/src/uv.rs new file mode 100644 index 0000000000..555470c008 --- /dev/null +++ b/tests/napi/src/uv.rs @@ -0,0 +1,206 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use libuv_sys_lite::uv_async_init; +use libuv_sys_lite::uv_async_t; +use libuv_sys_lite::uv_close; +use libuv_sys_lite::uv_handle_t; +use libuv_sys_lite::uv_mutex_destroy; +use libuv_sys_lite::uv_mutex_lock; +use libuv_sys_lite::uv_mutex_t; +use libuv_sys_lite::uv_mutex_unlock; +use napi_sys::*; +use std::mem::MaybeUninit; +use std::ptr; +use std::ptr::addr_of_mut; +use std::ptr::null_mut; +use std::time::Duration; + +struct KeepAlive { + tsfn: napi_threadsafe_function, +} + +impl KeepAlive { + fn new(env: napi_env) -> Self { + let mut name = null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + c"test_uv_async".as_ptr(), + 13, + &mut name + )); + + unsafe extern "C" fn dummy( + _env: napi_env, + _cb: napi_callback_info, + ) -> napi_value { + ptr::null_mut() + } + + let mut func = null_mut(); + assert_napi_ok!(napi_create_function( + env, + c"dummy".as_ptr(), + usize::MAX, + Some(dummy), + null_mut(), + &mut func, + )); + + let mut tsfn = null_mut(); + assert_napi_ok!(napi_create_threadsafe_function( + env, + func, + null_mut(), + name, + 0, + 1, + null_mut(), + None, + null_mut(), + None, + &mut tsfn, + )); + assert_napi_ok!(napi_ref_threadsafe_function(env, tsfn)); + Self { tsfn } + } +} + +impl Drop for KeepAlive { + fn drop(&mut self) { + assert_napi_ok!(napi_release_threadsafe_function( + self.tsfn, + ThreadsafeFunctionReleaseMode::release, + )); + } +} + +struct Async { + mutex: *mut uv_mutex_t, + env: napi_env, + value: u32, + callback: napi_ref, + _keep_alive: KeepAlive, +} + +#[derive(Clone, Copy)] +struct UvAsyncPtr(*mut uv_async_t); + +unsafe impl Send for UvAsyncPtr {} + +fn new_raw(t: T) -> *mut T { + Box::into_raw(Box::new(t)) +} + +unsafe extern "C" fn close_cb(handle: *mut uv_handle_t) { + let handle = handle.cast::(); + let async_ = (*handle).data as *mut Async; + let env = (*async_).env; + assert_napi_ok!(napi_delete_reference(env, (*async_).callback)); + + uv_mutex_destroy((*async_).mutex); + let _ = Box::from_raw((*async_).mutex); + let _ = Box::from_raw(async_); + let _ = Box::from_raw(handle); +} + +unsafe extern "C" fn callback(handle: *mut uv_async_t) { + eprintln!("callback"); + let async_ = (*handle).data as *mut Async; + uv_mutex_lock((*async_).mutex); + let env = (*async_).env; + let mut js_cb = null_mut(); + assert_napi_ok!(napi_get_reference_value( + env, + (*async_).callback, + &mut js_cb + )); + let mut global: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_global(env, &mut global)); + + let mut result: napi_value = ptr::null_mut(); + let value = (*async_).value; + eprintln!("value is {value}"); + let mut value_js = ptr::null_mut(); + assert_napi_ok!(napi_create_uint32(env, value, &mut value_js)); + let args = &[value_js]; + assert_napi_ok!(napi_call_function( + env, + global, + js_cb, + 1, + args.as_ptr(), + &mut result, + )); + uv_mutex_unlock((*async_).mutex); + if value == 5 { + uv_close(handle.cast(), Some(close_cb)); + } +} + +unsafe fn uv_async_send(ptr: UvAsyncPtr) { + assert_napi_ok!(libuv_sys_lite::uv_async_send(ptr.0)); +} + +fn make_uv_mutex() -> *mut uv_mutex_t { + let mutex = new_raw(MaybeUninit::::uninit()); + assert_napi_ok!(libuv_sys_lite::uv_mutex_init(mutex.cast())); + mutex.cast() +} + +#[allow(unused_unsafe)] +extern "C" fn test_uv_async( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut loop_ = null_mut(); + assert_napi_ok!(napi_get_uv_event_loop(env, &mut loop_)); + let uv_async = new_raw(MaybeUninit::::uninit()); + let uv_async = uv_async.cast::(); + let mut js_cb = null_mut(); + assert_napi_ok!(napi_create_reference(env, args[0], 1, &mut js_cb)); + // let mut tsfn = null_mut(); + + let data = new_raw(Async { + env, + callback: js_cb, + mutex: make_uv_mutex(), + value: 0, + _keep_alive: KeepAlive::new(env), + }); + unsafe { + addr_of_mut!((*uv_async).data).write(data.cast()); + assert_napi_ok!(uv_async_init(loop_.cast(), uv_async, Some(callback))); + let uv_async = UvAsyncPtr(uv_async); + std::thread::spawn({ + move || { + let data = (*uv_async.0).data as *mut Async; + for _ in 0..5 { + uv_mutex_lock((*data).mutex); + (*data).value += 1; + uv_mutex_unlock((*data).mutex); + std::thread::sleep(Duration::from_millis(10)); + uv_async_send(uv_async); + } + } + }); + } + + ptr::null_mut() +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[napi_new_property!(env, "test_uv_async", test_uv_async)]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/tests/napi/uv_test.js b/tests/napi/uv_test.js new file mode 100644 index 0000000000..af20b26493 --- /dev/null +++ b/tests/napi/uv_test.js @@ -0,0 +1,22 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const uv = loadTestLibrary(); + +Deno.test({ + name: "napi uv async", + ignore: true, + fn: async () => { + let called = false; + await new Promise((resolve) => { + uv.test_uv_async((value) => { + called = true; + if (value === 5) { + resolve(); + } + }); + }); + assertEquals(called, true); + }, +}); diff --git a/tests/specs/install/byonm_run_tag_after_install/__test__.jsonc b/tests/specs/install/byonm_run_tag_after_install/__test__.jsonc new file mode 100644 index 0000000000..3803fd26f7 --- /dev/null +++ b/tests/specs/install/byonm_run_tag_after_install/__test__.jsonc @@ -0,0 +1,44 @@ +{ + "tempDir": true, + + "tests": { + "tag_with_byonm": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "run -A main.ts", + "output": "" + } + ] + }, + "no_tag_then_tag": { + "steps": [ + { + "args": "run -A replace-version-req.ts 1.0.0", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "run -A replace-version-req.ts latest", + "output": "" + }, + { + "args": "run -A main.ts", + "output": "node_modules_out_of_date.out", + "exitCode": 1 + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { "args": "run -A main.ts", "output": "" } + ] + } + } +} diff --git a/tests/specs/install/byonm_run_tag_after_install/deno.json b/tests/specs/install/byonm_run_tag_after_install/deno.json new file mode 100644 index 0000000000..13238b1697 --- /dev/null +++ b/tests/specs/install/byonm_run_tag_after_install/deno.json @@ -0,0 +1,5 @@ +{ + "imports": { + "@denotest/esm-basic": "npm:@denotest/esm-basic@latest" + } +} diff --git a/tests/specs/install/byonm_run_tag_after_install/main.ts b/tests/specs/install/byonm_run_tag_after_install/main.ts new file mode 100644 index 0000000000..7feb95f96a --- /dev/null +++ b/tests/specs/install/byonm_run_tag_after_install/main.ts @@ -0,0 +1 @@ +import { add } from "@denotest/esm-basic"; diff --git a/tests/specs/install/byonm_run_tag_after_install/node_modules_out_of_date.out b/tests/specs/install/byonm_run_tag_after_install/node_modules_out_of_date.out new file mode 100644 index 0000000000..2d7fc81d17 --- /dev/null +++ b/tests/specs/install/byonm_run_tag_after_install/node_modules_out_of_date.out @@ -0,0 +1,2 @@ +error: Could not find a matching package for 'npm:@denotest/esm-basic@latest' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `"nodeModulesDir": "auto"` in your deno.json file. + at [WILDCARD]main.ts:1:21 diff --git a/tests/specs/install/byonm_run_tag_after_install/package.json b/tests/specs/install/byonm_run_tag_after_install/package.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/tests/specs/install/byonm_run_tag_after_install/package.json @@ -0,0 +1 @@ +{} diff --git a/tests/specs/install/byonm_run_tag_after_install/replace-version-req.ts b/tests/specs/install/byonm_run_tag_after_install/replace-version-req.ts new file mode 100644 index 0000000000..6f86ce2b71 --- /dev/null +++ b/tests/specs/install/byonm_run_tag_after_install/replace-version-req.ts @@ -0,0 +1,7 @@ +const newReq = Deno.args[0]?.trim(); +if (!newReq) { + throw new Error("Missing required argument"); +} +const config = JSON.parse(Deno.readTextFileSync("deno.json")); +config.imports["@denotest/esm-basic"] = `npm:@denotest/esm-basic@${newReq}`; +Deno.writeTextFileSync("deno.json", JSON.stringify(config)); diff --git a/tests/specs/install/workspace_member_with_tag_dep/__test__.jsonc b/tests/specs/install/workspace_member_with_tag_dep/__test__.jsonc new file mode 100644 index 0000000000..254fe8b989 --- /dev/null +++ b/tests/specs/install/workspace_member_with_tag_dep/__test__.jsonc @@ -0,0 +1,9 @@ +{ + "tempDir": true, + "steps": [ + { + "args": "install", + "output": "install.out" + } + ] +} diff --git a/tests/specs/install/workspace_member_with_tag_dep/install.out b/tests/specs/install/workspace_member_with_tag_dep/install.out new file mode 100644 index 0000000000..b8114c12a0 --- /dev/null +++ b/tests/specs/install/workspace_member_with_tag_dep/install.out @@ -0,0 +1,3 @@ +Download http://localhost:4260/@denotest/esm-basic +Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz +Initialize @denotest/esm-basic@1.0.0 diff --git a/tests/specs/install/workspace_member_with_tag_dep/package.json b/tests/specs/install/workspace_member_with_tag_dep/package.json new file mode 100644 index 0000000000..58aa5d5cc6 --- /dev/null +++ b/tests/specs/install/workspace_member_with_tag_dep/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "@denotest/esm-basic": "latest" + }, + "workspaces": ["package1"] +} diff --git a/tests/specs/install/workspace_member_with_tag_dep/package1/package.json b/tests/specs/install/workspace_member_with_tag_dep/package1/package.json new file mode 100644 index 0000000000..150a7df6af --- /dev/null +++ b/tests/specs/install/workspace_member_with_tag_dep/package1/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "@denotest/esm-basic": "latest" + } +} diff --git a/tests/specs/npm/byonm_run_npm/__test__.jsonc b/tests/specs/npm/byonm_run_npm/__test__.jsonc new file mode 100644 index 0000000000..4f0b21b0ff --- /dev/null +++ b/tests/specs/npm/byonm_run_npm/__test__.jsonc @@ -0,0 +1,30 @@ +{ + "tests": { + "not_in_deps": { + "steps": [{ + "args": "run -A --quiet npm:cowsay moo", + "output": "not_in_deps.out" + }, { + // ensure it doesn't make any lockfile modifications and thus doesn't write to the lockfile + "args": [ + "eval", + "try { Deno.statSync('deno.lock') } catch (e) { console.log(e instanceof Deno.errors.NotFound); }" + ], + "output": "true\n" + }] + }, + "in_deps": { + "tempDir": true, + "steps": [{ + "args": "install npm:cowsay", + "output": "[WILDCARD]" + }, { + "args": "run --allow-write overwrite.ts", + "output": "[WILDCARD]" + }, { + "args": "run -A npm:cowsay moo", + "output": "replaced\n" + }] + } + } +} diff --git a/tests/specs/npm/byonm_run_npm/deno.json b/tests/specs/npm/byonm_run_npm/deno.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/specs/npm/byonm_run_npm/deno.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/specs/npm/byonm_run_npm/not_in_deps.out b/tests/specs/npm/byonm_run_npm/not_in_deps.out new file mode 100644 index 0000000000..dc07eb43ee --- /dev/null +++ b/tests/specs/npm/byonm_run_npm/not_in_deps.out @@ -0,0 +1,8 @@ + _____ +< moo > + ----- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || diff --git a/tests/specs/npm/byonm_run_npm/overwrite.ts b/tests/specs/npm/byonm_run_npm/overwrite.ts new file mode 100644 index 0000000000..3749c5e4e8 --- /dev/null +++ b/tests/specs/npm/byonm_run_npm/overwrite.ts @@ -0,0 +1,4 @@ +Deno.writeTextFileSync( + "node_modules/cowsay/cli.js", + "console.log('replaced');", +); diff --git a/tests/specs/npm/byonm_run_npm/package.json b/tests/specs/npm/byonm_run_npm/package.json new file mode 100644 index 0000000000..9664f260a7 --- /dev/null +++ b/tests/specs/npm/byonm_run_npm/package.json @@ -0,0 +1,4 @@ +{ + "dependencies": { + } +} diff --git a/tests/specs/permission/deny_run_binary_absolute_path/main.out b/tests/specs/permission/deny_run_binary_absolute_path/main.out index fef29eae75..45b2283879 100644 --- a/tests/specs/permission/deny_run_binary_absolute_path/main.out +++ b/tests/specs/permission/deny_run_binary_absolute_path/main.out @@ -1,4 +1,3 @@ -Warning --allow-run without an allow list is susceptible to exploits. Prefer specifying an allow list (https://docs.deno.com/runtime/fundamentals/security/#running-subprocesses) NotCapable: Requires run access to "deno", run again with the --allow-run flag at [WILDCARD] { name: "NotCapable" diff --git a/tests/specs/run/allow_run_insecure_warnings/__test__.jsonc b/tests/specs/run/allow_run_insecure_warnings/__test__.jsonc deleted file mode 100644 index b64146ee95..0000000000 --- a/tests/specs/run/allow_run_insecure_warnings/__test__.jsonc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "tests": { - "no_allow_list": { - "args": "run --allow-run main.ts", - "output": "no_allow_list.out" - } - } -} diff --git a/tests/specs/run/allow_run_insecure_warnings/main.ts b/tests/specs/run/allow_run_insecure_warnings/main.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/specs/run/allow_run_insecure_warnings/no_allow_list.out b/tests/specs/run/allow_run_insecure_warnings/no_allow_list.out deleted file mode 100644 index 293b4446ad..0000000000 --- a/tests/specs/run/allow_run_insecure_warnings/no_allow_list.out +++ /dev/null @@ -1 +0,0 @@ -Warning --allow-run without an allow list is susceptible to exploits. Prefer specifying an allow list (https://docs.deno.com/runtime/fundamentals/security/#running-subprocesses) diff --git a/tests/specs/run/package_json/invalid_value/__test__.jsonc b/tests/specs/run/package_json/invalid_value/__test__.jsonc index 7f85713cb5..195734d95f 100644 --- a/tests/specs/run/package_json/invalid_value/__test__.jsonc +++ b/tests/specs/run/package_json/invalid_value/__test__.jsonc @@ -1,22 +1,29 @@ { "tempDir": true, - // TODO(2.0): update the tests, should probably run install first - "ignore": true, "tests": { // should run fine when not referencing a failing dep entry - "run_ok": { - "args": "run ok.ts", + "run_ok_auto": { + "args": "run --quiet --node-modules-dir=auto ok.ts", "output": "ok.ts.out" }, // should fail when referencing a failing dep entry - "run_error": { - "args": "run error.ts", + "run_error_auto": { + "args": "run --node-modules-dir=auto error.ts", "exitCode": 1, - "output": "error.ts.out" + "output": "error_auto.out" + }, + "install_error_byonm": { + "args": "install", + "output": "install.out", + "exitCode": 1 + }, + "add_error_byonm": { + "args": "add npm:cowsay", + "output": "add.out", + "exitCode": 1 }, - // should output a warning about the failing dep entry "task_test": { - "args": "task test", + "args": "task --node-modules-dir=auto test", "output": "task.out" } } diff --git a/tests/specs/run/package_json/invalid_value/add.out b/tests/specs/run/package_json/invalid_value/add.out new file mode 100644 index 0000000000..9b7493c1a2 --- /dev/null +++ b/tests/specs/run/package_json/invalid_value/add.out @@ -0,0 +1,8 @@ +Add npm:cowsay@1.5.0 +error: Failed to install from package.json + +Caused by: + 0: Invalid version requirement + 1: Unexpected character. + invalid stuff that won't parse + ~ diff --git a/tests/specs/run/package_json/invalid_value/error.out b/tests/specs/run/package_json/invalid_value/error.out new file mode 100644 index 0000000000..5975351131 --- /dev/null +++ b/tests/specs/run/package_json/invalid_value/error.out @@ -0,0 +1,2 @@ +error: Could not resolve "@denotest/cjs-default-export", but found it in a package.json. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`? + at file:///[WILDCARD]/error.ts:2:23 diff --git a/tests/specs/run/package_json/invalid_value/error.ts.out b/tests/specs/run/package_json/invalid_value/error_auto.out similarity index 50% rename from tests/specs/run/package_json/invalid_value/error.ts.out rename to tests/specs/run/package_json/invalid_value/error_auto.out index 37796d6da1..7641a4d863 100644 --- a/tests/specs/run/package_json/invalid_value/error.ts.out +++ b/tests/specs/run/package_json/invalid_value/error_auto.out @@ -1,3 +1,6 @@ +Download http://localhost:4260/@denotest/esm-basic +Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz +Initialize @denotest/esm-basic@1.0.0 error: Invalid version requirement 0: Unexpected character. invalid stuff that won't parse diff --git a/tests/specs/run/package_json/invalid_value/install.out b/tests/specs/run/package_json/invalid_value/install.out new file mode 100644 index 0000000000..cc82b345b0 --- /dev/null +++ b/tests/specs/run/package_json/invalid_value/install.out @@ -0,0 +1,7 @@ +error: Failed to install from package.json + +Caused by: + 0: Invalid version requirement + 1: Unexpected character. + invalid stuff that won't parse + ~ diff --git a/tests/specs/run/package_json/invalid_value/ok.ts.out b/tests/specs/run/package_json/invalid_value/ok.ts.out index e8fff7ed7a..0cfbf08886 100644 --- a/tests/specs/run/package_json/invalid_value/ok.ts.out +++ b/tests/specs/run/package_json/invalid_value/ok.ts.out @@ -1,4 +1 @@ -Download http://localhost:4260/@denotest/esm-basic -Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz -Initialize @denotest/esm-basic@1.0.0 2 diff --git a/tests/specs/task/package_json_node_modules_dir_none/__test__.jsonc b/tests/specs/task/package_json_node_modules_dir_none/__test__.jsonc new file mode 100644 index 0000000000..21d9d73ba0 --- /dev/null +++ b/tests/specs/task/package_json_node_modules_dir_none/__test__.jsonc @@ -0,0 +1,6 @@ +{ + "tempDir": true, + "args": "task echo", + "output": "bin.out", + "exitCode": 0 +} diff --git a/tests/specs/task/package_json_node_modules_dir_none/bin.out b/tests/specs/task/package_json_node_modules_dir_none/bin.out new file mode 100644 index 0000000000..c86261d9e1 --- /dev/null +++ b/tests/specs/task/package_json_node_modules_dir_none/bin.out @@ -0,0 +1,8 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/bin +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Download http://localhost:4260/@denotest/bin/0.5.0.tgz +[UNORDERED_END] +Task echo deno eval 'console.log(1)' && cli-esm hi +1 +hi diff --git a/tests/specs/task/package_json_node_modules_dir_none/deno.json b/tests/specs/task/package_json_node_modules_dir_none/deno.json new file mode 100644 index 0000000000..38af4024b0 --- /dev/null +++ b/tests/specs/task/package_json_node_modules_dir_none/deno.json @@ -0,0 +1,3 @@ +{ + "nodeModulesDir": "none" +} diff --git a/tests/testdata/task/package_json_node_modules_dir_false/package.json b/tests/specs/task/package_json_node_modules_dir_none/package.json similarity index 67% rename from tests/testdata/task/package_json_node_modules_dir_false/package.json rename to tests/specs/task/package_json_node_modules_dir_none/package.json index 081e076b9f..713a9dc5b4 100644 --- a/tests/testdata/task/package_json_node_modules_dir_false/package.json +++ b/tests/specs/task/package_json_node_modules_dir_none/package.json @@ -1,6 +1,6 @@ { "scripts": { - "echo": "deno eval 'console.log(1)'" + "echo": "deno eval 'console.log(1)' && cli-esm hi" }, "dependencies": { "@denotest/bin": "0.5", diff --git a/tests/testdata/run/deny_some_permission_args.out b/tests/testdata/run/deny_some_permission_args.out index be2e832fec..abb5274eed 100644 --- a/tests/testdata/run/deny_some_permission_args.out +++ b/tests/testdata/run/deny_some_permission_args.out @@ -1,4 +1,3 @@ -Warning --allow-run without an allow list is susceptible to exploits. Prefer specifying an allow list (https://docs.deno.com/runtime/fundamentals/security/#running-subprocesses) PermissionStatus { state: "granted", onchange: null, partial: true } PermissionStatus { state: "denied", onchange: null } PermissionStatus { state: "granted", onchange: null } diff --git a/tests/testdata/run/permission_request_no_prompt.ts b/tests/testdata/run/permission_request_no_prompt.ts new file mode 100644 index 0000000000..e33ffe3ce8 --- /dev/null +++ b/tests/testdata/run/permission_request_no_prompt.ts @@ -0,0 +1 @@ +console.log(await Deno.permissions.request({ name: "read" })); diff --git a/tests/testdata/task/package_json_node_modules_dir_false/bin.out b/tests/testdata/task/package_json_node_modules_dir_false/bin.out deleted file mode 100644 index d5d59d5515..0000000000 --- a/tests/testdata/task/package_json_node_modules_dir_false/bin.out +++ /dev/null @@ -1,2 +0,0 @@ -Task echo deno eval 'console.log(1)' -1 diff --git a/tests/testdata/task/package_json_node_modules_dir_false/deno.json b/tests/testdata/task/package_json_node_modules_dir_false/deno.json deleted file mode 100644 index 23a325cfc2..0000000000 --- a/tests/testdata/task/package_json_node_modules_dir_false/deno.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "nodeModulesDir": false -} diff --git a/tests/unit/console_test.ts b/tests/unit/console_test.ts index 201d18f004..878d17ae33 100644 --- a/tests/unit/console_test.ts +++ b/tests/unit/console_test.ts @@ -1913,6 +1913,21 @@ Deno.test(function consoleLogWhenCauseIsAssignedShouldNotPrintCauseTwice() { }); }); +Deno.test(function consoleLogCauseNotFilteredOnNonError() { + mockConsole((console, out) => { + const foo = { + a: 1, + b: 2, + cause: 3, + }; + console.log(foo); + + const result = stripAnsiCode(out.toString()); + const expected = "{ a: 1, b: 2, cause: 3 }\n"; + assertEquals(result.trim(), expected.trim()); + }); +}); + // console.log(new Proxy(new RegExp(), {})) Deno.test(function consoleLogShouldNotThrowErrorWhenInputIsProxiedRegExp() { mockConsole((console, out) => { diff --git a/tests/unit/permissions_test.ts b/tests/unit/permissions_test.ts index 82524d556c..f981b10fd6 100644 --- a/tests/unit/permissions_test.ts +++ b/tests/unit/permissions_test.ts @@ -20,54 +20,72 @@ Deno.test(function permissionInvalidNameSync() { }, TypeError); }); -Deno.test(async function permissionNetInvalidHost() { - await assertRejects(async () => { - await Deno.permissions.query({ name: "net", host: ":" }); - }, URIError); -}); +Deno.test( + { permissions: { net: [] } }, + async function permissionNetInvalidHost() { + await assertRejects(async () => { + await Deno.permissions.query({ name: "net", host: ":" }); + }, URIError); + }, +); -Deno.test(function permissionNetInvalidHostSync() { - assertThrows(() => { - Deno.permissions.querySync({ name: "net", host: ":" }); - }, URIError); -}); +Deno.test( + { permissions: { net: [] } }, + function permissionNetInvalidHostSync() { + assertThrows(() => { + Deno.permissions.querySync({ name: "net", host: ":" }); + }, URIError); + }, +); -Deno.test(async function permissionSysValidKind() { - await Deno.permissions.query({ name: "sys", kind: "loadavg" }); - await Deno.permissions.query({ name: "sys", kind: "osRelease" }); - await Deno.permissions.query({ name: "sys", kind: "osUptime" }); - await Deno.permissions.query({ name: "sys", kind: "networkInterfaces" }); - await Deno.permissions.query({ name: "sys", kind: "systemMemoryInfo" }); - await Deno.permissions.query({ name: "sys", kind: "hostname" }); - await Deno.permissions.query({ name: "sys", kind: "uid" }); - await Deno.permissions.query({ name: "sys", kind: "gid" }); - await Deno.permissions.query({ name: "sys", kind: "cpus" }); -}); +Deno.test( + { permissions: { sys: [] } }, + async function permissionSysValidKind() { + await Deno.permissions.query({ name: "sys", kind: "loadavg" }); + await Deno.permissions.query({ name: "sys", kind: "osRelease" }); + await Deno.permissions.query({ name: "sys", kind: "osUptime" }); + await Deno.permissions.query({ name: "sys", kind: "networkInterfaces" }); + await Deno.permissions.query({ name: "sys", kind: "systemMemoryInfo" }); + await Deno.permissions.query({ name: "sys", kind: "hostname" }); + await Deno.permissions.query({ name: "sys", kind: "uid" }); + await Deno.permissions.query({ name: "sys", kind: "gid" }); + await Deno.permissions.query({ name: "sys", kind: "cpus" }); + }, +); -Deno.test(function permissionSysValidKindSync() { - Deno.permissions.querySync({ name: "sys", kind: "loadavg" }); - Deno.permissions.querySync({ name: "sys", kind: "osRelease" }); - Deno.permissions.querySync({ name: "sys", kind: "networkInterfaces" }); - Deno.permissions.querySync({ name: "sys", kind: "systemMemoryInfo" }); - Deno.permissions.querySync({ name: "sys", kind: "hostname" }); - Deno.permissions.querySync({ name: "sys", kind: "uid" }); - Deno.permissions.querySync({ name: "sys", kind: "gid" }); - Deno.permissions.querySync({ name: "sys", kind: "cpus" }); -}); +Deno.test( + { permissions: { sys: [] } }, + function permissionSysValidKindSync() { + Deno.permissions.querySync({ name: "sys", kind: "loadavg" }); + Deno.permissions.querySync({ name: "sys", kind: "osRelease" }); + Deno.permissions.querySync({ name: "sys", kind: "networkInterfaces" }); + Deno.permissions.querySync({ name: "sys", kind: "systemMemoryInfo" }); + Deno.permissions.querySync({ name: "sys", kind: "hostname" }); + Deno.permissions.querySync({ name: "sys", kind: "uid" }); + Deno.permissions.querySync({ name: "sys", kind: "gid" }); + Deno.permissions.querySync({ name: "sys", kind: "cpus" }); + }, +); -Deno.test(async function permissionSysInvalidKind() { - await assertRejects(async () => { - // deno-lint-ignore no-explicit-any - await Deno.permissions.query({ name: "sys", kind: "abc" as any }); - }, TypeError); -}); +Deno.test( + { permissions: { sys: [] } }, + async function permissionSysInvalidKind() { + await assertRejects(async () => { + // deno-lint-ignore no-explicit-any + await Deno.permissions.query({ name: "sys", kind: "abc" as any }); + }, TypeError); + }, +); -Deno.test(function permissionSysInvalidKindSync() { - assertThrows(() => { - // deno-lint-ignore no-explicit-any - Deno.permissions.querySync({ name: "sys", kind: "abc" as any }); - }, TypeError); -}); +Deno.test( + { permissions: { sys: [] } }, + function permissionSysInvalidKindSync() { + assertThrows(() => { + // deno-lint-ignore no-explicit-any + Deno.permissions.querySync({ name: "sys", kind: "abc" as any }); + }, TypeError); + }, +); Deno.test(async function permissionQueryReturnsEventTarget() { const status = await Deno.permissions.query({ name: "read", path: "." }); @@ -134,29 +152,35 @@ Deno.test(function permissionStatusIllegalConstructor() { }); // Regression test for https://github.com/denoland/deno/issues/17020 -Deno.test(async function permissionURL() { - const path = new URL(".", import.meta.url); +Deno.test( + { permissions: { read: [], write: [], ffi: [], run: [] } }, + async function permissionURL() { + const path = new URL(".", import.meta.url); - await Deno.permissions.query({ name: "read", path }); - await Deno.permissions.query({ name: "write", path }); - await Deno.permissions.query({ name: "ffi", path }); - await Deno.permissions.query({ name: "run", command: path }); -}); + await Deno.permissions.query({ name: "read", path }); + await Deno.permissions.query({ name: "write", path }); + await Deno.permissions.query({ name: "ffi", path }); + await Deno.permissions.query({ name: "run", command: path }); + }, +); -Deno.test(function permissionURLSync() { - Deno.permissions.querySync({ - name: "read", - path: new URL(".", import.meta.url), - }); - Deno.permissions.querySync({ - name: "write", - path: new URL(".", import.meta.url), - }); - Deno.permissions.querySync({ - name: "run", - command: new URL(".", import.meta.url), - }); -}); +Deno.test( + { permissions: { read: [], write: [], ffi: [], run: [] } }, + function permissionURLSync() { + Deno.permissions.querySync({ + name: "read", + path: new URL(".", import.meta.url), + }); + Deno.permissions.querySync({ + name: "write", + path: new URL(".", import.meta.url), + }); + Deno.permissions.querySync({ + name: "run", + command: new URL(".", import.meta.url), + }); + }, +); Deno.test(async function permissionDescriptorValidation() { for (const value of [undefined, null, {}]) { diff --git a/tests/unit/urlpattern_test.ts b/tests/unit/urlpattern_test.ts index 65c2241737..3c1fb0cf19 100644 --- a/tests/unit/urlpattern_test.ts +++ b/tests/unit/urlpattern_test.ts @@ -63,9 +63,3 @@ Deno.test(function urlPatternWithPrototypePollution() { RegExp.prototype.exec = originalExec; } }); - -Deno.test(function urlPatternEmptyFallback() { - const p = new URLPattern({ pathname: "/foo/bar{/:qaz}?" }); - const match = p.exec("https://example.com/foo/bar"); - assertEquals(match!.pathname.groups.qaz, ""); -}); diff --git a/tests/unit/webcrypto_test.ts b/tests/unit/webcrypto_test.ts index 57aa19eaee..09552a0587 100644 --- a/tests/unit/webcrypto_test.ts +++ b/tests/unit/webcrypto_test.ts @@ -2045,3 +2045,43 @@ Deno.test(async function p521Generate() { assert(key.privateKey instanceof CryptoKey); assert(key.publicKey instanceof CryptoKey); }); + +Deno.test(async function x25519SharedSecret() { + const alicesKeyPair = await crypto.subtle.generateKey( + { + name: "X25519", + }, + false, + ["deriveBits"], + ) as CryptoKeyPair; + + const bobsKeyPair = await crypto.subtle.generateKey( + { + name: "X25519", + }, + false, + ["deriveBits"], + ) as CryptoKeyPair; + + const sharedSecret1 = await crypto.subtle.deriveBits( + { + name: "X25519", + public: bobsKeyPair.publicKey, + }, + alicesKeyPair.privateKey, + 128, + ); + + const sharedSecret2 = await crypto.subtle.deriveBits( + { + name: "X25519", + public: alicesKeyPair.publicKey, + }, + bobsKeyPair.privateKey, + 128, + ); + + assertEquals(sharedSecret1.byteLength, sharedSecret2.byteLength); + assertEquals(sharedSecret1.byteLength, 16); + assertEquals(new Uint8Array(sharedSecret1), new Uint8Array(sharedSecret2)); +}); diff --git a/tests/unit/webstorage_test.ts b/tests/unit/webstorage_test.ts index 9dc560af1b..aa832b1c4b 100644 --- a/tests/unit/webstorage_test.ts +++ b/tests/unit/webstorage_test.ts @@ -50,3 +50,8 @@ Deno.test(function webstorageProxy() { assertEquals(localStorage[symbol as any], "bar"); assertEquals(symbol in localStorage, true); }); + +Deno.test(function webstorageGetOwnPropertyDescriptorSymbol() { + localStorage.clear(); + Object.getOwnPropertyDescriptor(localStorage, Symbol("foo")); +}); diff --git a/tests/unit_node/fs_test.ts b/tests/unit_node/fs_test.ts index b1f7c53e8d..2d1465aec6 100644 --- a/tests/unit_node/fs_test.ts +++ b/tests/unit_node/fs_test.ts @@ -4,13 +4,16 @@ import { assert, assertEquals, assertThrows } from "@std/assert"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { + closeSync, constants, createWriteStream, existsSync, lstatSync, mkdtempSync, + openSync, promises, readFileSync, + readSync, Stats, statSync, writeFileSync, @@ -201,3 +204,11 @@ Deno.test( assertEquals(res, [0, 1, 2, 3, 4, 5]); }, ); + +Deno.test("[node/fs] readSync works", () => { + const fd = openSync("tests/testdata/assets/hello.txt", "r"); + const buf = new Uint8Array(256); + const bytesRead = readSync(fd!, buf); + assertEquals(bytesRead, 12); + closeSync(fd!); +}); diff --git a/tests/unit_node/worker_threads_test.ts b/tests/unit_node/worker_threads_test.ts index ac797601f5..24a9107898 100644 --- a/tests/unit_node/worker_threads_test.ts +++ b/tests/unit_node/worker_threads_test.ts @@ -621,3 +621,204 @@ Deno.test({ worker.terminate(); }, }); + +Deno.test({ + name: "[node/worker_threads] receiveMessageOnPort doesn't exit receive loop", + async fn() { + const worker = new workerThreads.Worker( + ` + import { parentPort, receiveMessageOnPort } from "node:worker_threads"; + parentPort.on("message", (msg) => { + const port = msg.port; + port.on("message", (msg2) => { + if (msg2 === "c") { + port.postMessage("done"); + port.unref(); + parentPort.unref(); + } + }); + parentPort.postMessage("ready"); + const msg2 = receiveMessageOnPort(port); + }); + `, + { eval: true }, + ); + + const { port1, port2 } = new workerThreads.MessageChannel(); + + worker.postMessage({ port: port2 }, [port2]); + + const done = Promise.withResolvers(); + + port1.on("message", (msg) => { + assertEquals(msg, "done"); + worker.unref(); + port1.close(); + done.resolve(true); + }); + worker.on("message", (msg) => { + assertEquals(msg, "ready"); + port1.postMessage("a"); + port1.postMessage("b"); + port1.postMessage("c"); + }); + + const timeout = setTimeout(() => { + fail("Test timed out"); + }, 20_000); + try { + const result = await done.promise; + assertEquals(result, true); + } finally { + clearTimeout(timeout); + } + }, +}); + +Deno.test({ + name: "[node/worker_threads] MessagePort.unref doesn't exit receive loop", + async fn() { + const worker = new workerThreads.Worker( + ` + import { parentPort } from "node:worker_threads"; + const assertEquals = (a, b) => { + if (a !== b) { + throw new Error(); + } + }; + let state = 0; + parentPort.on("message", (msg) => { + const port = msg.port; + const expect = ["a", "b", "c"]; + port.on("message", (msg2) => { + assertEquals(msg2, expect[state++]); + if (msg2 === "c") { + port.postMessage({ type: "done", got: msg2 }); + parentPort.unref(); + } + }); + port.unref(); + parentPort.postMessage("ready"); + }); + `, + { eval: true }, + ); + + const { port1, port2 } = new workerThreads.MessageChannel(); + + const done = Promise.withResolvers(); + + port1.on("message", (msg) => { + assertEquals(msg.type, "done"); + assertEquals(msg.got, "c"); + worker.unref(); + port1.close(); + done.resolve(true); + }); + worker.on("message", (msg) => { + assertEquals(msg, "ready"); + port1.postMessage("a"); + port1.postMessage("b"); + port1.postMessage("c"); + }); + worker.postMessage({ port: port2 }, [port2]); + + const timeout = setTimeout(() => { + fail("Test timed out"); + }, 20_000); + try { + const result = await done.promise; + assertEquals(result, true); + } finally { + clearTimeout(timeout); + } + }, +}); + +Deno.test({ + name: "[node/worker_threads] npm:piscina wait loop hang regression", + async fn() { + const worker = new workerThreads.Worker( + ` + import { assert, assertEquals } from "@std/assert"; + import { parentPort, receiveMessageOnPort } from "node:worker_threads"; + + assert(parentPort !== null); + + let currentTasks = 0; + let lastSeen = 0; + + parentPort.on("message", (msg) => { + (async () => { + assert(typeof msg === "object" && msg !== null); + assert(msg.buf !== undefined); + assert(msg.port !== undefined); + const { buf, port } = msg; + port.postMessage("ready"); + port.on("message", (msg) => onMessage(msg, buf, port)); + atomicsWaitLoop(buf, port); + })(); + }); + + function onMessage(msg, buf, port) { + currentTasks++; + (async () => { + assert(msg.taskName !== undefined); + port.postMessage({ type: "response", taskName: msg.taskName }); + currentTasks--; + atomicsWaitLoop(buf, port); + })(); + } + + function atomicsWaitLoop(buf, port) { + while (currentTasks === 0) { + Atomics.wait(buf, 0, lastSeen); + lastSeen = Atomics.load(buf, 0); + let task; + while ((task = receiveMessageOnPort(port)) !== undefined) { + onMessage(task.message, buf, port); + } + } + } + `, + { eval: true }, + ); + + const sab = new SharedArrayBuffer(4); + const buf = new Int32Array(sab); + const { port1, port2 } = new workerThreads.MessageChannel(); + + const done = Promise.withResolvers(); + + port1.unref(); + + worker.postMessage({ + type: "init", + buf, + port: port2, + }, [port2]); + + let count = 0; + port1.on("message", (msg) => { + if (count++ === 0) { + assertEquals(msg, "ready"); + } else { + assertEquals(msg.type, "response"); + port1.close(); + done.resolve(true); + } + }); + + port1.postMessage({ + taskName: "doThing", + }); + + Atomics.add(buf, 0, 1); + Atomics.notify(buf, 0, 1); + + worker.unref(); + + const result = await done.promise; + assertEquals(result, true); + }, +}); diff --git a/tests/wpt/runner/expectation.json b/tests/wpt/runner/expectation.json index 1b31f26e5a..3477d8d9bf 100644 --- a/tests/wpt/runner/expectation.json +++ b/tests/wpt/runner/expectation.json @@ -54,127 +54,65 @@ "pbkdf2.https.any.worker.html?7001-8000": true, "pbkdf2.https.any.worker.html?8001-last": true, "cfrg_curves_bits.https.any.html": [ - "X25519 key derivation checks for all-zero value result with a key of order 0", - "X25519 key derivation checks for all-zero value result with a key of order 1", - "X25519 key derivation checks for all-zero value result with a key of order 8", - "X25519 key derivation checks for all-zero value result with a key of order p-1 (order 2)", - "X25519 key derivation checks for all-zero value result with a key of order p (=0, order 4)", - "X25519 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)", "X448 key derivation checks for all-zero value result with a key of order 0", "X448 key derivation checks for all-zero value result with a key of order 1", "X448 key derivation checks for all-zero value result with a key of order p-1 (order 2)", "X448 key derivation checks for all-zero value result with a key of order p (=0, order 4)", "X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)", - "X25519 good parameters", - "X25519 mixed case parameters", - "X25519 short result", - "X25519 non-multiple of 8 bits", - "X25519 mismatched algorithms", - "X25519 no deriveBits usage for base key", "X448 good parameters", "X448 mixed case parameters", "X448 short result", "X448 non-multiple of 8 bits", "X448 mismatched algorithms", "X448 no deriveBits usage for base key", - "X448 base key is not a private key", "X448 public property value is a private key", "X448 public property value is a secret key", "X448 asking for too many bits" ], "cfrg_curves_bits.https.any.worker.html": [ - "X25519 key derivation checks for all-zero value result with a key of order 0", - "X25519 key derivation checks for all-zero value result with a key of order 1", - "X25519 key derivation checks for all-zero value result with a key of order 8", - "X25519 key derivation checks for all-zero value result with a key of order p-1 (order 2)", - "X25519 key derivation checks for all-zero value result with a key of order p (=0, order 4)", - "X25519 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)", "X448 key derivation checks for all-zero value result with a key of order 0", "X448 key derivation checks for all-zero value result with a key of order 1", "X448 key derivation checks for all-zero value result with a key of order p-1 (order 2)", "X448 key derivation checks for all-zero value result with a key of order p (=0, order 4)", "X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)", - "X25519 good parameters", - "X25519 mixed case parameters", - "X25519 short result", - "X25519 non-multiple of 8 bits", - "X25519 mismatched algorithms", - "X25519 no deriveBits usage for base key", "X448 good parameters", "X448 mixed case parameters", "X448 short result", "X448 non-multiple of 8 bits", "X448 mismatched algorithms", "X448 no deriveBits usage for base key", - "X448 base key is not a private key", "X448 public property value is a private key", "X448 public property value is a secret key", "X448 asking for too many bits" ], "cfrg_curves_keys.https.any.html": [ - "X25519 deriveBits checks for all-zero value result with a key of order 0", - "X25519 deriveBits checks for all-zero value result with a key of order 1", - "X25519 deriveBits checks for all-zero value result with a key of order 8", - "X25519 deriveBits checks for all-zero value result with a key of order p-1 (order 2)", - "X25519 deriveBits checks for all-zero value result with a key of order p (=0, order 4)", - "X25519 deriveBits checks for all-zero value result with a key of order p+1 (=1, order 1)", "X448 deriveBits checks for all-zero value result with a key of order 0", "X448 deriveBits checks for all-zero value result with a key of order 1", "X448 deriveBits checks for all-zero value result with a key of order p-1 (order 2)", "X448 deriveBits checks for all-zero value result with a key of order p (=0, order 4)", "X448 deriveBits checks for all-zero value result with a key of order p+1 (=1, order 1)", - "Key derivation using a X25519 generated keys.", - "Key derivation using a X448 generated keys.", - "X25519 good parameters", - "X25519 mixed case parameters", - "X25519 mismatched algorithms", "X448 good parameters", "X448 mixed case parameters", "X448 mismatched algorithms", "X448 no deriveKey usage for base key", - "X448 base key is not a private key", "X448 public property value is a private key", "X448 public property value is a secret key" ], "cfrg_curves_keys.https.any.worker.html": [ - "X25519 deriveBits checks for all-zero value result with a key of order 0", - "X25519 deriveBits checks for all-zero value result with a key of order 1", - "X25519 deriveBits checks for all-zero value result with a key of order 8", - "X25519 deriveBits checks for all-zero value result with a key of order p-1 (order 2)", - "X25519 deriveBits checks for all-zero value result with a key of order p (=0, order 4)", - "X25519 deriveBits checks for all-zero value result with a key of order p+1 (=1, order 1)", "X448 deriveBits checks for all-zero value result with a key of order 0", "X448 deriveBits checks for all-zero value result with a key of order 1", "X448 deriveBits checks for all-zero value result with a key of order p-1 (order 2)", "X448 deriveBits checks for all-zero value result with a key of order p (=0, order 4)", "X448 deriveBits checks for all-zero value result with a key of order p+1 (=1, order 1)", - "Key derivation using a X25519 generated keys.", - "Key derivation using a X448 generated keys.", - "X25519 good parameters", - "X25519 mixed case parameters", - "X25519 mismatched algorithms", "X448 good parameters", "X448 mixed case parameters", "X448 mismatched algorithms", "X448 no deriveKey usage for base key", - "X448 base key is not a private key", "X448 public property value is a private key", "X448 public property value is a secret key" ], - "derived_bits_length.https.any.html": [ - "X25519 derivation with 256 as 'length' parameter", - "X25519 derivation with 0 as 'length' parameter", - "X25519 derivation with null as 'length' parameter", - "X25519 derivation with undefined as 'length' parameter", - "X25519 derivation with omitted as 'length' parameter" - ], - "derived_bits_length.https.any.worker.html": [ - "X25519 derivation with 256 as 'length' parameter", - "X25519 derivation with 0 as 'length' parameter", - "X25519 derivation with null as 'length' parameter", - "X25519 derivation with undefined as 'length' parameter", - "X25519 derivation with omitted as 'length' parameter" - ] + "derived_bits_length.https.any.html": true, + "derived_bits_length.https.any.worker.html": true }, "digest": { "digest.https.any.html": true, @@ -819,82 +757,16 @@ ], "failures_X25519.https.any.html": true, "failures_X25519.https.any.worker.html": true, - "failures_X448.https.any.html": [ - "Bad usages: generateKey({name: X448}, true, [encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt])", - "Bad usages: generateKey({name: X448}, true, [decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt])", - "Bad usages: generateKey({name: X448}, true, [sign])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign])", - "Bad usages: generateKey({name: X448}, true, [verify])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify])", - "Bad usages: generateKey({name: X448}, true, [wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])", - "Empty usages: generateKey({name: X448}, false, [])", - "Empty usages: generateKey({name: X448}, true, [])" - ], - "failures_X448.https.any.worker.html": [ - "Bad usages: generateKey({name: X448}, true, [encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt])", - "Bad usages: generateKey({name: X448}, true, [decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt])", - "Bad usages: generateKey({name: X448}, true, [sign])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign])", - "Bad usages: generateKey({name: X448}, true, [verify])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify])", - "Bad usages: generateKey({name: X448}, true, [wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])", - "Empty usages: generateKey({name: X448}, false, [])", - "Empty usages: generateKey({name: X448}, true, [])" - ], + "failures_X448.https.any.html": true, + "failures_X448.https.any.worker.html": true, "successes_Ed25519.https.any.html": true, "successes_Ed25519.https.any.worker.html": true, "successes_Ed448.https.any.html": false, "successes_Ed448.https.any.worker.html": false, "successes_X25519.https.any.html": true, "successes_X25519.https.any.worker.html": true, - "successes_X448.https.any.html": false, - "successes_X448.https.any.worker.html": false + "successes_X448.https.any.html": true, + "successes_X448.https.any.worker.html": true }, "historical.any.html": false, "historical.any.worker.html": false, @@ -981,10 +853,6 @@ "Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])", - "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, true, [])", - "Good parameters with ignored JWK alg: X448 (jwk, object(kty, crv, x), {name: X448}, true, [])", - "Good parameters: X448 bits (raw, buffer(56), {name: X448}, true, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])", "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", "Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", @@ -997,17 +865,10 @@ "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (spki, buffer(68), {name: X448}, false, [])", - "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, false, [])", - "Good parameters: X448 bits (raw, buffer(56), {name: X448}, false, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits, deriveKey])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits, deriveKey])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits])", - "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])" + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])" ], "okp_importKey.https.any.worker.html": [ "Good parameters: Ed448 bits (spki, buffer(69), {name: Ed448}, true, [verify])", @@ -1049,10 +910,6 @@ "Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])", - "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, true, [])", - "Good parameters with ignored JWK alg: X448 (jwk, object(kty, crv, x), {name: X448}, true, [])", - "Good parameters: X448 bits (raw, buffer(56), {name: X448}, true, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])", "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", "Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", @@ -1065,17 +922,10 @@ "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (spki, buffer(68), {name: X448}, false, [])", - "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, false, [])", - "Good parameters: X448 bits (raw, buffer(56), {name: X448}, false, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits, deriveKey])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits, deriveKey])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits])", - "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])" + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])" ], "okp_importKey_failures_Ed25519.https.any.html": [ "Bad key length: importKey(raw, {name: Ed25519}, true, [verify])", @@ -1155,8 +1005,62 @@ "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits])", "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" ], - "okp_importKey_failures_X448.https.any.html": false, - "okp_importKey_failures_X448.https.any.worker.html": false, + "okp_importKey_failures_X448.https.any.html": [ + "Empty usages: importKey(pkcs8, {name: X448}, true, [])", + "Empty usages: importKey(pkcs8, {name: X448}, false, [])", + "Bad key length: importKey(raw, {name: X448}, true, [])", + "Bad key length: importKey(raw, {name: X448}, false, [])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk (public) , {name: X448}, true, [])", + "Bad key length: importKey(jwk (public) , {name: X448}, false, [])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" + ], + "okp_importKey_failures_X448.https.any.worker.html": [ + "Empty usages: importKey(pkcs8, {name: X448}, true, [])", + "Empty usages: importKey(pkcs8, {name: X448}, false, [])", + "Bad key length: importKey(raw, {name: X448}, true, [])", + "Bad key length: importKey(raw, {name: X448}, false, [])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk (public) , {name: X448}, true, [])", + "Bad key length: importKey(jwk (public) , {name: X448}, false, [])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" + ], "crashtests": { "importKey-unsettled-promise.https.any.html": true, "importKey-unsettled-promise.https.any.worker.html": true @@ -1531,18 +1435,28 @@ }, "wrapKey_unwrapKey": { "wrapKey_unwrapKey.https.any.html": [ - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-CTR", - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-CBC", - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-GCM", - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-KW", - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and RSA-OAEP" + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-CTR", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CTR", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-CBC", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CBC", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-KW", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-KW", + "Can wrap and unwrap X448 private key keys using pkcs8 and RSA-OAEP", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and RSA-OAEP" ], "wrapKey_unwrapKey.https.any.worker.html": [ - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-CTR", - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-CBC", - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-GCM", - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-KW", - "Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and RSA-OAEP" + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-CTR", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CTR", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-CBC", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CBC", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-KW", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-KW", + "Can wrap and unwrap X448 private key keys using pkcs8 and RSA-OAEP", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and RSA-OAEP" ] } }, @@ -11711,9 +11625,6 @@ "ctor-1.html": false, "ctor-null.html": false, "ctor-undefined.html": false, - "same-origin.html": [ - "non-parsable URL" - ], "terminate.html": true, "use-base-url.html": false }, @@ -12297,18 +12208,8 @@ "Component: hash Left: {\"hash\":\"a\"} Right: {\"hash\":\"b\"}" ], "urlpattern.any.html": [ - "Pattern: [{\"pathname\":\"/foo/:bar?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/:bar*\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/(.*)?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/*?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/(.*)*\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/**\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [\"https://(sub.)?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", - "Pattern: [\"https://(sub(?:.))?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", - "Pattern: [\"data\\\\:text/javascript,let x = 100/:tens?5;\"] Inputs: [\"data:text/javascript,let x = 100/5;\"]", "Pattern: [{\"hostname\":\"bad\\\\:hostname\"}] Inputs: undefined", "Pattern: [] Inputs: []", - "Pattern: [{\"pathname\":\"*{}**?\"}] Inputs: [{\"pathname\":\"foobar\"}]", "Pattern: [{\"pathname\":\"/foo/bar\"},{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO/BAR\"}]", "Pattern: [\"https://example.com:8080/foo?bar#baz\",{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO\",\"search\":\"BAR\",\"hash\":\"BAZ\",\"baseURL\":\"https://example.com:8080\"}]", "Pattern: [{\"pathname\":\"/([[a-z]--a])\"}] Inputs: [{\"pathname\":\"/a\"}]", @@ -12317,18 +12218,8 @@ "Pattern: [{\"pathname\":\"/([\\\\d&&[0-1]])\"}] Inputs: [{\"pathname\":\"/3\"}]" ], "urlpattern.any.worker.html": [ - "Pattern: [{\"pathname\":\"/foo/:bar?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/:bar*\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/(.*)?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/*?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/(.*)*\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/**\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [\"https://(sub.)?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", - "Pattern: [\"https://(sub(?:.))?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", - "Pattern: [\"data\\\\:text/javascript,let x = 100/:tens?5;\"] Inputs: [\"data:text/javascript,let x = 100/5;\"]", "Pattern: [{\"hostname\":\"bad\\\\:hostname\"}] Inputs: undefined", "Pattern: [] Inputs: []", - "Pattern: [{\"pathname\":\"*{}**?\"}] Inputs: [{\"pathname\":\"foobar\"}]", "Pattern: [{\"pathname\":\"/foo/bar\"},{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO/BAR\"}]", "Pattern: [\"https://example.com:8080/foo?bar#baz\",{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO\",\"search\":\"BAR\",\"hash\":\"BAZ\",\"baseURL\":\"https://example.com:8080\"}]", "Pattern: [{\"pathname\":\"/([[a-z]--a])\"}] Inputs: [{\"pathname\":\"/a\"}]", @@ -12337,18 +12228,8 @@ "Pattern: [{\"pathname\":\"/([\\\\d&&[0-1]])\"}] Inputs: [{\"pathname\":\"/3\"}]" ], "urlpattern.https.any.html": [ - "Pattern: [{\"pathname\":\"/foo/:bar?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/:bar*\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/(.*)?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/*?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/(.*)*\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/**\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [\"https://(sub.)?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", - "Pattern: [\"https://(sub(?:.))?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", - "Pattern: [\"data\\\\:text/javascript,let x = 100/:tens?5;\"] Inputs: [\"data:text/javascript,let x = 100/5;\"]", "Pattern: [{\"hostname\":\"bad\\\\:hostname\"}] Inputs: undefined", "Pattern: [] Inputs: []", - "Pattern: [{\"pathname\":\"*{}**?\"}] Inputs: [{\"pathname\":\"foobar\"}]", "Pattern: [{\"pathname\":\"/foo/bar\"},{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO/BAR\"}]", "Pattern: [\"https://example.com:8080/foo?bar#baz\",{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO\",\"search\":\"BAR\",\"hash\":\"BAZ\",\"baseURL\":\"https://example.com:8080\"}]", "Pattern: [{\"pathname\":\"/([[a-z]--a])\"}] Inputs: [{\"pathname\":\"/a\"}]", @@ -12357,18 +12238,8 @@ "Pattern: [{\"pathname\":\"/([\\\\d&&[0-1]])\"}] Inputs: [{\"pathname\":\"/3\"}]" ], "urlpattern.https.any.worker.html": [ - "Pattern: [{\"pathname\":\"/foo/:bar?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/:bar*\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/(.*)?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/*?\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/(.*)*\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [{\"pathname\":\"/foo/**\"}] Inputs: [{\"pathname\":\"/foo\"}]", - "Pattern: [\"https://(sub.)?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", - "Pattern: [\"https://(sub(?:.))?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", - "Pattern: [\"data\\\\:text/javascript,let x = 100/:tens?5;\"] Inputs: [\"data:text/javascript,let x = 100/5;\"]", "Pattern: [{\"hostname\":\"bad\\\\:hostname\"}] Inputs: undefined", "Pattern: [] Inputs: []", - "Pattern: [{\"pathname\":\"*{}**?\"}] Inputs: [{\"pathname\":\"foobar\"}]", "Pattern: [{\"pathname\":\"/foo/bar\"},{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO/BAR\"}]", "Pattern: [\"https://example.com:8080/foo?bar#baz\",{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO\",\"search\":\"BAR\",\"hash\":\"BAZ\",\"baseURL\":\"https://example.com:8080\"}]", "Pattern: [{\"pathname\":\"/([[a-z]--a])\"}] Inputs: [{\"pathname\":\"/a\"}]", diff --git a/tests/wpt/runner/utils.ts b/tests/wpt/runner/utils.ts index 1674419cd1..140c388ec2 100644 --- a/tests/wpt/runner/utils.ts +++ b/tests/wpt/runner/utils.ts @@ -76,7 +76,10 @@ export function getManifest(): Manifest { /// WPT TEST EXPECTATIONS -const EXPECTATION_PATH = join(ROOT_PATH, "./tests/wpt/runner/expectation.json"); +export const EXPECTATION_PATH = join( + ROOT_PATH, + "./tests/wpt/runner/expectation.json", +); export interface Expectation { [key: string]: Expectation | boolean | string[] | { ignore: boolean }; @@ -87,9 +90,12 @@ export function getExpectation(): Expectation { return JSON.parse(expectationText); } -export function saveExpectation(expectation: Expectation) { +export function saveExpectation( + expectation: Expectation, + path: string = EXPECTATION_PATH, +) { Deno.writeTextFileSync( - EXPECTATION_PATH, + path, JSON.stringify(expectation, undefined, " ") + "\n", ); } @@ -134,6 +140,15 @@ export function runPy>( }).spawn(); } +export async function runGitDiff(args: string[]): string { + await new Deno.Command("git", { + args: ["diff", ...args], + stdout: "inherit", + stderr: "inherit", + cwd: ROOT_PATH, + }).output(); +} + export async function checkPy3Available() { const { success, stdout } = await runPy(["--version"], { stdout: "piped", diff --git a/tests/wpt/wpt.ts b/tests/wpt/wpt.ts index c42ff51e6e..b13a10cf4c 100755 --- a/tests/wpt/wpt.ts +++ b/tests/wpt/wpt.ts @@ -18,6 +18,7 @@ import { checkPy3Available, escapeLoneSurrogates, Expectation, + EXPECTATION_PATH, generateRunInfo, getExpectation, getExpectFailForCase, @@ -30,6 +31,7 @@ import { noIgnore, quiet, rest, + runGitDiff, runPy, updateManifest, wptreport, @@ -256,7 +258,16 @@ async function run() { await Deno.writeTextFile(wptreport, JSON.stringify(report) + "\n"); } + const newExpectations = newExpectation(results); + const tmp = Deno.makeTempFileSync(); + saveExpectation(newExpectations, tmp); + const code = reportFinal(results, endTime - startTime); + + // Run git diff to see what changed + await runGitDiff([EXPECTATION_PATH, tmp]); + Deno.removeSync(tmp); + Deno.exit(code); } @@ -390,6 +401,19 @@ async function update() { await Deno.writeTextFile(json, JSON.stringify(results) + "\n"); } + const newExpectations = newExpectation(results); + saveExpectation(newExpectations); + + reportFinal(results, endTime - startTime); + + console.log(blue("Updated expectation.json to match reality.")); + + Deno.exit(0); +} + +function newExpectation( + results: { test: TestToRun; result: TestResult }[], +): Expectation { const resultTests: Record< string, { passed: string[]; failed: string[]; testSucceeded: boolean } @@ -431,13 +455,7 @@ async function update() { ); } - saveExpectation(currentExpectation); - - reportFinal(results, endTime - startTime); - - console.log(blue("Updated expectation.json to match reality.")); - - Deno.exit(0); + return currentExpectation; } function insertExpectation( diff --git a/tools/format.js b/tools/format.js index 74c608644b..b29667ca77 100755 --- a/tools/format.js +++ b/tools/format.js @@ -1,4 +1,4 @@ -#!/usr/bin/env -S deno run --allow-write --allow-read --allow-run=deno --allow-net --config=tests/config/deno.json +#!/usr/bin/env -S deno run --allow-all --config=tests/config/deno.json // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. import { join, ROOT_PATH } from "./util.js"; diff --git a/tools/lint.js b/tools/lint.js index a4302b3788..1f3f56498b 100755 --- a/tools/lint.js +++ b/tools/lint.js @@ -1,4 +1,4 @@ -#!/usr/bin/env -S deno run --allow-write --allow-read --allow-run --allow-net --config=tests/config/deno.json +#!/usr/bin/env -S deno run --allow-all --config=tests/config/deno.json // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // deno-lint-ignore-file no-console @@ -220,7 +220,7 @@ async function ensureNoNewITests() { "repl_tests.rs": 0, "run_tests.rs": 331, "shared_library_tests.rs": 0, - "task_tests.rs": 3, + "task_tests.rs": 2, "test_tests.rs": 0, "upgrade_tests.rs": 0, "vendor_tests.rs": 1,