diff --git a/.dprint.json b/.dprint.json index b9c2d1ebc1..bd1279fd4a 100644 --- a/.dprint.json +++ b/.dprint.json @@ -13,7 +13,7 @@ }, "exec": { "commands": [{ - "command": "rustfmt --config imports_granularity=item", + "command": "rustfmt --config imports_granularity=item --config group_imports=StdExternalCrate", "exts": ["rs"] }] }, diff --git a/.github/mtime_cache/action.js b/.github/mtime_cache/action.js index 72821749e3..1bf5b492fc 100644 --- a/.github/mtime_cache/action.js +++ b/.github/mtime_cache/action.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file contains the implementation of a Github Action. Github uses // Node.js v20.x to run actions, so this is Node code and not Deno code. diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index 5c2de96006..3214fad509 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S deno run --allow-write=. --lock=./tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. 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 = 30; +const cacheVersion = 33; const ubuntuX86Runner = "ubuntu-24.04"; const ubuntuX86XlRunner = "ubuntu-24.04-xl"; @@ -360,7 +360,7 @@ const ci = { needs: ["pre_build"], if: "${{ needs.pre_build.outputs.skip_build != 'true' }}", "runs-on": "${{ matrix.runner }}", - "timeout-minutes": 180, + "timeout-minutes": 240, defaults: { run: { // GH actions does not fail fast by default on @@ -484,6 +484,27 @@ const ci = { " -czvf target/release/deno_src.tar.gz -C .. deno", ].join("\n"), }, + { + name: "Cache Cargo home", + uses: "actions/cache@v4", + with: { + // See https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci + // Note that with the new sparse registry format, we no longer have to cache a `.git` dir + path: [ + "~/.cargo/.crates.toml", + "~/.cargo/.crates2.json", + "~/.cargo/bin", + "~/.cargo/registry/index", + "~/.cargo/registry/cache", + "~/.cargo/git/db", + ].join("\n"), + key: + `${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ hashFiles('Cargo.lock') }}`, + // We will try to restore from the closest cargo-home we can find + "restore-keys": + `${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-`, + }, + }, installRustStep, { if: @@ -607,23 +628,6 @@ const ci = { installBenchTools, ].join("\n"), }, - { - name: "Cache Cargo home", - uses: "actions/cache@v4", - with: { - // See https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci - // Note that with the new sparse registry format, we no longer have to cache a `.git` dir - path: [ - "~/.cargo/registry/index", - "~/.cargo/registry/cache", - ].join("\n"), - key: - `${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ hashFiles('Cargo.lock') }}`, - // We will try to restore from the closest cargo-home we can find - "restore-keys": - `${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-`, - }, - }, { // Restore cache from the latest 'main' branch build. name: "Restore cache build output (PR)", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44eb15cb95..e560840d2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: - pre_build if: '${{ needs.pre_build.outputs.skip_build != ''true'' }}' runs-on: '${{ matrix.runner }}' - timeout-minutes: 180 + timeout-minutes: 240 defaults: run: shell: bash @@ -174,6 +174,19 @@ jobs: mkdir -p target/release tar --exclude=".git*" --exclude=target --exclude=third_party/prebuilt \ -czvf target/release/deno_src.tar.gz -C .. deno + - name: Cache Cargo home + uses: actions/cache@v4 + with: + path: |- + ~/.cargo/.crates.toml + ~/.cargo/.crates2.json + ~/.cargo/bin + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + key: '33-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '33-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' + if: '!(matrix.skip)' - uses: dsherret/rust-toolchain-file@v1 if: '!(matrix.skip)' - if: '!(matrix.skip) && (matrix.job == ''lint'' || matrix.job == ''test'' || matrix.job == ''bench'')' @@ -355,15 +368,6 @@ jobs: - name: Install benchmark tools if: '!(matrix.skip) && (matrix.job == ''bench'')' run: ./tools/install_prebuilt.js wrk hyperfine - - name: Cache Cargo home - uses: actions/cache@v4 - with: - path: |- - ~/.cargo/registry/index - ~/.cargo/registry/cache - key: '30-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '30-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' - if: '!(matrix.skip)' - name: Restore cache build output (PR) uses: actions/cache/restore@v4 if: '!(matrix.skip) && (github.ref != ''refs/heads/main'' && !startsWith(github.ref, ''refs/tags/''))' @@ -375,7 +379,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '30-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '33-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 @@ -685,7 +689,7 @@ jobs: !./target/*/gn_root !./target/*/*.zip !./target/*/*.tar.gz - key: '30-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '33-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-24.04 diff --git a/Cargo.lock b/Cargo.lock index 077f1e1e18..aadd0c5f95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "aead-gcm-stream" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4947a169074c7e038fa43051d1c4e073f4488b0e4b0a30658f1e1a1b06449ce8" +checksum = "e70c8dec860340effb00f6945c49c0daaa6dac963602750db862eabb74bf7886" dependencies = [ "aead", "aes", @@ -380,7 +380,7 @@ dependencies = [ "rustversion", "serde", "sync_wrapper", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", ] @@ -596,9 +596,9 @@ dependencies = [ [[package]] name = "boxed_error" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69aae56aaf59d1994b902ed5c0c79024012bdc2426741def75a635999a030e7e" +checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" dependencies = [ "quote", "syn 2.0.87", @@ -670,13 +670,35 @@ checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" [[package]] name = "capacity_builder" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c0f637033edd76ceb881faaee372868a383f0ed7a4a59e8fdf90db2502f3d3" +checksum = "58ec49028cb308564429cd8fac4ef21290067a0afe8f5955330a8d487d0d790c" dependencies = [ "itoa", ] +[[package]] +name = "capacity_builder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6" +dependencies = [ + "capacity_builder_macros", + "ecow", + "hipstr", + "itoa", +] + +[[package]] +name = "capacity_builder_macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" +dependencies = [ + "quote", + "syn 2.0.87", +] + [[package]] name = "caseless" version = "0.2.1" @@ -728,6 +750,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.37" @@ -839,6 +867,7 @@ dependencies = [ "regex", "reqwest", "serde", + "sys_traits", "test_server", "tokio", "url", @@ -1193,9 +1222,9 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "data-url" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b319d1b62ffbd002e057f36bebd1f42b9f97927c9577461d855f3513c4289f" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "debug-ignore" @@ -1215,14 +1244,16 @@ dependencies = [ [[package]] name = "deno" -version = "2.1.4" +version = "2.1.5" dependencies = [ "anstream", "async-trait", "base64 0.21.7", "bincode", + "boxed_error", "bytes", "cache_control", + "capacity_builder 0.5.0", "chrono", "clap", "clap_complete", @@ -1237,6 +1268,7 @@ dependencies = [ "deno_config", "deno_core", "deno_doc", + "deno_error", "deno_graph", "deno_lint", "deno_lockfile", @@ -1309,6 +1341,7 @@ dependencies = [ "spki", "sqlformat", "strsim", + "sys_traits", "tar", "tempfile", "test_server", @@ -1322,7 +1355,7 @@ dependencies = [ "typed-arena", "uuid", "walkdir", - "which 4.4.2", + "which", "winapi", "winres", "zeromq", @@ -1388,7 +1421,7 @@ dependencies = [ [[package]] name = "deno_bench_util" -version = "0.176.0" +version = "0.179.0" dependencies = [ "bencher", "deno_core", @@ -1397,10 +1430,11 @@ dependencies = [ [[package]] name = "deno_broadcast_channel" -version = "0.176.0" +version = "0.179.0" dependencies = [ "async-trait", "deno_core", + "deno_error", "thiserror 2.0.3", "tokio", "uuid", @@ -1408,10 +1442,11 @@ dependencies = [ [[package]] name = "deno_cache" -version = "0.114.0" +version = "0.117.0" dependencies = [ "async-trait", "deno_core", + "deno_error", "rusqlite", "serde", "sha2", @@ -1421,13 +1456,21 @@ dependencies = [ [[package]] name = "deno_cache_dir" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca43605c8cbce6c6787e0daf227864487c07c2b31d438c0bf43d1b38da94b7f" +checksum = "e73ed17f285731a23df9779ca1e0e721de866db6776ed919ebd9235e0a107c4c" dependencies = [ + "async-trait", "base32", + "base64 0.21.7", + "boxed_error", + "cache_control", + "chrono", + "data-url", + "deno_error", "deno_media_type", "deno_path_util", + "http 1.1.0", "indexmap 2.3.0", "log", "once_cell", @@ -1435,15 +1478,17 @@ dependencies = [ "serde", "serde_json", "sha2", + "sys_traits", "thiserror 1.0.64", "url", ] [[package]] name = "deno_canvas" -version = "0.51.0" +version = "0.54.0" dependencies = [ "deno_core", + "deno_error", "deno_webgpu", "image", "serde", @@ -1452,11 +1497,13 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.39.3" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce717af3fe6788dae63965d58d5637fd62be8fe4f345f189137ffc06c51837d2" +checksum = "47a47412627aa0d08414eca0e8329128013ab70bdb2cdfdc5456c2214cf24c8f" dependencies = [ - "anyhow", + "boxed_error", + "capacity_builder 0.5.0", + "deno_error", "deno_package_json", "deno_path_util", "deno_semver", @@ -1470,22 +1517,23 @@ dependencies = [ "phf", "serde", "serde_json", - "thiserror 1.0.64", + "sys_traits", + "thiserror 2.0.3", "url", ] [[package]] name = "deno_console" -version = "0.182.0" +version = "0.185.0" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.324.0" +version = "0.330.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24503eda646f246aa6eb0f794909f9a857c8f05095fed66f36e0eaef92edce23" +checksum = "fd38bbbd68ed873165ccb630322704b44140d3a8c8d50f898beac4d1a8a3358c" dependencies = [ "anyhow", "az", @@ -1493,8 +1541,10 @@ dependencies = [ "bit-set", "bit-vec", "bytes", + "capacity_builder 0.1.3", "cooked-waker", "deno_core_icudata", + "deno_error", "deno_ops", "deno_unsync", "futures", @@ -1510,6 +1560,7 @@ dependencies = [ "smallvec", "sourcemap 8.0.1", "static_assertions", + "thiserror 2.0.3", "tokio", "url", "v8", @@ -1524,12 +1575,13 @@ checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" [[package]] name = "deno_cron" -version = "0.62.0" +version = "0.65.0" dependencies = [ "anyhow", "async-trait", "chrono", "deno_core", + "deno_error", "saffron", "thiserror 2.0.3", "tokio", @@ -1537,7 +1589,7 @@ dependencies = [ [[package]] name = "deno_crypto" -version = "0.196.0" +version = "0.199.0" dependencies = [ "aes", "aes-gcm", @@ -1548,6 +1600,7 @@ dependencies = [ "ctr", "curve25519-dalek", "deno_core", + "deno_error", "deno_web", "ed448-goldilocks", "elliptic-curve", @@ -1574,9 +1627,9 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.161.2" +version = "0.164.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af787319136f3e7f73ef551c618aeec70794522e36cd75ae35132a3bad983ef" +checksum = "ad1edb02603c7e8a4003c84af2482a05e5eda3a14f1af275434fda89223f054d" dependencies = [ "anyhow", "cfg-if", @@ -1584,6 +1637,7 @@ dependencies = [ "deno_ast", "deno_graph", "deno_path_util", + "deno_terminal 0.2.0", "handlebars", "html-escape", "import_map", @@ -1603,21 +1657,23 @@ dependencies = [ [[package]] name = "deno_error" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c66ffd17ee1a948904d33f3d3f364573951c1f9fb3f859bfe7770bf33862a" +checksum = "c4da6a58de6932a96f84e133c072fd3b525966ee122a71f3efd48bbff2eed5ac" dependencies = [ "deno_error_macro", "libc", "serde", "serde_json", + "tokio", + "url", ] [[package]] name = "deno_error_macro" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd99df6ae75443907e1f959fc42ec6dcea67a7bd083e76cf23a117102c9a2ce" +checksum = "46351dff93aed2039407c91e2ded2a5591e42d2795ab3d111288625bb710d3d2" dependencies = [ "proc-macro2", "quote", @@ -1626,18 +1682,20 @@ dependencies = [ [[package]] name = "deno_fetch" -version = "0.206.0" +version = "0.209.0" dependencies = [ "base64 0.21.7", "bytes", "data-url", "deno_core", + "deno_error", "deno_path_util", "deno_permissions", "deno_tls", "dyn-clone", "error_reporter", "fast-socks5", + "h2 0.4.4", "hickory-resolver", "http 1.1.0", "http-body-util", @@ -1654,16 +1712,17 @@ dependencies = [ "tokio-rustls", "tokio-socks", "tokio-util", - "tower", + "tower 0.5.2", "tower-http", "tower-service", ] [[package]] name = "deno_ffi" -version = "0.169.0" +version = "0.172.0" dependencies = [ "deno_core", + "deno_error", "deno_permissions", "dlopen2", "dynasmrt", @@ -1681,12 +1740,13 @@ dependencies = [ [[package]] name = "deno_fs" -version = "0.92.0" +version = "0.95.0" dependencies = [ "async-trait", "base32", "boxed_error", "deno_core", + "deno_error", "deno_io", "deno_path_util", "deno_permissions", @@ -1704,14 +1764,17 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.86.3" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc78ed0b4bbcb4197300f0d6e7d1edc2d2c5019cdb9dedba7ff229158441885b" +checksum = "f56d4eb4b7c81ae920b6d18c45a1866924f93110caee80bbbc362dc28143f2bb" dependencies = [ - "anyhow", "async-trait", + "capacity_builder 0.5.0", "data-url", "deno_ast", + "deno_error", + "deno_media_type", + "deno_path_util", "deno_semver", "deno_unsync", "encoding_rs", @@ -1726,6 +1789,7 @@ dependencies = [ "serde", "serde_json", "sha2", + "sys_traits", "thiserror 2.0.3", "twox-hash", "url", @@ -1734,7 +1798,7 @@ dependencies = [ [[package]] name = "deno_http" -version = "0.180.0" +version = "0.183.0" dependencies = [ "async-compression", "async-trait", @@ -1744,6 +1808,7 @@ dependencies = [ "bytes", "cache_control", "deno_core", + "deno_error", "deno_net", "deno_websocket", "flate2", @@ -1773,10 +1838,11 @@ dependencies = [ [[package]] name = "deno_io" -version = "0.92.0" +version = "0.95.0" dependencies = [ "async-trait", "deno_core", + "deno_error", "filetime", "fs3", "libc", @@ -1794,7 +1860,7 @@ dependencies = [ [[package]] name = "deno_kv" -version = "0.90.0" +version = "0.93.0" dependencies = [ "anyhow", "async-trait", @@ -1803,6 +1869,7 @@ dependencies = [ "bytes", "chrono", "deno_core", + "deno_error", "deno_fetch", "deno_path_util", "deno_permissions", @@ -1844,9 +1911,9 @@ dependencies = [ [[package]] name = "deno_lockfile" -version = "0.23.2" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c19feb00af0c34f0bd4a20e56e12463fafd5c5069d6005f3ce33008027eea" +checksum = "632e835a53ed667d62fdd766c5780fe8361c831d3e3fbf1a760a0b7896657587" dependencies = [ "deno_semver", "serde", @@ -1856,9 +1923,9 @@ dependencies = [ [[package]] name = "deno_media_type" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf552fbdedbe81c89705349d7d2485c7051382b000dfddbdbf7fc25931cf83" +checksum = "a417f8bd3f1074185c4c8ccb6ea6261ae173781596cc358e68ad07aaac11009d" dependencies = [ "data-url", "serde", @@ -1867,9 +1934,10 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.113.0" +version = "0.116.0" dependencies = [ "deno_core", + "deno_error", "deno_permissions", "libc", "libloading 0.7.4", @@ -1895,14 +1963,16 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.174.0" +version = "0.177.0" dependencies = [ "deno_core", + "deno_error", "deno_permissions", "deno_tls", "hickory-proto", "hickory-resolver", "pin-project", + "quinn", "rustls-tokio-stream", "serde", "socket2", @@ -1912,7 +1982,7 @@ dependencies = [ [[package]] name = "deno_node" -version = "0.119.0" +version = "0.123.0" dependencies = [ "aead-gcm-stream", "aes", @@ -1926,6 +1996,7 @@ dependencies = [ "const-oid", "data-encoding", "deno_core", + "deno_error", "deno_fetch", "deno_fs", "deno_io", @@ -1946,7 +2017,6 @@ dependencies = [ "faster-hex", "h2 0.4.4", "hkdf", - "home", "http 1.1.0", "http-body-util", "hyper 1.4.1", @@ -1990,6 +2060,7 @@ dependencies = [ "sm3", "spki", "stable_deref_trait", + "sys_traits", "thiserror 2.0.3", "tokio", "tokio-eld", @@ -2004,12 +2075,13 @@ dependencies = [ [[package]] name = "deno_npm" -version = "0.26.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f125a5dba7839c46394a0a9c835da9fe60f5f412587ab4956a76492a1cc6a8" +checksum = "4adceb4c34f10e837d0e3ae76e88dddefb13e83c05c1ef1699fa5519241c9d27" dependencies = [ - "anyhow", "async-trait", + "capacity_builder 0.5.0", + "deno_error", "deno_lockfile", "deno_semver", "futures", @@ -2023,15 +2095,15 @@ dependencies = [ [[package]] name = "deno_npm_cache" -version = "0.2.0" +version = "0.4.0" dependencies = [ - "anyhow", "async-trait", "base64 0.21.7", "boxed_error", "deno_cache_dir", - "deno_core", + "deno_error", "deno_npm", + "deno_path_util", "deno_semver", "deno_unsync", "faster-hex", @@ -2044,6 +2116,7 @@ dependencies = [ "rand", "ring", "serde_json", + "sys_traits", "tar", "tempfile", "thiserror 2.0.3", @@ -2052,10 +2125,11 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.200.0" +version = "0.206.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a529a2c488cd3042f12f35666569ebe5b3cf89d2b7d1cafc1a652f6d7bcc8f" +checksum = "4c25ffa9d088ea00748dbef870bba110ac22ebf8cf7b2e9eb288409c5d852af3" dependencies = [ + "indexmap 2.3.0", "proc-macro-rules", "proc-macro2", "quote", @@ -2063,42 +2137,68 @@ dependencies = [ "strum", "strum_macros", "syn 2.0.87", - "thiserror 1.0.64", + "thiserror 2.0.3", +] + +[[package]] +name = "deno_os" +version = "0.1.0" +dependencies = [ + "deno_core", + "deno_error", + "deno_path_util", + "deno_permissions", + "deno_telemetry", + "libc", + "netif", + "ntapi", + "once_cell", + "serde", + "signal-hook", + "signal-hook-registry", + "thiserror 2.0.3", + "tokio", + "winapi", ] [[package]] name = "deno_package_json" -version = "0.2.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b0a3d81c592624a1ae15332a04b4dc2b7c163ef1dfc7c60171f736d1babdf5" +checksum = "e1d3c0f699ba2040669204ce24ab73720499fc290af843e4ce0fc8a9b3d67735" dependencies = [ + "boxed_error", "deno_error", "deno_path_util", "deno_semver", "indexmap 2.3.0", "serde", "serde_json", + "sys_traits", "thiserror 2.0.3", "url", ] [[package]] name = "deno_path_util" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99" +checksum = "420e8211aaba7fde83ccaa9a5dad855c3b940ed988d70c95159acd600a70dc87" dependencies = [ + "deno_error", "percent-encoding", - "thiserror 1.0.64", + "sys_traits", + "thiserror 2.0.3", "url", ] [[package]] name = "deno_permissions" -version = "0.42.0" +version = "0.44.0" dependencies = [ - "capacity_builder", + "capacity_builder 0.5.0", "deno_core", + "deno_error", "deno_path_util", "deno_terminal 0.2.0", "fqdn", @@ -2108,24 +2208,31 @@ dependencies = [ "percent-encoding", "serde", "thiserror 2.0.3", - "which 4.4.2", + "which", "winapi", ] [[package]] name = "deno_resolver" -version = "0.14.0" +version = "0.16.0" dependencies = [ "anyhow", + "async-trait", "base32", "boxed_error", "dashmap", + "deno_cache_dir", "deno_config", + "deno_error", "deno_media_type", + "deno_npm", "deno_package_json", "deno_path_util", "deno_semver", + "log", "node_resolver", + "parking_lot", + "sys_traits", "test_server", "thiserror 2.0.3", "url", @@ -2133,7 +2240,7 @@ dependencies = [ [[package]] name = "deno_runtime" -version = "0.191.0" +version = "0.193.0" dependencies = [ "color-print", "deno_ast", @@ -2144,6 +2251,7 @@ dependencies = [ "deno_core", "deno_cron", "deno_crypto", + "deno_error", "deno_fetch", "deno_ffi", "deno_fs", @@ -2153,8 +2261,10 @@ dependencies = [ "deno_napi", "deno_net", "deno_node", + "deno_os", "deno_path_util", "deno_permissions", + "deno_resolver", "deno_telemetry", "deno_terminal 0.2.0", "deno_tls", @@ -2175,7 +2285,6 @@ dependencies = [ "hyper-util", "libc", "log", - "netif", "nix", "node_resolver", "notify", @@ -2186,8 +2295,7 @@ dependencies = [ "rustyline", "same-file", "serde", - "signal-hook", - "signal-hook-registry", + "sys_traits", "tempfile", "test_server", "thiserror 2.0.3", @@ -2195,18 +2303,21 @@ dependencies = [ "tokio-metrics", "twox-hash", "uuid", - "which 4.4.2", + "which", "winapi", "windows-sys 0.59.0", ] [[package]] name = "deno_semver" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d1259270d66a5e6d29bb75c9289656541874f79ae9ff6c9f1c790846d5c07ba" +checksum = "4775271f9b5602482698f76d24ea9ed8ba27af7f587a7e9a876916300c542435" dependencies = [ + "capacity_builder 0.5.0", "deno_error", + "ecow", + "hipstr", "monch", "once_cell", "serde", @@ -2234,10 +2345,11 @@ dependencies = [ [[package]] name = "deno_telemetry" -version = "0.4.0" +version = "0.7.0" dependencies = [ "async-trait", "deno_core", + "deno_error", "http-body-util", "hyper 1.4.1", "hyper-util", @@ -2250,6 +2362,7 @@ dependencies = [ "opentelemetry_sdk", "pin-project", "serde", + "thiserror 2.0.3", "tokio", ] @@ -2275,9 +2388,10 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.169.0" +version = "0.172.0" dependencies = [ "deno_core", + "deno_error", "deno_native_certs", "rustls", "rustls-pemfile", @@ -2308,7 +2422,7 @@ dependencies = [ "serde_json", "tokio", "tokio-util", - "tower", + "tower 0.4.13", "tracing", ] @@ -2325,11 +2439,12 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.182.0" +version = "0.185.0" dependencies = [ "deno_bench_util", "deno_console", "deno_core", + "deno_error", "deno_webidl", "thiserror 2.0.3", "urlpattern", @@ -2337,7 +2452,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.213.0" +version = "0.216.0" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -2345,6 +2460,7 @@ dependencies = [ "deno_bench_util", "deno_console", "deno_core", + "deno_error", "deno_permissions", "deno_url", "deno_webidl", @@ -2359,9 +2475,10 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.149.0" +version = "0.152.0" dependencies = [ "deno_core", + "deno_error", "raw-window-handle", "serde", "thiserror 2.0.3", @@ -2372,7 +2489,7 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.182.0" +version = "0.185.0" dependencies = [ "deno_bench_util", "deno_core", @@ -2380,10 +2497,11 @@ dependencies = [ [[package]] name = "deno_websocket" -version = "0.187.0" +version = "0.190.0" dependencies = [ "bytes", "deno_core", + "deno_error", "deno_net", "deno_permissions", "deno_tls", @@ -2402,9 +2520,10 @@ dependencies = [ [[package]] name = "deno_webstorage" -version = "0.177.0" +version = "0.180.0" dependencies = [ "deno_core", + "deno_error", "deno_web", "rusqlite", "thiserror 2.0.3", @@ -2422,13 +2541,13 @@ dependencies = [ [[package]] name = "denokv_proto" -version = "0.8.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ba1f99ed11a9c11e868a8521b1f71a7e1aba785d7f42ea9ecbdc01146c89ec" +checksum = "d5b77de4d3b9215e14624d4f4eb16cb38c0810e3f5860ba3b3fc47d0537f9a4d" dependencies = [ - "anyhow", "async-trait", "chrono", + "deno_error", "futures", "num-bigint", "prost", @@ -2438,15 +2557,15 @@ dependencies = [ [[package]] name = "denokv_remote" -version = "0.8.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ed833073189e8f6d03155fe3b05a024e75e29d8a28a4c2e9ec3b5c925e727b" +checksum = "c6497c28eec268ed99f1e8664f0842935f02d1508529c67d94c57ca5d893d743" dependencies = [ - "anyhow", "async-stream", "async-trait", "bytes", "chrono", + "deno_error", "denokv_proto", "futures", "http 1.1.0", @@ -2455,6 +2574,7 @@ dependencies = [ "rand", "serde", "serde_json", + "thiserror 2.0.3", "tokio", "tokio-util", "url", @@ -2463,14 +2583,14 @@ dependencies = [ [[package]] name = "denokv_sqlite" -version = "0.8.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b790f01d1302d53a0c3cbd27de88a06b3abd64ec8ab8673924e490541c7c713" +checksum = "dc0f21a450a35eb85760761401fddf9bfff9840127be07a6ca5c31863127913d" dependencies = [ - "anyhow", "async-stream", "async-trait", "chrono", + "deno_error", "denokv_proto", "futures", "hex", @@ -2479,7 +2599,7 @@ dependencies = [ "rand", "rusqlite", "serde_json", - "thiserror 1.0.64", + "thiserror 2.0.3", "tokio", "tokio-stream", "uuid", @@ -2873,6 +2993,15 @@ dependencies = [ "spki", ] +[[package]] +name = "ecow" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42fc0a93992b20c58b99e59d61eaf1635a25bfbe49e4275c34ba0aee98119ba" +dependencies = [ + "serde", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -3428,6 +3557,19 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +dependencies = [ + "cfg-if", + "libc", + "log", + "rustversion", + "windows 0.58.0", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -3503,8 +3645,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -3809,6 +3951,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "hipstr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97971ffc85d4c98de12e2608e992a43f5294ebb625fdb045b27c731b64c4c6d6" +dependencies = [ + "serde", + "serde_bytes", + "sptr", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -4009,9 +4162,9 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper 1.4.1", "hyper-util", @@ -4222,16 +4375,18 @@ dependencies = [ [[package]] name = "import_map" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a787decc56f38d65d16d32687265045d6d6a4531b4a0e1b649def3590354e" +checksum = "1215d4d92511fbbdaea50e750e91f2429598ef817f02b579158e92803b52c00a" dependencies = [ + "boxed_error", + "deno_error", "indexmap 2.3.0", "log", "percent-encoding", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 2.0.3", "url", ] @@ -4423,12 +4578,12 @@ dependencies = [ [[package]] name = "junction" -version = "0.2.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be39922b087cecaba4e2d5592dedfc8bda5d4a5a1231f143337cca207950b61d" +checksum = "72bbdfd737a243da3dfc1f99ee8d6e166480f17ab4ac84d7c34aacd73fc7bd16" dependencies = [ "scopeguard", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4538,9 +4693,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libffi" @@ -4681,6 +4836,19 @@ dependencies = [ "serde", ] +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "lsp-types" version = "0.97.0" @@ -4735,6 +4903,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -4847,21 +5024,20 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.7" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e0d88686dc561d743b40de8269b26eaf0dc58781bde087b0984646602021d08" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "once_cell", + "loom", "parking_lot", - "quanta", + "portable-atomic", "rustc_version 0.4.0", "smallvec", "tagptr", "thiserror 1.0.64", - "triomphe", "uuid", ] @@ -4916,7 +5092,7 @@ dependencies = [ [[package]] name = "napi_sym" -version = "0.112.0" +version = "0.115.0" dependencies = [ "quote", "serde", @@ -4971,11 +5147,12 @@ dependencies = [ [[package]] name = "node_resolver" -version = "0.21.0" +version = "0.23.0" dependencies = [ "anyhow", "async-trait", "boxed_error", + "deno_error", "deno_media_type", "deno_package_json", "deno_path_util", @@ -4985,6 +5162,7 @@ dependencies = [ "path-clean", "regex", "serde_json", + "sys_traits", "thiserror 2.0.3", "tokio", "url", @@ -5038,6 +5216,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -5295,6 +5483,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "p224" version = "0.13.2" @@ -5629,6 +5823,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + [[package]] name = "powerfmt" version = "0.2.0" @@ -5846,21 +6046,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "quanta" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" -dependencies = [ - "crossbeam-utils", - "libc", - "once_cell", - "raw-cpuid", - "wasi", - "web-sys", - "winapi", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -5892,49 +6077,54 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.2" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "rustls", - "thiserror 1.0.64", + "socket2", + "thiserror 2.0.3", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom", "rand", "ring", "rustc-hash 2.0.0", "rustls", + "rustls-pki-types", "slab", - "thiserror 1.0.64", + "thiserror 2.0.3", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.2" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" +checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" dependencies = [ + "cfg_aliases 0.2.1", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6025,15 +6215,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" -[[package]] -name = "raw-cpuid" -version = "11.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" -dependencies = [ - "bitflags 2.6.0", -] - [[package]] name = "raw-window-handle" version = "0.6.1" @@ -6108,8 +6289,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -6120,9 +6310,15 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.3", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.3" @@ -6411,6 +6607,9 @@ name = "rustls-pki-types" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +dependencies = [ + "web-time", +] [[package]] name = "rustls-tokio-stream" @@ -6694,14 +6893,15 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.233.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307f176b7475480cee690c34c7118f96fe564d1f2a974bf990294b8310ae4983" +checksum = "3caa6d882827148e5d9052d9d8d6d1c9d6ad426ed00cab46cafb8c07a0e7126a" dependencies = [ + "deno_error", "num-bigint", "serde", "smallvec", - "thiserror 1.0.64", + "thiserror 2.0.3", "v8", ] @@ -6753,6 +6953,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell-escape" version = "0.1.5" @@ -6972,6 +7181,12 @@ dependencies = [ "der", ] +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "sqlformat" version = "0.3.2" @@ -7567,6 +7782,19 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "sys_traits" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b46ac05dfbe9fd3a9703eff20e17f5b31e7b6a54daf27a421dcd56c7a27ecdd" +dependencies = [ + "filetime", + "getrandom", + "libc", + "parking_lot", + "windows-sys 0.59.0", +] + [[package]] name = "tagptr" version = "0.2.0" @@ -7962,7 +8190,7 @@ dependencies = [ "socket2", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -7988,6 +8216,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-http" version = "0.6.1" @@ -8016,9 +8259,9 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -8050,6 +8293,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -8281,9 +8554,9 @@ dependencies = [ [[package]] name = "v8" -version = "130.0.1" +version = "130.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c23b5c2caff00209b03a716609b275acae94b02dd3b63c4648e7232a84a8402f" +checksum = "a511192602f7b435b0a241c1947aa743eb7717f20a9195f4b5e8ed1952e01db1" dependencies = [ "bindgen", "bitflags 2.6.0", @@ -8293,7 +8566,7 @@ dependencies = [ "miniz_oxide", "once_cell", "paste", - "which 6.0.1", + "which", ] [[package]] @@ -8311,6 +8584,12 @@ dependencies = [ "wtf8", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "value-trait" version = "0.10.0" @@ -8475,11 +8754,12 @@ dependencies = [ [[package]] name = "wasm_dep_analyzer" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f270206a91783fd90625c8bb0d8fbd459d0b1d1bf209b656f713f01ae7c04b8" +checksum = "2eeee3bdea6257cc36d756fa745a70f9d393571e47d69e0ed97581676a5369ca" dependencies = [ - "thiserror 1.0.64", + "deno_error", + "thiserror 2.0.3", ] [[package]] @@ -8492,6 +8772,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-root-certs" version = "0.26.6" @@ -8519,7 +8809,7 @@ dependencies = [ "arrayvec", "bit-vec", "bitflags 2.6.0", - "cfg_aliases", + "cfg_aliases 0.1.1", "codespan-reporting", "document-features", "indexmap 2.3.0", @@ -8551,7 +8841,7 @@ dependencies = [ "bit-set", "bitflags 2.6.0", "block", - "cfg_aliases", + "cfg_aliases 0.1.1", "core-graphics-types", "d3d12", "glow", @@ -8593,18 +8883,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "which" version = "6.0.1" @@ -8641,7 +8919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b2b1bf557d947847a30eb73f79aa6cdb3eaf3ce02f5e9599438f77896a62b3c" dependencies = [ "thiserror 1.0.64", - "windows", + "windows 0.52.0", ] [[package]] @@ -8681,7 +8959,17 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", "windows-targets 0.52.6", ] @@ -8694,6 +8982,60 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 27038110d3..d9f976d863 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [workspace] resolver = "2" @@ -48,55 +48,56 @@ repository = "https://github.com/denoland/deno" [workspace.dependencies] deno_ast = { version = "=0.44.0", features = ["transpiling"] } -deno_core = { version = "0.324.0" } +deno_core = { version = "0.330.0" } -deno_bench_util = { version = "0.176.0", path = "./bench_util" } -deno_config = { version = "=0.39.3", features = ["workspace", "sync"] } -deno_lockfile = "=0.23.2" -deno_media_type = { version = "0.2.0", features = ["module_specifier"] } -deno_npm = "=0.26.0" -deno_path_util = "=0.2.1" -deno_permissions = { version = "0.42.0", path = "./runtime/permissions" } -deno_runtime = { version = "0.191.0", path = "./runtime" } -deno_semver = "=0.6.1" +deno_bench_util = { version = "0.179.0", path = "./bench_util" } +deno_config = { version = "=0.45.0", features = ["workspace", "sync"] } +deno_lockfile = "=0.24.0" +deno_media_type = { version = "0.2.3", features = ["module_specifier"] } +deno_npm = "=0.27.2" +deno_path_util = "=0.3.0" +deno_permissions = { version = "0.44.0", path = "./runtime/permissions" } +deno_runtime = { version = "0.193.0", path = "./runtime" } +deno_semver = "=0.7.1" deno_terminal = "0.2.0" -napi_sym = { version = "0.112.0", path = "./ext/napi/sym" } +napi_sym = { version = "0.115.0", path = "./ext/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } -denokv_proto = "0.8.4" -denokv_remote = "0.8.4" +denokv_proto = "0.9.0" +denokv_remote = "0.9.0" # denokv_sqlite brings in bundled sqlite if we don't disable the default features -denokv_sqlite = { default-features = false, version = "0.8.4" } +denokv_sqlite = { default-features = false, version = "0.9.0" } # exts -deno_broadcast_channel = { version = "0.176.0", path = "./ext/broadcast_channel" } -deno_cache = { version = "0.114.0", path = "./ext/cache" } -deno_canvas = { version = "0.51.0", path = "./ext/canvas" } -deno_console = { version = "0.182.0", path = "./ext/console" } -deno_cron = { version = "0.62.0", path = "./ext/cron" } -deno_crypto = { version = "0.196.0", path = "./ext/crypto" } -deno_fetch = { version = "0.206.0", path = "./ext/fetch" } -deno_ffi = { version = "0.169.0", path = "./ext/ffi" } -deno_fs = { version = "0.92.0", path = "./ext/fs" } -deno_http = { version = "0.180.0", path = "./ext/http" } -deno_io = { version = "0.92.0", path = "./ext/io" } -deno_kv = { version = "0.90.0", path = "./ext/kv" } -deno_napi = { version = "0.113.0", path = "./ext/napi" } -deno_net = { version = "0.174.0", path = "./ext/net" } -deno_node = { version = "0.119.0", path = "./ext/node" } -deno_telemetry = { version = "0.4.0", path = "./ext/telemetry" } -deno_tls = { version = "0.169.0", path = "./ext/tls" } -deno_url = { version = "0.182.0", path = "./ext/url" } -deno_web = { version = "0.213.0", path = "./ext/web" } -deno_webgpu = { version = "0.149.0", path = "./ext/webgpu" } -deno_webidl = { version = "0.182.0", path = "./ext/webidl" } -deno_websocket = { version = "0.187.0", path = "./ext/websocket" } -deno_webstorage = { version = "0.177.0", path = "./ext/webstorage" } +deno_broadcast_channel = { version = "0.179.0", path = "./ext/broadcast_channel" } +deno_cache = { version = "0.117.0", path = "./ext/cache" } +deno_canvas = { version = "0.54.0", path = "./ext/canvas" } +deno_console = { version = "0.185.0", path = "./ext/console" } +deno_cron = { version = "0.65.0", path = "./ext/cron" } +deno_crypto = { version = "0.199.0", path = "./ext/crypto" } +deno_fetch = { version = "0.209.0", path = "./ext/fetch" } +deno_ffi = { version = "0.172.0", path = "./ext/ffi" } +deno_fs = { version = "0.95.0", path = "./ext/fs" } +deno_http = { version = "0.183.0", path = "./ext/http" } +deno_io = { version = "0.95.0", path = "./ext/io" } +deno_kv = { version = "0.93.0", path = "./ext/kv" } +deno_napi = { version = "0.116.0", path = "./ext/napi" } +deno_net = { version = "0.177.0", path = "./ext/net" } +deno_node = { version = "0.123.0", path = "./ext/node" } +deno_os = { version = "0.1.0", path = "./ext/os" } +deno_telemetry = { version = "0.7.0", path = "./ext/telemetry" } +deno_tls = { version = "0.172.0", path = "./ext/tls" } +deno_url = { version = "0.185.0", path = "./ext/url" } +deno_web = { version = "0.216.0", path = "./ext/web" } +deno_webgpu = { version = "0.152.0", path = "./ext/webgpu" } +deno_webidl = { version = "0.185.0", path = "./ext/webidl" } +deno_websocket = { version = "0.190.0", path = "./ext/websocket" } +deno_webstorage = { version = "0.180.0", path = "./ext/webstorage" } # resolvers -deno_npm_cache = { version = "0.2.0", path = "./resolvers/npm_cache" } -deno_resolver = { version = "0.14.0", path = "./resolvers/deno" } -node_resolver = { version = "0.21.0", path = "./resolvers/node" } +deno_npm_cache = { version = "0.4.0", path = "./resolvers/npm_cache" } +deno_resolver = { version = "0.16.0", path = "./resolvers/deno" } +node_resolver = { version = "0.23.0", path = "./resolvers/node" } aes = "=0.8.3" anyhow = "1.0.57" @@ -104,11 +105,11 @@ async-trait = "0.1.73" base32 = "=0.5.1" base64 = "0.21.7" bencher = "0.1" -boxed_error = "0.2.2" +boxed_error = "0.2.3" brotli = "6.0.0" bytes = "1.4.0" cache_control = "=0.2.0" -capacity_builder = "0.1.0" +capacity_builder = "0.5.0" cbc = { version = "=0.1.2", features = ["alloc"] } # Note: Do not use the "clock" feature of chrono, as it links us to CoreFoundation on macOS. # Instead use util::time::utc_now() @@ -117,9 +118,10 @@ color-print = "0.3.5" console_static_text = "=0.8.1" dashmap = "5.5.3" data-encoding = "2.3.3" -data-url = "=0.3.0" -deno_cache_dir = "=0.14.0" -deno_package_json = { version = "0.2.1", default-features = false } +data-url = "=0.3.1" +deno_cache_dir = "=0.16.0" +deno_error = "=0.5.3" +deno_package_json = { version = "0.4.0", default-features = false } deno_unsync = "0.4.2" dlopen2 = "0.6.1" ecb = "=0.1.2" @@ -148,7 +150,7 @@ indexmap = { version = "2", features = ["serde"] } ipnet = "2.3" jsonc-parser = { version = "=0.26.2", features = ["serde"] } lazy-regex = "3" -libc = "0.2.126" +libc = "0.2.168" libz-sys = { version = "1.1.20", default-features = false } log = { version = "0.4.20", features = ["kv"] } lsp-types = "=0.97.0" # used by tower-lsp and "proposed" feature is unstable in patch releases @@ -192,6 +194,7 @@ slab = "0.4" smallvec = "1.8" socket2 = { version = "0.5.3", features = ["all"] } spki = "0.7.2" +sys_traits = "=0.1.7" tar = "=0.4.40" tempfile = "3.4.0" termcolor = "1.1.3" @@ -201,7 +204,7 @@ tokio-metrics = { version = "0.3.0", features = ["rt"] } tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring", "tls12"] } tokio-socks = "0.5.1" tokio-util = "0.7.4" -tower = { version = "0.4.13", default-features = false, features = ["util"] } +tower = { version = "0.5.2", default-features = false, features = ["retry", "util"] } tower-http = { version = "0.6.1", features = ["decompression-br", "decompression-gzip"] } tower-lsp = { package = "deno_tower_lsp", version = "0.1.0", features = ["proposed"] } tower-service = "0.3.2" @@ -210,7 +213,7 @@ url = { version = "2.5", features = ["serde", "expose_internals"] } uuid = { version = "1.3.0", features = ["v4"] } webpki-root-certs = "0.26.5" webpki-roots = "0.26" -which = "4.2.5" +which = "6" yoke = { version = "0.7.4", features = ["derive"] } zeromq = { version = "=0.4.1", default-features = false, features = ["tcp-transport", "tokio-runtime"] } zstd = "=0.12.4" @@ -238,7 +241,7 @@ syn = { version = "2", features = ["full", "extra-traits"] } nix = "=0.27.1" # windows deps -junction = "=0.2.0" +junction = "=1.2.0" winapi = "=0.3.9" windows-sys = { version = "0.59.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", "Win32_System_Threading", "Win32_UI", "Win32_UI_Shell"] } winres = "=0.1.12" diff --git a/LICENSE.md b/LICENSE.md index 56753af367..406ae09364 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright 2018-2024 the Deno authors +Copyright 2018-2025 the Deno authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/Releases.md b/Releases.md index aaae202a37..71cf524ca2 100644 --- a/Releases.md +++ b/Releases.md @@ -6,6 +6,85 @@ https://github.com/denoland/deno/releases We also have one-line install commands at: https://github.com/denoland/deno_install +### 2.1.5 / 2025.01.09 + +- feat(unstable): implement QUIC (#21942) +- feat(unstable): add JS linting plugin infrastructure (#27416) +- feat(unstable): add OTEL MeterProvider (#27240) +- feat(unstable): no config npm:@opentelemetry/api integration (#27541) +- feat(unstable): replace SpanExporter with TracerProvider (#27473) +- feat(unstable): support selectors in JS lint plugins (#27452) +- fix(check): line-break between diagnostic message chain entries (#27543) +- fix(check): move module not found errors to typescript diagnostics (#27533) +- fix(compile): analyze modules in directory specified in --include (#27296) +- fix(compile): be more deterministic when compiling the same code in different + directories (#27395) +- fix(compile): display embedded file sizes and total (#27360) +- fix(compile): output contents of embedded file system (#27302) +- fix(ext/fetch): better error message when body resource is unavailable + (#27429) +- fix(ext/fetch): retry some http/2 errors (#27417) +- fix(ext/fs): do not throw for bigint ctime/mtime/atime (#27453) +- fix(ext/http): improve error message when underlying resource of request body + unavailable (#27463) +- fix(ext/net): update moka cache to avoid potential panic in `Deno.resolveDns` + on some laptops with Ryzen CPU (#27572) +- fix(ext/node): fix `fs.access`/`fs.promises.access` with `X_OK` mode parameter + on Windows (#27407) +- fix(ext/node): fix `os.cpus()` on Linux (#27592) +- fix(ext/node): RangeError timingSafeEqual with different byteLength (#27470) +- fix(ext/node): add `truncate` method to the `FileHandle` class (#27389) +- fix(ext/node): add support of any length IV for aes-(128|256)-gcm ciphers + (#27476) +- fix(ext/node): convert brotli chunks with proper byte offset (#27455) +- fix(ext/node): do not exit worker thread when there is pending async op + (#27378) +- fix(ext/node): have `process` global available in Node context (#27562) +- fix(ext/node): make getCiphers return supported ciphers (#27466) +- fix(ext/node): sort list of built-in modules alphabetically (#27410) +- fix(ext/node): support createConnection option in node:http.request() (#25470) +- fix(ext/node): support private key export in JWK format (#27325) +- fix(ext/web): add `[[ErrorData]]` slot to `DOMException` (#27342) +- fix(ext/websocket): Fix close code without reason (#27578) +- fix(jsr): Wasm imports fail to load (#27594) +- fix(kv): improve backoff error message and inline documentation (#27537) +- fix(lint): fix single char selectors being ignored (#27576) +- fix(lockfile): include dependencies listed in external import map in lockfile + (#27337) +- fix(lsp): css preprocessor formatting (#27526) +- fix(lsp): don't skip dirs with enabled subdirs (#27580) +- fix(lsp): include "node:" prefix for node builtin auto-imports (#27404) +- fix(lsp): respect "typescript.suggestionActions.enabled" setting (#27373) +- fix(lsp): rewrite imports for 'Move to a new file' action (#27427) +- fix(lsp): sql and component file formatting (#27350) +- fix(lsp): use verbatim specifier for URL auto-imports (#27605) +- fix(no-slow-types): handle rest param with internal assignments (#27581) +- fix(node/fs): add a chmod method to the FileHandle class (#27522) +- fix(node): add missing `inspector/promises` (#27491) +- fix(node): handle cjs exports with escaped chars (#27438) +- fix(npm): deterministically output tags to initialized file (#27514) +- fix(npm): search node_modules folder for package matching npm specifier + (#27345) +- fix(outdated): ensure "Latest" version is greater than "Update" version + (#27390) +- fix(outdated): support updating dependencies in external import maps (#27339) +- fix(permissions): implicit `--allow-import` when using `--cached-only` + (#27530) +- fix(publish): infer literal types in const contexts (#27425) +- fix(task): properly handle task name wildcards with --recursive (#27396) +- fix(task): support tasks without commands (#27191) +- fix(unstable): don't error on non-existing attrs or type attr (#27456) +- fix: FastString v8_string() should error when cannot allocated (#27375) +- fix: deno_resolver crate without 'sync' feature (#27403) +- fix: incorrect memory info free/available bytes on mac (#27460) +- fix: upgrade deno_doc to 0.161.3 (#27377) +- perf(fs/windows): stat - only open file once (#27487) +- perf(node/fs/copy): reduce metadata lookups copying directory (#27495) +- perf: don't store duplicate info for ops in the snapshot (#27430) +- perf: remove now needless canonicalization getting closest package.json + (#27437) +- perf: upgrade to deno_semver 0.7 (#27426) + ### 2.1.4 / 2024.12.11 - feat(unstable): support caching npm dependencies only as they're needed diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index 9833996fd3..30a88e931e 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_bench_util" -version = "0.176.0" +version = "0.179.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/bench_util/README.md b/bench_util/README.md index 12474a86b6..30616a08fd 100644 --- a/bench_util/README.md +++ b/bench_util/README.md @@ -7,7 +7,6 @@ use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::Bencher; - use deno_core::Extension; #[op2] diff --git a/bench_util/benches/utf8.rs b/bench_util/benches/utf8.rs index 48af4dba7e..88afce86c3 100644 --- a/bench_util/benches/utf8.rs +++ b/bench_util/benches/utf8.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_sync_with; use deno_bench_util::bench_or_profile; diff --git a/bench_util/js_runtime.rs b/bench_util/js_runtime.rs index a97d8ae501..402c9a4b00 100644 --- a/bench_util/js_runtime.rs +++ b/bench_util/js_runtime.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use bencher::Bencher; use deno_core::v8; use deno_core::Extension; diff --git a/bench_util/lib.rs b/bench_util/lib.rs index 39183be7fc..22587a5f7e 100644 --- a/bench_util/lib.rs +++ b/bench_util/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod js_runtime; mod profiling; diff --git a/bench_util/profiling.rs b/bench_util/profiling.rs index 151a29e599..1d2bfb51d2 100644 --- a/bench_util/profiling.rs +++ b/bench_util/profiling.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use bencher::DynBenchFn; use bencher::StaticBenchFn; use bencher::TestDescAndFn; diff --git a/cli/Cargo.toml b/cli/Cargo.toml index cf76dfe69d..8248d40701 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno" -version = "2.1.4" +version = "2.1.5" authors.workspace = true default-run = "deno" edition.workspace = true @@ -62,6 +62,7 @@ serde_json.workspace = true zstd.workspace = true glibc_version = "0.1.2" flate2 = { workspace = true, features = ["default"] } +deno_error.workspace = true [target.'cfg(windows)'.build-dependencies] winapi.workspace = true @@ -72,15 +73,16 @@ deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposa deno_cache_dir.workspace = true deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } -deno_doc = { version = "=0.161.2", features = ["rust", "comrak"] } -deno_graph = { version = "=0.86.3" } +deno_doc = { version = "=0.164.0", features = ["rust", "comrak"] } +deno_error.workspace = true +deno_graph = { version = "=0.87.0" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true deno_npm.workspace = true deno_npm_cache.workspace = true deno_package_json.workspace = true deno_path_util.workspace = true -deno_resolver.workspace = true +deno_resolver = { workspace = true, features = ["sync"] } deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_semver.workspace = true deno_task_shell = "=0.20.2" @@ -93,8 +95,10 @@ anstream = "0.6.14" async-trait.workspace = true base64.workspace = true bincode = "=1.3.3" +boxed_error.workspace = true bytes.workspace = true cache_control.workspace = true +capacity_builder.workspace = true chrono = { workspace = true, features = ["now"] } clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] } clap_complete = "=4.5.24" @@ -121,7 +125,7 @@ http.workspace = true http-body.workspace = true http-body-util.workspace = true hyper-util.workspace = true -import_map = { version = "=0.20.1", features = ["ext"] } +import_map = { version = "=0.21.0", features = ["ext"] } indexmap.workspace = true jsonc-parser = { workspace = true, features = ["cst", "serde"] } jupyter_runtime = { package = "runtimelib", version = "=0.19.0", features = ["tokio-runtime"] } @@ -155,6 +159,7 @@ shell-escape = "=0.1.5" spki = { version = "0.7", features = ["pem"] } sqlformat = "=0.3.2" strsim = "0.11.1" +sys_traits = { workspace = true, features = ["getrandom", "filetime", "libc", "real", "strip_unc", "winapi"] } tar.workspace = true tempfile.workspace = true text-size = "=1.1.0" @@ -183,6 +188,7 @@ nix.workspace = true [dev-dependencies] deno_bench_util.workspace = true pretty_assertions.workspace = true +sys_traits = { workspace = true, features = ["memory"] } test_util.workspace = true [package.metadata.winres] diff --git a/cli/args/deno_json.rs b/cli/args/deno_json.rs index 8853107eef..c27b1d3924 100644 --- a/cli/args/deno_json.rs +++ b/cli/args/deno_json.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; @@ -8,62 +8,6 @@ use deno_semver::jsr::JsrDepPackageReq; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; -#[cfg(test)] // happens to only be used by the tests at the moment -pub struct DenoConfigFsAdapter<'a>( - pub &'a dyn deno_runtime::deno_fs::FileSystem, -); - -#[cfg(test)] -impl<'a> deno_config::fs::DenoConfigFs for DenoConfigFsAdapter<'a> { - fn read_to_string_lossy( - &self, - path: &std::path::Path, - ) -> Result, std::io::Error> { - self - .0 - .read_text_file_lossy_sync(path, None) - .map_err(|err| err.into_io_error()) - } - - fn stat_sync( - &self, - path: &std::path::Path, - ) -> Result { - self - .0 - .stat_sync(path) - .map(|stat| deno_config::fs::FsMetadata { - is_file: stat.is_file, - is_directory: stat.is_directory, - is_symlink: stat.is_symlink, - }) - .map_err(|err| err.into_io_error()) - } - - fn read_dir( - &self, - path: &std::path::Path, - ) -> Result, std::io::Error> { - self - .0 - .read_dir_sync(path) - .map_err(|err| err.into_io_error()) - .map(|entries| { - entries - .into_iter() - .map(|e| deno_config::fs::FsDirEntry { - path: path.join(e.name), - metadata: deno_config::fs::FsMetadata { - is_file: e.is_file, - is_directory: e.is_directory, - is_symlink: e.is_symlink, - }, - }) - .collect() - }) - } -} - pub fn import_map_deps( import_map: &serde_json::Value, ) -> HashSet { diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 418edcf34b..fb64b4eeaa 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1,6 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use std::borrow::Cow; use std::collections::HashSet; use std::env; use std::ffi::OsString; @@ -34,7 +33,6 @@ use deno_core::url::Url; use deno_graph::GraphKind; use deno_path_util::normalize_path; use deno_path_util::url_to_file_path; -use deno_runtime::deno_permissions::PermissionsOptions; use deno_runtime::deno_permissions::SysDescriptor; use deno_telemetry::OtelConfig; use deno_telemetry::OtelConsoleConfig; @@ -43,11 +41,8 @@ use log::Level; use serde::Deserialize; use serde::Serialize; -use crate::args::resolve_no_prompt; -use crate::util::fs::canonicalize_path; - use super::flags_net; -use super::jsr_url; +use crate::util::fs::canonicalize_path; #[derive(Clone, Debug, Default, Eq, PartialEq)] pub enum ConfigFlag { @@ -693,97 +688,6 @@ impl PermissionFlags { || self.deny_write.is_some() || self.allow_import.is_some() } - - pub fn to_options(&self, cli_arg_urls: &[Cow]) -> PermissionsOptions { - fn handle_allow( - allow_all: bool, - value: Option, - ) -> Option { - if allow_all { - assert!(value.is_none()); - Some(T::default()) - } else { - value - } - } - - fn handle_imports( - cli_arg_urls: &[Cow], - imports: Option>, - ) -> Option> { - if imports.is_some() { - return imports; - } - - let builtin_allowed_import_hosts = [ - "jsr.io:443", - "deno.land:443", - "esm.sh:443", - "cdn.jsdelivr.net:443", - "raw.githubusercontent.com:443", - "gist.githubusercontent.com:443", - ]; - - let mut imports = - Vec::with_capacity(builtin_allowed_import_hosts.len() + 1); - imports - .extend(builtin_allowed_import_hosts.iter().map(|s| s.to_string())); - - // also add the JSR_URL env var - if let Some(jsr_host) = allow_import_host_from_url(jsr_url()) { - imports.push(jsr_host); - } - // include the cli arg urls - for url in cli_arg_urls { - if let Some(host) = allow_import_host_from_url(url) { - imports.push(host); - } - } - - Some(imports) - } - - PermissionsOptions { - allow_all: self.allow_all, - allow_env: handle_allow(self.allow_all, self.allow_env.clone()), - deny_env: self.deny_env.clone(), - allow_net: handle_allow(self.allow_all, self.allow_net.clone()), - deny_net: self.deny_net.clone(), - allow_ffi: handle_allow(self.allow_all, self.allow_ffi.clone()), - deny_ffi: self.deny_ffi.clone(), - allow_read: handle_allow(self.allow_all, self.allow_read.clone()), - deny_read: self.deny_read.clone(), - allow_run: handle_allow(self.allow_all, self.allow_run.clone()), - deny_run: self.deny_run.clone(), - allow_sys: handle_allow(self.allow_all, self.allow_sys.clone()), - deny_sys: self.deny_sys.clone(), - allow_write: handle_allow(self.allow_all, self.allow_write.clone()), - deny_write: self.deny_write.clone(), - allow_import: handle_imports( - cli_arg_urls, - handle_allow(self.allow_all, self.allow_import.clone()), - ), - prompt: !resolve_no_prompt(self), - } - } -} - -/// Gets the --allow-import host from the provided url -fn allow_import_host_from_url(url: &Url) -> Option { - let host = url.host()?; - if let Some(port) = url.port() { - Some(format!("{}:{}", host, port)) - } else { - use deno_core::url::Host::*; - match host { - Domain(domain) if domain == "jsr.io" && url.scheme() == "https" => None, - _ => match url.scheme() { - "https" => Some(format!("{}:443", host)), - "http" => Some(format!("{}:80", host)), - _ => None, - }, - } - } } fn join_paths(allowlist: &[String], d: &str) -> String { @@ -1006,6 +910,8 @@ impl Flags { OtelConfig { tracing_enabled: !disabled && otel_var("OTEL_DENO_TRACING").unwrap_or(default), + metrics_enabled: !disabled + && otel_var("OTEL_DENO_METRICS").unwrap_or(default), console: match std::env::var("OTEL_DENO_CONSOLE").as_deref() { Ok(_) if disabled => OtelConsoleConfig::Ignore, Ok("ignore") => OtelConsoleConfig::Ignore, @@ -6057,9 +5963,10 @@ pub fn resolve_urls(urls: Vec) -> Vec { #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; + use super::*; + /// Creates vector of strings, Vec macro_rules! svec { ($($x:expr),* $(,)?) => (vec![$($x.to_string().into()),*]); @@ -11547,8 +11454,6 @@ mod tests { ..Default::default() } ); - // just make sure this doesn't panic - let _ = flags.permissions.to_options(&[]); } #[test] @@ -11624,29 +11529,6 @@ Usage: deno repl [OPTIONS] [-- [ARGS]...]\n" ) } - #[test] - fn test_allow_import_host_from_url() { - fn parse(text: &str) -> Option { - allow_import_host_from_url(&Url::parse(text).unwrap()) - } - - assert_eq!(parse("https://jsr.io"), None); - assert_eq!( - parse("http://127.0.0.1:4250"), - Some("127.0.0.1:4250".to_string()) - ); - assert_eq!(parse("http://jsr.io"), Some("jsr.io:80".to_string())); - assert_eq!( - parse("https://example.com"), - Some("example.com:443".to_string()) - ); - assert_eq!( - parse("http://example.com"), - Some("example.com:80".to_string()) - ); - assert_eq!(parse("file:///example.com"), None); - } - #[test] fn allow_all_conflicts_allow_perms() { let flags = [ diff --git a/cli/args/flags_net.rs b/cli/args/flags_net.rs index abfcf28382..c39e377e10 100644 --- a/cli/args/flags_net.rs +++ b/cli/args/flags_net.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::net::IpAddr; +use std::str::FromStr; use deno_core::url::Url; use deno_runtime::deno_permissions::NetDescriptor; -use std::net::IpAddr; -use std::str::FromStr; #[derive(Debug, PartialEq, Eq)] pub struct ParsePortError(String); diff --git a/cli/args/import_map.rs b/cli/args/import_map.rs index ff2f158715..ff7e42ef20 100644 --- a/cli/args/import_map.rs +++ b/cli/args/import_map.rs @@ -1,24 +1,24 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::TextDecodedFile; pub async fn resolve_import_map_value_from_specifier( specifier: &Url, - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, ) -> Result { if specifier.scheme() == "data" { let data_url_text = deno_graph::source::RawDataUrl::parse(specifier)?.decode()?; Ok(serde_json::from_str(&data_url_text)?) } else { - let file = file_fetcher - .fetch_bypass_permissions(specifier) - .await? - .into_text_decoded()?; + let file = TextDecodedFile::decode( + file_fetcher.fetch_bypass_permissions(specifier).await?, + )?; Ok(serde_json::from_str(&file.source)?) } } diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index 1075f93a6f..976992aac8 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::path::PathBuf; @@ -10,20 +10,20 @@ use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::parking_lot::MutexGuard; use deno_core::serde_json; +use deno_error::JsErrorBox; +use deno_lockfile::Lockfile; use deno_lockfile::WorkspaceMemberConfig; use deno_package_json::PackageJsonDepValue; +use deno_path_util::fs::atomic_write_file_with_retries; use deno_runtime::deno_node::PackageJson; use deno_semver::jsr::JsrDepPackageReq; use crate::args::deno_json::import_map_deps; -use crate::cache; -use crate::util::fs::atomic_write_file_with_retries; -use crate::Flags; - use crate::args::DenoSubcommand; use crate::args::InstallFlags; - -use deno_lockfile::Lockfile; +use crate::cache; +use crate::sys::CliSys; +use crate::Flags; #[derive(Debug)] pub struct CliLockfileReadFromPathOptions { @@ -35,6 +35,7 @@ pub struct CliLockfileReadFromPathOptions { #[derive(Debug)] pub struct CliLockfile { + sys: CliSys, lockfile: Mutex, pub filename: PathBuf, frozen: bool, @@ -59,6 +60,14 @@ impl<'a, T> std::ops::DerefMut for Guard<'a, T> { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[error("Failed writing lockfile")] +#[class(inherit)] +struct AtomicWriteFileWithRetriesError { + #[source] + source: std::io::Error, +} + impl CliLockfile { /// Get the inner deno_lockfile::Lockfile. pub fn lock(&self) -> Guard { @@ -78,7 +87,7 @@ impl CliLockfile { self.lockfile.lock().overwrite } - pub fn write_if_changed(&self) -> Result<(), AnyError> { + pub fn write_if_changed(&self) -> Result<(), JsErrorBox> { if self.skip_write { return Ok(()); } @@ -91,16 +100,20 @@ impl CliLockfile { // do an atomic write to reduce the chance of multiple deno // processes corrupting the file atomic_write_file_with_retries( + &self.sys, &lockfile.filename, - bytes, + &bytes, cache::CACHE_PERM, ) - .context("Failed writing lockfile.")?; + .map_err(|source| { + JsErrorBox::from_err(AtomicWriteFileWithRetriesError { source }) + })?; lockfile.has_content_changed = false; Ok(()) } pub fn discover( + sys: &CliSys, flags: &Flags, workspace: &Workspace, maybe_external_import_map: Option<&serde_json::Value>, @@ -163,11 +176,14 @@ impl CliLockfile { .unwrap_or(false) }); - let lockfile = Self::read_from_path(CliLockfileReadFromPathOptions { - file_path, - frozen, - skip_write: flags.internal.lockfile_skip_write, - })?; + let lockfile = Self::read_from_path( + sys, + 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(); @@ -223,6 +239,7 @@ impl CliLockfile { } pub fn read_from_path( + sys: &CliSys, opts: CliLockfileReadFromPathOptions, ) -> Result { let lockfile = match std::fs::read_to_string(&opts.file_path) { @@ -241,6 +258,7 @@ impl CliLockfile { } }; Ok(CliLockfile { + sys: sys.clone(), filename: lockfile.filename.clone(), lockfile: Mutex::new(lockfile), frozen: opts.frozen, @@ -248,7 +266,7 @@ impl CliLockfile { }) } - pub fn error_if_changed(&self) -> Result<(), AnyError> { + pub fn error_if_changed(&self) -> Result<(), JsErrorBox> { if !self.frozen { return Ok(()); } @@ -260,9 +278,7 @@ impl CliLockfile { let diff = crate::util::diff::diff(&contents, &new_contents); // has an extra newline at the end let diff = diff.trim_end(); - Err(deno_core::anyhow::anyhow!( - "The lockfile is out of date. Run `deno install --frozen=false`, or rerun with `--frozen=false` to update it.\nchanges:\n{diff}" - )) + Err(JsErrorBox::generic(format!("The lockfile is out of date. Run `deno install --frozen=false`, or rerun with `--frozen=false` to update it.\nchanges:\n{diff}"))) } else { Ok(()) } diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 450aa11652..4ba85f1706 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod deno_json; mod flags; @@ -7,68 +7,6 @@ mod import_map; mod lockfile; mod package_json; -use deno_ast::MediaType; -use deno_ast::SourceMapOption; -use deno_config::deno_json::NodeModulesDirMode; -use deno_config::workspace::CreateResolverOptions; -use deno_config::workspace::FolderConfigs; -use deno_config::workspace::PackageJsonDepResolution; -use deno_config::workspace::VendorEnablement; -use deno_config::workspace::Workspace; -use deno_config::workspace::WorkspaceDirectory; -use deno_config::workspace::WorkspaceDirectoryEmptyOptions; -use deno_config::workspace::WorkspaceDiscoverOptions; -use deno_config::workspace::WorkspaceDiscoverStart; -use deno_config::workspace::WorkspaceLintConfig; -use deno_config::workspace::WorkspaceResolver; -use deno_core::resolve_url_or_path; -use deno_graph::GraphKind; -use deno_npm::npm_rc::NpmRc; -use deno_npm::npm_rc::ResolvedNpmRc; -use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; -use deno_npm::NpmSystemInfo; -use deno_npm_cache::NpmCacheSetting; -use deno_path_util::normalize_path; -use deno_semver::npm::NpmPackageReqReference; -use deno_telemetry::OtelConfig; -use deno_telemetry::OtelRuntimeConfig; -use import_map::resolve_import_map_value_from_specifier; - -pub use deno_config::deno_json::BenchConfig; -pub use deno_config::deno_json::ConfigFile; -pub use deno_config::deno_json::FmtOptionsConfig; -pub use deno_config::deno_json::LintRulesConfig; -pub use deno_config::deno_json::ProseWrap; -pub use deno_config::deno_json::TsConfig; -pub use deno_config::deno_json::TsConfigForEmit; -pub use deno_config::deno_json::TsConfigType; -pub use deno_config::deno_json::TsTypeLib; -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; -pub use package_json::PackageJsonDepValueParseWithLocationError; - -use deno_ast::ModuleSpecifier; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::url::Url; -use deno_runtime::deno_permissions::PermissionsOptions; -use deno_runtime::deno_tls::deno_native_certs::load_native_certs; -use deno_runtime::deno_tls::rustls; -use deno_runtime::deno_tls::rustls::RootCertStore; -use deno_runtime::deno_tls::rustls_pemfile; -use deno_runtime::deno_tls::webpki_roots; -use deno_runtime::inspector_server::InspectorServer; -use deno_terminal::colors; -use dotenvy::from_filename; -use once_cell::sync::Lazy; -use serde::Deserialize; -use serde::Serialize; use std::borrow::Cow; use std::collections::HashMap; use std::env; @@ -81,18 +19,82 @@ use std::num::NonZeroUsize; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; + +use deno_ast::MediaType; +use deno_ast::ModuleSpecifier; +use deno_ast::SourceMapOption; +use deno_cache_dir::file_fetcher::CacheSetting; +pub use deno_config::deno_json::BenchConfig; +pub use deno_config::deno_json::ConfigFile; +use deno_config::deno_json::ConfigFileError; +use deno_config::deno_json::FmtConfig; +pub use deno_config::deno_json::FmtOptionsConfig; +use deno_config::deno_json::LintConfig; +pub use deno_config::deno_json::LintRulesConfig; +use deno_config::deno_json::NodeModulesDirMode; +pub use deno_config::deno_json::ProseWrap; +use deno_config::deno_json::TestConfig; +pub use deno_config::deno_json::TsConfig; +pub use deno_config::deno_json::TsConfigForEmit; +pub use deno_config::deno_json::TsConfigType; +pub use deno_config::deno_json::TsTypeLib; +pub use deno_config::glob::FilePatterns; +use deno_config::workspace::CreateResolverOptions; +use deno_config::workspace::FolderConfigs; +use deno_config::workspace::PackageJsonDepResolution; +use deno_config::workspace::VendorEnablement; +use deno_config::workspace::Workspace; +use deno_config::workspace::WorkspaceDirectory; +use deno_config::workspace::WorkspaceDirectoryEmptyOptions; +use deno_config::workspace::WorkspaceDiscoverOptions; +use deno_config::workspace::WorkspaceDiscoverStart; +use deno_config::workspace::WorkspaceLintConfig; +use deno_config::workspace::WorkspaceResolver; +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::resolve_url_or_path; +use deno_core::serde_json; +use deno_core::url::Url; +use deno_graph::GraphKind; +pub use deno_json::check_warn_tsconfig; +use deno_lint::linter::LintConfig as DenoLintConfig; +use deno_npm::npm_rc::NpmRc; +use deno_npm::npm_rc::ResolvedNpmRc; +use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; +use deno_npm::NpmSystemInfo; +use deno_path_util::normalize_path; +use deno_runtime::deno_permissions::PermissionsOptions; +use deno_runtime::deno_tls::deno_native_certs::load_native_certs; +use deno_runtime::deno_tls::rustls; +use deno_runtime::deno_tls::rustls::RootCertStore; +use deno_runtime::deno_tls::rustls_pemfile; +use deno_runtime::deno_tls::webpki_roots; +use deno_runtime::inspector_server::InspectorServer; +use deno_semver::npm::NpmPackageReqReference; +use deno_semver::StackString; +use deno_telemetry::OtelConfig; +use deno_telemetry::OtelRuntimeConfig; +use deno_terminal::colors; +use dotenvy::from_filename; +pub use flags::*; +use import_map::resolve_import_map_value_from_specifier; +pub use lockfile::CliLockfile; +pub use lockfile::CliLockfileReadFromPathOptions; +use once_cell::sync::Lazy; +pub use package_json::NpmInstallDepsProvider; +pub use package_json::PackageJsonDepValueParseWithLocationError; +use serde::Deserialize; +use serde::Serialize; +use sys_traits::EnvHomeDir; use thiserror::Error; -use crate::cache; use crate::cache::DenoDirProvider; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; +use crate::sys::CliSys; use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::version; -use deno_config::deno_json::FmtConfig; -use deno_config::deno_json::LintConfig; -use deno_config::deno_json::TestConfig; - pub fn npm_registry_url() -> &'static Url { static NPM_REGISTRY_DEFAULT_URL: Lazy = Lazy::new(|| { let env_var_name = "NPM_CONFIG_REGISTRY"; @@ -217,52 +219,6 @@ pub fn ts_config_to_transpile_and_emit_options( )) } -/// Indicates how cached source files should be handled. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum CacheSetting { - /// Only the cached files should be used. Any files not in the cache will - /// error. This is the equivalent of `--cached-only` in the CLI. - Only, - /// No cached source files should be used, and all files should be reloaded. - /// This is the equivalent of `--reload` in the CLI. - ReloadAll, - /// Only some cached resources should be used. This is the equivalent of - /// `--reload=jsr:@std/http/file-server` or - /// `--reload=jsr:@std/http/file-server,jsr:@std/assert/assert-equals`. - ReloadSome(Vec), - /// The usability of a cached value is determined by analyzing the cached - /// headers and other metadata associated with a cached response, reloading - /// any cached "non-fresh" cached responses. - RespectHeaders, - /// The cached source files should be used for local modules. This is the - /// default behavior of the CLI. - Use, -} - -impl CacheSetting { - pub fn as_npm_cache_setting(&self) -> NpmCacheSetting { - match self { - CacheSetting::Only => NpmCacheSetting::Only, - CacheSetting::ReloadAll => NpmCacheSetting::ReloadAll, - CacheSetting::ReloadSome(values) => { - if values.iter().any(|v| v == "npm:") { - NpmCacheSetting::ReloadAll - } else { - NpmCacheSetting::ReloadSome { - npm_package_names: values - .iter() - .filter_map(|v| v.strip_prefix("npm:")) - .map(|n| n.to_string()) - .collect(), - } - } - } - CacheSetting::RespectHeaders => unreachable!(), // not supported - CacheSetting::Use => NpmCacheSetting::Use, - } - } -} - pub struct WorkspaceBenchOptions { pub filter: Option, pub json: bool, @@ -616,7 +572,7 @@ fn discover_npmrc( // TODO(bartlomieju): update to read both files - one in the project root and one and // home dir and then merge them. // 3. Try `.npmrc` in the user's home directory - if let Some(home_dir) = cache::home_dir() { + if let Some(home_dir) = crate::sys::CliSys::default().env_home_dir() { match try_to_read_npmrc(&home_dir) { Ok(Some((source, path))) => { return try_to_parse_npmrc(source, &path).map(|r| (r, Some(path))); @@ -649,7 +605,8 @@ pub fn create_default_npmrc() -> Arc { }) } -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, deno_error::JsError)] +#[class(generic)] pub enum RootCertStoreLoadError { #[error( "Unknown certificate store \"{0}\" specified (allowed: \"system,mozilla\")" @@ -815,7 +772,9 @@ pub struct CliOptions { } impl CliOptions { + #[allow(clippy::too_many_arguments)] pub fn new( + sys: &CliSys, flags: Arc, initial_cwd: PathBuf, maybe_lockfile: Option>, @@ -840,8 +799,10 @@ impl CliOptions { } let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache); - let deno_dir_provider = - Arc::new(DenoDirProvider::new(flags.internal.cache_path.clone())); + let deno_dir_provider = Arc::new(DenoDirProvider::new( + sys.clone(), + flags.internal.cache_path.clone(), + )); let maybe_node_modules_folder = resolve_node_modules_folder( &initial_cwd, &flags, @@ -866,7 +827,7 @@ impl CliOptions { }) } - pub fn from_flags(flags: Arc) -> Result { + pub fn from_flags(sys: &CliSys, flags: Arc) -> Result { let initial_cwd = std::env::current_dir().with_context(|| "Failed getting cwd.")?; let maybe_vendor_override = flags.vendor.map(|v| match v { @@ -880,8 +841,6 @@ impl CliOptions { } else { &[] }; - let config_parse_options = - deno_config::deno_json::ConfigParseOptions::default(); let discover_pkg_json = flags.config_flag != ConfigFlag::Disabled && !flags.no_npm && !has_flag_env_var("DENO_NO_PACKAGE_JSON"); @@ -889,11 +848,9 @@ impl CliOptions { log::debug!("package.json auto-discovery is disabled"); } WorkspaceDiscoverOptions { - fs: Default::default(), // use real fs deno_json_cache: None, pkg_json_cache: Some(&node_resolver::PackageJsonThreadLocalCache), workspace_cache: None, - config_parse_options, additional_config_file_names, discover_pkg_json, maybe_vendor_override, @@ -911,6 +868,7 @@ impl CliOptions { ConfigFlag::Discover => { if let Some(start_paths) = flags.config_path_args(&initial_cwd) { WorkspaceDirectory::discover( + sys, WorkspaceDiscoverStart::Paths(&start_paths), &resolve_workspace_discover_options(), )? @@ -921,6 +879,7 @@ impl CliOptions { ConfigFlag::Path(path) => { let config_path = normalize_path(initial_cwd.join(path)); WorkspaceDirectory::discover( + sys, WorkspaceDiscoverStart::ConfigFile(&config_path), &resolve_workspace_discover_options(), )? @@ -959,6 +918,7 @@ impl CliOptions { }; let maybe_lock_file = CliLockfile::discover( + sys, &flags, &start_dir.workspace, external_import_map.as_ref().map(|(_, v)| v), @@ -967,6 +927,7 @@ impl CliOptions { log::debug!("Finished config loading."); Self::new( + sys, flags, initial_cwd, maybe_lock_file.map(Arc::new), @@ -1037,24 +998,24 @@ impl CliOptions { // https://nodejs.org/api/process.html match target.as_str() { "aarch64-apple-darwin" => NpmSystemInfo { - os: "darwin".to_string(), - cpu: "arm64".to_string(), + os: "darwin".into(), + cpu: "arm64".into(), }, "aarch64-unknown-linux-gnu" => NpmSystemInfo { - os: "linux".to_string(), - cpu: "arm64".to_string(), + os: "linux".into(), + cpu: "arm64".into(), }, "x86_64-apple-darwin" => NpmSystemInfo { - os: "darwin".to_string(), - cpu: "x64".to_string(), + os: "darwin".into(), + cpu: "x64".into(), }, "x86_64-unknown-linux-gnu" => NpmSystemInfo { - os: "linux".to_string(), - cpu: "x64".to_string(), + os: "linux".into(), + cpu: "x64".into(), }, "x86_64-pc-windows-msvc" => NpmSystemInfo { - os: "win32".to_string(), - cpu: "x64".to_string(), + os: "win32".into(), + cpu: "x64".into(), }, value => { log::warn!( @@ -1091,7 +1052,7 @@ impl CliOptions { pub async fn create_workspace_resolver( &self, - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, pkg_json_dep_resolution: PackageJsonDepResolution, ) -> Result { let overrode_no_import_map: bool = self @@ -1138,11 +1099,11 @@ impl CliOptions { } }; Ok(self.workspace().create_resolver( + &CliSys::default(), CreateResolverOptions { pkg_json_dep_resolution, specified_import_map: cli_arg_specified_import_map, }, - |path| Ok(std::fs::read_to_string(path)?), )?) } @@ -1284,11 +1245,14 @@ impl CliOptions { pub fn node_modules_dir( &self, - ) -> Result, AnyError> { + ) -> Result< + Option, + deno_config::deno_json::NodeModulesDirParseError, + > { if let Some(flag) = self.flags.node_modules_dir { return Ok(Some(flag)); } - self.workspace().node_modules_dir().map_err(Into::into) + self.workspace().node_modules_dir() } pub fn vendor_dir_path(&self) -> Option<&PathBuf> { @@ -1298,7 +1262,7 @@ impl CliOptions { pub fn resolve_ts_config_for_emit( &self, config_type: TsConfigType, - ) -> Result { + ) -> Result { self.workspace().resolve_ts_config_for_emit(config_type) } @@ -1327,7 +1291,7 @@ impl CliOptions { pub fn to_compiler_option_types( &self, - ) -> Result, AnyError> { + ) -> Result, serde_json::Error> { self .workspace() .to_compiler_option_types() @@ -1397,9 +1361,7 @@ impl CliOptions { Ok(result) } - pub fn resolve_deno_lint_config( - &self, - ) -> Result { + pub fn resolve_deno_lint_config(&self) -> Result { let ts_config_result = self.resolve_ts_config_for_emit(TsConfigType::Emit)?; @@ -1408,11 +1370,11 @@ impl CliOptions { ts_config_result.ts_config, )?; - Ok(deno_lint::linter::LintConfig { + Ok(DenoLintConfig { default_jsx_factory: (!transpile_options.jsx_automatic) - .then(|| transpile_options.jsx_factory.clone()), + .then_some(transpile_options.jsx_factory), default_jsx_fragment_factory: (!transpile_options.jsx_automatic) - .then(|| transpile_options.jsx_fragment_factory.clone()), + .then_some(transpile_options.jsx_fragment_factory), }) } @@ -1566,20 +1528,100 @@ impl CliOptions { self.flags.no_npm } - pub fn permission_flags(&self) -> &PermissionFlags { - &self.flags.permissions - } - pub fn permissions_options(&self) -> PermissionsOptions { - fn files_to_urls(files: &[String]) -> Vec> { - files - .iter() - .filter_map(|f| Url::parse(f).ok().map(Cow::Owned)) - .collect() + // bury this in here to ensure people use cli_options.permissions_options() + fn flags_to_options(flags: &PermissionFlags) -> PermissionsOptions { + fn handle_allow( + allow_all: bool, + value: Option, + ) -> Option { + if allow_all { + assert!(value.is_none()); + Some(T::default()) + } else { + value + } + } + + PermissionsOptions { + allow_all: flags.allow_all, + allow_env: handle_allow(flags.allow_all, flags.allow_env.clone()), + deny_env: flags.deny_env.clone(), + allow_net: handle_allow(flags.allow_all, flags.allow_net.clone()), + deny_net: flags.deny_net.clone(), + allow_ffi: handle_allow(flags.allow_all, flags.allow_ffi.clone()), + deny_ffi: flags.deny_ffi.clone(), + allow_read: handle_allow(flags.allow_all, flags.allow_read.clone()), + deny_read: flags.deny_read.clone(), + allow_run: handle_allow(flags.allow_all, flags.allow_run.clone()), + deny_run: flags.deny_run.clone(), + allow_sys: handle_allow(flags.allow_all, flags.allow_sys.clone()), + deny_sys: flags.deny_sys.clone(), + allow_write: handle_allow(flags.allow_all, flags.allow_write.clone()), + deny_write: flags.deny_write.clone(), + allow_import: handle_allow(flags.allow_all, flags.allow_import.clone()), + prompt: !resolve_no_prompt(flags), + } } - // get a list of urls to imply for --allow-import - let cli_arg_urls = self + let mut permissions_options = flags_to_options(&self.flags.permissions); + self.augment_import_permissions(&mut permissions_options); + permissions_options + } + + fn augment_import_permissions(&self, options: &mut PermissionsOptions) { + // do not add if the user specified --allow-all or --allow-import + if !options.allow_all && options.allow_import.is_none() { + options.allow_import = Some(self.implicit_allow_import()); + } + } + + fn implicit_allow_import(&self) -> Vec { + // allow importing from anywhere when using cached only + if self.cache_setting() == CacheSetting::Only { + vec![] // allow all imports + } else { + // implicitly allow some trusted hosts and the CLI arg urls + let cli_arg_urls = self.get_cli_arg_urls(); + let builtin_allowed_import_hosts = [ + "jsr.io:443", + "deno.land:443", + "esm.sh:443", + "cdn.jsdelivr.net:443", + "raw.githubusercontent.com:443", + "gist.githubusercontent.com:443", + ]; + let mut imports = Vec::with_capacity( + builtin_allowed_import_hosts.len() + cli_arg_urls.len() + 1, + ); + imports + .extend(builtin_allowed_import_hosts.iter().map(|s| s.to_string())); + // also add the JSR_URL env var + if let Some(jsr_host) = allow_import_host_from_url(jsr_url()) { + if jsr_host != "jsr.io:443" { + imports.push(jsr_host); + } + } + // include the cli arg urls + for url in cli_arg_urls { + if let Some(host) = allow_import_host_from_url(&url) { + imports.push(host); + } + } + imports + } + } + + fn get_cli_arg_urls(&self) -> Vec> { + fn files_to_urls(files: &[String]) -> Vec> { + files.iter().filter_map(|f| file_to_url(f)).collect() + } + + fn file_to_url(file: &str) -> Option> { + Url::parse(file).ok().map(Cow::Owned) + } + + self .resolve_main_module() .ok() .map(|url| vec![Cow::Borrowed(url)]) @@ -1591,18 +1633,18 @@ impl CliOptions { Some(files_to_urls(&check_flags.files)) } DenoSubcommand::Install(InstallFlags::Global(flags)) => { - Url::parse(&flags.module_url) - .ok() - .map(|url| vec![Cow::Owned(url)]) + file_to_url(&flags.module_url).map(|url| vec![url]) } DenoSubcommand::Doc(DocFlags { source_files: DocSourceFileFlag::Paths(paths), .. }) => Some(files_to_urls(paths)), + DenoSubcommand::Info(InfoFlags { + file: Some(file), .. + }) => file_to_url(file).map(|url| vec![url]), _ => None, }) - .unwrap_or_default(); - self.flags.permissions.to_options(&cli_arg_urls) + .unwrap_or_default() } pub fn reload_flag(&self) -> bool { @@ -1993,15 +2035,17 @@ pub fn has_flag_env_var(name: &str) -> bool { pub fn npm_pkg_req_ref_to_binary_command( req_ref: &NpmPackageReqReference, ) -> String { - let binary_name = req_ref.sub_path().unwrap_or(req_ref.req().name.as_str()); - binary_name.to_string() + req_ref + .sub_path() + .map(|s| s.to_string()) + .unwrap_or_else(|| req_ref.req().name.to_string()) } pub fn config_to_deno_graph_workspace_member( config: &ConfigFile, ) -> Result { - let name = match &config.json.name { - Some(name) => name.clone(), + let name: StackString = match &config.json.name { + Some(name) => name.as_str().into(), None => bail!("Missing 'name' field in config file."), }; let version = match &config.json.version { @@ -2036,6 +2080,20 @@ fn load_env_variables_from_env_file(filename: Option<&Vec>) { } } +/// Gets the --allow-import host from the provided url +fn allow_import_host_from_url(url: &Url) -> Option { + let host = url.host()?; + if let Some(port) = url.port() { + Some(format!("{}:{}", host, port)) + } else { + match url.scheme() { + "https" => Some(format!("{}:443", host)), + "http" => Some(format!("{}:80", host)), + _ => None, + } + } +} + #[derive(Debug, Clone, Copy)] pub enum NpmCachingStrategy { Eager, @@ -2043,7 +2101,7 @@ pub enum NpmCachingStrategy { Manual, } -pub(crate) fn otel_runtime_config() -> OtelRuntimeConfig { +pub fn otel_runtime_config() -> OtelRuntimeConfig { OtelRuntimeConfig { runtime_name: Cow::Borrowed("deno"), runtime_version: Cow::Borrowed(crate::version::DENO_VERSION_INFO.deno), @@ -2064,12 +2122,7 @@ mod test { let cwd = &std::env::current_dir().unwrap(); let config_specifier = ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = ConfigFile::new( - config_text, - config_specifier, - &deno_config::deno_json::ConfigParseOptions::default(), - ) - .unwrap(); + let config_file = ConfigFile::new(config_text, config_specifier).unwrap(); let actual = resolve_import_map_specifier( Some("import-map.json"), Some(&config_file), @@ -2088,12 +2141,7 @@ mod test { let config_text = r#"{}"#; let config_specifier = ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = ConfigFile::new( - config_text, - config_specifier, - &deno_config::deno_json::ConfigParseOptions::default(), - ) - .unwrap(); + let config_file = ConfigFile::new(config_text, config_specifier).unwrap(); let actual = resolve_import_map_specifier( None, Some(&config_file), @@ -2140,4 +2188,26 @@ mod test { let reg_api_url = jsr_api_url(); assert!(reg_api_url.as_str().ends_with('/')); } + + #[test] + fn test_allow_import_host_from_url() { + fn parse(text: &str) -> Option { + allow_import_host_from_url(&Url::parse(text).unwrap()) + } + + assert_eq!( + parse("http://127.0.0.1:4250"), + Some("127.0.0.1:4250".to_string()) + ); + assert_eq!(parse("http://jsr.io"), Some("jsr.io:80".to_string())); + assert_eq!( + parse("https://example.com"), + Some("example.com:443".to_string()) + ); + assert_eq!( + parse("http://example.com"), + Some("example.com:80".to_string()) + ); + assert_eq!(parse("file:///example.com"), None); + } } diff --git a/cli/args/package_json.rs b/cli/args/package_json.rs index b0f0a2f9ba..efa5d46966 100644 --- a/cli/args/package_json.rs +++ b/cli/args/package_json.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::PathBuf; use std::sync::Arc; @@ -11,19 +11,20 @@ use deno_package_json::PackageJsonDepValueParseError; use deno_package_json::PackageJsonDepWorkspaceReq; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::VersionReq; use thiserror::Error; #[derive(Debug)] pub struct InstallNpmRemotePkg { - pub alias: Option, + pub alias: Option, pub base_dir: PathBuf, pub req: PackageReq, } #[derive(Debug)] pub struct InstallNpmWorkspacePkg { - pub alias: Option, + pub alias: Option, pub target_dir: PathBuf, } @@ -31,7 +32,7 @@ pub struct InstallNpmWorkspacePkg { #[error("Failed to install '{}'\n at {}", alias, location)] pub struct PackageJsonDepValueParseWithLocationError { pub location: Url, - pub alias: String, + pub alias: StackString, #[source] pub source: PackageJsonDepValueParseError, } @@ -100,10 +101,8 @@ impl NpmInstallDepsProvider { let mut pkg_pkgs = Vec::with_capacity( deps.dependencies.len() + deps.dev_dependencies.len(), ); - for (alias, dep) in deps - .dependencies - .into_iter() - .chain(deps.dev_dependencies.into_iter()) + for (alias, dep) in + deps.dependencies.iter().chain(deps.dev_dependencies.iter()) { let dep = match dep { Ok(dep) => dep, @@ -111,8 +110,8 @@ impl NpmInstallDepsProvider { pkg_json_dep_errors.push( PackageJsonDepValueParseWithLocationError { location: pkg_json.specifier(), - alias, - source: err, + alias: alias.clone(), + source: err.clone(), }, ); continue; @@ -121,28 +120,28 @@ impl NpmInstallDepsProvider { match dep { PackageJsonDepValue::Req(pkg_req) => { let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| { - pkg.matches_req(&pkg_req) + pkg.matches_req(pkg_req) // do not resolve to the current package && pkg.pkg_json.path != pkg_json.path }); if let Some(pkg) = workspace_pkg { workspace_pkgs.push(InstallNpmWorkspacePkg { - alias: Some(alias), + alias: Some(alias.clone()), target_dir: pkg.pkg_json.dir_path().to_path_buf(), }); } else { pkg_pkgs.push(InstallNpmRemotePkg { - alias: Some(alias), + alias: Some(alias.clone()), base_dir: pkg_json.dir_path().to_path_buf(), - req: pkg_req, + req: pkg_req.clone(), }); } } PackageJsonDepValue::Workspace(workspace_version_req) => { let version_req = match workspace_version_req { PackageJsonDepWorkspaceReq::VersionReq(version_req) => { - version_req + version_req.clone() } PackageJsonDepWorkspaceReq::Tilde | PackageJsonDepWorkspaceReq::Caret => { @@ -150,10 +149,10 @@ impl NpmInstallDepsProvider { } }; if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| { - pkg.matches_name_and_version_req(&alias, &version_req) + pkg.matches_name_and_version_req(alias, &version_req) }) { workspace_pkgs.push(InstallNpmWorkspacePkg { - alias: Some(alias), + alias: Some(alias.clone()), target_dir: pkg.pkg_json.dir_path().to_path_buf(), }); } diff --git a/cli/auth_tokens.rs b/cli/auth_tokens.rs deleted file mode 100644 index ef9f9d0746..0000000000 --- a/cli/auth_tokens.rs +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use base64::prelude::BASE64_STANDARD; -use base64::Engine; -use deno_core::ModuleSpecifier; -use log::debug; -use log::error; -use std::borrow::Cow; -use std::fmt; -use std::net::IpAddr; -use std::net::Ipv4Addr; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::str::FromStr; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum AuthTokenData { - Bearer(String), - Basic { username: String, password: String }, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AuthToken { - host: AuthDomain, - token: AuthTokenData, -} - -impl fmt::Display for AuthToken { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match &self.token { - AuthTokenData::Bearer(token) => write!(f, "Bearer {token}"), - AuthTokenData::Basic { username, password } => { - let credentials = format!("{username}:{password}"); - write!(f, "Basic {}", BASE64_STANDARD.encode(credentials)) - } - } - } -} - -/// A structure which contains bearer tokens that can be used when sending -/// requests to websites, intended to authorize access to private resources -/// such as remote modules. -#[derive(Debug, Clone)] -pub struct AuthTokens(Vec); - -/// An authorization domain, either an exact or suffix match. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum AuthDomain { - Ip(IpAddr), - IpPort(SocketAddr), - /// Suffix match, no dot. May include a port. - Suffix(Cow<'static, str>), -} - -impl From for AuthDomain { - fn from(value: T) -> Self { - let s = value.to_string().to_lowercase(); - if let Ok(ip) = SocketAddr::from_str(&s) { - return AuthDomain::IpPort(ip); - }; - if s.starts_with('[') && s.ends_with(']') { - if let Ok(ip) = Ipv6Addr::from_str(&s[1..s.len() - 1]) { - return AuthDomain::Ip(ip.into()); - } - } else if let Ok(ip) = Ipv4Addr::from_str(&s) { - return AuthDomain::Ip(ip.into()); - } - if let Some(s) = s.strip_prefix('.') { - AuthDomain::Suffix(Cow::Owned(s.to_owned())) - } else { - AuthDomain::Suffix(Cow::Owned(s)) - } - } -} - -impl AuthDomain { - pub fn matches(&self, specifier: &ModuleSpecifier) -> bool { - let Some(host) = specifier.host_str() else { - return false; - }; - match *self { - Self::Ip(ip) => { - let AuthDomain::Ip(parsed) = AuthDomain::from(host) else { - return false; - }; - ip == parsed && specifier.port().is_none() - } - Self::IpPort(ip) => { - let AuthDomain::Ip(parsed) = AuthDomain::from(host) else { - return false; - }; - ip.ip() == parsed && specifier.port() == Some(ip.port()) - } - Self::Suffix(ref suffix) => { - let hostname = if let Some(port) = specifier.port() { - Cow::Owned(format!("{}:{}", host, port)) - } else { - Cow::Borrowed(host) - }; - - if suffix.len() == hostname.len() { - return suffix == &hostname; - } - - // If it's a suffix match, ensure a dot - if hostname.ends_with(suffix.as_ref()) - && hostname.ends_with(&format!(".{suffix}")) - { - return true; - } - - false - } - } - } -} - -impl AuthTokens { - /// Create a new set of tokens based on the provided string. It is intended - /// that the string be the value of an environment variable and the string is - /// parsed for token values. The string is expected to be a semi-colon - /// separated string, where each value is `{token}@{hostname}`. - pub fn new(maybe_tokens_str: Option) -> Self { - let mut tokens = Vec::new(); - if let Some(tokens_str) = maybe_tokens_str { - for token_str in tokens_str.trim().split(';') { - if token_str.contains('@') { - let mut iter = token_str.rsplitn(2, '@'); - let host = AuthDomain::from(iter.next().unwrap()); - let token = iter.next().unwrap(); - if token.contains(':') { - let mut iter = token.rsplitn(2, ':'); - let password = iter.next().unwrap().to_owned(); - let username = iter.next().unwrap().to_owned(); - tokens.push(AuthToken { - host, - token: AuthTokenData::Basic { username, password }, - }); - } else { - tokens.push(AuthToken { - host, - token: AuthTokenData::Bearer(token.to_string()), - }); - } - } else { - error!("Badly formed auth token discarded."); - } - } - debug!("Parsed {} auth token(s).", tokens.len()); - } - - Self(tokens) - } - - /// Attempt to match the provided specifier to the tokens in the set. The - /// matching occurs from the right of the hostname plus port, irrespective of - /// scheme. For example `https://www.deno.land:8080/` would match a token - /// with a host value of `deno.land:8080` but not match `www.deno.land`. The - /// matching is case insensitive. - pub fn get(&self, specifier: &ModuleSpecifier) -> Option { - self.0.iter().find_map(|t| { - if t.host.matches(specifier) { - Some(t.clone()) - } else { - None - } - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use deno_core::resolve_url; - - #[test] - fn test_auth_token() { - let auth_tokens = AuthTokens::new(Some("abc123@deno.land".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123" - ); - let fixture = resolve_url("https://www.deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - let fixture = resolve_url("http://127.0.0.1:8080/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = - resolve_url("https://deno.land.example.com/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = resolve_url("https://deno.land:8080/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - } - - #[test] - fn test_auth_tokens_multiple() { - let auth_tokens = - AuthTokens::new(Some("abc123@deno.land;def456@example.com".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - let fixture = resolve_url("http://example.com/a/file.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer def456".to_string() - ); - } - - #[test] - fn test_auth_tokens_space() { - let auth_tokens = AuthTokens::new(Some( - " abc123@deno.land;def456@example.com\t".to_string(), - )); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - let fixture = resolve_url("http://example.com/a/file.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer def456".to_string() - ); - } - - #[test] - fn test_auth_tokens_newline() { - let auth_tokens = AuthTokens::new(Some( - "\nabc123@deno.land;def456@example.com\n".to_string(), - )); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - let fixture = resolve_url("http://example.com/a/file.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer def456".to_string() - ); - } - - #[test] - fn test_auth_tokens_port() { - let auth_tokens = - AuthTokens::new(Some("abc123@deno.land:8080".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = resolve_url("http://deno.land:8080/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - } - - #[test] - fn test_auth_tokens_contain_at() { - let auth_tokens = AuthTokens::new(Some("abc@123@deno.land".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc@123".to_string() - ); - } - - #[test] - fn test_auth_token_basic() { - let auth_tokens = AuthTokens::new(Some("abc:123@deno.land".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Basic YWJjOjEyMw==" - ); - let fixture = resolve_url("https://www.deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Basic YWJjOjEyMw==".to_string() - ); - let fixture = resolve_url("http://127.0.0.1:8080/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = - resolve_url("https://deno.land.example.com/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = resolve_url("https://deno.land:8080/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - } - - #[test] - fn test_parse_ip() { - let ip = AuthDomain::from("[2001:db8:a::123]"); - assert_eq!("Ip(2001:db8:a::123)", format!("{ip:?}")); - let ip = AuthDomain::from("[2001:db8:a::123]:8080"); - assert_eq!("IpPort([2001:db8:a::123]:8080)", format!("{ip:?}")); - let ip = AuthDomain::from("1.1.1.1"); - assert_eq!("Ip(1.1.1.1)", format!("{ip:?}")); - } - - #[test] - fn test_case_insensitive() { - let domain = AuthDomain::from("EXAMPLE.com"); - assert!( - domain.matches(&ModuleSpecifier::parse("http://example.com").unwrap()) - ); - assert!( - domain.matches(&ModuleSpecifier::parse("http://example.COM").unwrap()) - ); - } - - #[test] - fn test_matches() { - let candidates = [ - "example.com", - "www.example.com", - "1.1.1.1", - "[2001:db8:a::123]", - // These will never match - "example.com.evil.com", - "1.1.1.1.evil.com", - "notexample.com", - "www.notexample.com", - ]; - let domains = [ - ("example.com", vec!["example.com", "www.example.com"]), - (".example.com", vec!["example.com", "www.example.com"]), - ("www.example.com", vec!["www.example.com"]), - ("1.1.1.1", vec!["1.1.1.1"]), - ("[2001:db8:a::123]", vec!["[2001:db8:a::123]"]), - ]; - let url = |c: &str| ModuleSpecifier::parse(&format!("http://{c}")).unwrap(); - let url_port = - |c: &str| ModuleSpecifier::parse(&format!("http://{c}:8080")).unwrap(); - - // Generate each candidate with and without a port - let candidates = candidates - .into_iter() - .flat_map(|c| [url(c), url_port(c)]) - .collect::>(); - - for (domain, expected_domain) in domains { - // Test without a port -- all candidates return without a port - let auth_domain = AuthDomain::from(domain); - let actual = candidates - .iter() - .filter(|c| auth_domain.matches(c)) - .cloned() - .collect::>(); - let expected = expected_domain.iter().map(|u| url(u)).collect::>(); - assert_eq!(actual, expected); - - // Test with a port, all candidates return with a port - let auth_domain = AuthDomain::from(&format!("{domain}:8080")); - let actual = candidates - .iter() - .filter(|c| auth_domain.matches(c)) - .cloned() - .collect::>(); - let expected = expected_domain - .iter() - .map(|u| url_port(u)) - .collect::>(); - assert_eq!(actual, expected); - } - } -} diff --git a/cli/bench/cache_api.js b/cli/bench/cache_api.js index af55fc132e..4b092ab627 100644 --- a/cli/bench/cache_api.js +++ b/cli/bench/cache_api.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const cacheName = "cache-v1"; const cache = await caches.open(cacheName); diff --git a/cli/bench/command.js b/cli/bench/command.js index 5b7c300d26..5916dcfee2 100644 --- a/cli/bench/command.js +++ b/cli/bench/command.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. Deno.bench("echo deno", async () => { await new Deno.Command("echo", { args: ["deno"] }).output(); diff --git a/cli/bench/console.js b/cli/bench/console.js index 1d336fbbde..c1704549c6 100644 --- a/cli/bench/console.js +++ b/cli/bench/console.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/cli/bench/deno_common.js b/cli/bench/deno_common.js index 3693333915..011a1244aa 100644 --- a/cli/bench/deno_common.js +++ b/cli/bench/deno_common.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // v8 builtin that's close to the upper bound non-NOPs Deno.bench("date_now", { n: 5e5 }, () => { diff --git a/cli/bench/encode_into.js b/cli/bench/encode_into.js index ab5e11b04d..57313ca04a 100644 --- a/cli/bench/encode_into.js +++ b/cli/bench/encode_into.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals let [total, count] = typeof Deno !== "undefined" diff --git a/cli/bench/fs/run.mjs b/cli/bench/fs/run.mjs index 94240f20d4..7f080daf6a 100644 --- a/cli/bench/fs/run.mjs +++ b/cli/bench/fs/run.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. let total = 5; let current = ""; diff --git a/cli/bench/fs/serve.jsx b/cli/bench/fs/serve.jsx index 51125235fe..8b3328617a 100644 --- a/cli/bench/fs/serve.jsx +++ b/cli/bench/fs/serve.jsx @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** @jsx h */ import results from "./deno.json" assert { type: "json" }; diff --git a/cli/bench/getrandom.js b/cli/bench/getrandom.js index fe99bbcbdf..775d02fc75 100644 --- a/cli/bench/getrandom.js +++ b/cli/bench/getrandom.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals let [total, count] = typeof Deno !== "undefined" diff --git a/cli/bench/lsp.rs b/cli/bench/lsp.rs index 7baaffca7e..7a93dcae1e 100644 --- a/cli/bench/lsp.rs +++ b/cli/bench/lsp.rs @@ -1,14 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashMap; +use std::path::Path; +use std::str::FromStr; +use std::time::Duration; use deno_core::serde::Deserialize; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use lsp_types::Uri; -use std::collections::HashMap; -use std::path::Path; -use std::str::FromStr; -use std::time::Duration; use test_util::lsp::LspClientBuilder; use test_util::PathRef; use tower_lsp::lsp_types as lsp; diff --git a/cli/bench/lsp_bench_standalone.rs b/cli/bench/lsp_bench_standalone.rs index 3c946cfbe3..45d8788256 100644 --- a/cli/bench/lsp_bench_standalone.rs +++ b/cli/bench/lsp_bench_standalone.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::benchmark_main; diff --git a/cli/bench/main.rs b/cli/bench/main.rs index c3c42d2488..e7f71f8cfa 100644 --- a/cli/bench/main.rs +++ b/cli/bench/main.rs @@ -1,11 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::serde_json::Value; use std::collections::HashMap; use std::convert::From; use std::env; @@ -15,6 +12,10 @@ use std::path::PathBuf; use std::process::Command; use std::process::Stdio; use std::time::SystemTime; + +use deno_core::error::AnyError; +use deno_core::serde_json; +use deno_core::serde_json::Value; use test_util::PathRef; mod lsp; diff --git a/cli/bench/napi/bench.js b/cli/bench/napi/bench.js index c12c7aacbc..f40611e7a8 100644 --- a/cli/bench/napi/bench.js +++ b/cli/bench/napi/bench.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { loadTestLibrary } from "../../../tests/napi/common.js"; diff --git a/cli/bench/napi/bench_node.mjs b/cli/bench/napi/bench_node.mjs index a772eeafa1..557c4daefd 100644 --- a/cli/bench/napi/bench_node.mjs +++ b/cli/bench/napi/bench_node.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { bench, run } from "mitata"; import { createRequire } from "module"; diff --git a/cli/bench/op_now.js b/cli/bench/op_now.js index 7c1427c809..26c4958fe0 100644 --- a/cli/bench/op_now.js +++ b/cli/bench/op_now.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; diff --git a/cli/bench/secure_curves.js b/cli/bench/secure_curves.js index 912b75cccd..a3fb4ebc41 100644 --- a/cli/bench/secure_curves.js +++ b/cli/bench/secure_curves.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals let [total, count] = typeof Deno !== "undefined" diff --git a/cli/bench/stdio/stdio.c b/cli/bench/stdio/stdio.c index acce207995..15df422405 100644 --- a/cli/bench/stdio/stdio.c +++ b/cli/bench/stdio/stdio.c @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // From https://github.com/just-js/benchmarks/tree/main/01-stdio #include diff --git a/cli/bench/stdio/stdio.js b/cli/bench/stdio/stdio.js index 81bea835a6..1ca947809a 100644 --- a/cli/bench/stdio/stdio.js +++ b/cli/bench/stdio/stdio.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // From https://github.com/just-js/benchmarks/tree/main/01-stdio diff --git a/cli/bench/tcp.js b/cli/bench/tcp.js index b9f05e3a7e..6681eeeb52 100644 --- a/cli/bench/tcp.js +++ b/cli/bench/tcp.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const listener = Deno.listen({ port: 4500 }); const response = new TextEncoder().encode( diff --git a/cli/bench/tty.js b/cli/bench/tty.js index e494e76af7..c61541ffa6 100644 --- a/cli/bench/tty.js +++ b/cli/bench/tty.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; diff --git a/cli/bench/url_parse.js b/cli/bench/url_parse.js index 9cb0045f64..80579d6f6b 100644 --- a/cli/bench/url_parse.js +++ b/cli/bench/url_parse.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; diff --git a/cli/bench/webstorage.js b/cli/bench/webstorage.js index d19f024c63..d284378d60 100644 --- a/cli/bench/webstorage.js +++ b/cli/bench/webstorage.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/cli/bench/write_file.js b/cli/bench/write_file.js index 747503ce2a..c7200a6f5b 100644 --- a/cli/bench/write_file.js +++ b/cli/bench/write_file.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; diff --git a/cli/build.rs b/cli/build.rs index 3d98661284..590fee795d 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::env; use std::path::PathBuf; @@ -8,17 +8,18 @@ use deno_runtime::*; mod shared; mod ts { - use super::*; - use deno_core::error::custom_error; - use deno_core::error::AnyError; - use deno_core::op2; - use deno_core::OpState; - use serde::Serialize; use std::collections::HashMap; use std::io::Write; use std::path::Path; use std::path::PathBuf; + use deno_core::op2; + use deno_core::OpState; + use deno_error::JsErrorBox; + use serde::Serialize; + + use super::*; + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct BuildInfoResponse { @@ -51,7 +52,7 @@ mod ts { fn op_script_version( _state: &mut OpState, #[string] _arg: &str, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { Ok(Some("1".to_string())) } @@ -70,7 +71,7 @@ mod ts { fn op_load( state: &mut OpState, #[string] load_specifier: &str, - ) -> Result { + ) -> Result { let op_crate_libs = state.borrow::>(); let path_dts = state.borrow::(); let re_asset = lazy_regex::regex!(r"asset:/{3}lib\.(\S+)\.d\.ts"); @@ -91,12 +92,15 @@ mod ts { // if it comes from an op crate, we were supplied with the path to the // file. let path = if let Some(op_crate_lib) = op_crate_libs.get(lib) { - PathBuf::from(op_crate_lib).canonicalize()? + PathBuf::from(op_crate_lib) + .canonicalize() + .map_err(JsErrorBox::from_err)? // otherwise we will generate the path ourself } else { path_dts.join(format!("lib.{lib}.d.ts")) }; - let data = std::fs::read_to_string(path)?; + let data = + std::fs::read_to_string(path).map_err(JsErrorBox::from_err)?; Ok(LoadResponse { data, version: "1".to_string(), @@ -104,13 +108,13 @@ mod ts { script_kind: 3, }) } else { - Err(custom_error( + Err(JsErrorBox::new( "InvalidSpecifier", format!("An invalid specifier was requested: {}", load_specifier), )) } } else { - Err(custom_error( + Err(JsErrorBox::new( "InvalidSpecifier", format!("An invalid specifier was requested: {}", load_specifier), )) diff --git a/cli/cache/cache_db.rs b/cli/cache/cache_db.rs index 329ed2d970..7fd66e9333 100644 --- a/cli/cache/cache_db.rs +++ b/cli/cache/cache_db.rs @@ -1,4 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::io::IsTerminal; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; @@ -9,10 +14,6 @@ use deno_runtime::deno_webstorage::rusqlite::Connection; use deno_runtime::deno_webstorage::rusqlite::OptionalExtension; use deno_runtime::deno_webstorage::rusqlite::Params; use once_cell::sync::OnceCell; -use std::io::IsTerminal; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use super::FastInsecureHasher; @@ -24,12 +25,12 @@ impl CacheDBHash { Self(hash) } - pub fn from_source(source: impl std::hash::Hash) -> Self { + pub fn from_hashable(hashable: impl std::hash::Hash) -> Self { Self::new( // always write in the deno version just in case // the clearing on deno version change doesn't work FastInsecureHasher::new_deno_versioned() - .write_hashable(source) + .write_hashable(hashable) .finish(), ) } diff --git a/cli/cache/caches.rs b/cli/cache/caches.rs index 54371cee48..b83364c61b 100644 --- a/cli/cache/caches.rs +++ b/cli/cache/caches.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::PathBuf; use std::sync::Arc; diff --git a/cli/cache/check.rs b/cli/cache/check.rs index ca4e938533..a886f9fe0f 100644 --- a/cli/cache/check.rs +++ b/cli/cache/check.rs @@ -1,12 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_ast::ModuleSpecifier; +use deno_core::error::AnyError; +use deno_runtime::deno_webstorage::rusqlite::params; use super::cache_db::CacheDB; use super::cache_db::CacheDBConfiguration; use super::cache_db::CacheDBHash; use super::cache_db::CacheFailure; -use deno_ast::ModuleSpecifier; -use deno_core::error::AnyError; -use deno_runtime::deno_webstorage::rusqlite::params; pub static TYPE_CHECK_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration { table_initializer: concat!( diff --git a/cli/cache/code_cache.rs b/cli/cache/code_cache.rs index b1d9ae757b..27ec544b5f 100644 --- a/cli/cache/code_cache.rs +++ b/cli/cache/code_cache.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; @@ -7,12 +7,11 @@ use deno_core::error::AnyError; use deno_runtime::code_cache; use deno_runtime::deno_webstorage::rusqlite::params; -use crate::worker::CliCodeCache; - use super::cache_db::CacheDB; use super::cache_db::CacheDBConfiguration; use super::cache_db::CacheDBHash; use super::cache_db::CacheFailure; +use crate::worker::CliCodeCache; pub static CODE_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration { table_initializer: concat!( diff --git a/cli/cache/common.rs b/cli/cache/common.rs index 0a68e95159..da607a27f2 100644 --- a/cli/cache/common.rs +++ b/cli/cache/common.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::hash::Hasher; diff --git a/cli/cache/deno_dir.rs b/cli/cache/deno_dir.rs index 7b7059c224..1b35f53071 100644 --- a/cli/cache/deno_dir.rs +++ b/cli/cache/deno_dir.rs @@ -1,33 +1,48 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use once_cell::sync::OnceCell; - -use super::DiskCache; +// Copyright 2018-2025 the Deno authors. MIT license. use std::env; use std::path::PathBuf; +use deno_cache_dir::DenoDirResolutionError; +use once_cell::sync::OnceCell; + +use super::DiskCache; +use crate::sys::CliSys; + /// Lazily creates the deno dir which might be useful in scenarios /// where functionality wants to continue if the DENO_DIR can't be created. pub struct DenoDirProvider { + sys: CliSys, maybe_custom_root: Option, - deno_dir: OnceCell>, + deno_dir: OnceCell>, } impl DenoDirProvider { - pub fn new(maybe_custom_root: Option) -> Self { + pub fn new(sys: CliSys, maybe_custom_root: Option) -> Self { Self { + sys, maybe_custom_root, deno_dir: Default::default(), } } - pub fn get_or_create(&self) -> Result<&DenoDir, std::io::Error> { + pub fn get_or_create(&self) -> Result<&DenoDir, DenoDirResolutionError> { self .deno_dir - .get_or_init(|| DenoDir::new(self.maybe_custom_root.clone())) + .get_or_init(|| { + DenoDir::new(self.sys.clone(), self.maybe_custom_root.clone()) + }) .as_ref() - .map_err(|err| std::io::Error::new(err.kind(), err.to_string())) + .map_err(|err| match err { + DenoDirResolutionError::NoCacheOrHomeDir => { + DenoDirResolutionError::NoCacheOrHomeDir + } + DenoDirResolutionError::FailedCwd { source } => { + DenoDirResolutionError::FailedCwd { + source: std::io::Error::new(source.kind(), source.to_string()), + } + } + }) } } @@ -42,33 +57,20 @@ pub struct DenoDir { } impl DenoDir { - pub fn new(maybe_custom_root: Option) -> std::io::Result { - let maybe_custom_root = - maybe_custom_root.or_else(|| env::var("DENO_DIR").map(String::into).ok()); - let root: PathBuf = if let Some(root) = maybe_custom_root { - root - } else if let Some(cache_dir) = dirs::cache_dir() { - // We use the OS cache dir because all files deno writes are cache files - // Once that changes we need to start using different roots if DENO_DIR - // is not set, and keep a single one if it is. - cache_dir.join("deno") - } else if let Some(home_dir) = dirs::home_dir() { - // fallback path - home_dir.join(".deno") - } else { - panic!("Could not set the Deno root directory") - }; - let root = if root.is_absolute() { - root - } else { - std::env::current_dir()?.join(root) - }; + pub fn new( + sys: CliSys, + maybe_custom_root: Option, + ) -> Result { + let root = deno_cache_dir::resolve_deno_dir( + &sys_traits::impls::RealSys, + maybe_custom_root, + )?; assert!(root.is_absolute()); let gen_path = root.join("gen"); let deno_dir = Self { root, - gen_cache: DiskCache::new(&gen_path), + gen_cache: DiskCache::new(sys, &gen_path), }; Ok(deno_dir) @@ -166,112 +168,3 @@ impl DenoDir { self.root.join("dl") } } - -/// To avoid the poorly managed dirs crate -#[cfg(not(windows))] -pub mod dirs { - use std::path::PathBuf; - - pub fn cache_dir() -> Option { - if cfg!(target_os = "macos") { - home_dir().map(|h| h.join("Library/Caches")) - } else { - std::env::var_os("XDG_CACHE_HOME") - .map(PathBuf::from) - .or_else(|| home_dir().map(|h| h.join(".cache"))) - } - } - - pub fn home_dir() -> Option { - std::env::var_os("HOME") - .and_then(|h| if h.is_empty() { None } else { Some(h) }) - .or_else(|| { - // TODO(bartlomieju): - #[allow(clippy::undocumented_unsafe_blocks)] - unsafe { - fallback() - } - }) - .map(PathBuf::from) - } - - // This piece of code is taken from the deprecated home_dir() function in Rust's standard library: https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/os.rs#L579 - // The same code is used by the dirs crate - unsafe fn fallback() -> Option { - let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { - n if n < 0 => 512_usize, - n => n as usize, - }; - let mut buf = Vec::with_capacity(amt); - let mut passwd: libc::passwd = std::mem::zeroed(); - let mut result = std::ptr::null_mut(); - match libc::getpwuid_r( - libc::getuid(), - &mut passwd, - buf.as_mut_ptr(), - buf.capacity(), - &mut result, - ) { - 0 if !result.is_null() => { - let ptr = passwd.pw_dir as *const _; - let bytes = std::ffi::CStr::from_ptr(ptr).to_bytes().to_vec(); - Some(std::os::unix::ffi::OsStringExt::from_vec(bytes)) - } - _ => None, - } - } -} - -/// To avoid the poorly managed dirs crate -// Copied from -// https://github.com/dirs-dev/dirs-sys-rs/blob/ec7cee0b3e8685573d847f0a0f60aae3d9e07fa2/src/lib.rs#L140-L164 -// MIT license. Copyright (c) 2018-2019 dirs-rs contributors -#[cfg(windows)] -pub mod dirs { - use std::ffi::OsString; - use std::os::windows::ffi::OsStringExt; - use std::path::PathBuf; - use winapi::shared::winerror; - use winapi::um::combaseapi; - use winapi::um::knownfolders; - use winapi::um::shlobj; - use winapi::um::shtypes; - use winapi::um::winbase; - use winapi::um::winnt; - - fn known_folder(folder_id: shtypes::REFKNOWNFOLDERID) -> Option { - // SAFETY: winapi calls - unsafe { - let mut path_ptr: winnt::PWSTR = std::ptr::null_mut(); - let result = shlobj::SHGetKnownFolderPath( - folder_id, - 0, - std::ptr::null_mut(), - &mut path_ptr, - ); - if result == winerror::S_OK { - let len = winbase::lstrlenW(path_ptr) as usize; - let path = std::slice::from_raw_parts(path_ptr, len); - let ostr: OsString = OsStringExt::from_wide(path); - combaseapi::CoTaskMemFree(path_ptr as *mut winapi::ctypes::c_void); - Some(PathBuf::from(ostr)) - } else { - None - } - } - } - - pub fn cache_dir() -> Option { - known_folder(&knownfolders::FOLDERID_LocalAppData) - } - - pub fn home_dir() -> Option { - if let Some(userprofile) = std::env::var_os("USERPROFILE") { - if !userprofile.is_empty() { - return Some(PathBuf::from(userprofile)); - } - } - - known_folder(&knownfolders::FOLDERID_Profile) - } -} diff --git a/cli/cache/disk_cache.rs b/cli/cache/disk_cache.rs index 2fee1efe09..f03b60854f 100644 --- a/cli/cache/disk_cache.rs +++ b/cli/cache/disk_cache.rs @@ -1,11 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::CACHE_PERM; -use crate::util::fs::atomic_write_file_with_retries; - -use deno_cache_dir::url_to_filename; -use deno_core::url::Host; -use deno_core::url::Url; use std::ffi::OsStr; use std::fs; use std::path::Component; @@ -14,16 +8,26 @@ use std::path::PathBuf; use std::path::Prefix; use std::str; +use deno_cache_dir::url_to_filename; +use deno_core::url::Host; +use deno_core::url::Url; +use deno_path_util::fs::atomic_write_file_with_retries; + +use super::CACHE_PERM; +use crate::sys::CliSys; + #[derive(Debug, Clone)] pub struct DiskCache { + sys: CliSys, pub location: PathBuf, } impl DiskCache { /// `location` must be an absolute path. - pub fn new(location: &Path) -> Self { + pub fn new(sys: CliSys, location: &Path) -> Self { assert!(location.is_absolute()); Self { + sys, location: location.to_owned(), } } @@ -120,20 +124,21 @@ impl DiskCache { pub fn set(&self, filename: &Path, data: &[u8]) -> std::io::Result<()> { let path = self.location.join(filename); - atomic_write_file_with_retries(&path, data, CACHE_PERM) + atomic_write_file_with_retries(&self.sys, &path, data, CACHE_PERM) } } #[cfg(test)] mod tests { - use super::*; use test_util::TempDir; + use super::*; + #[test] fn test_set_get_cache_file() { let temp_dir = TempDir::new(); let sub_dir = temp_dir.path().join("sub_dir"); - let cache = DiskCache::new(&sub_dir.to_path_buf()); + let cache = DiskCache::new(CliSys::default(), &sub_dir.to_path_buf()); let path = PathBuf::from("foo/bar.txt"); cache.set(&path, b"hello").unwrap(); assert_eq!(cache.get(&path).unwrap(), b"hello"); @@ -147,7 +152,7 @@ mod tests { PathBuf::from("/deno_dir/") }; - let cache = DiskCache::new(&cache_location); + let cache = DiskCache::new(CliSys::default(), &cache_location); let mut test_cases = vec![ ( @@ -203,7 +208,7 @@ mod tests { } else { "/foo" }; - let cache = DiskCache::new(&PathBuf::from(p)); + let cache = DiskCache::new(CliSys::default(), &PathBuf::from(p)); let mut test_cases = vec![ ( @@ -251,7 +256,7 @@ mod tests { PathBuf::from("/deno_dir/") }; - let cache = DiskCache::new(&cache_location); + let cache = DiskCache::new(CliSys::default(), &cache_location); let mut test_cases = vec!["unknown://localhost/test.ts"]; diff --git a/cli/cache/emit.rs b/cli/cache/emit.rs index 3c9eecfcbd..2ba43d58b9 100644 --- a/cli/cache/emit.rs +++ b/cli/cache/emit.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::PathBuf; @@ -160,11 +160,13 @@ mod test { use test_util::TempDir; use super::*; + use crate::sys::CliSys; #[test] pub fn emit_cache_general_use() { let temp_dir = TempDir::new(); - let disk_cache = DiskCache::new(temp_dir.path().as_path()); + let disk_cache = + DiskCache::new(CliSys::default(), temp_dir.path().as_path()); let cache = EmitCache { disk_cache: disk_cache.clone(), file_serializer: EmitFileSerializer { diff --git a/cli/cache/fast_check.rs b/cli/cache/fast_check.rs index 43be1b7186..323312d057 100644 --- a/cli/cache/fast_check.rs +++ b/cli/cache/fast_check.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::AnyError; use deno_graph::FastCheckCacheItem; diff --git a/cli/cache/incremental.rs b/cli/cache/incremental.rs index 2d31b4125e..f430c1266f 100644 --- a/cli/cache/incremental.rs +++ b/cli/cache/incremental.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::path::Path; @@ -34,12 +34,16 @@ pub static INCREMENTAL_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration { pub struct IncrementalCache(IncrementalCacheInner); impl IncrementalCache { - pub fn new( + pub fn new( db: CacheDB, - state: &TState, + state_hash: CacheDBHash, initial_file_paths: &[PathBuf], ) -> Self { - IncrementalCache(IncrementalCacheInner::new(db, state, initial_file_paths)) + IncrementalCache(IncrementalCacheInner::new( + db, + state_hash, + initial_file_paths, + )) } pub fn is_file_same(&self, file_path: &Path, file_text: &str) -> bool { @@ -67,12 +71,11 @@ struct IncrementalCacheInner { } impl IncrementalCacheInner { - pub fn new( + pub fn new( db: CacheDB, - state: &TState, + state_hash: CacheDBHash, initial_file_paths: &[PathBuf], ) -> Self { - let state_hash = CacheDBHash::from_source(state); let sql_cache = SqlIncrementalCache::new(db, state_hash); Self::from_sql_incremental_cache(sql_cache, initial_file_paths) } @@ -112,13 +115,13 @@ impl IncrementalCacheInner { pub fn is_file_same(&self, file_path: &Path, file_text: &str) -> bool { match self.previous_hashes.get(file_path) { - Some(hash) => *hash == CacheDBHash::from_source(file_text), + Some(hash) => *hash == CacheDBHash::from_hashable(file_text), None => false, } } pub fn update_file(&self, file_path: &Path, file_text: &str) { - let hash = CacheDBHash::from_source(file_text); + let hash = CacheDBHash::from_hashable(file_text); if let Some(previous_hash) = self.previous_hashes.get(file_path) { if *previous_hash == hash { return; // do not bother updating the db file because nothing has changed @@ -262,7 +265,7 @@ mod test { let sql_cache = SqlIncrementalCache::new(conn, CacheDBHash::new(1)); let file_path = PathBuf::from("/mod.ts"); let file_text = "test"; - let file_hash = CacheDBHash::from_source(file_text); + let file_hash = CacheDBHash::from_hashable(file_text); sql_cache.set_source_hash(&file_path, file_hash).unwrap(); let cache = IncrementalCacheInner::from_sql_incremental_cache( sql_cache, diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index e3e242e975..0d7808cba6 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -1,18 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::args::jsr_url; -use crate::args::CacheSetting; -use crate::errors::get_error_class_name; -use crate::file_fetcher::FetchNoFollowOptions; -use crate::file_fetcher::FetchOptions; -use crate::file_fetcher::FetchPermissionsOptionRef; -use crate::file_fetcher::FileFetcher; -use crate::file_fetcher::FileOrRedirect; -use crate::util::fs::atomic_write_file_with_retries; -use crate::util::fs::atomic_write_file_with_retries_and_fs; -use crate::util::fs::AtomicWriteFileFsAdapter; +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::Arc; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::CacheSetting; +use deno_cache_dir::file_fetcher::FetchNoFollowErrorKind; +use deno_cache_dir::file_fetcher::FileOrRedirect; use deno_core::futures; use deno_core::futures::FutureExt; use deno_core::ModuleSpecifier; @@ -20,15 +15,16 @@ use deno_graph::source::CacheInfo; use deno_graph::source::LoadFuture; use deno_graph::source::LoadResponse; use deno_graph::source::Loader; -use deno_runtime::deno_fs; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::deno_permissions::PermissionsContainer; use node_resolver::InNpmPackageChecker; -use std::borrow::Cow; -use std::collections::HashMap; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; -use std::time::SystemTime; + +use crate::args::jsr_url; +use crate::file_fetcher::CliFetchNoFollowErrorKind; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::FetchNoFollowOptions; +use crate::file_fetcher::FetchPermissionsOptionRef; +use crate::sys::CliSys; mod cache_db; mod caches; @@ -49,7 +45,8 @@ pub use caches::Caches; pub use check::TypeCheckCache; pub use code_cache::CodeCache; pub use common::FastInsecureHasher; -pub use deno_dir::dirs::home_dir; +/// Permissions used to save a file in the disk caches. +pub use deno_cache_dir::CACHE_PERM; pub use deno_dir::DenoDir; pub use deno_dir::DenoDirProvider; pub use disk_cache::DiskCache; @@ -61,123 +58,11 @@ pub use node::NodeAnalysisCache; pub use parsed_source::LazyGraphSourceParser; pub use parsed_source::ParsedSourceCache; -/// Permissions used to save a file in the disk caches. -pub const CACHE_PERM: u32 = 0o644; - -#[derive(Debug, Clone)] -pub struct RealDenoCacheEnv; - -impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv { - fn read_file_bytes( - &self, - path: &Path, - ) -> std::io::Result> { - std::fs::read(path).map(Cow::Owned) - } - - fn atomic_write_file( - &self, - path: &Path, - bytes: &[u8], - ) -> std::io::Result<()> { - atomic_write_file_with_retries(path, bytes, CACHE_PERM) - } - - fn canonicalize_path(&self, path: &Path) -> std::io::Result { - crate::util::fs::canonicalize_path(path) - } - - fn create_dir_all(&self, path: &Path) -> std::io::Result<()> { - std::fs::create_dir_all(path) - } - - fn modified(&self, path: &Path) -> std::io::Result> { - match std::fs::metadata(path) { - Ok(metadata) => Ok(Some( - metadata.modified().unwrap_or_else(|_| SystemTime::now()), - )), - Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None), - Err(err) => Err(err), - } - } - - fn is_file(&self, path: &Path) -> bool { - path.is_file() - } - - fn time_now(&self) -> SystemTime { - SystemTime::now() - } -} - -#[derive(Debug, Clone)] -pub struct DenoCacheEnvFsAdapter<'a>( - pub &'a dyn deno_runtime::deno_fs::FileSystem, -); - -impl<'a> deno_cache_dir::DenoCacheEnv for DenoCacheEnvFsAdapter<'a> { - fn read_file_bytes( - &self, - path: &Path, - ) -> std::io::Result> { - self - .0 - .read_file_sync(path, None) - .map_err(|err| err.into_io_error()) - } - - fn atomic_write_file( - &self, - path: &Path, - bytes: &[u8], - ) -> std::io::Result<()> { - atomic_write_file_with_retries_and_fs( - &AtomicWriteFileFsAdapter { - fs: self.0, - write_mode: CACHE_PERM, - }, - path, - bytes, - ) - } - - fn canonicalize_path(&self, path: &Path) -> std::io::Result { - self.0.realpath_sync(path).map_err(|e| e.into_io_error()) - } - - fn create_dir_all(&self, path: &Path) -> std::io::Result<()> { - self - .0 - .mkdir_sync(path, true, None) - .map_err(|e| e.into_io_error()) - } - - fn modified(&self, path: &Path) -> std::io::Result> { - self - .0 - .stat_sync(path) - .map(|stat| { - stat - .mtime - .map(|ts| SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(ts)) - }) - .map_err(|e| e.into_io_error()) - } - - fn is_file(&self, path: &Path) -> bool { - self.0.is_file_sync(path) - } - - fn time_now(&self) -> SystemTime { - SystemTime::now() - } -} - -pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache; -pub type LocalHttpCache = deno_cache_dir::LocalHttpCache; -pub type LocalLspHttpCache = - deno_cache_dir::LocalLspHttpCache; +pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache; +pub type LocalHttpCache = deno_cache_dir::LocalHttpCache; +pub type LocalLspHttpCache = deno_cache_dir::LocalLspHttpCache; pub use deno_cache_dir::HttpCache; +use deno_error::JsErrorBox; pub struct FetchCacherOptions { pub file_header_overrides: HashMap>, @@ -190,31 +75,31 @@ pub struct FetchCacherOptions { /// a concise interface to the DENO_DIR when building module graphs. pub struct FetchCacher { pub file_header_overrides: HashMap>, - file_fetcher: Arc, - fs: Arc, + file_fetcher: Arc, global_http_cache: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, module_info_cache: Arc, permissions: PermissionsContainer, + sys: CliSys, is_deno_publish: bool, cache_info_enabled: bool, } impl FetchCacher { pub fn new( - file_fetcher: Arc, - fs: Arc, + file_fetcher: Arc, global_http_cache: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, module_info_cache: Arc, + sys: CliSys, options: FetchCacherOptions, ) -> Self { Self { file_fetcher, - fs, global_http_cache, in_npm_pkg_checker, module_info_cache, + sys, file_header_overrides: options.file_header_overrides, permissions: options.permissions, is_deno_publish: options.is_deno_publish, @@ -276,9 +161,8 @@ impl Loader for FetchCacher { // symlinked to `/my-project-2/node_modules`), so first we checked if the path // is in a node_modules dir to avoid needlessly canonicalizing, then now compare // against the canonicalized specifier. - let specifier = crate::node::resolve_specifier_into_node_modules( - specifier, - self.fs.as_ref(), + let specifier = node_resolver::resolve_specifier_into_node_modules( + &self.sys, specifier, ); if self.in_npm_pkg_checker.in_npm_package(&specifier) { return Box::pin(futures::future::ready(Ok(Some( @@ -311,27 +195,27 @@ impl Loader for FetchCacher { LoaderCacheSetting::Use => None, LoaderCacheSetting::Reload => { if matches!(file_fetcher.cache_setting(), CacheSetting::Only) { - return Err(deno_core::anyhow::anyhow!( + return Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::generic( "Could not resolve version constraint using only cached data. Try running again without --cached-only" - )); + )))); } Some(CacheSetting::ReloadAll) } LoaderCacheSetting::Only => Some(CacheSetting::Only), }; file_fetcher - .fetch_no_follow_with_options(FetchNoFollowOptions { - fetch_options: FetchOptions { - specifier: &specifier, - permissions: if is_statically_analyzable { - FetchPermissionsOptionRef::StaticContainer(&permissions) - } else { - FetchPermissionsOptionRef::DynamicContainer(&permissions) - }, - maybe_auth: None, - maybe_accept: None, - maybe_cache_setting: maybe_cache_setting.as_ref(), - }, + .fetch_no_follow( + &specifier, + FetchPermissionsOptionRef::Restricted(&permissions, + if is_statically_analyzable { + deno_runtime::deno_permissions::CheckSpecifierKind::Static + } else { + deno_runtime::deno_permissions::CheckSpecifierKind::Dynamic + }), + FetchNoFollowOptions { + maybe_auth: None, + maybe_accept: None, + maybe_cache_setting: maybe_cache_setting.as_ref(), maybe_checksum: options.maybe_checksum.as_ref(), }) .await @@ -348,7 +232,7 @@ impl Loader for FetchCacher { (None, None) => None, }; Ok(Some(LoadResponse::Module { - specifier: file.specifier, + specifier: file.url, maybe_headers, content: file.source, })) @@ -361,18 +245,45 @@ impl Loader for FetchCacher { } }) .unwrap_or_else(|err| { - if let Some(io_err) = err.downcast_ref::() { - if io_err.kind() == std::io::ErrorKind::NotFound { - return Ok(None); - } else { - return Err(err); - } - } - let error_class_name = get_error_class_name(&err); - match error_class_name { - "NotFound" => Ok(None), - "NotCached" if options.cache_setting == LoaderCacheSetting::Only => Ok(None), - _ => Err(err), + let err = err.into_kind(); + match err { + CliFetchNoFollowErrorKind::FetchNoFollow(err) => { + let err = err.into_kind(); + match err { + FetchNoFollowErrorKind::NotFound(_) => Ok(None), + FetchNoFollowErrorKind::UrlToFilePath { .. } | + FetchNoFollowErrorKind::ReadingBlobUrl { .. } | + FetchNoFollowErrorKind::ReadingFile { .. } | + FetchNoFollowErrorKind::FetchingRemote { .. } | + FetchNoFollowErrorKind::ClientError { .. } | + FetchNoFollowErrorKind::NoRemote { .. } | + FetchNoFollowErrorKind::DataUrlDecode { .. } | + FetchNoFollowErrorKind::RedirectResolution { .. } | + FetchNoFollowErrorKind::CacheRead { .. } | + FetchNoFollowErrorKind::CacheSave { .. } | + FetchNoFollowErrorKind::UnsupportedScheme { .. } | + FetchNoFollowErrorKind::RedirectHeaderParse { .. } | + FetchNoFollowErrorKind::InvalidHeader { .. } => Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::from_err(err)))), + FetchNoFollowErrorKind::NotCached { .. } => { + if options.cache_setting == LoaderCacheSetting::Only { + Ok(None) + } else { + Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::from_err(err)))) + } + }, + FetchNoFollowErrorKind::ChecksumIntegrity(err) => { + // convert to the equivalent deno_graph error so that it + // enhances it if this is passed to deno_graph + Err( + deno_graph::source::LoadError::ChecksumIntegrity(deno_graph::source::ChecksumIntegrityError { + actual: err.actual, + expected: err.expected, + }), + ) + } + } + }, + CliFetchNoFollowErrorKind::PermissionCheck(permission_check_error) => Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::from_err(permission_check_error)))), } }) } @@ -387,7 +298,7 @@ impl Loader for FetchCacher { module_info: &deno_graph::ModuleInfo, ) { log::debug!("Caching module info for {}", specifier); - let source_hash = CacheDBHash::from_source(source); + let source_hash = CacheDBHash::from_hashable(source); let result = self.module_info_cache.set_module_info( specifier, media_type, diff --git a/cli/cache/module_info.rs b/cli/cache/module_info.rs index 469e2fafac..63f52c06f9 100644 --- a/cli/cache/module_info.rs +++ b/cli/cache/module_info.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; @@ -194,7 +194,7 @@ impl<'a> ModuleInfoCacheModuleAnalyzer<'a> { source: &Arc, ) -> Result { // attempt to load from the cache - let source_hash = CacheDBHash::from_source(source); + let source_hash = CacheDBHash::from_hashable(source); if let Some(info) = self.load_cached_module_info(specifier, media_type, source_hash) { @@ -228,7 +228,7 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> { media_type: MediaType, ) -> Result { // attempt to load from the cache - let source_hash = CacheDBHash::from_source(&source); + let source_hash = CacheDBHash::from_hashable(&source); if let Some(info) = self.load_cached_module_info(specifier, media_type, source_hash) { diff --git a/cli/cache/node.rs b/cli/cache/node.rs index e80342e5c0..89e372de43 100644 --- a/cli/cache/node.rs +++ b/cli/cache/node.rs @@ -1,15 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::AnyError; use deno_core::serde_json; use deno_runtime::deno_webstorage::rusqlite::params; -use crate::node::CliCjsAnalysis; - use super::cache_db::CacheDB; use super::cache_db::CacheDBConfiguration; use super::cache_db::CacheFailure; use super::CacheDBHash; +use crate::node::CliCjsAnalysis; pub static NODE_ANALYSIS_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration { diff --git a/cli/cache/parsed_source.rs b/cli/cache/parsed_source.rs index 4d031f8bf2..15207f4ba7 100644 --- a/cli/cache/parsed_source.rs +++ b/cli/cache/parsed_source.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::sync::Arc; diff --git a/cli/cdp.rs b/cli/cdp.rs index c5ff587dde..df82d58d9f 100644 --- a/cli/cdp.rs +++ b/cli/cdp.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// use deno_core::serde_json::Value; diff --git a/cli/clippy.toml b/cli/clippy.toml index f1c25acfb8..21a544aebd 100644 --- a/cli/clippy.toml +++ b/cli/clippy.toml @@ -4,6 +4,7 @@ disallowed-methods = [ ] disallowed-types = [ { path = "reqwest::Client", reason = "use crate::http_util::HttpClient instead" }, + { path = "sys_traits::impls::RealSys", reason = "use crate::sys::CliSys instead" }, ] ignore-interior-mutability = [ "lsp_types::Uri", diff --git a/cli/emit.rs b/cli/emit.rs index 3cd23b7abb..69ac8323bb 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -1,10 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::cache::EmitCache; -use crate::cache::FastInsecureHasher; -use crate::cache::ParsedSourceCache; -use crate::resolver::CjsTracker; +use std::sync::Arc; +use deno_ast::EmittedSourceText; use deno_ast::ModuleKind; use deno_ast::SourceMapOption; use deno_ast::SourceRange; @@ -13,18 +11,24 @@ use deno_ast::SourceRangedForSpanned; use deno_ast::TranspileModuleOptions; use deno_ast::TranspileResult; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::FutureExt; use deno_core::futures::StreamExt; use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; use deno_graph::MediaType; use deno_graph::Module; use deno_graph::ModuleGraph; -use std::sync::Arc; + +use crate::cache::EmitCache; +use crate::cache::FastInsecureHasher; +use crate::cache::ParsedSourceCache; +use crate::resolver::CliCjsTracker; #[derive(Debug)] pub struct Emitter { - cjs_tracker: Arc, + cjs_tracker: Arc, emit_cache: Arc, parsed_source_cache: Arc, transpile_and_emit_options: @@ -35,7 +39,7 @@ pub struct Emitter { impl Emitter { pub fn new( - cjs_tracker: Arc, + cjs_tracker: Arc, emit_cache: Arc, parsed_source_cache: Arc, transpile_options: deno_ast::TranspileOptions, @@ -122,7 +126,7 @@ impl Emitter { let transpiled_source = deno_core::unsync::spawn_blocking({ let specifier = specifier.clone(); let source = source.clone(); - move || -> Result<_, AnyError> { + move || { EmitParsedSourceHelper::transpile( &parsed_source_cache, &specifier, @@ -132,6 +136,7 @@ impl Emitter { &transpile_and_emit_options.0, &transpile_and_emit_options.1, ) + .map(|r| r.text) } }) .await @@ -152,7 +157,7 @@ impl Emitter { media_type: MediaType, module_kind: deno_ast::ModuleKind, source: &Arc, - ) -> Result { + ) -> Result { // Note: keep this in sync with the async version above let helper = EmitParsedSourceHelper(self); match helper.pre_emit_parsed_source(specifier, module_kind, source) { @@ -166,7 +171,8 @@ impl Emitter { source.clone(), &self.transpile_and_emit_options.0, &self.transpile_and_emit_options.1, - )?; + )? + .text; helper.post_emit_parsed_source( specifier, &transpiled_source, @@ -177,11 +183,36 @@ impl Emitter { } } + pub fn emit_parsed_source_for_deno_compile( + &self, + specifier: &ModuleSpecifier, + media_type: MediaType, + module_kind: deno_ast::ModuleKind, + source: &Arc, + ) -> Result<(String, String), AnyError> { + let mut emit_options = self.transpile_and_emit_options.1.clone(); + emit_options.inline_sources = false; + emit_options.source_map = SourceMapOption::Separate; + // strip off the path to have more deterministic builds as we don't care + // about the source name because we manually provide the source map to v8 + emit_options.source_map_base = Some(deno_path_util::url_parent(specifier)); + let source = EmitParsedSourceHelper::transpile( + &self.parsed_source_cache, + specifier, + media_type, + module_kind, + source.clone(), + &self.transpile_and_emit_options.0, + &emit_options, + )?; + Ok((source.text, source.source_map.unwrap())) + } + /// Expects a file URL, panics otherwise. pub async fn load_and_emit_for_hmr( &self, specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { let media_type = MediaType::from_specifier(specifier); let source_code = tokio::fs::read_to_string( ModuleSpecifier::to_file_path(specifier).unwrap(), @@ -196,17 +227,21 @@ impl Emitter { let source_arc: Arc = source_code.into(); let parsed_source = self .parsed_source_cache - .remove_or_parse_module(specifier, source_arc, media_type)?; + .remove_or_parse_module(specifier, source_arc, media_type) + .map_err(JsErrorBox::from_err)?; // HMR doesn't work with embedded source maps for some reason, so set // the option to not use them (though you should test this out because // this statement is probably wrong) let mut options = self.transpile_and_emit_options.1.clone(); options.source_map = SourceMapOption::None; - let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script( - specifier, - media_type, - parsed_source.compute_is_script(), - )?; + let is_cjs = self + .cjs_tracker + .is_cjs_with_known_is_script( + specifier, + media_type, + parsed_source.compute_is_script(), + ) + .map_err(JsErrorBox::from_err)?; let transpiled_source = parsed_source .transpile( &self.transpile_and_emit_options.0, @@ -214,7 +249,8 @@ impl Emitter { module_kind: Some(ModuleKind::from_is_cjs(is_cjs)), }, &options, - )? + ) + .map_err(JsErrorBox::from_err)? .into_source(); Ok(transpiled_source.text) } @@ -253,6 +289,19 @@ enum PreEmitResult { NotCached { source_hash: u64 }, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum EmitParsedSourceHelperError { + #[class(inherit)] + #[error(transparent)] + ParseDiagnostic(#[from] deno_ast::ParseDiagnostic), + #[class(inherit)] + #[error(transparent)] + Transpile(#[from] deno_ast::TranspileError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + /// Helper to share code between async and sync emit_parsed_source methods. struct EmitParsedSourceHelper<'a>(&'a Emitter); @@ -282,7 +331,7 @@ impl<'a> EmitParsedSourceHelper<'a> { source: Arc, transpile_options: &deno_ast::TranspileOptions, emit_options: &deno_ast::EmitOptions, - ) -> Result { + ) -> Result { // nothing else needs the parsed source at this point, so remove from // the cache in order to not transpile owned let parsed_source = parsed_source_cache @@ -302,8 +351,7 @@ impl<'a> EmitParsedSourceHelper<'a> { source } }; - debug_assert!(transpiled_source.source_map.is_none()); - Ok(transpiled_source.text) + Ok(transpiled_source) } pub fn post_emit_parsed_source( @@ -323,7 +371,7 @@ impl<'a> EmitParsedSourceHelper<'a> { // todo(dsherret): this is a temporary measure until we have swc erroring for this fn ensure_no_import_assertion( parsed_source: &deno_ast::ParsedSource, -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { fn has_import_assertion(text: &str) -> bool { // good enough text.contains(" assert ") && !text.contains(" with ") @@ -332,7 +380,7 @@ fn ensure_no_import_assertion( fn create_err( parsed_source: &deno_ast::ParsedSource, range: SourceRange, - ) -> AnyError { + ) -> JsErrorBox { let text_info = parsed_source.text_info_lazy(); let loc = text_info.line_and_column_display(range.start); let mut msg = "Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.".to_string(); @@ -345,7 +393,7 @@ fn ensure_no_import_assertion( loc.line_number, loc.column_number, )); - deno_core::anyhow::anyhow!("{}", msg) + JsErrorBox::generic(msg) } let deno_ast::ProgramRef::Module(module) = parsed_source.program_ref() else { diff --git a/cli/errors.rs b/cli/errors.rs deleted file mode 100644 index 38dc8259e3..0000000000 --- a/cli/errors.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -//! There are many types of errors in Deno: -//! - AnyError: a generic wrapper that can encapsulate any type of error. -//! - JsError: a container for the error message and stack trace for exceptions -//! thrown in JavaScript code. We use this to pretty-print stack traces. -//! - Diagnostic: these are errors that originate in TypeScript's compiler. -//! They're similar to JsError, in that they have line numbers. But -//! Diagnostics are compile-time type errors, whereas JsErrors are runtime -//! exceptions. - -use deno_ast::ParseDiagnostic; -use deno_core::error::AnyError; -use deno_graph::source::ResolveError; -use deno_graph::ModuleError; -use deno_graph::ModuleGraphError; -use deno_graph::ModuleLoadError; -use deno_graph::ResolutionError; -use import_map::ImportMapError; - -fn get_import_map_error_class(_: &ImportMapError) -> &'static str { - "URIError" -} - -fn get_diagnostic_class(_: &ParseDiagnostic) -> &'static str { - "SyntaxError" -} - -fn get_module_graph_error_class(err: &ModuleGraphError) -> &'static str { - use deno_graph::JsrLoadError; - use deno_graph::NpmLoadError; - - match err { - ModuleGraphError::ResolutionError(err) - | ModuleGraphError::TypesResolutionError(err) => { - get_resolution_error_class(err) - } - ModuleGraphError::ModuleError(err) => match err { - ModuleError::InvalidTypeAssertion { .. } => "SyntaxError", - ModuleError::ParseErr(_, diagnostic) => get_diagnostic_class(diagnostic), - ModuleError::WasmParseErr(..) => "SyntaxError", - ModuleError::UnsupportedMediaType { .. } - | ModuleError::UnsupportedImportAttributeType { .. } => "TypeError", - ModuleError::Missing(_, _) | ModuleError::MissingDynamic(_, _) => { - "NotFound" - } - ModuleError::LoadingErr(_, _, err) => match err { - ModuleLoadError::Loader(err) => get_error_class_name(err.as_ref()), - ModuleLoadError::HttpsChecksumIntegrity(_) - | ModuleLoadError::TooManyRedirects => "Error", - ModuleLoadError::NodeUnknownBuiltinModule(_) => "NotFound", - ModuleLoadError::Decode(_) => "TypeError", - ModuleLoadError::Npm(err) => match err { - NpmLoadError::NotSupportedEnvironment - | NpmLoadError::PackageReqResolution(_) - | NpmLoadError::RegistryInfo(_) => "Error", - NpmLoadError::PackageReqReferenceParse(_) => "TypeError", - }, - ModuleLoadError::Jsr(err) => match err { - JsrLoadError::UnsupportedManifestChecksum - | JsrLoadError::PackageFormat(_) => "TypeError", - JsrLoadError::ContentLoadExternalSpecifier - | JsrLoadError::ContentLoad(_) - | JsrLoadError::ContentChecksumIntegrity(_) - | JsrLoadError::PackageManifestLoad(_, _) - | JsrLoadError::PackageVersionManifestChecksumIntegrity(..) - | JsrLoadError::PackageVersionManifestLoad(_, _) - | JsrLoadError::RedirectInPackage(_) => "Error", - JsrLoadError::PackageNotFound(_) - | JsrLoadError::PackageReqNotFound(_) - | JsrLoadError::PackageVersionNotFound(_) - | JsrLoadError::UnknownExport { .. } => "NotFound", - }, - }, - }, - } -} - -fn get_resolution_error_class(err: &ResolutionError) -> &'static str { - match err { - ResolutionError::ResolverError { error, .. } => { - use ResolveError::*; - match error.as_ref() { - Specifier(_) => "TypeError", - Other(e) => get_error_class_name(e), - } - } - _ => "TypeError", - } -} - -fn get_try_from_int_error_class(_: &std::num::TryFromIntError) -> &'static str { - "TypeError" -} - -pub fn get_error_class_name(e: &AnyError) -> &'static str { - deno_runtime::errors::get_error_class_name(e) - .or_else(|| { - e.downcast_ref::() - .map(get_import_map_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_diagnostic_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_module_graph_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_resolution_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_try_from_int_error_class) - }) - .unwrap_or("Error") -} diff --git a/cli/factory.rs b/cli/factory.rs index f08bf7e4b1..3280fd379b 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -1,4 +1,39 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::future::Future; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_cache_dir::npm::NpmCacheDir; +use deno_config::workspace::PackageJsonDepResolution; +use deno_config::workspace::WorkspaceResolver; +use deno_core::error::AnyError; +use deno_core::futures::FutureExt; +use deno_core::FeatureChecker; +use deno_error::JsErrorBox; +use deno_npm_cache::NpmCacheSetting; +use deno_resolver::cjs::IsCjsResolutionMode; +use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_resolver::npm::CreateInNpmPkgCheckerOptions; +use deno_resolver::npm::DenoInNpmPackageChecker; +use deno_resolver::npm::NpmReqResolverOptions; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; +use deno_resolver::DenoResolverOptions; +use deno_resolver::NodeAndNpmReqResolver; +use deno_runtime::deno_fs; +use deno_runtime::deno_fs::RealFs; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; +use deno_runtime::deno_permissions::Permissions; +use deno_runtime::deno_permissions::PermissionsContainer; +use deno_runtime::deno_tls::rustls::RootCertStore; +use deno_runtime::deno_tls::RootCertStoreProvider; +use deno_runtime::deno_web::BlobStore; +use deno_runtime::inspector_server::InspectorServer; +use deno_runtime::permissions::RuntimePermissionDescriptorParser; +use log::warn; +use node_resolver::analyze::NodeCodeTranslator; +use once_cell::sync::OnceCell; use crate::args::check_warn_tsconfig; use crate::args::get_root_cert_store; @@ -11,7 +46,6 @@ use crate::args::StorageKeyResolver; use crate::args::TsConfigType; use crate::cache::Caches; use crate::cache::CodeCache; -use crate::cache::DenoCacheEnvFsAdapter; use crate::cache::DenoDir; use crate::cache::DenoDirProvider; use crate::cache::EmitCache; @@ -22,7 +56,7 @@ use crate::cache::ModuleInfoCache; use crate::cache::NodeAnalysisCache; use crate::cache::ParsedSourceCache; use crate::emit::Emitter; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::graph_container::MainModuleGraphContainer; use crate::graph_util::FileWatcherReporter; use crate::graph_util::ModuleGraphBuilder; @@ -32,25 +66,32 @@ use crate::module_loader::CliModuleLoaderFactory; use crate::module_loader::ModuleLoadPreparer; use crate::node::CliCjsCodeAnalyzer; use crate::node::CliNodeCodeTranslator; -use crate::npm::create_cli_npm_resolver; -use crate::npm::create_in_npm_pkg_checker; +use crate::node::CliNodeResolver; +use crate::node::CliPackageJsonResolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::NpmResolutionInstaller; use crate::npm::CliByonmNpmResolverCreateOptions; -use crate::npm::CliManagedInNpmPkgCheckerCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmCacheHttpClient; +use crate::npm::CliNpmRegistryInfoProvider; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; -use crate::npm::CreateInNpmPkgCheckerOptions; -use crate::resolver::CjsTracker; +use crate::npm::CliNpmTarballCache; +use crate::npm::NpmRegistryReadPermissionChecker; +use crate::npm::NpmRegistryReadPermissionCheckerMode; +use crate::npm::NpmResolutionInitializer; +use crate::resolver::CliCjsTracker; use crate::resolver::CliDenoResolver; -use crate::resolver::CliDenoResolverFs; +use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; -use crate::resolver::CliResolverOptions; use crate::resolver::CliSloppyImportsResolver; +use crate::resolver::FoundPackageJsonDepFlag; use crate::resolver::NpmModuleLoader; -use crate::resolver::SloppyImportsCachedFs; -use crate::standalone::DenoCompileBinaryWriter; +use crate::standalone::binary::DenoCompileBinaryWriter; +use crate::sys::CliSys; use crate::tools::check::TypeChecker; use crate::tools::coverage::CoverageCollector; use crate::tools::lint::LintRuleProvider; @@ -62,36 +103,6 @@ use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::worker::CliMainWorkerFactory; use crate::worker::CliMainWorkerOptions; -use std::path::PathBuf; - -use deno_cache_dir::npm::NpmCacheDir; -use deno_config::workspace::PackageJsonDepResolution; -use deno_config::workspace::WorkspaceResolver; -use deno_core::error::AnyError; -use deno_core::futures::FutureExt; -use deno_core::FeatureChecker; - -use deno_resolver::cjs::IsCjsResolutionMode; -use deno_resolver::npm::NpmReqResolverOptions; -use deno_resolver::DenoResolverOptions; -use deno_resolver::NodeAndNpmReqResolver; -use deno_runtime::deno_fs; -use deno_runtime::deno_node::DenoFsNodeResolverEnv; -use deno_runtime::deno_node::NodeResolver; -use deno_runtime::deno_node::PackageJsonResolver; -use deno_runtime::deno_permissions::Permissions; -use deno_runtime::deno_permissions::PermissionsContainer; -use deno_runtime::deno_tls::rustls::RootCertStore; -use deno_runtime::deno_tls::RootCertStoreProvider; -use deno_runtime::deno_web::BlobStore; -use deno_runtime::inspector_server::InspectorServer; -use deno_runtime::permissions::RuntimePermissionDescriptorParser; -use log::warn; -use node_resolver::analyze::NodeCodeTranslator; -use node_resolver::InNpmPackageChecker; -use once_cell::sync::OnceCell; -use std::future::Future; -use std::sync::Arc; struct CliRootCertStoreProvider { cell: OnceCell, @@ -116,7 +127,7 @@ impl CliRootCertStoreProvider { } impl RootCertStoreProvider for CliRootCertStoreProvider { - fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> { + fn get_or_try_init(&self) -> Result<&RootCertStore, JsErrorBox> { self .cell .get_or_try_init(|| { @@ -126,7 +137,7 @@ impl RootCertStoreProvider for CliRootCertStoreProvider { self.maybe_ca_data.clone(), ) }) - .map_err(|e| e.into()) + .map_err(JsErrorBox::from_err) } } @@ -178,19 +189,20 @@ impl Deferred { struct CliFactoryServices { blob_store: Deferred>, caches: Deferred>, - cjs_tracker: Deferred>, + cjs_tracker: Deferred>, cli_options: Deferred>, code_cache: Deferred>, deno_resolver: Deferred>, emit_cache: Deferred>, emitter: Deferred>, feature_checker: Deferred>, - file_fetcher: Deferred>, + file_fetcher: Deferred>, + found_pkg_json_dep_flag: Arc, fs: Deferred>, global_http_cache: Deferred>, http_cache: Deferred>, http_client_provider: Deferred>, - in_npm_pkg_checker: Deferred>, + in_npm_pkg_checker: Deferred, main_graph_container: Deferred>, maybe_file_watcher_reporter: Deferred>, maybe_inspector_server: Deferred>>, @@ -199,13 +211,23 @@ struct CliFactoryServices { module_info_cache: Deferred>, module_load_preparer: Deferred>, node_code_translator: Deferred>, - node_resolver: Deferred>, + node_resolver: Deferred>, + npm_cache: Deferred>, npm_cache_dir: Deferred>, + npm_cache_http_client: Deferred>, + npm_graph_resolver: Deferred>, + npm_installer: Deferred>, + npm_registry_info_provider: Deferred>, npm_req_resolver: Deferred>, - npm_resolver: Deferred>, + npm_resolution: Arc, + npm_resolution_initializer: Deferred>, + npm_resolution_installer: Deferred>, + npm_resolver: Deferred, + npm_tarball_cache: Deferred>, parsed_source_cache: Deferred>, - permission_desc_parser: Deferred>, - pkg_json_resolver: Deferred>, + permission_desc_parser: + Deferred>>, + pkg_json_resolver: Deferred>, resolver: Deferred>, root_cert_store_provider: Deferred>, root_permissions_container: Deferred, @@ -255,7 +277,7 @@ impl CliFactory { pub fn cli_options(&self) -> Result<&Arc, AnyError> { self.services.cli_options.get_or_try_init(|| { - CliOptions::from_flags(self.flags.clone()).map(Arc::new) + CliOptions::from_flags(&self.sys(), self.flags.clone()).map(Arc::new) }) } @@ -318,8 +340,8 @@ impl CliFactory { pub fn global_http_cache(&self) -> Result<&Arc, AnyError> { self.services.global_http_cache.get_or_try_init(|| { Ok(Arc::new(GlobalHttpCache::new( + self.sys(), self.deno_dir()?.remote_folder_path(), - crate::cache::RealDenoCacheEnv, ))) }) } @@ -350,34 +372,40 @@ impl CliFactory { }) } - pub fn file_fetcher(&self) -> Result<&Arc, AnyError> { + pub fn file_fetcher(&self) -> Result<&Arc, AnyError> { self.services.file_fetcher.get_or_try_init(|| { let cli_options = self.cli_options()?; - Ok(Arc::new(FileFetcher::new( + Ok(Arc::new(CliFileFetcher::new( self.http_cache()?.clone(), - cli_options.cache_setting(), - !cli_options.no_remote(), self.http_client_provider().clone(), + self.sys(), self.blob_store().clone(), Some(self.text_only_progress_bar().clone()), + !cli_options.no_remote(), + cli_options.cache_setting(), + log::Level::Info, ))) }) } pub fn fs(&self) -> &Arc { - self.services.fs.get_or_init(|| Arc::new(deno_fs::RealFs)) + self.services.fs.get_or_init(|| Arc::new(RealFs)) + } + + pub fn sys(&self) -> CliSys { + CliSys::default() // very cheap to make } pub fn in_npm_pkg_checker( &self, - ) -> Result<&Arc, AnyError> { + ) -> Result<&DenoInNpmPackageChecker, AnyError> { self.services.in_npm_pkg_checker.get_or_try_init(|| { let cli_options = self.cli_options()?; let options = if cli_options.use_byonm() { CreateInNpmPkgCheckerOptions::Byonm } else { CreateInNpmPkgCheckerOptions::Managed( - CliManagedInNpmPkgCheckerCreateOptions { + ManagedInNpmPkgCheckerCreateOptions { root_cache_dir_url: self.npm_cache_dir()?.root_dir_url(), maybe_node_modules_path: cli_options .node_modules_dir_path() @@ -385,37 +413,162 @@ impl CliFactory { }, ) }; - Ok(create_in_npm_pkg_checker(options)) + Ok(DenoInNpmPackageChecker::new(options)) + }) + } + + pub fn npm_cache(&self) -> Result<&Arc, AnyError> { + self.services.npm_cache.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmCache::new( + self.npm_cache_dir()?.clone(), + self.sys(), + NpmCacheSetting::from_cache_setting(&cli_options.cache_setting()), + cli_options.npmrc().clone(), + ))) }) } pub fn npm_cache_dir(&self) -> Result<&Arc, AnyError> { self.services.npm_cache_dir.get_or_try_init(|| { - let fs = self.fs(); let global_path = self.deno_dir()?.npm_folder_path(); let cli_options = self.cli_options()?; Ok(Arc::new(NpmCacheDir::new( - &DenoCacheEnvFsAdapter(fs.as_ref()), + &self.sys(), global_path, cli_options.npmrc().get_all_known_registries_urls(), ))) }) } - pub async fn npm_resolver( + pub fn npm_cache_http_client(&self) -> &Arc { + self.services.npm_cache_http_client.get_or_init(|| { + Arc::new(CliNpmCacheHttpClient::new( + self.http_client_provider().clone(), + self.text_only_progress_bar().clone(), + )) + }) + } + + pub fn npm_graph_resolver( &self, - ) -> Result<&Arc, AnyError> { + ) -> Result<&Arc, AnyError> { + self.services.npm_graph_resolver.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmGraphResolver::new( + self.npm_installer_if_managed()?.cloned(), + self.services.found_pkg_json_dep_flag.clone(), + cli_options.unstable_bare_node_builtins(), + cli_options.default_npm_caching_strategy(), + ))) + }) + } + + pub fn npm_installer_if_managed( + &self, + ) -> Result>, AnyError> { + let options = self.cli_options()?; + if options.use_byonm() || options.no_npm() { + Ok(None) + } else { + Ok(Some(self.npm_installer()?)) + } + } + + pub fn npm_installer(&self) -> Result<&Arc, AnyError> { + self.services.npm_installer.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(NpmInstaller::new( + self.npm_cache()?.clone(), + Arc::new(NpmInstallDepsProvider::from_workspace( + cli_options.workspace(), + )), + self.npm_resolution().clone(), + self.npm_resolution_initializer()?.clone(), + self.npm_resolution_installer()?.clone(), + self.text_only_progress_bar(), + self.sys(), + self.npm_tarball_cache()?.clone(), + cli_options.maybe_lockfile().cloned(), + cli_options.node_modules_dir_path().cloned(), + cli_options.lifecycle_scripts_config(), + cli_options.npm_system_info(), + ))) + }) + } + + pub fn npm_registry_info_provider( + &self, + ) -> Result<&Arc, AnyError> { + self + .services + .npm_registry_info_provider + .get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmRegistryInfoProvider::new( + self.npm_cache()?.clone(), + self.npm_cache_http_client().clone(), + cli_options.npmrc().clone(), + ))) + }) + } + + pub fn npm_resolution(&self) -> &Arc { + &self.services.npm_resolution + } + + pub fn npm_resolution_initializer( + &self, + ) -> Result<&Arc, AnyError> { + self + .services + .npm_resolution_initializer + .get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(NpmResolutionInitializer::new( + self.npm_registry_info_provider()?.clone(), + self.npm_resolution().clone(), + match cli_options.resolve_npm_resolution_snapshot()? { + Some(snapshot) => { + CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot)) + } + None => match cli_options.maybe_lockfile() { + Some(lockfile) => { + CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( + lockfile.clone(), + ) + } + None => CliNpmResolverManagedSnapshotOption::Specified(None), + }, + }, + ))) + }) + } + + pub fn npm_resolution_installer( + &self, + ) -> Result<&Arc, AnyError> { + self.services.npm_resolution_installer.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(NpmResolutionInstaller::new( + self.npm_registry_info_provider()?.clone(), + self.npm_resolution().clone(), + cli_options.maybe_lockfile().cloned(), + ))) + }) + } + + pub async fn npm_resolver(&self) -> Result<&CliNpmResolver, AnyError> { self .services .npm_resolver .get_or_try_init_async( async { - let fs = self.fs(); let cli_options = self.cli_options()?; - create_cli_npm_resolver(if cli_options.use_byonm() { + Ok(CliNpmResolver::new(if cli_options.use_byonm() { CliNpmResolverCreateOptions::Byonm( CliByonmNpmResolverCreateOptions { - fs: CliDenoResolverFs(fs.clone()), + sys: self.sys(), pkg_json_resolver: self.pkg_json_resolver().clone(), root_node_modules_dir: Some( match cli_options.node_modules_dir_path() { @@ -431,52 +584,43 @@ impl CliFactory { }, ) } else { + self + .npm_resolution_initializer()? + .ensure_initialized() + .await?; CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { - snapshot: match cli_options.resolve_npm_resolution_snapshot()? { - Some(snapshot) => { - CliNpmResolverManagedSnapshotOption::Specified(Some( - snapshot, - )) - } - None => match cli_options.maybe_lockfile() { - Some(lockfile) => { - CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( - lockfile.clone(), - ) - } - None => { - CliNpmResolverManagedSnapshotOption::Specified(None) - } - }, - }, - maybe_lockfile: cli_options.maybe_lockfile().cloned(), - fs: fs.clone(), - http_client_provider: self.http_client_provider().clone(), + sys: self.sys(), + npm_resolution: self.npm_resolution().clone(), npm_cache_dir: self.npm_cache_dir()?.clone(), - cache_setting: cli_options.cache_setting(), - text_only_progress_bar: self.text_only_progress_bar().clone(), maybe_node_modules_path: cli_options .node_modules_dir_path() .cloned(), - npm_install_deps_provider: Arc::new( - NpmInstallDepsProvider::from_workspace( - cli_options.workspace(), - ), - ), npm_system_info: cli_options.npm_system_info(), npmrc: cli_options.npmrc().clone(), - lifecycle_scripts: cli_options.lifecycle_scripts_config(), }, ) - }) - .await + })) } .boxed_local(), ) .await } + pub fn npm_tarball_cache( + &self, + ) -> Result<&Arc, AnyError> { + self.services.npm_tarball_cache.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmTarballCache::new( + self.npm_cache()?.clone(), + self.npm_cache_http_client().clone(), + self.sys(), + cli_options.npmrc().clone(), + ))) + }) + } + pub fn sloppy_imports_resolver( &self, ) -> Result>, AnyError> { @@ -486,7 +630,7 @@ impl CliFactory { .get_or_try_init(|| { Ok(self.cli_options()?.unstable_sloppy_imports().then(|| { Arc::new(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new( - self.fs().clone(), + self.sys(), ))) })) }) @@ -564,17 +708,10 @@ impl CliFactory { .resolver .get_or_try_init_async( async { - let cli_options = self.cli_options()?; - Ok(Arc::new(CliResolver::new(CliResolverOptions { - npm_resolver: if cli_options.no_npm() { - None - } else { - Some(self.npm_resolver().await?.clone()) - }, - bare_node_builtins_enabled: cli_options - .unstable_bare_node_builtins(), - deno_resolver: self.deno_resolver().await?.clone(), - }))) + Ok(Arc::new(CliResolver::new( + self.deno_resolver().await?.clone(), + self.services.found_pkg_json_dep_flag.clone(), + ))) } .boxed_local(), ) @@ -647,21 +784,19 @@ impl CliFactory { )) } - pub async fn node_resolver(&self) -> Result<&Arc, AnyError> { + pub async fn node_resolver(&self) -> Result<&Arc, AnyError> { self .services .node_resolver .get_or_try_init_async( async { - Ok(Arc::new(NodeResolver::new( - DenoFsNodeResolverEnv::new(self.fs().clone()), + Ok(Arc::new(CliNodeResolver::new( self.in_npm_pkg_checker()?.clone(), - self - .npm_resolver() - .await? - .clone() - .into_npm_pkg_folder_resolver(), + RealIsBuiltInNodeModuleChecker, + self.npm_resolver().await?.clone(), self.pkg_json_resolver().clone(), + self.sys(), + node_resolver::ConditionsFromResolutionMode::default(), ))) } .boxed_local(), @@ -689,15 +824,11 @@ impl CliFactory { Ok(Arc::new(NodeCodeTranslator::new( cjs_esm_analyzer, - DenoFsNodeResolverEnv::new(self.fs().clone()), self.in_npm_pkg_checker()?.clone(), node_resolver, - self - .npm_resolver() - .await? - .clone() - .into_npm_pkg_folder_resolver(), + self.npm_resolver().await?.clone(), self.pkg_json_resolver().clone(), + self.sys(), ))) }) .await @@ -712,22 +843,20 @@ impl CliFactory { .get_or_try_init_async(async { let npm_resolver = self.npm_resolver().await?; Ok(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { - byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), - fs: CliDenoResolverFs(self.fs().clone()), + sys: self.sys(), in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(), node_resolver: self.node_resolver().await?.clone(), - npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), + npm_resolver: npm_resolver.clone(), }))) }) .await } - pub fn pkg_json_resolver(&self) -> &Arc { - self.services.pkg_json_resolver.get_or_init(|| { - Arc::new(PackageJsonResolver::new(DenoFsNodeResolverEnv::new( - self.fs().clone(), - ))) - }) + pub fn pkg_json_resolver(&self) -> &Arc { + self + .services + .pkg_json_resolver + .get_or_init(|| Arc::new(CliPackageJsonResolver::new(self.sys()))) } pub async fn type_checker(&self) -> Result<&Arc, AnyError> { @@ -745,7 +874,9 @@ impl CliFactory { cli_options.clone(), self.module_graph_builder().await?.clone(), self.node_resolver().await?.clone(), + self.npm_installer_if_managed()?.cloned(), self.npm_resolver().await?.clone(), + self.sys(), ))) }) .await @@ -764,16 +895,18 @@ impl CliFactory { self.cjs_tracker()?.clone(), cli_options.clone(), self.file_fetcher()?.clone(), - self.fs().clone(), self.global_http_cache()?.clone(), self.in_npm_pkg_checker()?.clone(), cli_options.maybe_lockfile().cloned(), self.maybe_file_watcher_reporter().clone(), self.module_info_cache()?.clone(), + self.npm_graph_resolver()?.clone(), + self.npm_installer_if_managed()?.cloned(), self.npm_resolver().await?.clone(), self.parsed_source_cache().clone(), self.resolver().await?.clone(), self.root_permissions_container()?.clone(), + self.sys(), ))) }) .await @@ -789,7 +922,7 @@ impl CliFactory { let cli_options = self.cli_options()?; Ok(Arc::new(ModuleGraphCreator::new( cli_options.clone(), - self.npm_resolver().await?.clone(), + self.npm_installer_if_managed()?.cloned(), self.module_graph_builder().await?.clone(), self.type_checker().await?.clone(), ))) @@ -844,10 +977,10 @@ impl CliFactory { .await } - pub fn cjs_tracker(&self) -> Result<&Arc, AnyError> { + pub fn cjs_tracker(&self) -> Result<&Arc, AnyError> { self.services.cjs_tracker.get_or_try_init(|| { let options = self.cli_options()?; - Ok(Arc::new(CjsTracker::new( + Ok(Arc::new(CliCjsTracker::new( self.in_npm_pkg_checker()?.clone(), self.pkg_json_resolver().clone(), if options.is_node_main() || options.unstable_detect_cjs() { @@ -863,10 +996,9 @@ impl CliFactory { pub fn permission_desc_parser( &self, - ) -> Result<&Arc, AnyError> { + ) -> Result<&Arc>, AnyError> { self.services.permission_desc_parser.get_or_try_init(|| { - let fs = self.fs().clone(); - Ok(Arc::new(RuntimePermissionDescriptorParser::new(fs))) + Ok(Arc::new(RuntimePermissionDescriptorParser::new(self.sys()))) }) } @@ -897,7 +1029,7 @@ impl CliFactory { self.emitter()?, self.file_fetcher()?, self.http_client_provider(), - self.npm_resolver().await?.as_ref(), + self.npm_resolver().await?, self.workspace_resolver().await?.as_ref(), cli_options.npm_system_info(), )) @@ -937,6 +1069,19 @@ impl CliFactory { let cjs_tracker = self.cjs_tracker()?.clone(); let pkg_json_resolver = self.pkg_json_resolver().clone(); let npm_req_resolver = self.npm_req_resolver().await?; + let npm_registry_permission_checker = { + let mode = if cli_options.use_byonm() { + NpmRegistryReadPermissionCheckerMode::Byonm + } else if let Some(node_modules_dir) = cli_options.node_modules_dir_path() + { + NpmRegistryReadPermissionCheckerMode::Local(node_modules_dir.clone()) + } else { + NpmRegistryReadPermissionCheckerMode::Global( + self.npm_cache_dir()?.root_dir().to_path_buf(), + ) + }; + Arc::new(NpmRegistryReadPermissionChecker::new(self.sys(), mode)) + }; Ok(CliMainWorkerFactory::new( self.blob_store().clone(), @@ -959,28 +1104,31 @@ impl CliFactory { None }, self.emitter()?.clone(), - fs.clone(), in_npm_pkg_checker.clone(), self.main_module_graph_container().await?.clone(), self.module_load_preparer().await?.clone(), node_code_translator.clone(), node_resolver.clone(), - npm_req_resolver.clone(), - cli_npm_resolver.clone(), NpmModuleLoader::new( self.cjs_tracker()?.clone(), fs.clone(), node_code_translator.clone(), ), + npm_registry_permission_checker, + npm_req_resolver.clone(), + cli_npm_resolver.clone(), self.parsed_source_cache().clone(), self.resolver().await?.clone(), + self.sys(), )), node_resolver.clone(), + self.npm_installer_if_managed()?.cloned(), npm_resolver.clone(), pkg_json_resolver, self.root_cert_store_provider().clone(), self.root_permissions_container()?.clone(), StorageKeyResolver::from_options(cli_options), + self.sys(), cli_options.sub_command().clone(), self.create_cli_main_worker_options()?, self.cli_options()?.otel_config(), diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 29f9c6ba3f..cfc26d7e69 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -1,41 +1,44 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::args::CacheSetting; -use crate::auth_tokens::AuthTokens; -use crate::cache::HttpCache; -use crate::colors; -use crate::http_util::CacheSemantics; -use crate::http_util::FetchOnceArgs; -use crate::http_util::FetchOnceResult; -use crate::http_util::HttpClientProvider; -use crate::util::progress_bar::ProgressBar; +use std::borrow::Cow; +use std::collections::HashMap; +use std::sync::Arc; +use boxed_error::Boxed; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::AuthTokens; +use deno_cache_dir::file_fetcher::BlobData; +use deno_cache_dir::file_fetcher::CacheSetting; +use deno_cache_dir::file_fetcher::FetchNoFollowError; +use deno_cache_dir::file_fetcher::File; +use deno_cache_dir::file_fetcher::FileFetcherOptions; +use deno_cache_dir::file_fetcher::FileOrRedirect; +use deno_cache_dir::file_fetcher::SendError; +use deno_cache_dir::file_fetcher::SendResponse; +use deno_cache_dir::file_fetcher::TooManyRedirectsError; +use deno_cache_dir::file_fetcher::UnsupportedSchemeError; use deno_core::anyhow::Context; -use deno_core::error::custom_error; -use deno_core::error::generic_error; -use deno_core::error::uri_error; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::url::Url; use deno_core::ModuleSpecifier; +use deno_error::JsError; use deno_graph::source::LoaderChecksum; - -use deno_path_util::url_to_file_path; +use deno_runtime::deno_permissions::CheckSpecifierKind; +use deno_runtime::deno_permissions::PermissionCheckError; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_web::BlobStore; use http::header; -use log::debug; -use std::borrow::Cow; -use std::collections::HashMap; -use std::env; -use std::fs; -use std::path::PathBuf; -use std::sync::Arc; -use std::time::SystemTime; +use http::HeaderMap; +use http::StatusCode; +use thiserror::Error; -pub const SUPPORTED_SCHEMES: [&str; 5] = - ["data", "blob", "file", "http", "https"]; +use crate::cache::HttpCache; +use crate::colors; +use crate::http_util::get_response_body_with_progress; +use crate::http_util::HttpClientProvider; +use crate::sys::CliSys; +use crate::util::progress_bar::ProgressBar; #[derive(Debug, Clone, Eq, PartialEq)] pub struct TextDecodedFile { @@ -47,62 +50,19 @@ pub struct TextDecodedFile { pub source: Arc, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum FileOrRedirect { - File(File), - Redirect(ModuleSpecifier), -} - -impl FileOrRedirect { - fn from_deno_cache_entry( - specifier: &ModuleSpecifier, - cache_entry: deno_cache_dir::CacheEntry, - ) -> Result { - if let Some(redirect_to) = cache_entry.metadata.headers.get("location") { - let redirect = specifier.join(redirect_to)?; - Ok(FileOrRedirect::Redirect(redirect)) - } else { - Ok(FileOrRedirect::File(File { - specifier: specifier.clone(), - maybe_headers: Some(cache_entry.metadata.headers), - source: Arc::from(cache_entry.content), - })) - } - } -} - -/// A structure representing a source file. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct File { - /// The _final_ specifier for the file. The requested specifier and the final - /// specifier maybe different for remote files that have been redirected. - pub specifier: ModuleSpecifier, - pub maybe_headers: Option>, - /// The source of the file. - pub source: Arc<[u8]>, -} - -impl File { - pub fn resolve_media_type_and_charset(&self) -> (MediaType, Option<&str>) { - deno_graph::source::resolve_media_type_and_charset_from_headers( - &self.specifier, - self.maybe_headers.as_ref(), - ) - } - +impl TextDecodedFile { /// Decodes the source bytes into a string handling any encoding rules /// for local vs remote files and dealing with the charset. - pub fn into_text_decoded(self) -> Result { - // lots of borrow checker fighting here + pub fn decode(file: File) -> Result { let (media_type, maybe_charset) = deno_graph::source::resolve_media_type_and_charset_from_headers( - &self.specifier, - self.maybe_headers.as_ref(), + &file.url, + file.maybe_headers.as_ref(), ); - let specifier = self.specifier; + let specifier = file.url; match deno_graph::source::decode_source( &specifier, - self.source, + file.source, maybe_charset, ) { Ok(source) => Ok(TextDecodedFile { @@ -117,14 +77,146 @@ impl File { } } -#[derive(Debug, Clone, Default)] -struct MemoryFiles(Arc>>); +#[derive(Debug)] +struct BlobStoreAdapter(Arc); + +#[async_trait::async_trait(?Send)] +impl deno_cache_dir::file_fetcher::BlobStore for BlobStoreAdapter { + async fn get(&self, specifier: &Url) -> std::io::Result> { + let Some(blob) = self.0.get_object_url(specifier.clone()) else { + return Ok(None); + }; + Ok(Some(BlobData { + media_type: blob.media_type.clone(), + bytes: blob.read_all().await, + })) + } +} + +#[derive(Debug)] +struct HttpClientAdapter { + http_client_provider: Arc, + download_log_level: log::Level, + progress_bar: Option, +} + +#[async_trait::async_trait(?Send)] +impl deno_cache_dir::file_fetcher::HttpClient for HttpClientAdapter { + async fn send_no_follow( + &self, + url: &Url, + headers: HeaderMap, + ) -> Result { + async fn handle_request_or_server_error( + retried: &mut bool, + specifier: &Url, + err_str: String, + ) -> Result<(), ()> { + // Retry once, and bail otherwise. + if !*retried { + *retried = true; + log::debug!("Import '{}' failed: {}. Retrying...", specifier, err_str); + tokio::time::sleep(std::time::Duration::from_millis(50)).await; + Ok(()) + } else { + Err(()) + } + } + + let mut maybe_progress_guard = None; + if let Some(pb) = self.progress_bar.as_ref() { + maybe_progress_guard = Some(pb.update(url.as_str())); + } else { + log::log!( + self.download_log_level, + "{} {}", + colors::green("Download"), + url + ); + } + + let mut retried = false; // retry intermittent failures + loop { + let response = match self + .http_client_provider + .get_or_create() + .map_err(|err| SendError::Failed(err.into()))? + .send(url, headers.clone()) + .await + { + Ok(response) => response, + Err(crate::http_util::SendError::Send(err)) => { + if err.is_connect_error() { + handle_request_or_server_error(&mut retried, url, err.to_string()) + .await + .map_err(|()| SendError::Failed(err.into()))?; + continue; + } else { + return Err(SendError::Failed(err.into())); + } + } + Err(crate::http_util::SendError::InvalidUri(err)) => { + return Err(SendError::Failed(err.into())); + } + }; + if response.status() == StatusCode::NOT_MODIFIED { + return Ok(SendResponse::NotModified); + } + + if let Some(warning) = response.headers().get("X-Deno-Warning") { + log::warn!( + "{} {}", + crate::colors::yellow("Warning"), + warning.to_str().unwrap() + ); + } + + if response.status().is_redirection() { + return Ok(SendResponse::Redirect(response.into_parts().0.headers)); + } + + if response.status().is_server_error() { + handle_request_or_server_error( + &mut retried, + url, + response.status().to_string(), + ) + .await + .map_err(|()| SendError::StatusCode(response.status()))?; + } else if response.status().is_client_error() { + let err = if response.status() == StatusCode::NOT_FOUND { + SendError::NotFound + } else { + SendError::StatusCode(response.status()) + }; + return Err(err); + } else { + let body_result = get_response_body_with_progress( + response, + maybe_progress_guard.as_ref(), + ) + .await; + + match body_result { + Ok((headers, body)) => { + return Ok(SendResponse::Success(headers, body)); + } + Err(err) => { + handle_request_or_server_error(&mut retried, url, err.to_string()) + .await + .map_err(|()| SendError::Failed(err.into()))?; + continue; + } + } + } + } + } +} + +#[derive(Debug, Default)] +struct MemoryFiles(Mutex>); impl MemoryFiles { - pub fn get(&self, specifier: &ModuleSpecifier) -> Option { - self.0.lock().get(specifier).cloned() - } - pub fn insert(&self, specifier: ModuleSpecifier, file: File) -> Option { self.0.lock().insert(specifier, file) } @@ -134,416 +226,96 @@ impl MemoryFiles { } } -/// Fetch a source file from the local file system. -fn fetch_local(specifier: &ModuleSpecifier) -> Result { - let local = url_to_file_path(specifier).map_err(|_| { - uri_error(format!("Invalid file path.\n Specifier: {specifier}")) - })?; - // If it doesnt have a extension, we want to treat it as typescript by default - let headers = if local.extension().is_none() { - Some(HashMap::from([( - "content-type".to_string(), - "application/typescript".to_string(), - )])) - } else { - None - }; - let bytes = fs::read(local)?; - - Ok(File { - specifier: specifier.clone(), - maybe_headers: headers, - source: bytes.into(), - }) +impl deno_cache_dir::file_fetcher::MemoryFiles for MemoryFiles { + fn get(&self, specifier: &ModuleSpecifier) -> Option { + self.0.lock().get(specifier).cloned() + } } -/// Return a validated scheme for a given module specifier. -fn get_validated_scheme( - specifier: &ModuleSpecifier, -) -> Result { - let scheme = specifier.scheme(); - if !SUPPORTED_SCHEMES.contains(&scheme) { - // NOTE(bartlomieju): this message list additional `npm` and `jsr` schemes, but they should actually be handled - // before `file_fetcher.rs` APIs are even hit. - let mut all_supported_schemes = SUPPORTED_SCHEMES.to_vec(); - all_supported_schemes.extend_from_slice(&["npm", "jsr"]); - all_supported_schemes.sort(); - let scheme_list = all_supported_schemes - .iter() - .map(|scheme| format!(" - \"{}\"", scheme)) - .collect::>() - .join("\n"); - Err(generic_error(format!( - "Unsupported scheme \"{scheme}\" for module \"{specifier}\". Supported schemes:\n{}", - scheme_list - ))) - } else { - Ok(scheme.to_string()) - } +#[derive(Debug, Boxed, JsError)] +pub struct CliFetchNoFollowError(pub Box); + +#[derive(Debug, Error, JsError)] +pub enum CliFetchNoFollowErrorKind { + #[error(transparent)] + #[class(inherit)] + FetchNoFollow(#[from] FetchNoFollowError), + #[error(transparent)] + #[class(generic)] + PermissionCheck(#[from] PermissionCheckError), } #[derive(Debug, Copy, Clone)] pub enum FetchPermissionsOptionRef<'a> { AllowAll, - DynamicContainer(&'a PermissionsContainer), - StaticContainer(&'a PermissionsContainer), + Restricted(&'a PermissionsContainer, CheckSpecifierKind), } +#[derive(Debug, Default)] pub struct FetchOptions<'a> { - pub specifier: &'a ModuleSpecifier, - pub permissions: FetchPermissionsOptionRef<'a>, pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, pub maybe_accept: Option<&'a str>, pub maybe_cache_setting: Option<&'a CacheSetting>, } pub struct FetchNoFollowOptions<'a> { - pub fetch_options: FetchOptions<'a>, - /// This setting doesn't make sense to provide for `FetchOptions` - /// since the required checksum may change for a redirect. + pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, + pub maybe_accept: Option<&'a str>, + pub maybe_cache_setting: Option<&'a CacheSetting>, pub maybe_checksum: Option<&'a LoaderChecksum>, } +type DenoCacheDirFileFetcher = deno_cache_dir::file_fetcher::FileFetcher< + BlobStoreAdapter, + CliSys, + HttpClientAdapter, +>; + /// A structure for resolving, fetching and caching source files. #[derive(Debug)] -pub struct FileFetcher { - auth_tokens: AuthTokens, - allow_remote: bool, - memory_files: MemoryFiles, - cache_setting: CacheSetting, - http_cache: Arc, - http_client_provider: Arc, - blob_store: Arc, - download_log_level: log::Level, - progress_bar: Option, +pub struct CliFileFetcher { + file_fetcher: DenoCacheDirFileFetcher, + memory_files: Arc, } -impl FileFetcher { +impl CliFileFetcher { + #[allow(clippy::too_many_arguments)] pub fn new( http_cache: Arc, - cache_setting: CacheSetting, - allow_remote: bool, http_client_provider: Arc, + sys: CliSys, blob_store: Arc, progress_bar: Option, + allow_remote: bool, + cache_setting: CacheSetting, + download_log_level: log::Level, ) -> Self { - Self { - auth_tokens: AuthTokens::new(env::var("DENO_AUTH_TOKENS").ok()), - allow_remote, - memory_files: Default::default(), - cache_setting, + let memory_files = Arc::new(MemoryFiles::default()); + let auth_tokens = AuthTokens::new_from_sys(&sys); + let file_fetcher = DenoCacheDirFileFetcher::new( + BlobStoreAdapter(blob_store), + sys, http_cache, - http_client_provider, - blob_store, - download_log_level: log::Level::Info, - progress_bar, + HttpClientAdapter { + http_client_provider: http_client_provider.clone(), + download_log_level, + progress_bar, + }, + memory_files.clone(), + FileFetcherOptions { + allow_remote, + cache_setting, + auth_tokens, + }, + ); + Self { + file_fetcher, + memory_files, } } pub fn cache_setting(&self) -> &CacheSetting { - &self.cache_setting - } - - /// Sets the log level to use when outputting the download message. - pub fn set_download_log_level(&mut self, level: log::Level) { - self.download_log_level = level; - } - - /// Fetch cached remote file. - /// - /// This is a recursive operation if source file has redirections. - pub fn fetch_cached( - &self, - specifier: &ModuleSpecifier, - redirect_limit: i64, - ) -> Result, AnyError> { - let mut specifier = Cow::Borrowed(specifier); - for _ in 0..=redirect_limit { - match self.fetch_cached_no_follow(&specifier, None)? { - Some(FileOrRedirect::File(file)) => { - return Ok(Some(file)); - } - Some(FileOrRedirect::Redirect(redirect_specifier)) => { - specifier = Cow::Owned(redirect_specifier); - } - None => { - return Ok(None); - } - } - } - Err(custom_error("Http", "Too many redirects.")) - } - - fn fetch_cached_no_follow( - &self, - specifier: &ModuleSpecifier, - maybe_checksum: Option<&LoaderChecksum>, - ) -> Result, AnyError> { - debug!( - "FileFetcher::fetch_cached_no_follow - specifier: {}", - specifier - ); - - let cache_key = self.http_cache.cache_item_key(specifier)?; // compute this once - let result = self.http_cache.get( - &cache_key, - maybe_checksum - .as_ref() - .map(|c| deno_cache_dir::Checksum::new(c.as_str())), - ); - match result { - Ok(Some(cache_data)) => Ok(Some(FileOrRedirect::from_deno_cache_entry( - specifier, cache_data, - )?)), - Ok(None) => Ok(None), - Err(err) => match err { - deno_cache_dir::CacheReadFileError::Io(err) => Err(err.into()), - deno_cache_dir::CacheReadFileError::ChecksumIntegrity(err) => { - // convert to the equivalent deno_graph error so that it - // enhances it if this is passed to deno_graph - Err( - deno_graph::source::ChecksumIntegrityError { - actual: err.actual, - expected: err.expected, - } - .into(), - ) - } - }, - } - } - - /// Convert a data URL into a file, resulting in an error if the URL is - /// invalid. - fn fetch_data_url( - &self, - specifier: &ModuleSpecifier, - ) -> Result { - debug!("FileFetcher::fetch_data_url() - specifier: {}", specifier); - let data_url = deno_graph::source::RawDataUrl::parse(specifier)?; - let (bytes, headers) = data_url.into_bytes_and_headers(); - Ok(File { - specifier: specifier.clone(), - maybe_headers: Some(headers), - source: Arc::from(bytes), - }) - } - - /// Get a blob URL. - async fn fetch_blob_url( - &self, - specifier: &ModuleSpecifier, - ) -> Result { - debug!("FileFetcher::fetch_blob_url() - specifier: {}", specifier); - let blob = self - .blob_store - .get_object_url(specifier.clone()) - .ok_or_else(|| { - custom_error( - "NotFound", - format!("Blob URL not found: \"{specifier}\"."), - ) - })?; - - let bytes = blob.read_all().await; - let headers = - HashMap::from([("content-type".to_string(), blob.media_type.clone())]); - - Ok(File { - specifier: specifier.clone(), - maybe_headers: Some(headers), - source: Arc::from(bytes), - }) - } - - async fn fetch_remote_no_follow( - &self, - specifier: &ModuleSpecifier, - maybe_accept: Option<&str>, - cache_setting: &CacheSetting, - maybe_checksum: Option<&LoaderChecksum>, - maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, - ) -> Result { - debug!( - "FileFetcher::fetch_remote_no_follow - specifier: {}", - specifier - ); - - if self.should_use_cache(specifier, cache_setting) { - if let Some(file_or_redirect) = - self.fetch_cached_no_follow(specifier, maybe_checksum)? - { - return Ok(file_or_redirect); - } - } - - if *cache_setting == CacheSetting::Only { - return Err(custom_error( - "NotCached", - format!( - "Specifier not found in cache: \"{specifier}\", --cached-only is specified." - ), - )); - } - - let mut maybe_progress_guard = None; - if let Some(pb) = self.progress_bar.as_ref() { - maybe_progress_guard = Some(pb.update(specifier.as_str())); - } else { - log::log!( - self.download_log_level, - "{} {}", - colors::green("Download"), - specifier - ); - } - - let maybe_etag_cache_entry = self - .http_cache - .cache_item_key(specifier) - .ok() - .and_then(|key| { - self - .http_cache - .get( - &key, - maybe_checksum - .as_ref() - .map(|c| deno_cache_dir::Checksum::new(c.as_str())), - ) - .ok() - .flatten() - }) - .and_then(|cache_entry| { - cache_entry - .metadata - .headers - .get("etag") - .cloned() - .map(|etag| (cache_entry, etag)) - }); - let maybe_auth_token = self.auth_tokens.get(specifier); - - async fn handle_request_or_server_error( - retried: &mut bool, - specifier: &Url, - err_str: String, - ) -> Result<(), AnyError> { - // Retry once, and bail otherwise. - if !*retried { - *retried = true; - log::debug!("Import '{}' failed: {}. Retrying...", specifier, err_str); - tokio::time::sleep(std::time::Duration::from_millis(50)).await; - Ok(()) - } else { - Err(generic_error(format!( - "Import '{}' failed: {}", - specifier, err_str - ))) - } - } - - let mut retried = false; // retry intermittent failures - let result = loop { - let result = match self - .http_client_provider - .get_or_create()? - .fetch_no_follow(FetchOnceArgs { - url: specifier.clone(), - maybe_accept: maybe_accept.map(ToOwned::to_owned), - maybe_etag: maybe_etag_cache_entry - .as_ref() - .map(|(_, etag)| etag.clone()), - maybe_auth_token: maybe_auth_token.clone(), - maybe_auth: maybe_auth.clone(), - maybe_progress_guard: maybe_progress_guard.as_ref(), - }) - .await? - { - FetchOnceResult::NotModified => { - let (cache_entry, _) = maybe_etag_cache_entry.unwrap(); - FileOrRedirect::from_deno_cache_entry(specifier, cache_entry) - } - FetchOnceResult::Redirect(redirect_url, headers) => { - self.http_cache.set(specifier, headers, &[])?; - Ok(FileOrRedirect::Redirect(redirect_url)) - } - FetchOnceResult::Code(bytes, headers) => { - self.http_cache.set(specifier, headers.clone(), &bytes)?; - if let Some(checksum) = &maybe_checksum { - checksum.check_source(&bytes)?; - } - Ok(FileOrRedirect::File(File { - specifier: specifier.clone(), - maybe_headers: Some(headers), - source: Arc::from(bytes), - })) - } - FetchOnceResult::RequestError(err) => { - handle_request_or_server_error(&mut retried, specifier, err).await?; - continue; - } - FetchOnceResult::ServerError(status) => { - handle_request_or_server_error( - &mut retried, - specifier, - status.to_string(), - ) - .await?; - continue; - } - }; - break result; - }; - - drop(maybe_progress_guard); - result - } - - /// Returns if the cache should be used for a given specifier. - fn should_use_cache( - &self, - specifier: &ModuleSpecifier, - cache_setting: &CacheSetting, - ) -> bool { - match cache_setting { - CacheSetting::ReloadAll => false, - CacheSetting::Use | CacheSetting::Only => true, - CacheSetting::RespectHeaders => { - let Ok(cache_key) = self.http_cache.cache_item_key(specifier) else { - return false; - }; - let Ok(Some(headers)) = self.http_cache.read_headers(&cache_key) else { - return false; - }; - let Ok(Some(download_time)) = - self.http_cache.read_download_time(&cache_key) - else { - return false; - }; - let cache_semantics = - CacheSemantics::new(headers, download_time, SystemTime::now()); - cache_semantics.should_use() - } - CacheSetting::ReloadSome(list) => { - let mut url = specifier.clone(); - url.set_fragment(None); - if list.iter().any(|x| x == url.as_str()) { - return false; - } - url.set_query(None); - let mut path = PathBuf::from(url.as_str()); - loop { - if list.contains(&path.to_str().unwrap().to_string()) { - return false; - } - if !path.pop() { - break; - } - } - true - } - } + self.file_fetcher.cache_setting() } #[inline(always)] @@ -578,7 +350,10 @@ impl FileFetcher { .fetch_inner( specifier, None, - FetchPermissionsOptionRef::StaticContainer(permissions), + FetchPermissionsOptionRef::Restricted( + permissions, + CheckSpecifierKind::Static, + ), ) .await } @@ -590,42 +365,50 @@ impl FileFetcher { permissions: FetchPermissionsOptionRef<'_>, ) -> Result { self - .fetch_with_options(FetchOptions { + .fetch_with_options( specifier, permissions, - maybe_auth, - maybe_accept: None, - maybe_cache_setting: None, - }) + FetchOptions { + maybe_auth, + maybe_accept: None, + maybe_cache_setting: None, + }, + ) .await } pub async fn fetch_with_options( &self, + specifier: &ModuleSpecifier, + permissions: FetchPermissionsOptionRef<'_>, options: FetchOptions<'_>, ) -> Result { - self.fetch_with_options_and_max_redirect(options, 10).await + self + .fetch_with_options_and_max_redirect(specifier, permissions, options, 10) + .await } async fn fetch_with_options_and_max_redirect( &self, + specifier: &ModuleSpecifier, + permissions: FetchPermissionsOptionRef<'_>, options: FetchOptions<'_>, max_redirect: usize, ) -> Result { - let mut specifier = Cow::Borrowed(options.specifier); - let mut maybe_auth = options.maybe_auth.clone(); + let mut specifier = Cow::Borrowed(specifier); + let mut maybe_auth = options.maybe_auth; for _ in 0..=max_redirect { match self - .fetch_no_follow_with_options(FetchNoFollowOptions { - fetch_options: FetchOptions { - specifier: &specifier, - permissions: options.permissions, + .fetch_no_follow( + &specifier, + permissions, + FetchNoFollowOptions { maybe_auth: maybe_auth.clone(), maybe_accept: options.maybe_accept, maybe_cache_setting: options.maybe_cache_setting, + maybe_checksum: None, }, - maybe_checksum: None, - }) + ) .await? { FileOrRedirect::File(file) => { @@ -641,92 +424,61 @@ impl FileFetcher { } } - Err(custom_error("Http", "Too many redirects.")) + Err(TooManyRedirectsError(specifier.into_owned()).into()) } /// Fetches without following redirects. - pub async fn fetch_no_follow_with_options( + pub async fn fetch_no_follow( &self, + specifier: &ModuleSpecifier, + permissions: FetchPermissionsOptionRef<'_>, options: FetchNoFollowOptions<'_>, - ) -> Result { - let maybe_checksum = options.maybe_checksum; - let options = options.fetch_options; - let specifier = options.specifier; - // note: this debug output is used by the tests - debug!( - "FileFetcher::fetch_no_follow_with_options - specifier: {}", - specifier - ); - let scheme = get_validated_scheme(specifier)?; - match options.permissions { + ) -> Result { + validate_scheme(specifier).map_err(|err| { + CliFetchNoFollowErrorKind::FetchNoFollow(err.into()).into_box() + })?; + match permissions { FetchPermissionsOptionRef::AllowAll => { // allow } - FetchPermissionsOptionRef::StaticContainer(permissions) => { - permissions.check_specifier( - specifier, - deno_runtime::deno_permissions::CheckSpecifierKind::Static, - )?; - } - FetchPermissionsOptionRef::DynamicContainer(permissions) => { - permissions.check_specifier( - specifier, - deno_runtime::deno_permissions::CheckSpecifierKind::Dynamic, - )?; + FetchPermissionsOptionRef::Restricted(permissions, kind) => { + permissions.check_specifier(specifier, kind)?; } } - if let Some(file) = self.memory_files.get(specifier) { - Ok(FileOrRedirect::File(file)) - } else if scheme == "file" { - // we do not in memory cache files, as this would prevent files on the - // disk changing effecting things like workers and dynamic imports. - fetch_local(specifier).map(FileOrRedirect::File) - } else if scheme == "data" { - self.fetch_data_url(specifier).map(FileOrRedirect::File) - } else if scheme == "blob" { - self - .fetch_blob_url(specifier) - .await - .map(FileOrRedirect::File) - } else if !self.allow_remote { - Err(custom_error( - "NoRemote", - format!("A remote specifier was requested: \"{specifier}\", but --no-remote is specified."), - )) - } else { - self - .fetch_remote_no_follow( - specifier, - options.maybe_accept, - options.maybe_cache_setting.unwrap_or(&self.cache_setting), - maybe_checksum, - options.maybe_auth, - ) - .await - } + self + .file_fetcher + .fetch_no_follow( + specifier, + deno_cache_dir::file_fetcher::FetchNoFollowOptions { + maybe_auth: options.maybe_auth, + maybe_checksum: options + .maybe_checksum + .map(|c| deno_cache_dir::Checksum::new(c.as_str())), + maybe_accept: options.maybe_accept, + maybe_cache_setting: options.maybe_cache_setting, + }, + ) + .await + .map_err(|err| CliFetchNoFollowErrorKind::FetchNoFollow(err).into_box()) } /// A synchronous way to retrieve a source file, where if the file has already /// been cached in memory it will be returned, otherwise for local files will /// be read from disk. - pub fn get_source(&self, specifier: &ModuleSpecifier) -> Option { - let maybe_file = self.memory_files.get(specifier); - if maybe_file.is_none() { - let is_local = specifier.scheme() == "file"; - if is_local { - if let Ok(file) = fetch_local(specifier) { - return Some(file); - } - } - None + pub fn get_cached_source_or_local( + &self, + specifier: &ModuleSpecifier, + ) -> Result, AnyError> { + if specifier.scheme() == "file" { + Ok(self.file_fetcher.fetch_local(specifier)?) } else { - maybe_file + Ok(self.file_fetcher.fetch_cached(specifier, 10)?) } } /// Insert a temporary module for the file fetcher. pub fn insert_memory_files(&self, file: File) -> Option { - self.memory_files.insert(file.specifier.clone(), file) + self.memory_files.insert(file.url.clone(), file) } pub fn clear_memory_files(&self) { @@ -734,23 +486,33 @@ impl FileFetcher { } } +fn validate_scheme(specifier: &Url) -> Result<(), UnsupportedSchemeError> { + match deno_cache_dir::file_fetcher::is_valid_scheme(specifier.scheme()) { + true => Ok(()), + false => Err(UnsupportedSchemeError { + scheme: specifier.scheme().to_string(), + url: specifier.clone(), + }), + } +} + #[cfg(test)] mod tests { - use crate::cache::GlobalHttpCache; - use crate::cache::RealDenoCacheEnv; - use crate::http_util::HttpClientProvider; - - use super::*; - use deno_core::error::get_custom_error_class; + use deno_cache_dir::file_fetcher::FetchNoFollowErrorKind; + use deno_cache_dir::file_fetcher::HttpClient; use deno_core::resolve_url; use deno_runtime::deno_web::Blob; use deno_runtime::deno_web::InMemoryBlobPart; use test_util::TempDir; + use super::*; + use crate::cache::GlobalHttpCache; + use crate::http_util::HttpClientProvider; + fn setup( cache_setting: CacheSetting, maybe_temp_dir: Option, - ) -> (FileFetcher, TempDir) { + ) -> (CliFileFetcher, TempDir) { let (file_fetcher, temp_dir, _) = setup_with_blob_store(cache_setting, maybe_temp_dir); (file_fetcher, temp_dir) @@ -759,22 +521,39 @@ mod tests { fn setup_with_blob_store( cache_setting: CacheSetting, maybe_temp_dir: Option, - ) -> (FileFetcher, TempDir, Arc) { - let temp_dir = maybe_temp_dir.unwrap_or_default(); - let location = temp_dir.path().join("remote").to_path_buf(); - let blob_store: Arc = Default::default(); - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)), - cache_setting, - true, - Arc::new(HttpClientProvider::new(None, None)), - blob_store.clone(), - None, - ); + ) -> (CliFileFetcher, TempDir, Arc) { + let (file_fetcher, temp_dir, blob_store, _) = + setup_with_blob_store_and_cache(cache_setting, maybe_temp_dir); (file_fetcher, temp_dir, blob_store) } - async fn test_fetch(specifier: &ModuleSpecifier) -> (File, FileFetcher) { + fn setup_with_blob_store_and_cache( + cache_setting: CacheSetting, + maybe_temp_dir: Option, + ) -> ( + CliFileFetcher, + TempDir, + Arc, + Arc, + ) { + let temp_dir = maybe_temp_dir.unwrap_or_default(); + let location = temp_dir.path().join("remote").to_path_buf(); + let blob_store: Arc = Default::default(); + let cache = Arc::new(GlobalHttpCache::new(CliSys::default(), location)); + let file_fetcher = CliFileFetcher::new( + cache.clone(), + Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), + blob_store.clone(), + None, + true, + cache_setting, + log::Level::Info, + ); + (file_fetcher, temp_dir, blob_store, cache) + } + + async fn test_fetch(specifier: &ModuleSpecifier) -> (File, CliFileFetcher) { let (file_fetcher, _) = setup(CacheSetting::ReloadAll, None); let result = file_fetcher.fetch_bypass_permissions(specifier).await; assert!(result.is_ok()); @@ -785,27 +564,20 @@ mod tests { specifier: &ModuleSpecifier, ) -> (File, HashMap) { let _http_server_guard = test_util::http_server(); - let (file_fetcher, _) = setup(CacheSetting::ReloadAll, None); + let (file_fetcher, _, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::ReloadAll, None); let result: Result = file_fetcher .fetch_with_options_and_max_redirect( - FetchOptions { - specifier, - permissions: FetchPermissionsOptionRef::AllowAll, - maybe_auth: None, - maybe_accept: None, - maybe_cache_setting: Some(&file_fetcher.cache_setting), - }, + specifier, + FetchPermissionsOptionRef::AllowAll, + Default::default(), 1, ) .await; - let cache_key = file_fetcher.http_cache.cache_item_key(specifier).unwrap(); + let cache_key = http_cache.cache_item_key(specifier).unwrap(); ( result.unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), ) } @@ -850,28 +622,6 @@ mod tests { ); } - #[test] - fn test_get_validated_scheme() { - let fixtures = vec![ - ("https://deno.land/x/mod.ts", true, "https"), - ("http://deno.land/x/mod.ts", true, "http"), - ("file:///a/b/c.ts", true, "file"), - ("file:///C:/a/b/c.ts", true, "file"), - ("data:,some%20text", true, "data"), - ("ftp://a/b/c.ts", false, ""), - ("mailto:dino@deno.land", false, ""), - ]; - - for (specifier, is_ok, expected) in fixtures { - let specifier = ModuleSpecifier::parse(specifier).unwrap(); - let actual = get_validated_scheme(&specifier); - assert_eq!(actual.is_ok(), is_ok); - if is_ok { - assert_eq!(actual.unwrap(), expected); - } - } - } - #[tokio::test] async fn test_insert_cached() { let (file_fetcher, temp_dir) = setup(CacheSetting::Use, None); @@ -879,7 +629,7 @@ mod tests { let specifier = ModuleSpecifier::from_file_path(&local).unwrap(); let file = File { source: Arc::from("some source code".as_bytes()), - specifier: specifier.clone(), + url: specifier.clone(), maybe_headers: Some(HashMap::from([( "content-type".to_string(), "application/javascript".to_string(), @@ -900,7 +650,7 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export const a = \"a\";\n\nexport enum A {\n A,\n B,\n C,\n}\n" @@ -929,7 +679,7 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export const a = \"a\";\n\nexport enum A {\n A,\n B,\n C,\n}\n" @@ -941,33 +691,36 @@ mod tests { #[tokio::test] async fn test_fetch_complex() { let _http_server_guard = test_util::http_server(); - let (file_fetcher, temp_dir) = setup(CacheSetting::Use, None); + let (file_fetcher, temp_dir, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::Use, None); let (file_fetcher_01, _) = setup(CacheSetting::Use, Some(temp_dir.clone())); - let (file_fetcher_02, _) = setup(CacheSetting::Use, Some(temp_dir.clone())); + let (file_fetcher_02, _, _, http_cache_02) = + setup_with_blob_store_and_cache( + CacheSetting::Use, + Some(temp_dir.clone()), + ); let specifier = ModuleSpecifier::parse("http://localhost:4545/subdir/mod2.ts").unwrap(); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export { printHello } from \"./print_hello.ts\";\n" ); assert_eq!(file.media_type, MediaType::TypeScript); - let cache_item_key = - file_fetcher.http_cache.cache_item_key(&specifier).unwrap(); + let cache_item_key = http_cache.cache_item_key(&specifier).unwrap(); let mut headers = HashMap::new(); headers.insert("content-type".to_string(), "text/javascript".to_string()); - file_fetcher - .http_cache + http_cache .set(&specifier, headers.clone(), file.source.as_bytes()) .unwrap(); let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export { printHello } from \"./print_hello.ts\";\n" @@ -976,22 +729,20 @@ mod tests { // the value above. assert_eq!(file.media_type, MediaType::JavaScript); - let headers2 = file_fetcher_02 - .http_cache + let headers2 = http_cache_02 .read_headers(&cache_item_key) .unwrap() .unwrap(); assert_eq!(headers2.get("content-type").unwrap(), "text/javascript"); headers = HashMap::new(); headers.insert("content-type".to_string(), "application/json".to_string()); - file_fetcher_02 - .http_cache + http_cache_02 .set(&specifier, headers.clone(), file.source.as_bytes()) .unwrap(); let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export { printHello } from \"./print_hello.ts\";\n" @@ -1001,20 +752,19 @@ mod tests { // This creates a totally new instance, simulating another Deno process // invocation and indicates to "cache bust". let location = temp_dir.path().join("remote").to_path_buf(); - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new( - location, - crate::cache::RealDenoCacheEnv, - )), - CacheSetting::ReloadAll, - true, + let file_fetcher = CliFileFetcher::new( + Arc::new(GlobalHttpCache::new(CliSys::default(), location)), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, + true, + CacheSetting::ReloadAll, + log::Level::Info, ); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export { printHello } from \"./print_hello.ts\";\n" @@ -1030,73 +780,49 @@ mod tests { let specifier = resolve_url("http://localhost:4545/subdir/mismatch_ext.ts").unwrap(); + let http_cache = + Arc::new(GlobalHttpCache::new(CliSys::default(), location.clone())); let file_modified_01 = { - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new( - location.clone(), - crate::cache::RealDenoCacheEnv, - )), - CacheSetting::Use, - true, + let file_fetcher = CliFileFetcher::new( + http_cache.clone(), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let cache_key = - file_fetcher.http_cache.cache_item_key(&specifier).unwrap(); + let cache_key = http_cache.cache_item_key(&specifier).unwrap(); ( - file_fetcher - .http_cache - .read_modified_time(&cache_key) - .unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), - file_fetcher - .http_cache - .read_download_time(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_modified_time(&cache_key).unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), + http_cache.read_download_time(&cache_key).unwrap().unwrap(), ) }; let file_modified_02 = { - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new( - location, - crate::cache::RealDenoCacheEnv, - )), - CacheSetting::Use, - true, + let file_fetcher = CliFileFetcher::new( + Arc::new(GlobalHttpCache::new(CliSys::default(), location)), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let cache_key = - file_fetcher.http_cache.cache_item_key(&specifier).unwrap(); + let cache_key = http_cache.cache_item_key(&specifier).unwrap(); ( - file_fetcher - .http_cache - .read_modified_time(&cache_key) - .unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), - file_fetcher - .http_cache - .read_download_time(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_modified_time(&cache_key).unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), + http_cache.read_download_time(&cache_key).unwrap().unwrap(), ) }; @@ -1106,7 +832,8 @@ mod tests { #[tokio::test] async fn test_fetch_redirected() { let _http_server_guard = test_util::http_server(); - let (file_fetcher, _) = setup(CacheSetting::Use, None); + let (file_fetcher, _, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::Use, None); let specifier = resolve_url("http://localhost:4546/subdir/redirects/redirect1.js") .unwrap(); @@ -1117,24 +844,27 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); let file = result.unwrap(); - assert_eq!(file.specifier, redirected_specifier); + assert_eq!(file.url, redirected_specifier); assert_eq!( - get_text_from_cache(&file_fetcher, &specifier), + get_text_from_cache(http_cache.as_ref(), &specifier), "", "redirected files should have empty cached contents" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &specifier), + get_location_header_from_cache(http_cache.as_ref(), &specifier), Some("http://localhost:4545/subdir/redirects/redirect1.js".to_string()), ); assert_eq!( - get_text_from_cache(&file_fetcher, &redirected_specifier), + get_text_from_cache(http_cache.as_ref(), &redirected_specifier), "export const redirect = 1;\n" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &redirected_specifier), + get_location_header_from_cache( + http_cache.as_ref(), + &redirected_specifier + ), None, ); } @@ -1142,7 +872,8 @@ mod tests { #[tokio::test] async fn test_fetch_multiple_redirects() { let _http_server_guard = test_util::http_server(); - let (file_fetcher, _) = setup(CacheSetting::Use, None); + let (file_fetcher, _, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::Use, None); let specifier = resolve_url("http://localhost:4548/subdir/redirects/redirect1.js") .unwrap(); @@ -1156,34 +887,40 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); let file = result.unwrap(); - assert_eq!(file.specifier, redirected_02_specifier); + assert_eq!(file.url, redirected_02_specifier); assert_eq!( - get_text_from_cache(&file_fetcher, &specifier), + get_text_from_cache(http_cache.as_ref(), &specifier), "", "redirected files should have empty cached contents" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &specifier), + get_location_header_from_cache(http_cache.as_ref(), &specifier), Some("http://localhost:4546/subdir/redirects/redirect1.js".to_string()), ); assert_eq!( - get_text_from_cache(&file_fetcher, &redirected_01_specifier), + get_text_from_cache(http_cache.as_ref(), &redirected_01_specifier), "", "redirected files should have empty cached contents" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &redirected_01_specifier), + get_location_header_from_cache( + http_cache.as_ref(), + &redirected_01_specifier + ), Some("http://localhost:4545/subdir/redirects/redirect1.js".to_string()), ); assert_eq!( - get_text_from_cache(&file_fetcher, &redirected_02_specifier), + get_text_from_cache(http_cache.as_ref(), &redirected_02_specifier), "export const redirect = 1;\n" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &redirected_02_specifier), + get_location_header_from_cache( + http_cache.as_ref(), + &redirected_02_specifier + ), None, ); } @@ -1197,81 +934,53 @@ mod tests { resolve_url("http://localhost:4548/subdir/mismatch_ext.ts").unwrap(); let redirected_specifier = resolve_url("http://localhost:4546/subdir/mismatch_ext.ts").unwrap(); + let http_cache = + Arc::new(GlobalHttpCache::new(CliSys::default(), location.clone())); let metadata_file_modified_01 = { - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new( - location.clone(), - crate::cache::RealDenoCacheEnv, - )), - CacheSetting::Use, - true, + let file_fetcher = CliFileFetcher::new( + http_cache.clone(), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let cache_key = file_fetcher - .http_cache - .cache_item_key(&redirected_specifier) - .unwrap(); + let cache_key = http_cache.cache_item_key(&redirected_specifier).unwrap(); ( - file_fetcher - .http_cache - .read_modified_time(&cache_key) - .unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), - file_fetcher - .http_cache - .read_download_time(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_modified_time(&cache_key).unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), + http_cache.read_download_time(&cache_key).unwrap().unwrap(), ) }; let metadata_file_modified_02 = { - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new( - location, - crate::cache::RealDenoCacheEnv, - )), - CacheSetting::Use, - true, + let file_fetcher = CliFileFetcher::new( + http_cache.clone(), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let result = file_fetcher .fetch_bypass_permissions(&redirected_specifier) .await; assert!(result.is_ok()); - let cache_key = file_fetcher - .http_cache - .cache_item_key(&redirected_specifier) - .unwrap(); + let cache_key = http_cache.cache_item_key(&redirected_specifier).unwrap(); ( - file_fetcher - .http_cache - .read_modified_time(&cache_key) - .unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), - file_fetcher - .http_cache - .read_download_time(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_modified_time(&cache_key).unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), + http_cache.read_download_time(&cache_key).unwrap().unwrap(), ) }; @@ -1288,13 +997,9 @@ mod tests { let result = file_fetcher .fetch_with_options_and_max_redirect( - FetchOptions { - specifier: &specifier, - permissions: FetchPermissionsOptionRef::AllowAll, - maybe_auth: None, - maybe_accept: None, - maybe_cache_setting: Some(&file_fetcher.cache_setting), - }, + &specifier, + FetchPermissionsOptionRef::AllowAll, + Default::default(), 2, ) .await; @@ -1302,29 +1007,26 @@ mod tests { let result = file_fetcher .fetch_with_options_and_max_redirect( - FetchOptions { - specifier: &specifier, - permissions: FetchPermissionsOptionRef::AllowAll, - maybe_auth: None, - maybe_accept: None, - maybe_cache_setting: Some(&file_fetcher.cache_setting), - }, + &specifier, + FetchPermissionsOptionRef::AllowAll, + Default::default(), 1, ) .await; assert!(result.is_err()); - let result = file_fetcher.fetch_cached(&specifier, 2); + let result = file_fetcher.file_fetcher.fetch_cached(&specifier, 2); assert!(result.is_ok()); - let result = file_fetcher.fetch_cached(&specifier, 1); + let result = file_fetcher.file_fetcher.fetch_cached(&specifier, 1); assert!(result.is_err()); } #[tokio::test] async fn test_fetch_same_host_redirect() { let _http_server_guard = test_util::http_server(); - let (file_fetcher, _) = setup(CacheSetting::Use, None); + let (file_fetcher, _, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::Use, None); let specifier = resolve_url( "http://localhost:4550/REDIRECT/subdir/redirects/redirect1.js", ) @@ -1336,24 +1038,27 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); let file = result.unwrap(); - assert_eq!(file.specifier, redirected_specifier); + assert_eq!(file.url, redirected_specifier); assert_eq!( - get_text_from_cache(&file_fetcher, &specifier), + get_text_from_cache(http_cache.as_ref(), &specifier), "", "redirected files should have empty cached contents" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &specifier), + get_location_header_from_cache(http_cache.as_ref(), &specifier), Some("/subdir/redirects/redirect1.js".to_string()), ); assert_eq!( - get_text_from_cache(&file_fetcher, &redirected_specifier), + get_text_from_cache(http_cache.as_ref(), &redirected_specifier), "export const redirect = 1;\n" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &redirected_specifier), + get_location_header_from_cache( + http_cache.as_ref(), + &redirected_specifier + ), None ); } @@ -1363,16 +1068,15 @@ mod tests { let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new(); let location = temp_dir.path().join("remote").to_path_buf(); - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new( - location, - crate::cache::RealDenoCacheEnv, - )), - CacheSetting::Use, - false, + let file_fetcher = CliFileFetcher::new( + Arc::new(GlobalHttpCache::new(CliSys::default(), location)), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, + false, + CacheSetting::Use, + log::Level::Info, ); let specifier = resolve_url("http://localhost:4545/run/002_hello.ts").unwrap(); @@ -1380,8 +1084,19 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_err()); let err = result.unwrap_err(); - assert_eq!(get_custom_error_class(&err), Some("NoRemote")); - assert_eq!(err.to_string(), "A remote specifier was requested: \"http://localhost:4545/run/002_hello.ts\", but --no-remote is specified."); + let err = err.downcast::().unwrap().into_kind(); + match err { + CliFetchNoFollowErrorKind::FetchNoFollow(err) => { + let err = err.into_kind(); + match &err { + FetchNoFollowErrorKind::NoRemote { .. } => { + assert_eq!(err.to_string(), "A remote specifier was requested: \"http://localhost:4545/run/002_hello.ts\", but --no-remote is specified."); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } } #[tokio::test] @@ -1389,21 +1104,25 @@ mod tests { let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new(); let location = temp_dir.path().join("remote").to_path_buf(); - let file_fetcher_01 = FileFetcher::new( - Arc::new(GlobalHttpCache::new(location.clone(), RealDenoCacheEnv)), + let file_fetcher_01 = CliFileFetcher::new( + Arc::new(GlobalHttpCache::new(CliSys::default(), location.clone())), + Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), + Default::default(), + None, + true, CacheSetting::Only, - true, - Arc::new(HttpClientProvider::new(None, None)), - Default::default(), - None, + log::Level::Info, ); - let file_fetcher_02 = FileFetcher::new( - Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)), - CacheSetting::Use, - true, + let file_fetcher_02 = CliFileFetcher::new( + Arc::new(GlobalHttpCache::new(CliSys::default(), location)), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let specifier = resolve_url("http://localhost:4545/run/002_hello.ts").unwrap(); @@ -1411,8 +1130,19 @@ mod tests { let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await; assert!(result.is_err()); let err = result.unwrap_err(); - assert_eq!(err.to_string(), "Specifier not found in cache: \"http://localhost:4545/run/002_hello.ts\", --cached-only is specified."); - assert_eq!(get_custom_error_class(&err), Some("NotCached")); + let err = err.downcast::().unwrap().into_kind(); + match err { + CliFetchNoFollowErrorKind::FetchNoFollow(err) => { + let err = err.into_kind(); + match &err { + FetchNoFollowErrorKind::NotCached { .. } => { + assert_eq!(err.to_string(), "Specifier not found in cache: \"http://localhost:4545/run/002_hello.ts\", --cached-only is specified."); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); @@ -1426,16 +1156,16 @@ mod tests { let (file_fetcher, temp_dir) = setup(CacheSetting::Use, None); let fixture_path = temp_dir.path().join("mod.ts"); let specifier = ModuleSpecifier::from_file_path(&fixture_path).unwrap(); - fs::write(fixture_path.clone(), r#"console.log("hello deno");"#).unwrap(); + fixture_path.write(r#"console.log("hello deno");"#); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!(&*file.source, r#"console.log("hello deno");"#); - fs::write(fixture_path, r#"console.log("goodbye deno");"#).unwrap(); + fixture_path.write(r#"console.log("goodbye deno");"#); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!(&*file.source, r#"console.log("goodbye deno");"#); } @@ -1527,29 +1257,169 @@ mod tests { test_fetch_remote_encoded("windows-1255", "windows-1255", expected).await; } + fn create_http_client_adapter() -> HttpClientAdapter { + HttpClientAdapter { + http_client_provider: Arc::new(HttpClientProvider::new(None, None)), + download_log_level: log::Level::Info, + progress_bar: None, + } + } + + #[tokio::test] + async fn test_fetch_string() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/assets/fixture.json").unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, HeaderMap::new()).await; + if let Ok(SendResponse::Success(headers, body)) = result { + assert!(!body.is_empty()); + assert_eq!(headers.get("content-type").unwrap(), "application/json"); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); + } else { + panic!(); + } + } + + #[tokio::test] + async fn test_fetch_gzip() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/run/import_compression/gziped") + .unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, HeaderMap::new()).await; + if let Ok(SendResponse::Success(headers, body)) = result { + assert_eq!(String::from_utf8(body).unwrap(), "console.log('gzip')"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/javascript" + ); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); + } else { + panic!(); + } + } + + #[tokio::test] + async fn test_fetch_with_etag() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, HeaderMap::new()).await; + if let Ok(SendResponse::Success(headers, body)) = result { + assert!(!body.is_empty()); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('etag')"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/typescript" + ); + assert_eq!(headers.get("etag").unwrap(), "33a64df551425fcc55e"); + } else { + panic!(); + } + + let mut headers = HeaderMap::new(); + headers.insert("if-none-match", "33a64df551425fcc55e".parse().unwrap()); + let res = client.send_no_follow(&url, headers).await; + assert_eq!(res.unwrap(), SendResponse::NotModified); + } + + #[tokio::test] + async fn test_fetch_brotli() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/run/import_compression/brotli") + .unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, HeaderMap::new()).await; + if let Ok(SendResponse::Success(headers, body)) = result { + assert!(!body.is_empty()); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('brotli');"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/javascript" + ); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); + } else { + panic!(); + } + } + + #[tokio::test] + async fn test_fetch_accept() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/echo_accept").unwrap(); + let client = create_http_client_adapter(); + let mut headers = HeaderMap::new(); + headers.insert("accept", "application/json".parse().unwrap()); + let result = client.send_no_follow(&url, headers).await; + if let Ok(SendResponse::Success(_, body)) = result { + assert_eq!(body, r#"{"accept":"application/json"}"#.as_bytes()); + } else { + panic!(); + } + } + + #[tokio::test] + async fn test_fetch_no_follow_with_redirect() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4546/assets/fixture.json").unwrap(); + // Dns resolver substitutes `127.0.0.1` with `localhost` + let target_url = + Url::parse("http://localhost:4545/assets/fixture.json").unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, Default::default()).await; + if let Ok(SendResponse::Redirect(headers)) = result { + assert_eq!(headers.get("location").unwrap(), target_url.as_str()); + } else { + panic!(); + } + } + + #[tokio::test] + async fn server_error() { + let _g = test_util::http_server(); + let url_str = "http://127.0.0.1:4545/server_error"; + let url = Url::parse(url_str).unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, Default::default()).await; + + if let Err(SendError::StatusCode(status)) = result { + assert_eq!(status, 500); + } else { + panic!("{:?}", result); + } + } + + #[tokio::test] + async fn request_error() { + let _g = test_util::http_server(); + let url_str = "http://127.0.0.1:9999/"; + let url = Url::parse(url_str).unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, Default::default()).await; + + assert!(matches!(result, Err(SendError::Failed(_)))); + } + #[track_caller] fn get_text_from_cache( - file_fetcher: &FileFetcher, + http_cache: &dyn HttpCache, url: &ModuleSpecifier, ) -> String { - let cache_key = file_fetcher.http_cache.cache_item_key(url).unwrap(); - let bytes = file_fetcher - .http_cache - .get(&cache_key, None) - .unwrap() - .unwrap() - .content; + let cache_key = http_cache.cache_item_key(url).unwrap(); + let bytes = http_cache.get(&cache_key, None).unwrap().unwrap().content; String::from_utf8(bytes.into_owned()).unwrap() } #[track_caller] fn get_location_header_from_cache( - file_fetcher: &FileFetcher, + http_cache: &dyn HttpCache, url: &ModuleSpecifier, ) -> Option { - let cache_key = file_fetcher.http_cache.cache_item_key(url).unwrap(); - file_fetcher - .http_cache + let cache_key = http_cache.cache_item_key(url).unwrap(); + http_cache .read_headers(&cache_key) .unwrap() .unwrap() diff --git a/cli/graph_container.rs b/cli/graph_container.rs index c463d71a6a..1fe30b47ab 100644 --- a/cli/graph_container.rs +++ b/cli/graph_container.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/graph_util.rs b/cli/graph_util.rs index b655dda0f6..e57fcf8a94 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -1,4 +1,43 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashSet; +use std::error::Error; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_config::deno_json; +use deno_config::deno_json::JsxImportSourceConfig; +use deno_config::deno_json::NodeModulesDirMode; +use deno_config::workspace::JsrPackageConfig; +use deno_core::error::AnyError; +use deno_core::parking_lot::Mutex; +use deno_core::serde_json; +use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; +use deno_graph::source::Loader; +use deno_graph::source::LoaderChecksum; +use deno_graph::source::ResolutionKind; +use deno_graph::source::ResolveError; +use deno_graph::FillFromLockfileOptions; +use deno_graph::GraphKind; +use deno_graph::JsrLoadError; +use deno_graph::ModuleError; +use deno_graph::ModuleGraph; +use deno_graph::ModuleGraphError; +use deno_graph::ModuleLoadError; +use deno_graph::ResolutionError; +use deno_graph::SpecifierError; +use deno_graph::WorkspaceFastCheckOption; +use deno_path_util::url_to_file_path; +use deno_resolver::npm::DenoInNpmPackageChecker; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; +use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; +use deno_runtime::deno_node; +use deno_runtime::deno_permissions::PermissionsContainer; +use deno_semver::jsr::JsrDepPackageReq; +use deno_semver::package::PackageNv; +use deno_semver::SmallStackString; use crate::args::config_to_deno_graph_workspace_member; use crate::args::jsr_url; @@ -12,53 +51,20 @@ use crate::cache::GlobalHttpCache; use crate::cache::ModuleInfoCache; use crate::cache::ParsedSourceCache; use crate::colors; -use crate::errors::get_error_class_name; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; -use crate::resolver::CjsTracker; +use crate::resolver::CliCjsTracker; +use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliResolver; use crate::resolver::CliSloppyImportsResolver; -use crate::resolver::SloppyImportsCachedFs; +use crate::sys::CliSys; use crate::tools::check; +use crate::tools::check::CheckError; use crate::tools::check::TypeChecker; use crate::util::file_watcher::WatcherCommunicator; use crate::util::fs::canonicalize_path; -use deno_config::deno_json::JsxImportSourceConfig; -use deno_config::workspace::JsrPackageConfig; -use deno_core::anyhow::bail; -use deno_graph::source::LoaderChecksum; -use deno_graph::source::ResolutionKind; -use deno_graph::FillFromLockfileOptions; -use deno_graph::JsrLoadError; -use deno_graph::ModuleLoadError; -use deno_graph::WorkspaceFastCheckOption; - -use deno_core::error::custom_error; -use deno_core::error::AnyError; -use deno_core::parking_lot::Mutex; -use deno_core::ModuleSpecifier; -use deno_graph::source::Loader; -use deno_graph::source::ResolveError; -use deno_graph::GraphKind; -use deno_graph::ModuleError; -use deno_graph::ModuleGraph; -use deno_graph::ModuleGraphError; -use deno_graph::ResolutionError; -use deno_graph::SpecifierError; -use deno_path_util::url_to_file_path; -use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; -use deno_runtime::deno_fs::FileSystem; -use deno_runtime::deno_node; -use deno_runtime::deno_permissions::PermissionsContainer; -use deno_semver::jsr::JsrDepPackageReq; -use deno_semver::package::PackageNv; -use import_map::ImportMapError; -use node_resolver::InNpmPackageChecker; -use std::collections::HashSet; -use std::error::Error; -use std::ops::Deref; -use std::path::PathBuf; -use std::sync::Arc; #[derive(Clone)] pub struct GraphValidOptions { @@ -79,17 +85,17 @@ pub struct GraphValidOptions { /// for the CLI. pub fn graph_valid( graph: &ModuleGraph, - fs: &Arc, + sys: &CliSys, roots: &[ModuleSpecifier], options: GraphValidOptions, -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if options.exit_integrity_errors { graph_exit_integrity_errors(graph); } let mut errors = graph_walk_errors( graph, - fs, + sys, roots, GraphWalkErrorsOptions { check_js: options.check_js, @@ -101,9 +107,9 @@ pub fn graph_valid( } else { // finally surface the npm resolution result if let Err(err) = &graph.npm_dep_graph_result { - return Err(custom_error( - get_error_class_name(err), - format_deno_graph_error(err.as_ref().deref()), + return Err(JsErrorBox::new( + err.get_class(), + format_deno_graph_error(err), )); } Ok(()) @@ -139,10 +145,10 @@ pub struct GraphWalkErrorsOptions { /// and enhances them with CLI information. pub fn graph_walk_errors<'a>( graph: &'a ModuleGraph, - fs: &'a Arc, + sys: &'a CliSys, roots: &'a [ModuleSpecifier], options: GraphWalkErrorsOptions, -) -> impl Iterator + 'a { +) -> impl Iterator + 'a { graph .walk( roots.iter(), @@ -162,29 +168,15 @@ pub fn graph_walk_errors<'a>( roots.contains(error.specifier()) } }; - let mut message = match &error { - ModuleGraphError::ResolutionError(resolution_error) => { - enhanced_resolution_error_message(resolution_error) - } - ModuleGraphError::TypesResolutionError(resolution_error) => { - format!( - "Failed resolving types. {}", - enhanced_resolution_error_message(resolution_error) - ) - } - ModuleGraphError::ModuleError(error) => { - enhanced_integrity_error_message(error) - .or_else(|| enhanced_sloppy_imports_error_message(fs, error)) - .unwrap_or_else(|| format_deno_graph_error(error)) - } - }; - - if let Some(range) = error.maybe_range() { - if !is_root && !range.specifier.as_str().contains("/$deno$eval") { - message.push_str("\n at "); - message.push_str(&format_range_with_colors(range)); - } - } + let message = enhance_graph_error( + sys, + &error, + if is_root { + EnhanceGraphErrorMode::HideRange + } else { + EnhanceGraphErrorMode::ShowRange + }, + ); if graph.graph_kind() == GraphKind::TypesOnly && matches!( @@ -196,10 +188,61 @@ pub fn graph_walk_errors<'a>( return None; } - Some(custom_error(get_error_class_name(&error.into()), message)) + if graph.graph_kind().include_types() + && (message.contains(RUN_WITH_SLOPPY_IMPORTS_MSG) + || matches!( + error, + ModuleGraphError::ModuleError(ModuleError::Missing(..)) + )) + { + // ignore and let typescript surface this as a diagnostic instead + log::debug!("Ignoring: {}", message); + return None; + } + + Some(JsErrorBox::new(error.get_class(), message)) }) } +#[derive(Debug, PartialEq, Eq)] +pub enum EnhanceGraphErrorMode { + ShowRange, + HideRange, +} + +pub fn enhance_graph_error( + sys: &CliSys, + error: &ModuleGraphError, + mode: EnhanceGraphErrorMode, +) -> String { + let mut message = match &error { + ModuleGraphError::ResolutionError(resolution_error) => { + enhanced_resolution_error_message(resolution_error) + } + ModuleGraphError::TypesResolutionError(resolution_error) => { + format!( + "Failed resolving types. {}", + enhanced_resolution_error_message(resolution_error) + ) + } + ModuleGraphError::ModuleError(error) => { + enhanced_integrity_error_message(error) + .or_else(|| enhanced_sloppy_imports_error_message(sys, error)) + .unwrap_or_else(|| format_deno_graph_error(error)) + } + }; + + if let Some(range) = error.maybe_range() { + if mode == EnhanceGraphErrorMode::ShowRange + && !range.specifier.as_str().contains("/$deno$eval") + { + message.push_str("\n at "); + message.push_str(&format_range_with_colors(range)); + } + } + message +} + pub fn graph_exit_integrity_errors(graph: &ModuleGraph) { for error in graph.module_errors() { exit_for_integrity_error(error); @@ -224,7 +267,7 @@ pub struct CreateGraphOptions<'a> { pub struct ModuleGraphCreator { options: Arc, - npm_resolver: Arc, + npm_installer: Option>, module_graph_builder: Arc, type_checker: Arc, } @@ -232,13 +275,13 @@ pub struct ModuleGraphCreator { impl ModuleGraphCreator { pub fn new( options: Arc, - npm_resolver: Arc, + npm_installer: Option>, module_graph_builder: Arc, type_checker: Arc, ) -> Self { Self { options, - npm_resolver, + npm_installer, module_graph_builder, type_checker, } @@ -361,9 +404,9 @@ impl ModuleGraphCreator { .build_graph_with_npm_resolution(&mut graph, options) .await?; - if let Some(npm_resolver) = self.npm_resolver.as_managed() { + if let Some(npm_installer) = &self.npm_installer { if graph.has_node_specifier && self.options.type_check_mode().is_true() { - npm_resolver.inject_synthetic_types_node_package().await?; + npm_installer.inject_synthetic_types_node_package().await?; } } @@ -397,14 +440,14 @@ impl ModuleGraphCreator { } } - pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), AnyError> { + pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), JsErrorBox> { self.module_graph_builder.graph_valid(graph) } async fn type_check_graph( &self, graph: ModuleGraph, - ) -> Result, AnyError> { + ) -> Result, CheckError> { self .type_checker .check( @@ -427,56 +470,83 @@ pub struct BuildFastCheckGraphOptions<'a> { pub workspace_fast_check: deno_graph::WorkspaceFastCheckOption<'a>, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum BuildGraphWithNpmResolutionError { + #[class(inherit)] + #[error(transparent)] + SerdeJson(#[from] serde_json::Error), + #[class(inherit)] + #[error(transparent)] + ToMaybeJsxImportSourceConfig( + #[from] deno_json::ToMaybeJsxImportSourceConfigError, + ), + #[class(inherit)] + #[error(transparent)] + NodeModulesDirParse(#[from] deno_json::NodeModulesDirParseError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), + #[class(generic)] + #[error("Resolving npm specifier entrypoints this way is currently not supported with \"nodeModules\": \"manual\". In the meantime, try with --node-modules-dir=auto instead")] + UnsupportedNpmSpecifierEntrypointResolutionWay, +} + pub struct ModuleGraphBuilder { caches: Arc, - cjs_tracker: Arc, + cjs_tracker: Arc, cli_options: Arc, - file_fetcher: Arc, - fs: Arc, + file_fetcher: Arc, global_http_cache: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, - npm_resolver: Arc, + npm_graph_resolver: Arc, + npm_installer: Option>, + npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, + sys: CliSys, } impl ModuleGraphBuilder { #[allow(clippy::too_many_arguments)] pub fn new( caches: Arc, - cjs_tracker: Arc, + cjs_tracker: Arc, cli_options: Arc, - file_fetcher: Arc, - fs: Arc, + file_fetcher: Arc, global_http_cache: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, - npm_resolver: Arc, + npm_graph_resolver: Arc, + npm_installer: Option>, + npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, + sys: CliSys, ) -> Self { Self { caches, cjs_tracker, cli_options, file_fetcher, - fs, global_http_cache, in_npm_pkg_checker, lockfile, maybe_file_watcher_reporter, module_info_cache, + npm_graph_resolver, + npm_installer, npm_resolver, parsed_source_cache, resolver, root_permissions_container, + sys, } } @@ -484,7 +554,7 @@ impl ModuleGraphBuilder { &self, graph: &mut ModuleGraph, options: CreateGraphOptions<'a>, - ) -> Result<(), AnyError> { + ) -> Result<(), BuildGraphWithNpmResolutionError> { enum MutLoaderRef<'a> { Borrowed(&'a mut dyn Loader), Owned(cache::FetchCacher), @@ -570,10 +640,7 @@ impl ModuleGraphBuilder { Some(loader) => MutLoaderRef::Borrowed(loader), None => MutLoaderRef::Owned(self.create_graph_loader()), }; - let cli_resolver = &self.resolver; let graph_resolver = self.create_graph_resolver()?; - let graph_npm_resolver = - cli_resolver.create_graph_npm_resolver(options.npm_caching); let maybe_file_watcher_reporter = self .maybe_file_watcher_reporter .as_ref() @@ -592,9 +659,9 @@ impl ModuleGraphBuilder { is_dynamic: options.is_dynamic, passthrough_jsr_specifiers: false, executor: Default::default(), - file_system: &DenoGraphFsAdapter(self.fs.as_ref()), + file_system: &self.sys, jsr_url_provider: &CliJsrUrlProvider, - npm_resolver: Some(&graph_npm_resolver), + npm_resolver: Some(self.npm_graph_resolver.as_ref()), module_analyzer: &analyzer, reporter: maybe_file_watcher_reporter, resolver: Some(&graph_resolver), @@ -612,22 +679,21 @@ impl ModuleGraphBuilder { loader: &'a mut dyn deno_graph::source::Loader, options: deno_graph::BuildOptions<'a>, npm_caching: NpmCachingStrategy, - ) -> Result<(), AnyError> { + ) -> Result<(), BuildGraphWithNpmResolutionError> { // ensure an "npm install" is done if the user has explicitly // opted into using a node_modules directory if self .cli_options .node_modules_dir()? - .map(|m| m.uses_node_modules_dir()) + .map(|m| m == NodeModulesDirMode::Auto) .unwrap_or(false) { - if let Some(npm_resolver) = self.npm_resolver.as_managed() { - let already_done = - npm_resolver.ensure_top_level_package_json_install().await?; + if let Some(npm_installer) = &self.npm_installer { + let already_done = npm_installer + .ensure_top_level_package_json_install() + .await?; if !already_done && matches!(npm_caching, NpmCachingStrategy::Eager) { - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) - .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } } } @@ -646,10 +712,9 @@ impl ModuleGraphBuilder { let initial_package_deps_len = graph.packages.package_deps_sum(); let initial_package_mappings_len = graph.packages.mappings().len(); - if roots.iter().any(|r| r.scheme() == "npm") - && self.npm_resolver.as_byonm().is_some() + if roots.iter().any(|r| r.scheme() == "npm") && self.npm_resolver.is_byonm() { - bail!("Resolving npm specifier entrypoints this way is currently not supported with \"nodeModules\": \"manual\". In the meantime, try with --node-modules-dir=auto instead"); + return Err(BuildGraphWithNpmResolutionError::UnsupportedNpmSpecifierEntrypointResolutionWay); } graph.build(roots, loader, options).await; @@ -680,7 +745,7 @@ impl ModuleGraphBuilder { for (from, to) in graph.packages.mappings() { lockfile.insert_package_specifier( JsrDepPackageReq::jsr(from.clone()), - to.version.to_string(), + to.version.to_custom_string::(), ); } } @@ -700,7 +765,7 @@ impl ModuleGraphBuilder { &self, graph: &mut ModuleGraph, options: BuildFastCheckGraphOptions, - ) -> Result<(), AnyError> { + ) -> Result<(), deno_json::ToMaybeJsxImportSourceConfigError> { if !graph.graph_kind().include_types() { return Ok(()); } @@ -715,11 +780,7 @@ impl ModuleGraphBuilder { None }; let parser = self.parsed_source_cache.as_capturing_parser(); - let cli_resolver = &self.resolver; let graph_resolver = self.create_graph_resolver()?; - let graph_npm_resolver = cli_resolver.create_graph_npm_resolver( - self.cli_options.default_npm_caching_strategy(), - ); graph.build_fast_check_type_graph( deno_graph::BuildFastCheckTypeGraphOptions { @@ -728,7 +789,7 @@ impl ModuleGraphBuilder { fast_check_dts: false, jsr_url_provider: &CliJsrUrlProvider, resolver: Some(&graph_resolver), - npm_resolver: Some(&graph_npm_resolver), + npm_resolver: Some(self.npm_graph_resolver.as_ref()), workspace_fast_check: options.workspace_fast_check, }, ); @@ -746,10 +807,10 @@ impl ModuleGraphBuilder { ) -> cache::FetchCacher { cache::FetchCacher::new( self.file_fetcher.clone(), - self.fs.clone(), self.global_http_cache.clone(), self.in_npm_pkg_checker.clone(), self.module_info_cache.clone(), + self.sys.clone(), cache::FetchCacherOptions { file_header_overrides: self.cli_options.resolve_file_header_overrides(), permissions, @@ -764,7 +825,7 @@ impl ModuleGraphBuilder { /// Check if `roots` and their deps are available. Returns `Ok(())` if /// so. Returns `Err(_)` if there is a known module graph or resolution /// error statically reachable from `roots` and not a dynamic import. - pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), AnyError> { + pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), JsErrorBox> { self.graph_roots_valid( graph, &graph.roots.iter().cloned().collect::>(), @@ -775,10 +836,10 @@ impl ModuleGraphBuilder { &self, graph: &ModuleGraph, roots: &[ModuleSpecifier], - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { graph_valid( graph, - &self.fs, + &self.sys, roots, GraphValidOptions { kind: if self.cli_options.type_check_mode().is_true() { @@ -792,7 +853,10 @@ impl ModuleGraphBuilder { ) } - fn create_graph_resolver(&self) -> Result { + fn create_graph_resolver( + &self, + ) -> Result + { let jsx_import_source_config = self .cli_options .workspace() @@ -833,18 +897,19 @@ pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String { message } +static RUN_WITH_SLOPPY_IMPORTS_MSG: &str = + "or run with --unstable-sloppy-imports"; + fn enhanced_sloppy_imports_error_message( - fs: &Arc, + sys: &CliSys, error: &ModuleError, ) -> Option { match error { ModuleError::LoadingErr(specifier, _, ModuleLoadError::Loader(_)) // ex. "Is a directory" error | ModuleError::Missing(specifier, _) => { - let additional_message = CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(fs.clone())) - .resolve(specifier, SloppyImportsResolutionKind::Execution)? - .as_suggestion_message(); + let additional_message = maybe_additional_sloppy_imports_message(sys, specifier)?; Some(format!( - "{} {} or run with --unstable-sloppy-imports", + "{} {}", error, additional_message, )) @@ -853,6 +918,19 @@ fn enhanced_sloppy_imports_error_message( } } +pub fn maybe_additional_sloppy_imports_message( + sys: &CliSys, + specifier: &ModuleSpecifier, +) -> Option { + Some(format!( + "{} {}", + CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(sys.clone())) + .resolve(specifier, SloppyImportsResolutionKind::Execution)? + .as_suggestion_message(), + RUN_WITH_SLOPPY_IMPORTS_MSG + )) +} + fn enhanced_integrity_error_message(err: &ModuleError) -> Option { match err { ModuleError::LoadingErr( @@ -946,9 +1024,11 @@ fn get_resolution_error_bare_specifier( { Some(specifier.as_str()) } else if let ResolutionError::ResolverError { error, .. } = error { - if let ResolveError::Other(error) = (*error).as_ref() { - if let Some(ImportMapError::UnmappedBareSpecifier(specifier, _)) = - error.downcast_ref::() + if let ResolveError::ImportMap(error) = (*error).as_ref() { + if let import_map::ImportMapErrorKind::UnmappedBareSpecifier( + specifier, + _, + ) = error.as_kind() { Some(specifier.as_str()) } else { @@ -985,11 +1065,12 @@ fn get_import_prefix_missing_error(error: &ResolutionError) -> Option<&str> { ResolveError::Other(other_error) => { if let Some(SpecifierError::ImportPrefixMissing { specifier, .. - }) = other_error.downcast_ref::() + }) = other_error.as_any().downcast_ref::() { maybe_specifier = Some(specifier); } } + ResolveError::ImportMap(_) => {} } } } @@ -1081,71 +1162,6 @@ impl deno_graph::source::Reporter for FileWatcherReporter { } } -pub struct DenoGraphFsAdapter<'a>( - pub &'a dyn deno_runtime::deno_fs::FileSystem, -); - -impl<'a> deno_graph::source::FileSystem for DenoGraphFsAdapter<'a> { - fn read_dir( - &self, - dir_url: &deno_graph::ModuleSpecifier, - ) -> Vec { - use deno_core::anyhow; - use deno_graph::source::DirEntry; - use deno_graph::source::DirEntryKind; - - let dir_path = match dir_url.to_file_path() { - Ok(path) => path, - // ignore, treat as non-analyzable - Err(()) => return vec![], - }; - let entries = match self.0.read_dir_sync(&dir_path) { - Ok(dir) => dir, - Err(err) - if matches!( - err.kind(), - std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::NotFound - ) => - { - return vec![]; - } - Err(err) => { - return vec![DirEntry { - kind: DirEntryKind::Error( - anyhow::Error::from(err) - .context("Failed to read directory.".to_string()), - ), - url: dir_url.clone(), - }]; - } - }; - let mut dir_entries = Vec::with_capacity(entries.len()); - for entry in entries { - let entry_path = dir_path.join(&entry.name); - dir_entries.push(if entry.is_directory { - DirEntry { - kind: DirEntryKind::Dir, - url: ModuleSpecifier::from_directory_path(&entry_path).unwrap(), - } - } else if entry.is_file { - DirEntry { - kind: DirEntryKind::File, - url: ModuleSpecifier::from_file_path(&entry_path).unwrap(), - } - } else if entry.is_symlink { - DirEntry { - kind: DirEntryKind::Symlink, - url: ModuleSpecifier::from_file_path(&entry_path).unwrap(), - } - } else { - continue; - }); - } - - dir_entries - } -} - pub fn format_range_with_colors(referrer: &deno_graph::Range) -> String { format!( "{}:{}:{}", @@ -1209,7 +1225,7 @@ fn format_deno_graph_error(err: &dyn Error) -> String { #[derive(Debug)] struct CliGraphResolver<'a> { - cjs_tracker: &'a CjsTracker, + cjs_tracker: &'a CliCjsTracker, resolver: &'a CliResolver, jsx_import_source_config: Option, } @@ -1305,7 +1321,7 @@ mod test { let specifier = ModuleSpecifier::parse("file:///file.ts").unwrap(); let err = import_map.resolve(input, &specifier).err().unwrap(); let err = ResolutionError::ResolverError { - error: Arc::new(ResolveError::Other(err.into())), + error: Arc::new(ResolveError::ImportMap(err)), specifier: input.to_string(), range: Range { specifier, diff --git a/cli/http_util.rs b/cli/http_util.rs index 4b17936d68..5e63ab0a4a 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -1,212 +1,41 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::auth_tokens::AuthToken; -use crate::util::progress_bar::UpdateGuard; -use crate::version; +use std::collections::HashMap; +use std::sync::Arc; +use std::thread::ThreadId; -use cache_control::Cachability; -use cache_control::CacheControl; -use chrono::DateTime; -use deno_core::error::custom_error; -use deno_core::error::generic_error; +use boxed_error::Boxed; +use deno_cache_dir::file_fetcher::RedirectHeaderParseError; use deno_core::error::AnyError; use deno_core::futures::StreamExt; use deno_core::parking_lot::Mutex; use deno_core::serde; use deno_core::serde_json; use deno_core::url::Url; +use deno_error::JsError; +use deno_error::JsErrorBox; use deno_runtime::deno_fetch; use deno_runtime::deno_fetch::create_http_client; use deno_runtime::deno_fetch::CreateHttpClientOptions; +use deno_runtime::deno_fetch::ResBody; use deno_runtime::deno_tls::RootCertStoreProvider; -use http::header; use http::header::HeaderName; use http::header::HeaderValue; -use http::header::ACCEPT; -use http::header::AUTHORIZATION; use http::header::CONTENT_LENGTH; -use http::header::IF_NONE_MATCH; -use http::header::LOCATION; +use http::HeaderMap; use http::StatusCode; use http_body_util::BodyExt; - -use std::collections::HashMap; -use std::sync::Arc; -use std::thread::ThreadId; -use std::time::Duration; -use std::time::SystemTime; use thiserror::Error; -// TODO(ry) HTTP headers are not unique key, value pairs. There may be more than -// one header line with the same key. This should be changed to something like -// Vec<(String, String)> -pub type HeadersMap = HashMap; +use crate::util::progress_bar::UpdateGuard; +use crate::version; -/// A structure used to determine if a entity in the http cache can be used. -/// -/// This is heavily influenced by -/// which is BSD -/// 2-Clause Licensed and copyright Kornel Lesiński -pub struct CacheSemantics { - cache_control: CacheControl, - cached: SystemTime, - headers: HashMap, - now: SystemTime, -} - -impl CacheSemantics { - pub fn new( - headers: HashMap, - cached: SystemTime, - now: SystemTime, - ) -> Self { - let cache_control = headers - .get("cache-control") - .map(|v| CacheControl::from_value(v).unwrap_or_default()) - .unwrap_or_default(); - Self { - cache_control, - cached, - headers, - now, - } - } - - fn age(&self) -> Duration { - let mut age = self.age_header_value(); - - if let Ok(resident_time) = self.now.duration_since(self.cached) { - age += resident_time; - } - - age - } - - fn age_header_value(&self) -> Duration { - Duration::from_secs( - self - .headers - .get("age") - .and_then(|v| v.parse().ok()) - .unwrap_or(0), - ) - } - - fn is_stale(&self) -> bool { - self.max_age() <= self.age() - } - - fn max_age(&self) -> Duration { - if self.cache_control.cachability == Some(Cachability::NoCache) { - return Duration::from_secs(0); - } - - if self.headers.get("vary").map(|s| s.trim()) == Some("*") { - return Duration::from_secs(0); - } - - if let Some(max_age) = self.cache_control.max_age { - return max_age; - } - - let default_min_ttl = Duration::from_secs(0); - - let server_date = self.raw_server_date(); - if let Some(expires) = self.headers.get("expires") { - return match DateTime::parse_from_rfc2822(expires) { - Err(_) => Duration::from_secs(0), - Ok(expires) => { - let expires = SystemTime::UNIX_EPOCH - + Duration::from_secs(expires.timestamp().max(0) as _); - return default_min_ttl - .max(expires.duration_since(server_date).unwrap_or_default()); - } - }; - } - - if let Some(last_modified) = self.headers.get("last-modified") { - if let Ok(last_modified) = DateTime::parse_from_rfc2822(last_modified) { - let last_modified = SystemTime::UNIX_EPOCH - + Duration::from_secs(last_modified.timestamp().max(0) as _); - if let Ok(diff) = server_date.duration_since(last_modified) { - let secs_left = diff.as_secs() as f64 * 0.1; - return default_min_ttl.max(Duration::from_secs(secs_left as _)); - } - } - } - - default_min_ttl - } - - fn raw_server_date(&self) -> SystemTime { - self - .headers - .get("date") - .and_then(|d| DateTime::parse_from_rfc2822(d).ok()) - .and_then(|d| { - SystemTime::UNIX_EPOCH - .checked_add(Duration::from_secs(d.timestamp() as _)) - }) - .unwrap_or(self.cached) - } - - /// Returns true if the cached value is "fresh" respecting cached headers, - /// otherwise returns false. - pub fn should_use(&self) -> bool { - if self.cache_control.cachability == Some(Cachability::NoCache) { - return false; - } - - if let Some(max_age) = self.cache_control.max_age { - if self.age() > max_age { - return false; - } - } - - if let Some(min_fresh) = self.cache_control.min_fresh { - if self.time_to_live() < min_fresh { - return false; - } - } - - if self.is_stale() { - let has_max_stale = self.cache_control.max_stale.is_some(); - let allows_stale = has_max_stale - && self - .cache_control - .max_stale - .map(|val| val > self.age() - self.max_age()) - .unwrap_or(true); - if !allows_stale { - return false; - } - } - - true - } - - fn time_to_live(&self) -> Duration { - self.max_age().checked_sub(self.age()).unwrap_or_default() - } -} - -#[derive(Debug, Eq, PartialEq)] -pub enum FetchOnceResult { - Code(Vec, HeadersMap), - NotModified, - Redirect(Url, HeadersMap), - RequestError(String), - ServerError(StatusCode), -} - -#[derive(Debug)] -pub struct FetchOnceArgs<'a> { - pub url: Url, - pub maybe_accept: Option, - pub maybe_etag: Option, - pub maybe_auth_token: Option, - pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, - pub maybe_progress_guard: Option<&'a UpdateGuard>, +#[derive(Debug, Error)] +pub enum SendError { + #[error(transparent)] + Send(#[from] deno_fetch::ClientSendError), + #[error(transparent)] + InvalidUri(#[from] http::uri::InvalidUri), } pub struct HttpClientProvider { @@ -241,7 +70,7 @@ impl HttpClientProvider { } } - pub fn get_or_create(&self) -> Result { + pub fn get_or_create(&self) -> Result { use std::collections::hash_map::Entry; let thread_id = std::thread::current().id(); let mut clients = self.clients_by_thread_id.lock(); @@ -258,7 +87,8 @@ impl HttpClientProvider { }, ..self.options.clone() }, - )?; + ) + .map_err(JsErrorBox::from_err)?; entry.insert(client.clone()); Ok(HttpClient::new(client)) } @@ -266,31 +96,49 @@ impl HttpClientProvider { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(type)] #[error("Bad response: {:?}{}", .status_code, .response_text.as_ref().map(|s| format!("\n\n{}", s)).unwrap_or_else(String::new))] pub struct BadResponseError { pub status_code: StatusCode, pub response_text: Option, } -#[derive(Debug, Error)] -pub enum DownloadError { +#[derive(Debug, Boxed, JsError)] +pub struct DownloadError(pub Box); + +#[derive(Debug, Error, JsError)] +pub enum DownloadErrorKind { + #[class(inherit)] #[error(transparent)] - Fetch(AnyError), + Fetch(deno_fetch::ClientSendError), + #[class(inherit)] #[error(transparent)] UrlParse(#[from] deno_core::url::ParseError), + #[class(generic)] #[error(transparent)] HttpParse(#[from] http::Error), + #[class(inherit)] #[error(transparent)] Json(#[from] serde_json::Error), + #[class(generic)] #[error(transparent)] ToStr(#[from] http::header::ToStrError), - #[error("Redirection from '{}' did not provide location header", .request_url)] - NoRedirectHeader { request_url: Url }, + #[class(inherit)] + #[error(transparent)] + RedirectHeaderParse(RedirectHeaderParseError), + #[class(type)] #[error("Too many redirects.")] TooManyRedirects, + #[class(inherit)] #[error(transparent)] BadResponse(#[from] BadResponseError), + #[class("Http")] + #[error("Not Found.")] + NotFound, + #[class(inherit)] + #[error(transparent)] + Other(JsErrorBox), } #[derive(Debug)] @@ -314,9 +162,7 @@ impl HttpClient { } pub fn get(&self, url: Url) -> Result { - let body = http_body_util::Empty::new() - .map_err(|never| match never {}) - .boxed(); + let body = deno_fetch::ReqBody::empty(); let mut req = http::Request::new(body); *req.uri_mut() = url.as_str().parse()?; Ok(RequestBuilder { @@ -348,9 +194,7 @@ impl HttpClient { S: serde::Serialize, { let json = deno_core::serde_json::to_vec(ser)?; - let body = http_body_util::Full::new(json.into()) - .map_err(|never| match never {}) - .boxed(); + let body = deno_fetch::ReqBody::full(json.into()); let builder = self.post(url, body)?; Ok(builder.header( http::header::CONTENT_TYPE, @@ -358,107 +202,22 @@ impl HttpClient { )) } - /// Asynchronously fetches the given HTTP URL one pass only. - /// If no redirect is present and no error occurs, - /// yields Code(ResultPayload). - /// If redirect occurs, does not follow and - /// yields Redirect(url). - pub async fn fetch_no_follow<'a>( + pub async fn send( &self, - args: FetchOnceArgs<'a>, - ) -> Result { - let body = http_body_util::Empty::new() - .map_err(|never| match never {}) - .boxed(); + url: &Url, + headers: HeaderMap, + ) -> Result, SendError> { + let body = deno_fetch::ReqBody::empty(); let mut request = http::Request::new(body); - *request.uri_mut() = args.url.as_str().parse()?; + *request.uri_mut() = http::Uri::try_from(url.as_str())?; + *request.headers_mut() = headers; - if let Some(etag) = args.maybe_etag { - let if_none_match_val = HeaderValue::from_str(&etag)?; - request - .headers_mut() - .insert(IF_NONE_MATCH, if_none_match_val); - } - if let Some(auth_token) = args.maybe_auth_token { - let authorization_val = HeaderValue::from_str(&auth_token.to_string())?; - request - .headers_mut() - .insert(AUTHORIZATION, authorization_val); - } else if let Some((header, value)) = args.maybe_auth { - request.headers_mut().insert(header, value); - } - if let Some(accept) = args.maybe_accept { - let accepts_val = HeaderValue::from_str(&accept)?; - request.headers_mut().insert(ACCEPT, accepts_val); - } - let response = match self.client.clone().send(request).await { - Ok(resp) => resp, - Err(err) => { - if err.is_connect_error() { - return Ok(FetchOnceResult::RequestError(err.to_string())); - } - return Err(err.into()); - } - }; - - if response.status() == StatusCode::NOT_MODIFIED { - return Ok(FetchOnceResult::NotModified); - } - - let mut result_headers = HashMap::new(); - let response_headers = response.headers(); - - if let Some(warning) = response_headers.get("X-Deno-Warning") { - log::warn!( - "{} {}", - crate::colors::yellow("Warning"), - warning.to_str().unwrap() - ); - } - - for key in response_headers.keys() { - let key_str = key.to_string(); - let values = response_headers.get_all(key); - let values_str = values - .iter() - .map(|e| e.to_str().unwrap().to_string()) - .collect::>() - .join(","); - result_headers.insert(key_str, values_str); - } - - if response.status().is_redirection() { - let new_url = resolve_redirect_from_response(&args.url, &response)?; - return Ok(FetchOnceResult::Redirect(new_url, result_headers)); - } - - let status = response.status(); - - if status.is_server_error() { - return Ok(FetchOnceResult::ServerError(status)); - } - - if status.is_client_error() { - let err = if response.status() == StatusCode::NOT_FOUND { - custom_error( - "NotFound", - format!("Import '{}' failed, not found.", args.url), - ) - } else { - generic_error(format!( - "Import '{}' failed: {}", - args.url, - response.status() - )) - }; - return Err(err); - } - - let body = - get_response_body_with_progress(response, args.maybe_progress_guard) - .await?; - - Ok(FetchOnceResult::Code(body, result_headers)) + self + .client + .clone() + .send(request) + .await + .map_err(SendError::Send) } pub async fn download_text(&self, url: Url) -> Result { @@ -466,11 +225,11 @@ impl HttpClient { Ok(String::from_utf8(bytes)?) } - pub async fn download(&self, url: Url) -> Result, AnyError> { + pub async fn download(&self, url: Url) -> Result, DownloadError> { let maybe_bytes = self.download_inner(url, None, None).await?; match maybe_bytes { Some(bytes) => Ok(bytes), - None => Err(custom_error("Http", "Not found.")), + None => Err(DownloadErrorKind::NotFound.into_box()), } } @@ -488,7 +247,12 @@ impl HttpClient { Some(progress_guard), ) }, - |e| matches!(e, DownloadError::BadResponse(_) | DownloadError::Fetch(_)), + |e| { + matches!( + e.as_kind(), + DownloadErrorKind::BadResponse(_) | DownloadErrorKind::Fetch(_) + ) + }, ) .await } @@ -515,18 +279,21 @@ impl HttpClient { } else if !response.status().is_success() { let status = response.status(); let maybe_response_text = body_to_string(response).await.ok(); - return Err(DownloadError::BadResponse(BadResponseError { - status_code: status, - response_text: maybe_response_text - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()), - })); + return Err( + DownloadErrorKind::BadResponse(BadResponseError { + status_code: status, + response_text: maybe_response_text + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()), + }) + .into_box(), + ); } get_response_body_with_progress(response, progress_guard) .await - .map(Some) - .map_err(DownloadError::Fetch) + .map(|(_, body)| Some(body)) + .map_err(|err| DownloadErrorKind::Other(err).into_box()) } async fn get_redirected_response( @@ -543,7 +310,7 @@ impl HttpClient { .clone() .send(req) .await - .map_err(|e| DownloadError::Fetch(e.into()))?; + .map_err(|e| DownloadErrorKind::Fetch(e).into_box())?; let status = response.status(); if status.is_redirection() { for _ in 0..5 { @@ -563,7 +330,7 @@ impl HttpClient { .clone() .send(req) .await - .map_err(|e| DownloadError::Fetch(e.into()))?; + .map_err(|e| DownloadErrorKind::Fetch(e).into_box())?; let status = new_response.status(); if status.is_redirection() { response = new_response; @@ -572,17 +339,17 @@ impl HttpClient { return Ok((new_response, new_url)); } } - Err(DownloadError::TooManyRedirects) + Err(DownloadErrorKind::TooManyRedirects.into_box()) } else { Ok((response, url)) } } } -async fn get_response_body_with_progress( +pub async fn get_response_body_with_progress( response: http::Response, progress_guard: Option<&UpdateGuard>, -) -> Result, AnyError> { +) -> Result<(HeaderMap, Vec), JsErrorBox> { use http_body::Body as _; if let Some(progress_guard) = progress_guard { let mut total_size = response.body().size_hint().exact(); @@ -597,45 +364,21 @@ async fn get_response_body_with_progress( progress_guard.set_total_size(total_size); let mut current_size = 0; let mut data = Vec::with_capacity(total_size as usize); - let mut stream = response.into_body().into_data_stream(); + let (parts, body) = response.into_parts(); + let mut stream = body.into_data_stream(); while let Some(item) = stream.next().await { let bytes = item?; current_size += bytes.len() as u64; progress_guard.set_position(current_size); data.extend(bytes.into_iter()); } - return Ok(data); + return Ok((parts.headers, data)); } } - let bytes = response.collect().await?.to_bytes(); - Ok(bytes.into()) -} -/// Construct the next uri based on base uri and location header fragment -/// See -fn resolve_url_from_location(base_url: &Url, location: &str) -> Url { - if location.starts_with("http://") || location.starts_with("https://") { - // absolute uri - Url::parse(location).expect("provided redirect url should be a valid url") - } else if location.starts_with("//") { - // "//" authority path-abempty - Url::parse(&format!("{}:{}", base_url.scheme(), location)) - .expect("provided redirect url should be a valid url") - } else if location.starts_with('/') { - // path-absolute - base_url - .join(location) - .expect("provided redirect url should be a valid url") - } else { - // assuming path-noscheme | path-empty - let base_url_path_str = base_url.path().to_owned(); - // Pop last part or url (after last slash) - let segs: Vec<&str> = base_url_path_str.rsplitn(2, '/').collect(); - let new_path = format!("{}/{}", segs.last().unwrap_or(&""), location); - base_url - .join(&new_path) - .expect("provided redirect url should be a valid url") - } + let (parts, body) = response.into_parts(); + let bytes = body.collect().await?.to_bytes(); + Ok((parts.headers, bytes.into())) } fn resolve_redirect_from_response( @@ -643,16 +386,11 @@ fn resolve_redirect_from_response( response: &http::Response, ) -> Result { debug_assert!(response.status().is_redirection()); - if let Some(location) = response.headers().get(LOCATION) { - let location_string = location.to_str()?; - log::debug!("Redirecting to {:?}...", &location_string); - let new_url = resolve_url_from_location(request_url, location_string); - Ok(new_url) - } else { - Err(DownloadError::NoRedirectHeader { - request_url: request_url.clone(), - }) - } + deno_cache_dir::file_fetcher::resolve_redirect_from_headers( + request_url, + response.headers(), + ) + .map_err(|err| DownloadErrorKind::RedirectHeaderParse(*err).into_box()) } pub async fn body_to_string(body: B) -> Result @@ -707,8 +445,6 @@ mod test { use deno_runtime::deno_tls::rustls::RootCertStore; - use crate::version; - use super::*; #[tokio::test] @@ -738,231 +474,9 @@ mod test { assert_eq!(err.to_string(), "Too many redirects."); } - #[test] - fn test_resolve_url_from_location_full_1() { - let url = "http://deno.land".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "http://golang.org"); - assert_eq!(new_uri.host_str().unwrap(), "golang.org"); - } - - #[test] - fn test_resolve_url_from_location_full_2() { - let url = "https://deno.land".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "https://golang.org"); - assert_eq!(new_uri.host_str().unwrap(), "golang.org"); - } - - #[test] - fn test_resolve_url_from_location_relative_1() { - let url = "http://deno.land/x".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "//rust-lang.org/en-US"); - assert_eq!(new_uri.host_str().unwrap(), "rust-lang.org"); - assert_eq!(new_uri.path(), "/en-US"); - } - - #[test] - fn test_resolve_url_from_location_relative_2() { - let url = "http://deno.land/x".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "/y"); - assert_eq!(new_uri.host_str().unwrap(), "deno.land"); - assert_eq!(new_uri.path(), "/y"); - } - - #[test] - fn test_resolve_url_from_location_relative_3() { - let url = "http://deno.land/x".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "z"); - assert_eq!(new_uri.host_str().unwrap(), "deno.land"); - assert_eq!(new_uri.path(), "/z"); - } - - fn create_test_client() -> HttpClient { - HttpClient::new( - create_http_client("test_client", CreateHttpClientOptions::default()) - .unwrap(), - ) - } - - #[tokio::test] - async fn test_fetch_string() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4545/assets/fixture.json").unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(headers.get("content-type").unwrap(), "application/json"); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } - } - - #[tokio::test] - async fn test_fetch_gzip() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4545/run/import_compression/gziped") - .unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert_eq!(String::from_utf8(body).unwrap(), "console.log('gzip')"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/javascript" - ); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } - } - - #[tokio::test] - async fn test_fetch_with_etag() { - let _http_server_guard = test_util::http_server(); - let url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url: url.clone(), - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(String::from_utf8(body).unwrap(), "console.log('etag')"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/typescript" - ); - assert_eq!(headers.get("etag").unwrap(), "33a64df551425fcc55e"); - } else { - panic!(); - } - - let res = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: Some("33a64df551425fcc55e".to_string()), - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - assert_eq!(res.unwrap(), FetchOnceResult::NotModified); - } - - #[tokio::test] - async fn test_fetch_brotli() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4545/run/import_compression/brotli") - .unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(String::from_utf8(body).unwrap(), "console.log('brotli');"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/javascript" - ); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } - } - - #[tokio::test] - async fn test_fetch_accept() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4545/echo_accept").unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: Some("application/json".to_string()), - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, _)) = result { - assert_eq!(body, r#"{"accept":"application/json"}"#.as_bytes()); - } else { - panic!(); - } - } - - #[tokio::test] - async fn test_fetch_no_follow_with_redirect() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4546/assets/fixture.json").unwrap(); - // Dns resolver substitutes `127.0.0.1` with `localhost` - let target_url = - Url::parse("http://localhost:4545/assets/fixture.json").unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Redirect(url, _)) = result { - assert_eq!(url, target_url); - } else { - panic!(); - } - } - #[tokio::test] async fn test_fetch_with_cafile_string() { let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server let url = Url::parse("https://localhost:5545/assets/fixture.json").unwrap(); let client = HttpClient::new( @@ -978,24 +492,15 @@ mod test { ) .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(headers.get("content-type").unwrap(), "application/json"); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } + let response = client.send(&url, Default::default()).await.unwrap(); + assert!(response.status().is_success()); + let (parts, body) = response.into_parts(); + let headers = parts.headers; + let body = body.collect().await.unwrap().to_bytes(); + assert!(!body.is_empty()); + assert_eq!(headers.get("content-type").unwrap(), "application/json"); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); } static PUBLIC_HTTPS_URLS: &[&str] = &[ @@ -1026,34 +531,15 @@ mod test { .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - + let result = client.send(&url, Default::default()).await; match result { - Err(_) => { - eprintln!("Fetch error: {result:?}"); - continue; + Ok(response) if response.status().is_success() => { + return; // success } - Ok( - FetchOnceResult::Code(..) - | FetchOnceResult::NotModified - | FetchOnceResult::Redirect(..), - ) => return, - Ok( - FetchOnceResult::RequestError(_) | FetchOnceResult::ServerError(_), - ) => { - eprintln!("HTTP error: {result:?}"); - continue; + _ => { + // keep going } - }; + } } // Use 1.1.1.1 and 8.8.8.8 as our last-ditch internet check @@ -1089,42 +575,13 @@ mod test { .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - - match result { - Err(_) => { - eprintln!("Fetch error (expected): {result:?}"); - return; - } - Ok( - FetchOnceResult::Code(..) - | FetchOnceResult::NotModified - | FetchOnceResult::Redirect(..), - ) => { - panic!("Should not have successfully fetched a URL"); - } - Ok( - FetchOnceResult::RequestError(_) | FetchOnceResult::ServerError(_), - ) => { - eprintln!("HTTP error (expected): {result:?}"); - return; - } - }; + let result = client.send(&url, HeaderMap::new()).await; + assert!(result.is_err() || !result.unwrap().status().is_success()); } #[tokio::test] async fn test_fetch_with_cafile_gzip() { let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server let url = Url::parse("https://localhost:5545/run/import_compression/gziped") .unwrap(); @@ -1143,27 +600,18 @@ mod test { ) .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert_eq!(String::from_utf8(body).unwrap(), "console.log('gzip')"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/javascript" - ); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } + let response = client.send(&url, Default::default()).await.unwrap(); + assert!(response.status().is_success()); + let (parts, body) = response.into_parts(); + let headers = parts.headers; + let body = body.collect().await.unwrap().to_bytes().to_vec(); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('gzip')"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/javascript" + ); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); } #[tokio::test] @@ -1185,46 +633,29 @@ mod test { ) .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url: url.clone(), - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(String::from_utf8(body).unwrap(), "console.log('etag')"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/typescript" - ); - assert_eq!(headers.get("etag").unwrap(), "33a64df551425fcc55e"); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } + let response = client.send(&url, Default::default()).await.unwrap(); + assert!(response.status().is_success()); + let (parts, body) = response.into_parts(); + let headers = parts.headers; + let body = body.collect().await.unwrap().to_bytes().to_vec(); + assert!(!body.is_empty()); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('etag')"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/typescript" + ); + assert_eq!(headers.get("etag").unwrap(), "33a64df551425fcc55e"); + assert_eq!(headers.get("x-typescript-types"), None); - let res = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: Some("33a64df551425fcc55e".to_string()), - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - assert_eq!(res.unwrap(), FetchOnceResult::NotModified); + let mut headers = HeaderMap::new(); + headers.insert("If-None-Match", "33a64df551425fcc55e".parse().unwrap()); + let res = client.send(&url, headers).await.unwrap(); + assert_eq!(res.status(), StatusCode::NOT_MODIFIED); } #[tokio::test] async fn test_fetch_with_cafile_brotli() { let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server let url = Url::parse("https://localhost:5545/run/import_compression/brotli") .unwrap(); @@ -1243,93 +674,18 @@ mod test { ) .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(String::from_utf8(body).unwrap(), "console.log('brotli');"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/javascript" - ); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } - } - - #[tokio::test] - async fn bad_redirect() { - let _g = test_util::http_server(); - let url_str = "http://127.0.0.1:4545/bad_redirect"; - let url = Url::parse(url_str).unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - assert!(result.is_err()); - let err = result.unwrap_err(); - // Check that the error message contains the original URL - assert!(err.to_string().contains(url_str)); - } - - #[tokio::test] - async fn server_error() { - let _g = test_util::http_server(); - let url_str = "http://127.0.0.1:4545/server_error"; - let url = Url::parse(url_str).unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - - if let Ok(FetchOnceResult::ServerError(status)) = result { - assert_eq!(status, 500); - } else { - panic!(); - } - } - - #[tokio::test] - async fn request_error() { - let _g = test_util::http_server(); - let url_str = "http://127.0.0.1:9999/"; - let url = Url::parse(url_str).unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - - assert!(matches!(result, Ok(FetchOnceResult::RequestError(_)))); + let response = client.send(&url, Default::default()).await.unwrap(); + assert!(response.status().is_success()); + let (parts, body) = response.into_parts(); + let headers = parts.headers; + let body = body.collect().await.unwrap().to_bytes().to_vec(); + assert!(!body.is_empty()); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('brotli');"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/javascript" + ); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); } } diff --git a/cli/integration_tests_runner.rs b/cli/integration_tests_runner.rs index 12e83a0194..7342e62fa0 100644 --- a/cli/integration_tests_runner.rs +++ b/cli/integration_tests_runner.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub fn main() { let mut args = vec!["cargo", "test", "-p", "cli_tests", "--features", "run"]; diff --git a/cli/js.rs b/cli/js.rs index 2c93f004ca..5337c53f76 100644 --- a/cli/js.rs +++ b/cli/js.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use log::debug; diff --git a/cli/js/40_bench.js b/cli/js/40_bench.js index b07df3993c..fb0e86463d 100644 --- a/cli/js/40_bench.js +++ b/cli/js/40_bench.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file import { core, primordials } from "ext:core/mod.js"; @@ -8,7 +8,7 @@ import { restorePermissions, } from "ext:cli/40_test_common.js"; import { Console } from "ext:deno_console/01_console.js"; -import { setExitHandler } from "ext:runtime/30_os.js"; +import { setExitHandler } from "ext:deno_os/30_os.js"; const { op_register_bench, op_bench_get_origin, diff --git a/cli/js/40_jupyter.js b/cli/js/40_jupyter.js index 198b6a3502..f392af1d43 100644 --- a/cli/js/40_jupyter.js +++ b/cli/js/40_jupyter.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file /* diff --git a/cli/js/40_lint.js b/cli/js/40_lint.js new file mode 100644 index 0000000000..9f85f0871d --- /dev/null +++ b/cli/js/40_lint.js @@ -0,0 +1,1089 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +// @ts-check + +import { + compileSelector, + parseSelector, + splitSelectors, +} from "ext:cli/40_lint_selector.js"; +import { core, internals } from "ext:core/mod.js"; + +const { + op_lint_create_serialized_ast, +} = core.ops; + +// Keep these in sync with Rust +const AST_IDX_INVALID = 0; +const AST_GROUP_TYPE = 1; +/// +/// +/// +/// +/// +const NODE_SIZE = 1 + 4 + 4 + 4 + 4; +const PROP_OFFSET = 1; +const CHILD_OFFSET = 1 + 4; +const NEXT_OFFSET = 1 + 4 + 4; +const PARENT_OFFSET = 1 + 4 + 4 + 4; +// Span size in buffer: u32 + u32 +const SPAN_SIZE = 4 + 4; + +// Keep in sync with Rust +// These types are expected to be present on every node. Note that this +// isn't set in stone. We could revise this at a future point. +const AST_PROP_TYPE = 1; +const AST_PROP_PARENT = 2; +const AST_PROP_RANGE = 3; +const AST_PROP_LENGTH = 4; + +// Keep in sync with Rust +// Each node property is tagged with this enum to denote +// what kind of value it holds. +/** @enum {number} */ +const PropFlags = { + /** This is an offset to another node */ + Ref: 0, + /** This is an array of offsets to other nodes (like children of a BlockStatement) */ + RefArr: 1, + /** + * This is a string id. The actual string needs to be looked up in + * the string table that was included in the message. + */ + String: 2, + /** + * A numnber field. Numbers are represented as strings internally. + */ + Number: 3, + /** This value is either 0 = false, or 1 = true */ + Bool: 4, + /** No value, it's null */ + Null: 5, + /** No value, it's undefined */ + Undefined: 6, + /** An object */ + Obj: 7, + Regex: 8, + BigInt: 9, + Array: 10, +}; + +/** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */ +/** @typedef {import("./40_lint_types.d.ts").VisitorFn} VisitorFn */ +/** @typedef {import("./40_lint_types.d.ts").CompiledVisitor} CompiledVisitor */ +/** @typedef {import("./40_lint_types.d.ts").LintState} LintState */ +/** @typedef {import("./40_lint_types.d.ts").RuleContext} RuleContext */ +/** @typedef {import("./40_lint_types.d.ts").NodeFacade} NodeFacade */ +/** @typedef {import("./40_lint_types.d.ts").LintPlugin} LintPlugin */ +/** @typedef {import("./40_lint_types.d.ts").TransformFn} TransformFn */ +/** @typedef {import("./40_lint_types.d.ts").MatchContext} MatchContext */ +/** @typedef {import("./40_lint_types.d.ts").Node} Node */ + +/** @type {LintState} */ +const state = { + plugins: [], + installedPlugins: new Set(), +}; + +/** + * Every rule gets their own instance of this class. This is the main + * API lint rules interact with. + * @implements {RuleContext} + */ +export class Context { + id; + + fileName; + + /** + * @param {string} id + * @param {string} fileName + */ + constructor(id, fileName) { + this.id = id; + this.fileName = fileName; + } +} + +/** + * @param {LintPlugin} plugin + */ +export function installPlugin(plugin) { + if (typeof plugin !== "object") { + throw new Error("Linter plugin must be an object"); + } + if (typeof plugin.name !== "string") { + throw new Error("Linter plugin name must be a string"); + } + if (typeof plugin.rules !== "object") { + throw new Error("Linter plugin rules must be an object"); + } + if (state.installedPlugins.has(plugin.name)) { + throw new Error(`Linter plugin ${plugin.name} has already been registered`); + } + state.plugins.push(plugin); + state.installedPlugins.add(plugin.name); +} + +/** + * @param {AstContext} ctx + * @param {number} idx + * @returns {FacadeNode | null} + */ +function getNode(ctx, idx) { + if (idx === AST_IDX_INVALID) return null; + const cached = ctx.nodes.get(idx); + if (cached !== undefined) return /** @type {*} */ (cached); + + const node = new FacadeNode(ctx, idx); + ctx.nodes.set(idx, /** @type {*} */ (node)); + return /** @type {*} */ (node); +} + +/** + * Find the offset of a specific property of a specific node. This will + * be used later a lot more for selectors. + * @param {Uint8Array} buf + * @param {number} search + * @param {number} offset + * @returns {number} + */ +function findPropOffset(buf, offset, search) { + const count = buf[offset]; + offset += 1; + + for (let i = 0; i < count; i++) { + const maybe = offset; + const prop = buf[offset++]; + const kind = buf[offset++]; + if (prop === search) return maybe; + + if (kind === PropFlags.Obj) { + const len = readU32(buf, offset); + offset += 4; + // prop + kind + value + offset += len * (1 + 1 + 4); + } else { + offset += 4; + } + } + + return -1; +} + +const INTERNAL_CTX = Symbol("ctx"); +const INTERNAL_IDX = Symbol("offset"); + +// This class is a facade for all materialized nodes. Instead of creating a +// unique class per AST node, we have one class with getters for every +// possible node property. This allows us to lazily materialize child node +// only when they are needed. +class FacadeNode { + [INTERNAL_CTX]; + [INTERNAL_IDX]; + + /** + * @param {AstContext} ctx + * @param {number} idx + */ + constructor(ctx, idx) { + this[INTERNAL_CTX] = ctx; + this[INTERNAL_IDX] = idx; + } + + /** + * Logging a class with only getters prints just the class name. This + * makes debugging difficult because you don't see any of the properties. + * For that reason we'll intercept inspection and serialize the node to + * a plain JSON structure which can be logged and allows users to see all + * properties and their values. + * + * This is only expected to be used during development of a rule. + * @param {*} _ + * @param {Deno.InspectOptions} options + * @returns {string} + */ + [Symbol.for("Deno.customInspect")](_, options) { + const json = nodeToJson(this[INTERNAL_CTX], this[INTERNAL_IDX]); + return Deno.inspect(json, options); + } + + [Symbol.for("Deno.lint.toJsValue")]() { + return nodeToJson(this[INTERNAL_CTX], this[INTERNAL_IDX]); + } +} + +/** @type {Set} */ +const appliedGetters = new Set(); + +/** + * Add getters for all potential properties found in the message. + * @param {AstContext} ctx + */ +function setNodeGetters(ctx) { + if (appliedGetters.size === ctx.strByProp.length) return; + + for (let i = 0; i < ctx.strByProp.length; i++) { + const id = ctx.strByProp[i]; + if (id === 0 || appliedGetters.has(i)) continue; + appliedGetters.add(i); + + const name = getString(ctx.strTable, id); + + Object.defineProperty(FacadeNode.prototype, name, { + get() { + return readValue( + this[INTERNAL_CTX], + this[INTERNAL_IDX], + i, + getNode, + ); + }, + }); + } +} + +/** + * @param {AstContext} ctx + * @param {number} idx + */ +function nodeToJson(ctx, idx) { + /** @type {Record} */ + const node = { + type: readValue(ctx, idx, AST_PROP_TYPE, nodeToJson), + range: readValue(ctx, idx, AST_PROP_RANGE, nodeToJson), + }; + + const { buf } = ctx; + let offset = readPropOffset(ctx, idx); + + const count = buf[offset++]; + + for (let i = 0; i < count; i++) { + const prop = buf[offset]; + const _kind = buf[offset + 1]; + + const name = getString(ctx.strTable, ctx.strByProp[prop]); + node[name] = readProperty(ctx, offset, nodeToJson); + + // prop + type + value + offset += 1 + 1 + 4; + } + + return node; +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readType(buf, idx) { + return buf[idx * NODE_SIZE]; +} + +/** + * @param {AstContext} ctx + * @param {number} idx + * @returns {Node["range"]} + */ +function readSpan(ctx, idx) { + let offset = ctx.spansOffset + (idx * SPAN_SIZE); + const start = readU32(ctx.buf, offset); + offset += 4; + const end = readU32(ctx.buf, offset); + + return [start, end]; +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readRawPropOffset(buf, idx) { + const offset = (idx * NODE_SIZE) + PROP_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext} ctx + * @param {number} idx + * @returns {number} + */ +function readPropOffset(ctx, idx) { + return readRawPropOffset(ctx.buf, idx) + ctx.propsOffset; +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readChild(buf, idx) { + const offset = (idx * NODE_SIZE) + CHILD_OFFSET; + return readU32(buf, offset); +} +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readNext(buf, idx) { + const offset = (idx * NODE_SIZE) + NEXT_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readParent(buf, idx) { + const offset = (idx * NODE_SIZE) + PARENT_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext["strTable"]} strTable + * @param {number} strId + * @returns {RegExp} + */ +function readRegex(strTable, strId) { + const raw = getString(strTable, strId); + const idx = raw.lastIndexOf("/"); + const pattern = raw.slice(1, idx); + const flags = idx < raw.length - 1 ? raw.slice(idx + 1) : undefined; + + return new RegExp(pattern, flags); +} + +/** + * @param {AstContext} ctx + * @param {number} offset + * @param {(ctx: AstContext, idx: number) => any} parseNode + * @returns {Record} + */ +function readObject(ctx, offset, parseNode) { + const { buf, strTable, strByProp } = ctx; + + /** @type {Record} */ + const obj = {}; + + const count = readU32(buf, offset); + offset += 4; + + for (let i = 0; i < count; i++) { + const prop = buf[offset]; + const name = getString(strTable, strByProp[prop]); + obj[name] = readProperty(ctx, offset, parseNode); + // name + kind + value + offset += 1 + 1 + 4; + } + + return obj; +} + +/** + * @param {AstContext} ctx + * @param {number} offset + * @param {(ctx: AstContext, idx: number) => any} parseNode + * @returns {any} + */ +function readProperty(ctx, offset, parseNode) { + const { buf } = ctx; + + // skip over name + const _name = buf[offset++]; + const kind = buf[offset++]; + + if (kind === PropFlags.Ref) { + const value = readU32(buf, offset); + return parseNode(ctx, value); + } else if (kind === PropFlags.RefArr) { + const groupId = readU32(buf, offset); + + const nodes = []; + let next = readChild(buf, groupId); + while (next > AST_IDX_INVALID) { + nodes.push(parseNode(ctx, next)); + next = readNext(buf, next); + } + + return nodes; + } else if (kind === PropFlags.Bool) { + const v = readU32(buf, offset); + return v === 1; + } else if (kind === PropFlags.String) { + const v = readU32(buf, offset); + return getString(ctx.strTable, v); + } else if (kind === PropFlags.Number) { + const v = readU32(buf, offset); + return Number(getString(ctx.strTable, v)); + } else if (kind === PropFlags.BigInt) { + const v = readU32(buf, offset); + return BigInt(getString(ctx.strTable, v)); + } else if (kind === PropFlags.Regex) { + const v = readU32(buf, offset); + return readRegex(ctx.strTable, v); + } else if (kind === PropFlags.Null) { + return null; + } else if (kind === PropFlags.Undefined) { + return undefined; + } else if (kind === PropFlags.Obj) { + const objOffset = readU32(buf, offset) + ctx.propsOffset; + return readObject(ctx, objOffset, parseNode); + } + + throw new Error(`Unknown prop kind: ${kind}`); +} + +/** + * Read a specific property from a node + * @param {AstContext} ctx + * @param {number} idx + * @param {number} search + * @param {(ctx: AstContext, idx: number) => any} parseNode + * @returns {*} + */ +function readValue(ctx, idx, search, parseNode) { + const { buf } = ctx; + + if (search === AST_PROP_TYPE) { + const type = readType(buf, idx); + return getString(ctx.strTable, ctx.strByType[type]); + } else if (search === AST_PROP_RANGE) { + return readSpan(ctx, idx); + } else if (search === AST_PROP_PARENT) { + const parent = readParent(buf, idx); + return getNode(ctx, parent); + } + + const propOffset = readPropOffset(ctx, idx); + + const offset = findPropOffset(ctx.buf, propOffset, search); + if (offset === -1) return undefined; + + return readProperty(ctx, offset, parseNode); +} + +const DECODER = new TextDecoder(); + +/** + * TODO: Check if it's faster to use the `ArrayView` API instead. + * @param {Uint8Array} buf + * @param {number} i + * @returns {number} + */ +function readU32(buf, i) { + return (buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + + buf[i + 3]; +} + +/** + * Get a string by id and error if it wasn't found + * @param {AstContext["strTable"]} strTable + * @param {number} id + * @returns {string} + */ +function getString(strTable, id) { + const name = strTable.get(id); + if (name === undefined) { + throw new Error(`Missing string id: ${id}`); + } + + return name; +} + +/** @implements {MatchContext} */ +class MatchCtx { + /** + * @param {AstContext} ctx + */ + constructor(ctx) { + this.ctx = ctx; + } + + /** + * @param {number} idx + * @returns {number} + */ + getParent(idx) { + return readParent(this.ctx.buf, idx); + } + + /** + * @param {number} idx + * @returns {number} + */ + getType(idx) { + return readType(this.ctx.buf, idx); + } + + /** + * @param {number} idx - Node idx + * @param {number[]} propIds + * @param {number} propIdx + * @returns {unknown} + */ + getAttrPathValue(idx, propIds, propIdx) { + if (idx === 0) throw -1; + + const { buf, strTable, strByType } = this.ctx; + + const propId = propIds[propIdx]; + + switch (propId) { + case AST_PROP_TYPE: { + const type = readType(buf, idx); + return getString(strTable, strByType[type]); + } + case AST_PROP_PARENT: + case AST_PROP_RANGE: + throw -1; + } + + let offset = readPropOffset(this.ctx, idx); + + offset = findPropOffset(buf, offset, propId); + if (offset === -1) throw -1; + const _prop = buf[offset++]; + const kind = buf[offset++]; + + if (kind === PropFlags.Ref) { + const value = readU32(buf, offset); + // Checks need to end with a value, not a node + if (propIdx === propIds.length - 1) throw -1; + return this.getAttrPathValue(value, propIds, propIdx + 1); + } else if (kind === PropFlags.RefArr) { + const arrIdx = readU32(buf, offset); + offset += 4; + + let count = 0; + let child = readChild(buf, arrIdx); + while (child > AST_IDX_INVALID) { + count++; + child = readNext(buf, child); + } + + if ( + propIdx < propIds.length - 1 && propIds[propIdx + 1] === AST_PROP_LENGTH + ) { + return count; + } + + // TODO(@marvinhagemeister): Allow traversing into array children? + throw -1; + } else if (kind === PropFlags.Obj) { + // TODO(@marvinhagemeister) + } + + // Cannot traverse into primitives further + if (propIdx < propIds.length - 1) throw -1; + + if (kind === PropFlags.String) { + const s = readU32(buf, offset); + return getString(strTable, s); + } else if (kind === PropFlags.Number) { + const s = readU32(buf, offset); + return Number(getString(strTable, s)); + } else if (kind === PropFlags.Regex) { + const v = readU32(buf, offset); + return readRegex(strTable, v); + } else if (kind === PropFlags.Bool) { + return readU32(buf, offset) === 1; + } else if (kind === PropFlags.Null) { + return null; + } else if (kind === PropFlags.Undefined) { + return undefined; + } + + throw -1; + } + + /** + * @param {number} idx + * @returns {number} + */ + getFirstChild(idx) { + const siblings = this.getSiblings(idx); + return siblings[0] ?? -1; + } + + /** + * @param {number} idx + * @returns {number} + */ + getLastChild(idx) { + const siblings = this.getSiblings(idx); + return siblings.at(-1) ?? -1; + } + + /** + * @param {number} idx + * @returns {number[]} + */ + getSiblings(idx) { + const { buf } = this.ctx; + const parent = readParent(buf, idx); + + // Only RefArrays have siblings + const parentType = readType(buf, parent); + if (parentType !== AST_GROUP_TYPE) { + return []; + } + + const out = []; + let child = readChild(buf, parent); + while (child > AST_IDX_INVALID) { + out.push(child); + child = readNext(buf, child); + } + + return out; + } +} + +/** + * @param {Uint8Array} buf + * @returns {AstContext} + */ +function createAstContext(buf) { + /** @type {Map} */ + const strTable = new Map(); + + // The buffer has a few offsets at the end which allows us to easily + // jump to the relevant sections of the message. + const propsOffset = readU32(buf, buf.length - 24); + const spansOffset = readU32(buf, buf.length - 20); + const typeMapOffset = readU32(buf, buf.length - 16); + const propMapOffset = readU32(buf, buf.length - 12); + const strTableOffset = readU32(buf, buf.length - 8); + + // Offset of the topmost node in the AST Tree. + const rootOffset = readU32(buf, buf.length - 4); + + let offset = strTableOffset; + const stringCount = readU32(buf, offset); + offset += 4; + + let strId = 0; + for (let i = 0; i < stringCount; i++) { + const len = readU32(buf, offset); + offset += 4; + + const strBytes = buf.slice(offset, offset + len); + offset += len; + const s = DECODER.decode(strBytes); + strTable.set(strId, s); + strId++; + } + + if (strTable.size !== stringCount) { + throw new Error( + `Could not deserialize string table. Expected ${stringCount} items, but got ${strTable.size}`, + ); + } + + offset = typeMapOffset; + const typeCount = readU32(buf, offset); + offset += 4; + + const typeByStr = new Map(); + const strByType = new Array(typeCount).fill(0); + for (let i = 0; i < typeCount; i++) { + const v = readU32(buf, offset); + offset += 4; + + strByType[i] = v; + typeByStr.set(strTable.get(v), i); + } + + offset = propMapOffset; + const propCount = readU32(buf, offset); + offset += 4; + + const propByStr = new Map(); + const strByProp = new Array(propCount).fill(0); + for (let i = 0; i < propCount; i++) { + const v = readU32(buf, offset); + offset += 4; + + strByProp[i] = v; + propByStr.set(strTable.get(v), i); + } + + /** @type {AstContext} */ + const ctx = { + buf, + strTable, + rootOffset, + spansOffset, + propsOffset, + nodes: new Map(), + strTableOffset, + strByProp, + strByType, + typeByStr, + propByStr, + matcher: /** @type {*} */ (null), + }; + ctx.matcher = new MatchCtx(ctx); + + setNodeGetters(ctx); + + // DEV ONLY: Enable this to inspect the buffer message + // _dump(ctx); + + return ctx; +} + +/** + * @param {*} _node + */ +const NOOP = (_node) => {}; + +/** + * Kick off the actual linting process of JS plugins. + * @param {string} fileName + * @param {Uint8Array} serializedAst + */ +export function runPluginsForFile(fileName, serializedAst) { + const ctx = createAstContext(serializedAst); + + /** @type {Map}>} */ + const bySelector = new Map(); + + const destroyFns = []; + + // Instantiate and merge visitors. This allows us to only traverse + // the AST once instead of per plugin. When ever we enter or exit a + // node we'll call all visitors that match. + for (let i = 0; i < state.plugins.length; i++) { + const plugin = state.plugins[i]; + + for (const name of Object.keys(plugin.rules)) { + const rule = plugin.rules[name]; + const id = `${plugin.name}/${name}`; + const ctx = new Context(id, fileName); + const visitor = rule.create(ctx); + + // deno-lint-ignore guard-for-in + for (let key in visitor) { + const fn = visitor[key]; + if (fn === undefined) continue; + + // Support enter and exit callbacks on a visitor. + // Exit callbacks are marked by having `:exit` at the end. + let isExit = false; + if (key.endsWith(":exit")) { + isExit = true; + key = key.slice(0, -":exit".length); + } + + const selectors = splitSelectors(key); + + for (let j = 0; j < selectors.length; j++) { + const key = selectors[j]; + + let info = bySelector.get(key); + if (info === undefined) { + info = { enter: NOOP, exit: NOOP }; + bySelector.set(key, info); + } + const prevFn = isExit ? info.exit : info.enter; + + /** + * @param {*} node + */ + const wrapped = (node) => { + prevFn(node); + + try { + fn(node); + } catch (err) { + throw new Error(`Visitor "${name}" of plugin "${id}" errored`, { + cause: err, + }); + } + }; + + if (isExit) { + info.exit = wrapped; + } else { + info.enter = wrapped; + } + } + } + + if (typeof rule.destroy === "function") { + const destroyFn = rule.destroy.bind(rule); + destroyFns.push(() => { + try { + destroyFn(ctx); + } catch (err) { + throw new Error(`Destroy hook of "${id}" errored`, { cause: err }); + } + }); + } + } + } + + // Create selectors + /** @type {TransformFn} */ + const toElem = (str) => { + const id = ctx.typeByStr.get(str); + return id === undefined ? 0 : id; + }; + /** @type {TransformFn} */ + const toAttr = (str) => { + const id = ctx.propByStr.get(str); + return id === undefined ? 0 : id; + }; + + /** @type {CompiledVisitor[]} */ + const visitors = []; + for (const [sel, info] of bySelector.entries()) { + // Selectors are already split here. + // TODO(@marvinhagemeister): Avoid array allocation (not sure if that matters) + const parsed = parseSelector(sel, toElem, toAttr)[0]; + const matcher = compileSelector(parsed); + + visitors.push({ info, matcher }); + } + + // Traverse ast with all visitors at the same time to avoid traversing + // multiple times. + try { + traverse(ctx, visitors, ctx.rootOffset); + } finally { + ctx.nodes.clear(); + + // Optional: Destroy rules + for (let i = 0; i < destroyFns.length; i++) { + destroyFns[i](); + } + } +} + +/** + * @param {AstContext} ctx + * @param {CompiledVisitor[]} visitors + * @param {number} idx + */ +function traverse(ctx, visitors, idx) { + if (idx === AST_IDX_INVALID) return; + + const { buf } = ctx; + const nodeType = readType(ctx.buf, idx); + + /** @type {VisitorFn[] | null} */ + let exits = null; + + // Only visit if it's an actual node + if (nodeType !== AST_GROUP_TYPE) { + // Loop over visitors and check if any selector matches + for (let i = 0; i < visitors.length; i++) { + const v = visitors[i]; + if (v.matcher(ctx.matcher, idx)) { + if (v.info.exit !== NOOP) { + if (exits === null) { + exits = [v.info.exit]; + } else { + exits.push(v.info.exit); + } + } + + if (v.info.enter !== NOOP) { + const node = /** @type {*} */ (getNode(ctx, idx)); + v.info.enter(node); + } + } + } + } + + try { + const childIdx = readChild(buf, idx); + if (childIdx > AST_IDX_INVALID) { + traverse(ctx, visitors, childIdx); + } + + const nextIdx = readNext(buf, idx); + if (nextIdx > AST_IDX_INVALID) { + traverse(ctx, visitors, nextIdx); + } + } finally { + if (exits !== null) { + for (let i = 0; i < exits.length; i++) { + const node = /** @type {*} */ (getNode(ctx, idx)); + exits[i](node); + } + } + } +} + +/** + * This is useful debugging helper to display the buffer's contents. + * @param {AstContext} ctx + */ +function _dump(ctx) { + const { buf, strTableOffset, strTable, strByType, strByProp } = ctx; + + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(strTable); + + for (let i = 0; i < strByType.length; i++) { + const v = strByType[i]; + // @ts-ignore dump fn + // deno-lint-ignore no-console + if (v > 0) console.log(" > type:", i, getString(ctx.strTable, v), v); + } + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(); + for (let i = 0; i < strByProp.length; i++) { + const v = strByProp[i]; + // @ts-ignore dump fn + // deno-lint-ignore no-console + if (v > 0) console.log(" > prop:", i, getString(ctx.strTable, v), v); + } + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(); + + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(); + + let idx = 0; + while (idx < (strTableOffset / NODE_SIZE)) { + const type = readType(buf, idx); + const child = readChild(buf, idx); + const next = readNext(buf, idx); + const parent = readParent(buf, idx); + const range = readSpan(ctx, idx); + + const name = type === AST_IDX_INVALID + ? "" + : type === AST_GROUP_TYPE + ? "" + : getString(ctx.strTable, ctx.strByType[type]); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(`${name}, idx: ${idx}, type: ${type}`); + + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` child: ${child}, next: ${next}, parent: ${parent}`); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` range: ${range[0]}, ${range[1]}`); + + const rawOffset = readRawPropOffset(ctx.buf, idx); + let propOffset = readPropOffset(ctx, idx); + const count = buf[propOffset++]; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log( + ` prop count: ${count}, prop offset: ${propOffset} raw offset: ${rawOffset}`, + ); + + for (let i = 0; i < count; i++) { + const prop = buf[propOffset++]; + const kind = buf[propOffset++]; + const name = getString(ctx.strTable, ctx.strByProp[prop]); + + let kindName = "unknown"; + for (const k in PropFlags) { + // @ts-ignore dump fn + if (kind === PropFlags[k]) { + kindName = k; + } + } + + const v = readU32(buf, propOffset); + propOffset += 4; + + if (kind === PropFlags.Ref) { + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${v} (${kindName}, ${prop})`); + } else if (kind === PropFlags.RefArr) { + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: RefArray: ${v}, (${kindName}, ${prop})`); + } else if (kind === PropFlags.Bool) { + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${v} (${kindName}, ${prop})`); + } else if (kind === PropFlags.String) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Number) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Regex) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Null) { + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: null (${kindName}, ${prop})`); + } else if (kind === PropFlags.Undefined) { + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: undefined (${kindName}, ${prop})`); + } else if (kind === PropFlags.BigInt) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Obj) { + let offset = v + ctx.propsOffset; + const count = readU32(ctx.buf, offset); + offset += 4; + + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log( + ` ${name}: Object (${count}) (${kindName}, ${prop}), raw offset ${v}`, + ); + + // TODO(@marvinhagemeister): Show object + } + } + + idx++; + } +} + +// TODO(bartlomieju): this is temporary, until we get plugins plumbed through +// the CLI linter +/** + * @param {LintPlugin} plugin + * @param {string} fileName + * @param {string} sourceText + */ +function runLintPlugin(plugin, fileName, sourceText) { + installPlugin(plugin); + + try { + const serializedAst = op_lint_create_serialized_ast(fileName, sourceText); + + runPluginsForFile(fileName, serializedAst); + } finally { + // During testing we don't want to keep plugins around + state.installedPlugins.clear(); + } +} + +// TODO(bartlomieju): this is temporary, until we get plugins plumbed through +// the CLI linter +internals.runLintPlugin = runLintPlugin; diff --git a/cli/js/40_lint_selector.js b/cli/js/40_lint_selector.js new file mode 100644 index 0000000000..362130076a --- /dev/null +++ b/cli/js/40_lint_selector.js @@ -0,0 +1,1029 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +// @ts-check + +/** @typedef {import("./40_lint_types.d.ts").LintState} LintState */ +/** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */ +/** @typedef {import("./40_lint_types.d.ts").MatchContext} MatchCtx */ +/** @typedef {import("./40_lint_types.d.ts").AttrExists} AttrExists */ +/** @typedef {import("./40_lint_types.d.ts").AttrBin} AttrBin */ +/** @typedef {import("./40_lint_types.d.ts").AttrSelector} AttrSelector */ +/** @typedef {import("./40_lint_types.d.ts").ElemSelector} ElemSelector */ +/** @typedef {import("./40_lint_types.d.ts").PseudoNthChild} PseudoNthChild */ +/** @typedef {import("./40_lint_types.d.ts").PseudoHas} PseudoHas */ +/** @typedef {import("./40_lint_types.d.ts").PseudoNot} PseudoNot */ +/** @typedef {import("./40_lint_types.d.ts").Relation} SRelation */ +/** @typedef {import("./40_lint_types.d.ts").Selector} Selector */ +/** @typedef {import("./40_lint_types.d.ts").SelectorParseCtx} SelectorParseCtx */ +/** @typedef {import("./40_lint_types.d.ts").NextFn} NextFn */ +/** @typedef {import("./40_lint_types.d.ts").MatcherFn} MatcherFn */ +/** @typedef {import("./40_lint_types.d.ts").TransformFn} Transformer */ + +const Char = { + Tab: 9, + Space: 32, + Bang: 33, + DoubleQuote: 34, + Quote: 39, + BraceOpen: 40, + BraceClose: 41, + Plus: 43, + Comma: 44, + Minus: 45, + Dot: 46, + Slash: 47, + n0: 49, + n9: 57, + Colon: 58, + Less: 60, + Equal: 61, + Greater: 62, + A: 65, + Z: 90, + BracketOpen: 91, + BackSlash: 92, + BracketClose: 93, + Underscore: 95, + a: 97, + z: 122, + Tilde: 126, +}; + +export const Token = { + EOF: 0, + Word: 1, + Space: 2, + Op: 3, + Colon: 4, + Comma: 7, + BraceOpen: 8, + BraceClose: 9, + BracketOpen: 10, + BracketClose: 11, + String: 12, + Number: 13, + Bool: 14, + Null: 15, + Undefined: 16, + Dot: 17, + Minus: 17, +}; + +export const BinOp = { + /** [attr="value"] or [attr=value] */ + Equal: 1, + /** [attr!="value"] or [attr!=value] */ + NotEqual: 2, + /** [attr>1] */ + Greater: 3, + /** [attr>=1] */ + GreaterThan: 4, + /** [attr<1] */ + Less: 5, + /** [attr<=1] */ + LessThan: 6, + Tilde: 7, + Plus: 8, + Space: 9, +}; + +/** + * @param {string} s + * @returns {number} + */ +function getAttrOp(s) { + switch (s) { + case "=": + return BinOp.Equal; + case "!=": + return BinOp.NotEqual; + case ">": + return BinOp.Greater; + case ">=": + return BinOp.GreaterThan; + case "<": + return BinOp.Less; + case "<=": + return BinOp.LessThan; + case "~": + return BinOp.Tilde; + case "+": + return BinOp.Plus; + default: + throw new Error(`Unknown attribute operator: '${s}'`); + } +} + +export class Lexer { + token = Token.Word; + start = 0; + end = 0; + ch = 0; + i = -1; + + value = ""; + + /** + * @param {string} input + */ + constructor(input) { + this.input = input; + this.step(); + this.next(); + } + + /** + * @param {number} token + */ + expect(token) { + if (this.token !== token) { + throw new Error( + `Expected token '${token}', but got '${this.token}'.\n\n${this.input}\n${ + " ".repeat(this.i) + }^`, + ); + } + } + + /** + * @param {number} token + */ + readAsWordUntil(token) { + const s = this.i; + while (this.token !== Token.EOF && this.token !== token) { + this.next(); + } + + this.start = s; + this.end = this.i - 1; + this.value = this.getSlice(); + } + + getSlice() { + return this.input.slice(this.start, this.end); + } + + step() { + this.i++; + if (this.i >= this.input.length) { + this.ch = -1; + } else { + this.ch = this.input.charCodeAt(this.i); + } + } + + next() { + this.value = ""; + + if (this.i >= this.input.length) { + this.token = Token.EOF; + return; + } + + // console.log( + // "NEXT", + // this.input, + // this.i, + // JSON.stringify(String.fromCharCode(this.ch)), + // ); + + while (true) { + switch (this.ch) { + case Char.Space: + while (this.isWhiteSpace()) { + this.step(); + } + + // Check if space preceeded operator + if (this.isOpContinue()) { + continue; + } + + this.token = Token.Space; + return; + case Char.BracketOpen: + this.token = Token.BracketOpen; + this.step(); + return; + case Char.BracketClose: + this.token = Token.BracketClose; + this.step(); + return; + case Char.BraceOpen: + this.token = Token.BraceOpen; + this.step(); + return; + case Char.BraceClose: + this.token = Token.BraceClose; + this.step(); + return; + case Char.Colon: + this.token = Token.Colon; + this.step(); + return; + case Char.Comma: + this.token = Token.Comma; + this.step(); + return; + case Char.Dot: + this.token = Token.Dot; + this.step(); + return; + case Char.Minus: + this.token = Token.Minus; + this.step(); + return; + + case Char.Plus: + case Char.Tilde: + case Char.Greater: + case Char.Equal: + case Char.Less: + case Char.Bang: { + this.token = Token.Op; + this.start = this.i; + this.step(); + + while (this.isOpContinue()) { + this.step(); + } + + this.end = this.i; + this.value = this.getSlice(); + + // Consume remaining space + while (this.isWhiteSpace()) { + this.step(); + } + + return; + } + + case Char.Quote: + case Char.DoubleQuote: { + this.token = Token.String; + const ch = this.ch; + + this.step(); + this.start = this.i; + + while (this.ch > 0 && this.ch !== ch) { + this.step(); + } + + this.end = this.i; + this.value = this.getSlice(); + this.step(); + + return; + } + + default: + this.start = this.i; + this.step(); + + while (this.isWordContinue()) { + this.step(); + } + + this.end = this.i; + this.value = this.getSlice(); + this.token = Token.Word; + return; + } + } + } + + isWordContinue() { + const ch = this.ch; + switch (ch) { + case Char.Minus: + case Char.Underscore: + return true; + default: + return (ch >= Char.a && ch <= Char.z) || + (ch >= Char.A && ch <= Char.Z) || + (ch >= Char.n0 && ch <= Char.n9); + } + } + + isOpContinue() { + const ch = this.ch; + switch (ch) { + case Char.Equal: + case Char.Bang: + case Char.Greater: + case Char.Less: + case Char.Tilde: + case Char.Plus: + return true; + default: + return false; + } + } + + isWhiteSpace() { + return this.ch === Char.Space || this.ch === Char.Tab; + } +} + +const NUMBER_REG = /^(\d+\.)?\d+$/; +const BIGINT_REG = /^\d+n$/; + +/** + * @param {string} raw + * @returns {any} + */ +function getFromRawValue(raw) { + switch (raw) { + case "true": + return true; + case "false": + return false; + case "null": + return null; + case "undefined": + return undefined; + default: + if (raw.startsWith("'") && raw.endsWith("'")) { + if (raw.length === 2) return ""; + return raw.slice(1, -1); + } else if (raw.startsWith('"') && raw.endsWith('"')) { + if (raw.length === 2) return ""; + return raw.slice(1, -1); + } else if (raw.startsWith("/")) { + const end = raw.lastIndexOf("/"); + if (end === -1) throw new Error(`Invalid RegExp pattern: ${raw}`); + const pattern = raw.slice(1, end); + const flags = end < raw.length - 1 ? raw.slice(end + 1) : undefined; + return new RegExp(pattern, flags); + } else if (NUMBER_REG.test(raw)) { + return Number(raw); + } else if (BIGINT_REG.test(raw)) { + return BigInt(raw.slice(0, -1)); + } + + return raw; + } +} + +export const ELEM_NODE = 1; +export const RELATION_NODE = 2; +export const ATTR_EXISTS_NODE = 3; +export const ATTR_BIN_NODE = 4; +export const PSEUDO_NTH_CHILD = 5; +export const PSEUDO_HAS = 6; +export const PSEUDO_NOT = 7; +export const PSEUDO_FIRST_CHILD = 8; +export const PSEUDO_LAST_CHILD = 9; + +/** + * Parse out all unique selectors of a selector list. + * @param {string} input + * @returns {string[]} + */ +export function splitSelectors(input) { + /** @type {string[]} */ + const out = []; + + let last = 0; + let depth = 0; + for (let i = 0; i < input.length; i++) { + const ch = input.charCodeAt(i); + switch (ch) { + case Char.BraceOpen: + depth++; + break; + case Char.BraceClose: + depth--; + break; + case Char.Comma: + if (depth === 0) { + out.push(input.slice(last, i).trim()); + last = i + 1; + } + break; + } + } + + const remaining = input.slice(last).trim(); + if (remaining.length > 0) { + out.push(remaining); + } + + return out; +} + +/** + * @param {string} input + * @param {Transformer} toElem + * @param {Transformer} toAttr + * @returns {Selector[]} + */ +export function parseSelector(input, toElem, toAttr) { + /** @type {Selector[]} */ + const result = []; + + /** @type {Selector[]} */ + const stack = [[]]; + + const lex = new Lexer(input); + + // Some subselectors like `:nth-child(.. of )` must have + // a single selector instead of selector list. + let throwOnComma = false; + + while (lex.token !== Token.EOF) { + const current = /** @type {Selector} */ (stack.at(-1)); + + if (lex.token === Token.Word) { + const value = lex.value; + const wildcard = value === "*"; + + const elem = !wildcard ? toElem(value) : 0; + current.push({ + type: ELEM_NODE, + elem, + wildcard, + }); + lex.next(); + + continue; + } else if (lex.token === Token.Space) { + lex.next(); + + if (lex.token === Token.Word) { + current.push({ + type: RELATION_NODE, + op: BinOp.Space, + }); + } + + continue; + } else if (lex.token === Token.BracketOpen) { + lex.next(); + lex.expect(Token.Word); + + // Check for value comparison + const prop = [toAttr(lex.value)]; + lex.next(); + + while (lex.token === Token.Dot) { + lex.next(); + lex.expect(Token.Word); + + prop.push(toAttr(lex.value)); + lex.next(); + } + + if (lex.token === Token.Op) { + const op = getAttrOp(lex.value); + lex.readAsWordUntil(Token.BracketClose); + + const value = getFromRawValue(lex.value); + current.push({ type: ATTR_BIN_NODE, prop, op, value }); + } else { + current.push({ + type: ATTR_EXISTS_NODE, + prop, + }); + } + + lex.expect(Token.BracketClose); + lex.next(); + continue; + } else if (lex.token === Token.Colon) { + lex.next(); + lex.expect(Token.Word); + + switch (lex.value) { + case "first-child": + current.push({ + type: PSEUDO_FIRST_CHILD, + }); + break; + case "last-child": + current.push({ + type: PSEUDO_LAST_CHILD, + }); + break; + case "nth-child": { + lex.next(); + lex.expect(Token.BraceOpen); + lex.next(); + + let mul = 1; + let repeat = false; + let step = 0; + if (lex.token === Token.Minus) { + mul = -1; + lex.next(); + } + + lex.expect(Token.Word); + const value = lex.getSlice(); + + if (value.endsWith("n")) { + repeat = true; + step = +value.slice(0, -1) * mul; + } else { + step = +value * mul; + } + + lex.next(); + + /** @type {PseudoNthChild} */ + const node = { + type: PSEUDO_NTH_CHILD, + of: null, + op: null, + step, + stepOffset: 0, + repeat, + }; + current.push(node); + + if (lex.token === Token.Space) lex.next(); + + if (lex.token !== Token.BraceClose) { + if (lex.token === Token.Op) { + node.op = lex.value; + lex.next(); + + if (lex.token === Token.Space) lex.next(); + } else if (lex.token === Token.Minus) { + node.op = "-"; + lex.next(); + + if (lex.token === Token.Space) { + lex.next(); + } + } + + lex.expect(Token.Word); + node.stepOffset = +lex.value; + lex.next(); + + if (lex.token !== Token.BraceClose) { + lex.next(); // Space + + if (lex.token === Token.Word) { + if (/** @type {string} */ (lex.value) !== "of") { + throw new Error( + `Expected 'of' keyword in ':nth-child' but got: ${lex.value}`, + ); + } + + lex.next(); + lex.expect(Token.Space); + lex.next(); + throwOnComma = true; + stack.push([]); + } + + continue; + } + + lex.expect(Token.BraceClose); + } else if (!node.repeat) { + // :nth-child(2) -> step is actually stepOffset + node.stepOffset = node.step - 1; + node.step = 0; + } + + lex.next(); + + continue; + } + + case "has": + case "where": + case "is": { + lex.next(); + lex.expect(Token.BraceOpen); + lex.next(); + + current.push({ + type: PSEUDO_HAS, + selectors: [], + }); + stack.push([]); + + continue; + } + case "not": { + lex.next(); + lex.expect(Token.BraceOpen); + lex.next(); + + current.push({ + type: PSEUDO_NOT, + selectors: [], + }); + stack.push([]); + + continue; + } + default: + throw new Error(`Unknown pseudo selector: '${lex.value}'`); + } + } else if (lex.token === Token.Comma) { + if (throwOnComma) { + throw new Error(`Multiple selector arguments not supported here`); + } + + lex.next(); + if (lex.token === Token.Space) { + lex.next(); + } + + popSelector(result, stack); + stack.push([]); + continue; + } else if (lex.token === Token.BraceClose) { + throwOnComma = false; + popSelector(result, stack); + } else if (lex.token === Token.Op) { + current.push({ + type: RELATION_NODE, + op: getAttrOp(lex.value), + }); + } + + lex.next(); + } + + if (stack.length > 0) { + result.push(stack[0]); + } + + return result; +} + +/** + * @param {Selector[]} result + * @param {Selector[]} stack + */ +function popSelector(result, stack) { + const sel = /** @type {Selector} */ (stack.pop()); + + if (stack.length === 0) { + result.push(sel); + stack.push([]); + } else { + const prev = /** @type {Selector} */ (stack.at(-1)); + if (prev.length === 0) { + throw new Error(`Empty selector`); + } + + const node = prev.at(-1); + if (node === undefined) { + throw new Error(`Empty node`); + } + + if (node.type === PSEUDO_NTH_CHILD) { + node.of = sel; + } else if (node.type === PSEUDO_HAS || node.type === PSEUDO_NOT) { + node.selectors.push(sel); + } else { + throw new Error(`Multiple selectors not allowed here`); + } + } +} + +const TRUE_FN = () => { + return true; +}; + +/** + * @param {Selector} selector + * @returns {MatcherFn} + */ +export function compileSelector(selector) { + /** @type {MatcherFn} */ + let fn = TRUE_FN; + + for (let i = 0; i < selector.length; i++) { + const node = selector[i]; + + switch (node.type) { + case ELEM_NODE: + fn = matchElem(node, fn); + break; + case RELATION_NODE: + switch (node.op) { + case BinOp.Space: + fn = matchDescendant(fn); + break; + case BinOp.Greater: + fn = matchChild(fn); + break; + case BinOp.Plus: + fn = matchAdjacent(fn); + break; + case BinOp.Tilde: + fn = matchFollowing(fn); + break; + default: + throw new Error(`Unknown relation op ${node.op}`); + } + break; + case ATTR_EXISTS_NODE: + fn = matchAttrExists(node, fn); + break; + case ATTR_BIN_NODE: + fn = matchAttrBin(node, fn); + break; + case PSEUDO_FIRST_CHILD: + fn = matchFirstChild(fn); + break; + case PSEUDO_LAST_CHILD: + fn = matchLastChild(fn); + break; + case PSEUDO_NTH_CHILD: + fn = matchNthChild(node, fn); + break; + case PSEUDO_HAS: + // TODO(@marvinhagemeister) + throw new Error("TODO: :has"); + case PSEUDO_NOT: + fn = matchNot(node.selectors, fn); + break; + default: + // @ts-ignore error handling + // deno-lint-ignore no-console + console.log(node); + throw new Error(`Unknown selector node`); + } + } + + return fn; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchFirstChild(next) { + return (ctx, id) => { + const first = ctx.getFirstChild(id); + return first === id && next(ctx, first); + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchLastChild(next) { + return (ctx, id) => { + const last = ctx.getLastChild(id); + return last === id && next(ctx, id); + }; +} + +/** + * @param {PseudoNthChild} node + * @param {number} i + * @returns {number} + */ +function getNthAnB(node, i) { + const n = node.step * i; + + if (node.op === null) return n; + + switch (node.op) { + case "+": + return n + node.stepOffset; + case "-": + return n - node.stepOffset; + default: + throw new Error("Not supported nth-child operator: " + node.op); + } +} + +/** + * @param {PseudoNthChild} node + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchNthChild(node, next) { + const ofSelector = node.of !== null ? compileSelector(node.of) : TRUE_FN; + + // TODO(@marvinhagemeister): we should probably cache results here + + return (ctx, id) => { + const siblings = ctx.getSiblings(id); + const idx = siblings.indexOf(id); + + if (!node.repeat) { + return idx === node.stepOffset && next(ctx, id); + } + + for (let i = 0; i < siblings.length; i++) { + const n = getNthAnB(node, i); + + if (n > siblings.length - 1) return false; + + const search = siblings[n]; + if (id === search) { + if (node.of !== null && !ofSelector(ctx, id)) { + continue; + } else if (next(ctx, id)) { + return true; + } + } else if (n > idx) { + return false; + } + } + + return false; + }; +} + +/** + * @param {Selector[]} selectors + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchNot(selectors, next) { + /** @type {MatcherFn[]} */ + const compiled = []; + + for (let i = 0; i < selectors.length; i++) { + const sel = selectors[i]; + compiled.push(compileSelector(sel)); + } + + return (ctx, id) => { + for (let i = 0; i < compiled.length; i++) { + const fn = compiled[i]; + if (fn(ctx, id)) { + return false; + } + } + + return next(ctx, id); + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchDescendant(next) { + // TODO(@marvinhagemeister): we should probably cache results here + return (ctx, id) => { + let current = ctx.getParent(id); + while (current > 0) { + if (next(ctx, current)) { + return true; + } + + current = ctx.getParent(current); + } + + return false; + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchChild(next) { + return (ctx, id) => { + const parent = ctx.getParent(id); + if (parent < 0) return false; + + return next(ctx, parent); + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchAdjacent(next) { + return (ctx, id) => { + const siblings = ctx.getSiblings(id); + const idx = siblings.indexOf(id) - 1; + + if (idx < 0) return false; + + const prev = siblings[idx]; + return next(ctx, prev); + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchFollowing(next) { + return (ctx, id) => { + const siblings = ctx.getSiblings(id); + const idx = siblings.indexOf(id) - 1; + + if (idx < 0) return false; + + for (let i = idx; i >= 0; i--) { + const sib = siblings[i]; + if (next(ctx, sib)) return true; + } + + return false; + }; +} + +/** + * @param {ElemSelector} part + * @param {MatcherFn} next + * @returns {MatcherFn} + */ +function matchElem(part, next) { + return (ctx, id) => { + // Placeholder node cannot be matched + if (id === 0) return false; + // Wildcard always matches + else if (part.wildcard) return next(ctx, id); + // 0 means it's the placeholder node which + // can never be matched. + else if (part.elem === 0) return false; + + const type = ctx.getType(id); + if (type > 0 && type === part.elem) { + return next(ctx, id); + } + + return false; + }; +} + +/** + * @param {AttrExists} attr + * @param {MatcherFn} next + * @returns {MatcherFn} + */ +function matchAttrExists(attr, next) { + return (ctx, id) => { + try { + ctx.getAttrPathValue(id, attr.prop, 0); + return next(ctx, id); + } catch (err) { + if (err === -1) { + return false; + } + + throw err; + } + }; +} + +/** + * @param {AttrBin} attr + * @param {MatcherFn} next + * @returns {MatcherFn} + */ +function matchAttrBin(attr, next) { + return (ctx, id) => { + try { + const value = ctx.getAttrPathValue(id, attr.prop, 0); + if (!matchAttrValue(attr, value)) return false; + } catch (err) { + if (err === -1) { + return false; + } + throw err; + } + return next(ctx, id); + }; +} + +/** + * @param {AttrBin} attr + * @param {*} value + * @returns {boolean} + */ +function matchAttrValue(attr, value) { + switch (attr.op) { + case BinOp.Equal: + return value === attr.value; + case BinOp.NotEqual: + return value !== attr.value; + case BinOp.Greater: + return typeof value === "number" && typeof attr.value === "number" && + value > attr.value; + case BinOp.GreaterThan: + return typeof value === "number" && typeof attr.value === "number" && + value >= attr.value; + case BinOp.Less: + return typeof value === "number" && typeof attr.value === "number" && + value < attr.value; + case BinOp.LessThan: + return typeof value === "number" && typeof attr.value === "number" && + value <= attr.value; + default: + return false; + } +} diff --git a/cli/js/40_lint_types.d.ts b/cli/js/40_lint_types.d.ts new file mode 100644 index 0000000000..f07d16581e --- /dev/null +++ b/cli/js/40_lint_types.d.ts @@ -0,0 +1,139 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +export interface NodeFacade { + type: string; + range: [number, number]; + [key: string]: unknown; +} + +export interface AstContext { + buf: Uint8Array; + strTable: Map; + strTableOffset: number; + rootOffset: number; + nodes: Map; + spansOffset: number; + propsOffset: number; + strByType: number[]; + strByProp: number[]; + typeByStr: Map; + propByStr: Map; + matcher: MatchContext; +} + +export interface Node { + range: Range; +} + +export type Range = [number, number]; + +// TODO(@marvinhagemeister) Remove once we land "official" types +export interface RuleContext { + id: string; +} + +// TODO(@marvinhagemeister) Remove once we land "official" types +export interface LintRule { + create(ctx: RuleContext): Record void>; + destroy?(ctx: RuleContext): void; +} + +// TODO(@marvinhagemeister) Remove once we land "official" types +export interface LintPlugin { + name: string; + rules: Record; +} + +export interface LintState { + plugins: LintPlugin[]; + installedPlugins: Set; +} + +export type VisitorFn = (node: unknown) => void; + +export interface CompiledVisitor { + matcher: (ctx: MatchContext, offset: number) => boolean; + info: { enter: VisitorFn; exit: VisitorFn }; +} + +export interface AttrExists { + type: 3; + prop: number[]; +} + +export interface AttrBin { + type: 4; + prop: number[]; + op: number; + // deno-lint-ignore no-explicit-any + value: any; +} + +export type AttrSelector = AttrExists | AttrBin; + +export interface ElemSelector { + type: 1; + wildcard: boolean; + elem: number; +} + +export interface PseudoNthChild { + type: 5; + op: string | null; + step: number; + stepOffset: number; + of: Selector | null; + repeat: boolean; +} + +export interface PseudoHas { + type: 6; + selectors: Selector[]; +} +export interface PseudoNot { + type: 7; + selectors: Selector[]; +} +export interface PseudoFirstChild { + type: 8; +} +export interface PseudoLastChild { + type: 9; +} + +export interface Relation { + type: 2; + op: number; +} + +export type Selector = Array< + | ElemSelector + | Relation + | AttrExists + | AttrBin + | PseudoNthChild + | PseudoNot + | PseudoHas + | PseudoFirstChild + | PseudoLastChild +>; + +export interface SelectorParseCtx { + root: Selector; + current: Selector; +} + +export interface MatchContext { + getFirstChild(id: number): number; + getLastChild(id: number): number; + getSiblings(id: number): number[]; + getParent(id: number): number; + getType(id: number): number; + getAttrPathValue(id: number, propIds: number[], idx: number): unknown; +} + +export type NextFn = (ctx: MatchContext, id: number) => boolean; +export type MatcherFn = (ctx: MatchContext, id: number) => boolean; +export type TransformFn = (value: string) => number; + +export {}; diff --git a/cli/js/40_test.js b/cli/js/40_test.js index ea526a17d4..3dbb7ec340 100644 --- a/cli/js/40_test.js +++ b/cli/js/40_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { escapeName, withPermissions } from "ext:cli/40_test_common.js"; @@ -26,7 +26,7 @@ const { TypeError, } = primordials; -import { setExitHandler } from "ext:runtime/30_os.js"; +import { setExitHandler } from "ext:deno_os/30_os.js"; // Capture `Deno` global so that users deleting or mangling it, won't // have impact on our sanitizers. diff --git a/cli/js/40_test_common.js b/cli/js/40_test_common.js index 7711148f1e..6b7828cd2d 100644 --- a/cli/js/40_test_common.js +++ b/cli/js/40_test_common.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { serializePermissions } from "ext:runtime/10_permissions.js"; const ops = core.ops; diff --git a/cli/jsr.rs b/cli/jsr.rs index 767d304d60..34a2ec04e4 100644 --- a/cli/jsr.rs +++ b/cli/jsr.rs @@ -1,14 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::sync::Arc; -use crate::args::jsr_url; -use crate::file_fetcher::FileFetcher; use dashmap::DashMap; use deno_core::serde_json; use deno_graph::packages::JsrPackageInfo; use deno_graph::packages::JsrPackageVersionInfo; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; -use std::sync::Arc; + +use crate::args::jsr_url; +use crate::file_fetcher::CliFileFetcher; /// This is similar to a subset of `JsrCacheResolver` which fetches rather than /// just reads the cache. Keep in sync! @@ -19,11 +21,11 @@ pub struct JsrFetchResolver { /// It can be large and we don't want to store it. info_by_nv: DashMap>>, info_by_name: DashMap>>, - file_fetcher: Arc, + file_fetcher: Arc, } impl JsrFetchResolver { - pub fn new(file_fetcher: Arc) -> Self { + pub fn new(file_fetcher: Arc) -> Self { Self { nv_by_req: Default::default(), info_by_nv: Default::default(), diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index c128372dcd..f8f382f594 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -1,4 +1,46 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cmp::Ordering; +use std::collections::HashMap; +use std::collections::HashSet; +use std::path::Path; + +use deno_ast::SourceRange; +use deno_ast::SourceRangedForSpanned; +use deno_ast::SourceTextInfo; +use deno_config::workspace::MappedResolution; +use deno_core::error::AnyError; +use deno_core::serde::Deserialize; +use deno_core::serde::Serialize; +use deno_core::serde_json; +use deno_core::serde_json::json; +use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; +use deno_lint::diagnostic::LintDiagnosticRange; +use deno_path_util::url_to_file_path; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_runtime::deno_node::PathClean; +use deno_semver::jsr::JsrPackageNvReference; +use deno_semver::jsr::JsrPackageReqReference; +use deno_semver::npm::NpmPackageReqReference; +use deno_semver::package::PackageNv; +use deno_semver::package::PackageNvReference; +use deno_semver::package::PackageReq; +use deno_semver::package::PackageReqReference; +use deno_semver::SmallStackString; +use deno_semver::StackString; +use deno_semver::Version; +use import_map::ImportMap; +use node_resolver::InNpmPackageChecker; +use node_resolver::NodeResolutionKind; +use node_resolver::ResolutionMode; +use once_cell::sync::Lazy; +use regex::Regex; +use text_lines::LineAndColumnIndex; +use tower_lsp::lsp_types as lsp; +use tower_lsp::lsp_types::Position; +use tower_lsp::lsp_types::Range; use super::diagnostics::DenoDiagnostic; use super::diagnostics::DiagnosticSource; @@ -8,49 +50,11 @@ use super::language_server; use super::resolver::LspResolver; use super::tsc; use super::urls::url_to_uri; - use crate::args::jsr_url; use crate::lsp::logging::lsp_warn; use crate::lsp::search::PackageSearchApi; use crate::tools::lint::CliLinter; use crate::util::path::relative_specifier; -use deno_config::workspace::MappedResolution; -use deno_lint::diagnostic::LintDiagnosticRange; - -use deno_ast::SourceRange; -use deno_ast::SourceRangedForSpanned; -use deno_ast::SourceTextInfo; -use deno_core::error::custom_error; -use deno_core::error::AnyError; -use deno_core::serde::Deserialize; -use deno_core::serde::Serialize; -use deno_core::serde_json; -use deno_core::serde_json::json; -use deno_core::ModuleSpecifier; -use deno_path_util::url_to_file_path; -use deno_runtime::deno_node::PathClean; -use deno_semver::jsr::JsrPackageNvReference; -use deno_semver::jsr::JsrPackageReqReference; -use deno_semver::npm::NpmPackageReqReference; -use deno_semver::package::PackageNv; -use deno_semver::package::PackageNvReference; -use deno_semver::package::PackageReq; -use deno_semver::package::PackageReqReference; -use deno_semver::Version; -use import_map::ImportMap; -use node_resolver::NodeResolutionKind; -use node_resolver::ResolutionMode; -use once_cell::sync::Lazy; -use regex::Regex; -use std::borrow::Cow; -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; /// Diagnostic error codes which actually are the same, and so when grouping /// fixes we treat them the same. @@ -270,13 +274,24 @@ impl<'a> TsResponseImportMapper<'a> { } } + if specifier.scheme() == "node" { + return Some(specifier.to_string()); + } + if let Some(jsr_path) = specifier.as_str().strip_prefix(jsr_url().as_str()) { let mut segments = jsr_path.split('/'); let name = if jsr_path.starts_with('@') { - format!("{}/{}", segments.next()?, segments.next()?) + let scope = segments.next()?; + let name = segments.next()?; + capacity_builder::StringBuilder::::build(|builder| { + builder.append(scope); + builder.append("/"); + builder.append(name); + }) + .unwrap() } else { - segments.next()?.to_string() + StackString::from(segments.next()?) }; let version = Version::parse_standard(segments.next()?).ok()?; let nv = PackageNv { name, version }; @@ -286,7 +301,9 @@ impl<'a> TsResponseImportMapper<'a> { &path, Some(&self.file_referrer), )?; - let sub_path = (export != ".").then_some(export); + let sub_path = (export != ".") + .then_some(export) + .map(SmallStackString::from_string); let mut req = None; req = req.or_else(|| { let import_map = self.maybe_import_map?; @@ -350,7 +367,9 @@ impl<'a> TsResponseImportMapper<'a> { if let Ok(Some(pkg_id)) = npm_resolver.resolve_pkg_id_from_specifier(specifier) { - let pkg_reqs = npm_resolver.resolve_pkg_reqs_from_pkg_id(&pkg_id); + let pkg_reqs = npm_resolver + .resolution() + .resolve_pkg_reqs_from_pkg_id(&pkg_id); // check if any pkg reqs match what is found in an import map if !pkg_reqs.is_empty() { let sub_path = npm_resolver @@ -599,18 +618,24 @@ fn try_reverse_map_package_json_exports( /// For a set of tsc changes, can them for any that contain something that looks /// like an import and rewrite the import specifier to include the extension pub fn fix_ts_import_changes( - referrer: &ModuleSpecifier, - resolution_mode: ResolutionMode, changes: &[tsc::FileTextChanges], language_server: &language_server::Inner, ) -> Result, AnyError> { - let import_mapper = language_server.get_ts_response_import_mapper(referrer); let mut r = Vec::new(); for change in changes { + let Ok(referrer) = ModuleSpecifier::parse(&change.file_name) else { + continue; + }; + let referrer_doc = language_server.get_asset_or_document(&referrer).ok(); + let resolution_mode = referrer_doc + .as_ref() + .map(|d| d.resolution_mode()) + .unwrap_or(ResolutionMode::Import); + let import_mapper = + language_server.get_ts_response_import_mapper(&referrer); let mut text_changes = Vec::new(); for text_change in &change.text_changes { let lines = text_change.new_text.split('\n'); - let new_lines: Vec = lines .map(|line| { // This assumes that there's only one import per line. @@ -618,7 +643,7 @@ pub fn fix_ts_import_changes( let specifier = captures.iter().skip(1).find_map(|s| s).unwrap().as_str(); if let Some(new_specifier) = import_mapper - .check_unresolved_specifier(specifier, referrer, resolution_mode) + .check_unresolved_specifier(specifier, &referrer, resolution_mode) { line.replace(specifier, &new_specifier) } else { @@ -1049,10 +1074,13 @@ impl CodeActionCollection { // we wrap tsc, we can't handle the asynchronous response, so it is // actually easier to return errors if we ever encounter one of these, // which we really wouldn't expect from the Deno lsp. - return Err(custom_error( - "UnsupportedFix", - "The action returned from TypeScript is unsupported.", - )); + return Err( + JsErrorBox::new( + "UnsupportedFix", + "The action returned from TypeScript is unsupported.", + ) + .into(), + ); } let Some(action) = fix_ts_import_action(specifier, resolution_mode, action, language_server) @@ -1271,6 +1299,19 @@ impl CodeActionCollection { range: &lsp::Range, language_server: &language_server::Inner, ) -> Option { + fn top_package_req_for_name( + resolution: &NpmResolutionCell, + name: &str, + ) -> Option { + let package_reqs = resolution.package_reqs(); + let mut entries = package_reqs + .into_iter() + .filter(|(_, nv)| nv.name == name) + .collect::>(); + entries.sort_by(|a, b| a.1.version.cmp(&b.1.version)); + Some(entries.pop()?.0) + } + let (dep_key, dependency, _) = document.get_maybe_dependency(&range.end)?; if dependency.maybe_deno_types_specifier.is_some() { @@ -1358,9 +1399,10 @@ impl CodeActionCollection { .and_then(|versions| versions.first().cloned())?; let types_specifier_text = if let Some(npm_resolver) = managed_npm_resolver { - let mut specifier_text = if let Some(req) = - npm_resolver.top_package_req_for_name(&types_package_name) - { + let mut specifier_text = if let Some(req) = top_package_req_for_name( + npm_resolver.resolution(), + &types_package_name, + ) { format!("npm:{req}") } else { format!("npm:{}@^{}", &types_package_name, types_package_version) diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs index fbf9ea6f1b..97fbbaff14 100644 --- a/cli/lsp/cache.rs +++ b/cli/lsp/cache.rs @@ -1,4 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::BTreeMap; +use std::fs; +use std::path::Path; +use std::sync::Arc; +use std::time::SystemTime; + +use deno_core::url::Url; +use deno_core::ModuleSpecifier; +use deno_path_util::url_to_file_path; use crate::cache::DenoDir; use crate::cache::GlobalHttpCache; @@ -7,15 +17,7 @@ use crate::cache::LocalLspHttpCache; use crate::lsp::config::Config; use crate::lsp::logging::lsp_log; use crate::lsp::logging::lsp_warn; - -use deno_core::url::Url; -use deno_core::ModuleSpecifier; -use deno_path_util::url_to_file_path; -use std::collections::BTreeMap; -use std::fs; -use std::path::Path; -use std::sync::Arc; -use std::time::SystemTime; +use crate::sys::CliSys; pub fn calculate_fs_version( cache: &LspCache, @@ -91,12 +93,11 @@ impl LspCache { }) .ok() }); - let deno_dir = DenoDir::new(global_cache_path) + let sys = CliSys::default(); + let deno_dir = DenoDir::new(sys.clone(), global_cache_path) .expect("should be infallible with absolute custom root"); - let global = Arc::new(GlobalHttpCache::new( - deno_dir.remote_folder_path(), - crate::cache::RealDenoCacheEnv, - )); + let global = + Arc::new(GlobalHttpCache::new(sys, deno_dir.remote_folder_path())); Self { deno_dir, global, diff --git a/cli/lsp/capabilities.rs b/cli/lsp/capabilities.rs index 5cdb1224d8..4c81edeea2 100644 --- a/cli/lsp/capabilities.rs +++ b/cli/lsp/capabilities.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! //! Provides information about what capabilities that are supported by the diff --git a/cli/lsp/client.rs b/cli/lsp/client.rs index 65865d5b32..85ea1bb4e5 100644 --- a/cli/lsp/client.rs +++ b/cli/lsp/client.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; @@ -12,12 +12,11 @@ use lsp_types::Uri; use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types::ConfigurationItem; -use crate::lsp::repl::get_repl_workspace_settings; - use super::config::WorkspaceSettings; use super::config::SETTINGS_SECTION; use super::lsp_custom; use super::testing::lsp_custom as testing_lsp_custom; +use crate::lsp::repl::get_repl_workspace_settings; #[derive(Debug)] pub enum TestingNotification { diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index a57ca3ac9f..bb72d0db77 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -1,13 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::lsp::logging::lsp_warn; - -use super::analysis::source_range_to_lsp_range; -use super::config::CodeLensSettings; -use super::language_server; -use super::text::LineIndex; -use super::tsc; -use super::tsc::NavigationTree; +use std::cell::RefCell; +use std::collections::HashSet; +use std::rc::Rc; +use std::sync::Arc; use deno_ast::swc::ast; use deno_ast::swc::visit::Visit; @@ -25,13 +21,17 @@ use deno_core::ModuleSpecifier; use lazy_regex::lazy_regex; use once_cell::sync::Lazy; use regex::Regex; -use std::cell::RefCell; -use std::collections::HashSet; -use std::rc::Rc; -use std::sync::Arc; use tower_lsp::jsonrpc::Error as LspError; use tower_lsp::lsp_types as lsp; +use super::analysis::source_range_to_lsp_range; +use super::config::CodeLensSettings; +use super::language_server; +use super::text::LineIndex; +use super::tsc; +use super::tsc::NavigationTree; +use crate::lsp::logging::lsp_warn; + static ABSTRACT_MODIFIER: Lazy = lazy_regex!(r"\babstract\b"); static EXPORT_MODIFIER: Lazy = lazy_regex!(r"\bexport\b"); diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs index 31f0b066ed..7309d984b4 100644 --- a/cli/lsp/completions.rs +++ b/cli/lsp/completions.rs @@ -1,4 +1,26 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_ast::LineAndColumnIndex; +use deno_ast::SourceTextInfo; +use deno_core::resolve_path; +use deno_core::resolve_url; +use deno_core::serde::Deserialize; +use deno_core::serde::Serialize; +use deno_core::serde_json::json; +use deno_core::url::Position; +use deno_core::ModuleSpecifier; +use deno_path_util::url_to_file_path; +use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES; +use deno_semver::jsr::JsrPackageReqReference; +use deno_semver::package::PackageNv; +use import_map::ImportMap; +use indexmap::IndexSet; +use lsp_types::CompletionList; +use node_resolver::NodeResolutionKind; +use node_resolver::ResolutionMode; +use once_cell::sync::Lazy; +use regex::Regex; +use tower_lsp::lsp_types as lsp; use super::client::Client; use super::config::Config; @@ -12,33 +34,10 @@ use super::registries::ModuleRegistry; use super::resolver::LspResolver; use super::search::PackageSearchApi; use super::tsc; - use crate::graph_util::to_node_resolution_mode; use crate::jsr::JsrFetchResolver; use crate::util::path::is_importable_ext; use crate::util::path::relative_specifier; -use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES; - -use deno_ast::LineAndColumnIndex; -use deno_ast::SourceTextInfo; -use deno_core::resolve_path; -use deno_core::resolve_url; -use deno_core::serde::Deserialize; -use deno_core::serde::Serialize; -use deno_core::serde_json::json; -use deno_core::url::Position; -use deno_core::ModuleSpecifier; -use deno_path_util::url_to_file_path; -use deno_semver::jsr::JsrPackageReqReference; -use deno_semver::package::PackageNv; -use import_map::ImportMap; -use indexmap::IndexSet; -use lsp_types::CompletionList; -use node_resolver::NodeResolutionKind; -use node_resolver::ResolutionMode; -use once_cell::sync::Lazy; -use regex::Regex; -use tower_lsp::lsp_types as lsp; static FILE_PROTO_RE: Lazy = lazy_regex::lazy_regex!(r#"^file:/{2}(?:/[A-Za-z]:)?"#); @@ -822,16 +821,18 @@ fn get_workspace_completions( #[cfg(test)] mod tests { + use std::collections::HashMap; + + use deno_core::resolve_url; + use pretty_assertions::assert_eq; + use test_util::TempDir; + use super::*; use crate::cache::HttpCache; use crate::lsp::cache::LspCache; use crate::lsp::documents::Documents; use crate::lsp::documents::LanguageId; use crate::lsp::search::tests::TestPackageSearchApi; - use deno_core::resolve_url; - use pretty_assertions::assert_eq; - use std::collections::HashMap; - use test_util::TempDir; fn setup( open_sources: &[(&str, &str, i32, LanguageId)], diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index a629757788..ba57502298 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1,4 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::ops::Deref; +use std::ops::DerefMut; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; use deno_ast::MediaType; use deno_config::deno_json::DenoJsonCache; @@ -9,8 +18,6 @@ use deno_config::deno_json::LintConfig; use deno_config::deno_json::NodeModulesDirMode; use deno_config::deno_json::TestConfig; use deno_config::deno_json::TsConfig; -use deno_config::fs::DenoConfigFs; -use deno_config::fs::RealDenoConfigFs; use deno_config::glob::FilePatterns; use deno_config::glob::PathOrPatternSet; use deno_config::workspace::CreateResolverOptions; @@ -38,18 +45,10 @@ use deno_lint::linter::LintConfig as DenoLintConfig; use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonCache; use deno_path_util::url_to_file_path; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_runtime::deno_node::PackageJson; use indexmap::IndexSet; use lsp_types::ClientCapabilities; -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::ops::Deref; -use std::ops::DerefMut; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use tower_lsp::lsp_types as lsp; use super::logging::lsp_log; @@ -63,10 +62,10 @@ use crate::args::ConfigFile; use crate::args::LintFlags; use crate::args::LintOptions; use crate::cache::FastInsecureHasher; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::lsp::logging::lsp_warn; use crate::resolver::CliSloppyImportsResolver; -use crate::resolver::SloppyImportsCachedFs; +use crate::sys::CliSys; use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinterOptions; use crate::tools::lint::LintRuleProvider; @@ -459,6 +458,19 @@ impl Default for LanguagePreferences { } } +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct SuggestionActionsSettings { + #[serde(default = "is_true")] + pub enabled: bool, +} + +impl Default for SuggestionActionsSettings { + fn default() -> Self { + SuggestionActionsSettings { enabled: true } + } +} + #[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct UpdateImportsOnFileMoveOptions { @@ -490,6 +502,8 @@ pub struct LanguageWorkspaceSettings { #[serde(default)] pub suggest: CompletionSettings, #[serde(default)] + pub suggestion_actions: SuggestionActionsSettings, + #[serde(default)] pub update_imports_on_file_move: UpdateImportsOnFileMoveOptions, } @@ -839,7 +853,8 @@ impl Settings { Some(false) } else if let Some(enable_paths) = &enable_paths { for enable_path in enable_paths { - if path.starts_with(enable_path) { + // Also enable if the checked path is a dir containing an enabled path. + if path.starts_with(enable_path) || enable_path.starts_with(&path) { return Some(true); } } @@ -1203,9 +1218,8 @@ impl ConfigData { specified_config: Option<&Path>, scope: &ModuleSpecifier, settings: &Settings, - file_fetcher: &Arc, + file_fetcher: &Arc, // sync requirement is because the lsp requires sync - cached_deno_config_fs: &(dyn DenoConfigFs + Sync), deno_json_cache: &(dyn DenoJsonCache + Sync), pkg_json_cache: &(dyn PackageJsonCache + Sync), workspace_cache: &(dyn WorkspaceCache + Sync), @@ -1215,6 +1229,7 @@ impl ConfigData { Ok(scope_dir_path) => { let paths = [scope_dir_path]; WorkspaceDirectory::discover( + &CliSys::default(), match specified_config { Some(config_path) => { deno_config::workspace::WorkspaceDiscoverStart::ConfigFile( @@ -1226,13 +1241,11 @@ impl ConfigData { } }, &WorkspaceDiscoverOptions { - fs: cached_deno_config_fs, additional_config_file_names: &[], deno_json_cache: Some(deno_json_cache), pkg_json_cache: Some(pkg_json_cache), workspace_cache: Some(workspace_cache), discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"), - config_parse_options: Default::default(), maybe_vendor_override: None, }, ) @@ -1298,7 +1311,7 @@ impl ConfigData { member_dir: Arc, scope: Arc, settings: &Settings, - file_fetcher: Option<&Arc>, + file_fetcher: Option<&Arc>, ) -> Self { let (settings, workspace_folder) = settings.get_for_specifier(&scope); let mut watched_files = HashMap::with_capacity(10); @@ -1557,11 +1570,11 @@ impl ConfigData { let resolver = member_dir .workspace .create_resolver( + &CliSys::default(), CreateResolverOptions { pkg_json_dep_resolution, specified_import_map, }, - |path| Ok(std::fs::read_to_string(path)?), ) .inspect_err(|err| { lsp_warn!( @@ -1603,9 +1616,7 @@ impl ConfigData { || unstable.contains("sloppy-imports"); let sloppy_imports_resolver = unstable_sloppy_imports.then(|| { Arc::new(CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new_without_stat_cache(Arc::new( - deno_runtime::deno_fs::RealFs, - )), + SloppyImportsCachedFs::new_without_stat_cache(CliSys::default()), )) }); let resolver = Arc::new(resolver); @@ -1819,13 +1830,12 @@ impl ConfigTree { &mut self, settings: &Settings, workspace_files: &IndexSet, - file_fetcher: &Arc, + file_fetcher: &Arc, ) { lsp_log!("Refreshing configuration tree..."); // since we're resolving a workspace multiple times in different // folders, we want to cache all the lookups and config files across // ConfigData::load calls - let cached_fs = CachedDenoConfigFs::default(); let deno_json_cache = DenoJsonMemCache::default(); let pkg_json_cache = PackageJsonMemCache::default(); let workspace_cache = WorkspaceMemCache::default(); @@ -1850,7 +1860,6 @@ impl ConfigTree { folder_uri, settings, file_fetcher, - &cached_fs, &deno_json_cache, &pkg_json_cache, &workspace_cache, @@ -1881,7 +1890,6 @@ impl ConfigTree { &scope, settings, file_fetcher, - &cached_fs, &deno_json_cache, &pkg_json_cache, &workspace_cache, @@ -1898,7 +1906,6 @@ impl ConfigTree { member_scope, settings, file_fetcher, - &cached_fs, &deno_json_cache, &pkg_json_cache, &workspace_cache, @@ -1913,21 +1920,24 @@ impl ConfigTree { #[cfg(test)] pub async fn inject_config_file(&mut self, config_file: ConfigFile) { + use sys_traits::FsCreateDirAll; + use sys_traits::FsWrite; + let scope = config_file.specifier.join(".").unwrap(); let json_text = serde_json::to_string(&config_file.json).unwrap(); - let test_fs = deno_runtime::deno_fs::InMemoryFs::default(); + let memory_sys = sys_traits::impls::InMemorySys::default(); let config_path = url_to_file_path(&config_file.specifier).unwrap(); - test_fs.setup_text_files(vec![( - config_path.to_string_lossy().to_string(), - json_text, - )]); + memory_sys + .fs_create_dir_all(config_path.parent().unwrap()) + .unwrap(); + memory_sys.fs_write(&config_path, json_text).unwrap(); let workspace_dir = Arc::new( WorkspaceDirectory::discover( + &memory_sys, deno_config::workspace::WorkspaceDiscoverStart::ConfigFile( &config_path, ), &deno_config::workspace::WorkspaceDiscoverOptions { - fs: &crate::args::deno_json::DenoConfigFsAdapter(&test_fs), ..Default::default() }, ) @@ -2000,11 +2010,14 @@ fn resolve_lockfile_from_path( lockfile_path: PathBuf, frozen: bool, ) -> Option { - match CliLockfile::read_from_path(CliLockfileReadFromPathOptions { - file_path: lockfile_path, - frozen, - skip_write: false, - }) { + match CliLockfile::read_from_path( + &CliSys::default(), + 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) @@ -2061,81 +2074,8 @@ impl deno_config::workspace::WorkspaceCache for WorkspaceMemCache { } } -#[derive(Default)] -struct CachedFsItems { - items: HashMap>, -} - -impl CachedFsItems { - pub fn get( - &mut self, - path: &Path, - action: impl FnOnce(&Path) -> Result, - ) -> Result { - let value = if let Some(value) = self.items.get(path) { - value - } else { - let value = action(path); - // just in case this gets really large for some reason - if self.items.len() == 16_384 { - return value; - } - self.items.insert(path.to_owned(), value); - self.items.get(path).unwrap() - }; - value - .as_ref() - .map(|v| (*v).clone()) - .map_err(|e| std::io::Error::new(e.kind(), e.to_string())) - } -} - -#[derive(Default)] -struct InnerData { - stat_calls: CachedFsItems, - read_to_string_calls: CachedFsItems>, -} - -#[derive(Default)] -struct CachedDenoConfigFs(Mutex); - -impl DenoConfigFs for CachedDenoConfigFs { - fn stat_sync( - &self, - path: &Path, - ) -> Result { - self - .0 - .lock() - .stat_calls - .get(path, |path| RealDenoConfigFs.stat_sync(path)) - } - - fn read_to_string_lossy( - &self, - path: &Path, - ) -> Result, std::io::Error> { - self - .0 - .lock() - .read_to_string_calls - .get(path, |path| RealDenoConfigFs.read_to_string_lossy(path)) - } - - fn read_dir( - &self, - path: &Path, - ) -> Result, std::io::Error> { - // no need to cache these because the workspace cache will ensure - // we only do read_dir calls once (read_dirs are only used for - // npm workspace resolution) - RealDenoConfigFs.read_dir(path) - } -} - #[cfg(test)] mod tests { - use deno_config::deno_json::ConfigParseOptions; use deno_core::resolve_url; use deno_core::serde_json; use deno_core::serde_json::json; @@ -2292,6 +2232,7 @@ mod tests { enabled: true, }, }, + suggestion_actions: SuggestionActionsSettings { enabled: true }, update_imports_on_file_move: UpdateImportsOnFileMoveOptions { enabled: UpdateImportsOnFileMoveEnabled::Prompt } @@ -2338,6 +2279,7 @@ mod tests { enabled: true, }, }, + suggestion_actions: SuggestionActionsSettings { enabled: true }, update_imports_on_file_move: UpdateImportsOnFileMoveOptions { enabled: UpdateImportsOnFileMoveEnabled::Prompt } @@ -2407,12 +2349,7 @@ mod tests { config .tree .inject_config_file( - ConfigFile::new( - "{}", - root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), - ) - .unwrap(), + ConfigFile::new("{}", root_uri.join("deno.json").unwrap()).unwrap(), ) .await; assert!(config.specifier_enabled(&root_uri)); @@ -2468,7 +2405,6 @@ mod tests { }) .to_string(), root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) @@ -2494,7 +2430,6 @@ mod tests { }) .to_string(), root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) @@ -2512,7 +2447,6 @@ mod tests { }) .to_string(), root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 01fc3bf69e..fe8dc4c8d5 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -1,30 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::analysis; -use super::client::Client; -use super::config::Config; -use super::documents; -use super::documents::Document; -use super::documents::Documents; -use super::documents::DocumentsFilter; -use super::language_server; -use super::language_server::StateSnapshot; -use super::performance::Performance; -use super::tsc; -use super::tsc::TsServer; -use super::urls::uri_parse_unencoded; -use super::urls::url_to_uri; -use super::urls::LspUrlMap; - -use crate::graph_util; -use crate::graph_util::enhanced_resolution_error_message; -use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams; -use crate::resolver::CliSloppyImportsResolver; -use crate::resolver::SloppyImportsCachedFs; -use crate::tools::lint::CliLinter; -use crate::tools::lint::CliLinterOptions; -use crate::tools::lint::LintRuleProvider; -use crate::util::path::to_percent_decoded_str; +use std::collections::HashMap; +use std::collections::HashSet; +use std::path::PathBuf; +use std::sync::atomic::AtomicUsize; +use std::sync::Arc; +use std::thread; use deno_ast::MediaType; use deno_config::deno_json::LintConfig; @@ -44,29 +25,50 @@ use deno_graph::source::ResolveError; use deno_graph::Resolution; use deno_graph::ResolutionError; use deno_graph::SpecifierError; +use deno_lint::linter::LintConfig as DenoLintConfig; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::sloppy_imports::SloppyImportsResolution; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; -use deno_runtime::deno_fs; use deno_runtime::deno_node; use deno_runtime::tokio_util::create_basic_runtime; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; use import_map::ImportMap; -use import_map::ImportMapError; +use import_map::ImportMapErrorKind; use log::error; -use std::collections::HashMap; -use std::collections::HashSet; -use std::path::PathBuf; -use std::sync::atomic::AtomicUsize; -use std::sync::Arc; -use std::thread; use tokio::sync::mpsc; use tokio::sync::Mutex; use tokio::time::Duration; use tokio_util::sync::CancellationToken; use tower_lsp::lsp_types as lsp; +use super::analysis; +use super::client::Client; +use super::config::Config; +use super::documents; +use super::documents::Document; +use super::documents::Documents; +use super::documents::DocumentsFilter; +use super::language_server; +use super::language_server::StateSnapshot; +use super::performance::Performance; +use super::tsc; +use super::tsc::TsServer; +use super::urls::uri_parse_unencoded; +use super::urls::url_to_uri; +use super::urls::LspUrlMap; +use crate::graph_util; +use crate::graph_util::enhanced_resolution_error_message; +use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams; +use crate::resolver::CliSloppyImportsResolver; +use crate::sys::CliSys; +use crate::tools::lint::CliLinter; +use crate::tools::lint::CliLinterOptions; +use crate::tools::lint::LintRuleProvider; +use crate::tsc::DiagnosticCategory; +use crate::util::path::to_percent_decoded_str; + #[derive(Debug)] pub struct DiagnosticServerUpdateMessage { pub snapshot: Arc, @@ -833,7 +835,7 @@ fn generate_lint_diagnostics( lint_rule_provider.resolve_lint_rules(Default::default(), None) }, fix: false, - deno_lint_config: deno_lint::linter::LintConfig { + deno_lint_config: DenoLintConfig { default_jsx_factory: None, default_jsx_fragment_factory: None, }, @@ -906,8 +908,22 @@ async fn generate_ts_diagnostics( } else { Default::default() }; - for (specifier_str, ts_json_diagnostics) in ts_diagnostics_map { + for (specifier_str, mut ts_json_diagnostics) in ts_diagnostics_map { let specifier = resolve_url(&specifier_str)?; + let suggestion_actions_settings = snapshot + .config + .language_settings_for_specifier(&specifier) + .map(|s| s.suggestion_actions.clone()) + .unwrap_or_default(); + if !suggestion_actions_settings.enabled { + ts_json_diagnostics.retain(|d| { + d.category != DiagnosticCategory::Suggestion + // Still show deprecated and unused diagnostics. + // https://github.com/microsoft/vscode/blob/ce50bd4876af457f64d83cfd956bc916535285f4/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts#L113-L114 + || d.reports_deprecated == Some(true) + || d.reports_unnecessary == Some(true) + }); + } let version = snapshot .documents .get(&specifier) @@ -1265,7 +1281,7 @@ impl DenoDiagnostic { Self::NotInstalledNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("npm package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))), Self::NoLocal(specifier) => { let maybe_sloppy_resolution = CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new(Arc::new(deno_fs::RealFs)) + SloppyImportsCachedFs::new(CliSys::default()) ).resolve(specifier, SloppyImportsResolutionKind::Execution); let data = maybe_sloppy_resolution.as_ref().map(|res| { json!({ @@ -1281,8 +1297,8 @@ impl DenoDiagnostic { let mut message; message = enhanced_resolution_error_message(err); if let deno_graph::ResolutionError::ResolverError {error, ..} = err{ - if let ResolveError::Other(resolve_error, ..) = (*error).as_ref() { - if let Some(ImportMapError::UnmappedBareSpecifier(specifier, _)) = resolve_error.downcast_ref::() { + if let ResolveError::ImportMap(importmap) = (*error).as_ref() { + if let ImportMapErrorKind::UnmappedBareSpecifier(specifier, _) = &**importmap { if specifier.chars().next().unwrap_or('\0') == '@'{ let hint = format!("\nHint: Use [deno add {}] to add the dependency.", specifier); message.push_str(hint.as_str()); @@ -1630,6 +1646,12 @@ fn generate_deno_diagnostics( #[cfg(test)] mod tests { + use std::sync::Arc; + + use deno_config::deno_json::ConfigFile; + use pretty_assertions::assert_eq; + use test_util::TempDir; + use super::*; use crate::lsp::cache::LspCache; use crate::lsp::config::Config; @@ -1640,11 +1662,6 @@ mod tests { use crate::lsp::language_server::StateSnapshot; use crate::lsp::resolver::LspResolver; - use deno_config::deno_json::ConfigFile; - use pretty_assertions::assert_eq; - use std::sync::Arc; - use test_util::TempDir; - fn mock_config() -> Config { let root_url = resolve_url("file:///").unwrap(); let root_uri = url_to_uri(&root_url).unwrap(); @@ -1678,12 +1695,7 @@ mod tests { let mut config = Config::new_with_roots([root_uri.clone()]); if let Some((relative_path, json_string)) = maybe_import_map { let base_url = root_uri.join(relative_path).unwrap(); - let config_file = ConfigFile::new( - json_string, - base_url, - &deno_config::deno_json::ConfigParseOptions::default(), - ) - .unwrap(); + let config_file = ConfigFile::new(json_string, base_url).unwrap(); config.tree.inject_config_file(config_file).await; } let resolver = diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index bdb64c9da3..6a7a08b5a1 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -1,41 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::cache::calculate_fs_version; -use super::cache::LspCache; -use super::config::Config; -use super::resolver::LspResolver; -use super::resolver::ScopeDepInfo; -use super::resolver::SingleReferrerGraphResolver; -use super::testing::TestCollector; -use super::testing::TestModule; -use super::text::LineIndex; -use super::tsc; -use super::tsc::AssetDocument; - -use crate::graph_util::CliJsrUrlProvider; - -use dashmap::DashMap; -use deno_ast::swc::visit::VisitWith; -use deno_ast::MediaType; -use deno_ast::ParsedSource; -use deno_ast::SourceTextInfo; -use deno_core::error::custom_error; -use deno_core::error::AnyError; -use deno_core::futures::future; -use deno_core::futures::future::Shared; -use deno_core::futures::FutureExt; -use deno_core::parking_lot::Mutex; -use deno_core::ModuleSpecifier; -use deno_graph::Resolution; -use deno_path_util::url_to_file_path; -use deno_runtime::deno_node; -use deno_semver::jsr::JsrPackageReqReference; -use deno_semver::npm::NpmPackageReqReference; -use deno_semver::package::PackageReq; -use indexmap::IndexMap; -use indexmap::IndexSet; -use node_resolver::NodeResolutionKind; -use node_resolver::ResolutionMode; use std::borrow::Cow; use std::collections::BTreeMap; use std::collections::HashMap; @@ -48,8 +12,44 @@ use std::str::FromStr; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::sync::Arc; + +use dashmap::DashMap; +use deno_ast::swc::visit::VisitWith; +use deno_ast::MediaType; +use deno_ast::ParsedSource; +use deno_ast::SourceTextInfo; +use deno_core::error::AnyError; +use deno_core::futures::future; +use deno_core::futures::future::Shared; +use deno_core::futures::FutureExt; +use deno_core::parking_lot::Mutex; +use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; +use deno_graph::Resolution; +use deno_path_util::url_to_file_path; +use deno_runtime::deno_node; +use deno_semver::jsr::JsrPackageReqReference; +use deno_semver::npm::NpmPackageReqReference; +use deno_semver::package::PackageReq; +use indexmap::IndexMap; +use indexmap::IndexSet; +use node_resolver::NodeResolutionKind; +use node_resolver::ResolutionMode; use tower_lsp::lsp_types as lsp; +use super::cache::calculate_fs_version; +use super::cache::LspCache; +use super::config::Config; +use super::resolver::LspResolver; +use super::resolver::ScopeDepInfo; +use super::resolver::SingleReferrerGraphResolver; +use super::testing::TestCollector; +use super::testing::TestModule; +use super::text::LineIndex; +use super::tsc; +use super::tsc::AssetDocument; +use crate::graph_util::CliJsrUrlProvider; + pub const DOCUMENT_SCHEMES: [&str; 5] = ["data", "blob", "file", "http", "https"]; @@ -64,6 +64,9 @@ pub enum LanguageId { Markdown, Html, Css, + Scss, + Sass, + Less, Yaml, Sql, Svelte, @@ -86,6 +89,9 @@ impl LanguageId { LanguageId::Markdown => Some("md"), LanguageId::Html => Some("html"), LanguageId::Css => Some("css"), + LanguageId::Scss => Some("scss"), + LanguageId::Sass => Some("sass"), + LanguageId::Less => Some("less"), LanguageId::Yaml => Some("yaml"), LanguageId::Sql => Some("sql"), LanguageId::Svelte => Some("svelte"), @@ -107,6 +113,9 @@ impl LanguageId { LanguageId::Markdown => Some("text/markdown"), LanguageId::Html => Some("text/html"), LanguageId::Css => Some("text/css"), + LanguageId::Scss => None, + LanguageId::Sass => None, + LanguageId::Less => None, LanguageId::Yaml => Some("application/yaml"), LanguageId::Sql => None, LanguageId::Svelte => None, @@ -140,6 +149,9 @@ impl FromStr for LanguageId { "markdown" => Ok(Self::Markdown), "html" => Ok(Self::Html), "css" => Ok(Self::Css), + "scss" => Ok(Self::Scss), + "sass" => Ok(Self::Sass), + "less" => Ok(Self::Less), "yaml" => Ok(Self::Yaml), "sql" => Ok(Self::Sql), "svelte" => Ok(Self::Svelte), @@ -251,6 +263,13 @@ impl AssetOrDocument { pub fn document_lsp_version(&self) -> Option { self.document().and_then(|d| d.maybe_lsp_version()) } + + pub fn resolution_mode(&self) -> ResolutionMode { + match self { + AssetOrDocument::Asset(_) => ResolutionMode::Import, + AssetOrDocument::Document(d) => d.resolution_mode(), + } + } } type ModuleResult = Result; @@ -461,7 +480,7 @@ impl Document { let is_cjs_resolver = resolver.as_is_cjs_resolver(self.file_referrer.as_ref()); let npm_resolver = - resolver.create_graph_npm_resolver(self.file_referrer.as_ref()); + resolver.as_graph_npm_resolver(self.file_referrer.as_ref()); let config_data = resolver.as_config_data(self.file_referrer.as_ref()); let jsx_import_source_config = config_data.and_then(|d| d.maybe_jsx_import_source_config()); @@ -484,7 +503,7 @@ impl Document { s, &CliJsrUrlProvider, Some(&resolver), - Some(&npm_resolver), + Some(npm_resolver.as_ref()), ), ) }) @@ -494,7 +513,7 @@ impl Document { Arc::new(d.with_new_resolver( &CliJsrUrlProvider, Some(&resolver), - Some(&npm_resolver), + Some(npm_resolver.as_ref()), )) }); is_script = self.is_script; @@ -1062,7 +1081,7 @@ impl Documents { .or_else(|| self.file_system_docs.remove_document(specifier)) .map(Ok) .unwrap_or_else(|| { - Err(custom_error( + Err(JsErrorBox::new( "NotFound", format!("The specifier \"{specifier}\" was not found."), )) @@ -1683,7 +1702,7 @@ fn analyze_module( ) -> (ModuleResult, ResolutionMode) { match parsed_source_result { Ok(parsed_source) => { - let npm_resolver = resolver.create_graph_npm_resolver(file_referrer); + let npm_resolver = resolver.as_graph_npm_resolver(file_referrer); let cli_resolver = resolver.as_cli_resolver(file_referrer); let is_cjs_resolver = resolver.as_is_cjs_resolver(file_referrer); let config_data = resolver.as_config_data(file_referrer); @@ -1712,7 +1731,7 @@ fn analyze_module( file_system: &deno_graph::source::NullFileSystem, jsr_url_provider: &CliJsrUrlProvider, maybe_resolver: Some(&resolver), - maybe_npm_resolver: Some(&npm_resolver), + maybe_npm_resolver: Some(npm_resolver.as_ref()), }, )), module_resolution_mode, @@ -1747,16 +1766,15 @@ fn bytes_to_content( #[cfg(test)] mod tests { - use super::*; - use crate::lsp::cache::LspCache; - use deno_config::deno_json::ConfigFile; - use deno_config::deno_json::ConfigParseOptions; use deno_core::serde_json; use deno_core::serde_json::json; use pretty_assertions::assert_eq; use test_util::TempDir; + use super::*; + use crate::lsp::cache::LspCache; + async fn setup() -> (Documents, LspCache, TempDir) { let temp_dir = TempDir::new(); temp_dir.create_dir_all(".deno_dir"); @@ -1905,7 +1923,6 @@ console.log(b, "hello deno"); }) .to_string(), config.root_uri().unwrap().join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) @@ -1949,7 +1966,6 @@ console.log(b, "hello deno"); }) .to_string(), config.root_uri().unwrap().join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) diff --git a/cli/lsp/jsr.rs b/cli/lsp/jsr.rs index ab570f6348..1bcd6f3930 100644 --- a/cli/lsp/jsr.rs +++ b/cli/lsp/jsr.rs @@ -1,10 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashMap; +use std::sync::Arc; -use crate::args::jsr_api_url; -use crate::args::jsr_url; -use crate::file_fetcher::FileFetcher; -use crate::jsr::partial_jsr_package_version_info_from_slice; -use crate::jsr::JsrFetchResolver; use dashmap::DashMap; use deno_cache_dir::HttpCache; use deno_core::anyhow::anyhow; @@ -17,13 +15,18 @@ use deno_graph::ModuleSpecifier; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::Version; use serde::Deserialize; -use std::collections::HashMap; -use std::sync::Arc; use super::config::ConfigData; use super::search::PackageSearchApi; +use crate::args::jsr_api_url; +use crate::args::jsr_url; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::TextDecodedFile; +use crate::jsr::partial_jsr_package_version_info_from_slice; +use crate::jsr::JsrFetchResolver; /// Keep in sync with `JsrFetchResolver`! #[derive(Debug)] @@ -32,8 +35,8 @@ pub struct JsrCacheResolver { /// The `module_graph` fields of the version infos should be forcibly absent. /// It can be large and we don't want to store it. info_by_nv: DashMap>>, - info_by_name: DashMap>>, - workspace_scope_by_name: HashMap, + info_by_name: DashMap>>, + workspace_scope_by_name: HashMap, cache: Arc, } @@ -58,7 +61,7 @@ impl JsrCacheResolver { continue; }; let nv = PackageNv { - name: jsr_pkg_config.name.clone(), + name: jsr_pkg_config.name.as_str().into(), version: version.clone(), }; info_by_name.insert( @@ -124,8 +127,8 @@ impl JsrCacheResolver { return nv.value().clone(); } let maybe_get_nv = || { - let name = req.name.clone(); - let package_info = self.package_info(&name)?; + let name = &req.name; + let package_info = self.package_info(name)?; // Find the first matching version of the package which is cached. let mut versions = package_info.versions.keys().collect::>(); versions.sort(); @@ -143,7 +146,10 @@ impl JsrCacheResolver { self.package_version_info(&nv).is_some() }) .cloned()?; - Some(PackageNv { name, version }) + Some(PackageNv { + name: name.clone(), + version, + }) }; let nv = maybe_get_nv(); self.nv_by_req.insert(req.clone(), nv.clone()); @@ -215,7 +221,10 @@ impl JsrCacheResolver { None } - pub fn package_info(&self, name: &str) -> Option> { + pub fn package_info( + &self, + name: &StackString, + ) -> Option> { if let Some(info) = self.info_by_name.get(name) { return info.value().clone(); } @@ -225,7 +234,7 @@ impl JsrCacheResolver { serde_json::from_slice::(&meta_bytes).ok() }; let info = read_cached_package_info().map(Arc::new); - self.info_by_name.insert(name.to_string(), info.clone()); + self.info_by_name.insert(name.clone(), info.clone()); info } @@ -267,7 +276,7 @@ fn read_cached_url( #[derive(Debug)] pub struct CliJsrSearchApi { - file_fetcher: Arc, + file_fetcher: Arc, resolver: JsrFetchResolver, search_cache: DashMap>>, versions_cache: DashMap>>, @@ -275,7 +284,7 @@ pub struct CliJsrSearchApi { } impl CliJsrSearchApi { - pub fn new(file_fetcher: Arc) -> Self { + pub fn new(file_fetcher: Arc) -> Self { let resolver = JsrFetchResolver::new(file_fetcher.clone()); Self { file_fetcher, @@ -309,10 +318,8 @@ impl PackageSearchApi for CliJsrSearchApi { let file_fetcher = self.file_fetcher.clone(); // spawn due to the lsp's `Send` requirement let file = deno_core::unsync::spawn(async move { - file_fetcher - .fetch_bypass_permissions(&search_url) - .await? - .into_text_decoded() + let file = file_fetcher.fetch_bypass_permissions(&search_url).await?; + TextDecodedFile::decode(file) }) .await??; let names = Arc::new(parse_jsr_search_response(&file.source)?); diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 839d28469e..0f3bfcdf59 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1,6 +1,18 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::collections::HashSet; +use std::collections::VecDeque; +use std::env; +use std::fmt::Write as _; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_config::workspace::WorkspaceDirectory; use deno_config::workspace::WorkspaceDiscoverOptions; use deno_core::anyhow::anyhow; @@ -26,16 +38,6 @@ use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use serde::Deserialize; use serde_json::from_value; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::collections::HashSet; -use std::collections::VecDeque; -use std::env; -use std::fmt::Write as _; -use std::path::PathBuf; -use std::str::FromStr; -use std::sync::Arc; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; @@ -95,19 +97,19 @@ use crate::args::create_default_npmrc; use crate::args::get_root_cert_store; use crate::args::has_flag_env_var; 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; +use crate::file_fetcher::CliFileFetcher; use crate::graph_util; use crate::http_util::HttpClientProvider; use crate::lsp::config::ConfigWatchedFileType; use crate::lsp::logging::init_log_file; use crate::lsp::tsc::file_text_changes_to_workspace_edit; use crate::lsp::urls::LspUrlKind; +use crate::sys::CliSys; use crate::tools::fmt::format_file; use crate::tools::fmt::format_parsed_source; use crate::tools::upgrade::check_for_upgrades_for_lsp; @@ -120,7 +122,7 @@ use crate::util::sync::AsyncFlag; struct LspRootCertStoreProvider(RootCertStore); impl RootCertStoreProvider for LspRootCertStoreProvider { - fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> { + fn get_or_try_init(&self) -> Result<&RootCertStore, deno_error::JsErrorBox> { Ok(&self.0) } } @@ -279,7 +281,7 @@ impl LanguageServer { .await?; graph_util::graph_valid( &graph, - factory.fs(), + &CliSys::default(), &roots, graph_util::GraphValidOptions { kind: GraphKind::All, @@ -958,15 +960,16 @@ impl Inner { } async fn refresh_config_tree(&mut self) { - let mut file_fetcher = FileFetcher::new( + let file_fetcher = CliFileFetcher::new( self.cache.global().clone(), - CacheSetting::RespectHeaders, - true, self.http_client_provider.clone(), + CliSys::default(), Default::default(), None, + true, + CacheSetting::RespectHeaders, + super::logging::lsp_log_level(), ); - file_fetcher.set_download_log_level(super::logging::lsp_log_level()); let file_fetcher = Arc::new(file_fetcher); self .config @@ -1416,18 +1419,16 @@ impl Inner { // the file path is only used to determine what formatter should // be used to format the file, so give the filepath an extension // that matches what the user selected as the language - let file_path = document + let ext = document .maybe_language_id() - .and_then(|id| id.as_extension()) - .map(|ext| file_path.with_extension(ext)) - .unwrap_or(file_path); + .and_then(|id| id.as_extension().map(|s| s.to_string())); // it's not a js/ts file, so attempt to format its contents format_file( &file_path, document.content(), &fmt_options, &unstable_options, - None, + ext, ) } }; @@ -1855,20 +1856,12 @@ impl Inner { } let changes = if code_action_data.fix_id == "fixMissingImport" { - fix_ts_import_changes( - &code_action_data.specifier, - maybe_asset_or_doc - .as_ref() - .and_then(|d| d.document()) - .map(|d| d.resolution_mode()) - .unwrap_or(ResolutionMode::Import), - &combined_code_actions.changes, - self, - ) - .map_err(|err| { - error!("Unable to remap changes: {:#}", err); - LspError::internal_error() - })? + fix_ts_import_changes(&combined_code_actions.changes, self).map_err( + |err| { + error!("Unable to remap changes: {:#}", err); + LspError::internal_error() + }, + )? } else { combined_code_actions.changes }; @@ -1912,20 +1905,16 @@ impl Inner { asset_or_doc.scope().cloned(), ) .await?; - if kind_suffix == ".rewrite.function.returnType" { - refactor_edit_info.edits = fix_ts_import_changes( - &action_data.specifier, - asset_or_doc - .document() - .map(|d| d.resolution_mode()) - .unwrap_or(ResolutionMode::Import), - &refactor_edit_info.edits, - self, - ) - .map_err(|err| { - error!("Unable to remap changes: {:#}", err); - LspError::internal_error() - })? + if kind_suffix == ".rewrite.function.returnType" + || kind_suffix == ".move.newFile" + { + refactor_edit_info.edits = + fix_ts_import_changes(&refactor_edit_info.edits, self).map_err( + |err| { + error!("Unable to remap changes: {:#}", err); + LspError::internal_error() + }, + )? } code_action.edit = refactor_edit_info.to_workspace_edit(self)?; code_action @@ -3624,16 +3613,14 @@ impl Inner { let workspace = match config_data { Some(d) => d.member_dir.clone(), None => Arc::new(WorkspaceDirectory::discover( + &CliSys::default(), deno_config::workspace::WorkspaceDiscoverStart::Paths(&[ initial_cwd.clone() ]), &WorkspaceDiscoverOptions { - fs: Default::default(), // use real fs, deno_json_cache: None, pkg_json_cache: None, workspace_cache: None, - config_parse_options: - deno_config::deno_json::ConfigParseOptions::default(), additional_config_file_names: &[], discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"), maybe_vendor_override: if force_global_cache { @@ -3645,6 +3632,7 @@ impl Inner { )?), }; let cli_options = CliOptions::new( + &CliSys::default(), Arc::new(Flags { internal: InternalFlags { cache_path: Some(self.cache.deno_dir().root.clone()), @@ -3793,7 +3781,7 @@ impl Inner { for (name, command) in scripts { result.push(TaskDefinition { name: name.clone(), - command: command.clone(), + command: Some(command.clone()), source_uri: url_to_uri(&package_json.specifier()) .map_err(|_| LspError::internal_error())?, }); @@ -3972,10 +3960,11 @@ impl Inner { #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; use test_util::TempDir; + use super::*; + #[test] fn test_walk_workspace() { let temp_dir = TempDir::new(); @@ -4012,12 +4001,14 @@ mod tests { temp_dir.write("root1/target/main.ts", ""); // no, because there is a Cargo.toml in the root directory temp_dir.create_dir_all("root2/folder"); + temp_dir.create_dir_all("root2/folder2/inner_folder"); temp_dir.create_dir_all("root2/sub_folder"); temp_dir.create_dir_all("root2/root2.1"); temp_dir.write("root2/file1.ts", ""); // yes, enabled temp_dir.write("root2/file2.ts", ""); // no, not enabled temp_dir.write("root2/folder/main.ts", ""); // yes, enabled temp_dir.write("root2/folder/other.ts", ""); // no, disabled + temp_dir.write("root2/folder2/inner_folder/main.ts", ""); // yes, enabled (regression test for https://github.com/denoland/vscode_deno/issues/1239) temp_dir.write("root2/sub_folder/a.js", ""); // no, not enabled temp_dir.write("root2/sub_folder/b.ts", ""); // no, not enabled temp_dir.write("root2/sub_folder/c.js", ""); // no, not enabled @@ -4058,6 +4049,7 @@ mod tests { enable_paths: Some(vec![ "file1.ts".to_string(), "folder".to_string(), + "folder2/inner_folder".to_string(), ]), disable_paths: vec!["folder/other.ts".to_string()], ..Default::default() @@ -4108,6 +4100,10 @@ mod tests { temp_dir.url().join("root1/folder/mod.ts").unwrap(), temp_dir.url().join("root2/folder/main.ts").unwrap(), temp_dir.url().join("root2/root2.1/main.ts").unwrap(), + temp_dir + .url() + .join("root2/folder2/inner_folder/main.ts") + .unwrap(), ]) ); } diff --git a/cli/lsp/logging.rs b/cli/lsp/logging.rs index 2b85d77ec1..efb49b2d48 100644 --- a/cli/lsp/logging.rs +++ b/cli/lsp/logging.rs @@ -1,8 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use chrono::DateTime; -use chrono::Utc; -use deno_core::parking_lot::Mutex; use std::fs; use std::io::prelude::*; use std::path::Path; @@ -12,6 +9,10 @@ use std::sync::atomic::Ordering; use std::thread; use std::time::SystemTime; +use chrono::DateTime; +use chrono::Utc; +use deno_core::parking_lot::Mutex; + static LSP_DEBUG_FLAG: AtomicBool = AtomicBool::new(false); static LSP_LOG_LEVEL: AtomicUsize = AtomicUsize::new(log::Level::Info as usize); static LSP_WARN_LEVEL: AtomicUsize = diff --git a/cli/lsp/lsp_custom.rs b/cli/lsp/lsp_custom.rs index 74c6aca88b..050fcf3184 100644 --- a/cli/lsp/lsp_custom.rs +++ b/cli/lsp/lsp_custom.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde::Deserialize; use deno_core::serde::Serialize; @@ -14,7 +14,7 @@ pub const LATEST_DIAGNOSTIC_BATCH_INDEX: &str = #[serde(rename_all = "camelCase")] pub struct TaskDefinition { pub name: String, - pub command: String, + pub command: Option, pub source_uri: lsp::Uri, } diff --git a/cli/lsp/mod.rs b/cli/lsp/mod.rs index afb949f68d..6b5d17798b 100644 --- a/cli/lsp/mod.rs +++ b/cli/lsp/mod.rs @@ -1,16 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::AnyError; use deno_core::unsync::spawn; +pub use repl::ReplCompletionItem; +pub use repl::ReplLanguageServer; use tower_lsp::LspService; use tower_lsp::Server; +use self::diagnostics::should_send_diagnostic_batch_index_notifications; use crate::lsp::language_server::LanguageServer; use crate::util::sync::AsyncFlag; -pub use repl::ReplCompletionItem; -pub use repl::ReplLanguageServer; - -use self::diagnostics::should_send_diagnostic_batch_index_notifications; mod analysis; mod cache; diff --git a/cli/lsp/npm.rs b/cli/lsp/npm.rs index 2decfc3429..d53c8cb2ab 100644 --- a/cli/lsp/npm.rs +++ b/cli/lsp/npm.rs @@ -1,4 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::sync::Arc; use dashmap::DashMap; use deno_core::anyhow::anyhow; @@ -8,24 +10,23 @@ use deno_npm::npm_rc::NpmRc; use deno_semver::package::PackageNv; use deno_semver::Version; use serde::Deserialize; -use std::sync::Arc; - -use crate::args::npm_registry_url; -use crate::file_fetcher::FileFetcher; -use crate::npm::NpmFetchResolver; use super::search::PackageSearchApi; +use crate::args::npm_registry_url; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::TextDecodedFile; +use crate::npm::NpmFetchResolver; #[derive(Debug)] pub struct CliNpmSearchApi { - file_fetcher: Arc, + file_fetcher: Arc, resolver: NpmFetchResolver, search_cache: DashMap>>, versions_cache: DashMap>>, } impl CliNpmSearchApi { - pub fn new(file_fetcher: Arc) -> Self { + pub fn new(file_fetcher: Arc) -> Self { let resolver = NpmFetchResolver::new( file_fetcher.clone(), Arc::new(NpmRc::default().as_resolved(npm_registry_url()).unwrap()), @@ -57,10 +58,8 @@ impl PackageSearchApi for CliNpmSearchApi { .append_pair("text", &format!("{} boost-exact:false", query)); let file_fetcher = self.file_fetcher.clone(); let file = deno_core::unsync::spawn(async move { - file_fetcher - .fetch_bypass_permissions(&search_url) - .await? - .into_text_decoded() + let file = file_fetcher.fetch_bypass_permissions(&search_url).await?; + TextDecodedFile::decode(file) }) .await??; let names = Arc::new(parse_npm_search_response(&file.source)?); diff --git a/cli/lsp/parent_process_checker.rs b/cli/lsp/parent_process_checker.rs index b8a42cd1a4..b8ab60922b 100644 --- a/cli/lsp/parent_process_checker.rs +++ b/cli/lsp/parent_process_checker.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::time::Duration; @@ -52,10 +52,12 @@ fn is_process_active(process_id: u32) -> bool { #[cfg(test)] mod test { - use super::is_process_active; use std::process::Command; + use test_util::deno_exe_path; + use super::is_process_active; + #[test] fn process_active() { // launch a long running process diff --git a/cli/lsp/path_to_regex.rs b/cli/lsp/path_to_regex.rs index 88d8a2ec68..65322da6d0 100644 --- a/cli/lsp/path_to_regex.rs +++ b/cli/lsp/path_to_regex.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // The logic of this module is heavily influenced by path-to-regexp at: // https://github.com/pillarjs/path-to-regexp/ which is licensed as follows: @@ -26,15 +26,16 @@ // THE SOFTWARE. // +use std::collections::HashMap; +use std::fmt; +use std::fmt::Write as _; +use std::iter::Peekable; + use deno_core::anyhow::anyhow; use deno_core::error::AnyError; use fancy_regex::Regex as FancyRegex; use once_cell::sync::Lazy; use regex::Regex; -use std::collections::HashMap; -use std::fmt; -use std::fmt::Write as _; -use std::iter::Peekable; static ESCAPE_STRING_RE: Lazy = lazy_regex::lazy_regex!(r"([.+*?=^!:${}()\[\]|/\\])"); diff --git a/cli/lsp/performance.rs b/cli/lsp/performance.rs index b3dc53b283..8d836c7adf 100644 --- a/cli/lsp/performance.rs +++ b/cli/lsp/performance.rs @@ -1,9 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::parking_lot::Mutex; -use deno_core::serde::Deserialize; -use deno_core::serde::Serialize; -use deno_core::serde_json::json; use std::cmp; use std::collections::HashMap; use std::collections::VecDeque; @@ -12,6 +8,11 @@ use std::sync::Arc; use std::time::Duration; use std::time::Instant; +use deno_core::parking_lot::Mutex; +use deno_core::serde::Deserialize; +use deno_core::serde::Serialize; +use deno_core::serde_json::json; + use super::logging::lsp_debug; #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] diff --git a/cli/lsp/refactor.rs b/cli/lsp/refactor.rs index 82751d3b12..e1abd963a2 100644 --- a/cli/lsp/refactor.rs +++ b/cli/lsp/refactor.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // The logic of this module is heavily influenced by // https://github.com/microsoft/vscode/blob/main/extensions/typescript-language-features/src/languageFeatures/refactor.ts diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index ade353e683..f8c38d97ea 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -1,25 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::completions::IMPORT_COMMIT_CHARS; -use super::logging::lsp_log; -use super::path_to_regex::parse; -use super::path_to_regex::string_to_regex; -use super::path_to_regex::Compiler; -use super::path_to_regex::Key; -use super::path_to_regex::MatchResult; -use super::path_to_regex::Matcher; -use super::path_to_regex::StringOrNumber; -use super::path_to_regex::StringOrVec; -use super::path_to_regex::Token; - -use crate::args::CacheSetting; -use crate::cache::GlobalHttpCache; -use crate::cache::HttpCache; -use crate::file_fetcher::FetchOptions; -use crate::file_fetcher::FetchPermissionsOptionRef; -use crate::file_fetcher::FileFetcher; -use crate::http_util::HttpClientProvider; +use std::borrow::Cow; +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::Arc; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; use deno_core::serde::Deserialize; @@ -33,12 +19,28 @@ use deno_core::ModuleSpecifier; use deno_graph::Dependency; use log::error; use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::Arc; use tower_lsp::lsp_types as lsp; +use super::completions::IMPORT_COMMIT_CHARS; +use super::logging::lsp_log; +use super::path_to_regex::parse; +use super::path_to_regex::string_to_regex; +use super::path_to_regex::Compiler; +use super::path_to_regex::Key; +use super::path_to_regex::MatchResult; +use super::path_to_regex::Matcher; +use super::path_to_regex::StringOrNumber; +use super::path_to_regex::StringOrVec; +use super::path_to_regex::Token; +use crate::cache::GlobalHttpCache; +use crate::cache::HttpCache; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::FetchOptions; +use crate::file_fetcher::FetchPermissionsOptionRef; +use crate::file_fetcher::TextDecodedFile; +use crate::http_util::HttpClientProvider; +use crate::sys::CliSys; + const CONFIG_PATH: &str = "/.well-known/deno-import-intellisense.json"; const COMPONENT: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS .add(b' ') @@ -418,7 +420,7 @@ enum VariableItems { pub struct ModuleRegistry { origins: HashMap>, pub location: PathBuf, - pub file_fetcher: Arc, + pub file_fetcher: Arc, http_cache: Arc, } @@ -428,19 +430,18 @@ impl ModuleRegistry { http_client_provider: Arc, ) -> Self { // the http cache should always be the global one for registry completions - let http_cache = Arc::new(GlobalHttpCache::new( - location.clone(), - crate::cache::RealDenoCacheEnv, - )); - let mut file_fetcher = FileFetcher::new( + let http_cache = + Arc::new(GlobalHttpCache::new(CliSys::default(), location.clone())); + let file_fetcher = CliFileFetcher::new( http_cache.clone(), - CacheSetting::RespectHeaders, - true, http_client_provider, + CliSys::default(), Default::default(), None, + true, + CacheSetting::RespectHeaders, + super::logging::lsp_log_level(), ); - file_fetcher.set_download_log_level(super::logging::lsp_log_level()); Self { origins: HashMap::new(), @@ -479,13 +480,15 @@ impl ModuleRegistry { let specifier = specifier.clone(); async move { file_fetcher - .fetch_with_options(FetchOptions { - specifier: &specifier, - permissions: FetchPermissionsOptionRef::AllowAll, - maybe_auth: None, - maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"), - maybe_cache_setting: None, - }) + .fetch_with_options( + &specifier, +FetchPermissionsOptionRef::AllowAll, + FetchOptions { + maybe_auth: None, + maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"), + maybe_cache_setting: None, + } + ) .await } }).await?; @@ -500,7 +503,7 @@ impl ModuleRegistry { ); self.http_cache.set(specifier, headers_map, &[])?; } - let file = fetch_result?.into_text_decoded()?; + let file = TextDecodedFile::decode(fetch_result?)?; let config: RegistryConfigurationJson = serde_json::from_str(&file.source)?; validate_config(&config)?; Ok(config.registries) @@ -584,12 +587,11 @@ impl ModuleRegistry { // spawn due to the lsp's `Send` requirement let file = deno_core::unsync::spawn({ async move { - file_fetcher + let file = file_fetcher .fetch_bypass_permissions(&endpoint) .await - .ok()? - .into_text_decoded() - .ok() + .ok()?; + TextDecodedFile::decode(file).ok() } }) .await @@ -983,12 +985,11 @@ impl ModuleRegistry { let file_fetcher = self.file_fetcher.clone(); // spawn due to the lsp's `Send` requirement let file = deno_core::unsync::spawn(async move { - file_fetcher + let file = file_fetcher .fetch_bypass_permissions(&specifier) .await - .ok()? - .into_text_decoded() - .ok() + .ok()?; + TextDecodedFile::decode(file).ok() }) .await .ok()??; @@ -1049,7 +1050,7 @@ impl ModuleRegistry { let file_fetcher = self.file_fetcher.clone(); let specifier = specifier.clone(); async move { - file_fetcher + let file = file_fetcher .fetch_bypass_permissions(&specifier) .await .map_err(|err| { @@ -1058,9 +1059,8 @@ impl ModuleRegistry { specifier, err ); }) - .ok()? - .into_text_decoded() - .ok() + .ok()?; + TextDecodedFile::decode(file).ok() } }) .await @@ -1095,7 +1095,7 @@ impl ModuleRegistry { let file_fetcher = self.file_fetcher.clone(); let specifier = specifier.clone(); async move { - file_fetcher + let file = file_fetcher .fetch_bypass_permissions(&specifier) .await .map_err(|err| { @@ -1104,9 +1104,8 @@ impl ModuleRegistry { specifier, err ); }) - .ok()? - .into_text_decoded() - .ok() + .ok()?; + TextDecodedFile::decode(file).ok() } }) .await @@ -1129,9 +1128,10 @@ impl ModuleRegistry { #[cfg(test)] mod tests { - use super::*; use test_util::TempDir; + use super::*; + #[test] fn test_validate_registry_configuration() { assert!(validate_config(&RegistryConfigurationJson { diff --git a/cli/lsp/repl.rs b/cli/lsp/repl.rs index b4aaa8cd0d..a30427312d 100644 --- a/cli/lsp/repl.rs +++ b/cli/lsp/repl.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 28c7b04fc9..1b393ad22b 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -1,4 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::collections::HashSet; +use std::sync::Arc; use dashmap::DashMap; use deno_ast::MediaType; @@ -13,36 +20,32 @@ use deno_graph::GraphImport; use deno_graph::ModuleSpecifier; use deno_graph::Range; use deno_npm::NpmSystemInfo; +use deno_npm_cache::TarballCache; use deno_path_util::url_to_file_path; use deno_resolver::cjs::IsCjsResolutionMode; +use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_resolver::npm::CreateInNpmPkgCheckerOptions; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; use deno_resolver::NodeAndNpmReqResolver; -use deno_runtime::deno_fs; -use deno_runtime::deno_node::NodeResolver; -use deno_runtime::deno_node::PackageJsonResolver; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use indexmap::IndexMap; -use node_resolver::InNpmPackageChecker; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::collections::HashSet; -use std::sync::Arc; use super::cache::LspCache; use super::jsr::JsrCacheResolver; use crate::args::create_default_npmrc; -use crate::args::CacheSetting; use crate::args::CliLockfile; +use crate::args::LifecycleScriptsConfig; +use crate::args::NpmCachingStrategy; use crate::args::NpmInstallDepsProvider; -use crate::cache::DenoCacheEnvFsAdapter; use crate::factory::Deferred; use crate::graph_util::to_node_resolution_kind; use crate::graph_util::to_node_resolution_mode; @@ -51,22 +54,27 @@ use crate::http_util::HttpClientProvider; use crate::lsp::config::Config; use crate::lsp::config::ConfigData; use crate::lsp::logging::lsp_warn; -use crate::npm::create_cli_npm_resolver_for_lsp; +use crate::node::CliNodeResolver; +use crate::node::CliPackageJsonResolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::NpmResolutionInstaller; use crate::npm::CliByonmNpmResolverCreateOptions; -use crate::npm::CliManagedInNpmPkgCheckerCreateOptions; +use crate::npm::CliManagedNpmResolver; use crate::npm::CliManagedNpmResolverCreateOptions; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmCacheHttpClient; +use crate::npm::CliNpmRegistryInfoProvider; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; -use crate::npm::CreateInNpmPkgCheckerOptions; -use crate::npm::ManagedCliNpmResolver; +use crate::npm::NpmResolutionInitializer; use crate::resolver::CliDenoResolver; -use crate::resolver::CliDenoResolverFs; +use crate::resolver::CliIsCjsResolver; +use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; -use crate::resolver::CliResolverOptions; -use crate::resolver::IsCjsResolver; -use crate::resolver::WorkerCliNpmGraphResolver; +use crate::resolver::FoundPackageJsonDepFlag; +use crate::sys::CliSys; use crate::tsc::into_specifier_and_media_type; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; @@ -74,13 +82,16 @@ use crate::util::progress_bar::ProgressBarStyle; #[derive(Debug, Clone)] struct LspScopeResolver { resolver: Arc, - in_npm_pkg_checker: Arc, - is_cjs_resolver: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, + is_cjs_resolver: Arc, jsr_resolver: Option>, - npm_resolver: Option>, - node_resolver: Option>, + npm_graph_resolver: Arc, + npm_installer: Option>, + npm_resolution: Arc, + npm_resolver: Option, + node_resolver: Option>, npm_pkg_req_resolver: Option>, - pkg_json_resolver: Arc, + pkg_json_resolver: Arc, redirect_resolver: Option>, graph_imports: Arc>, dep_info: Arc>>, @@ -96,8 +107,11 @@ impl Default for LspScopeResolver { in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), is_cjs_resolver: factory.is_cjs_resolver().clone(), jsr_resolver: None, + npm_graph_resolver: factory.npm_graph_resolver().clone(), + npm_installer: None, npm_resolver: None, node_resolver: None, + npm_resolution: factory.services.npm_resolution.clone(), npm_pkg_req_resolver: None, pkg_json_resolver: factory.pkg_json_resolver().clone(), redirect_resolver: None, @@ -121,6 +135,7 @@ impl LspScopeResolver { } let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone(); let npm_resolver = factory.npm_resolver().cloned(); + let npm_installer = factory.npm_installer().cloned(); let node_resolver = factory.node_resolver().cloned(); let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned(); let cli_resolver = factory.cli_resolver().clone(); @@ -133,8 +148,7 @@ impl LspScopeResolver { cache.for_specifier(config_data.map(|d| d.scope.as_ref())), config_data.and_then(|d| d.lockfile.clone()), ))); - let npm_graph_resolver = cli_resolver - .create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager); + let npm_graph_resolver = factory.npm_graph_resolver(); let maybe_jsx_import_source_config = config_data.and_then(|d| d.maybe_jsx_import_source_config()); let graph_imports = config_data @@ -156,7 +170,7 @@ impl LspScopeResolver { imports, &CliJsrUrlProvider, Some(&resolver), - Some(&npm_graph_resolver), + Some(npm_graph_resolver.as_ref()), ); (referrer, graph_import) }) @@ -207,8 +221,11 @@ impl LspScopeResolver { in_npm_pkg_checker, is_cjs_resolver: factory.is_cjs_resolver().clone(), jsr_resolver, + npm_graph_resolver: factory.npm_graph_resolver().clone(), npm_pkg_req_resolver, npm_resolver, + npm_installer, + npm_resolution: factory.services.npm_resolution.clone(), node_resolver, pkg_json_resolver, redirect_resolver, @@ -220,18 +237,68 @@ impl LspScopeResolver { } fn snapshot(&self) -> Arc { + // create a copy of the resolution and then re-initialize the npm resolver from that + // todo(dsherret): this is pretty terrible... we should improve this. It should + // be possible to just change the npm_resolution on the new factory then access + // another method to create a new npm resolver let mut factory = ResolverFactory::new(self.config_data.as_ref()); - let npm_resolver = - self.npm_resolver.as_ref().map(|r| r.clone_snapshotted()); + factory + .services + .npm_resolution + .set_snapshot(self.npm_resolution.snapshot()); + let npm_resolver = self.npm_resolver.as_ref(); if let Some(npm_resolver) = &npm_resolver { - factory.set_npm_resolver(npm_resolver.clone()); + factory.set_npm_resolver(CliNpmResolver::new::( + match npm_resolver { + CliNpmResolver::Byonm(byonm_npm_resolver) => { + CliNpmResolverCreateOptions::Byonm( + CliByonmNpmResolverCreateOptions { + root_node_modules_dir: byonm_npm_resolver + .root_node_modules_path() + .map(|p| p.to_path_buf()), + sys: CliSys::default(), + pkg_json_resolver: self.pkg_json_resolver.clone(), + }, + ) + } + CliNpmResolver::Managed(managed_npm_resolver) => { + CliNpmResolverCreateOptions::Managed({ + let npmrc = self + .config_data + .as_ref() + .and_then(|d| d.npmrc.clone()) + .unwrap_or_else(create_default_npmrc); + let npm_cache_dir = Arc::new(NpmCacheDir::new( + &CliSys::default(), + managed_npm_resolver.global_cache_root_path().to_path_buf(), + npmrc.get_all_known_registries_urls(), + )); + CliManagedNpmResolverCreateOptions { + sys: CliSys::default(), + npm_cache_dir, + maybe_node_modules_path: managed_npm_resolver + .root_node_modules_path() + .map(|p| p.to_path_buf()), + npmrc, + npm_resolution: factory.services.npm_resolution.clone(), + npm_system_info: NpmSystemInfo::default(), + } + }) + } + }, + )); } + Arc::new(Self { resolver: factory.cli_resolver().clone(), in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), is_cjs_resolver: factory.is_cjs_resolver().clone(), jsr_resolver: self.jsr_resolver.clone(), + npm_graph_resolver: factory.npm_graph_resolver().clone(), + // npm installer isn't necessary for a snapshot + npm_installer: None, npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(), + npm_resolution: factory.services.npm_resolution.clone(), npm_resolver: factory.npm_resolver().cloned(), node_resolver: factory.node_resolver().cloned(), redirect_resolver: self.redirect_resolver.clone(), @@ -318,14 +385,12 @@ impl LspResolver { if let Some(dep_info) = dep_info { *resolver.dep_info.lock() = dep_info.clone(); } - if let Some(npm_resolver) = resolver.npm_resolver.as_ref() { - if let Some(npm_resolver) = npm_resolver.as_managed() { - let reqs = dep_info - .map(|i| i.npm_reqs.iter().cloned().collect::>()) - .unwrap_or_default(); - if let Err(err) = npm_resolver.set_package_reqs(&reqs).await { - lsp_warn!("Could not set npm package requirements: {:#}", err); - } + if let Some(npm_installer) = resolver.npm_installer.as_ref() { + let reqs = dep_info + .map(|i| i.npm_reqs.iter().cloned().collect::>()) + .unwrap_or_default(); + if let Err(err) = npm_installer.set_package_reqs(&reqs).await { + lsp_warn!("Could not set npm package requirements: {:#}", err); } } } @@ -339,20 +404,18 @@ impl LspResolver { resolver.resolver.as_ref() } - pub fn create_graph_npm_resolver( + pub fn as_graph_npm_resolver( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> WorkerCliNpmGraphResolver { + ) -> &Arc { let resolver = self.get_scope_resolver(file_referrer); - resolver - .resolver - .create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager) + &resolver.npm_graph_resolver } pub fn as_is_cjs_resolver( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> &IsCjsResolver { + ) -> &CliIsCjsResolver { let resolver = self.get_scope_resolver(file_referrer); resolver.is_cjs_resolver.as_ref() } @@ -368,7 +431,7 @@ impl LspResolver { pub fn in_npm_pkg_checker( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> &Arc { + ) -> &DenoInNpmPackageChecker { let resolver = self.get_scope_resolver(file_referrer); &resolver.in_npm_pkg_checker } @@ -376,7 +439,7 @@ impl LspResolver { pub fn maybe_managed_npm_resolver( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> Option<&ManagedCliNpmResolver> { + ) -> Option<&CliManagedNpmResolver> { let resolver = self.get_scope_resolver(file_referrer); resolver.npm_resolver.as_ref().and_then(|r| r.as_managed()) } @@ -384,7 +447,7 @@ impl LspResolver { pub fn pkg_json_resolver( &self, referrer: &ModuleSpecifier, - ) -> &Arc { + ) -> &Arc { let resolver = self.get_scope_resolver(Some(referrer)); &resolver.pkg_json_resolver } @@ -590,43 +653,50 @@ pub struct ScopeDepInfo { #[derive(Default)] struct ResolverFactoryServices { cli_resolver: Deferred>, - in_npm_pkg_checker: Deferred>, - is_cjs_resolver: Deferred>, - node_resolver: Deferred>>, + found_pkg_json_dep_flag: Arc, + in_npm_pkg_checker: Deferred, + is_cjs_resolver: Deferred>, + node_resolver: Deferred>>, + npm_graph_resolver: Deferred>, + npm_installer: Option>, npm_pkg_req_resolver: Deferred>>, - npm_resolver: Option>, + npm_resolver: Option, + npm_resolution: Arc, } struct ResolverFactory<'a> { config_data: Option<&'a Arc>, - fs: Arc, - pkg_json_resolver: Arc, + pkg_json_resolver: Arc, + sys: CliSys, services: ResolverFactoryServices, } impl<'a> ResolverFactory<'a> { pub fn new(config_data: Option<&'a Arc>) -> Self { - let fs = Arc::new(deno_fs::RealFs); - let pkg_json_resolver = Arc::new(PackageJsonResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), - )); + let sys = CliSys::default(); + let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone())); Self { config_data, - fs, pkg_json_resolver, + sys, services: Default::default(), } } + // todo(dsherret): probably this method could be removed in the future + // and instead just `npm_resolution_initializer.ensure_initialized()` could + // be called. The reason this exists is because creating the npm resolvers + // used to be async. async fn init_npm_resolver( &mut self, http_client_provider: &Arc, cache: &LspCache, ) { let enable_byonm = self.config_data.map(|d| d.byonm).unwrap_or(false); + let sys = CliSys::default(); let options = if enable_byonm { CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { - fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)), + sys, pkg_json_resolver: self.pkg_json_resolver.clone(), root_node_modules_dir: self.config_data.and_then(|config_data| { config_data.node_modules_dir.clone().or_else(|| { @@ -642,13 +712,34 @@ impl<'a> ResolverFactory<'a> { .and_then(|d| d.npmrc.clone()) .unwrap_or_else(create_default_npmrc); let npm_cache_dir = Arc::new(NpmCacheDir::new( - &DenoCacheEnvFsAdapter(self.fs.as_ref()), + &sys, cache.deno_dir().npm_folder_path(), npmrc.get_all_known_registries_urls(), )); - CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { - http_client_provider: http_client_provider.clone(), - snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) { + let npm_cache = Arc::new(CliNpmCache::new( + npm_cache_dir.clone(), + sys.clone(), + // Use an "only" cache setting in order to make the + // user do an explicit "cache" command and prevent + // the cache from being filled with lots of packages while + // the user is typing. + deno_npm_cache::NpmCacheSetting::Only, + npmrc.clone(), + )); + let pb = ProgressBar::new(ProgressBarStyle::TextOnly); + let npm_client = Arc::new(CliNpmCacheHttpClient::new( + http_client_provider.clone(), + pb.clone(), + )); + let registry_info_provider = Arc::new(CliNpmRegistryInfoProvider::new( + npm_cache.clone(), + npm_client.clone(), + npmrc.clone(), + )); + let npm_resolution_initializer = Arc::new(NpmResolutionInitializer::new( + registry_info_provider.clone(), + self.services.npm_resolution.clone(), + match self.config_data.and_then(|d| d.lockfile.as_ref()) { Some(lockfile) => { CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( lockfile.clone(), @@ -656,35 +747,69 @@ impl<'a> ResolverFactory<'a> { } None => CliNpmResolverManagedSnapshotOption::Specified(None), }, - // Don't provide the lockfile. We don't want these resolvers - // updating it. Only the cache request should update the lockfile. - maybe_lockfile: None, - fs: Arc::new(deno_fs::RealFs), + )); + // Don't provide the lockfile. We don't want these resolvers + // updating it. Only the cache request should update the lockfile. + let maybe_lockfile: Option> = None; + let maybe_node_modules_path = + self.config_data.and_then(|d| d.node_modules_dir.clone()); + let tarball_cache = Arc::new(TarballCache::new( + npm_cache.clone(), + npm_client.clone(), + sys.clone(), + npmrc.clone(), + )); + let npm_resolution_installer = Arc::new(NpmResolutionInstaller::new( + registry_info_provider, + self.services.npm_resolution.clone(), + maybe_lockfile.clone(), + )); + let npm_installer = Arc::new(NpmInstaller::new( + npm_cache.clone(), + Arc::new(NpmInstallDepsProvider::empty()), + self.services.npm_resolution.clone(), + npm_resolution_initializer.clone(), + npm_resolution_installer, + &pb, + sys.clone(), + tarball_cache.clone(), + maybe_lockfile, + maybe_node_modules_path.clone(), + LifecycleScriptsConfig::default(), + NpmSystemInfo::default(), + )); + self.set_npm_installer(npm_installer); + // spawn due to the lsp's `Send` requirement + deno_core::unsync::spawn(async move { + if let Err(err) = npm_resolution_initializer.ensure_initialized().await + { + log::warn!("failed to initialize npm resolution: {}", err); + } + }) + .await + .unwrap(); + + CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { + sys: CliSys::default(), npm_cache_dir, - // Use an "only" cache setting in order to make the - // user do an explicit "cache" command and prevent - // the cache from being filled with lots of packages while - // the user is typing. - cache_setting: CacheSetting::Only, - text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly), - maybe_node_modules_path: self - .config_data - .and_then(|d| d.node_modules_dir.clone()), - // only used for top level install, so we can ignore this - npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), + maybe_node_modules_path, npmrc, + npm_resolution: self.services.npm_resolution.clone(), npm_system_info: NpmSystemInfo::default(), - lifecycle_scripts: Default::default(), }) }; - self.set_npm_resolver(create_cli_npm_resolver_for_lsp(options).await); + self.set_npm_resolver(CliNpmResolver::new(options)); } - pub fn set_npm_resolver(&mut self, npm_resolver: Arc) { + pub fn set_npm_installer(&mut self, npm_installer: Arc) { + self.services.npm_installer = Some(npm_installer); + } + + pub fn set_npm_resolver(&mut self, npm_resolver: CliNpmResolver) { self.services.npm_resolver = Some(npm_resolver); } - pub fn npm_resolver(&self) -> Option<&Arc> { + pub fn npm_resolver(&self) -> Option<&CliNpmResolver> { self.services.npm_resolver.as_ref() } @@ -721,43 +846,55 @@ impl<'a> ResolverFactory<'a> { is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false), maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()), })); - Arc::new(CliResolver::new(CliResolverOptions { + Arc::new(CliResolver::new( deno_resolver, - npm_resolver: self.npm_resolver().cloned(), - bare_node_builtins_enabled: self - .config_data - .is_some_and(|d| d.unstable.contains("bare-node-builtins")), - })) + self.services.found_pkg_json_dep_flag.clone(), + )) }) } - pub fn pkg_json_resolver(&self) -> &Arc { + pub fn npm_installer(&self) -> Option<&Arc> { + self.services.npm_installer.as_ref() + } + + pub fn npm_graph_resolver(&self) -> &Arc { + self.services.npm_graph_resolver.get_or_init(|| { + Arc::new(CliNpmGraphResolver::new( + None, + self.services.found_pkg_json_dep_flag.clone(), + self + .config_data + .is_some_and(|d| d.unstable.contains("bare-node-builtins")), + NpmCachingStrategy::Eager, + )) + }) + } + + pub fn pkg_json_resolver(&self) -> &Arc { &self.pkg_json_resolver } - pub fn in_npm_pkg_checker(&self) -> &Arc { + pub fn in_npm_pkg_checker(&self) -> &DenoInNpmPackageChecker { self.services.in_npm_pkg_checker.get_or_init(|| { - crate::npm::create_in_npm_pkg_checker( - match self.services.npm_resolver.as_ref().map(|r| r.as_inner()) { - Some(crate::npm::InnerCliNpmResolverRef::Byonm(_)) | None => { - CreateInNpmPkgCheckerOptions::Byonm - } - Some(crate::npm::InnerCliNpmResolverRef::Managed(m)) => { - CreateInNpmPkgCheckerOptions::Managed( - CliManagedInNpmPkgCheckerCreateOptions { - root_cache_dir_url: m.global_cache_root_url(), - maybe_node_modules_path: m.maybe_node_modules_path(), - }, - ) - } - }, - ) + DenoInNpmPackageChecker::new(match &self.services.npm_resolver { + Some(CliNpmResolver::Byonm(_)) | None => { + CreateInNpmPkgCheckerOptions::Byonm + } + Some(CliNpmResolver::Managed(m)) => { + CreateInNpmPkgCheckerOptions::Managed( + ManagedInNpmPkgCheckerCreateOptions { + root_cache_dir_url: m.global_cache_root_url(), + maybe_node_modules_path: m.root_node_modules_path(), + }, + ) + } + }) }) } - pub fn is_cjs_resolver(&self) -> &Arc { + pub fn is_cjs_resolver(&self) -> &Arc { self.services.is_cjs_resolver.get_or_init(|| { - Arc::new(IsCjsResolver::new( + Arc::new(CliIsCjsResolver::new( self.in_npm_pkg_checker().clone(), self.pkg_json_resolver().clone(), if self @@ -772,17 +909,19 @@ impl<'a> ResolverFactory<'a> { }) } - pub fn node_resolver(&self) -> Option<&Arc> { + pub fn node_resolver(&self) -> Option<&Arc> { self .services .node_resolver .get_or_init(|| { let npm_resolver = self.services.npm_resolver.as_ref()?; - Some(Arc::new(NodeResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(self.fs.clone()), + Some(Arc::new(CliNodeResolver::new( self.in_npm_pkg_checker().clone(), - npm_resolver.clone().into_npm_pkg_folder_resolver(), + RealIsBuiltInNodeModuleChecker, + npm_resolver.clone(), self.pkg_json_resolver.clone(), + self.sys.clone(), + node_resolver::ConditionsFromResolutionMode::default(), ))) }) .as_ref() @@ -796,11 +935,10 @@ impl<'a> ResolverFactory<'a> { let node_resolver = self.node_resolver()?; let npm_resolver = self.npm_resolver()?; Some(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { - byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), - fs: CliDenoResolverFs(self.fs.clone()), in_npm_pkg_checker: self.in_npm_pkg_checker().clone(), node_resolver: node_resolver.clone(), - npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), + npm_resolver: npm_resolver.clone(), + sys: self.sys.clone(), }))) }) .as_ref() diff --git a/cli/lsp/search.rs b/cli/lsp/search.rs index 8933eeb186..281542742d 100644 --- a/cli/lsp/search.rs +++ b/cli/lsp/search.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::sync::Arc; use deno_core::error::AnyError; use deno_semver::package::PackageNv; use deno_semver::Version; -use std::sync::Arc; #[async_trait::async_trait] pub trait PackageSearchApi { @@ -15,10 +16,12 @@ pub trait PackageSearchApi { #[cfg(test)] pub mod tests { - use super::*; - use deno_core::anyhow::anyhow; use std::collections::BTreeMap; + use deno_core::anyhow::anyhow; + + use super::*; + #[derive(Debug, Default)] pub struct TestPackageSearchApi { /// [(name -> [(version -> [export])])] @@ -67,7 +70,9 @@ pub mod tests { &self, nv: &PackageNv, ) -> Result>, AnyError> { - let Some(exports_by_version) = self.package_versions.get(&nv.name) else { + let Some(exports_by_version) = + self.package_versions.get(nv.name.as_str()) + else { return Err(anyhow!("Package not found.")); }; let Some(exports) = exports_by_version.get(&nv.version) else { diff --git a/cli/lsp/semantic_tokens.rs b/cli/lsp/semantic_tokens.rs index 0cf154d0ff..d2466f4c14 100644 --- a/cli/lsp/semantic_tokens.rs +++ b/cli/lsp/semantic_tokens.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // The logic of this module is heavily influenced by // https://github.com/microsoft/vscode/blob/main/extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts @@ -7,6 +7,7 @@ use std::ops::Index; use std::ops::IndexMut; + use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types::SemanticToken; use tower_lsp::lsp_types::SemanticTokenModifier; diff --git a/cli/lsp/testing/collectors.rs b/cli/lsp/testing/collectors.rs index 2dd7ec0d96..8a36ca1f25 100644 --- a/cli/lsp/testing/collectors.rs +++ b/cli/lsp/testing/collectors.rs @@ -1,8 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::lsp::analysis::source_range_to_lsp_range; - -use super::definitions::TestModule; +use std::collections::HashMap; +use std::collections::HashSet; use deno_ast::swc::ast; use deno_ast::swc::visit::Visit; @@ -11,10 +10,11 @@ use deno_ast::SourceRangedForSpanned; use deno_ast::SourceTextInfo; use deno_core::ModuleSpecifier; use lsp::Range; -use std::collections::HashMap; -use std::collections::HashSet; use tower_lsp::lsp_types as lsp; +use super::definitions::TestModule; +use crate::lsp::analysis::source_range_to_lsp_range; + /// Parse an arrow expression for any test steps and return them. fn visit_arrow( arrow_expr: &ast::ArrowExpr, @@ -626,12 +626,12 @@ impl Visit for TestCollector { #[cfg(test)] pub mod tests { - use crate::lsp::testing::definitions::TestDefinition; - - use super::*; use deno_core::resolve_url; use lsp::Position; + use super::*; + use crate::lsp::testing::definitions::TestDefinition; + pub fn new_range(l1: u32, c1: u32, l2: u32, c2: u32) -> Range { Range::new(Position::new(l1, c1), Position::new(l2, c2)) } diff --git a/cli/lsp/testing/definitions.rs b/cli/lsp/testing/definitions.rs index f23411852f..8277dcbf00 100644 --- a/cli/lsp/testing/definitions.rs +++ b/cli/lsp/testing/definitions.rs @@ -1,8 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashMap; +use std::collections::HashSet; + +use deno_core::error::AnyError; +use deno_core::ModuleSpecifier; +use lsp::Range; +use tower_lsp::lsp_types as lsp; use super::lsp_custom; use super::lsp_custom::TestData; - use crate::lsp::client::TestingNotification; use crate::lsp::logging::lsp_warn; use crate::lsp::urls::url_to_uri; @@ -10,13 +17,6 @@ use crate::tools::test::TestDescription; use crate::tools::test::TestStepDescription; use crate::util::checksum; -use deno_core::error::AnyError; -use deno_core::ModuleSpecifier; -use lsp::Range; -use std::collections::HashMap; -use std::collections::HashSet; -use tower_lsp::lsp_types as lsp; - #[derive(Debug, Clone, PartialEq)] pub struct TestDefinition { pub id: String, diff --git a/cli/lsp/testing/execution.rs b/cli/lsp/testing/execution.rs index 88fb496e4e..3d6d6f6b73 100644 --- a/cli/lsp/testing/execution.rs +++ b/cli/lsp/testing/execution.rs @@ -1,24 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::definitions::TestDefinition; -use super::definitions::TestModule; -use super::lsp_custom; -use super::server::TestServerTests; - -use crate::args::flags_from_vec; -use crate::args::DenoSubcommand; -use crate::factory::CliFactory; -use crate::lsp::client::Client; -use crate::lsp::client::TestingNotification; -use crate::lsp::config; -use crate::lsp::logging::lsp_log; -use crate::lsp::urls::uri_parse_unencoded; -use crate::lsp::urls::uri_to_url; -use crate::lsp::urls::url_to_uri; -use crate::tools::test; -use crate::tools::test::create_test_event_channel; -use crate::tools::test::FailFastTracker; -use crate::tools::test::TestFailureFormatOptions; +use std::borrow::Cow; +use std::collections::HashMap; +use std::collections::HashSet; +use std::num::NonZeroUsize; +use std::sync::Arc; +use std::time::Duration; +use std::time::Instant; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; @@ -34,16 +22,28 @@ use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::tokio_util::create_and_run_current_thread; use indexmap::IndexMap; -use std::borrow::Cow; -use std::collections::HashMap; -use std::collections::HashSet; -use std::num::NonZeroUsize; -use std::sync::Arc; -use std::time::Duration; -use std::time::Instant; use tokio_util::sync::CancellationToken; use tower_lsp::lsp_types as lsp; +use super::definitions::TestDefinition; +use super::definitions::TestModule; +use super::lsp_custom; +use super::server::TestServerTests; +use crate::args::flags_from_vec; +use crate::args::DenoSubcommand; +use crate::factory::CliFactory; +use crate::lsp::client::Client; +use crate::lsp::client::TestingNotification; +use crate::lsp::config; +use crate::lsp::logging::lsp_log; +use crate::lsp::urls::uri_parse_unencoded; +use crate::lsp::urls::uri_to_url; +use crate::lsp::urls::url_to_uri; +use crate::tools::test; +use crate::tools::test::create_test_event_channel; +use crate::tools::test::FailFastTracker; +use crate::tools::test::TestFailureFormatOptions; + /// Logic to convert a test request into a set of test modules to be tested and /// any filters to be applied to those tests fn as_queue_and_filters( @@ -794,9 +794,10 @@ impl LspTestReporter { #[cfg(test)] mod tests { + use deno_core::serde_json::json; + use super::*; use crate::lsp::testing::collectors::tests::new_range; - use deno_core::serde_json::json; #[test] fn test_as_queue_and_filters() { diff --git a/cli/lsp/testing/lsp_custom.rs b/cli/lsp/testing/lsp_custom.rs index 84ac30de57..5ee6e84f52 100644 --- a/cli/lsp/testing/lsp_custom.rs +++ b/cli/lsp/testing/lsp_custom.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde::Deserialize; use deno_core::serde::Serialize; diff --git a/cli/lsp/testing/mod.rs b/cli/lsp/testing/mod.rs index d285481f06..1398ba1eca 100644 --- a/cli/lsp/testing/mod.rs +++ b/cli/lsp/testing/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod collectors; mod definitions; diff --git a/cli/lsp/testing/server.rs b/cli/lsp/testing/server.rs index c9c39d9ffc..e0570fcada 100644 --- a/cli/lsp/testing/server.rs +++ b/cli/lsp/testing/server.rs @@ -1,16 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::definitions::TestModule; -use super::execution::TestRun; -use super::lsp_custom; - -use crate::lsp::client::Client; -use crate::lsp::client::TestingNotification; -use crate::lsp::config; -use crate::lsp::documents::DocumentsFilter; -use crate::lsp::language_server::StateSnapshot; -use crate::lsp::performance::Performance; -use crate::lsp::urls::url_to_uri; +use std::collections::HashMap; +use std::collections::HashSet; +use std::sync::Arc; +use std::thread; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; @@ -18,15 +11,22 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::ModuleSpecifier; use deno_runtime::tokio_util::create_basic_runtime; -use std::collections::HashMap; -use std::collections::HashSet; -use std::sync::Arc; -use std::thread; use tokio::sync::mpsc; use tower_lsp::jsonrpc::Error as LspError; use tower_lsp::jsonrpc::Result as LspResult; use tower_lsp::lsp_types as lsp; +use super::definitions::TestModule; +use super::execution::TestRun; +use super::lsp_custom; +use crate::lsp::client::Client; +use crate::lsp::client::TestingNotification; +use crate::lsp::config; +use crate::lsp::documents::DocumentsFilter; +use crate::lsp::language_server::StateSnapshot; +use crate::lsp::performance::Performance; +use crate::lsp::urls::url_to_uri; + fn as_delete_notification( url: &ModuleSpecifier, ) -> Result { diff --git a/cli/lsp/text.rs b/cli/lsp/text.rs index 88f27915bb..efb9a072a4 100644 --- a/cli/lsp/text.rs +++ b/cli/lsp/text.rs @@ -1,10 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashMap; -use deno_core::error::custom_error; use deno_core::error::AnyError; +use deno_error::JsErrorBox; use dissimilar::diff; use dissimilar::Chunk; -use std::collections::HashMap; use text_size::TextRange; use text_size::TextSize; use tower_lsp::jsonrpc; @@ -136,7 +137,7 @@ impl LineIndex { if let Some(line_offset) = self.utf8_offsets.get(position.line as usize) { Ok(line_offset + col) } else { - Err(custom_error("OutOfRange", "The position is out of range.")) + Err(JsErrorBox::new("OutOfRange", "The position is out of range.").into()) } } @@ -156,7 +157,7 @@ impl LineIndex { if let Some(line_offset) = self.utf16_offsets.get(position.line as usize) { Ok(line_offset + TextSize::from(position.character)) } else { - Err(custom_error("OutOfRange", "The position is out of range.")) + Err(JsErrorBox::new("OutOfRange", "The position is out of range.").into()) } } diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 931e008a72..e2a0fc430d 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -1,4 +1,67 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::cell::RefCell; +use std::cmp; +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::HashSet; +use std::convert::Infallible; +use std::net::SocketAddr; +use std::ops::Range; +use std::path::Path; +use std::rc::Rc; +use std::sync::Arc; +use std::thread; + +use dashmap::DashMap; +use deno_ast::MediaType; +use deno_core::anyhow::anyhow; +use deno_core::anyhow::Context as _; +use deno_core::convert::Smi; +use deno_core::convert::ToV8; +use deno_core::error::AnyError; +use deno_core::futures::stream::FuturesOrdered; +use deno_core::futures::FutureExt; +use deno_core::futures::StreamExt; +use deno_core::op2; +use deno_core::parking_lot::Mutex; +use deno_core::resolve_url; +use deno_core::serde::de; +use deno_core::serde::Deserialize; +use deno_core::serde::Serialize; +use deno_core::serde_json; +use deno_core::serde_json::json; +use deno_core::serde_json::Value; +use deno_core::serde_v8; +use deno_core::v8; +use deno_core::JsRuntime; +use deno_core::ModuleSpecifier; +use deno_core::OpState; +use deno_core::PollEventLoopOptions; +use deno_core::RuntimeOptions; +use deno_path_util::url_to_file_path; +use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES; +use deno_runtime::inspector_server::InspectorServer; +use deno_runtime::tokio_util::create_basic_runtime; +use indexmap::IndexMap; +use indexmap::IndexSet; +use lazy_regex::lazy_regex; +use log::error; +use node_resolver::ResolutionMode; +use once_cell::sync::Lazy; +use regex::Captures; +use regex::Regex; +use serde_repr::Deserialize_repr; +use serde_repr::Serialize_repr; +use text_size::TextRange; +use text_size::TextSize; +use tokio::sync::mpsc; +use tokio::sync::mpsc::UnboundedReceiver; +use tokio::sync::oneshot; +use tokio_util::sync::CancellationToken; +use tower_lsp::jsonrpc::Error as LspError; +use tower_lsp::jsonrpc::Result as LspResult; +use tower_lsp::lsp_types as lsp; use super::analysis::CodeActionData; use super::code_lens; @@ -23,7 +86,6 @@ use super::urls::uri_to_url; use super::urls::url_to_uri; use super::urls::INVALID_SPECIFIER; use super::urls::INVALID_URI; - use crate::args::jsr_url; use crate::args::FmtOptionsConfig; use crate::lsp::logging::lsp_warn; @@ -35,68 +97,6 @@ use crate::util::path::to_percent_decoded_str; use crate::util::result::InfallibleResultExt; use crate::util::v8::convert; use crate::worker::create_isolate_create_params; -use deno_core::convert::Smi; -use deno_core::convert::ToV8; -use deno_core::error::StdAnyError; -use deno_core::futures::stream::FuturesOrdered; -use deno_core::futures::StreamExt; - -use dashmap::DashMap; -use deno_ast::MediaType; -use deno_core::anyhow::anyhow; -use deno_core::anyhow::Context as _; -use deno_core::error::AnyError; -use deno_core::futures::FutureExt; -use deno_core::op2; -use deno_core::parking_lot::Mutex; -use deno_core::resolve_url; -use deno_core::serde::de; -use deno_core::serde::Deserialize; -use deno_core::serde::Serialize; -use deno_core::serde_json; -use deno_core::serde_json::json; -use deno_core::serde_json::Value; -use deno_core::serde_v8; -use deno_core::v8; -use deno_core::JsRuntime; -use deno_core::ModuleSpecifier; -use deno_core::OpState; -use deno_core::PollEventLoopOptions; -use deno_core::RuntimeOptions; -use deno_path_util::url_to_file_path; -use deno_runtime::inspector_server::InspectorServer; -use deno_runtime::tokio_util::create_basic_runtime; -use indexmap::IndexMap; -use indexmap::IndexSet; -use lazy_regex::lazy_regex; -use log::error; -use node_resolver::ResolutionMode; -use once_cell::sync::Lazy; -use regex::Captures; -use regex::Regex; -use serde_repr::Deserialize_repr; -use serde_repr::Serialize_repr; -use std::cell::RefCell; -use std::cmp; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::HashSet; -use std::convert::Infallible; -use std::net::SocketAddr; -use std::ops::Range; -use std::path::Path; -use std::rc::Rc; -use std::sync::Arc; -use std::thread; -use text_size::TextRange; -use text_size::TextSize; -use tokio::sync::mpsc; -use tokio::sync::mpsc::UnboundedReceiver; -use tokio::sync::oneshot; -use tokio_util::sync::CancellationToken; -use tower_lsp::jsonrpc::Error as LspError; -use tower_lsp::jsonrpc::Result as LspResult; -use tower_lsp::lsp_types as lsp; static BRACKET_ACCESSOR_RE: Lazy = lazy_regex!(r#"^\[['"](.+)[\['"]\]$"#); @@ -3411,10 +3411,18 @@ fn parse_code_actions( additional_text_edits.extend(change.text_changes.iter().map(|tc| { let mut text_edit = tc.as_text_edit(asset_or_doc.line_index()); if let Some(specifier_rewrite) = &data.specifier_rewrite { - text_edit.new_text = text_edit.new_text.replace( - &specifier_rewrite.old_specifier, - &specifier_rewrite.new_specifier, - ); + let specifier_index = text_edit + .new_text + .char_indices() + .find_map(|(b, c)| (c == '\'' || c == '"').then_some(b)); + if let Some(i) = specifier_index { + let mut specifier_part = text_edit.new_text.split_off(i); + specifier_part = specifier_part.replace( + &specifier_rewrite.old_specifier, + &specifier_rewrite.new_specifier, + ); + text_edit.new_text.push_str(&specifier_part); + } if let Some(deno_types_specifier) = &specifier_rewrite.new_deno_types_specifier { @@ -3587,10 +3595,17 @@ impl CompletionEntryDetails { &mut insert_replace_edit.new_text } }; - *new_text = new_text.replace( - &specifier_rewrite.old_specifier, - &specifier_rewrite.new_specifier, - ); + let specifier_index = new_text + .char_indices() + .find_map(|(b, c)| (c == '\'' || c == '"').then_some(b)); + if let Some(i) = specifier_index { + let mut specifier_part = new_text.split_off(i); + specifier_part = specifier_part.replace( + &specifier_rewrite.old_specifier, + &specifier_rewrite.new_specifier, + ); + new_text.push_str(&specifier_part); + } if let Some(deno_types_specifier) = &specifier_rewrite.new_deno_types_specifier { @@ -3729,7 +3744,7 @@ pub struct CompletionItemData { #[serde(rename_all = "camelCase")] struct CompletionEntryDataAutoImport { module_specifier: String, - file_name: String, + file_name: Option, } #[derive(Debug)] @@ -3786,9 +3801,20 @@ impl CompletionEntry { else { return; }; - if let Ok(normalized) = specifier_map.normalize(&raw.file_name) { - self.auto_import_data = - Some(CompletionNormalizedAutoImportData { raw, normalized }); + if let Some(file_name) = &raw.file_name { + if let Ok(normalized) = specifier_map.normalize(file_name) { + self.auto_import_data = + Some(CompletionNormalizedAutoImportData { raw, normalized }); + } + } else if SUPPORTED_BUILTIN_NODE_MODULES + .contains(&raw.module_specifier.as_str()) + { + if let Ok(normalized) = + resolve_url(&format!("node:{}", &raw.module_specifier)) + { + self.auto_import_data = + Some(CompletionNormalizedAutoImportData { raw, normalized }); + } } } @@ -3946,6 +3972,11 @@ impl CompletionEntry { if let Some(mut new_specifier) = import_mapper .check_specifier(&import_data.normalized, specifier) .or_else(|| relative_specifier(specifier, &import_data.normalized)) + .or_else(|| { + ModuleSpecifier::parse(&import_data.raw.module_specifier) + .is_ok() + .then(|| import_data.normalized.to_string()) + }) { if new_specifier.contains("/node_modules/") { return None; @@ -4304,7 +4335,7 @@ impl TscSpecifierMap { pub fn normalize>( &self, specifier: S, - ) -> Result { + ) -> Result { let original = specifier.as_ref(); if let Some(specifier) = self.normalized_specifiers.get(original) { return Ok(specifier.clone()); @@ -4312,7 +4343,7 @@ impl TscSpecifierMap { let specifier_str = original.replace(".d.ts.d.ts", ".d.ts"); let specifier = match ModuleSpecifier::parse(&specifier_str) { Ok(s) => s, - Err(err) => return Err(err.into()), + Err(err) => return Err(err), }; if specifier.as_str() != original { self @@ -4410,6 +4441,16 @@ fn op_is_node_file(state: &mut OpState, #[string] path: String) -> bool { r } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +enum LoadError { + #[error("{0}")] + #[class(inherit)] + UrlParse(#[from] deno_core::url::ParseError), + #[error("{0}")] + #[class(inherit)] + SerdeV8(#[from] serde_v8::Error), +} + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct LoadResponse { @@ -4424,7 +4465,7 @@ fn op_load<'s>( scope: &'s mut v8::HandleScope, state: &mut OpState, #[string] specifier: &str, -) -> Result, AnyError> { +) -> Result, LoadError> { let state = state.borrow_mut::(); let mark = state .performance @@ -4455,7 +4496,7 @@ fn op_load<'s>( fn op_release( state: &mut OpState, #[string] specifier: &str, -) -> Result<(), AnyError> { +) -> Result<(), deno_core::url::ParseError> { let state = state.borrow_mut::(); let mark = state .performance @@ -4468,11 +4509,12 @@ fn op_release( #[op2] #[serde] +#[allow(clippy::type_complexity)] fn op_resolve( state: &mut OpState, #[string] base: String, #[serde] specifiers: Vec<(bool, String)>, -) -> Result>, AnyError> { +) -> Result)>>, deno_core::url::ParseError> { op_resolve_inner(state, ResolveArgs { base, specifiers }) } @@ -4484,7 +4526,7 @@ struct TscRequestArray { } impl<'a> ToV8<'a> for TscRequestArray { - type Error = StdAnyError; + type Error = serde_v8::Error; fn to_v8( self, @@ -4496,11 +4538,10 @@ impl<'a> ToV8<'a> for TscRequestArray { let method_name = deno_core::FastString::from_static(method_name) .v8_string(scope) + .unwrap() .into(); let args = args.unwrap_or_else(|| v8::Array::new(scope, 0).into()); - let scope_url = serde_v8::to_v8(scope, self.scope) - .map_err(AnyError::from) - .map_err(StdAnyError::from)?; + let scope_url = serde_v8::to_v8(scope, self.scope)?; let change = self.change.to_v8(scope).unwrap_infallible(); @@ -4555,10 +4596,11 @@ async fn op_poll_requests( } #[inline] +#[allow(clippy::type_complexity)] fn op_resolve_inner( state: &mut OpState, args: ResolveArgs, -) -> Result>, AnyError> { +) -> Result)>>, deno_core::url::ParseError> { let state = state.borrow_mut::(); let mark = state.performance.mark_with_args("tsc.op.op_resolve", &args); let referrer = state.specifier_map.normalize(&args.base)?; @@ -4571,7 +4613,11 @@ fn op_resolve_inner( o.map(|(s, mt)| { ( state.specifier_map.denormalize(&s), - mt.as_ts_extension().to_string(), + if matches!(mt, MediaType::Unknown) { + None + } else { + Some(mt.as_ts_extension().to_string()) + }, ) }) }) @@ -4715,7 +4761,7 @@ fn op_script_names(state: &mut OpState) -> ScriptNames { fn op_script_version( state: &mut OpState, #[string] specifier: &str, -) -> Result, AnyError> { +) -> Result, deno_core::url::ParseError> { let state = state.borrow_mut::(); let mark = state.performance.mark("tsc.op.op_script_version"); let specifier = state.specifier_map.normalize(specifier)?; @@ -5370,7 +5416,8 @@ impl TscRequest { fn to_server_request<'s>( &self, scope: &mut v8::HandleScope<'s>, - ) -> Result<(&'static str, Option>), AnyError> { + ) -> Result<(&'static str, Option>), serde_v8::Error> + { let args = match self { TscRequest::GetDiagnostics(args) => { ("$getDiagnostics", Some(serde_v8::to_v8(scope, args)?)) @@ -5513,9 +5560,11 @@ impl TscRequest { #[cfg(test)] mod tests { + use pretty_assertions::assert_eq; + use test_util::TempDir; + use super::*; use crate::cache::HttpCache; - use crate::http_util::HeadersMap; use crate::lsp::cache::LspCache; use crate::lsp::config::Config; use crate::lsp::config::WorkspaceSettings; @@ -5523,8 +5572,6 @@ mod tests { use crate::lsp::documents::LanguageId; use crate::lsp::resolver::LspResolver; use crate::lsp::text::LineIndex; - use pretty_assertions::assert_eq; - use test_util::TempDir; async fn setup( ts_config: Value, @@ -5542,7 +5589,6 @@ mod tests { }) .to_string(), temp_dir.url().join("deno.json").unwrap(), - &Default::default(), ) .unwrap(), ) @@ -5745,6 +5791,7 @@ mod tests { "sourceLine": " import { A } from \".\";", "category": 2, "code": 6133, + "reportsUnnecessary": true, }] }) ); @@ -5827,6 +5874,7 @@ mod tests { "sourceLine": " import {", "category": 2, "code": 6192, + "reportsUnnecessary": true, }, { "start": { "line": 8, @@ -5950,7 +5998,7 @@ mod tests { .global() .set( &specifier_dep, - HeadersMap::default(), + Default::default(), b"export const b = \"b\";\n", ) .unwrap(); @@ -5989,7 +6037,7 @@ mod tests { .global() .set( &specifier_dep, - HeadersMap::default(), + Default::default(), b"export const b = \"b\";\n\nexport const a = \"b\";\n", ) .unwrap(); @@ -6418,7 +6466,7 @@ mod tests { resolved, vec![Some(( temp_dir.url().join("b.ts").unwrap().to_string(), - MediaType::TypeScript.as_ts_extension().to_string() + Some(MediaType::TypeScript.as_ts_extension().to_string()) ))] ); } diff --git a/cli/lsp/urls.rs b/cli/lsp/urls.rs index 6c7da4f134..2aadaf5352 100644 --- a/cli/lsp/urls.rs +++ b/cli/lsp/urls.rs @@ -1,4 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashMap; +use std::str::FromStr; +use std::sync::Arc; use deno_ast::MediaType; use deno_core::error::AnyError; @@ -8,9 +12,6 @@ use deno_core::url::Url; use deno_core::ModuleSpecifier; use lsp_types::Uri; use once_cell::sync::Lazy; -use std::collections::HashMap; -use std::str::FromStr; -use std::sync::Arc; use super::cache::LspCache; use super::logging::lsp_warn; @@ -281,24 +282,26 @@ impl LspUrlMap { } } -/// Convert a e.g. `deno-notebook-cell:` specifier to a `file:` specifier. +/// Convert a e.g. `vscode-notebook-cell:` specifier to a `file:` specifier. /// ```rust /// assert_eq!( /// file_like_to_file_specifier( -/// &Url::parse("deno-notebook-cell:/path/to/file.ipynb#abc").unwrap(), +/// &Url::parse("vscode-notebook-cell:/path/to/file.ipynb#abc").unwrap(), /// ), -/// Some(Url::parse("file:///path/to/file.ipynb.ts?scheme=deno-notebook-cell#abc").unwrap()), +/// Some(Url::parse("file:///path/to/file.ipynb?scheme=untitled#abc").unwrap()), /// ); fn file_like_to_file_specifier(specifier: &Url) -> Option { - if matches!(specifier.scheme(), "untitled" | "deno-notebook-cell") { + if matches!( + specifier.scheme(), + "untitled" | "vscode-notebook-cell" | "deno-notebook-cell" + ) { if let Ok(mut s) = ModuleSpecifier::parse(&format!( - "file://{}", + "file:///{}", &specifier.as_str()[deno_core::url::quirks::internal_components(specifier) - .host_end as usize..], + .host_end as usize..].trim_start_matches('/'), )) { s.query_pairs_mut() .append_pair("scheme", specifier.scheme()); - s.set_path(&format!("{}.ts", s.path())); return Some(s); } } @@ -307,9 +310,10 @@ fn file_like_to_file_specifier(specifier: &Url) -> Option { #[cfg(test)] mod tests { - use super::*; use deno_core::resolve_url; + use super::*; + #[test] fn test_hash_data_specifier() { let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap(); @@ -430,11 +434,11 @@ mod tests { fn test_file_like_to_file_specifier() { assert_eq!( file_like_to_file_specifier( - &Url::parse("deno-notebook-cell:/path/to/file.ipynb#abc").unwrap(), + &Url::parse("vscode-notebook-cell:/path/to/file.ipynb#abc").unwrap(), ), Some( Url::parse( - "file:///path/to/file.ipynb.ts?scheme=deno-notebook-cell#abc" + "file:///path/to/file.ipynb?scheme=vscode-notebook-cell#abc" ) .unwrap() ), @@ -444,8 +448,7 @@ mod tests { &Url::parse("untitled:/path/to/file.ipynb#123").unwrap(), ), Some( - Url::parse("file:///path/to/file.ipynb.ts?scheme=untitled#123") - .unwrap() + Url::parse("file:///path/to/file.ipynb?scheme=untitled#123").unwrap() ), ); } diff --git a/cli/main.rs b/cli/main.rs index 0594739fd8..f97ea81e5d 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -1,11 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod args; -mod auth_tokens; mod cache; mod cdp; mod emit; -mod errors; mod factory; mod file_fetcher; mod graph_container; @@ -21,6 +19,7 @@ mod ops; mod resolver; mod shared; mod standalone; +mod sys; mod task_runner; mod tools; mod tsc; @@ -28,31 +27,6 @@ mod util; mod version; mod worker; -use crate::args::flags_from_vec; -use crate::args::DenoSubcommand; -use crate::args::Flags; -use crate::util::display; -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_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::WorkerExecutionMode; -pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; - -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::error::JsError; -use deno_core::futures::FutureExt; -use deno_core::unsync::JoinHandle; -use deno_npm::resolution::SnapshotFromLockfileError; -use deno_runtime::fmt_errors::format_js_error; -use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; -use deno_terminal::colors; -use factory::CliFactory; -use standalone::MODULE_NOT_FOUND; -use standalone::UNSUPPORTED_SCHEME; use std::env; use std::future::Future; use std::io::IsTerminal; @@ -60,6 +34,31 @@ use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; +use args::TaskFlags; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::error::CoreError; +use deno_core::futures::FutureExt; +use deno_core::unsync::JoinHandle; +use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; +use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; +use deno_runtime::fmt_errors::format_js_error; +use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; +use deno_runtime::WorkerExecutionMode; +pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; +use deno_terminal::colors; +use factory::CliFactory; +use standalone::MODULE_NOT_FOUND; +use standalone::UNSUPPORTED_SCHEME; + +use self::npm::ResolveSnapshotError; +use crate::args::flags_from_vec; +use crate::args::DenoSubcommand; +use crate::args::Flags; +use crate::util::display; +use crate::util::v8::get_v8_flags_from_env; +use crate::util::v8::init_v8_flags; + #[cfg(feature = "dhat-heap")] #[global_allocator] static ALLOC: dhat::Alloc = dhat::Alloc; @@ -202,7 +201,7 @@ 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 let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = util::result::any_and_jserrorbox_downcast_ref::(&script_err) { if flags.node_modules_dir.is_none() { let mut flags = flags.deref().clone(); let watch = match &flags.subcommand { @@ -373,13 +372,19 @@ fn exit_for_error(error: AnyError) -> ! { let mut error_string = format!("{error:?}"); let mut error_code = 1; - if let Some(e) = error.downcast_ref::() { - error_string = format_js_error(e); - } else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) = - error.downcast_ref::() + if let Some(CoreError::Js(e)) = + util::result::any_and_jserrorbox_downcast_ref::(&error) { - error_string = e.to_string(); - error_code = 10; + error_string = format_js_error(e); + } else if let Some(e @ ResolveSnapshotError { .. }) = + util::result::any_and_jserrorbox_downcast_ref::( + &error, + ) + { + if let Some(e) = e.maybe_integrity_check_error() { + error_string = e.to_string(); + error_code = 10; + } } exit_with_message(&error_string, error_code); @@ -447,8 +452,9 @@ fn resolve_flags_and_init( } }; - deno_telemetry::init(crate::args::otel_runtime_config())?; - util::logger::init(flags.log_level, Some(flags.otel_config())); + let otel_config = flags.otel_config(); + deno_telemetry::init(crate::args::otel_runtime_config(), &otel_config)?; + util::logger::init(flags.log_level, Some(otel_config)); // TODO(bartlomieju): remove in Deno v2.5 and hard error then. if flags.unstable_config.legacy_flag_enabled { diff --git a/cli/mainrt.rs b/cli/mainrt.rs index 18142bd0e7..8eea3f85ed 100644 --- a/cli/mainrt.rs +++ b/cli/mainrt.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Allow unused code warnings because we share // code between the two bin targets. @@ -8,10 +8,8 @@ mod standalone; mod args; -mod auth_tokens; mod cache; mod emit; -mod errors; mod file_fetcher; mod http_util; mod js; @@ -19,26 +17,30 @@ mod node; mod npm; mod resolver; mod shared; +mod sys; mod task_runner; mod util; mod version; mod worker; -use deno_core::error::generic_error; +use std::borrow::Cow; +use std::collections::HashMap; +use std::env; +use std::env::current_exe; +use std::sync::Arc; + use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::error::JsError; use deno_runtime::fmt_errors::format_js_error; use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; use deno_terminal::colors; use indexmap::IndexMap; - -use std::borrow::Cow; -use std::collections::HashMap; -use std::env; -use std::env::current_exe; +use standalone::DenoCompileFileSystem; use crate::args::Flags; +use crate::util::result::any_and_jserrorbox_downcast_ref; pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) { log::error!( @@ -63,8 +65,10 @@ fn unwrap_or_exit(result: Result) -> T { Err(error) => { let mut error_string = format!("{:?}", error); - if let Some(e) = error.downcast_ref::() { - error_string = format_js_error(e); + if let Some(CoreError::Js(js_error)) = + any_and_jserrorbox_downcast_ref::(&error) + { + error_string = format_js_error(js_error); } exit_with_message(&error_string, 1); @@ -87,13 +91,18 @@ fn main() { let future = async move { match standalone { Ok(Some(data)) => { - deno_telemetry::init(crate::args::otel_runtime_config())?; + deno_telemetry::init( + crate::args::otel_runtime_config(), + &data.metadata.otel_config, + )?; util::logger::init( data.metadata.log_level, Some(data.metadata.otel_config.clone()), ); load_env_vars(&data.metadata.env_vars_from_env_file); - let exit_code = standalone::run(data).await?; + let fs = DenoCompileFileSystem::new(data.vfs.clone()); + let sys = crate::sys::CliSys::DenoCompile(fs.clone()); + let exit_code = standalone::run(Arc::new(fs), sys, data).await?; deno_runtime::exit(exit_code); } Ok(None) => Ok(()), diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 5e4ff875dc..daeb4dda37 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; @@ -11,6 +11,46 @@ use std::sync::atomic::AtomicU16; use std::sync::atomic::Ordering; use std::sync::Arc; +use deno_ast::MediaType; +use deno_ast::ModuleKind; +use deno_core::anyhow::anyhow; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::error::ModuleLoaderError; +use deno_core::futures::future::FutureExt; +use deno_core::futures::Future; +use deno_core::parking_lot::Mutex; +use deno_core::resolve_url; +use deno_core::ModuleCodeString; +use deno_core::ModuleLoader; +use deno_core::ModuleSource; +use deno_core::ModuleSourceCode; +use deno_core::ModuleSpecifier; +use deno_core::ModuleType; +use deno_core::RequestedModuleType; +use deno_core::SourceCodeCacheInfo; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; +use deno_graph::GraphKind; +use deno_graph::JsModule; +use deno_graph::JsonModule; +use deno_graph::Module; +use deno_graph::ModuleGraph; +use deno_graph::ModuleGraphError; +use deno_graph::Resolution; +use deno_graph::WasmModule; +use deno_resolver::npm::DenoInNpmPackageChecker; +use deno_runtime::code_cache; +use deno_runtime::deno_node::create_host_defined_options; +use deno_runtime::deno_node::NodeRequireLoader; +use deno_runtime::deno_permissions::PermissionsContainer; +use deno_semver::npm::NpmPackageReqReference; +use node_resolver::errors::ClosestPkgJsonError; +use node_resolver::InNpmPackageChecker; +use node_resolver::NodeResolutionKind; +use node_resolver::ResolutionMode; +use sys_traits::FsRead; + use crate::args::jsr_url; use crate::args::CliLockfile; use crate::args::CliOptions; @@ -23,62 +63,44 @@ use crate::emit::Emitter; use crate::graph_container::MainModuleGraphContainer; use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; +use crate::graph_util::enhance_graph_error; use crate::graph_util::CreateGraphOptions; +use crate::graph_util::EnhanceGraphErrorMode; use crate::graph_util::ModuleGraphBuilder; -use crate::node; use crate::node::CliNodeCodeTranslator; +use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; -use crate::resolver::CjsTracker; +use crate::npm::NpmRegistryReadPermissionChecker; +use crate::resolver::CliCjsTracker; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; use crate::resolver::ModuleCodeStringSource; use crate::resolver::NotSupportedKindInNpmError; use crate::resolver::NpmModuleLoader; +use crate::sys::CliSys; use crate::tools::check; +use crate::tools::check::CheckError; use crate::tools::check::TypeChecker; use crate::util::progress_bar::ProgressBar; use crate::util::text_encoding::code_without_source_map; use crate::util::text_encoding::source_map_from_code; use crate::worker::CreateModuleLoaderResult; use crate::worker::ModuleLoaderFactory; -use deno_ast::MediaType; -use deno_ast::ModuleKind; -use deno_core::anyhow::anyhow; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::custom_error; -use deno_core::error::generic_error; -use deno_core::error::AnyError; -use deno_core::futures::future::FutureExt; -use deno_core::futures::Future; -use deno_core::parking_lot::Mutex; -use deno_core::resolve_url; -use deno_core::ModuleCodeString; -use deno_core::ModuleLoader; -use deno_core::ModuleSource; -use deno_core::ModuleSourceCode; -use deno_core::ModuleSpecifier; -use deno_core::ModuleType; -use deno_core::RequestedModuleType; -use deno_core::SourceCodeCacheInfo; -use deno_graph::GraphKind; -use deno_graph::JsModule; -use deno_graph::JsonModule; -use deno_graph::Module; -use deno_graph::ModuleGraph; -use deno_graph::Resolution; -use deno_graph::WasmModule; -use deno_runtime::code_cache; -use deno_runtime::deno_fs::FileSystem; -use deno_runtime::deno_node::create_host_defined_options; -use deno_runtime::deno_node::NodeRequireLoader; -use deno_runtime::deno_node::NodeResolver; -use deno_runtime::deno_permissions::PermissionsContainer; -use deno_semver::npm::NpmPackageReqReference; -use node_resolver::errors::ClosestPkgJsonError; -use node_resolver::InNpmPackageChecker; -use node_resolver::NodeResolutionKind; -use node_resolver::ResolutionMode; + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum PrepareModuleLoadError { + #[class(inherit)] + #[error(transparent)] + BuildGraphWithNpmResolution( + #[from] crate::graph_util::BuildGraphWithNpmResolutionError, + ), + #[class(inherit)] + #[error(transparent)] + Check(#[from] CheckError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} pub struct ModuleLoadPreparer { options: Arc, @@ -119,7 +141,7 @@ impl ModuleLoadPreparer { lib: TsTypeLib, permissions: PermissionsContainer, ext_overwrite: Option<&String>, - ) -> Result<(), AnyError> { + ) -> Result<(), PrepareModuleLoadError> { log::debug!("Preparing module load."); let _pb_clear_guard = self.progress_bar.clear_guard(); @@ -200,7 +222,7 @@ impl ModuleLoadPreparer { &self, graph: &ModuleGraph, roots: &[ModuleSpecifier], - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { self.module_graph_builder.graph_roots_valid(graph, roots) } } @@ -212,20 +234,21 @@ struct SharedCliModuleLoaderState { initial_cwd: PathBuf, is_inspecting: bool, is_repl: bool, - cjs_tracker: Arc, + cjs_tracker: Arc, code_cache: Option>, emitter: Arc, - fs: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, main_module_graph_container: Arc, module_load_preparer: Arc, node_code_translator: Arc, - node_resolver: Arc, - npm_req_resolver: Arc, - npm_resolver: Arc, + node_resolver: Arc, npm_module_loader: NpmModuleLoader, + npm_registry_permission_checker: Arc, + npm_req_resolver: Arc, + npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, + sys: CliSys, in_flight_loads_tracker: InFlightModuleLoadsTracker, } @@ -272,20 +295,21 @@ impl CliModuleLoaderFactory { #[allow(clippy::too_many_arguments)] pub fn new( options: &CliOptions, - cjs_tracker: Arc, + cjs_tracker: Arc, code_cache: Option>, emitter: Arc, - fs: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, main_module_graph_container: Arc, module_load_preparer: Arc, node_code_translator: Arc, - node_resolver: Arc, - npm_req_resolver: Arc, - npm_resolver: Arc, + node_resolver: Arc, npm_module_loader: NpmModuleLoader, + npm_registry_permission_checker: Arc, + npm_req_resolver: Arc, + npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, + sys: CliSys, ) -> Self { Self { shared: Arc::new(SharedCliModuleLoaderState { @@ -301,17 +325,18 @@ impl CliModuleLoaderFactory { cjs_tracker, code_cache, emitter, - fs, in_npm_pkg_checker, main_module_graph_container, module_load_preparer, node_code_translator, node_resolver, + npm_module_loader, + npm_registry_permission_checker, npm_req_resolver, npm_resolver, - npm_module_loader, parsed_source_cache, resolver, + sys, in_flight_loads_tracker: InFlightModuleLoadsTracker { loads_number: Arc::new(AtomicU16::new(0)), cleanup_task_timeout: 10_000, @@ -344,10 +369,13 @@ impl CliModuleLoaderFactory { let node_require_loader = Rc::new(CliNodeRequireLoader { cjs_tracker: self.shared.cjs_tracker.clone(), emitter: self.shared.emitter.clone(), - fs: self.shared.fs.clone(), + sys: self.shared.sys.clone(), graph_container, in_npm_pkg_checker: self.shared.in_npm_pkg_checker.clone(), - npm_resolver: self.shared.npm_resolver.clone(), + npm_registry_permission_checker: self + .shared + .npm_registry_permission_checker + .clone(), }); CreateModuleLoaderResult { module_loader, @@ -411,7 +439,7 @@ impl specifier: &ModuleSpecifier, maybe_referrer: Option<&ModuleSpecifier>, requested_module_type: RequestedModuleType, - ) -> Result { + ) -> Result { let code_source = self.load_code_source(specifier, maybe_referrer).await?; let code = if self.shared.is_inspecting || code_source.media_type == MediaType::Wasm @@ -434,7 +462,7 @@ impl if module_type == ModuleType::Json && requested_module_type != RequestedModuleType::Json { - return Err(generic_error("Attempted to load JSON module without specifying \"type\": \"json\" attribute in the import statement.")); + return Err(JsErrorBox::generic("Attempted to load JSON module without specifying \"type\": \"json\" attribute in the import statement.").into()); } let code_cache = if module_type == ModuleType::JavaScript { @@ -495,7 +523,7 @@ impl fn resolve_referrer( &self, referrer: &str, - ) -> Result { + ) -> Result { let referrer = if referrer.is_empty() && self.shared.is_repl { // FIXME(bartlomieju): this is a hacky way to provide compatibility with REPL // and `Deno.core.evalContext` API. Ideally we should always have a referrer filled @@ -521,7 +549,7 @@ impl &self, raw_specifier: &str, referrer: &ModuleSpecifier, - ) -> Result { + ) -> Result { let graph = self.graph_container.graph(); let resolution = match graph.get(referrer) { Some(Module::Js(module)) => module @@ -535,19 +563,25 @@ impl let specifier = match resolution { Resolution::Ok(resolved) => Cow::Borrowed(&resolved.specifier), Resolution::Err(err) => { - return Err(custom_error( - "TypeError", - format!("{}\n", err.to_string_with_range()), - )); + return Err( + JsErrorBox::type_error(format!("{}\n", err.to_string_with_range())) + .into(), + ); } - Resolution::None => Cow::Owned(self.shared.resolver.resolve( - raw_specifier, - referrer, - deno_graph::Position::zeroed(), - // if we're here, that means it's resolving a dynamic import - ResolutionMode::Import, - NodeResolutionKind::Execution, - )?), + Resolution::None => Cow::Owned( + self + .shared + .resolver + .resolve( + raw_specifier, + referrer, + deno_graph::Position::zeroed(), + // if we're here, that means it's resolving a dynamic import + ResolutionMode::Import, + NodeResolutionKind::Execution, + ) + .map_err(JsErrorBox::from_err)?, + ), }; if self.shared.is_repl { @@ -562,7 +596,7 @@ impl ResolutionMode::Import, NodeResolutionKind::Execution, ) - .map_err(AnyError::from); + .map_err(|e| JsErrorBox::from_err(e).into()); } } @@ -573,7 +607,8 @@ impl .npm_resolver .as_managed() .unwrap() // byonm won't create a Module::Npm - .resolve_pkg_folder_from_deno_module(module.nv_reference.nv())?; + .resolve_pkg_folder_from_deno_module(module.nv_reference.nv()) + .map_err(JsErrorBox::from_err)?; self .shared .node_resolver @@ -593,9 +628,9 @@ impl Some(Module::Json(module)) => module.specifier.clone(), Some(Module::Wasm(module)) => module.specifier.clone(), Some(Module::External(module)) => { - node::resolve_specifier_into_node_modules( + node_resolver::resolve_specifier_into_node_modules( + &self.shared.sys, &module.specifier, - self.shared.fs.as_ref(), ) } None => specifier.into_owned(), @@ -689,13 +724,27 @@ impl &self, graph: &'graph ModuleGraph, specifier: &ModuleSpecifier, - ) -> Result>, AnyError> { + ) -> Result>, JsErrorBox> { if specifier.scheme() == "node" { // Node built-in modules should be handled internally. unreachable!("Deno bug. {} was misconfigured internally.", specifier); } - match graph.get(specifier) { + let maybe_module = match graph.try_get(specifier) { + Ok(module) => module, + Err(err) => { + return Err(JsErrorBox::new( + err.get_class(), + enhance_graph_error( + &self.shared.sys, + &ModuleGraphError::ModuleError(err.clone()), + EnhanceGraphErrorMode::ShowRange, + ), + )) + } + }; + + match maybe_module { Some(deno_graph::Module::Json(JsonModule { source, media_type, @@ -713,11 +762,12 @@ impl is_script, .. })) => { - if self.shared.cjs_tracker.is_cjs_with_known_is_script( - specifier, - *media_type, - *is_script, - )? { + if self + .shared + .cjs_tracker + .is_cjs_with_known_is_script(specifier, *media_type, *is_script) + .map_err(JsErrorBox::from_err)? + { return Ok(Some(CodeOrDeferredEmit::Cjs { specifier, media_type: *media_type, @@ -849,16 +899,16 @@ impl ModuleLoader specifier: &str, referrer: &str, _kind: deno_core::ResolutionKind, - ) -> Result { + ) -> Result { fn ensure_not_jsr_non_jsr_remote_import( specifier: &ModuleSpecifier, referrer: &ModuleSpecifier, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { if referrer.as_str().starts_with(jsr_url().as_str()) && !specifier.as_str().starts_with(jsr_url().as_str()) && matches!(specifier.scheme(), "http" | "https") { - bail!("Importing {} blocked. JSR packages cannot import non-JSR remote modules for security reasons.", specifier); + return Err(JsErrorBox::generic(format!("Importing {} blocked. JSR packages cannot import non-JSR remote modules for security reasons.", specifier))); } Ok(()) } @@ -911,7 +961,7 @@ impl ModuleLoader specifier: &ModuleSpecifier, _maybe_referrer: Option, is_dynamic: bool, - ) -> Pin>>> { + ) -> Pin>>> { self.0.shared.in_flight_loads_tracker.increase(); if self.0.shared.in_npm_pkg_checker.in_npm_package(specifier) { return Box::pin(deno_core::futures::future::ready(Ok(()))); @@ -960,7 +1010,8 @@ impl ModuleLoader permissions, None, ) - .await?; + .await + .map_err(JsErrorBox::from_err)?; update_permit.commit(); Ok(()) } @@ -996,7 +1047,7 @@ impl ModuleLoader std::future::ready(()).boxed_local() } - fn get_source_map(&self, file_name: &str) -> Option> { + fn get_source_map(&self, file_name: &str) -> Option> { let specifier = resolve_url(file_name).ok()?; match specifier.scheme() { // we should only be looking for emits for schemes that denote external @@ -1008,7 +1059,7 @@ impl ModuleLoader .0 .load_prepared_module_for_source_map_sync(&specifier) .ok()??; - source_map_from_code(source.code.as_bytes()) + source_map_from_code(source.code.as_bytes()).map(Cow::Owned) } fn get_source_mapped_source_line( @@ -1089,12 +1140,12 @@ impl ModuleGraphUpdatePermit for WorkerModuleGraphUpdatePermit { #[derive(Debug)] struct CliNodeRequireLoader { - cjs_tracker: Arc, + cjs_tracker: Arc, emitter: Arc, - fs: Arc, + sys: CliSys, graph_container: TGraphContainer, - in_npm_pkg_checker: Arc, - npm_resolver: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, + npm_registry_permission_checker: Arc, } impl NodeRequireLoader @@ -1104,33 +1155,37 @@ impl NodeRequireLoader &self, permissions: &mut dyn deno_runtime::deno_node::NodePermissions, path: &'a Path, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { if let Ok(url) = deno_path_util::url_from_file_path(path) { // allow reading if it's in the module graph if self.graph_container.graph().get(&url).is_some() { - return Ok(std::borrow::Cow::Borrowed(path)); + return Ok(Cow::Borrowed(path)); } } - self.npm_resolver.ensure_read_permission(permissions, path) + self + .npm_registry_permission_checker + .ensure_read_permission(permissions, path) + .map_err(JsErrorBox::from_err) } fn load_text_file_lossy( &self, path: &Path, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { // todo(dsherret): use the preloaded module from the graph if available? let media_type = MediaType::from_path(path); - let text = self.fs.read_text_file_lossy_sync(path, None)?; + let text = self + .sys + .fs_read_to_string_lossy(path) + .map_err(JsErrorBox::from_err)?; if media_type.is_emittable() { - let specifier = deno_path_util::url_from_file_path(path)?; + let specifier = deno_path_util::url_from_file_path(path) + .map_err(JsErrorBox::from_err)?; if self.in_npm_pkg_checker.in_npm_package(&specifier) { - return Err( - NotSupportedKindInNpmError { - media_type, - specifier, - } - .into(), - ); + return Err(JsErrorBox::from_err(NotSupportedKindInNpmError { + media_type, + specifier, + })); } self .emitter @@ -1144,6 +1199,7 @@ impl NodeRequireLoader &text.into(), ) .map(Cow::Owned) + .map_err(JsErrorBox::from_err) } else { Ok(text) } @@ -1160,9 +1216,10 @@ impl NodeRequireLoader #[cfg(test)] mod tests { - use super::*; use deno_graph::ParsedSourceStore; + use super::*; + #[tokio::test] async fn test_inflight_module_loads_tracker() { let tracker = InFlightModuleLoadsTracker { diff --git a/cli/node.rs b/cli/node.rs index 11959df6b9..892e25914a 100644 --- a/cli/node.rs +++ b/cli/node.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::sync::Arc; @@ -7,8 +7,9 @@ use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; use deno_graph::ParsedSourceStore; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::deno_fs; -use deno_runtime::deno_node::DenoFsNodeResolverEnv; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis; use node_resolver::analyze::CjsAnalysisExports; use node_resolver::analyze::CjsCodeAnalyzer; @@ -19,25 +20,23 @@ use serde::Serialize; use crate::cache::CacheDBHash; use crate::cache::NodeAnalysisCache; use crate::cache::ParsedSourceCache; -use crate::resolver::CjsTracker; +use crate::npm::CliNpmResolver; +use crate::resolver::CliCjsTracker; +use crate::sys::CliSys; -pub type CliNodeCodeTranslator = - NodeCodeTranslator; - -/// Resolves a specifier that is pointing into a node_modules folder. -/// -/// Note: This should be called whenever getting the specifier from -/// a Module::External(module) reference because that module might -/// not be fully resolved at the time deno_graph is analyzing it -/// because the node_modules folder might not exist at that time. -pub fn resolve_specifier_into_node_modules( - specifier: &ModuleSpecifier, - fs: &dyn deno_fs::FileSystem, -) -> ModuleSpecifier { - node_resolver::resolve_specifier_into_node_modules(specifier, &|path| { - fs.realpath_sync(path).map_err(|err| err.into_io_error()) - }) -} +pub type CliNodeCodeTranslator = NodeCodeTranslator< + CliCjsCodeAnalyzer, + DenoInNpmPackageChecker, + RealIsBuiltInNodeModuleChecker, + CliNpmResolver, + CliSys, +>; +pub type CliNodeResolver = deno_runtime::deno_node::NodeResolver< + DenoInNpmPackageChecker, + CliNpmResolver, + CliSys, +>; +pub type CliPackageJsonResolver = node_resolver::PackageJsonResolver; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum CliCjsAnalysis { @@ -52,7 +51,7 @@ pub enum CliCjsAnalysis { pub struct CliCjsCodeAnalyzer { cache: NodeAnalysisCache, - cjs_tracker: Arc, + cjs_tracker: Arc, fs: deno_fs::FileSystemRc, parsed_source_cache: Option>, } @@ -60,7 +59,7 @@ pub struct CliCjsCodeAnalyzer { impl CliCjsCodeAnalyzer { pub fn new( cache: NodeAnalysisCache, - cjs_tracker: Arc, + cjs_tracker: Arc, fs: deno_fs::FileSystemRc, parsed_source_cache: Option>, ) -> Self { @@ -77,7 +76,7 @@ impl CliCjsCodeAnalyzer { specifier: &ModuleSpecifier, source: &str, ) -> Result { - let source_hash = CacheDBHash::from_source(source); + let source_hash = CacheDBHash::from_hashable(source); if let Some(analysis) = self.cache.get_cjs_analysis(specifier.as_str(), source_hash) { diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index eca399251b..8dc498bb04 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -1,99 +1,32 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use std::borrow::Cow; -use std::path::Path; use std::sync::Arc; -use deno_core::error::AnyError; use deno_core::serde_json; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmNpmResolverCreateOptions; -use deno_resolver::npm::CliNpmReqResolver; -use deno_runtime::deno_node::DenoFsNodeResolverEnv; -use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; -use node_resolver::NpmPackageFolderResolver; use crate::args::NpmProcessState; use crate::args::NpmProcessStateKind; -use crate::resolver::CliDenoResolverFs; - -use super::CliNpmResolver; -use super::InnerCliNpmResolverRef; +use crate::sys::CliSys; pub type CliByonmNpmResolverCreateOptions = - ByonmNpmResolverCreateOptions; -pub type CliByonmNpmResolver = - ByonmNpmResolver; + ByonmNpmResolverCreateOptions; +pub type CliByonmNpmResolver = ByonmNpmResolver; -// todo(dsherret): the services hanging off `CliNpmResolver` doesn't seem ideal. We should probably decouple. #[derive(Debug)] -struct CliByonmWrapper(Arc); +pub struct CliByonmNpmProcessStateProvider(pub Arc); -impl NpmProcessStateProvider for CliByonmWrapper { +impl NpmProcessStateProvider for CliByonmNpmProcessStateProvider { fn get_npm_process_state(&self) -> String { serde_json::to_string(&NpmProcessState { kind: NpmProcessStateKind::Byonm, local_node_modules_path: self .0 - .root_node_modules_dir() + .root_node_modules_path() .map(|p| p.to_string_lossy().to_string()), }) .unwrap() } } - -impl CliNpmResolver for CliByonmNpmResolver { - fn into_npm_pkg_folder_resolver( - self: Arc, - ) -> Arc { - self - } - - fn into_npm_req_resolver(self: Arc) -> Arc { - self - } - - fn into_process_state_provider( - self: Arc, - ) -> Arc { - Arc::new(CliByonmWrapper(self)) - } - - fn into_maybe_byonm(self: Arc) -> Option> { - Some(self) - } - - fn clone_snapshotted(&self) -> Arc { - Arc::new(self.clone()) - } - - fn as_inner(&self) -> InnerCliNpmResolverRef { - InnerCliNpmResolverRef::Byonm(self) - } - - fn root_node_modules_path(&self) -> Option<&Path> { - self.root_node_modules_dir() - } - - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError> { - if !path - .components() - .any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules") - { - permissions.check_read_path(path).map_err(Into::into) - } else { - Ok(Cow::Borrowed(path)) - } - } - - fn check_state_hash(&self) -> Option { - // it is very difficult to determine the check state hash for byonm - // so we just return None to signify check caching is not supported - None - } -} diff --git a/cli/npm/managed/resolvers/common/bin_entries.rs b/cli/npm/installer/common/bin_entries.rs similarity index 78% rename from cli/npm/managed/resolvers/common/bin_entries.rs rename to cli/npm/installer/common/bin_entries.rs index e4a1845689..2f7bed285a 100644 --- a/cli/npm/managed/resolvers/common/bin_entries.rs +++ b/cli/npm/installer/common/bin_entries.rs @@ -1,16 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::npm::managed::NpmResolutionPackage; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_npm::resolution::NpmResolutionSnapshot; -use deno_npm::NpmPackageId; use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; use std::path::Path; use std::path::PathBuf; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::NpmPackageId; +use deno_npm::NpmResolutionPackage; + #[derive(Default)] pub struct BinEntries<'a> { /// Packages that have colliding bin names @@ -28,8 +27,10 @@ fn default_bin_name(package: &NpmResolutionPackage) -> &str { .id .nv .name + .as_str() .rsplit_once('/') - .map_or(package.id.nv.name.as_str(), |(_, name)| name) + .map(|(_, name)| name) + .unwrap_or(package.id.nv.name.as_str()) } pub fn warn_missing_entrypoint( @@ -46,6 +47,48 @@ pub fn warn_missing_entrypoint( ); } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum BinEntriesError { + #[class(inherit)] + #[error("Creating '{path}'")] + Creating { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[cfg(unix)] + #[class(inherit)] + #[error("Setting permissions on '{path}'")] + Permissions { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Can't set up '{name}' bin at {path}")] + SetUpBin { + name: String, + path: PathBuf, + #[source] + #[inherit] + source: Box, + }, + #[cfg(unix)] + #[class(inherit)] + #[error("Setting permissions on '{path}'")] + RemoveBinSymlink { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), +} + impl<'a> BinEntries<'a> { pub fn new() -> Self { Self::default() @@ -88,15 +131,15 @@ impl<'a> BinEntries<'a> { mut already_seen: impl FnMut( &Path, &str, // bin script - ) -> Result<(), AnyError>, + ) -> Result<(), BinEntriesError>, mut new: impl FnMut( &NpmResolutionPackage, &Path, &str, // bin name &str, // bin script - ) -> Result<(), AnyError>, + ) -> Result<(), BinEntriesError>, mut filter: impl FnMut(&NpmResolutionPackage) -> bool, - ) -> Result<(), AnyError> { + ) -> Result<(), BinEntriesError> { if !self.collisions.is_empty() && !self.sorted { // walking the dependency tree to find out the depth of each package // is sort of expensive, so we only do it if there's a collision @@ -164,11 +207,14 @@ impl<'a> BinEntries<'a> { bin_node_modules_dir_path: &Path, filter: impl FnMut(&NpmResolutionPackage) -> bool, mut handler: impl FnMut(&EntrySetupOutcome<'_>), - ) -> Result<(), AnyError> { + ) -> Result<(), BinEntriesError> { if !self.entries.is_empty() && !bin_node_modules_dir_path.exists() { - std::fs::create_dir_all(bin_node_modules_dir_path).with_context( - || format!("Creating '{}'", bin_node_modules_dir_path.display()), - )?; + std::fs::create_dir_all(bin_node_modules_dir_path).map_err(|source| { + BinEntriesError::Creating { + path: bin_node_modules_dir_path.to_path_buf(), + source, + } + })?; } self.for_each_entry( @@ -205,7 +251,7 @@ impl<'a> BinEntries<'a> { snapshot: &NpmResolutionSnapshot, bin_node_modules_dir_path: &Path, handler: impl FnMut(&EntrySetupOutcome<'_>), - ) -> Result<(), AnyError> { + ) -> Result<(), BinEntriesError> { self.set_up_entries_filtered( snapshot, bin_node_modules_dir_path, @@ -222,7 +268,7 @@ impl<'a> BinEntries<'a> { bin_node_modules_dir_path: &Path, handler: impl FnMut(&EntrySetupOutcome<'_>), only: &HashSet<&NpmPackageId>, - ) -> Result<(), AnyError> { + ) -> Result<(), BinEntriesError> { self.set_up_entries_filtered( snapshot, bin_node_modules_dir_path, @@ -297,7 +343,7 @@ pub fn set_up_bin_entry<'a>( #[allow(unused_variables)] bin_script: &str, #[allow(unused_variables)] package_path: &'a Path, bin_node_modules_dir_path: &Path, -) -> Result, AnyError> { +) -> Result, BinEntriesError> { #[cfg(windows)] { set_up_bin_shim(package, bin_name, bin_node_modules_dir_path)?; @@ -320,14 +366,16 @@ fn set_up_bin_shim( package: &NpmResolutionPackage, bin_name: &str, bin_node_modules_dir_path: &Path, -) -> Result<(), AnyError> { +) -> Result<(), BinEntriesError> { use std::fs; let mut cmd_shim = bin_node_modules_dir_path.join(bin_name); cmd_shim.set_extension("cmd"); let shim = format!("@deno run -A npm:{}/{bin_name} %*", package.id.nv); - fs::write(&cmd_shim, shim).with_context(|| { - format!("Can't set up '{}' bin at {}", bin_name, cmd_shim.display()) + fs::write(&cmd_shim, shim).map_err(|err| BinEntriesError::SetUpBin { + name: bin_name.to_string(), + path: cmd_shim.clone(), + source: Box::new(err.into()), })?; Ok(()) @@ -336,7 +384,7 @@ fn set_up_bin_shim( #[cfg(unix)] /// Make the file at `path` executable if it exists. /// Returns `true` if the file exists, `false` otherwise. -fn make_executable_if_exists(path: &Path) -> Result { +fn make_executable_if_exists(path: &Path) -> Result { use std::io; use std::os::unix::fs::PermissionsExt; let mut perms = match std::fs::metadata(path) { @@ -351,8 +399,11 @@ fn make_executable_if_exists(path: &Path) -> Result { if perms.mode() & 0o111 == 0 { // if the original file is not executable, make it executable perms.set_mode(perms.mode() | 0o111); - std::fs::set_permissions(path, perms).with_context(|| { - format!("Setting permissions on '{}'", path.display()) + std::fs::set_permissions(path, perms).map_err(|source| { + BinEntriesError::Permissions { + path: path.to_path_buf(), + source, + } })?; } @@ -391,14 +442,18 @@ fn symlink_bin_entry<'a>( bin_script: &str, package_path: &'a Path, bin_node_modules_dir_path: &Path, -) -> Result, AnyError> { +) -> Result, BinEntriesError> { use std::io; use std::os::unix::fs::symlink; let link = bin_node_modules_dir_path.join(bin_name); let original = package_path.join(bin_script); - let found = make_executable_if_exists(&original).with_context(|| { - format!("Can't set up '{}' bin at {}", bin_name, original.display()) + let found = make_executable_if_exists(&original).map_err(|source| { + BinEntriesError::SetUpBin { + name: bin_name.to_string(), + path: original.to_path_buf(), + source: Box::new(source), + } })?; if !found { return Ok(EntrySetupOutcome::MissingEntrypoint { @@ -416,27 +471,25 @@ fn symlink_bin_entry<'a>( if let Err(err) = symlink(&original_relative, &link) { if err.kind() == io::ErrorKind::AlreadyExists { // remove and retry - std::fs::remove_file(&link).with_context(|| { - format!( - "Failed to remove existing bin symlink at {}", - link.display() - ) + std::fs::remove_file(&link).map_err(|source| { + BinEntriesError::RemoveBinSymlink { + path: link.clone(), + source, + } })?; - symlink(&original_relative, &link).with_context(|| { - format!( - "Can't set up '{}' bin at {}", - bin_name, - original_relative.display() - ) + symlink(&original_relative, &link).map_err(|source| { + BinEntriesError::SetUpBin { + name: bin_name.to_string(), + path: original_relative.to_path_buf(), + source: Box::new(source.into()), + } })?; return Ok(EntrySetupOutcome::Success); } - return Err(err).with_context(|| { - format!( - "Can't set up '{}' bin at {}", - bin_name, - original_relative.display() - ) + return Err(BinEntriesError::SetUpBin { + name: bin_name.to_string(), + path: original_relative.to_path_buf(), + source: Box::new(err.into()), }); } diff --git a/cli/npm/managed/resolvers/common/lifecycle_scripts.rs b/cli/npm/installer/common/lifecycle_scripts.rs similarity index 91% rename from cli/npm/managed/resolvers/common/lifecycle_scripts.rs rename to cli/npm/installer/common/lifecycle_scripts.rs index 958c4bcd19..a0d821cdfc 100644 --- a/cli/npm/managed/resolvers/common/lifecycle_scripts.rs +++ b/cli/npm/installer/common/lifecycle_scripts.rs @@ -1,24 +1,23 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::collections::HashSet; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; + +use deno_core::error::AnyError; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::NpmResolutionPackage; +use deno_runtime::deno_io::FromRawIoHandle; +use deno_semver::package::PackageNv; +use deno_semver::Version; +use deno_task_shell::KillSignal; use super::bin_entries::BinEntries; use crate::args::LifecycleScriptsConfig; use crate::task_runner::TaskStdio; use crate::util::progress_bar::ProgressBar; -use deno_core::anyhow::Context; -use deno_npm::resolution::NpmResolutionSnapshot; -use deno_runtime::deno_io::FromRawIoHandle; -use deno_semver::package::PackageNv; -use deno_semver::Version; -use deno_task_shell::KillSignal; -use std::borrow::Cow; -use std::collections::HashSet; -use std::rc::Rc; - -use std::path::Path; -use std::path::PathBuf; - -use deno_core::error::AnyError; -use deno_npm::NpmResolutionPackage; pub trait LifecycleScriptsStrategy { fn can_run_scripts(&self) -> bool { @@ -29,7 +28,7 @@ pub trait LifecycleScriptsStrategy { fn warn_on_scripts_not_run( &self, packages: &[(&NpmResolutionPackage, PathBuf)], - ) -> Result<(), AnyError>; + ) -> Result<(), std::io::Error>; fn has_warned(&self, package: &NpmResolutionPackage) -> bool; @@ -38,7 +37,7 @@ pub trait LifecycleScriptsStrategy { fn did_run_scripts( &self, package: &NpmResolutionPackage, - ) -> Result<(), AnyError>; + ) -> Result<(), std::io::Error>; } pub struct LifecycleScripts<'a> { @@ -84,6 +83,27 @@ fn is_broken_default_install_script(script: &str, package_path: &Path) -> bool { script == "node-gyp rebuild" && !package_path.join("binding.gyp").exists() } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum LifecycleScriptsError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + BinEntries(#[from] super::bin_entries::BinEntriesError), + #[class(inherit)] + #[error( + "failed to create npm process state tempfile for running lifecycle scripts" + )] + CreateNpmProcessState(#[source] std::io::Error), + #[class(generic)] + #[error(transparent)] + Task(AnyError), + #[class(generic)] + #[error("failed to run scripts for packages: {}", .0.join(", "))] + RunScripts(Vec), +} + impl<'a> LifecycleScripts<'a> { pub fn can_run_scripts(&self, package_nv: &PackageNv) -> bool { if !self.strategy.can_run_scripts() { @@ -141,7 +161,7 @@ impl<'a> LifecycleScripts<'a> { } } - pub fn warn_not_run_scripts(&self) -> Result<(), AnyError> { + pub fn warn_not_run_scripts(&self) -> Result<(), std::io::Error> { if !self.packages_with_scripts_not_run.is_empty() { self .strategy @@ -156,7 +176,7 @@ impl<'a> LifecycleScripts<'a> { packages: &[NpmResolutionPackage], root_node_modules_dir_path: &Path, progress_bar: &ProgressBar, - ) -> Result<(), AnyError> { + ) -> Result<(), LifecycleScriptsError> { let kill_signal = KillSignal::default(); let _drop_signal = kill_signal.clone().drop_guard(); // we don't run with signals forwarded because once signals @@ -179,7 +199,7 @@ impl<'a> LifecycleScripts<'a> { root_node_modules_dir_path: &Path, progress_bar: &ProgressBar, kill_signal: KillSignal, - ) -> Result<(), AnyError> { + ) -> Result<(), LifecycleScriptsError> { self.warn_not_run_scripts()?; let get_package_path = |p: &NpmResolutionPackage| self.strategy.package_path(p); @@ -198,7 +218,7 @@ impl<'a> LifecycleScripts<'a> { snapshot, packages, get_package_path, - )?; + ); let init_cwd = &self.config.initial_cwd; let process_state = crate::npm::managed::npm_process_state( snapshot.as_valid_serialized(), @@ -222,7 +242,8 @@ impl<'a> LifecycleScripts<'a> { let temp_file_fd = deno_runtime::ops::process::npm_process_state_tempfile( process_state.as_bytes(), - ).context("failed to create npm process state tempfile for running lifecycle scripts")?; + ) + .map_err(LifecycleScriptsError::CreateNpmProcessState)?; // SAFETY: fd/handle is valid let _temp_file = unsafe { std::fs::File::from_raw_io_handle(temp_file_fd) }; // make sure the file gets closed @@ -240,7 +261,7 @@ impl<'a> LifecycleScripts<'a> { package, snapshot, get_package_path, - )?; + ); for script_name in ["preinstall", "install", "postinstall"] { if let Some(script) = package.scripts.get(script_name) { if script_name == "install" @@ -273,7 +294,8 @@ impl<'a> LifecycleScripts<'a> { kill_signal: kill_signal.clone(), }, ) - .await?; + .await + .map_err(LifecycleScriptsError::Task)?; let stdout = stdout.unwrap(); let stderr = stderr.unwrap(); if exit_code != 0 { @@ -322,14 +344,12 @@ impl<'a> LifecycleScripts<'a> { if failed_packages.is_empty() { Ok(()) } else { - Err(AnyError::msg(format!( - "failed to run scripts for packages: {}", + Err(LifecycleScriptsError::RunScripts( failed_packages .iter() .map(|p| p.to_string()) - .collect::>() - .join(", ") - ))) + .collect::>(), + )) } } } @@ -349,7 +369,7 @@ fn resolve_baseline_custom_commands<'a>( snapshot: &'a NpmResolutionSnapshot, packages: &'a [NpmResolutionPackage], get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf, -) -> Result { +) -> crate::task_runner::TaskCustomCommands { let mut custom_commands = crate::task_runner::TaskCustomCommands::new(); custom_commands .insert("npx".to_string(), Rc::new(crate::task_runner::NpxCommand)); @@ -390,7 +410,7 @@ fn resolve_custom_commands_from_packages< snapshot: &'a NpmResolutionSnapshot, packages: P, get_package_path: impl Fn(&'a NpmResolutionPackage) -> PathBuf, -) -> Result { +) -> crate::task_runner::TaskCustomCommands { for package in packages { let package_path = get_package_path(package); @@ -409,7 +429,7 @@ fn resolve_custom_commands_from_packages< ); } - Ok(commands) + commands } // resolves the custom commands from the dependencies of a package @@ -420,7 +440,7 @@ fn resolve_custom_commands_from_deps( package: &NpmResolutionPackage, snapshot: &NpmResolutionSnapshot, get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf, -) -> Result { +) -> crate::task_runner::TaskCustomCommands { let mut bin_entries = BinEntries::new(); resolve_custom_commands_from_packages( &mut bin_entries, diff --git a/cli/npm/installer/common/mod.rs b/cli/npm/installer/common/mod.rs new file mode 100644 index 0000000000..bd22a58f03 --- /dev/null +++ b/cli/npm/installer/common/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use async_trait::async_trait; +use deno_error::JsErrorBox; + +use super::PackageCaching; + +pub mod bin_entries; +pub mod lifecycle_scripts; + +/// Part of the resolution that interacts with the file system. +#[async_trait(?Send)] +pub trait NpmPackageFsInstaller: std::fmt::Debug + Send + Sync { + async fn cache_packages<'a>( + &self, + caching: PackageCaching<'a>, + ) -> Result<(), JsErrorBox>; +} diff --git a/cli/npm/installer/global.rs b/cli/npm/installer/global.rs new file mode 100644 index 0000000000..a6b296c6d8 --- /dev/null +++ b/cli/npm/installer/global.rs @@ -0,0 +1,190 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use async_trait::async_trait; +use deno_core::futures::stream::FuturesUnordered; +use deno_core::futures::StreamExt; +use deno_error::JsErrorBox; +use deno_npm::NpmResolutionPackage; +use deno_npm::NpmSystemInfo; +use deno_resolver::npm::managed::NpmResolutionCell; + +use super::common::lifecycle_scripts::LifecycleScriptsStrategy; +use super::common::NpmPackageFsInstaller; +use super::PackageCaching; +use crate::args::LifecycleScriptsConfig; +use crate::cache::FastInsecureHasher; +use crate::colors; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; + +/// Resolves packages from the global npm cache. +#[derive(Debug)] +pub struct GlobalNpmPackageInstaller { + cache: Arc, + tarball_cache: Arc, + resolution: Arc, + lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, +} + +impl GlobalNpmPackageInstaller { + pub fn new( + cache: Arc, + tarball_cache: Arc, + resolution: Arc, + lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, + ) -> Self { + Self { + cache, + tarball_cache, + resolution, + lifecycle_scripts, + system_info, + } + } +} + +#[async_trait(?Send)] +impl NpmPackageFsInstaller for GlobalNpmPackageInstaller { + async fn cache_packages<'a>( + &self, + caching: PackageCaching<'a>, + ) -> Result<(), JsErrorBox> { + let package_partitions = match caching { + PackageCaching::All => self + .resolution + .all_system_packages_partitioned(&self.system_info), + PackageCaching::Only(reqs) => self + .resolution + .subset(&reqs) + .all_system_packages_partitioned(&self.system_info), + }; + cache_packages(&package_partitions.packages, &self.tarball_cache) + .await + .map_err(JsErrorBox::from_err)?; + + // create the copy package folders + for copy in package_partitions.copy_packages { + self + .cache + .ensure_copy_package(©.get_package_cache_folder_id()) + .map_err(JsErrorBox::from_err)?; + } + + let mut lifecycle_scripts = + super::common::lifecycle_scripts::LifecycleScripts::new( + &self.lifecycle_scripts, + GlobalLifecycleScripts::new(self, &self.lifecycle_scripts.root_dir), + ); + for package in &package_partitions.packages { + let package_folder = self.cache.package_folder_for_nv(&package.id.nv); + lifecycle_scripts.add(package, Cow::Borrowed(&package_folder)); + } + + lifecycle_scripts + .warn_not_run_scripts() + .map_err(JsErrorBox::from_err)?; + + Ok(()) + } +} + +async fn cache_packages( + packages: &[NpmResolutionPackage], + tarball_cache: &Arc, +) -> Result<(), deno_npm_cache::EnsurePackageError> { + let mut futures_unordered = FuturesUnordered::new(); + for package in packages { + futures_unordered.push(async move { + tarball_cache + .ensure_package(&package.id.nv, &package.dist) + .await + }); + } + while let Some(result) = futures_unordered.next().await { + // surface the first error + result?; + } + Ok(()) +} + +struct GlobalLifecycleScripts<'a> { + installer: &'a GlobalNpmPackageInstaller, + path_hash: u64, +} + +impl<'a> GlobalLifecycleScripts<'a> { + fn new(installer: &'a GlobalNpmPackageInstaller, root_dir: &Path) -> Self { + let mut hasher = FastInsecureHasher::new_without_deno_version(); + hasher.write(root_dir.to_string_lossy().as_bytes()); + let path_hash = hasher.finish(); + Self { + installer, + path_hash, + } + } + + fn warned_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf { + self + .package_path(package) + .join(format!(".scripts-warned-{}", self.path_hash)) + } +} + +impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy + for GlobalLifecycleScripts<'a> +{ + fn can_run_scripts(&self) -> bool { + false + } + fn package_path(&self, package: &NpmResolutionPackage) -> PathBuf { + self.installer.cache.package_folder_for_nv(&package.id.nv) + } + + fn warn_on_scripts_not_run( + &self, + packages: &[(&NpmResolutionPackage, PathBuf)], + ) -> std::result::Result<(), std::io::Error> { + log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall")); + for (package, _) in packages { + log::warn!("┠─ {}", colors::gray(format!("npm:{}", package.id.nv))); + } + log::warn!("┃"); + log::warn!( + "┠─ {}", + colors::italic("This may cause the packages to not work correctly.") + ); + log::warn!("┠─ {}", colors::italic("Lifecycle scripts are only supported when using a `node_modules` directory.")); + log::warn!( + "┠─ {}", + colors::italic("Enable it in your deno config file:") + ); + log::warn!("┖─ {}", colors::bold("\"nodeModulesDir\": \"auto\"")); + + for (package, _) in packages { + std::fs::write(self.warned_scripts_file(package), "")?; + } + Ok(()) + } + + fn did_run_scripts( + &self, + _package: &NpmResolutionPackage, + ) -> Result<(), std::io::Error> { + Ok(()) + } + + fn has_warned(&self, package: &NpmResolutionPackage) -> bool { + self.warned_scripts_file(package).exists() + } + + fn has_run(&self, _package: &NpmResolutionPackage) -> bool { + false + } +} diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/installer/local.rs similarity index 74% rename from cli/npm/managed/resolvers/local.rs rename to cli/npm/installer/local.rs index 1e83717f15..87288c6c8e 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/installer/local.rs @@ -1,12 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! Code for local node_modules resolution. -use std::borrow::Cow; use std::cell::RefCell; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::BTreeMap; +use std::collections::BTreeSet; use std::collections::HashMap; use std::collections::HashSet; use std::fs; @@ -15,249 +15,86 @@ use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; -use crate::args::LifecycleScriptsConfig; -use crate::colors; -use crate::npm::managed::PackageCaching; -use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; use async_trait::async_trait; -use deno_ast::ModuleSpecifier; -use deno_cache_dir::npm::mixed_case_package_name_decode; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::StreamExt; use deno_core::parking_lot::Mutex; -use deno_core::url::Url; +use deno_error::JsErrorBox; use deno_npm::resolution::NpmResolutionSnapshot; -use deno_npm::NpmPackageCacheFolderId; -use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; -use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder; -use deno_runtime::deno_fs; -use deno_runtime::deno_node::NodePermissions; +use deno_path_util::fs::atomic_write_file_with_retries; +use deno_resolver::npm::get_package_folder_id_folder_name; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_semver::package::PackageNv; -use node_resolver::errors::PackageFolderResolveError; -use node_resolver::errors::PackageFolderResolveIoError; -use node_resolver::errors::PackageNotFoundError; -use node_resolver::errors::ReferrerNotFoundError; +use deno_semver::StackString; use serde::Deserialize; use serde::Serialize; +use super::common::bin_entries; +use super::common::NpmPackageFsInstaller; +use super::PackageCaching; +use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; use crate::cache::CACHE_PERM; -use crate::util::fs::atomic_write_file_with_retries; -use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; +use crate::colors; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; use crate::util::fs::clone_dir_recursive; use crate::util::fs::symlink_dir; use crate::util::fs::LaxSingleProcessFsFlag; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressMessagePrompt; -use super::super::resolution::NpmResolution; -use super::common::bin_entries; -use super::common::NpmPackageFsResolver; -use super::common::RegistryReadPermissionChecker; - /// Resolver that creates a local node_modules directory /// and resolves packages from it. #[derive(Debug)] -pub struct LocalNpmPackageResolver { +pub struct LocalNpmPackageInstaller { cache: Arc, - fs: Arc, npm_install_deps_provider: Arc, progress_bar: ProgressBar, - resolution: Arc, + resolution: Arc, + sys: CliSys, tarball_cache: Arc, - root_node_modules_path: PathBuf, - root_node_modules_url: Url, - system_info: NpmSystemInfo, - registry_read_permission_checker: RegistryReadPermissionChecker, lifecycle_scripts: LifecycleScriptsConfig, + root_node_modules_path: PathBuf, + system_info: NpmSystemInfo, } -impl LocalNpmPackageResolver { +impl LocalNpmPackageInstaller { #[allow(clippy::too_many_arguments)] pub fn new( cache: Arc, - fs: Arc, npm_install_deps_provider: Arc, progress_bar: ProgressBar, - resolution: Arc, + resolution: Arc, + sys: CliSys, tarball_cache: Arc, node_modules_folder: PathBuf, - system_info: NpmSystemInfo, lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, ) -> Self { Self { cache, - fs: fs.clone(), npm_install_deps_provider, progress_bar, resolution, tarball_cache, - registry_read_permission_checker: RegistryReadPermissionChecker::new( - fs, - node_modules_folder.clone(), - ), - root_node_modules_url: Url::from_directory_path(&node_modules_folder) - .unwrap(), + sys, + lifecycle_scripts, root_node_modules_path: node_modules_folder, system_info, - lifecycle_scripts, } } - - fn resolve_package_root(&self, path: &Path) -> PathBuf { - let mut last_found = path; - loop { - let parent = last_found.parent().unwrap(); - if parent.file_name().unwrap() == "node_modules" { - return last_found.to_path_buf(); - } else { - last_found = parent; - } - } - } - - fn resolve_folder_for_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result, std::io::Error> { - let Some(relative_url) = - self.root_node_modules_url.make_relative(specifier) - else { - return Ok(None); - }; - if relative_url.starts_with("../") { - return Ok(None); - } - // it's within the directory, so use it - let Some(path) = specifier.to_file_path().ok() else { - return Ok(None); - }; - // Canonicalize the path so it's not pointing to the symlinked directory - // in `node_modules` directory of the referrer. - canonicalize_path_maybe_not_exists_with_fs(&path, self.fs.as_ref()) - .map(Some) - } - - fn resolve_package_folder_from_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result, AnyError> { - let Some(local_path) = self.resolve_folder_for_specifier(specifier)? else { - return Ok(None); - }; - let package_root_path = self.resolve_package_root(&local_path); - Ok(Some(package_root_path)) - } } #[async_trait(?Send)] -impl NpmPackageFsResolver for LocalNpmPackageResolver { - fn node_modules_path(&self) -> Option<&Path> { - Some(self.root_node_modules_path.as_ref()) - } - - fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { - let cache_folder_id = self - .resolution - .resolve_pkg_cache_folder_id_from_pkg_id(id)?; - // package is stored at: - // node_modules/.deno//node_modules/ - Some( - self - .root_node_modules_path - .join(".deno") - .join(get_package_folder_id_folder_name(&cache_folder_id)) - .join("node_modules") - .join(&cache_folder_id.nv.name), - ) - } - - fn resolve_package_folder_from_package( - &self, - name: &str, - referrer: &ModuleSpecifier, - ) -> Result { - let maybe_local_path = self - .resolve_folder_for_specifier(referrer) - .map_err(|err| PackageFolderResolveIoError { - package_name: name.to_string(), - referrer: referrer.clone(), - source: err, - })?; - let Some(local_path) = maybe_local_path else { - return Err( - ReferrerNotFoundError { - referrer: referrer.clone(), - referrer_extra: None, - } - .into(), - ); - }; - let package_root_path = self.resolve_package_root(&local_path); - let mut current_folder = package_root_path.as_path(); - while let Some(parent_folder) = current_folder.parent() { - current_folder = parent_folder; - let node_modules_folder = if current_folder.ends_with("node_modules") { - Cow::Borrowed(current_folder) - } else { - Cow::Owned(current_folder.join("node_modules")) - }; - - let sub_dir = join_package_name(&node_modules_folder, name); - if self.fs.is_dir_sync(&sub_dir) { - return Ok(sub_dir); - } - - if current_folder == self.root_node_modules_path { - break; - } - } - - Err( - PackageNotFoundError { - package_name: name.to_string(), - referrer: referrer.clone(), - referrer_extra: None, - } - .into(), - ) - } - - fn resolve_package_cache_folder_id_from_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result, AnyError> { - let Some(folder_path) = - self.resolve_package_folder_from_specifier(specifier)? - else { - return Ok(None); - }; - // ex. project/node_modules/.deno/preact@10.24.3/node_modules/preact/ - let Some(node_modules_ancestor) = folder_path - .ancestors() - .find(|ancestor| ancestor.ends_with("node_modules")) - else { - return Ok(None); - }; - let Some(folder_name) = - node_modules_ancestor.parent().and_then(|p| p.file_name()) - else { - return Ok(None); - }; - Ok(get_package_folder_id_from_folder_name( - &folder_name.to_string_lossy(), - )) - } - +impl NpmPackageFsInstaller for LocalNpmPackageInstaller { async fn cache_packages<'a>( &self, caching: PackageCaching<'a>, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { let snapshot = match caching { PackageCaching::All => self.resolution.snapshot(), PackageCaching::Only(reqs) => self.resolution.subset(&reqs), @@ -269,20 +106,12 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { &self.progress_bar, &self.tarball_cache, &self.root_node_modules_path, + &self.sys, &self.system_info, &self.lifecycle_scripts, ) .await - } - - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError> { - self - .registry_read_permission_checker - .ensure_registry_read_permission(permissions, path) + .map_err(JsErrorBox::from_err) } } @@ -301,6 +130,38 @@ fn local_node_modules_package_contents_path( .join(&package.id.nv.name) } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum SyncResolutionWithFsError { + #[class(inherit)] + #[error("Creating '{path}'")] + Creating { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + CopyDirRecursive(#[from] crate::util::fs::CopyDirRecursiveError), + #[class(inherit)] + #[error(transparent)] + SymlinkPackageDir(#[from] SymlinkPackageDirError), + #[class(inherit)] + #[error(transparent)] + BinEntries(#[from] bin_entries::BinEntriesError), + #[class(inherit)] + #[error(transparent)] + LifecycleScripts( + #[from] super::common::lifecycle_scripts::LifecycleScriptsError, + ), + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + /// Creates a pnpm style folder structure. #[allow(clippy::too_many_arguments)] async fn sync_resolution_with_fs( @@ -310,9 +171,10 @@ async fn sync_resolution_with_fs( progress_bar: &ProgressBar, tarball_cache: &Arc, root_node_modules_dir_path: &Path, + sys: &CliSys, system_info: &NpmSystemInfo, lifecycle_scripts: &LifecycleScriptsConfig, -) -> Result<(), AnyError> { +) -> Result<(), SyncResolutionWithFsError> { if snapshot.is_empty() && npm_install_deps_provider.workspace_pkgs().is_empty() { @@ -327,12 +189,18 @@ async fn sync_resolution_with_fs( let deno_local_registry_dir = root_node_modules_dir_path.join(".deno"); let deno_node_modules_dir = deno_local_registry_dir.join("node_modules"); - fs::create_dir_all(&deno_node_modules_dir).with_context(|| { - format!("Creating '{}'", deno_local_registry_dir.display()) + fs::create_dir_all(&deno_node_modules_dir).map_err(|source| { + SyncResolutionWithFsError::Creating { + path: deno_local_registry_dir.to_path_buf(), + source, + } })?; let bin_node_modules_dir_path = root_node_modules_dir_path.join(".bin"); - fs::create_dir_all(&bin_node_modules_dir_path).with_context(|| { - format!("Creating '{}'", bin_node_modules_dir_path.display()) + fs::create_dir_all(&bin_node_modules_dir_path).map_err(|source| { + SyncResolutionWithFsError::Creating { + path: deno_local_registry_dir.to_path_buf(), + source, + } })?; let single_process_lock = LaxSingleProcessFsFlag::lock( @@ -355,8 +223,10 @@ async fn sync_resolution_with_fs( let package_partitions = snapshot.all_system_packages_partitioned(system_info); let mut cache_futures = FuturesUnordered::new(); - let mut newest_packages_by_name: HashMap<&String, &NpmResolutionPackage> = - HashMap::with_capacity(package_partitions.packages.len()); + let mut newest_packages_by_name: HashMap< + &StackString, + &NpmResolutionPackage, + > = HashMap::with_capacity(package_partitions.packages.len()); let bin_entries = Rc::new(RefCell::new(bin_entries::BinEntries::new())); let mut lifecycle_scripts = super::common::lifecycle_scripts::LifecycleScripts::new( @@ -367,10 +237,10 @@ 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(); + let mut package_tags: HashMap<&PackageNv, BTreeSet<&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); + package_tags.entry(package_nv).or_default().insert(tag); } } @@ -390,7 +260,17 @@ async fn sync_resolution_with_fs( let folder_path = deno_local_registry_dir.join(&package_folder_name); let tags = package_tags .get(&package.id.nv) - .map(|tags| tags.join(",")) + .map(|tags| { + capacity_builder::StringBuilder::::build(|builder| { + for (i, tag) in tags.iter().enumerate() { + if i > 0 { + builder.append(',') + } + builder.append(*tag); + } + }) + .unwrap() + }) .unwrap_or_default(); enum PackageFolderState { UpToDate, @@ -424,7 +304,8 @@ async fn sync_resolution_with_fs( cache_futures.push(async move { tarball_cache .ensure_package(&package.id.nv, &package.dist) - .await?; + .await + .map_err(JsErrorBox::from_err)?; let pb_guard = progress_bar.update_with_prompt( ProgressMessagePrompt::Initialize, &package.id.nv.to_string(), @@ -436,15 +317,18 @@ async fn sync_resolution_with_fs( deno_core::unsync::spawn_blocking({ let package_path = package_path.clone(); + let sys = sys.clone(); move || { - clone_dir_recursive(&cache_folder, &package_path)?; + clone_dir_recursive(&sys, &cache_folder, &package_path)?; // write out a file that indicates this folder has been initialized fs::write(initialized_file, tags)?; - Ok::<_, AnyError>(()) + Ok::<_, SyncResolutionWithFsError>(()) } }) - .await??; + .await + .map_err(JsErrorBox::from_err)? + .map_err(JsErrorBox::from_err)?; if package.bin.is_some() { bin_entries_to_setup.borrow_mut().add(package, package_path); @@ -458,7 +342,7 @@ async fn sync_resolution_with_fs( // finally stop showing the progress bar drop(pb_guard); // explicit for clarity - Ok::<_, AnyError>(()) + Ok::<_, JsErrorBox>(()) }); } else if matches!(package_state, PackageFolderState::TagsOutdated) { fs::write(initialized_file, tags)?; @@ -494,7 +378,7 @@ async fn sync_resolution_with_fs( &package.id.nv.name, ); - clone_dir_recursive(&source_path, &package_path)?; + clone_dir_recursive(sys, &source_path, &package_path)?; // write out a file that indicates this folder has been initialized fs::write(initialized_file, "")?; } @@ -536,7 +420,7 @@ async fn sync_resolution_with_fs( } } - let mut found_names: HashMap<&String, &PackageNv> = HashMap::new(); + let mut found_names: HashMap<&StackString, &PackageNv> = HashMap::new(); // set of node_modules in workspace packages that we've already ensured exist let mut existing_child_node_modules_dirs: HashSet = HashSet::new(); @@ -593,8 +477,11 @@ async fn sync_resolution_with_fs( // symlink the dep into the package's child node_modules folder let dest_node_modules = remote.base_dir.join("node_modules"); if !existing_child_node_modules_dirs.contains(&dest_node_modules) { - fs::create_dir_all(&dest_node_modules).with_context(|| { - format!("Creating '{}'", dest_node_modules.display()) + fs::create_dir_all(&dest_node_modules).map_err(|source| { + SyncResolutionWithFsError::Creating { + path: dest_node_modules.clone(), + source, + } })?; existing_child_node_modules_dirs.insert(dest_node_modules.clone()); } @@ -809,7 +696,7 @@ impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy fn did_run_scripts( &self, package: &NpmResolutionPackage, - ) -> std::result::Result<(), deno_core::anyhow::Error> { + ) -> std::result::Result<(), std::io::Error> { std::fs::write(self.ran_scripts_file(package), "")?; Ok(()) } @@ -817,7 +704,7 @@ impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy fn warn_on_scripts_not_run( &self, packages: &[(&NpmResolutionPackage, std::path::PathBuf)], - ) -> Result<(), AnyError> { + ) -> Result<(), std::io::Error> { if !packages.is_empty() { log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall")); @@ -922,7 +809,13 @@ impl SetupCache { } bincode::serialize(&self.current).ok().and_then(|data| { - atomic_write_file_with_retries(&self.file_path, data, CACHE_PERM).ok() + atomic_write_file_with_retries( + &CliSys::default(), + &self.file_path, + &data, + CACHE_PERM, + ) + .ok() }); true } @@ -994,52 +887,42 @@ impl SetupCache { } } -fn get_package_folder_id_folder_name( - folder_id: &NpmPackageCacheFolderId, -) -> String { - let copy_str = if folder_id.copy_index == 0 { - Cow::Borrowed("") - } else { - Cow::Owned(format!("_{}", folder_id.copy_index)) - }; - let nv = &folder_id.nv; - let name = normalize_pkg_name_for_node_modules_deno_folder(&nv.name); - format!("{}@{}{}", name, nv.version, copy_str) -} - -fn get_package_folder_id_from_folder_name( - folder_name: &str, -) -> Option { - let folder_name = folder_name.replace('+', "/"); - let (name, ending) = folder_name.rsplit_once('@')?; - let name = if let Some(encoded_name) = name.strip_prefix('_') { - mixed_case_package_name_decode(encoded_name)? - } else { - name.to_string() - }; - let (raw_version, copy_index) = match ending.split_once('_') { - Some((raw_version, copy_index)) => { - let copy_index = copy_index.parse::().ok()?; - (raw_version, copy_index) - } - None => (ending, 0), - }; - let version = deno_semver::Version::parse_from_npm(raw_version).ok()?; - Some(NpmPackageCacheFolderId { - nv: PackageNv { name, version }, - copy_index, - }) +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum SymlinkPackageDirError { + #[class(inherit)] + #[error("Creating '{parent}'")] + Creating { + parent: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + Other(#[from] std::io::Error), + #[cfg(windows)] + #[class(inherit)] + #[error("Creating junction in node_modules folder")] + FailedCreatingJunction { + #[source] + #[inherit] + source: std::io::Error, + }, } fn symlink_package_dir( old_path: &Path, new_path: &Path, -) -> Result<(), AnyError> { +) -> Result<(), SymlinkPackageDirError> { let new_parent = new_path.parent().unwrap(); if new_parent.file_name().unwrap() != "node_modules" { // create the parent folder that will contain the symlink - fs::create_dir_all(new_parent) - .with_context(|| format!("Creating '{}'", new_parent.display()))?; + fs::create_dir_all(new_parent).map_err(|source| { + SymlinkPackageDirError::Creating { + parent: new_parent.to_path_buf(), + source, + } + })?; } // need to delete the previous symlink before creating a new one @@ -1055,7 +938,8 @@ fn symlink_package_dir( } #[cfg(not(windows))] { - symlink_dir(&old_path_relative, new_path).map_err(Into::into) + symlink_dir(&crate::sys::CliSys::default(), &old_path_relative, new_path) + .map_err(Into::into) } } @@ -1064,7 +948,7 @@ fn junction_or_symlink_dir( old_path_relative: &Path, old_path: &Path, new_path: &Path, -) -> Result<(), AnyError> { +) -> Result<(), SymlinkPackageDirError> { static USE_JUNCTIONS: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); @@ -1073,18 +957,21 @@ fn junction_or_symlink_dir( // needing to elevate privileges on Windows. // Note: junctions don't support relative paths, so we need to use the // absolute path here. - return junction::create(old_path, new_path) - .context("Failed creating junction in node_modules folder"); + return junction::create(old_path, new_path).map_err(|source| { + SymlinkPackageDirError::FailedCreatingJunction { source } + }); } - match symlink_dir(old_path_relative, new_path) { + match symlink_dir(&crate::sys::CliSys::default(), old_path_relative, new_path) + { Ok(()) => Ok(()), Err(symlink_err) if symlink_err.kind() == std::io::ErrorKind::PermissionDenied => { USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); - junction::create(old_path, new_path) - .context("Failed creating junction in node_modules folder") + junction::create(old_path, new_path).map_err(|source| { + SymlinkPackageDirError::FailedCreatingJunction { source } + }) } Err(symlink_err) => { log::warn!( @@ -1092,8 +979,9 @@ fn junction_or_symlink_dir( colors::yellow("Warning") ); USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); - junction::create(old_path, new_path) - .context("Failed creating junction in node_modules folder") + junction::create(old_path, new_path).map_err(|source| { + SymlinkPackageDirError::FailedCreatingJunction { source } + }) } } } @@ -1109,37 +997,10 @@ fn join_package_name(path: &Path, package_name: &str) -> PathBuf { #[cfg(test)] mod test { - use deno_npm::NpmPackageCacheFolderId; - use deno_semver::package::PackageNv; use test_util::TempDir; use super::*; - #[test] - fn test_get_package_folder_id_folder_name() { - let cases = vec![ - ( - NpmPackageCacheFolderId { - nv: PackageNv::from_str("@types/foo@1.2.3").unwrap(), - copy_index: 1, - }, - "@types+foo@1.2.3_1".to_string(), - ), - ( - NpmPackageCacheFolderId { - nv: PackageNv::from_str("JSON@3.2.1").unwrap(), - copy_index: 0, - }, - "_jjju6tq@3.2.1".to_string(), - ), - ]; - for (input, output) in cases { - assert_eq!(get_package_folder_id_folder_name(&input), output); - let folder_id = get_package_folder_id_from_folder_name(&output).unwrap(); - assert_eq!(folder_id, input); - } - } - #[test] fn test_setup_cache() { let temp_dir = TempDir::new(); diff --git a/cli/npm/installer/mod.rs b/cli/npm/installer/mod.rs new file mode 100644 index 0000000000..58b9cb1bc7 --- /dev/null +++ b/cli/npm/installer/mod.rs @@ -0,0 +1,283 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_core::error::AnyError; +use deno_core::unsync::sync::AtomicFlag; +use deno_error::JsErrorBox; +use deno_npm::registry::NpmPackageInfo; +use deno_npm::registry::NpmRegistryPackageInfoLoadError; +use deno_npm::NpmSystemInfo; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_runtime::colors; +use deno_semver::package::PackageReq; + +pub use self::common::NpmPackageFsInstaller; +use self::global::GlobalNpmPackageInstaller; +use self::local::LocalNpmPackageInstaller; +pub use self::resolution::AddPkgReqsResult; +pub use self::resolution::NpmResolutionInstaller; +use super::NpmResolutionInitializer; +use crate::args::CliLockfile; +use crate::args::LifecycleScriptsConfig; +use crate::args::NpmInstallDepsProvider; +use crate::args::PackageJsonDepValueParseWithLocationError; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; +use crate::util::progress_bar::ProgressBar; + +mod common; +mod global; +mod local; +mod resolution; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PackageCaching<'a> { + Only(Cow<'a, [PackageReq]>), + All, +} + +#[derive(Debug)] +pub struct NpmInstaller { + fs_installer: Arc, + npm_install_deps_provider: Arc, + npm_resolution_initializer: Arc, + npm_resolution_installer: Arc, + maybe_lockfile: Option>, + npm_resolution: Arc, + top_level_install_flag: AtomicFlag, +} + +impl NpmInstaller { + #[allow(clippy::too_many_arguments)] + pub fn new( + npm_cache: Arc, + npm_install_deps_provider: Arc, + npm_resolution: Arc, + npm_resolution_initializer: Arc, + npm_resolution_installer: Arc, + progress_bar: &ProgressBar, + sys: CliSys, + tarball_cache: Arc, + maybe_lockfile: Option>, + maybe_node_modules_path: Option, + lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, + ) -> Self { + let fs_installer: Arc = + match maybe_node_modules_path { + Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new( + npm_cache, + npm_install_deps_provider.clone(), + progress_bar.clone(), + npm_resolution.clone(), + sys, + tarball_cache, + node_modules_folder, + lifecycle_scripts, + system_info, + )), + None => Arc::new(GlobalNpmPackageInstaller::new( + npm_cache, + tarball_cache, + npm_resolution.clone(), + lifecycle_scripts, + system_info, + )), + }; + Self { + fs_installer, + npm_install_deps_provider, + npm_resolution, + npm_resolution_initializer, + npm_resolution_installer, + maybe_lockfile, + top_level_install_flag: Default::default(), + } + } + + /// Adds package requirements to the resolver and ensures everything is setup. + /// This includes setting up the `node_modules` directory, if applicable. + pub async fn add_and_cache_package_reqs( + &self, + packages: &[PackageReq], + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + self + .add_package_reqs_raw( + packages, + Some(PackageCaching::Only(packages.into())), + ) + .await + .dependencies_result + } + + pub async fn add_package_reqs_no_cache( + &self, + packages: &[PackageReq], + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + self + .add_package_reqs_raw(packages, None) + .await + .dependencies_result + } + + pub async fn add_package_reqs( + &self, + packages: &[PackageReq], + caching: PackageCaching<'_>, + ) -> Result<(), JsErrorBox> { + self + .add_package_reqs_raw(packages, Some(caching)) + .await + .dependencies_result + } + + pub async fn add_package_reqs_raw<'a>( + &self, + packages: &[PackageReq], + caching: Option>, + ) -> AddPkgReqsResult { + if packages.is_empty() { + return AddPkgReqsResult { + dependencies_result: Ok(()), + results: vec![], + }; + } + + #[cfg(debug_assertions)] + self.npm_resolution_initializer.debug_assert_initialized(); + + let mut result = self + .npm_resolution_installer + .add_package_reqs(packages) + .await; + + if result.dependencies_result.is_ok() { + if let Some(lockfile) = self.maybe_lockfile.as_ref() { + result.dependencies_result = lockfile.error_if_changed(); + } + } + if result.dependencies_result.is_ok() { + if let Some(caching) = caching { + result.dependencies_result = self.cache_packages(caching).await; + } + } + + result + } + + /// Sets package requirements to the resolver, removing old requirements and adding new ones. + /// + /// This will retrieve and resolve package information, but not cache any package files. + pub async fn set_package_reqs( + &self, + packages: &[PackageReq], + ) -> Result<(), AnyError> { + self + .npm_resolution_installer + .set_package_reqs(packages) + .await + } + + pub async fn inject_synthetic_types_node_package( + &self, + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + let reqs = &[PackageReq::from_str("@types/node").unwrap()]; + // add and ensure this isn't added to the lockfile + self + .add_package_reqs(reqs, PackageCaching::Only(reqs.into())) + .await?; + + Ok(()) + } + + pub async fn cache_package_info( + &self, + package_name: &str, + ) -> Result, NpmRegistryPackageInfoLoadError> { + self + .npm_resolution_installer + .cache_package_info(package_name) + .await + } + + pub async fn cache_packages( + &self, + caching: PackageCaching<'_>, + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + self.fs_installer.cache_packages(caching).await + } + + pub fn ensure_no_pkg_json_dep_errors( + &self, + ) -> Result<(), Box> { + for err in self.npm_install_deps_provider.pkg_json_dep_errors() { + match err.source.as_kind() { + deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => { + return Err(Box::new(err.clone())); + } + deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported { + .. + } => { + // only warn for this one + log::warn!( + "{} {}\n at {}", + colors::yellow("Warning"), + err.source, + err.location, + ) + } + } + } + Ok(()) + } + + /// Ensures that the top level `package.json` dependencies are installed. + /// This may set up the `node_modules` directory. + /// + /// Returns `true` if the top level packages are already installed. A + /// return value of `false` means that new packages were added to the NPM resolution. + pub async fn ensure_top_level_package_json_install( + &self, + ) -> Result { + if !self.top_level_install_flag.raise() { + return Ok(true); // already did this + } + + self.npm_resolution_initializer.ensure_initialized().await?; + + let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); + if pkg_json_remote_pkgs.is_empty() { + return Ok(true); + } + + // check if something needs resolving before bothering to load all + // the package information (which is slow) + if pkg_json_remote_pkgs.iter().all(|pkg| { + self + .npm_resolution + .resolve_pkg_id_from_pkg_req(&pkg.req) + .is_ok() + }) { + log::debug!( + "All package.json deps resolvable. Skipping top level install." + ); + return Ok(true); // everything is already resolvable + } + + let pkg_reqs = pkg_json_remote_pkgs + .iter() + .map(|pkg| pkg.req.clone()) + .collect::>(); + self.add_package_reqs_no_cache(&pkg_reqs).await?; + + Ok(false) + } +} diff --git a/cli/npm/installer/resolution.rs b/cli/npm/installer/resolution.rs new file mode 100644 index 0000000000..06bbcd4f76 --- /dev/null +++ b/cli/npm/installer/resolution.rs @@ -0,0 +1,237 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashSet; +use std::sync::Arc; + +use capacity_builder::StringBuilder; +use deno_core::error::AnyError; +use deno_error::JsErrorBox; +use deno_lockfile::NpmPackageDependencyLockfileInfo; +use deno_lockfile::NpmPackageLockfileInfo; +use deno_npm::registry::NpmPackageInfo; +use deno_npm::registry::NpmRegistryApi; +use deno_npm::registry::NpmRegistryPackageInfoLoadError; +use deno_npm::resolution::AddPkgReqsOptions; +use deno_npm::resolution::NpmResolutionError; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::NpmResolutionPackage; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_semver::jsr::JsrDepPackageReq; +use deno_semver::package::PackageNv; +use deno_semver::package::PackageReq; +use deno_semver::SmallStackString; +use deno_semver::VersionReq; + +use crate::args::CliLockfile; +use crate::npm::CliNpmRegistryInfoProvider; +use crate::util::sync::TaskQueue; + +pub struct AddPkgReqsResult { + /// Results from adding the individual packages. + /// + /// The indexes of the results correspond to the indexes of the provided + /// package requirements. + pub results: Vec>, + /// The final result of resolving and caching all the package requirements. + pub dependencies_result: Result<(), JsErrorBox>, +} + +/// Updates the npm resolution with the provided package requirements. +#[derive(Debug)] +pub struct NpmResolutionInstaller { + registry_info_provider: Arc, + resolution: Arc, + maybe_lockfile: Option>, + update_queue: TaskQueue, +} + +impl NpmResolutionInstaller { + pub fn new( + registry_info_provider: Arc, + resolution: Arc, + maybe_lockfile: Option>, + ) -> Self { + Self { + registry_info_provider, + resolution, + maybe_lockfile, + update_queue: Default::default(), + } + } + + pub async fn cache_package_info( + &self, + package_name: &str, + ) -> Result, NpmRegistryPackageInfoLoadError> { + // this will internally cache the package information + self.registry_info_provider.package_info(package_name).await + } + + pub async fn add_package_reqs( + &self, + package_reqs: &[PackageReq], + ) -> AddPkgReqsResult { + // only allow one thread in here at a time + let _snapshot_lock = self.update_queue.acquire().await; + let result = add_package_reqs_to_snapshot( + &self.registry_info_provider, + package_reqs, + self.maybe_lockfile.clone(), + || self.resolution.snapshot(), + ) + .await; + + AddPkgReqsResult { + results: result.results, + dependencies_result: match result.dep_graph_result { + Ok(snapshot) => { + self.resolution.set_snapshot(snapshot); + Ok(()) + } + Err(err) => Err(JsErrorBox::from_err(err)), + }, + } + } + + pub async fn set_package_reqs( + &self, + package_reqs: &[PackageReq], + ) -> Result<(), AnyError> { + // only allow one thread in here at a time + let _snapshot_lock = self.update_queue.acquire().await; + + let reqs_set = package_reqs.iter().collect::>(); + let snapshot = add_package_reqs_to_snapshot( + &self.registry_info_provider, + package_reqs, + self.maybe_lockfile.clone(), + || { + let snapshot = self.resolution.snapshot(); + let has_removed_package = !snapshot + .package_reqs() + .keys() + .all(|req| reqs_set.contains(req)); + // if any packages were removed, we need to completely recreate the npm resolution snapshot + if has_removed_package { + snapshot.into_empty() + } else { + snapshot + } + }, + ) + .await + .into_result()?; + + self.resolution.set_snapshot(snapshot); + + Ok(()) + } +} + +async fn add_package_reqs_to_snapshot( + registry_info_provider: &Arc, + package_reqs: &[PackageReq], + maybe_lockfile: Option>, + get_new_snapshot: impl Fn() -> NpmResolutionSnapshot, +) -> deno_npm::resolution::AddPkgReqsResult { + let snapshot = get_new_snapshot(); + if package_reqs + .iter() + .all(|req| snapshot.package_reqs().contains_key(req)) + { + log::debug!("Snapshot already up to date. Skipping npm resolution."); + return deno_npm::resolution::AddPkgReqsResult { + results: package_reqs + .iter() + .map(|req| Ok(snapshot.package_reqs().get(req).unwrap().clone())) + .collect(), + dep_graph_result: Ok(snapshot), + }; + } + log::debug!( + /* this string is used in tests */ + "Running npm resolution." + ); + let npm_registry_api = registry_info_provider.as_npm_registry_api(); + let result = snapshot + .add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs)) + .await; + let result = match &result.dep_graph_result { + Err(NpmResolutionError::Resolution(err)) + if npm_registry_api.mark_force_reload() => + { + log::debug!("{err:#}"); + log::debug!("npm resolution failed. Trying again..."); + + // try again with forced reloading + let snapshot = get_new_snapshot(); + snapshot + .add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs)) + .await + } + _ => result, + }; + + registry_info_provider.clear_memory_cache(); + + if let Ok(snapshot) = &result.dep_graph_result { + if let Some(lockfile) = maybe_lockfile { + populate_lockfile_from_snapshot(&lockfile, snapshot); + } + } + + result +} + +fn get_add_pkg_reqs_options(package_reqs: &[PackageReq]) -> AddPkgReqsOptions { + AddPkgReqsOptions { + package_reqs, + // WARNING: When bumping this version, check if anything needs to be + // updated in the `setNodeOnlyGlobalNames` call in 99_main_compiler.js + types_node_version_req: Some( + VersionReq::parse_from_npm("22.0.0 - 22.5.4").unwrap(), + ), + } +} + +fn populate_lockfile_from_snapshot( + lockfile: &CliLockfile, + snapshot: &NpmResolutionSnapshot, +) { + fn npm_package_to_lockfile_info( + pkg: &NpmResolutionPackage, + ) -> NpmPackageLockfileInfo { + let dependencies = pkg + .dependencies + .iter() + .map(|(name, id)| NpmPackageDependencyLockfileInfo { + name: name.clone(), + id: id.as_serialized(), + }) + .collect(); + + NpmPackageLockfileInfo { + serialized_id: pkg.id.as_serialized(), + integrity: pkg.dist.integrity().for_lockfile(), + dependencies, + } + } + + let mut lockfile = lockfile.lock(); + for (package_req, nv) in snapshot.package_reqs() { + let id = &snapshot.resolve_package_from_deno_module(nv).unwrap().id; + lockfile.insert_package_specifier( + JsrDepPackageReq::npm(package_req.clone()), + { + StringBuilder::::build(|builder| { + builder.append(&id.nv.version); + builder.append(&id.peer_dependencies); + }) + .unwrap() + }, + ); + } + for package in snapshot.all_packages_for_every_system() { + lockfile.insert_npm_package(npm_package_to_lockfile_info(package)); + } +} diff --git a/cli/npm/managed.rs b/cli/npm/managed.rs new file mode 100644 index 0000000000..4122c881f1 --- /dev/null +++ b/cli/npm/managed.rs @@ -0,0 +1,233 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_core::parking_lot::Mutex; +use deno_core::serde_json; +use deno_error::JsError; +use deno_error::JsErrorBox; +use deno_npm::registry::NpmRegistryApi; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; +use deno_resolver::npm::managed::ManagedNpmResolverCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_resolver::npm::ManagedNpmResolverRc; +use deno_runtime::ops::process::NpmProcessStateProvider; +use thiserror::Error; + +use super::CliNpmRegistryInfoProvider; +use crate::args::CliLockfile; +use crate::args::NpmProcessState; +use crate::args::NpmProcessStateKind; +use crate::sys::CliSys; + +pub type CliManagedNpmResolverCreateOptions = + ManagedNpmResolverCreateOptions; + +#[derive(Debug, Clone)] +pub enum CliNpmResolverManagedSnapshotOption { + ResolveFromLockfile(Arc), + Specified(Option), +} + +#[derive(Debug)] +enum SyncState { + Pending(Option), + Err(ResolveSnapshotError), + Success, +} + +#[derive(Debug)] +pub struct NpmResolutionInitializer { + npm_registry_info_provider: Arc, + npm_resolution: Arc, + queue: tokio::sync::Mutex<()>, + sync_state: Mutex, +} + +impl NpmResolutionInitializer { + pub fn new( + npm_registry_info_provider: Arc, + npm_resolution: Arc, + snapshot_option: CliNpmResolverManagedSnapshotOption, + ) -> Self { + Self { + npm_registry_info_provider, + npm_resolution, + queue: tokio::sync::Mutex::new(()), + sync_state: Mutex::new(SyncState::Pending(Some(snapshot_option))), + } + } + + #[cfg(debug_assertions)] + pub fn debug_assert_initialized(&self) { + if !matches!(*self.sync_state.lock(), SyncState::Success) { + panic!("debug assert: npm resolution must be initialized before calling this code"); + } + } + + pub async fn ensure_initialized(&self) -> Result<(), JsErrorBox> { + // fast exit if not pending + { + match &*self.sync_state.lock() { + SyncState::Pending(_) => {} + SyncState::Err(err) => return Err(JsErrorBox::from_err(err.clone())), + SyncState::Success => return Ok(()), + } + } + + // only allow one task in here at a time + let _guard = self.queue.lock().await; + + let snapshot_option = { + let mut sync_state = self.sync_state.lock(); + match &mut *sync_state { + SyncState::Pending(snapshot_option) => { + // this should never panic, but if it does it means that a + // previous future was dropped while initialization occurred... + // that should never happen because this is initialized during + // startup + snapshot_option.take().unwrap() + } + // another thread updated the state while we were waiting + SyncState::Err(resolve_snapshot_error) => { + return Err(JsErrorBox::from_err(resolve_snapshot_error.clone())); + } + SyncState::Success => { + return Ok(()); + } + } + }; + + match resolve_snapshot(&self.npm_registry_info_provider, snapshot_option) + .await + { + Ok(maybe_snapshot) => { + if let Some(snapshot) = maybe_snapshot { + self + .npm_resolution + .set_snapshot(NpmResolutionSnapshot::new(snapshot)); + } + let mut sync_state = self.sync_state.lock(); + *sync_state = SyncState::Success; + Ok(()) + } + Err(err) => { + let mut sync_state = self.sync_state.lock(); + *sync_state = SyncState::Err(err.clone()); + Err(JsErrorBox::from_err(err)) + } + } + } +} + +#[derive(Debug, Error, Clone, JsError)] +#[error("failed reading lockfile '{}'", lockfile_path.display())] +#[class(inherit)] +pub struct ResolveSnapshotError { + lockfile_path: PathBuf, + #[inherit] + #[source] + source: SnapshotFromLockfileError, +} + +impl ResolveSnapshotError { + pub fn maybe_integrity_check_error( + &self, + ) -> Option<&deno_npm::resolution::IntegrityCheckFailedError> { + match &self.source { + SnapshotFromLockfileError::SnapshotFromLockfile( + deno_npm::resolution::SnapshotFromLockfileError::IntegrityCheckFailed( + err, + ), + ) => Some(err), + _ => None, + } + } +} + +async fn resolve_snapshot( + registry_info_provider: &Arc, + snapshot: CliNpmResolverManagedSnapshotOption, +) -> Result, ResolveSnapshotError> +{ + match snapshot { + CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => { + if !lockfile.overwrite() { + let snapshot = snapshot_from_lockfile( + lockfile.clone(), + ®istry_info_provider.as_npm_registry_api(), + ) + .await + .map_err(|source| ResolveSnapshotError { + lockfile_path: lockfile.filename.clone(), + source, + })?; + Ok(Some(snapshot)) + } else { + Ok(None) + } + } + CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot), + } +} + +#[derive(Debug, Error, Clone, JsError)] +pub enum SnapshotFromLockfileError { + #[error(transparent)] + #[class(inherit)] + IncompleteError( + #[from] deno_npm::resolution::IncompleteSnapshotFromLockfileError, + ), + #[error(transparent)] + #[class(inherit)] + SnapshotFromLockfile(#[from] deno_npm::resolution::SnapshotFromLockfileError), +} + +async fn snapshot_from_lockfile( + lockfile: Arc, + api: &dyn NpmRegistryApi, +) -> Result { + let (incomplete_snapshot, skip_integrity_check) = { + let lock = lockfile.lock(); + ( + deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?, + lock.overwrite, + ) + }; + let snapshot = deno_npm::resolution::snapshot_from_lockfile( + deno_npm::resolution::SnapshotFromLockfileParams { + incomplete_snapshot, + api, + skip_integrity_check, + }, + ) + .await?; + Ok(snapshot) +} + +pub fn npm_process_state( + snapshot: ValidSerializedNpmResolutionSnapshot, + node_modules_path: Option<&Path>, +) -> String { + serde_json::to_string(&NpmProcessState { + kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()), + local_node_modules_path: node_modules_path + .map(|p| p.to_string_lossy().to_string()), + }) + .unwrap() +} + +#[derive(Debug)] +pub struct CliManagedNpmProcessStateProvider(pub ManagedNpmResolverRc); + +impl NpmProcessStateProvider for CliManagedNpmProcessStateProvider { + fn get_npm_process_state(&self) -> String { + npm_process_state( + self.0.resolution().serialized_valid_snapshot(), + self.0.root_node_modules_path(), + ) + } +} diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs deleted file mode 100644 index 2c6e6d318a..0000000000 --- a/cli/npm/managed/mod.rs +++ /dev/null @@ -1,789 +0,0 @@ -// 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; - -use deno_ast::ModuleSpecifier; -use deno_cache_dir::npm::NpmCacheDir; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::url::Url; -use deno_npm::npm_rc::ResolvedNpmRc; -use deno_npm::registry::NpmPackageInfo; -use deno_npm::registry::NpmRegistryApi; -use deno_npm::resolution::NpmResolutionSnapshot; -use deno_npm::resolution::PackageReqNotFoundError; -use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; -use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_npm::NpmSystemInfo; -use deno_resolver::npm::CliNpmReqResolver; -use deno_runtime::colors; -use deno_runtime::deno_fs::FileSystem; -use deno_runtime::deno_node::NodePermissions; -use deno_runtime::ops::process::NpmProcessStateProvider; -use deno_semver::package::PackageNv; -use deno_semver::package::PackageReq; -use node_resolver::errors::PackageFolderResolveError; -use node_resolver::errors::PackageFolderResolveIoError; -use node_resolver::InNpmPackageChecker; -use node_resolver::NpmPackageFolderResolver; -use resolution::AddPkgReqsResult; - -use crate::args::CliLockfile; -use crate::args::LifecycleScriptsConfig; -use crate::args::NpmInstallDepsProvider; -use crate::args::NpmProcessState; -use crate::args::NpmProcessStateKind; -use crate::args::PackageJsonDepValueParseWithLocationError; -use crate::cache::FastInsecureHasher; -use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; -use crate::util::progress_bar::ProgressBar; -use crate::util::sync::AtomicFlag; - -use self::resolution::NpmResolution; -use self::resolvers::create_npm_fs_resolver; -use self::resolvers::NpmPackageFsResolver; - -use super::CliNpmCache; -use super::CliNpmCacheEnv; -use super::CliNpmRegistryInfoProvider; -use super::CliNpmResolver; -use super::CliNpmTarballCache; -use super::InnerCliNpmResolverRef; -use super::ResolvePkgFolderFromDenoReqError; - -mod resolution; -mod resolvers; - -pub enum CliNpmResolverManagedSnapshotOption { - ResolveFromLockfile(Arc), - Specified(Option), -} - -pub struct CliManagedNpmResolverCreateOptions { - pub snapshot: CliNpmResolverManagedSnapshotOption, - pub maybe_lockfile: Option>, - pub fs: Arc, - pub http_client_provider: Arc, - pub npm_cache_dir: Arc, - pub cache_setting: crate::args::CacheSetting, - pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, - pub maybe_node_modules_path: Option, - pub npm_system_info: NpmSystemInfo, - pub npm_install_deps_provider: Arc, - pub npmrc: Arc, - pub lifecycle_scripts: LifecycleScriptsConfig, -} - -pub async fn create_managed_npm_resolver_for_lsp( - options: CliManagedNpmResolverCreateOptions, -) -> Arc { - let cache_env = create_cache_env(&options); - let npm_cache = create_cache(cache_env.clone(), &options); - let npm_api = create_api(npm_cache.clone(), cache_env.clone(), &options); - // spawn due to the lsp's `Send` requirement - deno_core::unsync::spawn(async move { - let snapshot = match resolve_snapshot(&npm_api, options.snapshot).await { - Ok(snapshot) => snapshot, - Err(err) => { - log::warn!("failed to resolve snapshot: {}", err); - None - } - }; - create_inner( - cache_env, - options.fs, - options.maybe_lockfile, - npm_api, - npm_cache, - options.npmrc, - options.npm_install_deps_provider, - options.text_only_progress_bar, - options.maybe_node_modules_path, - options.npm_system_info, - snapshot, - options.lifecycle_scripts, - ) - }) - .await - .unwrap() -} - -pub async fn create_managed_npm_resolver( - options: CliManagedNpmResolverCreateOptions, -) -> Result, AnyError> { - let npm_cache_env = create_cache_env(&options); - let npm_cache = create_cache(npm_cache_env.clone(), &options); - let api = create_api(npm_cache.clone(), npm_cache_env.clone(), &options); - let snapshot = resolve_snapshot(&api, options.snapshot).await?; - Ok(create_inner( - npm_cache_env, - options.fs, - options.maybe_lockfile, - api, - npm_cache, - options.npmrc, - options.npm_install_deps_provider, - options.text_only_progress_bar, - options.maybe_node_modules_path, - options.npm_system_info, - snapshot, - options.lifecycle_scripts, - )) -} - -#[allow(clippy::too_many_arguments)] -fn create_inner( - env: Arc, - fs: Arc, - maybe_lockfile: Option>, - registry_info_provider: Arc, - npm_cache: Arc, - npm_rc: Arc, - npm_install_deps_provider: Arc, - text_only_progress_bar: crate::util::progress_bar::ProgressBar, - node_modules_dir_path: Option, - npm_system_info: NpmSystemInfo, - snapshot: Option, - lifecycle_scripts: LifecycleScriptsConfig, -) -> Arc { - let resolution = Arc::new(NpmResolution::from_serialized( - registry_info_provider.clone(), - snapshot, - maybe_lockfile.clone(), - )); - let tarball_cache = Arc::new(CliNpmTarballCache::new( - npm_cache.clone(), - env, - npm_rc.clone(), - )); - let fs_resolver = create_npm_fs_resolver( - fs.clone(), - npm_cache.clone(), - &npm_install_deps_provider, - &text_only_progress_bar, - resolution.clone(), - tarball_cache.clone(), - node_modules_dir_path, - npm_system_info.clone(), - lifecycle_scripts.clone(), - ); - Arc::new(ManagedCliNpmResolver::new( - fs, - fs_resolver, - maybe_lockfile, - registry_info_provider, - npm_cache, - npm_install_deps_provider, - resolution, - tarball_cache, - text_only_progress_bar, - npm_system_info, - lifecycle_scripts, - )) -} - -fn create_cache_env( - options: &CliManagedNpmResolverCreateOptions, -) -> Arc { - Arc::new(CliNpmCacheEnv::new( - options.fs.clone(), - options.http_client_provider.clone(), - options.text_only_progress_bar.clone(), - )) -} - -fn create_cache( - env: Arc, - options: &CliManagedNpmResolverCreateOptions, -) -> Arc { - Arc::new(CliNpmCache::new( - options.npm_cache_dir.clone(), - options.cache_setting.as_npm_cache_setting(), - env, - options.npmrc.clone(), - )) -} - -fn create_api( - cache: Arc, - env: Arc, - options: &CliManagedNpmResolverCreateOptions, -) -> Arc { - Arc::new(CliNpmRegistryInfoProvider::new( - cache, - env, - options.npmrc.clone(), - )) -} - -async fn resolve_snapshot( - registry_info_provider: &Arc, - snapshot: CliNpmResolverManagedSnapshotOption, -) -> Result, AnyError> { - match snapshot { - CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => { - if !lockfile.overwrite() { - let snapshot = snapshot_from_lockfile( - lockfile.clone(), - ®istry_info_provider.as_npm_registry_api(), - ) - .await - .with_context(|| { - format!("failed reading lockfile '{}'", lockfile.filename.display()) - })?; - Ok(Some(snapshot)) - } else { - Ok(None) - } - } - CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot), - } -} - -async fn snapshot_from_lockfile( - lockfile: Arc, - api: &dyn NpmRegistryApi, -) -> Result { - let (incomplete_snapshot, skip_integrity_check) = { - let lock = lockfile.lock(); - ( - deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?, - lock.overwrite, - ) - }; - let snapshot = deno_npm::resolution::snapshot_from_lockfile( - deno_npm::resolution::SnapshotFromLockfileParams { - incomplete_snapshot, - api, - skip_integrity_check, - }, - ) - .await?; - Ok(snapshot) -} - -#[derive(Debug)] -struct ManagedInNpmPackageChecker { - root_dir: Url, -} - -impl InNpmPackageChecker for ManagedInNpmPackageChecker { - fn in_npm_package(&self, specifier: &Url) -> bool { - specifier.as_ref().starts_with(self.root_dir.as_str()) - } -} - -pub struct CliManagedInNpmPkgCheckerCreateOptions<'a> { - pub root_cache_dir_url: &'a Url, - pub maybe_node_modules_path: Option<&'a Path>, -} - -pub fn create_managed_in_npm_pkg_checker( - options: CliManagedInNpmPkgCheckerCreateOptions, -) -> Arc { - let root_dir = match options.maybe_node_modules_path { - Some(node_modules_folder) => { - deno_path_util::url_from_directory_path(node_modules_folder).unwrap() - } - None => options.root_cache_dir_url.clone(), - }; - debug_assert!(root_dir.as_str().ends_with('/')); - Arc::new(ManagedInNpmPackageChecker { root_dir }) -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum PackageCaching<'a> { - Only(Cow<'a, [PackageReq]>), - All, -} - -/// An npm resolver where the resolution is managed by Deno rather than -/// the user bringing their own node_modules (BYONM) on the file system. -pub struct ManagedCliNpmResolver { - fs: Arc, - fs_resolver: Arc, - maybe_lockfile: Option>, - registry_info_provider: Arc, - npm_cache: Arc, - npm_install_deps_provider: Arc, - resolution: Arc, - tarball_cache: Arc, - text_only_progress_bar: ProgressBar, - npm_system_info: NpmSystemInfo, - top_level_install_flag: AtomicFlag, - lifecycle_scripts: LifecycleScriptsConfig, -} - -impl std::fmt::Debug for ManagedCliNpmResolver { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ManagedNpmResolver") - .field("", &"") - .finish() - } -} - -impl ManagedCliNpmResolver { - #[allow(clippy::too_many_arguments)] - pub fn new( - fs: Arc, - fs_resolver: Arc, - maybe_lockfile: Option>, - registry_info_provider: Arc, - npm_cache: Arc, - npm_install_deps_provider: Arc, - resolution: Arc, - tarball_cache: Arc, - text_only_progress_bar: ProgressBar, - npm_system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, - ) -> Self { - Self { - fs, - fs_resolver, - maybe_lockfile, - registry_info_provider, - npm_cache, - npm_install_deps_provider, - text_only_progress_bar, - resolution, - tarball_cache, - npm_system_info, - top_level_install_flag: Default::default(), - lifecycle_scripts, - } - } - - pub fn resolve_pkg_folder_from_pkg_id( - &self, - pkg_id: &NpmPackageId, - ) -> Result { - let path = self.fs_resolver.package_folder(pkg_id)?; - let path = - canonicalize_path_maybe_not_exists_with_fs(&path, self.fs.as_ref())?; - log::debug!( - "Resolved package folder of {} to {}", - pkg_id.as_serialized(), - path.display() - ); - Ok(path) - } - - /// Resolves the package id from the provided specifier. - pub fn resolve_pkg_id_from_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result, AnyError> { - let Some(cache_folder_id) = self - .fs_resolver - .resolve_package_cache_folder_id_from_specifier(specifier)? - else { - return Ok(None); - }; - Ok(Some( - self - .resolution - .resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?, - )) - } - - pub fn resolve_pkg_reqs_from_pkg_id( - &self, - id: &NpmPackageId, - ) -> Vec { - self.resolution.resolve_pkg_reqs_from_pkg_id(id) - } - - /// Attempts to get the package size in bytes. - pub fn package_size( - &self, - package_id: &NpmPackageId, - ) -> Result { - let package_folder = self.fs_resolver.package_folder(package_id)?; - Ok(crate::util::fs::dir_size(&package_folder)?) - } - - pub fn all_system_packages( - &self, - system_info: &NpmSystemInfo, - ) -> Vec { - self.resolution.all_system_packages(system_info) - } - - /// Checks if the provided package req's folder is cached. - pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool { - self - .resolve_pkg_id_from_pkg_req(req) - .ok() - .and_then(|id| self.fs_resolver.package_folder(&id).ok()) - .map(|folder| folder.exists()) - .unwrap_or(false) - } - - /// Adds package requirements to the resolver and ensures everything is setup. - /// This includes setting up the `node_modules` directory, if applicable. - pub async fn add_and_cache_package_reqs( - &self, - packages: &[PackageReq], - ) -> Result<(), AnyError> { - self - .add_package_reqs_raw( - packages, - Some(PackageCaching::Only(packages.into())), - ) - .await - .dependencies_result - } - - pub async fn add_package_reqs_no_cache( - &self, - packages: &[PackageReq], - ) -> Result<(), AnyError> { - self - .add_package_reqs_raw(packages, None) - .await - .dependencies_result - } - - pub async fn add_package_reqs( - &self, - packages: &[PackageReq], - caching: PackageCaching<'_>, - ) -> Result<(), AnyError> { - self - .add_package_reqs_raw(packages, Some(caching)) - .await - .dependencies_result - } - - pub async fn add_package_reqs_raw<'a>( - &self, - packages: &[PackageReq], - caching: Option>, - ) -> AddPkgReqsResult { - if packages.is_empty() { - return AddPkgReqsResult { - dependencies_result: Ok(()), - results: vec![], - }; - } - - let mut result = self.resolution.add_package_reqs(packages).await; - - if result.dependencies_result.is_ok() { - if let Some(lockfile) = self.maybe_lockfile.as_ref() { - result.dependencies_result = lockfile.error_if_changed(); - } - } - if result.dependencies_result.is_ok() { - if let Some(caching) = caching { - result.dependencies_result = self.cache_packages(caching).await; - } - } - - result - } - - /// Sets package requirements to the resolver, removing old requirements and adding new ones. - /// - /// This will retrieve and resolve package information, but not cache any package files. - pub async fn set_package_reqs( - &self, - packages: &[PackageReq], - ) -> Result<(), AnyError> { - self.resolution.set_package_reqs(packages).await - } - - pub fn snapshot(&self) -> NpmResolutionSnapshot { - self.resolution.snapshot() - } - - pub fn top_package_req_for_name(&self, name: &str) -> Option { - let package_reqs = self.resolution.package_reqs(); - let mut entries = package_reqs - .iter() - .filter(|(_, nv)| nv.name == name) - .collect::>(); - entries.sort_by_key(|(_, nv)| &nv.version); - Some(entries.last()?.0.clone()) - } - - pub fn serialized_valid_snapshot_for_system( - &self, - system_info: &NpmSystemInfo, - ) -> ValidSerializedNpmResolutionSnapshot { - self - .resolution - .serialized_valid_snapshot_for_system(system_info) - } - - pub async fn inject_synthetic_types_node_package( - &self, - ) -> Result<(), AnyError> { - let reqs = &[PackageReq::from_str("@types/node").unwrap()]; - // add and ensure this isn't added to the lockfile - self - .add_package_reqs(reqs, PackageCaching::Only(reqs.into())) - .await?; - - Ok(()) - } - - pub async fn cache_packages( - &self, - caching: PackageCaching<'_>, - ) -> Result<(), AnyError> { - self.fs_resolver.cache_packages(caching).await - } - - pub fn resolve_pkg_folder_from_deno_module( - &self, - nv: &PackageNv, - ) -> Result { - let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?; - self.resolve_pkg_folder_from_pkg_id(&pkg_id) - } - - pub fn resolve_pkg_id_from_pkg_req( - &self, - req: &PackageReq, - ) -> Result { - self.resolution.resolve_pkg_id_from_pkg_req(req) - } - - pub fn ensure_no_pkg_json_dep_errors( - &self, - ) -> Result<(), Box> { - for err in self.npm_install_deps_provider.pkg_json_dep_errors() { - match &err.source { - deno_package_json::PackageJsonDepValueParseError::VersionReq(_) => { - return Err(Box::new(err.clone())); - } - deno_package_json::PackageJsonDepValueParseError::Unsupported { - .. - } => { - // only warn for this one - log::warn!( - "{} {}\n at {}", - colors::yellow("Warning"), - err.source, - err.location, - ) - } - } - } - Ok(()) - } - - /// Ensures that the top level `package.json` dependencies are installed. - /// This may set up the `node_modules` directory. - /// - /// Returns `true` if the top level packages are already installed. A - /// return value of `false` means that new packages were added to the NPM resolution. - pub async fn ensure_top_level_package_json_install( - &self, - ) -> Result { - if !self.top_level_install_flag.raise() { - return Ok(true); // already did this - } - - let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); - if pkg_json_remote_pkgs.is_empty() { - return Ok(true); - } - - // check if something needs resolving before bothering to load all - // the package information (which is slow) - if pkg_json_remote_pkgs.iter().all(|pkg| { - self - .resolution - .resolve_pkg_id_from_pkg_req(&pkg.req) - .is_ok() - }) { - log::debug!( - "All package.json deps resolvable. Skipping top level install." - ); - return Ok(true); // everything is already resolvable - } - - let pkg_reqs = pkg_json_remote_pkgs - .iter() - .map(|pkg| pkg.req.clone()) - .collect::>(); - self.add_package_reqs_no_cache(&pkg_reqs).await?; - - Ok(false) - } - - pub async fn cache_package_info( - &self, - package_name: &str, - ) -> Result, AnyError> { - // this will internally cache the package information - self - .registry_info_provider - .package_info(package_name) - .await - .map_err(|err| err.into()) - } - - pub fn maybe_node_modules_path(&self) -> Option<&Path> { - self.fs_resolver.node_modules_path() - } - - pub fn global_cache_root_path(&self) -> &Path { - self.npm_cache.root_dir_path() - } - - pub fn global_cache_root_url(&self) -> &Url { - self.npm_cache.root_dir_url() - } -} - -fn npm_process_state( - snapshot: ValidSerializedNpmResolutionSnapshot, - node_modules_path: Option<&Path>, -) -> String { - serde_json::to_string(&NpmProcessState { - kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()), - local_node_modules_path: node_modules_path - .map(|p| p.to_string_lossy().to_string()), - }) - .unwrap() -} - -impl NpmPackageFolderResolver for ManagedCliNpmResolver { - fn resolve_package_folder_from_package( - &self, - name: &str, - referrer: &ModuleSpecifier, - ) -> Result { - let path = self - .fs_resolver - .resolve_package_folder_from_package(name, referrer)?; - let path = - canonicalize_path_maybe_not_exists_with_fs(&path, self.fs.as_ref()) - .map_err(|err| PackageFolderResolveIoError { - package_name: name.to_string(), - referrer: referrer.clone(), - source: err, - })?; - log::debug!("Resolved {} from {} to {}", name, referrer, path.display()); - Ok(path) - } -} - -impl NpmProcessStateProvider for ManagedCliNpmResolver { - fn get_npm_process_state(&self) -> String { - npm_process_state( - self.resolution.serialized_valid_snapshot(), - self.fs_resolver.node_modules_path(), - ) - } -} - -impl CliNpmReqResolver for ManagedCliNpmResolver { - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - _referrer: &ModuleSpecifier, - ) -> 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) - } -} - -impl CliNpmResolver for ManagedCliNpmResolver { - fn into_npm_pkg_folder_resolver( - self: Arc, - ) -> Arc { - self - } - - fn into_npm_req_resolver(self: Arc) -> Arc { - self - } - - fn into_process_state_provider( - self: Arc, - ) -> Arc { - self - } - - fn clone_snapshotted(&self) -> Arc { - // create a new snapshotted npm resolution and resolver - let npm_resolution = Arc::new(NpmResolution::new( - self.registry_info_provider.clone(), - self.resolution.snapshot(), - self.maybe_lockfile.clone(), - )); - - Arc::new(ManagedCliNpmResolver::new( - self.fs.clone(), - create_npm_fs_resolver( - self.fs.clone(), - self.npm_cache.clone(), - &self.npm_install_deps_provider, - &self.text_only_progress_bar, - npm_resolution.clone(), - self.tarball_cache.clone(), - self.root_node_modules_path().map(ToOwned::to_owned), - self.npm_system_info.clone(), - self.lifecycle_scripts.clone(), - ), - self.maybe_lockfile.clone(), - self.registry_info_provider.clone(), - self.npm_cache.clone(), - self.npm_install_deps_provider.clone(), - npm_resolution, - self.tarball_cache.clone(), - self.text_only_progress_bar.clone(), - self.npm_system_info.clone(), - self.lifecycle_scripts.clone(), - )) - } - - fn as_inner(&self) -> InnerCliNpmResolverRef { - InnerCliNpmResolverRef::Managed(self) - } - - fn root_node_modules_path(&self) -> Option<&Path> { - self.fs_resolver.node_modules_path() - } - - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError> { - self.fs_resolver.ensure_read_permission(permissions, path) - } - - fn check_state_hash(&self) -> Option { - // We could go further and check all the individual - // npm packages, but that's probably overkill. - let mut package_reqs = self - .resolution - .package_reqs() - .into_iter() - .collect::>(); - package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism - let mut hasher = FastInsecureHasher::new_without_deno_version(); - // ensure the cache gets busted when turning nodeModulesDir on or off - // as this could cause changes in resolution - hasher.write_hashable(self.fs_resolver.node_modules_path().is_some()); - for (pkg_req, pkg_nv) in package_reqs { - hasher.write_hashable(&pkg_req); - hasher.write_hashable(&pkg_nv); - } - Some(hasher.finish()) - } -} diff --git a/cli/npm/managed/resolution.rs b/cli/npm/managed/resolution.rs deleted file mode 100644 index 73c5c31caf..0000000000 --- a/cli/npm/managed/resolution.rs +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use std::collections::HashMap; -use std::collections::HashSet; -use std::sync::Arc; - -use deno_core::error::AnyError; -use deno_lockfile::NpmPackageDependencyLockfileInfo; -use deno_lockfile::NpmPackageLockfileInfo; -use deno_npm::registry::NpmRegistryApi; -use deno_npm::resolution::AddPkgReqsOptions; -use deno_npm::resolution::NpmPackagesPartitioned; -use deno_npm::resolution::NpmResolutionError; -use deno_npm::resolution::NpmResolutionSnapshot; -use deno_npm::resolution::PackageCacheFolderIdNotFoundError; -use deno_npm::resolution::PackageNotFoundFromReferrerError; -use deno_npm::resolution::PackageNvNotFoundError; -use deno_npm::resolution::PackageReqNotFoundError; -use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; -use deno_npm::NpmPackageCacheFolderId; -use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_npm::NpmSystemInfo; -use deno_semver::jsr::JsrDepPackageReq; -use deno_semver::package::PackageNv; -use deno_semver::package::PackageReq; -use deno_semver::VersionReq; - -use crate::args::CliLockfile; -use crate::npm::CliNpmRegistryInfoProvider; -use crate::util::sync::SyncReadAsyncWriteLock; - -pub struct AddPkgReqsResult { - /// Results from adding the individual packages. - /// - /// The indexes of the results correspond to the indexes of the provided - /// package requirements. - pub results: Vec>, - /// The final result of resolving and caching all the package requirements. - pub dependencies_result: Result<(), AnyError>, -} - -/// Handles updating and storing npm resolution in memory where the underlying -/// snapshot can be updated concurrently. Additionally handles updating the lockfile -/// based on changes to the resolution. -/// -/// This does not interact with the file system. -pub struct NpmResolution { - registry_info_provider: Arc, - snapshot: SyncReadAsyncWriteLock, - maybe_lockfile: Option>, -} - -impl std::fmt::Debug for NpmResolution { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let snapshot = self.snapshot.read(); - f.debug_struct("NpmResolution") - .field("snapshot", &snapshot.as_valid_serialized().as_serialized()) - .finish() - } -} - -impl NpmResolution { - pub fn from_serialized( - registry_info_provider: Arc, - initial_snapshot: Option, - maybe_lockfile: Option>, - ) -> Self { - let snapshot = - NpmResolutionSnapshot::new(initial_snapshot.unwrap_or_default()); - Self::new(registry_info_provider, snapshot, maybe_lockfile) - } - - pub fn new( - registry_info_provider: Arc, - initial_snapshot: NpmResolutionSnapshot, - maybe_lockfile: Option>, - ) -> Self { - Self { - registry_info_provider, - snapshot: SyncReadAsyncWriteLock::new(initial_snapshot), - maybe_lockfile, - } - } - - pub async fn add_package_reqs( - &self, - package_reqs: &[PackageReq], - ) -> AddPkgReqsResult { - // only allow one thread in here at a time - let snapshot_lock = self.snapshot.acquire().await; - let result = add_package_reqs_to_snapshot( - &self.registry_info_provider, - package_reqs, - self.maybe_lockfile.clone(), - || snapshot_lock.read().clone(), - ) - .await; - - AddPkgReqsResult { - results: result.results, - dependencies_result: match result.dep_graph_result { - Ok(snapshot) => { - *snapshot_lock.write() = snapshot; - Ok(()) - } - Err(err) => Err(err.into()), - }, - } - } - - pub async fn set_package_reqs( - &self, - package_reqs: &[PackageReq], - ) -> Result<(), AnyError> { - // only allow one thread in here at a time - let snapshot_lock = self.snapshot.acquire().await; - - let reqs_set = package_reqs.iter().collect::>(); - let snapshot = add_package_reqs_to_snapshot( - &self.registry_info_provider, - package_reqs, - self.maybe_lockfile.clone(), - || { - let snapshot = snapshot_lock.read().clone(); - let has_removed_package = !snapshot - .package_reqs() - .keys() - .all(|req| reqs_set.contains(req)); - // if any packages were removed, we need to completely recreate the npm resolution snapshot - if has_removed_package { - snapshot.into_empty() - } else { - snapshot - } - }, - ) - .await - .into_result()?; - - *snapshot_lock.write() = snapshot; - - Ok(()) - } - - pub fn resolve_pkg_cache_folder_id_from_pkg_id( - &self, - id: &NpmPackageId, - ) -> Option { - self - .snapshot - .read() - .package_from_id(id) - .map(|p| p.get_package_cache_folder_id()) - } - - pub fn resolve_pkg_id_from_pkg_cache_folder_id( - &self, - id: &NpmPackageCacheFolderId, - ) -> Result { - self - .snapshot - .read() - .resolve_pkg_from_pkg_cache_folder_id(id) - .map(|pkg| pkg.id.clone()) - } - - pub fn resolve_package_from_package( - &self, - name: &str, - referrer: &NpmPackageCacheFolderId, - ) -> Result> { - self - .snapshot - .read() - .resolve_package_from_package(name, referrer) - .cloned() - } - - /// Resolve a node package from a deno module. - pub fn resolve_pkg_id_from_pkg_req( - &self, - req: &PackageReq, - ) -> Result { - self - .snapshot - .read() - .resolve_pkg_from_pkg_req(req) - .map(|pkg| pkg.id.clone()) - } - - pub fn resolve_pkg_reqs_from_pkg_id( - &self, - id: &NpmPackageId, - ) -> Vec { - let snapshot = self.snapshot.read(); - let mut pkg_reqs = snapshot - .package_reqs() - .iter() - .filter(|(_, nv)| *nv == &id.nv) - .map(|(req, _)| req.clone()) - .collect::>(); - pkg_reqs.sort(); // be deterministic - pkg_reqs - } - - pub fn resolve_pkg_id_from_deno_module( - &self, - id: &PackageNv, - ) -> Result { - self - .snapshot - .read() - .resolve_package_from_deno_module(id) - .map(|pkg| pkg.id.clone()) - } - - pub fn package_reqs(&self) -> HashMap { - self.snapshot.read().package_reqs().clone() - } - - pub fn all_system_packages( - &self, - system_info: &NpmSystemInfo, - ) -> Vec { - self.snapshot.read().all_system_packages(system_info) - } - - pub fn all_system_packages_partitioned( - &self, - system_info: &NpmSystemInfo, - ) -> NpmPackagesPartitioned { - self - .snapshot - .read() - .all_system_packages_partitioned(system_info) - } - - pub fn snapshot(&self) -> NpmResolutionSnapshot { - self.snapshot.read().clone() - } - - pub fn serialized_valid_snapshot( - &self, - ) -> ValidSerializedNpmResolutionSnapshot { - self.snapshot.read().as_valid_serialized() - } - - pub fn serialized_valid_snapshot_for_system( - &self, - system_info: &NpmSystemInfo, - ) -> ValidSerializedNpmResolutionSnapshot { - self - .snapshot - .read() - .as_valid_serialized_for_system(system_info) - } - - pub fn subset(&self, package_reqs: &[PackageReq]) -> NpmResolutionSnapshot { - self.snapshot.read().subset(package_reqs) - } -} - -async fn add_package_reqs_to_snapshot( - registry_info_provider: &Arc, - package_reqs: &[PackageReq], - maybe_lockfile: Option>, - get_new_snapshot: impl Fn() -> NpmResolutionSnapshot, -) -> deno_npm::resolution::AddPkgReqsResult { - let snapshot = get_new_snapshot(); - if package_reqs - .iter() - .all(|req| snapshot.package_reqs().contains_key(req)) - { - log::debug!("Snapshot already up to date. Skipping npm resolution."); - return deno_npm::resolution::AddPkgReqsResult { - results: package_reqs - .iter() - .map(|req| Ok(snapshot.package_reqs().get(req).unwrap().clone())) - .collect(), - dep_graph_result: Ok(snapshot), - }; - } - log::debug!( - /* this string is used in tests */ - "Running npm resolution." - ); - let npm_registry_api = registry_info_provider.as_npm_registry_api(); - let result = snapshot - .add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs)) - .await; - let result = match &result.dep_graph_result { - Err(NpmResolutionError::Resolution(err)) - if npm_registry_api.mark_force_reload() => - { - log::debug!("{err:#}"); - log::debug!("npm resolution failed. Trying again..."); - - // try again with forced reloading - let snapshot = get_new_snapshot(); - snapshot - .add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs)) - .await - } - _ => result, - }; - - registry_info_provider.clear_memory_cache(); - - if let Ok(snapshot) = &result.dep_graph_result { - if let Some(lockfile) = maybe_lockfile { - populate_lockfile_from_snapshot(&lockfile, snapshot); - } - } - - result -} - -fn get_add_pkg_reqs_options(package_reqs: &[PackageReq]) -> AddPkgReqsOptions { - AddPkgReqsOptions { - package_reqs, - // WARNING: When bumping this version, check if anything needs to be - // updated in the `setNodeOnlyGlobalNames` call in 99_main_compiler.js - types_node_version_req: Some( - VersionReq::parse_from_npm("22.0.0 - 22.5.4").unwrap(), - ), - } -} - -fn populate_lockfile_from_snapshot( - lockfile: &CliLockfile, - snapshot: &NpmResolutionSnapshot, -) { - let mut lockfile = lockfile.lock(); - for (package_req, nv) in snapshot.package_reqs() { - let id = &snapshot.resolve_package_from_deno_module(nv).unwrap().id; - lockfile.insert_package_specifier( - JsrDepPackageReq::npm(package_req.clone()), - format!("{}{}", id.nv.version, id.peer_deps_serialized()), - ); - } - for package in snapshot.all_packages_for_every_system() { - lockfile.insert_npm_package(npm_package_to_lockfile_info(package)); - } -} - -fn npm_package_to_lockfile_info( - pkg: &NpmResolutionPackage, -) -> NpmPackageLockfileInfo { - let dependencies = pkg - .dependencies - .iter() - .map(|(name, id)| NpmPackageDependencyLockfileInfo { - name: name.clone(), - id: id.as_serialized(), - }) - .collect(); - - NpmPackageLockfileInfo { - serialized_id: pkg.id.as_serialized(), - integrity: pkg.dist.integrity().for_lockfile(), - dependencies, - } -} diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs deleted file mode 100644 index 68e95fb39a..0000000000 --- a/cli/npm/managed/resolvers/common.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -pub mod bin_entries; -pub mod lifecycle_scripts; - -use std::borrow::Cow; -use std::collections::HashMap; -use std::io::ErrorKind; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; -use std::sync::Mutex; - -use super::super::PackageCaching; -use async_trait::async_trait; -use deno_ast::ModuleSpecifier; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::futures; -use deno_core::futures::StreamExt; -use deno_npm::NpmPackageCacheFolderId; -use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_runtime::deno_fs::FileSystem; -use deno_runtime::deno_node::NodePermissions; -use node_resolver::errors::PackageFolderResolveError; - -use crate::npm::CliNpmTarballCache; - -/// Part of the resolution that interacts with the file system. -#[async_trait(?Send)] -pub trait NpmPackageFsResolver: Send + Sync { - /// The local node_modules folder if it is applicable to the implementation. - fn node_modules_path(&self) -> Option<&Path>; - - fn maybe_package_folder(&self, package_id: &NpmPackageId) -> Option; - - fn package_folder( - &self, - package_id: &NpmPackageId, - ) -> Result { - self.maybe_package_folder(package_id).ok_or_else(|| { - deno_core::anyhow::anyhow!( - "Package folder not found for '{}'", - package_id.as_serialized() - ) - }) - } - - fn resolve_package_folder_from_package( - &self, - name: &str, - referrer: &ModuleSpecifier, - ) -> Result; - - fn resolve_package_cache_folder_id_from_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result, AnyError>; - - async fn cache_packages<'a>( - &self, - caching: PackageCaching<'a>, - ) -> Result<(), AnyError>; - - #[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: &'a Path, - ) -> Result, AnyError>; -} - -#[derive(Debug)] -pub struct RegistryReadPermissionChecker { - fs: Arc, - cache: Mutex>, - registry_path: PathBuf, -} - -impl RegistryReadPermissionChecker { - pub fn new(fs: Arc, registry_path: PathBuf) -> Self { - Self { - fs, - registry_path, - cache: Default::default(), - } - } - - pub fn ensure_registry_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - 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 - .components() - .all(|c| !matches!(c, std::path::Component::ParentDir)); - - if is_path_in_node_modules { - let mut cache = self.cache.lock().unwrap(); - let mut canonicalize = - |path: &Path| -> Result, AnyError> { - match cache.get(path) { - Some(canon) => Ok(Some(canon.clone())), - None => match self.fs.realpath_sync(path) { - Ok(canon) => { - cache.insert(path.to_path_buf(), canon.clone()); - Ok(Some(canon)) - } - Err(e) => { - if e.kind() == ErrorKind::NotFound { - return Ok(None); - } - Err(AnyError::from(e)).with_context(|| { - format!("failed canonicalizing '{}'", path.display()) - }) - } - }, - } - }; - 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).map_err(Into::into) - } -} - -/// Caches all the packages in parallel. -pub async fn cache_packages( - packages: &[NpmResolutionPackage], - tarball_cache: &Arc, -) -> Result<(), AnyError> { - let mut futures_unordered = futures::stream::FuturesUnordered::new(); - for package in packages { - futures_unordered.push(async move { - tarball_cache - .ensure_package(&package.id.nv, &package.dist) - .await - }); - } - while let Some(result) = futures_unordered.next().await { - // surface the first error - result?; - } - Ok(()) -} diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs deleted file mode 100644 index 4e79941af6..0000000000 --- a/cli/npm/managed/resolvers/global.rs +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -//! Code for global npm cache resolution. - -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; - -use crate::colors; -use crate::npm::managed::PackageCaching; -use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; -use async_trait::async_trait; -use deno_ast::ModuleSpecifier; -use deno_core::error::AnyError; -use deno_npm::NpmPackageCacheFolderId; -use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_npm::NpmSystemInfo; -use deno_runtime::deno_fs::FileSystem; -use deno_runtime::deno_node::NodePermissions; -use node_resolver::errors::PackageFolderResolveError; -use node_resolver::errors::PackageNotFoundError; -use node_resolver::errors::ReferrerNotFoundError; - -use crate::args::LifecycleScriptsConfig; -use crate::cache::FastInsecureHasher; - -use super::super::resolution::NpmResolution; -use super::common::cache_packages; -use super::common::lifecycle_scripts::LifecycleScriptsStrategy; -use super::common::NpmPackageFsResolver; -use super::common::RegistryReadPermissionChecker; - -/// Resolves packages from the global npm cache. -#[derive(Debug)] -pub struct GlobalNpmPackageResolver { - cache: Arc, - tarball_cache: Arc, - resolution: Arc, - system_info: NpmSystemInfo, - registry_read_permission_checker: RegistryReadPermissionChecker, - lifecycle_scripts: LifecycleScriptsConfig, -} - -impl GlobalNpmPackageResolver { - pub fn new( - cache: Arc, - fs: Arc, - tarball_cache: Arc, - resolution: Arc, - system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, - ) -> Self { - Self { - registry_read_permission_checker: RegistryReadPermissionChecker::new( - fs, - cache.root_dir_path().to_path_buf(), - ), - cache, - tarball_cache, - resolution, - system_info, - lifecycle_scripts, - } - } -} - -#[async_trait(?Send)] -impl NpmPackageFsResolver for GlobalNpmPackageResolver { - fn node_modules_path(&self) -> Option<&Path> { - None - } - - fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { - let folder_id = self - .resolution - .resolve_pkg_cache_folder_id_from_pkg_id(id)?; - Some(self.cache.package_folder_for_id(&folder_id)) - } - - fn resolve_package_folder_from_package( - &self, - name: &str, - referrer: &ModuleSpecifier, - ) -> Result { - use deno_npm::resolution::PackageNotFoundFromReferrerError; - let Some(referrer_cache_folder_id) = self - .cache - .resolve_package_folder_id_from_specifier(referrer) - else { - return Err( - ReferrerNotFoundError { - referrer: referrer.clone(), - referrer_extra: None, - } - .into(), - ); - }; - let resolve_result = self - .resolution - .resolve_package_from_package(name, &referrer_cache_folder_id); - match resolve_result { - Ok(pkg) => match self.maybe_package_folder(&pkg.id) { - Some(folder) => Ok(folder), - None => Err( - PackageNotFoundError { - package_name: name.to_string(), - referrer: referrer.clone(), - referrer_extra: Some(format!( - "{} -> {}", - referrer_cache_folder_id, - pkg.id.as_serialized() - )), - } - .into(), - ), - }, - Err(err) => match *err { - PackageNotFoundFromReferrerError::Referrer(cache_folder_id) => Err( - ReferrerNotFoundError { - referrer: referrer.clone(), - referrer_extra: Some(cache_folder_id.to_string()), - } - .into(), - ), - PackageNotFoundFromReferrerError::Package { - name, - referrer: cache_folder_id_referrer, - } => Err( - PackageNotFoundError { - package_name: name, - referrer: referrer.clone(), - referrer_extra: Some(cache_folder_id_referrer.to_string()), - } - .into(), - ), - }, - } - } - - fn resolve_package_cache_folder_id_from_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result, AnyError> { - Ok( - self - .cache - .resolve_package_folder_id_from_specifier(specifier), - ) - } - - async fn cache_packages<'a>( - &self, - caching: PackageCaching<'a>, - ) -> Result<(), AnyError> { - let package_partitions = match caching { - PackageCaching::All => self - .resolution - .all_system_packages_partitioned(&self.system_info), - PackageCaching::Only(reqs) => self - .resolution - .subset(&reqs) - .all_system_packages_partitioned(&self.system_info), - }; - cache_packages(&package_partitions.packages, &self.tarball_cache).await?; - - // create the copy package folders - for copy in package_partitions.copy_packages { - self - .cache - .ensure_copy_package(©.get_package_cache_folder_id())?; - } - - let mut lifecycle_scripts = - super::common::lifecycle_scripts::LifecycleScripts::new( - &self.lifecycle_scripts, - GlobalLifecycleScripts::new(self, &self.lifecycle_scripts.root_dir), - ); - for package in &package_partitions.packages { - let package_folder = self.cache.package_folder_for_nv(&package.id.nv); - lifecycle_scripts.add(package, Cow::Borrowed(&package_folder)); - } - - lifecycle_scripts.warn_not_run_scripts()?; - - Ok(()) - } - - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError> { - self - .registry_read_permission_checker - .ensure_registry_read_permission(permissions, path) - } -} - -struct GlobalLifecycleScripts<'a> { - resolver: &'a GlobalNpmPackageResolver, - path_hash: u64, -} - -impl<'a> GlobalLifecycleScripts<'a> { - fn new(resolver: &'a GlobalNpmPackageResolver, root_dir: &Path) -> Self { - let mut hasher = FastInsecureHasher::new_without_deno_version(); - hasher.write(root_dir.to_string_lossy().as_bytes()); - let path_hash = hasher.finish(); - Self { - resolver, - path_hash, - } - } - - fn warned_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf { - self - .package_path(package) - .join(format!(".scripts-warned-{}", self.path_hash)) - } -} - -impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy - for GlobalLifecycleScripts<'a> -{ - fn can_run_scripts(&self) -> bool { - false - } - fn package_path(&self, package: &NpmResolutionPackage) -> PathBuf { - self.resolver.cache.package_folder_for_nv(&package.id.nv) - } - - fn warn_on_scripts_not_run( - &self, - packages: &[(&NpmResolutionPackage, PathBuf)], - ) -> std::result::Result<(), deno_core::anyhow::Error> { - log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall")); - for (package, _) in packages { - log::warn!("┠─ {}", colors::gray(format!("npm:{}", package.id.nv))); - } - log::warn!("┃"); - log::warn!( - "┠─ {}", - colors::italic("This may cause the packages to not work correctly.") - ); - log::warn!("┠─ {}", colors::italic("Lifecycle scripts are only supported when using a `node_modules` directory.")); - log::warn!( - "┠─ {}", - colors::italic("Enable it in your deno config file:") - ); - log::warn!("┖─ {}", colors::bold("\"nodeModulesDir\": \"auto\"")); - - for (package, _) in packages { - std::fs::write(self.warned_scripts_file(package), "")?; - } - Ok(()) - } - - fn did_run_scripts( - &self, - _package: &NpmResolutionPackage, - ) -> std::result::Result<(), deno_core::anyhow::Error> { - Ok(()) - } - - fn has_warned(&self, package: &NpmResolutionPackage) -> bool { - self.warned_scripts_file(package).exists() - } - - fn has_run(&self, _package: &NpmResolutionPackage) -> bool { - false - } -} diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs deleted file mode 100644 index 736270749f..0000000000 --- a/cli/npm/managed/resolvers/mod.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -mod common; -mod global; -mod local; - -use std::path::PathBuf; -use std::sync::Arc; - -use deno_npm::NpmSystemInfo; -use deno_runtime::deno_fs::FileSystem; - -use crate::args::LifecycleScriptsConfig; -use crate::args::NpmInstallDepsProvider; -use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; -use crate::util::progress_bar::ProgressBar; - -pub use self::common::NpmPackageFsResolver; - -use self::global::GlobalNpmPackageResolver; -use self::local::LocalNpmPackageResolver; - -use super::resolution::NpmResolution; - -#[allow(clippy::too_many_arguments)] -pub fn create_npm_fs_resolver( - fs: Arc, - npm_cache: Arc, - npm_install_deps_provider: &Arc, - progress_bar: &ProgressBar, - resolution: Arc, - tarball_cache: Arc, - maybe_node_modules_path: Option, - system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, -) -> Arc { - match maybe_node_modules_path { - Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new( - npm_cache, - fs, - npm_install_deps_provider.clone(), - progress_bar.clone(), - resolution, - tarball_cache, - node_modules_folder, - system_info, - lifecycle_scripts, - )), - None => Arc::new(GlobalNpmPackageResolver::new( - npm_cache, - fs, - tarball_cache, - resolution, - system_info, - lifecycle_scripts, - )), - } -} diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index b39e0a340d..1c12ce6c59 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -1,104 +1,79 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod byonm; +pub mod installer; mod managed; +mod permission_checker; -use std::borrow::Cow; -use std::path::Path; use std::sync::Arc; use dashmap::DashMap; -use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; -use deno_resolver::npm::ByonmInNpmPackageChecker; -use deno_resolver::npm::ByonmNpmResolver; -use deno_resolver::npm::CliNpmReqResolver; -use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::deno_fs::FileSystem; -use deno_runtime::deno_node::NodePermissions; -use deno_runtime::ops::process::NpmProcessStateProvider; +use deno_runtime::ops::process::NpmProcessStateProviderRc; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use http::HeaderName; use http::HeaderValue; -use managed::create_managed_in_npm_pkg_checker; -use node_resolver::InNpmPackageChecker; -use node_resolver::NpmPackageFolderResolver; -use crate::file_fetcher::FileFetcher; -use crate::http_util::HttpClientProvider; -use crate::util::fs::atomic_write_file_with_retries_and_fs; -use crate::util::fs::hard_link_dir_recursive; -use crate::util::fs::AtomicWriteFileFsAdapter; -use crate::util::progress_bar::ProgressBar; - -pub use self::byonm::CliByonmNpmResolver; pub use self::byonm::CliByonmNpmResolverCreateOptions; -pub use self::managed::CliManagedInNpmPkgCheckerCreateOptions; pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliNpmResolverManagedSnapshotOption; -pub use self::managed::ManagedCliNpmResolver; -pub use self::managed::PackageCaching; +pub use self::managed::NpmResolutionInitializer; +pub use self::managed::ResolveSnapshotError; +pub use self::permission_checker::NpmRegistryReadPermissionChecker; +pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode; +use crate::file_fetcher::CliFileFetcher; +use crate::http_util::HttpClientProvider; +use crate::sys::CliSys; +use crate::util::progress_bar::ProgressBar; -pub type CliNpmTarballCache = deno_npm_cache::TarballCache; -pub type CliNpmCache = deno_npm_cache::NpmCache; +pub type CliNpmTarballCache = + deno_npm_cache::TarballCache; +pub type CliNpmCache = deno_npm_cache::NpmCache; pub type CliNpmRegistryInfoProvider = - deno_npm_cache::RegistryInfoProvider; + deno_npm_cache::RegistryInfoProvider; +pub type CliNpmResolver = deno_resolver::npm::NpmResolver; +pub type CliManagedNpmResolver = deno_resolver::npm::ManagedNpmResolver; +pub type CliNpmResolverCreateOptions = + deno_resolver::npm::NpmResolverCreateOptions; #[derive(Debug)] -pub struct CliNpmCacheEnv { - fs: Arc, +pub struct CliNpmCacheHttpClient { http_client_provider: Arc, progress_bar: ProgressBar, } -impl CliNpmCacheEnv { +impl CliNpmCacheHttpClient { pub fn new( - fs: Arc, http_client_provider: Arc, progress_bar: ProgressBar, ) -> Self { Self { - fs, http_client_provider, progress_bar, } } } +pub fn create_npm_process_state_provider( + npm_resolver: &CliNpmResolver, +) -> NpmProcessStateProviderRc { + match npm_resolver { + CliNpmResolver::Byonm(byonm_npm_resolver) => Arc::new( + byonm::CliByonmNpmProcessStateProvider(byonm_npm_resolver.clone()), + ), + CliNpmResolver::Managed(managed_npm_resolver) => Arc::new( + managed::CliManagedNpmProcessStateProvider(managed_npm_resolver.clone()), + ), + } +} + #[async_trait::async_trait(?Send)] -impl deno_npm_cache::NpmCacheEnv for CliNpmCacheEnv { - fn exists(&self, path: &Path) -> bool { - self.fs.exists_sync(path) - } - - fn hard_link_dir_recursive( - &self, - from: &Path, - to: &Path, - ) -> Result<(), AnyError> { - // todo(dsherret): use self.fs here instead - hard_link_dir_recursive(from, to) - } - - fn atomic_write_file_with_retries( - &self, - file_path: &Path, - data: &[u8], - ) -> std::io::Result<()> { - atomic_write_file_with_retries_and_fs( - &AtomicWriteFileFsAdapter { - fs: self.fs.as_ref(), - write_mode: crate::cache::CACHE_PERM, - }, - file_path, - data, - ) - } - +impl deno_npm_cache::NpmCacheHttpClient for CliNpmCacheHttpClient { async fn download_with_retries_on_any_tokio_runtime( &self, url: Url, @@ -115,130 +90,40 @@ impl deno_npm_cache::NpmCacheEnv for CliNpmCacheEnv { .download_with_progress_and_retries(url, maybe_auth_header, &guard) .await .map_err(|err| { - use crate::http_util::DownloadError::*; - let status_code = match &err { + use crate::http_util::DownloadErrorKind::*; + let status_code = match err.as_kind() { Fetch { .. } | UrlParse { .. } | HttpParse { .. } | Json { .. } | ToStr { .. } - | NoRedirectHeader { .. } - | TooManyRedirects => None, + | RedirectHeaderParse { .. } + | TooManyRedirects + | NotFound + | Other(_) => None, BadResponse(bad_response_error) => { Some(bad_response_error.status_code) } }; deno_npm_cache::DownloadError { status_code, - error: err.into(), + error: JsErrorBox::from_err(err), } }) } } -pub enum CliNpmResolverCreateOptions { - Managed(CliManagedNpmResolverCreateOptions), - Byonm(CliByonmNpmResolverCreateOptions), -} - -pub async fn create_cli_npm_resolver_for_lsp( - options: CliNpmResolverCreateOptions, -) -> Arc { - use CliNpmResolverCreateOptions::*; - match options { - Managed(options) => { - managed::create_managed_npm_resolver_for_lsp(options).await - } - Byonm(options) => Arc::new(ByonmNpmResolver::new(options)), - } -} - -pub async fn create_cli_npm_resolver( - options: CliNpmResolverCreateOptions, -) -> Result, AnyError> { - use CliNpmResolverCreateOptions::*; - match options { - Managed(options) => managed::create_managed_npm_resolver(options).await, - Byonm(options) => Ok(Arc::new(ByonmNpmResolver::new(options))), - } -} - -pub enum CreateInNpmPkgCheckerOptions<'a> { - Managed(CliManagedInNpmPkgCheckerCreateOptions<'a>), - Byonm, -} - -pub fn create_in_npm_pkg_checker( - options: CreateInNpmPkgCheckerOptions, -) -> Arc { - match options { - CreateInNpmPkgCheckerOptions::Managed(options) => { - create_managed_in_npm_pkg_checker(options) - } - CreateInNpmPkgCheckerOptions::Byonm => Arc::new(ByonmInNpmPackageChecker), - } -} - -pub enum InnerCliNpmResolverRef<'a> { - Managed(&'a ManagedCliNpmResolver), - #[allow(dead_code)] - Byonm(&'a CliByonmNpmResolver), -} - -pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver { - fn into_npm_pkg_folder_resolver( - self: Arc, - ) -> Arc; - fn into_npm_req_resolver(self: Arc) -> Arc; - fn into_process_state_provider( - self: Arc, - ) -> Arc; - fn into_maybe_byonm(self: Arc) -> Option> { - None - } - - fn clone_snapshotted(&self) -> Arc; - - fn as_inner(&self) -> InnerCliNpmResolverRef; - - fn as_managed(&self) -> Option<&ManagedCliNpmResolver> { - match self.as_inner() { - InnerCliNpmResolverRef::Managed(inner) => Some(inner), - InnerCliNpmResolverRef::Byonm(_) => None, - } - } - - fn as_byonm(&self) -> Option<&CliByonmNpmResolver> { - match self.as_inner() { - InnerCliNpmResolverRef::Managed(_) => None, - InnerCliNpmResolverRef::Byonm(inner) => Some(inner), - } - } - - fn root_node_modules_path(&self) -> Option<&Path>; - - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError>; - - /// Returns a hash returning the state of the npm resolver - /// or `None` if the state currently can't be determined. - fn check_state_hash(&self) -> Option; -} - #[derive(Debug)] pub struct NpmFetchResolver { nv_by_req: DashMap>, info_by_name: DashMap>>, - file_fetcher: Arc, + file_fetcher: Arc, npmrc: Arc, } impl NpmFetchResolver { pub fn new( - file_fetcher: Arc, + file_fetcher: Arc, npmrc: Arc, ) -> Self { Self { diff --git a/cli/npm/permission_checker.rs b/cli/npm/permission_checker.rs new file mode 100644 index 0000000000..53031b5bd4 --- /dev/null +++ b/cli/npm/permission_checker.rs @@ -0,0 +1,121 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::collections::HashMap; +use std::io::ErrorKind; +use std::path::Path; +use std::path::PathBuf; + +use deno_core::parking_lot::Mutex; +use deno_error::JsErrorBox; +use deno_runtime::deno_node::NodePermissions; +use sys_traits::FsCanonicalize; + +use crate::sys::CliSys; + +#[derive(Debug)] +pub enum NpmRegistryReadPermissionCheckerMode { + Byonm, + Global(PathBuf), + Local(PathBuf), +} + +#[derive(Debug)] +pub struct NpmRegistryReadPermissionChecker { + sys: CliSys, + cache: Mutex>, + mode: NpmRegistryReadPermissionCheckerMode, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(inherit)] +#[error("failed canonicalizing '{path}'")] +struct EnsureRegistryReadPermissionError { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, +} + +impl NpmRegistryReadPermissionChecker { + pub fn new(sys: CliSys, mode: NpmRegistryReadPermissionCheckerMode) -> Self { + Self { + sys, + cache: Default::default(), + mode, + } + } + + #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] + pub fn ensure_read_permission<'a>( + &self, + permissions: &mut dyn NodePermissions, + path: &'a Path, + ) -> Result, JsErrorBox> { + if permissions.query_read_all() { + return Ok(Cow::Borrowed(path)); // skip permissions checks below + } + + match &self.mode { + NpmRegistryReadPermissionCheckerMode::Byonm => { + if path.components().any(|c| c.as_os_str() == "node_modules") { + Ok(Cow::Borrowed(path)) + } else { + permissions + .check_read_path(path) + .map_err(JsErrorBox::from_err) + } + } + NpmRegistryReadPermissionCheckerMode::Global(registry_path) + | NpmRegistryReadPermissionCheckerMode::Local(registry_path) => { + // allow reading if it's in the node_modules + let is_path_in_node_modules = path.starts_with(registry_path) + && path + .components() + .all(|c| !matches!(c, std::path::Component::ParentDir)); + + if is_path_in_node_modules { + let mut cache = self.cache.lock(); + let mut canonicalize = + |path: &Path| -> Result, JsErrorBox> { + match cache.get(path) { + Some(canon) => Ok(Some(canon.clone())), + None => match self.sys.fs_canonicalize(path) { + Ok(canon) => { + cache.insert(path.to_path_buf(), canon.clone()); + Ok(Some(canon)) + } + Err(e) => { + if e.kind() == ErrorKind::NotFound { + return Ok(None); + } + Err(JsErrorBox::from_err( + EnsureRegistryReadPermissionError { + path: path.to_path_buf(), + source: e, + }, + )) + } + }, + } + }; + if let Some(registry_path_canon) = canonicalize(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(registry_path) + { + return Ok(Cow::Borrowed(path)); + } + } + } + + permissions + .check_read_path(path) + .map_err(JsErrorBox::from_err) + } + } + } +} diff --git a/cli/ops/bench.rs b/cli/ops/bench.rs index a7c788a189..a06182fbd0 100644 --- a/cli/ops/bench.rs +++ b/cli/ops/bench.rs @@ -1,15 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; -use deno_core::error::generic_error; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::v8; use deno_core::ModuleSpecifier; use deno_core::OpState; +use deno_error::JsErrorBox; use deno_runtime::deno_permissions::ChildPermissionsArg; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_web::StartTime; @@ -78,7 +76,7 @@ pub fn op_pledge_test_permissions( pub fn op_restore_test_permissions( state: &mut OpState, #[serde] token: Uuid, -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if let Some(permissions_holder) = state.try_take::() { if token != permissions_holder.0 { panic!("restore test permissions token does not match the stored token"); @@ -88,7 +86,7 @@ pub fn op_restore_test_permissions( state.put::(permissions); Ok(()) } else { - Err(generic_error("no permissions to restore")) + Err(JsErrorBox::generic("no permissions to restore")) } } @@ -106,9 +104,9 @@ fn op_register_bench( only: bool, warmup: bool, #[buffer] ret_buf: &mut [u8], -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if ret_buf.len() != 4 { - return Err(type_error(format!( + return Err(JsErrorBox::type_error(format!( "Invalid ret_buf length: {}", ret_buf.len() ))); diff --git a/cli/ops/jupyter.rs b/cli/ops/jupyter.rs index 5bdf97e60f..3160f991bf 100644 --- a/cli/ops/jupyter.rs +++ b/cli/ops/jupyter.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // NOTE(bartlomieju): unfortunately it appears that clippy is broken // and can't allow a single line ignore for `await_holding_lock`. @@ -8,17 +8,16 @@ use std::cell::RefCell; use std::rc::Rc; use std::sync::Arc; -use jupyter_runtime::InputRequest; -use jupyter_runtime::JupyterMessage; -use jupyter_runtime::JupyterMessageContent; -use jupyter_runtime::KernelIoPubConnection; -use jupyter_runtime::StreamContent; - use deno_core::error::AnyError; use deno_core::op2; use deno_core::parking_lot::Mutex; use deno_core::serde_json; use deno_core::OpState; +use jupyter_runtime::InputRequest; +use jupyter_runtime::JupyterMessage; +use jupyter_runtime::JupyterMessageContent; +use jupyter_runtime::KernelIoPubConnection; +use jupyter_runtime::StreamContent; use tokio::sync::mpsc; use crate::tools::jupyter::server::StdinConnectionProxy; @@ -95,10 +94,12 @@ pub fn op_jupyter_input( None } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum JupyterBroadcastError { + #[class(inherit)] #[error(transparent)] SerdeJson(serde_json::Error), + #[class(generic)] #[error(transparent)] ZeroMq(AnyError), } diff --git a/cli/ops/lint.rs b/cli/ops/lint.rs new file mode 100644 index 0000000000..c13cb21a53 --- /dev/null +++ b/cli/ops/lint.rs @@ -0,0 +1,45 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_ast::MediaType; +use deno_ast::ModuleSpecifier; +use deno_ast::ParseDiagnostic; +use deno_core::op2; + +use crate::tools::lint; + +deno_core::extension!(deno_lint, ops = [op_lint_create_serialized_ast,],); + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum LintError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + ParseDiagnostic(#[from] ParseDiagnostic), + #[class(type)] + #[error("Failed to parse path as URL: {0}")] + PathParse(std::path::PathBuf), +} + +#[op2] +#[buffer] +fn op_lint_create_serialized_ast( + #[string] file_name: &str, + #[string] source: String, +) -> Result, LintError> { + let file_text = deno_ast::strip_bom(source); + let path = std::env::current_dir()?.join(file_name); + let specifier = ModuleSpecifier::from_file_path(&path) + .map_err(|_| LintError::PathParse(path))?; + let media_type = MediaType::from_specifier(&specifier); + let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { + specifier, + text: file_text.into(), + media_type, + capture_tokens: false, + scope_analysis: false, + maybe_syntax: None, + })?; + Ok(lint::serialize_ast_to_buffer(&parsed_source)) +} diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index 230d268ab4..7cee5bcfa1 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -1,5 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod bench; pub mod jupyter; +pub mod lint; pub mod testing; diff --git a/cli/ops/testing.rs b/cli/ops/testing.rs index 3c6936971a..c00ab949be 100644 --- a/cli/ops/testing.rs +++ b/cli/ops/testing.rs @@ -1,4 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; + +use deno_core::op2; +use deno_core::v8; +use deno_core::ModuleSpecifier; +use deno_core::OpState; +use deno_error::JsErrorBox; +use deno_runtime::deno_permissions::ChildPermissionsArg; +use deno_runtime::deno_permissions::PermissionsContainer; +use uuid::Uuid; use crate::tools::test::TestContainer; use crate::tools::test::TestDescription; @@ -9,19 +21,6 @@ use crate::tools::test::TestLocation; use crate::tools::test::TestStepDescription; use crate::tools::test::TestStepResult; -use deno_core::error::generic_error; -use deno_core::error::type_error; -use deno_core::error::AnyError; -use deno_core::op2; -use deno_core::v8; -use deno_core::ModuleSpecifier; -use deno_core::OpState; -use deno_runtime::deno_permissions::ChildPermissionsArg; -use deno_runtime::deno_permissions::PermissionsContainer; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use uuid::Uuid; - deno_core::extension!(deno_test, ops = [ op_pledge_test_permissions, @@ -72,7 +71,7 @@ pub fn op_pledge_test_permissions( pub fn op_restore_test_permissions( state: &mut OpState, #[serde] token: Uuid, -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if let Some(permissions_holder) = state.try_take::() { if token != permissions_holder.0 { panic!("restore test permissions token does not match the stored token"); @@ -82,7 +81,7 @@ pub fn op_restore_test_permissions( state.put::(permissions); Ok(()) } else { - Err(generic_error("no permissions to restore")) + Err(JsErrorBox::generic("no permissions to restore")) } } @@ -102,9 +101,9 @@ fn op_register_test( #[smi] line_number: u32, #[smi] column_number: u32, #[buffer] ret_buf: &mut [u8], -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if ret_buf.len() != 4 { - return Err(type_error(format!( + return Err(JsErrorBox::type_error(format!( "Invalid ret_buf length: {}", ret_buf.len() ))); diff --git a/cli/resolver.rs b/cli/resolver.rs index f5c3f68f36..5677767fdd 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -1,57 +1,66 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::sync::Arc; use async_trait::async_trait; -use dashmap::DashMap; use dashmap::DashSet; use deno_ast::MediaType; use deno_config::workspace::MappedResolutionDiagnostic; use deno_config::workspace::MappedResolutionError; -use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::url::Url; use deno_core::ModuleSourceCode; use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; use deno_graph::source::ResolveError; use deno_graph::source::UnknownBuiltInNodeModuleError; use deno_graph::NpmLoadError; use deno_graph::NpmResolvePkgReqsResult; use deno_npm::resolution::NpmResolutionError; +use deno_resolver::npm::DenoInNpmPackageChecker; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::sloppy_imports::SloppyImportsResolver; use deno_runtime::colors; use deno_runtime::deno_fs; -use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_node::is_builtin_node_module; -use deno_runtime::deno_node::DenoFsNodeResolverEnv; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_semver::package::PackageReq; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use thiserror::Error; use crate::args::NpmCachingStrategy; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::node::CliNodeCodeTranslator; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; -use crate::npm::InnerCliNpmResolverRef; +use crate::sys::CliSys; use crate::util::sync::AtomicFlag; use crate::util::text_encoding::from_utf8_lossy_cow; -pub type CjsTracker = deno_resolver::cjs::CjsTracker; -pub type IsCjsResolver = - deno_resolver::cjs::IsCjsResolver; +pub type CliCjsTracker = + deno_resolver::cjs::CjsTracker; +pub type CliIsCjsResolver = + deno_resolver::cjs::IsCjsResolver; +pub type CliSloppyImportsCachedFs = SloppyImportsCachedFs; pub type CliSloppyImportsResolver = - SloppyImportsResolver; + SloppyImportsResolver; pub type CliDenoResolver = deno_resolver::DenoResolver< - CliDenoResolverFs, - DenoFsNodeResolverEnv, - SloppyImportsCachedFs, + DenoInNpmPackageChecker, + RealIsBuiltInNodeModuleChecker, + CliNpmResolver, + CliSloppyImportsCachedFs, + CliSys, +>; +pub type CliNpmReqResolver = deno_resolver::npm::NpmReqResolver< + DenoInNpmPackageChecker, + RealIsBuiltInNodeModuleChecker, + CliNpmResolver, + CliSys, >; -pub type CliNpmReqResolver = - deno_resolver::npm::NpmReqResolver; pub struct ModuleCodeStringSource { pub code: ModuleSourceCode, @@ -59,54 +68,8 @@ pub struct ModuleCodeStringSource { pub media_type: MediaType, } -#[derive(Debug, Clone)] -pub struct CliDenoResolverFs(pub Arc); - -impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs { - fn read_to_string_lossy( - &self, - path: &Path, - ) -> std::io::Result> { - self - .0 - .read_text_file_lossy_sync(path, None) - .map_err(|e| e.into_io_error()) - } - - fn realpath_sync(&self, path: &Path) -> std::io::Result { - self.0.realpath_sync(path).map_err(|e| e.into_io_error()) - } - - fn exists_sync(&self, path: &Path) -> bool { - self.0.exists_sync(path) - } - - fn is_dir_sync(&self, path: &Path) -> bool { - self.0.is_dir_sync(path) - } - - fn read_dir_sync( - &self, - dir_path: &Path, - ) -> std::io::Result> { - self - .0 - .read_dir_sync(dir_path) - .map(|entries| { - entries - .into_iter() - .map(|e| deno_resolver::fs::DirEntry { - name: e.name, - is_file: e.is_file, - is_directory: e.is_directory, - }) - .collect::>() - }) - .map_err(|err| err.into_io_error()) - } -} - -#[derive(Debug, Error)] +#[derive(Debug, Error, deno_error::JsError)] +#[class(type)] #[error("{media_type} files are not supported in npm packages: {specifier}")] pub struct NotSupportedKindInNpmError { pub media_type: MediaType, @@ -116,14 +79,14 @@ pub struct NotSupportedKindInNpmError { // todo(dsherret): move to module_loader.rs (it seems to be here due to use in standalone) #[derive(Clone)] pub struct NpmModuleLoader { - cjs_tracker: Arc, + cjs_tracker: Arc, fs: Arc, node_code_translator: Arc, } impl NpmModuleLoader { pub fn new( - cjs_tracker: Arc, + cjs_tracker: Arc, fs: Arc, node_code_translator: Arc, ) -> Self { @@ -211,48 +174,30 @@ impl NpmModuleLoader { } } -pub struct CliResolverOptions { - pub deno_resolver: Arc, - pub npm_resolver: Option>, - pub bare_node_builtins_enabled: bool, -} +#[derive(Debug, Default)] +pub struct FoundPackageJsonDepFlag(AtomicFlag); /// A resolver that takes care of resolution, taking into account loaded /// import map, JSX settings. #[derive(Debug)] pub struct CliResolver { deno_resolver: Arc, - npm_resolver: Option>, - found_package_json_dep_flag: AtomicFlag, - bare_node_builtins_enabled: bool, + found_package_json_dep_flag: Arc, warned_pkgs: DashSet, } impl CliResolver { - pub fn new(options: CliResolverOptions) -> Self { + pub fn new( + deno_resolver: Arc, + found_package_json_dep_flag: Arc, + ) -> Self { Self { - deno_resolver: options.deno_resolver, - npm_resolver: options.npm_resolver, - found_package_json_dep_flag: Default::default(), - bare_node_builtins_enabled: options.bare_node_builtins_enabled, + deno_resolver, + found_package_json_dep_flag, warned_pkgs: Default::default(), } } - // todo(dsherret): move this off CliResolver as CliResolver is acting - // like a factory by doing this (it's beyond its responsibility) - pub fn create_graph_npm_resolver( - &self, - npm_caching: NpmCachingStrategy, - ) -> WorkerCliNpmGraphResolver { - WorkerCliNpmGraphResolver { - npm_resolver: self.npm_resolver.as_ref(), - found_package_json_dep_flag: &self.found_package_json_dep_flag, - bare_node_builtins_enabled: self.bare_node_builtins_enabled, - npm_caching, - } - } - pub fn resolve( &self, raw_specifier: &str, @@ -270,15 +215,17 @@ impl CliResolver { ) => match mapped_resolution_error { MappedResolutionError::Specifier(e) => ResolveError::Specifier(e), // deno_graph checks specifically for an ImportMapError - MappedResolutionError::ImportMap(e) => ResolveError::Other(e.into()), - err => ResolveError::Other(err.into()), + MappedResolutionError::ImportMap(e) => ResolveError::ImportMap(e), + MappedResolutionError::Workspace(e) => { + ResolveError::Other(JsErrorBox::from_err(e)) + } }, - err => ResolveError::Other(err.into()), + err => ResolveError::Other(JsErrorBox::from_err(err)), })?; if resolution.found_package_json_dep { // mark that we need to do an "npm install" later - self.found_package_json_dep_flag.raise(); + self.found_package_json_dep_flag.0.raise(); } if let Some(diagnostic) = resolution.maybe_diagnostic { @@ -305,15 +252,31 @@ impl CliResolver { } #[derive(Debug)] -pub struct WorkerCliNpmGraphResolver<'a> { - npm_resolver: Option<&'a Arc>, - found_package_json_dep_flag: &'a AtomicFlag, +pub struct CliNpmGraphResolver { + npm_installer: Option>, + found_package_json_dep_flag: Arc, bare_node_builtins_enabled: bool, npm_caching: NpmCachingStrategy, } +impl CliNpmGraphResolver { + pub fn new( + npm_installer: Option>, + found_package_json_dep_flag: Arc, + bare_node_builtins_enabled: bool, + npm_caching: NpmCachingStrategy, + ) -> Self { + Self { + npm_installer, + found_package_json_dep_flag, + bare_node_builtins_enabled, + npm_caching, + } + } +} + #[async_trait(?Send)] -impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { +impl deno_graph::source::NpmResolver for CliNpmGraphResolver { fn resolve_builtin_node_module( &self, specifier: &ModuleSpecifier, @@ -343,17 +306,12 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { } fn load_and_cache_npm_package_info(&self, package_name: &str) { - match self.npm_resolver { - Some(npm_resolver) if npm_resolver.as_managed().is_some() => { - let npm_resolver = npm_resolver.clone(); - let package_name = package_name.to_string(); - deno_core::unsync::spawn(async move { - if let Some(managed) = npm_resolver.as_managed() { - let _ignore = managed.cache_package_info(&package_name).await; - } - }); - } - _ => {} + if let Some(npm_installer) = &self.npm_installer { + let npm_installer = npm_installer.clone(); + let package_name = package_name.to_string(); + deno_core::unsync::spawn(async move { + let _ignore = npm_installer.cache_package_info(&package_name).await; + }); } } @@ -361,17 +319,11 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { &self, package_reqs: &[PackageReq], ) -> NpmResolvePkgReqsResult { - match &self.npm_resolver { - Some(npm_resolver) => { - let npm_resolver = match npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(npm_resolver) => npm_resolver, - // if we are using byonm, then this should never be called because - // we don't use deno_graph's npm resolution in this case - InnerCliNpmResolverRef::Byonm(_) => unreachable!(), - }; - - let top_level_result = if self.found_package_json_dep_flag.is_raised() { - npm_resolver + match &self.npm_installer { + Some(npm_installer) => { + let top_level_result = if self.found_package_json_dep_flag.0.is_raised() + { + npm_installer .ensure_top_level_package_json_install() .await .map(|_| ()) @@ -379,15 +331,13 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { Ok(()) }; - let result = npm_resolver + let result = npm_installer .add_package_reqs_raw( package_reqs, match self.npm_caching { - NpmCachingStrategy::Eager => { - Some(crate::npm::PackageCaching::All) - } + NpmCachingStrategy::Eager => Some(PackageCaching::All), NpmCachingStrategy::Lazy => { - Some(crate::npm::PackageCaching::Only(package_reqs.into())) + Some(PackageCaching::Only(package_reqs.into())) } NpmCachingStrategy::Manual => None, }, @@ -401,26 +351,28 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { .map(|r| { r.map_err(|err| match err { NpmResolutionError::Registry(e) => { - NpmLoadError::RegistryInfo(Arc::new(e.into())) + NpmLoadError::RegistryInfo(Arc::new(e)) } NpmResolutionError::Resolution(e) => { - NpmLoadError::PackageReqResolution(Arc::new(e.into())) + NpmLoadError::PackageReqResolution(Arc::new(e)) } NpmResolutionError::DependencyEntry(e) => { - NpmLoadError::PackageReqResolution(Arc::new(e.into())) + NpmLoadError::PackageReqResolution(Arc::new(e)) } }) }) .collect(), dep_graph_result: match top_level_result { - Ok(()) => result.dependencies_result.map_err(Arc::new), + Ok(()) => result + .dependencies_result + .map_err(|e| Arc::new(e) as Arc), Err(err) => Err(Arc::new(err)), }, } } None => { - let err = Arc::new(anyhow!( - "npm specifiers were requested; but --no-npm is specified" + let err = Arc::new(JsErrorBox::generic( + "npm specifiers were requested; but --no-npm is specified", )); NpmResolvePkgReqsResult { results: package_reqs @@ -437,57 +389,3 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { self.bare_node_builtins_enabled } } - -#[derive(Debug)] -pub struct SloppyImportsCachedFs { - fs: Arc, - cache: Option< - DashMap< - PathBuf, - Option, - >, - >, -} - -impl SloppyImportsCachedFs { - pub fn new(fs: Arc) -> Self { - Self { - fs, - cache: Some(Default::default()), - } - } - - pub fn new_without_stat_cache(fs: Arc) -> Self { - Self { fs, cache: None } - } -} - -impl deno_resolver::sloppy_imports::SloppyImportResolverFs - for SloppyImportsCachedFs -{ - fn stat_sync( - &self, - path: &Path, - ) -> Option { - if let Some(cache) = &self.cache { - if let Some(entry) = cache.get(path) { - return *entry; - } - } - - let entry = self.fs.stat_sync(path).ok().and_then(|stat| { - if stat.is_file { - Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::File) - } else if stat.is_directory { - Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::Dir) - } else { - None - } - }); - - if let Some(cache) = &self.cache { - cache.insert(path.to_owned(), entry); - } - entry - } -} diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json index 1e3abb2c0d..d644072f4c 100644 --- a/cli/schemas/config-file.v1.json +++ b/cli/schemas/config-file.v1.json @@ -446,7 +446,6 @@ }, "command": { "type": "string", - "required": true, "description": "The task to execute" }, "dependencies": { diff --git a/cli/shared.rs b/cli/shared.rs index 808aff707b..6a28473edd 100644 --- a/cli/shared.rs +++ b/cli/shared.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// This module is shared between build script and the binaries. Use it sparsely. use deno_core::anyhow::bail; diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 85a22cf837..c9b57f3d6b 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::BTreeMap; @@ -37,7 +37,6 @@ use deno_core::futures::AsyncReadExt; use deno_core::futures::AsyncSeekExt; use deno_core::serde_json; use deno_core::url::Url; -use deno_graph::source::RealFileSystem; use deno_graph::ModuleGraph; use deno_npm::resolution::SerializedNpmResolutionSnapshot; use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage; @@ -52,6 +51,7 @@ use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_io::fs::FsError; use deno_runtime::deno_node::PackageJson; +use deno_runtime::deno_permissions::PermissionsOptions; use deno_semver::npm::NpmVersionReqParseError; use deno_semver::package::PackageReq; use deno_semver::Version; @@ -62,6 +62,24 @@ use log::Level; use serde::Deserialize; use serde::Serialize; +use super::file_system::DenoCompileFileSystem; +use super::serialization::deserialize_binary_data_section; +use super::serialization::serialize_binary_data_section; +use super::serialization::DenoCompileModuleData; +use super::serialization::DeserializedDataSection; +use super::serialization::RemoteModulesStore; +use super::serialization::RemoteModulesStoreBuilder; +use super::serialization::SourceMapStore; +use super::virtual_fs::output_vfs; +use super::virtual_fs::BuiltVfs; +use super::virtual_fs::FileBackedVfs; +use super::virtual_fs::FileSystemCaseSensitivity; +use super::virtual_fs::VfsBuilder; +use super::virtual_fs::VfsFileSubDataKind; +use super::virtual_fs::VfsRoot; +use super::virtual_fs::VirtualDirectory; +use super::virtual_fs::VirtualDirectoryEntries; +use super::virtual_fs::WindowsSystemRootablePath; use crate::args::CaData; use crate::args::CliOptions; use crate::args::CompileFlags; @@ -71,11 +89,10 @@ use crate::args::UnstableConfig; use crate::cache::DenoDir; use crate::cache::FastInsecureHasher; use crate::emit::Emitter; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::http_util::HttpClientProvider; use crate::npm::CliNpmResolver; -use crate::npm::InnerCliNpmResolverRef; -use crate::resolver::CjsTracker; +use crate::resolver::CliCjsTracker; use crate::shared::ReleaseChannel; use crate::standalone::virtual_fs::VfsEntry; use crate::util::archive; @@ -84,22 +101,6 @@ use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; -use super::file_system::DenoCompileFileSystem; -use super::serialization::deserialize_binary_data_section; -use super::serialization::serialize_binary_data_section; -use super::serialization::DenoCompileModuleData; -use super::serialization::DeserializedDataSection; -use super::serialization::RemoteModulesStore; -use super::serialization::RemoteModulesStoreBuilder; -use super::virtual_fs::output_vfs; -use super::virtual_fs::BuiltVfs; -use super::virtual_fs::FileBackedVfs; -use super::virtual_fs::VfsBuilder; -use super::virtual_fs::VfsFileSubDataKind; -use super::virtual_fs::VfsRoot; -use super::virtual_fs::VirtualDirectory; -use super::virtual_fs::WindowsSystemRootablePath; - pub static DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME: &str = ".deno_compile_node_modules"; @@ -188,7 +189,7 @@ pub struct Metadata { pub argv: Vec, pub seed: Option, pub code_cache_key: Option, - pub permissions: PermissionFlags, + pub permissions: PermissionsOptions, pub location: Option, pub v8_flags: Vec, pub log_level: Option, @@ -201,20 +202,28 @@ pub struct Metadata { pub node_modules: Option, pub unstable_config: UnstableConfig, pub otel_config: OtelConfig, + pub vfs_case_sensitivity: FileSystemCaseSensitivity, } +#[allow(clippy::too_many_arguments)] fn write_binary_bytes( mut file_writer: File, original_bin: Vec, metadata: &Metadata, npm_snapshot: Option, remote_modules: &RemoteModulesStoreBuilder, + source_map_store: &SourceMapStore, vfs: &BuiltVfs, compile_flags: &CompileFlags, ) -> Result<(), AnyError> { - let data_section_bytes = - serialize_binary_data_section(metadata, npm_snapshot, remote_modules, vfs) - .context("Serializing binary data section.")?; + let data_section_bytes = serialize_binary_data_section( + metadata, + npm_snapshot, + remote_modules, + source_map_store, + vfs, + ) + .context("Serializing binary data section.")?; let target = compile_flags.resolve_target(); if target.contains("linux") { @@ -251,11 +260,11 @@ pub fn is_standalone_binary(exe_path: &Path) -> bool { } pub struct StandaloneData { - pub fs: Arc, pub metadata: Metadata, pub modules: StandaloneModules, pub npm_snapshot: Option, pub root_path: PathBuf, + pub source_maps: SourceMapStore, pub vfs: Arc, } @@ -283,13 +292,12 @@ impl StandaloneModules { pub fn read<'a>( &'a self, specifier: &'a ModuleSpecifier, + kind: VfsFileSubDataKind, ) -> Result>, AnyError> { if specifier.scheme() == "file" { let path = deno_path_util::url_to_file_path(specifier)?; let bytes = match self.vfs.file_entry(&path) { - Ok(entry) => self - .vfs - .read_file_all(entry, VfsFileSubDataKind::ModuleGraph)?, + Ok(entry) => self.vfs.read_file_all(entry, kind)?, Err(err) if err.kind() == ErrorKind::NotFound => { match RealFs.read_file_sync(&path, None) { Ok(bytes) => bytes, @@ -307,7 +315,18 @@ impl StandaloneModules { data: bytes, })) } else { - self.remote_modules.read(specifier) + self.remote_modules.read(specifier).map(|maybe_entry| { + maybe_entry.map(|entry| DenoCompileModuleData { + media_type: entry.media_type, + specifier: entry.specifier, + data: match kind { + VfsFileSubDataKind::Raw => entry.data, + VfsFileSubDataKind::ModuleGraph => { + entry.transpiled_data.unwrap_or(entry.data) + } + }, + }) + }) } } } @@ -328,7 +347,8 @@ pub fn extract_standalone( mut metadata, npm_snapshot, remote_modules, - mut vfs_dir, + source_maps, + vfs_root_entries, vfs_files_data, } = match deserialize_binary_data_section(data)? { Some(data_section) => data_section, @@ -351,20 +371,22 @@ pub fn extract_standalone( metadata.argv.push(arg.into_string().unwrap()); } let vfs = { - // align the name of the directory with the root dir - vfs_dir.name = root_path.file_name().unwrap().to_string_lossy().to_string(); - let fs_root = VfsRoot { - dir: vfs_dir, + dir: VirtualDirectory { + // align the name of the directory with the root dir + name: root_path.file_name().unwrap().to_string_lossy().to_string(), + entries: vfs_root_entries, + }, root_path: root_path.clone(), start_file_offset: 0, }; - Arc::new(FileBackedVfs::new(Cow::Borrowed(vfs_files_data), fs_root)) + Arc::new(FileBackedVfs::new( + Cow::Borrowed(vfs_files_data), + fs_root, + metadata.vfs_case_sensitivity, + )) }; - let fs: Arc = - Arc::new(DenoCompileFileSystem::new(vfs.clone())); Ok(Some(StandaloneData { - fs, metadata, modules: StandaloneModules { remote_modules, @@ -372,6 +394,7 @@ pub fn extract_standalone( }, npm_snapshot, root_path, + source_maps, vfs, })) } @@ -386,13 +409,13 @@ pub struct WriteBinOptions<'a> { } pub struct DenoCompileBinaryWriter<'a> { - cjs_tracker: &'a CjsTracker, + cjs_tracker: &'a CliCjsTracker, cli_options: &'a CliOptions, deno_dir: &'a DenoDir, emitter: &'a Emitter, - file_fetcher: &'a FileFetcher, + file_fetcher: &'a CliFileFetcher, http_client_provider: &'a HttpClientProvider, - npm_resolver: &'a dyn CliNpmResolver, + npm_resolver: &'a CliNpmResolver, workspace_resolver: &'a WorkspaceResolver, npm_system_info: NpmSystemInfo, } @@ -400,13 +423,13 @@ pub struct DenoCompileBinaryWriter<'a> { impl<'a> DenoCompileBinaryWriter<'a> { #[allow(clippy::too_many_arguments)] pub fn new( - cjs_tracker: &'a CjsTracker, + cjs_tracker: &'a CliCjsTracker, cli_options: &'a CliOptions, deno_dir: &'a DenoDir, emitter: &'a Emitter, - file_fetcher: &'a FileFetcher, + file_fetcher: &'a CliFileFetcher, http_client_provider: &'a HttpClientProvider, - npm_resolver: &'a dyn CliNpmResolver, + npm_resolver: &'a CliNpmResolver, workspace_resolver: &'a WorkspaceResolver, npm_system_info: NpmSystemInfo, ) -> Self { @@ -451,7 +474,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { ) } } - self.write_standalone_binary(options, original_binary).await + self.write_standalone_binary(options, original_binary) } async fn get_base_binary( @@ -554,7 +577,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { /// This functions creates a standalone deno binary by appending a bundle /// and magic trailer to the currently executing binary. #[allow(clippy::too_many_arguments)] - async fn write_standalone_binary( + fn write_standalone_binary( &self, options: WriteBinOptions<'_>, original_bin: Vec, @@ -575,10 +598,11 @@ impl<'a> DenoCompileBinaryWriter<'a> { None => None, }; let mut vfs = VfsBuilder::new(); - let npm_snapshot = match self.npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(managed) => { - let snapshot = - managed.serialized_valid_snapshot_for_system(&self.npm_system_info); + let npm_snapshot = match &self.npm_resolver { + CliNpmResolver::Managed(managed) => { + let snapshot = managed + .resolution() + .serialized_valid_snapshot_for_system(&self.npm_system_info); if !snapshot.as_serialized().packages.is_empty() { self.fill_npm_vfs(&mut vfs).context("Building npm vfs.")?; Some(snapshot) @@ -586,7 +610,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { None } } - InnerCliNpmResolverRef::Byonm(_) => { + CliNpmResolver::Byonm(_) => { self.fill_npm_vfs(&mut vfs)?; None } @@ -598,71 +622,81 @@ impl<'a> DenoCompileBinaryWriter<'a> { .with_context(|| format!("Including {}", path.display()))?; } let mut remote_modules_store = RemoteModulesStoreBuilder::default(); - let mut code_cache_key_hasher = if self.cli_options.code_cache_enabled() { - Some(FastInsecureHasher::new_deno_versioned()) - } else { - None - }; + let mut source_maps = Vec::with_capacity(graph.specifiers_count()); + // todo(dsherret): transpile in parallel for module in graph.modules() { if module.specifier().scheme() == "data" { continue; // don't store data urls as an entry as they're in the code } - if let Some(hasher) = &mut code_cache_key_hasher { - if let Some(source) = module.source() { - hasher.write(module.specifier().as_str().as_bytes()); - hasher.write(source.as_bytes()); - } - } - let (maybe_source, media_type) = match module { + let (maybe_original_source, maybe_transpiled, media_type) = match module { deno_graph::Module::Js(m) => { - let source = if m.media_type.is_emittable() { + let original_bytes = m.source.as_bytes().to_vec(); + let maybe_transpiled = if m.media_type.is_emittable() { let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script( &m.specifier, m.media_type, m.is_script, )?; let module_kind = ModuleKind::from_is_cjs(is_cjs); - let source = self - .emitter - .emit_parsed_source( + let (source, source_map) = + self.emitter.emit_parsed_source_for_deno_compile( &m.specifier, m.media_type, module_kind, &m.source, - ) - .await?; - source.into_bytes() + )?; + if source != m.source.as_ref() { + source_maps.push((&m.specifier, source_map)); + Some(source.into_bytes()) + } else { + None + } } else { - m.source.as_bytes().to_vec() + None }; - (Some(source), m.media_type) + (Some(original_bytes), maybe_transpiled, m.media_type) } deno_graph::Module::Json(m) => { - (Some(m.source.as_bytes().to_vec()), m.media_type) + (Some(m.source.as_bytes().to_vec()), None, m.media_type) } deno_graph::Module::Wasm(m) => { - (Some(m.source.to_vec()), MediaType::Wasm) + (Some(m.source.to_vec()), None, MediaType::Wasm) } deno_graph::Module::Npm(_) | deno_graph::Module::Node(_) - | deno_graph::Module::External(_) => (None, MediaType::Unknown), + | deno_graph::Module::External(_) => (None, None, MediaType::Unknown), }; - if module.specifier().scheme() == "file" { - let file_path = deno_path_util::url_to_file_path(module.specifier())?; - vfs - .add_file_with_data( - &file_path, - match maybe_source { - Some(source) => source, - None => RealFs.read_file_sync(&file_path, None)?.into_owned(), - }, - VfsFileSubDataKind::ModuleGraph, - ) - .with_context(|| { - format!("Failed adding '{}'", file_path.display()) - })?; - } else if let Some(source) = maybe_source { - remote_modules_store.add(module.specifier(), media_type, source); + if let Some(original_source) = maybe_original_source { + if module.specifier().scheme() == "file" { + let file_path = deno_path_util::url_to_file_path(module.specifier())?; + vfs + .add_file_with_data( + &file_path, + original_source, + VfsFileSubDataKind::Raw, + ) + .with_context(|| { + format!("Failed adding '{}'", file_path.display()) + })?; + if let Some(transpiled_source) = maybe_transpiled { + vfs + .add_file_with_data( + &file_path, + transpiled_source, + VfsFileSubDataKind::ModuleGraph, + ) + .with_context(|| { + format!("Failed adding '{}'", file_path.display()) + })?; + } + } else { + remote_modules_store.add( + module.specifier(), + media_type, + original_source, + maybe_transpiled, + ); + } } } remote_modules_store.add_redirects(&graph.redirects); @@ -695,8 +729,30 @@ impl<'a> DenoCompileBinaryWriter<'a> { None => StandaloneRelativeFileBaseUrl::WindowsSystemRoot, }; - let node_modules = match self.npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(_) => { + let code_cache_key = if self.cli_options.code_cache_enabled() { + let mut hasher = FastInsecureHasher::new_deno_versioned(); + for module in graph.modules() { + if let Some(source) = module.source() { + hasher + .write(root_dir_url.specifier_key(module.specifier()).as_bytes()); + hasher.write(source.as_bytes()); + } + } + Some(hasher.finish()) + } else { + None + }; + + let mut source_map_store = SourceMapStore::with_capacity(source_maps.len()); + for (specifier, source_map) in source_maps { + source_map_store.add( + Cow::Owned(root_dir_url.specifier_key(specifier).into_owned()), + Cow::Owned(source_map.into_bytes()), + ); + } + + let node_modules = match &self.npm_resolver { + CliNpmResolver::Managed(_) => { npm_snapshot.as_ref().map(|_| NodeModules::Managed { node_modules_dir: self.npm_resolver.root_node_modules_path().map( |path| { @@ -709,7 +765,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { ), }) } - InnerCliNpmResolverRef::Byonm(resolver) => Some(NodeModules::Byonm { + CliNpmResolver::Byonm(resolver) => Some(NodeModules::Byonm { root_node_modules_dir: resolver.root_node_modules_path().map( |node_modules_dir| { root_dir_url @@ -742,9 +798,9 @@ impl<'a> DenoCompileBinaryWriter<'a> { let metadata = Metadata { argv: compile_flags.args.clone(), seed: self.cli_options.seed(), - code_cache_key: code_cache_key_hasher.map(|h| h.finish()), + code_cache_key, location: self.cli_options.location_flag().clone(), - permissions: self.cli_options.permission_flags().clone(), + permissions: self.cli_options.permissions_options(), v8_flags: self.cli_options.v8_flags().clone(), unsafely_ignore_certificate_errors: self .cli_options @@ -801,6 +857,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { npm_lazy_caching: self.cli_options.unstable_npm_lazy_caching(), }, otel_config: self.cli_options.otel_config(), + vfs_case_sensitivity: vfs.case_sensitivity, }; write_binary_bytes( @@ -809,6 +866,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { &metadata, npm_snapshot.map(|s| s.into_serialized()), &remote_modules_store, + &source_map_store, &vfs, compile_flags, ) @@ -822,16 +880,17 @@ impl<'a> DenoCompileBinaryWriter<'a> { } } - match self.npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(npm_resolver) => { + match &self.npm_resolver { + CliNpmResolver::Managed(npm_resolver) => { if let Some(node_modules_path) = npm_resolver.root_node_modules_path() { maybe_warn_different_system(&self.npm_system_info); builder.add_dir_recursive(node_modules_path)?; Ok(()) } else { // we'll flatten to remove any custom registries later - let mut packages = - npm_resolver.all_system_packages(&self.npm_system_info); + let mut packages = npm_resolver + .resolution() + .all_system_packages(&self.npm_system_info); packages.sort_by(|a, b| a.id.cmp(&b.id)); // determinism for package in packages { let folder = @@ -841,7 +900,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { Ok(()) } } - InnerCliNpmResolverRef::Byonm(_) => { + CliNpmResolver::Byonm(_) => { maybe_warn_different_system(&self.npm_system_info); for pkg_json in self.cli_options.workspace().package_jsons() { builder.add_file_at_path(&pkg_json.path)?; @@ -884,8 +943,8 @@ impl<'a> DenoCompileBinaryWriter<'a> { &self, mut vfs: VfsBuilder, ) -> BuiltVfs { - match self.npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(npm_resolver) => { + match &self.npm_resolver { + CliNpmResolver::Managed(npm_resolver) => { if npm_resolver.root_node_modules_path().is_some() { return vfs.build(); } @@ -903,10 +962,10 @@ impl<'a> DenoCompileBinaryWriter<'a> { root_dir.name = DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME.to_string(); let mut new_entries = Vec::with_capacity(root_dir.entries.len()); let mut localhost_entries = IndexMap::new(); - for entry in std::mem::take(&mut root_dir.entries) { + for entry in root_dir.entries.take_inner() { match entry { - VfsEntry::Dir(dir) => { - for entry in dir.entries { + VfsEntry::Dir(mut dir) => { + for entry in dir.entries.take_inner() { log::debug!("Flattening {} into node_modules", entry.name()); if let Some(existing) = localhost_entries.insert(entry.name().to_string(), entry) @@ -925,22 +984,23 @@ impl<'a> DenoCompileBinaryWriter<'a> { } new_entries.push(VfsEntry::Dir(VirtualDirectory { name: "localhost".to_string(), - entries: localhost_entries.into_iter().map(|(_, v)| v).collect(), + entries: VirtualDirectoryEntries::new( + localhost_entries.into_iter().map(|(_, v)| v).collect(), + ), })); - // needs to be sorted by name - new_entries.sort_by(|a, b| a.name().cmp(b.name())); - root_dir.entries = new_entries; + root_dir.entries = VirtualDirectoryEntries::new(new_entries); // it's better to not expose the user's cache directory, so take it out // of there + let case_sensitivity = vfs.case_sensitivity(); let parent = global_cache_root_path.parent().unwrap(); let parent_dir = vfs.get_dir_mut(parent).unwrap(); let index = parent_dir .entries - .iter() - .position(|entry| { - entry.name() == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME - }) + .binary_search( + DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME, + case_sensitivity, + ) .unwrap(); let npm_global_cache_dir_entry = parent_dir.entries.remove(index); @@ -948,12 +1008,18 @@ impl<'a> DenoCompileBinaryWriter<'a> { // this is not as optimized as it could be let mut last_name = Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME); - for ancestor in parent.ancestors() { - let dir = vfs.get_dir_mut(ancestor).unwrap(); - if let Some(index) = dir - .entries - .iter() - .position(|entry| entry.name() == last_name) + for ancestor in + parent.ancestors().map(Some).chain(std::iter::once(None)) + { + let dir = if let Some(ancestor) = ancestor { + vfs.get_dir_mut(ancestor).unwrap() + } else if cfg!(windows) { + vfs.get_system_root_dir_mut() + } else { + break; + }; + if let Ok(index) = + dir.entries.binary_search(&last_name, case_sensitivity) { dir.entries.remove(index); } @@ -965,10 +1031,12 @@ impl<'a> DenoCompileBinaryWriter<'a> { // now build the vfs and add the global cache dir entry there let mut built_vfs = vfs.build(); - built_vfs.root.insert_entry(npm_global_cache_dir_entry); + built_vfs + .entries + .insert(npm_global_cache_dir_entry, case_sensitivity); built_vfs } - InnerCliNpmResolverRef::Byonm(_) => vfs.build(), + CliNpmResolver::Byonm(_) => vfs.build(), } } } diff --git a/cli/standalone/code_cache.rs b/cli/standalone/code_cache.rs index 9580b9b44e..de9ff2a141 100644 --- a/cli/standalone/code_cache.rs +++ b/cli/standalone/code_cache.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::BTreeMap; use std::collections::HashMap; @@ -15,11 +15,11 @@ use deno_core::anyhow::bail; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::unsync::sync::AtomicFlag; +use deno_path_util::get_atomic_path; use deno_runtime::code_cache::CodeCache; use deno_runtime::code_cache::CodeCacheType; use crate::cache::FastInsecureHasher; -use crate::util::path::get_atomic_file_path; use crate::worker::CliCodeCache; enum CodeCacheStrategy { @@ -189,7 +189,8 @@ impl FirstRunCodeCacheStrategy { cache_data: &HashMap, ) { let count = cache_data.len(); - let temp_file = get_atomic_file_path(&self.file_path); + let temp_file = + get_atomic_path(&sys_traits::impls::RealSys, &self.file_path); match serialize(&temp_file, self.cache_key, cache_data) { Ok(()) => { if let Err(err) = std::fs::rename(&temp_file, &self.file_path) { @@ -394,10 +395,11 @@ fn deserialize_with_reader( #[cfg(test)] mod test { + use std::fs::File; + use test_util::TempDir; use super::*; - use std::fs::File; #[test] fn serialize_deserialize() { diff --git a/cli/standalone/file_system.rs b/cli/standalone/file_system.rs index 48dc907570..b04db88c90 100644 --- a/cli/standalone/file_system.rs +++ b/cli/standalone/file_system.rs @@ -1,9 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; +use std::time::Duration; +use std::time::SystemTime; use deno_runtime::deno_fs::AccessCheckCb; use deno_runtime::deno_fs::FileSystem; @@ -15,8 +19,17 @@ use deno_runtime::deno_io::fs::File; use deno_runtime::deno_io::fs::FsError; use deno_runtime::deno_io::fs::FsResult; use deno_runtime::deno_io::fs::FsStat; +use sys_traits::boxed::BoxedFsDirEntry; +use sys_traits::boxed::BoxedFsMetadataValue; +use sys_traits::boxed::FsMetadataBoxed; +use sys_traits::boxed::FsReadDirBoxed; +use sys_traits::FsCopy; +use sys_traits::FsMetadata; use super::virtual_fs::FileBackedVfs; +use super::virtual_fs::FileBackedVfsDirEntry; +use super::virtual_fs::FileBackedVfsFile; +use super::virtual_fs::FileBackedVfsMetadata; use super::virtual_fs::VfsFileSubDataKind; #[derive(Debug, Clone)] @@ -35,24 +48,32 @@ impl DenoCompileFileSystem { } } - fn copy_to_real_path(&self, oldpath: &Path, newpath: &Path) -> FsResult<()> { + fn copy_to_real_path( + &self, + oldpath: &Path, + newpath: &Path, + ) -> std::io::Result { let old_file = self.0.file_entry(oldpath)?; let old_file_bytes = self.0.read_file_all(old_file, VfsFileSubDataKind::Raw)?; - RealFs.write_file_sync( - newpath, - OpenOptions { - read: false, - write: true, - create: true, - truncate: true, - append: false, - create_new: false, - mode: None, - }, - None, - &old_file_bytes, - ) + let len = old_file_bytes.len() as u64; + RealFs + .write_file_sync( + newpath, + OpenOptions { + read: false, + write: true, + create: true, + truncate: true, + append: false, + create_new: false, + mode: None, + }, + None, + &old_file_bytes, + ) + .map_err(|err| err.into_io_error())?; + Ok(len) } } @@ -82,7 +103,7 @@ impl FileSystem for DenoCompileFileSystem { access_check: Option, ) -> FsResult> { if self.0.is_path_within(path) { - Ok(self.0.open_file(path)?) + Ok(Rc::new(self.0.open_file(path)?)) } else { RealFs.open_sync(path, options, access_check) } @@ -94,7 +115,7 @@ impl FileSystem for DenoCompileFileSystem { access_check: Option>, ) -> FsResult> { if self.0.is_path_within(&path) { - Ok(self.0.open_file(&path)?) + Ok(Rc::new(self.0.open_file(&path)?)) } else { RealFs.open_async(path, options, access_check).await } @@ -179,7 +200,10 @@ impl FileSystem for DenoCompileFileSystem { fn copy_file_sync(&self, oldpath: &Path, newpath: &Path) -> FsResult<()> { self.error_if_in_vfs(newpath)?; if self.0.is_path_within(oldpath) { - self.copy_to_real_path(oldpath, newpath) + self + .copy_to_real_path(oldpath, newpath) + .map(|_| ()) + .map_err(FsError::Io) } else { RealFs.copy_file_sync(oldpath, newpath) } @@ -194,6 +218,8 @@ impl FileSystem for DenoCompileFileSystem { let fs = self.clone(); tokio::task::spawn_blocking(move || { fs.copy_to_real_path(&oldpath, &newpath) + .map(|_| ()) + .map_err(FsError::Io) }) .await? } else { @@ -214,14 +240,14 @@ impl FileSystem for DenoCompileFileSystem { fn stat_sync(&self, path: &Path) -> FsResult { if self.0.is_path_within(path) { - Ok(self.0.stat(path)?) + Ok(self.0.stat(path)?.as_fs_stat()) } else { RealFs.stat_sync(path) } } async fn stat_async(&self, path: PathBuf) -> FsResult { if self.0.is_path_within(&path) { - Ok(self.0.stat(&path)?) + Ok(self.0.stat(&path)?.as_fs_stat()) } else { RealFs.stat_async(path).await } @@ -229,14 +255,14 @@ impl FileSystem for DenoCompileFileSystem { fn lstat_sync(&self, path: &Path) -> FsResult { if self.0.is_path_within(path) { - Ok(self.0.lstat(path)?) + Ok(self.0.lstat(path)?.as_fs_stat()) } else { RealFs.lstat_sync(path) } } async fn lstat_async(&self, path: PathBuf) -> FsResult { if self.0.is_path_within(&path) { - Ok(self.0.lstat(&path)?) + Ok(self.0.lstat(&path)?.as_fs_stat()) } else { RealFs.lstat_async(path).await } @@ -397,3 +423,462 @@ impl FileSystem for DenoCompileFileSystem { .await } } + +impl sys_traits::BaseFsHardLink for DenoCompileFileSystem { + #[inline] + fn base_fs_hard_link(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + self.link_sync(src, dst).map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsRead for DenoCompileFileSystem { + #[inline] + fn base_fs_read(&self, path: &Path) -> std::io::Result> { + self + .read_file_sync(path, None) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::FsMetadataValue for FileBackedVfsMetadata { + fn file_type(&self) -> sys_traits::FileType { + self.file_type + } + + fn len(&self) -> u64 { + self.len + } + + fn accessed(&self) -> std::io::Result { + Err(not_supported("accessed time")) + } + + fn created(&self) -> std::io::Result { + Err(not_supported("created time")) + } + + fn changed(&self) -> std::io::Result { + Err(not_supported("changed time")) + } + + fn modified(&self) -> std::io::Result { + Err(not_supported("modified time")) + } + + fn dev(&self) -> std::io::Result { + Ok(0) + } + + fn ino(&self) -> std::io::Result { + Ok(0) + } + + fn mode(&self) -> std::io::Result { + Ok(0) + } + + fn nlink(&self) -> std::io::Result { + Ok(0) + } + + fn uid(&self) -> std::io::Result { + Ok(0) + } + + fn gid(&self) -> std::io::Result { + Ok(0) + } + + fn rdev(&self) -> std::io::Result { + Ok(0) + } + + fn blksize(&self) -> std::io::Result { + Ok(0) + } + + fn blocks(&self) -> std::io::Result { + Ok(0) + } + + fn is_block_device(&self) -> std::io::Result { + Ok(false) + } + + fn is_char_device(&self) -> std::io::Result { + Ok(false) + } + + fn is_fifo(&self) -> std::io::Result { + Ok(false) + } + + fn is_socket(&self) -> std::io::Result { + Ok(false) + } + + fn file_attributes(&self) -> std::io::Result { + Ok(0) + } +} + +fn not_supported(name: &str) -> std::io::Error { + std::io::Error::new( + ErrorKind::Unsupported, + format!( + "{} is not supported for an embedded deno compile file", + name + ), + ) +} + +impl sys_traits::FsDirEntry for FileBackedVfsDirEntry { + type Metadata = BoxedFsMetadataValue; + + fn file_name(&self) -> Cow { + Cow::Borrowed(self.metadata.name.as_ref()) + } + + fn file_type(&self) -> std::io::Result { + Ok(self.metadata.file_type) + } + + fn metadata(&self) -> std::io::Result { + Ok(BoxedFsMetadataValue(Box::new(self.metadata.clone()))) + } + + fn path(&self) -> Cow { + Cow::Owned(self.parent_path.join(&self.metadata.name)) + } +} + +impl sys_traits::BaseFsReadDir for DenoCompileFileSystem { + type ReadDirEntry = BoxedFsDirEntry; + + fn base_fs_read_dir( + &self, + path: &Path, + ) -> std::io::Result< + Box> + '_>, + > { + if self.0.is_path_within(path) { + let entries = self.0.read_dir_with_metadata(path)?; + Ok(Box::new( + entries.map(|entry| Ok(BoxedFsDirEntry::new(entry))), + )) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.fs_read_dir_boxed(path) + } + } +} + +impl sys_traits::BaseFsCanonicalize for DenoCompileFileSystem { + #[inline] + fn base_fs_canonicalize(&self, path: &Path) -> std::io::Result { + self.realpath_sync(path).map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsMetadata for DenoCompileFileSystem { + type Metadata = BoxedFsMetadataValue; + + #[inline] + fn base_fs_metadata(&self, path: &Path) -> std::io::Result { + if self.0.is_path_within(path) { + Ok(BoxedFsMetadataValue::new(self.0.stat(path)?)) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.fs_metadata_boxed(path) + } + } + + #[inline] + fn base_fs_symlink_metadata( + &self, + path: &Path, + ) -> std::io::Result { + if self.0.is_path_within(path) { + Ok(BoxedFsMetadataValue::new(self.0.lstat(path)?)) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.fs_symlink_metadata_boxed(path) + } + } +} + +impl sys_traits::BaseFsCopy for DenoCompileFileSystem { + #[inline] + fn base_fs_copy(&self, from: &Path, to: &Path) -> std::io::Result { + self + .error_if_in_vfs(to) + .map_err(|err| err.into_io_error())?; + if self.0.is_path_within(from) { + self.copy_to_real_path(from, to) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.fs_copy(from, to) + } + } +} + +impl sys_traits::BaseFsCloneFile for DenoCompileFileSystem { + fn base_fs_clone_file( + &self, + _from: &Path, + _to: &Path, + ) -> std::io::Result<()> { + // will cause a fallback in the code that uses this + Err(not_supported("cloning files")) + } +} + +impl sys_traits::BaseFsCreateDir for DenoCompileFileSystem { + #[inline] + fn base_fs_create_dir( + &self, + path: &Path, + options: &sys_traits::CreateDirOptions, + ) -> std::io::Result<()> { + self + .mkdir_sync(path, options.recursive, options.mode) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsRemoveFile for DenoCompileFileSystem { + #[inline] + fn base_fs_remove_file(&self, path: &Path) -> std::io::Result<()> { + self + .remove_sync(path, false) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsRename for DenoCompileFileSystem { + #[inline] + fn base_fs_rename(&self, from: &Path, to: &Path) -> std::io::Result<()> { + self + .rename_sync(from, to) + .map_err(|err| err.into_io_error()) + } +} + +pub enum FsFileAdapter { + Real(sys_traits::impls::RealFsFile), + Vfs(FileBackedVfsFile), +} + +impl sys_traits::FsFile for FsFileAdapter {} + +impl sys_traits::FsFileAsRaw for FsFileAdapter { + #[cfg(windows)] + fn fs_file_as_raw_handle(&self) -> Option { + match self { + Self::Real(file) => file.fs_file_as_raw_handle(), + Self::Vfs(_) => None, + } + } + + #[cfg(unix)] + fn fs_file_as_raw_fd(&self) -> Option { + match self { + Self::Real(file) => file.fs_file_as_raw_fd(), + Self::Vfs(_) => None, + } + } +} + +impl sys_traits::FsFileSyncData for FsFileAdapter { + fn fs_file_sync_data(&mut self) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_sync_data(), + Self::Vfs(_) => Ok(()), + } + } +} + +impl sys_traits::FsFileSyncAll for FsFileAdapter { + fn fs_file_sync_all(&mut self) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_sync_all(), + Self::Vfs(_) => Ok(()), + } + } +} + +impl sys_traits::FsFileSetPermissions for FsFileAdapter { + #[inline] + fn fs_file_set_permissions(&mut self, mode: u32) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_set_permissions(mode), + Self::Vfs(_) => Ok(()), + } + } +} + +impl std::io::Read for FsFileAdapter { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + match self { + Self::Real(file) => file.read(buf), + Self::Vfs(file) => file.read_to_buf(buf), + } + } +} + +impl std::io::Seek for FsFileAdapter { + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + match self { + Self::Real(file) => file.seek(pos), + Self::Vfs(file) => file.seek(pos), + } + } +} + +impl std::io::Write for FsFileAdapter { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + match self { + Self::Real(file) => file.write(buf), + Self::Vfs(_) => Err(not_supported("writing files")), + } + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + match self { + Self::Real(file) => file.flush(), + Self::Vfs(_) => Err(not_supported("writing files")), + } + } +} + +impl sys_traits::FsFileSetLen for FsFileAdapter { + #[inline] + fn fs_file_set_len(&mut self, len: u64) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_set_len(len), + Self::Vfs(_) => Err(not_supported("setting file length")), + } + } +} + +impl sys_traits::FsFileSetTimes for FsFileAdapter { + fn fs_file_set_times( + &mut self, + times: sys_traits::FsFileTimes, + ) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_set_times(times), + Self::Vfs(_) => Err(not_supported("setting file times")), + } + } +} + +impl sys_traits::FsFileLock for FsFileAdapter { + fn fs_file_lock( + &mut self, + mode: sys_traits::FsFileLockMode, + ) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_lock(mode), + Self::Vfs(_) => Err(not_supported("locking files")), + } + } + + fn fs_file_try_lock( + &mut self, + mode: sys_traits::FsFileLockMode, + ) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_try_lock(mode), + Self::Vfs(_) => Err(not_supported("locking files")), + } + } + + fn fs_file_unlock(&mut self) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_unlock(), + Self::Vfs(_) => Err(not_supported("unlocking files")), + } + } +} + +impl sys_traits::FsFileIsTerminal for FsFileAdapter { + #[inline] + fn fs_file_is_terminal(&self) -> bool { + match self { + Self::Real(file) => file.fs_file_is_terminal(), + Self::Vfs(_) => false, + } + } +} + +impl sys_traits::BaseFsOpen for DenoCompileFileSystem { + type File = FsFileAdapter; + + fn base_fs_open( + &self, + path: &Path, + options: &sys_traits::OpenOptions, + ) -> std::io::Result { + if self.0.is_path_within(path) { + Ok(FsFileAdapter::Vfs(self.0.open_file(path)?)) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + Ok(FsFileAdapter::Real( + sys_traits::impls::RealSys.base_fs_open(path, options)?, + )) + } + } +} + +impl sys_traits::BaseFsSymlinkDir for DenoCompileFileSystem { + fn base_fs_symlink_dir(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + self + .symlink_sync(src, dst, Some(FsFileType::Directory)) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::SystemRandom for DenoCompileFileSystem { + #[inline] + fn sys_random(&self, buf: &mut [u8]) -> std::io::Result<()> { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.sys_random(buf) + } +} + +impl sys_traits::SystemTimeNow for DenoCompileFileSystem { + #[inline] + fn sys_time_now(&self) -> SystemTime { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.sys_time_now() + } +} + +impl sys_traits::ThreadSleep for DenoCompileFileSystem { + #[inline] + fn thread_sleep(&self, dur: Duration) { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.thread_sleep(dur) + } +} + +impl sys_traits::EnvCurrentDir for DenoCompileFileSystem { + fn env_current_dir(&self) -> std::io::Result { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.env_current_dir() + } +} + +impl sys_traits::BaseEnvVar for DenoCompileFileSystem { + fn base_env_var_os( + &self, + key: &std::ffi::OsStr, + ) -> Option { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.base_env_var_os(key) + } +} diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 22e0b6d115..876c194ed1 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -1,23 +1,28 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Allow unused code warnings because we share // code between the two bin targets. #![allow(dead_code)] #![allow(unused_imports)] +use std::borrow::Cow; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + use binary::StandaloneData; use binary::StandaloneModules; use code_cache::DenoCompileCodeCache; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::npm::NpmCacheDir; use deno_config::workspace::MappedResolution; use deno_config::workspace::MappedResolutionError; use deno_config::workspace::ResolverWorkspaceJsrPackage; use deno_config::workspace::WorkspaceResolver; use deno_core::anyhow::Context; -use deno_core::error::generic_error; -use deno_core::error::type_error; use deno_core::error::AnyError; +use deno_core::error::ModuleLoaderError; use deno_core::futures::future::LocalBoxFuture; use deno_core::futures::FutureExt; use deno_core::v8_set_flags; @@ -30,15 +35,23 @@ use deno_core::ModuleType; use deno_core::RequestedModuleType; use deno_core::ResolutionKind; use deno_core::SourceCodeCacheInfo; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; +use deno_npm::resolution::NpmResolutionSnapshot; use deno_package_json::PackageJsonDepValue; use deno_resolver::cjs::IsCjsResolutionMode; +use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_resolver::npm::ByonmNpmResolverCreateOptions; +use deno_resolver::npm::CreateInNpmPkgCheckerOptions; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::npm::NpmReqResolverOptions; use deno_runtime::deno_fs; +use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::NodeRequireLoader; use deno_runtime::deno_node::NodeResolver; -use deno_runtime::deno_node::PackageJsonResolver; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_tls::rustls::RootCertStore; @@ -54,9 +67,7 @@ use node_resolver::errors::ClosestPkgJsonError; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use serialization::DenoCompileModuleSource; -use std::borrow::Cow; -use std::rc::Rc; -use std::sync::Arc; +use serialization::SourceMapStore; use virtual_fs::FileBackedVfs; use virtual_fs::VfsFileSubDataKind; @@ -64,31 +75,29 @@ use crate::args::create_default_npmrc; use crate::args::get_root_cert_store; use crate::args::npm_pkg_req_ref_to_binary_command; use crate::args::CaData; -use crate::args::CacheSetting; use crate::args::NpmInstallDepsProvider; use crate::args::StorageKeyResolver; use crate::cache::Caches; -use crate::cache::DenoCacheEnvFsAdapter; use crate::cache::DenoDirProvider; use crate::cache::FastInsecureHasher; use crate::cache::NodeAnalysisCache; -use crate::cache::RealDenoCacheEnv; use crate::http_util::HttpClientProvider; use crate::node::CliCjsCodeAnalyzer; use crate::node::CliNodeCodeTranslator; -use crate::npm::create_cli_npm_resolver; -use crate::npm::create_in_npm_pkg_checker; +use crate::node::CliNodeResolver; +use crate::node::CliPackageJsonResolver; use crate::npm::CliByonmNpmResolverCreateOptions; -use crate::npm::CliManagedInNpmPkgCheckerCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; -use crate::npm::CreateInNpmPkgCheckerOptions; -use crate::resolver::CjsTracker; -use crate::resolver::CliDenoResolverFs; +use crate::npm::NpmRegistryReadPermissionChecker; +use crate::npm::NpmRegistryReadPermissionCheckerMode; +use crate::npm::NpmResolutionInitializer; +use crate::resolver::CliCjsTracker; use crate::resolver::CliNpmReqResolver; use crate::resolver::NpmModuleLoader; +use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::util::text_encoding::from_utf8_lossy_cow; @@ -110,18 +119,20 @@ pub use binary::is_standalone_binary; pub use binary::DenoCompileBinaryWriter; use self::binary::Metadata; -use self::file_system::DenoCompileFileSystem; +pub use self::file_system::DenoCompileFileSystem; struct SharedModuleLoaderState { - cjs_tracker: Arc, + cjs_tracker: Arc, code_cache: Option>, fs: Arc, modules: StandaloneModules, node_code_translator: Arc, - node_resolver: Arc, + node_resolver: Arc, npm_module_loader: Arc, + npm_registry_permission_checker: NpmRegistryReadPermissionChecker, npm_req_resolver: Arc, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, + source_maps: SourceMapStore, vfs: Arc, workspace_resolver: WorkspaceResolver, } @@ -174,25 +185,32 @@ impl ModuleLoader for EmbeddedModuleLoader { raw_specifier: &str, referrer: &str, kind: ResolutionKind, - ) -> Result { + ) -> Result { let referrer = if referrer == "." { if kind != ResolutionKind::MainModule { - return Err(generic_error(format!( - "Expected to resolve main module, got {:?} instead.", - kind - ))); + return Err( + JsErrorBox::generic(format!( + "Expected to resolve main module, got {:?} instead.", + kind + )) + .into(), + ); } let current_dir = std::env::current_dir().unwrap(); deno_core::resolve_path(".", ¤t_dir)? } else { ModuleSpecifier::parse(referrer).map_err(|err| { - type_error(format!("Referrer uses invalid specifier: {}", err)) + JsErrorBox::type_error(format!( + "Referrer uses invalid specifier: {}", + err + )) })? }; let referrer_kind = if self .shared .cjs_tracker - .is_maybe_cjs(&referrer, MediaType::from_specifier(&referrer))? + .is_maybe_cjs(&referrer, MediaType::from_specifier(&referrer)) + .map_err(JsErrorBox::from_err)? { ResolutionMode::Require } else { @@ -209,7 +227,8 @@ impl ModuleLoader for EmbeddedModuleLoader { &referrer, referrer_kind, NodeResolutionKind::Execution, - )? + ) + .map_err(JsErrorBox::from_err)? .into_url(), ); } @@ -237,14 +256,18 @@ impl ModuleLoader for EmbeddedModuleLoader { Some(&referrer), referrer_kind, NodeResolutionKind::Execution, - )?, + ) + .map_err(JsErrorBox::from_err)?, ), Ok(MappedResolution::PackageJson { dep_result, sub_path, alias, .. - }) => match dep_result.as_ref().map_err(|e| AnyError::from(e.clone()))? { + }) => match dep_result + .as_ref() + .map_err(|e| JsErrorBox::from_err(e.clone()))? + { PackageJsonDepValue::Req(req) => self .shared .npm_req_resolver @@ -255,7 +278,7 @@ impl ModuleLoader for EmbeddedModuleLoader { referrer_kind, NodeResolutionKind::Execution, ) - .map_err(AnyError::from), + .map_err(|e| JsErrorBox::from_err(e).into()), PackageJsonDepValue::Workspace(version_req) => { let pkg_folder = self .shared @@ -263,7 +286,8 @@ impl ModuleLoader for EmbeddedModuleLoader { .resolve_workspace_pkg_json_folder_for_pkg_json_dep( alias, version_req, - )?; + ) + .map_err(JsErrorBox::from_err)?; Ok( self .shared @@ -274,7 +298,8 @@ impl ModuleLoader for EmbeddedModuleLoader { Some(&referrer), referrer_kind, NodeResolutionKind::Execution, - )?, + ) + .map_err(JsErrorBox::from_err)?, ) } }, @@ -283,12 +308,18 @@ impl ModuleLoader for EmbeddedModuleLoader { if let Ok(reference) = NpmPackageReqReference::from_specifier(&specifier) { - return Ok(self.shared.npm_req_resolver.resolve_req_reference( - &reference, - &referrer, - referrer_kind, - NodeResolutionKind::Execution, - )?); + return Ok( + self + .shared + .npm_req_resolver + .resolve_req_reference( + &reference, + &referrer, + referrer_kind, + NodeResolutionKind::Execution, + ) + .map_err(JsErrorBox::from_err)?, + ); } if specifier.scheme() == "jsr" { @@ -310,18 +341,22 @@ impl ModuleLoader for EmbeddedModuleLoader { Err(err) if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" => { - let maybe_res = self.shared.npm_req_resolver.resolve_if_for_npm_pkg( - raw_specifier, - &referrer, - referrer_kind, - NodeResolutionKind::Execution, - )?; + let maybe_res = self + .shared + .npm_req_resolver + .resolve_if_for_npm_pkg( + raw_specifier, + &referrer, + referrer_kind, + NodeResolutionKind::Execution, + ) + .map_err(JsErrorBox::from_err)?; if let Some(res) = maybe_res { return Ok(res.into_url()); } - Err(err.into()) + Err(JsErrorBox::from_err(err).into()) } - Err(err) => Err(err.into()), + Err(err) => Err(JsErrorBox::from_err(err).into()), } } @@ -352,9 +387,9 @@ impl ModuleLoader for EmbeddedModuleLoader { { Ok(response) => response, Err(err) => { - return deno_core::ModuleLoadResponse::Sync(Err(type_error( - format!("{:#}", err), - ))); + return deno_core::ModuleLoadResponse::Sync(Err( + JsErrorBox::type_error(format!("{:#}", err)).into(), + )); } }; return deno_core::ModuleLoadResponse::Sync(Ok( @@ -396,7 +431,11 @@ impl ModuleLoader for EmbeddedModuleLoader { ); } - match self.shared.modules.read(original_specifier) { + match self + .shared + .modules + .read(original_specifier, VfsFileSubDataKind::ModuleGraph) + { Ok(Some(module)) => { let media_type = module.media_type; let (module_specifier, module_type, module_source) = @@ -408,9 +447,9 @@ impl ModuleLoader for EmbeddedModuleLoader { { Ok(is_maybe_cjs) => is_maybe_cjs, Err(err) => { - return deno_core::ModuleLoadResponse::Sync(Err(type_error( - format!("{:?}", err), - ))); + return deno_core::ModuleLoadResponse::Sync(Err( + JsErrorBox::type_error(format!("{:?}", err)).into(), + )); } }; if is_maybe_cjs { @@ -470,12 +509,16 @@ impl ModuleLoader for EmbeddedModuleLoader { )) } } - Ok(None) => deno_core::ModuleLoadResponse::Sync(Err(type_error( - format!("{MODULE_NOT_FOUND}: {}", original_specifier), - ))), - Err(err) => deno_core::ModuleLoadResponse::Sync(Err(type_error( - format!("{:?}", err), - ))), + Ok(None) => deno_core::ModuleLoadResponse::Sync(Err( + JsErrorBox::type_error(format!( + "{MODULE_NOT_FOUND}: {}", + original_specifier + )) + .into(), + )), + Err(err) => deno_core::ModuleLoadResponse::Sync(Err( + JsErrorBox::type_error(format!("{:?}", err)).into(), + )), } } @@ -495,6 +538,45 @@ impl ModuleLoader for EmbeddedModuleLoader { } std::future::ready(()).boxed_local() } + + fn get_source_map(&self, file_name: &str) -> Option> { + if file_name.starts_with("file:///") { + let url = + deno_path_util::url_from_directory_path(self.shared.vfs.root()).ok()?; + let file_url = ModuleSpecifier::parse(file_name).ok()?; + let relative_path = url.make_relative(&file_url)?; + self.shared.source_maps.get(&relative_path) + } else { + self.shared.source_maps.get(file_name) + } + .map(Cow::Borrowed) + } + + fn get_source_mapped_source_line( + &self, + file_name: &str, + line_number: usize, + ) -> Option { + let specifier = ModuleSpecifier::parse(file_name).ok()?; + let data = self + .shared + .modules + .read(&specifier, VfsFileSubDataKind::Raw) + .ok()??; + + let source = String::from_utf8_lossy(&data.data); + // Do NOT use .lines(): it skips the terminating empty line. + // (due to internally using_terminator() instead of .split()) + let lines: Vec<&str> = source.split('\n').collect(); + if line_number >= lines.len() { + Some(format!( + "{} Couldn't format source line: Line {} is out of bounds (source may have changed at runtime)", + crate::colors::yellow("Warning"), line_number + 1, + )) + } else { + Some(lines[line_number].to_string()) + } + } } impl NodeRequireLoader for EmbeddedModuleLoader { @@ -502,7 +584,7 @@ impl NodeRequireLoader for EmbeddedModuleLoader { &self, permissions: &mut dyn deno_runtime::deno_node::NodePermissions, path: &'a std::path::Path, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { if self.shared.modules.has_file(path) { // allow reading if the file is in the snapshot return Ok(Cow::Borrowed(path)); @@ -510,19 +592,25 @@ impl NodeRequireLoader for EmbeddedModuleLoader { self .shared - .npm_resolver + .npm_registry_permission_checker .ensure_read_permission(permissions, path) + .map_err(JsErrorBox::from_err) } fn load_text_file_lossy( &self, path: &std::path::Path, - ) -> Result, AnyError> { - let file_entry = self.shared.vfs.file_entry(path)?; + ) -> Result, JsErrorBox> { + let file_entry = self + .shared + .vfs + .file_entry(path) + .map_err(JsErrorBox::from_err)?; let file_bytes = self .shared .vfs - .read_file_all(file_entry, VfsFileSubDataKind::ModuleGraph)?; + .read_file_all(file_entry, VfsFileSubDataKind::ModuleGraph) + .map_err(JsErrorBox::from_err)?; Ok(from_utf8_lossy_cow(file_bytes)) } @@ -575,44 +663,57 @@ struct StandaloneRootCertStoreProvider { } impl RootCertStoreProvider for StandaloneRootCertStoreProvider { - fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> { + fn get_or_try_init(&self) -> Result<&RootCertStore, JsErrorBox> { self.cell.get_or_try_init(|| { get_root_cert_store(None, self.ca_stores.clone(), self.ca_data.clone()) - .map_err(|err| err.into()) + .map_err(JsErrorBox::from_err) }) } } -pub async fn run(data: StandaloneData) -> Result { +pub async fn run( + fs: Arc, + sys: CliSys, + data: StandaloneData, +) -> Result { let StandaloneData { - fs, metadata, modules, npm_snapshot, root_path, + source_maps, vfs, } = data; - let deno_dir_provider = Arc::new(DenoDirProvider::new(None)); + let deno_dir_provider = Arc::new(DenoDirProvider::new(sys.clone(), None)); let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider { ca_stores: metadata.ca_stores, ca_data: metadata.ca_data.map(CaData::Bytes), cell: Default::default(), }); - let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly); - let http_client_provider = Arc::new(HttpClientProvider::new( - Some(root_cert_store_provider.clone()), - metadata.unsafely_ignore_certificate_errors.clone(), - )); // use a dummy npm registry url let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap(); let root_dir_url = Arc::new(ModuleSpecifier::from_directory_path(&root_path).unwrap()); let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap(); let npm_global_cache_dir = root_path.join(".deno_compile_node_modules"); - let cache_setting = CacheSetting::Only; - let pkg_json_resolver = Arc::new(PackageJsonResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), - )); + let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone())); + let npm_registry_permission_checker = { + let mode = match &metadata.node_modules { + Some(binary::NodeModules::Managed { + node_modules_dir: Some(path), + }) => NpmRegistryReadPermissionCheckerMode::Local(PathBuf::from(path)), + Some(binary::NodeModules::Byonm { .. }) => { + NpmRegistryReadPermissionCheckerMode::Byonm + } + Some(binary::NodeModules::Managed { + node_modules_dir: None, + }) + | None => NpmRegistryReadPermissionCheckerMode::Global( + npm_global_cache_dir.clone(), + ), + }; + NpmRegistryReadPermissionChecker::new(sys.clone(), mode) + }; let (in_npm_pkg_checker, npm_resolver) = match metadata.node_modules { Some(binary::NodeModules::Managed { node_modules_dir }) => { // create an npmrc that uses the fake npm_registry_url to resolve packages @@ -625,7 +726,7 @@ pub async fn run(data: StandaloneData) -> Result { registry_configs: Default::default(), }); let npm_cache_dir = Arc::new(NpmCacheDir::new( - &DenoCacheEnvFsAdapter(fs.as_ref()), + &sys, npm_global_cache_dir, npmrc.get_all_known_registries_urls(), )); @@ -633,35 +734,25 @@ pub async fn run(data: StandaloneData) -> Result { let maybe_node_modules_path = node_modules_dir .map(|node_modules_dir| root_path.join(node_modules_dir)); let in_npm_pkg_checker = - create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed( - CliManagedInNpmPkgCheckerCreateOptions { + DenoInNpmPackageChecker::new(CreateInNpmPkgCheckerOptions::Managed( + ManagedInNpmPkgCheckerCreateOptions { root_cache_dir_url: npm_cache_dir.root_dir_url(), maybe_node_modules_path: maybe_node_modules_path.as_deref(), }, )); + let npm_resolution = + Arc::new(NpmResolutionCell::new(NpmResolutionSnapshot::new(snapshot))); let npm_resolver = - create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( + CliNpmResolver::new(CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { - snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some( - snapshot, - )), - maybe_lockfile: None, - fs: fs.clone(), - http_client_provider: http_client_provider.clone(), + npm_resolution, npm_cache_dir, - cache_setting, - text_only_progress_bar: progress_bar, + sys: sys.clone(), maybe_node_modules_path, npm_system_info: Default::default(), - npm_install_deps_provider: Arc::new( - // this is only used for installing packages, which isn't necessary with deno compile - NpmInstallDepsProvider::empty(), - ), npmrc, - lifecycle_scripts: Default::default(), }, - )) - .await?; + )); (in_npm_pkg_checker, npm_resolver) } Some(binary::NodeModules::Byonm { @@ -670,15 +761,14 @@ pub async fn run(data: StandaloneData) -> Result { let root_node_modules_dir = root_node_modules_dir.map(|p| vfs.root().join(p)); let in_npm_pkg_checker = - create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Byonm); - let npm_resolver = create_cli_npm_resolver( + DenoInNpmPackageChecker::new(CreateInNpmPkgCheckerOptions::Byonm); + let npm_resolver = CliNpmResolver::new( CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { - fs: CliDenoResolverFs(fs.clone()), + sys: sys.clone(), pkg_json_resolver: pkg_json_resolver.clone(), root_node_modules_dir, }), - ) - .await?; + ); (in_npm_pkg_checker, npm_resolver) } None => { @@ -686,50 +776,43 @@ pub async fn run(data: StandaloneData) -> Result { // so no need to create actual `.npmrc` configuration. let npmrc = create_default_npmrc(); let npm_cache_dir = Arc::new(NpmCacheDir::new( - &DenoCacheEnvFsAdapter(fs.as_ref()), + &sys, npm_global_cache_dir, npmrc.get_all_known_registries_urls(), )); let in_npm_pkg_checker = - create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed( - CliManagedInNpmPkgCheckerCreateOptions { + DenoInNpmPackageChecker::new(CreateInNpmPkgCheckerOptions::Managed( + ManagedInNpmPkgCheckerCreateOptions { root_cache_dir_url: npm_cache_dir.root_dir_url(), maybe_node_modules_path: None, }, )); + let npm_resolution = Arc::new(NpmResolutionCell::default()); let npm_resolver = - create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( + CliNpmResolver::new(CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { - snapshot: CliNpmResolverManagedSnapshotOption::Specified(None), - maybe_lockfile: None, - fs: fs.clone(), - http_client_provider: http_client_provider.clone(), + npm_resolution, + sys: sys.clone(), npm_cache_dir, - cache_setting, - text_only_progress_bar: progress_bar, maybe_node_modules_path: None, npm_system_info: Default::default(), - npm_install_deps_provider: Arc::new( - // this is only used for installing packages, which isn't necessary with deno compile - NpmInstallDepsProvider::empty(), - ), npmrc: create_default_npmrc(), - lifecycle_scripts: Default::default(), }, - )) - .await?; + )); (in_npm_pkg_checker, npm_resolver) } }; let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some(); let node_resolver = Arc::new(NodeResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), in_npm_pkg_checker.clone(), - npm_resolver.clone().into_npm_pkg_folder_resolver(), + RealIsBuiltInNodeModuleChecker, + npm_resolver.clone(), pkg_json_resolver.clone(), + sys.clone(), + node_resolver::ConditionsFromResolutionMode::default(), )); - let cjs_tracker = Arc::new(CjsTracker::new( + let cjs_tracker = Arc::new(CliCjsTracker::new( in_npm_pkg_checker.clone(), pkg_json_resolver.clone(), if metadata.unstable_config.detect_cjs { @@ -744,11 +827,10 @@ pub async fn run(data: StandaloneData) -> Result { let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db()); let npm_req_resolver = Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { - byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), - fs: CliDenoResolverFs(fs.clone()), + sys: sys.clone(), in_npm_pkg_checker: in_npm_pkg_checker.clone(), node_resolver: node_resolver.clone(), - npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), + npm_resolver: npm_resolver.clone(), })); let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new( node_analysis_cache, @@ -758,11 +840,11 @@ pub async fn run(data: StandaloneData) -> Result { ); let node_code_translator = Arc::new(NodeCodeTranslator::new( cjs_esm_code_analyzer, - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), in_npm_pkg_checker, node_resolver.clone(), - npm_resolver.clone().into_npm_pkg_folder_resolver(), + npm_resolver.clone(), pkg_json_resolver.clone(), + sys.clone(), )); let workspace_resolver = { let import_map = match metadata.workspace_resolver.import_map { @@ -839,16 +921,17 @@ pub async fn run(data: StandaloneData) -> Result { fs.clone(), node_code_translator, )), + npm_registry_permission_checker, npm_resolver: npm_resolver.clone(), npm_req_resolver, + source_maps, vfs, workspace_resolver, }), }; let permissions = { - let mut permissions = - metadata.permissions.to_options(/* cli_arg_urls */ &[]); + let mut permissions = metadata.permissions; // grant read access to the vfs match &mut permissions.allow_read { Some(vec) if vec.is_empty() => { @@ -864,7 +947,7 @@ pub async fn run(data: StandaloneData) -> Result { } let desc_parser = - Arc::new(RuntimePermissionDescriptorParser::new(fs.clone())); + Arc::new(RuntimePermissionDescriptorParser::new(sys.clone())); let permissions = Permissions::from_options(desc_parser.as_ref(), &permissions)?; PermissionsContainer::new(desc_parser, permissions) @@ -889,11 +972,13 @@ pub async fn run(data: StandaloneData) -> Result { None, Box::new(module_loader_factory), node_resolver, + None, npm_resolver, pkg_json_resolver, root_cert_store_provider, permissions, StorageKeyResolver::empty(), + sys, crate::args::DenoSubcommand::Run(Default::default()), CliMainWorkerOptions { argv: metadata.argv, diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index 6062e21019..ae2e411b5f 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -1,10 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; +use std::cell::Cell; use std::collections::BTreeMap; use std::collections::HashMap; use std::io::Write; +use capacity_builder::BytesAppendable; +use deno_ast::swc::common::source_map; use deno_ast::MediaType; use deno_core::anyhow::bail; use deno_core::anyhow::Context; @@ -19,12 +22,15 @@ use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmPackageId; use deno_semver::package::PackageReq; - -use crate::standalone::virtual_fs::VirtualDirectory; +use deno_semver::StackString; +use indexmap::IndexMap; use super::binary::Metadata; use super::virtual_fs::BuiltVfs; +use super::virtual_fs::FileSystemCaseSensitivity; use super::virtual_fs::VfsBuilder; +use super::virtual_fs::VirtualDirectoryEntries; +use crate::standalone::virtual_fs::VirtualDirectory; const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd"; @@ -32,60 +38,64 @@ const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd"; /// * d3n0l4nd /// * /// * -/// * +/// * /// * /// * +/// * /// * d3n0l4nd pub fn serialize_binary_data_section( metadata: &Metadata, npm_snapshot: Option, remote_modules: &RemoteModulesStoreBuilder, + source_map_store: &SourceMapStore, vfs: &BuiltVfs, ) -> Result, AnyError> { - fn write_bytes_with_len(bytes: &mut Vec, data: &[u8]) { - bytes.extend_from_slice(&(data.len() as u64).to_le_bytes()); - bytes.extend_from_slice(data); - } + let metadata = serde_json::to_string(metadata)?; + let npm_snapshot = + npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default(); + let serialized_vfs = serde_json::to_string(&vfs.entries)?; - let mut bytes = Vec::new(); - bytes.extend_from_slice(MAGIC_BYTES); - - // 1. Metadata - { - let metadata = serde_json::to_string(metadata)?; - write_bytes_with_len(&mut bytes, metadata.as_bytes()); - } - // 2. Npm snapshot - { - let npm_snapshot = - npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default(); - write_bytes_with_len(&mut bytes, &npm_snapshot); - } - // 3. Remote modules - { - let update_index = bytes.len(); - bytes.extend_from_slice(&(0_u64).to_le_bytes()); - let start_index = bytes.len(); - remote_modules.write(&mut bytes)?; - let length = bytes.len() - start_index; - let length_bytes = (length as u64).to_le_bytes(); - bytes[update_index..update_index + length_bytes.len()] - .copy_from_slice(&length_bytes); - } - // 4. VFS - { - let serialized_vfs = serde_json::to_string(&vfs.root)?; - write_bytes_with_len(&mut bytes, serialized_vfs.as_bytes()); - let vfs_bytes_len = vfs.files.iter().map(|f| f.len() as u64).sum::(); - bytes.extend_from_slice(&vfs_bytes_len.to_le_bytes()); - for file in &vfs.files { - bytes.extend_from_slice(file); + let bytes = capacity_builder::BytesBuilder::build(|builder| { + builder.append(MAGIC_BYTES); + // 1. Metadata + { + builder.append_le(metadata.len() as u64); + builder.append(&metadata); + } + // 2. Npm snapshot + { + builder.append_le(npm_snapshot.len() as u64); + builder.append(&npm_snapshot); + } + // 3. Remote modules + { + remote_modules.write(builder); + } + // 4. VFS + { + builder.append_le(serialized_vfs.len() as u64); + builder.append(&serialized_vfs); + let vfs_bytes_len = vfs.files.iter().map(|f| f.len() as u64).sum::(); + builder.append_le(vfs_bytes_len); + for file in &vfs.files { + builder.append(file); + } + } + // 5. Source maps + { + builder.append_le(source_map_store.data.len() as u32); + for (specifier, source_map) in &source_map_store.data { + builder.append_le(specifier.len() as u32); + builder.append(specifier); + builder.append_le(source_map.len() as u32); + builder.append(source_map.as_ref()); + } } - } - // write the magic bytes at the end so we can use it - // to make sure we've deserialized correctly - bytes.extend_from_slice(MAGIC_BYTES); + // write the magic bytes at the end so we can use it + // to make sure we've deserialized correctly + builder.append(MAGIC_BYTES); + })?; Ok(bytes) } @@ -94,19 +104,14 @@ pub struct DeserializedDataSection { pub metadata: Metadata, pub npm_snapshot: Option, pub remote_modules: RemoteModulesStore, - pub vfs_dir: VirtualDirectory, + pub source_maps: SourceMapStore, + pub vfs_root_entries: VirtualDirectoryEntries, pub vfs_files_data: &'static [u8], } pub fn deserialize_binary_data_section( data: &'static [u8], ) -> Result, AnyError> { - fn read_bytes_with_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> { - let (input, len) = read_u64(input)?; - let (input, data) = read_bytes(input, len as usize)?; - Ok((input, data)) - } - fn read_magic_bytes(input: &[u8]) -> Result<(&[u8], bool), AnyError> { if input.len() < MAGIC_BYTES.len() { bail!("Unexpected end of data. Could not find magic bytes."); @@ -118,34 +123,51 @@ pub fn deserialize_binary_data_section( Ok((input, true)) } + #[allow(clippy::type_complexity)] + fn read_source_map_entry( + input: &[u8], + ) -> Result<(&[u8], (Cow, &[u8])), AnyError> { + let (input, specifier) = read_string_lossy(input)?; + let (input, source_map) = read_bytes_with_u32_len(input)?; + Ok((input, (specifier, source_map))) + } + let (input, found) = read_magic_bytes(data)?; if !found { return Ok(None); } // 1. Metadata - let (input, data) = read_bytes_with_len(input).context("reading metadata")?; + let (input, data) = + read_bytes_with_u64_len(input).context("reading metadata")?; let metadata: Metadata = serde_json::from_slice(data).context("deserializing metadata")?; // 2. Npm snapshot let (input, data) = - read_bytes_with_len(input).context("reading npm snapshot")?; + read_bytes_with_u64_len(input).context("reading npm snapshot")?; let npm_snapshot = if data.is_empty() { None } else { Some(deserialize_npm_snapshot(data).context("deserializing npm snapshot")?) }; // 3. Remote modules - let (input, data) = - read_bytes_with_len(input).context("reading remote modules data")?; - let remote_modules = - RemoteModulesStore::build(data).context("deserializing remote modules")?; + let (input, remote_modules) = + RemoteModulesStore::build(input).context("deserializing remote modules")?; // 4. VFS - let (input, data) = read_bytes_with_len(input).context("vfs")?; - let vfs_dir: VirtualDirectory = + let (input, data) = read_bytes_with_u64_len(input).context("vfs")?; + let vfs_root_entries: VirtualDirectoryEntries = serde_json::from_slice(data).context("deserializing vfs data")?; let (input, vfs_files_data) = - read_bytes_with_len(input).context("reading vfs files data")?; + read_bytes_with_u64_len(input).context("reading vfs files data")?; + // 5. Source maps + let (mut input, source_map_data_len) = read_u32_as_usize(input)?; + let mut source_maps = SourceMapStore::with_capacity(source_map_data_len); + for _ in 0..source_map_data_len { + let (current_input, (specifier, source_map)) = + read_source_map_entry(input)?; + input = current_input; + source_maps.add(specifier, Cow::Borrowed(source_map)); + } // finally ensure we read the magic bytes at the end let (_input, found) = read_magic_bytes(input)?; @@ -157,7 +179,8 @@ pub fn deserialize_binary_data_section( metadata, npm_snapshot, remote_modules, - vfs_dir, + source_maps, + vfs_root_entries, vfs_files_data, })) } @@ -165,19 +188,31 @@ pub fn deserialize_binary_data_section( #[derive(Default)] pub struct RemoteModulesStoreBuilder { specifiers: Vec<(String, u64)>, - data: Vec<(MediaType, Vec)>, + data: Vec<(MediaType, Vec, Option>)>, data_byte_len: u64, redirects: Vec<(String, String)>, redirects_len: u64, } impl RemoteModulesStoreBuilder { - pub fn add(&mut self, specifier: &Url, media_type: MediaType, data: Vec) { + pub fn add( + &mut self, + specifier: &Url, + media_type: MediaType, + data: Vec, + maybe_transpiled: Option>, + ) { log::debug!("Adding '{}' ({})", specifier, media_type); let specifier = specifier.to_string(); self.specifiers.push((specifier, self.data_byte_len)); - self.data_byte_len += 1 + 8 + data.len() as u64; // media type (1 byte), data length (8 bytes), data - self.data.push((media_type, data)); + let maybe_transpiled_len = match &maybe_transpiled { + // data length (4 bytes), data + Some(data) => 4 + data.len() as u64, + None => 0, + }; + // media type (1 byte), data length (4 bytes), data, has transpiled (1 byte), transpiled length + self.data_byte_len += 1 + 4 + data.len() as u64 + 1 + maybe_transpiled_len; + self.data.push((media_type, data, maybe_transpiled)); } pub fn add_redirects(&mut self, redirects: &BTreeMap) { @@ -191,26 +226,50 @@ impl RemoteModulesStoreBuilder { } } - fn write(&self, writer: &mut dyn Write) -> Result<(), AnyError> { - writer.write_all(&(self.specifiers.len() as u32).to_le_bytes())?; - writer.write_all(&(self.redirects.len() as u32).to_le_bytes())?; + fn write<'a, TBytes: capacity_builder::BytesType>( + &'a self, + builder: &mut capacity_builder::BytesBuilder<'a, TBytes>, + ) { + builder.append_le(self.specifiers.len() as u32); + builder.append_le(self.redirects.len() as u32); for (specifier, offset) in &self.specifiers { - writer.write_all(&(specifier.len() as u32).to_le_bytes())?; - writer.write_all(specifier.as_bytes())?; - writer.write_all(&offset.to_le_bytes())?; + builder.append_le(specifier.len() as u32); + builder.append(specifier); + builder.append_le(*offset); } for (from, to) in &self.redirects { - writer.write_all(&(from.len() as u32).to_le_bytes())?; - writer.write_all(from.as_bytes())?; - writer.write_all(&(to.len() as u32).to_le_bytes())?; - writer.write_all(to.as_bytes())?; + builder.append_le(from.len() as u32); + builder.append(from); + builder.append_le(to.len() as u32); + builder.append(to); } - for (media_type, data) in &self.data { - writer.write_all(&[serialize_media_type(*media_type)])?; - writer.write_all(&(data.len() as u64).to_le_bytes())?; - writer.write_all(data)?; + builder.append_le( + self + .data + .iter() + .map(|(_, data, maybe_transpiled)| { + 1 + 4 + + (data.len() as u64) + + 1 + + match maybe_transpiled { + Some(transpiled) => 4 + (transpiled.len() as u64), + None => 0, + } + }) + .sum::(), + ); + for (media_type, data, maybe_transpiled) in &self.data { + builder.append(serialize_media_type(*media_type)); + builder.append_le(data.len() as u32); + builder.append(data); + if let Some(transpiled) = maybe_transpiled { + builder.append(1); + builder.append_le(transpiled.len() as u32); + builder.append(transpiled); + } else { + builder.append(0); + } } - Ok(()) } } @@ -238,6 +297,30 @@ impl DenoCompileModuleSource { } } +pub struct SourceMapStore { + data: IndexMap, Cow<'static, [u8]>>, +} + +impl SourceMapStore { + pub fn with_capacity(capacity: usize) -> Self { + Self { + data: IndexMap::with_capacity(capacity), + } + } + + pub fn add( + &mut self, + specifier: Cow<'static, str>, + source_map: Cow<'static, [u8]>, + ) { + self.data.insert(specifier, source_map); + } + + pub fn get(&self, specifier: &str) -> Option<&[u8]> { + self.data.get(specifier).map(|v| v.as_ref()) + } +} + pub struct DenoCompileModuleData<'a> { pub specifier: &'a Url, pub media_type: MediaType, @@ -284,6 +367,13 @@ impl<'a> DenoCompileModuleData<'a> { } } +pub struct RemoteModuleEntry<'a> { + pub specifier: &'a Url, + pub media_type: MediaType, + pub data: Cow<'static, [u8]>, + pub transpiled_data: Option>, +} + enum RemoteModulesStoreSpecifierValue { Data(usize), Redirect(Url), @@ -295,7 +385,7 @@ pub struct RemoteModulesStore { } impl RemoteModulesStore { - fn build(data: &'static [u8]) -> Result { + fn build(input: &'static [u8]) -> Result<(&'static [u8], Self), AnyError> { fn read_specifier(input: &[u8]) -> Result<(&[u8], (Url, u64)), AnyError> { let (input, specifier) = read_string_lossy(input)?; let specifier = Url::parse(&specifier)?; @@ -338,12 +428,16 @@ impl RemoteModulesStore { Ok((input, specifiers)) } - let (files_data, specifiers) = read_headers(data)?; + let (input, specifiers) = read_headers(input)?; + let (input, files_data) = read_bytes_with_u64_len(input)?; - Ok(Self { - specifiers, - files_data, - }) + Ok(( + input, + Self { + specifiers, + files_data, + }, + )) } pub fn resolve_specifier<'a>( @@ -374,7 +468,7 @@ impl RemoteModulesStore { pub fn read<'a>( &'a self, original_specifier: &'a Url, - ) -> Result>, AnyError> { + ) -> Result>, AnyError> { let mut count = 0; let mut specifier = original_specifier; loop { @@ -390,12 +484,25 @@ impl RemoteModulesStore { let input = &self.files_data[*offset..]; let (input, media_type_byte) = read_bytes(input, 1)?; let media_type = deserialize_media_type(media_type_byte[0])?; - let (input, len) = read_u64(input)?; - let (_input, data) = read_bytes(input, len as usize)?; - return Ok(Some(DenoCompileModuleData { + let (input, data) = read_bytes_with_u32_len(input)?; + check_has_len(input, 1)?; + let (input, has_transpiled) = (&input[1..], input[0]); + let (_, transpiled_data) = match has_transpiled { + 0 => (input, None), + 1 => { + let (input, data) = read_bytes_with_u32_len(input)?; + (input, Some(data)) + } + value => bail!( + "Invalid transpiled data flag: {}. Compiled data is corrupt.", + value + ), + }; + return Ok(Some(RemoteModuleEntry { specifier, media_type, data: Cow::Borrowed(data), + transpiled_data: transpiled_data.map(Cow::Borrowed), })); } None => { @@ -479,12 +586,13 @@ fn deserialize_npm_snapshot( #[allow(clippy::needless_lifetimes)] // clippy bug fn parse_package_dep<'a>( id_to_npm_id: &'a impl Fn(usize) -> Result, - ) -> impl Fn(&[u8]) -> Result<(&[u8], (String, NpmPackageId)), AnyError> + 'a + ) -> impl Fn(&[u8]) -> Result<(&[u8], (StackString, NpmPackageId)), AnyError> + 'a { |input| { let (input, req) = read_string_lossy(input)?; let (input, id) = read_u32_as_usize(input)?; - Ok((input, (req.into_owned(), id_to_npm_id(id)?))) + let req = StackString::from_cow(req); + Ok((input, (req, id_to_npm_id(id)?))) } } @@ -634,17 +742,34 @@ fn parse_vec_n_times_with_index( Ok((input, results)) } +fn read_bytes_with_u64_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> { + let (input, len) = read_u64(input)?; + let (input, data) = read_bytes(input, len as usize)?; + Ok((input, data)) +} + +fn read_bytes_with_u32_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> { + let (input, len) = read_u32_as_usize(input)?; + let (input, data) = read_bytes(input, len)?; + Ok((input, data)) +} + fn read_bytes(input: &[u8], len: usize) -> Result<(&[u8], &[u8]), AnyError> { - if input.len() < len { - bail!("Unexpected end of data.",); - } + check_has_len(input, len)?; let (len_bytes, input) = input.split_at(len); Ok((input, len_bytes)) } +#[inline(always)] +fn check_has_len(input: &[u8], len: usize) -> Result<(), AnyError> { + if input.len() < len { + bail!("Unexpected end of data."); + } + Ok(()) +} + fn read_string_lossy(input: &[u8]) -> Result<(&[u8], Cow), AnyError> { - let (input, str_len) = read_u32_as_usize(input)?; - let (input, data_bytes) = read_bytes(input, str_len)?; + let (input, data_bytes) = read_bytes_with_u32_len(input)?; Ok((input, String::from_utf8_lossy(data_bytes))) } diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index 8ddd179c7a..0b1f33259d 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; +use std::cmp::Ordering; use std::collections::HashMap; use std::collections::HashSet; use std::fs::File; @@ -34,12 +35,12 @@ use serde::Deserialize; use serde::Serialize; use thiserror::Error; +use super::binary::DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME; use crate::util; +use crate::util::display::human_size; use crate::util::display::DisplayTreeNode; use crate::util::fs::canonicalize_path; -use super::binary::DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME; - #[derive(Debug, PartialEq, Eq)] pub enum WindowsSystemRootablePath { /// The root of the system above any drive letters. @@ -50,8 +51,16 @@ pub enum WindowsSystemRootablePath { impl WindowsSystemRootablePath { pub fn join(&self, name_component: &str) -> PathBuf { // this method doesn't handle multiple components - debug_assert!(!name_component.contains('\\')); - debug_assert!(!name_component.contains('/')); + debug_assert!( + !name_component.contains('\\'), + "Invalid component: {}", + name_component + ); + debug_assert!( + !name_component.contains('/'), + "Invalid component: {}", + name_component + ); match self { WindowsSystemRootablePath::WindowSystemRoot => { @@ -66,7 +75,8 @@ impl WindowsSystemRootablePath { #[derive(Debug)] pub struct BuiltVfs { pub root_path: WindowsSystemRootablePath, - pub root: VirtualDirectory, + pub case_sensitivity: FileSystemCaseSensitivity, + pub entries: VirtualDirectoryEntries, pub files: Vec>, } @@ -87,6 +97,7 @@ pub struct VfsBuilder { file_offsets: HashMap, /// The minimum root directory that should be included in the VFS. min_root_dir: Option, + case_sensitivity: FileSystemCaseSensitivity, } impl VfsBuilder { @@ -94,15 +105,29 @@ impl VfsBuilder { Self { executable_root: VirtualDirectory { name: "/".to_string(), - entries: Vec::new(), + entries: Default::default(), }, files: Vec::new(), current_offset: 0, file_offsets: Default::default(), min_root_dir: Default::default(), + // This is not exactly correct because file systems on these OSes + // may be case-sensitive or not based on the directory, but this + // is a good enough approximation and limitation. In the future, + // we may want to store this information per directory instead + // depending on the feedback we get. + case_sensitivity: if cfg!(windows) || cfg!(target_os = "macos") { + FileSystemCaseSensitivity::Insensitive + } else { + FileSystemCaseSensitivity::Sensitive + }, } } + pub fn case_sensitivity(&self) -> FileSystemCaseSensitivity { + self.case_sensitivity + } + /// Add a directory that might be the minimum root directory /// of the VFS. /// @@ -209,21 +234,21 @@ impl VfsBuilder { let name = component.as_os_str().to_string_lossy(); let index = match current_dir .entries - .binary_search_by(|e| e.name().cmp(&name)) + .binary_search(&name, self.case_sensitivity) { Ok(index) => index, Err(insert_index) => { - current_dir.entries.insert( + current_dir.entries.0.insert( insert_index, VfsEntry::Dir(VirtualDirectory { name: name.to_string(), - entries: Vec::new(), + entries: Default::default(), }), ); insert_index } }; - match &mut current_dir.entries[index] { + match &mut current_dir.entries.0[index] { VfsEntry::Dir(dir) => { current_dir = dir; } @@ -247,14 +272,10 @@ impl VfsBuilder { continue; } let name = component.as_os_str().to_string_lossy(); - let index = match current_dir + let entry = current_dir .entries - .binary_search_by(|e| e.name().cmp(&name)) - { - Ok(index) => index, - Err(_) => return None, - }; - match &mut current_dir.entries[index] { + .get_mut_by_name(&name, self.case_sensitivity)?; + match entry { VfsEntry::Dir(dir) => { current_dir = dir; } @@ -305,6 +326,7 @@ impl VfsBuilder { ) -> Result<(), AnyError> { log::debug!("Adding file '{}'", path.display()); let checksum = util::checksum::gen(&[&data]); + let case_sensitivity = self.case_sensitivity; let offset = if let Some(offset) = self.file_offsets.get(&checksum) { // duplicate file, reuse an old offset *offset @@ -319,9 +341,9 @@ impl VfsBuilder { offset, len: data.len() as u64, }; - match dir.entries.binary_search_by(|e| e.name().cmp(&name)) { + match dir.entries.binary_search(&name, case_sensitivity) { Ok(index) => { - let entry = &mut dir.entries[index]; + let entry = &mut dir.entries.0[index]; match entry { VfsEntry::File(virtual_file) => match sub_data_kind { VfsFileSubDataKind::Raw => { @@ -335,7 +357,7 @@ impl VfsBuilder { } } Err(insert_index) => { - dir.entries.insert( + dir.entries.0.insert( insert_index, VfsEntry::File(VirtualFile { name: name.to_string(), @@ -380,13 +402,14 @@ impl VfsBuilder { std::fs::read_link(path) .with_context(|| format!("Reading symlink '{}'", path.display()))?, ); + let case_sensitivity = self.case_sensitivity; let target = normalize_path(path.parent().unwrap().join(&target)); let dir = self.add_dir_raw(path.parent().unwrap()); let name = path.file_name().unwrap().to_string_lossy(); - match dir.entries.binary_search_by(|e| e.name().cmp(&name)) { + match dir.entries.binary_search(&name, case_sensitivity) { Ok(_) => {} // previously inserted Err(insert_index) => { - dir.entries.insert( + dir.entries.0.insert( insert_index, VfsEntry::Symlink(VirtualSymlink { name: name.to_string(), @@ -425,7 +448,7 @@ impl VfsBuilder { dir: &mut VirtualDirectory, parts: &[String], ) { - for entry in &mut dir.entries { + for entry in &mut dir.entries.0 { match entry { VfsEntry::Dir(dir) => { strip_prefix_from_symlinks(dir, parts); @@ -453,13 +476,13 @@ impl VfsBuilder { if self.min_root_dir.as_ref() == Some(¤t_path) { break; } - match ¤t_dir.entries[0] { + match ¤t_dir.entries.0[0] { VfsEntry::Dir(dir) => { if dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME { // special directory we want to maintain break; } - match current_dir.entries.remove(0) { + match current_dir.entries.0.remove(0) { VfsEntry::Dir(dir) => { current_path = WindowsSystemRootablePath::Path(current_path.join(&dir.name)); @@ -479,7 +502,8 @@ impl VfsBuilder { } BuiltVfs { root_path: current_path, - root: current_dir, + case_sensitivity: self.case_sensitivity, + entries: current_dir.entries, files: self.files, } } @@ -505,103 +529,245 @@ pub fn output_vfs(vfs: &BuiltVfs, executable_name: &str) { return; // no need to compute if won't output } - if vfs.root.entries.is_empty() { + if vfs.entries.is_empty() { return; // nothing to output } let mut text = String::new(); let display_tree = vfs_as_display_tree(vfs, executable_name); display_tree.print(&mut text).unwrap(); // unwrap ok because it's writing to a string - log::info!( - "\n{}\n", - deno_terminal::colors::bold("Embedded File System") - ); + log::info!("\n{}\n", deno_terminal::colors::bold("Embedded Files")); log::info!("{}\n", text.trim()); + log::info!( + "Size: {}\n", + human_size(vfs.files.iter().map(|f| f.len() as f64).sum()) + ); } fn vfs_as_display_tree( vfs: &BuiltVfs, executable_name: &str, ) -> DisplayTreeNode { + /// The VFS only stores duplicate files once, so track that and display + /// it to the user so that it's not confusing. + #[derive(Debug, Default, Copy, Clone)] + struct Size { + unique: u64, + total: u64, + } + + impl std::ops::Add for Size { + type Output = Self; + + fn add(self, other: Self) -> Self { + Self { + unique: self.unique + other.unique, + total: self.total + other.total, + } + } + } + + impl std::iter::Sum for Size { + fn sum>(iter: I) -> Self { + iter.fold(Self::default(), std::ops::Add::add) + } + } + enum EntryOutput<'a> { - All, + All(Size), Subset(Vec>), - File, + File(Size), Symlink(&'a [String]), } impl<'a> EntryOutput<'a> { - pub fn as_display_tree(&self, name: String) -> DisplayTreeNode { - let mut children = match self { - EntryOutput::Subset(vec) => vec - .iter() - .map(|e| e.output.as_display_tree(e.name.to_string())) - .collect(), - EntryOutput::All | EntryOutput::File | EntryOutput::Symlink(_) => { - vec![] + pub fn size(&self) -> Size { + match self { + EntryOutput::All(size) => *size, + EntryOutput::Subset(children) => { + children.iter().map(|c| c.output.size()).sum() } - }; - // we only want to collapse leafs so that nodes of the - // same depth have the same indentation - let collapse_single_child = - children.len() == 1 && children[0].children.is_empty(); + EntryOutput::File(size) => *size, + EntryOutput::Symlink(_) => Size { + unique: 0, + total: 0, + }, + } + } + } + + impl<'a> EntryOutput<'a> { + pub fn as_display_tree(&self, name: String) -> DisplayTreeNode { + fn format_size(size: Size) -> String { + if size.unique == size.total { + human_size(size.unique as f64) + } else { + format!( + "{}{}", + human_size(size.total as f64), + deno_terminal::colors::gray(format!( + " - {} unique", + human_size(size.unique as f64) + )) + ) + } + } + DisplayTreeNode { text: match self { - EntryOutput::All => format!("{}/*", name), - EntryOutput::Subset(_) => { - if collapse_single_child { - format!("{}/{}", name, children[0].text) - } else { - name - } + EntryOutput::All(size) => { + format!("{}/* ({})", name, format_size(*size)) + } + EntryOutput::Subset(children) => { + let size = children.iter().map(|c| c.output.size()).sum::(); + format!("{} ({})", name, format_size(size)) + } + EntryOutput::File(size) => { + format!("{} ({})", name, format_size(*size)) } - EntryOutput::File => name, EntryOutput::Symlink(parts) => { format!("{} --> {}", name, parts.join("/")) } }, - children: if collapse_single_child { - children.remove(0).children - } else { - children + children: match self { + EntryOutput::All(_) => Vec::new(), + EntryOutput::Subset(children) => children + .iter() + .map(|entry| entry.output.as_display_tree(entry.name.to_string())) + .collect(), + EntryOutput::File(_) => Vec::new(), + EntryOutput::Symlink(_) => Vec::new(), }, } } } pub struct DirEntryOutput<'a> { - name: &'a str, + name: Cow<'a, str>, output: EntryOutput<'a>, } - fn show_global_node_modules_dir( - vfs_dir: &VirtualDirectory, - ) -> Vec { - fn show_subset_deep( - vfs_dir: &VirtualDirectory, - depth: usize, - ) -> EntryOutput { - if depth == 0 { - EntryOutput::All + impl<'a> DirEntryOutput<'a> { + /// Collapses leaf nodes so they don't take up so much space when being + /// displayed. + /// + /// We only want to collapse leafs so that nodes of the same depth have + /// the same indentation. + pub fn collapse_leaf_nodes(&mut self) { + let EntryOutput::Subset(vec) = &mut self.output else { + return; + }; + for dir_entry in vec.iter_mut() { + dir_entry.collapse_leaf_nodes(); + } + if vec.len() != 1 { + return; + } + let child = &mut vec[0]; + let child_name = &child.name; + match &mut child.output { + EntryOutput::All(size) => { + self.name = Cow::Owned(format!("{}/{}", self.name, child_name)); + self.output = EntryOutput::All(*size); + } + EntryOutput::Subset(children) => { + if children.is_empty() { + self.name = Cow::Owned(format!("{}/{}", self.name, child_name)); + self.output = EntryOutput::Subset(vec![]); + } + } + EntryOutput::File(size) => { + self.name = Cow::Owned(format!("{}/{}", self.name, child_name)); + self.output = EntryOutput::File(*size); + } + EntryOutput::Symlink(parts) => { + let new_name = format!("{}/{}", self.name, child_name); + self.output = EntryOutput::Symlink(parts); + self.name = Cow::Owned(new_name); + } + } + } + } + + fn file_size(file: &VirtualFile, seen_offsets: &mut HashSet) -> Size { + fn add_offset_to_size( + offset: OffsetWithLength, + size: &mut Size, + seen_offsets: &mut HashSet, + ) { + if offset.len == 0 { + // some empty files have a dummy offset, so don't + // insert them into the seen offsets + return; + } + + if seen_offsets.insert(offset.offset) { + size.total += offset.len; + size.unique += offset.len; } else { - EntryOutput::Subset(show_subset(vfs_dir, depth)) + size.total += offset.len; } } - fn show_subset( - vfs_dir: &VirtualDirectory, + let mut size = Size::default(); + add_offset_to_size(file.offset, &mut size, seen_offsets); + if file.module_graph_offset.offset != file.offset.offset { + add_offset_to_size(file.module_graph_offset, &mut size, seen_offsets); + } + size + } + + fn dir_size(dir: &VirtualDirectory, seen_offsets: &mut HashSet) -> Size { + let mut size = Size::default(); + for entry in dir.entries.iter() { + match entry { + VfsEntry::Dir(virtual_directory) => { + size = size + dir_size(virtual_directory, seen_offsets); + } + VfsEntry::File(file) => { + size = size + file_size(file, seen_offsets); + } + VfsEntry::Symlink(_) => { + // ignore + } + } + } + size + } + + fn show_global_node_modules_dir<'a>( + vfs_dir: &'a VirtualDirectory, + seen_offsets: &mut HashSet, + ) -> Vec> { + fn show_subset_deep<'a>( + vfs_dir: &'a VirtualDirectory, depth: usize, - ) -> Vec { + seen_offsets: &mut HashSet, + ) -> EntryOutput<'a> { + if depth == 0 { + EntryOutput::All(dir_size(vfs_dir, seen_offsets)) + } else { + EntryOutput::Subset(show_subset(vfs_dir, depth, seen_offsets)) + } + } + + fn show_subset<'a>( + vfs_dir: &'a VirtualDirectory, + depth: usize, + seen_offsets: &mut HashSet, + ) -> Vec> { vfs_dir .entries .iter() .map(|entry| DirEntryOutput { - name: entry.name(), + name: Cow::Borrowed(entry.name()), output: match entry { VfsEntry::Dir(virtual_directory) => { - show_subset_deep(virtual_directory, depth - 1) + show_subset_deep(virtual_directory, depth - 1, seen_offsets) + } + VfsEntry::File(file) => { + EntryOutput::File(file_size(file, seen_offsets)) } - VfsEntry::File(_) => EntryOutput::File, VfsEntry::Symlink(virtual_symlink) => { EntryOutput::Symlink(&virtual_symlink.dest_parts.0) } @@ -612,40 +778,49 @@ fn vfs_as_display_tree( // in this scenario, we want to show // .deno_compile_node_modules/localhost///* - show_subset(vfs_dir, 3) + show_subset(vfs_dir, 3, seen_offsets) } fn include_all_entries<'a>( dir_path: &WindowsSystemRootablePath, - vfs_dir: &'a VirtualDirectory, + entries: &'a VirtualDirectoryEntries, + seen_offsets: &mut HashSet, ) -> Vec> { - if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME { - return show_global_node_modules_dir(vfs_dir); - } - - vfs_dir - .entries + entries .iter() .map(|entry| DirEntryOutput { - name: entry.name(), - output: analyze_entry(dir_path.join(entry.name()), entry), + name: Cow::Borrowed(entry.name()), + output: analyze_entry(dir_path.join(entry.name()), entry, seen_offsets), }) .collect() } - fn analyze_entry(path: PathBuf, entry: &VfsEntry) -> EntryOutput { + fn analyze_entry<'a>( + path: PathBuf, + entry: &'a VfsEntry, + seen_offsets: &mut HashSet, + ) -> EntryOutput<'a> { match entry { - VfsEntry::Dir(virtual_directory) => analyze_dir(path, virtual_directory), - VfsEntry::File(_) => EntryOutput::File, + VfsEntry::Dir(virtual_directory) => { + analyze_dir(path, virtual_directory, seen_offsets) + } + VfsEntry::File(file) => EntryOutput::File(file_size(file, seen_offsets)), VfsEntry::Symlink(virtual_symlink) => { EntryOutput::Symlink(&virtual_symlink.dest_parts.0) } } } - fn analyze_dir(dir: PathBuf, vfs_dir: &VirtualDirectory) -> EntryOutput { + fn analyze_dir<'a>( + dir: PathBuf, + vfs_dir: &'a VirtualDirectory, + seen_offsets: &mut HashSet, + ) -> EntryOutput<'a> { if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME { - return EntryOutput::Subset(show_global_node_modules_dir(vfs_dir)); + return EntryOutput::Subset(show_global_node_modules_dir( + vfs_dir, + seen_offsets, + )); } let real_entry_count = std::fs::read_dir(&dir) @@ -657,29 +832,37 @@ fn vfs_as_display_tree( .entries .iter() .map(|entry| DirEntryOutput { - name: entry.name(), - output: analyze_entry(dir.join(entry.name()), entry), + name: Cow::Borrowed(entry.name()), + output: analyze_entry(dir.join(entry.name()), entry, seen_offsets), }) .collect::>(); if children .iter() - .all(|c| !matches!(c.output, EntryOutput::Subset(_))) + .all(|c| !matches!(c.output, EntryOutput::Subset { .. })) { - EntryOutput::All + EntryOutput::All(children.iter().map(|c| c.output.size()).sum()) } else { EntryOutput::Subset(children) } + } else if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME { + EntryOutput::Subset(show_global_node_modules_dir(vfs_dir, seen_offsets)) } else { EntryOutput::Subset(include_all_entries( &WindowsSystemRootablePath::Path(dir), - vfs_dir, + &vfs_dir.entries, + seen_offsets, )) } } // always include all the entries for the root directory, otherwise the // user might not have context about what's being shown - let child_entries = include_all_entries(&vfs.root_path, &vfs.root); + let mut seen_offsets = HashSet::with_capacity(vfs.files.len()); + let mut child_entries = + include_all_entries(&vfs.root_path, &vfs.entries, &mut seen_offsets); + for child_entry in &mut child_entries { + child_entry.collapse_leaf_nodes(); + } DisplayTreeNode { text: deno_terminal::colors::italic(executable_name).to_string(), children: child_entries @@ -696,78 +879,28 @@ enum VfsEntryRef<'a> { Symlink(&'a VirtualSymlink), } -impl<'a> VfsEntryRef<'a> { - pub fn as_fs_stat(&self) -> FsStat { +impl VfsEntryRef<'_> { + pub fn as_metadata(&self) -> FileBackedVfsMetadata { + FileBackedVfsMetadata { + file_type: match self { + Self::Dir(_) => sys_traits::FileType::Dir, + Self::File(_) => sys_traits::FileType::File, + Self::Symlink(_) => sys_traits::FileType::Symlink, + }, + name: self.name().to_string(), + len: match self { + Self::Dir(_) => 0, + Self::File(file) => file.offset.len, + Self::Symlink(_) => 0, + }, + } + } + + pub fn name(&self) -> &str { match self { - VfsEntryRef::Dir(_) => FsStat { - is_directory: true, - is_file: false, - is_symlink: false, - atime: None, - birthtime: None, - mtime: None, - ctime: None, - blksize: 0, - size: 0, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }, - VfsEntryRef::File(file) => FsStat { - is_directory: false, - is_file: true, - is_symlink: false, - atime: None, - birthtime: None, - mtime: None, - ctime: None, - blksize: 0, - size: file.offset.len, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }, - VfsEntryRef::Symlink(_) => FsStat { - is_directory: false, - is_file: false, - is_symlink: true, - atime: None, - birthtime: None, - mtime: None, - ctime: None, - blksize: 0, - size: 0, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }, + Self::Dir(dir) => &dir.name, + Self::File(file) => &file.name, + Self::Symlink(symlink) => &symlink.name, } } } @@ -783,9 +916,9 @@ pub enum VfsEntry { impl VfsEntry { pub fn name(&self) -> &str { match self { - VfsEntry::Dir(dir) => &dir.name, - VfsEntry::File(file) => &file.name, - VfsEntry::Symlink(symlink) => &symlink.name, + Self::Dir(dir) => &dir.name, + Self::File(file) => &file.name, + Self::Symlink(symlink) => &symlink.name, } } @@ -798,27 +931,109 @@ impl VfsEntry { } } +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum FileSystemCaseSensitivity { + #[serde(rename = "s")] + Sensitive, + #[serde(rename = "i")] + Insensitive, +} + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct VirtualDirectoryEntries(Vec); + +impl VirtualDirectoryEntries { + pub fn new(mut entries: Vec) -> Self { + // needs to be sorted by name + entries.sort_by(|a, b| a.name().cmp(b.name())); + Self(entries) + } + + pub fn take_inner(&mut self) -> Vec { + std::mem::take(&mut self.0) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get_by_name( + &self, + name: &str, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Option<&VfsEntry> { + self + .binary_search(name, case_sensitivity) + .ok() + .map(|index| &self.0[index]) + } + + pub fn get_mut_by_name( + &mut self, + name: &str, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Option<&mut VfsEntry> { + self + .binary_search(name, case_sensitivity) + .ok() + .map(|index| &mut self.0[index]) + } + + pub fn binary_search( + &self, + name: &str, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Result { + match case_sensitivity { + FileSystemCaseSensitivity::Sensitive => { + self.0.binary_search_by(|e| e.name().cmp(name)) + } + FileSystemCaseSensitivity::Insensitive => self.0.binary_search_by(|e| { + e.name() + .chars() + .zip(name.chars()) + .map(|(a, b)| a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase())) + .find(|&ord| ord != Ordering::Equal) + .unwrap_or_else(|| e.name().len().cmp(&name.len())) + }), + } + } + + pub fn insert( + &mut self, + entry: VfsEntry, + case_sensitivity: FileSystemCaseSensitivity, + ) { + match self.binary_search(entry.name(), case_sensitivity) { + Ok(index) => { + self.0[index] = entry; + } + Err(insert_index) => { + self.0.insert(insert_index, entry); + } + } + } + + pub fn remove(&mut self, index: usize) -> VfsEntry { + self.0.remove(index) + } + + pub fn iter(&self) -> std::slice::Iter<'_, VfsEntry> { + self.0.iter() + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct VirtualDirectory { #[serde(rename = "n")] pub name: String, // should be sorted by name #[serde(rename = "e")] - pub entries: Vec, -} - -impl VirtualDirectory { - pub fn insert_entry(&mut self, entry: VfsEntry) { - let name = entry.name(); - match self.entries.binary_search_by(|e| e.name().cmp(name)) { - Ok(index) => { - self.entries[index] = entry; - } - Err(insert_index) => { - self.entries.insert(insert_index, entry); - } - } - } + pub entries: VirtualDirectoryEntries, } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] @@ -888,19 +1103,21 @@ impl VfsRoot { fn find_entry<'a>( &'a self, path: &Path, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> { - self.find_entry_inner(path, &mut HashSet::new()) + self.find_entry_inner(path, &mut HashSet::new(), case_sensitivity) } fn find_entry_inner<'a>( &'a self, path: &Path, seen: &mut HashSet, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> { let mut path = Cow::Borrowed(path); loop { let (resolved_path, entry) = - self.find_entry_no_follow_inner(&path, seen)?; + self.find_entry_no_follow_inner(&path, seen, case_sensitivity)?; match entry { VfsEntryRef::Symlink(symlink) => { if !seen.insert(path.to_path_buf()) { @@ -921,14 +1138,16 @@ impl VfsRoot { fn find_entry_no_follow( &self, path: &Path, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef)> { - self.find_entry_no_follow_inner(path, &mut HashSet::new()) + self.find_entry_no_follow_inner(path, &mut HashSet::new(), case_sensitivity) } fn find_entry_no_follow_inner<'a>( &'a self, path: &Path, seen: &mut HashSet, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> { let relative_path = match path.strip_prefix(&self.root_path) { Ok(p) => p, @@ -950,7 +1169,8 @@ impl VfsRoot { } VfsEntryRef::Symlink(symlink) => { let dest = symlink.resolve_dest_from_root(&self.root_path); - let (resolved_path, entry) = self.find_entry_inner(&dest, seen)?; + let (resolved_path, entry) = + self.find_entry_inner(&dest, seen, case_sensitivity)?; final_path = resolved_path; // overwrite with the new resolved path match entry { VfsEntryRef::Dir(dir) => { @@ -973,34 +1193,27 @@ impl VfsRoot { } }; let component = component.to_string_lossy(); - match current_dir + current_entry = current_dir .entries - .binary_search_by(|e| e.name().cmp(&component)) - { - Ok(index) => { - current_entry = current_dir.entries[index].as_ref(); - } - Err(_) => { - return Err(std::io::Error::new( - std::io::ErrorKind::NotFound, - "path not found", - )); - } - } + .get_by_name(&component, case_sensitivity) + .ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::NotFound, "path not found") + })? + .as_ref(); } Ok((final_path, current_entry)) } } -struct FileBackedVfsFile { +pub struct FileBackedVfsFile { file: VirtualFile, pos: RefCell, vfs: Arc, } impl FileBackedVfsFile { - fn seek(&self, pos: SeekFrom) -> FsResult { + pub fn seek(&self, pos: SeekFrom) -> std::io::Result { match pos { SeekFrom::Start(pos) => { *self.pos.borrow_mut() = pos; @@ -1009,10 +1222,10 @@ impl FileBackedVfsFile { SeekFrom::End(offset) => { if offset < 0 && -offset as u64 > self.file.offset.len { let msg = "An attempt was made to move the file pointer before the beginning of the file."; - Err( - std::io::Error::new(std::io::ErrorKind::PermissionDenied, msg) - .into(), - ) + Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + msg, + )) } else { let mut current_pos = self.pos.borrow_mut(); *current_pos = if offset >= 0 { @@ -1028,7 +1241,7 @@ impl FileBackedVfsFile { if offset >= 0 { *current_pos += offset as u64; } else if -offset as u64 > *current_pos { - return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "An attempt was made to move the file pointer before the beginning of the file.").into()); + return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "An attempt was made to move the file pointer before the beginning of the file.")); } else { *current_pos -= -offset as u64; } @@ -1037,7 +1250,7 @@ impl FileBackedVfsFile { } } - fn read_to_buf(&self, buf: &mut [u8]) -> FsResult { + pub fn read_to_buf(&self, buf: &mut [u8]) -> std::io::Result { let read_pos = { let mut pos = self.pos.borrow_mut(); let read_pos = *pos; @@ -1045,10 +1258,7 @@ impl FileBackedVfsFile { *pos = std::cmp::min(self.file.offset.len, *pos + buf.len() as u64); read_pos }; - self - .vfs - .read_file(&self.file, read_pos, buf) - .map_err(|err| err.into()) + self.vfs.read_file(&self.file, read_pos, buf) } fn read_to_end(&self) -> FsResult> { @@ -1083,7 +1293,7 @@ impl FileBackedVfsFile { #[async_trait::async_trait(?Send)] impl deno_io::fs::File for FileBackedVfsFile { fn read_sync(self: Rc, buf: &mut [u8]) -> FsResult { - self.read_to_buf(buf) + self.read_to_buf(buf).map_err(Into::into) } async fn read_byob( self: Rc, @@ -1127,10 +1337,10 @@ impl deno_io::fs::File for FileBackedVfsFile { } fn seek_sync(self: Rc, pos: SeekFrom) -> FsResult { - self.seek(pos) + self.seek(pos).map_err(|err| err.into()) } async fn seek_async(self: Rc, pos: SeekFrom) -> FsResult { - self.seek(pos) + self.seek(pos).map_err(|err| err.into()) } fn datasync_sync(self: Rc) -> FsResult<()> { @@ -1206,17 +1416,64 @@ impl deno_io::fs::File for FileBackedVfsFile { } } +#[derive(Debug, Clone)] +pub struct FileBackedVfsDirEntry { + pub parent_path: PathBuf, + pub metadata: FileBackedVfsMetadata, +} + +#[derive(Debug, Clone)] +pub struct FileBackedVfsMetadata { + pub name: String, + pub file_type: sys_traits::FileType, + pub len: u64, +} + +impl FileBackedVfsMetadata { + pub fn as_fs_stat(&self) -> FsStat { + FsStat { + is_directory: self.file_type == sys_traits::FileType::Dir, + is_file: self.file_type == sys_traits::FileType::File, + is_symlink: self.file_type == sys_traits::FileType::Symlink, + atime: None, + birthtime: None, + mtime: None, + ctime: None, + blksize: 0, + size: self.len, + dev: 0, + ino: 0, + mode: 0, + nlink: 0, + uid: 0, + gid: 0, + rdev: 0, + blocks: 0, + is_block_device: false, + is_char_device: false, + is_fifo: false, + is_socket: false, + } + } +} + #[derive(Debug)] pub struct FileBackedVfs { vfs_data: Cow<'static, [u8]>, fs_root: VfsRoot, + case_sensitivity: FileSystemCaseSensitivity, } impl FileBackedVfs { - pub fn new(data: Cow<'static, [u8]>, fs_root: VfsRoot) -> Self { + pub fn new( + data: Cow<'static, [u8]>, + fs_root: VfsRoot, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Self { Self { vfs_data: data, fs_root, + case_sensitivity, } } @@ -1231,13 +1488,13 @@ impl FileBackedVfs { pub fn open_file( self: &Arc, path: &Path, - ) -> std::io::Result> { + ) -> std::io::Result { let file = self.file_entry(path)?; - Ok(Rc::new(FileBackedVfsFile { + Ok(FileBackedVfsFile { file: file.clone(), vfs: self.clone(), pos: Default::default(), - })) + }) } pub fn read_dir(&self, path: &Path) -> std::io::Result> { @@ -1256,8 +1513,22 @@ impl FileBackedVfs { ) } + pub fn read_dir_with_metadata<'a>( + &'a self, + path: &Path, + ) -> std::io::Result + 'a> { + let dir = self.dir_entry(path)?; + let path = path.to_path_buf(); + Ok(dir.entries.iter().map(move |entry| FileBackedVfsDirEntry { + parent_path: path.to_path_buf(), + metadata: entry.as_ref().as_metadata(), + })) + } + pub fn read_link(&self, path: &Path) -> std::io::Result { - let (_, entry) = self.fs_root.find_entry_no_follow(path)?; + let (_, entry) = self + .fs_root + .find_entry_no_follow(path, self.case_sensitivity)?; match entry { VfsEntryRef::Symlink(symlink) => { Ok(symlink.resolve_dest_from_root(&self.fs_root.root_path)) @@ -1269,18 +1540,20 @@ impl FileBackedVfs { } } - pub fn lstat(&self, path: &Path) -> std::io::Result { - let (_, entry) = self.fs_root.find_entry_no_follow(path)?; - Ok(entry.as_fs_stat()) + pub fn lstat(&self, path: &Path) -> std::io::Result { + let (_, entry) = self + .fs_root + .find_entry_no_follow(path, self.case_sensitivity)?; + Ok(entry.as_metadata()) } - pub fn stat(&self, path: &Path) -> std::io::Result { - let (_, entry) = self.fs_root.find_entry(path)?; - Ok(entry.as_fs_stat()) + pub fn stat(&self, path: &Path) -> std::io::Result { + let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?; + Ok(entry.as_metadata()) } pub fn canonicalize(&self, path: &Path) -> std::io::Result { - let (path, _) = self.fs_root.find_entry(path)?; + let (path, _) = self.fs_root.find_entry(path, self.case_sensitivity)?; Ok(path) } @@ -1342,7 +1615,7 @@ impl FileBackedVfs { } pub fn dir_entry(&self, path: &Path) -> std::io::Result<&VirtualDirectory> { - let (_, entry) = self.fs_root.find_entry(path)?; + let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?; match entry { VfsEntryRef::Dir(dir) => Ok(dir), VfsEntryRef::Symlink(_) => unreachable!(), @@ -1354,7 +1627,7 @@ impl FileBackedVfs { } pub fn file_entry(&self, path: &Path) -> std::io::Result<&VirtualFile> { - let (_, entry) = self.fs_root.find_entry(path)?; + let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?; match entry { VfsEntryRef::Dir(_) => Err(std::io::Error::new( std::io::ErrorKind::Other, @@ -1368,8 +1641,10 @@ impl FileBackedVfs { #[cfg(test)] mod test { - use console_static_text::ansi::strip_ansi_codes; use std::io::Write; + + use console_static_text::ansi::strip_ansi_codes; + use deno_io::fs::File; use test_util::assert_contains; use test_util::TempDir; @@ -1454,25 +1729,31 @@ mod test { ); // metadata - assert!( + assert_eq!( virtual_fs .lstat(&dest_path.join("sub_dir").join("e.txt")) .unwrap() - .is_symlink + .file_type, + sys_traits::FileType::Symlink, ); - assert!( + assert_eq!( virtual_fs .stat(&dest_path.join("sub_dir").join("e.txt")) .unwrap() - .is_file + .file_type, + sys_traits::FileType::File, ); - assert!( + assert_eq!( virtual_fs .stat(&dest_path.join("sub_dir")) .unwrap() - .is_directory, + .file_type, + sys_traits::FileType::Dir, + ); + assert_eq!( + virtual_fs.stat(&dest_path.join("e.txt")).unwrap().file_type, + sys_traits::FileType::File ); - assert!(virtual_fs.stat(&dest_path.join("e.txt")).unwrap().is_file,); } #[test] @@ -1483,6 +1764,7 @@ mod test { temp_dir.write("src/a.txt", "data"); temp_dir.write("src/b.txt", "data"); util::fs::symlink_dir( + &crate::sys::CliSys::default(), temp_dir_path.join("src/nested/sub_dir").as_path(), temp_dir_path.join("src/sub_dir_link").as_path(), ) @@ -1509,11 +1791,12 @@ mod test { read_file(&virtual_fs, &dest_path.join("sub_dir_link").join("c.txt")), "c", ); - assert!( + assert_eq!( virtual_fs .lstat(&dest_path.join("sub_dir_link")) .unwrap() - .is_symlink + .file_type, + sys_traits::FileType::Symlink, ); assert_eq!( @@ -1543,10 +1826,14 @@ mod test { FileBackedVfs::new( Cow::Owned(data), VfsRoot { - dir: vfs.root, + dir: VirtualDirectory { + name: "".to_string(), + entries: vfs.entries, + }, root_path: dest_path.to_path_buf(), start_file_offset: 0, }, + FileSystemCaseSensitivity::Sensitive, ), ) } @@ -1582,37 +1869,35 @@ mod test { let (dest_path, virtual_fs) = into_virtual_fs(builder, &temp_dir); let virtual_fs = Arc::new(virtual_fs); let file = virtual_fs.open_file(&dest_path.join("a.txt")).unwrap(); - file.clone().seek_sync(SeekFrom::Current(2)).unwrap(); + file.seek(SeekFrom::Current(2)).unwrap(); let mut buf = vec![0; 2]; - file.clone().read_sync(&mut buf).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"23"); - file.clone().read_sync(&mut buf).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"45"); - file.clone().seek_sync(SeekFrom::Current(-4)).unwrap(); - file.clone().read_sync(&mut buf).unwrap(); + file.seek(SeekFrom::Current(-4)).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"23"); - file.clone().seek_sync(SeekFrom::Start(2)).unwrap(); - file.clone().read_sync(&mut buf).unwrap(); + file.seek(SeekFrom::Start(2)).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"23"); - file.clone().seek_sync(SeekFrom::End(2)).unwrap(); - file.clone().read_sync(&mut buf).unwrap(); + file.seek(SeekFrom::End(2)).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"89"); - file.clone().seek_sync(SeekFrom::Current(-8)).unwrap(); - file.clone().read_sync(&mut buf).unwrap(); + file.seek(SeekFrom::Current(-8)).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"23"); assert_eq!( file - .clone() - .seek_sync(SeekFrom::Current(-5)) - .err() - .unwrap() - .into_io_error() + .seek(SeekFrom::Current(-5)) + .unwrap_err() .to_string(), "An attempt was made to move the file pointer before the beginning of the file." ); // go beyond the file length, then back - file.clone().seek_sync(SeekFrom::Current(40)).unwrap(); - file.clone().seek_sync(SeekFrom::Current(-38)).unwrap(); + file.seek(SeekFrom::Current(40)).unwrap(); + file.seek(SeekFrom::Current(-38)).unwrap(); + let file = Rc::new(file); let read_buf = file.clone().read(2).await.unwrap(); assert_eq!(read_buf.to_vec(), b"67"); file.clone().seek_sync(SeekFrom::Current(-2)).unwrap(); @@ -1637,8 +1922,8 @@ mod test { let temp_dir = TempDir::new(); temp_dir.write("root.txt", ""); temp_dir.create_dir_all("a"); - temp_dir.write("a/a.txt", ""); - temp_dir.write("a/b.txt", ""); + temp_dir.write("a/a.txt", "data"); + temp_dir.write("a/b.txt", "other data"); temp_dir.create_dir_all("b"); temp_dir.write("b/a.txt", ""); temp_dir.write("b/b.txt", ""); @@ -1666,10 +1951,10 @@ mod test { assert_eq!( strip_ansi_codes(&text), r#"executable -├── a/* -├── b/a.txt -└─┬ c - ├── a.txt +├── a/* (14B) +├── b/a.txt (0B) +└─┬ c (8B) + ├── a.txt (8B) └── b.txt --> c/a.txt "# ); diff --git a/cli/sys.rs b/cli/sys.rs new file mode 100644 index 0000000000..718e9981e2 --- /dev/null +++ b/cli/sys.rs @@ -0,0 +1,230 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +// todo(dsherret): this should instead use conditional compilation and directly +// surface the underlying implementation. +// +// The problem atm is that there's no way to have conditional compilation for +// denort or the deno binary. We should extract out denort to a separate binary. + +use std::borrow::Cow; +use std::path::Path; +use std::path::PathBuf; + +use sys_traits::boxed::BoxedFsDirEntry; +use sys_traits::boxed::BoxedFsFile; +use sys_traits::boxed::BoxedFsMetadataValue; +use sys_traits::boxed::FsMetadataBoxed; +use sys_traits::boxed::FsOpenBoxed; +use sys_traits::boxed::FsReadDirBoxed; +use sys_traits::CreateDirOptions; + +use crate::standalone::DenoCompileFileSystem; + +#[derive(Debug, Clone)] +pub enum CliSys { + #[allow(dead_code)] // will be dead code for denort + #[allow(clippy::disallowed_types)] // ok because sys impl + Real(sys_traits::impls::RealSys), + #[allow(dead_code)] // will be dead code for deno + DenoCompile(DenoCompileFileSystem), +} + +impl Default for CliSys { + fn default() -> Self { + Self::Real(sys_traits::impls::RealSys) + } +} + +impl deno_runtime::deno_node::ExtNodeSys for CliSys {} + +impl sys_traits::BaseFsCloneFile for CliSys { + fn base_fs_clone_file(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_clone_file(src, dst), + Self::DenoCompile(sys) => sys.base_fs_clone_file(src, dst), + } + } +} + +impl sys_traits::BaseFsSymlinkDir for CliSys { + fn base_fs_symlink_dir(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_symlink_dir(src, dst), + Self::DenoCompile(sys) => sys.base_fs_symlink_dir(src, dst), + } + } +} + +impl sys_traits::BaseFsCopy for CliSys { + fn base_fs_copy(&self, src: &Path, dst: &Path) -> std::io::Result { + match self { + Self::Real(sys) => sys.base_fs_copy(src, dst), + Self::DenoCompile(sys) => sys.base_fs_copy(src, dst), + } + } +} + +impl sys_traits::BaseFsHardLink for CliSys { + fn base_fs_hard_link(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_hard_link(src, dst), + Self::DenoCompile(sys) => sys.base_fs_hard_link(src, dst), + } + } +} + +impl sys_traits::BaseFsRead for CliSys { + fn base_fs_read(&self, p: &Path) -> std::io::Result> { + match self { + Self::Real(sys) => sys.base_fs_read(p), + Self::DenoCompile(sys) => sys.base_fs_read(p), + } + } +} + +impl sys_traits::BaseFsReadDir for CliSys { + type ReadDirEntry = BoxedFsDirEntry; + + fn base_fs_read_dir( + &self, + p: &Path, + ) -> std::io::Result< + Box> + '_>, + > { + match self { + Self::Real(sys) => sys.fs_read_dir_boxed(p), + Self::DenoCompile(sys) => sys.fs_read_dir_boxed(p), + } + } +} + +impl sys_traits::BaseFsCanonicalize for CliSys { + fn base_fs_canonicalize(&self, p: &Path) -> std::io::Result { + match self { + Self::Real(sys) => sys.base_fs_canonicalize(p), + Self::DenoCompile(sys) => sys.base_fs_canonicalize(p), + } + } +} + +impl sys_traits::BaseFsMetadata for CliSys { + type Metadata = BoxedFsMetadataValue; + + fn base_fs_metadata(&self, path: &Path) -> std::io::Result { + match self { + Self::Real(sys) => sys.fs_metadata_boxed(path), + Self::DenoCompile(sys) => sys.fs_metadata_boxed(path), + } + } + + fn base_fs_symlink_metadata( + &self, + path: &Path, + ) -> std::io::Result { + match self { + Self::Real(sys) => sys.fs_symlink_metadata_boxed(path), + Self::DenoCompile(sys) => sys.fs_symlink_metadata_boxed(path), + } + } +} + +impl sys_traits::BaseFsCreateDir for CliSys { + fn base_fs_create_dir( + &self, + p: &Path, + options: &CreateDirOptions, + ) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_create_dir(p, options), + Self::DenoCompile(sys) => sys.base_fs_create_dir(p, options), + } + } +} + +impl sys_traits::BaseFsOpen for CliSys { + type File = BoxedFsFile; + + fn base_fs_open( + &self, + path: &Path, + options: &sys_traits::OpenOptions, + ) -> std::io::Result { + match self { + Self::Real(sys) => sys.fs_open_boxed(path, options), + Self::DenoCompile(sys) => sys.fs_open_boxed(path, options), + } + } +} + +impl sys_traits::BaseFsRemoveFile for CliSys { + fn base_fs_remove_file(&self, p: &Path) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_remove_file(p), + Self::DenoCompile(sys) => sys.base_fs_remove_file(p), + } + } +} + +impl sys_traits::BaseFsRename for CliSys { + fn base_fs_rename(&self, old: &Path, new: &Path) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_rename(old, new), + Self::DenoCompile(sys) => sys.base_fs_rename(old, new), + } + } +} + +impl sys_traits::SystemRandom for CliSys { + fn sys_random(&self, buf: &mut [u8]) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.sys_random(buf), + Self::DenoCompile(sys) => sys.sys_random(buf), + } + } +} + +impl sys_traits::SystemTimeNow for CliSys { + fn sys_time_now(&self) -> std::time::SystemTime { + match self { + Self::Real(sys) => sys.sys_time_now(), + Self::DenoCompile(sys) => sys.sys_time_now(), + } + } +} + +impl sys_traits::ThreadSleep for CliSys { + fn thread_sleep(&self, dur: std::time::Duration) { + match self { + Self::Real(sys) => sys.thread_sleep(dur), + Self::DenoCompile(sys) => sys.thread_sleep(dur), + } + } +} + +impl sys_traits::EnvCurrentDir for CliSys { + fn env_current_dir(&self) -> std::io::Result { + match self { + Self::Real(sys) => sys.env_current_dir(), + Self::DenoCompile(sys) => sys.env_current_dir(), + } + } +} + +impl sys_traits::BaseEnvVar for CliSys { + fn base_env_var_os( + &self, + key: &std::ffi::OsStr, + ) -> Option { + match self { + Self::Real(sys) => sys.base_env_var_os(key), + Self::DenoCompile(sys) => sys.base_env_var_os(key), + } + } +} + +impl sys_traits::EnvHomeDir for CliSys { + fn env_home_dir(&self) -> Option { + #[allow(clippy::disallowed_types)] // ok because sys impl + sys_traits::impls::RealSys.env_home_dir() + } +} diff --git a/cli/task_runner.rs b/cli/task_runner.rs index d6589a1832..14e850ee76 100644 --- a/cli/task_runner.rs +++ b/cli/task_runner.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::path::Path; @@ -10,7 +10,6 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::futures; use deno_core::futures::future::LocalBoxFuture; -use deno_runtime::deno_node::NodeResolver; use deno_semver::package::PackageNv; use deno_task_shell::ExecutableCommand; use deno_task_shell::ExecuteResult; @@ -25,9 +24,9 @@ use tokio::task::JoinHandle; use tokio::task::LocalSet; use tokio_util::sync::CancellationToken; +use crate::node::CliNodeResolver; +use crate::npm::CliManagedNpmResolver; use crate::npm::CliNpmResolver; -use crate::npm::InnerCliNpmResolverRef; -use crate::npm::ManagedCliNpmResolver; pub fn get_script_with_args(script: &str, argv: &[String]) -> String { let additional_args = argv @@ -414,15 +413,15 @@ impl ShellCommand for NodeModulesFileRunCommand { } pub fn resolve_custom_commands( - npm_resolver: &dyn CliNpmResolver, - node_resolver: &NodeResolver, + npm_resolver: &CliNpmResolver, + node_resolver: &CliNodeResolver, ) -> Result>, AnyError> { - let mut commands = match npm_resolver.as_inner() { - InnerCliNpmResolverRef::Byonm(npm_resolver) => { + let mut commands = match npm_resolver { + CliNpmResolver::Byonm(npm_resolver) => { let node_modules_dir = npm_resolver.root_node_modules_path().unwrap(); resolve_npm_commands_from_bin_dir(node_modules_dir) } - InnerCliNpmResolverRef::Managed(npm_resolver) => { + CliNpmResolver::Managed(npm_resolver) => { resolve_managed_npm_commands(npm_resolver, node_resolver)? } }; @@ -521,13 +520,12 @@ fn resolve_execution_path_from_npx_shim( } fn resolve_managed_npm_commands( - npm_resolver: &ManagedCliNpmResolver, - node_resolver: &NodeResolver, + npm_resolver: &CliManagedNpmResolver, + node_resolver: &CliNodeResolver, ) -> Result>, AnyError> { let mut result = HashMap::new(); - let snapshot = npm_resolver.snapshot(); - for id in snapshot.top_level_packages() { - let package_folder = npm_resolver.resolve_pkg_folder_from_pkg_id(id)?; + for id in npm_resolver.resolution().top_level_packages() { + let package_folder = npm_resolver.resolve_pkg_folder_from_pkg_id(&id)?; let bin_commands = node_resolver.resolve_binary_commands(&package_folder)?; for bin_command in bin_commands { @@ -598,7 +596,7 @@ async fn listen_ctrl_c(kill_signal: KillSignal) { #[cfg(unix)] async fn listen_and_forward_all_signals(kill_signal: KillSignal) { use deno_core::futures::FutureExt; - use deno_runtime::signal::SIGNAL_NUMS; + use deno_runtime::deno_os::signal::SIGNAL_NUMS; // listen and forward every signal we support let mut futures = Vec::with_capacity(SIGNAL_NUMS.len()); diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index 5983590531..6a57c4ce6c 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -1,23 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::args::BenchFlags; -use crate::args::Flags; -use crate::colors; -use crate::display::write_json_to_stdout; -use crate::factory::CliFactory; -use crate::graph_util::has_graph_root_local_dependent_changed; -use crate::ops; -use crate::tools::test::format_test_error; -use crate::tools::test::TestFilter; -use crate::util::file_watcher; -use crate::util::fs::collect_specifiers; -use crate::util::path::is_script_ext; -use crate::util::path::matches_pattern_or_exact_path; -use crate::worker::CliMainWorkerFactory; +use std::collections::HashSet; +use std::path::Path; +use std::sync::Arc; +use std::time::Duration; use deno_config::glob::WalkEntry; -use deno_core::error::generic_error; +use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::error::JsError; use deno_core::futures::future; use deno_core::futures::stream; @@ -28,6 +19,7 @@ use deno_core::unsync::spawn_blocking; use deno_core::v8; use deno_core::ModuleSpecifier; use deno_core::PollEventLoopOptions; +use deno_error::JsErrorBox; use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::permissions::RuntimePermissionDescriptorParser; @@ -38,13 +30,25 @@ use indexmap::IndexSet; use log::Level; use serde::Deserialize; use serde::Serialize; -use std::collections::HashSet; -use std::path::Path; -use std::sync::Arc; -use std::time::Duration; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedSender; +use crate::args::BenchFlags; +use crate::args::Flags; +use crate::colors; +use crate::display::write_json_to_stdout; +use crate::factory::CliFactory; +use crate::graph_util::has_graph_root_local_dependent_changed; +use crate::ops; +use crate::sys::CliSys; +use crate::tools::test::format_test_error; +use crate::tools::test::TestFilter; +use crate::util::file_watcher; +use crate::util::fs::collect_specifiers; +use crate::util::path::is_script_ext; +use crate::util::path::matches_pattern_or_exact_path; +use crate::worker::CliMainWorkerFactory; + mod mitata; mod reporters; @@ -160,17 +164,14 @@ async fn bench_specifier( .await { Ok(()) => Ok(()), - Err(error) => { - if error.is::() { - sender.send(BenchEvent::UncaughtError( - specifier.to_string(), - Box::new(error.downcast::().unwrap()), - ))?; - Ok(()) - } else { - Err(error) - } + Err(CoreError::Js(error)) => { + sender.send(BenchEvent::UncaughtError( + specifier.to_string(), + Box::new(error), + ))?; + Ok(()) } + Err(e) => Err(e.into()), } } @@ -181,7 +182,7 @@ async fn bench_specifier_inner( specifier: ModuleSpecifier, sender: &UnboundedSender, filter: TestFilter, -) -> Result<(), AnyError> { +) -> Result<(), CoreError> { let mut worker = worker_factory .create_custom_worker( WorkerExecutionMode::Bench, @@ -228,14 +229,18 @@ async fn bench_specifier_inner( .partial_cmp(&groups.get_index_of(&d2.group).unwrap()) .unwrap() }); - sender.send(BenchEvent::Plan(BenchPlan { - origin: specifier.to_string(), - total: benchmarks.len(), - used_only, - names: benchmarks.iter().map(|(d, _)| d.name.clone()).collect(), - }))?; + sender + .send(BenchEvent::Plan(BenchPlan { + origin: specifier.to_string(), + total: benchmarks.len(), + used_only, + names: benchmarks.iter().map(|(d, _)| d.name.clone()).collect(), + })) + .map_err(JsErrorBox::from_err)?; for (desc, function) in benchmarks { - sender.send(BenchEvent::Wait(desc.id))?; + sender + .send(BenchEvent::Wait(desc.id)) + .map_err(JsErrorBox::from_err)?; let call = worker.js_runtime.call(&function); let result = worker .js_runtime @@ -243,8 +248,11 @@ async fn bench_specifier_inner( .await?; let scope = &mut worker.js_runtime.handle_scope(); let result = v8::Local::new(scope, result); - let result = serde_v8::from_v8::(scope, result)?; - sender.send(BenchEvent::Result(desc.id, result))?; + let result = serde_v8::from_v8::(scope, result) + .map_err(JsErrorBox::from_err)?; + sender + .send(BenchEvent::Result(desc.id, result)) + .map_err(JsErrorBox::from_err)?; } // Ignore `defaultPrevented` of the `beforeunload` event. We don't allow the @@ -265,7 +273,7 @@ async fn bench_specifier_inner( async fn bench_specifiers( worker_factory: Arc, permissions: &Permissions, - permissions_desc_parser: &Arc, + permissions_desc_parser: &Arc>, specifiers: Vec, options: BenchSpecifierOptions, ) -> Result<(), AnyError> { @@ -354,13 +362,13 @@ async fn bench_specifiers( reporter.report_end(&report); if used_only { - return Err(generic_error( + return Err(anyhow!( "Bench failed because the \"only\" option was used", )); } if report.failed > 0 { - return Err(generic_error("Bench failed")); + return Err(anyhow!("Bench failed")); } Ok(()) @@ -438,7 +446,7 @@ pub async fn run_benchmarks( .collect::>(); if specifiers.is_empty() { - return Err(generic_error("No bench modules found")); + return Err(anyhow!("No bench modules found")); } let main_graph_container = factory.main_module_graph_container().await?; diff --git a/cli/tools/bench/reporters.rs b/cli/tools/bench/reporters.rs index 9aabd760b3..68a0c56bce 100644 --- a/cli/tools/bench/reporters.rs +++ b/cli/tools/bench/reporters.rs @@ -1,12 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use serde::Serialize; +use super::*; use crate::tools::test::TestFailureFormatOptions; use crate::version; -use super::*; - pub trait BenchReporter { fn report_group_summary(&mut self); fn report_plan(&mut self, plan: &BenchPlan); diff --git a/cli/tools/check.rs b/cli/tools/check.rs index ad5c7c3ab1..8c584113cb 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::collections::VecDeque; @@ -6,10 +6,13 @@ use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; +use deno_config::deno_json; use deno_core::error::AnyError; +use deno_error::JsErrorBox; use deno_graph::Module; +use deno_graph::ModuleError; use deno_graph::ModuleGraph; -use deno_runtime::deno_node::NodeResolver; +use deno_graph::ModuleLoadError; use deno_terminal::colors; use once_cell::sync::Lazy; use regex::Regex; @@ -27,9 +30,13 @@ use crate::cache::Caches; use crate::cache::FastInsecureHasher; use crate::cache::TypeCheckCache; use crate::factory::CliFactory; +use crate::graph_util::maybe_additional_sloppy_imports_message; use crate::graph_util::BuildFastCheckGraphOptions; use crate::graph_util::ModuleGraphBuilder; +use crate::node::CliNodeResolver; +use crate::npm::installer::NpmInstaller; use crate::npm::CliNpmResolver; +use crate::sys::CliSys; use crate::tsc; use crate::tsc::Diagnostics; use crate::tsc::TypeCheckingCjsTracker; @@ -64,7 +71,7 @@ pub async fn check( let file = file_fetcher.fetch(&s, root_permissions).await?; let snippet_files = extract::extract_snippet_files(file)?; for snippet_file in snippet_files { - specifiers_for_typecheck.push(snippet_file.specifier.clone()); + specifiers_for_typecheck.push(snippet_file.url.clone()); file_fetcher.insert_memory_files(snippet_file); } } @@ -103,18 +110,44 @@ pub struct TypeChecker { cjs_tracker: Arc, cli_options: Arc, module_graph_builder: Arc, - node_resolver: Arc, - npm_resolver: Arc, + npm_installer: Option>, + node_resolver: Arc, + npm_resolver: CliNpmResolver, + sys: CliSys, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum CheckError { + #[class(inherit)] + #[error(transparent)] + Diagnostics(#[from] Diagnostics), + #[class(inherit)] + #[error(transparent)] + ConfigFile(#[from] deno_json::ConfigFileError), + #[class(inherit)] + #[error(transparent)] + ToMaybeJsxImportSourceConfig( + #[from] deno_json::ToMaybeJsxImportSourceConfigError, + ), + #[class(inherit)] + #[error(transparent)] + TscExec(#[from] tsc::ExecError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), } impl TypeChecker { + #[allow(clippy::too_many_arguments)] pub fn new( caches: Arc, cjs_tracker: Arc, cli_options: Arc, module_graph_builder: Arc, - node_resolver: Arc, - npm_resolver: Arc, + node_resolver: Arc, + npm_installer: Option>, + npm_resolver: CliNpmResolver, + sys: CliSys, ) -> Self { Self { caches, @@ -122,7 +155,9 @@ impl TypeChecker { cli_options, module_graph_builder, node_resolver, + npm_installer, npm_resolver, + sys, } } @@ -134,7 +169,7 @@ impl TypeChecker { &self, graph: ModuleGraph, options: CheckOptions, - ) -> Result, AnyError> { + ) -> Result, CheckError> { let (graph, mut diagnostics) = self.check_diagnostics(graph, options).await?; diagnostics.emit_warnings(); @@ -153,7 +188,30 @@ impl TypeChecker { &self, mut graph: ModuleGraph, options: CheckOptions, - ) -> Result<(Arc, Diagnostics), AnyError> { + ) -> Result<(Arc, Diagnostics), CheckError> { + fn check_state_hash(resolver: &CliNpmResolver) -> Option { + match resolver { + CliNpmResolver::Byonm(_) => { + // not feasible and probably slower to compute + None + } + CliNpmResolver::Managed(resolver) => { + // we should probably go further and check all the individual npm packages + let mut package_reqs = resolver.resolution().package_reqs(); + package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism + let mut hasher = FastInsecureHasher::new_without_deno_version(); + // ensure the cache gets busted when turning nodeModulesDir on or off + // as this could cause changes in resolution + hasher.write_hashable(resolver.root_node_modules_path().is_some()); + for (pkg_req, pkg_nv) in package_reqs { + hasher.write_hashable(&pkg_req); + hasher.write_hashable(&pkg_nv); + } + Some(hasher.finish()) + } + } + } + if !options.type_check_mode.is_true() || graph.roots.is_empty() { return Ok((graph.into(), Default::default())); } @@ -161,9 +219,9 @@ impl TypeChecker { // node built-in specifiers use the @types/node package to determine // types, so inject that now (the caller should do this after the lockfile // has been written) - if let Some(npm_resolver) = self.npm_resolver.as_managed() { + if let Some(npm_installer) = &self.npm_installer { if graph.has_node_specifier { - npm_resolver.inject_synthetic_types_node_package().await?; + npm_installer.inject_synthetic_types_node_package().await?; } } @@ -177,26 +235,47 @@ impl TypeChecker { let type_check_mode = options.type_check_mode; let ts_config = ts_config_result.ts_config; - let maybe_check_hash = match self.npm_resolver.check_state_hash() { - Some(npm_check_hash) => { - match get_check_hash( - &graph, - npm_check_hash, - type_check_mode, - &ts_config, - ) { - CheckHashResult::NoFiles => { - return Ok((graph.into(), Default::default())) - } - CheckHashResult::Hash(hash) => Some(hash), - } - } - None => None, // we can't determine a check hash - }; - - // do not type check if we know this is type checked let cache = TypeCheckCache::new(self.caches.type_checking_cache_db()); + let check_js = ts_config.get_check_js(); + + // add fast check to the graph before getting the roots + if options.build_fast_check_graph { + self.module_graph_builder.build_fast_check_graph( + &mut graph, + BuildFastCheckGraphOptions { + workspace_fast_check: deno_graph::WorkspaceFastCheckOption::Disabled, + }, + )?; + } + + let filter_remote_diagnostics = |d: &tsc::Diagnostic| { + if self.is_remote_diagnostic(d) { + type_check_mode == TypeCheckMode::All && d.include_when_remote() + } else { + true + } + }; + let TscRoots { + roots: root_names, + missing_diagnostics, + maybe_check_hash, + } = get_tsc_roots( + &self.sys, + &graph, + check_js, + check_state_hash(&self.npm_resolver), + type_check_mode, + &ts_config, + ); + + let missing_diagnostics = + missing_diagnostics.filter(filter_remote_diagnostics); + + if root_names.is_empty() && missing_diagnostics.is_empty() { + return Ok((graph.into(), Default::default())); + } if !options.reload { + // do not type check if we know this is type checked if let Some(check_hash) = maybe_check_hash { if cache.has_check_hash(check_hash) { log::debug!("Already type checked."); @@ -214,7 +293,6 @@ impl TypeChecker { ); } - let check_js = ts_config.get_check_js(); // while there might be multiple roots, we can't "merge" the build info, so we // try to retrieve the build info for first root, which is the most common use // case. @@ -226,27 +304,15 @@ impl TypeChecker { // to make tsc build info work, we need to consistently hash modules, so that // tsc can better determine if an emit is still valid or not, so we provide // that data here. - let hash_data = FastInsecureHasher::new_deno_versioned() + let tsconfig_hash_data = FastInsecureHasher::new_deno_versioned() .write(&ts_config.as_bytes()) .finish(); - - // add fast check to the graph before getting the roots - if options.build_fast_check_graph { - self.module_graph_builder.build_fast_check_graph( - &mut graph, - BuildFastCheckGraphOptions { - workspace_fast_check: deno_graph::WorkspaceFastCheckOption::Disabled, - }, - )?; - } - - let root_names = get_tsc_roots(&graph, check_js); let graph = Arc::new(graph); let response = tsc::exec(tsc::Request { config: ts_config, debug: self.cli_options.log_level() == Some(log::Level::Debug), graph: graph.clone(), - hash_data, + hash_data: tsconfig_hash_data, maybe_npm: Some(tsc::RequestNpmState { cjs_tracker: self.cjs_tracker.clone(), node_resolver: self.node_resolver.clone(), @@ -257,13 +323,11 @@ impl TypeChecker { check_mode: type_check_mode, })?; - let mut diagnostics = response.diagnostics.filter(|d| { - if self.is_remote_diagnostic(d) { - type_check_mode == TypeCheckMode::All && d.include_when_remote() - } else { - true - } - }); + let response_diagnostics = + response.diagnostics.filter(filter_remote_diagnostics); + + let mut diagnostics = missing_diagnostics; + diagnostics.extend(response_diagnostics); diagnostics.apply_fast_check_source_maps(&graph); @@ -297,108 +361,10 @@ impl TypeChecker { } } -enum CheckHashResult { - Hash(CacheDBHash), - NoFiles, -} - -/// Gets a hash of the inputs for type checking. This can then -/// be used to tell -fn get_check_hash( - graph: &ModuleGraph, - package_reqs_hash: u64, - type_check_mode: TypeCheckMode, - ts_config: &TsConfig, -) -> CheckHashResult { - let mut hasher = FastInsecureHasher::new_deno_versioned(); - hasher.write_u8(match type_check_mode { - TypeCheckMode::All => 0, - TypeCheckMode::Local => 1, - TypeCheckMode::None => 2, - }); - hasher.write(&ts_config.as_bytes()); - - let check_js = ts_config.get_check_js(); - let mut has_file = false; - let mut has_file_to_type_check = false; - // this iterator of modules is already deterministic, so no need to sort it - for module in graph.modules() { - match module { - Module::Js(module) => { - let ts_check = has_ts_check(module.media_type, &module.source); - if ts_check { - has_file_to_type_check = true; - } - - match module.media_type { - MediaType::TypeScript - | MediaType::Dts - | MediaType::Dmts - | MediaType::Dcts - | MediaType::Mts - | MediaType::Cts - | MediaType::Tsx => { - has_file = true; - has_file_to_type_check = true; - } - MediaType::JavaScript - | MediaType::Mjs - | MediaType::Cjs - | MediaType::Jsx => { - has_file = true; - if !check_js && !ts_check { - continue; - } - } - MediaType::Json - | MediaType::Css - | MediaType::SourceMap - | MediaType::Wasm - | MediaType::Unknown => continue, - } - - hasher.write_str(module.specifier.as_str()); - hasher.write_str( - // the fast check module will only be set when publishing - module - .fast_check_module() - .map(|s| s.source.as_ref()) - .unwrap_or(&module.source), - ); - } - Module::Node(_) => { - // the @types/node package will be in the resolved - // snapshot below so don't bother including it here - } - Module::Npm(_) => { - // don't bother adding this specifier to the hash - // because what matters is the resolved npm snapshot, - // which is hashed below - } - Module::Json(module) => { - has_file_to_type_check = true; - hasher.write_str(module.specifier.as_str()); - hasher.write_str(&module.source); - } - Module::Wasm(module) => { - has_file_to_type_check = true; - hasher.write_str(module.specifier.as_str()); - hasher.write_str(&module.source_dts); - } - Module::External(module) => { - hasher.write_str(module.specifier.as_str()); - } - } - } - - hasher.write_hashable(package_reqs_hash); - - if !has_file || !check_js && !has_file_to_type_check { - // no files to type check - CheckHashResult::NoFiles - } else { - CheckHashResult::Hash(CacheDBHash::new(hasher.finish())) - } +struct TscRoots { + roots: Vec<(ModuleSpecifier, MediaType)>, + missing_diagnostics: tsc::Diagnostics, + maybe_check_hash: Option, } /// Transform the graph into root specifiers that we can feed `tsc`. We have to @@ -408,52 +374,115 @@ fn get_check_hash( /// the roots, so they get type checked and optionally emitted, /// otherwise they would be ignored if only imported into JavaScript. fn get_tsc_roots( + sys: &CliSys, graph: &ModuleGraph, check_js: bool, -) -> Vec<(ModuleSpecifier, MediaType)> { + npm_cache_state_hash: Option, + type_check_mode: TypeCheckMode, + ts_config: &TsConfig, +) -> TscRoots { fn maybe_get_check_entry( module: &deno_graph::Module, check_js: bool, + hasher: Option<&mut FastInsecureHasher>, ) -> Option<(ModuleSpecifier, MediaType)> { match module { - Module::Js(module) => match module.media_type { - MediaType::TypeScript - | MediaType::Tsx - | MediaType::Mts - | MediaType::Cts - | MediaType::Dts - | MediaType::Dmts - | MediaType::Dcts => { - Some((module.specifier.clone(), module.media_type)) - } - MediaType::JavaScript - | MediaType::Mjs - | MediaType::Cjs - | MediaType::Jsx => { - if check_js || has_ts_check(module.media_type, &module.source) { + Module::Js(module) => { + let result = match module.media_type { + MediaType::TypeScript + | MediaType::Tsx + | MediaType::Mts + | MediaType::Cts + | MediaType::Dts + | MediaType::Dmts + | MediaType::Dcts => { Some((module.specifier.clone(), module.media_type)) - } else { - None + } + MediaType::JavaScript + | MediaType::Mjs + | MediaType::Cjs + | MediaType::Jsx => { + if check_js || has_ts_check(module.media_type, &module.source) { + Some((module.specifier.clone(), module.media_type)) + } else { + None + } + } + MediaType::Json + | MediaType::Wasm + | MediaType::Css + | MediaType::SourceMap + | MediaType::Unknown => None, + }; + if result.is_some() { + if let Some(hasher) = hasher { + hasher.write_str(module.specifier.as_str()); + hasher.write_str( + // the fast check module will only be set when publishing + module + .fast_check_module() + .map(|s| s.source.as_ref()) + .unwrap_or(&module.source), + ); } } - MediaType::Json - | MediaType::Wasm - | MediaType::Css - | MediaType::SourceMap - | MediaType::Unknown => None, - }, - Module::Wasm(module) => Some((module.specifier.clone(), MediaType::Dmts)), - Module::External(_) - | Module::Node(_) - | Module::Npm(_) - | Module::Json(_) => None, + result + } + Module::Node(_) => { + // the @types/node package will be in the resolved + // snapshot so don't bother including it in the hash + None + } + Module::Npm(_) => { + // don't bother adding this specifier to the hash + // because what matters is the resolved npm snapshot, + // which is hashed below + None + } + Module::Json(module) => { + if let Some(hasher) = hasher { + hasher.write_str(module.specifier.as_str()); + hasher.write_str(&module.source); + } + None + } + Module::Wasm(module) => { + if let Some(hasher) = hasher { + hasher.write_str(module.specifier.as_str()); + hasher.write_str(&module.source_dts); + } + Some((module.specifier.clone(), MediaType::Dmts)) + } + Module::External(module) => { + if let Some(hasher) = hasher { + hasher.write_str(module.specifier.as_str()); + } + None + } } } - let mut result = Vec::with_capacity(graph.specifiers_count()); + let mut result = TscRoots { + roots: Vec::with_capacity(graph.specifiers_count()), + missing_diagnostics: Default::default(), + maybe_check_hash: None, + }; + let mut maybe_hasher = npm_cache_state_hash.map(|npm_cache_state_hash| { + let mut hasher = FastInsecureHasher::new_deno_versioned(); + hasher.write_hashable(npm_cache_state_hash); + hasher.write_u8(match type_check_mode { + TypeCheckMode::All => 0, + TypeCheckMode::Local => 1, + TypeCheckMode::None => 2, + }); + hasher.write_hashable(graph.has_node_specifier); + hasher.write(&ts_config.as_bytes()); + hasher + }); + if graph.has_node_specifier { // inject a specifier that will resolve node types - result.push(( + result.roots.push(( ModuleSpecifier::parse("asset:///node_types.d.ts").unwrap(), MediaType::Dts, )); @@ -464,65 +493,134 @@ fn get_tsc_roots( let mut pending = VecDeque::new(); // put in the global types first so that they're resolved before anything else - for import in graph.imports.values() { - for dep in import.dependencies.values() { - let specifier = dep.get_type().or_else(|| dep.get_code()); - if let Some(specifier) = &specifier { - let specifier = graph.resolve(specifier); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); - } - } + let get_import_specifiers = || { + graph + .imports + .values() + .flat_map(|i| i.dependencies.values()) + .filter_map(|dep| dep.get_type().or_else(|| dep.get_code())) + }; + for specifier in get_import_specifiers() { + let specifier = graph.resolve(specifier); + if seen.insert(specifier) { + pending.push_back((specifier, false)); } } // then the roots for root in &graph.roots { let specifier = graph.resolve(root); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); + if seen.insert(specifier) { + pending.push_back((specifier, false)); } } // now walk the graph that only includes the fast check dependencies - while let Some(specifier) = pending.pop_front() { - let Some(module) = graph.get(specifier) else { - continue; + while let Some((specifier, is_dynamic)) = pending.pop_front() { + let module = match graph.try_get(specifier) { + Ok(Some(module)) => module, + Ok(None) => continue, + Err(ModuleError::Missing(specifier, maybe_range)) => { + if !is_dynamic { + result + .missing_diagnostics + .push(tsc::Diagnostic::from_missing_error( + specifier, + maybe_range.as_ref(), + maybe_additional_sloppy_imports_message(sys, specifier), + )); + } + continue; + } + Err(ModuleError::LoadingErr( + specifier, + maybe_range, + ModuleLoadError::Loader(_), + )) => { + // these will be errors like attempting to load a directory + if !is_dynamic { + result + .missing_diagnostics + .push(tsc::Diagnostic::from_missing_error( + specifier, + maybe_range.as_ref(), + maybe_additional_sloppy_imports_message(sys, specifier), + )); + } + continue; + } + Err(_) => continue, }; - if let Some(entry) = maybe_get_check_entry(module, check_js) { - result.push(entry); + if is_dynamic && !seen.insert(specifier) { + continue; } - if let Some(module) = module.js() { - let deps = module.dependencies_prefer_fast_check(); + if let Some(entry) = + maybe_get_check_entry(module, check_js, maybe_hasher.as_mut()) + { + result.roots.push(entry); + } + + let mut maybe_module_dependencies = None; + let mut maybe_types_dependency = None; + if let Module::Js(module) = module { + maybe_module_dependencies = Some(module.dependencies_prefer_fast_check()); + maybe_types_dependency = module + .maybe_types_dependency + .as_ref() + .and_then(|d| d.dependency.ok()); + } else if let Module::Wasm(module) = module { + maybe_module_dependencies = Some(&module.dependencies); + } + + fn handle_specifier<'a>( + graph: &'a ModuleGraph, + seen: &mut HashSet<&'a ModuleSpecifier>, + pending: &mut VecDeque<(&'a ModuleSpecifier, bool)>, + specifier: &'a ModuleSpecifier, + is_dynamic: bool, + ) { + let specifier = graph.resolve(specifier); + if is_dynamic { + if !seen.contains(specifier) { + pending.push_back((specifier, true)); + } + } else if seen.insert(specifier) { + pending.push_back((specifier, false)); + } + } + + if let Some(deps) = maybe_module_dependencies { for dep in deps.values() { // walk both the code and type dependencies if let Some(specifier) = dep.get_code() { - let specifier = graph.resolve(specifier); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); - } + handle_specifier( + graph, + &mut seen, + &mut pending, + specifier, + dep.is_dynamic, + ); } if let Some(specifier) = dep.get_type() { - let specifier = graph.resolve(specifier); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); - } - } - } - - if let Some(dep) = module - .maybe_types_dependency - .as_ref() - .and_then(|d| d.dependency.ok()) - { - let specifier = graph.resolve(&dep.specifier); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); + handle_specifier( + graph, + &mut seen, + &mut pending, + specifier, + dep.is_dynamic, + ); } } } + + if let Some(dep) = maybe_types_dependency { + handle_specifier(graph, &mut seen, &mut pending, &dep.specifier, false); + } } + result.maybe_check_hash = + maybe_hasher.map(|hasher| CacheDBHash::new(hasher.finish())); + result } diff --git a/cli/tools/clean.rs b/cli/tools/clean.rs index 2a77434f88..e6f8c1e52b 100644 --- a/cli/tools/clean.rs +++ b/cli/tools/clean.rs @@ -1,12 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::path::Path; use deno_core::anyhow::Context; use deno_core::error::AnyError; -use std::path::Path; use crate::cache::DenoDir; use crate::colors; use crate::display; +use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressMessagePrompt; @@ -28,7 +30,7 @@ impl CleanState { } pub fn clean() -> Result<(), AnyError> { - let deno_dir = DenoDir::new(None)?; + let deno_dir = DenoDir::new(CliSys::default(), None)?; if deno_dir.root.exists() { let no_of_files = walkdir::WalkDir::new(&deno_dir.root).into_iter().count(); let progress_bar = ProgressBar::new(ProgressBarStyle::ProgressBars); diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index 7a463a7b09..96dd6798f5 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -1,17 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashSet; +use std::collections::VecDeque; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; -use crate::args::check_warn_tsconfig; -use crate::args::CompileFlags; -use crate::args::Flags; -use crate::factory::CliFactory; -use crate::http_util::HttpClientProvider; -use crate::standalone::binary::WriteBinOptions; -use crate::standalone::is_standalone_binary; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; +use deno_core::anyhow::anyhow; use deno_core::anyhow::bail; use deno_core::anyhow::Context; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_graph::GraphKind; @@ -19,13 +18,15 @@ use deno_path_util::url_from_file_path; use deno_path_util::url_to_file_path; use deno_terminal::colors; use rand::Rng; -use std::collections::HashSet; -use std::collections::VecDeque; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use super::installer::infer_name_from_url; +use crate::args::check_warn_tsconfig; +use crate::args::CompileFlags; +use crate::args::Flags; +use crate::factory::CliFactory; +use crate::http_util::HttpClientProvider; +use crate::standalone::binary::is_standalone_binary; +use crate::standalone::binary::WriteBinOptions; pub async fn compile( flags: Arc, @@ -329,7 +330,7 @@ async fn resolve_compile_executable_output_path( .map(PathBuf::from) } - output_path.ok_or_else(|| generic_error( + output_path.ok_or_else(|| anyhow!( "An executable name was not provided. One could not be inferred from the URL. Aborting.", )).map(|output_path| { get_os_specific_filepath(output_path, &compile_flags.target) diff --git a/cli/tools/coverage/merge.rs b/cli/tools/coverage/merge.rs index 81317df559..9c898e78d3 100644 --- a/cli/tools/coverage/merge.rs +++ b/cli/tools/coverage/merge.rs @@ -1,16 +1,17 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Forked from https://github.com/demurgos/v8-coverage/tree/d0ca18da8740198681e0bc68971b0a6cdb11db3e/rust // Copyright 2021 Charles Samborski. All rights reserved. MIT license. -use super::range_tree::RangeTree; -use super::range_tree::RangeTreeArena; -use crate::cdp; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::HashMap; use std::iter::Peekable; +use super::range_tree::RangeTree; +use super::range_tree::RangeTreeArena; +use crate::cdp; + #[derive(Eq, PartialEq, Clone, Debug)] pub struct ProcessCoverage { pub result: Vec, diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 2a554c1335..9b6ef81ea3 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -1,14 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::args::CliOptions; -use crate::args::CoverageFlags; -use crate::args::FileFlags; -use crate::args::Flags; -use crate::cdp; -use crate::factory::CliFactory; -use crate::tools::fmt::format_json; -use crate::tools::test::is_supported_test_path; -use crate::util::text_encoding::source_map_from_code; +use std::fs; +use std::fs::File; +use std::io::BufWriter; +use std::io::Write; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleKind; @@ -19,24 +17,29 @@ use deno_config::glob::PathOrPattern; use deno_config::glob::PathOrPatternSet; use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::sourcemap::SourceMap; use deno_core::url::Url; use deno_core::LocalInspectorSession; +use deno_resolver::npm::DenoInNpmPackageChecker; use node_resolver::InNpmPackageChecker; use regex::Regex; -use std::fs; -use std::fs::File; -use std::io::BufWriter; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use text_lines::TextLines; use uuid::Uuid; +use crate::args::CliOptions; +use crate::args::CoverageFlags; +use crate::args::FileFlags; +use crate::args::Flags; +use crate::cdp; +use crate::factory::CliFactory; +use crate::file_fetcher::TextDecodedFile; +use crate::sys::CliSys; +use crate::tools::fmt::format_json; +use crate::tools::test::is_supported_test_path; +use crate::util::text_encoding::source_map_from_code; + mod merge; mod range_tree; mod reporter; @@ -197,7 +200,7 @@ pub struct CoverageReport { fn generate_coverage_report( script_coverage: &cdp::ScriptCoverage, script_source: String, - maybe_source_map: &Option>, + maybe_source_map: Option<&[u8]>, output: &Option, ) -> CoverageReport { let maybe_source_map = maybe_source_map @@ -427,7 +430,7 @@ fn collect_coverages( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, file_patterns)?; + .collect_file_patterns(&CliSys::default(), file_patterns); let coverage_patterns = FilePatterns { base: initial_cwd.to_path_buf(), @@ -462,7 +465,7 @@ fn filter_coverages( coverages: Vec, include: Vec, exclude: Vec, - in_npm_pkg_checker: &dyn InNpmPackageChecker, + in_npm_pkg_checker: &DenoInNpmPackageChecker, ) -> Vec { let include: Vec = include.iter().map(|e| Regex::new(e).unwrap()).collect(); @@ -502,7 +505,7 @@ pub fn cover_files( coverage_flags: CoverageFlags, ) -> Result<(), AnyError> { if coverage_flags.files.include.is_empty() { - return Err(generic_error("No matching coverage profiles found")); + return Err(anyhow!("No matching coverage profiles found")); } let factory = CliFactory::from_flags(flags); @@ -524,16 +527,16 @@ pub fn cover_files( cli_options.initial_cwd(), )?; if script_coverages.is_empty() { - return Err(generic_error("No coverage files found")); + return Err(anyhow!("No coverage files found")); } let script_coverages = filter_coverages( script_coverages, coverage_flags.include, coverage_flags.exclude, - in_npm_pkg_checker.as_ref(), + in_npm_pkg_checker, ); if script_coverages.is_empty() { - return Err(generic_error("No covered files included in the report")); + return Err(anyhow!("No covered files included in the report")); } let proc_coverages: Vec<_> = script_coverages @@ -559,6 +562,12 @@ pub fn cover_files( }, None => None, }; + let get_message = |specifier: &ModuleSpecifier| -> String { + format!( + "Failed to fetch \"{}\" from cache. Before generating coverage report, run `deno test --coverage` to ensure consistent state.", + specifier, + ) + }; for script_coverage in script_coverages { let module_specifier = deno_core::resolve_url_or_path( @@ -566,21 +575,14 @@ pub fn cover_files( cli_options.initial_cwd(), )?; - let maybe_file = if module_specifier.scheme() == "file" { - file_fetcher.get_source(&module_specifier) - } else { - file_fetcher - .fetch_cached(&module_specifier, 10) - .with_context(|| { - format!("Failed to fetch \"{module_specifier}\" from cache.") - })? + let maybe_file_result = file_fetcher + .get_cached_source_or_local(&module_specifier) + .map_err(AnyError::from); + let file = match maybe_file_result { + Ok(Some(file)) => TextDecodedFile::decode(file)?, + Ok(None) => return Err(anyhow!("{}", get_message(&module_specifier))), + Err(err) => return Err(err).context(get_message(&module_specifier)), }; - let file = maybe_file.ok_or_else(|| { - anyhow!("Failed to fetch \"{}\" from cache. - Before generating coverage report, run `deno test --coverage` to ensure consistent state.", - module_specifier - ) - })?.into_text_decoded()?; let original_source = file.source.clone(); // Check if file was transpiled @@ -625,7 +627,7 @@ pub fn cover_files( let coverage_report = generate_coverage_report( &script_coverage, runtime_code.as_str().to_owned(), - &source_map, + source_map.as_deref(), &out_mode, ); diff --git a/cli/tools/coverage/range_tree.rs b/cli/tools/coverage/range_tree.rs index bca52844c0..08ac914cd2 100644 --- a/cli/tools/coverage/range_tree.rs +++ b/cli/tools/coverage/range_tree.rs @@ -1,12 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Forked from https://github.com/demurgos/v8-coverage/tree/d0ca18da8740198681e0bc68971b0a6cdb11db3e/rust // Copyright 2021 Charles Samborski. All rights reserved. MIT license. -use crate::cdp; use std::iter::Peekable; + use typed_arena::Arena; +use crate::cdp; + pub struct RangeTreeArena<'a>(Arena>); impl<'a> RangeTreeArena<'a> { diff --git a/cli/tools/coverage/reporter.rs b/cli/tools/coverage/reporter.rs index 6b0e5c885e..bc6e85b47e 100644 --- a/cli/tools/coverage/reporter.rs +++ b/cli/tools/coverage/reporter.rs @@ -1,11 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::util; -use super::CoverageReport; -use crate::args::CoverageType; -use crate::colors; -use deno_core::error::AnyError; -use deno_core::url::Url; use std::collections::HashMap; use std::fs; use std::fs::File; @@ -15,6 +9,14 @@ use std::io::{self}; use std::path::Path; use std::path::PathBuf; +use deno_core::error::AnyError; +use deno_core::url::Url; + +use super::util; +use super::CoverageReport; +use crate::args::CoverageType; +use crate::colors; + #[derive(Default)] pub struct CoverageStats<'a> { pub line_hit: usize, diff --git a/cli/tools/coverage/util.rs b/cli/tools/coverage/util.rs index e9518e1f78..e61830b7fe 100644 --- a/cli/tools/coverage/util.rs +++ b/cli/tools/coverage/util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::url::Url; diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 647a36dc48..114c8f958a 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -1,17 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::BTreeMap; +use std::rc::Rc; +use std::sync::Arc; -use crate::args::DocFlags; -use crate::args::DocHtmlFlag; -use crate::args::DocSourceFileFlag; -use crate::args::Flags; -use crate::colors; -use crate::display; -use crate::factory::CliFactory; -use crate::graph_util::graph_exit_integrity_errors; -use crate::graph_util::graph_walk_errors; -use crate::graph_util::GraphWalkErrorsOptions; -use crate::tsc::get_types_declaration_file_text; -use crate::util::fs::collect_specifiers; use deno_ast::diagnostics::Diagnostic; use deno_config::glob::FilePatterns; use deno_config::glob::PathOrPatternSet; @@ -31,9 +23,20 @@ use deno_graph::ModuleSpecifier; use doc::html::ShortPath; use doc::DocDiagnostic; use indexmap::IndexMap; -use std::collections::BTreeMap; -use std::rc::Rc; -use std::sync::Arc; + +use crate::args::DocFlags; +use crate::args::DocHtmlFlag; +use crate::args::DocSourceFileFlag; +use crate::args::Flags; +use crate::colors; +use crate::display; +use crate::factory::CliFactory; +use crate::graph_util::graph_exit_integrity_errors; +use crate::graph_util::graph_walk_errors; +use crate::graph_util::GraphWalkErrorsOptions; +use crate::sys::CliSys; +use crate::tsc::get_types_declaration_file_text; +use crate::util::fs::collect_specifiers; const JSON_SCHEMA_VERSION: u8 = 1; @@ -59,10 +62,11 @@ async fn generate_doc_nodes_for_builtin_types( )], Vec::new(), ); + let roots = vec![source_file_specifier.clone()]; let mut graph = deno_graph::ModuleGraph::new(GraphKind::TypesOnly); graph .build( - vec![source_file_specifier.clone()], + roots.clone(), &loader, deno_graph::BuildOptions { imports: Vec::new(), @@ -82,14 +86,13 @@ async fn generate_doc_nodes_for_builtin_types( let doc_parser = doc::DocParser::new( &graph, parser, + &roots, doc::DocParserOptions { diagnostics: false, private: doc_flags.private, }, )?; - let nodes = doc_parser.parse_module(&source_file_specifier)?.definitions; - - Ok(IndexMap::from([(source_file_specifier, nodes)])) + Ok(doc_parser.parse()?) } pub async fn doc( @@ -114,7 +117,7 @@ pub async fn doc( } DocSourceFileFlag::Paths(ref source_files) => { let module_graph_creator = factory.module_graph_creator().await?; - let fs = factory.fs(); + let sys = CliSys::default(); let module_specifiers = collect_specifiers( FilePatterns { @@ -141,7 +144,7 @@ pub async fn doc( graph_exit_integrity_errors(&graph); let errors = graph_walk_errors( &graph, - fs, + &sys, &module_specifiers, GraphWalkErrorsOptions { check_js: false, @@ -155,19 +158,13 @@ pub async fn doc( let doc_parser = doc::DocParser::new( &graph, &capturing_parser, + &module_specifiers, doc::DocParserOptions { private: doc_flags.private, diagnostics: doc_flags.lint, }, )?; - - let mut doc_nodes_by_url = - IndexMap::with_capacity(module_specifiers.len()); - - for module_specifier in module_specifiers { - let nodes = doc_parser.parse_with_reexports(&module_specifier)?; - doc_nodes_by_url.insert(module_specifier, nodes); - } + let doc_nodes_by_url = doc_parser.parse()?; if doc_flags.lint { let diagnostics = doc_parser.take_diagnostics(); @@ -188,29 +185,9 @@ pub async fn doc( .await?; let (_, deno_ns) = deno_ns.into_iter().next().unwrap(); - let short_path = Rc::new(ShortPath::new( - ModuleSpecifier::parse("file:///lib.deno.d.ts").unwrap(), - None, - None, - None, - )); - - deno_doc::html::compute_namespaced_symbols( - &deno_ns - .into_iter() - .map(|node| deno_doc::html::DocNodeWithContext { - origin: short_path.clone(), - ns_qualifiers: Rc::new([]), - kind_with_drilldown: - deno_doc::html::DocNodeKindWithDrilldown::Other(node.kind()), - inner: Rc::new(node), - drilldown_name: None, - parent: None, - }) - .collect::>(), - ) + Some(deno_ns) } else { - Default::default() + None }; let mut main_entrypoint = None; @@ -343,14 +320,14 @@ impl deno_doc::html::HrefResolver for DocResolver { let name = &res.req().name; Some(( format!("https://www.npmjs.com/package/{name}"), - name.to_owned(), + name.to_string(), )) } "jsr" => { let res = deno_semver::jsr::JsrPackageReqReference::from_str(module).ok()?; let name = &res.req().name; - Some((format!("https://jsr.io/{name}"), name.to_owned())) + Some((format!("https://jsr.io/{name}"), name.to_string())) } _ => None, } @@ -390,7 +367,7 @@ impl UsageComposer for DocComposer { fn generate_docs_directory( doc_nodes_by_url: IndexMap>, html_options: &DocHtmlFlag, - deno_ns: std::collections::HashMap, Option>>, + built_in_types: Option>, rewrite_map: Option>, main_entrypoint: Option, ) -> Result<(), AnyError> { @@ -423,12 +400,12 @@ fn generate_docs_directory( None }; - let options = deno_doc::html::GenerateOptions { + let mut options = deno_doc::html::GenerateOptions { package_name: html_options.name.clone(), main_entrypoint, rewrite_map, href_resolver: Rc::new(DocResolver { - deno_ns, + deno_ns: Default::default(), strip_trailing_html: html_options.strip_trailing_html, }), usage_composer: Rc::new(DocComposer), @@ -448,7 +425,58 @@ fn generate_docs_directory( })), }; - let mut files = deno_doc::html::generate(options, doc_nodes_by_url) + if let Some(built_in_types) = built_in_types { + let ctx = deno_doc::html::GenerateCtx::create_basic( + deno_doc::html::GenerateOptions { + package_name: None, + main_entrypoint: Some( + ModuleSpecifier::parse("file:///lib.deno.d.ts").unwrap(), + ), + href_resolver: Rc::new(DocResolver { + deno_ns: Default::default(), + strip_trailing_html: false, + }), + usage_composer: Rc::new(DocComposer), + rewrite_map: Default::default(), + category_docs: Default::default(), + disable_search: Default::default(), + symbol_redirect_map: Default::default(), + default_symbol_map: Default::default(), + markdown_renderer: deno_doc::html::comrak::create_renderer( + None, None, None, + ), + markdown_stripper: Rc::new(deno_doc::html::comrak::strip), + head_inject: None, + }, + IndexMap::from([( + ModuleSpecifier::parse("file:///lib.deno.d.ts").unwrap(), + built_in_types, + )]), + )?; + + let deno_ns = deno_doc::html::compute_namespaced_symbols( + &ctx, + Box::new( + ctx + .doc_nodes + .values() + .next() + .unwrap() + .iter() + .map(std::borrow::Cow::Borrowed), + ), + ); + + options.href_resolver = Rc::new(DocResolver { + deno_ns, + strip_trailing_html: html_options.strip_trailing_html, + }); + } + + let ctx = + deno_doc::html::GenerateCtx::create_basic(options, doc_nodes_by_url)?; + + let mut files = deno_doc::html::generate(ctx) .context("Failed to generate HTML documentation")?; files.insert("prism.js".to_string(), PRISM_JS.to_string()); diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index e29627345c..d0948fd4f7 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This module provides file formatting utilities using //! [`dprint-plugin-typescript`](https://github.com/dprint/dprint-plugin-typescript). @@ -7,36 +7,6 @@ //! the future it can be easily extended to provide //! the same functions as ops available in JS runtime. -use crate::args::CliOptions; -use crate::args::Flags; -use crate::args::FmtFlags; -use crate::args::FmtOptions; -use crate::args::FmtOptionsConfig; -use crate::args::ProseWrap; -use crate::args::UnstableFmtOptions; -use crate::cache::Caches; -use crate::colors; -use crate::factory::CliFactory; -use crate::util::diff::diff; -use crate::util::file_watcher; -use crate::util::fs::canonicalize_path; -use crate::util::path::get_extension; -use async_trait::async_trait; -use deno_ast::ParsedSource; -use deno_config::glob::FileCollector; -use deno_config::glob::FilePatterns; -use deno_core::anyhow::anyhow; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::generic_error; -use deno_core::error::AnyError; -use deno_core::futures; -use deno_core::parking_lot::Mutex; -use deno_core::unsync::spawn_blocking; -use deno_core::url::Url; -use log::debug; -use log::info; -use log::warn; use std::borrow::Cow; use std::fs; use std::io::stdin; @@ -49,7 +19,39 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::sync::Arc; +use async_trait::async_trait; +use deno_ast::ParsedSource; +use deno_config::glob::FileCollector; +use deno_config::glob::FilePatterns; +use deno_core::anyhow::anyhow; +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::futures; +use deno_core::parking_lot::Mutex; +use deno_core::unsync::spawn_blocking; +use deno_core::url::Url; +use log::debug; +use log::info; +use log::warn; + +use crate::args::CliOptions; +use crate::args::Flags; +use crate::args::FmtFlags; +use crate::args::FmtOptions; +use crate::args::FmtOptionsConfig; +use crate::args::ProseWrap; +use crate::args::UnstableFmtOptions; +use crate::cache::CacheDBHash; +use crate::cache::Caches; use crate::cache::IncrementalCache; +use crate::colors; +use crate::factory::CliFactory; +use crate::sys::CliSys; +use crate::util::diff::diff; +use crate::util::file_watcher; +use crate::util::fs::canonicalize_path; +use crate::util::path::get_extension; /// Format JavaScript/TypeScript files. pub async fn format( @@ -57,7 +59,7 @@ pub async fn format( fmt_flags: FmtFlags, ) -> Result<(), AnyError> { if fmt_flags.is_stdin() { - let cli_options = CliOptions::from_flags(flags)?; + let cli_options = CliOptions::from_flags(&CliSys::default(), flags)?; let start_dir = &cli_options.start_dir; let fmt_config = start_dir .to_fmt_config(FilePatterns::new_with_base(start_dir.dir_path()))?; @@ -164,7 +166,7 @@ fn resolve_paths_with_options_batches( Vec::with_capacity(members_fmt_options.len()); for (_ctx, member_fmt_options) in members_fmt_options { let files = - collect_fmt_files(cli_options, member_fmt_options.files.clone())?; + collect_fmt_files(cli_options, member_fmt_options.files.clone()); if !files.is_empty() { paths_with_options_batches.push(PathsWithOptions { base: member_fmt_options.files.base.clone(), @@ -174,7 +176,7 @@ fn resolve_paths_with_options_batches( } } if paths_with_options_batches.is_empty() { - return Err(generic_error("No target files found.")); + return Err(anyhow!("No target files found.")); } Ok(paths_with_options_batches) } @@ -200,7 +202,7 @@ async fn format_files( let paths = paths_with_options.paths; let incremental_cache = Arc::new(IncrementalCache::new( caches.fmt_incremental_cache_db(), - &(&fmt_options.options, &fmt_options.unstable), // cache key + CacheDBHash::from_hashable((&fmt_options.options, &fmt_options.unstable)), &paths, )); formatter @@ -221,7 +223,7 @@ async fn format_files( fn collect_fmt_files( cli_options: &CliOptions, files: FilePatterns, -) -> Result, AnyError> { +) -> Vec { FileCollector::new(|e| { is_supported_ext_fmt(e.path) || (e.path.extension().is_none() && cli_options.ext_flag().is_some()) @@ -230,7 +232,7 @@ fn collect_fmt_files( .ignore_node_modules() .use_gitignore() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files) + .collect_file_patterns(&CliSys::default(), files) } /// Formats markdown (using ) and its code blocks @@ -481,7 +483,7 @@ pub fn format_html( } if let Some(error_msg) = inner(&error, file_path) { - AnyError::from(generic_error(error_msg)) + AnyError::msg(error_msg) } else { AnyError::from(error) } @@ -729,9 +731,9 @@ impl Formatter for CheckFormatter { Ok(()) } else { let not_formatted_files_str = files_str(not_formatted_files_count); - Err(generic_error(format!( + Err(anyhow!( "Found {not_formatted_files_count} not formatted {not_formatted_files_str} in {checked_files_str}", - ))) + )) } } } diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 7a35f597c3..8c3b2665c5 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; @@ -11,6 +11,7 @@ use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::url; +use deno_error::JsErrorClass; use deno_graph::Dependency; use deno_graph::GraphKind; use deno_graph::Module; @@ -31,8 +32,7 @@ use crate::args::InfoFlags; use crate::display; use crate::factory::CliFactory; use crate::graph_util::graph_exit_integrity_errors; -use crate::npm::CliNpmResolver; -use crate::npm::ManagedCliNpmResolver; +use crate::npm::CliManagedNpmResolver; use crate::util::checksum; use crate::util::display::DisplayTreeNode; @@ -137,6 +137,10 @@ pub async fn info( lockfile.write_if_changed()?; } + let maybe_npm_info = npm_resolver + .as_managed() + .map(|r| (r, r.resolution().snapshot())); + if info_flags.json { let mut json_graph = serde_json::json!(graph); if let Some(output) = json_graph.as_object_mut() { @@ -147,11 +151,19 @@ pub async fn info( ); } - add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref(), npmrc); + add_npm_packages_to_json( + &mut json_graph, + maybe_npm_info.as_ref().map(|(_, s)| s), + npmrc, + ); display::write_json_to_stdout(&json_graph)?; } else { let mut output = String::new(); - GraphDisplayContext::write(&graph, npm_resolver.as_ref(), &mut output)?; + GraphDisplayContext::write( + &graph, + maybe_npm_info.as_ref().map(|(r, s)| (*r, s)), + &mut output, + )?; display::write_to_stdout_ignore_sigpipe(output.as_bytes())?; } } else { @@ -250,15 +262,14 @@ fn print_cache_info( fn add_npm_packages_to_json( json: &mut serde_json::Value, - npm_resolver: &dyn CliNpmResolver, + npm_snapshot: Option<&NpmResolutionSnapshot>, npmrc: &ResolvedNpmRc, ) { - let Some(npm_resolver) = npm_resolver.as_managed() else { + let Some(npm_snapshot) = npm_snapshot else { return; // does not include byonm to deno info's output }; // ideally deno_graph could handle this, but for now we just modify the json here - let snapshot = npm_resolver.snapshot(); let json = json.as_object_mut().unwrap(); let modules = json.get_mut("modules").and_then(|m| m.as_array_mut()); if let Some(modules) = modules { @@ -272,14 +283,16 @@ fn add_npm_packages_to_json( .and_then(|k| k.as_str()) .and_then(|specifier| NpmPackageNvReference::from_str(specifier).ok()) .and_then(|package_ref| { - snapshot + npm_snapshot .resolve_package_from_deno_module(package_ref.nv()) .ok() }); if let Some(pkg) = maybe_package { if let Some(module) = module.as_object_mut() { - module - .insert("npmPackage".to_string(), pkg.id.as_serialized().into()); + module.insert( + "npmPackage".to_string(), + pkg.id.as_serialized().into_string().into(), + ); } } } @@ -292,11 +305,12 @@ fn add_npm_packages_to_json( if let Some(specifier) = dep.get("specifier").and_then(|s| s.as_str()) { if let Ok(npm_ref) = NpmPackageReqReference::from_str(specifier) { - if let Ok(pkg) = snapshot.resolve_pkg_from_pkg_req(npm_ref.req()) + if let Ok(pkg) = + npm_snapshot.resolve_pkg_from_pkg_req(npm_ref.req()) { dep.insert( "npmPackage".to_string(), - pkg.id.as_serialized().into(), + pkg.id.as_serialized().into_string().into(), ); } } @@ -318,25 +332,26 @@ fn add_npm_packages_to_json( } } - let mut sorted_packages = - snapshot.all_packages_for_every_system().collect::>(); + let mut sorted_packages = npm_snapshot + .all_packages_for_every_system() + .collect::>(); sorted_packages.sort_by(|a, b| a.id.cmp(&b.id)); let mut json_packages = serde_json::Map::with_capacity(sorted_packages.len()); for pkg in sorted_packages { let mut kv = serde_json::Map::new(); - kv.insert("name".to_string(), pkg.id.nv.name.clone().into()); + kv.insert("name".to_string(), pkg.id.nv.name.to_string().into()); kv.insert("version".to_string(), pkg.id.nv.version.to_string().into()); let mut deps = pkg.dependencies.values().collect::>(); deps.sort(); let deps = deps .into_iter() - .map(|id| serde_json::Value::String(id.as_serialized())) + .map(|id| serde_json::Value::String(id.as_serialized().into_string())) .collect::>(); kv.insert("dependencies".to_string(), deps.into()); let registry_url = npmrc.get_registry_url(&pkg.id.nv.name); kv.insert("registryUrl".to_string(), registry_url.to_string().into()); - json_packages.insert(pkg.id.as_serialized(), kv.into()); + json_packages.insert(pkg.id.as_serialized().into_string(), kv.into()); } json.insert("npmPackages".to_string(), json_packages.into()); @@ -353,7 +368,7 @@ struct NpmInfo { impl NpmInfo { pub fn build<'a>( graph: &'a ModuleGraph, - npm_resolver: &'a ManagedCliNpmResolver, + npm_resolver: &'a CliManagedNpmResolver, npm_snapshot: &'a NpmResolutionSnapshot, ) -> Self { let mut info = NpmInfo::default(); @@ -379,12 +394,15 @@ impl NpmInfo { fn fill_package_info<'a>( &mut self, package: &NpmResolutionPackage, - npm_resolver: &'a ManagedCliNpmResolver, + npm_resolver: &'a CliManagedNpmResolver, npm_snapshot: &'a NpmResolutionSnapshot, ) { self.packages.insert(package.id.clone(), package.clone()); - if let Ok(size) = npm_resolver.package_size(&package.id) { - self.package_sizes.insert(package.id.clone(), size); + if let Ok(folder) = npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id) + { + if let Ok(size) = crate::util::fs::dir_size(&folder) { + self.package_sizes.insert(package.id.clone(), size); + } } for id in package.dependencies.values() { if !self.packages.contains_key(id) { @@ -413,13 +431,15 @@ struct GraphDisplayContext<'a> { impl<'a> GraphDisplayContext<'a> { pub fn write( graph: &'a ModuleGraph, - npm_resolver: &'a dyn CliNpmResolver, + managed_npm_info: Option<( + &'a CliManagedNpmResolver, + &'a NpmResolutionSnapshot, + )>, writer: &mut TWrite, ) -> Result<(), AnyError> { - let npm_info = match npm_resolver.as_managed() { - Some(npm_resolver) => { - let npm_snapshot = npm_resolver.snapshot(); - NpmInfo::build(graph, npm_resolver, &npm_snapshot) + let npm_info = match managed_npm_info { + Some((npm_resolver, npm_snapshot)) => { + NpmInfo::build(graph, npm_resolver, npm_snapshot) } None => NpmInfo::default(), }; @@ -549,7 +569,7 @@ impl<'a> GraphDisplayContext<'a> { None => Specifier(module.specifier().clone()), }; let was_seen = !self.seen.insert(match &package_or_specifier { - Package(package) => package.id.as_serialized(), + Package(package) => package.id.as_serialized().into_string(), Specifier(specifier) => specifier.to_string(), }); let header_text = if was_seen { @@ -631,7 +651,8 @@ impl<'a> GraphDisplayContext<'a> { )); if let Some(package) = self.npm_info.packages.get(dep_id) { if !package.dependencies.is_empty() { - let was_seen = !self.seen.insert(package.id.as_serialized()); + let was_seen = + !self.seen.insert(package.id.as_serialized().into_string()); if was_seen { child.text = format!("{} {}", child.text, colors::gray("*")); } else { @@ -661,9 +682,10 @@ impl<'a> GraphDisplayContext<'a> { HttpsChecksumIntegrity(_) => "(checksum integrity error)", Decode(_) => "(loading decode error)", Loader(err) => { - match deno_runtime::errors::get_error_class_name(err) { - Some("NotCapable") => "(not capable, requires --allow-import)", - _ => "(loading error)", + if err.get_class() == "NotCapable" { + "(not capable, requires --allow-import)" + } else { + "(loading error)" } } Jsr(_) => "(loading error)", diff --git a/cli/tools/init/mod.rs b/cli/tools/init/mod.rs index 36bdbac2bc..d077de44ce 100644 --- a/cli/tools/init/mod.rs +++ b/cli/tools/init/mod.rs @@ -1,12 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::io::IsTerminal; +use std::io::Write; +use std::path::Path; -use crate::args::DenoSubcommand; -use crate::args::Flags; -use crate::args::InitFlags; -use crate::args::PackagesAllowedScripts; -use crate::args::PermissionFlags; -use crate::args::RunFlags; -use crate::colors; use color_print::cformat; use color_print::cstr; use deno_config::deno_json::NodeModulesDirMode; @@ -15,9 +12,14 @@ use deno_core::error::AnyError; use deno_core::serde_json::json; use deno_runtime::WorkerExecutionMode; use log::info; -use std::io::IsTerminal; -use std::io::Write; -use std::path::Path; + +use crate::args::DenoSubcommand; +use crate::args::Flags; +use crate::args::InitFlags; +use crate::args::PackagesAllowedScripts; +use crate::args::PermissionFlags; +use crate::args::RunFlags; +use crate::colors; pub async fn init_project(init_flags: InitFlags) -> Result { if let Some(package) = &init_flags.package { diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index d7c484beba..596d087589 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -1,9 +1,32 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::env; +use std::fs; +use std::fs::File; +use std::io; +use std::io::Write; +#[cfg(not(windows))] +use std::os::unix::fs::PermissionsExt; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_cache_dir::file_fetcher::CacheSetting; +use deno_core::anyhow::anyhow; +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::resolve_url_or_path; +use deno_core::url::Url; +use deno_semver::npm::NpmPackageReqReference; +use log::Level; +use once_cell::sync::Lazy; +use regex::Regex; +use regex::RegexBuilder; use crate::args::resolve_no_prompt; use crate::args::AddFlags; use crate::args::CaData; -use crate::args::CacheSetting; use crate::args::ConfigFlag; use crate::args::Flags; use crate::args::InstallFlags; @@ -13,36 +36,13 @@ use crate::args::TypeCheckMode; use crate::args::UninstallFlags; use crate::args::UninstallKind; use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::graph_container::ModuleGraphContainer; use crate::http_util::HttpClientProvider; use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; use crate::util::fs::canonicalize_path_maybe_not_exists; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::generic_error; -use deno_core::error::AnyError; -use deno_core::resolve_url_or_path; -use deno_core::url::Url; -use deno_semver::npm::NpmPackageReqReference; -use log::Level; -use once_cell::sync::Lazy; -use regex::Regex; -use regex::RegexBuilder; -use std::env; -use std::fs; -use std::fs::File; -use std::io; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; - -#[cfg(not(windows))] -use std::os::unix::fs::PermissionsExt; -use std::sync::Arc; - static EXEC_NAME_RE: Lazy = Lazy::new(|| { RegexBuilder::new(r"^[a-z0-9][\w-]*$") .case_insensitive(true) @@ -54,9 +54,7 @@ fn validate_name(exec_name: &str) -> Result<(), AnyError> { if EXEC_NAME_RE.is_match(exec_name) { Ok(()) } else { - Err(generic_error(format!( - "Invalid executable name: {exec_name}" - ))) + Err(anyhow!("Invalid executable name: {exec_name}")) } } @@ -161,11 +159,11 @@ pub async fn infer_name_from_url( let npm_ref = npm_ref.into_inner(); if let Some(sub_path) = npm_ref.sub_path { if !sub_path.contains('/') { - return Some(sub_path); + return Some(sub_path.to_string()); } } if !npm_ref.req.name.contains('/') { - return Some(npm_ref.req.name); + return Some(npm_ref.req.name.into_string()); } return None; } @@ -223,7 +221,7 @@ pub async fn uninstall( // ensure directory exists if let Ok(metadata) = fs::metadata(&installation_dir) { if !metadata.is_dir() { - return Err(generic_error("Installation path is not a directory")); + return Err(anyhow!("Installation path is not a directory")); } } @@ -247,10 +245,10 @@ pub async fn uninstall( } if !removed { - return Err(generic_error(format!( + return Err(anyhow!( "No installation found for {}", uninstall_flags.name - ))); + )); } // There might be some extra files to delete @@ -302,8 +300,8 @@ 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()?; + if let Some(npm_installer) = factory.npm_installer_if_managed()? { + npm_installer.ensure_no_pkg_json_dep_errors()?; } crate::tools::registry::cache_top_level_deps(&factory, None).await?; @@ -361,18 +359,19 @@ async fn install_global( let cli_options = factory.cli_options()?; let http_client = factory.http_client_provider(); let deps_http_cache = factory.global_http_cache()?; - let mut deps_file_fetcher = FileFetcher::new( + let deps_file_fetcher = CliFileFetcher::new( deps_http_cache.clone(), - CacheSetting::ReloadAll, - true, http_client.clone(), + factory.sys(), Default::default(), None, + true, + CacheSetting::ReloadAll, + log::Level::Trace, ); let npmrc = factory.cli_options().unwrap().npmrc(); - deps_file_fetcher.set_download_log_level(log::Level::Trace); let deps_file_fetcher = Arc::new(deps_file_fetcher); let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone())); let npm_resolver = Arc::new(NpmFetchResolver::new( @@ -422,14 +421,14 @@ async fn create_install_shim( // ensure directory exists if let Ok(metadata) = fs::metadata(&shim_data.installation_dir) { if !metadata.is_dir() { - return Err(generic_error("Installation path is not a directory")); + return Err(anyhow!("Installation path is not a directory")); } } else { fs::create_dir_all(&shim_data.installation_dir)?; }; if shim_data.file_path.exists() && !install_flags_global.force { - return Err(generic_error( + return Err(anyhow!( "Existing installation found. Aborting (Use -f to overwrite).", )); }; @@ -491,7 +490,7 @@ async fn resolve_shim_data( let name = match name { Some(name) => name, - None => return Err(generic_error( + None => return Err(anyhow!( "An executable name was not provided. One could not be inferred from the URL. Aborting.", )), }; @@ -523,9 +522,7 @@ async fn resolve_shim_data( let log_level = match log_level { Level::Debug => "debug", Level::Info => "info", - _ => { - return Err(generic_error(format!("invalid log level {log_level}"))) - } + _ => return Err(anyhow!(format!("invalid log level {log_level}"))), }; executable_args.push(log_level.to_string()); } @@ -658,16 +655,17 @@ fn is_in_path(dir: &Path) -> bool { #[cfg(test)] mod tests { - use super::*; + use std::process::Command; + use test_util::testdata_path; + use test_util::TempDir; + + use super::*; use crate::args::ConfigFlag; use crate::args::PermissionFlags; use crate::args::UninstallFlagsGlobal; use crate::args::UnstableConfig; use crate::util::fs::canonicalize_path; - use std::process::Command; - use test_util::testdata_path; - use test_util::TempDir; #[tokio::test] async fn install_infer_name_from_url() { diff --git a/cli/tools/jupyter/install.rs b/cli/tools/jupyter/install.rs index aeff89ccf4..d76c59343b 100644 --- a/cli/tools/jupyter/install.rs +++ b/cli/tools/jupyter/install.rs @@ -1,12 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::serde_json::json; use std::env::current_exe; use std::io::Write; use std::path::Path; +use deno_core::error::AnyError; +use deno_core::serde_json; +use deno_core::serde_json::json; use jupyter_runtime::dirs::user_data_dir; const DENO_ICON_32: &[u8] = include_bytes!("./resources/deno-logo-32x32.png"); diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs index 732f95c49f..67c604118c 100644 --- a/cli/tools/jupyter/mod.rs +++ b/cli/tools/jupyter/mod.rs @@ -1,21 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; -use crate::args::Flags; -use crate::args::JupyterFlags; -use crate::cdp; -use crate::lsp::ReplCompletionItem; -use crate::ops; -use crate::tools::repl; -use crate::tools::test::create_single_test_event_channel; -use crate::tools::test::reporters::PrettyTestReporter; -use crate::tools::test::TestEventWorkerSender; -use crate::tools::test::TestFailureFormatOptions; -use crate::CliFactory; +use deno_core::anyhow::anyhow; use deno_core::anyhow::bail; use deno_core::anyhow::Context; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::futures::FutureExt; use deno_core::located_script_name; @@ -34,6 +23,18 @@ use tokio::sync::mpsc; use tokio::sync::mpsc::UnboundedSender; use tokio::sync::oneshot; +use crate::args::Flags; +use crate::args::JupyterFlags; +use crate::cdp; +use crate::lsp::ReplCompletionItem; +use crate::ops; +use crate::tools::repl; +use crate::tools::test::create_single_test_event_channel; +use crate::tools::test::reporters::PrettyTestReporter; +use crate::tools::test::TestEventWorkerSender; +use crate::tools::test::TestFailureFormatOptions; +use crate::CliFactory; + mod install; pub mod server; @@ -66,7 +67,7 @@ pub async fn kernel( // TODO(bartlomieju): should we run with all permissions? let permissions = PermissionsContainer::allow_all(factory.permission_desc_parser()?.clone()); - let npm_resolver = factory.npm_resolver().await?.clone(); + let npm_installer = factory.npm_installer_if_managed()?.cloned(); let resolver = factory.resolver().await?.clone(); let worker_factory = factory.create_cli_main_worker_factory().await?; let (stdio_tx, stdio_rx) = mpsc::unbounded_channel(); @@ -114,7 +115,7 @@ pub async fn kernel( let worker = worker.into_main_worker(); let mut repl_session = repl::ReplSession::initialize( cli_options, - npm_resolver, + npm_installer, resolver, worker, main_module, @@ -136,10 +137,10 @@ pub async fn kernel( } let cwd_url = Url::from_directory_path(cli_options.initial_cwd()).map_err(|_| { - generic_error(format!( + anyhow!( "Unable to construct URL from the path of cwd: {}", cli_options.initial_cwd().to_string_lossy(), - )) + ) })?; repl_session.set_test_reporter_factory(Box::new(move || { Box::new( diff --git a/cli/tools/jupyter/server.rs b/cli/tools/jupyter/server.rs index 5680ed4c13..bc045d9d9b 100644 --- a/cli/tools/jupyter/server.rs +++ b/cli/tools/jupyter/server.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file is forked/ported from // Copyright 2020 The Evcxr Authors. MIT license. @@ -11,8 +11,6 @@ use std::collections::HashMap; use std::rc::Rc; use std::sync::Arc; -use crate::cdp; -use crate::tools::repl; use deno_core::anyhow::bail; use deno_core::error::AnyError; use deno_core::futures; @@ -20,12 +18,9 @@ use deno_core::parking_lot::Mutex; use deno_core::serde_json; use deno_core::CancelFuture; use deno_core::CancelHandle; -use jupyter_runtime::ExecutionCount; -use tokio::sync::mpsc; -use tokio::sync::oneshot; - use jupyter_runtime::messaging; use jupyter_runtime::ConnectionInfo; +use jupyter_runtime::ExecutionCount; use jupyter_runtime::JupyterMessage; use jupyter_runtime::JupyterMessageContent; use jupyter_runtime::KernelControlConnection; @@ -34,9 +29,13 @@ use jupyter_runtime::KernelShellConnection; use jupyter_runtime::ReplyError; use jupyter_runtime::ReplyStatus; use jupyter_runtime::StreamContent; +use tokio::sync::mpsc; +use tokio::sync::oneshot; use uuid::Uuid; use super::JupyterReplProxy; +use crate::cdp; +use crate::tools::repl; pub struct JupyterServer { execution_count: ExecutionCount, diff --git a/cli/tools/lint/ast_buffer/buffer.rs b/cli/tools/lint/ast_buffer/buffer.rs new file mode 100644 index 0000000000..a884ee24f9 --- /dev/null +++ b/cli/tools/lint/ast_buffer/buffer.rs @@ -0,0 +1,587 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::fmt::Display; + +use deno_ast::swc::common::Span; +use deno_ast::swc::common::DUMMY_SP; +use indexmap::IndexMap; + +/// Each property has this flag to mark what kind of value it holds- +/// Plain objects and arrays are not supported yet, but could be easily +/// added if needed. +#[derive(Debug, PartialEq)] +pub enum PropFlags { + Ref, + RefArr, + String, + Number, + Bool, + Null, + Undefined, + Object, + Regex, + BigInt, + Array, +} + +impl From for u8 { + fn from(m: PropFlags) -> u8 { + m as u8 + } +} + +impl TryFrom for PropFlags { + type Error = &'static str; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(PropFlags::Ref), + 1 => Ok(PropFlags::RefArr), + 2 => Ok(PropFlags::String), + 3 => Ok(PropFlags::Number), + 4 => Ok(PropFlags::Bool), + 5 => Ok(PropFlags::Null), + 6 => Ok(PropFlags::Undefined), + 7 => Ok(PropFlags::Object), + 8 => Ok(PropFlags::Regex), + 9 => Ok(PropFlags::BigInt), + 10 => Ok(PropFlags::Array), + _ => Err("Unknown Prop flag"), + } + } +} + +pub type Index = u32; + +const GROUP_KIND: u8 = 1; +const MASK_U32_1: u32 = 0b11111111_00000000_00000000_00000000; +const MASK_U32_2: u32 = 0b00000000_11111111_00000000_00000000; +const MASK_U32_3: u32 = 0b00000000_00000000_11111111_00000000; +const MASK_U32_4: u32 = 0b00000000_00000000_00000000_11111111; + +#[inline] +fn append_u32(result: &mut Vec, value: u32) { + let v1: u8 = ((value & MASK_U32_1) >> 24) as u8; + let v2: u8 = ((value & MASK_U32_2) >> 16) as u8; + let v3: u8 = ((value & MASK_U32_3) >> 8) as u8; + let v4: u8 = (value & MASK_U32_4) as u8; + + result.push(v1); + result.push(v2); + result.push(v3); + result.push(v4); +} + +fn append_usize(result: &mut Vec, value: usize) { + let raw = u32::try_from(value).unwrap(); + append_u32(result, raw); +} + +#[derive(Debug)] +pub struct StringTable { + id: usize, + table: IndexMap, +} + +impl StringTable { + pub fn new() -> Self { + Self { + id: 0, + table: IndexMap::new(), + } + } + + pub fn insert(&mut self, s: &str) -> usize { + if let Some(id) = self.table.get(s) { + return *id; + } + + let id = self.id; + self.id += 1; + self.table.insert(s.to_string(), id); + id + } + + pub fn serialize(&mut self) -> Vec { + let mut result: Vec = vec![]; + append_u32(&mut result, self.table.len() as u32); + + // Assume that it's sorted by id + for (s, _id) in &self.table { + let bytes = s.as_bytes(); + append_u32(&mut result, bytes.len() as u32); + result.append(&mut bytes.to_vec()); + } + + result + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct NodeRef(pub Index); +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct PendingRef(pub Index); + +pub trait AstBufSerializer { + fn serialize(&mut self) -> Vec; +} + +/// +/// +/// +/// +/// +#[derive(Debug)] +struct Node { + kind: u8, + prop_offset: u32, + child: u32, + next: u32, + parent: u32, +} + +#[derive(Debug)] +pub struct SerializeCtx { + root_idx: Index, + + nodes: Vec, + prop_stack: Vec>, + field_count: Vec, + field_buf: Vec, + prev_sibling_stack: Vec, + + /// Vec of spans + spans: Vec, + + /// Maps string id to the actual string + str_table: StringTable, + /// Maps kind id to string id + kind_name_map: Vec, + /// Maps prop id to string id + prop_name_map: Vec, +} + +/// This is the internal context used to allocate and fill the buffer. The point +/// is to be able to write absolute offsets directly in place. +/// +/// The typical workflow is to reserve all necessary space for the currrent +/// node with placeholders for the offsets of the child nodes. Once child +/// nodes have been traversed, we know their offsets and can replace the +/// placeholder values with the actual ones. +impl SerializeCtx { + pub fn new(kind_len: u8, prop_len: u8) -> Self { + let kind_size = kind_len as usize; + let prop_size = prop_len as usize; + let mut ctx = Self { + spans: Vec::with_capacity(512), + root_idx: 0, + nodes: Vec::with_capacity(512), + prop_stack: vec![vec![]], + prev_sibling_stack: vec![0], + field_count: vec![0], + field_buf: Vec::with_capacity(1024), + str_table: StringTable::new(), + kind_name_map: vec![0; kind_size], + prop_name_map: vec![0; prop_size], + }; + + let empty_str = ctx.str_table.insert(""); + + // Placeholder node is always 0 + ctx.append_node(0, &DUMMY_SP); + ctx.kind_name_map[0] = empty_str; + ctx.kind_name_map[1] = empty_str; + + // Insert default props that are always present + let type_str = ctx.str_table.insert("type"); + let parent_str = ctx.str_table.insert("parent"); + let range_str = ctx.str_table.insert("range"); + let length_str = ctx.str_table.insert("length"); + + // These values are expected to be in this order on the JS side + ctx.prop_name_map[0] = empty_str; + ctx.prop_name_map[1] = type_str; + ctx.prop_name_map[2] = parent_str; + ctx.prop_name_map[3] = range_str; + ctx.prop_name_map[4] = length_str; + + ctx + } + + pub fn set_root_idx(&mut self, idx: Index) { + self.root_idx = idx; + } + + /// Allocate a node's header + fn field_header

(&mut self, prop: P, prop_flags: PropFlags) + where + P: Into + Display + Clone, + { + let flags: u8 = prop_flags.into(); + let n: u8 = prop.clone().into(); + + if let Some(v) = self.prop_name_map.get::(n.into()) { + if *v == 0 { + let id = self.str_table.insert(&format!("{prop}")); + self.prop_name_map[n as usize] = id; + } + } + + // Increment field counter + let idx = self.field_count.len() - 1; + let count = self.field_count[idx]; + self.field_count[idx] = count + 1; + + let buf = self.prop_stack.last_mut().unwrap(); + buf.push(n); + buf.push(flags); + } + + fn get_node(&mut self, id: Index) -> &mut Node { + self.nodes.get_mut(id as usize).unwrap() + } + + fn set_parent(&mut self, child_id: Index, parent_id: Index) { + let child = self.get_node(child_id); + child.parent = parent_id; + } + + fn set_child(&mut self, parent_id: Index, child_id: Index) { + let parent = self.get_node(parent_id); + parent.child = child_id; + } + + fn set_next(&mut self, node_id: Index, next_id: Index) { + let node = self.get_node(node_id); + node.next = next_id; + } + + fn update_ref_links(&mut self, parent_id: Index, child_id: Index) { + let last_idx = self.prev_sibling_stack.len() - 1; + let parent = self.get_node(parent_id); + if parent.child == 0 { + parent.child = child_id; + } else { + let prev_id = self.prev_sibling_stack[last_idx]; + self.set_next(prev_id, child_id); + } + + self.prev_sibling_stack[last_idx] = child_id; + self.set_parent(child_id, parent_id); + } + + pub fn append_node(&mut self, kind: K, span: &Span) -> PendingRef + where + K: Into + Display + Clone, + { + self.append_inner(kind, span.lo.0, span.hi.0) + } + + pub fn append_inner( + &mut self, + kind: K, + span_lo: u32, + span_hi: u32, + ) -> PendingRef + where + K: Into + Display + Clone, + { + let kind_u8: u8 = kind.clone().into(); + + let id: Index = self.nodes.len() as u32; + + self.nodes.push(Node { + kind: kind_u8, + prop_offset: 0, + child: 0, + next: 0, + parent: 0, + }); + + if let Some(v) = self.kind_name_map.get::(kind_u8.into()) { + if *v == 0 { + let s_id = self.str_table.insert(&format!("{kind}")); + self.kind_name_map[kind_u8 as usize] = s_id; + } + } + + self.field_count.push(0); + self.prop_stack.push(vec![]); + self.prev_sibling_stack.push(0); + + // write spans + self.spans.push(span_lo); + self.spans.push(span_hi); + + PendingRef(id) + } + + pub fn commit_node(&mut self, id: PendingRef) -> NodeRef { + let mut buf = self.prop_stack.pop().unwrap(); + let count = self.field_count.pop().unwrap(); + let offset = self.field_buf.len(); + + // All nodes have <10 fields + self.field_buf.push(count as u8); + self.field_buf.append(&mut buf); + + let node = self.nodes.get_mut(id.0 as usize).unwrap(); + node.prop_offset = offset as u32; + + self.prev_sibling_stack.pop(); + + NodeRef(id.0) + } + + // Allocate an object field + pub fn open_obj(&mut self) { + self.field_count.push(0); + self.prop_stack.push(vec![]); + } + + pub fn commit_obj

(&mut self, prop: P) + where + P: Into + Display + Clone, + { + let mut buf = self.prop_stack.pop().unwrap(); + let count = self.field_count.pop().unwrap(); + let offset = self.field_buf.len(); + append_usize(&mut self.field_buf, count); + self.field_buf.append(&mut buf); + + self.field_header(prop, PropFlags::Object); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, offset); + } + + /// Allocate an null field + pub fn write_null

(&mut self, prop: P) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Null); + + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, 0); + } + + /// Allocate an null field + pub fn write_undefined

(&mut self, prop: P) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Undefined); + + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, 0); + } + + /// Allocate a number field + pub fn write_num

(&mut self, prop: P, value: &str) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Number); + + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); + } + + /// Allocate a bigint field + pub fn write_bigint

(&mut self, prop: P, value: &str) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::BigInt); + + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); + } + + /// Allocate a RegExp field + pub fn write_regex

(&mut self, prop: P, value: &str) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Regex); + + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); + } + + /// Store the string in our string table and save the id of the string + /// in the current field. + pub fn write_str

(&mut self, prop: P, value: &str) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::String); + + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); + } + + /// Write a bool to a field. + pub fn write_bool

(&mut self, prop: P, value: bool) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Bool); + + let n = if value { 1 } else { 0 }; + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, n); + } + + /// Replace the placeholder of a reference field with the actual offset + /// to the node we want to point to. + pub fn write_ref

(&mut self, prop: P, parent: &PendingRef, value: NodeRef) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Ref); + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, value.0); + + if parent.0 > 0 { + self.update_ref_links(parent.0, value.0); + } + } + + /// Helper for writing optional node offsets + pub fn write_maybe_ref

( + &mut self, + prop: P, + parent: &PendingRef, + value: Option, + ) where + P: Into + Display + Clone, + { + if let Some(v) = value { + self.write_ref(prop, parent, v); + } else { + self.write_null(prop); + }; + } + + /// Write a vec of node offsets into the property. The necessary space + /// has been reserved earlier. + pub fn write_ref_vec

( + &mut self, + prop: P, + parent_ref: &PendingRef, + value: Vec, + ) where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::RefArr); + let group_id = self.append_node(GROUP_KIND, &DUMMY_SP); + let group_id = self.commit_node(group_id).0; + + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, group_id); + + self.update_ref_links(parent_ref.0, group_id); + + let mut prev_id = 0; + for (i, item) in value.iter().enumerate() { + self.set_parent(item.0, group_id); + + if i == 0 { + self.set_child(group_id, item.0); + } else { + self.set_next(prev_id, item.0); + } + + prev_id = item.0; + } + } + + /// Serialize all information we have into a buffer that can be sent to JS. + /// It has the following structure: + /// + /// <...ast> + /// + /// <- node kind id maps to string id + /// <- node property id maps to string id + /// <- List of spans, rarely needed + /// + /// + /// + /// + pub fn serialize(&mut self) -> Vec { + let mut buf: Vec = vec![]; + + // The buffer starts with the serialized AST first, because that + // contains absolute offsets. By butting this at the start of the + // message we don't have to waste time updating any offsets. + for node in &self.nodes { + buf.push(node.kind); + append_u32(&mut buf, node.prop_offset); + append_u32(&mut buf, node.child); + append_u32(&mut buf, node.next); + append_u32(&mut buf, node.parent); + } + + // Next follows the string table. We'll keep track of the offset + // in the message of where the string table begins + let offset_str_table = buf.len(); + + // Serialize string table + buf.append(&mut self.str_table.serialize()); + + // Next, serialize the mappings of kind -> string of encountered + // nodes in the AST. We use this additional lookup table to compress + // the message so that we can save space by using a u8 . All nodes of + // JS, TS and JSX together are <200 + let offset_kind_map = buf.len(); + + // Write the total number of entries in the kind -> str mapping table + // TODO: make this a u8 + append_usize(&mut buf, self.kind_name_map.len()); + for v in &self.kind_name_map { + append_usize(&mut buf, *v); + } + + // Store offset to prop -> string map. It's the same as with node kind + // as the total number of properties is <120 which allows us to store it + // as u8. + let offset_prop_map = buf.len(); + // Write the total number of entries in the kind -> str mapping table + append_usize(&mut buf, self.prop_name_map.len()); + for v in &self.prop_name_map { + append_usize(&mut buf, *v); + } + + // Spans are rarely needed, so they're stored in a separate array. + // They're indexed by the node id. + let offset_spans = buf.len(); + for v in &self.spans { + append_u32(&mut buf, *v); + } + + // The field value table. They're detached from nodes as they're not + // as frequently needed as the nodes themselves. The most common + // operation is traversal and we can traverse nodes without knowing + // about the fields. + let offset_props = buf.len(); + buf.append(&mut self.field_buf); + + // Putting offsets of relevant parts of the buffer at the end. This + // allows us to hop to the relevant part by merely looking at the last + // for values in the message. Each value represents an offset into the + // buffer. + append_usize(&mut buf, offset_props); + append_usize(&mut buf, offset_spans); + append_usize(&mut buf, offset_kind_map); + append_usize(&mut buf, offset_prop_map); + append_usize(&mut buf, offset_str_table); + append_u32(&mut buf, self.root_idx); + + buf + } +} diff --git a/cli/tools/lint/ast_buffer/mod.rs b/cli/tools/lint/ast_buffer/mod.rs new file mode 100644 index 0000000000..fc4045fb60 --- /dev/null +++ b/cli/tools/lint/ast_buffer/mod.rs @@ -0,0 +1,13 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_ast::ParsedSource; +use swc::serialize_swc_to_buffer; + +mod buffer; +mod swc; +mod ts_estree; + +pub fn serialize_ast_to_buffer(parsed_source: &ParsedSource) -> Vec { + // TODO: We could support multiple languages here + serialize_swc_to_buffer(parsed_source) +} diff --git a/cli/tools/lint/ast_buffer/swc.rs b/cli/tools/lint/ast_buffer/swc.rs new file mode 100644 index 0000000000..925d1bcd17 --- /dev/null +++ b/cli/tools/lint/ast_buffer/swc.rs @@ -0,0 +1,2614 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_ast::swc::ast::AssignTarget; +use deno_ast::swc::ast::AssignTargetPat; +use deno_ast::swc::ast::BindingIdent; +use deno_ast::swc::ast::BlockStmtOrExpr; +use deno_ast::swc::ast::Callee; +use deno_ast::swc::ast::ClassMember; +use deno_ast::swc::ast::Decl; +use deno_ast::swc::ast::Decorator; +use deno_ast::swc::ast::DefaultDecl; +use deno_ast::swc::ast::ExportSpecifier; +use deno_ast::swc::ast::Expr; +use deno_ast::swc::ast::ExprOrSpread; +use deno_ast::swc::ast::FnExpr; +use deno_ast::swc::ast::ForHead; +use deno_ast::swc::ast::Function; +use deno_ast::swc::ast::Ident; +use deno_ast::swc::ast::IdentName; +use deno_ast::swc::ast::ImportSpecifier; +use deno_ast::swc::ast::JSXAttrName; +use deno_ast::swc::ast::JSXAttrOrSpread; +use deno_ast::swc::ast::JSXAttrValue; +use deno_ast::swc::ast::JSXElement; +use deno_ast::swc::ast::JSXElementChild; +use deno_ast::swc::ast::JSXElementName; +use deno_ast::swc::ast::JSXExpr; +use deno_ast::swc::ast::JSXExprContainer; +use deno_ast::swc::ast::JSXFragment; +use deno_ast::swc::ast::JSXMemberExpr; +use deno_ast::swc::ast::JSXNamespacedName; +use deno_ast::swc::ast::JSXObject; +use deno_ast::swc::ast::JSXOpeningElement; +use deno_ast::swc::ast::Key; +use deno_ast::swc::ast::Lit; +use deno_ast::swc::ast::MemberExpr; +use deno_ast::swc::ast::MemberProp; +use deno_ast::swc::ast::ModuleDecl; +use deno_ast::swc::ast::ModuleExportName; +use deno_ast::swc::ast::ModuleItem; +use deno_ast::swc::ast::ObjectLit; +use deno_ast::swc::ast::ObjectPatProp; +use deno_ast::swc::ast::OptChainBase; +use deno_ast::swc::ast::Param; +use deno_ast::swc::ast::ParamOrTsParamProp; +use deno_ast::swc::ast::Pat; +use deno_ast::swc::ast::PrivateName; +use deno_ast::swc::ast::Program; +use deno_ast::swc::ast::Prop; +use deno_ast::swc::ast::PropName; +use deno_ast::swc::ast::PropOrSpread; +use deno_ast::swc::ast::SimpleAssignTarget; +use deno_ast::swc::ast::Stmt; +use deno_ast::swc::ast::SuperProp; +use deno_ast::swc::ast::TsEntityName; +use deno_ast::swc::ast::TsEnumMemberId; +use deno_ast::swc::ast::TsExprWithTypeArgs; +use deno_ast::swc::ast::TsFnOrConstructorType; +use deno_ast::swc::ast::TsFnParam; +use deno_ast::swc::ast::TsIndexSignature; +use deno_ast::swc::ast::TsLit; +use deno_ast::swc::ast::TsLitType; +use deno_ast::swc::ast::TsModuleName; +use deno_ast::swc::ast::TsModuleRef; +use deno_ast::swc::ast::TsNamespaceBody; +use deno_ast::swc::ast::TsParamPropParam; +use deno_ast::swc::ast::TsThisTypeOrIdent; +use deno_ast::swc::ast::TsType; +use deno_ast::swc::ast::TsTypeAnn; +use deno_ast::swc::ast::TsTypeElement; +use deno_ast::swc::ast::TsTypeParam; +use deno_ast::swc::ast::TsTypeParamDecl; +use deno_ast::swc::ast::TsTypeParamInstantiation; +use deno_ast::swc::ast::TsTypeQueryExpr; +use deno_ast::swc::ast::TsUnionOrIntersectionType; +use deno_ast::swc::ast::VarDeclOrExpr; +use deno_ast::swc::common::Span; +use deno_ast::swc::common::Spanned; +use deno_ast::swc::common::SyntaxContext; +use deno_ast::view::Accessibility; +use deno_ast::view::AssignOp; +use deno_ast::view::BinaryOp; +use deno_ast::view::MethodKind; +use deno_ast::view::TsKeywordTypeKind; +use deno_ast::view::TsTypeOperatorOp; +use deno_ast::view::UnaryOp; +use deno_ast::view::UpdateOp; +use deno_ast::view::VarDeclKind; +use deno_ast::ParsedSource; + +use super::buffer::AstBufSerializer; +use super::buffer::NodeRef; +use super::ts_estree::AstNode; +use super::ts_estree::TsEsTreeBuilder; +use super::ts_estree::TsKeywordKind; + +pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec { + let mut ctx = TsEsTreeBuilder::new(); + + let program = &parsed_source.program(); + + match program.as_ref() { + Program::Module(module) => { + let children = module + .body + .iter() + .map(|item| match item { + ModuleItem::ModuleDecl(module_decl) => { + serialize_module_decl(&mut ctx, module_decl) + } + ModuleItem::Stmt(stmt) => serialize_stmt(&mut ctx, stmt), + }) + .collect::>(); + + ctx.write_program(&module.span, "module", children); + } + Program::Script(script) => { + let children = script + .body + .iter() + .map(|stmt| serialize_stmt(&mut ctx, stmt)) + .collect::>(); + + ctx.write_program(&script.span, "script", children); + } + } + + ctx.serialize() +} + +fn serialize_module_decl( + ctx: &mut TsEsTreeBuilder, + module_decl: &ModuleDecl, +) -> NodeRef { + match module_decl { + ModuleDecl::Import(node) => { + let src = serialize_lit(ctx, &Lit::Str(node.src.as_ref().clone())); + let attrs = serialize_import_attrs(ctx, &node.with); + + let specifiers = node + .specifiers + .iter() + .map(|spec| match spec { + ImportSpecifier::Named(spec) => { + let local = serialize_ident(ctx, &spec.local, None); + let imported = spec + .imported + .as_ref() + .map_or(serialize_ident(ctx, &spec.local, None), |v| { + serialize_module_export_name(ctx, v) + }); + ctx.write_import_spec( + &spec.span, + spec.is_type_only, + local, + imported, + ) + } + ImportSpecifier::Default(spec) => { + let local = serialize_ident(ctx, &spec.local, None); + ctx.write_import_default_spec(&spec.span, local) + } + ImportSpecifier::Namespace(spec) => { + let local = serialize_ident(ctx, &spec.local, None); + ctx.write_import_ns_spec(&spec.span, local) + } + }) + .collect::>(); + + ctx.write_import_decl(&node.span, node.type_only, src, specifiers, attrs) + } + ModuleDecl::ExportDecl(node) => { + let decl = serialize_decl(ctx, &node.decl); + ctx.write_export_decl(&node.span, decl) + } + ModuleDecl::ExportNamed(node) => { + let attrs = serialize_import_attrs(ctx, &node.with); + let source = node + .src + .as_ref() + .map(|src| serialize_lit(ctx, &Lit::Str(*src.clone()))); + + if let Some(ExportSpecifier::Namespace(ns)) = node.specifiers.first() { + let exported = serialize_module_export_name(ctx, &ns.name); + ctx.write_export_all_decl( + &node.span, + node.type_only, + exported, + source, + attrs, + ) + } else { + let specifiers = node + .specifiers + .iter() + .map(|spec| { + match spec { + ExportSpecifier::Named(spec) => { + let local = serialize_module_export_name(ctx, &spec.orig); + + let exported = spec.exported.as_ref().map_or( + serialize_module_export_name(ctx, &spec.orig), + |exported| serialize_module_export_name(ctx, exported), + ); + + ctx.write_export_spec( + &spec.span, + spec.is_type_only, + local, + exported, + ) + } + + // Already handled earlier + ExportSpecifier::Namespace(_) => unreachable!(), + // this is not syntactically valid + ExportSpecifier::Default(_) => unreachable!(), + } + }) + .collect::>(); + + ctx.write_export_named_decl(&node.span, specifiers, source, attrs) + } + } + ModuleDecl::ExportDefaultDecl(node) => { + let (is_type_only, decl) = match &node.decl { + DefaultDecl::Class(node) => { + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let super_class = node + .class + .super_class + .as_ref() + .map(|expr| serialize_expr(ctx, expr.as_ref())); + + let implements = node + .class + .implements + .iter() + .map(|item| serialize_ts_expr_with_type_args(ctx, item)) + .collect::>(); + + let members = node + .class + .body + .iter() + .filter_map(|member| serialize_class_member(ctx, member)) + .collect::>(); + + let body = ctx.write_class_body(&node.class.span, members); + + let decl = ctx.write_class_decl( + &node.class.span, + false, + node.class.is_abstract, + ident, + super_class, + implements, + body, + ); + + (false, decl) + } + DefaultDecl::Fn(node) => { + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let fn_obj = node.function.as_ref(); + + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &fn_obj.type_params); + + let params = fn_obj + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat)) + .collect::>(); + + let return_type = + maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type); + let body = fn_obj + .body + .as_ref() + .map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone()))); + + let decl = ctx.write_fn_decl( + &fn_obj.span, + false, + fn_obj.is_async, + fn_obj.is_generator, + ident, + type_params, + return_type, + body, + params, + ); + + (false, decl) + } + DefaultDecl::TsInterfaceDecl(node) => { + let ident_id = serialize_ident(ctx, &node.id, None); + let type_param = + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); + + let extend_ids = node + .extends + .iter() + .map(|item| { + let expr = serialize_expr(ctx, &item.expr); + let type_args = item + .type_args + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); + + ctx.write_ts_interface_heritage(&item.span, expr, type_args) + }) + .collect::>(); + + let body_elem_ids = node + .body + .body + .iter() + .map(|item| serialize_ts_type_elem(ctx, item)) + .collect::>(); + + let body_pos = + ctx.write_ts_interface_body(&node.body.span, body_elem_ids); + + let decl = ctx.write_ts_interface_decl( + &node.span, + node.declare, + ident_id, + type_param, + extend_ids, + body_pos, + ); + + (true, decl) + } + }; + + ctx.write_export_default_decl(&node.span, is_type_only, decl) + } + ModuleDecl::ExportDefaultExpr(node) => { + let expr = serialize_expr(ctx, &node.expr); + ctx.write_export_default_decl(&node.span, false, expr) + } + ModuleDecl::ExportAll(node) => { + let src = serialize_lit(ctx, &Lit::Str(node.src.as_ref().clone())); + let attrs = serialize_import_attrs(ctx, &node.with); + + ctx.write_export_all_decl(&node.span, node.type_only, src, None, attrs) + } + ModuleDecl::TsImportEquals(node) => { + let ident = serialize_ident(ctx, &node.id, None); + let module_ref = match &node.module_ref { + TsModuleRef::TsEntityName(entity) => { + serialize_ts_entity_name(ctx, entity) + } + TsModuleRef::TsExternalModuleRef(external) => { + let expr = serialize_lit(ctx, &Lit::Str(external.expr.clone())); + ctx.write_ts_external_mod_ref(&external.span, expr) + } + }; + + ctx.write_export_ts_import_equals( + &node.span, + node.is_type_only, + ident, + module_ref, + ) + } + ModuleDecl::TsExportAssignment(node) => { + let expr = serialize_expr(ctx, &node.expr); + ctx.write_export_assign(&node.span, expr) + } + ModuleDecl::TsNamespaceExport(node) => { + let decl = serialize_ident(ctx, &node.id, None); + ctx.write_export_ts_namespace(&node.span, decl) + } + } +} + +fn serialize_import_attrs( + ctx: &mut TsEsTreeBuilder, + raw_attrs: &Option>, +) -> Vec { + raw_attrs.as_ref().map_or(vec![], |obj| { + obj + .props + .iter() + .map(|prop| { + let (key, value) = match prop { + // Invalid syntax + PropOrSpread::Spread(_) => unreachable!(), + PropOrSpread::Prop(prop) => { + match prop.as_ref() { + Prop::Shorthand(ident) => ( + serialize_ident(ctx, ident, None), + serialize_ident(ctx, ident, None), + ), + Prop::KeyValue(kv) => ( + serialize_prop_name(ctx, &kv.key), + serialize_expr(ctx, kv.value.as_ref()), + ), + // Invalid syntax + Prop::Assign(_) + | Prop::Getter(_) + | Prop::Setter(_) + | Prop::Method(_) => unreachable!(), + } + } + }; + + ctx.write_import_attr(&prop.span(), key, value) + }) + .collect::>() + }) +} + +fn serialize_stmt(ctx: &mut TsEsTreeBuilder, stmt: &Stmt) -> NodeRef { + match stmt { + Stmt::Block(node) => { + let children = node + .stmts + .iter() + .map(|stmt| serialize_stmt(ctx, stmt)) + .collect::>(); + + ctx.write_block_stmt(&node.span, children) + } + Stmt::Empty(_) => NodeRef(0), + Stmt::Debugger(node) => ctx.write_debugger_stmt(&node.span), + Stmt::With(node) => { + let obj = serialize_expr(ctx, &node.obj); + let body = serialize_stmt(ctx, &node.body); + + ctx.write_with_stmt(&node.span, obj, body) + } + Stmt::Return(node) => { + let arg = node.arg.as_ref().map(|arg| serialize_expr(ctx, arg)); + ctx.write_return_stmt(&node.span, arg) + } + Stmt::Labeled(node) => { + let ident = serialize_ident(ctx, &node.label, None); + let stmt = serialize_stmt(ctx, &node.body); + + ctx.write_labeled_stmt(&node.span, ident, stmt) + } + Stmt::Break(node) => { + let arg = node + .label + .as_ref() + .map(|label| serialize_ident(ctx, label, None)); + ctx.write_break_stmt(&node.span, arg) + } + Stmt::Continue(node) => { + let arg = node + .label + .as_ref() + .map(|label| serialize_ident(ctx, label, None)); + + ctx.write_continue_stmt(&node.span, arg) + } + Stmt::If(node) => { + let test = serialize_expr(ctx, node.test.as_ref()); + let cons = serialize_stmt(ctx, node.cons.as_ref()); + let alt = node.alt.as_ref().map(|alt| serialize_stmt(ctx, alt)); + + ctx.write_if_stmt(&node.span, test, cons, alt) + } + Stmt::Switch(node) => { + let disc = serialize_expr(ctx, &node.discriminant); + + let cases = node + .cases + .iter() + .map(|case| { + let test = case.test.as_ref().map(|test| serialize_expr(ctx, test)); + + let cons = case + .cons + .iter() + .map(|cons| serialize_stmt(ctx, cons)) + .collect::>(); + + ctx.write_switch_case(&case.span, test, cons) + }) + .collect::>(); + + ctx.write_switch_stmt(&node.span, disc, cases) + } + Stmt::Throw(node) => { + let arg = serialize_expr(ctx, &node.arg); + ctx.write_throw_stmt(&node.span, arg) + } + Stmt::Try(node) => { + let block = serialize_stmt(ctx, &Stmt::Block(node.block.clone())); + + let handler = node.handler.as_ref().map(|catch| { + let param = catch.param.as_ref().map(|param| serialize_pat(ctx, param)); + + let body = serialize_stmt(ctx, &Stmt::Block(catch.body.clone())); + + ctx.write_catch_clause(&catch.span, param, body) + }); + + let finalizer = node + .finalizer + .as_ref() + .map(|finalizer| serialize_stmt(ctx, &Stmt::Block(finalizer.clone()))); + + ctx.write_try_stmt(&node.span, block, handler, finalizer) + } + Stmt::While(node) => { + let test = serialize_expr(ctx, node.test.as_ref()); + let stmt = serialize_stmt(ctx, node.body.as_ref()); + + ctx.write_while_stmt(&node.span, test, stmt) + } + Stmt::DoWhile(node) => { + let expr = serialize_expr(ctx, node.test.as_ref()); + let stmt = serialize_stmt(ctx, node.body.as_ref()); + + ctx.write_do_while_stmt(&node.span, expr, stmt) + } + Stmt::For(node) => { + let init = node.init.as_ref().map(|init| match init { + VarDeclOrExpr::VarDecl(var_decl) => { + serialize_stmt(ctx, &Stmt::Decl(Decl::Var(var_decl.clone()))) + } + VarDeclOrExpr::Expr(expr) => serialize_expr(ctx, expr), + }); + + let test = node.test.as_ref().map(|expr| serialize_expr(ctx, expr)); + let update = node.update.as_ref().map(|expr| serialize_expr(ctx, expr)); + let body = serialize_stmt(ctx, node.body.as_ref()); + + ctx.write_for_stmt(&node.span, init, test, update, body) + } + Stmt::ForIn(node) => { + let left = serialize_for_head(ctx, &node.left); + let right = serialize_expr(ctx, node.right.as_ref()); + let body = serialize_stmt(ctx, node.body.as_ref()); + + ctx.write_for_in_stmt(&node.span, left, right, body) + } + Stmt::ForOf(node) => { + let left = serialize_for_head(ctx, &node.left); + let right = serialize_expr(ctx, node.right.as_ref()); + let body = serialize_stmt(ctx, node.body.as_ref()); + + ctx.write_for_of_stmt(&node.span, node.is_await, left, right, body) + } + Stmt::Decl(node) => serialize_decl(ctx, node), + Stmt::Expr(node) => { + let expr = serialize_expr(ctx, node.expr.as_ref()); + ctx.write_expr_stmt(&node.span, expr) + } + } +} + +fn serialize_expr(ctx: &mut TsEsTreeBuilder, expr: &Expr) -> NodeRef { + match expr { + Expr::This(node) => ctx.write_this_expr(&node.span), + Expr::Array(node) => { + let elems = node + .elems + .iter() + .map(|item| { + item + .as_ref() + .map_or(NodeRef(0), |item| serialize_expr_or_spread(ctx, item)) + }) + .collect::>(); + + ctx.write_arr_expr(&node.span, elems) + } + Expr::Object(node) => { + let props = node + .props + .iter() + .map(|prop| serialize_prop_or_spread(ctx, prop)) + .collect::>(); + + ctx.write_obj_expr(&node.span, props) + } + Expr::Fn(node) => { + let fn_obj = node.function.as_ref(); + + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &fn_obj.type_params); + + let params = fn_obj + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat)) + .collect::>(); + + let return_id = maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type); + let body = fn_obj + .body + .as_ref() + .map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone()))); + + ctx.write_fn_expr( + &fn_obj.span, + fn_obj.is_async, + fn_obj.is_generator, + ident, + type_params, + params, + return_id, + body, + ) + } + Expr::Unary(node) => { + let arg = serialize_expr(ctx, &node.arg); + let op = match node.op { + UnaryOp::Minus => "-", + UnaryOp::Plus => "+", + UnaryOp::Bang => "!", + UnaryOp::Tilde => "~", + UnaryOp::TypeOf => "typeof", + UnaryOp::Void => "void", + UnaryOp::Delete => "delete", + }; + + ctx.write_unary_expr(&node.span, op, arg) + } + Expr::Update(node) => { + let arg = serialize_expr(ctx, node.arg.as_ref()); + let op = match node.op { + UpdateOp::PlusPlus => "++", + UpdateOp::MinusMinus => "--", + }; + + ctx.write_update_expr(&node.span, node.prefix, op, arg) + } + Expr::Bin(node) => { + let (node_type, flag_str) = match node.op { + BinaryOp::LogicalAnd => (AstNode::LogicalExpression, "&&"), + BinaryOp::LogicalOr => (AstNode::LogicalExpression, "||"), + BinaryOp::NullishCoalescing => (AstNode::LogicalExpression, "??"), + BinaryOp::EqEq => (AstNode::BinaryExpression, "=="), + BinaryOp::NotEq => (AstNode::BinaryExpression, "!="), + BinaryOp::EqEqEq => (AstNode::BinaryExpression, "==="), + BinaryOp::NotEqEq => (AstNode::BinaryExpression, "!="), + BinaryOp::Lt => (AstNode::BinaryExpression, "<"), + BinaryOp::LtEq => (AstNode::BinaryExpression, "<="), + BinaryOp::Gt => (AstNode::BinaryExpression, ">"), + BinaryOp::GtEq => (AstNode::BinaryExpression, ">="), + BinaryOp::LShift => (AstNode::BinaryExpression, "<<"), + BinaryOp::RShift => (AstNode::BinaryExpression, ">>"), + BinaryOp::ZeroFillRShift => (AstNode::BinaryExpression, ">>>"), + BinaryOp::Add => (AstNode::BinaryExpression, "+"), + BinaryOp::Sub => (AstNode::BinaryExpression, "-"), + BinaryOp::Mul => (AstNode::BinaryExpression, "*"), + BinaryOp::Div => (AstNode::BinaryExpression, "/"), + BinaryOp::Mod => (AstNode::BinaryExpression, "%"), + BinaryOp::BitOr => (AstNode::BinaryExpression, "|"), + BinaryOp::BitXor => (AstNode::BinaryExpression, "^"), + BinaryOp::BitAnd => (AstNode::BinaryExpression, "&"), + BinaryOp::In => (AstNode::BinaryExpression, "in"), + BinaryOp::InstanceOf => (AstNode::BinaryExpression, "instanceof"), + BinaryOp::Exp => (AstNode::BinaryExpression, "**"), + }; + + let left = serialize_expr(ctx, node.left.as_ref()); + let right = serialize_expr(ctx, node.right.as_ref()); + + match node_type { + AstNode::LogicalExpression => { + ctx.write_logical_expr(&node.span, flag_str, left, right) + } + AstNode::BinaryExpression => { + ctx.write_bin_expr(&node.span, flag_str, left, right) + } + _ => unreachable!(), + } + } + Expr::Assign(node) => { + let left = match &node.left { + AssignTarget::Simple(simple_assign_target) => { + match simple_assign_target { + SimpleAssignTarget::Ident(target) => { + serialize_binding_ident(ctx, target) + } + SimpleAssignTarget::Member(target) => { + serialize_expr(ctx, &Expr::Member(target.clone())) + } + SimpleAssignTarget::SuperProp(target) => { + serialize_expr(ctx, &Expr::SuperProp(target.clone())) + } + SimpleAssignTarget::Paren(target) => { + serialize_expr(ctx, &target.expr) + } + SimpleAssignTarget::OptChain(target) => { + serialize_expr(ctx, &Expr::OptChain(target.clone())) + } + SimpleAssignTarget::TsAs(target) => { + serialize_expr(ctx, &Expr::TsAs(target.clone())) + } + SimpleAssignTarget::TsSatisfies(target) => { + serialize_expr(ctx, &Expr::TsSatisfies(target.clone())) + } + SimpleAssignTarget::TsNonNull(target) => { + serialize_expr(ctx, &Expr::TsNonNull(target.clone())) + } + SimpleAssignTarget::TsTypeAssertion(target) => { + serialize_expr(ctx, &Expr::TsTypeAssertion(target.clone())) + } + SimpleAssignTarget::TsInstantiation(target) => { + serialize_expr(ctx, &Expr::TsInstantiation(target.clone())) + } + SimpleAssignTarget::Invalid(_) => unreachable!(), + } + } + AssignTarget::Pat(target) => match target { + AssignTargetPat::Array(array_pat) => { + serialize_pat(ctx, &Pat::Array(array_pat.clone())) + } + AssignTargetPat::Object(object_pat) => { + serialize_pat(ctx, &Pat::Object(object_pat.clone())) + } + AssignTargetPat::Invalid(_) => unreachable!(), + }, + }; + + let right = serialize_expr(ctx, node.right.as_ref()); + + let op = match node.op { + AssignOp::Assign => "=", + AssignOp::AddAssign => "+=", + AssignOp::SubAssign => "-=", + AssignOp::MulAssign => "*=", + AssignOp::DivAssign => "/=", + AssignOp::ModAssign => "%=", + AssignOp::LShiftAssign => "<<=", + AssignOp::RShiftAssign => ">>=", + AssignOp::ZeroFillRShiftAssign => ">>>=", + AssignOp::BitOrAssign => "|=", + AssignOp::BitXorAssign => "^=", + AssignOp::BitAndAssign => "&=", + AssignOp::ExpAssign => "**=", + AssignOp::AndAssign => "&&=", + AssignOp::OrAssign => "||=", + AssignOp::NullishAssign => "??=", + }; + + ctx.write_assignment_expr(&node.span, op, left, right) + } + Expr::Member(node) => serialize_member_expr(ctx, node, false), + Expr::SuperProp(node) => { + let obj = ctx.write_super(&node.obj.span); + + let mut computed = false; + let prop = match &node.prop { + SuperProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name), + SuperProp::Computed(prop) => { + computed = true; + serialize_expr(ctx, &prop.expr) + } + }; + + ctx.write_member_expr(&node.span, false, computed, obj, prop) + } + Expr::Cond(node) => { + let test = serialize_expr(ctx, node.test.as_ref()); + let cons = serialize_expr(ctx, node.cons.as_ref()); + let alt = serialize_expr(ctx, node.alt.as_ref()); + + ctx.write_conditional_expr(&node.span, test, cons, alt) + } + Expr::Call(node) => { + if let Callee::Import(_) = node.callee { + let source = node + .args + .first() + .map_or(NodeRef(0), |arg| serialize_expr_or_spread(ctx, arg)); + + let options = node + .args + .get(1) + .map_or(NodeRef(0), |arg| serialize_expr_or_spread(ctx, arg)); + + ctx.write_import_expr(&node.span, source, options) + } else { + let callee = match &node.callee { + Callee::Super(super_node) => ctx.write_super(&super_node.span), + Callee::Import(_) => unreachable!("Already handled"), + Callee::Expr(expr) => serialize_expr(ctx, expr), + }; + + let type_arg = node + .type_args + .clone() + .map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref())); + + let args = node + .args + .iter() + .map(|arg| serialize_expr_or_spread(ctx, arg)) + .collect::>(); + + ctx.write_call_expr(&node.span, false, callee, type_arg, args) + } + } + Expr::New(node) => { + let callee = serialize_expr(ctx, node.callee.as_ref()); + + let args: Vec = node.args.as_ref().map_or(vec![], |args| { + args + .iter() + .map(|arg| serialize_expr_or_spread(ctx, arg)) + .collect::>() + }); + + let type_args = node + .type_args + .clone() + .map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref())); + + ctx.write_new_expr(&node.span, callee, type_args, args) + } + Expr::Seq(node) => { + let children = node + .exprs + .iter() + .map(|expr| serialize_expr(ctx, expr)) + .collect::>(); + + ctx.write_sequence_expr(&node.span, children) + } + Expr::Ident(node) => serialize_ident(ctx, node, None), + Expr::Lit(node) => serialize_lit(ctx, node), + Expr::Tpl(node) => { + let quasis = node + .quasis + .iter() + .map(|quasi| { + ctx.write_template_elem( + &quasi.span, + quasi.tail, + &quasi.raw, + &quasi + .cooked + .as_ref() + .map_or("".to_string(), |v| v.to_string()), + ) + }) + .collect::>(); + + let exprs = node + .exprs + .iter() + .map(|expr| serialize_expr(ctx, expr)) + .collect::>(); + + ctx.write_template_lit(&node.span, quasis, exprs) + } + Expr::TaggedTpl(node) => { + let tag = serialize_expr(ctx, &node.tag); + let type_param = node + .type_params + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); + let quasi = serialize_expr(ctx, &Expr::Tpl(*node.tpl.clone())); + + ctx.write_tagged_template_expr(&node.span, tag, type_param, quasi) + } + Expr::Arrow(node) => { + let type_param = + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); + + let params = node + .params + .iter() + .map(|param| serialize_pat(ctx, param)) + .collect::>(); + + let body = match node.body.as_ref() { + BlockStmtOrExpr::BlockStmt(block_stmt) => { + serialize_stmt(ctx, &Stmt::Block(block_stmt.clone())) + } + BlockStmtOrExpr::Expr(expr) => serialize_expr(ctx, expr.as_ref()), + }; + + let return_type = maybe_serialize_ts_type_ann(ctx, &node.return_type); + + ctx.write_arrow_fn_expr( + &node.span, + node.is_async, + node.is_generator, + type_param, + params, + return_type, + body, + ) + } + Expr::Class(node) => { + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let super_class = node + .class + .super_class + .as_ref() + .map(|expr| serialize_expr(ctx, expr.as_ref())); + + let implements = node + .class + .implements + .iter() + .map(|item| serialize_ts_expr_with_type_args(ctx, item)) + .collect::>(); + + let members = node + .class + .body + .iter() + .filter_map(|member| serialize_class_member(ctx, member)) + .collect::>(); + + let body = ctx.write_class_body(&node.class.span, members); + + ctx.write_class_expr( + &node.class.span, + false, + node.class.is_abstract, + ident, + super_class, + implements, + body, + ) + } + Expr::Yield(node) => { + let arg = node + .arg + .as_ref() + .map(|arg| serialize_expr(ctx, arg.as_ref())); + + ctx.write_yield_expr(&node.span, node.delegate, arg) + } + Expr::MetaProp(node) => { + let prop = ctx.write_identifier(&node.span, "meta", false, None); + ctx.write_meta_prop(&node.span, prop) + } + Expr::Await(node) => { + let arg = serialize_expr(ctx, node.arg.as_ref()); + ctx.write_await_expr(&node.span, arg) + } + Expr::Paren(node) => { + // Paren nodes are treated as a syntax only thing in TSEStree + // and are never materialized to actual AST nodes. + serialize_expr(ctx, &node.expr) + } + Expr::JSXMember(node) => serialize_jsx_member_expr(ctx, node), + Expr::JSXNamespacedName(node) => serialize_jsx_namespaced_name(ctx, node), + Expr::JSXEmpty(node) => ctx.write_jsx_empty_expr(&node.span), + Expr::JSXElement(node) => serialize_jsx_element(ctx, node), + Expr::JSXFragment(node) => serialize_jsx_fragment(ctx, node), + Expr::TsTypeAssertion(node) => { + let expr = serialize_expr(ctx, &node.expr); + let type_ann = serialize_ts_type(ctx, &node.type_ann); + + ctx.write_ts_type_assertion(&node.span, expr, type_ann) + } + Expr::TsConstAssertion(node) => { + let expr = serialize_expr(ctx, node.expr.as_ref()); + + let type_name = ctx.write_identifier(&node.span, "const", false, None); + let type_ann = ctx.write_ts_type_ref(&node.span, type_name, None); + + ctx.write_ts_as_expr(&node.span, expr, type_ann) + } + Expr::TsNonNull(node) => { + let expr = serialize_expr(ctx, node.expr.as_ref()); + ctx.write_ts_non_null(&node.span, expr) + } + Expr::TsAs(node) => { + let expr = serialize_expr(ctx, node.expr.as_ref()); + let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref()); + + ctx.write_ts_as_expr(&node.span, expr, type_ann) + } + Expr::TsInstantiation(_) => { + // Invalid syntax + unreachable!() + } + Expr::TsSatisfies(node) => { + let expr = serialize_expr(ctx, node.expr.as_ref()); + let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref()); + + ctx.write_ts_satisfies_expr(&node.span, expr, type_ann) + } + Expr::PrivateName(node) => serialize_private_name(ctx, node), + Expr::OptChain(node) => { + let expr = match node.base.as_ref() { + OptChainBase::Member(member_expr) => { + serialize_member_expr(ctx, member_expr, true) + } + OptChainBase::Call(opt_call) => { + let callee = serialize_expr(ctx, &opt_call.callee); + + let type_param_id = opt_call + .type_args + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); + + let args = opt_call + .args + .iter() + .map(|arg| serialize_expr_or_spread(ctx, arg)) + .collect::>(); + + ctx.write_call_expr(&opt_call.span, true, callee, type_param_id, args) + } + }; + + ctx.write_chain_expr(&node.span, expr) + } + Expr::Invalid(_) => { + unreachable!() + } + } +} + +fn serialize_prop_or_spread( + ctx: &mut TsEsTreeBuilder, + prop: &PropOrSpread, +) -> NodeRef { + match prop { + PropOrSpread::Spread(spread_element) => serialize_spread( + ctx, + spread_element.expr.as_ref(), + &spread_element.dot3_token, + ), + PropOrSpread::Prop(prop) => { + let mut shorthand = false; + let mut computed = false; + let mut method = false; + let mut kind = "init"; + + let (key, value) = match prop.as_ref() { + Prop::Shorthand(ident) => { + shorthand = true; + + let value = serialize_ident(ctx, ident, None); + let value2 = serialize_ident(ctx, ident, None); + (value, value2) + } + Prop::KeyValue(key_value_prop) => { + if let PropName::Computed(_) = key_value_prop.key { + computed = true; + } + + let key = serialize_prop_name(ctx, &key_value_prop.key); + let value = serialize_expr(ctx, key_value_prop.value.as_ref()); + + (key, value) + } + Prop::Assign(assign_prop) => { + let left = serialize_ident(ctx, &assign_prop.key, None); + let right = serialize_expr(ctx, assign_prop.value.as_ref()); + + let child_pos = ctx.write_assign_pat(&assign_prop.span, left, right); + + (left, child_pos) + } + Prop::Getter(getter_prop) => { + kind = "get"; + + let key = serialize_prop_name(ctx, &getter_prop.key); + + let value = serialize_expr( + ctx, + &Expr::Fn(FnExpr { + ident: None, + function: Box::new(Function { + params: vec![], + decorators: vec![], + span: getter_prop.span, + ctxt: SyntaxContext::empty(), + body: getter_prop.body.clone(), + is_generator: false, + is_async: false, + type_params: None, + return_type: getter_prop.type_ann.clone(), + }), + }), + ); + + (key, value) + } + Prop::Setter(setter_prop) => { + kind = "set"; + + let key_id = serialize_prop_name(ctx, &setter_prop.key); + + let param = Param::from(*setter_prop.param.clone()); + + let value_id = serialize_expr( + ctx, + &Expr::Fn(FnExpr { + ident: None, + function: Box::new(Function { + params: vec![param], + decorators: vec![], + span: setter_prop.span, + ctxt: SyntaxContext::empty(), + body: setter_prop.body.clone(), + is_generator: false, + is_async: false, + type_params: None, + return_type: None, + }), + }), + ); + + (key_id, value_id) + } + Prop::Method(method_prop) => { + method = true; + + let key_id = serialize_prop_name(ctx, &method_prop.key); + + let value_id = serialize_expr( + ctx, + &Expr::Fn(FnExpr { + ident: None, + function: method_prop.function.clone(), + }), + ); + + (key_id, value_id) + } + }; + + ctx.write_property( + &prop.span(), + shorthand, + computed, + method, + kind, + key, + value, + ) + } + } +} + +fn serialize_member_expr( + ctx: &mut TsEsTreeBuilder, + node: &MemberExpr, + optional: bool, +) -> NodeRef { + let mut computed = false; + let obj = serialize_expr(ctx, node.obj.as_ref()); + + let prop = match &node.prop { + MemberProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name), + MemberProp::PrivateName(private_name) => { + serialize_private_name(ctx, private_name) + } + MemberProp::Computed(computed_prop_name) => { + computed = true; + serialize_expr(ctx, computed_prop_name.expr.as_ref()) + } + }; + + ctx.write_member_expr(&node.span, optional, computed, obj, prop) +} + +fn serialize_expr_or_spread( + ctx: &mut TsEsTreeBuilder, + arg: &ExprOrSpread, +) -> NodeRef { + if let Some(spread) = &arg.spread { + serialize_spread(ctx, &arg.expr, spread) + } else { + serialize_expr(ctx, arg.expr.as_ref()) + } +} + +fn serialize_ident( + ctx: &mut TsEsTreeBuilder, + ident: &Ident, + type_ann: Option, +) -> NodeRef { + ctx.write_identifier( + &ident.span, + ident.sym.as_str(), + ident.optional, + type_ann, + ) +} + +fn serialize_module_export_name( + ctx: &mut TsEsTreeBuilder, + name: &ModuleExportName, +) -> NodeRef { + match &name { + ModuleExportName::Ident(ident) => serialize_ident(ctx, ident, None), + ModuleExportName::Str(lit) => serialize_lit(ctx, &Lit::Str(lit.clone())), + } +} + +fn serialize_decl(ctx: &mut TsEsTreeBuilder, decl: &Decl) -> NodeRef { + match decl { + Decl::Class(node) => { + let ident = serialize_ident(ctx, &node.ident, None); + + let super_class = node + .class + .super_class + .as_ref() + .map(|expr| serialize_expr(ctx, expr.as_ref())); + + let implements = node + .class + .implements + .iter() + .map(|item| serialize_ts_expr_with_type_args(ctx, item)) + .collect::>(); + + let members = node + .class + .body + .iter() + .filter_map(|member| serialize_class_member(ctx, member)) + .collect::>(); + + let body = ctx.write_class_body(&node.class.span, members); + + ctx.write_class_decl( + &node.class.span, + false, + node.class.is_abstract, + Some(ident), + super_class, + implements, + body, + ) + } + Decl::Fn(node) => { + let ident_id = serialize_ident(ctx, &node.ident, None); + let type_param_id = + maybe_serialize_ts_type_param_decl(ctx, &node.function.type_params); + let return_type = + maybe_serialize_ts_type_ann(ctx, &node.function.return_type); + + let body = node + .function + .body + .as_ref() + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()))); + + let params = node + .function + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat)) + .collect::>(); + + ctx.write_fn_decl( + &node.function.span, + node.declare, + node.function.is_async, + node.function.is_generator, + Some(ident_id), + type_param_id, + return_type, + body, + params, + ) + } + Decl::Var(node) => { + let children = node + .decls + .iter() + .map(|decl| { + let ident = serialize_pat(ctx, &decl.name); + let init = decl + .init + .as_ref() + .map(|init| serialize_expr(ctx, init.as_ref())); + + ctx.write_var_declarator(&decl.span, ident, init) + }) + .collect::>(); + + let kind = match node.kind { + VarDeclKind::Var => "var", + VarDeclKind::Let => "let", + VarDeclKind::Const => "const", + }; + + ctx.write_var_decl(&node.span, node.declare, kind, children) + } + Decl::Using(node) => { + let kind = if node.is_await { + "await using" + } else { + "using" + }; + + let children = node + .decls + .iter() + .map(|decl| { + let ident = serialize_pat(ctx, &decl.name); + let init = decl + .init + .as_ref() + .map(|init| serialize_expr(ctx, init.as_ref())); + + ctx.write_var_declarator(&decl.span, ident, init) + }) + .collect::>(); + + ctx.write_var_decl(&node.span, false, kind, children) + } + Decl::TsInterface(node) => { + let ident_id = serialize_ident(ctx, &node.id, None); + let type_param = + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); + + let extend_ids = node + .extends + .iter() + .map(|item| { + let expr = serialize_expr(ctx, &item.expr); + let type_args = item + .type_args + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); + + ctx.write_ts_interface_heritage(&item.span, expr, type_args) + }) + .collect::>(); + + let body_elem_ids = node + .body + .body + .iter() + .map(|item| serialize_ts_type_elem(ctx, item)) + .collect::>(); + + let body_pos = + ctx.write_ts_interface_body(&node.body.span, body_elem_ids); + ctx.write_ts_interface( + &node.span, + node.declare, + ident_id, + type_param, + extend_ids, + body_pos, + ) + } + Decl::TsTypeAlias(node) => { + let ident = serialize_ident(ctx, &node.id, None); + let type_ann = serialize_ts_type(ctx, &node.type_ann); + let type_param = + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); + + ctx.write_ts_type_alias( + &node.span, + node.declare, + ident, + type_param, + type_ann, + ) + } + Decl::TsEnum(node) => { + let id = serialize_ident(ctx, &node.id, None); + + let members = node + .members + .iter() + .map(|member| { + let ident = match &member.id { + TsEnumMemberId::Ident(ident) => serialize_ident(ctx, ident, None), + TsEnumMemberId::Str(lit_str) => { + serialize_lit(ctx, &Lit::Str(lit_str.clone())) + } + }; + + let init = member.init.as_ref().map(|init| serialize_expr(ctx, init)); + + ctx.write_ts_enum_member(&member.span, ident, init) + }) + .collect::>(); + + let body = ctx.write_ts_enum_body(&node.span, members); + ctx.write_ts_enum(&node.span, node.declare, node.is_const, id, body) + } + Decl::TsModule(node) => { + let ident = match &node.id { + TsModuleName::Ident(ident) => serialize_ident(ctx, ident, None), + TsModuleName::Str(str_lit) => { + serialize_lit(ctx, &Lit::Str(str_lit.clone())) + } + }; + + let body = node + .body + .as_ref() + .map(|body| serialize_ts_namespace_body(ctx, body)); + + ctx.write_ts_module_decl( + &node.span, + node.declare, + node.global, + ident, + body, + ) + } + } +} + +fn serialize_ts_namespace_body( + ctx: &mut TsEsTreeBuilder, + node: &TsNamespaceBody, +) -> NodeRef { + match node { + TsNamespaceBody::TsModuleBlock(mod_block) => { + let items = mod_block + .body + .iter() + .map(|item| match item { + ModuleItem::ModuleDecl(decl) => serialize_module_decl(ctx, decl), + ModuleItem::Stmt(stmt) => serialize_stmt(ctx, stmt), + }) + .collect::>(); + + ctx.write_ts_module_block(&mod_block.span, items) + } + TsNamespaceBody::TsNamespaceDecl(node) => { + let ident = serialize_ident(ctx, &node.id, None); + let body = serialize_ts_namespace_body(ctx, &node.body); + + ctx.write_ts_module_decl( + &node.span, + node.declare, + node.global, + ident, + Some(body), + ) + } + } +} + +fn serialize_ts_type_elem( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeElement, +) -> NodeRef { + match node { + TsTypeElement::TsCallSignatureDecl(ts_call) => { + let type_ann = + maybe_serialize_ts_type_param_decl(ctx, &ts_call.type_params); + let return_type = maybe_serialize_ts_type_ann(ctx, &ts_call.type_ann); + let params = ts_call + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + ctx.write_ts_call_sig_decl(&ts_call.span, type_ann, params, return_type) + } + TsTypeElement::TsConstructSignatureDecl(sig) => { + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &sig.type_params); + + let params = sig + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + // Must be present + let return_type = + maybe_serialize_ts_type_ann(ctx, &sig.type_ann).unwrap(); + + ctx.write_ts_construct_sig(&sig.span, type_params, params, return_type) + } + TsTypeElement::TsPropertySignature(sig) => { + let key = serialize_expr(ctx, &sig.key); + let type_ann = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); + + ctx.write_ts_property_sig( + &sig.span, + sig.computed, + sig.optional, + sig.readonly, + key, + type_ann, + ) + } + TsTypeElement::TsGetterSignature(sig) => { + let key = serialize_expr(ctx, sig.key.as_ref()); + let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); + + ctx.write_ts_getter_sig(&sig.span, key, return_type) + } + TsTypeElement::TsSetterSignature(sig) => { + let key = serialize_expr(ctx, sig.key.as_ref()); + let param = serialize_ts_fn_param(ctx, &sig.param); + + ctx.write_ts_setter_sig(&sig.span, key, param) + } + TsTypeElement::TsMethodSignature(sig) => { + let key = serialize_expr(ctx, &sig.key); + let type_parms = + maybe_serialize_ts_type_param_decl(ctx, &sig.type_params); + let params = sig + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); + + ctx.write_ts_method_sig( + &sig.span, + sig.computed, + sig.optional, + key, + type_parms, + params, + return_type, + ) + } + TsTypeElement::TsIndexSignature(sig) => serialize_ts_index_sig(ctx, sig), + } +} + +fn serialize_ts_index_sig( + ctx: &mut TsEsTreeBuilder, + node: &TsIndexSignature, +) -> NodeRef { + let params = node + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + + ctx.write_ts_index_sig(&node.span, node.readonly, params, type_ann) +} + +fn accessibility_to_str(accessibility: &Accessibility) -> String { + match accessibility { + Accessibility::Public => "public".to_string(), + Accessibility::Protected => "protected".to_string(), + Accessibility::Private => "private".to_string(), + } +} + +fn serialize_private_name( + ctx: &mut TsEsTreeBuilder, + node: &PrivateName, +) -> NodeRef { + ctx.write_private_identifier(&node.span, node.name.as_str()) +} + +fn serialize_jsx_element( + ctx: &mut TsEsTreeBuilder, + node: &JSXElement, +) -> NodeRef { + let open = serialize_jsx_opening_element(ctx, &node.opening); + + let close = node.closing.as_ref().map(|closing| { + let name = serialize_jsx_element_name(ctx, &closing.name); + ctx.write_jsx_closing_elem(&closing.span, name) + }); + + let children = serialize_jsx_children(ctx, &node.children); + + ctx.write_jsx_elem(&node.span, open, close, children) +} + +fn serialize_jsx_fragment( + ctx: &mut TsEsTreeBuilder, + node: &JSXFragment, +) -> NodeRef { + let opening = ctx.write_jsx_opening_frag(&node.opening.span); + let closing = ctx.write_jsx_closing_frag(&node.closing.span); + let children = serialize_jsx_children(ctx, &node.children); + + ctx.write_jsx_frag(&node.span, opening, closing, children) +} + +fn serialize_jsx_children( + ctx: &mut TsEsTreeBuilder, + children: &[JSXElementChild], +) -> Vec { + children + .iter() + .map(|child| { + match child { + JSXElementChild::JSXText(text) => { + ctx.write_jsx_text(&text.span, &text.raw, &text.value) + } + JSXElementChild::JSXExprContainer(container) => { + serialize_jsx_container_expr(ctx, container) + } + JSXElementChild::JSXElement(el) => serialize_jsx_element(ctx, el), + JSXElementChild::JSXFragment(frag) => serialize_jsx_fragment(ctx, frag), + // No parser supports this + JSXElementChild::JSXSpreadChild(_) => unreachable!(), + } + }) + .collect::>() +} + +fn serialize_jsx_member_expr( + ctx: &mut TsEsTreeBuilder, + node: &JSXMemberExpr, +) -> NodeRef { + let obj = match &node.obj { + JSXObject::JSXMemberExpr(member) => serialize_jsx_member_expr(ctx, member), + JSXObject::Ident(ident) => serialize_jsx_identifier(ctx, ident), + }; + + let prop = serialize_ident_name_as_jsx_identifier(ctx, &node.prop); + + ctx.write_jsx_member_expr(&node.span, obj, prop) +} + +fn serialize_jsx_element_name( + ctx: &mut TsEsTreeBuilder, + node: &JSXElementName, +) -> NodeRef { + match &node { + JSXElementName::Ident(ident) => serialize_jsx_identifier(ctx, ident), + JSXElementName::JSXMemberExpr(member) => { + serialize_jsx_member_expr(ctx, member) + } + JSXElementName::JSXNamespacedName(ns) => { + serialize_jsx_namespaced_name(ctx, ns) + } + } +} + +fn serialize_jsx_opening_element( + ctx: &mut TsEsTreeBuilder, + node: &JSXOpeningElement, +) -> NodeRef { + let name = serialize_jsx_element_name(ctx, &node.name); + + let type_args = node + .type_args + .as_ref() + .map(|arg| serialize_ts_param_inst(ctx, arg)); + + let attrs = node + .attrs + .iter() + .map(|attr| match attr { + JSXAttrOrSpread::JSXAttr(attr) => { + let name = match &attr.name { + JSXAttrName::Ident(name) => { + serialize_ident_name_as_jsx_identifier(ctx, name) + } + JSXAttrName::JSXNamespacedName(node) => { + serialize_jsx_namespaced_name(ctx, node) + } + }; + + let value = attr.value.as_ref().map(|value| match value { + JSXAttrValue::Lit(lit) => serialize_lit(ctx, lit), + JSXAttrValue::JSXExprContainer(container) => { + serialize_jsx_container_expr(ctx, container) + } + JSXAttrValue::JSXElement(el) => serialize_jsx_element(ctx, el), + JSXAttrValue::JSXFragment(frag) => serialize_jsx_fragment(ctx, frag), + }); + + ctx.write_jsx_attr(&attr.span, name, value) + } + JSXAttrOrSpread::SpreadElement(spread) => { + let arg = serialize_expr(ctx, &spread.expr); + ctx.write_jsx_spread_attr(&spread.dot3_token, arg) + } + }) + .collect::>(); + + ctx.write_jsx_opening_elem( + &node.span, + node.self_closing, + name, + attrs, + type_args, + ) +} + +fn serialize_jsx_container_expr( + ctx: &mut TsEsTreeBuilder, + node: &JSXExprContainer, +) -> NodeRef { + let expr = match &node.expr { + JSXExpr::JSXEmptyExpr(expr) => ctx.write_jsx_empty_expr(&expr.span), + JSXExpr::Expr(expr) => serialize_expr(ctx, expr), + }; + + ctx.write_jsx_expr_container(&node.span, expr) +} + +fn serialize_jsx_namespaced_name( + ctx: &mut TsEsTreeBuilder, + node: &JSXNamespacedName, +) -> NodeRef { + let ns = ctx.write_jsx_identifier(&node.ns.span, &node.ns.sym); + let name = ctx.write_jsx_identifier(&node.name.span, &node.name.sym); + + ctx.write_jsx_namespaced_name(&node.span, ns, name) +} + +fn serialize_ident_name_as_jsx_identifier( + ctx: &mut TsEsTreeBuilder, + node: &IdentName, +) -> NodeRef { + ctx.write_jsx_identifier(&node.span, &node.sym) +} + +fn serialize_jsx_identifier( + ctx: &mut TsEsTreeBuilder, + node: &Ident, +) -> NodeRef { + ctx.write_jsx_identifier(&node.span, &node.sym) +} + +fn serialize_pat(ctx: &mut TsEsTreeBuilder, pat: &Pat) -> NodeRef { + match pat { + Pat::Ident(node) => serialize_binding_ident(ctx, node), + Pat::Array(node) => { + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + + let children = node + .elems + .iter() + .map(|pat| pat.as_ref().map_or(NodeRef(0), |v| serialize_pat(ctx, v))) + .collect::>(); + + ctx.write_arr_pat(&node.span, node.optional, type_ann, children) + } + Pat::Rest(node) => { + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + let arg = serialize_pat(ctx, &node.arg); + + ctx.write_rest_elem(&node.span, type_ann, arg) + } + Pat::Object(node) => { + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + + let children = node + .props + .iter() + .map(|prop| match prop { + ObjectPatProp::KeyValue(key_value_prop) => { + let computed = matches!(key_value_prop.key, PropName::Computed(_)); + + let key = serialize_prop_name(ctx, &key_value_prop.key); + let value = serialize_pat(ctx, key_value_prop.value.as_ref()); + + ctx.write_property( + &key_value_prop.span(), + false, + computed, + false, + "init", + key, + value, + ) + } + ObjectPatProp::Assign(assign_pat_prop) => { + let ident = serialize_binding_ident(ctx, &assign_pat_prop.key); + + let value = assign_pat_prop + .value + .as_ref() + .map_or(NodeRef(0), |value| serialize_expr(ctx, value)); + + ctx.write_property( + &assign_pat_prop.span, + false, + false, + false, + "init", + ident, + value, + ) + } + ObjectPatProp::Rest(rest_pat) => { + serialize_pat(ctx, &Pat::Rest(rest_pat.clone())) + } + }) + .collect::>(); + + ctx.write_obj_pat(&node.span, node.optional, type_ann, children) + } + Pat::Assign(node) => { + let left = serialize_pat(ctx, &node.left); + let right = serialize_expr(ctx, &node.right); + + ctx.write_assign_pat(&node.span, left, right) + } + Pat::Invalid(_) => unreachable!(), + Pat::Expr(node) => serialize_expr(ctx, node), + } +} + +fn serialize_for_head( + ctx: &mut TsEsTreeBuilder, + for_head: &ForHead, +) -> NodeRef { + match for_head { + ForHead::VarDecl(var_decl) => { + serialize_decl(ctx, &Decl::Var(var_decl.clone())) + } + ForHead::UsingDecl(using_decl) => { + serialize_decl(ctx, &Decl::Using(using_decl.clone())) + } + ForHead::Pat(pat) => serialize_pat(ctx, pat), + } +} + +fn serialize_spread( + ctx: &mut TsEsTreeBuilder, + expr: &Expr, + span: &Span, +) -> NodeRef { + let expr = serialize_expr(ctx, expr); + ctx.write_spread(span, expr) +} + +fn serialize_ident_name( + ctx: &mut TsEsTreeBuilder, + ident_name: &IdentName, +) -> NodeRef { + ctx.write_identifier(&ident_name.span, ident_name.sym.as_str(), false, None) +} + +fn serialize_prop_name( + ctx: &mut TsEsTreeBuilder, + prop_name: &PropName, +) -> NodeRef { + match prop_name { + PropName::Ident(ident_name) => serialize_ident_name(ctx, ident_name), + PropName::Str(str_prop) => serialize_lit(ctx, &Lit::Str(str_prop.clone())), + PropName::Num(number) => serialize_lit(ctx, &Lit::Num(number.clone())), + PropName::Computed(node) => serialize_expr(ctx, &node.expr), + PropName::BigInt(big_int) => { + serialize_lit(ctx, &Lit::BigInt(big_int.clone())) + } + } +} + +fn serialize_lit(ctx: &mut TsEsTreeBuilder, lit: &Lit) -> NodeRef { + match lit { + Lit::Str(node) => { + let raw_value = if let Some(v) = &node.raw { + v.to_string() + } else { + format!("{}", node.value).to_string() + }; + + ctx.write_str_lit(&node.span, &node.value, &raw_value) + } + Lit::Bool(node) => ctx.write_bool_lit(&node.span, node.value), + Lit::Null(node) => ctx.write_null_lit(&node.span), + Lit::Num(node) => { + let raw_value = if let Some(v) = &node.raw { + v.to_string() + } else { + format!("{}", node.value).to_string() + }; + + let value = node.raw.as_ref().unwrap(); + ctx.write_num_lit(&node.span, value, &raw_value) + } + Lit::BigInt(node) => { + let raw_bigint_value = if let Some(v) = &node.raw { + let mut s = v.to_string(); + s.pop(); + s.to_string() + } else { + format!("{}", node.value).to_string() + }; + + let raw_value = if let Some(v) = &node.raw { + v.to_string() + } else { + format!("{}", node.value).to_string() + }; + + ctx.write_bigint_lit( + &node.span, + &node.value.to_string(), + &raw_value, + &raw_bigint_value, + ) + } + Lit::Regex(node) => { + let raw = format!("/{}/{}", node.exp.as_str(), node.flags.as_str()); + + ctx.write_regex_lit( + &node.span, + node.exp.as_str(), + node.flags.as_str(), + &raw, + &raw, + ) + } + Lit::JSXText(node) => { + ctx.write_jsx_text(&node.span, &node.raw, &node.value) + } + } +} + +fn serialize_class_member( + ctx: &mut TsEsTreeBuilder, + member: &ClassMember, +) -> Option { + match member { + ClassMember::Constructor(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let key = serialize_prop_name(ctx, &node.key); + let params = node + .params + .iter() + .map(|param| match param { + ParamOrTsParamProp::TsParamProp(prop) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let decorators = prop + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + let paramter = match &prop.param { + TsParamPropParam::Ident(binding_ident) => { + serialize_binding_ident(ctx, binding_ident) + } + TsParamPropParam::Assign(assign_pat) => { + serialize_pat(ctx, &Pat::Assign(assign_pat.clone())) + } + }; + + ctx.write_ts_param_prop( + &prop.span, + prop.is_override, + prop.readonly, + a11y, + decorators, + paramter, + ) + } + ParamOrTsParamProp::Param(param) => serialize_pat(ctx, ¶m.pat), + }) + .collect::>(); + + let body = node + .body + .as_ref() + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()))); + + let value = ctx.write_fn_expr( + &node.span, false, false, None, None, params, None, body, + ); + + Some(ctx.write_class_method( + &node.span, + false, + false, + node.is_optional, + false, + false, + "constructor", + a11y, + key, + value, + )) + } + ClassMember::Method(node) => { + let key = serialize_prop_name(ctx, &node.key); + + Some(serialize_class_method( + ctx, + &node.span, + node.is_abstract, + node.is_override, + node.is_optional, + node.is_static, + node.accessibility, + &node.kind, + key, + &node.function, + )) + } + ClassMember::PrivateMethod(node) => { + let key = serialize_private_name(ctx, &node.key); + + Some(serialize_class_method( + ctx, + &node.span, + node.is_abstract, + node.is_override, + node.is_optional, + node.is_static, + node.accessibility, + &node.kind, + key, + &node.function, + )) + } + ClassMember::ClassProp(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let key = serialize_prop_name(ctx, &node.key); + let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr)); + + let decorators = node + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + Some(ctx.write_class_prop( + &node.span, + node.declare, + false, + node.is_optional, + node.is_override, + node.readonly, + node.is_static, + a11y, + decorators, + key, + value, + )) + } + ClassMember::PrivateProp(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let decorators = node + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + let key = serialize_private_name(ctx, &node.key); + + let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr)); + + Some(ctx.write_class_prop( + &node.span, + false, + false, + node.is_optional, + node.is_override, + node.readonly, + node.is_static, + a11y, + decorators, + key, + value, + )) + } + ClassMember::TsIndexSignature(node) => { + Some(serialize_ts_index_sig(ctx, node)) + } + ClassMember::Empty(_) => None, + ClassMember::StaticBlock(node) => { + let body = serialize_stmt(ctx, &Stmt::Block(node.body.clone())); + Some(ctx.write_static_block(&node.span, body)) + } + ClassMember::AutoAccessor(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + let decorators = node + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + let key = match &node.key { + Key::Private(private_name) => serialize_private_name(ctx, private_name), + Key::Public(prop_name) => serialize_prop_name(ctx, prop_name), + }; + + let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr)); + + Some(ctx.write_accessor_property( + &node.span, + false, + false, + false, + node.is_override, + false, + node.is_static, + a11y, + decorators, + key, + value, + )) + } + } +} + +#[allow(clippy::too_many_arguments)] +fn serialize_class_method( + ctx: &mut TsEsTreeBuilder, + span: &Span, + is_abstract: bool, + is_override: bool, + is_optional: bool, + is_static: bool, + accessibility: Option, + method_kind: &MethodKind, + key: NodeRef, + function: &Function, +) -> NodeRef { + let kind = match method_kind { + MethodKind::Method => "method", + MethodKind::Getter => "getter", + MethodKind::Setter => "setter", + }; + + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &function.type_params); + let params = function + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat)) + .collect::>(); + + let return_type = maybe_serialize_ts_type_ann(ctx, &function.return_type); + + let body = function + .body + .as_ref() + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()))); + + let value = if let Some(body) = body { + ctx.write_fn_expr( + &function.span, + function.is_async, + function.is_generator, + None, + type_params, + params, + return_type, + Some(body), + ) + } else { + ctx.write_ts_empty_body_fn_expr( + span, + false, + false, + function.is_async, + function.is_generator, + None, + type_params, + params, + return_type, + ) + }; + + let a11y = accessibility.as_ref().map(accessibility_to_str); + + if is_abstract { + ctx.write_ts_abstract_method_def( + span, + false, + is_optional, + is_override, + false, + a11y, + key, + value, + ) + } else { + ctx.write_class_method( + span, + false, + false, + is_optional, + is_override, + is_static, + kind, + a11y, + key, + value, + ) + } +} + +fn serialize_ts_expr_with_type_args( + ctx: &mut TsEsTreeBuilder, + node: &TsExprWithTypeArgs, +) -> NodeRef { + let expr = serialize_expr(ctx, &node.expr); + let type_args = node + .type_args + .as_ref() + .map(|arg| serialize_ts_param_inst(ctx, arg)); + + ctx.write_ts_class_implements(&node.span, expr, type_args) +} + +fn serialize_decorator(ctx: &mut TsEsTreeBuilder, node: &Decorator) -> NodeRef { + let expr = serialize_expr(ctx, &node.expr); + ctx.write_decorator(&node.span, expr) +} + +fn serialize_binding_ident( + ctx: &mut TsEsTreeBuilder, + node: &BindingIdent, +) -> NodeRef { + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + ctx.write_identifier(&node.span, &node.sym, node.optional, type_ann) +} + +fn serialize_ts_param_inst( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeParamInstantiation, +) -> NodeRef { + let params = node + .params + .iter() + .map(|param| serialize_ts_type(ctx, param)) + .collect::>(); + + ctx.write_ts_type_param_inst(&node.span, params) +} + +fn serialize_ts_type(ctx: &mut TsEsTreeBuilder, node: &TsType) -> NodeRef { + match node { + TsType::TsKeywordType(node) => { + let kind = match node.kind { + TsKeywordTypeKind::TsAnyKeyword => TsKeywordKind::Any, + TsKeywordTypeKind::TsUnknownKeyword => TsKeywordKind::Unknown, + TsKeywordTypeKind::TsNumberKeyword => TsKeywordKind::Number, + TsKeywordTypeKind::TsObjectKeyword => TsKeywordKind::Object, + TsKeywordTypeKind::TsBooleanKeyword => TsKeywordKind::Boolean, + TsKeywordTypeKind::TsBigIntKeyword => TsKeywordKind::BigInt, + TsKeywordTypeKind::TsStringKeyword => TsKeywordKind::String, + TsKeywordTypeKind::TsSymbolKeyword => TsKeywordKind::Symbol, + TsKeywordTypeKind::TsVoidKeyword => TsKeywordKind::Void, + TsKeywordTypeKind::TsUndefinedKeyword => TsKeywordKind::Undefined, + TsKeywordTypeKind::TsNullKeyword => TsKeywordKind::Null, + TsKeywordTypeKind::TsNeverKeyword => TsKeywordKind::Never, + TsKeywordTypeKind::TsIntrinsicKeyword => TsKeywordKind::Intrinsic, + }; + + ctx.write_ts_keyword(kind, &node.span) + } + TsType::TsThisType(node) => ctx.write_ts_this_type(&node.span), + TsType::TsFnOrConstructorType(node) => match node { + TsFnOrConstructorType::TsFnType(node) => { + let param_ids = node + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + ctx.write_ts_fn_type(&node.span, param_ids) + } + TsFnOrConstructorType::TsConstructorType(node) => { + // interface Foo { new(arg1: any): any } + let type_params = node + .type_params + .as_ref() + .map(|param| serialize_ts_type_param_decl(ctx, param)); + + let params = node + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + let return_type = serialize_ts_type_ann(ctx, node.type_ann.as_ref()); + + ctx.write_ts_construct_sig(&node.span, type_params, params, return_type) + } + }, + TsType::TsTypeRef(node) => { + let name = serialize_ts_entity_name(ctx, &node.type_name); + + let type_args = node + .type_params + .clone() + .map(|param| serialize_ts_param_inst(ctx, ¶m)); + + ctx.write_ts_type_ref(&node.span, name, type_args) + } + TsType::TsTypeQuery(node) => { + let expr_name = match &node.expr_name { + TsTypeQueryExpr::TsEntityName(entity) => { + serialize_ts_entity_name(ctx, entity) + } + TsTypeQueryExpr::Import(child) => { + serialize_ts_type(ctx, &TsType::TsImportType(child.clone())) + } + }; + + let type_args = node + .type_args + .clone() + .map(|param| serialize_ts_param_inst(ctx, ¶m)); + + ctx.write_ts_type_query(&node.span, expr_name, type_args) + } + TsType::TsTypeLit(node) => { + let members = node + .members + .iter() + .map(|member| serialize_ts_type_elem(ctx, member)) + .collect::>(); + + ctx.write_ts_type_lit(&node.span, members) + } + TsType::TsArrayType(node) => { + let elem = serialize_ts_type(ctx, &node.elem_type); + ctx.write_ts_array_type(&node.span, elem) + } + TsType::TsTupleType(node) => { + let children = node + .elem_types + .iter() + .map(|elem| { + if let Some(label) = &elem.label { + let label = serialize_pat(ctx, label); + let type_id = serialize_ts_type(ctx, elem.ty.as_ref()); + + ctx.write_ts_named_tuple_member(&elem.span, label, type_id) + } else { + serialize_ts_type(ctx, elem.ty.as_ref()) + } + }) + .collect::>(); + + ctx.write_ts_tuple_type(&node.span, children) + } + TsType::TsOptionalType(node) => { + let type_ann = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_optional_type(&node.span, type_ann) + } + TsType::TsRestType(node) => { + let type_ann = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_rest_type(&node.span, type_ann) + } + TsType::TsUnionOrIntersectionType(node) => match node { + TsUnionOrIntersectionType::TsUnionType(node) => { + let children = node + .types + .iter() + .map(|item| serialize_ts_type(ctx, item)) + .collect::>(); + + ctx.write_ts_union_type(&node.span, children) + } + TsUnionOrIntersectionType::TsIntersectionType(node) => { + let children = node + .types + .iter() + .map(|item| serialize_ts_type(ctx, item)) + .collect::>(); + + ctx.write_ts_intersection_type(&node.span, children) + } + }, + TsType::TsConditionalType(node) => { + let check = serialize_ts_type(ctx, &node.check_type); + let extends = serialize_ts_type(ctx, &node.extends_type); + let v_true = serialize_ts_type(ctx, &node.true_type); + let v_false = serialize_ts_type(ctx, &node.false_type); + + ctx.write_ts_conditional_type(&node.span, check, extends, v_true, v_false) + } + TsType::TsInferType(node) => { + let param = serialize_ts_type_param(ctx, &node.type_param); + ctx.write_ts_infer_type(&node.span, param) + } + TsType::TsParenthesizedType(node) => { + // Not materialized in TSEstree + serialize_ts_type(ctx, &node.type_ann) + } + TsType::TsTypeOperator(node) => { + let type_ann = serialize_ts_type(ctx, &node.type_ann); + + let op = match node.op { + TsTypeOperatorOp::KeyOf => "keyof", + TsTypeOperatorOp::Unique => "unique", + TsTypeOperatorOp::ReadOnly => "readonly", + }; + + ctx.write_ts_type_op(&node.span, op, type_ann) + } + TsType::TsIndexedAccessType(node) => { + let index = serialize_ts_type(ctx, &node.index_type); + let obj = serialize_ts_type(ctx, &node.obj_type); + + ctx.write_ts_indexed_access_type(&node.span, index, obj) + } + TsType::TsMappedType(node) => { + let name = maybe_serialize_ts_type(ctx, &node.name_type); + let type_ann = maybe_serialize_ts_type(ctx, &node.type_ann); + let type_param = serialize_ts_type_param(ctx, &node.type_param); + + ctx.write_ts_mapped_type( + &node.span, + node.readonly, + node.optional, + name, + type_ann, + type_param, + ) + } + TsType::TsLitType(node) => serialize_ts_lit_type(ctx, node), + TsType::TsTypePredicate(node) => { + let param_name = match &node.param_name { + TsThisTypeOrIdent::TsThisType(node) => { + ctx.write_ts_this_type(&node.span) + } + TsThisTypeOrIdent::Ident(ident) => serialize_ident(ctx, ident, None), + }; + + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + + ctx.write_ts_type_predicate( + &node.span, + node.asserts, + param_name, + type_ann, + ) + } + TsType::TsImportType(node) => { + let arg = serialize_ts_lit_type( + ctx, + &TsLitType { + lit: TsLit::Str(node.arg.clone()), + span: node.arg.span, + }, + ); + + let type_arg = node + .type_args + .clone() + .map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref())); + + let qualifier = node + .qualifier + .clone() + .map(|quali| serialize_ts_entity_name(ctx, &quali)); + + ctx.write_ts_import_type(&node.span, arg, qualifier, type_arg) + } + } +} + +fn serialize_ts_lit_type( + ctx: &mut TsEsTreeBuilder, + node: &TsLitType, +) -> NodeRef { + match &node.lit { + TsLit::Number(lit) => { + let lit = serialize_lit(ctx, &Lit::Num(lit.clone())); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::Str(lit) => { + let lit = serialize_lit(ctx, &Lit::Str(lit.clone())); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::Bool(lit) => { + let lit = serialize_lit(ctx, &Lit::Bool(*lit)); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::BigInt(lit) => { + let lit = serialize_lit(ctx, &Lit::BigInt(lit.clone())); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::Tpl(lit) => { + let quasis = lit + .quasis + .iter() + .map(|quasi| { + ctx.write_template_elem( + &quasi.span, + quasi.tail, + &quasi.raw, + &quasi + .cooked + .as_ref() + .map_or("".to_string(), |v| v.to_string()), + ) + }) + .collect::>(); + let types = lit + .types + .iter() + .map(|ts_type| serialize_ts_type(ctx, ts_type)) + .collect::>(); + + ctx.write_ts_tpl_lit(&node.span, quasis, types) + } + } +} + +fn serialize_ts_entity_name( + ctx: &mut TsEsTreeBuilder, + node: &TsEntityName, +) -> NodeRef { + match &node { + TsEntityName::TsQualifiedName(node) => { + let left = serialize_ts_entity_name(ctx, &node.left); + let right = serialize_ident_name(ctx, &node.right); + + ctx.write_ts_qualified_name(&node.span, left, right) + } + TsEntityName::Ident(ident) => serialize_ident(ctx, ident, None), + } +} + +fn maybe_serialize_ts_type_ann( + ctx: &mut TsEsTreeBuilder, + node: &Option>, +) -> Option { + node + .as_ref() + .map(|type_ann| serialize_ts_type_ann(ctx, type_ann)) +} + +fn serialize_ts_type_ann( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeAnn, +) -> NodeRef { + let v_type = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_type_ann(&node.span, v_type) +} + +fn maybe_serialize_ts_type( + ctx: &mut TsEsTreeBuilder, + node: &Option>, +) -> Option { + node.as_ref().map(|item| serialize_ts_type(ctx, item)) +} + +fn serialize_ts_type_param( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeParam, +) -> NodeRef { + let name = serialize_ident(ctx, &node.name, None); + let constraint = maybe_serialize_ts_type(ctx, &node.constraint); + let default = maybe_serialize_ts_type(ctx, &node.default); + + ctx.write_ts_type_param( + &node.span, + node.is_in, + node.is_out, + node.is_const, + name, + constraint, + default, + ) +} + +fn maybe_serialize_ts_type_param_decl( + ctx: &mut TsEsTreeBuilder, + node: &Option>, +) -> Option { + node + .as_ref() + .map(|node| serialize_ts_type_param_decl(ctx, node)) +} + +fn serialize_ts_type_param_decl( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeParamDecl, +) -> NodeRef { + let params = node + .params + .iter() + .map(|param| serialize_ts_type_param(ctx, param)) + .collect::>(); + + ctx.write_ts_type_param_decl(&node.span, params) +} + +fn serialize_ts_fn_param( + ctx: &mut TsEsTreeBuilder, + node: &TsFnParam, +) -> NodeRef { + match node { + TsFnParam::Ident(ident) => serialize_binding_ident(ctx, ident), + TsFnParam::Array(pat) => serialize_pat(ctx, &Pat::Array(pat.clone())), + TsFnParam::Rest(pat) => serialize_pat(ctx, &Pat::Rest(pat.clone())), + TsFnParam::Object(pat) => serialize_pat(ctx, &Pat::Object(pat.clone())), + } +} diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs new file mode 100644 index 0000000000..340f9f3225 --- /dev/null +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -0,0 +1,2748 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::fmt; +use std::fmt::Debug; +use std::fmt::Display; + +use deno_ast::swc::common::Span; +use deno_ast::view::TruePlusMinus; + +use super::buffer::AstBufSerializer; +use super::buffer::NodeRef; +use super::buffer::SerializeCtx; + +#[derive(Debug, Clone, PartialEq)] +pub enum AstNode { + // First node must always be the empty/invalid node + Invalid, + RefArray, + // Typically the + Program, + + // Module declarations + ExportAllDeclaration, + ExportDefaultDeclaration, + ExportNamedDeclaration, + ImportDeclaration, + ImportSpecifier, + ImportAttribute, + ImportDefaultSpecifier, + ImportNamespaceSpecifier, + TSExportAssignment, + TSImportEqualss, + TSNamespaceExport, + TSNamespaceExportDeclaration, + TSImportEqualsDeclaration, + TSExternalModuleReference, + TSModuleDeclaration, + TSModuleBlock, + + // Decls + ClassDeclaration, + FunctionDeclaration, + TSEnumDeclaration, + TSInterface, + TSInterfaceDeclaration, + TSModule, + TSTypeAliasDeclaration, + Using, + VariableDeclaration, + + // Statements + BlockStatement, + BreakStatement, + ContinueStatement, + DebuggerStatement, + DoWhileStatement, + EmptyStatement, + ExpressionStatement, + ForInStatement, + ForOfStatement, + ForStatement, + IfStatement, + LabeledStatement, + ReturnStatement, + SwitchCase, + SwitchStatement, + ThrowStatement, + TryStatement, + WhileStatement, + WithStatement, + + // Expressions + ArrayExpression, + ArrowFunctionExpression, + AssignmentExpression, + AwaitExpression, + BinaryExpression, + CallExpression, + ChainExpression, + ClassExpression, + ConditionalExpression, + EmptyExpr, + FunctionExpression, + Identifier, + ImportExpression, + LogicalExpression, + MemberExpression, + MetaProperty, + NewExpression, + ObjectExpression, + PrivateIdentifier, + SequenceExpression, + Super, + TaggedTemplateExpression, + TemplateLiteral, + ThisExpression, + TSAsExpression, + TSNonNullExpression, + TSSatisfiesExpression, + TSTypeAssertion, + UnaryExpression, + UpdateExpression, + YieldExpression, + + // Other + Literal, + SpreadElement, + Property, + VariableDeclarator, + CatchClause, + RestElement, + ExportSpecifier, + TemplateElement, + MethodDefinition, + ClassBody, + PropertyDefinition, + Decorator, + StaticBlock, + AccessorProperty, + + // Patterns + ArrayPattern, + AssignmentPattern, + ObjectPattern, + + // JSX + JSXAttribute, + JSXClosingElement, + JSXClosingFragment, + JSXElement, + JSXEmptyExpression, + JSXExpressionContainer, + JSXFragment, + JSXIdentifier, + JSXMemberExpression, + JSXNamespacedName, + JSXOpeningElement, + JSXOpeningFragment, + JSXSpreadAttribute, + JSXSpreadChild, + JSXText, + + TSTypeAnnotation, + TSTypeParameterDeclaration, + TSTypeParameter, + TSTypeParameterInstantiation, + TSEnumMember, + TSInterfaceBody, + TSInterfaceHeritage, + TSTypeReference, + TSThisType, + TSLiteralType, + TSTypeLiteral, + TSInferType, + TSConditionalType, + TSUnionType, + TSIntersectionType, + TSMappedType, + TSTypeQuery, + TSTupleType, + TSNamedTupleMember, + TSFunctionType, + TSCallSignatureDeclaration, + TSPropertySignature, + TSMethodSignature, + TSIndexSignature, + TSIndexedAccessType, + TSTypeOperator, + TSTypePredicate, + TSImportType, + TSRestType, + TSArrayType, + TSClassImplements, + TSAbstractMethodDefinition, + TSEmptyBodyFunctionExpression, + TSParameterProperty, + TSConstructSignatureDeclaration, + TSQualifiedName, + TSOptionalType, + TSTemplateLiteralType, + + TSAnyKeyword, + TSBigIntKeyword, + TSBooleanKeyword, + TSIntrinsicKeyword, + TSNeverKeyword, + TSNullKeyword, + TSNumberKeyword, + TSObjectKeyword, + TSStringKeyword, + TSSymbolKeyword, + TSUndefinedKeyword, + TSUnknownKeyword, + TSVoidKeyword, + TSEnumBody, // Last value is used for max value +} + +impl Display for AstNode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(self, f) + } +} + +impl From for u8 { + fn from(m: AstNode) -> u8 { + m as u8 + } +} + +#[derive(Debug, Clone)] +pub enum AstProp { + // Base, these must be in sync with JS in the same order. + Invalid, + Type, + Parent, + Range, + Length, // Not used in AST, but can be used in attr selectors + + // Starting from here the order doesn't matter. + // Following are all possible AST node properties. + Abstract, + Accessibility, + Alternate, + Argument, + Arguments, + Asserts, + Async, + Attributes, + Await, + BigInt, + Block, + Body, + Callee, + Cases, + Children, + CheckType, + ClosingElement, + ClosingFragment, + Computed, + Consequent, + Const, + Constraint, + Cooked, + Declaration, + Declarations, + Declare, + Decorators, + Default, + Definite, + Delegate, + Discriminant, + Elements, + ElementType, + ElementTypes, + ExprName, + Expression, + Expressions, + Exported, + ExportKind, + Extends, + ExtendsType, + FalseType, + Finalizer, + Flags, + Generator, + Global, + Handler, + Id, + In, + IndexType, + Init, + Initializer, + Implements, + Imported, + ImportKind, + Key, + Kind, + Label, + Left, + Literal, + Local, + Members, + Meta, + Method, + ModuleReference, + Name, + Namespace, + NameType, + Object, + ObjectType, + OpeningElement, + OpeningFragment, + Operator, + Optional, + Options, + Out, + Override, + Param, + Parameter, + Parameters, + ParameterName, + Params, + Pattern, + Prefix, + Properties, + Property, + Qualifier, + Quasi, + Quasis, + Raw, + Readonly, + Regex, + ReturnType, + Right, + SelfClosing, + Shorthand, + Source, + SourceType, + Specifiers, + Static, + SuperClass, + SuperTypeArguments, + Tag, + Tail, + Test, + TrueType, + TypeAnnotation, + TypeArguments, + TypeName, + TypeParameter, + TypeParameters, + Types, + Update, + Value, // Last value is used for max value +} + +impl Display for AstProp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + AstProp::Invalid => "__invalid__", // unused + AstProp::Parent => "parent", + AstProp::Range => "range", + AstProp::Type => "type", + AstProp::Length => "length", + AstProp::Abstract => "abstract", + AstProp::Accessibility => "accessibility", + AstProp::Alternate => "alternate", + AstProp::Argument => "argument", + AstProp::Arguments => "arguments", + AstProp::Asserts => "asserts", + AstProp::Async => "async", + AstProp::Attributes => "attributes", + AstProp::Await => "await", + AstProp::BigInt => "bigint", + AstProp::Block => "block", + AstProp::Body => "body", + AstProp::Callee => "callee", + AstProp::Cases => "cases", + AstProp::Children => "children", + AstProp::CheckType => "checkType", + AstProp::ClosingElement => "closingElement", + AstProp::ClosingFragment => "closingFragment", + AstProp::Computed => "computed", + AstProp::Consequent => "consequent", + AstProp::Const => "const", + AstProp::Constraint => "constraint", + AstProp::Cooked => "cooked", + AstProp::Declaration => "declaration", + AstProp::Declarations => "declarations", + AstProp::Declare => "declare", + AstProp::Decorators => "decorators", + AstProp::Default => "default", + AstProp::Definite => "definite", + AstProp::Delegate => "delegate", + AstProp::Discriminant => "discriminant", + AstProp::Elements => "elements", + AstProp::ElementType => "elementType", + AstProp::ElementTypes => "elementTypes", + AstProp::ExprName => "exprName", + AstProp::Expression => "expression", + AstProp::Expressions => "expressions", + AstProp::ExportKind => "exportKind", + AstProp::Exported => "exported", + AstProp::Extends => "extends", + AstProp::ExtendsType => "extendsType", + AstProp::FalseType => "falseType", + AstProp::Finalizer => "finalizer", + AstProp::Flags => "flags", + AstProp::Generator => "generator", + AstProp::Global => "global", + AstProp::Handler => "handler", + AstProp::Id => "id", + AstProp::In => "in", + AstProp::IndexType => "indexType", + AstProp::Init => "init", + AstProp::Initializer => "initializer", + AstProp::Implements => "implements", + AstProp::Imported => "imported", + AstProp::ImportKind => "importKind", + AstProp::Key => "key", + AstProp::Kind => "kind", + AstProp::Label => "label", + AstProp::Left => "left", + AstProp::Literal => "literal", + AstProp::Local => "local", + AstProp::Members => "members", + AstProp::Meta => "meta", + AstProp::Method => "method", + AstProp::ModuleReference => "moduleReference", + AstProp::Name => "name", + AstProp::Namespace => "namespace", + AstProp::NameType => "nameType", + AstProp::Object => "object", + AstProp::ObjectType => "objectType", + AstProp::OpeningElement => "openingElement", + AstProp::OpeningFragment => "openingFragment", + AstProp::Operator => "operator", + AstProp::Optional => "optional", + AstProp::Options => "options", + AstProp::Out => "out", + AstProp::Override => "override", + AstProp::Param => "param", + AstProp::Parameter => "parameter", + AstProp::Parameters => "parameters", + AstProp::ParameterName => "parameterName", + AstProp::Params => "params", + AstProp::Pattern => "pattern", + AstProp::Prefix => "prefix", + AstProp::Properties => "properties", + AstProp::Property => "property", + AstProp::Qualifier => "qualifier", + AstProp::Quasi => "quasi", + AstProp::Quasis => "quasis", + AstProp::Raw => "raw", + AstProp::Readonly => "readonly", + AstProp::Regex => "regex", + AstProp::ReturnType => "returnType", + AstProp::Right => "right", + AstProp::SelfClosing => "selfClosing", + AstProp::Shorthand => "shorthand", + AstProp::Source => "source", + AstProp::SourceType => "sourceType", + AstProp::Specifiers => "specifiers", + AstProp::Static => "static", + AstProp::SuperClass => "superClass", + AstProp::SuperTypeArguments => "superTypeArguments", + AstProp::Tag => "tag", + AstProp::Tail => "tail", + AstProp::Test => "test", + AstProp::TrueType => "trueType", + AstProp::TypeAnnotation => "typeAnnotation", + AstProp::TypeArguments => "typeArguments", + AstProp::TypeName => "typeName", + AstProp::TypeParameter => "typeParameter", + AstProp::TypeParameters => "typeParameters", + AstProp::Types => "types", + AstProp::Update => "update", + AstProp::Value => "value", + }; + + write!(f, "{}", s) + } +} + +impl From for u8 { + fn from(m: AstProp) -> u8 { + m as u8 + } +} + +pub struct TsEsTreeBuilder { + ctx: SerializeCtx, +} + +impl AstBufSerializer for TsEsTreeBuilder { + fn serialize(&mut self) -> Vec { + self.ctx.serialize() + } +} + +impl TsEsTreeBuilder { + pub fn new() -> Self { + // Max values + let kind_max_count: u8 = u8::from(AstNode::TSEnumBody) + 1; + let prop_max_count: u8 = u8::from(AstProp::Value) + 1; + Self { + ctx: SerializeCtx::new(kind_max_count, prop_max_count), + } + } + + pub fn write_program( + &mut self, + span: &Span, + source_kind: &str, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Program, span); + + self.ctx.write_str(AstProp::SourceType, source_kind); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + + self.ctx.set_root_idx(id.0); + + self.ctx.commit_node(id) + } + + pub fn write_import_decl( + &mut self, + span: &Span, + type_only: bool, + source: NodeRef, + specifiers: Vec, + attributes: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportDeclaration, span); + + let kind = if type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ImportKind, kind); + self.ctx.write_ref(AstProp::Source, &id, source); + self.ctx.write_ref_vec(AstProp::Specifiers, &id, specifiers); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attributes); + + self.ctx.commit_node(id) + } + + pub fn write_import_spec( + &mut self, + span: &Span, + type_only: bool, + local: NodeRef, + imported: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportSpecifier, span); + + let kind = if type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ImportKind, kind); + + self.ctx.write_ref(AstProp::Imported, &id, imported); + self.ctx.write_ref(AstProp::Local, &id, local); + + self.ctx.commit_node(id) + } + + pub fn write_import_attr( + &mut self, + span: &Span, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportAttribute, span); + + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_import_default_spec( + &mut self, + span: &Span, + local: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportDefaultSpecifier, span); + self.ctx.write_ref(AstProp::Local, &id, local); + self.ctx.commit_node(id) + } + + pub fn write_import_ns_spec( + &mut self, + span: &Span, + local: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::ImportNamespaceSpecifier, span); + self.ctx.write_ref(AstProp::Local, &id, local); + self.ctx.commit_node(id) + } + + pub fn write_export_decl(&mut self, span: &Span, decl: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportNamedDeclaration, span); + self.ctx.write_ref(AstProp::Declaration, &id, decl); + self.ctx.commit_node(id) + } + + pub fn write_export_all_decl( + &mut self, + span: &Span, + is_type_only: bool, + source: NodeRef, + exported: Option, + attributes: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportAllDeclaration, span); + + let value = if is_type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ExportKind, value); + + self.ctx.write_maybe_ref(AstProp::Exported, &id, exported); + self.ctx.write_ref(AstProp::Source, &id, source); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attributes); + self.ctx.commit_node(id) + } + + pub fn write_export_default_decl( + &mut self, + span: &Span, + is_type_only: bool, + decl: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::ExportDefaultDeclaration, span); + + let value = if is_type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ExportKind, value); + self.ctx.write_ref(AstProp::Declaration, &id, decl); + self.ctx.commit_node(id) + } + + pub fn write_export_named_decl( + &mut self, + span: &Span, + specifiers: Vec, + source: Option, + attributes: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportNamedDeclaration, span); + + self.ctx.write_ref_vec(AstProp::Specifiers, &id, specifiers); + self.ctx.write_maybe_ref(AstProp::Source, &id, source); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attributes); + + self.ctx.commit_node(id) + } + + pub fn write_export_ts_namespace( + &mut self, + span: &Span, + ident: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSNamespaceExportDeclaration, span); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.commit_node(id) + } + + pub fn write_export_ts_import_equals( + &mut self, + span: &Span, + is_type_only: bool, + ident: NodeRef, + reference: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSImportEqualsDeclaration, span); + + let value = if is_type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ImportKind, value); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_ref(AstProp::ModuleReference, &id, reference); + + self.ctx.commit_node(id) + } + + pub fn write_ts_external_mod_ref( + &mut self, + span: &Span, + expr: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSExternalModuleReference, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_export_spec( + &mut self, + span: &Span, + type_only: bool, + local: NodeRef, + exported: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportSpecifier, span); + + let kind = if type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ExportKind, kind); + + self.ctx.write_ref(AstProp::Exported, &id, exported); + self.ctx.write_ref(AstProp::Local, &id, local); + + self.ctx.commit_node(id) + } + + pub fn write_var_decl( + &mut self, + span: &Span, + declare: bool, + kind: &str, + decls: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::VariableDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_str(AstProp::Kind, kind); + self.ctx.write_ref_vec(AstProp::Declarations, &id, decls); + + self.ctx.commit_node(id) + } + + pub fn write_var_declarator( + &mut self, + span: &Span, + ident: NodeRef, + init: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::VariableDeclarator, span); + + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Init, &id, init); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_fn_decl( + &mut self, + span: &Span, + is_declare: bool, + is_async: bool, + is_generator: bool, + // Ident is required in most cases, but optional as default export + // declaration. TsEstree is weird... + ident: Option, + type_param: Option, + return_type: Option, + body: Option, + params: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::FunctionDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_param); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + + self.ctx.commit_node(id) + } + + pub fn write_decorator(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::Decorator, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_decl( + &mut self, + span: &Span, + is_declare: bool, + is_abstract: bool, + // Ident is required in most cases, but optional as default export + // declaration. TsEstree is weird... + ident: Option, + super_class: Option, + implements: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ClassDeclaration, span); + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Abstract, is_abstract); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::SuperClass, &id, super_class); + self.ctx.write_ref_vec(AstProp::Implements, &id, implements); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_expr( + &mut self, + span: &Span, + is_declare: bool, + is_abstract: bool, + ident: Option, + super_class: Option, + implements: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ClassExpression, span); + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Abstract, is_abstract); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::SuperClass, &id, super_class); + self.ctx.write_ref_vec(AstProp::Implements, &id, implements); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_class_body( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ClassBody, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_static_block(&mut self, span: &Span, body: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::StaticBlock, span); + self.ctx.write_ref(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_accessor_property( + &mut self, + span: &Span, + is_declare: bool, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_readonly: bool, + is_static: bool, + accessibility: Option, + decorators: Vec, + key: NodeRef, + value: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::AccessorProperty, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_bool(AstProp::Static, is_static); + self.write_accessibility(accessibility); + self.ctx.write_ref_vec(AstProp::Decorators, &id, decorators); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_maybe_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_prop( + &mut self, + span: &Span, + is_declare: bool, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_readonly: bool, + is_static: bool, + accessibility: Option, + decorators: Vec, + key: NodeRef, + value: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::PropertyDefinition, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_bool(AstProp::Static, is_static); + + self.write_accessibility(accessibility); + self.ctx.write_ref_vec(AstProp::Decorators, &id, decorators); + + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_maybe_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_method( + &mut self, + span: &Span, + is_declare: bool, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_static: bool, + kind: &str, + accessibility: Option, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::MethodDefinition, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Static, is_static); + self.ctx.write_str(AstProp::Kind, kind); + self.write_accessibility(accessibility); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_block_stmt( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::BlockStatement, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_debugger_stmt(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::DebuggerStatement, span); + self.ctx.commit_node(id) + } + + pub fn write_with_stmt( + &mut self, + span: &Span, + obj: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::WithStatement, span); + + self.ctx.write_ref(AstProp::Object, &id, obj); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_return_stmt( + &mut self, + span: &Span, + arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ReturnStatement, span); + self.ctx.write_maybe_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_labeled_stmt( + &mut self, + span: &Span, + label: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::LabeledStatement, span); + + self.ctx.write_ref(AstProp::Label, &id, label); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_break_stmt( + &mut self, + span: &Span, + label: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::BreakStatement, span); + self.ctx.write_maybe_ref(AstProp::Label, &id, label); + self.ctx.commit_node(id) + } + + pub fn write_continue_stmt( + &mut self, + span: &Span, + label: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ContinueStatement, span); + self.ctx.write_maybe_ref(AstProp::Label, &id, label); + self.ctx.commit_node(id) + } + + pub fn write_if_stmt( + &mut self, + span: &Span, + test: NodeRef, + consequent: NodeRef, + alternate: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::IfStatement, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Consequent, &id, consequent); + self.ctx.write_maybe_ref(AstProp::Alternate, &id, alternate); + + self.ctx.commit_node(id) + } + + pub fn write_switch_stmt( + &mut self, + span: &Span, + discriminant: NodeRef, + cases: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::SwitchStatement, span); + + self.ctx.write_ref(AstProp::Discriminant, &id, discriminant); + self.ctx.write_ref_vec(AstProp::Cases, &id, cases); + + self.ctx.commit_node(id) + } + + pub fn write_switch_case( + &mut self, + span: &Span, + test: Option, + consequent: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::SwitchCase, span); + + self.ctx.write_maybe_ref(AstProp::Test, &id, test); + self.ctx.write_ref_vec(AstProp::Consequent, &id, consequent); + + self.ctx.commit_node(id) + } + + pub fn write_throw_stmt(&mut self, span: &Span, arg: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ThrowStatement, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_while_stmt( + &mut self, + span: &Span, + test: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::WhileStatement, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_do_while_stmt( + &mut self, + span: &Span, + test: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::DoWhileStatement, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_for_stmt( + &mut self, + span: &Span, + init: Option, + test: Option, + update: Option, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ForStatement, span); + + self.ctx.write_maybe_ref(AstProp::Init, &id, init); + self.ctx.write_maybe_ref(AstProp::Test, &id, test); + self.ctx.write_maybe_ref(AstProp::Update, &id, update); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_for_in_stmt( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ForInStatement, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_for_of_stmt( + &mut self, + span: &Span, + is_await: bool, + left: NodeRef, + right: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ForOfStatement, span); + + self.ctx.write_bool(AstProp::Await, is_await); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_expr_stmt(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExpressionStatement, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_try_stmt( + &mut self, + span: &Span, + block: NodeRef, + handler: Option, + finalizer: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TryStatement, span); + + self.ctx.write_ref(AstProp::Block, &id, block); + self.ctx.write_maybe_ref(AstProp::Handler, &id, handler); + self.ctx.write_maybe_ref(AstProp::Finalizer, &id, finalizer); + + self.ctx.commit_node(id) + } + + pub fn write_catch_clause( + &mut self, + span: &Span, + param: Option, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::CatchClause, span); + + self.ctx.write_maybe_ref(AstProp::Param, &id, param); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_arr_expr( + &mut self, + span: &Span, + elems: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ArrayExpression, span); + self.ctx.write_ref_vec(AstProp::Elements, &id, elems); + self.ctx.commit_node(id) + } + + pub fn write_obj_expr( + &mut self, + span: &Span, + props: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ObjectExpression, span); + self.ctx.write_ref_vec(AstProp::Properties, &id, props); + self.ctx.commit_node(id) + } + + pub fn write_bin_expr( + &mut self, + span: &Span, + operator: &str, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::BinaryExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + pub fn write_logical_expr( + &mut self, + span: &Span, + operator: &str, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::LogicalExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_fn_expr( + &mut self, + span: &Span, + is_async: bool, + is_generator: bool, + ident: Option, + type_params: Option, + params: Vec, + return_type: Option, + body: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::FunctionExpression, span); + + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_arrow_fn_expr( + &mut self, + span: &Span, + is_async: bool, + is_generator: bool, + type_params: Option, + params: Vec, + return_type: Option, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ArrowFunctionExpression, span); + + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_this_expr(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::ThisExpression, span); + self.ctx.commit_node(id) + } + + pub fn write_super(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::Super, span); + self.ctx.commit_node(id) + } + + pub fn write_unary_expr( + &mut self, + span: &Span, + operator: &str, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::UnaryExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_new_expr( + &mut self, + span: &Span, + callee: NodeRef, + type_args: Option, + args: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::NewExpression, span); + + self.ctx.write_ref(AstProp::Callee, &id, callee); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + self.ctx.write_ref_vec(AstProp::Arguments, &id, args); + + self.ctx.commit_node(id) + } + + pub fn write_import_expr( + &mut self, + span: &Span, + source: NodeRef, + options: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportExpression, span); + + self.ctx.write_ref(AstProp::Source, &id, source); + self.ctx.write_ref(AstProp::Options, &id, options); + + self.ctx.commit_node(id) + } + + pub fn write_call_expr( + &mut self, + span: &Span, + optional: bool, + callee: NodeRef, + type_args: Option, + args: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::CallExpression, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self.ctx.write_ref(AstProp::Callee, &id, callee); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + self.ctx.write_ref_vec(AstProp::Arguments, &id, args); + + self.ctx.commit_node(id) + } + + pub fn write_update_expr( + &mut self, + span: &Span, + prefix: bool, + operator: &str, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::UpdateExpression, span); + + self.ctx.write_bool(AstProp::Prefix, prefix); + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_assignment_expr( + &mut self, + span: &Span, + operator: &str, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::AssignmentExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + pub fn write_conditional_expr( + &mut self, + span: &Span, + test: NodeRef, + consequent: NodeRef, + alternate: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ConditionalExpression, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Consequent, &id, consequent); + self.ctx.write_ref(AstProp::Alternate, &id, alternate); + + self.ctx.commit_node(id) + } + + pub fn write_member_expr( + &mut self, + span: &Span, + optional: bool, + computed: bool, + obj: NodeRef, + prop: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::MemberExpression, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self.ctx.write_bool(AstProp::Computed, computed); + self.ctx.write_ref(AstProp::Object, &id, obj); + self.ctx.write_ref(AstProp::Property, &id, prop); + + self.ctx.commit_node(id) + } + + pub fn write_chain_expr(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ChainExpression, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_sequence_expr( + &mut self, + span: &Span, + exprs: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::SequenceExpression, span); + self.ctx.write_ref_vec(AstProp::Expressions, &id, exprs); + self.ctx.commit_node(id) + } + + pub fn write_template_lit( + &mut self, + span: &Span, + quasis: Vec, + exprs: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TemplateLiteral, span); + + self.ctx.write_ref_vec(AstProp::Quasis, &id, quasis); + self.ctx.write_ref_vec(AstProp::Expressions, &id, exprs); + + self.ctx.commit_node(id) + } + + pub fn write_template_elem( + &mut self, + span: &Span, + tail: bool, + raw: &str, + cooked: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TemplateElement, span); + + self.ctx.write_bool(AstProp::Tail, tail); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_str(AstProp::Cooked, cooked); + + self.ctx.commit_node(id) + } + + pub fn write_tagged_template_expr( + &mut self, + span: &Span, + tag: NodeRef, + type_args: Option, + quasi: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TaggedTemplateExpression, span); + + self.ctx.write_ref(AstProp::Tag, &id, tag); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + self.ctx.write_ref(AstProp::Quasi, &id, quasi); + + self.ctx.commit_node(id) + } + + pub fn write_yield_expr( + &mut self, + span: &Span, + delegate: bool, + arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::YieldExpression, span); + + self.ctx.write_bool(AstProp::Delegate, delegate); + self.ctx.write_maybe_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_await_expr(&mut self, span: &Span, arg: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::AwaitExpression, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_meta_prop(&mut self, span: &Span, prop: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::MetaProperty, span); + self.ctx.write_ref(AstProp::Property, &id, prop); + self.ctx.commit_node(id) + } + + pub fn write_identifier( + &mut self, + span: &Span, + name: &str, + optional: bool, + type_annotation: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Identifier, span); + + self.ctx.write_str(AstProp::Name, name); + self.ctx.write_bool(AstProp::Optional, optional); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_annotation); + + self.ctx.commit_node(id) + } + + pub fn write_private_identifier( + &mut self, + span: &Span, + name: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::PrivateIdentifier, span); + self.ctx.write_str(AstProp::Name, name); + self.ctx.commit_node(id) + } + + pub fn write_assign_pat( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::AssignmentPattern, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + pub fn write_arr_pat( + &mut self, + span: &Span, + optional: bool, + type_ann: Option, + elems: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ArrayPattern, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref_vec(AstProp::Elements, &id, elems); + + self.ctx.commit_node(id) + } + + pub fn write_obj_pat( + &mut self, + span: &Span, + optional: bool, + type_ann: Option, + props: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ObjectPattern, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref_vec(AstProp::Properties, &id, props); + + self.ctx.commit_node(id) + } + + pub fn write_rest_elem( + &mut self, + span: &Span, + type_ann: Option, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::RestElement, span); + + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_spread(&mut self, span: &Span, arg: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::SpreadElement, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_property( + &mut self, + span: &Span, + shorthand: bool, + computed: bool, + method: bool, + kind: &str, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Property, span); + + self.ctx.write_bool(AstProp::Shorthand, shorthand); + self.ctx.write_bool(AstProp::Computed, computed); + self.ctx.write_bool(AstProp::Method, method); + self.ctx.write_str(AstProp::Kind, kind); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_str_lit( + &mut self, + span: &Span, + value: &str, + raw: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_str(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + + self.ctx.commit_node(id) + } + + pub fn write_bool_lit(&mut self, span: &Span, value: bool) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + let raw = &format!("{}", value); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_bool(AstProp::Value, value); + + self.ctx.commit_node(id) + } + + pub fn write_null_lit(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_null(AstProp::Value); + self.ctx.write_str(AstProp::Raw, "null"); + + self.ctx.commit_node(id) + } + + pub fn write_num_lit( + &mut self, + span: &Span, + value: &str, + raw: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_num(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + + self.ctx.commit_node(id) + } + + pub fn write_bigint_lit( + &mut self, + span: &Span, + value: &str, + raw: &str, + bigint_value: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_bigint(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_str(AstProp::BigInt, bigint_value); + + self.ctx.commit_node(id) + } + + pub fn write_regex_lit( + &mut self, + span: &Span, + pattern: &str, + flags: &str, + value: &str, + raw: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_regex(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.open_obj(); + self.ctx.write_str(AstProp::Flags, flags); + self.ctx.write_str(AstProp::Pattern, pattern); + self.ctx.commit_obj(AstProp::Regex); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_identifier(&mut self, span: &Span, name: &str) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXIdentifier, span); + self.ctx.write_str(AstProp::Name, name); + self.ctx.commit_node(id) + } + + pub fn write_jsx_namespaced_name( + &mut self, + span: &Span, + namespace: NodeRef, + name: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXNamespacedName, span); + + self.ctx.write_ref(AstProp::Namespace, &id, namespace); + self.ctx.write_ref(AstProp::Name, &id, name); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_empty_expr(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXEmptyExpression, span); + self.ctx.commit_node(id) + } + + pub fn write_jsx_elem( + &mut self, + span: &Span, + opening: NodeRef, + closing: Option, + children: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXElement, span); + + self.ctx.write_ref(AstProp::OpeningElement, &id, opening); + self + .ctx + .write_maybe_ref(AstProp::ClosingElement, &id, closing); + self.ctx.write_ref_vec(AstProp::Children, &id, children); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_opening_elem( + &mut self, + span: &Span, + self_closing: bool, + name: NodeRef, + attrs: Vec, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXOpeningElement, span); + + self.ctx.write_bool(AstProp::SelfClosing, self_closing); + self.ctx.write_ref(AstProp::Name, &id, name); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attrs); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_attr( + &mut self, + span: &Span, + name: NodeRef, + value: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXAttribute, span); + + self.ctx.write_ref(AstProp::Name, &id, name); + self.ctx.write_maybe_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_spread_attr( + &mut self, + span: &Span, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXSpreadAttribute, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_jsx_closing_elem( + &mut self, + span: &Span, + name: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXClosingElement, span); + self.ctx.write_ref(AstProp::Name, &id, name); + self.ctx.commit_node(id) + } + + pub fn write_jsx_frag( + &mut self, + span: &Span, + opening: NodeRef, + closing: NodeRef, + children: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXFragment, span); + + self.ctx.write_ref(AstProp::OpeningFragment, &id, opening); + self.ctx.write_ref(AstProp::ClosingFragment, &id, closing); + self.ctx.write_ref_vec(AstProp::Children, &id, children); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_opening_frag(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXOpeningFragment, span); + self.ctx.commit_node(id) + } + + pub fn write_jsx_closing_frag(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXClosingFragment, span); + self.ctx.commit_node(id) + } + + pub fn write_jsx_expr_container( + &mut self, + span: &Span, + expr: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXExpressionContainer, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_jsx_text( + &mut self, + span: &Span, + raw: &str, + value: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXText, span); + + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_str(AstProp::Value, value); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_member_expr( + &mut self, + span: &Span, + obj: NodeRef, + prop: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXMemberExpression, span); + + self.ctx.write_ref(AstProp::Object, &id, obj); + self.ctx.write_ref(AstProp::Property, &id, prop); + + self.ctx.commit_node(id) + } + + pub fn write_ts_module_decl( + &mut self, + span: &Span, + is_declare: bool, + is_global: bool, + ident: NodeRef, + body: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSModuleDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Global, is_global); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_module_block( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSModuleBlock, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_class_implements( + &mut self, + span: &Span, + expr: NodeRef, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSClassImplements, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_abstract_method_def( + &mut self, + span: &Span, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_static: bool, + accessibility: Option, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSAbstractMethodDefinition, span); + + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Static, is_static); + + self.write_accessibility(accessibility); + + self.ctx.write_str(AstProp::Kind, "method"); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Key, &id, value); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_empty_body_fn_expr( + &mut self, + span: &Span, + is_declare: bool, + is_expression: bool, + is_async: bool, + is_generator: bool, + ident: Option, + type_params: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSEmptyBodyFunctionExpression, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Expression, is_expression); + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_param_prop( + &mut self, + span: &Span, + is_override: bool, + is_readonly: bool, + accessibility: Option, + decorators: Vec, + param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSParameterProperty, span); + + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_bool(AstProp::Static, false); + self.write_accessibility(accessibility); + self.ctx.write_ref_vec(AstProp::Decorators, &id, decorators); + self.ctx.write_ref(AstProp::Parameter, &id, param); + + self.ctx.commit_node(id) + } + + pub fn write_ts_call_sig_decl( + &mut self, + span: &Span, + type_ann: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSCallSignatureDeclaration, span); + + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_property_sig( + &mut self, + span: &Span, + computed: bool, + optional: bool, + readonly: bool, + key: NodeRef, + type_ann: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSPropertySignature, span); + + self.ctx.write_bool(AstProp::Computed, computed); + self.ctx.write_bool(AstProp::Optional, optional); + self.ctx.write_bool(AstProp::Readonly, readonly); + // TODO(@marvinhagemeister) not sure where this is coming from + self.ctx.write_bool(AstProp::Static, false); + + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_enum( + &mut self, + span: &Span, + declare: bool, + is_const: bool, + ident: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSEnumDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_bool(AstProp::Const, is_const); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_ts_enum_body( + &mut self, + span: &Span, + members: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSEnumBody, span); + self.ctx.write_ref_vec(AstProp::Members, &id, members); + self.ctx.commit_node(id) + } + + pub fn write_ts_enum_member( + &mut self, + span: &Span, + ident: NodeRef, + init: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSEnumMember, span); + + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Initializer, &id, init); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_assertion( + &mut self, + span: &Span, + expr: NodeRef, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeAssertion, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_param_inst( + &mut self, + span: &Span, + params: Vec, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSTypeParameterInstantiation, span); + + self.ctx.write_ref_vec(AstProp::Params, &id, params); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_alias( + &mut self, + span: &Span, + declare: bool, + ident: NodeRef, + type_param: Option, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeAliasDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_param); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_satisfies_expr( + &mut self, + span: &Span, + expr: NodeRef, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSSatisfiesExpression, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_as_expr( + &mut self, + span: &Span, + expr: NodeRef, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSAsExpression, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_non_null(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSNonNullExpression, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_ts_this_type(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSThisType, span); + self.ctx.commit_node(id) + } + + pub fn write_ts_interface( + &mut self, + span: &Span, + declare: bool, + ident: NodeRef, + type_param: Option, + extends: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterface, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Extends, &id, type_param); + self + .ctx + .write_ref_vec(AstProp::TypeParameters, &id, extends); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_ts_interface_decl( + &mut self, + span: &Span, + declare: bool, + ident: NodeRef, + type_param: Option, + extends: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterfaceDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Extends, &id, type_param); + self + .ctx + .write_ref_vec(AstProp::TypeParameters, &id, extends); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_ts_interface_body( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterfaceBody, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_construct_sig( + &mut self, + span: &Span, + type_params: Option, + params: Vec, + return_type: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSConstructSignatureDeclaration, span); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self.ctx.write_ref(AstProp::ReturnType, &id, return_type); + self.ctx.commit_node(id) + } + + pub fn write_ts_getter_sig( + &mut self, + span: &Span, + key: NodeRef, + return_type: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, false); + self.ctx.write_bool(AstProp::Optional, false); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "getter"); + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_setter_sig( + &mut self, + span: &Span, + key: NodeRef, + param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, false); + self.ctx.write_bool(AstProp::Optional, false); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "setter"); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref_vec(AstProp::Params, &id, vec![param]); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_method_sig( + &mut self, + span: &Span, + is_computed: bool, + is_optional: bool, + key: NodeRef, + type_params: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "method"); + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_interface_heritage( + &mut self, + span: &Span, + expr: NodeRef, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterfaceHeritage, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + pub fn write_ts_index_sig( + &mut self, + span: &Span, + is_readonly: bool, + params: Vec, + type_ann: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSIndexSignature, span); + + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_ref_vec(AstProp::Parameters, &id, params); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_union_type( + &mut self, + span: &Span, + types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSUnionType, span); + self.ctx.write_ref_vec(AstProp::Types, &id, types); + self.ctx.commit_node(id) + } + + pub fn write_ts_intersection_type( + &mut self, + span: &Span, + types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSIntersectionType, span); + self.ctx.write_ref_vec(AstProp::Types, &id, types); + self.ctx.commit_node(id) + } + + pub fn write_ts_infer_type( + &mut self, + span: &Span, + type_param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInferType, span); + self.ctx.write_ref(AstProp::TypeParameter, &id, type_param); + self.ctx.commit_node(id) + } + + pub fn write_ts_type_op( + &mut self, + span: &Span, + op: &str, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeOperator, span); + + self.ctx.write_str(AstProp::Operator, op); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_indexed_access_type( + &mut self, + span: &Span, + index: NodeRef, + obj: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSIndexedAccessType, span); + + self.ctx.write_ref(AstProp::IndexType, &id, index); + self.ctx.write_ref(AstProp::ObjectType, &id, obj); + + self.ctx.commit_node(id) + } + + pub fn write_ts_keyword( + &mut self, + kind: TsKeywordKind, + span: &Span, + ) -> NodeRef { + let kind = match kind { + TsKeywordKind::Any => AstNode::TSAnyKeyword, + TsKeywordKind::Unknown => AstNode::TSUnknownKeyword, + TsKeywordKind::Number => AstNode::TSNumberKeyword, + TsKeywordKind::Object => AstNode::TSObjectKeyword, + TsKeywordKind::Boolean => AstNode::TSBooleanKeyword, + TsKeywordKind::BigInt => AstNode::TSBigIntKeyword, + TsKeywordKind::String => AstNode::TSStringKeyword, + TsKeywordKind::Symbol => AstNode::TSSymbolKeyword, + TsKeywordKind::Void => AstNode::TSVoidKeyword, + TsKeywordKind::Undefined => AstNode::TSUndefinedKeyword, + TsKeywordKind::Null => AstNode::TSNullKeyword, + TsKeywordKind::Never => AstNode::TSNeverKeyword, + TsKeywordKind::Intrinsic => AstNode::TSIntrinsicKeyword, + }; + + let id = self.ctx.append_node(kind, span); + self.ctx.commit_node(id) + } + + pub fn write_ts_rest_type( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSRestType, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + + pub fn write_ts_conditional_type( + &mut self, + span: &Span, + check: NodeRef, + extends: NodeRef, + true_type: NodeRef, + false_type: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSConditionalType, span); + + self.ctx.write_ref(AstProp::CheckType, &id, check); + self.ctx.write_ref(AstProp::ExtendsType, &id, extends); + self.ctx.write_ref(AstProp::TrueType, &id, true_type); + self.ctx.write_ref(AstProp::FalseType, &id, false_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_mapped_type( + &mut self, + span: &Span, + readonly: Option, + optional: Option, + name: Option, + type_ann: Option, + type_param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMappedType, span); + + self.write_plus_minus_true(AstProp::Readonly, readonly); + self.write_plus_minus_true(AstProp::Optional, optional); + self.ctx.write_maybe_ref(AstProp::NameType, &id, name); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref(AstProp::TypeParameter, &id, type_param); + + self.ctx.commit_node(id) + } + + pub fn write_ts_lit_type(&mut self, span: &Span, lit: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSLiteralType, span); + self.ctx.write_ref(AstProp::Literal, &id, lit); + self.ctx.commit_node(id) + } + + pub fn write_ts_tpl_lit( + &mut self, + span: &Span, + quasis: Vec, + types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTemplateLiteralType, span); + + self.ctx.write_ref_vec(AstProp::Quasis, &id, quasis); + self.ctx.write_ref_vec(AstProp::Types, &id, types); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_lit( + &mut self, + span: &Span, + members: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeLiteral, span); + self.ctx.write_ref_vec(AstProp::Members, &id, members); + self.ctx.commit_node(id) + } + + pub fn write_ts_optional_type( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSOptionalType, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + + pub fn write_ts_type_ann( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeAnnotation, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + + pub fn write_ts_array_type( + &mut self, + span: &Span, + elem_type: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSArrayType, span); + self.ctx.write_ref(AstProp::ElementType, &id, elem_type); + self.ctx.commit_node(id) + } + + pub fn write_ts_type_query( + &mut self, + span: &Span, + expr_name: NodeRef, + type_arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeQuery, span); + + self.ctx.write_ref(AstProp::ExprName, &id, expr_name); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_arg); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_ref( + &mut self, + span: &Span, + type_name: NodeRef, + type_arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeReference, span); + + self.ctx.write_ref(AstProp::TypeName, &id, type_name); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_arg); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_predicate( + &mut self, + span: &Span, + asserts: bool, + param_name: NodeRef, + type_ann: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypePredicate, span); + + self.ctx.write_bool(AstProp::Asserts, asserts); + self.ctx.write_ref(AstProp::ParameterName, &id, param_name); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_tuple_type( + &mut self, + span: &Span, + elem_types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTupleType, span); + + self + .ctx + .write_ref_vec(AstProp::ElementTypes, &id, elem_types); + + self.ctx.commit_node(id) + } + + pub fn write_ts_named_tuple_member( + &mut self, + span: &Span, + label: NodeRef, + elem_type: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSNamedTupleMember, span); + + self.ctx.write_ref(AstProp::Label, &id, label); + self.ctx.write_ref(AstProp::ElementType, &id, elem_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_param_decl( + &mut self, + span: &Span, + params: Vec, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSTypeParameterDeclaration, span); + + self.ctx.write_ref_vec(AstProp::Params, &id, params); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_type_param( + &mut self, + span: &Span, + is_in: bool, + is_out: bool, + is_const: bool, + name: NodeRef, + constraint: Option, + default: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeParameter, span); + + self.ctx.write_bool(AstProp::In, is_in); + self.ctx.write_bool(AstProp::Out, is_out); + self.ctx.write_bool(AstProp::Const, is_const); + self.ctx.write_ref(AstProp::Name, &id, name); + self + .ctx + .write_maybe_ref(AstProp::Constraint, &id, constraint); + self.ctx.write_maybe_ref(AstProp::Default, &id, default); + + self.ctx.commit_node(id) + } + + pub fn write_ts_import_type( + &mut self, + span: &Span, + arg: NodeRef, + qualifier: Option, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSImportType, span); + + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.write_maybe_ref(AstProp::Qualifier, &id, qualifier); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + pub fn write_export_assign(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSExportAssignment, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_ts_fn_type( + &mut self, + span: &Span, + params: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSFunctionType, span); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self.ctx.commit_node(id) + } + + pub fn write_ts_qualified_name( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSQualifiedName, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + fn write_accessibility(&mut self, accessibility: Option) { + if let Some(value) = accessibility { + self.ctx.write_str(AstProp::Accessibility, &value); + } else { + self.ctx.write_undefined(AstProp::Accessibility); + } + } + + fn write_plus_minus_true( + &mut self, + prop: AstProp, + value: Option, + ) { + match value { + Some(TruePlusMinus::Plus) => self.ctx.write_str(prop, "+"), + Some(TruePlusMinus::Minus) => self.ctx.write_str(prop, "-"), + Some(TruePlusMinus::True) => self.ctx.write_bool(prop, true), + _ => self.ctx.write_undefined(prop), + } + } +} + +#[derive(Debug)] +pub enum TsKeywordKind { + Any, + Unknown, + Number, + Object, + Boolean, + BigInt, + String, + Symbol, + Void, + Undefined, + Null, + Never, + Intrinsic, +} diff --git a/cli/tools/lint/linter.rs b/cli/tools/lint/linter.rs index 2c2bc43acb..5d6f845274 100644 --- a/cli/tools/lint/linter.rs +++ b/cli/tools/lint/linter.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::path::Path; @@ -15,13 +15,13 @@ use deno_lint::linter::LintConfig as DenoLintConfig; use deno_lint::linter::LintFileOptions; use deno_lint::linter::Linter as DenoLintLinter; use deno_lint::linter::LinterOptions; - -use crate::util::fs::atomic_write_file_with_retries; -use crate::util::fs::specifier_from_file_path; +use deno_path_util::fs::atomic_write_file_with_retries; use super::rules::FileOrPackageLintRule; use super::rules::PackageLintRule; use super::ConfiguredRules; +use crate::sys::CliSys; +use crate::util::fs::specifier_from_file_path; pub struct CliLinterOptions { pub configured_rules: ConfiguredRules, @@ -176,8 +176,9 @@ impl CliLinter { if fix_iterations > 0 { // everything looks good and the file still parses, so write it out atomic_write_file_with_retries( + &CliSys::default(), file_path, - source.text().as_ref(), + source.text().as_bytes(), crate::cache::CACHE_PERM, ) .context("Failed writing fix to file.")?; diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index 596359bdc0..36ba85f613 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -1,8 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This module provides file linting utilities using //! [`deno_lint`](https://github.com/denoland/deno_lint). +use std::collections::HashSet; +use std::fs; +use std::io::stdin; +use std::io::Read; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + use deno_ast::ModuleSpecifier; use deno_ast::ParsedSource; use deno_config::deno_json::LintRulesConfig; @@ -10,7 +18,6 @@ use deno_config::glob::FileCollector; use deno_config::glob::FilePatterns; use deno_config::workspace::WorkspaceDirectory; use deno_core::anyhow::anyhow; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::futures::future::LocalBoxFuture; use deno_core::futures::FutureExt; @@ -20,41 +27,39 @@ use deno_core::unsync::future::LocalFutureExt; use deno_core::unsync::future::SharedLocal; use deno_graph::ModuleGraph; use deno_lint::diagnostic::LintDiagnostic; -use deno_lint::linter::LintConfig; +use deno_lint::linter::LintConfig as DenoLintConfig; use log::debug; use reporters::create_reporter; use reporters::LintReporter; use serde::Serialize; -use std::collections::HashSet; -use std::fs; -use std::io::stdin; -use std::io::Read; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; use crate::args::CliOptions; use crate::args::Flags; use crate::args::LintFlags; use crate::args::LintOptions; use crate::args::WorkspaceLintOptions; +use crate::cache::CacheDBHash; use crate::cache::Caches; use crate::cache::IncrementalCache; use crate::colors; use crate::factory::CliFactory; use crate::graph_util::ModuleGraphCreator; +use crate::sys::CliSys; use crate::tools::fmt::run_parallelized; use crate::util::display; use crate::util::file_watcher; +use crate::util::file_watcher::WatcherCommunicator; use crate::util::fs::canonicalize_path; use crate::util::path::is_script_ext; use crate::util::sync::AtomicFlag; +mod ast_buffer; mod linter; mod reporters; mod rules; +// TODO(bartlomieju): remove once we wire plugins through the CLI linter +pub use ast_buffer::serialize_ast_to_buffer; pub use linter::CliLinter; pub use linter::CliLinterOptions; pub use rules::collect_no_slow_type_diagnostics; @@ -69,136 +74,137 @@ pub async fn lint( flags: Arc, lint_flags: LintFlags, ) -> Result<(), AnyError> { - if let Some(watch_flags) = &lint_flags.watch { + if lint_flags.watch.is_some() { if lint_flags.is_stdin() { - return Err(generic_error( - "Lint watch on standard input is not supported.", - )); + return Err(anyhow!("Lint watch on standard input is not supported.",)); } - file_watcher::watch_func( - flags, - file_watcher::PrintConfig::new("Lint", !watch_flags.no_clear_screen), - move |flags, watcher_communicator, changed_paths| { - let lint_flags = lint_flags.clone(); - watcher_communicator.show_path_changed(changed_paths.clone()); - Ok(async move { - let factory = CliFactory::from_flags(flags); - let cli_options = factory.cli_options()?; - let lint_config = cli_options.resolve_deno_lint_config()?; - let mut paths_with_options_batches = - resolve_paths_with_options_batches(cli_options, &lint_flags)?; - for paths_with_options in &mut paths_with_options_batches { - _ = watcher_communicator - .watch_paths(paths_with_options.paths.clone()); - let files = std::mem::take(&mut paths_with_options.paths); - paths_with_options.paths = if let Some(paths) = &changed_paths { - // lint all files on any changed (https://github.com/denoland/deno/issues/12446) - files - .iter() - .any(|path| { - canonicalize_path(path) - .map(|p| paths.contains(&p)) - .unwrap_or(false) - }) - .then_some(files) - .unwrap_or_else(|| [].to_vec()) - } else { - files - }; - } + return lint_with_watch(flags, lint_flags).await; + } - let mut linter = WorkspaceLinter::new( - factory.caches()?.clone(), - factory.lint_rule_provider().await?, - factory.module_graph_creator().await?.clone(), - cli_options.start_dir.clone(), - &cli_options.resolve_workspace_lint_options(&lint_flags)?, - ); - for paths_with_options in paths_with_options_batches { - linter - .lint_files( - cli_options, - paths_with_options.options, - lint_config.clone(), - paths_with_options.dir, - paths_with_options.paths, - ) - .await?; - } - - linter.finish(); - - Ok(()) - }) - }, - ) - .await?; + let factory = CliFactory::from_flags(flags); + let cli_options = factory.cli_options()?; + let lint_rule_provider = factory.lint_rule_provider().await?; + let is_stdin = lint_flags.is_stdin(); + let deno_lint_config = cli_options.resolve_deno_lint_config()?; + let workspace_lint_options = + cli_options.resolve_workspace_lint_options(&lint_flags)?; + let success = if is_stdin { + lint_stdin( + cli_options, + lint_rule_provider, + workspace_lint_options, + lint_flags, + deno_lint_config, + )? } else { - let factory = CliFactory::from_flags(flags); - let cli_options = factory.cli_options()?; - let is_stdin = lint_flags.is_stdin(); - let deno_lint_config = cli_options.resolve_deno_lint_config()?; - let workspace_lint_options = - cli_options.resolve_workspace_lint_options(&lint_flags)?; - let success = if is_stdin { - let start_dir = &cli_options.start_dir; - let reporter_lock = Arc::new(Mutex::new(create_reporter( - workspace_lint_options.reporter_kind, - ))); - let lint_config = start_dir - .to_lint_config(FilePatterns::new_with_base(start_dir.dir_path()))?; - let lint_options = LintOptions::resolve(lint_config, &lint_flags); - let lint_rules = factory - .lint_rule_provider() - .await? - .resolve_lint_rules_err_empty( - lint_options.rules, - start_dir.maybe_deno_json().map(|c| c.as_ref()), - )?; - let mut file_path = cli_options.initial_cwd().join(STDIN_FILE_NAME); - if let Some(ext) = cli_options.ext_flag() { - file_path.set_extension(ext); - } - let r = lint_stdin(&file_path, lint_rules, deno_lint_config); - let success = handle_lint_result( - &file_path.to_string_lossy(), - r, - reporter_lock.clone(), - ); - reporter_lock.lock().close(1); - success - } else { - let mut linter = WorkspaceLinter::new( - factory.caches()?.clone(), - factory.lint_rule_provider().await?, - factory.module_graph_creator().await?.clone(), - cli_options.start_dir.clone(), - &workspace_lint_options, - ); - let paths_with_options_batches = - resolve_paths_with_options_batches(cli_options, &lint_flags)?; - for paths_with_options in paths_with_options_batches { - linter - .lint_files( - cli_options, - paths_with_options.options, - deno_lint_config.clone(), - paths_with_options.dir, - paths_with_options.paths, - ) - .await?; - } - linter.finish() - }; - if !success { - deno_runtime::exit(1); + let mut linter = WorkspaceLinter::new( + factory.caches()?.clone(), + lint_rule_provider, + factory.module_graph_creator().await?.clone(), + cli_options.start_dir.clone(), + &workspace_lint_options, + ); + let paths_with_options_batches = + resolve_paths_with_options_batches(cli_options, &lint_flags)?; + for paths_with_options in paths_with_options_batches { + linter + .lint_files( + cli_options, + paths_with_options.options, + deno_lint_config.clone(), + paths_with_options.dir, + paths_with_options.paths, + ) + .await?; } + linter.finish() + }; + if !success { + deno_runtime::exit(1); } Ok(()) } +async fn lint_with_watch_inner( + flags: Arc, + lint_flags: LintFlags, + watcher_communicator: Arc, + changed_paths: Option>, +) -> Result<(), AnyError> { + let factory = CliFactory::from_flags(flags); + let cli_options = factory.cli_options()?; + let lint_config = cli_options.resolve_deno_lint_config()?; + let mut paths_with_options_batches = + resolve_paths_with_options_batches(cli_options, &lint_flags)?; + for paths_with_options in &mut paths_with_options_batches { + _ = watcher_communicator.watch_paths(paths_with_options.paths.clone()); + + let files = std::mem::take(&mut paths_with_options.paths); + paths_with_options.paths = if let Some(paths) = &changed_paths { + // lint all files on any changed (https://github.com/denoland/deno/issues/12446) + files + .iter() + .any(|path| { + canonicalize_path(path) + .map(|p| paths.contains(&p)) + .unwrap_or(false) + }) + .then_some(files) + .unwrap_or_else(|| [].to_vec()) + } else { + files + }; + } + + let mut linter = WorkspaceLinter::new( + factory.caches()?.clone(), + factory.lint_rule_provider().await?, + factory.module_graph_creator().await?.clone(), + cli_options.start_dir.clone(), + &cli_options.resolve_workspace_lint_options(&lint_flags)?, + ); + for paths_with_options in paths_with_options_batches { + linter + .lint_files( + cli_options, + paths_with_options.options, + lint_config.clone(), + paths_with_options.dir, + paths_with_options.paths, + ) + .await?; + } + + linter.finish(); + + Ok(()) +} + +async fn lint_with_watch( + flags: Arc, + lint_flags: LintFlags, +) -> Result<(), AnyError> { + let watch_flags = lint_flags.watch.as_ref().unwrap(); + + file_watcher::watch_func( + flags, + file_watcher::PrintConfig::new("Lint", !watch_flags.no_clear_screen), + move |flags, watcher_communicator, changed_paths| { + let lint_flags = lint_flags.clone(); + watcher_communicator.show_path_changed(changed_paths.clone()); + Ok(lint_with_watch_inner( + flags, + lint_flags, + watcher_communicator, + changed_paths, + )) + }, + ) + .await +} + struct PathsWithOptions { dir: WorkspaceDirectory, paths: Vec, @@ -214,7 +220,7 @@ fn resolve_paths_with_options_batches( let mut paths_with_options_batches = Vec::with_capacity(members_lint_options.len()); for (dir, lint_options) in members_lint_options { - let files = collect_lint_files(cli_options, lint_options.files.clone())?; + let files = collect_lint_files(cli_options, lint_options.files.clone()); if !files.is_empty() { paths_with_options_batches.push(PathsWithOptions { dir, @@ -224,7 +230,7 @@ fn resolve_paths_with_options_batches( } } if paths_with_options_batches.is_empty() { - return Err(generic_error("No target files found.")); + return Err(anyhow!("No target files found.")); } Ok(paths_with_options_batches) } @@ -269,7 +275,7 @@ impl WorkspaceLinter { &mut self, cli_options: &Arc, lint_options: LintOptions, - lint_config: LintConfig, + lint_config: DenoLintConfig, member_dir: WorkspaceDirectory, paths: Vec, ) -> Result<(), AnyError> { @@ -283,7 +289,7 @@ impl WorkspaceLinter { lint_rules.incremental_cache_state().map(|state| { Arc::new(IncrementalCache::new( self.caches.lint_incremental_cache_db(), - &state, + CacheDBHash::from_hashable(&state), &paths, )) }); @@ -294,112 +300,63 @@ impl WorkspaceLinter { deno_lint_config: lint_config, })); + let has_error = self.has_error.clone(); + let reporter_lock = self.reporter_lock.clone(); + let mut futures = Vec::with_capacity(2); if linter.has_package_rules() { - if self.workspace_module_graph.is_none() { - let module_graph_creator = self.module_graph_creator.clone(); - let packages = self.workspace_dir.jsr_packages_for_publish(); - self.workspace_module_graph = Some( - async move { - module_graph_creator - .create_and_validate_publish_graph(&packages, true) - .await - .map(Rc::new) - .map_err(Rc::new) - } - .boxed_local() - .shared_local(), - ); - } - let workspace_module_graph_future = - self.workspace_module_graph.as_ref().unwrap().clone(); - let publish_config = member_dir.maybe_package_config(); - if let Some(publish_config) = publish_config { - let has_error = self.has_error.clone(); - let reporter_lock = self.reporter_lock.clone(); - let linter = linter.clone(); - let path_urls = paths - .iter() - .filter_map(|p| ModuleSpecifier::from_file_path(p).ok()) - .collect::>(); - futures.push( - async move { - let graph = workspace_module_graph_future - .await - .map_err(|err| anyhow!("{:#}", err))?; - let export_urls = - publish_config.config_file.resolve_export_value_urls()?; - if !export_urls.iter().any(|url| path_urls.contains(url)) { - return Ok(()); // entrypoint is not specified, so skip - } - let diagnostics = linter.lint_package(&graph, &export_urls); - if !diagnostics.is_empty() { - has_error.raise(); - let mut reporter = reporter_lock.lock(); - for diagnostic in &diagnostics { - reporter.visit_diagnostic(diagnostic); - } - } - Ok(()) - } - .boxed_local(), - ); + if let Some(fut) = self.run_package_rules(&linter, &member_dir, &paths) { + futures.push(fut); } } - futures.push({ - let has_error = self.has_error.clone(); - let reporter_lock = self.reporter_lock.clone(); - let maybe_incremental_cache = maybe_incremental_cache.clone(); - let linter = linter.clone(); - let cli_options = cli_options.clone(); - async move { - run_parallelized(paths, { - move |file_path| { - let file_text = - deno_ast::strip_bom(fs::read_to_string(&file_path)?); + let maybe_incremental_cache_ = maybe_incremental_cache.clone(); + let linter = linter.clone(); + let cli_options = cli_options.clone(); + let fut = async move { + let operation = move |file_path: PathBuf| { + let file_text = deno_ast::strip_bom(fs::read_to_string(&file_path)?); - // don't bother rechecking this file if it didn't have any diagnostics before - if let Some(incremental_cache) = &maybe_incremental_cache { - if incremental_cache.is_file_same(&file_path, &file_text) { - return Ok(()); - } - } - - let r = linter.lint_file( - &file_path, - file_text, - cli_options.ext_flag().as_deref(), - ); - if let Ok((file_source, file_diagnostics)) = &r { - if let Some(incremental_cache) = &maybe_incremental_cache { - if file_diagnostics.is_empty() { - // update the incremental cache if there were no diagnostics - incremental_cache.update_file( - &file_path, - // ensure the returned text is used here as it may have been modified via --fix - file_source.text(), - ) - } - } - } - - let success = handle_lint_result( - &file_path.to_string_lossy(), - r, - reporter_lock.clone(), - ); - if !success { - has_error.raise(); - } - - Ok(()) + // don't bother rechecking this file if it didn't have any diagnostics before + if let Some(incremental_cache) = &maybe_incremental_cache_ { + if incremental_cache.is_file_same(&file_path, &file_text) { + return Ok(()); } - }) - .await - } - .boxed_local() - }); + } + + let r = linter.lint_file( + &file_path, + file_text, + cli_options.ext_flag().as_deref(), + ); + if let Ok((file_source, file_diagnostics)) = &r { + if let Some(incremental_cache) = &maybe_incremental_cache_ { + if file_diagnostics.is_empty() { + // update the incremental cache if there were no diagnostics + incremental_cache.update_file( + &file_path, + // ensure the returned text is used here as it may have been modified via --fix + file_source.text(), + ) + } + } + } + + let success = handle_lint_result( + &file_path.to_string_lossy(), + r, + reporter_lock.clone(), + ); + if !success { + has_error.raise(); + } + + Ok(()) + }; + run_parallelized(paths, operation).await + } + .boxed_local(); + futures.push(fut); if lint_options.fix { // run sequentially when using `--fix` to lower the chances of weird @@ -419,6 +376,63 @@ impl WorkspaceLinter { Ok(()) } + fn run_package_rules( + &mut self, + linter: &Arc, + member_dir: &WorkspaceDirectory, + paths: &[PathBuf], + ) -> Option>> { + if self.workspace_module_graph.is_none() { + let module_graph_creator = self.module_graph_creator.clone(); + let packages = self.workspace_dir.jsr_packages_for_publish(); + self.workspace_module_graph = Some( + async move { + module_graph_creator + .create_and_validate_publish_graph(&packages, true) + .await + .map(Rc::new) + .map_err(Rc::new) + } + .boxed_local() + .shared_local(), + ); + } + + let workspace_module_graph_future = + self.workspace_module_graph.as_ref().unwrap().clone(); + let maybe_publish_config = member_dir.maybe_package_config(); + let publish_config = maybe_publish_config?; + + let has_error = self.has_error.clone(); + let reporter_lock = self.reporter_lock.clone(); + let linter = linter.clone(); + let path_urls = paths + .iter() + .filter_map(|p| ModuleSpecifier::from_file_path(p).ok()) + .collect::>(); + let fut = async move { + let graph = workspace_module_graph_future + .await + .map_err(|err| anyhow!("{:#}", err))?; + let export_urls = + publish_config.config_file.resolve_export_value_urls()?; + if !export_urls.iter().any(|url| path_urls.contains(url)) { + return Ok(()); // entrypoint is not specified, so skip + } + let diagnostics = linter.lint_package(&graph, &export_urls); + if !diagnostics.is_empty() { + has_error.raise(); + let mut reporter = reporter_lock.lock(); + for diagnostic in &diagnostics { + reporter.visit_diagnostic(diagnostic); + } + } + Ok(()) + } + .boxed_local(); + Some(fut) + } + pub fn finish(self) -> bool { debug!("Found {} files", self.file_count); self.reporter_lock.lock().close(self.file_count); @@ -429,7 +443,7 @@ impl WorkspaceLinter { fn collect_lint_files( cli_options: &CliOptions, files: FilePatterns, -) -> Result, AnyError> { +) -> Vec { FileCollector::new(|e| { is_script_ext(e.path) || (e.path.extension().is_none() && cli_options.ext_flag().is_some()) @@ -438,7 +452,7 @@ fn collect_lint_files( .ignore_node_modules() .use_gitignore() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files) + .collect_file_patterns(&CliSys::default(), files) } #[allow(clippy::print_stdout)] @@ -494,13 +508,30 @@ pub fn print_rules_list(json: bool, maybe_rules_tags: Option>) { /// Treats input as TypeScript. /// Compatible with `--json` flag. fn lint_stdin( - file_path: &Path, - configured_rules: ConfiguredRules, - deno_lint_config: LintConfig, -) -> Result<(ParsedSource, Vec), AnyError> { + cli_options: &Arc, + lint_rule_provider: LintRuleProvider, + workspace_lint_options: WorkspaceLintOptions, + lint_flags: LintFlags, + deno_lint_config: DenoLintConfig, +) -> Result { + let start_dir = &cli_options.start_dir; + let reporter_lock = Arc::new(Mutex::new(create_reporter( + workspace_lint_options.reporter_kind, + ))); + let lint_config = start_dir + .to_lint_config(FilePatterns::new_with_base(start_dir.dir_path()))?; + let lint_options = LintOptions::resolve(lint_config, &lint_flags); + let configured_rules = lint_rule_provider.resolve_lint_rules_err_empty( + lint_options.rules, + start_dir.maybe_deno_json().map(|c| c.as_ref()), + )?; + let mut file_path = cli_options.initial_cwd().join(STDIN_FILE_NAME); + if let Some(ext) = cli_options.ext_flag() { + file_path.set_extension(ext); + } let mut source_code = String::new(); if stdin().read_to_string(&mut source_code).is_err() { - return Err(generic_error("Failed to read from stdin")); + return Err(anyhow!("Failed to read from stdin")); } let linter = CliLinter::new(CliLinterOptions { @@ -509,9 +540,14 @@ fn lint_stdin( deno_lint_config, }); - linter - .lint_file(file_path, deno_ast::strip_bom(source_code), None) - .map_err(AnyError::from) + let r = linter + .lint_file(&file_path, deno_ast::strip_bom(source_code), None) + .map_err(AnyError::from); + + let success = + handle_lint_result(&file_path.to_string_lossy(), r, reporter_lock.clone()); + reporter_lock.lock().close(1); + Ok(success) } fn handle_lint_result( @@ -559,11 +595,12 @@ struct LintError { #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; use serde::Deserialize; use test_util as util; + use super::*; + #[derive(Serialize, Deserialize)] struct RulesSchema { #[serde(rename = "$schema")] diff --git a/cli/tools/lint/reporters.rs b/cli/tools/lint/reporters.rs index 18bc1216a6..24e04e840f 100644 --- a/cli/tools/lint/reporters.rs +++ b/cli/tools/lint/reporters.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_ast::diagnostics::Diagnostic; use deno_core::error::AnyError; @@ -8,9 +8,8 @@ use deno_runtime::colors; use log::info; use serde::Serialize; -use crate::args::LintReporterKind; - use super::LintError; +use crate::args::LintReporterKind; const JSON_SCHEMA_VERSION: u8 = 1; diff --git a/cli/tools/lint/rules/mod.rs b/cli/tools/lint/rules/mod.rs index dd723ad159..e7be12d56d 100644 --- a/cli/tools/lint/rules/mod.rs +++ b/cli/tools/lint/rules/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashSet; diff --git a/cli/tools/lint/rules/no_sloppy_imports.rs b/cli/tools/lint/rules/no_sloppy_imports.rs index 1bf7eddf6e..825835f3b5 100644 --- a/cli/tools/lint/rules/no_sloppy_imports.rs +++ b/cli/tools/lint/rules/no_sloppy_imports.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; @@ -7,7 +7,7 @@ use std::sync::Arc; use deno_ast::SourceRange; use deno_config::workspace::WorkspaceResolver; -use deno_core::anyhow::anyhow; +use deno_error::JsErrorBox; use deno_graph::source::ResolutionKind; use deno_graph::source::ResolveError; use deno_graph::Range; @@ -20,11 +20,10 @@ use deno_resolver::sloppy_imports::SloppyImportsResolution; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; use text_lines::LineAndColumnIndex; +use super::ExtendedLintRule; use crate::graph_util::CliJsrUrlProvider; use crate::resolver::CliSloppyImportsResolver; -use super::ExtendedLintRule; - #[derive(Debug)] pub struct NoSloppyImportsRule { sloppy_imports_resolver: Option>, @@ -188,7 +187,7 @@ impl<'a> deno_graph::source::Resolver for SloppyImportCaptureResolver<'a> { let resolution = self .workspace_resolver .resolve(specifier_text, &referrer_range.specifier) - .map_err(|err| ResolveError::Other(err.into()))?; + .map_err(|err| ResolveError::Other(JsErrorBox::from_err(err)))?; match resolution { deno_config::workspace::MappedResolution::Normal { @@ -221,7 +220,7 @@ impl<'a> deno_graph::source::Resolver for SloppyImportCaptureResolver<'a> { } | deno_config::workspace::MappedResolution::PackageJson { .. } => { // this error is ignored - Err(ResolveError::Other(anyhow!(""))) + Err(ResolveError::Other(JsErrorBox::generic(""))) } } } diff --git a/cli/tools/lint/rules/no_slow_types.rs b/cli/tools/lint/rules/no_slow_types.rs index bc3f835b17..a792c38612 100644 --- a/cli/tools/lint/rules/no_slow_types.rs +++ b/cli/tools/lint/rules/no_slow_types.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; diff --git a/cli/tools/mod.rs b/cli/tools/mod.rs index a458da9f1b..35de8ab9fa 100644 --- a/cli/tools/mod.rs +++ b/cli/tools/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod bench; pub mod check; diff --git a/cli/tools/registry/api.rs b/cli/tools/registry/api.rs index 2f27cb2fea..c2d34442a1 100644 --- a/cli/tools/registry/api.rs +++ b/cli/tools/registry/api.rs @@ -1,12 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::http_util; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; use deno_runtime::deno_fetch; use serde::de::DeserializeOwned; +use crate::http_util; use crate::http_util::HttpClient; #[derive(serde::Deserialize)] diff --git a/cli/tools/registry/auth.rs b/cli/tools/registry/auth.rs index 820d3b6b60..3665990905 100644 --- a/cli/tools/registry/auth.rs +++ b/cli/tools/registry/auth.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::IsTerminal; diff --git a/cli/tools/registry/diagnostics.rs b/cli/tools/registry/diagnostics.rs index 9c32b8e36d..27753167f3 100644 --- a/cli/tools/registry/diagnostics.rs +++ b/cli/tools/registry/diagnostics.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::PathBuf; diff --git a/cli/tools/registry/graph.rs b/cli/tools/registry/graph.rs index 21962d009e..7152675ff8 100644 --- a/cli/tools/registry/graph.rs +++ b/cli/tools/registry/graph.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::sync::Arc; @@ -16,10 +16,9 @@ use deno_graph::WalkOptions; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; -use crate::cache::ParsedSourceCache; - use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; +use crate::cache::ParsedSourceCache; pub struct GraphDiagnosticsCollector { parsed_source_cache: Arc, diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index 001e401459..ea457f8a71 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; @@ -26,6 +26,7 @@ use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::url::Url; +use deno_runtime::deno_fetch; use deno_terminal::colors; use http_body_util::BodyExt; use serde::Deserialize; @@ -72,11 +73,10 @@ pub use pm::AddRmPackageReq; use publish_order::PublishOrderGraph; use unfurl::SpecifierUnfurler; -use super::check::TypeChecker; - use self::graph::GraphDiagnosticsCollector; use self::paths::CollectedPublishPath; use self::tar::PublishableTarball; +use super::check::TypeChecker; pub async fn publish( flags: Arc, @@ -911,9 +911,7 @@ async fn publish_package( package.config ); - let body = http_body_util::Full::new(package.tarball.bytes.clone()) - .map_err(|never| match never {}) - .boxed(); + let body = deno_fetch::ReqBody::full(package.tarball.bytes.clone()); let response = http_client .post(url.parse()?, body)? .header( @@ -1282,14 +1280,14 @@ fn ring_bell() { #[cfg(test)] mod tests { - use deno_ast::ModuleSpecifier; + use std::collections::HashMap; - use crate::tools::registry::has_license_file; + use deno_ast::ModuleSpecifier; use super::tar::PublishableTarball; use super::tar::PublishableTarballFile; use super::verify_version_manifest; - use std::collections::HashMap; + use crate::tools::registry::has_license_file; #[test] fn test_verify_version_manifest() { diff --git a/cli/tools/registry/paths.rs b/cli/tools/registry/paths.rs index 8b6c05fc01..563b841280 100644 --- a/cli/tools/registry/paths.rs +++ b/cli/tools/registry/paths.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Validation logic in this file is shared with registry/api/src/ids.rs @@ -13,10 +13,10 @@ use deno_config::glob::FilePatterns; use deno_core::error::AnyError; use thiserror::Error; -use crate::args::CliOptions; - use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; +use crate::args::CliOptions; +use crate::sys::CliSys; /// A package path, like '/foo' or '/foo/bar'. The path is prefixed with a slash /// and does not end with a slash. @@ -233,7 +233,7 @@ pub fn collect_publish_paths( ) -> Result, AnyError> { let diagnostics_collector = opts.diagnostics_collector; let publish_paths = - collect_paths(opts.cli_options, diagnostics_collector, opts.file_patterns)?; + collect_paths(opts.cli_options, diagnostics_collector, opts.file_patterns); let publish_paths_set = publish_paths.iter().cloned().collect::>(); let capacity = publish_paths.len() + opts.force_include_paths.len(); let mut paths = HashSet::with_capacity(capacity); @@ -321,13 +321,13 @@ fn collect_paths( cli_options: &CliOptions, diagnostics_collector: &PublishDiagnosticsCollector, file_patterns: FilePatterns, -) -> Result, AnyError> { +) -> Vec { FileCollector::new(|e| { - if !e.metadata.is_file { + if !e.metadata.file_type().is_file() { if let Ok(specifier) = ModuleSpecifier::from_file_path(e.path) { diagnostics_collector.push(PublishDiagnostic::UnsupportedFileType { specifier, - kind: if e.metadata.is_symlink { + kind: if e.metadata.file_type().is_symlink() { "symlink".to_string() } else { "Unknown".to_string() @@ -345,5 +345,5 @@ fn collect_paths( .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) .use_gitignore() - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, file_patterns) + .collect_file_patterns(&CliSys::default(), file_patterns) } diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 6f89ec7aae..0b27b1a3e3 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::AnyError; @@ -14,6 +15,7 @@ use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::Version; use deno_semver::VersionReq; use deps::KeyPath; @@ -23,12 +25,11 @@ use jsonc_parser::cst::CstRootNode; use jsonc_parser::json; use crate::args::AddFlags; -use crate::args::CacheSetting; use crate::args::CliOptions; use crate::args::Flags; use crate::args::RemoveFlags; use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; @@ -283,7 +284,7 @@ fn package_json_dependency_entry( (npm_package.into(), selected.version_req) } else { ( - selected.import_name, + selected.import_name.into_string(), format!("npm:{}@{}", npm_package, selected.version_req), ) } @@ -292,7 +293,7 @@ fn package_json_dependency_entry( let scope_replaced = jsr_package.replace('/', "__"); let version_req = format!("npm:@jsr/{scope_replaced}@{}", selected.version_req); - (selected.import_name, version_req) + (selected.import_name.into_string(), version_req) } else { (selected.package_name, selected.version_req) } @@ -411,18 +412,19 @@ pub async fn add( let http_client = cli_factory.http_client_provider(); let deps_http_cache = cli_factory.global_http_cache()?; - let mut deps_file_fetcher = FileFetcher::new( + let deps_file_fetcher = CliFileFetcher::new( deps_http_cache.clone(), - CacheSetting::ReloadAll, - true, http_client.clone(), + cli_factory.sys(), Default::default(), None, + true, + CacheSetting::ReloadAll, + log::Level::Trace, ); let npmrc = cli_factory.cli_options().unwrap().npmrc(); - deps_file_fetcher.set_download_log_level(log::Level::Trace); let deps_file_fetcher = Arc::new(deps_file_fetcher); let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone())); let npm_resolver = @@ -549,10 +551,10 @@ pub async fn add( } struct SelectedPackage { - import_name: String, + import_name: StackString, package_name: String, version_req: String, - selected_version: String, + selected_version: StackString, } enum NotFoundHelp { @@ -683,7 +685,7 @@ async fn find_package_and_select_version_for_req( import_name: add_package_req.alias, package_name: prefixed_name, version_req: format!("{}{}", range_symbol, &nv.version), - selected_version: nv.version.to_string(), + selected_version: nv.version.to_custom_string::(), })) } @@ -705,7 +707,7 @@ enum AddRmPackageReqValue { #[derive(Debug, PartialEq, Eq)] pub struct AddRmPackageReq { - alias: String, + alias: StackString, value: AddRmPackageReqValue, } @@ -753,7 +755,11 @@ impl AddRmPackageReq { return Ok(Err(PackageReq::from_str(entry_text)?)); } - (maybe_prefix.unwrap(), Some(alias.to_string()), entry_text) + ( + maybe_prefix.unwrap(), + Some(StackString::from(alias)), + entry_text, + ) } None => return Ok(Err(PackageReq::from_str(entry_text)?)), }, @@ -765,7 +771,7 @@ impl AddRmPackageReq { JsrPackageReqReference::from_str(&format!("jsr:{}", entry_text))?; let package_req = req_ref.into_inner().req; Ok(Ok(AddRmPackageReq { - alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()), + alias: maybe_alias.unwrap_or_else(|| package_req.name.clone()), value: AddRmPackageReqValue::Jsr(package_req), })) } @@ -785,7 +791,7 @@ impl AddRmPackageReq { ); } Ok(Ok(AddRmPackageReq { - alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()), + alias: maybe_alias.unwrap_or_else(|| package_req.name.clone()), value: AddRmPackageReqValue::Npm(package_req), })) } @@ -855,10 +861,8 @@ async fn npm_install_after_modification( // 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()?; - } + let npm_installer = cli_factory.npm_installer()?; + npm_installer.ensure_no_pkg_json_dep_errors()?; // npm install cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).await?; @@ -878,14 +882,14 @@ mod test { assert_eq!( AddRmPackageReq::parse("jsr:foo").unwrap().unwrap(), AddRmPackageReq { - alias: "foo".to_string(), + alias: "foo".into(), value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap()) } ); assert_eq!( AddRmPackageReq::parse("alias@jsr:foo").unwrap().unwrap(), AddRmPackageReq { - alias: "alias".to_string(), + alias: "alias".into(), value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap()) } ); @@ -894,7 +898,7 @@ mod test { .unwrap() .unwrap(), AddRmPackageReq { - alias: "@alias/pkg".to_string(), + alias: "@alias/pkg".into(), value: AddRmPackageReqValue::Npm( PackageReq::from_str("foo@latest").unwrap() ) @@ -905,7 +909,7 @@ mod test { .unwrap() .unwrap(), AddRmPackageReq { - alias: "@alias/pkg".to_string(), + alias: "@alias/pkg".into(), value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap()) } ); @@ -914,7 +918,7 @@ mod test { .unwrap() .unwrap(), AddRmPackageReq { - alias: "alias".to_string(), + alias: "alias".into(), value: AddRmPackageReqValue::Jsr( PackageReq::from_str("foo@^1.5.0").unwrap() ) diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index 814c76cb27..5683a30cc8 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -1,26 +1,30 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::sync::Arc; +use deno_core::error::AnyError; +use deno_core::futures::stream::FuturesUnordered; +use deno_core::futures::StreamExt; +use deno_semver::jsr::JsrPackageReqReference; + use crate::factory::CliFactory; use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; use crate::graph_util::CreateGraphOptions; -use deno_core::error::AnyError; -use deno_core::futures::stream::FuturesUnordered; -use deno_core::futures::StreamExt; -use deno_semver::jsr::JsrPackageReqReference; +use crate::npm::installer::PackageCaching; 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> { - let npm_resolver = factory.npm_resolver().await?; + let npm_installer = factory.npm_installer_if_managed()?; let cli_options = factory.cli_options()?; - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; + if let Some(npm_installer) = &npm_installer { + npm_installer + .ensure_top_level_package_json_install() + .await?; if let Some(lockfile) = cli_options.maybe_lockfile() { lockfile.error_if_changed()?; } @@ -137,10 +141,8 @@ pub async fn cache_top_level_deps( maybe_graph_error = graph_builder.graph_roots_valid(graph, &roots); } - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) - .await?; + if let Some(npm_installer) = &npm_installer { + npm_installer.cache_packages(PackageCaching::All).await?; } maybe_graph_error?; diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index e4c38276f7..3d152490e8 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -1,9 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; use std::path::PathBuf; -use std::sync::atomic::AtomicBool; use std::sync::Arc; use deno_ast::ModuleSpecifier; @@ -28,22 +27,25 @@ use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use deno_semver::package::PackageReqReference; +use deno_semver::StackString; +use deno_semver::Version; use deno_semver::VersionReq; use import_map::ImportMap; use import_map::ImportMapWithDiagnostics; use import_map::SpecifierMapEntry; use tokio::sync::Semaphore; +use super::ConfigUpdater; use crate::args::CliLockfile; use crate::graph_container::MainModuleGraphContainer; use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; use crate::jsr::JsrFetchResolver; use crate::module_loader::ModuleLoadPreparer; +use crate::npm::installer::NpmInstaller; use crate::npm::CliNpmResolver; use crate::npm::NpmFetchResolver; - -use super::ConfigUpdater; +use crate::util::sync::AtomicFlag; #[derive(Clone, Debug, PartialEq, Eq)] pub enum ImportMapKind { @@ -138,13 +140,7 @@ pub enum KeyPart { Scopes, Dependencies, DevDependencies, - String(String), -} - -impl From for KeyPart { - fn from(value: String) -> Self { - KeyPart::String(value) - } + String(StackString), } impl From for KeyPart { @@ -163,7 +159,7 @@ impl KeyPart { KeyPart::Scopes => "scopes", KeyPart::Dependencies => "dependencies", KeyPart::DevDependencies => "devDependencies", - KeyPart::String(s) => s, + KeyPart::String(s) => s.as_str(), } } } @@ -216,12 +212,12 @@ fn import_map_entries( .chain(import_map.scopes().flat_map(|scope| { let path = KeyPath::from_parts([ KeyPart::Scopes, - scope.raw_key.to_string().into(), + KeyPart::String(scope.raw_key.into()), ]); scope.imports.entries().map(move |entry| { let mut full_path = path.clone(); - full_path.push(KeyPart::String(entry.raw_key.to_string())); + full_path.push(KeyPart::String(entry.raw_key.into())); (full_path, entry) }) })) @@ -337,7 +333,7 @@ fn add_deps_from_package_json( package_json: &PackageJsonRc, mut filter: impl DepFilter, package_dep_kind: PackageJsonDepKind, - package_json_deps: PackageJsonDepsMap, + package_json_deps: &PackageJsonDepsMap, deps: &mut Vec, ) { for (k, v) in package_json_deps { @@ -352,7 +348,7 @@ fn add_deps_from_package_json( deno_package_json::PackageJsonDepValue::Req(req) => { let alias = k.as_str(); let alias = (alias != req.name).then(|| alias.to_string()); - if !filter.should_include(alias.as_deref(), &req, DepKind::Npm) { + if !filter.should_include(alias.as_deref(), req, DepKind::Npm) { continue; } let id = DepId(deps.len()); @@ -361,9 +357,12 @@ fn add_deps_from_package_json( kind: DepKind::Npm, location: DepLocation::PackageJson( package_json.clone(), - KeyPath::from_parts([package_dep_kind.into(), k.into()]), + KeyPath::from_parts([ + package_dep_kind.into(), + KeyPart::String(k.clone()), + ]), ), - req, + req: req.clone(), alias, }) } @@ -376,14 +375,14 @@ fn add_deps_from_package_json( package_json, filter, PackageJsonDepKind::Normal, - package_json_deps.dependencies, + &package_json_deps.dependencies, deps, ); iterate( package_json, filter, PackageJsonDepKind::Dev, - package_json_deps.dev_dependencies, + &package_json_deps.dev_dependencies, deps, ); } @@ -447,12 +446,13 @@ pub struct DepManager { pending_changes: Vec, - dependencies_resolved: AtomicBool, + dependencies_resolved: AtomicFlag, module_load_preparer: Arc, // TODO(nathanwhit): probably shouldn't be pub pub(crate) jsr_fetch_resolver: Arc, pub(crate) npm_fetch_resolver: Arc, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, + npm_installer: Arc, permissions_container: PermissionsContainer, main_module_graph_container: Arc, lockfile: Option>, @@ -462,7 +462,8 @@ pub struct DepManagerArgs { pub module_load_preparer: Arc, pub jsr_fetch_resolver: Arc, pub npm_fetch_resolver: Arc, - pub npm_resolver: Arc, + pub npm_installer: Arc, + pub npm_resolver: CliNpmResolver, pub permissions_container: PermissionsContainer, pub main_module_graph_container: Arc, pub lockfile: Option>, @@ -479,6 +480,7 @@ impl DepManager { module_load_preparer, jsr_fetch_resolver, npm_fetch_resolver, + npm_installer, npm_resolver, permissions_container, main_module_graph_container, @@ -489,9 +491,10 @@ impl DepManager { resolved_versions: Vec::new(), latest_versions: Vec::new(), jsr_fetch_resolver, - dependencies_resolved: AtomicBool::new(false), + dependencies_resolved: AtomicFlag::lowered(), module_load_preparer, npm_fetch_resolver, + npm_installer, npm_resolver, permissions_container, main_module_graph_container, @@ -530,10 +533,7 @@ impl DepManager { } async fn run_dependency_resolution(&self) -> Result<(), AnyError> { - if self - .dependencies_resolved - .load(std::sync::atomic::Ordering::Relaxed) - { + if self.dependencies_resolved.is_raised() { return Ok(()); } @@ -551,19 +551,21 @@ impl DepManager { let npm_resolver = self.npm_resolver.as_managed().unwrap(); if self.deps.iter().all(|dep| match dep.kind { - DepKind::Npm => { - npm_resolver.resolve_pkg_id_from_pkg_req(&dep.req).is_ok() - } + DepKind::Npm => npm_resolver + .resolution() + .resolve_pkg_id_from_pkg_req(&dep.req) + .is_ok(), DepKind::Jsr => graph.packages.mappings().contains_key(&dep.req), }) { - self - .dependencies_resolved - .store(true, std::sync::atomic::Ordering::Relaxed); + self.dependencies_resolved.raise(); graph_permit.commit(); return Ok(()); } - npm_resolver.ensure_top_level_package_json_install().await?; + self + .npm_installer + .ensure_top_level_package_json_install() + .await?; let mut roots = Vec::new(); let mut info_futures = FuturesUnordered::new(); for dep in &self.deps { @@ -613,6 +615,7 @@ impl DepManager { ) .await?; + self.dependencies_resolved.raise(); graph_permit.commit(); Ok(()) @@ -628,7 +631,12 @@ impl DepManager { let graph = self.main_module_graph_container.graph(); let mut resolved = Vec::with_capacity(self.deps.len()); - let snapshot = self.npm_resolver.as_managed().unwrap().snapshot(); + let snapshot = self + .npm_resolver + .as_managed() + .unwrap() + .resolution() + .snapshot(); let resolved_npm = snapshot.package_reqs(); let resolved_jsr = graph.packages.mappings(); for dep in &self.deps { @@ -655,10 +663,6 @@ impl DepManager { if self.latest_versions.len() == self.deps.len() { return Ok(self.latest_versions.clone()); } - let latest_tag_req = deno_semver::VersionReq::from_raw_text_and_inner( - "latest".into(), - deno_semver::RangeSetOrTag::Tag("latest".into()), - ); let mut latest_versions = Vec::with_capacity(self.deps.len()); let npm_sema = Semaphore::new(32); @@ -670,14 +674,25 @@ impl DepManager { DepKind::Npm => futs.push_back( async { let semver_req = &dep.req; - let latest_req = PackageReq { - name: dep.req.name.clone(), - version_req: latest_tag_req.clone(), - }; let _permit = npm_sema.acquire().await; let semver_compatible = self.npm_fetch_resolver.req_to_nv(semver_req).await; - let latest = self.npm_fetch_resolver.req_to_nv(&latest_req).await; + let info = + self.npm_fetch_resolver.package_info(&semver_req.name).await; + let latest = info + .and_then(|info| { + let latest_tag = info.dist_tags.get("latest")?; + let lower_bound = &semver_compatible.as_ref()?.version; + if latest_tag > lower_bound { + Some(latest_tag.clone()) + } else { + latest_version(Some(latest_tag), info.versions.keys()) + } + }) + .map(|version| PackageNv { + name: semver_req.name.clone(), + version, + }); PackageLatestVersion { latest, semver_compatible, @@ -688,14 +703,29 @@ impl DepManager { DepKind::Jsr => futs.push_back( async { let semver_req = &dep.req; - let latest_req = PackageReq { - name: dep.req.name.clone(), - version_req: deno_semver::WILDCARD_VERSION_REQ.clone(), - }; let _permit = jsr_sema.acquire().await; let semver_compatible = self.jsr_fetch_resolver.req_to_nv(semver_req).await; - let latest = self.jsr_fetch_resolver.req_to_nv(&latest_req).await; + let info = + self.jsr_fetch_resolver.package_info(&semver_req.name).await; + let latest = info + .and_then(|info| { + let lower_bound = &semver_compatible.as_ref()?.version; + latest_version( + Some(lower_bound), + info.versions.iter().filter_map(|(version, version_info)| { + if !version_info.yanked { + Some(version) + } else { + None + } + }), + ) + }) + .map(|version| PackageNv { + name: semver_req.name.clone(), + version, + }); PackageLatestVersion { latest, semver_compatible, @@ -893,3 +923,18 @@ fn parse_req_reference( DepKind::Jsr => JsrPackageReqReference::from_str(input)?.into_inner(), }) } + +fn latest_version<'a>( + start: Option<&Version>, + versions: impl IntoIterator, +) -> Option { + let mut best = start; + for version in versions { + match best { + Some(best_version) if version > best_version => best = Some(version), + None => best = Some(version), + _ => {} + } + } + best.cloned() +} diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index aef65a5de0..939c30b5c1 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -1,29 +1,29 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::sync::Arc; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_core::anyhow::bail; use deno_core::error::AnyError; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::VersionReq; use deno_terminal::colors; -use crate::args::CacheSetting; -use crate::args::CliOptions; -use crate::args::Flags; -use crate::args::OutdatedFlags; -use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; -use crate::jsr::JsrFetchResolver; -use crate::npm::NpmFetchResolver; -use crate::tools::registry::pm::deps::DepKind; - use super::deps::Dep; use super::deps::DepManager; use super::deps::DepManagerArgs; use super::deps::PackageLatestVersion; +use crate::args::CliOptions; +use crate::args::Flags; +use crate::args::OutdatedFlags; +use crate::factory::CliFactory; +use crate::file_fetcher::CliFileFetcher; +use crate::jsr::JsrFetchResolver; +use crate::npm::NpmFetchResolver; +use crate::tools::registry::pm::deps::DepKind; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] struct OutdatedPackage { @@ -31,7 +31,7 @@ struct OutdatedPackage { latest: String, semver_compatible: String, current: String, - name: String, + name: StackString, } #[allow(clippy::print_stdout)] @@ -181,15 +181,16 @@ pub async fn outdated( let workspace = cli_options.workspace(); let http_client = factory.http_client_provider(); let deps_http_cache = factory.global_http_cache()?; - let mut file_fetcher = FileFetcher::new( + let file_fetcher = CliFileFetcher::new( deps_http_cache.clone(), - CacheSetting::RespectHeaders, - true, http_client.clone(), + factory.sys(), Default::default(), None, + true, + CacheSetting::RespectHeaders, + log::Level::Trace, ); - file_fetcher.set_download_log_level(log::Level::Trace); let file_fetcher = Arc::new(file_fetcher); let npm_fetch_resolver = Arc::new(NpmFetchResolver::new( file_fetcher.clone(), @@ -444,6 +445,7 @@ async fn dep_manager_args( jsr_fetch_resolver, npm_fetch_resolver, npm_resolver: factory.npm_resolver().await?.clone(), + npm_installer: factory.npm_installer()?.clone(), permissions_container: factory.root_permissions_container()?.clone(), main_module_graph_container: factory .main_module_graph_container() diff --git a/cli/tools/registry/provenance.rs b/cli/tools/registry/provenance.rs index 47169f2132..bd5249b5cd 100644 --- a/cli/tools/registry/provenance.rs +++ b/cli/tools/registry/provenance.rs @@ -1,11 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::http_util; -use crate::http_util::HttpClient; +use std::collections::HashMap; +use std::env; -use super::api::OidcTokenResponse; -use super::auth::gha_oidc_token; -use super::auth::is_gha; use base64::engine::general_purpose::STANDARD_NO_PAD; use base64::prelude::BASE64_STANDARD; use base64::Engine as _; @@ -27,8 +24,12 @@ use sha2::Digest; use spki::der::asn1; use spki::der::pem::LineEnding; use spki::der::EncodePem; -use std::collections::HashMap; -use std::env; + +use super::api::OidcTokenResponse; +use super::auth::gha_oidc_token; +use super::auth::is_gha; +use crate::http_util; +use crate::http_util::HttpClient; const PAE_PREFIX: &str = "DSSEv1"; @@ -706,10 +707,11 @@ async fn testify( #[cfg(test)] mod tests { + use std::env; + use super::ProvenanceAttestation; use super::Subject; use super::SubjectDigest; - use std::env; #[test] fn slsa_github_actions() { diff --git a/cli/tools/registry/publish_order.rs b/cli/tools/registry/publish_order.rs index ad77a56bb1..577627f348 100644 --- a/cli/tools/registry/publish_order.rs +++ b/cli/tools/registry/publish_order.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs index 6d1801ce69..2d6f53b5af 100644 --- a/cli/tools/registry/tar.rs +++ b/cli/tools/registry/tar.rs @@ -1,4 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::fmt::Write as FmtWrite; +use std::io::Write; +use std::path::Path; use bytes::Bytes; use deno_ast::MediaType; @@ -6,17 +10,13 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::url::Url; use sha2::Digest; -use std::fmt::Write as FmtWrite; -use std::io::Write; -use std::path::Path; use tar::Header; -use crate::cache::LazyGraphSourceParser; - use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; use super::paths::CollectedPublishPath; use super::unfurl::SpecifierUnfurler; +use crate::cache::LazyGraphSourceParser; #[derive(Debug, Clone, PartialEq)] pub struct PublishableTarballFile { diff --git a/cli/tools/registry/unfurl.rs b/cli/tools/registry/unfurl.rs index bf6aaaf50d..e3fd4e715b 100644 --- a/cli/tools/registry/unfurl.rs +++ b/cli/tools/registry/unfurl.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::sync::Arc; @@ -655,15 +655,12 @@ fn to_range( mod tests { use std::sync::Arc; - use crate::resolver::SloppyImportsCachedFs; - - use super::*; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_config::workspace::ResolverWorkspaceJsrPackage; use deno_core::serde_json::json; use deno_core::url::Url; - use deno_runtime::deno_fs::RealFs; + use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_runtime::deno_node::PackageJson; use deno_semver::Version; use import_map::ImportMapWithDiagnostics; @@ -671,6 +668,9 @@ mod tests { use pretty_assertions::assert_eq; use test_util::testdata_path; + use super::*; + use crate::sys::CliSys; + fn parse_ast(specifier: &Url, source_code: &str) -> ParsedSource { let media_type = MediaType::from_specifier(specifier); deno_ast::parse_module(deno_ast::ParseParams { @@ -722,10 +722,9 @@ mod tests { vec![Arc::new(package_json)], deno_config::workspace::PackageJsonDepResolution::Enabled, ); - let fs = Arc::new(RealFs); let unfurler = SpecifierUnfurler::new( Some(Arc::new(CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new(fs), + SloppyImportsCachedFs::new(CliSys::default()), ))), Arc::new(workspace_resolver), true, @@ -863,10 +862,10 @@ const warn2 = await import(`${expr}`); ], deno_config::workspace::PackageJsonDepResolution::Enabled, ); - let fs = Arc::new(RealFs); + let sys = CliSys::default(); let unfurler = SpecifierUnfurler::new( Some(Arc::new(CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new(fs), + SloppyImportsCachedFs::new(sys), ))), Arc::new(workspace_resolver), true, diff --git a/cli/tools/repl/channel.rs b/cli/tools/repl/channel.rs index 823a13d288..dbafacebc9 100644 --- a/cli/tools/repl/channel.rs +++ b/cli/tools/repl/channel.rs @@ -1,10 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::cell::RefCell; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::serde_json; use deno_core::serde_json::Value; -use std::cell::RefCell; +use deno_error::JsErrorBox; use tokio::sync::mpsc::channel; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::Receiver; @@ -46,7 +49,7 @@ pub enum RustylineSyncMessage { } pub enum RustylineSyncResponse { - PostMessage(Result), + PostMessage(Result), LspCompletions(Vec), } @@ -60,7 +63,7 @@ impl RustylineSyncMessageSender { &self, method: &str, params: Option, - ) -> Result { + ) -> Result { if let Err(err) = self .message_tx @@ -68,10 +71,11 @@ impl RustylineSyncMessageSender { method: method.to_string(), params: params .map(|params| serde_json::to_value(params)) - .transpose()?, + .transpose() + .map_err(JsErrorBox::from_err)?, }) { - Err(anyhow!("{}", err)) + Err(JsErrorBox::from_err(err).into()) } else { match self.response_rx.borrow_mut().blocking_recv().unwrap() { RustylineSyncResponse::PostMessage(result) => result, diff --git a/cli/tools/repl/editor.rs b/cli/tools/repl/editor.rs index dbc9bce703..27d726255c 100644 --- a/cli/tools/repl/editor.rs +++ b/cli/tools/repl/editor.rs @@ -1,7 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::path::PathBuf; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::Arc; -use crate::cdp; -use crate::colors; use deno_ast::swc::parser::error::SyntaxError; use deno_ast::swc::parser::token::BinOpToken; use deno_ast::swc::parser::token::Token; @@ -32,14 +36,11 @@ use rustyline::Modifiers; use rustyline::RepeatCount; use rustyline_derive::Helper; use rustyline_derive::Hinter; -use std::borrow::Cow; -use std::path::PathBuf; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering::Relaxed; -use std::sync::Arc; use super::channel::RustylineSyncMessageSender; use super::session::REPL_INTERNALS_NAME; +use crate::cdp; +use crate::colors; // Provides helpers to the editor like validation for multi-line edits, completion candidates for // tab completion. diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index a303046879..05e2f320eb 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -1,23 +1,24 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io; use std::io::Write; - use std::sync::Arc; +use deno_core::error::AnyError; +use deno_core::futures::StreamExt; +use deno_core::serde_json; +use deno_core::unsync::spawn_blocking; +use deno_runtime::WorkerExecutionMode; +use rustyline::error::ReadlineError; + use crate::args::CliOptions; use crate::args::Flags; use crate::args::ReplFlags; use crate::cdp; use crate::colors; use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; -use deno_core::error::AnyError; -use deno_core::futures::StreamExt; -use deno_core::serde_json; -use deno_core::unsync::spawn_blocking; -use deno_runtime::WorkerExecutionMode; -use rustyline::error::ReadlineError; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::TextDecodedFile; mod channel; mod editor; @@ -143,7 +144,7 @@ async fn read_line_and_poll( async fn read_eval_file( cli_options: &CliOptions, - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, eval_file: &str, ) -> Result, AnyError> { let specifier = @@ -151,7 +152,7 @@ async fn read_eval_file( let file = file_fetcher.fetch_bypass_permissions(&specifier).await?; - Ok(file.into_text_decoded()?.source) + Ok(TextDecodedFile::decode(file)?.source) } #[allow(clippy::print_stdout)] @@ -163,7 +164,7 @@ pub async fn run( let cli_options = factory.cli_options()?; let main_module = cli_options.resolve_main_module()?; let permissions = factory.root_permissions_container()?; - let npm_resolver = factory.npm_resolver().await?.clone(); + let npm_installer = factory.npm_installer_if_managed()?.cloned(); let resolver = factory.resolver().await?.clone(); let file_fetcher = factory.file_fetcher()?; let worker_factory = factory.create_cli_main_worker_factory().await?; @@ -186,7 +187,7 @@ pub async fn run( let worker = worker.into_main_worker(); let session = ReplSession::initialize( cli_options, - npm_resolver, + npm_installer, resolver, worker, main_module.clone(), diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs index 02594f1519..5ea4523c48 100644 --- a/cli/tools/repl/session.rs +++ b/cli/tools/repl/session.rs @@ -1,23 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; -use crate::args::CliOptions; -use crate::cdp; -use crate::colors; -use crate::lsp::ReplLanguageServer; -use crate::npm::CliNpmResolver; -use crate::resolver::CliResolver; -use crate::tools::test::report_tests; -use crate::tools::test::reporters::PrettyTestReporter; -use crate::tools::test::reporters::TestReporter; -use crate::tools::test::run_tests_for_worker; -use crate::tools::test::send_test_event; -use crate::tools::test::worker_has_tests; -use crate::tools::test::TestEvent; -use crate::tools::test::TestEventReceiver; -use crate::tools::test::TestFailureFormatOptions; - use deno_ast::diagnostics::Diagnostic; use deno_ast::swc::ast as swc_ast; use deno_ast::swc::common::comments::CommentKind; @@ -32,8 +16,9 @@ use deno_ast::ParsedSource; use deno_ast::SourcePos; use deno_ast::SourceRangedForSpanned; use deno_ast::SourceTextInfo; -use deno_core::error::generic_error; +use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::futures::channel::mpsc::UnboundedReceiver; use deno_core::futures::FutureExt; use deno_core::futures::StreamExt; @@ -43,6 +28,7 @@ use deno_core::unsync::spawn; use deno_core::url::Url; use deno_core::LocalInspectorSession; use deno_core::PollEventLoopOptions; +use deno_error::JsErrorBox; use deno_graph::Position; use deno_graph::PositionRange; use deno_graph::SpecifierWithRange; @@ -55,6 +41,22 @@ use regex::Match; use regex::Regex; use tokio::sync::Mutex; +use crate::args::CliOptions; +use crate::cdp; +use crate::colors; +use crate::lsp::ReplLanguageServer; +use crate::npm::installer::NpmInstaller; +use crate::resolver::CliResolver; +use crate::tools::test::report_tests; +use crate::tools::test::reporters::PrettyTestReporter; +use crate::tools::test::reporters::TestReporter; +use crate::tools::test::run_tests_for_worker; +use crate::tools::test::send_test_event; +use crate::tools::test::worker_has_tests; +use crate::tools::test::TestEvent; +use crate::tools::test::TestEventReceiver; +use crate::tools::test::TestFailureFormatOptions; + fn comment_source_to_position_range( comment_start: SourcePos, m: &Match, @@ -179,7 +181,7 @@ struct ReplJsxState { } pub struct ReplSession { - npm_resolver: Arc, + npm_installer: Option>, resolver: Arc, pub worker: MainWorker, session: LocalInspectorSession, @@ -198,7 +200,7 @@ pub struct ReplSession { impl ReplSession { pub async fn initialize( cli_options: &CliOptions, - npm_resolver: Arc, + npm_installer: Option>, resolver: Arc, mut worker: MainWorker, main_module: ModuleSpecifier, @@ -250,10 +252,10 @@ impl ReplSession { let cwd_url = Url::from_directory_path(cli_options.initial_cwd()).map_err(|_| { - generic_error(format!( + anyhow!( "Unable to construct URL from the path of cwd: {}", cli_options.initial_cwd().to_string_lossy(), - )) + ) })?; let ts_config_for_emit = cli_options .resolve_ts_config_for_emit(deno_config::deno_json::TsConfigType::Emit)?; @@ -263,7 +265,7 @@ impl ReplSession { )?; let experimental_decorators = transpile_options.use_ts_decorators; let mut repl_session = ReplSession { - npm_resolver, + npm_installer, resolver, worker, session, @@ -322,7 +324,7 @@ impl ReplSession { &mut self, method: &str, params: Option, - ) -> Result { + ) -> Result { self .worker .js_runtime @@ -339,7 +341,7 @@ impl ReplSession { .await } - pub async fn run_event_loop(&mut self) -> Result<(), AnyError> { + pub async fn run_event_loop(&mut self) -> Result<(), CoreError> { self.worker.run_event_loop(true).await } @@ -400,21 +402,29 @@ impl ReplSession { } Err(err) => { // handle a parsing diagnostic - match err.downcast_ref::() { + match crate::util::result::any_and_jserrorbox_downcast_ref::< + deno_ast::ParseDiagnostic, + >(&err) + { Some(diagnostic) => { Ok(EvaluationOutput::Error(format_diagnostic(diagnostic))) } - None => match err.downcast_ref::() { - Some(diagnostics) => Ok(EvaluationOutput::Error( - diagnostics - .0 - .iter() - .map(format_diagnostic) - .collect::>() - .join("\n\n"), - )), - None => Err(err), - }, + None => { + match crate::util::result::any_and_jserrorbox_downcast_ref::< + ParseDiagnosticsError, + >(&err) + { + Some(diagnostics) => Ok(EvaluationOutput::Error( + diagnostics + .0 + .iter() + .map(format_diagnostic) + .collect::>() + .join("\n\n"), + )), + None => Err(err), + } + } } } } @@ -694,8 +704,8 @@ impl ReplSession { &mut self, program: &swc_ast::Program, ) -> Result<(), AnyError> { - let Some(npm_resolver) = self.npm_resolver.as_managed() else { - return Ok(()); // don't auto-install for byonm + let Some(npm_installer) = &self.npm_installer else { + return Ok(()); }; let mut collector = ImportCollector::new(); @@ -727,13 +737,13 @@ impl ReplSession { let has_node_specifier = resolved_imports.iter().any(|url| url.scheme() == "node"); if !npm_imports.is_empty() || has_node_specifier { - npm_resolver + npm_installer .add_and_cache_package_reqs(&npm_imports) .await?; // prevent messages in the repl about @types/node not being cached if has_node_specifier { - npm_resolver.inject_synthetic_types_node_package().await?; + npm_installer.inject_synthetic_types_node_package().await?; } } Ok(()) @@ -742,7 +752,7 @@ impl ReplSession { async fn evaluate_expression( &mut self, expression: &str, - ) -> Result { + ) -> Result { self .post_message_with_event_loop( "Runtime.evaluate", @@ -765,7 +775,9 @@ impl ReplSession { }), ) .await - .and_then(|res| serde_json::from_value(res).map_err(|e| e.into())) + .and_then(|res| { + serde_json::from_value(res).map_err(|e| JsErrorBox::from_err(e).into()) + }) } } diff --git a/cli/tools/run/hmr.rs b/cli/tools/run/hmr.rs index 373c207d69..913e119689 100644 --- a/cli/tools/run/hmr.rs +++ b/cli/tools/run/hmr.rs @@ -1,16 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; -use deno_core::error::generic_error; -use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::futures::StreamExt; use deno_core::serde_json::json; use deno_core::serde_json::{self}; use deno_core::url::Url; use deno_core::LocalInspectorSession; +use deno_error::JsErrorBox; use deno_terminal::colors; use tokio::select; @@ -66,19 +66,19 @@ pub struct HmrRunner { #[async_trait::async_trait(?Send)] impl crate::worker::HmrRunner for HmrRunner { // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs` - async fn start(&mut self) -> Result<(), AnyError> { + async fn start(&mut self) -> Result<(), CoreError> { self.enable_debugger().await } // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs` - async fn stop(&mut self) -> Result<(), AnyError> { + async fn stop(&mut self) -> Result<(), CoreError> { self .watcher_communicator .change_restart_mode(WatcherRestartMode::Automatic); self.disable_debugger().await } - async fn run(&mut self) -> Result<(), AnyError> { + async fn run(&mut self) -> Result<(), CoreError> { self .watcher_communicator .change_restart_mode(WatcherRestartMode::Manual); @@ -87,13 +87,13 @@ impl crate::worker::HmrRunner for HmrRunner { select! { biased; Some(notification) = session_rx.next() => { - let notification = serde_json::from_value::(notification)?; + let notification = serde_json::from_value::(notification).map_err(JsErrorBox::from_err)?; if notification.method == "Runtime.exceptionThrown" { - let exception_thrown = serde_json::from_value::(notification.params)?; + let exception_thrown = serde_json::from_value::(notification.params).map_err(JsErrorBox::from_err)?; let (message, description) = exception_thrown.exception_details.get_message_and_description(); - break Err(generic_error(format!("{} {}", message, description))); + break Err(JsErrorBox::generic(format!("{} {}", message, description)).into()); } else if notification.method == "Debugger.scriptParsed" { - let params = serde_json::from_value::(notification.params)?; + let params = serde_json::from_value::(notification.params).map_err(JsErrorBox::from_err)?; if params.url.starts_with("file://") { let file_url = Url::parse(¶ms.url).unwrap(); let file_path = file_url.to_file_path().unwrap(); @@ -105,7 +105,7 @@ impl crate::worker::HmrRunner for HmrRunner { } } changed_paths = self.watcher_communicator.watch_for_changed_paths() => { - let changed_paths = changed_paths?; + let changed_paths = changed_paths.map_err(JsErrorBox::from_err)?; let Some(changed_paths) = changed_paths else { let _ = self.watcher_communicator.force_restart(); @@ -187,7 +187,7 @@ impl HmrRunner { } // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs` - async fn enable_debugger(&mut self) -> Result<(), AnyError> { + async fn enable_debugger(&mut self) -> Result<(), CoreError> { self .session .post_message::<()>("Debugger.enable", None) @@ -200,7 +200,7 @@ impl HmrRunner { } // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs` - async fn disable_debugger(&mut self) -> Result<(), AnyError> { + async fn disable_debugger(&mut self) -> Result<(), CoreError> { self .session .post_message::<()>("Debugger.disable", None) @@ -216,7 +216,7 @@ impl HmrRunner { &mut self, script_id: &str, source: &str, - ) -> Result { + ) -> Result { let result = self .session .post_message( @@ -229,15 +229,16 @@ impl HmrRunner { ) .await?; - Ok(serde_json::from_value::( - result, - )?) + Ok( + serde_json::from_value::(result) + .map_err(JsErrorBox::from_err)?, + ) } async fn dispatch_hmr_event( &mut self, script_id: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let expr = format!( "dispatchEvent(new CustomEvent(\"hmr\", {{ detail: {{ path: \"{}\" }} }}));", script_id diff --git a/cli/tools/run/mod.rs b/cli/tools/run/mod.rs index d3f7b093d4..ecf1bd52f3 100644 --- a/cli/tools/run/mod.rs +++ b/cli/tools/run/mod.rs @@ -1,8 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::Read; use std::sync::Arc; +use deno_cache_dir::file_fetcher::File; use deno_config::deno_json::NodeModulesDirMode; use deno_core::error::AnyError; use deno_runtime::WorkerExecutionMode; @@ -11,7 +12,7 @@ use crate::args::EvalFlags; use crate::args::Flags; use crate::args::WatchFlagsWithPaths; use crate::factory::CliFactory; -use crate::file_fetcher::File; +use crate::npm::installer::PackageCaching; use crate::util; use crate::util::file_watcher::WatcherRestartMode; @@ -97,7 +98,7 @@ pub async fn run_from_stdin(flags: Arc) -> Result { // Save a fake file into file fetcher cache // to allow module access by TS compiler file_fetcher.insert_memory_files(File { - specifier: main_module.clone(), + url: main_module.clone(), maybe_headers: None, source: source.into(), }); @@ -184,7 +185,7 @@ pub async fn eval_command( // Save a fake file into file fetcher cache // to allow module access by TS compiler. file_fetcher.insert_memory_files(File { - specifier: main_module.clone(), + url: main_module.clone(), maybe_headers: None, source: source_code.into_bytes().into(), }); @@ -202,18 +203,17 @@ pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> { // ensure an "npm install" is done if the user has explicitly // opted into using a managed node_modules directory if cli_options.node_modules_dir()? == Some(NodeModulesDirMode::Auto) { - if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() { - let already_done = - npm_resolver.ensure_top_level_package_json_install().await?; + if let Some(npm_installer) = factory.npm_installer_if_managed()? { + let already_done = npm_installer + .ensure_top_level_package_json_install() + .await?; if !already_done && matches!( cli_options.default_npm_caching_strategy(), crate::graph_util::NpmCachingStrategy::Eager ) { - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) - .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } } } diff --git a/cli/tools/serve.rs b/cli/tools/serve.rs index d7989140ae..c2c53c1b69 100644 --- a/cli/tools/serve.rs +++ b/cli/tools/serve.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; @@ -73,7 +73,7 @@ async fn do_serve( ) .await?; let worker_count = match worker_count { - None | Some(1) => return worker.run().await, + None | Some(1) => return worker.run().await.map_err(Into::into), Some(c) => c, }; @@ -133,7 +133,7 @@ async fn run_worker( worker.run_for_watcher().await?; Ok(0) } else { - worker.run().await + worker.run().await.map_err(Into::into) } } diff --git a/cli/tools/task.rs b/cli/tools/task.rs index 25d1d66710..329195ab46 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; @@ -25,7 +25,6 @@ use deno_core::futures::FutureExt; use deno_core::futures::StreamExt; use deno_core::url::Url; use deno_path_util::normalize_path; -use deno_runtime::deno_node::NodeResolver; use deno_task_shell::KillSignal; use deno_task_shell::ShellCommand; use indexmap::IndexMap; @@ -36,6 +35,9 @@ use crate::args::Flags; use crate::args::TaskFlags; use crate::colors; use crate::factory::CliFactory; +use crate::node::CliNodeResolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; use crate::task_runner; use crate::task_runner::run_future_forwarding_signals; @@ -94,7 +96,7 @@ pub async fn execute_script( return Ok(0); }; - let task_regex = arg_to_regex(task_name)?; + let task_name_filter = arg_to_task_name_filter(task_name)?; let mut packages_task_info: Vec = vec![]; for folder in workspace.config_folders() { @@ -137,12 +139,20 @@ pub async fn execute_script( // Match tasks in deno.json for name in tasks_config.task_names() { - if task_regex.is_match(name) && !visited.contains(name) { + let matches_filter = match &task_name_filter { + TaskNameFilter::Exact(n) => *n == name, + TaskNameFilter::Regex(re) => re.is_match(name), + }; + if matches_filter && !visited.contains(name) { matched.insert(name.to_string()); visit_task(&tasks_config, &mut visited, name); } } + if matched.is_empty() { + continue; + } + packages_task_info.push(PackageTaskInfo { matched_tasks: matched .iter() @@ -195,6 +205,7 @@ pub async fn execute_script( }] }; + let npm_installer = factory.npm_installer_if_managed()?; let npm_resolver = factory.npm_resolver().await?; let node_resolver = factory.node_resolver().await?; let env_vars = task_runner::real_env_vars(); @@ -208,7 +219,8 @@ pub async fn execute_script( let task_runner = TaskRunner { task_flags: &task_flags, - npm_resolver: npm_resolver.as_ref(), + npm_installer: npm_installer.map(|n| n.as_ref()), + npm_resolver, node_resolver: node_resolver.as_ref(), env_vars, cli_options, @@ -223,7 +235,7 @@ pub async fn execute_script( &Url::from_directory_path(cli_options.initial_cwd()).unwrap(), "", &TaskDefinition { - command: task_flags.task.as_ref().unwrap().to_string(), + command: Some(task_flags.task.as_ref().unwrap().to_string()), dependencies: vec![], description: None, }, @@ -258,8 +270,9 @@ struct RunSingleOptions<'a> { struct TaskRunner<'a> { task_flags: &'a TaskFlags, - npm_resolver: &'a dyn CliNpmResolver, - node_resolver: &'a NodeResolver, + npm_installer: Option<&'a NpmInstaller>, + npm_resolver: &'a CliNpmResolver, + node_resolver: &'a CliNodeResolver, env_vars: HashMap, cli_options: &'a CliOptions, concurrency: usize, @@ -440,11 +453,21 @@ impl<'a> TaskRunner<'a> { kill_signal: KillSignal, argv: &'a [String], ) -> Result { - if let Some(npm_resolver) = self.npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) + let Some(command) = &definition.command else { + log::info!( + "{} {} {}", + colors::green("Task"), + colors::cyan(task_name), + colors::gray("(no command)") + ); + return Ok(0); + }; + + if let Some(npm_installer) = self.npm_installer { + npm_installer + .ensure_top_level_package_json_install() .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } let cwd = match &self.task_flags.cwd { @@ -461,7 +484,7 @@ impl<'a> TaskRunner<'a> { self .run_single(RunSingleOptions { task_name, - script: &definition.command, + script: command, cwd: &cwd, custom_commands, kill_signal, @@ -479,11 +502,11 @@ impl<'a> TaskRunner<'a> { argv: &[String], ) -> Result { // ensure the npm packages are installed if using a managed resolver - if let Some(npm_resolver) = self.npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) + if let Some(npm_installer) = self.npm_installer { + npm_installer + .ensure_top_level_package_json_install() .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } let cwd = match &self.task_flags.cwd { @@ -829,7 +852,7 @@ fn print_available_tasks( is_deno: false, name: name.to_string(), task: deno_config::deno_json::TaskDefinition { - command: script.to_string(), + command: Some(script.to_string()), dependencies: vec![], description: None, }, @@ -865,11 +888,13 @@ fn print_available_tasks( )?; } } - writeln!( - writer, - " {}", - strip_ansi_codes_and_escape_control_chars(&desc.task.command) - )?; + if let Some(command) = &desc.task.command { + writeln!( + writer, + " {}", + strip_ansi_codes_and_escape_control_chars(command) + )?; + }; if !desc.task.dependencies.is_empty() { let dependencies = desc .task @@ -902,3 +927,41 @@ fn strip_ansi_codes_and_escape_control_chars(s: &str) -> String { }) .collect() } + +fn arg_to_task_name_filter(input: &str) -> Result { + if !input.contains("*") { + return Ok(TaskNameFilter::Exact(input)); + } + + let mut regex_str = regex::escape(input); + regex_str = regex_str.replace("\\*", ".*"); + let re = Regex::new(®ex_str)?; + Ok(TaskNameFilter::Regex(re)) +} + +#[derive(Debug)] +enum TaskNameFilter<'s> { + Exact(&'s str), + Regex(regex::Regex), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_arg_to_task_name_filter() { + assert!(matches!( + arg_to_task_name_filter("test").unwrap(), + TaskNameFilter::Exact("test") + )); + assert!(matches!( + arg_to_task_name_filter("test-").unwrap(), + TaskNameFilter::Exact("test-") + )); + assert!(matches!( + arg_to_task_name_filter("test*").unwrap(), + TaskNameFilter::Regex(_) + )); + } +} diff --git a/cli/tools/test/channel.rs b/cli/tools/test/channel.rs index 9a003f2d5d..68633b17e6 100644 --- a/cli/tools/test/channel.rs +++ b/cli/tools/test/channel.rs @@ -1,15 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::TestEvent; -use deno_core::futures::future::poll_fn; -use deno_core::parking_lot; -use deno_core::parking_lot::lock_api::RawMutex; -use deno_core::parking_lot::lock_api::RawMutexTimed; -use deno_runtime::deno_io::pipe; -use deno_runtime::deno_io::AsyncPipeRead; -use deno_runtime::deno_io::PipeRead; -use deno_runtime::deno_io::PipeWrite; -use memmem::Searcher; use std::fmt::Display; use std::future::Future; use std::io::Write; @@ -19,6 +9,16 @@ use std::sync::atomic::Ordering; use std::task::ready; use std::task::Poll; use std::time::Duration; + +use deno_core::futures::future::poll_fn; +use deno_core::parking_lot; +use deno_core::parking_lot::lock_api::RawMutex; +use deno_core::parking_lot::lock_api::RawMutexTimed; +use deno_runtime::deno_io::pipe; +use deno_runtime::deno_io::AsyncPipeRead; +use deno_runtime::deno_io::PipeRead; +use deno_runtime::deno_io::PipeWrite; +use memmem::Searcher; use tokio::io::AsyncRead; use tokio::io::AsyncReadExt; use tokio::io::ReadBuf; @@ -27,6 +27,8 @@ use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::WeakUnboundedSender; +use super::TestEvent; + /// 8-byte sync marker that is unlikely to appear in normal output. Equivalent /// to the string `"\u{200B}\0\u{200B}\0"`. const SYNC_MARKER: &[u8; 8] = &[226, 128, 139, 0, 226, 128, 139, 0]; @@ -35,7 +37,8 @@ const HALF_SYNC_MARKER: &[u8; 4] = &[226, 128, 139, 0]; const BUFFER_SIZE: usize = 4096; /// The test channel has been closed and cannot be used to send further messages. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, deno_error::JsError)] +#[class(generic)] pub struct ChannelClosedError; impl std::error::Error for ChannelClosedError {} @@ -437,11 +440,12 @@ impl TestEventSender { #[allow(clippy::print_stderr)] #[cfg(test)] mod tests { - use super::*; - use crate::tools::test::TestResult; use deno_core::unsync::spawn; use deno_core::unsync::spawn_blocking; + use super::*; + use crate::tools::test::TestResult; + /// Test that output is correctly interleaved with messages. #[tokio::test] async fn spawn_worker() { diff --git a/cli/tools/test/fmt.rs b/cli/tools/test/fmt.rs index 0f6a9ed2b4..e5b40b874b 100644 --- a/cli/tools/test/fmt.rs +++ b/cli/tools/test/fmt.rs @@ -1,16 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::ops::AddAssign; use deno_core::stats::RuntimeActivity; use deno_core::stats::RuntimeActivityDiff; use deno_core::stats::RuntimeActivityTrace; use deno_core::stats::RuntimeActivityType; use phf::phf_map; -use std::borrow::Cow; -use std::ops::AddAssign; - -use crate::util::path::to_percent_decoded_str; use super::*; +use crate::util::path::to_percent_decoded_str; pub fn to_relative_path_or_remote_url(cwd: &Url, path_or_url: &str) -> String { let Ok(url) = Url::parse(path_or_url) else { diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 2e46bdd4da..21ee7e152d 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -1,33 +1,33 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::args::CliOptions; -use crate::args::Flags; -use crate::args::TestFlags; -use crate::args::TestReporterConfig; -use crate::colors; -use crate::display; -use crate::factory::CliFactory; -use crate::file_fetcher::File; -use crate::file_fetcher::FileFetcher; -use crate::graph_util::has_graph_root_local_dependent_changed; -use crate::ops; -use crate::util::extract::extract_doc_tests; -use crate::util::file_watcher; -use crate::util::fs::collect_specifiers; -use crate::util::path::get_extension; -use crate::util::path::is_script_ext; -use crate::util::path::matches_pattern_or_exact_path; -use crate::worker::CliMainWorkerFactory; -use crate::worker::CoverageCollector; +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::collections::HashSet; +use std::env; +use std::fmt::Write as _; +use std::future::poll_fn; +use std::io::Write; +use std::num::NonZeroUsize; +use std::path::Path; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::task::Poll; +use std::time::Duration; +use std::time::Instant; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::File; use deno_config::glob::FilePatterns; use deno_config::glob::WalkEntry; use deno_core::anyhow; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context as _; -use deno_core::error::generic_error; +use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::error::JsError; use deno_core::futures::future; use deno_core::futures::stream; @@ -48,6 +48,7 @@ use deno_core::v8; use deno_core::ModuleSpecifier; use deno_core::OpState; use deno_core::PollEventLoopOptions; +use deno_error::JsErrorBox; use deno_runtime::deno_io::Stdio; use deno_runtime::deno_io::StdioPipe; use deno_runtime::deno_permissions::Permissions; @@ -65,27 +66,28 @@ use rand::seq::SliceRandom; use rand::SeedableRng; use regex::Regex; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::collections::HashSet; -use std::env; -use std::fmt::Write as _; -use std::future::poll_fn; -use std::io::Write; -use std::num::NonZeroUsize; -use std::path::Path; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; -use std::task::Poll; -use std::time::Duration; -use std::time::Instant; use tokio::signal; +use crate::args::CliOptions; +use crate::args::Flags; +use crate::args::TestFlags; +use crate::args::TestReporterConfig; +use crate::colors; +use crate::display; +use crate::factory::CliFactory; +use crate::file_fetcher::CliFileFetcher; +use crate::graph_util::has_graph_root_local_dependent_changed; +use crate::ops; +use crate::sys::CliSys; +use crate::util::extract::extract_doc_tests; +use crate::util::file_watcher; +use crate::util::fs::collect_specifiers; +use crate::util::path::get_extension; +use crate::util::path::is_script_ext; +use crate::util::path::matches_pattern_or_exact_path; +use crate::worker::CliMainWorkerFactory; +use crate::worker::CoverageCollector; + mod channel; pub mod fmt; pub mod reporters; @@ -104,6 +106,8 @@ use reporters::PrettyTestReporter; use reporters::TapTestReporter; use reporters::TestReporter; +use crate::tools::test::channel::ChannelClosedError; + /// How many times we're allowed to spin the event loop before considering something a leak. const MAX_SANITIZER_LOOP_SPINS: usize = 16; @@ -610,13 +614,16 @@ async fn configure_main_worker( permissions_container: PermissionsContainer, worker_sender: TestEventWorkerSender, options: &TestSpecifierOptions, -) -> Result<(Option>, MainWorker), anyhow::Error> { +) -> Result<(Option>, MainWorker), CoreError> { let mut worker = worker_factory .create_custom_worker( WorkerExecutionMode::Test, specifier.clone(), permissions_container, - vec![ops::testing::deno_test::init_ops(worker_sender.sender)], + vec![ + ops::testing::deno_test::init_ops(worker_sender.sender), + ops::lint::deno_lint::init_ops(), + ], Stdio { stdin: StdioPipe::inherit(), stdout: StdioPipe::file(worker_sender.stdout), @@ -635,21 +642,15 @@ async fn configure_main_worker( let mut worker = worker.into_main_worker(); match res { Ok(()) => Ok(()), - Err(error) => { - // TODO(mmastrac): It would be nice to avoid having this error pattern repeated - if error.is::() { - send_test_event( - &worker.js_runtime.op_state(), - TestEvent::UncaughtError( - specifier.to_string(), - Box::new(error.downcast::().unwrap()), - ), - )?; - Ok(()) - } else { - Err(error) - } + Err(CoreError::Js(err)) => { + send_test_event( + &worker.js_runtime.op_state(), + TestEvent::UncaughtError(specifier.to_string(), Box::new(err)), + ) + .map_err(JsErrorBox::from_err)?; + Ok(()) } + Err(err) => Err(err), }?; Ok((coverage_collector, worker)) } @@ -686,21 +687,14 @@ pub async fn test_specifier( .await { Ok(()) => Ok(()), - Err(error) => { - // TODO(mmastrac): It would be nice to avoid having this error pattern repeated - if error.is::() { - send_test_event( - &worker.js_runtime.op_state(), - TestEvent::UncaughtError( - specifier.to_string(), - Box::new(error.downcast::().unwrap()), - ), - )?; - Ok(()) - } else { - Err(error) - } + Err(CoreError::Js(err)) => { + send_test_event( + &worker.js_runtime.op_state(), + TestEvent::UncaughtError(specifier.to_string(), Box::new(err)), + )?; + Ok(()) } + Err(e) => Err(e.into()), } } @@ -713,7 +707,7 @@ async fn test_specifier_inner( specifier: ModuleSpecifier, fail_fast_tracker: FailFastTracker, options: TestSpecifierOptions, -) -> Result<(), AnyError> { +) -> Result<(), CoreError> { // Ensure that there are no pending exceptions before we start running tests worker.run_up_to_duration(Duration::from_millis(0)).await?; @@ -760,7 +754,7 @@ pub fn worker_has_tests(worker: &mut MainWorker) -> bool { /// Yields to tokio to allow async work to process, and then polls /// the event loop once. #[must_use = "The event loop result should be checked"] -pub async fn poll_event_loop(worker: &mut MainWorker) -> Result<(), AnyError> { +pub async fn poll_event_loop(worker: &mut MainWorker) -> Result<(), CoreError> { // Allow any ops that to do work in the tokio event loop to do so tokio::task::yield_now().await; // Spin the event loop once @@ -779,13 +773,11 @@ pub async fn poll_event_loop(worker: &mut MainWorker) -> Result<(), AnyError> { pub fn send_test_event( op_state: &RefCell, event: TestEvent, -) -> Result<(), AnyError> { - Ok( - op_state - .borrow_mut() - .borrow_mut::() - .send(event)?, - ) +) -> Result<(), ChannelClosedError> { + op_state + .borrow_mut() + .borrow_mut::() + .send(event) } pub async fn run_tests_for_worker( @@ -981,13 +973,10 @@ async fn run_tests_for_worker_inner( let result = match result { Ok(r) => r, Err(error) => { - if error.is::() { + if let CoreError::Js(js_error) = error { send_test_event( &state_rc, - TestEvent::UncaughtError( - specifier.to_string(), - Box::new(error.downcast::().unwrap()), - ), + TestEvent::UncaughtError(specifier.to_string(), Box::new(js_error)), )?; fail_fast_tracker.add_failure(); send_test_event( @@ -997,7 +986,7 @@ async fn run_tests_for_worker_inner( had_uncaught_error = true; continue; } else { - return Err(error); + return Err(error.into()); } } }; @@ -1191,7 +1180,7 @@ static HAS_TEST_RUN_SIGINT_HANDLER: AtomicBool = AtomicBool::new(false); async fn test_specifiers( worker_factory: Arc, permissions: &Permissions, - permission_desc_parser: &Arc, + permission_desc_parser: &Arc>, specifiers: Vec, options: TestSpecifiersOptions, ) -> Result<(), AnyError> { @@ -1369,25 +1358,20 @@ pub async fn report_tests( reporter.report_summary(&elapsed, &tests, &test_steps); if let Err(err) = reporter.flush_report(&elapsed, &tests, &test_steps) { return ( - Err(generic_error(format!( - "Test reporter failed to flush: {}", - err - ))), + Err(anyhow!("Test reporter failed to flush: {}", err)), receiver, ); } if used_only { return ( - Err(generic_error( - "Test failed because the \"only\" option was used", - )), + Err(anyhow!("Test failed because the \"only\" option was used",)), receiver, ); } if failed { - return (Err(generic_error("Test failed")), receiver); + return (Err(anyhow!("Test failed")), receiver); } (Ok(()), receiver) @@ -1514,7 +1498,7 @@ fn collect_specifiers_with_test_mode( /// as well. async fn fetch_specifiers_with_test_mode( cli_options: &CliOptions, - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, member_patterns: impl Iterator, doc: &bool, ) -> Result, AnyError> { @@ -1570,7 +1554,7 @@ pub async fn run_tests( if !workspace_test_options.permit_no_files && specifiers_with_mode.is_empty() { - return Err(generic_error("No test modules found")); + return Err(anyhow!("No test modules found")); } let doc_tests = get_doc_tests(&specifiers_with_mode, file_fetcher).await?; @@ -1606,10 +1590,10 @@ pub async fn run_tests( TestSpecifiersOptions { cwd: Url::from_directory_path(cli_options.initial_cwd()).map_err( |_| { - generic_error(format!( + anyhow!( "Unable to construct URL from the path of cwd: {}", cli_options.initial_cwd().to_string_lossy(), - )) + ) }, )?, concurrent_jobs: workspace_test_options.concurrent_jobs, @@ -1788,10 +1772,10 @@ pub async fn run_tests_with_watch( TestSpecifiersOptions { cwd: Url::from_directory_path(cli_options.initial_cwd()).map_err( |_| { - generic_error(format!( + anyhow!( "Unable to construct URL from the path of cwd: {}", cli_options.initial_cwd().to_string_lossy(), - )) + ) }, )?, concurrent_jobs: workspace_test_options.concurrent_jobs, @@ -1822,7 +1806,7 @@ pub async fn run_tests_with_watch( /// Extracts doc tests from files specified by the given specifiers. async fn get_doc_tests( specifiers_with_mode: &[(Url, TestMode)], - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, ) -> Result, AnyError> { let specifiers_needing_extraction = specifiers_with_mode .iter() @@ -1847,7 +1831,7 @@ fn get_target_specifiers( specifiers_with_mode .into_iter() .filter_map(|(s, mode)| mode.needs_test_run().then_some(s)) - .chain(doc_tests.iter().map(|d| d.specifier.clone())) + .chain(doc_tests.iter().map(|d| d.url.clone())) .collect() } diff --git a/cli/tools/test/reporters/common.rs b/cli/tools/test/reporters/common.rs index 7ca83db809..2db6a36464 100644 --- a/cli/tools/test/reporters/common.rs +++ b/cli/tools/test/reporters/common.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::fmt::format_test_error; use super::fmt::to_relative_path_or_remote_url; diff --git a/cli/tools/test/reporters/compound.rs b/cli/tools/test/reporters/compound.rs index 3ab7297db5..3c4409ecaa 100644 --- a/cli/tools/test/reporters/compound.rs +++ b/cli/tools/test/reporters/compound.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::*; @@ -129,7 +129,7 @@ impl TestReporter for CompoundTestReporter { if errors.is_empty() { Ok(()) } else { - bail!( + anyhow::bail!( "error in one or more wrapped reporters:\n{}", errors .iter() diff --git a/cli/tools/test/reporters/dot.rs b/cli/tools/test/reporters/dot.rs index 169c4b0e72..0912858431 100644 --- a/cli/tools/test/reporters/dot.rs +++ b/cli/tools/test/reporters/dot.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::common; use super::fmt::to_relative_path_or_remote_url; diff --git a/cli/tools/test/reporters/junit.rs b/cli/tools/test/reporters/junit.rs index 3998bee40d..9418ac9fb2 100644 --- a/cli/tools/test/reporters/junit.rs +++ b/cli/tools/test/reporters/junit.rs @@ -1,8 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::VecDeque; use std::path::PathBuf; +use deno_core::anyhow::Context; + use super::fmt::to_relative_path_or_remote_url; use super::*; diff --git a/cli/tools/test/reporters/mod.rs b/cli/tools/test/reporters/mod.rs index 07351e9c3b..ef2b00b776 100644 --- a/cli/tools/test/reporters/mod.rs +++ b/cli/tools/test/reporters/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::*; diff --git a/cli/tools/test/reporters/pretty.rs b/cli/tools/test/reporters/pretty.rs index 4120bbfb54..3419fdea59 100644 --- a/cli/tools/test/reporters/pretty.rs +++ b/cli/tools/test/reporters/pretty.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::common; use super::fmt::to_relative_path_or_remote_url; diff --git a/cli/tools/test/reporters/tap.rs b/cli/tools/test/reporters/tap.rs index ea68ddd43a..c36df25756 100644 --- a/cli/tools/test/reporters/tap.rs +++ b/cli/tools/test/reporters/tap.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json::json; use deno_core::serde_json::{self}; diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs index cb85859f7a..521c3217fe 100644 --- a/cli/tools/upgrade.rs +++ b/cli/tools/upgrade.rs @@ -1,7 +1,28 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This module provides feature to upgrade deno executable +use std::borrow::Cow; +use std::env; +use std::fs; +use std::io::IsTerminal; +use std::ops::Sub; +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; +use std::sync::Arc; +use std::time::Duration; + +use async_trait::async_trait; +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::unsync::spawn; +use deno_core::url::Url; +use deno_semver::SmallStackString; +use deno_semver::Version; +use once_cell::sync::Lazy; + use crate::args::Flags; use crate::args::UpgradeFlags; use crate::args::UPGRADE_USAGE; @@ -15,25 +36,6 @@ use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::version; -use async_trait::async_trait; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::unsync::spawn; -use deno_core::url::Url; -use deno_semver::Version; -use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::env; -use std::fs; -use std::io::IsTerminal; -use std::ops::Sub; -use std::path::Path; -use std::path::PathBuf; -use std::process::Command; -use std::sync::Arc; -use std::time::Duration; - const RELEASE_URL: &str = "https://github.com/denoland/deno/releases"; const CANARY_URL: &str = "https://dl.deno.land/canary"; const DL_RELEASE_URL: &str = "https://dl.deno.land/release"; @@ -255,7 +257,7 @@ async fn print_release_notes( let is_deno_2_rc = new_semver.major == 2 && new_semver.minor == 0 && new_semver.patch == 0 - && new_semver.pre.first() == Some(&"rc".to_string()); + && new_semver.pre.first().map(|s| s.as_str()) == Some("rc"); if is_deno_2_rc || is_switching_from_deno1_to_deno2 { log::info!( @@ -674,7 +676,7 @@ impl RequestedVersion { ); }; - if semver.pre.contains(&"rc".to_string()) { + if semver.pre.contains(&SmallStackString::from_static("rc")) { (ReleaseChannel::Rc, passed_version) } else { (ReleaseChannel::Stable, passed_version) diff --git a/cli/tsc/00_typescript.js b/cli/tsc/00_typescript.js index 7d20f92367..b7626fe082 100644 --- a/cli/tsc/00_typescript.js +++ b/cli/tsc/00_typescript.js @@ -136063,6 +136063,7 @@ var unprefixedNodeCoreModuleList = [ "https", "http2", "inspector", + "inspector/promises", "module", "net", "os", diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 7e8a407cf9..b3279f54ac 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// @@ -41,6 +41,13 @@ delete Object.prototype.__proto__; "listen", "listenDatagram", "openKv", + "connectQuic", + "listenQuic", + "QuicBidirectionalStream", + "QuicConn", + "QuicListener", + "QuicReceiveStream", + "QuicSendStream", ]); const unstableMsgSuggestion = "If not, try changing the 'lib' compiler option to include 'deno.unstable' " + @@ -402,9 +409,20 @@ delete Object.prototype.__proto__; messageText = formatMessage(msgText, ri.code); } if (start !== undefined && length !== undefined && file) { - const startPos = file.getLineAndCharacterOfPosition(start); - const sourceLine = file.getFullText().split("\n")[startPos.line]; - const fileName = file.fileName; + let startPos = file.getLineAndCharacterOfPosition(start); + let sourceLine = file.getFullText().split("\n")[startPos.line]; + const originalFileName = file.fileName; + const fileName = ops.op_remap_specifier + ? (ops.op_remap_specifier(file.fileName) ?? file.fileName) + : file.fileName; + // Bit of a hack to detect when we have a .wasm file and want to hide + // the .d.ts text. This is not perfect, but will work in most scenarios + if ( + fileName.endsWith(".wasm") && originalFileName.endsWith(".wasm.d.mts") + ) { + startPos = { line: 0, character: 0 }; + sourceLine = undefined; + } return { start: startPos, end: file.getLineAndCharacterOfPosition(start + length), @@ -468,6 +486,9 @@ delete Object.prototype.__proto__; 2792, // TS2307: Cannot find module '{0}' or its corresponding type declarations. 2307, + // Relative import errors to add an extension + 2834, + 2835, // TS5009: Cannot find the common subdirectory path for the input files. 5009, // TS5055: Cannot write file @@ -702,7 +723,7 @@ delete Object.prototype.__proto__; } : arg; if (fileReference.fileName.startsWith("npm:")) { - /** @type {[string, ts.Extension] | undefined} */ + /** @type {[string, ts.Extension | null] | undefined} */ const resolved = ops.op_resolve( containingFilePath, [ @@ -714,7 +735,7 @@ delete Object.prototype.__proto__; ], ], )?.[0]; - if (resolved) { + if (resolved && resolved[1]) { return { resolvedTypeReferenceDirective: { primary: true, @@ -764,7 +785,7 @@ delete Object.prototype.__proto__; debug(` base: ${base}`); debug(` specifiers: ${specifiers.map((s) => s[1]).join(", ")}`); } - /** @type {Array<[string, ts.Extension] | undefined>} */ + /** @type {Array<[string, ts.Extension | null] | undefined>} */ const resolved = ops.op_resolve( base, specifiers, @@ -772,7 +793,7 @@ delete Object.prototype.__proto__; if (resolved) { /** @type {Array} */ const result = resolved.map((item) => { - if (item) { + if (item && item[1]) { const [resolvedFileName, extension] = item; return { resolvedModule: { @@ -1030,24 +1051,27 @@ delete Object.prototype.__proto__; configFileParsingDiagnostics, }); - const checkFiles = localOnly - ? rootNames - .filter((n) => !n.startsWith("http")) - .map((checkName) => { - const sourceFile = program.getSourceFile(checkName); - if (sourceFile == null) { - throw new Error("Could not find source file for: " + checkName); - } - return sourceFile; - }) - : undefined; + let checkFiles = undefined; + + if (localOnly) { + const checkFileNames = new Set(); + checkFiles = []; + + for (const checkName of rootNames) { + if (checkName.startsWith("http")) { + continue; + } + const sourceFile = program.getSourceFile(checkName); + if (sourceFile != null) { + checkFiles.push(sourceFile); + } + checkFileNames.add(checkName); + } - if (checkFiles != null) { // When calling program.getSemanticDiagnostics(...) with a source file, we // need to call this code first in order to get it to invalidate cached // diagnostics correctly. This is what program.getSemanticDiagnostics() // does internally when calling without any arguments. - const checkFileNames = new Set(checkFiles.map((f) => f.fileName)); while ( program.getSemanticDiagnosticsOfNextAffectedFile( undefined, diff --git a/cli/tsc/_analyze_types_node.ts b/cli/tsc/_analyze_types_node.ts index 10af00ee83..1a77b6c547 100755 --- a/cli/tsc/_analyze_types_node.ts +++ b/cli/tsc/_analyze_types_node.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-env --allow-read --allow-write=. -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { ModuleDeclarationKind, Node, diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts index 428e4d1ed8..0389527f5b 100644 --- a/cli/tsc/compiler.d.ts +++ b/cli/tsc/compiler.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Contains types that can be used to validate and check `99_main_compiler.js` diff --git a/cli/tsc/diagnostics.rs b/cli/tsc/diagnostics.rs index d3795706eb..3780f65e77 100644 --- a/cli/tsc/diagnostics.rs +++ b/cli/tsc/diagnostics.rs @@ -1,16 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::error::Error; +use std::fmt; use deno_ast::ModuleSpecifier; -use deno_graph::ModuleGraph; -use deno_terminal::colors; - use deno_core::serde::Deserialize; use deno_core::serde::Deserializer; use deno_core::serde::Serialize; use deno_core::serde::Serializer; use deno_core::sourcemap::SourceMap; -use std::error::Error; -use std::fmt; +use deno_graph::ModuleGraph; +use deno_terminal::colors; const MAX_SOURCE_LINE_LENGTH: usize = 150; @@ -90,9 +90,9 @@ impl DiagnosticMessageChain { s.push_str(&" ".repeat(level * 2)); s.push_str(&self.message_text); if let Some(next) = &self.next { - s.push('\n'); let arr = next.clone(); for dm in arr { + s.push('\n'); s.push_str(&dm.format_message(level + 1)); } } @@ -110,6 +110,15 @@ pub struct Position { pub character: u64, } +impl Position { + pub fn from_deno_graph(deno_graph_position: deno_graph::Position) -> Self { + Self { + line: deno_graph_position.line as u64, + character: deno_graph_position.character as u64, + } + } +} + #[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Diagnostic { @@ -133,9 +142,47 @@ pub struct Diagnostic { pub file_name: Option, #[serde(skip_serializing_if = "Option::is_none")] pub related_information: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub reports_deprecated: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub reports_unnecessary: Option, + #[serde(flatten)] + pub other: deno_core::serde_json::Map, } impl Diagnostic { + pub fn from_missing_error( + specifier: &ModuleSpecifier, + maybe_range: Option<&deno_graph::Range>, + additional_message: Option, + ) -> Self { + Self { + category: DiagnosticCategory::Error, + code: 2307, + start: maybe_range.map(|r| Position::from_deno_graph(r.range.start)), + end: maybe_range.map(|r| Position::from_deno_graph(r.range.end)), + original_source_start: None, // will be applied later + message_text: Some(format!( + "Cannot find module '{}'.{}{}", + specifier, + if additional_message.is_none() { + "" + } else { + " " + }, + additional_message.unwrap_or_default() + )), + message_chain: None, + source: None, + source_line: None, + file_name: maybe_range.map(|r| r.specifier.to_string()), + related_information: None, + reports_deprecated: None, + reports_unnecessary: None, + other: Default::default(), + } + } + /// If this diagnostic should be included when it comes from a remote module. pub fn include_when_remote(&self) -> bool { /// TS6133: value is declared but its value is never read (noUnusedParameters and noUnusedLocals) @@ -273,7 +320,8 @@ impl fmt::Display for Diagnostic { } } -#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Eq, PartialEq, deno_error::JsError)] +#[class(generic)] pub struct Diagnostics(Vec); impl Diagnostics { @@ -293,6 +341,14 @@ impl Diagnostics { }); } + pub fn push(&mut self, diagnostic: Diagnostic) { + self.0.push(diagnostic); + } + + pub fn extend(&mut self, diagnostic: Diagnostics) { + self.0.extend(diagnostic.0); + } + /// Return a set of diagnostics where only the values where the predicate /// returns `true` are included. pub fn filter

(self, predicate: P) -> Self @@ -395,11 +451,12 @@ impl Error for Diagnostics {} #[cfg(test)] mod tests { - use super::*; use deno_core::serde_json; use deno_core::serde_json::json; use test_util::strip_ansi_codes; + use super::*; + #[test] fn test_de_diagnostics() { let value = json!([ diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index d9f66f11a7..e98f68ea38 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/cli/tsc/dts/lib.deno.shared_globals.d.ts b/cli/tsc/dts/lib.deno.shared_globals.d.ts index 96790fb665..a469525270 100644 --- a/cli/tsc/dts/lib.deno.shared_globals.d.ts +++ b/cli/tsc/dts/lib.deno.shared_globals.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Documentation partially adapted from [MDN](https://developer.mozilla.org/), // by Mozilla Contributors, which is licensed under CC-BY-SA 2.5. diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index 6759856e6a..bd32845a6a 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// @@ -293,7 +293,8 @@ declare namespace Deno { * executions. Each element in the array represents the number of milliseconds * to wait before retrying the execution. For example, `[1000, 5000, 10000]` * means that a failed execution will be retried at most 3 times, with 1 - * second, 5 seconds, and 10 seconds delay between each retry. + * second, 5 seconds, and 10 seconds delay between each retry. There is a + * limit of 5 retries and a maximum interval of 1 hour (3600000 milliseconds). * * @category Cloud * @experimental @@ -1267,16 +1268,15 @@ declare namespace Deno { * OpenTelemetry API. This is done using the official OpenTelemetry package * for JavaScript: * [`npm:@opentelemetry/api`](https://opentelemetry.io/docs/languages/js/). - * Deno integrates with this package to provide trace context propagation - * between native Deno APIs (like `Deno.serve` or `fetch`) and custom user - * code. Deno also provides APIs that allow exporting custom telemetry data - * via the same OTLP channel used by the Deno runtime. This is done using the - * [`jsr:@deno/otel`](https://jsr.io/@deno/otel) package. + * Deno integrates with this package to provide tracing, metrics, and trace + * context propagation between native Deno APIs (like `Deno.serve` or `fetch`) + * and custom user code. Deno automatically registers the providers with the + * OpenTelemetry API, so users can start creating custom traces, metrics, and + * logs without any additional setup. * * @example Using OpenTelemetry API to create custom traces * ```ts,ignore * import { trace } from "npm:@opentelemetry/api@1"; - * import "jsr:@deno/otel@0.0.2/register"; * * const tracer = trace.getTracer("example-tracer"); * @@ -1300,20 +1300,43 @@ declare namespace Deno { */ export namespace telemetry { /** - * A SpanExporter compatible with OpenTelemetry.js - * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_sdk_trace_base.SpanExporter.html + * A TracerProvider compatible with OpenTelemetry.js + * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.TracerProvider.html + * + * This is a singleton object that implements the OpenTelemetry + * TracerProvider interface. + * * @category Telemetry * @experimental */ - export class SpanExporter {} + // deno-lint-ignore no-explicit-any + export const tracerProvider: any; /** * A ContextManager compatible with OpenTelemetry.js * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.ContextManager.html + * + * This is a singleton object that implements the OpenTelemetry + * ContextManager interface. + * * @category Telemetry * @experimental */ - export class ContextManager {} + // deno-lint-ignore no-explicit-any + export const contextManager: any; + + /** + * A MeterProvider compatible with OpenTelemetry.js + * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.MeterProvider.html + * + * This is a singleton object that implements the OpenTelemetry + * MeterProvider interface. + * + * @category Telemetry + * @experimental + */ + // deno-lint-ignore no-explicit-any + export const meterProvider: any; export {}; // only export exports } diff --git a/cli/tsc/dts/lib.deno.window.d.ts b/cli/tsc/dts/lib.deno.window.d.ts index 636e2b0fd0..251f338be6 100644 --- a/cli/tsc/dts/lib.deno.window.d.ts +++ b/cli/tsc/dts/lib.deno.window.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// @@ -83,8 +83,30 @@ declare var window: Window & typeof globalThis; declare var self: Window & typeof globalThis; /** @category Platform */ declare var closed: boolean; -/** @category Platform */ + +/** + * Exits the current Deno process. + * + * This function terminates the process by signaling the runtime to exit. + * Similar to exit(0) in posix. Its behavior is similar to the `window.close()` + * method in the browser, but specific to the Deno runtime. + * + * Note: Use this function cautiously, as it will stop the execution of the + * entire Deno program immediately. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/close + * + * @example + * ```ts + * console.log("About to close the Deno process."); + * close(); // The process will terminate here. + * console.log("This will not be logged."); // This line will never execute. + * ``` + * + * @category Platform + */ declare function close(): void; + /** @category Events */ declare var onerror: ((this: Window, ev: ErrorEvent) => any) | null; /** @category Events */ diff --git a/cli/tsc/dts/lib.deno.worker.d.ts b/cli/tsc/dts/lib.deno.worker.d.ts index fa69cc57d6..c309417031 100644 --- a/cli/tsc/dts/lib.deno.worker.d.ts +++ b/cli/tsc/dts/lib.deno.worker.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/cli/tsc/dts/lib.deno_webgpu.d.ts b/cli/tsc/dts/lib.deno_webgpu.d.ts index 2deb63abc7..6dbfc57768 100644 --- a/cli/tsc/dts/lib.deno_webgpu.d.ts +++ b/cli/tsc/dts/lib.deno_webgpu.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-empty-interface diff --git a/cli/tsc/dts/lib.dom.extras.d.ts b/cli/tsc/dts/lib.dom.extras.d.ts index a6de789f56..3a1667a99c 100644 --- a/cli/tsc/dts/lib.dom.extras.d.ts +++ b/cli/tsc/dts/lib.dom.extras.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /* * This library contains DOM standards that are not currently included in the diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index a8e8d73b68..37d52c6cf2 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -1,18 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::args::TsConfig; -use crate::args::TypeCheckMode; -use crate::cache::FastInsecureHasher; -use crate::cache::ModuleInfoCache; -use crate::node; -use crate::npm::CliNpmResolver; -use crate::resolver::CjsTracker; -use crate::util::checksum; -use crate::util::path::mapped_specifier_for_tsc; -use crate::worker::create_isolate_create_params; +use std::borrow::Cow; +use std::collections::HashMap; +use std::fmt; +use std::path::PathBuf; +use std::sync::Arc; use deno_ast::MediaType; -use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; use deno_core::ascii_str; use deno_core::error::AnyError; @@ -34,24 +28,30 @@ use deno_graph::GraphKind; use deno_graph::Module; use deno_graph::ModuleGraph; use deno_graph::ResolutionResolved; +use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::deno_fs; -use deno_runtime::deno_node::NodeResolver; use deno_semver::npm::NpmPackageReqReference; use node_resolver::errors::NodeJsErrorCode; use node_resolver::errors::NodeJsErrorCoded; use node_resolver::errors::PackageSubpathResolveError; +use node_resolver::resolve_specifier_into_node_modules; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::collections::HashMap; -use std::fmt; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use thiserror::Error; +use crate::args::TsConfig; +use crate::args::TypeCheckMode; +use crate::cache::FastInsecureHasher; +use crate::cache::ModuleInfoCache; +use crate::node::CliNodeResolver; +use crate::npm::CliNpmResolver; +use crate::resolver::CliCjsTracker; +use crate::sys::CliSys; +use crate::util::checksum; +use crate::util::path::mapped_specifier_for_tsc; +use crate::worker::create_isolate_create_params; + mod diagnostics; pub use self::diagnostics::Diagnostic; @@ -128,6 +128,7 @@ fn get_asset_texts_from_new_runtime() -> Result, AnyError> { op_emit, op_is_node_file, op_load, + op_remap_specifier, op_resolve, op_respond, ] @@ -274,30 +275,6 @@ fn hash_url(specifier: &ModuleSpecifier, media_type: MediaType) -> String { ) } -/// If the provided URLs derivable tsc media type doesn't match the media type, -/// we will add an extension to the output. This is to avoid issues with -/// specifiers that don't have extensions, that tsc refuses to emit because they -/// think a `.js` version exists, when it doesn't. -fn maybe_remap_specifier( - specifier: &ModuleSpecifier, - media_type: MediaType, -) -> Option { - let path = if specifier.scheme() == "file" { - if let Ok(path) = specifier.to_file_path() { - path - } else { - PathBuf::from(specifier.path()) - } - } else { - PathBuf::from(specifier.path()) - }; - if path.extension().is_none() { - Some(format!("{}{}", specifier, media_type.as_ts_extension())) - } else { - None - } -} - #[derive(Debug, Clone, Default, Eq, PartialEq)] pub struct EmittedFile { pub data: String, @@ -315,7 +292,7 @@ pub fn into_specifier_and_media_type( (specifier, media_type) } None => ( - Url::parse("internal:///missing_dependency.d.ts").unwrap(), + Url::parse(MISSING_DEPENDENCY_SPECIFIER).unwrap(), MediaType::Dts, ), } @@ -323,13 +300,13 @@ pub fn into_specifier_and_media_type( #[derive(Debug)] pub struct TypeCheckingCjsTracker { - cjs_tracker: Arc, + cjs_tracker: Arc, module_info_cache: Arc, } impl TypeCheckingCjsTracker { pub fn new( - cjs_tracker: Arc, + cjs_tracker: Arc, module_info_cache: Arc, ) -> Self { Self { @@ -380,8 +357,8 @@ impl TypeCheckingCjsTracker { #[derive(Debug)] pub struct RequestNpmState { pub cjs_tracker: Arc, - pub node_resolver: Arc, - pub npm_resolver: Arc, + pub node_resolver: Arc, + pub npm_resolver: CliNpmResolver, } /// A structure representing a request to be sent to the tsc runtime. @@ -421,6 +398,8 @@ struct State { maybe_tsbuildinfo: Option, maybe_response: Option, maybe_npm: Option, + // todo(dsherret): it looks like the remapped_specifiers and + // root_map could be combined... what is the point of the separation? remapped_specifiers: HashMap, root_map: HashMap, current_dir: PathBuf, @@ -462,13 +441,16 @@ impl State { current_dir, } } -} -fn normalize_specifier( - specifier: &str, - current_dir: &Path, -) -> Result { - resolve_url_or_path(specifier, current_dir).map_err(|err| err.into()) + pub fn maybe_remapped_specifier( + &self, + specifier: &str, + ) -> Option<&ModuleSpecifier> { + self + .remapped_specifiers + .get(specifier) + .or_else(|| self.root_map.get(specifier)) + } } #[op2] @@ -541,6 +523,21 @@ pub fn as_ts_script_kind(media_type: MediaType) -> i32 { pub const MISSING_DEPENDENCY_SPECIFIER: &str = "internal:///missing_dependency.d.ts"; +#[derive(Debug, Error, deno_error::JsError)] +pub enum LoadError { + #[class(generic)] + #[error("Unable to load {path}: {error}")] + LoadFromNodeModule { path: String, error: std::io::Error }, + #[class(inherit)] + #[error( + "Error converting a string module specifier for \"op_resolve\": {0}" + )] + ModuleResolution(#[from] deno_core::ModuleResolutionError), + #[class(inherit)] + #[error("{0}")] + ClosestPkgJson(#[from] node_resolver::errors::ClosestPkgJsonError), +} + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct LoadResponse { @@ -555,24 +552,28 @@ struct LoadResponse { fn op_load( state: &mut OpState, #[string] load_specifier: &str, -) -> Result, AnyError> { +) -> Result, LoadError> { op_load_inner(state, load_specifier) } fn op_load_inner( state: &mut OpState, load_specifier: &str, -) -> Result, AnyError> { +) -> Result, LoadError> { fn load_from_node_modules( specifier: &ModuleSpecifier, npm_state: Option<&RequestNpmState>, media_type: &mut MediaType, is_cjs: &mut bool, - ) -> Result { + ) -> Result { *media_type = MediaType::from_specifier(specifier); let file_path = specifier.to_file_path().unwrap(); - let code = std::fs::read_to_string(&file_path) - .with_context(|| format!("Unable to load {}", file_path.display()))?; + let code = std::fs::read_to_string(&file_path).map_err(|err| { + LoadError::LoadFromNodeModule { + path: file_path.display().to_string(), + error: err, + } + })?; let code: Arc = code.into(); *is_cjs = npm_state .map(|npm_state| { @@ -585,8 +586,7 @@ fn op_load_inner( let state = state.borrow_mut::(); - let specifier = normalize_specifier(load_specifier, &state.current_dir) - .context("Error converting a string module specifier for \"op_load\".")?; + let specifier = resolve_url_or_path(load_specifier, &state.current_dir)?; let mut hash: Option = None; let mut media_type = MediaType::Unknown; @@ -606,10 +606,7 @@ fn op_load_inner( maybe_source.map(Cow::Borrowed) } else { let specifier = if let Some(remapped_specifier) = - state.remapped_specifiers.get(load_specifier) - { - remapped_specifier - } else if let Some(remapped_specifier) = state.root_map.get(load_specifier) + state.maybe_remapped_specifier(load_specifier) { remapped_specifier } else { @@ -660,9 +657,9 @@ fn op_load_inner( None } else { // means it's Deno code importing an npm module - let specifier = node::resolve_specifier_into_node_modules( + let specifier = resolve_specifier_into_node_modules( + &CliSys::default(), &module.specifier, - &deno_fs::RealFs, ); Some(Cow::Owned(load_from_node_modules( &specifier, @@ -701,6 +698,24 @@ fn op_load_inner( })) } +#[derive(Debug, Error, deno_error::JsError)] +pub enum ResolveError { + #[class(inherit)] + #[error( + "Error converting a string module specifier for \"op_resolve\": {0}" + )] + ModuleResolution(#[from] deno_core::ModuleResolutionError), + #[class(inherit)] + #[error("{0}")] + PackageSubpathResolve(PackageSubpathResolveError), + #[class(inherit)] + #[error("{0}")] + ResolvePkgFolderFromDenoModule(#[from] ResolvePkgFolderFromDenoModuleError), + #[class(inherit)] + #[error("{0}")] + ResolveNonGraphSpecifierTypes(#[from] ResolveNonGraphSpecifierTypesError), +} + #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ResolveArgs { @@ -712,13 +727,25 @@ pub struct ResolveArgs { pub specifiers: Vec<(bool, String)>, } +#[op2] +#[string] +fn op_remap_specifier( + state: &mut OpState, + #[string] specifier: &str, +) -> Option { + let state = state.borrow::(); + state + .maybe_remapped_specifier(specifier) + .map(|url| url.to_string()) +} + #[op2] #[serde] fn op_resolve( state: &mut OpState, #[string] base: String, #[serde] specifiers: Vec<(bool, String)>, -) -> Result, AnyError> { +) -> Result)>, ResolveError> { op_resolve_inner(state, ResolveArgs { base, specifiers }) } @@ -726,40 +753,40 @@ fn op_resolve( fn op_resolve_inner( state: &mut OpState, args: ResolveArgs, -) -> Result, AnyError> { +) -> Result)>, ResolveError> { let state = state.borrow_mut::(); - let mut resolved: Vec<(String, &'static str)> = + let mut resolved: Vec<(String, Option<&'static str>)> = Vec::with_capacity(args.specifiers.len()); let referrer = if let Some(remapped_specifier) = - state.remapped_specifiers.get(&args.base) + state.maybe_remapped_specifier(&args.base) { remapped_specifier.clone() - } else if let Some(remapped_base) = state.root_map.get(&args.base) { - remapped_base.clone() } else { - normalize_specifier(&args.base, &state.current_dir).context( - "Error converting a string module specifier for \"op_resolve\".", - )? + resolve_url_or_path(&args.base, &state.current_dir)? }; let referrer_module = state.graph.get(&referrer); for (is_cjs, specifier) in args.specifiers { if specifier.starts_with("node:") { resolved.push(( MISSING_DEPENDENCY_SPECIFIER.to_string(), - MediaType::Dts.as_ts_extension(), + Some(MediaType::Dts.as_ts_extension()), )); continue; } if specifier.starts_with("asset:///") { let ext = MediaType::from_str(&specifier).as_ts_extension(); - resolved.push((specifier, ext)); + resolved.push((specifier, Some(ext))); continue; } let resolved_dep = referrer_module - .and_then(|m| m.js()) - .and_then(|m| m.dependencies_prefer_fast_check().get(&specifier)) + .and_then(|m| match m { + Module::Js(m) => m.dependencies_prefer_fast_check().get(&specifier), + Module::Json(_) => None, + Module::Wasm(m) => m.dependencies.get(&specifier), + Module::Npm(_) | Module::Node(_) | Module::External(_) => None, + }) .and_then(|d| d.maybe_type.ok().or_else(|| d.maybe_code.ok())); let resolution_mode = if is_cjs { ResolutionMode::Require @@ -815,7 +842,7 @@ fn op_resolve_inner( } _ => { if let Some(specifier_str) = - maybe_remap_specifier(&specifier, media_type) + mapped_specifier_for_tsc(&specifier, media_type) { state .remapped_specifiers @@ -829,17 +856,18 @@ fn op_resolve_inner( ( specifier_str, match media_type { - MediaType::Css => ".js", // surface these as .js for typescript - media_type => media_type.as_ts_extension(), + MediaType::Css => Some(".js"), // surface these as .js for typescript + MediaType::Unknown => None, + media_type => Some(media_type.as_ts_extension()), }, ) } None => ( MISSING_DEPENDENCY_SPECIFIER.to_string(), - MediaType::Dts.as_ts_extension(), + Some(MediaType::Dts.as_ts_extension()), ), }; - log::debug!("Resolved {} to {:?}", specifier, result); + log::debug!("Resolved {} from {} to {:?}", specifier, referrer, result); resolved.push(result); } @@ -851,7 +879,7 @@ fn resolve_graph_specifier_types( referrer: &ModuleSpecifier, resolution_mode: ResolutionMode, state: &State, -) -> Result, AnyError> { +) -> Result, ResolveError> { let graph = &state.graph; let maybe_module = match graph.try_get(specifier) { Ok(Some(module)) => Some(module), @@ -913,7 +941,7 @@ fn resolve_graph_specifier_types( Err(err) => match err.code() { NodeJsErrorCode::ERR_TYPES_NOT_FOUND | NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None, - _ => return Err(err.into()), + _ => return Err(ResolveError::PackageSubpathResolve(err)), }, }; Ok(Some(into_specifier_and_media_type(maybe_url))) @@ -924,9 +952,9 @@ fn resolve_graph_specifier_types( Some(Module::External(module)) => { // we currently only use "External" for when the module is in an npm package Ok(state.maybe_npm.as_ref().map(|_| { - let specifier = node::resolve_specifier_into_node_modules( + let specifier = resolve_specifier_into_node_modules( + &CliSys::default(), &module.specifier, - &deno_fs::RealFs, ); into_specifier_and_media_type(Some(specifier)) })) @@ -935,10 +963,12 @@ fn resolve_graph_specifier_types( } } -#[derive(Debug, Error)] -enum ResolveNonGraphSpecifierTypesError { +#[derive(Debug, Error, deno_error::JsError)] +pub enum ResolveNonGraphSpecifierTypesError { + #[class(inherit)] #[error(transparent)] ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError), + #[class(inherit)] #[error(transparent)] PackageSubpathResolve(#[from] PackageSubpathResolveError), } @@ -1035,10 +1065,20 @@ fn op_respond_inner(state: &mut OpState, args: RespondArgs) { state.maybe_response = Some(args); } +#[derive(Debug, Error, deno_error::JsError)] +pub enum ExecError { + #[class(generic)] + #[error("The response for the exec request was not set.")] + ResponseNotSet, + #[class(inherit)] + #[error(transparent)] + Core(deno_core::error::CoreError), +} + /// Execute a request on the supplied snapshot, returning a response which /// contains information, like any emitted files, diagnostics, statistics and /// optionally an updated TypeScript build info. -pub fn exec(request: Request) -> Result { +pub fn exec(request: Request) -> Result { // tsc cannot handle root specifiers that don't have one of the "acceptable" // extensions. Therefore, we have to check the root modules against their // extensions and remap any that are unacceptable to tsc and add them to the @@ -1071,6 +1111,7 @@ pub fn exec(request: Request) -> Result { op_emit, op_is_node_file, op_load, + op_remap_specifier, op_resolve, op_respond, ], @@ -1113,7 +1154,9 @@ pub fn exec(request: Request) -> Result { ..Default::default() }); - runtime.execute_script(located_script_name!(), exec_source)?; + runtime + .execute_script(located_script_name!(), exec_source) + .map_err(ExecError::Core)?; let op_state = runtime.op_state(); let mut op_state = op_state.borrow_mut(); @@ -1130,22 +1173,24 @@ pub fn exec(request: Request) -> Result { stats, }) } else { - Err(anyhow!("The response for the exec request was not set.")) + Err(ExecError::ResponseNotSet) } } #[cfg(test)] mod tests { + use deno_core::futures::future; + use deno_core::serde_json; + use deno_core::OpState; + use deno_error::JsErrorBox; + use deno_graph::GraphKind; + use deno_graph::ModuleGraph; + use test_util::PathRef; + use super::Diagnostic; use super::DiagnosticCategory; use super::*; use crate::args::TsConfig; - use deno_core::futures::future; - use deno_core::serde_json; - use deno_core::OpState; - use deno_graph::GraphKind; - use deno_graph::ModuleGraph; - use test_util::PathRef; #[derive(Debug, Default)] pub struct MockLoader { @@ -1164,13 +1209,20 @@ mod tests { .replace("://", "_") .replace('/', "-"); let source_path = self.fixtures.join(specifier_text); - let response = source_path.read_to_bytes_if_exists().map(|c| { - Some(deno_graph::source::LoadResponse::Module { - specifier: specifier.clone(), - maybe_headers: None, - content: c.into(), + let response = source_path + .read_to_bytes_if_exists() + .map(|c| { + Some(deno_graph::source::LoadResponse::Module { + specifier: specifier.clone(), + maybe_headers: None, + content: c.into(), + }) }) - }); + .map_err(|e| { + deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::generic( + e.to_string(), + ))) + }); Box::pin(future::ready(response)) } } @@ -1207,7 +1259,7 @@ mod tests { async fn test_exec( specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { let hash_data = 123; // something random let fixtures = test_util::testdata_path().join("tsc2"); let loader = MockLoader { fixtures }; @@ -1389,7 +1441,10 @@ mod tests { }, ) .expect("should have invoked op"); - assert_eq!(actual, vec![("https://deno.land/x/b.ts".into(), ".ts")]); + assert_eq!( + actual, + vec![("https://deno.land/x/b.ts".into(), Some(".ts"))] + ); } #[tokio::test] @@ -1408,7 +1463,10 @@ mod tests { }, ) .expect("should have not errored"); - assert_eq!(actual, vec![(MISSING_DEPENDENCY_SPECIFIER.into(), ".d.ts")]); + assert_eq!( + actual, + vec![(MISSING_DEPENDENCY_SPECIFIER.into(), Some(".d.ts"))] + ); } #[tokio::test] @@ -1444,6 +1502,9 @@ mod tests { source_line: None, file_name: None, related_information: None, + reports_deprecated: None, + reports_unnecessary: None, + other: Default::default(), }]), stats: Stats(vec![("a".to_string(), 12)]) }) diff --git a/cli/util/archive.rs b/cli/util/archive.rs index e2183d57ab..e3efd69adb 100644 --- a/cli/util/archive.rs +++ b/cli/util/archive.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fs; use std::path::Path; diff --git a/cli/util/checksum.rs b/cli/util/checksum.rs index c9c55ec2b4..b15380abd7 100644 --- a/cli/util/checksum.rs +++ b/cli/util/checksum.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use ring::digest::Context; use ring::digest::SHA256; diff --git a/cli/util/console.rs b/cli/util/console.rs index 74e6928a28..d22d41f5ea 100644 --- a/cli/util/console.rs +++ b/cli/util/console.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_runtime::ops::tty::ConsoleSize; diff --git a/cli/util/diff.rs b/cli/util/diff.rs index 14ece0c44c..a42da3a89a 100644 --- a/cli/util/diff.rs +++ b/cli/util/diff.rs @@ -1,9 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::fmt::Write as _; -use crate::colors; use dissimilar::diff as difference; use dissimilar::Chunk; -use std::fmt::Write as _; + +use crate::colors; /// Print diff of the same file_path, before and after formatting. /// diff --git a/cli/util/display.rs b/cli/util/display.rs index 8795d3db68..dff08f31fa 100644 --- a/cli/util/display.rs +++ b/cli/util/display.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::io::Write; use deno_core::error::AnyError; use deno_core::serde_json; use deno_runtime::colors; -use std::io::Write; /// A function that converts a float to a string the represents a human /// readable version of that number. diff --git a/cli/util/draw_thread.rs b/cli/util/draw_thread.rs index 164a8fc713..a2d7681425 100644 --- a/cli/util/draw_thread.rs +++ b/cli/util/draw_thread.rs @@ -1,13 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::io::IsTerminal; +use std::sync::Arc; +use std::time::Duration; use console_static_text::ConsoleStaticText; use deno_core::parking_lot::Mutex; use deno_core::unsync::spawn_blocking; use deno_runtime::ops::tty::ConsoleSize; use once_cell::sync::Lazy; -use std::io::IsTerminal; -use std::sync::Arc; -use std::time::Duration; use crate::util::console::console_size; diff --git a/cli/util/extract.rs b/cli/util/extract.rs index be68202aa1..825e81bb4b 100644 --- a/cli/util/extract.rs +++ b/cli/util/extract.rs @@ -1,4 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::BTreeSet; +use std::fmt::Write as _; +use std::sync::Arc; use deno_ast::swc::ast; use deno_ast::swc::atoms::Atom; @@ -13,14 +17,12 @@ use deno_ast::swc::visit::VisitMut; use deno_ast::swc::visit::VisitWith as _; use deno_ast::MediaType; use deno_ast::SourceRangedForSpanned as _; +use deno_cache_dir::file_fetcher::File; use deno_core::error::AnyError; use deno_core::ModuleSpecifier; use regex::Regex; -use std::collections::BTreeSet; -use std::fmt::Write as _; -use std::sync::Arc; -use crate::file_fetcher::File; +use crate::file_fetcher::TextDecodedFile; use crate::util::path::mapped_specifier_for_tsc; /// Extracts doc tests from a given file, transforms them into pseudo test @@ -52,7 +54,7 @@ fn extract_inner( file: File, wrap_kind: WrapKind, ) -> Result, AnyError> { - let file = file.into_text_decoded()?; + let file = TextDecodedFile::decode(file)?; let exports = match deno_ast::parse_program(deno_ast::ParseParams { specifier: file.specifier.clone(), @@ -230,7 +232,7 @@ fn extract_files_from_regex_blocks( .unwrap_or(file_specifier); Some(File { - specifier: file_specifier, + url: file_specifier, maybe_headers: None, source: file_source.into_bytes().into(), }) @@ -558,7 +560,7 @@ fn generate_pseudo_file( exports: &ExportCollector, wrap_kind: WrapKind, ) -> Result { - let file = file.into_text_decoded()?; + let file = TextDecodedFile::decode(file)?; let parsed = deno_ast::parse_program(deno_ast::ParseParams { specifier: file.specifier.clone(), @@ -594,7 +596,7 @@ fn generate_pseudo_file( log::debug!("{}:\n{}", file.specifier, source); Ok(File { - specifier: file.specifier, + url: file.specifier, maybe_headers: None, source: source.into_bytes().into(), }) @@ -807,11 +809,12 @@ fn wrap_in_deno_test(stmts: Vec, test_name: Atom) -> ast::Stmt { #[cfg(test)] mod tests { - use super::*; - use crate::file_fetcher::TextDecodedFile; use deno_ast::swc::atoms::Atom; use pretty_assertions::assert_eq; + use super::*; + use crate::file_fetcher::TextDecodedFile; + #[test] fn test_extract_doc_tests() { struct Input { @@ -1199,14 +1202,14 @@ Deno.test("file:///main.ts$3-7.ts", async ()=>{ for test in tests { let file = File { - specifier: ModuleSpecifier::parse(test.input.specifier).unwrap(), + url: ModuleSpecifier::parse(test.input.specifier).unwrap(), maybe_headers: None, source: test.input.source.as_bytes().into(), }; let got_decoded = extract_doc_tests(file) .unwrap() .into_iter() - .map(|f| f.into_text_decoded().unwrap()) + .map(|f| TextDecodedFile::decode(f).unwrap()) .collect::>(); let expected = test .expected @@ -1435,14 +1438,14 @@ add('1', '2'); for test in tests { let file = File { - specifier: ModuleSpecifier::parse(test.input.specifier).unwrap(), + url: ModuleSpecifier::parse(test.input.specifier).unwrap(), maybe_headers: None, source: test.input.source.as_bytes().into(), }; let got_decoded = extract_snippet_files(file) .unwrap() .into_iter() - .map(|f| f.into_text_decoded().unwrap()) + .map(|f| TextDecodedFile::decode(f).unwrap()) .collect::>(); let expected = test .expected diff --git a/cli/util/file_watcher.rs b/cli/util/file_watcher.rs index b9318a6e4b..65963214b9 100644 --- a/cli/util/file_watcher.rs +++ b/cli/util/file_watcher.rs @@ -1,12 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::args::Flags; -use crate::colors; -use crate::util::fs::canonicalize_path; +use std::cell::RefCell; +use std::collections::HashSet; +use std::io::IsTerminal; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; +use std::time::Duration; use deno_config::glob::PathOrPatternSet; use deno_core::error::AnyError; -use deno_core::error::JsError; +use deno_core::error::CoreError; use deno_core::futures::Future; use deno_core::futures::FutureExt; use deno_core::parking_lot::Mutex; @@ -18,18 +22,16 @@ use notify::Error as NotifyError; use notify::RecommendedWatcher; use notify::RecursiveMode; use notify::Watcher; -use std::cell::RefCell; -use std::collections::HashSet; -use std::io::IsTerminal; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; -use std::time::Duration; use tokio::select; +use tokio::sync::broadcast::error::RecvError; use tokio::sync::mpsc; use tokio::sync::mpsc::UnboundedReceiver; use tokio::time::sleep; +use crate::args::Flags; +use crate::colors; +use crate::util::fs::canonicalize_path; + const CLEAR_SCREEN: &str = "\x1B[H\x1B[2J\x1B[3J"; const DEBOUNCE_INTERVAL: Duration = Duration::from_millis(200); @@ -79,10 +81,13 @@ where { let result = watch_future.await; if let Err(err) = result { - let error_string = match err.downcast_ref::() { - Some(e) => format_js_error(e), - None => format!("{err:?}"), - }; + let error_string = + match crate::util::result::any_and_jserrorbox_downcast_ref::( + &err, + ) { + Some(CoreError::Js(e)) => format_js_error(e), + _ => format!("{err:?}"), + }; log::error!( "{}: {}", colors::red_bold("error"), @@ -170,9 +175,9 @@ impl WatcherCommunicator { pub async fn watch_for_changed_paths( &self, - ) -> Result>, AnyError> { + ) -> Result>, RecvError> { let mut rx = self.changed_paths_rx.resubscribe(); - rx.recv().await.map_err(AnyError::from) + rx.recv().await } pub fn change_restart_mode(&self, restart_mode: WatcherRestartMode) { diff --git a/cli/util/fs.rs b/cli/util/fs.rs index ba84a0e8f3..d9cebe10d5 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -1,9 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use std::fs::OpenOptions; use std::io::Error; use std::io::ErrorKind; -use std::io::Write; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -15,189 +13,18 @@ use deno_config::glob::PathOrPattern; use deno_config::glob::PathOrPatternSet; use deno_config::glob::WalkEntry; use deno_core::anyhow::anyhow; -use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::unsync::spawn_blocking; use deno_core::ModuleSpecifier; -use deno_runtime::deno_fs::FileSystem; +use sys_traits::FsCreateDirAll; +use sys_traits::FsDirEntry; +use sys_traits::FsSymlinkDir; -use crate::util::path::get_atomic_file_path; +use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressMessagePrompt; -/// Writes the file to the file system at a temporary path, then -/// renames it to the destination in a single sys call in order -/// to never leave the file system in a corrupted state. -/// -/// This also handles creating the directory if a NotFound error -/// occurs. -pub fn atomic_write_file_with_retries>( - file_path: &Path, - data: T, - mode: u32, -) -> std::io::Result<()> { - struct RealAtomicWriteFileFs { - mode: u32, - } - - impl AtomicWriteFileFs for RealAtomicWriteFileFs { - fn write_file(&self, path: &Path, bytes: &[u8]) -> std::io::Result<()> { - write_file(path, bytes, self.mode) - } - fn rename_file(&self, from: &Path, to: &Path) -> std::io::Result<()> { - std::fs::rename(from, to) - } - fn remove_file(&self, path: &Path) -> std::io::Result<()> { - std::fs::remove_file(path) - } - fn create_dir_all(&self, dir_path: &Path) -> std::io::Result<()> { - std::fs::create_dir_all(dir_path) - } - fn path_exists(&self, path: &Path) -> bool { - path.exists() - } - } - - atomic_write_file_with_retries_and_fs( - &RealAtomicWriteFileFs { mode }, - file_path, - data.as_ref(), - ) -} - -pub trait AtomicWriteFileFs { - fn write_file(&self, path: &Path, bytes: &[u8]) -> std::io::Result<()>; - fn rename_file(&self, from: &Path, to: &Path) -> std::io::Result<()>; - fn remove_file(&self, path: &Path) -> std::io::Result<()>; - fn create_dir_all(&self, dir_path: &Path) -> std::io::Result<()>; - fn path_exists(&self, path: &Path) -> bool; -} - -pub struct AtomicWriteFileFsAdapter<'a> { - pub fs: &'a dyn FileSystem, - pub write_mode: u32, -} - -impl<'a> AtomicWriteFileFs for AtomicWriteFileFsAdapter<'a> { - fn write_file(&self, path: &Path, bytes: &[u8]) -> std::io::Result<()> { - self - .fs - .write_file_sync( - path, - deno_runtime::deno_fs::OpenOptions::write( - true, - false, - false, - Some(self.write_mode), - ), - None, - bytes, - ) - .map_err(|e| e.into_io_error()) - } - - fn rename_file(&self, from: &Path, to: &Path) -> std::io::Result<()> { - self.fs.rename_sync(from, to).map_err(|e| e.into_io_error()) - } - - fn remove_file(&self, path: &Path) -> std::io::Result<()> { - self - .fs - .remove_sync(path, false) - .map_err(|e| e.into_io_error()) - } - - fn create_dir_all(&self, dir_path: &Path) -> std::io::Result<()> { - self - .fs - .mkdir_sync(dir_path, /* recursive */ true, None) - .map_err(|e| e.into_io_error()) - } - - fn path_exists(&self, path: &Path) -> bool { - self.fs.exists_sync(path) - } -} - -pub fn atomic_write_file_with_retries_and_fs>( - fs: &impl AtomicWriteFileFs, - file_path: &Path, - data: T, -) -> std::io::Result<()> { - let mut count = 0; - loop { - match atomic_write_file(fs, file_path, data.as_ref()) { - Ok(()) => return Ok(()), - Err(err) => { - if count >= 5 { - // too many retries, return the error - return Err(err); - } - count += 1; - let sleep_ms = std::cmp::min(50, 10 * count); - std::thread::sleep(std::time::Duration::from_millis(sleep_ms)); - } - } - } -} - -/// Writes the file to the file system at a temporary path, then -/// renames it to the destination in a single sys call in order -/// to never leave the file system in a corrupted state. -/// -/// This also handles creating the directory if a NotFound error -/// occurs. -fn atomic_write_file( - fs: &impl AtomicWriteFileFs, - file_path: &Path, - data: &[u8], -) -> std::io::Result<()> { - fn atomic_write_file_raw( - fs: &impl AtomicWriteFileFs, - temp_file_path: &Path, - file_path: &Path, - data: &[u8], - ) -> std::io::Result<()> { - fs.write_file(temp_file_path, data)?; - fs.rename_file(temp_file_path, file_path) - .inspect_err(|_err| { - // clean up the created temp file on error - let _ = fs.remove_file(temp_file_path); - }) - } - - let temp_file_path = get_atomic_file_path(file_path); - - if let Err(write_err) = - atomic_write_file_raw(fs, &temp_file_path, file_path, data) - { - if write_err.kind() == ErrorKind::NotFound { - let parent_dir_path = file_path.parent().unwrap(); - match fs.create_dir_all(parent_dir_path) { - Ok(()) => { - return atomic_write_file_raw(fs, &temp_file_path, file_path, data) - .map_err(|err| add_file_context_to_err(file_path, err)); - } - Err(create_err) => { - if !fs.path_exists(parent_dir_path) { - return Err(Error::new( - create_err.kind(), - format!( - "{:#} (for '{}')\nCheck the permission of the directory.", - create_err, - parent_dir_path.display() - ), - )); - } - } - } - } - return Err(add_file_context_to_err(file_path, write_err)); - } - Ok(()) -} - /// Creates a std::fs::File handling if the parent does not exist. pub fn create_file(file_path: &Path) -> std::io::Result { match std::fs::File::create(file_path) { @@ -236,45 +63,6 @@ fn add_file_context_to_err(file_path: &Path, err: Error) -> Error { ) } -pub fn write_file>( - filename: &Path, - data: T, - mode: u32, -) -> std::io::Result<()> { - write_file_2(filename, data, true, mode, true, false) -} - -pub fn write_file_2>( - filename: &Path, - data: T, - update_mode: bool, - mode: u32, - is_create: bool, - is_append: bool, -) -> std::io::Result<()> { - let mut file = OpenOptions::new() - .read(false) - .write(true) - .append(is_append) - .truncate(!is_append) - .create(is_create) - .open(filename)?; - - if update_mode { - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let mode = mode & 0o777; - let permissions = PermissionsExt::from_mode(mode); - file.set_permissions(permissions)?; - } - #[cfg(not(unix))] - let _ = mode; - } - - file.write_all(data.as_ref()) -} - /// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows. pub fn canonicalize_path(path: &Path) -> Result { Ok(deno_path_util::strip_unc_prefix(path.canonicalize()?)) @@ -289,16 +77,10 @@ pub fn canonicalize_path(path: &Path) -> Result { pub fn canonicalize_path_maybe_not_exists( path: &Path, ) -> Result { - deno_path_util::canonicalize_path_maybe_not_exists(path, &canonicalize_path) -} - -pub fn canonicalize_path_maybe_not_exists_with_fs( - path: &Path, - fs: &dyn FileSystem, -) -> Result { - deno_path_util::canonicalize_path_maybe_not_exists(path, &|path| { - fs.realpath_sync(path).map_err(|err| err.into_io_error()) - }) + deno_path_util::fs::canonicalize_path_maybe_not_exists( + &CliSys::default(), + path, + ) } /// Collects module specifiers that satisfy the given predicate as a file path, by recursively walking `include`. @@ -346,7 +128,7 @@ pub fn collect_specifiers( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(vendor_folder) - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files)?; + .collect_file_patterns(&CliSys::default(), files); let mut collected_files_as_urls = collected_files .iter() .map(|f| specifier_from_file_path(f).unwrap()) @@ -368,111 +150,123 @@ pub async fn remove_dir_all_if_exists(path: &Path) -> std::io::Result<()> { } } -mod clone_dir_imp { - - #[cfg(target_vendor = "apple")] - mod apple { - use super::super::copy_dir_recursive; - use deno_core::error::AnyError; - use std::os::unix::ffi::OsStrExt; - use std::path::Path; - fn clonefile(from: &Path, to: &Path) -> std::io::Result<()> { - let from = std::ffi::CString::new(from.as_os_str().as_bytes())?; - let to = std::ffi::CString::new(to.as_os_str().as_bytes())?; - // SAFETY: `from` and `to` are valid C strings. - let ret = unsafe { libc::clonefile(from.as_ptr(), to.as_ptr(), 0) }; - if ret != 0 { - return Err(std::io::Error::last_os_error()); - } - Ok(()) - } - - pub fn clone_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { - if let Some(parent) = to.parent() { - std::fs::create_dir_all(parent)?; - } - // Try to clone the whole directory - if let Err(err) = clonefile(from, to) { - if err.kind() != std::io::ErrorKind::AlreadyExists { - log::warn!( - "Failed to clone dir {:?} to {:?} via clonefile: {}", - from, - to, - err - ); - } - // clonefile won't overwrite existing files, so if the dir exists - // we need to handle it recursively. - copy_dir_recursive(from, to)?; - } - - Ok(()) - } - } - - #[cfg(target_vendor = "apple")] - pub(super) use apple::clone_dir_recursive; - - #[cfg(not(target_vendor = "apple"))] - pub(super) fn clone_dir_recursive( - from: &std::path::Path, - to: &std::path::Path, - ) -> Result<(), deno_core::error::AnyError> { - if let Err(e) = super::hard_link_dir_recursive(from, to) { - log::debug!("Failed to hard link dir {:?} to {:?}: {}", from, to, e); - super::copy_dir_recursive(from, to)?; - } - - Ok(()) - } -} - /// Clones a directory to another directory. The exact method /// is not guaranteed - it may be a hardlink, copy, or other platform-specific /// operation. /// /// Note: Does not handle symlinks. -pub fn clone_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { - clone_dir_imp::clone_dir_recursive(from, to) +pub fn clone_dir_recursive< + TSys: sys_traits::FsCopy + + sys_traits::FsCloneFile + + sys_traits::FsCloneFile + + sys_traits::FsCreateDir + + sys_traits::FsHardLink + + sys_traits::FsReadDir + + sys_traits::FsRemoveFile + + sys_traits::ThreadSleep, +>( + sys: &TSys, + from: &Path, + to: &Path, +) -> Result<(), CopyDirRecursiveError> { + if cfg!(target_vendor = "apple") { + if let Some(parent) = to.parent() { + sys.fs_create_dir_all(parent)?; + } + // Try to clone the whole directory + if let Err(err) = sys.fs_clone_file(from, to) { + if !matches!( + err.kind(), + std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::Unsupported + ) { + log::warn!( + "Failed to clone dir {:?} to {:?} via clonefile: {}", + from, + to, + err + ); + } + // clonefile won't overwrite existing files, so if the dir exists + // we need to handle it recursively. + copy_dir_recursive(sys, from, to)?; + } + } else if let Err(e) = deno_npm_cache::hard_link_dir_recursive(sys, from, to) + { + log::debug!("Failed to hard link dir {:?} to {:?}: {}", from, to, e); + copy_dir_recursive(sys, from, to)?; + } + + Ok(()) +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum CopyDirRecursiveError { + #[class(inherit)] + #[error("Creating {path}")] + Creating { + path: PathBuf, + #[source] + #[inherit] + source: Error, + }, + #[class(inherit)] + #[error("Creating {path}")] + Reading { + path: PathBuf, + #[source] + #[inherit] + source: Error, + }, + #[class(inherit)] + #[error("Dir {from} to {to}")] + Dir { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: Box, + }, + #[class(inherit)] + #[error("Copying {from} to {to}")] + Copying { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: Error, + }, + #[class(inherit)] + #[error(transparent)] + Other(#[from] Error), } /// Copies a directory to another directory. /// /// Note: Does not handle symlinks. -pub fn copy_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { - std::fs::create_dir_all(to) - .with_context(|| format!("Creating {}", to.display()))?; - let read_dir = std::fs::read_dir(from) - .with_context(|| format!("Reading {}", from.display()))?; - - for entry in read_dir { - let entry = entry?; - let file_type = entry.file_type()?; - let new_from = from.join(entry.file_name()); - let new_to = to.join(entry.file_name()); - - if file_type.is_dir() { - copy_dir_recursive(&new_from, &new_to).with_context(|| { - format!("Dir {} to {}", new_from.display(), new_to.display()) - })?; - } else if file_type.is_file() { - std::fs::copy(&new_from, &new_to).with_context(|| { - format!("Copying {} to {}", new_from.display(), new_to.display()) - })?; +pub fn copy_dir_recursive< + TSys: sys_traits::FsCopy + + sys_traits::FsCloneFile + + sys_traits::FsCreateDir + + sys_traits::FsHardLink + + sys_traits::FsReadDir, +>( + sys: &TSys, + from: &Path, + to: &Path, +) -> Result<(), CopyDirRecursiveError> { + sys.fs_create_dir_all(to).map_err(|source| { + CopyDirRecursiveError::Creating { + path: to.to_path_buf(), + source, } - } - - Ok(()) -} - -/// Hardlinks the files in one directory to another directory. -/// -/// Note: Does not handle symlinks. -pub fn hard_link_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { - std::fs::create_dir_all(to) - .with_context(|| format!("Creating {}", to.display()))?; - let read_dir = std::fs::read_dir(from) - .with_context(|| format!("Reading {}", from.display()))?; + })?; + let read_dir = + sys + .fs_read_dir(from) + .map_err(|source| CopyDirRecursiveError::Reading { + path: from.to_path_buf(), + source, + })?; for entry in read_dir { let entry = entry?; @@ -481,69 +275,32 @@ pub fn hard_link_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { let new_to = to.join(entry.file_name()); if file_type.is_dir() { - hard_link_dir_recursive(&new_from, &new_to).with_context(|| { - format!("Dir {} to {}", new_from.display(), new_to.display()) - })?; - } else if file_type.is_file() { - // note: chance for race conditions here between attempting to create, - // then removing, then attempting to create. There doesn't seem to be - // a way to hard link with overwriting in Rust, but maybe there is some - // way with platform specific code. The workaround here is to handle - // scenarios where something else might create or remove files. - if let Err(err) = std::fs::hard_link(&new_from, &new_to) { - if err.kind() == ErrorKind::AlreadyExists { - if let Err(err) = std::fs::remove_file(&new_to) { - if err.kind() == ErrorKind::NotFound { - // Assume another process/thread created this hard link to the file we are wanting - // to remove then sleep a little bit to let the other process/thread move ahead - // faster to reduce contention. - std::thread::sleep(Duration::from_millis(10)); - } else { - return Err(err).with_context(|| { - format!( - "Removing file to hard link {} to {}", - new_from.display(), - new_to.display() - ) - }); - } - } - - // Always attempt to recreate the hardlink. In contention scenarios, the other process - // might have been killed or exited after removing the file, but before creating the hardlink - if let Err(err) = std::fs::hard_link(&new_from, &new_to) { - // Assume another process/thread created this hard link to the file we are wanting - // to now create then sleep a little bit to let the other process/thread move ahead - // faster to reduce contention. - if err.kind() == ErrorKind::AlreadyExists { - std::thread::sleep(Duration::from_millis(10)); - } else { - return Err(err).with_context(|| { - format!( - "Hard linking {} to {}", - new_from.display(), - new_to.display() - ) - }); - } - } - } else { - return Err(err).with_context(|| { - format!( - "Hard linking {} to {}", - new_from.display(), - new_to.display() - ) - }); + copy_dir_recursive(sys, &new_from, &new_to).map_err(|source| { + CopyDirRecursiveError::Dir { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: Box::new(source), } - } + })?; + } else if file_type.is_file() { + sys.fs_copy(&new_from, &new_to).map_err(|source| { + CopyDirRecursiveError::Copying { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source, + } + })?; } } Ok(()) } -pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> { +pub fn symlink_dir( + sys: &TSys, + oldpath: &Path, + newpath: &Path, +) -> Result<(), Error> { let err_mapper = |err: Error, kind: Option| { Error::new( kind.unwrap_or_else(|| err.kind()), @@ -555,26 +312,18 @@ pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> { ), ) }; - #[cfg(unix)] - { - use std::os::unix::fs::symlink; - symlink(oldpath, newpath).map_err(|e| err_mapper(e, None))?; - } - #[cfg(not(unix))] - { - use std::os::windows::fs::symlink_dir; - symlink_dir(oldpath, newpath).map_err(|err| { - if let Some(code) = err.raw_os_error() { - if code as u32 == winapi::shared::winerror::ERROR_PRIVILEGE_NOT_HELD - || code as u32 == winapi::shared::winerror::ERROR_INVALID_FUNCTION - { - return err_mapper(err, Some(ErrorKind::PermissionDenied)); - } + + sys.fs_symlink_dir(oldpath, newpath).map_err(|err| { + #[cfg(windows)] + if let Some(code) = err.raw_os_error() { + if code as u32 == winapi::shared::winerror::ERROR_PRIVILEGE_NOT_HELD + || code as u32 == winapi::shared::winerror::ERROR_INVALID_FUNCTION + { + return err_mapper(err, Some(ErrorKind::PermissionDenied)); } - err_mapper(err, None) - })?; - } - Ok(()) + } + err_mapper(err, None) + }) } /// Gets the total size (in bytes) of a directory. @@ -756,7 +505,6 @@ pub fn specifier_from_file_path( #[cfg(test)] mod tests { - use super::*; use deno_core::futures; use deno_core::parking_lot::Mutex; use deno_path_util::normalize_path; @@ -765,6 +513,8 @@ mod tests { use test_util::TempDir; use tokio::sync::Notify; + use super::*; + #[test] fn test_normalize_path() { assert_eq!(normalize_path(Path::new("a/../b")), PathBuf::from("b")); diff --git a/cli/util/logger.rs b/cli/util/logger.rs index 783f8a5f68..2bd4760ebd 100644 --- a/cli/util/logger.rs +++ b/cli/util/logger.rs @@ -1,9 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::io::Write; -use super::draw_thread::DrawThread; use deno_telemetry::OtelConfig; use deno_telemetry::OtelConsoleConfig; -use std::io::Write; + +use super::draw_thread::DrawThread; struct CliLogger { otel_console_config: OtelConsoleConfig, diff --git a/cli/util/mod.rs b/cli/util/mod.rs index f81a74c449..0578ecb423 100644 --- a/cli/util/mod.rs +++ b/cli/util/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Note: Only add code in this folder that has no application specific logic pub mod archive; diff --git a/cli/util/path.rs b/cli/util/path.rs index de72843406..90b2df6a3c 100644 --- a/cli/util/path.rs +++ b/cli/util/path.rs @@ -1,7 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; -use std::fmt::Write; use std::path::Path; use std::path::PathBuf; @@ -52,19 +51,6 @@ pub fn get_extension(file_path: &Path) -> Option { .map(|e| e.to_lowercase()); } -pub fn get_atomic_file_path(file_path: &Path) -> PathBuf { - let rand = gen_rand_path_component(); - let extension = format!("{rand}.tmp"); - file_path.with_extension(extension) -} - -fn gen_rand_path_component() -> String { - (0..4).fold(String::with_capacity(8), |mut output, _| { - write!(&mut output, "{:02x}", rand::random::()).unwrap(); - output - }) -} - /// TypeScript figures out the type of file based on the extension, but we take /// other factors into account like the file headers. The hack here is to map the /// specifier passed to TypeScript to a new specifier with the file extension. diff --git a/cli/util/progress_bar/mod.rs b/cli/util/progress_bar/mod.rs index 85be056d84..3b5bb20e55 100644 --- a/cli/util/progress_bar/mod.rs +++ b/cli/util/progress_bar/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::atomic::AtomicU64; use std::sync::atomic::Ordering; @@ -8,15 +8,13 @@ use std::time::Instant; use deno_core::parking_lot::Mutex; use deno_runtime::ops::tty::ConsoleSize; -use crate::colors; - use self::renderer::ProgressBarRenderer; use self::renderer::ProgressData; use self::renderer::ProgressDataDisplayEntry; - use super::draw_thread::DrawThread; use super::draw_thread::DrawThreadGuard; use super::draw_thread::DrawThreadRenderer; +use crate::colors; mod renderer; diff --git a/cli/util/progress_bar/renderer.rs b/cli/util/progress_bar/renderer.rs index db3d37140f..3498c7ae0f 100644 --- a/cli/util/progress_bar/renderer.rs +++ b/cli/util/progress_bar/renderer.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Write; use std::sync::atomic::AtomicUsize; @@ -7,9 +7,8 @@ use std::time::Duration; use deno_terminal::colors; -use crate::util::display::human_download_size; - use super::ProgressMessagePrompt; +use crate::util::display::human_download_size; #[derive(Clone)] pub struct ProgressDataDisplayEntry { @@ -224,11 +223,13 @@ fn get_elapsed_text(elapsed: Duration) -> String { #[cfg(test)] mod test { - use super::*; - use pretty_assertions::assert_eq; use std::time::Duration; + + use pretty_assertions::assert_eq; use test_util::assert_contains; + use super::*; + #[test] fn should_get_elapsed_text() { assert_eq!(get_elapsed_text(Duration::from_secs(1)), "[00:01]"); diff --git a/cli/util/result.rs b/cli/util/result.rs index 3203d04eb7..0c1a75b1ce 100644 --- a/cli/util/result.rs +++ b/cli/util/result.rs @@ -1,6 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::convert::Infallible; +use std::fmt::Debug; +use std::fmt::Display; + +use deno_core::error::AnyError; +use deno_core::error::CoreError; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; pub trait InfallibleResultExt { fn unwrap_infallible(self) -> T; @@ -14,3 +21,23 @@ impl InfallibleResultExt for Result { } } } + +pub fn any_and_jserrorbox_downcast_ref< + E: Display + Debug + Send + Sync + 'static, +>( + err: &AnyError, +) -> Option<&E> { + err + .downcast_ref::() + .or_else(|| { + err + .downcast_ref::() + .and_then(|e| e.as_any().downcast_ref::()) + }) + .or_else(|| { + err.downcast_ref::().and_then(|e| match e { + CoreError::JsNative(e) => e.as_any().downcast_ref::(), + _ => None, + }) + }) +} diff --git a/cli/util/retry.rs b/cli/util/retry.rs index a8febe60de..ac36a380ed 100644 --- a/cli/util/retry.rs +++ b/cli/util/retry.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::future::Future; use std::time::Duration; diff --git a/cli/util/sync/async_flag.rs b/cli/util/sync/async_flag.rs index 2bdff63c04..a9ca46002d 100644 --- a/cli/util/sync/async_flag.rs +++ b/cli/util/sync/async_flag.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use tokio_util::sync::CancellationToken; diff --git a/cli/util/sync/mod.rs b/cli/util/sync/mod.rs index c3b2a315b0..717dcd768a 100644 --- a/cli/util/sync/mod.rs +++ b/cli/util/sync/mod.rs @@ -1,11 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod async_flag; -mod sync_read_async_write_lock; mod task_queue; pub use async_flag::AsyncFlag; pub use deno_core::unsync::sync::AtomicFlag; -pub use sync_read_async_write_lock::SyncReadAsyncWriteLock; pub use task_queue::TaskQueue; pub use task_queue::TaskQueuePermit; diff --git a/cli/util/sync/sync_read_async_write_lock.rs b/cli/util/sync/sync_read_async_write_lock.rs deleted file mode 100644 index 8bd211aa72..0000000000 --- a/cli/util/sync/sync_read_async_write_lock.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_core::parking_lot::RwLock; -use deno_core::parking_lot::RwLockReadGuard; -use deno_core::parking_lot::RwLockWriteGuard; - -use super::TaskQueue; -use super::TaskQueuePermit; - -/// A lock that can be read synchronously at any time (including when -/// being written to), but must write asynchronously. -pub struct SyncReadAsyncWriteLockWriteGuard<'a, T: Send + Sync> { - _update_permit: TaskQueuePermit<'a>, - data: &'a RwLock, -} - -impl<'a, T: Send + Sync> SyncReadAsyncWriteLockWriteGuard<'a, T> { - pub fn read(&self) -> RwLockReadGuard<'_, T> { - self.data.read() - } - - /// Warning: Only `write()` with data you created within this - /// write this `SyncReadAsyncWriteLockWriteGuard`. - /// - /// ```rs - /// let mut data = lock.write().await; - /// - /// let mut data = data.read().clone(); - /// data.value = 2; - /// *data.write() = data; - /// ``` - pub fn write(&self) -> RwLockWriteGuard<'_, T> { - self.data.write() - } -} - -/// A lock that can only be -pub struct SyncReadAsyncWriteLock { - data: RwLock, - update_queue: TaskQueue, -} - -impl SyncReadAsyncWriteLock { - pub fn new(data: T) -> Self { - Self { - data: RwLock::new(data), - update_queue: TaskQueue::default(), - } - } - - pub fn read(&self) -> RwLockReadGuard<'_, T> { - self.data.read() - } - - pub async fn acquire(&self) -> SyncReadAsyncWriteLockWriteGuard<'_, T> { - let update_permit = self.update_queue.acquire().await; - SyncReadAsyncWriteLockWriteGuard { - _update_permit: update_permit, - data: &self.data, - } - } -} diff --git a/cli/util/sync/task_queue.rs b/cli/util/sync/task_queue.rs index 6ef747e1ae..4c5abfab3b 100644 --- a/cli/util/sync/task_queue.rs +++ b/cli/util/sync/task_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::LinkedList; use std::sync::Arc; @@ -146,9 +146,10 @@ impl<'a> Future for TaskQueuePermitAcquireFuture<'a> { #[cfg(test)] mod test { + use std::sync::Arc; + use deno_core::futures; use deno_core::parking_lot::Mutex; - use std::sync::Arc; use super::*; diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs index 06b311e150..b5a288be7b 100644 --- a/cli/util/text_encoding.rs +++ b/cli/util/text_encoding.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::ops::Range; @@ -140,23 +140,23 @@ mod tests { #[test] fn test_source_map_from_code() { let to_string = - |bytes: Vec| -> String { String::from_utf8(bytes).unwrap() }; + |bytes: Vec| -> String { String::from_utf8(bytes.to_vec()).unwrap() }; assert_eq!( source_map_from_code( - b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=", + b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=" ).map(to_string), Some("testingtesting".to_string()) ); assert_eq!( source_map_from_code( - b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n \n", + b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n \n" ).map(to_string), Some("testingtesting".to_string()) ); assert_eq!( source_map_from_code( - b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n test\n", - ), + b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n test\n" + ).map(to_string), None ); assert_eq!( @@ -164,7 +164,7 @@ mod tests { b"\"use strict\"; throw new Error(\"Hello world!\"); -//# sourceMappingURL=data:application/json;base64,{", +//# sourceMappingURL=data:application/json;base64,{" ), None ); diff --git a/cli/util/unix.rs b/cli/util/unix.rs index 2c76a54c38..4b6ac1f320 100644 --- a/cli/util/unix.rs +++ b/cli/util/unix.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// Raise soft file descriptor limit to hard file descriptor limit. /// This is the difference between `ulimit -n` and `ulimit -n -H`. diff --git a/cli/util/v8.rs b/cli/util/v8.rs index 6e690e6f30..403d1955a5 100644 --- a/cli/util/v8.rs +++ b/cli/util/v8.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod convert; diff --git a/cli/util/v8/convert.rs b/cli/util/v8/convert.rs index 28107d9010..4d0cb732a8 100644 --- a/cli/util/v8/convert.rs +++ b/cli/util/v8/convert.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::v8; use deno_core::FromV8; diff --git a/cli/util/windows.rs b/cli/util/windows.rs index 37e78a5d08..7f516efbeb 100644 --- a/cli/util/windows.rs +++ b/cli/util/windows.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// Ensures that stdin, stdout, and stderr are open and have valid HANDLEs /// associated with them. There are many places where a `std::fs::File` is @@ -8,6 +8,7 @@ pub fn ensure_stdio_open() { // SAFETY: winapi calls unsafe { use std::mem::size_of; + use winapi::shared::minwindef::DWORD; use winapi::shared::minwindef::FALSE; use winapi::shared::minwindef::TRUE; diff --git a/cli/version.rs b/cli/version.rs index 0cdb32102a..fb0fe98b29 100644 --- a/cli/version.rs +++ b/cli/version.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use once_cell::sync::Lazy; diff --git a/cli/worker.rs b/cli/worker.rs index 0bbc27b29f..d9cdbd3fb0 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; use std::path::PathBuf; @@ -8,6 +8,7 @@ use std::sync::Arc; use deno_ast::ModuleSpecifier; use deno_core::anyhow::bail; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::futures::FutureExt; use deno_core::url::Url; use deno_core::v8; @@ -17,14 +18,14 @@ use deno_core::FeatureChecker; use deno_core::ModuleLoader; use deno_core::PollEventLoopOptions; use deno_core::SharedArrayBufferStore; +use deno_error::JsErrorBox; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::code_cache; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_fs; use deno_runtime::deno_node::NodeExtInitServices; use deno_runtime::deno_node::NodeRequireLoader; use deno_runtime::deno_node::NodeRequireLoaderRc; -use deno_runtime::deno_node::NodeResolver; -use deno_runtime::deno_node::PackageJsonResolver; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_web::BlobStore; @@ -52,8 +53,12 @@ use crate::args::CliLockfile; use crate::args::DenoSubcommand; use crate::args::NpmCachingStrategy; use crate::args::StorageKeyResolver; -use crate::errors; +use crate::node::CliNodeResolver; +use crate::node::CliPackageJsonResolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; +use crate::sys::CliSys; use crate::util::checksum; use crate::util::file_watcher::WatcherCommunicator; use crate::util::file_watcher::WatcherRestartMode; @@ -79,9 +84,9 @@ pub trait ModuleLoaderFactory: Send + Sync { #[async_trait::async_trait(?Send)] pub trait HmrRunner: Send + Sync { - async fn start(&mut self) -> Result<(), AnyError>; - async fn stop(&mut self) -> Result<(), AnyError>; - async fn run(&mut self) -> Result<(), AnyError>; + async fn start(&mut self) -> Result<(), CoreError>; + async fn stop(&mut self) -> Result<(), CoreError>; + async fn run(&mut self) -> Result<(), CoreError>; } pub trait CliCodeCache: code_cache::CodeCache { @@ -145,13 +150,15 @@ struct SharedWorkerState { maybe_inspector_server: Option>, maybe_lockfile: Option>, module_loader_factory: Box, - node_resolver: Arc, - npm_resolver: Arc, - pkg_json_resolver: Arc, + node_resolver: Arc, + npm_installer: Option>, + npm_resolver: CliNpmResolver, + pkg_json_resolver: Arc, root_cert_store_provider: Arc, root_permissions: PermissionsContainer, shared_array_buffer_store: SharedArrayBufferStore, storage_key_resolver: StorageKeyResolver, + sys: CliSys, options: CliMainWorkerOptions, subcommand: DenoSubcommand, otel_config: OtelConfig, @@ -162,17 +169,17 @@ impl SharedWorkerState { pub fn create_node_init_services( &self, node_require_loader: NodeRequireLoaderRc, - ) -> NodeExtInitServices { + ) -> NodeExtInitServices { NodeExtInitServices { node_require_loader, node_resolver: self.node_resolver.clone(), - npm_resolver: self.npm_resolver.clone().into_npm_pkg_folder_resolver(), pkg_json_resolver: self.pkg_json_resolver.clone(), + sys: self.sys.clone(), } } pub fn npm_process_state_provider(&self) -> NpmProcessStateProviderRc { - self.npm_resolver.clone().into_process_state_provider() + crate::npm::create_npm_process_state_provider(&self.npm_resolver) } } @@ -192,7 +199,7 @@ impl CliMainWorker { Ok(()) } - pub async fn run(&mut self) -> Result { + pub async fn run(&mut self) -> Result { let mut maybe_coverage_collector = self.maybe_setup_coverage_collector().await?; let mut maybe_hmr_runner = self.maybe_setup_hmr_runner().await?; @@ -213,7 +220,7 @@ impl CliMainWorker { let result; select! { hmr_result = hmr_future => { - result = hmr_result; + result = hmr_result.map_err(Into::into); }, event_loop_result = event_loop_future => { result = event_loop_result; @@ -328,12 +335,12 @@ impl CliMainWorker { executor.execute().await } - pub async fn execute_main_module(&mut self) -> Result<(), AnyError> { + pub async fn execute_main_module(&mut self) -> Result<(), CoreError> { let id = self.worker.preload_main_module(&self.main_module).await?; self.worker.evaluate_module(id).await } - pub async fn execute_side_module(&mut self) -> Result<(), AnyError> { + pub async fn execute_side_module(&mut self) -> Result<(), CoreError> { let id = self.worker.preload_side_module(&self.main_module).await?; self.worker.evaluate_module(id).await } @@ -390,7 +397,7 @@ impl CliMainWorker { &mut self, name: &'static str, source_code: &'static str, - ) -> Result, AnyError> { + ) -> Result, CoreError> { self.worker.js_runtime.execute_script(name, source_code) } } @@ -418,12 +425,14 @@ impl CliMainWorkerFactory { maybe_inspector_server: Option>, maybe_lockfile: Option>, module_loader_factory: Box, - node_resolver: Arc, - npm_resolver: Arc, - pkg_json_resolver: Arc, + node_resolver: Arc, + npm_installer: Option>, + npm_resolver: CliNpmResolver, + pkg_json_resolver: Arc, root_cert_store_provider: Arc, root_permissions: PermissionsContainer, storage_key_resolver: StorageKeyResolver, + sys: CliSys, subcommand: DenoSubcommand, options: CliMainWorkerOptions, otel_config: OtelConfig, @@ -442,12 +451,14 @@ impl CliMainWorkerFactory { maybe_lockfile, module_loader_factory, node_resolver, + npm_installer, npm_resolver, pkg_json_resolver, root_cert_store_provider, root_permissions, shared_array_buffer_store: Default::default(), storage_key_resolver, + sys, options, subcommand, otel_config, @@ -460,7 +471,7 @@ impl CliMainWorkerFactory { &self, mode: WorkerExecutionMode, main_module: ModuleSpecifier, - ) -> Result { + ) -> Result { self .create_custom_worker( mode, @@ -479,7 +490,7 @@ impl CliMainWorkerFactory { permissions: PermissionsContainer, custom_extensions: Vec, stdio: deno_runtime::deno_io::Stdio, - ) -> Result { + ) -> Result { let shared = &self.shared; let CreateModuleLoaderResult { module_loader, @@ -490,34 +501,33 @@ impl CliMainWorkerFactory { let main_module = if let Ok(package_ref) = NpmPackageReqReference::from_specifier(&main_module) { - if let Some(npm_resolver) = shared.npm_resolver.as_managed() { + if let Some(npm_installer) = &shared.npm_installer { let reqs = &[package_ref.req().clone()]; - npm_resolver + npm_installer .add_package_reqs( reqs, if matches!( shared.default_npm_caching_strategy, NpmCachingStrategy::Lazy ) { - crate::npm::PackageCaching::Only(reqs.into()) + PackageCaching::Only(reqs.into()) } else { - crate::npm::PackageCaching::All + PackageCaching::All }, ) .await?; } // use a fake referrer that can be used to discover the package.json if necessary - let referrer = - ModuleSpecifier::from_directory_path(self.shared.fs.cwd()?) - .unwrap() - .join("package.json")?; + let referrer = ModuleSpecifier::from_directory_path( + self.shared.fs.cwd().map_err(JsErrorBox::from_err)?, + ) + .unwrap() + .join("package.json")?; let package_folder = shared .npm_resolver - .resolve_pkg_folder_from_deno_module_req( - package_ref.req(), - &referrer, - )?; + .resolve_pkg_folder_from_deno_module_req(package_ref.req(), &referrer) + .map_err(JsErrorBox::from_err)?; let main_module = self .resolve_binary_entrypoint(&package_folder, package_ref.sub_path())?; @@ -612,6 +622,7 @@ impl CliMainWorkerFactory { serve_port: shared.options.serve_port, serve_host: shared.options.serve_host.clone(), otel_config: shared.otel_config.clone(), + close_on_idle: true, }, extensions: custom_extensions, startup_snapshot: crate::js::deno_isolate_init(), @@ -627,7 +638,6 @@ impl CliMainWorkerFactory { should_break_on_first_statement: shared.options.inspect_brk, should_wait_for_inspector_session: shared.options.inspect_wait, strace_ops: shared.options.strace_ops.clone(), - get_error_class_fn: Some(&errors::get_error_class_name), cache_storage_dir, origin_storage_dir, stdio, @@ -655,7 +665,10 @@ impl CliMainWorkerFactory { "40_test_common.js", "40_test.js", "40_bench.js", - "40_jupyter.js" + "40_jupyter.js", + // TODO(bartlomieju): probably shouldn't include these files here? + "40_lint_selector.js", + "40_lint.js" ); } @@ -812,6 +825,7 @@ fn create_web_worker_callback( serve_port: shared.options.serve_port, serve_host: shared.options.serve_host.clone(), otel_config: shared.otel_config.clone(), + close_on_idle: args.close_on_idle, }, extensions: vec![], startup_snapshot: crate::js::deno_isolate_init(), @@ -824,7 +838,6 @@ fn create_web_worker_callback( create_web_worker_cb, format_js_error_fn: Some(Arc::new(format_js_error)), worker_type: args.worker_type, - get_error_class_fn: Some(&errors::get_error_class_name), stdio: stdio.clone(), cache_storage_dir, strace_ops: shared.options.strace_ops.clone(), @@ -842,7 +855,7 @@ fn create_web_worker_callback( /// Instead probe for the total memory on the system and use it instead /// as a default. pub fn create_isolate_create_params() -> Option { - let maybe_mem_info = deno_runtime::sys_info::mem_info(); + let maybe_mem_info = deno_runtime::deno_os::sys_info::mem_info(); maybe_mem_info.map(|mem_info| { v8::CreateParams::default() .heap_limits_from_system_memory(mem_info.total, 0) @@ -853,25 +866,31 @@ pub fn create_isolate_create_params() -> Option { #[allow(clippy::print_stderr)] #[cfg(test)] mod tests { - use super::*; use deno_core::resolve_path; use deno_core::FsModuleLoader; use deno_fs::RealFs; use deno_runtime::deno_permissions::Permissions; use deno_runtime::permissions::RuntimePermissionDescriptorParser; + use super::*; + fn create_test_worker() -> MainWorker { let main_module = resolve_path("./hello.js", &std::env::current_dir().unwrap()).unwrap(); let fs = Arc::new(RealFs); - let permission_desc_parser = - Arc::new(RuntimePermissionDescriptorParser::new(fs.clone())); + let permission_desc_parser = Arc::new( + RuntimePermissionDescriptorParser::new(crate::sys::CliSys::default()), + ); let options = WorkerOptions { startup_snapshot: crate::js::deno_isolate_init(), ..Default::default() }; - MainWorker::bootstrap_from_options( + MainWorker::bootstrap_from_options::< + DenoInNpmPackageChecker, + CliNpmResolver, + CliSys, + >( main_module, WorkerServiceOptions { module_loader: Rc::new(FsModuleLoader), diff --git a/ext/broadcast_channel/01_broadcast_channel.js b/ext/broadcast_channel/01_broadcast_channel.js index 1a4bb36474..a71ae465cf 100644 --- a/ext/broadcast_channel/01_broadcast_channel.js +++ b/ext/broadcast_channel/01_broadcast_channel.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index 5b238aad25..0e1e0c3fbd 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_broadcast_channel" -version = "0.176.0" +version = "0.179.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] async-trait.workspace = true deno_core.workspace = true +deno_error.workspace = true thiserror.workspace = true tokio.workspace = true uuid.workspace = true diff --git a/ext/broadcast_channel/in_memory_broadcast_channel.rs b/ext/broadcast_channel/in_memory_broadcast_channel.rs index 61dc68e17d..33803c74f4 100644 --- a/ext/broadcast_channel/in_memory_broadcast_channel.rs +++ b/ext/broadcast_channel/in_memory_broadcast_channel.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/ext/broadcast_channel/lib.deno_broadcast_channel.d.ts b/ext/broadcast_channel/lib.deno_broadcast_channel.d.ts index 339765ec9c..a43f8609b9 100644 --- a/ext/broadcast_channel/lib.deno_broadcast_channel.d.ts +++ b/ext/broadcast_channel/lib.deno_broadcast_channel.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/broadcast_channel/lib.rs b/ext/broadcast_channel/lib.rs index c1de118a36..ae709c674c 100644 --- a/ext/broadcast_channel/lib.rs +++ b/ext/broadcast_channel/lib.rs @@ -1,10 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod in_memory_broadcast_channel; -pub use in_memory_broadcast_channel::InMemoryBroadcastChannel; -pub use in_memory_broadcast_channel::InMemoryBroadcastChannelResource; - use std::cell::RefCell; use std::path::PathBuf; use std::rc::Rc; @@ -15,23 +12,34 @@ use deno_core::JsBuffer; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; +pub use in_memory_broadcast_channel::InMemoryBroadcastChannel; +pub use in_memory_broadcast_channel::InMemoryBroadcastChannelResource; use tokio::sync::broadcast::error::SendError as BroadcastSendError; use tokio::sync::mpsc::error::SendError as MpscSendError; pub const UNSTABLE_FEATURE_NAME: &str = "broadcast-channel"; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BroadcastChannelError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(generic)] #[error(transparent)] MPSCSendError(MpscSendError>), + #[class(generic)] #[error(transparent)] BroadcastSendError( BroadcastSendError>, ), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[inherit] JsErrorBox), } impl From> @@ -101,10 +109,7 @@ pub fn op_broadcast_unsubscribe( where BC: BroadcastChannel + 'static, { - let resource = state - .resource_table - .get::(rid) - .map_err(BroadcastChannelError::Resource)?; + let resource = state.resource_table.get::(rid)?; let bc = state.borrow::(); bc.unsubscribe(&resource) } @@ -119,11 +124,7 @@ pub async fn op_broadcast_send( where BC: BroadcastChannel + 'static, { - let resource = state - .borrow() - .resource_table - .get::(rid) - .map_err(BroadcastChannelError::Resource)?; + let resource = state.borrow().resource_table.get::(rid)?; let bc = state.borrow().borrow::().clone(); bc.send(&resource, name, buf.to_vec()).await } @@ -137,11 +138,7 @@ pub async fn op_broadcast_recv( where BC: BroadcastChannel + 'static, { - let resource = state - .borrow() - .resource_table - .get::(rid) - .map_err(BroadcastChannelError::Resource)?; + let resource = state.borrow().resource_table.get::(rid)?; let bc = state.borrow().borrow::().clone(); bc.recv(&resource).await } diff --git a/ext/cache/01_cache.js b/ext/cache/01_cache.js index 269261f401..55aac3a4d8 100644 --- a/ext/cache/01_cache.js +++ b/ext/cache/01_cache.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { op_cache_delete, diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index d03779d364..18fbe23a23 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_cache" -version = "0.114.0" +version = "0.117.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] async-trait.workspace = true deno_core.workspace = true +deno_error.workspace = true rusqlite.workspace = true serde.workspace = true sha2.workspace = true diff --git a/ext/cache/lib.deno_cache.d.ts b/ext/cache/lib.deno_cache.d.ts index f9e1818482..9995af7f66 100644 --- a/ext/cache/lib.deno_cache.d.ts +++ b/ext/cache/lib.deno_cache.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-var diff --git a/ext/cache/lib.rs b/ext/cache/lib.rs index 524d4cea05..d3bfe23def 100644 --- a/ext/cache/lib.rs +++ b/ext/cache/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::path::PathBuf; @@ -6,7 +6,6 @@ use std::rc::Rc; use std::sync::Arc; use async_trait::async_trait; -use deno_core::error::type_error; use deno_core::op2; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; @@ -14,22 +13,38 @@ use deno_core::ByteString; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; mod sqlite; pub use sqlite::SqliteBackedCache; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CacheError { + #[class(type)] + #[error("CacheStorage is not available in this context")] + ContextUnsupported, + #[class(generic)] #[error(transparent)] Sqlite(#[from] rusqlite::Error), + #[class(generic)] #[error(transparent)] JoinError(#[from] tokio::task::JoinError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(JsErrorBox), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), + #[class(generic)] + #[error("Failed to create cache storage directory {}", .dir.display())] + CacheStorageDirectory { + dir: PathBuf, + #[source] + source: std::io::Error, + }, } #[derive(Clone)] @@ -237,9 +252,7 @@ where state.put(cache); Ok(state.borrow::().clone()) } else { - Err(CacheError::Other(type_error( - "CacheStorage is not available in this context", - ))) + Err(CacheError::ContextUnsupported) } } diff --git a/ext/cache/sqlite.rs b/ext/cache/sqlite.rs index 469e3e51d6..6587a52bac 100644 --- a/ext/cache/sqlite.rs +++ b/ext/cache/sqlite.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::PathBuf; use std::pin::Pin; @@ -8,8 +8,6 @@ use std::time::SystemTime; use std::time::UNIX_EPOCH; use async_trait::async_trait; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_core::futures::future::poll_fn; use deno_core::parking_lot::Mutex; use deno_core::unsync::spawn_blocking; @@ -45,14 +43,12 @@ pub struct SqliteBackedCache { impl SqliteBackedCache { pub fn new(cache_storage_dir: PathBuf) -> Result { { - std::fs::create_dir_all(&cache_storage_dir) - .with_context(|| { - format!( - "Failed to create cache storage directory {}", - cache_storage_dir.display() - ) - }) - .map_err(CacheError::Other)?; + std::fs::create_dir_all(&cache_storage_dir).map_err(|source| { + CacheError::CacheStorageDirectory { + dir: cache_storage_dir.clone(), + source, + } + })?; let path = cache_storage_dir.join("cache_metadata.db"); let connection = rusqlite::Connection::open(&path).unwrap_or_else(|_| { panic!("failed to open cache db at {}", path.display()) @@ -385,7 +381,10 @@ impl CacheResponseResource { } } - async fn read(self: Rc, data: &mut [u8]) -> Result { + async fn read( + self: Rc, + data: &mut [u8], + ) -> Result { let resource = deno_core::RcRef::map(&self, |r| &r.file); let mut file = resource.borrow_mut().await; let nread = file.read(data).await?; diff --git a/ext/canvas/01_image.js b/ext/canvas/01_image.js index 3ea72db6ac..4b39c041b4 100644 --- a/ext/canvas/01_image.js +++ b/ext/canvas/01_image.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { internals, primordials } from "ext:core/mod.js"; import { op_image_decode_png, op_image_process } from "ext:core/ops"; diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index c851b7724a..e5c70b0054 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_canvas" -version = "0.51.0" +version = "0.54.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_webgpu.workspace = true image = { version = "0.24.7", default-features = false, features = ["png"] } serde = { workspace = true, features = ["derive"] } diff --git a/ext/canvas/lib.deno_canvas.d.ts b/ext/canvas/lib.deno_canvas.d.ts index c695ba5cd8..84d3cbdd42 100644 --- a/ext/canvas/lib.deno_canvas.d.ts +++ b/ext/canvas/lib.deno_canvas.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-var diff --git a/ext/canvas/lib.rs b/ext/canvas/lib.rs index defb288ac9..91b4e44afe 100644 --- a/ext/canvas/lib.rs +++ b/ext/canvas/lib.rs @@ -1,4 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::path::PathBuf; use deno_core::op2; use deno_core::ToJsBuffer; @@ -9,12 +11,13 @@ use image::Pixel; use image::RgbaImage; use serde::Deserialize; use serde::Serialize; -use std::path::PathBuf; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CanvasError { + #[class(type)] #[error("Color type '{0:?}' not supported")] UnsupportedColorType(ColorType), + #[class(generic)] #[error(transparent)] Image(#[from] image::ImageError), } diff --git a/ext/console/01_console.js b/ext/console/01_console.js index 3803492b90..a85faaccf1 100644 --- a/ext/console/01_console.js +++ b/ext/console/01_console.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/console/Cargo.toml b/ext/console/Cargo.toml index 4a26917933..24cdb040a7 100644 --- a/ext/console/Cargo.toml +++ b/ext/console/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_console" -version = "0.182.0" +version = "0.185.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/console/internal.d.ts b/ext/console/internal.d.ts index 5f9627cf56..e90c9b6f11 100644 --- a/ext/console/internal.d.ts +++ b/ext/console/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/ext/console/lib.deno_console.d.ts b/ext/console/lib.deno_console.d.ts index 0c73972d36..54bbcab892 100644 --- a/ext/console/lib.deno_console.d.ts +++ b/ext/console/lib.deno_console.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any diff --git a/ext/console/lib.rs b/ext/console/lib.rs index 87fc8327da..48a75329af 100644 --- a/ext/console/lib.rs +++ b/ext/console/lib.rs @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::path::PathBuf; + use deno_core::op2; use deno_core::v8; -use std::path::PathBuf; deno_core::extension!( deno_console, diff --git a/ext/cron/01_cron.ts b/ext/cron/01_cron.ts index b5c556a677..db193053d8 100644 --- a/ext/cron/01_cron.ts +++ b/ext/cron/01_cron.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; const { diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index d8f2d949f7..7b41593841 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_cron" -version = "0.62.0" +version = "0.65.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -18,6 +18,7 @@ anyhow.workspace = true async-trait.workspace = true chrono = { workspace = true, features = ["now"] } deno_core.workspace = true +deno_error.workspace = true saffron.workspace = true thiserror.workspace = true tokio.workspace = true diff --git a/ext/cron/interface.rs b/ext/cron/interface.rs index a19525cc4e..f92d601796 100644 --- a/ext/cron/interface.rs +++ b/ext/cron/interface.rs @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use async_trait::async_trait; use crate::CronError; -use async_trait::async_trait; pub trait CronHandler { type EH: CronHandle + 'static; diff --git a/ext/cron/lib.rs b/ext/cron/lib.rs index feffb5e511..b4f4938b5e 100644 --- a/ext/cron/lib.rs +++ b/ext/cron/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod interface; pub mod local; @@ -7,12 +7,14 @@ use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; -pub use crate::interface::*; -use deno_core::error::get_custom_error_class; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; + +pub use crate::interface::*; pub const UNSTABLE_FEATURE_NAME: &str = "cron"; @@ -46,26 +48,35 @@ impl Resource for CronResource { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CronError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(type)] #[error("Cron name cannot exceed 64 characters: current length {0}")] NameExceeded(usize), + #[class(type)] #[error("Invalid cron name: only alphanumeric characters, whitespace, hyphens, and underscores are allowed")] NameInvalid, + #[class(type)] #[error("Cron with this name already exists")] AlreadyExists, + #[class(type)] #[error("Too many crons")] TooManyCrons, + #[class(type)] #[error("Invalid cron schedule")] InvalidCron, + #[class(type)] #[error("Invalid backoff schedule")] InvalidBackoff, + #[class(generic)] #[error(transparent)] AcquireError(#[from] tokio::sync::AcquireError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(JsErrorBox), } #[op2] @@ -118,7 +129,7 @@ where let resource = match state.resource_table.get::>(rid) { Ok(resource) => resource, Err(err) => { - if get_custom_error_class(&err) == Some("BadResource") { + if err.get_class() == "BadResource" { return Ok(false); } else { return Err(CronError::Resource(err)); diff --git a/ext/cron/local.rs b/ext/cron/local.rs index 1110baadb8..d6213a7e36 100644 --- a/ext/cron/local.rs +++ b/ext/cron/local.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::OnceCell; use std::cell::RefCell; diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 63b1905145..5a9f32cb00 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 63656bf642..c9876105e5 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_crypto" -version = "0.196.0" +version = "0.199.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -23,6 +23,7 @@ const-oid = "0.9.0" ctr = "0.9.1" curve25519-dalek = "4.1.3" deno_core.workspace = true +deno_error.workspace = true deno_web.workspace = true ed448-goldilocks = { version = "0.8.3", features = ["zeroize"] } elliptic-curve = { version = "0.13.1", features = ["std", "pem"] } diff --git a/ext/crypto/decrypt.rs b/ext/crypto/decrypt.rs index 1140475183..766f62d16f 100644 --- a/ext/crypto/decrypt.rs +++ b/ext/crypto/decrypt.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use aes::cipher::block_padding::Pkcs7; use aes::cipher::BlockDecryptMut; @@ -70,26 +70,40 @@ pub enum DecryptAlgorithm { }, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum DecryptError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), + #[class(generic)] #[error(transparent)] Pkcs1(#[from] rsa::pkcs1::Error), + #[class("DOMExceptionOperationError")] #[error("Decryption failed")] Failed, + #[class(type)] #[error("invalid length")] InvalidLength, + #[class(type)] #[error("invalid counter length. Currently supported 32/64/128 bits")] InvalidCounterLength, + #[class(type)] #[error("tag length not equal to 128")] InvalidTagLength, + #[class("DOMExceptionOperationError")] #[error("invalid key or iv")] InvalidKeyOrIv, + #[class("DOMExceptionOperationError")] #[error("tried to decrypt too much data")] TooMuchData, + #[class(type)] #[error("iv length not equal to 12 or 16")] InvalidIvLength, + #[class("DOMExceptionOperationError")] #[error("{0}")] Rsa(rsa::Error), } diff --git a/ext/crypto/ed25519.rs b/ext/crypto/ed25519.rs index da34b7d25d..c56fdc7c62 100644 --- a/ext/crypto/ed25519.rs +++ b/ext/crypto/ed25519.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use base64::prelude::BASE64_URL_SAFE_NO_PAD; use base64::Engine; @@ -13,12 +13,15 @@ use spki::der::asn1::BitString; use spki::der::Decode; use spki::der::Encode; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum Ed25519Error { + #[class("DOMExceptionOperationError")] #[error("Failed to export key")] FailedExport, + #[class(generic)] #[error(transparent)] Der(#[from] rsa::pkcs1::der::Error), + #[class(generic)] #[error(transparent)] KeyRejected(#[from] ring::error::KeyRejected), } diff --git a/ext/crypto/encrypt.rs b/ext/crypto/encrypt.rs index 66b27657f8..d94eb97cfd 100644 --- a/ext/crypto/encrypt.rs +++ b/ext/crypto/encrypt.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use aes::cipher::block_padding::Pkcs7; use aes::cipher::BlockEncryptMut; @@ -71,20 +71,31 @@ pub enum EncryptAlgorithm { }, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum EncryptError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), + #[class(type)] #[error("invalid length")] InvalidLength, + #[class("DOMExceptionOperationError")] #[error("invalid key or iv")] InvalidKeyOrIv, + #[class(type)] #[error("iv length not equal to 12 or 16")] InvalidIvLength, + #[class(type)] #[error("invalid counter length. Currently supported 32/64/128 bits")] InvalidCounterLength, + #[class("DOMExceptionOperationError")] #[error("tried to encrypt too much data")] TooMuchData, + #[class("DOMExceptionOperationError")] #[error("Encryption failed")] Failed, } diff --git a/ext/crypto/export_key.rs b/ext/crypto/export_key.rs index edf0d7239c..c7d59e3cc5 100644 --- a/ext/crypto/export_key.rs +++ b/ext/crypto/export_key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use base64::prelude::BASE64_URL_SAFE_NO_PAD; use base64::Engine; @@ -20,12 +20,19 @@ use spki::AlgorithmIdentifierOwned; use crate::shared::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ExportKeyError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), + #[class(generic)] #[error(transparent)] Der(#[from] spki::der::Error), + #[class("DOMExceptionNotSupportedError")] #[error("Unsupported named curve")] UnsupportedNamedCurve, } diff --git a/ext/crypto/generate_key.rs b/ext/crypto/generate_key.rs index 3c0bd77c22..211084af17 100644 --- a/ext/crypto/generate_key.rs +++ b/ext/crypto/generate_key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::unsync::spawn_blocking; @@ -15,10 +15,16 @@ use serde::Deserialize; use crate::shared::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class("DOMExceptionOperationError")] pub enum GenerateKeyError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), #[error("Bad public exponent")] BadPublicExponent, #[error("Invalid HMAC key length")] diff --git a/ext/crypto/import_key.rs b/ext/crypto/import_key.rs index 3463ca2beb..e9059bbdc6 100644 --- a/ext/crypto/import_key.rs +++ b/ext/crypto/import_key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use base64::Engine; use deno_core::op2; @@ -14,10 +14,16 @@ use spki::der::Decode; use crate::shared::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class("DOMExceptionDataError")] pub enum ImportKeyError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), #[error("invalid modulus")] InvalidModulus, #[error("invalid public exponent")] diff --git a/ext/crypto/key.rs b/ext/crypto/key.rs index 6b3bf26f43..52a035ef01 100644 --- a/ext/crypto/key.rs +++ b/ext/crypto/key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use ring::agreement::Algorithm as RingAlgorithm; use ring::digest; diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts index 827c0224ce..3f4edb9245 100644 --- a/ext/crypto/lib.deno_crypto.d.ts +++ b/ext/crypto/lib.deno_crypto.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-var diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 69dcd1413a..0d6eecb911 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -1,22 +1,22 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::num::NonZeroU32; +use std::path::PathBuf; use aes_kw::KekAes128; use aes_kw::KekAes192; use aes_kw::KekAes256; - use base64::prelude::BASE64_URL_SAFE_NO_PAD; use base64::Engine; -use deno_core::error::not_supported; use deno_core::op2; -use deno_core::ToJsBuffer; - use deno_core::unsync::spawn_blocking; use deno_core::JsBuffer; use deno_core::OpState; -use serde::Deserialize; - +use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use p256::elliptic_curve::sec1::FromEncodedPoint; use p256::pkcs8::DecodePrivateKey; +pub use rand; use rand::rngs::OsRng; use rand::rngs::StdRng; use rand::thread_rng; @@ -41,15 +41,12 @@ use rsa::traits::SignatureScheme; use rsa::Pss; use rsa::RsaPrivateKey; use rsa::RsaPublicKey; +use serde::Deserialize; use sha1::Sha1; use sha2::Digest; use sha2::Sha256; use sha2::Sha384; -use sha2::Sha512; -use std::num::NonZeroU32; -use std::path::PathBuf; - -pub use rand; // Re-export rand +use sha2::Sha512; // Re-export rand mod decrypt; mod ed25519; @@ -132,63 +129,99 @@ deno_core::extension!(deno_crypto, }, ); -#[derive(Debug, thiserror::Error)] -pub enum Error { +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum CryptoError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), + #[class(inherit)] #[error(transparent)] - JoinError(#[from] tokio::task::JoinError), + JoinError( + #[from] + #[inherit] + tokio::task::JoinError, + ), + #[class(generic)] #[error(transparent)] Der(#[from] rsa::pkcs1::der::Error), + #[class(type)] #[error("Missing argument hash")] MissingArgumentHash, + #[class(type)] #[error("Missing argument saltLength")] MissingArgumentSaltLength, + #[class(type)] #[error("unsupported algorithm")] UnsupportedAlgorithm, + #[class(generic)] #[error(transparent)] KeyRejected(#[from] ring::error::KeyRejected), + #[class(generic)] #[error(transparent)] RSA(#[from] rsa::Error), + #[class(generic)] #[error(transparent)] Pkcs1(#[from] rsa::pkcs1::Error), + #[class(generic)] #[error(transparent)] Unspecified(#[from] ring::error::Unspecified), + #[class(type)] #[error("Invalid key format")] InvalidKeyFormat, + #[class(generic)] #[error(transparent)] P256Ecdsa(#[from] p256::ecdsa::Error), + #[class(type)] #[error("Unexpected error decoding private key")] DecodePrivateKey, + #[class(type)] #[error("Missing argument publicKey")] MissingArgumentPublicKey, + #[class(type)] #[error("Missing argument namedCurve")] MissingArgumentNamedCurve, + #[class(type)] #[error("Missing argument info")] MissingArgumentInfo, + #[class("DOMExceptionOperationError")] #[error("The length provided for HKDF is too large")] HKDFLengthTooLarge, + #[class(generic)] #[error(transparent)] Base64Decode(#[from] base64::DecodeError), + #[class(type)] #[error("Data must be multiple of 8 bytes")] DataInvalidSize, + #[class(type)] #[error("Invalid key length")] InvalidKeyLength, + #[class("DOMExceptionOperationError")] #[error("encryption error")] EncryptionError, + #[class("DOMExceptionOperationError")] #[error("decryption error - integrity check failed")] DecryptionError, + #[class("DOMExceptionQuotaExceededError")] #[error("The ArrayBufferView's byte length ({0}) exceeds the number of bytes of entropy available via this API (65536)")] ArrayBufferViewLengthExceeded(usize), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other( + #[from] + #[inherit] + JsErrorBox, + ), } #[op2] #[serde] pub fn op_crypto_base64url_decode( #[string] data: String, -) -> Result { +) -> Result { let data: Vec = BASE64_URL_SAFE_NO_PAD.decode(data)?; Ok(data.into()) } @@ -204,9 +237,9 @@ pub fn op_crypto_base64url_encode(#[buffer] data: JsBuffer) -> String { pub fn op_crypto_get_random_values( state: &mut OpState, #[buffer] out: &mut [u8], -) -> Result<(), Error> { +) -> Result<(), CryptoError> { if out.len() > 65536 { - return Err(Error::ArrayBufferViewLengthExceeded(out.len())); + return Err(CryptoError::ArrayBufferViewLengthExceeded(out.len())); } let maybe_seeded_rng = state.try_borrow_mut::(); @@ -258,7 +291,7 @@ pub struct SignArg { pub async fn op_crypto_sign_key( #[serde] args: SignArg, #[buffer] zero_copy: JsBuffer, -) -> Result { +) -> Result { deno_core::unsync::spawn_blocking(move || { let data = &*zero_copy; let algorithm = args.algorithm; @@ -267,7 +300,7 @@ pub async fn op_crypto_sign_key( Algorithm::RsassaPkcs1v15 => { use rsa::pkcs1v15::SigningKey; let private_key = RsaPrivateKey::from_pkcs1_der(&args.key.data)?; - match args.hash.ok_or_else(|| Error::MissingArgumentHash)? { + match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let signing_key = SigningKey::::new(private_key); signing_key.sign(data) @@ -292,11 +325,11 @@ pub async fn op_crypto_sign_key( let salt_len = args .salt_length - .ok_or_else(|| Error::MissingArgumentSaltLength)? + .ok_or_else(|| CryptoError::MissingArgumentSaltLength)? as usize; let mut rng = OsRng; - match args.hash.ok_or_else(|| Error::MissingArgumentHash)? { + match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let signing_key = Pss::new_with_salt::(salt_len); let hashed = Sha1::digest(data); @@ -323,7 +356,7 @@ pub async fn op_crypto_sign_key( Algorithm::Ecdsa => { let curve: &EcdsaSigningAlgorithm = args .named_curve - .ok_or_else(|| Error::Other(not_supported()))? + .ok_or_else(JsErrorBox::not_supported)? .into(); let rng = RingRand::SystemRandom::new(); @@ -333,7 +366,7 @@ pub async fn op_crypto_sign_key( if let Some(hash) = args.hash { match hash { CryptoHash::Sha256 | CryptoHash::Sha384 => (), - _ => return Err(Error::UnsupportedAlgorithm), + _ => return Err(CryptoError::UnsupportedAlgorithm), } }; @@ -343,17 +376,15 @@ pub async fn op_crypto_sign_key( signature.as_ref().to_vec() } Algorithm::Hmac => { - let hash: HmacAlgorithm = args - .hash - .ok_or_else(|| Error::Other(not_supported()))? - .into(); + let hash: HmacAlgorithm = + args.hash.ok_or_else(JsErrorBox::not_supported)?.into(); let key = HmacKey::new(hash, &args.key.data); let signature = ring::hmac::sign(&key, data); signature.as_ref().to_vec() } - _ => return Err(Error::UnsupportedAlgorithm), + _ => return Err(CryptoError::UnsupportedAlgorithm), }; Ok(signature.into()) @@ -376,7 +407,7 @@ pub struct VerifyArg { pub async fn op_crypto_verify_key( #[serde] args: VerifyArg, #[buffer] zero_copy: JsBuffer, -) -> Result { +) -> Result { deno_core::unsync::spawn_blocking(move || { let data = &*zero_copy; let algorithm = args.algorithm; @@ -387,7 +418,7 @@ pub async fn op_crypto_verify_key( use rsa::pkcs1v15::VerifyingKey; let public_key = read_rsa_public_key(args.key)?; let signature: Signature = args.signature.as_ref().try_into()?; - match args.hash.ok_or_else(|| Error::MissingArgumentHash)? { + match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let verifying_key = VerifyingKey::::new(public_key); verifying_key.verify(data, &signature).is_ok() @@ -412,10 +443,10 @@ pub async fn op_crypto_verify_key( let salt_len = args .salt_length - .ok_or_else(|| Error::MissingArgumentSaltLength)? + .ok_or_else(|| CryptoError::MissingArgumentSaltLength)? as usize; - match args.hash.ok_or_else(|| Error::MissingArgumentHash)? { + match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let pss = Pss::new_with_salt::(salt_len); let hashed = Sha1::digest(data); @@ -439,21 +470,19 @@ pub async fn op_crypto_verify_key( } } Algorithm::Hmac => { - let hash: HmacAlgorithm = args - .hash - .ok_or_else(|| Error::Other(not_supported()))? - .into(); + let hash: HmacAlgorithm = + args.hash.ok_or_else(JsErrorBox::not_supported)?.into(); let key = HmacKey::new(hash, &args.key.data); ring::hmac::verify(&key, data, &args.signature).is_ok() } Algorithm::Ecdsa => { let signing_alg: &EcdsaSigningAlgorithm = args .named_curve - .ok_or_else(|| Error::Other(not_supported()))? + .ok_or_else(JsErrorBox::not_supported)? .into(); let verify_alg: &EcdsaVerificationAlgorithm = args .named_curve - .ok_or_else(|| Error::Other(not_supported()))? + .ok_or_else(JsErrorBox::not_supported)? .into(); let private_key; @@ -467,7 +496,7 @@ pub async fn op_crypto_verify_key( private_key.public_key().as_ref() } KeyType::Public => &*args.key.data, - _ => return Err(Error::InvalidKeyFormat), + _ => return Err(CryptoError::InvalidKeyFormat), }; let public_key = @@ -475,7 +504,7 @@ pub async fn op_crypto_verify_key( public_key.verify(data, &args.signature).is_ok() } - _ => return Err(Error::UnsupportedAlgorithm), + _ => return Err(CryptoError::UnsupportedAlgorithm), }; Ok(verification) @@ -503,31 +532,27 @@ pub struct DeriveKeyArg { pub async fn op_crypto_derive_bits( #[serde] args: DeriveKeyArg, #[buffer] zero_copy: Option, -) -> Result { +) -> Result { deno_core::unsync::spawn_blocking(move || { let algorithm = args.algorithm; match algorithm { Algorithm::Pbkdf2 => { - let zero_copy = - zero_copy.ok_or_else(|| Error::Other(not_supported()))?; + let zero_copy = zero_copy.ok_or_else(JsErrorBox::not_supported)?; let salt = &*zero_copy; // The caller must validate these cases. assert!(args.length > 0); assert!(args.length % 8 == 0); - let algorithm = - match args.hash.ok_or_else(|| Error::Other(not_supported()))? { - CryptoHash::Sha1 => pbkdf2::PBKDF2_HMAC_SHA1, - CryptoHash::Sha256 => pbkdf2::PBKDF2_HMAC_SHA256, - CryptoHash::Sha384 => pbkdf2::PBKDF2_HMAC_SHA384, - CryptoHash::Sha512 => pbkdf2::PBKDF2_HMAC_SHA512, - }; + let algorithm = match args.hash.ok_or_else(JsErrorBox::not_supported)? { + CryptoHash::Sha1 => pbkdf2::PBKDF2_HMAC_SHA1, + CryptoHash::Sha256 => pbkdf2::PBKDF2_HMAC_SHA256, + CryptoHash::Sha384 => pbkdf2::PBKDF2_HMAC_SHA384, + CryptoHash::Sha512 => pbkdf2::PBKDF2_HMAC_SHA512, + }; // This will never panic. We have already checked length earlier. let iterations = NonZeroU32::new( - args - .iterations - .ok_or_else(|| Error::Other(not_supported()))?, + args.iterations.ok_or_else(JsErrorBox::not_supported)?, ) .unwrap(); let secret = args.key.data; @@ -538,33 +563,33 @@ pub async fn op_crypto_derive_bits( Algorithm::Ecdh => { let named_curve = args .named_curve - .ok_or_else(|| Error::MissingArgumentNamedCurve)?; + .ok_or_else(|| CryptoError::MissingArgumentNamedCurve)?; let public_key = args .public_key - .ok_or_else(|| Error::MissingArgumentPublicKey)?; + .ok_or_else(|| CryptoError::MissingArgumentPublicKey)?; match named_curve { CryptoNamedCurve::P256 => { let secret_key = p256::SecretKey::from_pkcs8_der(&args.key.data) - .map_err(|_| Error::DecodePrivateKey)?; + .map_err(|_| CryptoError::DecodePrivateKey)?; let public_key = match public_key.r#type { KeyType::Private => { p256::SecretKey::from_pkcs8_der(&public_key.data) - .map_err(|_| Error::DecodePrivateKey)? + .map_err(|_| CryptoError::DecodePrivateKey)? .public_key() } KeyType::Public => { let point = p256::EncodedPoint::from_bytes(public_key.data) - .map_err(|_| Error::DecodePrivateKey)?; + .map_err(|_| CryptoError::DecodePrivateKey)?; let pk = p256::PublicKey::from_encoded_point(&point); // pk is a constant time Option. if pk.is_some().into() { pk.unwrap() } else { - return Err(Error::DecodePrivateKey); + return Err(CryptoError::DecodePrivateKey); } } _ => unreachable!(), @@ -580,24 +605,24 @@ pub async fn op_crypto_derive_bits( } CryptoNamedCurve::P384 => { let secret_key = p384::SecretKey::from_pkcs8_der(&args.key.data) - .map_err(|_| Error::DecodePrivateKey)?; + .map_err(|_| CryptoError::DecodePrivateKey)?; let public_key = match public_key.r#type { KeyType::Private => { p384::SecretKey::from_pkcs8_der(&public_key.data) - .map_err(|_| Error::DecodePrivateKey)? + .map_err(|_| CryptoError::DecodePrivateKey)? .public_key() } KeyType::Public => { let point = p384::EncodedPoint::from_bytes(public_key.data) - .map_err(|_| Error::DecodePrivateKey)?; + .map_err(|_| CryptoError::DecodePrivateKey)?; let pk = p384::PublicKey::from_encoded_point(&point); // pk is a constant time Option. if pk.is_some().into() { pk.unwrap() } else { - return Err(Error::DecodePrivateKey); + return Err(CryptoError::DecodePrivateKey); } } _ => unreachable!(), @@ -614,18 +639,16 @@ pub async fn op_crypto_derive_bits( } } Algorithm::Hkdf => { - let zero_copy = - zero_copy.ok_or_else(|| Error::Other(not_supported()))?; + let zero_copy = zero_copy.ok_or_else(JsErrorBox::not_supported)?; let salt = &*zero_copy; - let algorithm = - match args.hash.ok_or_else(|| Error::Other(not_supported()))? { - CryptoHash::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY, - CryptoHash::Sha256 => hkdf::HKDF_SHA256, - CryptoHash::Sha384 => hkdf::HKDF_SHA384, - CryptoHash::Sha512 => hkdf::HKDF_SHA512, - }; + let algorithm = match args.hash.ok_or_else(JsErrorBox::not_supported)? { + CryptoHash::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY, + CryptoHash::Sha256 => hkdf::HKDF_SHA256, + CryptoHash::Sha384 => hkdf::HKDF_SHA384, + CryptoHash::Sha512 => hkdf::HKDF_SHA512, + }; - let info = args.info.ok_or_else(|| Error::MissingArgumentInfo)?; + let info = args.info.ok_or(CryptoError::MissingArgumentInfo)?; // IKM let secret = args.key.data; // L @@ -636,18 +659,18 @@ pub async fn op_crypto_derive_bits( let info = &[&*info]; let okm = prk .expand(info, HkdfOutput(length)) - .map_err(|_e| Error::HKDFLengthTooLarge)?; + .map_err(|_e| CryptoError::HKDFLengthTooLarge)?; let mut r = vec![0u8; length]; okm.fill(&mut r)?; Ok(r.into()) } - _ => Err(Error::UnsupportedAlgorithm), + _ => Err(CryptoError::UnsupportedAlgorithm), } }) .await? } -fn read_rsa_public_key(key_data: KeyData) -> Result { +fn read_rsa_public_key(key_data: KeyData) -> Result { let public_key = match key_data.r#type { KeyType::Private => { RsaPrivateKey::from_pkcs1_der(&key_data.data)?.to_public_key() @@ -660,7 +683,9 @@ fn read_rsa_public_key(key_data: KeyData) -> Result { #[op2] #[string] -pub fn op_crypto_random_uuid(state: &mut OpState) -> Result { +pub fn op_crypto_random_uuid( + state: &mut OpState, +) -> Result { let maybe_seeded_rng = state.try_borrow_mut::(); let uuid = if let Some(seeded_rng) = maybe_seeded_rng { let mut bytes = [0u8; 16]; @@ -681,7 +706,7 @@ pub fn op_crypto_random_uuid(state: &mut OpState) -> Result { pub async fn op_crypto_subtle_digest( #[serde] algorithm: CryptoHash, #[buffer] data: JsBuffer, -) -> Result { +) -> Result { let output = spawn_blocking(move || { digest::digest(algorithm.into(), &data) .as_ref() @@ -705,7 +730,7 @@ pub struct WrapUnwrapKeyArg { pub fn op_crypto_wrap_key( #[serde] args: WrapUnwrapKeyArg, #[buffer] data: JsBuffer, -) -> Result { +) -> Result { let algorithm = args.algorithm; match algorithm { @@ -713,20 +738,20 @@ pub fn op_crypto_wrap_key( let key = args.key.as_secret_key()?; if data.len() % 8 != 0 { - return Err(Error::DataInvalidSize); + return Err(CryptoError::DataInvalidSize); } let wrapped_key = match key.len() { 16 => KekAes128::new(key.into()).wrap_vec(&data), 24 => KekAes192::new(key.into()).wrap_vec(&data), 32 => KekAes256::new(key.into()).wrap_vec(&data), - _ => return Err(Error::InvalidKeyLength), + _ => return Err(CryptoError::InvalidKeyLength), } - .map_err(|_| Error::EncryptionError)?; + .map_err(|_| CryptoError::EncryptionError)?; Ok(wrapped_key.into()) } - _ => Err(Error::UnsupportedAlgorithm), + _ => Err(CryptoError::UnsupportedAlgorithm), } } @@ -735,27 +760,27 @@ pub fn op_crypto_wrap_key( pub fn op_crypto_unwrap_key( #[serde] args: WrapUnwrapKeyArg, #[buffer] data: JsBuffer, -) -> Result { +) -> Result { let algorithm = args.algorithm; match algorithm { Algorithm::AesKw => { let key = args.key.as_secret_key()?; if data.len() % 8 != 0 { - return Err(Error::DataInvalidSize); + return Err(CryptoError::DataInvalidSize); } let unwrapped_key = match key.len() { 16 => KekAes128::new(key.into()).unwrap_vec(&data), 24 => KekAes192::new(key.into()).unwrap_vec(&data), 32 => KekAes256::new(key.into()).unwrap_vec(&data), - _ => return Err(Error::InvalidKeyLength), + _ => return Err(CryptoError::InvalidKeyLength), } - .map_err(|_| Error::DecryptionError)?; + .map_err(|_| CryptoError::DecryptionError)?; Ok(unwrapped_key.into()) } - _ => Err(Error::UnsupportedAlgorithm), + _ => Err(CryptoError::UnsupportedAlgorithm), } } diff --git a/ext/crypto/shared.rs b/ext/crypto/shared.rs index f70d32856c..1c28e0b87d 100644 --- a/ext/crypto/shared.rs +++ b/ext/crypto/shared.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; @@ -60,26 +60,36 @@ pub enum RustRawKeyData { Public(ToJsBuffer), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SharedError { + #[class(type)] #[error("expected valid private key")] ExpectedValidPrivateKey, + #[class(type)] #[error("expected valid public key")] ExpectedValidPublicKey, + #[class(type)] #[error("expected valid private EC key")] ExpectedValidPrivateECKey, + #[class(type)] #[error("expected valid public EC key")] ExpectedValidPublicECKey, + #[class(type)] #[error("expected private key")] ExpectedPrivateKey, + #[class(type)] #[error("expected public key")] ExpectedPublicKey, + #[class(type)] #[error("expected secret key")] ExpectedSecretKey, + #[class("DOMExceptionOperationError")] #[error("failed to decode private key")] FailedDecodePrivateKey, + #[class("DOMExceptionOperationError")] #[error("failed to decode public key")] FailedDecodePublicKey, + #[class("DOMExceptionNotSupportedError")] #[error("unsupported format")] UnsupportedFormat, } diff --git a/ext/crypto/x25519.rs b/ext/crypto/x25519.rs index d2c4d986b9..226ed89e40 100644 --- a/ext/crypto/x25519.rs +++ b/ext/crypto/x25519.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use curve25519_dalek::montgomery::MontgomeryPoint; use deno_core::op2; @@ -11,10 +11,12 @@ use spki::der::asn1::BitString; use spki::der::Decode; use spki::der::Encode; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum X25519Error { + #[class("DOMExceptionOperationError")] #[error("Failed to export key")] FailedExport, + #[class(generic)] #[error(transparent)] Der(#[from] spki::der::Error), } diff --git a/ext/crypto/x448.rs b/ext/crypto/x448.rs index 89bf48e28b..2086a8f048 100644 --- a/ext/crypto/x448.rs +++ b/ext/crypto/x448.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::ToJsBuffer; @@ -12,10 +12,12 @@ use spki::der::asn1::BitString; use spki::der::Decode; use spki::der::Encode; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum X448Error { + #[class("DOMExceptionOperationError")] #[error("Failed to export key")] FailedExport, + #[class(generic)] #[error(transparent)] Der(#[from] spki::der::Error), } diff --git a/ext/fetch/20_headers.js b/ext/fetch/20_headers.js index e56a74c423..fb97adb13b 100644 --- a/ext/fetch/20_headers.js +++ b/ext/fetch/20_headers.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/21_formdata.js b/ext/fetch/21_formdata.js index 7d466b8e25..8b91a0fa47 100644 --- a/ext/fetch/21_formdata.js +++ b/ext/fetch/21_formdata.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/22_body.js b/ext/fetch/22_body.js index a34758d19a..f180bda18d 100644 --- a/ext/fetch/22_body.js +++ b/ext/fetch/22_body.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// @@ -13,6 +13,7 @@ import { core, primordials } from "ext:core/mod.js"; const { + BadResourcePrototype, isAnyArrayBuffer, isArrayBuffer, isStringObject, @@ -26,6 +27,7 @@ const { JSONParse, ObjectDefineProperties, ObjectPrototypeIsPrototypeOf, + PromisePrototypeCatch, TypedArrayPrototypeGetBuffer, TypedArrayPrototypeGetByteLength, TypedArrayPrototypeGetByteOffset, @@ -160,7 +162,18 @@ class InnerBody { ) ) { readableStreamThrowIfErrored(this.stream); - return readableStreamCollectIntoUint8Array(this.stream); + return PromisePrototypeCatch( + readableStreamCollectIntoUint8Array(this.stream), + (e) => { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, e)) { + // TODO(kt3k): We probably like to pass e as `cause` if BadResource supports it. + throw new e.constructor( + "Cannot read body as underlying resource unavailable", + ); + } + throw e; + }, + ); } else { this.streamOrStatic.consumed = true; return this.streamOrStatic.body; diff --git a/ext/fetch/22_http_client.js b/ext/fetch/22_http_client.js index 6a1243ee0b..b74979c4c6 100644 --- a/ext/fetch/22_http_client.js +++ b/ext/fetch/22_http_client.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/23_request.js b/ext/fetch/23_request.js index 61cac22d2e..9aa2f0fe5c 100644 --- a/ext/fetch/23_request.js +++ b/ext/fetch/23_request.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/23_response.js b/ext/fetch/23_response.js index 278dcb7dec..0a86e04310 100644 --- a/ext/fetch/23_response.js +++ b/ext/fetch/23_response.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/26_fetch.js b/ext/fetch/26_fetch.js index 12b9c4582b..7d5f5ea2f7 100644 --- a/ext/fetch/26_fetch.js +++ b/ext/fetch/26_fetch.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// @@ -59,10 +59,9 @@ import { } from "ext:deno_fetch/23_response.js"; import * as abortSignal from "ext:deno_web/03_abort_signal.js"; import { - endSpan, + builtinTracer, enterSpan, - exitSpan, - Span, + restoreContext, TRACING_ENABLED, } from "ext:deno_telemetry/telemetry.ts"; import { @@ -320,10 +319,10 @@ function httpRedirectFetch(request, response, terminator) { // Drop confidential headers when redirecting to a less secure protocol // or to a different domain that is not a superdomain if ( - locationURL.protocol !== currentURL.protocol && - locationURL.protocol !== "https:" || - locationURL.host !== currentURL.host && - !isSubdomain(locationURL.host, currentURL.host) + (locationURL.protocol !== currentURL.protocol && + locationURL.protocol !== "https:") || + (locationURL.host !== currentURL.host && + !isSubdomain(locationURL.host, currentURL.host)) ) { for (let i = 0; i < request.headerList.length; i++) { if ( @@ -352,10 +351,11 @@ function httpRedirectFetch(request, response, terminator) { */ function fetch(input, init = { __proto__: null }) { let span; + let context; try { if (TRACING_ENABLED) { - span = new Span("fetch", { kind: 2 }); - enterSpan(span); + span = builtinTracer().startSpan("fetch", { kind: 2 }); + context = enterSpan(span); } // There is an async dispatch later that causes a stack trace disconnect. @@ -454,9 +454,7 @@ function fetch(input, init = { __proto__: null }) { await opPromise; return result; } finally { - if (span) { - endSpan(span); - } + span?.end(); } })(); } @@ -469,19 +467,17 @@ function fetch(input, init = { __proto__: null }) { // XXX: This should always be true, otherwise `opPromise` would be present. if (op_fetch_promise_is_settled(result)) { // It's already settled. - endSpan(span); + span?.end(); } else { // Not settled yet, we can return a new wrapper promise. return SafePromisePrototypeFinally(result, () => { - endSpan(span); + span?.end(); }); } } return result; } finally { - if (span) { - exitSpan(span); - } + if (context) restoreContext(context); } } @@ -508,8 +504,11 @@ function abortFetch(request, responseObject, error) { */ function isSubdomain(subdomain, domain) { const dot = subdomain.length - domain.length - 1; - return dot > 0 && subdomain[dot] === "." && - StringPrototypeEndsWith(subdomain, domain); + return ( + dot > 0 && + subdomain[dot] === "." && + StringPrototypeEndsWith(subdomain, domain) + ); } /** diff --git a/ext/fetch/27_eventsource.js b/ext/fetch/27_eventsource.js index aadbb5fe71..99ea4073a5 100644 --- a/ext/fetch/27_eventsource.js +++ b/ext/fetch/27_eventsource.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index 716d268a04..a0d29291b0 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_fetch" -version = "0.206.0" +version = "0.209.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -18,11 +18,13 @@ base64.workspace = true bytes.workspace = true data-url.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true deno_tls.workspace = true dyn-clone = "1" error_reporter = "1" +h2.workspace = true hickory-resolver.workspace = true http.workspace = true http-body-util.workspace = true diff --git a/ext/fetch/dns.rs b/ext/fetch/dns.rs index fdde4e17bb..e233021400 100644 --- a/ext/fetch/dns.rs +++ b/ext/fetch/dns.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::future::Future; use std::io; use std::net::SocketAddr; diff --git a/ext/fetch/fs_fetch_handler.rs b/ext/fetch/fs_fetch_handler.rs index c236dd9c67..8761eb2d65 100644 --- a/ext/fetch/fs_fetch_handler.rs +++ b/ext/fetch/fs_fetch_handler.rs @@ -1,8 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::CancelHandle; -use crate::CancelableResponseFuture; -use crate::FetchHandler; +use std::rc::Rc; use deno_core::futures::FutureExt; use deno_core::futures::TryFutureExt; @@ -10,11 +8,15 @@ use deno_core::futures::TryStreamExt; use deno_core::url::Url; use deno_core::CancelFuture; use deno_core::OpState; +use deno_error::JsErrorBox; use http::StatusCode; use http_body_util::BodyExt; -use std::rc::Rc; use tokio_util::io::ReaderStream; +use crate::CancelHandle; +use crate::CancelableResponseFuture; +use crate::FetchHandler; + /// An implementation which tries to read file URLs from the file system via /// tokio::fs. #[derive(Clone)] @@ -33,7 +35,7 @@ impl FetchHandler for FsFetchHandler { let file = tokio::fs::File::open(path).map_err(|_| ()).await?; let stream = ReaderStream::new(file) .map_ok(hyper::body::Frame::data) - .map_err(Into::into); + .map_err(JsErrorBox::from_err); let body = http_body_util::StreamBody::new(stream).boxed(); let response = http::Response::builder() .status(StatusCode::OK) diff --git a/ext/fetch/internal.d.ts b/ext/fetch/internal.d.ts index 17565992f4..8ab38f9b62 100644 --- a/ext/fetch/internal.d.ts +++ b/ext/fetch/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/fetch/lib.deno_fetch.d.ts b/ext/fetch/lib.deno_fetch.d.ts index 8614dec899..2be844f0f8 100644 --- a/ext/fetch/lib.deno_fetch.d.ts +++ b/ext/fetch/lib.deno_fetch.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index 919c6d3044..5af68695ef 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod dns; mod fs_fetch_handler; @@ -10,6 +10,7 @@ use std::borrow::Cow; use std::cell::RefCell; use std::cmp::min; use std::convert::From; +use std::future; use std::path::Path; use std::path::PathBuf; use std::pin::Pin; @@ -18,6 +19,10 @@ use std::sync::Arc; use std::task::Context; use std::task::Poll; +use bytes::Bytes; +// Re-export data_url +pub use data_url; +use data_url::DataUrl; use deno_core::futures::stream::Peekable; use deno_core::futures::Future; use deno_core::futures::FutureExt; @@ -41,6 +46,7 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; use deno_path_util::url_from_file_path; use deno_path_util::PathToUrlError; use deno_permissions::PermissionCheckError; @@ -50,9 +56,7 @@ use deno_tls::RootCertStoreProvider; use deno_tls::TlsKey; use deno_tls::TlsKeys; use deno_tls::TlsKeysHolder; - -use bytes::Bytes; -use data_url::DataUrl; +pub use fs_fetch_handler::FsFetchHandler; use http::header::HeaderName; use http::header::HeaderValue; use http::header::ACCEPT; @@ -66,6 +70,7 @@ use http::header::USER_AGENT; use http::Extensions; use http::Method; use http::Uri; +use http_body_util::combinators::BoxBody; use http_body_util::BodyExt; use hyper::body::Frame; use hyper_util::client::legacy::connect::HttpConnector; @@ -73,17 +78,13 @@ use hyper_util::client::legacy::connect::HttpInfo; use hyper_util::client::legacy::Builder as HyperClientBuilder; use hyper_util::rt::TokioExecutor; use hyper_util::rt::TokioTimer; +pub use proxy::basic_auth; use serde::Deserialize; use serde::Serialize; +use tower::retry; use tower::ServiceExt; use tower_http::decompression::Decompression; -// Re-export data_url -pub use data_url; -pub use proxy::basic_auth; - -pub use fs_fetch_handler::FsFetchHandler; - #[derive(Clone)] pub struct Options { pub user_agent: String, @@ -100,9 +101,8 @@ pub struct Options { /// For more info on what can be configured, see [`hyper_util::client::legacy::Builder`]. pub client_builder_hook: Option HyperClientBuilder>, #[allow(clippy::type_complexity)] - pub request_builder_hook: Option< - fn(&mut http::Request) -> Result<(), deno_core::error::AnyError>, - >, + pub request_builder_hook: + Option) -> Result<(), JsErrorBox>>, pub unsafely_ignore_certificate_errors: Option>, pub client_cert_chain_and_key: TlsKeys, pub file_fetch_handler: Rc, @@ -110,9 +110,7 @@ pub struct Options { } impl Options { - pub fn root_cert_store( - &self, - ) -> Result, deno_core::error::AnyError> { + pub fn root_cert_store(&self) -> Result, JsErrorBox> { Ok(match &self.root_cert_store_provider { Some(provider) => Some(provider.get_or_try_init()?.clone()), None => None, @@ -164,48 +162,71 @@ deno_core::extension!(deno_fetch, }, ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum FetchError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Permission(#[from] PermissionCheckError), + #[class(type)] #[error("NetworkError when attempting to fetch resource")] NetworkError, + #[class(type)] #[error("Fetching files only supports the GET method: received {0}")] FsNotGet(Method), + #[class(inherit)] #[error(transparent)] PathToUrl(#[from] PathToUrlError), + #[class(type)] #[error("Invalid URL {0}")] InvalidUrl(Url), + #[class(type)] #[error(transparent)] InvalidHeaderName(#[from] http::header::InvalidHeaderName), + #[class(type)] #[error(transparent)] InvalidHeaderValue(#[from] http::header::InvalidHeaderValue), + #[class(type)] #[error("{0:?}")] DataUrl(data_url::DataUrlError), + #[class(type)] #[error("{0:?}")] Base64(data_url::forgiving_base64::InvalidBase64), + #[class(type)] #[error("Blob for the given URL not found.")] BlobNotFound, + #[class(type)] #[error("Url scheme '{0}' not supported")] SchemeNotSupported(String), + #[class(type)] #[error("Request was cancelled")] RequestCanceled, + #[class(generic)] #[error(transparent)] Http(#[from] http::Error), + #[class(inherit)] #[error(transparent)] ClientCreate(#[from] HttpClientCreateError), + #[class(inherit)] #[error(transparent)] Url(#[from] url::ParseError), + #[class(type)] #[error(transparent)] Method(#[from] http::method::InvalidMethod), + #[class(inherit)] #[error(transparent)] ClientSend(#[from] ClientSendError), + #[class(inherit)] #[error(transparent)] - RequestBuilderHook(deno_core::error::AnyError), + RequestBuilderHook(JsErrorBox), + #[class(inherit)] #[error(transparent)] Io(#[from] std::io::Error), + #[class(generic)] + #[error(transparent)] + Dns(hickory_resolver::ResolveError), } pub type CancelableResponseFuture = @@ -294,9 +315,7 @@ pub fn create_client_from_options( #[allow(clippy::type_complexity)] pub struct ResourceToBodyAdapter( Rc, - Option< - Pin>>>, - >, + Option>>>>, ); impl ResourceToBodyAdapter { @@ -312,7 +331,7 @@ unsafe impl Send for ResourceToBodyAdapter {} unsafe impl Sync for ResourceToBodyAdapter {} impl Stream for ResourceToBodyAdapter { - type Item = Result; + type Item = Result; fn poll_next( self: Pin<&mut Self>, @@ -342,7 +361,7 @@ impl Stream for ResourceToBodyAdapter { impl hyper::body::Body for ResourceToBodyAdapter { type Data = Bytes; - type Error = deno_core::error::AnyError; + type Error = JsErrorBox; fn poll_frame( self: Pin<&mut Self>, @@ -417,10 +436,7 @@ where FP: FetchPermissions + 'static, { let (client, allow_host) = if let Some(rid) = client_rid { - let r = state - .resource_table - .get::(rid) - .map_err(FetchError::Resource)?; + let r = state.resource_table.get::(rid)?; (r.client.clone(), r.allow_host) } else { (get_or_create_client_from_state(state)?, false) @@ -476,22 +492,17 @@ where // If a body is passed, we use it, and don't return a body for streaming. con_len = Some(data.len() as u64); - http_body_util::Full::new(data.to_vec().into()) - .map_err(|never| match never {}) - .boxed() + ReqBody::full(data.to_vec().into()) } (_, Some(resource)) => { - let resource = state - .resource_table - .take_any(resource) - .map_err(FetchError::Resource)?; + let resource = state.resource_table.take_any(resource)?; match resource.size_hint() { (body_size, Some(n)) if body_size == n && body_size > 0 => { con_len = Some(body_size); } _ => {} } - ReqBody::new(ResourceToBodyAdapter::new(resource)) + ReqBody::streaming(ResourceToBodyAdapter::new(resource)) } (None, None) => unreachable!(), } @@ -501,9 +512,7 @@ where if matches!(method, Method::POST | Method::PUT) { con_len = Some(0); } - http_body_util::Empty::new() - .map_err(|never| match never {}) - .boxed() + ReqBody::empty() }; let mut request = http::Request::new(body); @@ -628,8 +637,7 @@ pub async fn op_fetch_send( let request = state .borrow_mut() .resource_table - .take::(rid) - .map_err(FetchError::Resource)?; + .take::(rid)?; let request = Rc::try_unwrap(request) .ok() @@ -808,9 +816,7 @@ impl Resource for FetchResponseResource { // safely call `await` on it without creating a race condition. Some(_) => match reader.as_mut().next().await.unwrap() { Ok(chunk) => assert!(chunk.is_empty()), - Err(err) => { - break Err(deno_core::error::type_error(err.to_string())) - } + Err(err) => break Err(JsErrorBox::type_error(err.to_string())), }, None => break Ok(BufView::empty()), } @@ -818,7 +824,10 @@ impl Resource for FetchResponseResource { }; let cancel_handle = RcRef::map(self, |r| &r.cancel); - fut.try_or_cancel(cancel_handle).await + fut + .try_or_cancel(cancel_handle) + .await + .map_err(JsErrorBox::from_err) }) } @@ -901,9 +910,7 @@ where ca_certs, proxy: args.proxy, dns_resolver: if args.use_hickory_resolver { - dns::Resolver::hickory() - .map_err(deno_core::error::AnyError::new) - .map_err(FetchError::Resource)? + dns::Resolver::hickory().map_err(FetchError::Dns)? } else { dns::Resolver::default() }, @@ -967,7 +974,8 @@ impl Default for CreateHttpClientOptions { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum HttpClientCreateError { #[error(transparent)] Tls(deno_tls::TlsError), @@ -977,8 +985,9 @@ pub enum HttpClientCreateError { InvalidProxyUrl, #[error("Cannot create Http Client: either `http1` or `http2` needs to be set to true")] HttpVersionSelectionInvalid, + #[class(inherit)] #[error(transparent)] - RootCertStore(deno_core::error::AnyError), + RootCertStore(JsErrorBox), } /// Create new instance of async Client. This client supports @@ -1066,7 +1075,8 @@ pub fn create_http_client( } let pooled_client = builder.build(connector); - let decompress = Decompression::new(pooled_client).gzip(true).br(true); + let retry_client = retry::Retry::new(FetchRetry, pooled_client); + let decompress = Decompression::new(retry_client).gzip(true).br(true); Ok(Client { inner: decompress, @@ -1083,7 +1093,12 @@ pub fn op_utf8_to_byte_string(#[string] input: String) -> ByteString { #[derive(Clone, Debug)] pub struct Client { - inner: Decompression>, + inner: Decompression< + retry::Retry< + FetchRetry, + hyper_util::client::legacy::Client, + >, + >, // Used to check whether to include a proxy-authorization header proxies: Arc, user_agent: HeaderValue, @@ -1095,7 +1110,8 @@ type Connector = proxy::ProxyConnector>; #[allow(clippy::declare_interior_mutable_const)] const STAR_STAR: HeaderValue = HeaderValue::from_static("*/*"); -#[derive(Debug)] +#[derive(Debug, deno_error::JsError)] +#[class(type)] pub struct ClientSendError { uri: Uri, pub source: hyper_util::client::legacy::Error, @@ -1170,14 +1186,74 @@ impl Client { .oneshot(req) .await .map_err(|e| ClientSendError { uri, source: e })?; - Ok(resp.map(|b| b.map_err(|e| deno_core::anyhow::anyhow!(e)).boxed())) + Ok(resp.map(|b| b.map_err(|e| JsErrorBox::generic(e.to_string())).boxed())) } } -pub type ReqBody = - http_body_util::combinators::BoxBody; -pub type ResBody = - http_body_util::combinators::BoxBody; +// This is a custom enum to allow the retry policy to clone the variants that could be retried. +pub enum ReqBody { + Full(http_body_util::Full), + Empty(http_body_util::Empty), + Streaming(BoxBody), +} + +pub type ResBody = BoxBody; + +impl ReqBody { + pub fn full(bytes: Bytes) -> Self { + ReqBody::Full(http_body_util::Full::new(bytes)) + } + + pub fn empty() -> Self { + ReqBody::Empty(http_body_util::Empty::new()) + } + + pub fn streaming(body: B) -> Self + where + B: hyper::body::Body + + Send + + Sync + + 'static, + { + ReqBody::Streaming(BoxBody::new(body)) + } +} + +impl hyper::body::Body for ReqBody { + type Data = Bytes; + type Error = JsErrorBox; + + fn poll_frame( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + match &mut *self { + ReqBody::Full(ref mut b) => { + Pin::new(b).poll_frame(cx).map_err(|never| match never {}) + } + ReqBody::Empty(ref mut b) => { + Pin::new(b).poll_frame(cx).map_err(|never| match never {}) + } + ReqBody::Streaming(ref mut b) => Pin::new(b).poll_frame(cx), + } + } + + fn is_end_stream(&self) -> bool { + match self { + ReqBody::Full(ref b) => b.is_end_stream(), + ReqBody::Empty(ref b) => b.is_end_stream(), + ReqBody::Streaming(ref b) => b.is_end_stream(), + } + } + + fn size_hint(&self) -> hyper::body::SizeHint { + match self { + ReqBody::Full(ref b) => b.size_hint(), + ReqBody::Empty(ref b) => b.size_hint(), + ReqBody::Streaming(ref b) => b.size_hint(), + } + } +} /// Copied from https://github.com/seanmonstar/reqwest/blob/b9d62a0323d96f11672a61a17bf8849baec00275/src/async_impl/request.rs#L572 /// Check the request URL for a "username:password" type authority, and if @@ -1214,3 +1290,102 @@ pub fn extract_authority(url: &mut Url) -> Option<(String, Option)> { fn op_fetch_promise_is_settled(promise: v8::Local) -> bool { promise.state() != v8::PromiseState::Pending } + +/// Deno.fetch's retry policy. +#[derive(Clone, Debug)] +struct FetchRetry; + +/// Marker extension that a request has been retried once. +#[derive(Clone, Debug)] +struct Retried; + +impl + retry::Policy, http::Response, E> + for FetchRetry +where + E: std::error::Error + 'static, +{ + /// Don't delay retries. + type Future = future::Ready<()>; + + fn retry( + &mut self, + req: &mut http::Request, + result: &mut Result, E>, + ) -> Option { + if req.extensions().get::().is_some() { + // only retry once + return None; + } + + match result { + Ok(..) => { + // never retry a Response + None + } + Err(err) => { + if is_error_retryable(&*err) { + req.extensions_mut().insert(Retried); + Some(future::ready(())) + } else { + None + } + } + } + } + + fn clone_request( + &mut self, + req: &http::Request, + ) -> Option> { + let body = match req.body() { + ReqBody::Full(b) => ReqBody::Full(b.clone()), + ReqBody::Empty(b) => ReqBody::Empty(*b), + ReqBody::Streaming(..) => return None, + }; + + let mut clone = http::Request::new(body); + *clone.method_mut() = req.method().clone(); + *clone.uri_mut() = req.uri().clone(); + *clone.headers_mut() = req.headers().clone(); + *clone.extensions_mut() = req.extensions().clone(); + Some(clone) + } +} + +fn is_error_retryable(err: &(dyn std::error::Error + 'static)) -> bool { + // Note: hyper doesn't promise it will always be this h2 version. Keep up to date. + if let Some(err) = find_source::(err) { + // They sent us a graceful shutdown, try with a new connection! + if err.is_go_away() + && err.is_remote() + && err.reason() == Some(h2::Reason::NO_ERROR) + { + return true; + } + + // REFUSED_STREAM was sent from the server, which is safe to retry. + // https://www.rfc-editor.org/rfc/rfc9113.html#section-8.7-3.2 + if err.is_reset() + && err.is_remote() + && err.reason() == Some(h2::Reason::REFUSED_STREAM) + { + return true; + } + } + + false +} + +fn find_source<'a, E: std::error::Error + 'static>( + err: &'a (dyn std::error::Error + 'static), +) -> Option<&'a E> { + let mut err = Some(err); + while let Some(src) = err { + if let Some(found) = src.downcast_ref::() { + return Some(found); + } + err = src.source(); + } + None +} diff --git a/ext/fetch/proxy.rs b/ext/fetch/proxy.rs index 88fc211ecc..3b371a07b1 100644 --- a/ext/fetch/proxy.rs +++ b/ext/fetch/proxy.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! Parts of this module should be able to be replaced with other crates //! eventually, once generic versions appear in hyper-util, et al. @@ -13,7 +13,6 @@ use std::task::Poll; use deno_core::futures::TryFutureExt; use deno_tls::rustls::ClientConfig as TlsConfig; - use http::header::HeaderValue; use http::uri::Scheme; use http::Uri; @@ -108,9 +107,10 @@ pub(crate) fn from_env() -> Proxies { } pub fn basic_auth(user: &str, pass: Option<&str>) -> HeaderValue { + use std::io::Write; + use base64::prelude::BASE64_STANDARD; use base64::write::EncoderWriter; - use std::io::Write; let mut buf = b"Basic ".to_vec(); { diff --git a/ext/fetch/tests.rs b/ext/fetch/tests.rs index 3da29f8aa7..4e023be4fa 100644 --- a/ext/fetch/tests.rs +++ b/ext/fetch/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::net::SocketAddr; use std::sync::atomic::AtomicUsize; @@ -12,10 +12,9 @@ use http_body_util::BodyExt; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; -use crate::dns; - use super::create_http_client; use super::CreateHttpClientOptions; +use crate::dns; static EXAMPLE_CRT: &[u8] = include_bytes!("../tls/testdata/example1_cert.der"); static EXAMPLE_KEY: &[u8] = @@ -133,11 +132,7 @@ async fn rust_test_client_with_resolver( let req = http::Request::builder() .uri(format!("https://{}/foo", src_addr)) - .body( - http_body_util::Empty::new() - .map_err(|err| match err {}) - .boxed(), - ) + .body(crate::ReqBody::empty()) .unwrap(); let resp = client.send(req).await.unwrap(); assert_eq!(resp.status(), http::StatusCode::OK); diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index d3b07dc373..b8c12180c9 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index d54249329d..d71c04f9ff 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_ffi" -version = "0.169.0" +version = "0.172.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_permissions.workspace = true dlopen2.workspace = true dynasmrt = "1.2.3" diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs index c964071a09..4f9a057d01 100644 --- a/ext/ffi/call.rs +++ b/ext/ffi/call.rs @@ -1,12 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::cell::RefCell; +use std::ffi::c_void; +use std::future::Future; +use std::rc::Rc; -use crate::callback::PtrSymbol; -use crate::dlfcn::DynamicLibraryResource; -use crate::ir::*; -use crate::symbol::NativeType; -use crate::symbol::Symbol; -use crate::FfiPermissions; -use crate::ForeignFunction; use deno_core::op2; use deno_core::serde_json::Value; use deno_core::serde_v8::BigInt as V8BigInt; @@ -18,23 +16,33 @@ use deno_core::ResourceId; use libffi::middle::Arg; use num_bigint::BigInt; use serde::Serialize; -use std::cell::RefCell; -use std::ffi::c_void; -use std::future::Future; -use std::rc::Rc; -#[derive(Debug, thiserror::Error)] +use crate::callback::PtrSymbol; +use crate::dlfcn::DynamicLibraryResource; +use crate::ir::*; +use crate::symbol::NativeType; +use crate::symbol::Symbol; +use crate::FfiPermissions; +use crate::ForeignFunction; + +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CallError { + #[class(type)] #[error(transparent)] IR(#[from] IRError), + #[class(generic)] #[error("Nonblocking FFI call failed: {0}")] NonblockingCallFailure(#[source] tokio::task::JoinError), + #[class(type)] #[error("Invalid FFI symbol name: '{0}'")] InvalidSymbol(String), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Callback(#[from] super::CallbackError), } @@ -344,10 +352,7 @@ pub fn op_ffi_call_nonblocking( ) -> Result>, CallError> { let symbol = { let state = state.borrow(); - let resource = state - .resource_table - .get::(rid) - .map_err(CallError::Resource)?; + let resource = state.resource_table.get::(rid)?; let symbols = &resource.symbols; *symbols .get(&symbol) diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs index eff14503d1..a6c104ef42 100644 --- a/ext/ffi/callback.rs +++ b/ext/ffi/callback.rs @@ -1,19 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::symbol::NativeType; -use crate::FfiPermissions; -use crate::ForeignFunction; -use deno_core::op2; -use deno_core::v8; -use deno_core::v8::TryCatch; -use deno_core::CancelFuture; -use deno_core::CancelHandle; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use deno_core::V8CrossThreadTaskSpawner; -use libffi::middle::Cif; -use serde::Deserialize; use std::borrow::Cow; use std::cell::RefCell; use std::ffi::c_void; @@ -27,20 +13,39 @@ use std::sync::atomic; use std::sync::atomic::AtomicU32; use std::task::Poll; +use deno_core::op2; +use deno_core::v8; +use deno_core::v8::TryCatch; +use deno_core::CancelFuture; +use deno_core::CancelHandle; +use deno_core::OpState; +use deno_core::Resource; +use deno_core::ResourceId; +use deno_core::V8CrossThreadTaskSpawner; +use libffi::middle::Cif; +use serde::Deserialize; + +use crate::symbol::NativeType; +use crate::FfiPermissions; +use crate::ForeignFunction; + static THREAD_ID_COUNTER: AtomicU32 = AtomicU32::new(1); thread_local! { static LOCAL_THREAD_ID: RefCell = const { RefCell::new(0) }; } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CallbackError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[from] deno_error::JsErrorBox), } #[derive(Clone)] @@ -61,13 +66,8 @@ impl PtrSymbol { .clone() .into_iter() .map(libffi::middle::Type::try_from) - .collect::, _>>() - .map_err(CallbackError::Other)?, - def - .result - .clone() - .try_into() - .map_err(CallbackError::Other)?, + .collect::, _>>()?, + def.result.clone().try_into()?, ); Ok(Self { cif, ptr }) @@ -538,10 +538,8 @@ pub fn op_ffi_unsafe_callback_ref( #[smi] rid: ResourceId, ) -> Result, CallbackError> { let state = state.borrow(); - let callback_resource = state - .resource_table - .get::(rid) - .map_err(CallbackError::Resource)?; + let callback_resource = + state.resource_table.get::(rid)?; Ok(async move { let info: &mut CallbackInfo = @@ -608,10 +606,8 @@ where .parameters .into_iter() .map(libffi::middle::Type::try_from) - .collect::, _>>() - .map_err(CallbackError::Other)?, - libffi::middle::Type::try_from(args.result) - .map_err(CallbackError::Other)?, + .collect::, _>>()?, + libffi::middle::Type::try_from(args.result)?, ); // SAFETY: CallbackInfo is leaked, is not null and stays valid as long as the callback exists. @@ -647,10 +643,8 @@ pub fn op_ffi_unsafe_callback_close( // It is up to the user to know that it is safe to call the `close()` on the // UnsafeCallback instance. unsafe { - let callback_resource = state - .resource_table - .take::(rid) - .map_err(CallbackError::Resource)?; + let callback_resource = + state.resource_table.take::(rid)?; let info = Box::from_raw(callback_resource.info); let _ = v8::Global::from_raw(scope, info.callback); let _ = v8::Global::from_raw(scope, info.context); diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs index e1bb121d8c..da5a85e7e3 100644 --- a/ext/ffi/dlfcn.rs +++ b/ext/ffi/dlfcn.rs @@ -1,4 +1,21 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; +use std::ffi::c_void; +use std::rc::Rc; + +use deno_core::op2; +use deno_core::v8; +use deno_core::GarbageCollected; +use deno_core::OpState; +use deno_core::Resource; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; +use dlopen2::raw::Library; +use serde::Deserialize; +use serde_value::ValueDeserializer; use crate::ir::out_buffer_as_ptr; use crate::symbol::NativeType; @@ -6,34 +23,35 @@ use crate::symbol::Symbol; use crate::turbocall; use crate::turbocall::Turbocall; use crate::FfiPermissions; -use deno_core::op2; -use deno_core::v8; -use deno_core::GarbageCollected; -use deno_core::OpState; -use deno_core::Resource; -use dlopen2::raw::Library; -use serde::Deserialize; -use serde_value::ValueDeserializer; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::HashMap; -use std::ffi::c_void; -use std::rc::Rc; -#[derive(Debug, thiserror::Error)] +deno_error::js_error_wrapper!(dlopen2::Error, JsDlopen2Error, |err| { + match err { + dlopen2::Error::NullCharacter(_) => "InvalidData".into(), + dlopen2::Error::OpeningLibraryError(e) => e.get_class(), + dlopen2::Error::SymbolGettingError(e) => e.get_class(), + dlopen2::Error::AddrNotMatchingDll(e) => e.get_class(), + dlopen2::Error::NullSymbol => "NotFound".into(), + } +}); + +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum DlfcnError { + #[class(generic)] #[error("Failed to register symbol {symbol}: {error}")] RegisterSymbol { symbol: String, #[source] error: dlopen2::Error, }, + #[class(generic)] #[error(transparent)] Dlopen(#[from] dlopen2::Error), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[from] JsErrorBox), } pub struct DynamicLibraryResource { @@ -188,13 +206,8 @@ where .clone() .into_iter() .map(libffi::middle::Type::try_from) - .collect::, _>>() - .map_err(DlfcnError::Other)?, - foreign_fn - .result - .clone() - .try_into() - .map_err(DlfcnError::Other)?, + .collect::, _>>()?, + foreign_fn.result.clone().try_into()?, ); let func_key = v8::String::new(scope, &symbol_key).unwrap(); @@ -302,9 +315,7 @@ fn sync_fn_impl<'s>( unsafe { result.to_v8(scope, data.symbol.result_type.clone()) }; rv.set(result); } - Err(err) => { - deno_core::_ops::throw_type_error(scope, err.to_string()); - } + Err(err) => deno_core::error::throw_js_error_class(scope, &err), }; } @@ -324,6 +335,7 @@ pub(crate) fn format_error( // https://github.com/denoland/deno/issues/11632 dlopen2::Error::OpeningLibraryError(e) => { use std::os::windows::ffi::OsStrExt; + use winapi::shared::minwindef::DWORD; use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER; use winapi::um::errhandlingapi::GetLastError; @@ -392,10 +404,11 @@ pub(crate) fn format_error( #[cfg(test)] mod tests { + use serde_json::json; + use super::ForeignFunction; use super::ForeignSymbol; use crate::symbol::NativeType; - use serde_json::json; #[cfg(target_os = "windows")] #[test] diff --git a/ext/ffi/ir.rs b/ext/ffi/ir.rs index 2e80842166..a1877b4a2b 100644 --- a/ext/ffi/ir.rs +++ b/ext/ffi/ir.rs @@ -1,12 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::symbol::NativeType; -use deno_core::v8; -use libffi::middle::Arg; use std::ffi::c_void; use std::ptr; -#[derive(Debug, thiserror::Error)] +use deno_core::v8; +use libffi::middle::Arg; + +use crate::symbol::NativeType; + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum IRError { #[error("Invalid FFI u8 type, expected boolean")] InvalidU8ExpectedBoolean, diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 73ec7757ab..7fed3c32aa 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::mem::size_of; use std::os::raw::c_char; @@ -17,24 +17,23 @@ mod turbocall; use call::op_ffi_call_nonblocking; use call::op_ffi_call_ptr; use call::op_ffi_call_ptr_nonblocking; +pub use call::CallError; use callback::op_ffi_unsafe_callback_close; use callback::op_ffi_unsafe_callback_create; use callback::op_ffi_unsafe_callback_ref; +pub use callback::CallbackError; +use deno_permissions::PermissionCheckError; use dlfcn::op_ffi_load; +pub use dlfcn::DlfcnError; use dlfcn::ForeignFunction; +pub use ir::IRError; use r#static::op_ffi_get_static; +pub use r#static::StaticError; +pub use repr::ReprError; use repr::*; use symbol::NativeType; use symbol::Symbol; -pub use call::CallError; -pub use callback::CallbackError; -use deno_permissions::PermissionCheckError; -pub use dlfcn::DlfcnError; -pub use ir::IRError; -pub use r#static::StaticError; -pub use repr::ReprError; - #[cfg(not(target_pointer_width = "64"))] compile_error!("platform not supported"); diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs index eea15f3e97..bcd80cbf03 100644 --- a/ext/ffi/repr.rs +++ b/ext/ffi/repr.rs @@ -1,15 +1,18 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::FfiPermissions; -use deno_core::op2; -use deno_core::v8; -use deno_core::OpState; use std::ffi::c_char; use std::ffi::c_void; use std::ffi::CStr; use std::ptr; -#[derive(Debug, thiserror::Error)] +use deno_core::op2; +use deno_core::v8; +use deno_core::OpState; + +use crate::FfiPermissions; + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum ReprError { #[error("Invalid pointer to offset, pointer is null")] InvalidOffset, @@ -45,6 +48,7 @@ pub enum ReprError { InvalidF64, #[error("Invalid pointer pointer, pointer is null")] InvalidPointer, + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), } diff --git a/ext/ffi/static.rs b/ext/ffi/static.rs index 61b4059336..6d999430e3 100644 --- a/ext/ffi/static.rs +++ b/ext/ffi/static.rs @@ -1,23 +1,29 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; -use crate::dlfcn::DynamicLibraryResource; -use crate::symbol::NativeType; use deno_core::op2; use deno_core::v8; use deno_core::OpState; use deno_core::ResourceId; -use std::ptr; -#[derive(Debug, thiserror::Error)] +use crate::dlfcn::DynamicLibraryResource; +use crate::symbol::NativeType; + +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum StaticError { + #[class(inherit)] #[error(transparent)] Dlfcn(super::DlfcnError), + #[class(type)] #[error("Invalid FFI static type 'void'")] InvalidTypeVoid, + #[class(type)] #[error("Invalid FFI static type 'struct'")] InvalidTypeStruct, + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), } #[op2] @@ -29,10 +35,7 @@ pub fn op_ffi_get_static<'scope>( #[serde] static_type: NativeType, optional: bool, ) -> Result, StaticError> { - let resource = state - .resource_table - .get::(rid) - .map_err(StaticError::Resource)?; + let resource = state.resource_table.get::(rid)?; let data_ptr = match resource.get_static(name) { Ok(data_ptr) => data_ptr, diff --git a/ext/ffi/symbol.rs b/ext/ffi/symbol.rs index cee1c7d33e..5bca5be6d2 100644 --- a/ext/ffi/symbol.rs +++ b/ext/ffi/symbol.rs @@ -1,7 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::error::type_error; -use deno_core::error::AnyError; +use deno_error::JsErrorBox; /// Defines the accepted types that can be used as /// parameters and return values in FFI. @@ -29,7 +28,7 @@ pub enum NativeType { } impl TryFrom for libffi::middle::Type { - type Error = AnyError; + type Error = JsErrorBox; fn try_from(native_type: NativeType) -> Result { Ok(match native_type { @@ -56,7 +55,9 @@ impl TryFrom for libffi::middle::Type { .map(|field| field.clone().try_into()) .collect::, _>>()?, false => { - return Err(type_error("Struct must have at least one field")) + return Err(JsErrorBox::type_error( + "Struct must have at least one field", + )) } }) } diff --git a/ext/ffi/turbocall.rs b/ext/ffi/turbocall.rs index 38b4062ab7..499168dceb 100644 --- a/ext/ffi/turbocall.rs +++ b/ext/ffi/turbocall.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cmp::max; use std::ffi::c_void; diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js index fc2b18be13..74e3c87c17 100644 --- a/ext/fs/30_fs.js +++ b/ext/fs/30_fs.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { @@ -77,6 +77,7 @@ const { Error, Function, MathTrunc, + Number, ObjectEntries, ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, @@ -373,12 +374,12 @@ function parseFileInfo(response) { isDirectory: response.isDirectory, isSymlink: response.isSymlink, size: response.size, - mtime: response.mtimeSet === true ? new Date(response.mtime) : null, - atime: response.atimeSet === true ? new Date(response.atime) : null, + mtime: response.mtimeSet === true ? new Date(Number(response.mtime)) : null, + atime: response.atimeSet === true ? new Date(Number(response.atime)) : null, birthtime: response.birthtimeSet === true ? new Date(response.birthtime) : null, - ctime: response.ctimeSet === true ? new Date(response.ctime) : null, + ctime: response.ctimeSet === true ? new Date(Number(response.ctime)) : null, dev: response.dev, mode: response.mode, ino: unix ? response.ino : null, diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index d11520ad8c..df9d140ac3 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_fs" -version = "0.92.0" +version = "0.95.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -21,6 +21,7 @@ async-trait.workspace = true base32.workspace = true boxed_error.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_io.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true diff --git a/ext/fs/in_memory_fs.rs b/ext/fs/in_memory_fs.rs deleted file mode 100644 index b79b0ae984..0000000000 --- a/ext/fs/in_memory_fs.rs +++ /dev/null @@ -1,481 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// Allow using Arc for this module. -#![allow(clippy::disallowed_types)] - -use std::borrow::Cow; -use std::collections::hash_map::Entry; -use std::collections::HashMap; -use std::io::Error; -use std::io::ErrorKind; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; - -use deno_core::parking_lot::Mutex; -use deno_io::fs::File; -use deno_io::fs::FsError; -use deno_io::fs::FsResult; -use deno_io::fs::FsStat; -use deno_path_util::normalize_path; - -use crate::interface::AccessCheckCb; -use crate::interface::FsDirEntry; -use crate::interface::FsFileType; -use crate::FileSystem; -use crate::OpenOptions; - -#[derive(Debug)] -enum PathEntry { - Dir, - File(Vec), -} - -/// A very basic in-memory file system useful for swapping out in -/// the place of a RealFs for testing purposes. -/// -/// Please develop this out as you need functionality. -#[derive(Debug, Default)] -pub struct InMemoryFs { - entries: Mutex>>, -} - -impl InMemoryFs { - pub fn setup_text_files(&self, files: Vec<(String, String)>) { - for (path, text) in files { - let path = PathBuf::from(path); - self.mkdir_sync(path.parent().unwrap(), true, None).unwrap(); - self - .write_file_sync( - &path, - OpenOptions::write(true, false, false, None), - None, - &text.into_bytes(), - ) - .unwrap(); - } - } - - fn get_entry(&self, path: &Path) -> Option> { - let path = normalize_path(path); - self.entries.lock().get(&path).cloned() - } -} - -#[async_trait::async_trait(?Send)] -impl FileSystem for InMemoryFs { - fn cwd(&self) -> FsResult { - Err(FsError::NotSupported) - } - - fn tmp_dir(&self) -> FsResult { - Err(FsError::NotSupported) - } - - fn chdir(&self, _path: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - - fn umask(&self, _mask: Option) -> FsResult { - Err(FsError::NotSupported) - } - - fn open_sync( - &self, - _path: &Path, - _options: OpenOptions, - _access_check: Option, - ) -> FsResult> { - Err(FsError::NotSupported) - } - async fn open_async<'a>( - &'a self, - path: PathBuf, - options: OpenOptions, - access_check: Option>, - ) -> FsResult> { - self.open_sync(&path, options, access_check) - } - - fn mkdir_sync( - &self, - path: &Path, - recursive: bool, - _mode: Option, - ) -> FsResult<()> { - let path = normalize_path(path); - - if let Some(parent) = path.parent() { - let entry = self.entries.lock().get(parent).cloned(); - match entry { - Some(entry) => match &*entry { - PathEntry::File(_) => { - return Err(FsError::Io(Error::new( - ErrorKind::InvalidInput, - "Parent is a file", - ))) - } - PathEntry::Dir => {} - }, - None => { - if recursive { - self.mkdir_sync(parent, true, None)?; - } else { - return Err(FsError::Io(Error::new( - ErrorKind::NotFound, - "Not found", - ))); - } - } - } - } - - let entry = self.entries.lock().get(&path).cloned(); - match entry { - Some(entry) => match &*entry { - PathEntry::File(_) => Err(FsError::Io(Error::new( - ErrorKind::InvalidInput, - "Is a file", - ))), - PathEntry::Dir => Ok(()), - }, - None => { - self.entries.lock().insert(path, Arc::new(PathEntry::Dir)); - Ok(()) - } - } - } - async fn mkdir_async( - &self, - path: PathBuf, - recursive: bool, - mode: Option, - ) -> FsResult<()> { - self.mkdir_sync(&path, recursive, mode) - } - - fn chmod_sync(&self, _path: &Path, _mode: u32) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn chmod_async(&self, path: PathBuf, mode: u32) -> FsResult<()> { - self.chmod_sync(&path, mode) - } - - fn chown_sync( - &self, - _path: &Path, - _uid: Option, - _gid: Option, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn chown_async( - &self, - path: PathBuf, - uid: Option, - gid: Option, - ) -> FsResult<()> { - self.chown_sync(&path, uid, gid) - } - - fn lchown_sync( - &self, - _path: &Path, - _uid: Option, - _gid: Option, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - - async fn lchown_async( - &self, - path: PathBuf, - uid: Option, - gid: Option, - ) -> FsResult<()> { - self.lchown_sync(&path, uid, gid) - } - - fn remove_sync(&self, _path: &Path, _recursive: bool) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn remove_async(&self, path: PathBuf, recursive: bool) -> FsResult<()> { - self.remove_sync(&path, recursive) - } - - fn copy_file_sync(&self, _from: &Path, _to: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn copy_file_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> { - self.copy_file_sync(&from, &to) - } - - fn cp_sync(&self, _from: &Path, _to: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn cp_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> { - self.cp_sync(&from, &to) - } - - fn stat_sync(&self, path: &Path) -> FsResult { - let entry = self.get_entry(path); - match entry { - Some(entry) => match &*entry { - PathEntry::Dir => Ok(FsStat { - is_file: false, - is_directory: true, - is_symlink: false, - size: 0, - mtime: None, - atime: None, - birthtime: None, - ctime: None, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blksize: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }), - PathEntry::File(data) => Ok(FsStat { - is_file: true, - is_directory: false, - is_symlink: false, - size: data.len() as u64, - mtime: None, - atime: None, - birthtime: None, - ctime: None, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blksize: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }), - }, - None => Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))), - } - } - async fn stat_async(&self, path: PathBuf) -> FsResult { - self.stat_sync(&path) - } - - fn lstat_sync(&self, _path: &Path) -> FsResult { - Err(FsError::NotSupported) - } - async fn lstat_async(&self, path: PathBuf) -> FsResult { - self.lstat_sync(&path) - } - - fn realpath_sync(&self, _path: &Path) -> FsResult { - Err(FsError::NotSupported) - } - async fn realpath_async(&self, path: PathBuf) -> FsResult { - self.realpath_sync(&path) - } - - fn read_dir_sync(&self, _path: &Path) -> FsResult> { - Err(FsError::NotSupported) - } - async fn read_dir_async(&self, path: PathBuf) -> FsResult> { - self.read_dir_sync(&path) - } - - fn rename_sync(&self, _oldpath: &Path, _newpath: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn rename_async( - &self, - oldpath: PathBuf, - newpath: PathBuf, - ) -> FsResult<()> { - self.rename_sync(&oldpath, &newpath) - } - - fn link_sync(&self, _oldpath: &Path, _newpath: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn link_async( - &self, - oldpath: PathBuf, - newpath: PathBuf, - ) -> FsResult<()> { - self.link_sync(&oldpath, &newpath) - } - - fn symlink_sync( - &self, - _oldpath: &Path, - _newpath: &Path, - _file_type: Option, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn symlink_async( - &self, - oldpath: PathBuf, - newpath: PathBuf, - file_type: Option, - ) -> FsResult<()> { - self.symlink_sync(&oldpath, &newpath, file_type) - } - - fn read_link_sync(&self, _path: &Path) -> FsResult { - Err(FsError::NotSupported) - } - async fn read_link_async(&self, path: PathBuf) -> FsResult { - self.read_link_sync(&path) - } - - fn truncate_sync(&self, _path: &Path, _len: u64) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn truncate_async(&self, path: PathBuf, len: u64) -> FsResult<()> { - self.truncate_sync(&path, len) - } - - fn utime_sync( - &self, - _path: &Path, - _atime_secs: i64, - _atime_nanos: u32, - _mtime_secs: i64, - _mtime_nanos: u32, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn utime_async( - &self, - path: PathBuf, - atime_secs: i64, - atime_nanos: u32, - mtime_secs: i64, - mtime_nanos: u32, - ) -> FsResult<()> { - self.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) - } - - fn lutime_sync( - &self, - _path: &Path, - _atime_secs: i64, - _atime_nanos: u32, - _mtime_secs: i64, - _mtime_nanos: u32, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn lutime_async( - &self, - path: PathBuf, - atime_secs: i64, - atime_nanos: u32, - mtime_secs: i64, - mtime_nanos: u32, - ) -> FsResult<()> { - self.lutime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) - } - - fn write_file_sync( - &self, - path: &Path, - options: OpenOptions, - _access_check: Option, - data: &[u8], - ) -> FsResult<()> { - let path = normalize_path(path); - let has_parent_dir = path - .parent() - .and_then(|parent| self.get_entry(parent)) - .map(|e| matches!(*e, PathEntry::Dir)) - .unwrap_or(false); - if !has_parent_dir { - return Err(FsError::Io(Error::new( - ErrorKind::NotFound, - "Parent directory does not exist", - ))); - } - let mut entries = self.entries.lock(); - let entry = entries.entry(path.clone()); - match entry { - Entry::Occupied(mut entry) => { - if let PathEntry::File(existing_data) = &**entry.get() { - if options.create_new { - return Err(FsError::Io(Error::new( - ErrorKind::AlreadyExists, - "File already exists", - ))); - } - if options.append { - let mut new_data = existing_data.clone(); - new_data.extend_from_slice(data); - entry.insert(Arc::new(PathEntry::File(new_data))); - } else { - entry.insert(Arc::new(PathEntry::File(data.to_vec()))); - } - Ok(()) - } else { - Err(FsError::Io(Error::new( - ErrorKind::InvalidInput, - "Not a file", - ))) - } - } - Entry::Vacant(entry) => { - entry.insert(Arc::new(PathEntry::File(data.to_vec()))); - Ok(()) - } - } - } - - async fn write_file_async<'a>( - &'a self, - path: PathBuf, - options: OpenOptions, - access_check: Option>, - data: Vec, - ) -> FsResult<()> { - self.write_file_sync(&path, options, access_check, &data) - } - - fn read_file_sync( - &self, - path: &Path, - _access_check: Option, - ) -> FsResult> { - let entry = self.get_entry(path); - match entry { - Some(entry) => match &*entry { - PathEntry::File(data) => Ok(Cow::Owned(data.clone())), - PathEntry::Dir => Err(FsError::Io(Error::new( - ErrorKind::InvalidInput, - "Is a directory", - ))), - }, - None => Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))), - } - } - async fn read_file_async<'a>( - &'a self, - path: PathBuf, - access_check: Option>, - ) -> FsResult> { - self.read_file_sync(&path, access_check) - } -} diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs index 28a49c5d9b..0aae2b998f 100644 --- a/ext/fs/interface.rs +++ b/ext/fs/interface.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use core::str; use std::borrow::Cow; @@ -6,12 +6,11 @@ use std::path::Path; use std::path::PathBuf; use std::rc::Rc; -use serde::Deserialize; -use serde::Serialize; - use deno_io::fs::File; use deno_io::fs::FsResult; use deno_io::fs::FsStat; +use serde::Deserialize; +use serde::Serialize; use crate::sync::MaybeSend; use crate::sync::MaybeSync; @@ -71,7 +70,7 @@ pub enum FsFileType { } /// WARNING: This is part of the public JS Deno API. -#[derive(Serialize)] +#[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct FsDirEntry { pub name: String, diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index 26fac1e79f..89bd5bc09a 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -1,12 +1,17 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -mod in_memory_fs; mod interface; mod ops; mod std_fs; pub mod sync; -pub use crate::in_memory_fs::InMemoryFs; +use std::borrow::Cow; +use std::path::Path; +use std::path::PathBuf; + +use deno_io::fs::FsError; +use deno_permissions::PermissionCheckError; + pub use crate::interface::AccessCheckCb; pub use crate::interface::AccessCheckFn; pub use crate::interface::FileSystem; @@ -17,19 +22,11 @@ pub use crate::interface::OpenOptions; pub use crate::ops::FsOpsError; pub use crate::ops::FsOpsErrorKind; pub use crate::ops::OperationError; -pub use crate::ops::V8MaybeStaticStr; +use crate::ops::*; pub use crate::std_fs::RealFs; pub use crate::sync::MaybeSend; pub use crate::sync::MaybeSync; -use crate::ops::*; - -use deno_io::fs::FsError; -use deno_permissions::PermissionCheckError; -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; - pub trait FsPermissions { fn check_open<'a>( &mut self, diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index 5e64585e0c..9f5f3c6e90 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; @@ -11,15 +11,9 @@ use std::path::PathBuf; use std::path::StripPrefixError; use std::rc::Rc; -use crate::interface::AccessCheckFn; -use crate::interface::FileSystemRc; -use crate::interface::FsDirEntry; -use crate::interface::FsFileType; -use crate::FsPermissions; -use crate::OpenOptions; use boxed_error::Boxed; +use deno_core::error::ResourceError; use deno_core::op2; -use deno_core::v8; use deno_core::CancelFuture; use deno_core::CancelHandle; use deno_core::FastString; @@ -27,7 +21,7 @@ use deno_core::JsBuffer; use deno_core::OpState; use deno_core::ResourceId; use deno_core::ToJsBuffer; -use deno_core::ToV8; +use deno_error::JsErrorBox; use deno_io::fs::FileResource; use deno_io::fs::FsError; use deno_io::fs::FsStat; @@ -37,34 +31,53 @@ use rand::thread_rng; use rand::Rng; use serde::Serialize; -#[derive(Debug, Boxed)] +use crate::interface::AccessCheckFn; +use crate::interface::FileSystemRc; +use crate::interface::FsDirEntry; +use crate::interface::FsFileType; +use crate::FsPermissions; +use crate::OpenOptions; + +#[derive(Debug, Boxed, deno_error::JsError)] pub struct FsOpsError(pub Box); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum FsOpsErrorKind { + #[class(inherit)] #[error("{0}")] Io(#[source] std::io::Error), + #[class(inherit)] #[error("{0}")] OperationError(#[source] OperationError), + #[class(inherit)] #[error(transparent)] Permission(#[from] PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] ResourceError), + #[class("InvalidData")] #[error("File name or path {0:?} is not valid UTF-8")] InvalidUtf8(std::ffi::OsString), + #[class(generic)] #[error("{0}")] StripPrefix(#[from] StripPrefixError), + #[class(inherit)] #[error("{0}")] Canceled(#[from] deno_core::Canceled), + #[class(type)] #[error("Invalid seek mode: {0}")] InvalidSeekMode(i32), + #[class(generic)] #[error("Invalid control character in prefix or suffix: {0:?}")] InvalidControlCharacter(String), + #[class(generic)] #[error("Invalid character in prefix or suffix: {0:?}")] InvalidCharacter(String), #[cfg(windows)] + #[class(generic)] #[error("Invalid trailing character in suffix")] InvalidTrailingCharacter, + #[class("NotCapable")] #[error("Requires {err} access to {path}, {}", print_not_capable_info(*.standalone, .err))] NotCapableAccess { // NotCapable @@ -72,21 +85,21 @@ pub enum FsOpsErrorKind { err: &'static str, path: String, }, + #[class("NotCapable")] #[error("permission denied: {0}")] - NotCapable(&'static str), // NotCapable + NotCapable(&'static str), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(JsErrorBox), } impl From for FsOpsError { fn from(err: FsError) -> Self { match err { FsError::Io(err) => FsOpsErrorKind::Io(err), - FsError::FileBusy => { - FsOpsErrorKind::Other(deno_core::error::resource_unavailable()) - } + FsError::FileBusy => FsOpsErrorKind::Resource(ResourceError::Unavailable), FsError::NotSupported => { - FsOpsErrorKind::Other(deno_core::error::not_supported()) + FsOpsErrorKind::Other(JsErrorBox::not_supported()) } FsError::NotCapable(err) => FsOpsErrorKind::NotCapable(err), } @@ -1384,57 +1397,12 @@ where Ok(buf.into_owned().into_boxed_slice().into()) } -// todo(https://github.com/denoland/deno_core/pull/986): remove -// when upgrading deno_core -#[derive(Debug)] -pub struct FastStringV8AllocationError; - -impl std::error::Error for FastStringV8AllocationError {} - -impl std::fmt::Display for FastStringV8AllocationError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "failed to allocate string; buffer exceeds maximum length" - ) - } -} - -/// Maintains a static reference to the string if possible. -pub struct V8MaybeStaticStr(pub Cow<'static, str>); - -impl<'s> ToV8<'s> for V8MaybeStaticStr { - type Error = FastStringV8AllocationError; - - #[inline] - fn to_v8( - self, - scope: &mut v8::HandleScope<'s>, - ) -> Result, Self::Error> { - // todo(https://github.com/denoland/deno_core/pull/986): remove this check - // when upgrading deno_core - const MAX_V8_STRING_LENGTH: usize = 536870888; - if self.0.len() > MAX_V8_STRING_LENGTH { - return Err(FastStringV8AllocationError); - } - - Ok( - match self.0 { - Cow::Borrowed(text) => FastString::from_static(text), - Cow::Owned(value) => value.into(), - } - .v8_string(scope) - .into(), - ) - } -} - #[op2(stack_trace)] #[to_v8] pub fn op_fs_read_file_text_sync

( state: &mut OpState, #[string] path: String, -) -> Result +) -> Result where P: FsPermissions + 'static, { @@ -1446,7 +1414,10 @@ where let str = fs .read_text_file_lossy_sync(&path, Some(&mut access_check)) .map_err(|error| map_permission_error("readfile", error, &path))?; - Ok(V8MaybeStaticStr(str)) + Ok(match str { + Cow::Borrowed(text) => FastString::from_static(text), + Cow::Owned(value) => value.into(), + }) } #[op2(async, stack_trace)] @@ -1455,7 +1426,7 @@ pub async fn op_fs_read_file_text_async

( state: Rc>, #[string] path: String, #[smi] cancel_rid: Option, -) -> Result +) -> Result where P: FsPermissions + 'static, { @@ -1489,7 +1460,10 @@ where .map_err(|error| map_permission_error("readfile", error, &path))? }; - Ok(V8MaybeStaticStr(str)) + Ok(match str { + Cow::Borrowed(text) => FastString::from_static(text), + Cow::Owned(value) => value.into(), + }) } fn to_seek_from(offset: i64, whence: i32) -> Result { @@ -1706,10 +1680,12 @@ pub async fn op_fs_futime_async( Ok(()) } -#[derive(Debug)] +#[derive(Debug, deno_error::JsError)] +#[class(inherit)] pub struct OperationError { operation: &'static str, kind: OperationErrorKind, + #[inherit] pub err: FsError, } diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index 86ad213160..4845e9c7bc 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::disallowed_methods)] @@ -26,7 +26,7 @@ use crate::interface::FsFileType; use crate::FileSystem; use crate::OpenOptions; -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone)] pub struct RealFs; #[async_trait::async_trait(?Send)] @@ -61,7 +61,7 @@ impl FileSystem for RealFs { umask(Mode::from_bits_truncate(mask as mode_t)) } else { // If no mask provided, we query the current. Requires two syscalls. - let prev = umask(Mode::from_bits_truncate(0o777)); + let prev = umask(Mode::from_bits_truncate(0)); let _ = umask(prev); prev }; @@ -503,6 +503,7 @@ fn remove(path: &Path, recursive: bool) -> FsResult<()> { #[cfg(not(unix))] { use std::os::windows::prelude::MetadataExt; + use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY; if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 { fs::remove_dir(path) @@ -520,13 +521,14 @@ fn remove(path: &Path, recursive: bool) -> FsResult<()> { fn copy_file(from: &Path, to: &Path) -> FsResult<()> { #[cfg(target_os = "macos")] { - use libc::clonefile; - use libc::stat; - use libc::unlink; use std::ffi::CString; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::fs::PermissionsExt; + use libc::clonefile; + use libc::stat; + use libc::unlink; + let from_str = CString::new(from.as_os_str().as_encoded_bytes()) .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; let to_str = CString::new(to.as_os_str().as_encoded_bytes()) @@ -670,11 +672,12 @@ fn cp(from: &Path, to: &Path) -> FsResult<()> { #[cfg(target_os = "macos")] { // Just clonefile() - use libc::clonefile; - use libc::unlink; use std::ffi::CString; use std::os::unix::ffi::OsStrExt; + use libc::clonefile; + use libc::unlink; + let from_str = CString::new(from.as_os_str().as_bytes()) .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; let to_str = CString::new(to.as_os_str().as_bytes()) @@ -723,30 +726,34 @@ fn cp(from: &Path, to: &Path) -> FsResult<()> { } } - match (fs::metadata(to), fs::symlink_metadata(to)) { - (Ok(m), _) if m.is_dir() => cp_( - source_meta, - from, - &to.join(from.file_name().ok_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidInput, - "the source path is not a valid file", - ) - })?), - )?, - (_, Ok(m)) if is_identical(&source_meta, &m) => { + if let Ok(m) = fs::metadata(to) { + if m.is_dir() { + return cp_( + source_meta, + from, + &to.join(from.file_name().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "the source path is not a valid file", + ) + })?), + ); + } + } + + if let Ok(m) = fs::symlink_metadata(to) { + if is_identical(&source_meta, &m) { return Err( io::Error::new( io::ErrorKind::InvalidInput, "the source and destination are the same file", ) .into(), - ) + ); } - _ => cp_(source_meta, from, to)?, } - Ok(()) + cp_(source_meta, from, to) } #[cfg(not(windows))] @@ -757,11 +764,17 @@ fn stat(path: &Path) -> FsResult { #[cfg(windows)] fn stat(path: &Path) -> FsResult { - let metadata = fs::metadata(path)?; - let mut fsstat = FsStat::from_std(metadata); + use std::os::windows::fs::OpenOptionsExt; + use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; - let path = path.canonicalize()?; - stat_extra(&mut fsstat, &path, FILE_FLAG_BACKUP_SEMANTICS)?; + + let mut opts = fs::OpenOptions::new(); + opts.access_mode(0); // no read or write + opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS); + let file = opts.open(path)?; + let metadata = file.metadata()?; + let mut fsstat = FsStat::from_std(metadata); + stat_extra(&file, &mut fsstat)?; Ok(fsstat) } @@ -773,34 +786,24 @@ fn lstat(path: &Path) -> FsResult { #[cfg(windows)] fn lstat(path: &Path) -> FsResult { + use std::os::windows::fs::OpenOptionsExt; + use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; use winapi::um::winbase::FILE_FLAG_OPEN_REPARSE_POINT; - let metadata = fs::symlink_metadata(path)?; + let mut opts = fs::OpenOptions::new(); + opts.access_mode(0); // no read or write + opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); + let file = opts.open(path)?; + let metadata = file.metadata()?; let mut fsstat = FsStat::from_std(metadata); - stat_extra( - &mut fsstat, - path, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, - )?; + stat_extra(&file, &mut fsstat)?; Ok(fsstat) } #[cfg(windows)] -fn stat_extra( - fsstat: &mut FsStat, - path: &Path, - file_flags: winapi::shared::minwindef::DWORD, -) -> FsResult<()> { - use std::os::windows::prelude::OsStrExt; - - use winapi::um::fileapi::CreateFileW; - use winapi::um::fileapi::OPEN_EXISTING; - use winapi::um::handleapi::CloseHandle; - use winapi::um::handleapi::INVALID_HANDLE_VALUE; - use winapi::um::winnt::FILE_SHARE_DELETE; - use winapi::um::winnt::FILE_SHARE_READ; - use winapi::um::winnt::FILE_SHARE_WRITE; +fn stat_extra(file: &std::fs::File, fsstat: &mut FsStat) -> FsResult<()> { + use std::os::windows::io::AsRawHandle; unsafe fn get_dev( handle: winapi::shared::ntdef::HANDLE, @@ -869,23 +872,9 @@ fn stat_extra( // SAFETY: winapi calls unsafe { - let mut path: Vec<_> = path.as_os_str().encode_wide().collect(); - path.push(0); - let file_handle = CreateFileW( - path.as_ptr(), - 0, - FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, - std::ptr::null_mut(), - OPEN_EXISTING, - file_flags, - std::ptr::null_mut(), - ); - if file_handle == INVALID_HANDLE_VALUE { - return Err(std::io::Error::last_os_error().into()); - } + let file_handle = file.as_raw_handle(); - let result = get_dev(file_handle); - fsstat.dev = result?; + fsstat.dev = get_dev(file_handle)?; if let Ok(file_info) = query_file_information(file_handle) { fsstat.ctime = Some(windows_time_to_unix_time_msec( @@ -924,7 +913,6 @@ fn stat_extra( } } - CloseHandle(file_handle); Ok(()) } } @@ -940,6 +928,7 @@ fn exists(path: &Path) -> bool { #[cfg(windows)] { use std::os::windows::ffi::OsStrExt; + use winapi::um::fileapi::GetFileAttributesW; use winapi::um::fileapi::INVALID_FILE_ATTRIBUTES; diff --git a/ext/fs/sync.rs b/ext/fs/sync.rs index 6a913f658a..2c07ba42df 100644 --- a/ext/fs/sync.rs +++ b/ext/fs/sync.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use inner::*; @@ -6,10 +6,9 @@ pub use inner::*; mod inner { #![allow(clippy::disallowed_types)] - pub use std::sync::Arc as MaybeArc; - pub use core::marker::Send as MaybeSend; pub use core::marker::Sync as MaybeSync; + pub use std::sync::Arc as MaybeArc; } #[cfg(not(feature = "sync_fs"))] @@ -21,3 +20,9 @@ mod inner { pub trait MaybeSend {} impl MaybeSend for T where T: ?Sized {} } + +#[allow(clippy::disallowed_types)] +#[inline] +pub fn new_rc(value: T) -> MaybeArc { + MaybeArc::new(value) +} diff --git a/ext/http/00_serve.ts b/ext/http/00_serve.ts index 446533e910..5ce0a4bf7f 100644 --- a/ext/http/00_serve.ts +++ b/ext/http/00_serve.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; const { @@ -43,10 +43,7 @@ const { Uint8Array, Promise, } = primordials; -const { - getAsyncContext, - setAsyncContext, -} = core; +const { getAsyncContext, setAsyncContext } = core; import { InnerBody } from "ext:deno_fetch/22_body.js"; import { Event } from "ext:deno_web/02_event.js"; @@ -90,9 +87,8 @@ import { import { hasTlsKeyPairOptions, listenTls } from "ext:deno_net/02_tls.js"; import { SymbolAsyncDispose } from "ext:deno_web/00_infra.js"; import { - endSpan, + builtinTracer, enterSpan, - Span, TRACING_ENABLED, } from "ext:deno_telemetry/telemetry.ts"; import { @@ -288,28 +284,28 @@ class InnerRequest { // * is valid for OPTIONS if (path === "*") { - return this.#urlValue = "*"; + return (this.#urlValue = "*"); } // If the path is empty, return the authority (valid for CONNECT) if (path == "") { - return this.#urlValue = this.#methodAndUri[1]; + return (this.#urlValue = this.#methodAndUri[1]); } // CONNECT requires an authority if (this.#methodAndUri[0] == "CONNECT") { - return this.#urlValue = this.#methodAndUri[1]; + return (this.#urlValue = this.#methodAndUri[1]); } const hostname = this.#methodAndUri[1]; if (hostname) { // Construct a URL from the scheme, the hostname, and the path - return this.#urlValue = this.#context.scheme + hostname + path; + return (this.#urlValue = this.#context.scheme + hostname + path); } // Construct a URL from the scheme, the fallback hostname, and the path - return this.#urlValue = this.#context.scheme + this.#context.fallbackHost + - path; + return (this.#urlValue = this.#context.scheme + this.#context.fallbackHost + + path); } get completed() { @@ -370,7 +366,25 @@ class InnerRequest { return null; } this.#streamRid = op_http_read_request_body(this.#external); - this.#body = new InnerBody(readableStreamForRid(this.#streamRid, false)); + this.#body = new InnerBody( + readableStreamForRid( + this.#streamRid, + false, + undefined, + (controller, error) => { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + // TODO(kt3k): We would like to pass `error` as `cause` when BadResource supports it. + controller.error( + new error.constructor( + `Cannot read request body as underlying resource unavailable`, + ), + ); + } else { + controller.error(error); + } + }, + ), + ); return this.#body; } @@ -396,10 +410,7 @@ class InnerRequest { return; } - PromisePrototypeThen( - op_http_request_on_cancel(this.#external), - callback, - ); + PromisePrototypeThen(op_http_request_on_cancel(this.#external), callback); } } @@ -503,12 +514,7 @@ function fastSyncResponseOrStream( autoClose = true; } PromisePrototypeThen( - op_http_set_response_body_resource( - req, - rid, - autoClose, - status, - ), + op_http_set_response_body_resource(req, rid, autoClose, status), (success) => { innerRequest?.close(success); op_http_close_after_finish(req); @@ -538,10 +544,7 @@ function mapToCallback(context, callback, onError) { updateSpanFromRequest(span, request); } - response = await callback( - request, - new ServeHandlerInfo(innerRequest), - ); + response = await callback(request, new ServeHandlerInfo(innerRequest)); // Throwing Error if the handler return value is not a Response class if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) { @@ -618,12 +621,12 @@ function mapToCallback(context, callback, onError) { mapped = function (req, _span) { const oldCtx = getAsyncContext(); setAsyncContext(context.asyncContext); - const span = new Span("deno.serve", { kind: 1 }); + const span = builtinTracer().startSpan("deno.serve", { kind: 1 }); try { enterSpan(span); return SafePromisePrototypeFinally( origMapped(req, span), - () => endSpan(span), + () => span.end(), ); } finally { // equiv to exitSpan. @@ -670,7 +673,7 @@ function formatHostName(hostname: string): string { // because browsers in Windows don't resolve "0.0.0.0". // See the discussion in https://github.com/denoland/deno_std/issues/1165 if ( - (Deno.build.os === "windows") && + Deno.build.os === "windows" && (hostname == "0.0.0.0" || hostname == "::") ) { return "localhost"; @@ -712,11 +715,12 @@ function serve(arg1, arg2) { const wantsHttps = hasTlsKeyPairOptions(options); const wantsUnix = ObjectHasOwn(options, "path"); const signal = options.signal; - const onError = options.onError ?? function (error) { - // deno-lint-ignore no-console - console.error(error); - return internalServerError(); - }; + const onError = options.onError ?? + function (error) { + // deno-lint-ignore no-console + console.error(error); + return internalServerError(); + }; if (wantsUnix) { const listener = listen({ @@ -825,10 +829,7 @@ function serveHttpOn(context, addr, callback) { const promiseErrorHandler = (error) => { // Abnormal exit // deno-lint-ignore no-console - console.error( - "Terminating Deno.serve loop due to unexpected error", - error, - ); + console.error("Terminating Deno.serve loop due to unexpected error", error); context.close(); }; @@ -946,7 +947,7 @@ function registerDeclarativeServer(exports) { port: servePort, hostname: serveHost, [kLoadBalanced]: (serveIsMain && serveWorkerCount > 1) || - (serveWorkerCount !== null), + serveWorkerCount !== null, onListen: ({ port, hostname }) => { if (serveIsMain) { const nThreads = serveWorkerCount > 1 diff --git a/ext/http/01_http.js b/ext/http/01_http.js index 9302bd8a0f..83983fa0ca 100644 --- a/ext/http/01_http.js +++ b/ext/http/01_http.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { diff --git a/ext/http/02_websocket.ts b/ext/http/02_websocket.ts index 96af4d4822..4d37b04a1d 100644 --- a/ext/http/02_websocket.ts +++ b/ext/http/02_websocket.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { internals, primordials } from "ext:core/mod.js"; import { op_http_websocket_accept_header } from "ext:core/ops"; const { diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index 27a91ca61b..01198cb8e6 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_http" -version = "0.180.0" +version = "0.183.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -28,6 +28,7 @@ brotli.workspace = true bytes.workspace = true cache_control.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_net.workspace = true deno_websocket.workspace = true flate2.workspace = true diff --git a/ext/http/benches/compressible.rs b/ext/http/benches/compressible.rs index 5ac09cb8bb..96b21512ba 100644 --- a/ext/http/benches/compressible.rs +++ b/ext/http/benches/compressible.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use bencher::benchmark_group; use bencher::benchmark_main; use bencher::Bencher; diff --git a/ext/http/compressible.rs b/ext/http/compressible.rs index 6e96582e7e..5c499f957d 100644 --- a/ext/http/compressible.rs +++ b/ext/http/compressible.rs @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::str::FromStr; use phf::phf_set; -use std::str::FromStr; // Data obtained from https://github.com/jshttp/mime-db/blob/fa5e4ef3cc8907ec3c5ec5b85af0c63d7059a5cd/db.json // Important! Keep this list sorted alphabetically. diff --git a/ext/http/fly_accept_encoding.rs b/ext/http/fly_accept_encoding.rs index 4d6fd2231e..1be864fd3b 100644 --- a/ext/http/fly_accept_encoding.rs +++ b/ext/http/fly_accept_encoding.rs @@ -1,5 +1,5 @@ // Copyright 2018 Yoshua Wuyts. All rights reserved. MIT license. -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Forked from https://github.com/superfly/accept-encoding/blob/1cded757ec7ff3916e5bfe7441db76cdc48170dc/ // Forked to support both http 0.3 and http 1.0 crates. @@ -124,11 +124,12 @@ fn encodings_iter_inner<'s>( #[cfg(test)] mod tests { - use super::*; use http_v02::header::ACCEPT_ENCODING; use http_v02::HeaderMap; use http_v02::HeaderValue; + use super::*; + fn encodings( headers: &HeaderMap, ) -> Result, f32)>, EncodingError> { diff --git a/ext/http/http_next.rs b/ext/http/http_next.rs index 7dbac6021a..82edf817bf 100644 --- a/ext/http/http_next.rs +++ b/ext/http/http_next.rs @@ -1,24 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::compressible::is_content_compressible; -use crate::extract_network_stream; -use crate::network_buffered_stream::NetworkStreamPrefixCheck; -use crate::request_body::HttpRequestBody; -use crate::request_properties::HttpConnectionProperties; -use crate::request_properties::HttpListenProperties; -use crate::request_properties::HttpPropertyExtractor; -use crate::response_body::Compression; -use crate::response_body::ResponseBytesInner; -use crate::service::handle_request; -use crate::service::http_general_trace; -use crate::service::http_trace; -use crate::service::HttpRecord; -use crate::service::HttpRecordResponse; -use crate::service::HttpRequestBodyAutocloser; -use crate::service::HttpServerState; -use crate::service::SignallingRc; -use crate::websocket_upgrade::WebSocketUpgrade; -use crate::LocalExecutor; -use crate::Options; +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::ffi::c_void; +use std::future::Future; +use std::io; +use std::pin::Pin; +use std::ptr::null; +use std::rc::Rc; + use cache_control::CacheControl; use deno_core::external; use deno_core::futures::future::poll_fn; @@ -44,6 +33,7 @@ use deno_core::ResourceId; use deno_net::ops_tls::TlsStream; use deno_net::raw::NetworkStream; use deno_websocket::ws_create_server_stream; +use fly_accept_encoding::Encoding; use hyper::body::Incoming; use hyper::header::HeaderMap; use hyper::header::ACCEPT_ENCODING; @@ -63,21 +53,31 @@ use hyper::StatusCode; use hyper_util::rt::TokioIo; use once_cell::sync::Lazy; use smallvec::SmallVec; -use std::borrow::Cow; -use std::cell::RefCell; -use std::ffi::c_void; -use std::future::Future; -use std::io; -use std::pin::Pin; -use std::ptr::null; -use std::rc::Rc; - -use super::fly_accept_encoding; -use fly_accept_encoding::Encoding; - use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; +use super::fly_accept_encoding; +use crate::compressible::is_content_compressible; +use crate::extract_network_stream; +use crate::network_buffered_stream::NetworkStreamPrefixCheck; +use crate::request_body::HttpRequestBody; +use crate::request_properties::HttpConnectionProperties; +use crate::request_properties::HttpListenProperties; +use crate::request_properties::HttpPropertyExtractor; +use crate::response_body::Compression; +use crate::response_body::ResponseBytesInner; +use crate::service::handle_request; +use crate::service::http_general_trace; +use crate::service::http_trace; +use crate::service::HttpRecord; +use crate::service::HttpRecordResponse; +use crate::service::HttpRequestBodyAutocloser; +use crate::service::HttpServerState; +use crate::service::SignallingRc; +use crate::websocket_upgrade::WebSocketUpgrade; +use crate::LocalExecutor; +use crate::Options; + type Request = hyper::Request; static USE_WRITEV: Lazy = Lazy::new(|| { @@ -146,24 +146,44 @@ macro_rules! clone_external { }}; } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HttpNextError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error("{0}")] Io(#[from] io::Error), + #[class(inherit)] #[error(transparent)] WebSocketUpgrade(crate::websocket_upgrade::WebSocketUpgradeError), + #[class("Http")] #[error("{0}")] Hyper(#[from] hyper::Error), + #[class(inherit)] #[error(transparent)] - JoinError(#[from] tokio::task::JoinError), + JoinError( + #[from] + #[inherit] + tokio::task::JoinError, + ), + #[class(inherit)] #[error(transparent)] - Canceled(#[from] deno_core::Canceled), - #[error(transparent)] - HttpPropertyExtractor(deno_core::error::AnyError), + Canceled( + #[from] + #[inherit] + deno_core::Canceled, + ), + #[class(generic)] #[error(transparent)] UpgradeUnavailable(#[from] crate::service::UpgradeUnavailableError), + #[class(inherit)] + #[error("{0}")] + Other( + #[from] + #[inherit] + deno_error::JsErrorBox, + ), } #[op2(fast)] @@ -747,15 +767,9 @@ pub async fn op_http_set_response_body_resource( let resource = { let mut state = state.borrow_mut(); if auto_close { - state - .resource_table - .take_any(stream_rid) - .map_err(HttpNextError::Resource)? + state.resource_table.take_any(stream_rid)? } else { - state - .resource_table - .get_any(stream_rid) - .map_err(HttpNextError::Resource)? + state.resource_table.get_any(stream_rid)? } }; @@ -1063,8 +1077,7 @@ where HTTP: HttpPropertyExtractor, { let listener = - HTTP::get_listener_for_rid(&mut state.borrow_mut(), listener_rid) - .map_err(HttpNextError::Resource)?; + HTTP::get_listener_for_rid(&mut state.borrow_mut(), listener_rid)?; let listen_properties = HTTP::listen_properties_from_listener(&listener)?; @@ -1084,8 +1097,7 @@ where loop { let conn = HTTP::accept_connection_from_listener(&listener) .try_or_cancel(listen_cancel_clone.clone()) - .await - .map_err(HttpNextError::HttpPropertyExtractor)?; + .await?; serve_http_on::( conn, &listen_properties_clone, @@ -1120,8 +1132,7 @@ where HTTP: HttpPropertyExtractor, { let connection = - HTTP::get_connection_for_rid(&mut state.borrow_mut(), connection_rid) - .map_err(HttpNextError::Resource)?; + HTTP::get_connection_for_rid(&mut state.borrow_mut(), connection_rid)?; let listen_properties = HTTP::listen_properties_from_connection(&connection)?; @@ -1190,8 +1201,7 @@ pub async fn op_http_wait( let join_handle = state .borrow_mut() .resource_table - .get::(rid) - .map_err(HttpNextError::Resource)?; + .get::(rid)?; let cancel = join_handle.listen_cancel_handle(); let next = async { @@ -1236,7 +1246,7 @@ pub fn op_http_cancel( state: &mut OpState, #[smi] rid: ResourceId, graceful: bool, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), deno_core::error::ResourceError> { let join_handle = state.resource_table.get::(rid)?; if graceful { @@ -1260,8 +1270,7 @@ pub async fn op_http_close( let join_handle = state .borrow_mut() .resource_table - .take::(rid) - .map_err(HttpNextError::Resource)?; + .take::(rid)?; if graceful { http_general_trace!("graceful shutdown"); @@ -1390,11 +1399,8 @@ pub async fn op_raw_write_vectored( #[buffer] buf1: JsBuffer, #[buffer] buf2: JsBuffer, ) -> Result { - let resource: Rc = state - .borrow() - .resource_table - .get::(rid) - .map_err(HttpNextError::Resource)?; + let resource: Rc = + state.borrow().resource_table.get::(rid)?; let nwritten = resource.write_vectored(&buf1, &buf2).await?; Ok(nwritten) } diff --git a/ext/http/lib.rs b/ext/http/lib.rs index 39b0bbc2af..981ca9f0c0 100644 --- a/ext/http/lib.rs +++ b/ext/http/lib.rs @@ -1,4 +1,20 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cell::RefCell; +use std::cmp::min; +use std::error::Error; +use std::future::Future; +use std::io; +use std::io::Write; +use std::mem::replace; +use std::mem::take; +use std::pin::pin; +use std::pin::Pin; +use std::rc::Rc; +use std::sync::Arc; +use std::task::Context; +use std::task::Poll; use async_compression::tokio::write::BrotliEncoder; use async_compression::tokio::write::GzipEncoder; @@ -35,6 +51,7 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::StringOrBuffer; +use deno_error::JsErrorBox; use deno_net::raw::NetworkStream; use deno_websocket::ws_create_server_stream; use flate2::write::GzEncoder; @@ -54,21 +71,6 @@ use hyper_v014::HeaderMap; use hyper_v014::Request; use hyper_v014::Response; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::cmp::min; -use std::error::Error; -use std::future::Future; -use std::io; -use std::io::Write; -use std::mem::replace; -use std::mem::take; -use std::pin::pin; -use std::pin::Pin; -use std::rc::Rc; -use std::sync::Arc; -use std::task::Context; -use std::task::Poll; use tokio::io::AsyncRead; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; @@ -164,36 +166,50 @@ deno_core::extension!( } ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HttpError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), + #[class("Http")] #[error("{0}")] HyperV014(#[source] Arc), + #[class(generic)] #[error("{0}")] InvalidHeaderName(#[from] hyper_v014::header::InvalidHeaderName), + #[class(generic)] #[error("{0}")] InvalidHeaderValue(#[from] hyper_v014::header::InvalidHeaderValue), + #[class(generic)] #[error("{0}")] Http(#[from] hyper_v014::http::Error), + #[class("Http")] #[error("response headers already sent")] ResponseHeadersAlreadySent, + #[class("Http")] #[error("connection closed while sending response")] ConnectionClosedWhileSendingResponse, + #[class("Http")] #[error("already in use")] AlreadyInUse, + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), + #[class("Http")] #[error("no response headers")] NoResponseHeaders, + #[class("Http")] #[error("response already completed")] ResponseAlreadyCompleted, + #[class("Http")] #[error("cannot upgrade because request body was used")] UpgradeBodyUsed, + #[class("Http")] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[from] JsErrorBox), } pub enum HttpSocketAddr { @@ -485,7 +501,9 @@ impl Resource for HttpStreamReadResource { Some(_) => match body.as_mut().next().await.unwrap() { Ok(chunk) => assert!(chunk.is_empty()), Err(err) => { - break Err(HttpError::HyperV014(Arc::new(err)).into()) + break Err(JsErrorBox::from_err(HttpError::HyperV014( + Arc::new(err), + ))) } }, None => break Ok(BufView::empty()), @@ -609,11 +627,7 @@ async fn op_http_accept( state: Rc>, #[smi] rid: ResourceId, ) -> Result, HttpError> { - let conn = state - .borrow() - .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + let conn = state.borrow().resource_table.get::(rid)?; match conn.accept().await { Ok(Some((read_stream, write_stream, method, url))) => { @@ -728,8 +742,7 @@ async fn op_http_write_headers( let stream = state .borrow_mut() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; // Track supported encoding let encoding = stream.accept_encoding; @@ -794,10 +807,7 @@ fn op_http_headers( state: &mut OpState, #[smi] rid: u32, ) -> Result, HttpError> { - let stream = state - .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + let stream = state.resource_table.get::(rid)?; let rd = RcRef::map(&stream, |r| &r.rd) .try_borrow() .ok_or(HttpError::AlreadyInUse)?; @@ -953,14 +963,9 @@ async fn op_http_write_resource( let http_stream = state .borrow() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; let mut wr = RcRef::map(&http_stream, |r| &r.wr).borrow_mut().await; - let resource = state - .borrow() - .resource_table - .get_any(stream) - .map_err(HttpError::Resource)?; + let resource = state.borrow().resource_table.get_any(stream)?; loop { match *wr { HttpResponseWriter::Headers(_) => { @@ -972,11 +977,7 @@ async fn op_http_write_resource( _ => {} }; - let view = resource - .clone() - .read(64 * 1024) - .await - .map_err(HttpError::Other)?; // 64KB + let view = resource.clone().read(64 * 1024).await?; // 64KB if view.is_empty() { break; } @@ -1021,8 +1022,7 @@ async fn op_http_write( let stream = state .borrow() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; let mut wr = RcRef::map(&stream, |r| &r.wr).borrow_mut().await; match &mut *wr { @@ -1074,8 +1074,7 @@ async fn op_http_shutdown( let stream = state .borrow() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; let mut wr = RcRef::map(&stream, |r| &r.wr).borrow_mut().await; let wr = take(&mut *wr); match wr { @@ -1121,8 +1120,7 @@ async fn op_http_upgrade_websocket( let stream = state .borrow_mut() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; let mut rd = RcRef::map(&stream, |r| &r.rd).borrow_mut().await; let request = match &mut *rd { diff --git a/ext/http/network_buffered_stream.rs b/ext/http/network_buffered_stream.rs index 73df2dbd9f..5882dd14e3 100644 --- a/ext/http/network_buffered_stream.rs +++ b/ext/http/network_buffered_stream.rs @@ -1,12 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use bytes::Bytes; -use deno_core::futures::future::poll_fn; -use deno_core::futures::ready; use std::io; use std::mem::MaybeUninit; use std::pin::Pin; use std::task::Poll; + +use bytes::Bytes; +use deno_core::futures::future::poll_fn; +use deno_core::futures::ready; use tokio::io::AsyncRead; use tokio::io::AsyncWrite; use tokio::io::ReadBuf; @@ -227,9 +228,10 @@ impl AsyncWrite #[cfg(test)] mod tests { - use super::*; use tokio::io::AsyncReadExt; + use super::*; + struct YieldsOneByteAtATime(&'static [u8]); impl AsyncRead for YieldsOneByteAtATime { diff --git a/ext/http/reader_stream.rs b/ext/http/reader_stream.rs index be6d571b1a..b6985927f4 100644 --- a/ext/http/reader_stream.rs +++ b/ext/http/reader_stream.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::pin::Pin; use std::sync::atomic::AtomicBool; @@ -66,11 +66,12 @@ impl Stream for ExternallyAbortableReaderStream { #[cfg(test)] mod tests { - use super::*; use bytes::Bytes; use deno_core::futures::StreamExt; use tokio::io::AsyncWriteExt; + use super::*; + #[tokio::test] async fn success() { let (a, b) = tokio::io::duplex(64 * 1024); diff --git a/ext/http/request_body.rs b/ext/http/request_body.rs index f1c3f358ea..50ca1635c3 100644 --- a/ext/http/request_body.rs +++ b/ext/http/request_body.rs @@ -1,4 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::pin::Pin; +use std::rc::Rc; +use std::task::ready; +use std::task::Poll; + use bytes::Bytes; use deno_core::futures::stream::Peekable; use deno_core::futures::Stream; @@ -9,14 +15,10 @@ use deno_core::AsyncResult; use deno_core::BufView; use deno_core::RcRef; use deno_core::Resource; +use deno_error::JsErrorBox; use hyper::body::Body; use hyper::body::Incoming; use hyper::body::SizeHint; -use std::borrow::Cow; -use std::pin::Pin; -use std::rc::Rc; -use std::task::ready; -use std::task::Poll; /// Converts a hyper incoming body stream into a stream of [`Bytes`] that we can use to read in V8. struct ReadFuture(Incoming); @@ -82,7 +84,10 @@ impl Resource for HttpRequestBody { } fn read(self: Rc, limit: usize) -> AsyncResult { - Box::pin(HttpRequestBody::read(self, limit).map_err(Into::into)) + Box::pin( + HttpRequestBody::read(self, limit) + .map_err(|e| JsErrorBox::new("Http", e.to_string())), + ) } fn size_hint(&self) -> (u64, Option) { diff --git a/ext/http/request_properties.rs b/ext/http/request_properties.rs index 39d35a79f1..32ae33a34a 100644 --- a/ext/http/request_properties.rs +++ b/ext/http/request_properties.rs @@ -1,7 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::error::AnyError; +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::net::Ipv4Addr; +use std::net::SocketAddr; +use std::net::SocketAddrV4; +use std::rc::Rc; + use deno_core::OpState; use deno_core::ResourceId; +use deno_error::JsErrorBox; use deno_net::raw::take_network_stream_listener_resource; use deno_net::raw::take_network_stream_resource; use deno_net::raw::NetworkStream; @@ -11,11 +17,6 @@ use deno_net::raw::NetworkStreamType; use hyper::header::HOST; use hyper::HeaderMap; use hyper::Uri; -use std::borrow::Cow; -use std::net::Ipv4Addr; -use std::net::SocketAddr; -use std::net::SocketAddrV4; -use std::rc::Rc; // TODO(mmastrac): I don't like that we have to clone this, but it's one-time setup #[derive(Clone)] @@ -49,13 +50,13 @@ pub trait HttpPropertyExtractor { fn get_listener_for_rid( state: &mut OpState, listener_rid: ResourceId, - ) -> Result; + ) -> Result; /// Given a connection [`ResourceId`], returns the [`HttpPropertyExtractor::Connection`]. fn get_connection_for_rid( state: &mut OpState, connection_rid: ResourceId, - ) -> Result; + ) -> Result; /// Determines the listener properties. fn listen_properties_from_listener( @@ -70,7 +71,7 @@ pub trait HttpPropertyExtractor { /// Accept a new [`HttpPropertyExtractor::Connection`] from the given listener [`HttpPropertyExtractor::Listener`]. async fn accept_connection_from_listener( listener: &Self::Listener, - ) -> Result; + ) -> Result; /// Determines the connection properties. fn connection_properties( @@ -102,7 +103,7 @@ impl HttpPropertyExtractor for DefaultHttpPropertyExtractor { fn get_listener_for_rid( state: &mut OpState, listener_rid: ResourceId, - ) -> Result { + ) -> Result { take_network_stream_listener_resource( &mut state.resource_table, listener_rid, @@ -112,17 +113,18 @@ impl HttpPropertyExtractor for DefaultHttpPropertyExtractor { fn get_connection_for_rid( state: &mut OpState, stream_rid: ResourceId, - ) -> Result { + ) -> Result { take_network_stream_resource(&mut state.resource_table, stream_rid) + .map_err(JsErrorBox::from_err) } async fn accept_connection_from_listener( listener: &NetworkStreamListener, - ) -> Result { + ) -> Result { listener .accept() .await - .map_err(Into::into) + .map_err(JsErrorBox::from_err) .map(|(stm, _)| stm) } diff --git a/ext/http/response_body.rs b/ext/http/response_body.rs index bac43bf3c8..6960e7c0fb 100644 --- a/ext/http/response_body.rs +++ b/ext/http/response_body.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::Write; use std::pin::Pin; use std::rc::Rc; @@ -9,12 +9,12 @@ use brotli::enc::encode::BrotliEncoderStateStruct; use brotli::writer::StandardAlloc; use bytes::Bytes; use bytes::BytesMut; -use deno_core::error::AnyError; use deno_core::futures::ready; use deno_core::futures::FutureExt; use deno_core::AsyncResult; use deno_core::BufView; use deno_core::Resource; +use deno_error::JsErrorBox; use flate2::write::GzEncoder; use hyper::body::Frame; use hyper::body::SizeHint; @@ -32,10 +32,10 @@ pub enum ResponseStreamResult { /// will only be returned from compression streams that require additional buffering. NoData, /// Stream failed. - Error(AnyError), + Error(JsErrorBox), } -impl From for Option, AnyError>> { +impl From for Option, JsErrorBox>> { fn from(value: ResponseStreamResult) -> Self { match value { ResponseStreamResult::EndOfStream => None, @@ -411,7 +411,9 @@ impl PollFrame for GZipResponseStream { }; let len = stm.total_out() - start_out; let res = match res { - Err(err) => ResponseStreamResult::Error(err.into()), + Err(err) => { + ResponseStreamResult::Error(JsErrorBox::generic(err.to_string())) + } Ok(flate2::Status::BufError) => { // This should not happen unreachable!("old={orig_state:?} new={state:?} buf_len={}", buf.len()); @@ -573,12 +575,14 @@ impl PollFrame for BrotliResponseStream { #[allow(clippy::print_stderr)] #[cfg(test)] mod tests { - use super::*; - use deno_core::futures::future::poll_fn; use std::hash::Hasher; use std::io::Read; use std::io::Write; + use deno_core::futures::future::poll_fn; + + use super::*; + fn zeros() -> Vec { vec![0; 1024 * 1024] } diff --git a/ext/http/service.rs b/ext/http/service.rs index ce24dea43f..f220f8d8a7 100644 --- a/ext/http/service.rs +++ b/ext/http/service.rs @@ -1,21 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::request_properties::HttpConnectionProperties; -use crate::response_body::ResponseBytesInner; -use crate::response_body::ResponseStreamResult; -use deno_core::futures::ready; -use deno_core::BufView; -use deno_core::OpState; -use deno_core::ResourceId; -use http::request::Parts; -use hyper::body::Body; -use hyper::body::Frame; -use hyper::body::Incoming; -use hyper::body::SizeHint; -use hyper::header::HeaderMap; -use hyper::upgrade::OnUpgrade; - -use scopeguard::guard; -use scopeguard::ScopeGuard; +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::Cell; use std::cell::Ref; use std::cell::RefCell; @@ -27,8 +10,27 @@ use std::rc::Rc; use std::task::Context; use std::task::Poll; use std::task::Waker; + +use deno_core::futures::ready; +use deno_core::BufView; +use deno_core::OpState; +use deno_core::ResourceId; +use deno_error::JsErrorBox; +use http::request::Parts; +use hyper::body::Body; +use hyper::body::Frame; +use hyper::body::Incoming; +use hyper::body::SizeHint; +use hyper::header::HeaderMap; +use hyper::upgrade::OnUpgrade; +use scopeguard::guard; +use scopeguard::ScopeGuard; use tokio::sync::oneshot; +use crate::request_properties::HttpConnectionProperties; +use crate::response_body::ResponseBytesInner; +use crate::response_body::ResponseStreamResult; + pub type Request = hyper::Request; pub type Response = hyper::Response; @@ -528,7 +530,7 @@ pub struct HttpRecordResponse(ManuallyDrop>); impl Body for HttpRecordResponse { type Data = BufView; - type Error = deno_core::error::AnyError; + type Error = JsErrorBox; fn poll_frame( self: Pin<&mut Self>, @@ -606,16 +608,18 @@ impl Drop for HttpRecordResponse { #[cfg(test)] mod tests { - use super::*; - use crate::response_body::Compression; - use crate::response_body::ResponseBytesInner; + use std::error::Error as StdError; + use bytes::Buf; use deno_net::raw::NetworkStreamType; use hyper::body::Body; use hyper::service::service_fn; use hyper::service::HttpService; use hyper_util::rt::TokioIo; - use std::error::Error as StdError; + + use super::*; + use crate::response_body::Compression; + use crate::response_body::ResponseBytesInner; /// Execute client request on service and concurrently map the response. async fn serve_request( diff --git a/ext/http/websocket_upgrade.rs b/ext/http/websocket_upgrade.rs index af9504717e..e030f1c7ae 100644 --- a/ext/http/websocket_upgrade.rs +++ b/ext/http/websocket_upgrade.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::marker::PhantomData; @@ -12,22 +12,30 @@ use memmem::Searcher; use memmem::TwoWaySearcher; use once_cell::sync::OnceCell; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WebSocketUpgradeError { + #[class("Http")] #[error("invalid headers")] InvalidHeaders, + #[class(generic)] #[error("{0}")] HttpParse(#[from] httparse::Error), + #[class(generic)] #[error("{0}")] Http(#[from] http::Error), + #[class(generic)] #[error("{0}")] Utf8(#[from] std::str::Utf8Error), + #[class(generic)] #[error("{0}")] InvalidHeaderName(#[from] http::header::InvalidHeaderName), + #[class(generic)] #[error("{0}")] InvalidHeaderValue(#[from] http::header::InvalidHeaderValue), + #[class("Http")] #[error("invalid HTTP status line")] InvalidHttpStatusLine, + #[class("Http")] #[error("attempted to write to completed upgrade buffer")] UpgradeBufferAlreadyCompleted, } @@ -169,9 +177,10 @@ impl WebSocketUpgrade { #[cfg(test)] mod tests { - use super::*; use hyper_v014::Body; + use super::*; + type ExpectedResponseAndHead = Option<(Response, &'static [u8])>; fn assert_response( diff --git a/ext/io/12_io.js b/ext/io/12_io.js index 3cdcb113ba..b2556649ee 100644 --- a/ext/io/12_io.js +++ b/ext/io/12_io.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Interfaces 100% copied from Go. // Documentation liberally lifted from them too. diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 1b73bad348..0de9dfca84 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_io" -version = "0.92.0" +version = "0.95.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] async-trait.workspace = true deno_core.workspace = true +deno_error.workspace = true filetime.workspace = true fs3.workspace = true log.workspace = true diff --git a/ext/io/bi_pipe.rs b/ext/io/bi_pipe.rs index bf65d3037f..33c3267075 100644 --- a/ext/io/bi_pipe.rs +++ b/ext/io/bi_pipe.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::rc::Rc; @@ -338,6 +338,11 @@ pub fn bi_pipe_pair_raw( // TODO(nathanwhit): more granular unsafe blocks // SAFETY: win32 calls unsafe { + use std::io; + use std::os::windows::ffi::OsStrExt; + use std::path::Path; + use std::ptr; + use windows_sys::Win32::Foundation::CloseHandle; use windows_sys::Win32::Foundation::ERROR_ACCESS_DENIED; use windows_sys::Win32::Foundation::ERROR_PIPE_CONNECTED; @@ -355,11 +360,6 @@ pub fn bi_pipe_pair_raw( use windows_sys::Win32::System::Pipes::PIPE_READMODE_BYTE; use windows_sys::Win32::System::Pipes::PIPE_TYPE_BYTE; - use std::io; - use std::os::windows::ffi::OsStrExt; - use std::path::Path; - use std::ptr; - let (path, hd1) = loop { let name = format!("\\\\.\\pipe\\{}", uuid::Uuid::new_v4()); let mut path = Path::new(&name) diff --git a/ext/io/fs.rs b/ext/io/fs.rs index bd5dfd0bb9..d8767aa116 100644 --- a/ext/io/fs.rs +++ b/ext/io/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::fmt::Formatter; @@ -7,18 +7,24 @@ use std::rc::Rc; use std::time::SystemTime; use std::time::UNIX_EPOCH; +use deno_core::error::ResourceError; use deno_core::BufMutView; use deno_core::BufView; use deno_core::OpState; use deno_core::ResourceHandleFd; use deno_core::ResourceId; +use deno_error::JsErrorBox; use tokio::task::JoinError; -#[derive(Debug)] +#[derive(Debug, deno_error::JsError)] pub enum FsError { + #[class(inherit)] Io(io::Error), + #[class("Busy")] FileBusy, + #[class(not_supported)] NotSupported, + #[class("NotCapable")] NotCapable(&'static str), } @@ -277,18 +283,21 @@ impl FileResource { state: &OpState, rid: ResourceId, f: F, - ) -> Result + ) -> Result where - F: FnOnce(Rc) -> Result, + F: FnOnce(Rc) -> Result, { - let resource = state.resource_table.get::(rid)?; + let resource = state + .resource_table + .get::(rid) + .map_err(JsErrorBox::from_err)?; f(resource) } pub fn get_file( state: &OpState, rid: ResourceId, - ) -> Result, deno_core::error::AnyError> { + ) -> Result, ResourceError> { let resource = state.resource_table.get::(rid)?; Ok(resource.file()) } @@ -297,9 +306,9 @@ impl FileResource { state: &OpState, rid: ResourceId, f: F, - ) -> Result + ) -> Result where - F: FnOnce(Rc) -> Result, + F: FnOnce(Rc) -> Result, { Self::with_resource(state, rid, |r| f(r.file.clone())) } @@ -321,7 +330,7 @@ impl deno_core::Resource for FileResource { .clone() .read(limit) .await - .map_err(|err| err.into()) + .map_err(JsErrorBox::from_err) }) } @@ -335,7 +344,7 @@ impl deno_core::Resource for FileResource { .clone() .read_byob(buf) .await - .map_err(|err| err.into()) + .map_err(JsErrorBox::from_err) }) } @@ -344,7 +353,12 @@ impl deno_core::Resource for FileResource { buf: BufView, ) -> deno_core::AsyncResult { Box::pin(async move { - self.file.clone().write(buf).await.map_err(|err| err.into()) + self + .file + .clone() + .write(buf) + .await + .map_err(JsErrorBox::from_err) }) } @@ -355,22 +369,27 @@ impl deno_core::Resource for FileResource { .clone() .write_all(buf) .await - .map_err(|err| err.into()) + .map_err(JsErrorBox::from_err) }) } fn read_byob_sync( self: Rc, data: &mut [u8], - ) -> Result { - self.file.clone().read_sync(data).map_err(|err| err.into()) + ) -> Result { + self + .file + .clone() + .read_sync(data) + .map_err(JsErrorBox::from_err) } - fn write_sync( - self: Rc, - data: &[u8], - ) -> Result { - self.file.clone().write_sync(data).map_err(|err| err.into()) + fn write_sync(self: Rc, data: &[u8]) -> Result { + self + .file + .clone() + .write_sync(data) + .map_err(JsErrorBox::from_err) } fn backing_fd(self: Rc) -> Option { diff --git a/ext/io/lib.rs b/ext/io/lib.rs index 873fccd7b8..1f92ae5c8b 100644 --- a/ext/io/lib.rs +++ b/ext/io/lib.rs @@ -1,5 +1,23 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::fs::File as StdFile; +use std::future::Future; +use std::io; +use std::io::ErrorKind; +use std::io::Read; +use std::io::Seek; +use std::io::Write; +#[cfg(unix)] +use std::os::unix::io::FromRawFd; +#[cfg(windows)] +use std::os::windows::io::FromRawHandle; +use std::rc::Rc; +#[cfg(windows)] +use std::sync::Arc; + +use deno_core::futures::TryFutureExt; use deno_core::op2; use deno_core::unsync::spawn_blocking; use deno_core::unsync::TaskQueue; @@ -15,46 +33,27 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceHandle; use deno_core::ResourceHandleFd; +use deno_error::JsErrorBox; use fs::FileResource; use fs::FsError; use fs::FsResult; use fs::FsStat; use fs3::FileExt; use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::cell::RefCell; -use std::fs::File as StdFile; -use std::future::Future; -use std::io; -use std::io::ErrorKind; -use std::io::Read; -use std::io::Seek; -use std::io::Write; -use std::rc::Rc; +#[cfg(windows)] +use parking_lot::Condvar; +#[cfg(windows)] +use parking_lot::Mutex; use tokio::io::AsyncRead; use tokio::io::AsyncReadExt; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; use tokio::process; - -#[cfg(unix)] -use std::os::unix::io::FromRawFd; - -#[cfg(windows)] -use std::os::windows::io::FromRawHandle; #[cfg(windows)] use winapi::um::processenv::GetStdHandle; #[cfg(windows)] use winapi::um::winbase; -use deno_core::futures::TryFutureExt; -#[cfg(windows)] -use parking_lot::Condvar; -#[cfg(windows)] -use parking_lot::Mutex; -#[cfg(windows)] -use std::sync::Arc; - pub mod fs; mod pipe; #[cfg(windows)] @@ -62,19 +61,18 @@ mod winpipe; mod bi_pipe; -pub use pipe::pipe; -pub use pipe::AsyncPipeRead; -pub use pipe::AsyncPipeWrite; -pub use pipe::PipeRead; -pub use pipe::PipeWrite; -pub use pipe::RawPipeHandle; - pub use bi_pipe::bi_pipe_pair_raw; pub use bi_pipe::BiPipe; pub use bi_pipe::BiPipeRead; pub use bi_pipe::BiPipeResource; pub use bi_pipe::BiPipeWrite; pub use bi_pipe::RawBiPipeHandle; +pub use pipe::pipe; +pub use pipe::AsyncPipeRead; +pub use pipe::AsyncPipeWrite; +pub use pipe::PipeRead; +pub use pipe::PipeWrite; +pub use pipe::RawPipeHandle; /// Abstraction over `AsRawFd` (unix) and `AsRawHandle` (windows) pub trait AsRawIoHandle { @@ -417,7 +415,7 @@ impl Resource for ChildStdinResource { deno_core::impl_writable!(); fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown().map_err(|e| e.into())) + Box::pin(self.shutdown().map_err(JsErrorBox::from_err)) } } @@ -1010,9 +1008,11 @@ pub fn op_print( state: &mut OpState, #[string] msg: &str, is_err: bool, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), JsErrorBox> { let rid = if is_err { 2 } else { 1 }; FileResource::with_file(state, rid, move |file| { - Ok(file.write_all_sync(msg.as_bytes())?) + file + .write_all_sync(msg.as_bytes()) + .map_err(JsErrorBox::from_err) }) } diff --git a/ext/io/pipe.rs b/ext/io/pipe.rs index e0e019e277..5172bea10e 100644 --- a/ext/io/pipe.rs +++ b/ext/io/pipe.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io; use std::pin::Pin; use std::process::Stdio; @@ -299,13 +299,14 @@ pub fn pipe_impl() -> io::Result<(PipeRead, PipeWrite)> { #[cfg(test)] mod tests { - use super::*; - use std::io::Read; use std::io::Write; + use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; + use super::*; + #[test] fn test_pipe() { let (mut read, mut write) = pipe().unwrap(); diff --git a/ext/io/winpipe.rs b/ext/io/winpipe.rs index 01d018008d..600eda4091 100644 --- a/ext/io/winpipe.rs +++ b/ext/io/winpipe.rs @@ -1,10 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use rand::thread_rng; -use rand::RngCore; +// Copyright 2018-2025 the Deno authors. MIT license. use std::io; use std::os::windows::io::RawHandle; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; + +use rand::thread_rng; +use rand::RngCore; use winapi::shared::minwindef::DWORD; use winapi::um::errhandlingapi::GetLastError; use winapi::um::fileapi::CreateFileA; @@ -116,7 +117,6 @@ fn create_named_pipe_inner() -> io::Result<(RawHandle, RawHandle)> { #[cfg(test)] mod tests { - use super::*; use std::fs::File; use std::io::Read; use std::io::Write; @@ -124,6 +124,8 @@ mod tests { use std::sync::Arc; use std::sync::Barrier; + use super::*; + #[test] fn make_named_pipe() { let (server, client) = create_named_pipe().unwrap(); diff --git a/ext/kv/01_db.ts b/ext/kv/01_db.ts index c644ff7121..37d4c58c11 100644 --- a/ext/kv/01_db.ts +++ b/ext/kv/01_db.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { @@ -77,7 +77,9 @@ const maxQueueBackoffInterval = 60 * 60 * 1000; function validateBackoffSchedule(backoffSchedule: number[]) { if (backoffSchedule.length > maxQueueBackoffIntervals) { - throw new TypeError("Invalid backoffSchedule"); + throw new TypeError( + `Invalid backoffSchedule, max ${maxQueueBackoffIntervals} intervals allowed`, + ); } for (let i = 0; i < backoffSchedule.length; ++i) { const interval = backoffSchedule[i]; @@ -85,7 +87,9 @@ function validateBackoffSchedule(backoffSchedule: number[]) { interval < 0 || interval > maxQueueBackoffInterval || NumberIsNaN(interval) ) { - throw new TypeError("Invalid backoffSchedule"); + throw new TypeError( + `Invalid backoffSchedule, interval at index ${i} is invalid`, + ); } } } diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index c69a962fa3..726dccdfba 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_kv" -version = "0.90.0" +version = "0.93.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -21,6 +21,7 @@ boxed_error.workspace = true bytes.workspace = true chrono = { workspace = true, features = ["now"] } deno_core.workspace = true +deno_error.workspace = true deno_fetch.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true diff --git a/ext/kv/config.rs b/ext/kv/config.rs index 7166bcbcc2..f762a74578 100644 --- a/ext/kv/config.rs +++ b/ext/kv/config.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[derive(Clone, Copy, Debug)] pub struct KvConfig { diff --git a/ext/kv/dynamic.rs b/ext/kv/dynamic.rs index 6d545d79f6..923e3cd4d8 100644 --- a/ext/kv/dynamic.rs +++ b/ext/kv/dynamic.rs @@ -1,8 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::rc::Rc; +use async_trait::async_trait; +use deno_core::OpState; +use deno_error::JsErrorBox; +use denokv_proto::CommitResult; +use denokv_proto::ReadRangeOutput; +use denokv_proto::WatchStream; + use crate::remote::RemoteDbHandlerPermissions; use crate::sqlite::SqliteDbHandler; use crate::sqlite::SqliteDbHandlerPermissions; @@ -12,13 +19,6 @@ use crate::DatabaseHandler; use crate::QueueMessageHandle; use crate::ReadRange; use crate::SnapshotReadOptions; -use async_trait::async_trait; -use deno_core::error::type_error; -use deno_core::error::AnyError; -use deno_core::OpState; -use denokv_proto::CommitResult; -use denokv_proto::ReadRangeOutput; -use denokv_proto::WatchStream; pub struct MultiBackendDbHandler { backends: Vec<(&'static [&'static str], Box)>, @@ -62,7 +62,7 @@ impl DatabaseHandler for MultiBackendDbHandler { &self, state: Rc>, path: Option, - ) -> Result { + ) -> Result { for (prefixes, handler) in &self.backends { for &prefix in *prefixes { if prefix.is_empty() { @@ -76,7 +76,7 @@ impl DatabaseHandler for MultiBackendDbHandler { } } } - Err(type_error(format!( + Err(JsErrorBox::type_error(format!( "No backend supports the given path: {:?}", path ))) @@ -89,7 +89,7 @@ pub trait DynamicDbHandler { &self, state: Rc>, path: Option, - ) -> Result; + ) -> Result; } #[async_trait(?Send)] @@ -100,7 +100,7 @@ impl DatabaseHandler for Box { &self, state: Rc>, path: Option, - ) -> Result { + ) -> Result { (**self).dyn_open(state, path).await } } @@ -115,7 +115,7 @@ where &self, state: Rc>, path: Option, - ) -> Result { + ) -> Result { Ok(RcDynamicDb(Rc::new(self.open(state, path).await?))) } } @@ -126,16 +126,16 @@ pub trait DynamicDb { &self, requests: Vec, options: SnapshotReadOptions, - ) -> Result, AnyError>; + ) -> Result, JsErrorBox>; async fn dyn_atomic_write( &self, write: AtomicWrite, - ) -> Result, AnyError>; + ) -> Result, JsErrorBox>; async fn dyn_dequeue_next_message( &self, - ) -> Result>, AnyError>; + ) -> Result>, JsErrorBox>; fn dyn_watch(&self, keys: Vec>) -> WatchStream; @@ -153,20 +153,20 @@ impl Database for RcDynamicDb { &self, requests: Vec, options: SnapshotReadOptions, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { (*self.0).dyn_snapshot_read(requests, options).await } async fn atomic_write( &self, write: AtomicWrite, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { (*self.0).dyn_atomic_write(write).await } async fn dequeue_next_message( &self, - ) -> Result>, AnyError> { + ) -> Result>, JsErrorBox> { (*self.0).dyn_dequeue_next_message().await } @@ -189,20 +189,20 @@ where &self, requests: Vec, options: SnapshotReadOptions, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { Ok(self.snapshot_read(requests, options).await?) } async fn dyn_atomic_write( &self, write: AtomicWrite, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { Ok(self.atomic_write(write).await?) } async fn dyn_dequeue_next_message( &self, - ) -> Result>, AnyError> { + ) -> Result>, JsErrorBox> { Ok( self .dequeue_next_message() diff --git a/ext/kv/interface.rs b/ext/kv/interface.rs index 9737a9366d..df106fde78 100644 --- a/ext/kv/interface.rs +++ b/ext/kv/interface.rs @@ -1,11 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::rc::Rc; use async_trait::async_trait; -use deno_core::error::AnyError; use deno_core::OpState; +use deno_error::JsErrorBox; use denokv_proto::Database; #[async_trait(?Send)] @@ -16,5 +16,5 @@ pub trait DatabaseHandler { &self, state: Rc>, path: Option, - ) -> Result; + ) -> Result; } diff --git a/ext/kv/lib.rs b/ext/kv/lib.rs index ce7509721a..61458888a9 100644 --- a/ext/kv/lib.rs +++ b/ext/kv/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod config; pub mod dynamic; @@ -17,7 +17,6 @@ use base64::Engine; use boxed_error::Boxed; use chrono::DateTime; use chrono::Utc; -use deno_core::error::get_custom_error_class; use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::serde_v8::AnyValue; @@ -32,6 +31,8 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; use denokv_proto::decode_key; use denokv_proto::encode_key; use denokv_proto::AtomicWrite; @@ -115,65 +116,93 @@ impl Resource for DatabaseWatcherResource { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, deno_error::JsError)] pub struct KvError(pub Box); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum KvErrorKind { + #[class(inherit)] #[error(transparent)] - DatabaseHandler(deno_core::error::AnyError), + DatabaseHandler(JsErrorBox), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(type)] #[error("Too many ranges (max {0})")] TooManyRanges(usize), + #[class(type)] #[error("Too many entries (max {0})")] TooManyEntries(usize), + #[class(type)] #[error("Too many checks (max {0})")] TooManyChecks(usize), + #[class(type)] #[error("Too many mutations (max {0})")] TooManyMutations(usize), + #[class(type)] #[error("Too many keys (max {0})")] TooManyKeys(usize), + #[class(type)] #[error("limit must be greater than 0")] InvalidLimit, + #[class(type)] #[error("Invalid boundary key")] InvalidBoundaryKey, + #[class(type)] #[error("Key too large for read (max {0} bytes)")] KeyTooLargeToRead(usize), + #[class(type)] #[error("Key too large for write (max {0} bytes)")] KeyTooLargeToWrite(usize), + #[class(type)] #[error("Total mutation size too large (max {0} bytes)")] TotalMutationTooLarge(usize), + #[class(type)] #[error("Total key size too large (max {0} bytes)")] TotalKeyTooLarge(usize), + #[class(inherit)] #[error(transparent)] - Kv(deno_core::error::AnyError), + Kv(JsErrorBox), + #[class(inherit)] #[error(transparent)] Io(#[from] std::io::Error), + #[class(type)] #[error("Queue message not found")] QueueMessageNotFound, + #[class(type)] #[error("Start key is not in the keyspace defined by prefix")] StartKeyNotInKeyspace, + #[class(type)] #[error("End key is not in the keyspace defined by prefix")] EndKeyNotInKeyspace, + #[class(type)] #[error("Start key is greater than end key")] StartKeyGreaterThanEndKey, + #[class(inherit)] #[error("Invalid check")] InvalidCheck(#[source] KvCheckError), + #[class(inherit)] #[error("Invalid mutation")] InvalidMutation(#[source] KvMutationError), + #[class(inherit)] #[error("Invalid enqueue")] InvalidEnqueue(#[source] std::io::Error), + #[class(type)] #[error("key cannot be empty")] - EmptyKey, // TypeError + EmptyKey, + #[class(type)] #[error("Value too large (max {0} bytes)")] - ValueTooLarge(usize), // TypeError + ValueTooLarge(usize), + #[class(type)] #[error("enqueue payload too large (max {0} bytes)")] - EnqueuePayloadTooLarge(usize), // TypeError + EnqueuePayloadTooLarge(usize), + #[class(type)] #[error("invalid cursor")] InvalidCursor, + #[class(type)] #[error("cursor out of bounds")] CursorOutOfBounds, + #[class(type)] #[error("Invalid range")] InvalidRange, } @@ -418,7 +447,7 @@ where match state.resource_table.get::>(rid) { Ok(resource) => resource, Err(err) => { - if get_custom_error_class(&err) == Some("BadResource") { + if err.get_class() == "BadResource" { return Ok(None); } else { return Err(KvErrorKind::Resource(err).into_box()); @@ -568,10 +597,12 @@ where Ok(()) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum KvCheckError { + #[class(type)] #[error("invalid versionstamp")] InvalidVersionstamp, + #[class(inherit)] #[error(transparent)] Io(std::io::Error), } @@ -597,14 +628,22 @@ fn check_from_v8(value: V8KvCheck) -> Result { }) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum KvMutationError { + #[class(generic)] #[error(transparent)] BigInt(#[from] num_bigint::TryFromBigIntError), + #[class(inherit)] #[error(transparent)] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + std::io::Error, + ), + #[class(type)] #[error("Invalid mutation '{0}' with value")] InvalidMutationWithValue(String), + #[class(type)] #[error("Invalid mutation '{0}' without value")] InvalidMutationWithoutValue(String), } diff --git a/ext/kv/remote.rs b/ext/kv/remote.rs index 1830aa67ee..e5a07ad96c 100644 --- a/ext/kv/remote.rs +++ b/ext/kv/remote.rs @@ -1,18 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::marker::PhantomData; use std::rc::Rc; use std::sync::Arc; -use crate::DatabaseHandler; use anyhow::Context; use async_trait::async_trait; use bytes::Bytes; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::futures::Stream; use deno_core::OpState; +use deno_error::JsErrorBox; use deno_fetch::create_http_client; use deno_fetch::CreateHttpClientOptions; use deno_permissions::PermissionCheckError; @@ -27,6 +25,8 @@ use denokv_remote::RemoteTransport; use http_body_util::BodyExt; use url::Url; +use crate::DatabaseHandler; + #[derive(Clone)] pub struct HttpOptions { pub user_agent: String, @@ -37,7 +37,7 @@ pub struct HttpOptions { } impl HttpOptions { - pub fn root_cert_store(&self) -> Result, AnyError> { + pub fn root_cert_store(&self) -> Result, JsErrorBox> { Ok(match &self.root_cert_store_provider { Some(provider) => Some(provider.get_or_try_init()?.clone()), None => None, @@ -101,12 +101,12 @@ impl Clone for PermissionChecker

{ impl denokv_remote::RemotePermissions for PermissionChecker

{ - fn check_net_url(&self, url: &Url) -> Result<(), anyhow::Error> { + fn check_net_url(&self, url: &Url) -> Result<(), JsErrorBox> { let mut state = self.state.borrow_mut(); let permissions = state.borrow_mut::

(); permissions .check_net_url(url, "Deno.openKv") - .map_err(Into::into) + .map_err(JsErrorBox::from_err) } } @@ -121,33 +121,43 @@ impl RemoteTransport for FetchClient { url: Url, headers: http::HeaderMap, body: Bytes, - ) -> Result<(Url, http::StatusCode, Self::Response), anyhow::Error> { - let body = http_body_util::Full::new(body) - .map_err(|never| match never {}) - .boxed(); + ) -> Result<(Url, http::StatusCode, Self::Response), JsErrorBox> { + let body = deno_fetch::ReqBody::full(body); let mut req = http::Request::new(body); *req.method_mut() = http::Method::POST; - *req.uri_mut() = url.as_str().parse()?; + *req.uri_mut() = + url.as_str().parse().map_err(|e: http::uri::InvalidUri| { + JsErrorBox::type_error(e.to_string()) + })?; *req.headers_mut() = headers; - let res = self.0.clone().send(req).await?; + let res = self + .0 + .clone() + .send(req) + .await + .map_err(JsErrorBox::from_err)?; let status = res.status(); Ok((url, status, FetchResponse(res))) } } impl RemoteResponse for FetchResponse { - async fn bytes(self) -> Result { + async fn bytes(self) -> Result { Ok(self.0.collect().await?.to_bytes()) } fn stream( self, - ) -> impl Stream> + Send + Sync { + ) -> impl Stream> + Send + Sync { self.0.into_body().into_data_stream() } - async fn text(self) -> Result { + async fn text(self) -> Result { let bytes = self.bytes().await?; - Ok(std::str::from_utf8(&bytes)?.into()) + Ok( + std::str::from_utf8(&bytes) + .map_err(JsErrorBox::from_err)? + .into(), + ) } } @@ -161,29 +171,36 @@ impl DatabaseHandler &self, state: Rc>, path: Option, - ) -> Result { + ) -> Result { const ENV_VAR_NAME: &str = "DENO_KV_ACCESS_TOKEN"; let Some(url) = path else { - return Err(type_error("Missing database url")); + return Err(JsErrorBox::type_error("Missing database url")); }; let Ok(parsed_url) = Url::parse(&url) else { - return Err(type_error(format!("Invalid database url: {}", url))); + return Err(JsErrorBox::type_error(format!( + "Invalid database url: {}", + url + ))); }; { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); - permissions.check_env(ENV_VAR_NAME)?; - permissions.check_net_url(&parsed_url, "Deno.openKv")?; + permissions + .check_env(ENV_VAR_NAME) + .map_err(JsErrorBox::from_err)?; + permissions + .check_net_url(&parsed_url, "Deno.openKv") + .map_err(JsErrorBox::from_err)?; } let access_token = std::env::var(ENV_VAR_NAME) .map_err(anyhow::Error::from) .with_context(|| { "Missing DENO_KV_ACCESS_TOKEN environment variable. Please set it to your access token from https://dash.deno.com/account." - })?; + }).map_err(|e| JsErrorBox::generic(e.to_string()))?; let metadata_endpoint = MetadataEndpoint { url: parsed_url.clone(), @@ -212,7 +229,8 @@ impl DatabaseHandler http2: true, client_builder_hook: None, }, - )?; + ) + .map_err(JsErrorBox::from_err)?; let fetch_client = FetchClient(client); let permissions = PermissionChecker { diff --git a/ext/kv/sqlite.rs b/ext/kv/sqlite.rs index 9de5209275..8be042eef0 100644 --- a/ext/kv/sqlite.rs +++ b/ext/kv/sqlite.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; @@ -13,12 +13,10 @@ use std::sync::Arc; use std::sync::Mutex; use std::sync::OnceLock; -use crate::DatabaseHandler; use async_trait::async_trait; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::unsync::spawn_blocking; use deno_core::OpState; +use deno_error::JsErrorBox; use deno_path_util::normalize_path; use deno_permissions::PermissionCheckError; pub use denokv_sqlite::SqliteBackendError; @@ -27,6 +25,8 @@ use denokv_sqlite::SqliteNotifier; use rand::SeedableRng; use rusqlite::OpenFlags; +use crate::DatabaseHandler; + static SQLITE_NOTIFIERS_MAP: OnceLock>> = OnceLock::new(); @@ -84,6 +84,12 @@ impl SqliteDbHandler

{ } } +deno_error::js_error_wrapper!( + SqliteBackendError, + JsSqliteBackendError, + "TypeError" +); + #[async_trait(?Send)] impl DatabaseHandler for SqliteDbHandler

{ type DB = denokv_sqlite::Sqlite; @@ -92,12 +98,12 @@ impl DatabaseHandler for SqliteDbHandler

{ &self, state: Rc>, path: Option, - ) -> Result { + ) -> Result { #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn validate_path( state: &RefCell, path: Option, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { let Some(path) = path else { return Ok(None); }; @@ -105,18 +111,22 @@ impl DatabaseHandler for SqliteDbHandler

{ return Ok(Some(path)); } if path.is_empty() { - return Err(type_error("Filename cannot be empty")); + return Err(JsErrorBox::type_error("Filename cannot be empty")); } if path.starts_with(':') { - return Err(type_error( + return Err(JsErrorBox::type_error( "Filename cannot start with ':' unless prefixed with './'", )); } { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); - let path = permissions.check_read(&path, "Deno.openKv")?; - let path = permissions.check_write(&path, "Deno.openKv")?; + let path = permissions + .check_read(&path, "Deno.openKv") + .map_err(JsErrorBox::from_err)?; + let path = permissions + .check_write(&path, "Deno.openKv") + .map_err(JsErrorBox::from_err)?; Ok(Some(path.to_string_lossy().to_string())) } } @@ -137,7 +147,7 @@ impl DatabaseHandler for SqliteDbHandler

{ let flags = OpenFlags::default().difference(OpenFlags::SQLITE_OPEN_URI); let resolved_path = canonicalize_path(&PathBuf::from(path)) - .map_err(anyhow::Error::from)?; + .map_err(JsErrorBox::from_err)?; let path = path.to_string(); ( Arc::new(move || { @@ -147,7 +157,7 @@ impl DatabaseHandler for SqliteDbHandler

{ ) } (None, Some(path)) => { - std::fs::create_dir_all(path).map_err(anyhow::Error::from)?; + std::fs::create_dir_all(path).map_err(JsErrorBox::from_err)?; let path = path.join("kv.sqlite3"); let path2 = path.clone(); ( @@ -161,7 +171,8 @@ impl DatabaseHandler for SqliteDbHandler

{ }) }) .await - .unwrap()?; + .unwrap() + .map_err(JsErrorBox::from_err)?; let notifier = if let Some(notifier_key) = notifier_key { SQLITE_NOTIFIERS_MAP @@ -184,8 +195,11 @@ impl DatabaseHandler for SqliteDbHandler

{ denokv_sqlite::Sqlite::new( move || { - let conn = conn_gen()?; - conn.pragma_update(None, "journal_mode", "wal")?; + let conn = + conn_gen().map_err(|e| JsErrorBox::generic(e.to_string()))?; + conn + .pragma_update(None, "journal_mode", "wal") + .map_err(|e| JsErrorBox::generic(e.to_string()))?; Ok(( conn, match versionstamp_rng_seed { @@ -197,11 +211,12 @@ impl DatabaseHandler for SqliteDbHandler

{ notifier, config, ) + .map_err(|e| JsErrorBox::generic(e.to_string())) } } /// Same as Path::canonicalize, but also handles non-existing paths. -fn canonicalize_path(path: &Path) -> Result { +fn canonicalize_path(path: &Path) -> Result { let path = normalize_path(path); let mut path = path; let mut names_stack = Vec::new(); @@ -224,7 +239,7 @@ fn canonicalize_path(path: &Path) -> Result { path.clone_from(¤t_dir); } } - Err(err) => return Err(err.into()), + Err(err) => return Err(err), } } } diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index 783b4b7cff..ff69d6a47e 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_napi" -version = "0.113.0" +version = "0.116.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_permissions.workspace = true libc.workspace = true libloading = { version = "0.7" } diff --git a/ext/napi/build.rs b/ext/napi/build.rs index 8705830a95..71686d6451 100644 --- a/ext/napi/build.rs +++ b/ext/napi/build.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. fn main() { let symbols_file_name = match std::env::consts::OS { diff --git a/ext/napi/function.rs b/ext/napi/function.rs index a128ad7900..681f014421 100644 --- a/ext/napi/function.rs +++ b/ext/napi/function.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use crate::*; #[repr(C)] diff --git a/ext/napi/js_native_api.rs b/ext/napi/js_native_api.rs index 53a12d6eba..80579081f3 100644 --- a/ext/napi/js_native_api.rs +++ b/ext/napi/js_native_api.rs @@ -1,12 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(non_upper_case_globals)] #![deny(unsafe_op_in_unsafe_fn)] const NAPI_VERSION: u32 = 9; -use crate::*; +use std::ptr::NonNull; + use libc::INT_MAX; +use napi_sym::napi_sym; use super::util::check_new_from_utf8; use super::util::check_new_from_utf8_len; @@ -20,8 +22,7 @@ use crate::check_env; use crate::function::create_function; use crate::function::create_function_template; use crate::function::CallbackInfo; -use napi_sym::napi_sym; -use std::ptr::NonNull; +use crate::*; #[derive(Debug, Clone, Copy, PartialEq)] enum ReferenceOwnership { diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs index 6db6af48a2..1db20ef647 100644 --- a/ext/napi/lib.rs +++ b/ext/napi/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] @@ -22,49 +22,51 @@ pub mod util; pub mod uv; use core::ptr::NonNull; -use deno_core::op2; -use deno_core::parking_lot::RwLock; -use deno_core::url::Url; -use deno_core::ExternalOpsTracker; -use deno_core::OpState; -use deno_core::V8CrossThreadTaskSpawner; use std::cell::RefCell; use std::collections::HashMap; -use std::path::PathBuf; -use std::rc::Rc; -use std::thread_local; - -#[derive(Debug, thiserror::Error)] -pub enum NApiError { - #[error("Invalid path")] - InvalidPath, - #[error(transparent)] - LibLoading(#[from] libloading::Error), - #[error("Unable to find register Node-API module at {}", .0.display())] - ModuleNotFound(PathBuf), - #[error(transparent)] - Permission(#[from] PermissionCheckError), -} - -#[cfg(unix)] -use libloading::os::unix::*; - -#[cfg(windows)] -use libloading::os::windows::*; - -// Expose common stuff for ease of use. -// `use deno_napi::*` -pub use deno_core::v8; -use deno_permissions::PermissionCheckError; pub use std::ffi::CStr; pub use std::os::raw::c_char; pub use std::os::raw::c_void; +use std::path::PathBuf; pub use std::ptr; +use std::rc::Rc; +use std::thread_local; + +use deno_core::op2; +use deno_core::parking_lot::RwLock; +use deno_core::url::Url; +// Expose common stuff for ease of use. +// `use deno_napi::*` +pub use deno_core::v8; +use deno_core::ExternalOpsTracker; +use deno_core::OpState; +use deno_core::V8CrossThreadTaskSpawner; +use deno_permissions::PermissionCheckError; +#[cfg(unix)] +use libloading::os::unix::*; +#[cfg(windows)] +use libloading::os::windows::*; pub use value::napi_value; pub mod function; mod value; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum NApiError { + #[class(type)] + #[error("Invalid path")] + InvalidPath, + #[class(type)] + #[error(transparent)] + LibLoading(#[from] libloading::Error), + #[class(type)] + #[error("Unable to find register Node-API module at {}", .0.display())] + ModuleNotFound(PathBuf), + #[class(inherit)] + #[error(transparent)] + Permission(#[from] PermissionCheckError), +} + pub type napi_status = i32; pub type napi_env = *mut c_void; pub type napi_callback_info = *mut c_void; diff --git a/ext/napi/node_api.rs b/ext/napi/node_api.rs index 2ca5c8d0b4..13ea0b3e62 100644 --- a/ext/napi/node_api.rs +++ b/ext/napi/node_api.rs @@ -1,7 +1,18 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![deny(unsafe_op_in_unsafe_fn)] +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicU8; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +use deno_core::parking_lot::Condvar; +use deno_core::parking_lot::Mutex; +use deno_core::V8CrossThreadTaskSpawner; +use napi_sym::napi_sym; + use super::util::get_array_buffer_ptr; use super::util::make_external_backing_store; use super::util::napi_clear_last_error; @@ -10,15 +21,6 @@ use super::util::SendPtr; use crate::check_arg; use crate::check_env; use crate::*; -use deno_core::parking_lot::Condvar; -use deno_core::parking_lot::Mutex; -use deno_core::V8CrossThreadTaskSpawner; -use napi_sym::napi_sym; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicU8; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; #[napi_sym] fn napi_module_register(module: *const NapiModule) -> napi_status { diff --git a/ext/napi/sym/Cargo.toml b/ext/napi/sym/Cargo.toml index a3dd56e2bf..f845b639ba 100644 --- a/ext/napi/sym/Cargo.toml +++ b/ext/napi/sym/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "napi_sym" -version = "0.112.0" +version = "0.115.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/sym/lib.rs b/ext/napi/sym/lib.rs index e2826306b9..94444d8541 100644 --- a/ext/napi/sym/lib.rs +++ b/ext/napi/sym/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use proc_macro::TokenStream; use quote::quote; diff --git a/ext/napi/util.rs b/ext/napi/util.rs index 21e9d433aa..a913eade16 100644 --- a/ext/napi/util.rs +++ b/ext/napi/util.rs @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::*; +// Copyright 2018-2025 the Deno authors. MIT license. use libc::INT_MAX; +use crate::*; + #[repr(transparent)] pub(crate) struct SendPtr(pub *const T); diff --git a/ext/napi/uv.rs b/ext/napi/uv.rs index ea6b539665..ef4ac495c3 100644 --- a/ext/napi/uv.rs +++ b/ext/napi/uv.rs @@ -1,10 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::*; -use deno_core::parking_lot::Mutex; use std::mem::MaybeUninit; use std::ptr::addr_of_mut; +use deno_core::parking_lot::Mutex; + +use crate::*; + fn assert_ok(res: c_int) -> c_int { if res != 0 { log::error!("bad result in uv polyfill: {res}"); @@ -15,11 +17,12 @@ fn assert_ok(res: c_int) -> c_int { res } +use std::ffi::c_int; + use js_native_api::napi_create_string_utf8; use node_api::napi_create_async_work; use node_api::napi_delete_async_work; use node_api::napi_queue_async_work; -use std::ffi::c_int; const UV_MUTEX_SIZE: usize = { #[cfg(unix)] diff --git a/ext/napi/value.rs b/ext/napi/value.rs index 71beac07e2..851a56e963 100644 --- a/ext/napi/value.rs +++ b/ext/napi/value.rs @@ -1,11 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::v8; use std::mem::transmute; use std::ops::Deref; use std::os::raw::c_void; use std::ptr::NonNull; +use deno_core::v8; + /// An FFI-opaque, nullable wrapper around v8::Local. /// rusty_v8 Local handle cannot be empty but napi_value can be. #[repr(transparent)] diff --git a/ext/net/01_net.js b/ext/net/01_net.js index c3e5f9e5ca..3afbd031e6 100644 --- a/ext/net/01_net.js +++ b/ext/net/01_net.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { diff --git a/ext/net/02_tls.js b/ext/net/02_tls.js index 6dad965590..21d5512ebd 100644 --- a/ext/net/02_tls.js +++ b/ext/net/02_tls.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; const { internalRidSymbol } = core; diff --git a/ext/net/03_quic.js b/ext/net/03_quic.js new file mode 100644 index 0000000000..d74d356edb --- /dev/null +++ b/ext/net/03_quic.js @@ -0,0 +1,451 @@ +// Copyright 2018-2025 the Deno authors. MIT license. +import { core, primordials } from "ext:core/mod.js"; +import { + op_quic_connecting_0rtt, + op_quic_connecting_1rtt, + op_quic_connection_accept_bi, + op_quic_connection_accept_uni, + op_quic_connection_close, + op_quic_connection_closed, + op_quic_connection_get_max_datagram_size, + op_quic_connection_get_protocol, + op_quic_connection_get_remote_addr, + op_quic_connection_get_server_name, + op_quic_connection_handshake, + op_quic_connection_open_bi, + op_quic_connection_open_uni, + op_quic_connection_read_datagram, + op_quic_connection_send_datagram, + op_quic_endpoint_close, + op_quic_endpoint_connect, + op_quic_endpoint_create, + op_quic_endpoint_get_addr, + op_quic_endpoint_listen, + op_quic_incoming_accept, + op_quic_incoming_accept_0rtt, + op_quic_incoming_ignore, + op_quic_incoming_local_ip, + op_quic_incoming_refuse, + op_quic_incoming_remote_addr, + op_quic_incoming_remote_addr_validated, + op_quic_listener_accept, + op_quic_listener_stop, + op_quic_recv_stream_get_id, + op_quic_send_stream_get_id, + op_quic_send_stream_get_priority, + op_quic_send_stream_set_priority, +} from "ext:core/ops"; +import { + getReadableStreamResourceBacking, + getWritableStreamResourceBacking, + ReadableStream, + readableStreamForRid, + WritableStream, + writableStreamForRid, +} from "ext:deno_web/06_streams.js"; +import { loadTlsKeyPair } from "ext:deno_net/02_tls.js"; +const { + BadResourcePrototype, +} = core; +const { + ObjectPrototypeIsPrototypeOf, + PromisePrototypeThen, + Symbol, + SymbolAsyncIterator, + SafePromisePrototypeFinally, +} = primordials; + +let getEndpointResource; + +function transportOptions({ + keepAliveInterval, + maxIdleTimeout, + maxConcurrentBidirectionalStreams, + maxConcurrentUnidirectionalStreams, + preferredAddressV4, + preferredAddressV6, + congestionControl, +}) { + return { + keepAliveInterval, + maxIdleTimeout, + maxConcurrentBidirectionalStreams, + maxConcurrentUnidirectionalStreams, + preferredAddressV4, + preferredAddressV6, + congestionControl, + }; +} + +const kRid = Symbol("rid"); + +class QuicEndpoint { + #endpoint; + + constructor( + { hostname = "::", port = 0, [kRid]: rid } = { __proto__: null }, + ) { + this.#endpoint = rid ?? op_quic_endpoint_create({ hostname, port }, true); + } + + get addr() { + return op_quic_endpoint_get_addr(this.#endpoint); + } + + listen(options) { + const keyPair = loadTlsKeyPair("Deno.QuicEndpoint.listen", { + cert: options.cert, + key: options.key, + }); + const listener = op_quic_endpoint_listen( + this.#endpoint, + { alpnProtocols: options.alpnProtocols }, + transportOptions(options), + keyPair, + ); + return new QuicListener(listener, this); + } + + close({ closeCode = 0, reason = "" } = { __proto__: null }) { + op_quic_endpoint_close(this.#endpoint, closeCode, reason); + } + + static { + getEndpointResource = (e) => e.#endpoint; + } +} + +class QuicListener { + #listener; + #endpoint; + + constructor(listener, endpoint) { + this.#listener = listener; + this.#endpoint = endpoint; + } + + get endpoint() { + return this.#endpoint; + } + + async incoming() { + const incoming = await op_quic_listener_accept(this.#listener); + return new QuicIncoming(incoming, this.#endpoint); + } + + async accept() { + const incoming = await this.incoming(); + const connection = await incoming.accept(); + return connection; + } + + async next() { + try { + const connection = await this.accept(); + return { value: connection, done: false }; + } catch (error) { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + return { value: undefined, done: true }; + } + throw error; + } + } + + [SymbolAsyncIterator]() { + return this; + } + + stop() { + op_quic_listener_stop(this.#listener); + } +} + +class QuicIncoming { + #incoming; + #endpoint; + + constructor(incoming, endpoint) { + this.#incoming = incoming; + this.#endpoint = endpoint; + } + + get localIp() { + return op_quic_incoming_local_ip(this.#incoming); + } + + get remoteAddr() { + return op_quic_incoming_remote_addr(this.#incoming); + } + + get remoteAddressValidated() { + return op_quic_incoming_remote_addr_validated(this.#incoming); + } + + accept(options) { + const tOptions = options ? transportOptions(options) : null; + if (options?.zeroRtt) { + const conn = op_quic_incoming_accept_0rtt( + this.#incoming, + tOptions, + ); + return new QuicConn(conn, this.#endpoint); + } + return PromisePrototypeThen( + op_quic_incoming_accept(this.#incoming, tOptions), + (conn) => new QuicConn(conn, this.#endpoint), + ); + } + + refuse() { + op_quic_incoming_refuse(this.#incoming); + } + + ignore() { + op_quic_incoming_ignore(this.#incoming); + } +} + +class QuicConn { + #resource; + #bidiStream = null; + #uniStream = null; + #closed; + #handshake; + #endpoint; + + constructor(resource, endpoint) { + this.#resource = resource; + this.#endpoint = endpoint; + + this.#closed = op_quic_connection_closed(this.#resource); + core.unrefOpPromise(this.#closed); + } + + get endpoint() { + return this.#endpoint; + } + + get protocol() { + return op_quic_connection_get_protocol(this.#resource); + } + + get remoteAddr() { + return op_quic_connection_get_remote_addr(this.#resource); + } + + get serverName() { + return op_quic_connection_get_server_name(this.#resource); + } + + async createBidirectionalStream( + { sendOrder, waitUntilAvailable } = { __proto__: null }, + ) { + const { 0: txRid, 1: rxRid } = await op_quic_connection_open_bi( + this.#resource, + waitUntilAvailable ?? false, + ); + if (sendOrder !== null && sendOrder !== undefined) { + op_quic_send_stream_set_priority(txRid, sendOrder); + } + return new QuicBidirectionalStream(txRid, rxRid, this.#closed); + } + + async createUnidirectionalStream( + { sendOrder, waitUntilAvailable } = { __proto__: null }, + ) { + const rid = await op_quic_connection_open_uni( + this.#resource, + waitUntilAvailable ?? false, + ); + if (sendOrder !== null && sendOrder !== undefined) { + op_quic_send_stream_set_priority(rid, sendOrder); + } + return writableStream(rid, this.#closed); + } + + get incomingBidirectionalStreams() { + if (this.#bidiStream === null) { + this.#bidiStream = ReadableStream.from( + bidiStream(this.#resource, this.#closed), + ); + } + return this.#bidiStream; + } + + get incomingUnidirectionalStreams() { + if (this.#uniStream === null) { + this.#uniStream = ReadableStream.from( + uniStream(this.#resource, this.#closed), + ); + } + return this.#uniStream; + } + + get maxDatagramSize() { + return op_quic_connection_get_max_datagram_size(this.#resource); + } + + async readDatagram() { + const buffer = await op_quic_connection_read_datagram(this.#resource); + return buffer; + } + + async sendDatagram(data) { + await op_quic_connection_send_datagram(this.#resource, data); + } + + get handshake() { + if (!this.#handshake) { + this.#handshake = op_quic_connection_handshake(this.#resource); + } + return this.#handshake; + } + + get closed() { + core.refOpPromise(this.#closed); + return this.#closed; + } + + close({ closeCode = 0, reason = "" } = { __proto__: null }) { + op_quic_connection_close(this.#resource, closeCode, reason); + } +} + +class QuicSendStream extends WritableStream { + get sendOrder() { + return op_quic_send_stream_get_priority( + getWritableStreamResourceBacking(this).rid, + ); + } + + set sendOrder(p) { + op_quic_send_stream_set_priority( + getWritableStreamResourceBacking(this).rid, + p, + ); + } + + get id() { + return op_quic_send_stream_get_id( + getWritableStreamResourceBacking(this).rid, + ); + } +} + +class QuicReceiveStream extends ReadableStream { + get id() { + return op_quic_recv_stream_get_id( + getReadableStreamResourceBacking(this).rid, + ); + } +} + +function readableStream(rid, closed) { + // stream can be indirectly closed by closing connection. + SafePromisePrototypeFinally(closed, () => { + core.tryClose(rid); + }); + return readableStreamForRid(rid, true, QuicReceiveStream); +} + +function writableStream(rid, closed) { + // stream can be indirectly closed by closing connection. + SafePromisePrototypeFinally(closed, () => { + core.tryClose(rid); + }); + return writableStreamForRid(rid, true, QuicSendStream); +} + +class QuicBidirectionalStream { + #readable; + #writable; + + constructor(txRid, rxRid, closed) { + this.#readable = readableStream(rxRid, closed); + this.#writable = writableStream(txRid, closed); + } + + get readable() { + return this.#readable; + } + + get writable() { + return this.#writable; + } +} + +async function* bidiStream(conn, closed) { + try { + while (true) { + const r = await op_quic_connection_accept_bi(conn); + yield new QuicBidirectionalStream(r[0], r[1], closed); + } + } catch (error) { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + return; + } + throw error; + } +} + +async function* uniStream(conn, closed) { + try { + while (true) { + const uniRid = await op_quic_connection_accept_uni(conn); + yield readableStream(uniRid, closed); + } + } catch (error) { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + return; + } + throw error; + } +} + +function connectQuic(options) { + const endpoint = options.endpoint ?? + new QuicEndpoint({ + [kRid]: op_quic_endpoint_create({ hostname: "::", port: 0 }, 0, false), + }); + const keyPair = loadTlsKeyPair("Deno.connectQuic", { + cert: options.cert, + key: options.key, + }); + const connecting = op_quic_endpoint_connect( + getEndpointResource(endpoint), + { + addr: { + hostname: options.hostname, + port: options.port, + }, + caCerts: options.caCerts, + alpnProtocols: options.alpnProtocols, + serverName: options.serverName, + }, + transportOptions(options), + keyPair, + ); + + if (options.zeroRtt) { + const conn = op_quic_connecting_0rtt(connecting); + if (conn) { + return new QuicConn(conn, endpoint); + } + } + + return PromisePrototypeThen( + op_quic_connecting_1rtt(connecting), + (conn) => new QuicConn(conn, endpoint), + ); +} + +export { + connectQuic, + QuicBidirectionalStream, + QuicConn, + QuicEndpoint, + QuicIncoming, + QuicListener, + QuicReceiveStream, + QuicSendStream, +}; diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index f5aa32c8ce..ba02fe8708 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_net" -version = "0.174.0" +version = "0.177.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,11 +15,13 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_permissions.workspace = true deno_tls.workspace = true hickory-proto = "0.25.0-alpha.4" hickory-resolver.workspace = true pin-project.workspace = true +quinn = { version = "0.11.6", default-features = false, features = ["runtime-tokio", "rustls", "ring"] } rustls-tokio-stream.workspace = true serde.workspace = true socket2.workspace = true diff --git a/ext/net/io.rs b/ext/net/io.rs index 2907fa398b..71ae577cd0 100644 --- a/ext/net/io.rs +++ b/ext/net/io.rs @@ -1,4 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::rc::Rc; use deno_core::futures::TryFutureExt; use deno_core::AsyncMutFuture; @@ -8,15 +11,13 @@ use deno_core::CancelHandle; use deno_core::CancelTryFuture; use deno_core::RcRef; use deno_core::Resource; +use deno_error::JsErrorBox; use socket2::SockRef; -use std::borrow::Cow; -use std::rc::Rc; use tokio::io::AsyncRead; use tokio::io::AsyncReadExt; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; use tokio::net::tcp; - #[cfg(unix)] use tokio::net::unix; @@ -90,10 +91,12 @@ where } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum MapError { + #[class(inherit)] #[error("{0}")] Io(std::io::Error), + #[class(generic)] #[error("Unable to get resources")] NoResources, } @@ -110,7 +113,7 @@ impl Resource for TcpStreamResource { } fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown().map_err(Into::into)) + Box::pin(self.shutdown().map_err(JsErrorBox::from_err)) } fn close(self: Rc) { @@ -162,9 +165,7 @@ impl UnixStreamResource { unreachable!() } #[allow(clippy::unused_async)] - pub async fn shutdown( - self: Rc, - ) -> Result<(), deno_core::error::AnyError> { + pub async fn shutdown(self: Rc) -> Result<(), JsErrorBox> { unreachable!() } pub fn cancel_read_ops(&self) { @@ -181,7 +182,7 @@ impl Resource for UnixStreamResource { } fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown().map_err(Into::into)) + Box::pin(self.shutdown().map_err(JsErrorBox::from_err)) } fn close(self: Rc) { diff --git a/ext/net/lib.deno_net.d.ts b/ext/net/lib.deno_net.d.ts index 827081f2a4..30d3357395 100644 --- a/ext/net/lib.deno_net.d.ts +++ b/ext/net/lib.deno_net.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// @@ -450,5 +450,406 @@ declare namespace Deno { options?: StartTlsOptions, ): Promise; + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicEndpointOptions { + /** + * A literal IP address or host name that can be resolved to an IP address. + * @default {"::"} + */ + hostname?: string; + /** + * The port to bind to. + * @default {0} + */ + port?: number; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicTransportOptions { + /** Period of inactivity before sending a keep-alive packet. Keep-alive + * packets prevent an inactive but otherwise healthy connection from timing + * out. Only one side of any given connection needs keep-alive enabled for + * the connection to be preserved. + * @default {undefined} + */ + keepAliveInterval?: number; + /** Maximum duration of inactivity to accept before timing out the + * connection. The true idle timeout is the minimum of this and the peer’s + * own max idle timeout. + * @default {undefined} + */ + maxIdleTimeout?: number; + /** Maximum number of incoming bidirectional streams that may be open + * concurrently. + * @default {100} + */ + maxConcurrentBidirectionalStreams?: number; + /** Maximum number of incoming unidirectional streams that may be open + * concurrently. + * @default {100} + */ + maxConcurrentUnidirectionalStreams?: number; + /** + * The congestion control algorithm used when sending data over this connection. + * @default {"default"} + */ + congestionControl?: "throughput" | "low-latency" | "default"; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface ConnectQuicOptions + extends QuicTransportOptions { + /** The port to connect to. */ + port: number; + /** A literal IP address or host name that can be resolved to an IP address. */ + hostname: string; + /** The name used for validating the certificate provided by the server. If + * not provided, defaults to `hostname`. */ + serverName?: string | undefined; + /** Application-Layer Protocol Negotiation (ALPN) protocols supported by + * the client. QUIC requires the use of ALPN. + */ + alpnProtocols: string[]; + /** A list of root certificates that will be used in addition to the + * default root certificates to verify the peer's certificate. + * + * Must be in PEM format. */ + caCerts?: string[]; + /** + * If no endpoint is provided, a new one is bound on an ephemeral port. + */ + endpoint?: QuicEndpoint; + /** + * Attempt to convert the connection into 0-RTT. Any data sent before + * the TLS handshake completes is vulnerable to replay attacks. + * @default {false} + */ + zeroRtt?: ZRTT; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicServerTransportOptions extends QuicTransportOptions { + /** + * Preferred IPv4 address to be communicated to the client during + * handshaking. If the client is able to reach this address it will switch + * to it. + * @default {undefined} + */ + preferredAddressV4?: string; + /** + * Preferred IPv6 address to be communicated to the client during + * handshaking. If the client is able to reach this address it will switch + * to it. + * @default {undefined} + */ + preferredAddressV6?: string; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicListenOptions extends QuicServerTransportOptions { + /** Application-Layer Protocol Negotiation (ALPN) protocols to announce to + * the client. QUIC requires the use of ALPN. + */ + alpnProtocols: string[]; + /** Server private key in PEM format */ + key: string; + /** Cert chain in PEM format */ + cert: string; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicAcceptOptions + extends QuicServerTransportOptions { + /** Application-Layer Protocol Negotiation (ALPN) protocols to announce to + * the client. QUIC requires the use of ALPN. + */ + alpnProtocols?: string[]; + /** + * Convert this connection into 0.5-RTT at the cost of weakened security, as + * 0.5-RTT data may be sent before TLS client authentication has occurred. + * @default {false} + */ + zeroRtt?: ZRTT; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicCloseInfo { + /** A number representing the error code for the error. */ + closeCode: number; + /** A string representing the reason for closing the connection. */ + reason: string; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicSendStreamOptions { + /** Indicates the send priority of this stream relative to other streams for + * which the value has been set. + * @default {0} + */ + sendOrder?: number; + /** Wait until there is sufficient flow credit to create the stream. + * @default {false} + */ + waitUntilAvailable?: boolean; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export class QuicEndpoint { + /** + * Create a QUIC endpoint which may be used for client or server connections. + * + * Requires `allow-net` permission. + * + * @experimental + * @tags allow-net + * @category Network + */ + constructor(options?: QuicEndpointOptions); + + /** Return the address of the `QuicListener`. */ + readonly addr: NetAddr; + + /** + * **UNSTABLE**: New API, yet to be vetted. + * Listen announces on the local transport address over QUIC. + * + * @experimental + * @category Network + */ + listen(options: QuicListenOptions): QuicListener; + + /** + * Closes the endpoint. All associated connections will be closed and incoming + * connections will be rejected. + */ + close(info?: QuicCloseInfo): void; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * Specialized listener that accepts QUIC connections. + * + * @experimental + * @category Network + */ + export interface QuicListener extends AsyncIterable { + /** Waits for and resolves to the next incoming connection. */ + incoming(): Promise; + + /** Wait for the next incoming connection and accepts it. */ + accept(): Promise; + + /** Stops the listener. This does not close the endpoint. */ + stop(): void; + + [Symbol.asyncIterator](): AsyncIterableIterator; + + /** The endpoint for this listener. */ + readonly endpoint: QuicEndpoint; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * An incoming connection for which the server has not yet begun its part of + * the handshake. + * + * @experimental + * @category Network + */ + export interface QuicIncoming { + /** + * The local IP address which was used when the peer established the + * connection. + */ + readonly localIp: string; + + /** + * The peer’s UDP address. + */ + readonly remoteAddr: NetAddr; + + /** + * Whether the socket address that is initiating this connection has proven + * that they can receive traffic. + */ + readonly remoteAddressValidated: boolean; + + /** + * Accept this incoming connection. + */ + accept( + options?: QuicAcceptOptions, + ): ZRTT extends true ? QuicConn : Promise; + + /** + * Refuse this incoming connection. + */ + refuse(): void; + + /** + * Ignore this incoming connection attempt, not sending any packet in response. + */ + ignore(): void; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicConn { + /** Close closes the listener. Any pending accept promises will be rejected + * with errors. */ + close(info?: QuicCloseInfo): void; + /** Opens and returns a bidirectional stream. */ + createBidirectionalStream( + options?: QuicSendStreamOptions, + ): Promise; + /** Opens and returns a unidirectional stream. */ + createUnidirectionalStream( + options?: QuicSendStreamOptions, + ): Promise; + /** Send a datagram. The provided data cannot be larger than + * `maxDatagramSize`. */ + sendDatagram(data: Uint8Array): Promise; + /** Receive a datagram. */ + readDatagram(): Promise; + + /** The endpoint for this connection. */ + readonly endpoint: QuicEndpoint; + /** Returns a promise that resolves when the TLS handshake is complete. */ + readonly handshake: Promise; + /** Return the remote address for the connection. Clients may change + * addresses at will, for example when switching to a cellular internet + * connection. + */ + readonly remoteAddr: NetAddr; + /** + * The negotiated ALPN protocol, if provided. Only available after the + * handshake is complete. */ + readonly protocol: string | undefined; + /** The negotiated server name. Only available on the server after the + * handshake is complete. */ + readonly serverName: string | undefined; + /** Returns a promise that resolves when the connection is closed. */ + readonly closed: Promise; + /** A stream of bidirectional streams opened by the peer. */ + readonly incomingBidirectionalStreams: ReadableStream< + QuicBidirectionalStream + >; + /** A stream of unidirectional streams opened by the peer. */ + readonly incomingUnidirectionalStreams: ReadableStream; + /** Returns the datagram stream for sending and receiving datagrams. */ + readonly maxDatagramSize: number; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicBidirectionalStream { + /** Returns a QuicReceiveStream instance that can be used to read incoming data. */ + readonly readable: QuicReceiveStream; + /** Returns a QuicSendStream instance that can be used to write outgoing data. */ + readonly writable: QuicSendStream; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicSendStream extends WritableStream { + /** Indicates the send priority of this stream relative to other streams for + * which the value has been set. */ + sendOrder: number; + + /** + * 62-bit stream ID, unique within this connection. + */ + readonly id: bigint; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicReceiveStream extends ReadableStream { + /** + * 62-bit stream ID, unique within this connection. + */ + readonly id: bigint; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * Establishes a secure connection over QUIC using a hostname and port. The + * cert file is optional and if not included Mozilla's root certificates will + * be used. See also https://github.com/ctz/webpki-roots for specifics. + * + * ```ts + * const caCert = await Deno.readTextFile("./certs/my_custom_root_CA.pem"); + * const conn1 = await Deno.connectQuic({ hostname: "example.com", port: 443, alpnProtocols: ["h3"] }); + * const conn2 = await Deno.connectQuic({ caCerts: [caCert], hostname: "example.com", port: 443, alpnProtocols: ["h3"] }); + * ``` + * + * If an endpoint is shared among many connections, 0-RTT can be enabled. + * When 0-RTT is successful, a QuicConn will be synchronously returned + * and data can be sent immediately with it. **Any data sent before the + * TLS handshake completes is vulnerable to replay attacks.** + * + * Requires `allow-net` permission. + * + * @experimental + * @tags allow-net + * @category Network + */ + export function connectQuic( + options: ConnectQuicOptions, + ): ZRTT extends true ? (QuicConn | Promise) : Promise; + export {}; // only export exports } diff --git a/ext/net/lib.rs b/ext/net/lib.rs index f482750b38..b21da19f30 100644 --- a/ext/net/lib.rs +++ b/ext/net/lib.rs @@ -1,24 +1,26 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod io; pub mod ops; pub mod ops_tls; #[cfg(unix)] pub mod ops_unix; +mod quic; pub mod raw; pub mod resolve_addr; pub mod tcp; -use deno_core::error::AnyError; -use deno_core::OpState; -use deno_permissions::PermissionCheckError; -use deno_tls::rustls::RootCertStore; -use deno_tls::RootCertStoreProvider; use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use deno_core::OpState; +use deno_permissions::PermissionCheckError; +use deno_tls::rustls::RootCertStore; +use deno_tls::RootCertStoreProvider; +pub use quic::QuicError; + pub const UNSTABLE_FEATURE_NAME: &str = "net"; pub trait NetPermissions { @@ -104,7 +106,9 @@ pub struct DefaultTlsOptions { } impl DefaultTlsOptions { - pub fn root_cert_store(&self) -> Result, AnyError> { + pub fn root_cert_store( + &self, + ) -> Result, deno_error::JsErrorBox> { Ok(match &self.root_cert_store_provider { Some(provider) => Some(provider.get_or_try_init()?.clone()), None => None, @@ -158,8 +162,43 @@ deno_core::extension!(deno_net, ops_unix::op_node_unstable_net_listen_unixpacket

, ops_unix::op_net_recv_unixpacket, ops_unix::op_net_send_unixpacket

, + + quic::op_quic_connecting_0rtt, + quic::op_quic_connecting_1rtt, + quic::op_quic_connection_accept_bi, + quic::op_quic_connection_accept_uni, + quic::op_quic_connection_close, + quic::op_quic_connection_closed, + quic::op_quic_connection_get_protocol, + quic::op_quic_connection_get_remote_addr, + quic::op_quic_connection_get_server_name, + quic::op_quic_connection_handshake, + quic::op_quic_connection_open_bi, + quic::op_quic_connection_open_uni, + quic::op_quic_connection_get_max_datagram_size, + quic::op_quic_connection_read_datagram, + quic::op_quic_connection_send_datagram, + quic::op_quic_endpoint_close, + quic::op_quic_endpoint_connect

, + quic::op_quic_endpoint_create

, + quic::op_quic_endpoint_get_addr, + quic::op_quic_endpoint_listen, + quic::op_quic_incoming_accept, + quic::op_quic_incoming_accept_0rtt, + quic::op_quic_incoming_ignore, + quic::op_quic_incoming_local_ip, + quic::op_quic_incoming_refuse, + quic::op_quic_incoming_remote_addr, + quic::op_quic_incoming_remote_addr_validated, + quic::op_quic_listener_accept, + quic::op_quic_listener_stop, + quic::op_quic_recv_stream_get_id, + quic::op_quic_send_stream_get_id, + quic::op_quic_send_stream_get_priority, + quic::op_quic_send_stream_set_priority, ], esm = [ "01_net.js", "02_tls.js" ], + lazy_loaded_esm = [ "03_quic.js" ], options = { root_cert_store_provider: Option>, unsafely_ignore_certificate_errors: Option>, @@ -177,9 +216,10 @@ deno_core::extension!(deno_net, /// Stub ops for non-unix platforms. #[cfg(not(unix))] mod ops_unix { - use crate::NetPermissions; use deno_core::op2; + use crate::NetPermissions; + macro_rules! stub_op { ($name:ident) => { #[op2(fast)] diff --git a/ext/net/ops.rs b/ext/net/ops.rs index 16148ac90d..768dd33135 100644 --- a/ext/net/ops.rs +++ b/ext/net/ops.rs @@ -1,16 +1,17 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cell::RefCell; +use std::net::Ipv4Addr; +use std::net::Ipv6Addr; +use std::net::SocketAddr; +use std::rc::Rc; +use std::str::FromStr; -use crate::io::TcpStreamResource; -use crate::raw::NetworkListenerResource; -use crate::resolve_addr::resolve_addr; -use crate::resolve_addr::resolve_addr_sync; -use crate::tcp::TcpListener; -use crate::NetPermissions; use deno_core::op2; -use deno_core::CancelFuture; - use deno_core::AsyncRefCell; use deno_core::ByteString; +use deno_core::CancelFuture; use deno_core::CancelHandle; use deno_core::CancelTryFuture; use deno_core::JsBuffer; @@ -35,16 +36,16 @@ use socket2::Domain; use socket2::Protocol; use socket2::Socket; use socket2::Type; -use std::borrow::Cow; -use std::cell::RefCell; -use std::net::Ipv4Addr; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::rc::Rc; -use std::str::FromStr; use tokio::net::TcpStream; use tokio::net::UdpSocket; +use crate::io::TcpStreamResource; +use crate::raw::NetworkListenerResource; +use crate::resolve_addr::resolve_addr; +use crate::resolve_addr::resolve_addr_sync; +use crate::tcp::TcpListener; +use crate::NetPermissions; + #[derive(Serialize, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct TlsHandshakeInfo { @@ -66,60 +67,87 @@ impl From for IpAddr { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum NetError { + #[class("BadResource")] #[error("Listener has been closed")] ListenerClosed, + #[class("Busy")] #[error("Listener already in use")] ListenerBusy, + #[class("BadResource")] #[error("Socket has been closed")] SocketClosed, + #[class("NotConnected")] #[error("Socket has been closed")] SocketClosedNotConnected, + #[class("Busy")] #[error("Socket already in use")] SocketBusy, + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), + #[class("Busy")] #[error("Another accept task is ongoing")] AcceptTaskOngoing, + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error("{0}")] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(generic)] #[error("No resolved address found")] NoResolvedAddress, + #[class(generic)] #[error("{0}")] AddrParse(#[from] std::net::AddrParseError), + #[class(inherit)] #[error("{0}")] Map(crate::io::MapError), + #[class(inherit)] #[error("{0}")] Canceled(#[from] deno_core::Canceled), + #[class("NotFound")] #[error("{0}")] DnsNotFound(ResolveError), + #[class("NotConnected")] #[error("{0}")] DnsNotConnected(ResolveError), + #[class("TimedOut")] #[error("{0}")] DnsTimedOut(ResolveError), + #[class(generic)] #[error("{0}")] Dns(#[from] ResolveError), + #[class("NotSupported")] #[error("Provided record type is not supported")] UnsupportedRecordType, + #[class("InvalidData")] #[error("File name or path {0:?} is not valid UTF-8")] InvalidUtf8(std::ffi::OsString), + #[class(generic)] #[error("unexpected key type")] UnexpectedKeyType, + #[class(type)] #[error("Invalid hostname: '{0}'")] - InvalidHostname(String), // TypeError + InvalidHostname(String), + #[class("Busy")] #[error("TCP stream is currently in use")] TcpStreamBusy, + #[class(generic)] #[error("{0}")] Rustls(#[from] deno_tls::rustls::Error), + #[class(inherit)] #[error("{0}")] Tls(#[from] deno_tls::TlsError), + #[class("InvalidData")] #[error("Error creating TLS certificate: Deno.listenTls requires a key")] - ListenTlsRequiresKey, // InvalidData + ListenTlsRequiresKey, + #[class(inherit)] #[error("{0}")] - RootCertStore(deno_core::anyhow::Error), + RootCertStore(deno_error::JsErrorBox), + #[class(generic)] #[error("{0}")] Reunite(tokio::net::tcp::ReuniteError), } @@ -712,10 +740,8 @@ pub fn op_set_nodelay_inner( rid: ResourceId, nodelay: bool, ) -> Result<(), NetError> { - let resource: Rc = state - .resource_table - .get::(rid) - .map_err(NetError::Resource)?; + let resource: Rc = + state.resource_table.get::(rid)?; resource.set_nodelay(nodelay).map_err(NetError::Map) } @@ -734,10 +760,8 @@ pub fn op_set_keepalive_inner( rid: ResourceId, keepalive: bool, ) -> Result<(), NetError> { - let resource: Rc = state - .resource_table - .get::(rid) - .map_err(NetError::Resource)?; + let resource: Rc = + state.resource_table.get::(rid)?; resource.set_keepalive(keepalive).map_err(NetError::Map) } @@ -834,7 +858,14 @@ fn rdata_to_return_record( #[cfg(test)] mod tests { - use super::*; + use std::net::Ipv4Addr; + use std::net::Ipv6Addr; + use std::net::ToSocketAddrs; + use std::path::Path; + use std::path::PathBuf; + use std::sync::Arc; + use std::sync::Mutex; + use deno_core::futures::FutureExt; use deno_core::JsRuntime; use deno_core::RuntimeOptions; @@ -855,13 +886,8 @@ mod tests { use hickory_proto::rr::record_data::RData; use hickory_proto::rr::Name; use socket2::SockRef; - use std::net::Ipv4Addr; - use std::net::Ipv6Addr; - use std::net::ToSocketAddrs; - use std::path::Path; - use std::path::PathBuf; - use std::sync::Arc; - use std::sync::Mutex; + + use super::*; #[test] fn rdata_to_return_record_a() { diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs index 4d2073fd09..12c6580136 100644 --- a/ext/net/ops_tls.rs +++ b/ext/net/ops_tls.rs @@ -1,16 +1,17 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cell::RefCell; +use std::convert::From; +use std::fs::File; +use std::io::BufReader; +use std::io::ErrorKind; +use std::io::Read; +use std::net::SocketAddr; +use std::num::NonZeroUsize; +use std::rc::Rc; +use std::sync::Arc; -use crate::io::TcpStreamResource; -use crate::ops::IpAddr; -use crate::ops::NetError; -use crate::ops::TlsHandshakeInfo; -use crate::raw::NetworkListenerResource; -use crate::resolve_addr::resolve_addr; -use crate::resolve_addr::resolve_addr_sync; -use crate::tcp::TcpListener; -use crate::DefaultTlsOptions; -use crate::NetPermissions; -use crate::UnsafelyIgnoreCertificateErrors; use deno_core::futures::TryFutureExt; use deno_core::op2; use deno_core::v8; @@ -22,6 +23,7 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; use deno_tls::create_client_config; use deno_tls::load_certs; use deno_tls::load_private_keys; @@ -35,25 +37,25 @@ use deno_tls::TlsKey; use deno_tls::TlsKeyLookup; use deno_tls::TlsKeys; use deno_tls::TlsKeysHolder; +pub use rustls_tokio_stream::TlsStream; use rustls_tokio_stream::TlsStreamRead; use rustls_tokio_stream::TlsStreamWrite; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::convert::From; -use std::fs::File; -use std::io::BufReader; -use std::io::ErrorKind; -use std::io::Read; -use std::net::SocketAddr; -use std::num::NonZeroUsize; -use std::rc::Rc; -use std::sync::Arc; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; use tokio::net::TcpStream; -pub use rustls_tokio_stream::TlsStream; +use crate::io::TcpStreamResource; +use crate::ops::IpAddr; +use crate::ops::NetError; +use crate::ops::TlsHandshakeInfo; +use crate::raw::NetworkListenerResource; +use crate::resolve_addr::resolve_addr; +use crate::resolve_addr::resolve_addr_sync; +use crate::tcp::TcpListener; +use crate::DefaultTlsOptions; +use crate::NetPermissions; +use crate::UnsafelyIgnoreCertificateErrors; pub(crate) const TLS_BUFFER_SIZE: Option = NonZeroUsize::new(65536); @@ -162,7 +164,7 @@ impl Resource for TlsStreamResource { } fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown().map_err(Into::into)) + Box::pin(self.shutdown().map_err(JsErrorBox::from_err)) } fn close(self: Rc) { diff --git a/ext/net/ops_unix.rs b/ext/net/ops_unix.rs index 483dc99b40..b347b81dc7 100644 --- a/ext/net/ops_unix.rs +++ b/ext/net/ops_unix.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cell::RefCell; +use std::path::Path; +use std::rc::Rc; -use crate::io::UnixStreamResource; -use crate::ops::NetError; -use crate::raw::NetworkListenerResource; -use crate::NetPermissions; use deno_core::op2; use deno_core::AsyncRefCell; use deno_core::CancelHandle; @@ -15,14 +16,15 @@ use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::path::Path; -use std::rc::Rc; use tokio::net::UnixDatagram; use tokio::net::UnixListener; pub use tokio::net::UnixStream; +use crate::io::UnixStreamResource; +use crate::ops::NetError; +use crate::raw::NetworkListenerResource; +use crate::NetPermissions; + /// A utility function to map OsStrings to Strings pub fn into_string(s: std::ffi::OsString) -> Result { s.into_string().map_err(NetError::InvalidUtf8) diff --git a/ext/net/quic.rs b/ext/net/quic.rs new file mode 100644 index 0000000000..e075745495 --- /dev/null +++ b/ext/net/quic.rs @@ -0,0 +1,930 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cell::RefCell; +use std::future::Future; +use std::net::IpAddr; +use std::net::Ipv6Addr; +use std::net::SocketAddr; +use std::net::SocketAddrV4; +use std::net::SocketAddrV6; +use std::pin::pin; +use std::rc::Rc; +use std::sync::atomic::AtomicI32; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::task::Context; +use std::task::Poll; +use std::time::Duration; + +use deno_core::error::ResourceError; +use deno_core::futures::task::noop_waker_ref; +use deno_core::op2; +use deno_core::AsyncRefCell; +use deno_core::AsyncResult; +use deno_core::BufMutView; +use deno_core::BufView; +use deno_core::GarbageCollected; +use deno_core::JsBuffer; +use deno_core::OpState; +use deno_core::RcRef; +use deno_core::Resource; +use deno_core::ResourceId; +use deno_core::WriteOutcome; +use deno_error::JsError; +use deno_error::JsErrorBox; +use deno_permissions::PermissionCheckError; +use deno_tls::create_client_config; +use deno_tls::SocketUse; +use deno_tls::TlsError; +use deno_tls::TlsKeys; +use deno_tls::TlsKeysHolder; +use quinn::crypto::rustls::QuicClientConfig; +use quinn::crypto::rustls::QuicServerConfig; +use quinn::rustls::client::ClientSessionMemoryCache; +use quinn::rustls::client::ClientSessionStore; +use quinn::rustls::client::Resumption; +use serde::Deserialize; +use serde::Serialize; + +use crate::resolve_addr::resolve_addr_sync; +use crate::DefaultTlsOptions; +use crate::NetPermissions; +use crate::UnsafelyIgnoreCertificateErrors; + +#[derive(Debug, thiserror::Error, JsError)] +pub enum QuicError { + #[class(generic)] + #[error("Endpoint created by 'connectQuic' cannot be used for listening")] + CannotListen, + #[class(type)] + #[error("key and cert are required")] + MissingTlsKey, + #[class(type)] + #[error("Duration is invalid")] + InvalidDuration, + #[class(generic)] + #[error("Unable to resolve hostname")] + UnableToResolve, + #[class(inherit)] + #[error("{0}")] + StdIo(#[from] std::io::Error), + #[class(inherit)] + #[error("{0}")] + PermissionCheck(#[from] PermissionCheckError), + #[class(range)] + #[error("{0}")] + VarIntBoundsExceeded(#[from] quinn::VarIntBoundsExceeded), + #[class(generic)] + #[error("{0}")] + Rustls(#[from] quinn::rustls::Error), + #[class(inherit)] + #[error("{0}")] + Tls(#[from] TlsError), + #[class(generic)] + #[error("{0}")] + ConnectionError(#[from] quinn::ConnectionError), + #[class(generic)] + #[error("{0}")] + ConnectError(#[from] quinn::ConnectError), + #[class(generic)] + #[error("{0}")] + SendDatagramError(#[from] quinn::SendDatagramError), + #[class("BadResource")] + #[error("{0}")] + ClosedStream(#[from] quinn::ClosedStream), + #[class("BadResource")] + #[error("Invalid {0} resource")] + BadResource(&'static str), + #[class(range)] + #[error("Connection has reached the maximum number of concurrent outgoing {0} streams")] + MaxStreams(&'static str), + #[class(generic)] + #[error("{0}")] + Core(#[from] deno_core::error::AnyError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct CloseInfo { + close_code: u64, + reason: String, +} + +#[derive(Debug, Deserialize, Serialize)] +struct Addr { + hostname: String, + port: u16, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct ListenArgs { + alpn_protocols: Option>, +} + +#[derive(Deserialize, Default, PartialEq)] +#[serde(rename_all = "camelCase")] +struct TransportConfig { + keep_alive_interval: Option, + max_idle_timeout: Option, + max_concurrent_bidirectional_streams: Option, + max_concurrent_unidirectional_streams: Option, + preferred_address_v4: Option, + preferred_address_v6: Option, + congestion_control: Option, +} + +impl TryInto for TransportConfig { + type Error = QuicError; + + fn try_into(self) -> Result { + let mut cfg = quinn::TransportConfig::default(); + + if let Some(interval) = self.keep_alive_interval { + cfg.keep_alive_interval(Some(Duration::from_millis(interval))); + } + + if let Some(timeout) = self.max_idle_timeout { + cfg.max_idle_timeout(Some( + Duration::from_millis(timeout) + .try_into() + .map_err(|_| QuicError::InvalidDuration)?, + )); + } + + if let Some(max) = self.max_concurrent_bidirectional_streams { + cfg.max_concurrent_bidi_streams(max.into()); + } + + if let Some(max) = self.max_concurrent_unidirectional_streams { + cfg.max_concurrent_uni_streams(max.into()); + } + + if let Some(v) = self.congestion_control { + let controller: Option< + Arc, + > = match v.as_str() { + "low-latency" => { + Some(Arc::new(quinn::congestion::BbrConfig::default())) + } + "throughput" => { + Some(Arc::new(quinn::congestion::CubicConfig::default())) + } + _ => None, + }; + if let Some(controller) = controller { + cfg.congestion_controller_factory(controller); + } + } + + Ok(cfg) + } +} + +fn apply_server_transport_config( + config: &mut quinn::ServerConfig, + transport_config: TransportConfig, +) -> Result<(), QuicError> { + config.preferred_address_v4(transport_config.preferred_address_v4); + config.preferred_address_v6(transport_config.preferred_address_v6); + config.transport_config(Arc::new(transport_config.try_into()?)); + Ok(()) +} + +struct EndpointResource { + endpoint: quinn::Endpoint, + can_listen: bool, + session_store: Arc, +} + +impl GarbageCollected for EndpointResource {} + +#[op2] +#[cppgc] +pub(crate) fn op_quic_endpoint_create( + state: Rc>, + #[serde] addr: Addr, + can_listen: bool, +) -> Result +where + NP: NetPermissions + 'static, +{ + let addr = resolve_addr_sync(&addr.hostname, addr.port)? + .next() + .ok_or_else(|| QuicError::UnableToResolve)?; + + if can_listen { + state.borrow_mut().borrow_mut::().check_net( + &(&addr.ip().to_string(), Some(addr.port())), + "new Deno.QuicEndpoint()", + )?; + } else { + // If this is not a can-listen, assert that we will bind to an ephemeral port. + assert_eq!( + addr, + SocketAddr::from(( + IpAddr::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), + 0 + )) + ); + } + + let config = quinn::EndpointConfig::default(); + let socket = std::net::UdpSocket::bind(addr)?; + let endpoint = quinn::Endpoint::new( + config, + None, + socket, + quinn::default_runtime().unwrap(), + )?; + + Ok(EndpointResource { + endpoint, + can_listen, + session_store: Arc::new(ClientSessionMemoryCache::new(256)), + }) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_endpoint_get_addr( + #[cppgc] endpoint: &EndpointResource, +) -> Result { + let addr = endpoint.endpoint.local_addr()?; + let addr = Addr { + hostname: format!("{}", addr.ip()), + port: addr.port(), + }; + Ok(addr) +} + +#[op2(fast)] +pub(crate) fn op_quic_endpoint_close( + #[cppgc] endpoint: &EndpointResource, + #[bigint] close_code: u64, + #[string] reason: String, +) -> Result<(), QuicError> { + endpoint + .endpoint + .close(quinn::VarInt::from_u64(close_code)?, reason.as_bytes()); + Ok(()) +} + +struct ListenerResource(quinn::Endpoint, Arc); + +impl Drop for ListenerResource { + fn drop(&mut self) { + self.0.set_server_config(None); + } +} + +impl GarbageCollected for ListenerResource {} + +#[op2] +#[cppgc] +pub(crate) fn op_quic_endpoint_listen( + #[cppgc] endpoint: &EndpointResource, + #[serde] args: ListenArgs, + #[serde] transport_config: TransportConfig, + #[cppgc] keys: &TlsKeysHolder, +) -> Result { + if !endpoint.can_listen { + return Err(QuicError::CannotListen); + } + + let TlsKeys::Static(deno_tls::TlsKey(cert, key)) = keys.take() else { + return Err(QuicError::MissingTlsKey); + }; + + let mut crypto = + quinn::rustls::ServerConfig::builder_with_protocol_versions(&[ + &quinn::rustls::version::TLS13, + ]) + .with_no_client_auth() + .with_single_cert(cert.clone(), key.clone_key())?; + + // required by QUIC spec. + crypto.max_early_data_size = u32::MAX; + + if let Some(alpn_protocols) = args.alpn_protocols { + crypto.alpn_protocols = alpn_protocols + .into_iter() + .map(|alpn| alpn.into_bytes()) + .collect(); + } + + let server_config = Arc::new( + QuicServerConfig::try_from(crypto).expect("TLS13 is explicitly configured"), + ); + let mut config = quinn::ServerConfig::with_crypto(server_config.clone()); + apply_server_transport_config(&mut config, transport_config)?; + + endpoint.endpoint.set_server_config(Some(config)); + + Ok(ListenerResource(endpoint.endpoint.clone(), server_config)) +} + +struct ConnectionResource( + quinn::Connection, + RefCell>, +); + +impl GarbageCollected for ConnectionResource {} + +struct IncomingResource( + RefCell>, + Arc, +); + +impl GarbageCollected for IncomingResource {} + +#[op2(async)] +#[cppgc] +pub(crate) async fn op_quic_listener_accept( + #[cppgc] resource: &ListenerResource, +) -> Result { + match resource.0.accept().await { + Some(incoming) => Ok(IncomingResource( + RefCell::new(Some(incoming)), + resource.1.clone(), + )), + None => Err(QuicError::BadResource("QuicListener")), + } +} + +#[op2(fast)] +pub(crate) fn op_quic_listener_stop(#[cppgc] resource: &ListenerResource) { + resource.0.set_server_config(None); +} + +#[op2] +#[string] +pub(crate) fn op_quic_incoming_local_ip( + #[cppgc] incoming_resource: &IncomingResource, +) -> Result, QuicError> { + let Some(incoming) = incoming_resource.0.borrow_mut().take() else { + return Err(QuicError::BadResource("QuicIncoming")); + }; + Ok(incoming.local_ip().map(|ip| ip.to_string())) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_incoming_remote_addr( + #[cppgc] incoming_resource: &IncomingResource, +) -> Result { + let Some(incoming) = incoming_resource.0.borrow_mut().take() else { + return Err(QuicError::BadResource("QuicIncoming")); + }; + let addr = incoming.remote_address(); + Ok(Addr { + hostname: format!("{}", addr.ip()), + port: addr.port(), + }) +} + +#[op2(fast)] +pub(crate) fn op_quic_incoming_remote_addr_validated( + #[cppgc] incoming_resource: &IncomingResource, +) -> Result { + let Some(incoming) = incoming_resource.0.borrow_mut().take() else { + return Err(QuicError::BadResource("QuicIncoming")); + }; + Ok(incoming.remote_address_validated()) +} + +fn quic_incoming_accept( + incoming_resource: &IncomingResource, + transport_config: Option, +) -> Result { + let Some(incoming) = incoming_resource.0.borrow_mut().take() else { + return Err(QuicError::BadResource("QuicIncoming")); + }; + match transport_config { + Some(transport_config) if transport_config != Default::default() => { + let mut config = + quinn::ServerConfig::with_crypto(incoming_resource.1.clone()); + apply_server_transport_config(&mut config, transport_config)?; + Ok(incoming.accept_with(Arc::new(config))?) + } + _ => Ok(incoming.accept()?), + } +} + +#[op2(async)] +#[cppgc] +pub(crate) async fn op_quic_incoming_accept( + #[cppgc] incoming_resource: &IncomingResource, + #[serde] transport_config: Option, +) -> Result { + let connecting = quic_incoming_accept(incoming_resource, transport_config)?; + let conn = connecting.await?; + Ok(ConnectionResource(conn, RefCell::new(None))) +} + +#[op2] +#[cppgc] +pub(crate) fn op_quic_incoming_accept_0rtt( + #[cppgc] incoming_resource: &IncomingResource, + #[serde] transport_config: Option, +) -> Result { + let connecting = quic_incoming_accept(incoming_resource, transport_config)?; + match connecting.into_0rtt() { + Ok((conn, zrtt_accepted)) => { + Ok(ConnectionResource(conn, RefCell::new(Some(zrtt_accepted)))) + } + Err(_connecting) => { + unreachable!("0.5-RTT always succeeds"); + } + } +} + +#[op2] +#[serde] +pub(crate) fn op_quic_incoming_refuse( + #[cppgc] incoming: &IncomingResource, +) -> Result<(), QuicError> { + let Some(incoming) = incoming.0.borrow_mut().take() else { + return Err(QuicError::BadResource("QuicIncoming")); + }; + incoming.refuse(); + Ok(()) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_incoming_ignore( + #[cppgc] incoming: &IncomingResource, +) -> Result<(), QuicError> { + let Some(incoming) = incoming.0.borrow_mut().take() else { + return Err(QuicError::BadResource("QuicIncoming")); + }; + incoming.ignore(); + Ok(()) +} + +struct ConnectingResource(RefCell>); + +impl GarbageCollected for ConnectingResource {} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct ConnectArgs { + addr: Addr, + ca_certs: Option>, + alpn_protocols: Option>, + server_name: Option, +} + +#[op2] +#[cppgc] +pub(crate) fn op_quic_endpoint_connect( + state: Rc>, + #[cppgc] endpoint: &EndpointResource, + #[serde] args: ConnectArgs, + #[serde] transport_config: TransportConfig, + #[cppgc] key_pair: &TlsKeysHolder, +) -> Result +where + NP: NetPermissions + 'static, +{ + state.borrow_mut().borrow_mut::().check_net( + &(&args.addr.hostname, Some(args.addr.port)), + "Deno.connectQuic()", + )?; + + let sock_addr = resolve_addr_sync(&args.addr.hostname, args.addr.port)? + .next() + .ok_or_else(|| QuicError::UnableToResolve)?; + + let root_cert_store = state + .borrow() + .borrow::() + .root_cert_store()?; + + let unsafely_ignore_certificate_errors = state + .borrow() + .try_borrow::() + .and_then(|it| it.0.clone()); + + let ca_certs = args + .ca_certs + .unwrap_or_default() + .into_iter() + .map(|s| s.into_bytes()) + .collect::>(); + + let mut tls_config = create_client_config( + root_cert_store, + ca_certs, + unsafely_ignore_certificate_errors, + key_pair.take(), + SocketUse::GeneralSsl, + )?; + + if let Some(alpn_protocols) = args.alpn_protocols { + tls_config.alpn_protocols = + alpn_protocols.into_iter().map(|s| s.into_bytes()).collect(); + } + + tls_config.enable_early_data = true; + tls_config.resumption = Resumption::store(endpoint.session_store.clone()); + + let client_config = + QuicClientConfig::try_from(tls_config).expect("TLS13 supported"); + let mut client_config = quinn::ClientConfig::new(Arc::new(client_config)); + client_config.transport_config(Arc::new(transport_config.try_into()?)); + + let connecting = endpoint.endpoint.connect_with( + client_config, + sock_addr, + &args.server_name.unwrap_or(args.addr.hostname), + )?; + + Ok(ConnectingResource(RefCell::new(Some(connecting)))) +} + +#[op2(async)] +#[cppgc] +pub(crate) async fn op_quic_connecting_1rtt( + #[cppgc] connecting: &ConnectingResource, +) -> Result { + let Some(connecting) = connecting.0.borrow_mut().take() else { + return Err(QuicError::BadResource("QuicConnecting")); + }; + let conn = connecting.await?; + Ok(ConnectionResource(conn, RefCell::new(None))) +} + +#[op2] +#[cppgc] +pub(crate) fn op_quic_connecting_0rtt( + #[cppgc] connecting_res: &ConnectingResource, +) -> Option { + let connecting = connecting_res.0.borrow_mut().take()?; + match connecting.into_0rtt() { + Ok((conn, zrtt_accepted)) => { + Some(ConnectionResource(conn, RefCell::new(Some(zrtt_accepted)))) + } + Err(connecting) => { + *connecting_res.0.borrow_mut() = Some(connecting); + None + } + } +} + +#[op2] +#[string] +pub(crate) fn op_quic_connection_get_protocol( + #[cppgc] connection: &ConnectionResource, +) -> Option { + connection + .0 + .handshake_data() + .and_then(|h| h.downcast::().ok()) + .and_then(|h| h.protocol) + .map(|p| String::from_utf8_lossy(&p).into_owned()) +} + +#[op2] +#[string] +pub(crate) fn op_quic_connection_get_server_name( + #[cppgc] connection: &ConnectionResource, +) -> Option { + connection + .0 + .handshake_data() + .and_then(|h| h.downcast::().ok()) + .and_then(|h| h.server_name) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_connection_get_remote_addr( + #[cppgc] connection: &ConnectionResource, +) -> Result { + let addr = connection.0.remote_address(); + Ok(Addr { + hostname: format!("{}", addr.ip()), + port: addr.port(), + }) +} + +#[op2(fast)] +pub(crate) fn op_quic_connection_close( + #[cppgc] connection: &ConnectionResource, + #[bigint] close_code: u64, + #[string] reason: String, +) -> Result<(), QuicError> { + connection + .0 + .close(quinn::VarInt::from_u64(close_code)?, reason.as_bytes()); + Ok(()) +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_connection_closed( + #[cppgc] connection: &ConnectionResource, +) -> Result { + let e = connection.0.closed().await; + match e { + quinn::ConnectionError::LocallyClosed => Ok(CloseInfo { + close_code: 0, + reason: "".into(), + }), + quinn::ConnectionError::ApplicationClosed(i) => Ok(CloseInfo { + close_code: i.error_code.into(), + reason: String::from_utf8_lossy(&i.reason).into_owned(), + }), + e => Err(e.into()), + } +} + +#[op2(async)] +pub(crate) async fn op_quic_connection_handshake( + #[cppgc] connection: &ConnectionResource, +) { + let Some(zrtt_accepted) = connection.1.borrow_mut().take() else { + return; + }; + zrtt_accepted.await; +} + +struct SendStreamResource { + stream: AsyncRefCell, + stream_id: quinn::StreamId, + priority: AtomicI32, +} + +impl SendStreamResource { + fn new(stream: quinn::SendStream) -> Self { + Self { + stream_id: stream.id(), + priority: AtomicI32::new(stream.priority().unwrap_or(0)), + stream: AsyncRefCell::new(stream), + } + } +} + +impl Resource for SendStreamResource { + fn name(&self) -> Cow { + "quicSendStream".into() + } + + fn write(self: Rc, view: BufView) -> AsyncResult { + Box::pin(async move { + let mut stream = + RcRef::map(self.clone(), |r| &r.stream).borrow_mut().await; + stream + .set_priority(self.priority.load(Ordering::Relaxed)) + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))?; + let nwritten = stream + .write(&view) + .await + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))?; + Ok(WriteOutcome::Partial { nwritten, view }) + }) + } + + fn close(self: Rc) {} +} + +struct RecvStreamResource { + stream: AsyncRefCell, + stream_id: quinn::StreamId, +} + +impl RecvStreamResource { + fn new(stream: quinn::RecvStream) -> Self { + Self { + stream_id: stream.id(), + stream: AsyncRefCell::new(stream), + } + } +} + +impl Resource for RecvStreamResource { + fn name(&self) -> Cow { + "quicReceiveStream".into() + } + + fn read(self: Rc, limit: usize) -> AsyncResult { + Box::pin(async move { + let mut r = RcRef::map(self, |r| &r.stream).borrow_mut().await; + let mut data = vec![0; limit]; + let nread = r + .read(&mut data) + .await + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))? + .unwrap_or(0); + data.truncate(nread); + Ok(BufView::from(data)) + }) + } + + fn read_byob( + self: Rc, + mut buf: BufMutView, + ) -> AsyncResult<(usize, BufMutView)> { + Box::pin(async move { + let mut r = RcRef::map(self, |r| &r.stream).borrow_mut().await; + let nread = r + .read(&mut buf) + .await + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))? + .unwrap_or(0); + Ok((nread, buf)) + }) + } + + fn shutdown(self: Rc) -> AsyncResult<()> { + Box::pin(async move { + let mut r = RcRef::map(self, |r| &r.stream).borrow_mut().await; + r.stop(quinn::VarInt::from(0u32)) + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))?; + Ok(()) + }) + } +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_connection_accept_bi( + #[cppgc] connection: &ConnectionResource, + state: Rc>, +) -> Result<(ResourceId, ResourceId), QuicError> { + match connection.0.accept_bi().await { + Ok((tx, rx)) => { + let mut state = state.borrow_mut(); + let tx_rid = state.resource_table.add(SendStreamResource::new(tx)); + let rx_rid = state.resource_table.add(RecvStreamResource::new(rx)); + Ok((tx_rid, rx_rid)) + } + Err(e) => match e { + quinn::ConnectionError::LocallyClosed + | quinn::ConnectionError::ApplicationClosed(..) => { + Err(QuicError::BadResource("QuicConnection")) + } + _ => Err(e.into()), + }, + } +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_connection_open_bi( + #[cppgc] connection: &ConnectionResource, + state: Rc>, + wait_for_available: bool, +) -> Result<(ResourceId, ResourceId), QuicError> { + let (tx, rx) = if wait_for_available { + connection.0.open_bi().await? + } else { + let waker = noop_waker_ref(); + let mut cx = Context::from_waker(waker); + match pin!(connection.0.open_bi()).poll(&mut cx) { + Poll::Ready(r) => r?, + Poll::Pending => { + return Err(QuicError::MaxStreams("bidirectional")); + } + } + }; + let mut state = state.borrow_mut(); + let tx_rid = state.resource_table.add(SendStreamResource::new(tx)); + let rx_rid = state.resource_table.add(RecvStreamResource::new(rx)); + Ok((tx_rid, rx_rid)) +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_connection_accept_uni( + #[cppgc] connection: &ConnectionResource, + state: Rc>, +) -> Result { + match connection.0.accept_uni().await { + Ok(rx) => { + let rid = state + .borrow_mut() + .resource_table + .add(RecvStreamResource::new(rx)); + Ok(rid) + } + Err(e) => match e { + quinn::ConnectionError::LocallyClosed + | quinn::ConnectionError::ApplicationClosed(..) => { + Err(QuicError::BadResource("QuicConnection")) + } + _ => Err(e.into()), + }, + } +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_connection_open_uni( + #[cppgc] connection: &ConnectionResource, + state: Rc>, + wait_for_available: bool, +) -> Result { + let tx = if wait_for_available { + connection.0.open_uni().await? + } else { + let waker = noop_waker_ref(); + let mut cx = Context::from_waker(waker); + match pin!(connection.0.open_uni()).poll(&mut cx) { + Poll::Ready(r) => r?, + Poll::Pending => { + return Err(QuicError::MaxStreams("unidirectional")); + } + } + }; + let rid = state + .borrow_mut() + .resource_table + .add(SendStreamResource::new(tx)); + Ok(rid) +} + +#[op2(async)] +pub(crate) async fn op_quic_connection_send_datagram( + #[cppgc] connection: &ConnectionResource, + #[buffer] buf: JsBuffer, +) -> Result<(), QuicError> { + connection.0.send_datagram_wait(buf.to_vec().into()).await?; + Ok(()) +} + +#[op2(async)] +#[buffer] +pub(crate) async fn op_quic_connection_read_datagram( + #[cppgc] connection: &ConnectionResource, +) -> Result, QuicError> { + let data = connection.0.read_datagram().await?; + Ok(data.into()) +} + +#[op2(fast)] +pub(crate) fn op_quic_connection_get_max_datagram_size( + #[cppgc] connection: &ConnectionResource, +) -> u32 { + connection.0.max_datagram_size().unwrap_or(0) as _ +} + +#[op2(fast)] +pub(crate) fn op_quic_send_stream_get_priority( + state: Rc>, + #[smi] rid: ResourceId, +) -> Result { + let resource = state + .borrow() + .resource_table + .get::(rid)?; + Ok(resource.priority.load(Ordering::Relaxed)) +} + +#[op2(fast)] +pub(crate) fn op_quic_send_stream_set_priority( + state: Rc>, + #[smi] rid: ResourceId, + priority: i32, +) -> Result<(), ResourceError> { + let resource = state + .borrow() + .resource_table + .get::(rid)?; + resource.priority.store(priority, Ordering::Relaxed); + Ok(()) +} + +#[op2(fast)] +#[bigint] +pub(crate) fn op_quic_send_stream_get_id( + state: Rc>, + #[smi] rid: ResourceId, +) -> Result { + let resource = state + .borrow() + .resource_table + .get::(rid)?; + let stream_id = quinn::VarInt::from(resource.stream_id).into_inner(); + Ok(stream_id) +} + +#[op2(fast)] +#[bigint] +pub(crate) fn op_quic_recv_stream_get_id( + state: Rc>, + #[smi] rid: ResourceId, +) -> Result { + let resource = state + .borrow() + .resource_table + .get::(rid)?; + let stream_id = quinn::VarInt::from(resource.stream_id).into_inner(); + Ok(stream_id) +} diff --git a/ext/net/raw.rs b/ext/net/raw.rs index a2ebfb5acb..fc380635f6 100644 --- a/ext/net/raw.rs +++ b/ext/net/raw.rs @@ -1,16 +1,18 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::io::TcpStreamResource; -use crate::ops_tls::TlsStreamResource; -use deno_core::error::bad_resource_id; -use deno_core::error::custom_error; -use deno_core::error::AnyError; +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::rc::Rc; + +use deno_core::error::ResourceError; use deno_core::AsyncRefCell; use deno_core::CancelHandle; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ResourceTable; -use std::borrow::Cow; -use std::rc::Rc; +use deno_error::JsErrorBox; + +use crate::io::TcpStreamResource; +use crate::ops_tls::TlsStreamResource; pub trait NetworkStreamTrait: Into { type Resource; @@ -67,10 +69,10 @@ impl NetworkListenerResource { fn take( resource_table: &mut ResourceTable, listener_rid: ResourceId, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { if let Ok(resource_rc) = resource_table.take::(listener_rid) { let resource = Rc::try_unwrap(resource_rc) - .map_err(|_| custom_error("Busy", "Listener is currently in use"))?; + .map_err(|_| JsErrorBox::new("Busy", "Listener is currently in use"))?; return Ok(Some(resource.listener.into_inner().into())); } Ok(None) @@ -241,13 +243,13 @@ macro_rules! network_stream { /// Return a `NetworkStreamListener` if a resource exists for this `ResourceId` and it is currently /// not locked. - pub fn take_resource(resource_table: &mut ResourceTable, listener_rid: ResourceId) -> Result { + pub fn take_resource(resource_table: &mut ResourceTable, listener_rid: ResourceId) -> Result { $( if let Some(resource) = NetworkListenerResource::<$listener>::take(resource_table, listener_rid)? { return Ok(resource) } )* - Err(bad_resource_id()) + Err(JsErrorBox::from_err(ResourceError::BadResourceId)) } } }; @@ -320,12 +322,36 @@ impl From for NetworkStreamAddress { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum TakeNetworkStreamError { + #[class("Busy")] + #[error("TCP stream is currently in use")] + TcpBusy, + #[class("Busy")] + #[error("TLS stream is currently in use")] + TlsBusy, + #[cfg(unix)] + #[class("Busy")] + #[error("Unix socket is currently in use")] + UnixBusy, + #[class(generic)] + #[error(transparent)] + ReuniteTcp(#[from] tokio::net::tcp::ReuniteError), + #[cfg(unix)] + #[class(generic)] + #[error(transparent)] + ReuniteUnix(#[from] tokio::net::unix::ReuniteError), + #[class(inherit)] + #[error(transparent)] + Resource(deno_core::error::ResourceError), +} + /// In some cases it may be more efficient to extract the resource from the resource table and use it directly (for example, an HTTP server). /// This method will extract a stream from the resource table and return it, unwrapped. pub fn take_network_stream_resource( resource_table: &mut ResourceTable, stream_rid: ResourceId, -) -> Result { +) -> Result { // The stream we're attempting to unwrap may be in use somewhere else. If that's the case, we cannot proceed // with the process of unwrapping this connection, so we just return a bad resource error. // See also: https://github.com/denoland/deno/pull/16242 @@ -334,7 +360,7 @@ pub fn take_network_stream_resource( { // This TCP connection might be used somewhere else. let resource = Rc::try_unwrap(resource_rc) - .map_err(|_| custom_error("Busy", "TCP stream is currently in use"))?; + .map_err(|_| TakeNetworkStreamError::TcpBusy)?; let (read_half, write_half) = resource.into_inner(); let tcp_stream = read_half.reunite(write_half)?; return Ok(NetworkStream::Tcp(tcp_stream)); @@ -344,7 +370,7 @@ pub fn take_network_stream_resource( { // This TLS connection might be used somewhere else. let resource = Rc::try_unwrap(resource_rc) - .map_err(|_| custom_error("Busy", "TLS stream is currently in use"))?; + .map_err(|_| TakeNetworkStreamError::TlsBusy)?; let (read_half, write_half) = resource.into_inner(); let tls_stream = read_half.unsplit(write_half); return Ok(NetworkStream::Tls(tls_stream)); @@ -356,13 +382,15 @@ pub fn take_network_stream_resource( { // This UNIX socket might be used somewhere else. let resource = Rc::try_unwrap(resource_rc) - .map_err(|_| custom_error("Busy", "Unix socket is currently in use"))?; + .map_err(|_| TakeNetworkStreamError::UnixBusy)?; let (read_half, write_half) = resource.into_inner(); let unix_stream = read_half.reunite(write_half)?; return Ok(NetworkStream::Unix(unix_stream)); } - Err(bad_resource_id()) + Err(TakeNetworkStreamError::Resource( + ResourceError::BadResourceId, + )) } /// In some cases it may be more efficient to extract the resource from the resource table and use it directly (for example, an HTTP server). @@ -370,6 +398,6 @@ pub fn take_network_stream_resource( pub fn take_network_stream_listener_resource( resource_table: &mut ResourceTable, listener_rid: ResourceId, -) -> Result { +) -> Result { NetworkStreamListener::take_resource(resource_table, listener_rid) } diff --git a/ext/net/resolve_addr.rs b/ext/net/resolve_addr.rs index 3a97081eac..9a757391ea 100644 --- a/ext/net/resolve_addr.rs +++ b/ext/net/resolve_addr.rs @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::net::SocketAddr; use std::net::ToSocketAddrs; + use tokio::net::lookup_host; /// Resolve network address *asynchronously*. @@ -38,12 +39,13 @@ fn make_addr_port_pair(hostname: &str, port: u16) -> (&str, u16) { #[cfg(test)] mod tests { - use super::*; use std::net::Ipv4Addr; use std::net::Ipv6Addr; use std::net::SocketAddrV4; use std::net::SocketAddrV6; + use super::*; + #[tokio::test] async fn resolve_addr1() { let expected = vec![SocketAddr::V4(SocketAddrV4::new( diff --git a/ext/net/tcp.rs b/ext/net/tcp.rs index 63baa8e4be..f2cd5f5705 100644 --- a/ext/net/tcp.rs +++ b/ext/net/tcp.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 1bfe3a4d8d..6cd2b32866 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_node" -version = "0.119.0" +version = "0.123.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -14,10 +14,10 @@ description = "Node compatibility for Deno" path = "lib.rs" [features] -sync_fs = ["deno_package_json/sync", "node_resolver/sync"] +sync_fs = ["deno_fs/sync_fs", "deno_package_json/sync", "node_resolver/sync"] [dependencies] -aead-gcm-stream = "0.3" +aead-gcm-stream = "0.4" aes.workspace = true async-trait.workspace = true base64.workspace = true @@ -29,6 +29,7 @@ cbc.workspace = true const-oid = "0.9.5" data-encoding.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_fetch.workspace = true deno_fs.workspace = true deno_io.workspace = true @@ -49,7 +50,6 @@ errno = "0.2.8" faster-hex.workspace = true h2.workspace = true hkdf.workspace = true -home = "0.5.9" http.workspace = true http-body-util.workspace = true hyper.workspace = true @@ -93,6 +93,7 @@ simd-json = "0.14.0" sm3 = "0.4.2" spki.workspace = true stable_deref_trait = "1.2.0" +sys_traits = { workspace = true, features = ["real", "winapi", "libc"] } thiserror.workspace = true tokio.workspace = true tokio-eld = "0.2" diff --git a/ext/node/benchmarks/child_process_ipc.mjs b/ext/node/benchmarks/child_process_ipc.mjs index fa671d76f7..aa4e2699d7 100644 --- a/ext/node/benchmarks/child_process_ipc.mjs +++ b/ext/node/benchmarks/child_process_ipc.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { fork } from "node:child_process"; import process from "node:process"; diff --git a/ext/node/build.rs b/ext/node/build.rs index 041110f257..0d42f2cd76 100644 --- a/ext/node/build.rs +++ b/ext/node/build.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::env; diff --git a/ext/node/global.rs b/ext/node/global.rs index 4d6695431d..92439773d6 100644 --- a/ext/node/global.rs +++ b/ext/node/global.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::v8; use deno_core::v8::GetPropertyNamesArgs; @@ -54,6 +54,8 @@ const fn str_to_utf16(s: &str) -> [u16; N] { // - clearTimeout (both, but different implementation) // - global (node only) // - performance (both, but different implementation) +// - process (always available in Node, while the availability in Deno depends +// on project creation time in Deno Deploy) // - setImmediate (node only) // - setInterval (both, but different implementation) // - setTimeout (both, but different implementation) @@ -61,7 +63,7 @@ const fn str_to_utf16(s: &str) -> [u16; N] { // UTF-16 encodings of the managed globals. THIS LIST MUST BE SORTED. #[rustfmt::skip] -const MANAGED_GLOBALS: [&[u16]; 12] = [ +const MANAGED_GLOBALS: [&[u16]; 13] = [ &str_to_utf16::<6>("Buffer"), &str_to_utf16::<17>("WorkerGlobalScope"), &str_to_utf16::<14>("clearImmediate"), @@ -69,6 +71,7 @@ const MANAGED_GLOBALS: [&[u16]; 12] = [ &str_to_utf16::<12>("clearTimeout"), &str_to_utf16::<6>("global"), &str_to_utf16::<11>("performance"), + &str_to_utf16::<7>("process"), &str_to_utf16::<4>("self"), &str_to_utf16::<12>("setImmediate"), &str_to_utf16::<11>("setInterval"), diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 1e6c920c9e..325cec6f5b 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![deny(clippy::print_stderr)] #![deny(clippy::print_stdout)] @@ -8,14 +8,17 @@ use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::url::Url; #[allow(unused_imports)] use deno_core::v8; use deno_core::v8::ExternalReference; +use deno_error::JsErrorBox; use node_resolver::errors::ClosestPkgJsonError; -use node_resolver::NpmPackageFolderResolverRc; +use node_resolver::InNpmPackageChecker; +use node_resolver::IsBuiltInNodeModuleChecker; +use node_resolver::NpmPackageFolderResolver; +use node_resolver::PackageJsonResolverRc; use once_cell::sync::Lazy; extern crate libz_sys as zlib; @@ -155,12 +158,12 @@ pub trait NodeRequireLoader { &self, permissions: &mut dyn NodePermissions, path: &'a Path, - ) -> Result, AnyError>; + ) -> Result, JsErrorBox>; fn load_text_file_lossy( &self, path: &Path, - ) -> Result, AnyError>; + ) -> Result, JsErrorBox>; /// Get if the module kind is maybe CJS and loading should determine /// if its CJS or ESM. @@ -183,16 +186,21 @@ fn op_node_build_os() -> String { } #[derive(Clone)] -pub struct NodeExtInitServices { +pub struct NodeExtInitServices< + TInNpmPackageChecker: InNpmPackageChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: ExtNodeSys, +> { pub node_require_loader: NodeRequireLoaderRc, - pub node_resolver: NodeResolverRc, - pub npm_resolver: NpmPackageFolderResolverRc, - pub pkg_json_resolver: PackageJsonResolverRc, + pub node_resolver: + NodeResolverRc, + pub pkg_json_resolver: PackageJsonResolverRc, + pub sys: TSys, } deno_core::extension!(deno_node, deps = [ deno_io, deno_fs ], - parameters = [P: NodePermissions], + parameters = [P: NodePermissions, TInNpmPackageChecker: InNpmPackageChecker, TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: ExtNodeSys], ops = [ ops::blocklist::op_socket_address_parse, ops::blocklist::op_socket_address_get_serialization, @@ -260,6 +268,7 @@ deno_core::extension!(deno_node, ops::crypto::keys::op_node_derive_public_key_from_private_key, ops::crypto::keys::op_node_dh_keys_generate_and_export, ops::crypto::keys::op_node_export_private_key_der, + ops::crypto::keys::op_node_export_private_key_jwk, ops::crypto::keys::op_node_export_private_key_pem, ops::crypto::keys::op_node_export_public_key_der, ops::crypto::keys::op_node_export_public_key_pem, @@ -389,29 +398,29 @@ deno_core::extension!(deno_node, op_node_build_os, ops::require::op_require_can_parse_as_esm, ops::require::op_require_init_paths, - ops::require::op_require_node_module_paths

, + ops::require::op_require_node_module_paths, ops::require::op_require_proxy_path, - ops::require::op_require_is_deno_dir_package, - ops::require::op_require_resolve_deno_dir, + ops::require::op_require_is_deno_dir_package, + ops::require::op_require_resolve_deno_dir, ops::require::op_require_is_maybe_cjs, ops::require::op_require_is_request_relative, ops::require::op_require_resolve_lookup_paths, - ops::require::op_require_try_self_parent_path

, - ops::require::op_require_try_self

, - ops::require::op_require_real_path

, + ops::require::op_require_try_self_parent_path, + ops::require::op_require_try_self, + ops::require::op_require_real_path, ops::require::op_require_path_is_absolute, ops::require::op_require_path_dirname, - ops::require::op_require_stat

, + ops::require::op_require_stat, ops::require::op_require_path_resolve, ops::require::op_require_path_basename, ops::require::op_require_read_file

, ops::require::op_require_as_file_path, - ops::require::op_require_resolve_exports

, - ops::require::op_require_read_package_scope

, - ops::require::op_require_package_imports_resolve

, + ops::require::op_require_resolve_exports, + ops::require::op_require_read_package_scope, + ops::require::op_require_package_imports_resolve, ops::require::op_require_break_on_next_statement, ops::util::op_node_guess_handle_type, - ops::worker_threads::op_worker_threads_filename

, + ops::worker_threads::op_worker_threads_filename, ops::ipc::op_node_child_ipc_pipe, ops::ipc::op_node_ipc_write, ops::ipc::op_node_ipc_read, @@ -483,7 +492,7 @@ deno_core::extension!(deno_node, "_fs/_fs_watch.ts", "_fs/_fs_write.mjs", "_fs/_fs_writeFile.ts", - "_fs/_fs_writev.mjs", + "_fs/_fs_writev.ts", "_next_tick.ts", "_process/exiting.ts", "_process/process.ts", @@ -677,16 +686,16 @@ deno_core::extension!(deno_node, "node:zlib" = "zlib.ts", ], options = { - maybe_init: Option, + maybe_init: Option>, fs: deno_fs::FileSystemRc, }, state = |state, options| { state.put(options.fs.clone()); if let Some(init) = &options.maybe_init { + state.put(init.sys.clone()); state.put(init.node_require_loader.clone()); state.put(init.node_resolver.clone()); - state.put(init.npm_resolver.clone()); state.put(init.pkg_json_resolver.clone()); } }, @@ -807,92 +816,40 @@ deno_core::extension!(deno_node, }, ); -pub type NodeResolver = node_resolver::NodeResolver; -#[allow(clippy::disallowed_types)] -pub type NodeResolverRc = - deno_fs::sync::MaybeArc>; -pub type PackageJsonResolver = - node_resolver::PackageJsonResolver; -#[allow(clippy::disallowed_types)] -pub type PackageJsonResolverRc = deno_fs::sync::MaybeArc< - node_resolver::PackageJsonResolver, ->; - #[derive(Debug)] -pub struct DenoFsNodeResolverEnv { - fs: deno_fs::FileSystemRc, -} +pub struct RealIsBuiltInNodeModuleChecker; -impl DenoFsNodeResolverEnv { - pub fn new(fs: deno_fs::FileSystemRc) -> Self { - Self { fs } - } -} - -impl node_resolver::env::NodeResolverEnv for DenoFsNodeResolverEnv { +impl IsBuiltInNodeModuleChecker for RealIsBuiltInNodeModuleChecker { + #[inline] fn is_builtin_node_module(&self, specifier: &str) -> bool { is_builtin_node_module(specifier) } - - fn realpath_sync( - &self, - path: &std::path::Path, - ) -> std::io::Result { - self - .fs - .realpath_sync(path) - .map_err(|err| err.into_io_error()) - } - - fn stat_sync( - &self, - path: &std::path::Path, - ) -> std::io::Result { - self - .fs - .stat_sync(path) - .map(|stat| node_resolver::env::NodeResolverFsStat { - is_file: stat.is_file, - is_dir: stat.is_directory, - is_symlink: stat.is_symlink, - }) - .map_err(|err| err.into_io_error()) - } - - fn exists_sync(&self, path: &std::path::Path) -> bool { - self.fs.exists_sync(path) - } - - fn pkg_json_fs(&self) -> &dyn deno_package_json::fs::DenoPkgJsonFs { - self - } } -impl deno_package_json::fs::DenoPkgJsonFs for DenoFsNodeResolverEnv { - fn read_to_string_lossy( - &self, - path: &std::path::Path, - ) -> Result, std::io::Error> { - self - .fs - .read_text_file_lossy_sync(path, None) - .map_err(|err| err.into_io_error()) - } +pub trait ExtNodeSys: + sys_traits::BaseFsCanonicalize + + sys_traits::BaseFsMetadata + + sys_traits::BaseFsRead + + sys_traits::EnvCurrentDir + + Clone +{ } -pub struct DenoPkgJsonFsAdapter<'a>(pub &'a dyn deno_fs::FileSystem); +impl ExtNodeSys for sys_traits::impls::RealSys {} -impl<'a> deno_package_json::fs::DenoPkgJsonFs for DenoPkgJsonFsAdapter<'a> { - fn read_to_string_lossy( - &self, - path: &Path, - ) -> Result, std::io::Error> { - self - .0 - .read_text_file_lossy_sync(path, None) - .map_err(|err| err.into_io_error()) - } -} +pub type NodeResolver = + node_resolver::NodeResolver< + TInNpmPackageChecker, + RealIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >; +#[allow(clippy::disallowed_types)] +pub type NodeResolverRc = + deno_fs::sync::MaybeArc< + NodeResolver, + >; +#[allow(clippy::disallowed_types)] pub fn create_host_defined_options<'s>( scope: &mut v8::HandleScope<'s>, diff --git a/ext/node/ops/blocklist.rs b/ext/node/ops/blocklist.rs index 6c64d68eca..16bda73fe0 100644 --- a/ext/node/ops/blocklist.rs +++ b/ext/node/ops/blocklist.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashSet; @@ -9,7 +9,6 @@ use std::net::SocketAddr; use deno_core::op2; use deno_core::OpState; - use ipnetwork::IpNetwork; use ipnetwork::Ipv4Network; use ipnetwork::Ipv6Network; @@ -24,7 +23,8 @@ impl deno_core::GarbageCollected for BlockListResource {} #[derive(Serialize)] struct SocketAddressSerialization(String, String); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] pub enum BlocklistError { #[error("{0}")] AddrParse(#[from] std::net::AddrParseError), diff --git a/ext/node/ops/buffer.rs b/ext/node/ops/buffer.rs index 01f878ec15..0e8cff5cc0 100644 --- a/ext/node/ops/buffer.rs +++ b/ext/node/ops/buffer.rs @@ -1,8 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::anyhow::anyhow; -use deno_core::anyhow::Result; use deno_core::op2; +use deno_error::JsErrorBox; #[op2(fast)] pub fn op_is_ascii(#[buffer] buf: &[u8]) -> bool { @@ -20,7 +19,7 @@ pub fn op_transcode( #[buffer] source: &[u8], #[string] from_encoding: &str, #[string] to_encoding: &str, -) -> Result> { +) -> Result, JsErrorBox> { match (from_encoding, to_encoding) { ("utf8", "ascii") => Ok(utf8_to_ascii(source)), ("utf8", "latin1") => Ok(utf8_to_latin1(source)), @@ -29,7 +28,9 @@ pub fn op_transcode( ("latin1", "utf16le") | ("ascii", "utf16le") => { Ok(latin1_ascii_to_utf16le(source)) } - (from, to) => Err(anyhow!("Unable to transcode Buffer {from}->{to}")), + (from, to) => Err(JsErrorBox::generic(format!( + "Unable to transcode Buffer {from}->{to}" + ))), } } @@ -42,18 +43,19 @@ fn latin1_ascii_to_utf16le(source: &[u8]) -> Vec { result } -fn utf16le_to_utf8(source: &[u8]) -> Result> { +fn utf16le_to_utf8(source: &[u8]) -> Result, JsErrorBox> { let ucs2_vec: Vec = source .chunks(2) .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) .collect(); String::from_utf16(&ucs2_vec) .map(|utf8_string| utf8_string.into_bytes()) - .map_err(|e| anyhow!("Invalid UTF-16 sequence: {}", e)) + .map_err(|e| JsErrorBox::generic(format!("Invalid UTF-16 sequence: {}", e))) } -fn utf8_to_utf16le(source: &[u8]) -> Result> { - let utf8_string = std::str::from_utf8(source)?; +fn utf8_to_utf16le(source: &[u8]) -> Result, JsErrorBox> { + let utf8_string = + std::str::from_utf8(source).map_err(JsErrorBox::from_err)?; let ucs2_vec: Vec = utf8_string.encode_utf16().collect(); let bytes: Vec = ucs2_vec.iter().flat_map(|&x| x.to_le_bytes()).collect(); Ok(bytes) diff --git a/ext/node/ops/crypto/cipher.rs b/ext/node/ops/crypto/cipher.rs index ec45146b49..a12f36e04a 100644 --- a/ext/node/ops/crypto/cipher.rs +++ b/ext/node/ops/crypto/cipher.rs @@ -1,4 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; use aes::cipher::block_padding::Pkcs7; use aes::cipher::BlockDecryptMut; @@ -8,10 +12,6 @@ use deno_core::Resource; use digest::generic_array::GenericArray; use digest::KeyInit; -use std::borrow::Cow; -use std::cell::RefCell; -use std::rc::Rc; - type Tag = Option>; type Aes128Gcm = aead_gcm_stream::AesGcm; @@ -47,12 +47,15 @@ pub struct DecipherContext { decipher: Rc>, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CipherContextError { + #[class(type)] #[error("Cipher context is already in use")] ContextInUse, + #[class(inherit)] #[error("{0}")] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Cipher(#[from] CipherError), } @@ -94,12 +97,15 @@ impl CipherContext { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum DecipherContextError { + #[class(type)] #[error("Decipher context is already in use")] ContextInUse, + #[class(inherit)] #[error("{0}")] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Decipher(#[from] DecipherError), } @@ -150,16 +156,21 @@ impl Resource for DecipherContext { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CipherError { + #[class(type)] #[error("IV length must be 12 bytes")] InvalidIvLength, + #[class(range)] #[error("Invalid key length")] InvalidKeyLength, + #[class(type)] #[error("Invalid initialization vector")] InvalidInitializationVector, + #[class(type)] #[error("Cannot pad the input data")] CannotPadInputData, + #[class(type)] #[error("Unknown cipher {0}")] UnknownCipher(String), } @@ -172,27 +183,19 @@ impl Cipher { ) -> Result { use Cipher::*; Ok(match algorithm_name { - "aes-128-cbc" => { + "aes128" | "aes-128-cbc" => { Aes128Cbc(Box::new(cbc::Encryptor::new(key.into(), iv.into()))) } "aes-128-ecb" => Aes128Ecb(Box::new(ecb::Encryptor::new(key.into()))), "aes-192-ecb" => Aes192Ecb(Box::new(ecb::Encryptor::new(key.into()))), "aes-256-ecb" => Aes256Ecb(Box::new(ecb::Encryptor::new(key.into()))), "aes-128-gcm" => { - if iv.len() != 12 { - return Err(CipherError::InvalidIvLength); - } - let cipher = aead_gcm_stream::AesGcm::::new(key.into(), iv); Aes128Gcm(Box::new(cipher)) } "aes-256-gcm" => { - if iv.len() != 12 { - return Err(CipherError::InvalidIvLength); - } - let cipher = aead_gcm_stream::AesGcm::::new(key.into(), iv); @@ -360,22 +363,30 @@ impl Cipher { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum DecipherError { + #[class(type)] #[error("IV length must be 12 bytes")] InvalidIvLength, + #[class(range)] #[error("Invalid key length")] InvalidKeyLength, + #[class(type)] #[error("Invalid initialization vector")] InvalidInitializationVector, + #[class(type)] #[error("Cannot unpad the input data")] CannotUnpadInputData, + #[class(type)] #[error("Failed to authenticate data")] DataAuthenticationFailed, + #[class(type)] #[error("setAutoPadding(false) not supported for Aes128Gcm yet")] SetAutoPaddingFalseAes128GcmUnsupported, + #[class(type)] #[error("setAutoPadding(false) not supported for Aes256Gcm yet")] SetAutoPaddingFalseAes256GcmUnsupported, + #[class(type)] #[error("Unknown cipher {0}")] UnknownCipher(String), } @@ -395,20 +406,12 @@ impl Decipher { "aes-192-ecb" => Aes192Ecb(Box::new(ecb::Decryptor::new(key.into()))), "aes-256-ecb" => Aes256Ecb(Box::new(ecb::Decryptor::new(key.into()))), "aes-128-gcm" => { - if iv.len() != 12 { - return Err(DecipherError::InvalidIvLength); - } - let decipher = aead_gcm_stream::AesGcm::::new(key.into(), iv); Aes128Gcm(Box::new(decipher)) } "aes-256-gcm" => { - if iv.len() != 12 { - return Err(DecipherError::InvalidIvLength); - } - let decipher = aead_gcm_stream::AesGcm::::new(key.into(), iv); diff --git a/ext/node/ops/crypto/dh.rs b/ext/node/ops/crypto/dh.rs index ff2bd030eb..88452c3660 100644 --- a/ext/node/ops/crypto/dh.rs +++ b/ext/node/ops/crypto/dh.rs @@ -1,10 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::primes::Prime; use num_bigint_dig::BigUint; use num_bigint_dig::RandBigInt; use num_traits::FromPrimitive; +use super::primes::Prime; + #[derive(Clone)] pub struct PublicKey(BigUint); diff --git a/ext/node/ops/crypto/digest.rs b/ext/node/ops/crypto/digest.rs index a7d8fb51f1..5f15dace30 100644 --- a/ext/node/ops/crypto/digest.rs +++ b/ext/node/ops/crypto/digest.rs @@ -1,11 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::cell::RefCell; +use std::rc::Rc; + use deno_core::GarbageCollected; use digest::Digest; use digest::DynDigest; use digest::ExtendableOutput; use digest::Update; -use std::cell::RefCell; -use std::rc::Rc; pub struct Hasher { pub hash: Rc>>, @@ -182,7 +183,8 @@ pub enum Hash { use Hash::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] pub enum HashError { #[error("Output length mismatch for non-extendable algorithm")] OutputLengthMismatch, diff --git a/ext/node/ops/crypto/keys.rs b/ext/node/ops/crypto/keys.rs index f164972d48..79b09faa26 100644 --- a/ext/node/ops/crypto/keys.rs +++ b/ext/node/ops/crypto/keys.rs @@ -1,15 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; use base64::Engine; -use deno_core::error::type_error; use deno_core::op2; use deno_core::serde_v8::BigInt as V8BigInt; use deno_core::unsync::spawn_blocking; use deno_core::GarbageCollected; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use ed25519_dalek::pkcs8::BitStringRef; use elliptic_curve::JwkEcKey; use num_bigint::BigInt; @@ -26,6 +26,7 @@ use rsa::pkcs1::DecodeRsaPrivateKey as _; use rsa::pkcs1::DecodeRsaPublicKey; use rsa::pkcs1::EncodeRsaPrivateKey as _; use rsa::pkcs1::EncodeRsaPublicKey; +use rsa::traits::PrivateKeyParts; use rsa::traits::PublicKeyParts; use rsa::RsaPrivateKey; use rsa::RsaPublicKey; @@ -255,6 +256,16 @@ impl EcPrivateKey { EcPrivateKey::P384(key) => EcPublicKey::P384(key.public_key()), } } + + pub fn to_jwk(&self) -> Result { + match self { + EcPrivateKey::P224(_) => { + Err(AsymmetricPrivateKeyJwkError::UnsupportedJwkEcCurveP224) + } + EcPrivateKey::P256(key) => Ok(key.to_jwk()), + EcPrivateKey::P384(key) => Ok(key.to_jwk()), + } + } } // https://oidref.com/ @@ -364,55 +375,72 @@ impl<'a> TryFrom> for RsaPssParameters<'a> { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum X509PublicKeyError { + #[class(generic)] #[error(transparent)] - X509(#[from] x509_parser::error::X509Error), + X509(#[from] X509Error), + #[class(generic)] #[error(transparent)] Rsa(#[from] rsa::Error), + #[class(generic)] #[error(transparent)] Asn1(#[from] x509_parser::der_parser::asn1_rs::Error), + #[class(generic)] #[error(transparent)] Ec(#[from] elliptic_curve::Error), + #[class(type)] #[error("unsupported ec named curve")] UnsupportedEcNamedCurve, + #[class(type)] #[error("missing ec parameters")] MissingEcParameters, + #[class(type)] #[error("malformed DSS public key")] MalformedDssPublicKey, + #[class(type)] #[error("unsupported x509 public key type")] UnsupportedX509KeyType, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum RsaJwkError { + #[class(generic)] #[error(transparent)] Base64(#[from] base64::DecodeError), + #[class(generic)] #[error(transparent)] Rsa(#[from] rsa::Error), + #[class(type)] #[error("missing RSA private component")] MissingRsaPrivateComponent, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum EcJwkError { + #[class(generic)] #[error(transparent)] Ec(#[from] elliptic_curve::Error), + #[class(type)] #[error("unsupported curve: {0}")] UnsupportedCurve(String), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum EdRawError { + #[class(generic)] #[error(transparent)] Ed25519Signature(#[from] ed25519_dalek::SignatureError), + #[class(type)] #[error("invalid Ed25519 key")] InvalidEd25519Key, + #[class(type)] #[error("unsupported curve")] UnsupportedCurve, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum AsymmetricPrivateKeyError { #[error("invalid PEM private key: not valid utf8 starting at byte {0}")] InvalidPemPrivateKeyInvalidUtf8(usize), @@ -428,8 +456,13 @@ pub enum AsymmetricPrivateKeyError { InvalidSec1PrivateKey, #[error("unsupported PEM label: {0}")] UnsupportedPemLabel(String), + #[class(inherit)] #[error(transparent)] - RsaPssParamsParse(#[from] RsaPssParamsParseError), + RsaPssParamsParse( + #[from] + #[inherit] + RsaPssParamsParseError, + ), #[error("invalid encrypted PKCS#8 private key")] InvalidEncryptedPkcs8PrivateKey, #[error("invalid PKCS#8 private key")] @@ -462,58 +495,96 @@ pub enum AsymmetricPrivateKeyError { UnsupportedPrivateKeyOid, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum AsymmetricPublicKeyError { + #[class(type)] #[error("invalid PEM private key: not valid utf8 starting at byte {0}")] InvalidPemPrivateKeyInvalidUtf8(usize), + #[class(type)] #[error("invalid PEM public key")] InvalidPemPublicKey, + #[class(type)] #[error("invalid PKCS#1 public key")] InvalidPkcs1PublicKey, + #[class(inherit)] #[error(transparent)] - AsymmetricPrivateKey(#[from] AsymmetricPrivateKeyError), + AsymmetricPrivateKey( + #[from] + #[inherit] + AsymmetricPrivateKeyError, + ), + #[class(type)] #[error("invalid x509 certificate")] InvalidX509Certificate, + #[class(generic)] #[error(transparent)] X509(#[from] x509_parser::nom::Err), + #[class(inherit)] #[error(transparent)] - X509PublicKey(#[from] X509PublicKeyError), + X509PublicKey( + #[from] + #[inherit] + X509PublicKeyError, + ), + #[class(type)] #[error("unsupported PEM label: {0}")] UnsupportedPemLabel(String), + #[class(type)] #[error("invalid SPKI public key")] InvalidSpkiPublicKey, + #[class(type)] #[error("unsupported key type: {0}")] UnsupportedKeyType(String), + #[class(type)] #[error("unsupported key format: {0}")] UnsupportedKeyFormat(String), + #[class(generic)] #[error(transparent)] Spki(#[from] spki::Error), + #[class(generic)] #[error(transparent)] Pkcs1(#[from] rsa::pkcs1::Error), + #[class(inherit)] #[error(transparent)] - RsaPssParamsParse(#[from] RsaPssParamsParseError), + RsaPssParamsParse( + #[from] + #[inherit] + RsaPssParamsParseError, + ), + #[class(type)] #[error("malformed DSS public key")] MalformedDssPublicKey, + #[class(type)] #[error("malformed or missing named curve in ec parameters")] MalformedOrMissingNamedCurveInEcParameters, + #[class(type)] #[error("malformed or missing public key in ec spki")] MalformedOrMissingPublicKeyInEcSpki, + #[class(generic)] #[error(transparent)] Ec(#[from] elliptic_curve::Error), + #[class(type)] #[error("unsupported ec named curve")] UnsupportedEcNamedCurve, + #[class(type)] #[error("malformed or missing public key in x25519 spki")] MalformedOrMissingPublicKeyInX25519Spki, + #[class(type)] #[error("x25519 public key is too short")] X25519PublicKeyIsTooShort, + #[class(type)] #[error("invalid Ed25519 public key")] InvalidEd25519PublicKey, + #[class(type)] #[error("missing dh parameters")] MissingDhParameters, + #[class(type)] #[error("malformed dh parameters")] MalformedDhParameters, + #[class(type)] #[error("malformed or missing public key in dh spki")] MalformedOrMissingPublicKeyInDhSpki, + #[class(type)] #[error("unsupported private key oid")] UnsupportedPrivateKeyOid, } @@ -1032,7 +1103,8 @@ impl KeyObjectHandle { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum RsaPssParamsParseError { #[error("malformed pss private key parameters")] MalformedPssPrivateKeyParameters, @@ -1107,7 +1179,19 @@ fn bytes_to_b64(bytes: &[u8]) -> String { BASE64_URL_SAFE_NO_PAD.encode(bytes) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] +pub enum AsymmetricPrivateKeyJwkError { + #[error("key is not an asymmetric private key")] + KeyIsNotAsymmetricPrivateKey, + #[error("Unsupported JWK EC curve: P224")] + UnsupportedJwkEcCurveP224, + #[error("jwk export not implemented for this key type")] + JwkExportNotImplementedForKeyType, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum AsymmetricPublicKeyJwkError { #[error("key is not an asymmetric public key")] KeyIsNotAsymmetricPublicKey, @@ -1117,7 +1201,8 @@ pub enum AsymmetricPublicKeyJwkError { JwkExportNotImplementedForKeyType, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum AsymmetricPublicKeyDerError { #[error("key is not an asymmetric public key")] KeyIsNotAsymmetricPublicKey, @@ -1302,7 +1387,8 @@ impl AsymmetricPublicKey { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum AsymmetricPrivateKeyDerError { #[error("key is not an asymmetric private key")] KeyIsNotAsymmetricPrivateKey, @@ -1314,6 +1400,7 @@ pub enum AsymmetricPrivateKeyDerError { InvalidEcPrivateKey, #[error("exporting non-EC private key as SEC1 is not supported")] ExportingNonEcPrivateKeyAsSec1Unsupported, + #[class(type)] #[error("exporting RSA-PSS private key as PKCS#8 is not supported yet")] ExportingNonRsaPssPrivateKeyAsPkcs8Unsupported, #[error("invalid DSA private key")] @@ -1328,7 +1415,73 @@ pub enum AsymmetricPrivateKeyDerError { UnsupportedKeyType(String), } +// https://datatracker.ietf.org/doc/html/rfc7518#section-6.3.2 +fn rsa_private_to_jwk(key: &RsaPrivateKey) -> deno_core::serde_json::Value { + let n = key.n(); + let e = key.e(); + let d = key.d(); + let p = &key.primes()[0]; + let q = &key.primes()[1]; + let dp = key.dp(); + let dq = key.dq(); + let qi = key.crt_coefficient(); + let oth = &key.primes()[2..]; + + let mut obj = deno_core::serde_json::json!({ + "kty": "RSA", + "n": bytes_to_b64(&n.to_bytes_be()), + "e": bytes_to_b64(&e.to_bytes_be()), + "d": bytes_to_b64(&d.to_bytes_be()), + "p": bytes_to_b64(&p.to_bytes_be()), + "q": bytes_to_b64(&q.to_bytes_be()), + "dp": dp.map(|dp| bytes_to_b64(&dp.to_bytes_be())), + "dq": dq.map(|dq| bytes_to_b64(&dq.to_bytes_be())), + "qi": qi.map(|qi| bytes_to_b64(&qi.to_bytes_be())), + }); + + if !oth.is_empty() { + obj["oth"] = deno_core::serde_json::json!(oth + .iter() + .map(|o| o.to_bytes_be()) + .collect::>()); + } + + obj +} + impl AsymmetricPrivateKey { + fn export_jwk( + &self, + ) -> Result { + match self { + AsymmetricPrivateKey::Rsa(key) => Ok(rsa_private_to_jwk(key)), + AsymmetricPrivateKey::RsaPss(key) => Ok(rsa_private_to_jwk(&key.key)), + AsymmetricPrivateKey::Ec(key) => { + let jwk = key.to_jwk()?; + Ok(deno_core::serde_json::json!(jwk)) + } + AsymmetricPrivateKey::X25519(static_secret) => { + let bytes = static_secret.to_bytes(); + + Ok(deno_core::serde_json::json!({ + "kty": "OKP", + "crv": "X25519", + "d": bytes_to_b64(&bytes), + })) + } + AsymmetricPrivateKey::Ed25519(key) => { + let bytes = key.to_bytes(); + + Ok(deno_core::serde_json::json!({ + "kty": "OKP", + "crv": "Ed25519", + "d": bytes_to_b64(&bytes), + })) + } + _ => Err(AsymmetricPrivateKeyJwkError::JwkExportNotImplementedForKeyType), + } + } + fn export_der( &self, typ: &str, @@ -1528,7 +1681,7 @@ pub fn op_node_create_secret_key( #[string] pub fn op_node_get_asymmetric_key_type( #[cppgc] handle: &KeyObjectHandle, -) -> Result<&'static str, deno_core::error::AnyError> { +) -> Result<&'static str, JsErrorBox> { match handle { KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Rsa(_)) | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Rsa(_)) => { @@ -1554,9 +1707,9 @@ pub fn op_node_get_asymmetric_key_type( } KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Dh(_)) | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Dh(_)) => Ok("dh"), - KeyObjectHandle::Secret(_) => { - Err(type_error("symmetric key is not an asymmetric key")) - } + KeyObjectHandle::Secret(_) => Err(JsErrorBox::type_error( + "symmetric key is not an asymmetric key", + )), } } @@ -1599,7 +1752,7 @@ pub enum AsymmetricKeyDetails { #[serde] pub fn op_node_get_asymmetric_key_details( #[cppgc] handle: &KeyObjectHandle, -) -> Result { +) -> Result { match handle { KeyObjectHandle::AsymmetricPrivate(private_key) => match private_key { AsymmetricPrivateKey::Rsa(key) => { @@ -1707,9 +1860,9 @@ pub fn op_node_get_asymmetric_key_details( AsymmetricPublicKey::Ed25519(_) => Ok(AsymmetricKeyDetails::Ed25519), AsymmetricPublicKey::Dh(_) => Ok(AsymmetricKeyDetails::Dh), }, - KeyObjectHandle::Secret(_) => { - Err(type_error("symmetric key is not an asymmetric key")) - } + KeyObjectHandle::Secret(_) => Err(JsErrorBox::type_error( + "symmetric key is not an asymmetric key", + )), } } @@ -1717,12 +1870,12 @@ pub fn op_node_get_asymmetric_key_details( #[smi] pub fn op_node_get_symmetric_key_size( #[cppgc] handle: &KeyObjectHandle, -) -> Result { +) -> Result { match handle { KeyObjectHandle::AsymmetricPrivate(_) - | KeyObjectHandle::AsymmetricPublic(_) => { - Err(type_error("asymmetric key is not a symmetric key")) - } + | KeyObjectHandle::AsymmetricPublic(_) => Err(JsErrorBox::type_error( + "asymmetric key is not a symmetric key", + )), KeyObjectHandle::Secret(key) => Ok(key.len() * 8), } } @@ -1825,7 +1978,8 @@ pub async fn op_node_generate_rsa_key_async( .unwrap() } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] #[error("digest not allowed for RSA-PSS keys{}", .0.as_ref().map(|digest| format!(": {digest}")).unwrap_or_default())] pub struct GenerateRsaPssError(Option); @@ -1929,7 +2083,7 @@ pub async fn op_node_generate_rsa_pss_key_async( fn dsa_generate( modulus_length: usize, divisor_length: usize, -) -> Result { +) -> Result { let mut rng = rand::thread_rng(); use dsa::Components; use dsa::KeySize; @@ -1942,7 +2096,7 @@ fn dsa_generate( (2048, 256) => KeySize::DSA_2048_256, (3072, 256) => KeySize::DSA_3072_256, _ => { - return Err(type_error( + return Err(JsErrorBox::type_error( "Invalid modulusLength+divisorLength combination", )) } @@ -1960,7 +2114,7 @@ fn dsa_generate( pub fn op_node_generate_dsa_key( #[smi] modulus_length: usize, #[smi] divisor_length: usize, -) -> Result { +) -> Result { dsa_generate(modulus_length, divisor_length) } @@ -1969,15 +2123,13 @@ pub fn op_node_generate_dsa_key( pub async fn op_node_generate_dsa_key_async( #[smi] modulus_length: usize, #[smi] divisor_length: usize, -) -> Result { +) -> Result { spawn_blocking(move || dsa_generate(modulus_length, divisor_length)) .await .unwrap() } -fn ec_generate( - named_curve: &str, -) -> Result { +fn ec_generate(named_curve: &str) -> Result { let mut rng = rand::thread_rng(); // TODO(@littledivy): Support public key point encoding. // Default is uncompressed. @@ -1995,7 +2147,7 @@ fn ec_generate( AsymmetricPrivateKey::Ec(EcPrivateKey::P384(key)) } _ => { - return Err(type_error(format!( + return Err(JsErrorBox::type_error(format!( "unsupported named curve: {}", named_curve ))) @@ -2009,7 +2161,7 @@ fn ec_generate( #[cppgc] pub fn op_node_generate_ec_key( #[string] named_curve: &str, -) -> Result { +) -> Result { ec_generate(named_curve) } @@ -2017,7 +2169,7 @@ pub fn op_node_generate_ec_key( #[cppgc] pub async fn op_node_generate_ec_key_async( #[string] named_curve: String, -) -> Result { +) -> Result { spawn_blocking(move || ec_generate(&named_curve)) .await .unwrap() @@ -2073,7 +2225,7 @@ fn u32_slice_to_u8_slice(slice: &[u32]) -> &[u8] { fn dh_group_generate( group_name: &str, -) -> Result { +) -> Result { let (dh, prime, generator) = match group_name { "modp5" => ( dh::DiffieHellman::group::(), @@ -2105,7 +2257,7 @@ fn dh_group_generate( dh::Modp8192::MODULUS, dh::Modp8192::GENERATOR, ), - _ => return Err(type_error("Unsupported group name")), + _ => return Err(JsErrorBox::type_error("Unsupported group name")), }; let params = DhParameter { prime: asn1::Int::new(u32_slice_to_u8_slice(prime)).unwrap(), @@ -2128,7 +2280,7 @@ fn dh_group_generate( #[cppgc] pub fn op_node_generate_dh_group_key( #[string] group_name: &str, -) -> Result { +) -> Result { dh_group_generate(group_name) } @@ -2136,7 +2288,7 @@ pub fn op_node_generate_dh_group_key( #[cppgc] pub async fn op_node_generate_dh_group_key_async( #[string] group_name: String, -) -> Result { +) -> Result { spawn_blocking(move || dh_group_generate(&group_name)) .await .unwrap() @@ -2210,10 +2362,10 @@ pub fn op_node_dh_keys_generate_and_export( #[buffer] pub fn op_node_export_secret_key( #[cppgc] handle: &KeyObjectHandle, -) -> Result, deno_core::error::AnyError> { +) -> Result, JsErrorBox> { let key = handle .as_secret_key() - .ok_or_else(|| type_error("key is not a secret key"))?; + .ok_or_else(|| JsErrorBox::type_error("key is not a secret key"))?; Ok(key.to_vec().into_boxed_slice()) } @@ -2221,10 +2373,10 @@ pub fn op_node_export_secret_key( #[string] pub fn op_node_export_secret_key_b64url( #[cppgc] handle: &KeyObjectHandle, -) -> Result { +) -> Result { let key = handle .as_secret_key() - .ok_or_else(|| type_error("key is not a secret key"))?; + .ok_or_else(|| JsErrorBox::type_error("key is not a secret key"))?; Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(key)) } @@ -2240,12 +2392,19 @@ pub fn op_node_export_public_key_jwk( public_key.export_jwk() } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ExportPublicKeyPemError { + #[class(inherit)] #[error(transparent)] - AsymmetricPublicKeyDer(#[from] AsymmetricPublicKeyDerError), + AsymmetricPublicKeyDer( + #[from] + #[inherit] + AsymmetricPublicKeyDerError, + ), + #[class(type)] #[error("very large data")] VeryLargeData, + #[class(generic)] #[error(transparent)] Der(#[from] der::Error), } @@ -2290,12 +2449,19 @@ pub fn op_node_export_public_key_der( public_key.export_der(typ) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ExportPrivateKeyPemError { + #[class(inherit)] #[error(transparent)] - AsymmetricPublicKeyDer(#[from] AsymmetricPrivateKeyDerError), + AsymmetricPublicKeyDer( + #[from] + #[inherit] + AsymmetricPrivateKeyDerError, + ), + #[class(type)] #[error("very large data")] VeryLargeData, + #[class(generic)] #[error(transparent)] Der(#[from] der::Error), } @@ -2329,6 +2495,31 @@ pub fn op_node_export_private_key_pem( Ok(String::from_utf8(out).expect("invalid pem is not possible")) } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ExportPrivateKeyJwkError { + #[class(inherit)] + #[error(transparent)] + AsymmetricPublicKeyJwk(#[from] AsymmetricPrivateKeyJwkError), + #[class(type)] + #[error("very large data")] + VeryLargeData, + #[class(generic)] + #[error(transparent)] + Der(#[from] der::Error), +} + +#[op2] +#[serde] +pub fn op_node_export_private_key_jwk( + #[cppgc] handle: &KeyObjectHandle, +) -> Result { + let private_key = handle + .as_private_key() + .ok_or(AsymmetricPrivateKeyJwkError::KeyIsNotAsymmetricPrivateKey)?; + + Ok(private_key.export_jwk()?) +} + #[op2] #[buffer] pub fn op_node_export_private_key_der( @@ -2355,9 +2546,9 @@ pub fn op_node_key_type(#[cppgc] handle: &KeyObjectHandle) -> &'static str { #[cppgc] pub fn op_node_derive_public_key_from_private_key( #[cppgc] handle: &KeyObjectHandle, -) -> Result { +) -> Result { let Some(private_key) = handle.as_private_key() else { - return Err(type_error("expected private key")); + return Err(JsErrorBox::type_error("expected private key")); }; Ok(KeyObjectHandle::AsymmetricPublic( diff --git a/ext/node/ops/crypto/md5_sha1.rs b/ext/node/ops/crypto/md5_sha1.rs index 9164b0a1cb..c6c6eb1c83 100644 --- a/ext/node/ops/crypto/md5_sha1.rs +++ b/ext/node/ops/crypto/md5_sha1.rs @@ -1,5 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use core::fmt; + use digest::core_api::AlgorithmName; use digest::core_api::BlockSizeUser; use digest::core_api::Buffer; diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index e90e820909..8c6b571316 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -1,12 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::error::generic_error; -use deno_core::error::type_error; +// Copyright 2018-2025 the Deno authors. MIT license. +use std::future::Future; +use std::rc::Rc; + use deno_core::op2; use deno_core::unsync::spawn_blocking; use deno_core::JsBuffer; use deno_core::OpState; use deno_core::StringOrBuffer; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use elliptic_curve::sec1::ToEncodedPoint; use hkdf::Hkdf; use keys::AsymmetricPrivateKey; @@ -16,16 +18,13 @@ use keys::EcPublicKey; use keys::KeyObjectHandle; use num_bigint::BigInt; use num_bigint_dig::BigUint; +use p224::NistP224; +use p256::NistP256; +use p384::NistP384; use rand::distributions::Distribution; use rand::distributions::Uniform; use rand::Rng; use ring::signature::Ed25519KeyPair; -use std::future::Future; -use std::rc::Rc; - -use p224::NistP224; -use p256::NistP256; -use p384::NistP384; use rsa::pkcs8::DecodePrivateKey; use rsa::pkcs8::DecodePublicKey; use rsa::Oaep; @@ -141,16 +140,21 @@ pub fn op_node_hash_clone( hasher.clone_inner(output_length.map(|l| l as usize)) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PrivateEncryptDecryptError { + #[class(generic)] #[error(transparent)] Pkcs8(#[from] pkcs8::Error), + #[class(generic)] #[error(transparent)] Spki(#[from] spki::Error), + #[class(generic)] #[error(transparent)] Utf8(#[from] std::str::Utf8Error), + #[class(generic)] #[error(transparent)] Rsa(#[from] rsa::Error), + #[class(type)] #[error("Unknown padding")] UnknownPadding, } @@ -269,10 +273,7 @@ pub fn op_node_cipheriv_final( #[buffer] input: &[u8], #[anybuffer] output: &mut [u8], ) -> Result>, cipher::CipherContextError> { - let context = state - .resource_table - .take::(rid) - .map_err(cipher::CipherContextError::Resource)?; + let context = state.resource_table.take::(rid)?; let context = Rc::try_unwrap(context) .map_err(|_| cipher::CipherContextError::ContextInUse)?; context.r#final(auto_pad, input, output).map_err(Into::into) @@ -284,10 +285,7 @@ pub fn op_node_cipheriv_take( state: &mut OpState, #[smi] rid: u32, ) -> Result>, cipher::CipherContextError> { - let context = state - .resource_table - .take::(rid) - .map_err(cipher::CipherContextError::Resource)?; + let context = state.resource_table.take::(rid)?; let context = Rc::try_unwrap(context) .map_err(|_| cipher::CipherContextError::ContextInUse)?; Ok(context.take_tag()) @@ -339,10 +337,7 @@ pub fn op_node_decipheriv_take( state: &mut OpState, #[smi] rid: u32, ) -> Result<(), cipher::DecipherContextError> { - let context = state - .resource_table - .take::(rid) - .map_err(cipher::DecipherContextError::Resource)?; + let context = state.resource_table.take::(rid)?; Rc::try_unwrap(context) .map_err(|_| cipher::DecipherContextError::ContextInUse)?; Ok(()) @@ -357,10 +352,7 @@ pub fn op_node_decipheriv_final( #[anybuffer] output: &mut [u8], #[buffer] auth_tag: &[u8], ) -> Result<(), cipher::DecipherContextError> { - let context = state - .resource_table - .take::(rid) - .map_err(cipher::DecipherContextError::Resource)?; + let context = state.resource_table.take::(rid)?; let context = Rc::try_unwrap(context) .map_err(|_| cipher::DecipherContextError::ContextInUse)?; context @@ -403,10 +395,12 @@ pub fn op_node_verify( ) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum Pbkdf2Error { + #[class(type)] #[error("unsupported digest: {0}")] UnsupportedDigest(String), + #[class(inherit)] #[error(transparent)] Join(#[from] tokio::task::JoinError), } @@ -475,14 +469,18 @@ pub async fn op_node_fill_random_async(#[smi] len: i32) -> ToJsBuffer { .unwrap() } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HkdfError { + #[class(type)] #[error("expected secret key")] ExpectedSecretKey, + #[class(type)] #[error("HKDF-Expand failed")] HkdfExpandFailed, + #[class(type)] #[error("Unsupported digest: {0}")] UnsupportedDigest(String), + #[class(inherit)] #[error(transparent)] Join(#[from] tokio::task::JoinError), } @@ -576,7 +574,7 @@ fn scrypt( parallelization: u32, _maxmem: u32, output_buffer: &mut [u8], -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), JsErrorBox> { // Construct Params let params = scrypt::Params::new( cost as u8, @@ -592,7 +590,7 @@ fn scrypt( Ok(()) } else { // TODO(lev): key derivation failed, so what? - Err(generic_error("scrypt key derivation failed")) + Err(JsErrorBox::generic("scrypt key derivation failed")) } } @@ -607,7 +605,7 @@ pub fn op_node_scrypt_sync( #[smi] parallelization: u32, #[smi] maxmem: u32, #[anybuffer] output_buffer: &mut [u8], -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), JsErrorBox> { scrypt( password, salt, @@ -620,12 +618,14 @@ pub fn op_node_scrypt_sync( ) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ScryptAsyncError { + #[class(inherit)] #[error(transparent)] Join(#[from] tokio::task::JoinError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(JsErrorBox), } #[op2(async)] @@ -658,12 +658,15 @@ pub async fn op_node_scrypt_async( .await? } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum EcdhEncodePubKey { + #[class(type)] #[error("Invalid public key")] InvalidPublicKey, + #[class(type)] #[error("Unsupported curve")] UnsupportedCurve, + #[class(generic)] #[error(transparent)] Sec1(#[from] sec1::Error), } @@ -743,7 +746,7 @@ pub fn op_node_ecdh_generate_keys( #[buffer] pubbuf: &mut [u8], #[buffer] privbuf: &mut [u8], #[string] format: &str, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), JsErrorBox> { let mut rng = rand::thread_rng(); let compress = format == "compressed"; match curve { @@ -780,7 +783,10 @@ pub fn op_node_ecdh_generate_keys( Ok(()) } - &_ => Err(type_error(format!("Unsupported curve: {}", curve))), + &_ => Err(JsErrorBox::type_error(format!( + "Unsupported curve: {}", + curve + ))), } } @@ -913,7 +919,8 @@ pub async fn op_node_gen_prime_async( spawn_blocking(move || gen_prime(size)).await } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum DiffieHellmanError { #[error("Expected private key")] ExpectedPrivateKey, @@ -1005,7 +1012,8 @@ pub fn op_node_diffie_hellman( Ok(res) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum SignEd25519Error { #[error("Expected private key")] ExpectedPrivateKey, @@ -1037,7 +1045,8 @@ pub fn op_node_sign_ed25519( Ok(()) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum VerifyEd25519Error { #[error("Expected public key")] ExpectedPublicKey, diff --git a/ext/node/ops/crypto/pkcs3.rs b/ext/node/ops/crypto/pkcs3.rs index 5772514608..6668148d23 100644 --- a/ext/node/ops/crypto/pkcs3.rs +++ b/ext/node/ops/crypto/pkcs3.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // PKCS #3: Diffie-Hellman Key Agreement Standard diff --git a/ext/node/ops/crypto/primes.rs b/ext/node/ops/crypto/primes.rs index 2e5d1ff63c..65ffa7a7a0 100644 --- a/ext/node/ops/crypto/primes.rs +++ b/ext/node/ops/crypto/primes.rs @@ -1,4 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ops::Deref; use num_bigint::BigInt; use num_bigint_dig::RandPrime; @@ -6,7 +8,6 @@ use num_integer::Integer; use num_traits::One; use num_traits::Zero; use rand::Rng; -use std::ops::Deref; #[derive(Clone)] pub struct Prime(pub num_bigint_dig::BigUint); @@ -283,9 +284,10 @@ static SMALL_PRIMES: [u32; 2047] = [ #[cfg(test)] mod tests { - use super::*; use num_bigint::BigInt; + use super::*; + #[test] fn test_prime() { for &p in SMALL_PRIMES.iter() { diff --git a/ext/node/ops/crypto/sign.rs b/ext/node/ops/crypto/sign.rs index 30094c0765..74ed50eb2b 100644 --- a/ext/node/ops/crypto/sign.rs +++ b/ext/node/ops/crypto/sign.rs @@ -1,24 +1,24 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use core::ops::Add; + +use ecdsa::der::MaxOverhead; +use ecdsa::der::MaxSize; +use elliptic_curve::generic_array::ArrayLength; +use elliptic_curve::FieldBytesSize; use rand::rngs::OsRng; use rsa::signature::hazmat::PrehashSigner as _; use rsa::signature::hazmat::PrehashVerifier as _; use rsa::traits::SignatureScheme as _; use spki::der::Decode; -use crate::ops::crypto::digest::match_fixed_digest; -use crate::ops::crypto::digest::match_fixed_digest_with_oid; - use super::keys::AsymmetricPrivateKey; use super::keys::AsymmetricPublicKey; use super::keys::EcPrivateKey; use super::keys::EcPublicKey; use super::keys::KeyObjectHandle; use super::keys::RsaPssHashAlgorithm; -use core::ops::Add; -use ecdsa::der::MaxOverhead; -use ecdsa::der::MaxSize; -use elliptic_curve::generic_array::ArrayLength; -use elliptic_curve::FieldBytesSize; +use crate::ops::crypto::digest::match_fixed_digest; +use crate::ops::crypto::digest::match_fixed_digest_with_oid; fn dsa_signature( encoding: u32, @@ -39,7 +39,8 @@ where } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum KeyObjectHandlePrehashedSignAndVerifyError { #[error("invalid DSA signature encoding")] InvalidDsaSignatureEncoding, @@ -47,10 +48,12 @@ pub enum KeyObjectHandlePrehashedSignAndVerifyError { KeyIsNotPrivate, #[error("digest not allowed for RSA signature: {0}")] DigestNotAllowedForRsaSignature(String), + #[class(generic)] #[error("failed to sign digest with RSA")] FailedToSignDigestWithRsa, #[error("digest not allowed for RSA-PSS signature: {0}")] DigestNotAllowedForRsaPssSignature(String), + #[class(generic)] #[error("failed to sign digest with RSA-PSS")] FailedToSignDigestWithRsaPss, #[error("failed to sign digest with DSA")] diff --git a/ext/node/ops/crypto/x509.rs b/ext/node/ops/crypto/x509.rs index ab8e52f703..ad931f01ff 100644 --- a/ext/node/ops/crypto/x509.rs +++ b/ext/node/ops/crypto/x509.rs @@ -1,7 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ops::Deref; use deno_core::op2; - +use digest::Digest; use x509_parser::der_parser::asn1_rs::Any; use x509_parser::der_parser::asn1_rs::Tag; use x509_parser::der_parser::oid::Oid; @@ -9,14 +11,10 @@ pub use x509_parser::error::X509Error; use x509_parser::extensions; use x509_parser::pem; use x509_parser::prelude::*; - -use super::KeyObjectHandle; - -use std::ops::Deref; use yoke::Yoke; use yoke::Yokeable; -use digest::Digest; +use super::KeyObjectHandle; enum CertificateSources { Der(Box<[u8]>), @@ -61,11 +59,13 @@ impl<'a> Deref for CertificateView<'a> { } } +deno_error::js_error_wrapper!(X509Error, JsX509Error, "Error"); + #[op2] #[cppgc] pub fn op_node_x509_parse( #[buffer] buf: &[u8], -) -> Result { +) -> Result { let source = match pem::parse_x509_pem(buf) { Ok((_, pem)) => CertificateSources::Pem(pem), Err(_) => CertificateSources::Der(buf.to_vec().into_boxed_slice()), @@ -156,18 +156,18 @@ pub fn op_node_x509_fingerprint512( #[string] pub fn op_node_x509_get_issuer( #[cppgc] cert: &Certificate, -) -> Result { +) -> Result { let cert = cert.inner.get().deref(); - x509name_to_string(cert.issuer(), oid_registry()) + x509name_to_string(cert.issuer(), oid_registry()).map_err(Into::into) } #[op2] #[string] pub fn op_node_x509_get_subject( #[cppgc] cert: &Certificate, -) -> Result { +) -> Result { let cert = cert.inner.get().deref(); - x509name_to_string(cert.subject(), oid_registry()) + x509name_to_string(cert.subject(), oid_registry()).map_err(Into::into) } #[op2] diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs index 58a688a1fe..0e9310375c 100644 --- a/ext/node/ops/fs.rs +++ b/ext/node/ops/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::rc::Rc; @@ -10,27 +10,40 @@ use serde::Serialize; use crate::NodePermissions; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum FsError { + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error("{0}")] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + std::io::Error, + ), #[cfg(windows)] + #[class(generic)] #[error("Path has no root.")] PathHasNoRoot, #[cfg(not(any(unix, windows)))] + #[class(generic)] #[error("Unsupported platform.")] UnsupportedPlatform, + #[class(inherit)] #[error(transparent)] - Fs(#[from] deno_io::fs::FsError), + Fs( + #[from] + #[inherit] + deno_io::fs::FsError, + ), } #[op2(fast, stack_trace)] pub fn op_node_fs_exists_sync

( state: &mut OpState, #[string] path: String, -) -> Result +) -> Result where P: NodePermissions + 'static, { @@ -207,6 +220,7 @@ where { use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; + use windows_sys::Win32::Storage::FileSystem::GetDiskFreeSpaceW; let _ = bigint; diff --git a/ext/node/ops/http.rs b/ext/node/ops/http.rs index eb28e68aee..9723b0d3be 100644 --- a/ext/node/ops/http.rs +++ b/ext/node/ops/http.rs @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; +use std::cmp::min; use std::fmt::Debug; use std::pin::Pin; use std::rc::Rc; @@ -9,8 +10,7 @@ use std::task::Context; use std::task::Poll; use bytes::Bytes; -use deno_core::error::bad_resource; -use deno_core::error::type_error; +use deno_core::error::ResourceError; use deno_core::futures::stream::Peekable; use deno_core::futures::Future; use deno_core::futures::FutureExt; @@ -32,6 +32,8 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsError; +use deno_error::JsErrorBox; use deno_fetch::FetchCancelHandle; use deno_fetch::FetchReturn; use deno_fetch::ResBody; @@ -48,7 +50,6 @@ use http_body_util::BodyExt; use hyper::body::Frame; use hyper::body::Incoming; use hyper_util::rt::TokioIo; -use std::cmp::min; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; @@ -88,32 +89,45 @@ impl deno_core::Resource for NodeHttpClientResponse { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, JsError)] pub enum ConnError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(ResourceError), + #[class(inherit)] #[error(transparent)] Permission(#[from] PermissionCheckError), + #[class(type)] #[error("Invalid URL {0}")] InvalidUrl(Url), + #[class(type)] #[error(transparent)] InvalidHeaderName(#[from] http::header::InvalidHeaderName), + #[class(type)] #[error(transparent)] InvalidHeaderValue(#[from] http::header::InvalidHeaderValue), + #[class(inherit)] #[error(transparent)] Url(#[from] url::ParseError), + #[class(type)] #[error(transparent)] Method(#[from] http::method::InvalidMethod), + #[class(inherit)] #[error(transparent)] Io(#[from] std::io::Error), + #[class("Busy")] #[error("TLS stream is currently in use")] TlsStreamBusy, + #[class("Busy")] #[error("TCP stream is currently in use")] TcpStreamBusy, + #[class(generic)] #[error(transparent)] ReuniteTcp(#[from] tokio::net::tcp::ReuniteError), + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), + #[class("Http")] #[error(transparent)] Hyper(#[from] hyper::Error), } @@ -274,8 +288,11 @@ pub async fn op_node_http_await_response( .resource_table .take::(rid) .map_err(ConnError::Resource)?; - let resource = Rc::try_unwrap(resource) - .map_err(|_| ConnError::Resource(bad_resource("NodeHttpClientResponse")))?; + let resource = Rc::try_unwrap(resource).map_err(|_| { + ConnError::Resource(ResourceError::Other( + "NodeHttpClientResponse".to_string(), + )) + })?; let res = resource.response.await??; let status = res.status(); @@ -296,7 +313,7 @@ pub async fn op_node_http_await_response( }; let (parts, body) = res.into_parts(); - let body = body.map_err(deno_core::anyhow::Error::from); + let body = body.map_err(|e| JsErrorBox::new("Http", e.to_string())); let body = body.boxed(); let res = http::Response::from_parts(parts, body); @@ -523,7 +540,7 @@ impl Resource for NodeHttpResponseResource { // safely call `await` on it without creating a race condition. Some(_) => match reader.as_mut().next().await.unwrap() { Ok(chunk) => assert!(chunk.is_empty()), - Err(err) => break Err(type_error(err.to_string())), + Err(err) => break Err(JsErrorBox::type_error(err.to_string())), }, None => break Ok(BufView::empty()), } @@ -547,9 +564,7 @@ impl Resource for NodeHttpResponseResource { #[allow(clippy::type_complexity)] pub struct NodeHttpResourceToBodyAdapter( Rc, - Option< - Pin>>>, - >, + Option>>>>, ); impl NodeHttpResourceToBodyAdapter { @@ -565,7 +580,7 @@ unsafe impl Send for NodeHttpResourceToBodyAdapter {} unsafe impl Sync for NodeHttpResourceToBodyAdapter {} impl Stream for NodeHttpResourceToBodyAdapter { - type Item = Result; + type Item = Result; fn poll_next( self: Pin<&mut Self>, @@ -596,7 +611,7 @@ impl Stream for NodeHttpResourceToBodyAdapter { impl hyper::body::Body for NodeHttpResourceToBodyAdapter { type Data = Bytes; - type Error = deno_core::anyhow::Error; + type Error = JsErrorBox; fn poll_frame( self: Pin<&mut Self>, diff --git a/ext/node/ops/http2.rs b/ext/node/ops/http2.rs index 53dada9f41..2308ca8254 100644 --- a/ext/node/ops/http2.rs +++ b/ext/node/ops/http2.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; @@ -7,6 +7,7 @@ use std::rc::Rc; use std::task::Poll; use bytes::Bytes; +use deno_core::error::ResourceError; use deno_core::futures::future::poll_fn; use deno_core::op2; use deno_core::serde::Serialize; @@ -109,14 +110,32 @@ impl Resource for Http2ServerSendResponse { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum Http2Error { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + ResourceError, + ), + #[class(inherit)] #[error(transparent)] - UrlParse(#[from] url::ParseError), + UrlParse( + #[from] + #[inherit] + url::ParseError, + ), + #[class(generic)] #[error(transparent)] H2(#[from] h2::Error), + #[class(inherit)] + #[error(transparent)] + TakeNetworkStream( + #[from] + #[inherit] + deno_net::raw::TakeNetworkStreamError, + ), } #[op2(async)] @@ -129,8 +148,7 @@ pub async fn op_http2_connect( // No permission check necessary because we're using an existing connection let network_stream = { let mut state = state.borrow_mut(); - take_network_stream_resource(&mut state.resource_table, rid) - .map_err(Http2Error::Resource)? + take_network_stream_resource(&mut state.resource_table, rid)? }; let url = Url::parse(&url)?; @@ -156,8 +174,7 @@ pub async fn op_http2_listen( #[smi] rid: ResourceId, ) -> Result { let stream = - take_network_stream_resource(&mut state.borrow_mut().resource_table, rid) - .map_err(Http2Error::Resource)?; + take_network_stream_resource(&mut state.borrow_mut().resource_table, rid)?; let conn = h2::server::Builder::new().handshake(stream).await?; Ok( @@ -182,8 +199,7 @@ pub async fn op_http2_accept( let resource = state .borrow() .resource_table - .get::(rid) - .map_err(Http2Error::Resource)?; + .get::(rid)?; let mut conn = RcRef::map(&resource, |r| &r.conn).borrow_mut().await; if let Some(res) = conn.accept().await { let (req, resp) = res?; @@ -249,8 +265,7 @@ pub async fn op_http2_send_response( let resource = state .borrow() .resource_table - .get::(rid) - .map_err(Http2Error::Resource)?; + .get::(rid)?; let mut send_response = RcRef::map(resource, |r| &r.send_response) .borrow_mut() .await; @@ -276,11 +291,7 @@ pub async fn op_http2_poll_client_connection( state: Rc>, #[smi] rid: ResourceId, ) -> Result<(), Http2Error> { - let resource = state - .borrow() - .resource_table - .get::(rid) - .map_err(Http2Error::Resource)?; + let resource = state.borrow().resource_table.get::(rid)?; let cancel_handle = RcRef::map(resource.clone(), |this| &this.cancel_handle); let mut conn = RcRef::map(resource, |this| &this.conn).borrow_mut().await; @@ -310,8 +321,7 @@ pub async fn op_http2_client_request( let resource = state .borrow() .resource_table - .get::(client_rid) - .map_err(Http2Error::Resource)?; + .get::(client_rid)?; let url = resource.url.clone(); @@ -344,10 +354,7 @@ pub async fn op_http2_client_request( let resource = { let state = state.borrow(); - state - .resource_table - .get::(client_rid) - .map_err(Http2Error::Resource)? + state.resource_table.get::(client_rid)? }; let mut client = RcRef::map(&resource, |r| &r.client).borrow_mut().await; poll_fn(|cx| client.poll_ready(cx)).await?; @@ -370,8 +377,7 @@ pub async fn op_http2_client_send_data( let resource = state .borrow() .resource_table - .get::(stream_rid) - .map_err(Http2Error::Resource)?; + .get::(stream_rid)?; let mut stream = RcRef::map(&resource, |r| &r.stream).borrow_mut().await; stream.send_data(data.to_vec().into(), end_of_stream)?; @@ -383,7 +389,7 @@ pub async fn op_http2_client_reset_stream( state: Rc>, #[smi] stream_rid: ResourceId, #[smi] code: u32, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), ResourceError> { let resource = state .borrow() .resource_table @@ -402,8 +408,7 @@ pub async fn op_http2_client_send_trailers( let resource = state .borrow() .resource_table - .get::(stream_rid) - .map_err(Http2Error::Resource)?; + .get::(stream_rid)?; let mut stream = RcRef::map(&resource, |r| &r.stream).borrow_mut().await; let mut trailers_map = http::HeaderMap::new(); @@ -435,8 +440,7 @@ pub async fn op_http2_client_get_response( let resource = state .borrow() .resource_table - .get::(stream_rid) - .map_err(Http2Error::Resource)?; + .get::(stream_rid)?; let mut response_future = RcRef::map(&resource, |r| &r.response).borrow_mut().await; @@ -506,8 +510,7 @@ pub async fn op_http2_client_get_response_body_chunk( let resource = state .borrow() .resource_table - .get::(body_rid) - .map_err(Http2Error::Resource)?; + .get::(body_rid)?; let mut body = RcRef::map(&resource, |r| &r.body).borrow_mut().await; loop { @@ -550,7 +553,7 @@ pub async fn op_http2_client_get_response_body_chunk( pub async fn op_http2_client_get_response_trailers( state: Rc>, #[smi] body_rid: ResourceId, -) -> Result>, deno_core::error::AnyError> { +) -> Result>, ResourceError> { let resource = state .borrow() .resource_table diff --git a/ext/node/ops/idna.rs b/ext/node/ops/idna.rs index a3d85e77c2..24bcb97c63 100644 --- a/ext/node/ops/idna.rs +++ b/ext/node/ops/idna.rs @@ -1,24 +1,29 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_core::op2; +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; +use deno_core::op2; + // map_domain, to_ascii and to_unicode are based on the punycode implementation in node.js // https://github.com/nodejs/node/blob/73025c4dec042e344eeea7912ed39f7b7c4a3991/lib/punycode.js const PUNY_PREFIX: &str = "xn--"; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum IdnaError { + #[class(range)] #[error("Invalid input")] InvalidInput, + #[class(generic)] #[error("Input would take more than 63 characters to encode")] InputTooLong, + #[class(range)] #[error("Illegal input >= 0x80 (not a basic code point)")] IllegalInput, } +deno_error::js_error_wrapper!(idna::Errors, JsIdnaErrors, "Error"); + /// map a domain by mapping each label with the given function fn map_domain( domain: &str, @@ -113,8 +118,8 @@ pub fn op_node_idna_punycode_to_unicode( #[string] pub fn op_node_idna_domain_to_ascii( #[string] domain: String, -) -> Result { - idna::domain_to_ascii(&domain) +) -> Result { + idna::domain_to_ascii(&domain).map_err(Into::into) } /// Converts a domain to Unicode as per the IDNA spec diff --git a/ext/node/ops/inspector.rs b/ext/node/ops/inspector.rs index 9986aeb197..c462523715 100644 --- a/ext/node/ops/inspector.rs +++ b/ext/node/ops/inspector.rs @@ -1,8 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::cell::RefCell; +use std::rc::Rc; -use crate::NodePermissions; -use deno_core::anyhow::Error; -use deno_core::error::generic_error; use deno_core::futures::channel::mpsc; use deno_core::op2; use deno_core::v8; @@ -11,8 +11,9 @@ use deno_core::InspectorSessionKind; use deno_core::InspectorSessionOptions; use deno_core::JsRuntimeInspector; use deno_core::OpState; -use std::cell::RefCell; -use std::rc::Rc; +use deno_error::JsErrorBox; + +use crate::NodePermissions; #[op2(fast)] pub fn op_inspector_enabled() -> bool { @@ -25,7 +26,7 @@ pub fn op_inspector_open

( _state: &mut OpState, _port: Option, #[string] _host: Option, -) -> Result<(), Error> +) -> Result<(), JsErrorBox> where P: NodePermissions + 'static, { @@ -85,6 +86,20 @@ struct JSInspectorSession { impl GarbageCollected for JSInspectorSession {} +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum InspectorConnectError { + #[class(inherit)] + #[error(transparent)] + Permission( + #[from] + #[inherit] + deno_permissions::PermissionCheckError, + ), + #[class(generic)] + #[error("connectToMainThread not supported")] + ConnectToMainThreadUnsupported, +} + #[op2(stack_trace)] #[cppgc] pub fn op_inspector_connect<'s, P>( @@ -93,7 +108,7 @@ pub fn op_inspector_connect<'s, P>( state: &mut OpState, connect_to_main_thread: bool, callback: v8::Local<'s, v8::Function>, -) -> Result +) -> Result where P: NodePermissions + 'static, { @@ -102,7 +117,7 @@ where .check_sys("inspector", "inspector.Session.connect")?; if connect_to_main_thread { - return Err(generic_error("connectToMainThread not supported")); + return Err(InspectorConnectError::ConnectToMainThreadUnsupported); } let context = scope.get_current_context(); diff --git a/ext/node/ops/ipc.rs b/ext/node/ops/ipc.rs index 672cf0d707..cf5e1e97ef 100644 --- a/ext/node/ops/ipc.rs +++ b/ext/node/ops/ipc.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use impl_::*; @@ -30,6 +30,10 @@ mod impl_ { use deno_core::RcRef; use deno_core::ResourceId; use deno_core::ToV8; + use deno_error::JsErrorBox; + use deno_io::BiPipe; + use deno_io::BiPipeRead; + use deno_io::BiPipeWrite; use memchr::memchr; use pin_project_lite::pin_project; use serde::Serialize; @@ -37,10 +41,6 @@ mod impl_ { use tokio::io::AsyncWriteExt; use tokio::io::ReadBuf; - use deno_io::BiPipe; - use deno_io::BiPipeRead; - use deno_io::BiPipeWrite; - /// Wrapper around v8 value that implements Serialize. struct SerializeWrapper<'a, 'b>( RefCell<&'b mut v8::HandleScope<'a>>, @@ -80,7 +80,7 @@ mod impl_ { } else if value.is_string_object() { let str = deno_core::serde_v8::to_utf8( value.to_string(scope).ok_or_else(|| { - S::Error::custom(deno_core::error::generic_error( + S::Error::custom(deno_error::JsErrorBox::generic( "toString on string object failed", )) })?, @@ -153,7 +153,7 @@ mod impl_ { map.end() } else { // TODO(nathanwhit): better error message - Err(S::Error::custom(deno_core::error::type_error(format!( + Err(S::Error::custom(JsErrorBox::type_error(format!( "Unsupported type: {}", value.type_repr() )))) @@ -178,14 +178,18 @@ mod impl_ { )) } - #[derive(Debug, thiserror::Error)] + #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum IpcError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] IpcJsonStream(#[from] IpcJsonStreamError), + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), + #[class(inherit)] #[error("failed to serialize json value: {0}")] SerdeJson(serde_json::Error), } @@ -211,8 +215,7 @@ mod impl_ { let stream = state .borrow() .resource_table - .get::(rid) - .map_err(IpcError::Resource)?; + .get::(rid)?; let old = stream .queued_bytes .fetch_add(serialized.len(), std::sync::atomic::Ordering::Relaxed); @@ -256,8 +259,7 @@ mod impl_ { let stream = state .borrow() .resource_table - .get::(rid) - .map_err(IpcError::Resource)?; + .get::(rid)?; let cancel = stream.cancel.clone(); let mut stream = RcRef::map(stream, |r| &r.read_half).borrow_mut().await; @@ -468,10 +470,12 @@ mod impl_ { } } - #[derive(Debug, thiserror::Error)] + #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum IpcJsonStreamError { + #[class(inherit)] #[error("{0}")] Io(#[source] std::io::Error), + #[class(generic)] #[error("{0}")] SimdJson(#[source] simd_json::Error), } @@ -624,13 +628,15 @@ mod impl_ { #[cfg(test)] mod tests { - use super::IpcJsonStreamResource; + use std::rc::Rc; + use deno_core::serde_json::json; use deno_core::v8; use deno_core::JsRuntime; use deno_core::RcRef; use deno_core::RuntimeOptions; - use std::rc::Rc; + + use super::IpcJsonStreamResource; #[allow(clippy::unused_async)] #[cfg(unix)] diff --git a/ext/node/ops/mod.rs b/ext/node/ops/mod.rs index e5ea8b4172..0b7be91860 100644 --- a/ext/node/ops/mod.rs +++ b/ext/node/ops/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod blocklist; pub mod buffer; diff --git a/ext/node/ops/os/cpus.rs b/ext/node/ops/os/cpus.rs index 3f5f430f65..4dd0e59a17 100644 --- a/ext/node/ops/os/cpus.rs +++ b/ext/node/ops/os/cpus.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde::Serialize; @@ -73,12 +73,17 @@ pub fn cpu_info() -> Option> { cpu_speed = 2_400_000_000; } + extern "C" { + fn mach_host_self() -> std::ffi::c_uint; + static mut mach_task_self_: std::ffi::c_uint; + } + let mut num_cpus: libc::natural_t = 0; let mut info: *mut libc::processor_cpu_load_info_data_t = std::ptr::null_mut(); let mut msg_type: libc::mach_msg_type_number_t = 0; if libc::host_processor_info( - libc::mach_host_self(), + mach_host_self(), libc::PROCESSOR_CPU_LOAD_INFO, &mut num_cpus, &mut info as *mut _ as *mut libc::processor_info_array_t, @@ -111,7 +116,7 @@ pub fn cpu_info() -> Option> { } libc::vm_deallocate( - libc::mach_task_self(), + mach_task_self_, info.as_ptr() as libc::vm_address_t, msg_type as _, ); @@ -122,13 +127,13 @@ pub fn cpu_info() -> Option> { #[cfg(target_os = "windows")] pub fn cpu_info() -> Option> { + use std::os::windows::ffi::OsStrExt; + use std::os::windows::ffi::OsStringExt; + use windows_sys::Wdk::System::SystemInformation::NtQuerySystemInformation; use windows_sys::Wdk::System::SystemInformation::SystemProcessorPerformanceInformation; use windows_sys::Win32::System::WindowsProgramming::SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; - use std::os::windows::ffi::OsStrExt; - use std::os::windows::ffi::OsStringExt; - fn encode_wide(s: &str) -> Vec { std::ffi::OsString::from(s) .encode_wide() @@ -259,13 +264,16 @@ pub fn cpu_info() -> Option> { let nice = fields.next()?.parse::().ok()?; let sys = fields.next()?.parse::().ok()?; let idle = fields.next()?.parse::().ok()?; + let _iowait = fields.next()?.parse::().ok()?; let irq = fields.next()?.parse::().ok()?; - cpus[i].times.user = user; - cpus[i].times.nice = nice; - cpus[i].times.sys = sys; - cpus[i].times.idle = idle; - cpus[i].times.irq = irq; + // sysconf(_SC_CLK_TCK) is fixed at 100 Hz, therefore the + // multiplier is always 1000/100 = 10 + cpus[i].times.user = user * 10; + cpus[i].times.nice = nice * 10; + cpus[i].times.sys = sys * 10; + cpus[i].times.idle = idle * 10; + cpus[i].times.irq = irq * 10; } let fp = std::fs::File::open("/proc/cpuinfo").ok()?; @@ -282,6 +290,18 @@ pub fn cpu_info() -> Option> { let model = fields.next()?.trim(); cpus[j].model = model.to_string(); + + if let Ok(fp) = std::fs::File::open(format!( + "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq", + j + )) { + let mut reader = std::io::BufReader::new(fp); + let mut speed = String::new(); + reader.read_line(&mut speed).ok()?; + + cpus[j].speed = speed.trim().parse::().ok()? / 1000; + } + j += 1; } diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs index ddb2a70c64..ad0be8200e 100644 --- a/ext/node/ops/os/mod.rs +++ b/ext/node/ops/os/mod.rs @@ -1,24 +1,39 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::mem::MaybeUninit; -use crate::NodePermissions; use deno_core::op2; use deno_core::OpState; +use deno_permissions::PermissionCheckError; +use sys_traits::EnvHomeDir; + +use crate::NodePermissions; mod cpus; pub mod priority; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum OsError { + #[class(inherit)] #[error(transparent)] - Priority(priority::PriorityError), + Priority(#[inherit] priority::PriorityError), + #[class(inherit)] #[error(transparent)] - Permission(#[from] deno_permissions::PermissionCheckError), + Permission( + #[from] + #[inherit] + PermissionCheckError, + ), + #[class(type)] #[error("Failed to get cpu info")] FailedToGetCpuInfo, + #[class(inherit)] #[error("Failed to get user info")] - FailedToGetUserInfo(#[source] std::io::Error), + FailedToGetUserInfo( + #[source] + #[inherit] + std::io::Error, + ), } #[op2(fast, stack_trace)] @@ -213,9 +228,7 @@ where } #[op2(fast, stack_trace)] -pub fn op_geteuid

( - state: &mut OpState, -) -> Result +pub fn op_geteuid

(state: &mut OpState) -> Result where P: NodePermissions + 'static, { @@ -234,9 +247,7 @@ where } #[op2(fast, stack_trace)] -pub fn op_getegid

( - state: &mut OpState, -) -> Result +pub fn op_getegid

(state: &mut OpState) -> Result where P: NodePermissions + 'static, { @@ -272,7 +283,7 @@ where #[string] pub fn op_homedir

( state: &mut OpState, -) -> Result, deno_core::error::AnyError> +) -> Result, PermissionCheckError> where P: NodePermissions + 'static, { @@ -281,5 +292,9 @@ where permissions.check_sys("homedir", "node:os.homedir()")?; } - Ok(home::home_dir().map(|path| path.to_string_lossy().to_string())) + Ok( + sys_traits::impls::RealSys + .env_home_dir() + .map(|path| path.to_string_lossy().to_string()), + ) } diff --git a/ext/node/ops/os/priority.rs b/ext/node/ops/os/priority.rs index 9a1ebcca70..10640e4942 100644 --- a/ext/node/ops/os/priority.rs +++ b/ext/node/ops/os/priority.rs @@ -1,12 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use impl_::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PriorityError { + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), #[cfg(windows)] + #[class(type)] #[error("Invalid priority")] InvalidPriority, } diff --git a/ext/node/ops/perf_hooks.rs b/ext/node/ops/perf_hooks.rs index 636d0b2adb..9c0fd01385 100644 --- a/ext/node/ops/perf_hooks.rs +++ b/ext/node/ops/perf_hooks.rs @@ -1,12 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::cell::Cell; use deno_core::op2; use deno_core::GarbageCollected; -use std::cell::Cell; - -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PerfHooksError { + #[class(generic)] #[error(transparent)] TokioEld(#[from] tokio_eld::Error), } diff --git a/ext/node/ops/process.rs b/ext/node/ops/process.rs index 45c599bee2..f28e452437 100644 --- a/ext/node/ops/process.rs +++ b/ext/node/ops/process.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::OpState; @@ -50,7 +50,7 @@ pub fn op_node_process_kill( state: &mut OpState, #[smi] pid: i32, #[smi] sig: i32, -) -> Result { +) -> Result { state .borrow_mut::() .check_run_all("process.kill")?; diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs index ddcdec0bbd..bff0cd79ed 100644 --- a/ext/node/ops/require.rs +++ b/ext/node/ops/require.rs @@ -1,39 +1,44 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use boxed_error::Boxed; -use deno_core::error::AnyError; -use deno_core::op2; -use deno_core::url::Url; -use deno_core::v8; -use deno_core::JsRuntimeInspector; -use deno_core::OpState; -use deno_fs::FileSystemRc; -use deno_fs::V8MaybeStaticStr; -use deno_package_json::PackageJsonRc; -use deno_path_util::normalize_path; -use deno_path_util::url_from_file_path; -use deno_path_util::url_to_file_path; -use node_resolver::errors::ClosestPkgJsonError; -use node_resolver::NodeResolutionKind; -use node_resolver::ResolutionMode; -use node_resolver::REQUIRE_CONDITIONS; use std::borrow::Cow; use std::cell::RefCell; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; +use boxed_error::Boxed; +use deno_core::op2; +use deno_core::url::Url; +use deno_core::v8; +use deno_core::FastString; +use deno_core::JsRuntimeInspector; +use deno_core::OpState; +use deno_error::JsErrorBox; +use deno_package_json::PackageJsonRc; +use deno_path_util::normalize_path; +use deno_path_util::url_from_file_path; +use deno_path_util::url_to_file_path; +use node_resolver::errors::ClosestPkgJsonError; +use node_resolver::InNpmPackageChecker; +use node_resolver::NodeResolutionKind; +use node_resolver::NpmPackageFolderResolver; +use node_resolver::ResolutionMode; +use node_resolver::REQUIRE_CONDITIONS; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsMetadataValue; + +use crate::ExtNodeSys; use crate::NodePermissions; use crate::NodeRequireLoaderRc; use crate::NodeResolverRc; -use crate::NpmPackageFolderResolverRc; use crate::PackageJsonResolverRc; #[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: &'a Path, -) -> Result, deno_core::error::AnyError> +) -> Result, JsErrorBox> where P: NodePermissions + 'static, { @@ -42,37 +47,67 @@ where loader.ensure_read_permission(permissions, file_path) } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, deno_error::JsError)] pub struct RequireError(pub Box); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum RequireErrorKind { + #[class(inherit)] #[error(transparent)] - UrlParse(#[from] url::ParseError), + UrlParse( + #[from] + #[inherit] + url::ParseError, + ), + #[class(inherit)] #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[inherit] JsErrorBox), + #[class(generic)] #[error(transparent)] PackageExportsResolve( #[from] node_resolver::errors::PackageExportsResolveError, ), + #[class(generic)] #[error(transparent)] PackageJsonLoad(#[from] node_resolver::errors::PackageJsonLoadError), + #[class(generic)] #[error(transparent)] - ClosestPkgJson(#[from] node_resolver::errors::ClosestPkgJsonError), + ClosestPkgJson(#[from] ClosestPkgJsonError), + #[class(generic)] #[error(transparent)] PackageImportsResolve( #[from] node_resolver::errors::PackageImportsResolveError, ), + #[class(generic)] #[error(transparent)] FilePathConversion(#[from] deno_path_util::UrlToFilePathError), + #[class(generic)] #[error(transparent)] UrlConversion(#[from] deno_path_util::PathToUrlError), + #[class(inherit)] #[error(transparent)] - Fs(#[from] deno_io::fs::FsError), + Fs( + #[from] + #[inherit] + deno_io::fs::FsError, + ), + #[class(inherit)] #[error(transparent)] - ReadModule(deno_core::error::AnyError), + Io( + #[from] + #[inherit] + std::io::Error, + ), + #[class(inherit)] + #[error(transparent)] + ReadModule( + #[from] + #[inherit] + JsErrorBox, + ), + #[class(inherit)] #[error("Unable to get CWD: {0}")] - UnableToGetCwd(deno_io::fs::FsError), + UnableToGetCwd(#[inherit] std::io::Error), } #[op2] @@ -128,19 +163,21 @@ pub fn op_require_init_paths() -> Vec { #[op2(stack_trace)] #[serde] -pub fn op_require_node_module_paths

( +pub fn op_require_node_module_paths< + P: NodePermissions + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] from: String, -) -> Result, RequireError> -where - P: NodePermissions + 'static, -{ - let fs = state.borrow::(); +) -> Result, RequireError> { + let sys = state.borrow::(); // Guarantee that "from" is absolute. let from = if from.starts_with("file:///") { url_to_file_path(&Url::parse(&from)?)? } else { - let current_dir = &fs.cwd().map_err(RequireErrorKind::UnableToGetCwd)?; + let current_dir = &sys + .env_current_dir() + .map_err(RequireErrorKind::UnableToGetCwd)?; normalize_path(current_dir.join(from)) }; @@ -220,12 +257,21 @@ pub fn op_require_is_request_relative(#[string] request: String) -> bool { #[op2] #[string] -pub fn op_require_resolve_deno_dir( +pub fn op_require_resolve_deno_dir< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] request: String, #[string] parent_filename: String, -) -> Result, AnyError> { - let resolver = state.borrow::(); +) -> Result, deno_path_util::PathToUrlError> { + let resolver = state.borrow::>(); + Ok( resolver .resolve_package_folder_from_package( @@ -238,11 +284,19 @@ pub fn op_require_resolve_deno_dir( } #[op2(fast)] -pub fn op_require_is_deno_dir_package( +pub fn op_require_is_deno_dir_package< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] path: String, ) -> bool { - let resolver = state.borrow::(); + let resolver = state.borrow::>(); match deno_path_util::url_from_file_path(&PathBuf::from(path)) { Ok(specifier) => resolver.in_npm_package(&specifier), Err(_) => false, @@ -297,18 +351,18 @@ pub fn op_require_path_is_absolute(#[string] p: String) -> bool { } #[op2(fast, stack_trace)] -pub fn op_require_stat

( +pub fn op_require_stat< + P: NodePermissions + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] path: String, -) -> Result -where - P: NodePermissions + 'static, -{ +) -> Result { let path = PathBuf::from(path); let path = ensure_read_permission::

(state, &path)?; - let fs = state.borrow::(); - if let Ok(metadata) = fs.stat_sync(&path) { - if metadata.is_file { + let sys = state.borrow::(); + if let Ok(metadata) = sys.fs_metadata(&path) { + if metadata.file_type().is_file() { return Ok(0); } else { return Ok(1); @@ -320,19 +374,20 @@ where #[op2(stack_trace)] #[string] -pub fn op_require_real_path

( +pub fn op_require_real_path< + P: NodePermissions + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] request: String, -) -> Result -where - P: NodePermissions + 'static, -{ +) -> Result { let path = PathBuf::from(request); let path = ensure_read_permission::

(state, &path) .map_err(RequireErrorKind::Permission)?; - let fs = state.borrow::(); - let canonicalized_path = - deno_path_util::strip_unc_prefix(fs.realpath_sync(&path)?); + let sys = state.borrow::(); + let canonicalized_path = deno_path_util::strip_unc_prefix( + sys.fs_canonicalize(&path).map_err(RequireErrorKind::Io)?, + ); Ok(canonicalized_path.to_string_lossy().into_owned()) } @@ -356,14 +411,12 @@ pub fn op_require_path_resolve(#[serde] parts: Vec) -> String { #[string] pub fn op_require_path_dirname( #[string] request: String, -) -> Result { +) -> Result { let p = PathBuf::from(request); if let Some(parent) = p.parent() { Ok(parent.to_string_lossy().into_owned()) } else { - Err(deno_core::error::generic_error( - "Path doesn't have a parent", - )) + Err(JsErrorBox::generic("Path doesn't have a parent")) } } @@ -371,28 +424,26 @@ pub fn op_require_path_dirname( #[string] pub fn op_require_path_basename( #[string] request: String, -) -> Result { +) -> Result { let p = PathBuf::from(request); if let Some(path) = p.file_name() { Ok(path.to_string_lossy().into_owned()) } else { - Err(deno_core::error::generic_error( - "Path doesn't have a file name", - )) + Err(JsErrorBox::generic("Path doesn't have a file name")) } } #[op2(stack_trace)] #[string] -pub fn op_require_try_self_parent_path

( +pub fn op_require_try_self_parent_path< + P: NodePermissions + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, has_parent: bool, #[string] maybe_parent_filename: Option, #[string] maybe_parent_id: Option, -) -> Result, deno_core::error::AnyError> -where - P: NodePermissions + 'static, -{ +) -> Result, JsErrorBox> { if !has_parent { return Ok(None); } @@ -403,8 +454,8 @@ where if let Some(parent_id) = maybe_parent_id { if parent_id == "" || parent_id == "internal/preload" { - let fs = state.borrow::(); - if let Ok(cwd) = fs.cwd() { + let sys = state.borrow::(); + if let Ok(cwd) = sys.env_current_dir() { let cwd = ensure_read_permission::

(state, &cwd)?; return Ok(Some(cwd.to_string_lossy().into_owned())); } @@ -415,19 +466,21 @@ where #[op2(stack_trace)] #[string] -pub fn op_require_try_self

( +pub fn op_require_try_self< + P: NodePermissions + 'static, + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] parent_path: Option, #[string] request: String, -) -> Result, RequireError> -where - P: NodePermissions + 'static, -{ +) -> Result, RequireError> { if parent_path.is_none() { return Ok(None); } - let pkg_json_resolver = state.borrow::(); + let pkg_json_resolver = state.borrow::>(); let pkg = pkg_json_resolver .get_closest_package_json_from_file_path(&PathBuf::from( parent_path.unwrap(), @@ -459,7 +512,11 @@ where let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap(); if let Some(exports) = &pkg.exports { - let node_resolver = state.borrow::(); + let node_resolver = state.borrow::>(); let r = node_resolver.package_exports_resolve( &pkg.path, &expansion, @@ -484,7 +541,7 @@ where pub fn op_require_read_file

( state: &mut OpState, #[string] file_path: String, -) -> Result +) -> Result where P: NodePermissions + 'static, { @@ -495,7 +552,10 @@ where let loader = state.borrow::(); loader .load_text_file_lossy(&file_path) - .map(V8MaybeStaticStr) + .map(|s| match s { + Cow::Borrowed(s) => FastString::from_static(s), + Cow::Owned(s) => s.into(), + }) .map_err(|e| RequireErrorKind::ReadModule(e).into_box()) } @@ -513,7 +573,12 @@ pub fn op_require_as_file_path(#[string] file_or_url: String) -> String { #[op2(stack_trace)] #[string] -pub fn op_require_resolve_exports

( +pub fn op_require_resolve_exports< + P: NodePermissions + 'static, + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, uses_local_node_modules_dir: bool, #[string] modules_path_str: String, @@ -521,13 +586,14 @@ pub fn op_require_resolve_exports

( #[string] name: String, #[string] expansion: String, #[string] parent_path: String, -) -> Result, RequireError> -where - P: NodePermissions + 'static, -{ - let fs = state.borrow::(); - let node_resolver = state.borrow::(); - let pkg_json_resolver = state.borrow::(); +) -> Result, RequireError> { + let sys = state.borrow::(); + let node_resolver = state.borrow::>(); + let pkg_json_resolver = state.borrow::>(); let modules_path = PathBuf::from(&modules_path_str); let modules_specifier = deno_path_util::url_from_file_path(&modules_path)?; @@ -538,7 +604,7 @@ where } else { let mod_dir = path_resolve([modules_path_str.as_str(), name.as_str()].into_iter()); - if fs.is_dir_sync(&mod_dir) { + if sys.fs_is_dir_no_err(&mod_dir) { mod_dir } else { modules_path @@ -574,29 +640,35 @@ where })) } +deno_error::js_error_wrapper!( + ClosestPkgJsonError, + JsClosestPkgJsonError, + "Error" +); + #[op2(fast)] pub fn op_require_is_maybe_cjs( state: &mut OpState, #[string] filename: String, -) -> Result { +) -> Result { let filename = PathBuf::from(filename); let Ok(url) = url_from_file_path(&filename) else { return Ok(false); }; let loader = state.borrow::(); - loader.is_maybe_cjs(&url) + loader.is_maybe_cjs(&url).map_err(Into::into) } #[op2(stack_trace)] #[serde] -pub fn op_require_read_package_scope

( +pub fn op_require_read_package_scope< + P: NodePermissions + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] package_json_path: String, -) -> Option -where - P: NodePermissions + 'static, -{ - let pkg_json_resolver = state.borrow::(); +) -> Option { + let pkg_json_resolver = state.borrow::>(); let package_json_path = PathBuf::from(package_json_path); if package_json_path.file_name() != Some("package.json".as_ref()) { // permissions: do not allow reading a non-package.json file @@ -610,18 +682,20 @@ where #[op2(stack_trace)] #[string] -pub fn op_require_package_imports_resolve

( +pub fn op_require_package_imports_resolve< + P: NodePermissions + 'static, + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] referrer_filename: String, #[string] request: String, -) -> Result, RequireError> -where - P: NodePermissions + 'static, -{ +) -> Result, RequireError> { let referrer_path = PathBuf::from(&referrer_filename); let referrer_path = ensure_read_permission::

(state, &referrer_path) .map_err(RequireErrorKind::Permission)?; - let pkg_json_resolver = state.borrow::(); + let pkg_json_resolver = state.borrow::>(); let Some(pkg) = pkg_json_resolver .get_closest_package_json_from_file_path(&referrer_path)? else { @@ -629,7 +703,11 @@ where }; if pkg.imports.is_some() { - let node_resolver = state.borrow::(); + let node_resolver = state.borrow::>(); let referrer_url = Url::from_file_path(&referrer_filename).unwrap(); let url = node_resolver.package_imports_resolve( &request, diff --git a/ext/node/ops/tls.rs b/ext/node/ops/tls.rs index 86b1779601..8b2845e023 100644 --- a/ext/node/ops/tls.rs +++ b/ext/node/ops/tls.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use base64::Engine; use deno_core::op2; use webpki_root_certs; diff --git a/ext/node/ops/util.rs b/ext/node/ops/util.rs index 1c177ac043..1af4f7edbd 100644 --- a/ext/node/ops/util.rs +++ b/ext/node/ops/util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::OpState; @@ -21,7 +21,7 @@ enum HandleType { pub fn op_node_guess_handle_type( state: &mut OpState, rid: u32, -) -> Result { +) -> Result { let handle = state.resource_table.get_handle(rid)?; let handle_type = match handle { diff --git a/ext/node/ops/v8.rs b/ext/node/ops/v8.rs index 61f67f11f7..c268d41925 100644 --- a/ext/node/ops/v8.rs +++ b/ext/node/ops/v8.rs @@ -1,11 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr::NonNull; use deno_core::op2; use deno_core::v8; use deno_core::FastString; use deno_core::GarbageCollected; use deno_core::ToJsBuffer; -use std::ptr::NonNull; +use deno_error::JsErrorBox; use v8::ValueDeserializerHelper; use v8::ValueSerializerHelper; @@ -68,6 +70,7 @@ impl v8::ValueSerializerImpl for SerializerDelegate { let obj = self.obj(scope); let key = FastString::from_static("_getSharedArrayBufferId") .v8_string(scope) + .unwrap() .into(); if let Some(v) = obj.get(scope, key) { if let Ok(fun) = v.try_cast::() { @@ -89,6 +92,7 @@ impl v8::ValueSerializerImpl for SerializerDelegate { let obj = self.obj(scope); let key = FastString::from_static("_getDataCloneError") .v8_string(scope) + .unwrap() .into(); if let Some(v) = obj.get(scope, key) { let fun = v @@ -112,6 +116,7 @@ impl v8::ValueSerializerImpl for SerializerDelegate { let obj = self.obj(scope); let key = FastString::from_static("_writeHostObject") .v8_string(scope) + .unwrap() .into(); if let Some(v) = obj.get(scope, key) { if let Ok(v) = v.try_cast::() { @@ -240,6 +245,7 @@ impl v8::ValueDeserializerImpl for DeserializerDelegate { let obj = v8::Local::new(scope, &self.obj); let key = FastString::from_static("_readHostObject") .v8_string(scope) + .unwrap() .into(); let scope = &mut v8::AllowJavascriptExecutionScope::new(scope); if let Some(v) = obj.get(scope, key) { @@ -250,7 +256,8 @@ impl v8::ValueDeserializerImpl for DeserializerDelegate { Err(_) => { let msg = FastString::from_static("readHostObject must return an object") - .v8_string(scope); + .v8_string(scope) + .unwrap(); let error = v8::Exception::type_error(scope, msg); scope.throw_exception(error); return None; @@ -268,13 +275,11 @@ pub fn op_v8_new_deserializer( scope: &mut v8::HandleScope, obj: v8::Local, buffer: v8::Local, -) -> Result, deno_core::error::AnyError> { +) -> Result, JsErrorBox> { let offset = buffer.byte_offset(); let len = buffer.byte_length(); let backing_store = buffer.get_backing_store().ok_or_else(|| { - deno_core::error::generic_error( - "deserialization buffer has no backing store", - ) + JsErrorBox::generic("deserialization buffer has no backing store") })?; let (buf_slice, buf_ptr) = if let Some(data) = backing_store.data() { // SAFETY: the offset is valid for the underlying buffer because we're getting it directly from v8 @@ -316,10 +321,10 @@ pub fn op_v8_transfer_array_buffer_de( #[op2(fast)] pub fn op_v8_read_double( #[cppgc] deser: &Deserializer, -) -> Result { +) -> Result { let mut double = 0f64; if !deser.inner.read_double(&mut double) { - return Err(deno_core::error::type_error("ReadDouble() failed")); + return Err(JsErrorBox::type_error("ReadDouble() failed")); } Ok(double) } @@ -354,10 +359,10 @@ pub fn op_v8_read_raw_bytes( #[op2(fast)] pub fn op_v8_read_uint32( #[cppgc] deser: &Deserializer, -) -> Result { +) -> Result { let mut value = 0; if !deser.inner.read_uint32(&mut value) { - return Err(deno_core::error::type_error("ReadUint32() failed")); + return Err(JsErrorBox::type_error("ReadUint32() failed")); } Ok(value) @@ -367,10 +372,10 @@ pub fn op_v8_read_uint32( #[serde] pub fn op_v8_read_uint64( #[cppgc] deser: &Deserializer, -) -> Result<(u32, u32), deno_core::error::AnyError> { +) -> Result<(u32, u32), JsErrorBox> { let mut val = 0; if !deser.inner.read_uint64(&mut val) { - return Err(deno_core::error::type_error("ReadUint64() failed")); + return Err(JsErrorBox::type_error("ReadUint64() failed")); } Ok(((val >> 32) as u32, val as u32)) diff --git a/ext/node/ops/vm.rs b/ext/node/ops/vm.rs index 25881cbaed..34eff8865c 100644 --- a/ext/node/ops/vm.rs +++ b/ext/node/ops/vm.rs @@ -1,14 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; +use std::time::Duration; -use crate::create_host_defined_options; use deno_core::op2; use deno_core::serde_v8; use deno_core::v8; use deno_core::v8::MapFnTo; use deno_core::JsBuffer; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; -use std::time::Duration; + +use crate::create_host_defined_options; pub const PRIVATE_SYMBOL_NAME: v8::OneByteConst = v8::String::create_external_onebyte_const(b"node:contextify:context"); diff --git a/ext/node/ops/vm_internal.rs b/ext/node/ops/vm_internal.rs index 815f570ead..2219d05cd0 100644 --- a/ext/node/ops/vm_internal.rs +++ b/ext/node/ops/vm_internal.rs @@ -1,10 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::create_host_defined_options; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::v8; use deno_core::v8::MapFnTo; +use deno_error::JsErrorBox; + +use crate::create_host_defined_options; pub const PRIVATE_SYMBOL_NAME: v8::OneByteConst = v8::String::create_external_onebyte_const(b"node:contextify:context"); @@ -19,7 +19,7 @@ impl ContextifyScript { pub fn new( scope: &mut v8::HandleScope, source_str: v8::Local, - ) -> Result { + ) -> Result { let resource_name = v8::undefined(scope); let host_defined_options = create_host_defined_options(scope); let origin = v8::ScriptOrigin::new( @@ -44,7 +44,7 @@ impl ContextifyScript { v8::script_compiler::CompileOptions::NoCompileOptions, v8::script_compiler::NoCacheReason::NoReason, ) - .ok_or_else(|| type_error("Failed to compile script"))?; + .ok_or_else(|| JsErrorBox::type_error("Failed to compile script"))?; let script = v8::Global::new(scope, unbound_script); Ok(Self { script }) } diff --git a/ext/node/ops/winerror.rs b/ext/node/ops/winerror.rs index cb053774ef..5cbeddc5ae 100644 --- a/ext/node/ops/winerror.rs +++ b/ext/node/ops/winerror.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/ext/node/ops/worker_threads.rs b/ext/node/ops/worker_threads.rs index 37a7b477d0..ae3c28ef35 100644 --- a/ext/node/ops/worker_threads.rs +++ b/ext/node/ops/worker_threads.rs @@ -1,13 +1,17 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::op2; -use deno_core::url::Url; -use deno_core::OpState; -use deno_fs::FileSystemRc; use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; +use deno_core::op2; +use deno_core::url::Url; +use deno_core::OpState; +use deno_error::JsErrorBox; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; + +use crate::ExtNodeSys; use crate::NodePermissions; use crate::NodeRequireLoaderRc; @@ -15,7 +19,7 @@ use crate::NodeRequireLoaderRc; fn ensure_read_permission<'a, P>( state: &mut OpState, file_path: &'a Path, -) -> Result, deno_core::error::AnyError> +) -> Result, JsErrorBox> where P: NodePermissions + 'static, { @@ -24,36 +28,59 @@ where loader.ensure_read_permission(permissions, file_path) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WorkerThreadsFilenameError { + #[class(inherit)] #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(JsErrorBox), + #[class(inherit)] #[error("{0}")] - UrlParse(#[from] url::ParseError), + UrlParse( + #[from] + #[inherit] + url::ParseError, + ), + #[class(generic)] #[error("Relative path entries must start with '.' or '..'")] InvalidRelativeUrl, + #[class(generic)] #[error("URL from Path-String")] UrlFromPathString, + #[class(generic)] #[error("URL to Path-String")] UrlToPathString, + #[class(generic)] #[error("URL to Path")] UrlToPath, + #[class(generic)] #[error("File not found [{0:?}]")] FileNotFound(PathBuf), + #[class(inherit)] #[error(transparent)] - Fs(#[from] deno_io::fs::FsError), + Fs( + #[from] + #[inherit] + deno_io::fs::FsError, + ), + #[class(inherit)] + #[error(transparent)] + Io( + #[from] + #[inherit] + std::io::Error, + ), } // todo(dsherret): we should remove this and do all this work inside op_create_worker #[op2(stack_trace)] #[string] -pub fn op_worker_threads_filename

( +pub fn op_worker_threads_filename< + P: NodePermissions + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] specifier: String, -) -> Result -where - P: NodePermissions + 'static, -{ +) -> Result { if specifier.starts_with("data:") { return Ok(specifier); } @@ -66,9 +93,9 @@ where } let path = ensure_read_permission::

(state, &path) .map_err(WorkerThreadsFilenameError::Permission)?; - let fs = state.borrow::(); + let sys = state.borrow::(); let canonicalized_path = - deno_path_util::strip_unc_prefix(fs.realpath_sync(&path)?); + deno_path_util::strip_unc_prefix(sys.fs_canonicalize(&path)?); Url::from_file_path(canonicalized_path) .map_err(|_| WorkerThreadsFilenameError::UrlFromPathString)? }; @@ -77,8 +104,8 @@ where .map_err(|_| WorkerThreadsFilenameError::UrlToPathString)?; let url_path = ensure_read_permission::

(state, &url_path) .map_err(WorkerThreadsFilenameError::Permission)?; - let fs = state.borrow::(); - if !fs.exists_sync(&url_path) { + let sys = state.borrow::(); + if !sys.fs_exists_no_err(&url_path) { return Err(WorkerThreadsFilenameError::FileNotFound( url_path.to_path_buf(), )); diff --git a/ext/node/ops/zlib/alloc.rs b/ext/node/ops/zlib/alloc.rs index d425a18d5e..6066ab6a8e 100644 --- a/ext/node/ops/zlib/alloc.rs +++ b/ext/node/ops/zlib/alloc.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Workaround for https://github.com/rust-lang/libz-sys/issues/55 // See https://github.com/rust-lang/flate2-rs/blob/31fb07820345691352aaa64f367c1e482ad9cfdc/src/ffi/c.rs#L60 diff --git a/ext/node/ops/zlib/brotli.rs b/ext/node/ops/zlib/brotli.rs index 1a681ff7f7..5e4c1d16e6 100644 --- a/ext/node/ops/zlib/brotli.rs +++ b/ext/node/ops/zlib/brotli.rs @@ -1,4 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::cell::RefCell; +use std::io::Read; + use brotli::enc::backward_references::BrotliEncoderMode; use brotli::enc::encode::BrotliEncoderCompress; use brotli::enc::encode::BrotliEncoderOperation; @@ -14,23 +17,35 @@ use deno_core::JsBuffer; use deno_core::OpState; use deno_core::Resource; use deno_core::ToJsBuffer; -use std::cell::RefCell; -use std::io::Read; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BrotliError { + #[class(type)] #[error("Invalid encoder mode")] InvalidEncoderMode, + #[class(type)] #[error("Failed to compress")] CompressFailed, + #[class(type)] #[error("Failed to decompress")] DecompressFailed, + #[class(inherit)] #[error(transparent)] - Join(#[from] tokio::task::JoinError), + Join( + #[from] + #[inherit] + tokio::task::JoinError, + ), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(inherit)] #[error("{0}")] - Io(std::io::Error), + Io(#[inherit] std::io::Error), } fn encoder_mode(mode: u32) -> Result { @@ -166,10 +181,7 @@ pub fn op_brotli_compress_stream( #[buffer] input: &[u8], #[buffer] output: &mut [u8], ) -> Result { - let ctx = state - .resource_table - .get::(rid) - .map_err(BrotliError::Resource)?; + let ctx = state.resource_table.get::(rid)?; let mut inst = ctx.inst.borrow_mut(); let mut output_offset = 0; @@ -198,10 +210,7 @@ pub fn op_brotli_compress_stream_end( #[smi] rid: u32, #[buffer] output: &mut [u8], ) -> Result { - let ctx = state - .resource_table - .get::(rid) - .map_err(BrotliError::Resource)?; + let ctx = state.resource_table.get::(rid)?; let mut inst = ctx.inst.borrow_mut(); let mut output_offset = 0; @@ -276,10 +285,7 @@ pub fn op_brotli_decompress_stream( #[buffer] input: &[u8], #[buffer] output: &mut [u8], ) -> Result { - let ctx = state - .resource_table - .get::(rid) - .map_err(BrotliError::Resource)?; + let ctx = state.resource_table.get::(rid)?; let mut inst = ctx.inst.borrow_mut(); let mut output_offset = 0; @@ -307,10 +313,7 @@ pub fn op_brotli_decompress_stream_end( #[smi] rid: u32, #[buffer] output: &mut [u8], ) -> Result { - let ctx = state - .resource_table - .get::(rid) - .map_err(BrotliError::Resource)?; + let ctx = state.resource_table.get::(rid)?; let mut inst = ctx.inst.borrow_mut(); let mut output_offset = 0; diff --git a/ext/node/ops/zlib/mod.rs b/ext/node/ops/zlib/mod.rs index 991c0925d2..892944bcea 100644 --- a/ext/node/ops/zlib/mod.rs +++ b/ext/node/ops/zlib/mod.rs @@ -1,9 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::op2; -use libc::c_ulong; use std::borrow::Cow; use std::cell::RefCell; + +use deno_core::op2; +use deno_error::JsErrorBox; +use libc::c_ulong; use zlib::*; mod alloc; @@ -17,11 +19,11 @@ use mode::Mode; use self::stream::StreamWrapper; #[inline] -fn check(condition: bool, msg: &str) -> Result<(), deno_core::error::AnyError> { +fn check(condition: bool, msg: &str) -> Result<(), JsErrorBox> { if condition { Ok(()) } else { - Err(deno_core::error::type_error(msg.to_string())) + Err(JsErrorBox::type_error(msg.to_string())) } } @@ -56,7 +58,7 @@ impl ZlibInner { out_off: u32, out_len: u32, flush: Flush, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), JsErrorBox> { check(self.init_done, "write before init")?; check(!self.write_in_progress, "write already in progress")?; check(!self.pending_close, "close already in progress")?; @@ -65,11 +67,11 @@ impl ZlibInner { let next_in = input .get(in_off as usize..in_off as usize + in_len as usize) - .ok_or_else(|| deno_core::error::type_error("invalid input range"))? + .ok_or_else(|| JsErrorBox::type_error("invalid input range"))? .as_ptr() as *mut _; let next_out = out .get_mut(out_off as usize..out_off as usize + out_len as usize) - .ok_or_else(|| deno_core::error::type_error("invalid output range"))? + .ok_or_else(|| JsErrorBox::type_error("invalid output range"))? .as_mut_ptr(); self.strm.avail_in = in_len; @@ -81,10 +83,7 @@ impl ZlibInner { Ok(()) } - fn do_write( - &mut self, - flush: Flush, - ) -> Result<(), deno_core::error::AnyError> { + fn do_write(&mut self, flush: Flush) -> Result<(), JsErrorBox> { self.flush = flush; match self.mode { Mode::Deflate | Mode::Gzip | Mode::DeflateRaw => { @@ -130,7 +129,7 @@ impl ZlibInner { self.mode = Mode::Inflate; } } else if next_expected_header_byte.is_some() { - return Err(deno_core::error::type_error( + return Err(JsErrorBox::type_error( "invalid number of gzip magic number bytes read", )); } @@ -184,7 +183,7 @@ impl ZlibInner { Ok(()) } - fn init_stream(&mut self) -> Result<(), deno_core::error::AnyError> { + fn init_stream(&mut self) -> Result<(), JsErrorBox> { match self.mode { Mode::Gzip | Mode::Gunzip => self.window_bits += 16, Mode::Unzip => self.window_bits += 32, @@ -202,7 +201,7 @@ impl ZlibInner { Mode::Inflate | Mode::Gunzip | Mode::InflateRaw | Mode::Unzip => { self.strm.inflate_init(self.window_bits) } - Mode::None => return Err(deno_core::error::type_error("Unknown mode")), + Mode::None => return Err(JsErrorBox::type_error("Unknown mode")), }; self.write_in_progress = false; @@ -211,7 +210,7 @@ impl ZlibInner { Ok(()) } - fn close(&mut self) -> Result { + fn close(&mut self) -> Result { if self.write_in_progress { self.pending_close = true; return Ok(false); @@ -257,14 +256,25 @@ pub fn op_zlib_new(#[smi] mode: i32) -> Result { }) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ZlibError { + #[class(type)] #[error("zlib not initialized")] NotInitialized, + #[class(inherit)] #[error(transparent)] - Mode(#[from] mode::ModeError), + Mode( + #[from] + #[inherit] + mode::ModeError, + ), + #[class(inherit)] #[error(transparent)] - Other(#[from] deno_core::error::AnyError), + Other( + #[from] + #[inherit] + JsErrorBox, + ), } #[op2(fast)] diff --git a/ext/node/ops/zlib/mode.rs b/ext/node/ops/zlib/mode.rs index 41565f9b11..5fa2e501dc 100644 --- a/ext/node/ops/zlib/mode.rs +++ b/ext/node/ops/zlib/mode.rs @@ -1,6 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] #[error("bad argument")] pub struct ModeError; diff --git a/ext/node/ops/zlib/stream.rs b/ext/node/ops/zlib/stream.rs index afcdcc4d70..de7abcacbd 100644 --- a/ext/node/ops/zlib/stream.rs +++ b/ext/node/ops/zlib/stream.rs @@ -1,11 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::mode::Flush; -use super::mode::Mode; use std::ffi::c_int; use std::ops::Deref; use std::ops::DerefMut; +use super::mode::Flush; +use super::mode::Mode; + pub struct StreamWrapper { pub strm: zlib::z_stream, } diff --git a/ext/node/polyfill.rs b/ext/node/polyfill.rs index 762e32f7ba..6e6e9d09c2 100644 --- a/ext/node/polyfill.rs +++ b/ext/node/polyfill.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// e.g. `is_builtin_node_module("assert")` pub fn is_builtin_node_module(module_name: &str) -> bool { @@ -57,6 +57,7 @@ generate_builtin_node_module_lists! { "http2", "https", "inspector", + "inspector/promises", "module", "net", "os", @@ -67,9 +68,9 @@ generate_builtin_node_module_lists! { "process", "punycode", "querystring", - "repl", "readline", "readline/promises", + "repl", "stream", "stream/consumers", "stream/promises", @@ -90,3 +91,10 @@ generate_builtin_node_module_lists! { "worker_threads", "zlib", } + +#[test] +fn test_builtins_are_sorted() { + let mut builtins_list = SUPPORTED_BUILTIN_NODE_MODULES.to_vec(); + builtins_list.sort(); + assert_eq!(SUPPORTED_BUILTIN_NODE_MODULES, builtins_list); +} diff --git a/ext/node/polyfills/00_globals.js b/ext/node/polyfills/00_globals.js index efe491acc1..8a896412bc 100644 --- a/ext/node/polyfills/00_globals.js +++ b/ext/node/polyfills/00_globals.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js index df73cad6b7..3e90750cd3 100644 --- a/ext/node/polyfills/01_require.js +++ b/ext/node/polyfills/01_require.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file @@ -946,7 +946,7 @@ Module.prototype.require = function (id) { // wrapper function we run the users code in. The only observable difference is // that in Deno `arguments.callee` is not null. Module.wrapper = [ - "(function (exports, require, module, __filename, __dirname, Buffer, clearImmediate, clearInterval, clearTimeout, global, setImmediate, setInterval, setTimeout, performance) { (function (exports, require, module, __filename, __dirname) {", + "(function (exports, require, module, __filename, __dirname, Buffer, clearImmediate, clearInterval, clearTimeout, global, process, setImmediate, setInterval, setTimeout, performance) { (function (exports, require, module, __filename, __dirname) {", "\n}).call(this, exports, require, module, __filename, __dirname); })", ]; Module.wrap = function (script) { @@ -1031,6 +1031,7 @@ Module.prototype._compile = function (content, filename, format) { clearInterval, clearTimeout, global, + process, setImmediate, setInterval, setTimeout, @@ -1049,6 +1050,7 @@ Module.prototype._compile = function (content, filename, format) { clearInterval, clearTimeout, global, + process, setImmediate, setInterval, setTimeout, diff --git a/ext/node/polyfills/02_init.js b/ext/node/polyfills/02_init.js index b25f7ad574..a069bd828c 100644 --- a/ext/node/polyfills/02_init.js +++ b/ext/node/polyfills/02_init.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/_brotli.js b/ext/node/polyfills/_brotli.js index ebd0351561..308cad42ad 100644 --- a/ext/node/polyfills/_brotli.js +++ b/ext/node/polyfills/_brotli.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { @@ -10,9 +10,12 @@ const { ArrayPrototypeMap, TypedArrayPrototypeSlice, TypedArrayPrototypeSubarray, - TypedArrayPrototypeGetByteLength, - DataViewPrototypeGetBuffer, TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetByteOffset, + DataViewPrototypeGetBuffer, + DataViewPrototypeGetByteLength, + DataViewPrototypeGetByteOffset, } = primordials; const { isTypedArray, isDataView, close } = core; import { @@ -40,9 +43,17 @@ const toU8 = (input) => { } if (isTypedArray(input)) { - return new Uint8Array(TypedArrayPrototypeGetBuffer(input)); + return new Uint8Array( + TypedArrayPrototypeGetBuffer(input), + TypedArrayPrototypeGetByteOffset(input), + TypedArrayPrototypeGetByteLength(input), + ); } else if (isDataView(input)) { - return new Uint8Array(DataViewPrototypeGetBuffer(input)); + return new Uint8Array( + DataViewPrototypeGetBuffer(input), + DataViewPrototypeGetByteOffset(input), + DataViewPrototypeGetByteLength(input), + ); } return input; diff --git a/ext/node/polyfills/_events.d.ts b/ext/node/polyfills/_events.d.ts index 1b765041e5..98a6b9d2f4 100644 --- a/ext/node/polyfills/_events.d.ts +++ b/ext/node/polyfills/_events.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any // Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/9b9cd671114a2a5178809798d8e7f4d8ca6c2671/types/node/events.d.ts diff --git a/ext/node/polyfills/_events.mjs b/ext/node/polyfills/_events.mjs index ce7a8ebf24..feecad2743 100644 --- a/ext/node/polyfills/_events.mjs +++ b/ext/node/polyfills/_events.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/_fs/_fs_access.ts b/ext/node/polyfills/_fs/_fs_access.ts index b501bcbcae..561e023cd8 100644 --- a/ext/node/polyfills/_fs/_fs_access.ts +++ b/ext/node/polyfills/_fs/_fs_access.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials @@ -30,50 +30,58 @@ export function access( mode = getValidMode(mode, "access"); const cb = makeCallback(callback); - Deno.lstat(path).then((info) => { - if (info.mode === null) { - // If the file mode is unavailable, we pretend it has - // the permission - cb(null); - return; - } - const m = +mode || 0; - let fileMode = +info.mode || 0; - if (Deno.build.os !== "windows" && info.uid === Deno.uid()) { - // If the user is the owner of the file, then use the owner bits of - // the file permission - fileMode >>= 6; - } - // TODO(kt3k): Also check the case when the user belong to the group - // of the file - if ((m & fileMode) === m) { - // all required flags exist - cb(null); - } else { - // some required flags don't - // deno-lint-ignore no-explicit-any - const e: any = new Error(`EACCES: permission denied, access '${path}'`); - e.path = path; - e.syscall = "access"; - e.errno = codeMap.get("EACCES"); - e.code = "EACCES"; - cb(e); - } - }, (err) => { - if (err instanceof Deno.errors.NotFound) { - // deno-lint-ignore no-explicit-any - const e: any = new Error( - `ENOENT: no such file or directory, access '${path}'`, - ); - e.path = path; - e.syscall = "access"; - e.errno = codeMap.get("ENOENT"); - e.code = "ENOENT"; - cb(e); - } else { - cb(err); - } - }); + Deno.lstat(path).then( + (info) => { + if (info.mode === null) { + // If the file mode is unavailable, we pretend it has + // the permission + cb(null); + return; + } + let m = +mode || 0; + let fileMode = +info.mode || 0; + + if (Deno.build.os === "windows") { + m &= ~fs.X_OK; // Ignore the X_OK bit on Windows + } else if (info.uid === Deno.uid()) { + // If the user is the owner of the file, then use the owner bits of + // the file permission + fileMode >>= 6; + } + + // TODO(kt3k): Also check the case when the user belong to the group + // of the file + + if ((m & fileMode) === m) { + // all required flags exist + cb(null); + } else { + // some required flags don't + // deno-lint-ignore no-explicit-any + const e: any = new Error(`EACCES: permission denied, access '${path}'`); + e.path = path; + e.syscall = "access"; + e.errno = codeMap.get("EACCES"); + e.code = "EACCES"; + cb(e); + } + }, + (err) => { + if (err instanceof Deno.errors.NotFound) { + // deno-lint-ignore no-explicit-any + const e: any = new Error( + `ENOENT: no such file or directory, access '${path}'`, + ); + e.path = path; + e.syscall = "access"; + e.errno = codeMap.get("ENOENT"); + e.code = "ENOENT"; + cb(e); + } else { + cb(err); + } + }, + ); } export const accessPromise = promisify(access) as ( @@ -91,9 +99,11 @@ export function accessSync(path: string | Buffer | URL, mode?: number) { // the permission return; } - const m = +mode! || 0; + let m = +mode! || 0; let fileMode = +info.mode! || 0; - if (Deno.build.os !== "windows" && info.uid === Deno.uid()) { + if (Deno.build.os === "windows") { + m &= ~fs.X_OK; // Ignore the X_OK bit on Windows + } else if (info.uid === Deno.uid()) { // If the user is the owner of the file, then use the owner bits of // the file permission fileMode >>= 6; diff --git a/ext/node/polyfills/_fs/_fs_appendFile.ts b/ext/node/polyfills/_fs/_fs_appendFile.ts index 7077898623..ed47ea5a83 100644 --- a/ext/node/polyfills/_fs/_fs_appendFile.ts +++ b/ext/node/polyfills/_fs/_fs_appendFile.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { CallbackWithError, diff --git a/ext/node/polyfills/_fs/_fs_chmod.ts b/ext/node/polyfills/_fs/_fs_chmod.ts index eec8c7a8a9..195b4f810a 100644 --- a/ext/node/polyfills/_fs/_fs_chmod.ts +++ b/ext/node/polyfills/_fs/_fs_chmod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_chown.ts b/ext/node/polyfills/_fs/_fs_chown.ts index 56364109d5..0056502f20 100644 --- a/ext/node/polyfills/_fs/_fs_chown.ts +++ b/ext/node/polyfills/_fs/_fs_chown.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_close.ts b/ext/node/polyfills/_fs/_fs_close.ts index fd01a0336a..476b3912be 100644 --- a/ext/node/polyfills/_fs/_fs_close.ts +++ b/ext/node/polyfills/_fs/_fs_close.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_common.ts b/ext/node/polyfills/_fs/_fs_common.ts index a29548bb36..8358f0271c 100644 --- a/ext/node/polyfills/_fs/_fs_common.ts +++ b/ext/node/polyfills/_fs/_fs_common.ts @@ -1,8 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// TODO(petamoriken): enable prefer-primordials for node polyfills -// deno-lint-ignore-file prefer-primordials +// Copyright 2018-2025 the Deno authors. MIT license. +import { primordials } from "ext:core/mod.js"; +const { + StringPrototypeToLowerCase, + ArrayPrototypeIncludes, + ReflectApply, + Error, +} = primordials; import { O_APPEND, O_CREAT, @@ -85,8 +89,10 @@ export function getEncoding( export function checkEncoding(encoding: Encodings | null): Encodings | null { if (!encoding) return null; - encoding = encoding.toLowerCase() as Encodings; - if (["utf8", "hex", "base64", "ascii"].includes(encoding)) return encoding; + encoding = StringPrototypeToLowerCase(encoding) as Encodings; + if (ArrayPrototypeIncludes(["utf8", "hex", "base64", "ascii"], encoding)) { + return encoding; + } if (encoding === "utf-8") { return "utf8"; @@ -99,7 +105,7 @@ export function checkEncoding(encoding: Encodings | null): Encodings | null { const notImplementedEncodings = ["utf16le", "latin1", "ucs2"]; - if (notImplementedEncodings.includes(encoding as string)) { + if (ArrayPrototypeIncludes(notImplementedEncodings, encoding as string)) { notImplemented(`"${encoding}" encoding`); } @@ -241,5 +247,5 @@ export function makeCallback( ) { validateFunction(cb, "cb"); - return (...args: unknown[]) => Reflect.apply(cb!, this, args); + return (...args: unknown[]) => ReflectApply(cb!, this, args); } diff --git a/ext/node/polyfills/_fs/_fs_constants.ts b/ext/node/polyfills/_fs/_fs_constants.ts index 0af75f072c..3d5d0b5898 100644 --- a/ext/node/polyfills/_fs/_fs_constants.ts +++ b/ext/node/polyfills/_fs/_fs_constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { fs } from "ext:deno_node/internal_binding/constants.ts"; diff --git a/ext/node/polyfills/_fs/_fs_copy.ts b/ext/node/polyfills/_fs/_fs_copy.ts index 0434bff4d1..87204e348c 100644 --- a/ext/node/polyfills/_fs/_fs_copy.ts +++ b/ext/node/polyfills/_fs/_fs_copy.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_cp.js b/ext/node/polyfills/_fs/_fs_cp.js index 52dd8f5056..29bc633224 100644 --- a/ext/node/polyfills/_fs/_fs_cp.js +++ b/ext/node/polyfills/_fs/_fs_cp.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { op_node_cp, op_node_cp_sync } from "ext:core/ops"; import { diff --git a/ext/node/polyfills/_fs/_fs_dir.ts b/ext/node/polyfills/_fs/_fs_dir.ts index ed051fb0bf..2a496562d6 100644 --- a/ext/node/polyfills/_fs/_fs_dir.ts +++ b/ext/node/polyfills/_fs/_fs_dir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import Dirent from "ext:deno_node/_fs/_fs_dirent.ts"; diff --git a/ext/node/polyfills/_fs/_fs_dirent.ts b/ext/node/polyfills/_fs/_fs_dirent.ts index d4ad6bb430..fce7a6e72d 100644 --- a/ext/node/polyfills/_fs/_fs_dirent.ts +++ b/ext/node/polyfills/_fs/_fs_dirent.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; export default class Dirent { diff --git a/ext/node/polyfills/_fs/_fs_exists.ts b/ext/node/polyfills/_fs/_fs_exists.ts index b5bbe235a4..6a285f5e60 100644 --- a/ext/node/polyfills/_fs/_fs_exists.ts +++ b/ext/node/polyfills/_fs/_fs_exists.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_fdatasync.ts b/ext/node/polyfills/_fs/_fs_fdatasync.ts index 0c3f50f1c6..02226e1c96 100644 --- a/ext/node/polyfills/_fs/_fs_fdatasync.ts +++ b/ext/node/polyfills/_fs/_fs_fdatasync.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_fstat.ts b/ext/node/polyfills/_fs/_fs_fstat.ts index 1a845dfff4..37c057c378 100644 --- a/ext/node/polyfills/_fs/_fs_fstat.ts +++ b/ext/node/polyfills/_fs/_fs_fstat.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_fsync.ts b/ext/node/polyfills/_fs/_fs_fsync.ts index 75d4b37569..a07f440778 100644 --- a/ext/node/polyfills/_fs/_fs_fsync.ts +++ b/ext/node/polyfills/_fs/_fs_fsync.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_ftruncate.ts b/ext/node/polyfills/_fs/_fs_ftruncate.ts index 92af46f521..6a8614b734 100644 --- a/ext/node/polyfills/_fs/_fs_ftruncate.ts +++ b/ext/node/polyfills/_fs/_fs_ftruncate.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials @@ -16,16 +16,24 @@ export function ftruncate( : undefined; const callback: CallbackWithError = typeof lenOrCallback === "function" ? lenOrCallback - : maybeCallback as CallbackWithError; + : (maybeCallback as CallbackWithError); if (!callback) throw new Error("No callback function supplied"); - new FsFile(fd, Symbol.for("Deno.internal.FsFile")).truncate(len).then( - () => callback(null), - callback, - ); + new FsFile(fd, Symbol.for("Deno.internal.FsFile")) + .truncate(len) + .then(() => callback(null), callback); } export function ftruncateSync(fd: number, len?: number) { new FsFile(fd, Symbol.for("Deno.internal.FsFile")).truncateSync(len); } + +export function ftruncatePromise(fd: number, len?: number): Promise { + return new Promise((resolve, reject) => { + ftruncate(fd, len, (err) => { + if (err) reject(err); + else resolve(); + }); + }); +} diff --git a/ext/node/polyfills/_fs/_fs_futimes.ts b/ext/node/polyfills/_fs/_fs_futimes.ts index 98cd1066c3..be274f4d11 100644 --- a/ext/node/polyfills/_fs/_fs_futimes.ts +++ b/ext/node/polyfills/_fs/_fs_futimes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_lchown.ts b/ext/node/polyfills/_fs/_fs_lchown.ts index 8611c8021d..293155f9d0 100644 --- a/ext/node/polyfills/_fs/_fs_lchown.ts +++ b/ext/node/polyfills/_fs/_fs_lchown.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_link.ts b/ext/node/polyfills/_fs/_fs_link.ts index a10860c12f..73ed890d36 100644 --- a/ext/node/polyfills/_fs/_fs_link.ts +++ b/ext/node/polyfills/_fs/_fs_link.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_lstat.ts b/ext/node/polyfills/_fs/_fs_lstat.ts index 6ce401444f..b9f80bfc51 100644 --- a/ext/node/polyfills/_fs/_fs_lstat.ts +++ b/ext/node/polyfills/_fs/_fs_lstat.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_lutimes.ts b/ext/node/polyfills/_fs/_fs_lutimes.ts index 2475c57149..3f8dd167c0 100644 --- a/ext/node/polyfills/_fs/_fs_lutimes.ts +++ b/ext/node/polyfills/_fs/_fs_lutimes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_mkdir.ts b/ext/node/polyfills/_fs/_fs_mkdir.ts index 06a8b35221..03d36629e5 100644 --- a/ext/node/polyfills/_fs/_fs_mkdir.ts +++ b/ext/node/polyfills/_fs/_fs_mkdir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_mkdtemp.ts b/ext/node/polyfills/_fs/_fs_mkdtemp.ts index 5498828b5b..46060971a8 100644 --- a/ext/node/polyfills/_fs/_fs_mkdtemp.ts +++ b/ext/node/polyfills/_fs/_fs_mkdtemp.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Node.js contributors. All rights reserved. MIT License. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_fs/_fs_open.ts b/ext/node/polyfills/_fs/_fs_open.ts index 31ca4bb619..12f8ed7035 100644 --- a/ext/node/polyfills/_fs/_fs_open.ts +++ b/ext/node/polyfills/_fs/_fs_open.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials @@ -153,7 +153,7 @@ export function openPromise( return new Promise((resolve, reject) => { open(path, flags, mode, (err, fd) => { if (err) reject(err); - else resolve(new FileHandle(fd)); + else resolve(new FileHandle(fd, path)); }); }); } diff --git a/ext/node/polyfills/_fs/_fs_opendir.ts b/ext/node/polyfills/_fs/_fs_opendir.ts index 3280f0fd5c..51379fd451 100644 --- a/ext/node/polyfills/_fs/_fs_opendir.ts +++ b/ext/node/polyfills/_fs/_fs_opendir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_read.ts b/ext/node/polyfills/_fs/_fs_read.ts index df4f5e375d..c7f3acf4d8 100644 --- a/ext/node/polyfills/_fs/_fs_read.ts +++ b/ext/node/polyfills/_fs/_fs_read.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_readFile.ts b/ext/node/polyfills/_fs/_fs_readFile.ts index b1bc53675d..752a683ca7 100644 --- a/ext/node/polyfills/_fs/_fs_readFile.ts +++ b/ext/node/polyfills/_fs/_fs_readFile.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_readdir.ts b/ext/node/polyfills/_fs/_fs_readdir.ts index 59c802e424..4b34451a38 100644 --- a/ext/node/polyfills/_fs/_fs_readdir.ts +++ b/ext/node/polyfills/_fs/_fs_readdir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_readlink.ts b/ext/node/polyfills/_fs/_fs_readlink.ts index 08bea843fa..084dbc2d09 100644 --- a/ext/node/polyfills/_fs/_fs_readlink.ts +++ b/ext/node/polyfills/_fs/_fs_readlink.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_readv.ts b/ext/node/polyfills/_fs/_fs_readv.ts index 2259f029ae..4486959b76 100644 --- a/ext/node/polyfills/_fs/_fs_readv.ts +++ b/ext/node/polyfills/_fs/_fs_readv.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_realpath.ts b/ext/node/polyfills/_fs/_fs_realpath.ts index 4568748ba7..12f5f10a43 100644 --- a/ext/node/polyfills/_fs/_fs_realpath.ts +++ b/ext/node/polyfills/_fs/_fs_realpath.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_rename.ts b/ext/node/polyfills/_fs/_fs_rename.ts index 41569c80f3..7a84cf8d98 100644 --- a/ext/node/polyfills/_fs/_fs_rename.ts +++ b/ext/node/polyfills/_fs/_fs_rename.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_rm.ts b/ext/node/polyfills/_fs/_fs_rm.ts index 5730f8c413..cce2ab95c0 100644 --- a/ext/node/polyfills/_fs/_fs_rm.ts +++ b/ext/node/polyfills/_fs/_fs_rm.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_rmdir.ts b/ext/node/polyfills/_fs/_fs_rmdir.ts index 00c085ebd5..6a78e9e467 100644 --- a/ext/node/polyfills/_fs/_fs_rmdir.ts +++ b/ext/node/polyfills/_fs/_fs_rmdir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_stat.ts b/ext/node/polyfills/_fs/_fs_stat.ts index f264746686..031c8dbb50 100644 --- a/ext/node/polyfills/_fs/_fs_stat.ts +++ b/ext/node/polyfills/_fs/_fs_stat.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_statfs.js b/ext/node/polyfills/_fs/_fs_statfs.js index 51da1ed684..90953c12b5 100644 --- a/ext/node/polyfills/_fs/_fs_statfs.js +++ b/ext/node/polyfills/_fs/_fs_statfs.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { BigInt } from "ext:deno_node/internal/primordials.mjs"; import { op_node_statfs } from "ext:core/ops"; diff --git a/ext/node/polyfills/_fs/_fs_symlink.ts b/ext/node/polyfills/_fs/_fs_symlink.ts index 350263423a..953697e44c 100644 --- a/ext/node/polyfills/_fs/_fs_symlink.ts +++ b/ext/node/polyfills/_fs/_fs_symlink.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_truncate.ts b/ext/node/polyfills/_fs/_fs_truncate.ts index a7e79b9c30..47b87eecdc 100644 --- a/ext/node/polyfills/_fs/_fs_truncate.ts +++ b/ext/node/polyfills/_fs/_fs_truncate.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_unlink.ts b/ext/node/polyfills/_fs/_fs_unlink.ts index 1d9aefa46f..d046c40f35 100644 --- a/ext/node/polyfills/_fs/_fs_unlink.ts +++ b/ext/node/polyfills/_fs/_fs_unlink.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_utimes.ts b/ext/node/polyfills/_fs/_fs_utimes.ts index 3fff4a4623..ff25fffed8 100644 --- a/ext/node/polyfills/_fs/_fs_utimes.ts +++ b/ext/node/polyfills/_fs/_fs_utimes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_watch.ts b/ext/node/polyfills/_fs/_fs_watch.ts index eb2cd8bfa2..8b92e9aa44 100644 --- a/ext/node/polyfills/_fs/_fs_watch.ts +++ b/ext/node/polyfills/_fs/_fs_watch.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_write.d.ts b/ext/node/polyfills/_fs/_fs_write.d.ts index 7171ec6dbd..cd8257ec35 100644 --- a/ext/node/polyfills/_fs/_fs_write.d.ts +++ b/ext/node/polyfills/_fs/_fs_write.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts import { BufferEncoding, ErrnoException } from "ext:deno_node/_global.d.ts"; diff --git a/ext/node/polyfills/_fs/_fs_write.mjs b/ext/node/polyfills/_fs/_fs_write.mjs index 254094c9ad..a0d0af6017 100644 --- a/ext/node/polyfills/_fs/_fs_write.mjs +++ b/ext/node/polyfills/_fs/_fs_write.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_fs/_fs_writeFile.ts b/ext/node/polyfills/_fs/_fs_writeFile.ts index dd324815b9..c76be8a6b8 100644 --- a/ext/node/polyfills/_fs/_fs_writeFile.ts +++ b/ext/node/polyfills/_fs/_fs_writeFile.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_writev.d.ts b/ext/node/polyfills/_fs/_fs_writev.d.ts deleted file mode 100644 index daa4dbbcf4..0000000000 --- a/ext/node/polyfills/_fs/_fs_writev.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts - -import { ErrnoException } from "ext:deno_node/_global.d.ts"; - -/** - * Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`. - * - * `position` is the offset from the beginning of the file where this data - * should be written. If `typeof position !== 'number'`, the data will be written - * at the current position. - * - * The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`. - * - * If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties. - * - * It is unsafe to use `fs.writev()` multiple times on the same file without - * waiting for the callback. For this scenario, use {@link createWriteStream}. - * - * On Linux, positional writes don't work when the file is opened in append mode. - * The kernel ignores the position argument and always appends the data to - * the end of the file. - * @since v12.9.0 - */ -export function writev( - fd: number, - buffers: ReadonlyArray, - cb: ( - err: ErrnoException | null, - bytesWritten: number, - buffers: ArrayBufferView[], - ) => void, -): void; -export function writev( - fd: number, - buffers: ReadonlyArray, - position: number | null, - cb: ( - err: ErrnoException | null, - bytesWritten: number, - buffers: ArrayBufferView[], - ) => void, -): void; -export interface WriteVResult { - bytesWritten: number; - buffers: ArrayBufferView[]; -} -export namespace writev { - function __promisify__( - fd: number, - buffers: ReadonlyArray, - position?: number, - ): Promise; -} -/** - * For detailed information, see the documentation of the asynchronous version of - * this API: {@link writev}. - * @since v12.9.0 - * @return The number of bytes written. - */ -export function writevSync( - fd: number, - buffers: ReadonlyArray, - position?: number, -): number; diff --git a/ext/node/polyfills/_fs/_fs_writev.mjs b/ext/node/polyfills/_fs/_fs_writev.ts similarity index 50% rename from ext/node/polyfills/_fs/_fs_writev.mjs rename to ext/node/polyfills/_fs/_fs_writev.ts index 055bce0b24..e38b44b19a 100644 --- a/ext/node/polyfills/_fs/_fs_writev.mjs +++ b/ext/node/polyfills/_fs/_fs_writev.ts @@ -1,19 +1,57 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials import { Buffer } from "node:buffer"; +import process from "node:process"; +import { ErrnoException } from "ext:deno_node/_global.d.ts"; import { validateBufferArray } from "ext:deno_node/internal/fs/utils.mjs"; import { getValidatedFd } from "ext:deno_node/internal/fs/utils.mjs"; +import { WriteVResult } from "ext:deno_node/internal/fs/handle.ts"; import { maybeCallback } from "ext:deno_node/_fs/_fs_common.ts"; import * as io from "ext:deno_io/12_io.js"; import { op_fs_seek_async, op_fs_seek_sync } from "ext:core/ops"; -export function writev(fd, buffers, position, callback) { +export interface WriteVResult { + bytesWritten: number; + buffers: ReadonlyArray; +} + +type writeVCallback = ( + err: ErrnoException | null, + bytesWritten: number, + buffers: ReadonlyArray, +) => void; + +/** + * Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`. + * + * `position` is the offset from the beginning of the file where this data + * should be written. If `typeof position !== 'number'`, the data will be written + * at the current position. + * + * The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`. + * + * If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties. + * + * It is unsafe to use `fs.writev()` multiple times on the same file without + * waiting for the callback. For this scenario, use {@link createWriteStream}. + * + * On Linux, positional writes don't work when the file is opened in append mode. + * The kernel ignores the position argument and always appends the data to + * the end of the file. + * @since v12.9.0 + */ +export function writev( + fd: number, + buffers: ReadonlyArray, + position?: number | null, + callback?: writeVCallback, +): void { const innerWritev = async (fd, buffers, position) => { - const chunks = []; + const chunks: Buffer[] = []; const offset = 0; for (let i = 0; i < buffers.length; i++) { if (Buffer.isBuffer(buffers[i])) { @@ -45,16 +83,24 @@ export function writev(fd, buffers, position, callback) { if (typeof position !== "number") position = null; innerWritev(fd, buffers, position).then( - (nwritten) => { - callback(null, nwritten, buffers); - }, + (nwritten) => callback(null, nwritten, buffers), (err) => callback(err), ); } -export function writevSync(fd, buffers, position) { +/** + * For detailed information, see the documentation of the asynchronous version of + * this API: {@link writev}. + * @since v12.9.0 + * @return The number of bytes written. + */ +export function writevSync( + fd: number, + buffers: ArrayBufferView[], + position?: number | null, +): number { const innerWritev = (fd, buffers, position) => { - const chunks = []; + const chunks: Buffer[] = []; const offset = 0; for (let i = 0; i < buffers.length; i++) { if (Buffer.isBuffer(buffers[i])) { @@ -85,3 +131,16 @@ export function writevSync(fd, buffers, position) { return innerWritev(fd, buffers, position); } + +export function writevPromise( + fd: number, + buffers: ArrayBufferView[], + position?: number, +): Promise { + return new Promise((resolve, reject) => { + writev(fd, buffers, position, (err, bytesWritten, buffers) => { + if (err) reject(err); + else resolve({ bytesWritten, buffers }); + }); + }); +} diff --git a/ext/node/polyfills/_global.d.ts b/ext/node/polyfills/_global.d.ts index 8587acbbb2..1e610de08d 100644 --- a/ext/node/polyfills/_global.d.ts +++ b/ext/node/polyfills/_global.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { EventEmitter } from "ext:deno_node/_events.d.ts"; import { Buffer } from "node:buffer"; diff --git a/ext/node/polyfills/_http_agent.mjs b/ext/node/polyfills/_http_agent.mjs index 53bb64ed53..4de58cb406 100644 --- a/ext/node/polyfills/_http_agent.mjs +++ b/ext/node/polyfills/_http_agent.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_http_common.ts b/ext/node/polyfills/_http_common.ts index 86143b4dea..f2ca75ec1c 100644 --- a/ext/node/polyfills/_http_common.ts +++ b/ext/node/polyfills/_http_common.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_http_outgoing.ts b/ext/node/polyfills/_http_outgoing.ts index 5a9a8ad7e6..fcc6394eb8 100644 --- a/ext/node/polyfills/_http_outgoing.ts +++ b/ext/node/polyfills/_http_outgoing.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_http_server.ts b/ext/node/polyfills/_http_server.ts index c2867de0c6..cf80f1e6e0 100644 --- a/ext/node/polyfills/_http_server.ts +++ b/ext/node/polyfills/_http_server.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export enum STATUS_CODES { /** RFC 7231, 6.2.1 */ diff --git a/ext/node/polyfills/_next_tick.ts b/ext/node/polyfills/_next_tick.ts index af306a29c8..355b6ca6ba 100644 --- a/ext/node/polyfills/_next_tick.ts +++ b/ext/node/polyfills/_next_tick.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_process/exiting.ts b/ext/node/polyfills/_process/exiting.ts index 01991e9c96..c1a032b643 100644 --- a/ext/node/polyfills/_process/exiting.ts +++ b/ext/node/polyfills/_process/exiting.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore prefer-const export let _exiting = false; diff --git a/ext/node/polyfills/_process/process.ts b/ext/node/polyfills/_process/process.ts index 6f69139c98..06e48160de 100644 --- a/ext/node/polyfills/_process/process.ts +++ b/ext/node/polyfills/_process/process.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // The following are all the process APIs that don't depend on the stream module diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs index 3573956c9d..9a838e7d38 100644 --- a/ext/node/polyfills/_process/streams.mjs +++ b/ext/node/polyfills/_process/streams.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_readline.d.ts b/ext/node/polyfills/_readline.d.ts index fcaa327a05..256cb87887 100644 --- a/ext/node/polyfills/_readline.d.ts +++ b/ext/node/polyfills/_readline.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any // Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/cd61f5b4d3d143108569ec3f88adc0eb34b961c4/types/node/readline.d.ts diff --git a/ext/node/polyfills/_readline.mjs b/ext/node/polyfills/_readline.mjs index 2af717152b..3943736ea9 100644 --- a/ext/node/polyfills/_readline.mjs +++ b/ext/node/polyfills/_readline.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/_readline_shared_types.d.ts b/ext/node/polyfills/_readline_shared_types.d.ts index b58ad035e7..f88de9091c 100644 --- a/ext/node/polyfills/_readline_shared_types.d.ts +++ b/ext/node/polyfills/_readline_shared_types.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Part of https://github.com/DefinitelyTyped/DefinitelyTyped/blob/cd61f5b4d3d143108569ec3f88adc0eb34b961c4/types/node/readline.d.ts diff --git a/ext/node/polyfills/_stream.d.ts b/ext/node/polyfills/_stream.d.ts index d251d9bee2..6ae3494118 100644 --- a/ext/node/polyfills/_stream.d.ts +++ b/ext/node/polyfills/_stream.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any // Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/4f538975138678878fed5b2555c0672aa578ab7d/types/node/stream.d.ts diff --git a/ext/node/polyfills/_stream.mjs b/ext/node/polyfills/_stream.mjs index 02640abcd9..3fc79b69df 100644 --- a/ext/node/polyfills/_stream.mjs +++ b/ext/node/polyfills/_stream.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-fmt-ignore-file // deno-lint-ignore-file diff --git a/ext/node/polyfills/_tls_common.ts b/ext/node/polyfills/_tls_common.ts index d00c3629e1..4e5904b971 100644 --- a/ext/node/polyfills/_tls_common.ts +++ b/ext/node/polyfills/_tls_common.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file no-explicit-any diff --git a/ext/node/polyfills/_tls_wrap.ts b/ext/node/polyfills/_tls_wrap.ts index 9e5def9f2b..dc7dac77ac 100644 --- a/ext/node/polyfills/_tls_wrap.ts +++ b/ext/node/polyfills/_tls_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills @@ -154,7 +154,7 @@ export class TLSSocket extends net.Socket { const afterConnect = handle.afterConnect; handle.afterConnect = async (req: any, status: number) => { options.hostname ??= undefined; // coerce to undefined if null, startTls expects hostname to be undefined - if (tlssock._isNpmAgent) { + if (tlssock._needsSockInitWorkaround) { // skips the TLS handshake for @npmcli/agent as it's handled by // onSocket handler of ClientRequest object. tlssock.emit("secure"); diff --git a/ext/node/polyfills/_util/_util_callbackify.js b/ext/node/polyfills/_util/_util_callbackify.js index cb30f79150..ae83850bc0 100644 --- a/ext/node/polyfills/_util/_util_callbackify.js +++ b/ext/node/polyfills/_util/_util_callbackify.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. // diff --git a/ext/node/polyfills/_util/asserts.ts b/ext/node/polyfills/_util/asserts.ts index 8f90cf168a..71c3cc863d 100644 --- a/ext/node/polyfills/_util/asserts.ts +++ b/ext/node/polyfills/_util/asserts.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/_util/async.ts b/ext/node/polyfills/_util/async.ts index 0cacccacc7..445febcaaf 100644 --- a/ext/node/polyfills/_util/async.ts +++ b/ext/node/polyfills/_util/async.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is vendored from std/async/delay.ts // (with some modifications) diff --git a/ext/node/polyfills/_util/os.ts b/ext/node/polyfills/_util/os.ts index 421d5d9da0..f72dc662d9 100644 --- a/ext/node/polyfills/_util/os.ts +++ b/ext/node/polyfills/_util/os.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_build_os } from "ext:core/ops"; diff --git a/ext/node/polyfills/_util/std_asserts.ts b/ext/node/polyfills/_util/std_asserts.ts index 78b749f549..7623741379 100644 --- a/ext/node/polyfills/_util/std_asserts.ts +++ b/ext/node/polyfills/_util/std_asserts.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // vendored from std/assert/mod.ts import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_util/std_fmt_colors.ts b/ext/node/polyfills/_util/std_fmt_colors.ts index 5072b298ee..f1dc97f6ca 100644 --- a/ext/node/polyfills/_util/std_fmt_colors.ts +++ b/ext/node/polyfills/_util/std_fmt_colors.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file is vendored from std/fmt/colors.ts import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_util/std_testing_diff.ts b/ext/node/polyfills/_util/std_testing_diff.ts index 5155fd242c..1b680c6cca 100644 --- a/ext/node/polyfills/_util/std_testing_diff.ts +++ b/ext/node/polyfills/_util/std_testing_diff.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file was vendored from std/testing/_diff.ts import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_utils.ts b/ext/node/polyfills/_utils.ts index 79d84e00f0..7f20782e2b 100644 --- a/ext/node/polyfills/_utils.ts +++ b/ext/node/polyfills/_utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/_zlib.mjs b/ext/node/polyfills/_zlib.mjs index 07fc440ef5..bb6bac1bd0 100644 --- a/ext/node/polyfills/_zlib.mjs +++ b/ext/node/polyfills/_zlib.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright (c) 2014-2015 Devon Govett // Forked from https://github.com/browserify/browserify-zlib diff --git a/ext/node/polyfills/_zlib_binding.mjs b/ext/node/polyfills/_zlib_binding.mjs index 069b7f8351..37125b88ac 100644 --- a/ext/node/polyfills/_zlib_binding.mjs +++ b/ext/node/polyfills/_zlib_binding.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/assert.ts b/ext/node/polyfills/assert.ts index 188c7a0c2f..48b4627044 100644 --- a/ext/node/polyfills/assert.ts +++ b/ext/node/polyfills/assert.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file ban-types prefer-primordials diff --git a/ext/node/polyfills/assert/strict.ts b/ext/node/polyfills/assert/strict.ts index 9837167627..883deaf087 100644 --- a/ext/node/polyfills/assert/strict.ts +++ b/ext/node/polyfills/assert/strict.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { strict } from "node:assert"; export { diff --git a/ext/node/polyfills/assertion_error.ts b/ext/node/polyfills/assertion_error.ts index ff1168dc30..b56cec2c1d 100644 --- a/ext/node/polyfills/assertion_error.ts +++ b/ext/node/polyfills/assertion_error.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. diff --git a/ext/node/polyfills/async_hooks.ts b/ext/node/polyfills/async_hooks.ts index 7a2f153dac..e01110bd3b 100644 --- a/ext/node/polyfills/async_hooks.ts +++ b/ext/node/polyfills/async_hooks.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/buffer.ts b/ext/node/polyfills/buffer.ts index efe3b07a97..38000c6edb 100644 --- a/ext/node/polyfills/buffer.ts +++ b/ext/node/polyfills/buffer.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @deno-types="./internal/buffer.d.ts" export { atob, diff --git a/ext/node/polyfills/child_process.ts b/ext/node/polyfills/child_process.ts index eda718ff34..184b29bd2b 100644 --- a/ext/node/polyfills/child_process.ts +++ b/ext/node/polyfills/child_process.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module implements 'child_process' module of Node.JS API. // ref: https://nodejs.org/api/child_process.html diff --git a/ext/node/polyfills/cluster.ts b/ext/node/polyfills/cluster.ts index 8abc2fedcd..67534676b4 100644 --- a/ext/node/polyfills/cluster.ts +++ b/ext/node/polyfills/cluster.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; diff --git a/ext/node/polyfills/console.ts b/ext/node/polyfills/console.ts index f74c9af165..1189d95064 100644 --- a/ext/node/polyfills/console.ts +++ b/ext/node/polyfills/console.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { Console } from "ext:deno_node/internal/console/constructor.mjs"; diff --git a/ext/node/polyfills/constants.ts b/ext/node/polyfills/constants.ts index e5004039b1..421c8b641a 100644 --- a/ext/node/polyfills/constants.ts +++ b/ext/node/polyfills/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Based on: https://github.com/nodejs/node/blob/0646eda/lib/constants.js diff --git a/ext/node/polyfills/crypto.ts b/ext/node/polyfills/crypto.ts index 908d21b006..ff3bdc7752 100644 --- a/ext/node/polyfills/crypto.ts +++ b/ext/node/polyfills/crypto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/dgram.ts b/ext/node/polyfills/dgram.ts index 99f4940ec2..56fb8d8f32 100644 --- a/ext/node/polyfills/dgram.ts +++ b/ext/node/polyfills/dgram.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/diagnostics_channel.js b/ext/node/polyfills/diagnostics_channel.js index 807c33e475..65533e0803 100644 --- a/ext/node/polyfills/diagnostics_channel.js +++ b/ext/node/polyfills/diagnostics_channel.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/dns.ts b/ext/node/polyfills/dns.ts index 78b934e602..b8e4af6eec 100644 --- a/ext/node/polyfills/dns.ts +++ b/ext/node/polyfills/dns.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/dns/promises.ts b/ext/node/polyfills/dns/promises.ts index 6bc539cba9..422ae05c64 100644 --- a/ext/node/polyfills/dns/promises.ts +++ b/ext/node/polyfills/dns/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/domain.ts b/ext/node/polyfills/domain.ts index 7093779966..daa613ede0 100644 --- a/ext/node/polyfills/domain.ts +++ b/ext/node/polyfills/domain.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // This code has been inspired by https://github.com/bevry/domain-browser/commit/8bce7f4a093966ca850da75b024239ad5d0b33c6 diff --git a/ext/node/polyfills/events.ts b/ext/node/polyfills/events.ts index 78f3d8768c..891f3d3b8d 100644 --- a/ext/node/polyfills/events.ts +++ b/ext/node/polyfills/events.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @deno-types="./_events.d.ts" export { addAbortListener, diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts index cbdc36afe5..a6e43b5baa 100644 --- a/ext/node/polyfills/fs.ts +++ b/ext/node/polyfills/fs.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { access, accessPromise, @@ -119,7 +119,7 @@ import { // @deno-types="./_fs/_fs_write.d.ts" import { write, writeSync } from "ext:deno_node/_fs/_fs_write.mjs"; // @deno-types="./_fs/_fs_writev.d.ts" -import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.mjs"; +import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.ts"; import { readv, readvSync } from "ext:deno_node/_fs/_fs_readv.ts"; import { writeFile, diff --git a/ext/node/polyfills/fs/promises.ts b/ext/node/polyfills/fs/promises.ts index a5125dac8d..f8d63e94e0 100644 --- a/ext/node/polyfills/fs/promises.ts +++ b/ext/node/polyfills/fs/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { promises as fsPromises } from "node:fs"; export const access = fsPromises.access; diff --git a/ext/node/polyfills/http.ts b/ext/node/polyfills/http.ts index e911535be5..f698ca01b3 100644 --- a/ext/node/polyfills/http.ts +++ b/ext/node/polyfills/http.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/http2.ts b/ext/node/polyfills/http2.ts index 1b3f74f6f6..ab2faa2d08 100644 --- a/ext/node/polyfills/http2.ts +++ b/ext/node/polyfills/http2.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/https.ts b/ext/node/polyfills/https.ts index fd700173eb..d958d8e746 100644 --- a/ext/node/polyfills/https.ts +++ b/ext/node/polyfills/https.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/inspector.js b/ext/node/polyfills/inspector.js index 7eb15ce917..bb2661b9cd 100644 --- a/ext/node/polyfills/inspector.js +++ b/ext/node/polyfills/inspector.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import process from "node:process"; diff --git a/ext/node/polyfills/inspector/promises.js b/ext/node/polyfills/inspector/promises.js index 3483e53f5e..a075eba001 100644 --- a/ext/node/polyfills/inspector/promises.js +++ b/ext/node/polyfills/inspector/promises.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import inspector from "node:inspector"; diff --git a/ext/node/polyfills/internal/assert.mjs b/ext/node/polyfills/internal/assert.mjs index 20ed511e02..01b5fad460 100644 --- a/ext/node/polyfills/internal/assert.mjs +++ b/ext/node/polyfills/internal/assert.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { ERR_INTERNAL_ASSERTION } from "ext:deno_node/internal/errors.ts"; function assert(value, message) { diff --git a/ext/node/polyfills/internal/async_hooks.ts b/ext/node/polyfills/internal/async_hooks.ts index a5d6728e33..a2f52e6979 100644 --- a/ext/node/polyfills/internal/async_hooks.ts +++ b/ext/node/polyfills/internal/async_hooks.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/blocklist.mjs b/ext/node/polyfills/internal/blocklist.mjs index 8a7c9c376a..d45592a8ff 100644 --- a/ext/node/polyfills/internal/blocklist.mjs +++ b/ext/node/polyfills/internal/blocklist.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/buffer.d.ts b/ext/node/polyfills/internal/buffer.d.ts index 2008dc2432..125deb0ef2 100644 --- a/ext/node/polyfills/internal/buffer.d.ts +++ b/ext/node/polyfills/internal/buffer.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright DefinitelyTyped contributors. All rights reserved. MIT license. /** diff --git a/ext/node/polyfills/internal/buffer.mjs b/ext/node/polyfills/internal/buffer.mjs index dd549221fa..033d8a1e1e 100644 --- a/ext/node/polyfills/internal/buffer.mjs +++ b/ext/node/polyfills/internal/buffer.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // Copyright Feross Aboukhadijeh, and other contributors. All rights reserved. MIT license. diff --git a/ext/node/polyfills/internal/child_process.ts b/ext/node/polyfills/internal/child_process.ts index cfff1079ff..569ee7d328 100644 --- a/ext/node/polyfills/internal/child_process.ts +++ b/ext/node/polyfills/internal/child_process.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module implements 'child_process' module of Node.JS API. // ref: https://nodejs.org/api/child_process.html @@ -277,6 +277,7 @@ export class ChildProcess extends EventEmitter { try { this.#process = new Deno.Command(cmd, { args: cmdArgs, + clearEnv: true, cwd, env: stringEnv, stdin: toDenoStdio(stdin), @@ -839,6 +840,7 @@ export function normalizeSpawnArguments( args, cwd, detached: !!options.detached, + env, envPairs, file, windowsHide: !!options.windowsHide, diff --git a/ext/node/polyfills/internal/cli_table.ts b/ext/node/polyfills/internal/cli_table.ts index 9826e524f6..c6991ed06d 100644 --- a/ext/node/polyfills/internal/cli_table.ts +++ b/ext/node/polyfills/internal/cli_table.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/console/constructor.mjs b/ext/node/polyfills/internal/console/constructor.mjs index ebf5cbec4f..0736fd4c47 100644 --- a/ext/node/polyfills/internal/console/constructor.mjs +++ b/ext/node/polyfills/internal/console/constructor.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/constants.ts b/ext/node/polyfills/internal/constants.ts index 521cea987c..3c83c0f0ef 100644 --- a/ext/node/polyfills/internal/constants.ts +++ b/ext/node/polyfills/internal/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { isWindows } from "ext:deno_node/_util/os.ts"; diff --git a/ext/node/polyfills/internal/crypto/_keys.ts b/ext/node/polyfills/internal/crypto/_keys.ts index e799862458..9a097f82e3 100644 --- a/ext/node/polyfills/internal/crypto/_keys.ts +++ b/ext/node/polyfills/internal/crypto/_keys.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file is here because to break a circular dependency between streams and // crypto. diff --git a/ext/node/polyfills/internal/crypto/_randomBytes.ts b/ext/node/polyfills/internal/crypto/_randomBytes.ts index e1dc5c5ac8..c8608a7c53 100644 --- a/ext/node/polyfills/internal/crypto/_randomBytes.ts +++ b/ext/node/polyfills/internal/crypto/_randomBytes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/crypto/_randomFill.mjs b/ext/node/polyfills/internal/crypto/_randomFill.mjs index 8ef864562f..20a6e1c022 100644 --- a/ext/node/polyfills/internal/crypto/_randomFill.mjs +++ b/ext/node/polyfills/internal/crypto/_randomFill.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/crypto/_randomInt.ts b/ext/node/polyfills/internal/crypto/_randomInt.ts index e08b3e9639..65a8bacf13 100644 --- a/ext/node/polyfills/internal/crypto/_randomInt.ts +++ b/ext/node/polyfills/internal/crypto/_randomInt.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_random_int } from "ext:core/ops"; import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/crypto/certificate.ts b/ext/node/polyfills/internal/crypto/certificate.ts index c15236c5cb..cd64ba36d9 100644 --- a/ext/node/polyfills/internal/crypto/certificate.ts +++ b/ext/node/polyfills/internal/crypto/certificate.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; diff --git a/ext/node/polyfills/internal/crypto/cipher.ts b/ext/node/polyfills/internal/crypto/cipher.ts index c1c5ce8901..dd1698f46e 100644 --- a/ext/node/polyfills/internal/crypto/cipher.ts +++ b/ext/node/polyfills/internal/crypto/cipher.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/constants.ts b/ext/node/polyfills/internal/crypto/constants.ts index d9c0b2d63b..77fe9bbf43 100644 --- a/ext/node/polyfills/internal/crypto/constants.ts +++ b/ext/node/polyfills/internal/crypto/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/diffiehellman.ts b/ext/node/polyfills/internal/crypto/diffiehellman.ts index a439306a97..fb0da4e60b 100644 --- a/ext/node/polyfills/internal/crypto/diffiehellman.ts +++ b/ext/node/polyfills/internal/crypto/diffiehellman.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/hash.ts b/ext/node/polyfills/internal/crypto/hash.ts index c42ca39892..3b3ec928fb 100644 --- a/ext/node/polyfills/internal/crypto/hash.ts +++ b/ext/node/polyfills/internal/crypto/hash.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/hkdf.ts b/ext/node/polyfills/internal/crypto/hkdf.ts index cb1dbee46b..282633ba26 100644 --- a/ext/node/polyfills/internal/crypto/hkdf.ts +++ b/ext/node/polyfills/internal/crypto/hkdf.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index b023ab1060..8d9a98e324 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/keys.ts b/ext/node/polyfills/internal/crypto/keys.ts index c91c23cc3d..a6ef6b156d 100644 --- a/ext/node/polyfills/internal/crypto/keys.ts +++ b/ext/node/polyfills/internal/crypto/keys.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills @@ -20,6 +20,7 @@ import { op_node_create_secret_key, op_node_derive_public_key_from_private_key, op_node_export_private_key_der, + op_node_export_private_key_jwk, op_node_export_private_key_pem, op_node_export_public_key_der, op_node_export_public_key_jwk, @@ -791,7 +792,7 @@ export class PrivateKeyObject extends AsymmetricKeyObject { export(options: JwkKeyExportOptions | KeyExportOptions) { if (options && options.format === "jwk") { - notImplemented("jwk private key export not implemented"); + return op_node_export_private_key_jwk(this[kHandle]); } const { format, diff --git a/ext/node/polyfills/internal/crypto/pbkdf2.ts b/ext/node/polyfills/internal/crypto/pbkdf2.ts index 4e58cb68b3..84a3032c75 100644 --- a/ext/node/polyfills/internal/crypto/pbkdf2.ts +++ b/ext/node/polyfills/internal/crypto/pbkdf2.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/crypto/random.ts b/ext/node/polyfills/internal/crypto/random.ts index a41b868190..9d8864aabc 100644 --- a/ext/node/polyfills/internal/crypto/random.ts +++ b/ext/node/polyfills/internal/crypto/random.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/scrypt.ts b/ext/node/polyfills/internal/crypto/scrypt.ts index ce8649bbe2..e20ad33011 100644 --- a/ext/node/polyfills/internal/crypto/scrypt.ts +++ b/ext/node/polyfills/internal/crypto/scrypt.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /* MIT License diff --git a/ext/node/polyfills/internal/crypto/sig.ts b/ext/node/polyfills/internal/crypto/sig.ts index a05f16478d..d75f522f37 100644 --- a/ext/node/polyfills/internal/crypto/sig.ts +++ b/ext/node/polyfills/internal/crypto/sig.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/types.ts b/ext/node/polyfills/internal/crypto/types.ts index 17b15127ef..60f913ca2d 100644 --- a/ext/node/polyfills/internal/crypto/types.ts +++ b/ext/node/polyfills/internal/crypto/types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { BufferEncoding } from "ext:deno_node/_global.d.ts"; diff --git a/ext/node/polyfills/internal/crypto/util.ts b/ext/node/polyfills/internal/crypto/util.ts index a39b031ee3..7e6f4a9b40 100644 --- a/ext/node/polyfills/internal/crypto/util.ts +++ b/ext/node/polyfills/internal/crypto/util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills @@ -67,22 +67,16 @@ export const ellipticCurves: Array = [ }, // NIST P-224 EC ]; -// deno-fmt-ignore const supportedCiphers = [ - "aes-128-ecb", "aes-192-ecb", - "aes-256-ecb", "aes-128-cbc", - "aes-192-cbc", "aes-256-cbc", - "aes128", "aes192", - "aes256", "aes-128-cfb", - "aes-192-cfb", "aes-256-cfb", - "aes-128-cfb8", "aes-192-cfb8", - "aes-256-cfb8", "aes-128-cfb1", - "aes-192-cfb1", "aes-256-cfb1", - "aes-128-ofb", "aes-192-ofb", - "aes-256-ofb", "aes-128-ctr", - "aes-192-ctr", "aes-256-ctr", - "aes-128-gcm", "aes-192-gcm", - "aes-256-gcm" + "aes-128-ecb", + "aes-192-ecb", + "aes-256-ecb", + "aes-128-cbc", + "aes-256-cbc", + "aes128", + "aes256", + "aes-128-gcm", + "aes-256-gcm", ]; export function getCiphers(): string[] { diff --git a/ext/node/polyfills/internal/crypto/x509.ts b/ext/node/polyfills/internal/crypto/x509.ts index 699e45a51b..a37db7365e 100644 --- a/ext/node/polyfills/internal/crypto/x509.ts +++ b/ext/node/polyfills/internal/crypto/x509.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/dgram.ts b/ext/node/polyfills/internal/dgram.ts index e34f92f1ea..d69baa7ef2 100644 --- a/ext/node/polyfills/internal/dgram.ts +++ b/ext/node/polyfills/internal/dgram.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/dns/promises.ts b/ext/node/polyfills/internal/dns/promises.ts index ee4248163d..b431c4502f 100644 --- a/ext/node/polyfills/internal/dns/promises.ts +++ b/ext/node/polyfills/internal/dns/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/dns/utils.ts b/ext/node/polyfills/internal/dns/utils.ts index 226fce93dd..a405d3f509 100644 --- a/ext/node/polyfills/internal/dns/utils.ts +++ b/ext/node/polyfills/internal/dns/utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/dtrace.ts b/ext/node/polyfills/internal/dtrace.ts index 3443aec7ff..716c48c7cc 100644 --- a/ext/node/polyfills/internal/dtrace.ts +++ b/ext/node/polyfills/internal/dtrace.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/error_codes.ts b/ext/node/polyfills/internal/error_codes.ts index 0603e9e114..638c0584c2 100644 --- a/ext/node/polyfills/internal/error_codes.ts +++ b/ext/node/polyfills/internal/error_codes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Lazily initializes the error classes in this object. // This trick is necessary for avoiding circular dendencies between diff --git a/ext/node/polyfills/internal/errors.ts b/ext/node/polyfills/internal/errors.ts index 61b53fa968..5d35f07dd8 100644 --- a/ext/node/polyfills/internal/errors.ts +++ b/ext/node/polyfills/internal/errors.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Node.js contributors. All rights reserved. MIT License. // TODO(petamoriken): enable prefer-primordials for node polyfills @@ -624,6 +624,15 @@ function createInvalidArgType( return msg; } +export class ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH extends NodeRangeError { + constructor() { + super( + "ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH", + "Input buffers must have the same length", + ); + } +} + export class ERR_INVALID_ARG_TYPE_RANGE extends NodeRangeError { constructor(name: string, expected: string | string[], actual: unknown) { const msg = createInvalidArgType(name, expected); @@ -2842,6 +2851,7 @@ export default { ERR_INVALID_ADDRESS_FAMILY, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_TYPE_RANGE, + ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH, ERR_INVALID_ARG_VALUE, ERR_INVALID_ARG_VALUE_RANGE, ERR_INVALID_ASYNC_ID, diff --git a/ext/node/polyfills/internal/event_target.mjs b/ext/node/polyfills/internal/event_target.mjs index 4409b00ed4..80c6041c57 100644 --- a/ext/node/polyfills/internal/event_target.mjs +++ b/ext/node/polyfills/internal/event_target.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Node.js contributors. All rights reserved. MIT License. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/events/abort_listener.mjs b/ext/node/polyfills/internal/events/abort_listener.mjs index f1430489fa..0407b93595 100644 --- a/ext/node/polyfills/internal/events/abort_listener.mjs +++ b/ext/node/polyfills/internal/events/abort_listener.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { primordials } from "ext:deno_node/internal/test/binding.ts"; diff --git a/ext/node/polyfills/internal/fixed_queue.ts b/ext/node/polyfills/internal/fixed_queue.ts index 0a2209c8d5..d9355e73c5 100644 --- a/ext/node/polyfills/internal/fixed_queue.ts +++ b/ext/node/polyfills/internal/fixed_queue.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/fs/handle.ts b/ext/node/polyfills/internal/fs/handle.ts index 9ec0fc97e2..774b36dfc2 100644 --- a/ext/node/polyfills/internal/fs/handle.ts +++ b/ext/node/polyfills/internal/fs/handle.ts @@ -1,19 +1,21 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials import { EventEmitter } from "node:events"; import { Buffer } from "node:buffer"; -import { promises, read, write } from "node:fs"; -export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts"; +import { Mode, promises, read, write } from "node:fs"; +import { core } from "ext:core/mod.js"; import { BinaryOptionsArgument, FileOptionsArgument, ReadOptions, TextOptionsArgument, } from "ext:deno_node/_fs/_fs_common.ts"; -import { core } from "ext:core/mod.js"; +import { ftruncatePromise } from "ext:deno_node/_fs/_fs_ftruncate.ts"; +export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts"; +import { writevPromise, WriteVResult } from "ext:deno_node/_fs/_fs_writev.ts"; interface WriteResult { bytesWritten: number; @@ -25,11 +27,15 @@ interface ReadResult { buffer: Buffer; } +type Path = string | Buffer | URL; export class FileHandle extends EventEmitter { #rid: number; - constructor(rid: number) { + #path: Path; + + constructor(rid: number, path: Path) { super(); this.#rid = rid; + this.#path = path; } get fd() { @@ -59,7 +65,7 @@ export class FileHandle extends EventEmitter { position, (err, bytesRead, buffer) => { if (err) reject(err); - else resolve({ buffer: buffer, bytesRead: bytesRead }); + else resolve({ buffer, bytesRead }); }, ); }); @@ -67,12 +73,16 @@ export class FileHandle extends EventEmitter { return new Promise((resolve, reject) => { read(this.fd, bufferOrOpt, (err, bytesRead, buffer) => { if (err) reject(err); - else resolve({ buffer: buffer, bytesRead: bytesRead }); + else resolve({ buffer, bytesRead }); }); }); } } + truncate(len?: number): Promise { + return fsCall(ftruncatePromise, this, len); + } + readFile( opt?: TextOptionsArgument | BinaryOptionsArgument | FileOptionsArgument, ): Promise { @@ -85,11 +95,7 @@ export class FileHandle extends EventEmitter { length: number, position: number, ): Promise; - write( - str: string, - position: number, - encoding: string, - ): Promise; + write(str: string, position: number, encoding: string): Promise; write( bufferOrStr: Uint8Array | string, offsetOrPosition: number, @@ -120,16 +126,10 @@ export class FileHandle extends EventEmitter { const encoding = lengthOrEncoding; return new Promise((resolve, reject) => { - write( - this.fd, - str, - position, - encoding, - (err, bytesWritten, buffer) => { - if (err) reject(err); - else resolve({ buffer, bytesWritten }); - }, - ); + write(this.fd, str, position, encoding, (err, bytesWritten, buffer) => { + if (err) reject(err); + else resolve({ buffer, bytesWritten }); + }); }); } } @@ -138,6 +138,10 @@ export class FileHandle extends EventEmitter { return fsCall(promises.writeFile, this, data, options); } + writev(buffers: ArrayBufferView[], position?: number): Promise { + return fsCall(writevPromise, this, buffers, position); + } + close(): Promise { // Note that Deno.close is not async return Promise.resolve(core.close(this.fd)); @@ -149,17 +153,32 @@ export class FileHandle extends EventEmitter { stat(options?: { bigint: boolean }): Promise { return fsCall(promises.fstat, this, options); } + chmod(mode: Mode): Promise { + assertNotClosed(this, promises.chmod.name); + return promises.chmod(this.#path, mode); + } + + utimes( + atime: number | string | Date, + mtime: number | string | Date, + ): Promise { + assertNotClosed(this, promises.utimes.name); + return promises.utimes(this.#path, atime, mtime); + } } -function fsCall(fn, handle, ...args) { +function assertNotClosed(handle: FileHandle, syscall: string) { if (handle.fd === -1) { const err = new Error("file closed"); throw Object.assign(err, { code: "EBADF", - syscall: fn.name, + syscall, }); } +} +function fsCall(fn, handle, ...args) { + assertNotClosed(handle, fn.name); return fn(handle.fd, ...args); } diff --git a/ext/node/polyfills/internal/fs/streams.d.ts b/ext/node/polyfills/internal/fs/streams.d.ts index b4c235f9b5..8459120573 100644 --- a/ext/node/polyfills/internal/fs/streams.d.ts +++ b/ext/node/polyfills/internal/fs/streams.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright DefinitelyTyped contributors. All rights reserved. MIT license. // deno-lint-ignore-file no-explicit-any diff --git a/ext/node/polyfills/internal/fs/streams.mjs b/ext/node/polyfills/internal/fs/streams.mjs index 3b4409c4cf..d2ebecbe9a 100644 --- a/ext/node/polyfills/internal/fs/streams.mjs +++ b/ext/node/polyfills/internal/fs/streams.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills @@ -18,7 +18,7 @@ import { errorOrDestroy } from "ext:deno_node/internal/streams/destroy.mjs"; import { open as fsOpen } from "ext:deno_node/_fs/_fs_open.ts"; import { read as fsRead } from "ext:deno_node/_fs/_fs_read.ts"; import { write as fsWrite } from "ext:deno_node/_fs/_fs_write.mjs"; -import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.mjs"; +import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.ts"; import { close as fsClose } from "ext:deno_node/_fs/_fs_close.ts"; import { Buffer } from "node:buffer"; import { diff --git a/ext/node/polyfills/internal/fs/utils.mjs b/ext/node/polyfills/internal/fs/utils.mjs index b0ef34873e..9e8558dea2 100644 --- a/ext/node/polyfills/internal/fs/utils.mjs +++ b/ext/node/polyfills/internal/fs/utils.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/hide_stack_frames.ts b/ext/node/polyfills/internal/hide_stack_frames.ts index 4145189fff..93d7aca095 100644 --- a/ext/node/polyfills/internal/hide_stack_frames.ts +++ b/ext/node/polyfills/internal/hide_stack_frames.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/http.ts b/ext/node/polyfills/internal/http.ts index ddf082f451..d559442bbc 100644 --- a/ext/node/polyfills/internal/http.ts +++ b/ext/node/polyfills/internal/http.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/idna.ts b/ext/node/polyfills/internal/idna.ts index 93ed065cce..8a53b7e721 100644 --- a/ext/node/polyfills/internal/idna.ts +++ b/ext/node/polyfills/internal/idna.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/net.ts b/ext/node/polyfills/internal/net.ts index a3dcb3ed21..a14dcae9b8 100644 --- a/ext/node/polyfills/internal/net.ts +++ b/ext/node/polyfills/internal/net.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/normalize_encoding.mjs b/ext/node/polyfills/internal/normalize_encoding.mjs index a367337880..6ae322f118 100644 --- a/ext/node/polyfills/internal/normalize_encoding.mjs +++ b/ext/node/polyfills/internal/normalize_encoding.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/options.ts b/ext/node/polyfills/internal/options.ts index 4b62a11285..16ac62a69f 100644 --- a/ext/node/polyfills/internal/options.ts +++ b/ext/node/polyfills/internal/options.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/primordials.mjs b/ext/node/polyfills/internal/primordials.mjs index cddd1aa66d..027994a3d2 100644 --- a/ext/node/polyfills/internal/primordials.mjs +++ b/ext/node/polyfills/internal/primordials.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/process/per_thread.mjs b/ext/node/polyfills/internal/process/per_thread.mjs index b4db3f3691..9d784be153 100644 --- a/ext/node/polyfills/internal/process/per_thread.mjs +++ b/ext/node/polyfills/internal/process/per_thread.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/process/report.ts b/ext/node/polyfills/internal/process/report.ts index c4a1ff5616..5374c449fc 100644 --- a/ext/node/polyfills/internal/process/report.ts +++ b/ext/node/polyfills/internal/process/report.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/internal/querystring.ts b/ext/node/polyfills/internal/querystring.ts index a34e4e4b76..1824203c60 100644 --- a/ext/node/polyfills/internal/querystring.ts +++ b/ext/node/polyfills/internal/querystring.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/readline/callbacks.mjs b/ext/node/polyfills/internal/readline/callbacks.mjs index 5e3d7382fc..4f860776e0 100644 --- a/ext/node/polyfills/internal/readline/callbacks.mjs +++ b/ext/node/polyfills/internal/readline/callbacks.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs b/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs index e016fd1533..27a010b420 100644 --- a/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs +++ b/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/readline/interface.mjs b/ext/node/polyfills/internal/readline/interface.mjs index bdb832f881..9988f6c46b 100644 --- a/ext/node/polyfills/internal/readline/interface.mjs +++ b/ext/node/polyfills/internal/readline/interface.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/readline/promises.mjs b/ext/node/polyfills/internal/readline/promises.mjs index f1de6a2d31..21ab8b6d93 100644 --- a/ext/node/polyfills/internal/readline/promises.mjs +++ b/ext/node/polyfills/internal/readline/promises.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/readline/symbols.mjs b/ext/node/polyfills/internal/readline/symbols.mjs index 299cc8c9a7..7b935d1243 100644 --- a/ext/node/polyfills/internal/readline/symbols.mjs +++ b/ext/node/polyfills/internal/readline/symbols.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/readline/utils.mjs b/ext/node/polyfills/internal/readline/utils.mjs index b9372ab0ea..495c373638 100644 --- a/ext/node/polyfills/internal/readline/utils.mjs +++ b/ext/node/polyfills/internal/readline/utils.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/stream_base_commons.ts b/ext/node/polyfills/internal/stream_base_commons.ts index 1fabb73f47..050658d4f5 100644 --- a/ext/node/polyfills/internal/stream_base_commons.ts +++ b/ext/node/polyfills/internal/stream_base_commons.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/streams/add-abort-signal.mjs b/ext/node/polyfills/internal/streams/add-abort-signal.mjs index 2e66c86643..d6a3ca099e 100644 --- a/ext/node/polyfills/internal/streams/add-abort-signal.mjs +++ b/ext/node/polyfills/internal/streams/add-abort-signal.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/buffer_list.mjs b/ext/node/polyfills/internal/streams/buffer_list.mjs index db3a703388..cb9dba563b 100644 --- a/ext/node/polyfills/internal/streams/buffer_list.mjs +++ b/ext/node/polyfills/internal/streams/buffer_list.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/destroy.mjs b/ext/node/polyfills/internal/streams/destroy.mjs index 27420e78bf..6e60e20ace 100644 --- a/ext/node/polyfills/internal/streams/destroy.mjs +++ b/ext/node/polyfills/internal/streams/destroy.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/duplex.mjs b/ext/node/polyfills/internal/streams/duplex.mjs index b7c0b077ff..0424c3d17b 100644 --- a/ext/node/polyfills/internal/streams/duplex.mjs +++ b/ext/node/polyfills/internal/streams/duplex.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/end-of-stream.mjs b/ext/node/polyfills/internal/streams/end-of-stream.mjs index aebdb90bf3..be9bf0a50c 100644 --- a/ext/node/polyfills/internal/streams/end-of-stream.mjs +++ b/ext/node/polyfills/internal/streams/end-of-stream.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/lazy_transform.mjs b/ext/node/polyfills/internal/streams/lazy_transform.mjs index 3033ee6d4c..f5cf8ac48a 100644 --- a/ext/node/polyfills/internal/streams/lazy_transform.mjs +++ b/ext/node/polyfills/internal/streams/lazy_transform.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/passthrough.mjs b/ext/node/polyfills/internal/streams/passthrough.mjs index 8c7d4f3560..aa3f26a52c 100644 --- a/ext/node/polyfills/internal/streams/passthrough.mjs +++ b/ext/node/polyfills/internal/streams/passthrough.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/readable.mjs b/ext/node/polyfills/internal/streams/readable.mjs index c5411d6451..7c291f3932 100644 --- a/ext/node/polyfills/internal/streams/readable.mjs +++ b/ext/node/polyfills/internal/streams/readable.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/state.mjs b/ext/node/polyfills/internal/streams/state.mjs index 428492306b..599620ce7a 100644 --- a/ext/node/polyfills/internal/streams/state.mjs +++ b/ext/node/polyfills/internal/streams/state.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/transform.mjs b/ext/node/polyfills/internal/streams/transform.mjs index af368365cb..ac2bf6acc9 100644 --- a/ext/node/polyfills/internal/streams/transform.mjs +++ b/ext/node/polyfills/internal/streams/transform.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/utils.mjs b/ext/node/polyfills/internal/streams/utils.mjs index a2506bd5de..65551b129d 100644 --- a/ext/node/polyfills/internal/streams/utils.mjs +++ b/ext/node/polyfills/internal/streams/utils.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/writable.mjs b/ext/node/polyfills/internal/streams/writable.mjs index c02e4e0e1d..7f1679e372 100644 --- a/ext/node/polyfills/internal/streams/writable.mjs +++ b/ext/node/polyfills/internal/streams/writable.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/test/binding.ts b/ext/node/polyfills/internal/test/binding.ts index cce56b922c..bbd78ec471 100644 --- a/ext/node/polyfills/internal/test/binding.ts +++ b/ext/node/polyfills/internal/test/binding.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { getBinding } from "ext:deno_node/internal_binding/mod.ts"; import type { BindingName } from "ext:deno_node/internal_binding/mod.ts"; diff --git a/ext/node/polyfills/internal/timers.mjs b/ext/node/polyfills/internal/timers.mjs index 363f55f693..84dd165e26 100644 --- a/ext/node/polyfills/internal/timers.mjs +++ b/ext/node/polyfills/internal/timers.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/url.ts b/ext/node/polyfills/internal/url.ts index 4b9da49ec0..82d7a8b4bf 100644 --- a/ext/node/polyfills/internal/url.ts +++ b/ext/node/polyfills/internal/url.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/util.mjs b/ext/node/polyfills/internal/util.mjs index a603a975ad..91de868920 100644 --- a/ext/node/polyfills/internal/util.mjs +++ b/ext/node/polyfills/internal/util.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/util/comparisons.ts b/ext/node/polyfills/internal/util/comparisons.ts index 39e30c69aa..44f0bef007 100644 --- a/ext/node/polyfills/internal/util/comparisons.ts +++ b/ext/node/polyfills/internal/util/comparisons.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/util/debuglog.ts b/ext/node/polyfills/internal/util/debuglog.ts index bbaba7b6fc..dc05aff825 100644 --- a/ext/node/polyfills/internal/util/debuglog.ts +++ b/ext/node/polyfills/internal/util/debuglog.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/util/inspect.mjs b/ext/node/polyfills/internal/util/inspect.mjs index ae797449bf..0587059122 100644 --- a/ext/node/polyfills/internal/util/inspect.mjs +++ b/ext/node/polyfills/internal/util/inspect.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/util/parse_args/parse_args.js b/ext/node/polyfills/internal/util/parse_args/parse_args.js index 742f2a1f0d..c4a60bcd35 100644 --- a/ext/node/polyfills/internal/util/parse_args/parse_args.js +++ b/ext/node/polyfills/internal/util/parse_args/parse_args.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/util/parse_args/utils.js b/ext/node/polyfills/internal/util/parse_args/utils.js index 1ceed0b9e8..dd76fa1512 100644 --- a/ext/node/polyfills/internal/util/parse_args/utils.js +++ b/ext/node/polyfills/internal/util/parse_args/utils.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/util/types.ts b/ext/node/polyfills/internal/util/types.ts index b8aca59680..9415c9db5f 100644 --- a/ext/node/polyfills/internal/util/types.ts +++ b/ext/node/polyfills/internal/util/types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. // diff --git a/ext/node/polyfills/internal/validators.mjs b/ext/node/polyfills/internal/validators.mjs index 12962b8432..0529dca546 100644 --- a/ext/node/polyfills/internal/validators.mjs +++ b/ext/node/polyfills/internal/validators.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal_binding/_libuv_winerror.ts b/ext/node/polyfills/internal_binding/_libuv_winerror.ts index 3ba7d9cdae..b84c60226c 100644 --- a/ext/node/polyfills/internal_binding/_libuv_winerror.ts +++ b/ext/node/polyfills/internal_binding/_libuv_winerror.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_sys_to_uv_error } from "ext:core/ops"; diff --git a/ext/node/polyfills/internal_binding/_listen.ts b/ext/node/polyfills/internal_binding/_listen.ts index 613fdd999d..06e13af79f 100644 --- a/ext/node/polyfills/internal_binding/_listen.ts +++ b/ext/node/polyfills/internal_binding/_listen.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal_binding/_node.ts b/ext/node/polyfills/internal_binding/_node.ts index bf6b776130..1e4f1d257e 100644 --- a/ext/node/polyfills/internal_binding/_node.ts +++ b/ext/node/polyfills/internal_binding/_node.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file contains C++ node globals accessed in internal binding calls /** diff --git a/ext/node/polyfills/internal_binding/_timingSafeEqual.ts b/ext/node/polyfills/internal_binding/_timingSafeEqual.ts index 559b7685b8..f24e70b723 100644 --- a/ext/node/polyfills/internal_binding/_timingSafeEqual.ts +++ b/ext/node/polyfills/internal_binding/_timingSafeEqual.ts @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials import { Buffer } from "node:buffer"; +import { ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH } from "ext:deno_node/internal/errors.ts"; function toDataView(ab: ArrayBufferLike | ArrayBufferView): DataView { if (ArrayBuffer.isView(ab)) { @@ -19,7 +20,7 @@ function stdTimingSafeEqual( b: ArrayBufferView | ArrayBufferLike | DataView, ): boolean { if (a.byteLength !== b.byteLength) { - return false; + throw new ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH(); } if (!(a instanceof DataView)) { a = toDataView(a); diff --git a/ext/node/polyfills/internal_binding/_utils.ts b/ext/node/polyfills/internal_binding/_utils.ts index 74dc3cbcd6..c3e5ca7adb 100644 --- a/ext/node/polyfills/internal_binding/_utils.ts +++ b/ext/node/polyfills/internal_binding/_utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal_binding/ares.ts b/ext/node/polyfills/internal_binding/ares.ts index 46afdfb195..807617aa94 100644 --- a/ext/node/polyfills/internal_binding/ares.ts +++ b/ext/node/polyfills/internal_binding/ares.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /* Copyright 1998 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this diff --git a/ext/node/polyfills/internal_binding/async_wrap.ts b/ext/node/polyfills/internal_binding/async_wrap.ts index affee03fab..4480e1a0a7 100644 --- a/ext/node/polyfills/internal_binding/async_wrap.ts +++ b/ext/node/polyfills/internal_binding/async_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/buffer.ts b/ext/node/polyfills/internal_binding/buffer.ts index 4b8e5388b2..7a543e6f03 100644 --- a/ext/node/polyfills/internal_binding/buffer.ts +++ b/ext/node/polyfills/internal_binding/buffer.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal_binding/cares_wrap.ts b/ext/node/polyfills/internal_binding/cares_wrap.ts index cbd0bb8ef6..6a864e5855 100644 --- a/ext/node/polyfills/internal_binding/cares_wrap.ts +++ b/ext/node/polyfills/internal_binding/cares_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/connection_wrap.ts b/ext/node/polyfills/internal_binding/connection_wrap.ts index ef07025e29..036d7a3e5b 100644 --- a/ext/node/polyfills/internal_binding/connection_wrap.ts +++ b/ext/node/polyfills/internal_binding/connection_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/constants.ts b/ext/node/polyfills/internal_binding/constants.ts index ccb0ba5702..5e1afb7222 100644 --- a/ext/node/polyfills/internal_binding/constants.ts +++ b/ext/node/polyfills/internal_binding/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_build_os } from "ext:core/ops"; diff --git a/ext/node/polyfills/internal_binding/crypto.ts b/ext/node/polyfills/internal_binding/crypto.ts index 3aabbbf344..e3d03a4b1a 100644 --- a/ext/node/polyfills/internal_binding/crypto.ts +++ b/ext/node/polyfills/internal_binding/crypto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; diff --git a/ext/node/polyfills/internal_binding/handle_wrap.ts b/ext/node/polyfills/internal_binding/handle_wrap.ts index 1b3036a7ef..61a34fbc06 100644 --- a/ext/node/polyfills/internal_binding/handle_wrap.ts +++ b/ext/node/polyfills/internal_binding/handle_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/http_parser.ts b/ext/node/polyfills/internal_binding/http_parser.ts index bad10d9851..ac087e24de 100644 --- a/ext/node/polyfills/internal_binding/http_parser.ts +++ b/ext/node/polyfills/internal_binding/http_parser.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/mod.ts b/ext/node/polyfills/internal_binding/mod.ts index ebbfc629f1..25a5a2c7c2 100644 --- a/ext/node/polyfills/internal_binding/mod.ts +++ b/ext/node/polyfills/internal_binding/mod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal_binding/node_file.ts b/ext/node/polyfills/internal_binding/node_file.ts index 6c134ec4bc..1e97ee5cf7 100644 --- a/ext/node/polyfills/internal_binding/node_file.ts +++ b/ext/node/polyfills/internal_binding/node_file.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/node_options.ts b/ext/node/polyfills/internal_binding/node_options.ts index ddacb28b6d..927fd39b94 100644 --- a/ext/node/polyfills/internal_binding/node_options.ts +++ b/ext/node/polyfills/internal_binding/node_options.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/pipe_wrap.ts b/ext/node/polyfills/internal_binding/pipe_wrap.ts index 3e366b3c76..3917be36ee 100644 --- a/ext/node/polyfills/internal_binding/pipe_wrap.ts +++ b/ext/node/polyfills/internal_binding/pipe_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/stream_wrap.ts b/ext/node/polyfills/internal_binding/stream_wrap.ts index 19c9357ce8..e66c737be0 100644 --- a/ext/node/polyfills/internal_binding/stream_wrap.ts +++ b/ext/node/polyfills/internal_binding/stream_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/string_decoder.ts b/ext/node/polyfills/internal_binding/string_decoder.ts index 7f8c954a35..4825ea0aa9 100644 --- a/ext/node/polyfills/internal_binding/string_decoder.ts +++ b/ext/node/polyfills/internal_binding/string_decoder.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Encodings } from "ext:deno_node/internal_binding/_node.ts"; const encodings = []; diff --git a/ext/node/polyfills/internal_binding/symbols.ts b/ext/node/polyfills/internal_binding/symbols.ts index 2b0656b0ea..affc63eb84 100644 --- a/ext/node/polyfills/internal_binding/symbols.ts +++ b/ext/node/polyfills/internal_binding/symbols.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/tcp_wrap.ts b/ext/node/polyfills/internal_binding/tcp_wrap.ts index d9f1c5356a..6c19e2dd29 100644 --- a/ext/node/polyfills/internal_binding/tcp_wrap.ts +++ b/ext/node/polyfills/internal_binding/tcp_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/types.ts b/ext/node/polyfills/internal_binding/types.ts index 2d301ba1b2..a0fd338600 100644 --- a/ext/node/polyfills/internal_binding/types.ts +++ b/ext/node/polyfills/internal_binding/types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. // diff --git a/ext/node/polyfills/internal_binding/udp_wrap.ts b/ext/node/polyfills/internal_binding/udp_wrap.ts index db6961ddb7..337dfe75c2 100644 --- a/ext/node/polyfills/internal_binding/udp_wrap.ts +++ b/ext/node/polyfills/internal_binding/udp_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/util.ts b/ext/node/polyfills/internal_binding/util.ts index c8fb32d8ca..9297e1b20c 100644 --- a/ext/node/polyfills/internal_binding/util.ts +++ b/ext/node/polyfills/internal_binding/util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/uv.ts b/ext/node/polyfills/internal_binding/uv.ts index 6cd70a7e85..cd868cb395 100644 --- a/ext/node/polyfills/internal_binding/uv.ts +++ b/ext/node/polyfills/internal_binding/uv.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/net.ts b/ext/node/polyfills/net.ts index b2b0c9857c..2d57507c1b 100644 --- a/ext/node/polyfills/net.ts +++ b/ext/node/polyfills/net.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -1157,6 +1157,13 @@ function _emitCloseNT(s: Socket | Server) { s.emit("close"); } +// The packages that need socket initialization workaround +const pkgsNeedsSockInitWorkaround = [ + "@npmcli/agent", + "npm-check-updates", + "playwright-core", +]; + /** * This class is an abstraction of a TCP socket or a streaming `IPC` endpoint * (uses named pipes on Windows, and Unix domain sockets otherwise). It is also @@ -1201,9 +1208,11 @@ export class Socket extends Duplex { _host: string | null = null; // deno-lint-ignore no-explicit-any _parent: any = null; - // The flag for detecting if it's called in @npmcli/agent + // Skip some initialization (initial read and tls handshake if it's tls socket). + // If this flag is true, it's used as connection for http(s) request, and + // the reading and TLS handshake is done by the http client. // See discussions in https://github.com/denoland/deno/pull/25470 for more details. - _isNpmAgent = false; + _needsSockInitWorkaround = false; autoSelectFamilyAttemptedAddresses: AddressInfo[] | undefined = undefined; constructor(options: SocketOptions | number) { @@ -1224,16 +1233,20 @@ export class Socket extends Duplex { super(options); - // Note: If the socket is created from @npmcli/agent, the 'socket' event - // on ClientRequest object happens after 'connect' event on Socket object. + // Note: If the socket is created from one of `pkgNeedsSockInitWorkaround`, + // the 'socket' event on ClientRequest object happens after 'connect' event on Socket object. // That swaps the sequence of op_node_http_request_with_conn() call and // initial socket read. That causes op_node_http_request_with_conn() not // working. // To avoid the above situation, we detect the socket created from - // @npmcli/agent and pause the socket (and also skips the startTls call - // if it's TLSSocket) - this._isNpmAgent = new Error().stack?.includes("@npmcli/agent") || false; - if (this._isNpmAgent) { + // one of those packages using stack trace and pause the socket + // (and also skips the startTls call if it's TLSSocket) + // TODO(kt3k): Remove this workaround + const errorStack = new Error().stack; + this._needsSockInitWorkaround = pkgsNeedsSockInitWorkaround.some((pkg) => + errorStack?.includes(pkg) + ); + if (this._needsSockInitWorkaround) { this.pause(); } diff --git a/ext/node/polyfills/os.ts b/ext/node/polyfills/os.ts index edc89ed2c3..4901b20fa8 100644 --- a/ext/node/polyfills/os.ts +++ b/ext/node/polyfills/os.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -35,7 +35,7 @@ import { validateIntegerRange } from "ext:deno_node/_utils.ts"; import process from "node:process"; import { isWindows } from "ext:deno_node/_util/os.ts"; import { os } from "ext:deno_node/internal_binding/constants.ts"; -import { osUptime } from "ext:runtime/30_os.js"; +import { osUptime } from "ext:deno_os/30_os.js"; import { Buffer } from "ext:deno_node/internal/buffer.mjs"; import { primordials } from "ext:core/mod.js"; const { StringPrototypeEndsWith, StringPrototypeSlice } = primordials; diff --git a/ext/node/polyfills/path.ts b/ext/node/polyfills/path.ts index 19ba2e26d3..255f9592a0 100644 --- a/ext/node/polyfills/path.ts +++ b/ext/node/polyfills/path.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export * from "ext:deno_node/path/mod.ts"; import m from "ext:deno_node/path/mod.ts"; export default m; diff --git a/ext/node/polyfills/path/_constants.ts b/ext/node/polyfills/path/_constants.ts index 3407515169..b18f90601f 100644 --- a/ext/node/polyfills/path/_constants.ts +++ b/ext/node/polyfills/path/_constants.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Alphabet chars. export const CHAR_UPPERCASE_A = 65; /* A */ diff --git a/ext/node/polyfills/path/_interface.ts b/ext/node/polyfills/path/_interface.ts index cb40bae12e..e37933962b 100644 --- a/ext/node/polyfills/path/_interface.ts +++ b/ext/node/polyfills/path/_interface.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** * A parsed path object generated by path.parse() or consumed by path.format(). diff --git a/ext/node/polyfills/path/_posix.ts b/ext/node/polyfills/path/_posix.ts index bf0b91d488..6a4aa2117a 100644 --- a/ext/node/polyfills/path/_posix.ts +++ b/ext/node/polyfills/path/_posix.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/path/_util.ts b/ext/node/polyfills/path/_util.ts index 9248c68ae5..1e95faea41 100644 --- a/ext/node/polyfills/path/_util.ts +++ b/ext/node/polyfills/path/_util.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/path/_win32.ts b/ext/node/polyfills/path/_win32.ts index 11c82e0eee..70a0d8a006 100644 --- a/ext/node/polyfills/path/_win32.ts +++ b/ext/node/polyfills/path/_win32.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/path/common.ts b/ext/node/polyfills/path/common.ts index ee2987307e..9c1c91a650 100644 --- a/ext/node/polyfills/path/common.ts +++ b/ext/node/polyfills/path/common.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/path/mod.ts b/ext/node/polyfills/path/mod.ts index e74c1da4db..5fd5e34e57 100644 --- a/ext/node/polyfills/path/mod.ts +++ b/ext/node/polyfills/path/mod.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported mostly from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { isWindows } from "ext:deno_node/_util/os.ts"; import _win32 from "ext:deno_node/path/_win32.ts"; diff --git a/ext/node/polyfills/path/posix.ts b/ext/node/polyfills/path/posix.ts index 2b6582ff6d..474d74afaf 100644 --- a/ext/node/polyfills/path/posix.ts +++ b/ext/node/polyfills/path/posix.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import path from "ext:deno_node/path/mod.ts"; diff --git a/ext/node/polyfills/path/separator.ts b/ext/node/polyfills/path/separator.ts index 36dce7df99..bb9eaf7bb4 100644 --- a/ext/node/polyfills/path/separator.ts +++ b/ext/node/polyfills/path/separator.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/path/win32.ts b/ext/node/polyfills/path/win32.ts index 02e6f97c5a..740a346508 100644 --- a/ext/node/polyfills/path/win32.ts +++ b/ext/node/polyfills/path/win32.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import path from "ext:deno_node/path/mod.ts"; diff --git a/ext/node/polyfills/perf_hooks.ts b/ext/node/polyfills/perf_hooks.ts index ec76b3ce2d..af4e7b261f 100644 --- a/ext/node/polyfills/perf_hooks.ts +++ b/ext/node/polyfills/perf_hooks.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index a30b078acd..8f4509aaf7 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills @@ -58,7 +58,7 @@ import { } from "ext:deno_node/_next_tick.ts"; import { isWindows } from "ext:deno_node/_util/os.ts"; import * as io from "ext:deno_io/12_io.js"; -import * as denoOs from "ext:runtime/30_os.js"; +import * as denoOs from "ext:deno_os/30_os.js"; export let argv0 = ""; diff --git a/ext/node/polyfills/punycode.ts b/ext/node/polyfills/punycode.ts index adecdf4f9a..6e29dd81f9 100644 --- a/ext/node/polyfills/punycode.ts +++ b/ext/node/polyfills/punycode.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_idna_punycode_decode, diff --git a/ext/node/polyfills/querystring.js b/ext/node/polyfills/querystring.js index 5eb6a077aa..206c3f5f82 100644 --- a/ext/node/polyfills/querystring.js +++ b/ext/node/polyfills/querystring.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/readline.ts b/ext/node/polyfills/readline.ts index 5813dd3d9b..6b7e51fe9d 100644 --- a/ext/node/polyfills/readline.ts +++ b/ext/node/polyfills/readline.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @deno-types="./_readline.d.ts" import { diff --git a/ext/node/polyfills/readline/promises.ts b/ext/node/polyfills/readline/promises.ts index 76c8b350d4..0e5ae6b8bb 100644 --- a/ext/node/polyfills/readline/promises.ts +++ b/ext/node/polyfills/readline/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/repl.ts b/ext/node/polyfills/repl.ts index a7acc5b19a..c0e69d93eb 100644 --- a/ext/node/polyfills/repl.ts +++ b/ext/node/polyfills/repl.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/stream.ts b/ext/node/polyfills/stream.ts index 96262428f0..2c9821dee6 100644 --- a/ext/node/polyfills/stream.ts +++ b/ext/node/polyfills/stream.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // compose, destroy and isDisturbed are experimental APIs without // typings. They can be exposed once they are released as stable in Node diff --git a/ext/node/polyfills/stream/consumers.mjs b/ext/node/polyfills/stream/consumers.mjs index dc5d29f611..5f436c3e17 100644 --- a/ext/node/polyfills/stream/consumers.mjs +++ b/ext/node/polyfills/stream/consumers.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/stream/promises.mjs b/ext/node/polyfills/stream/promises.mjs index 1282ca8963..007000d883 100644 --- a/ext/node/polyfills/stream/promises.mjs +++ b/ext/node/polyfills/stream/promises.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { Stream } from "ext:deno_node/_stream.mjs"; diff --git a/ext/node/polyfills/stream/web.ts b/ext/node/polyfills/stream/web.ts index 9cb361f862..c0594c0cb7 100644 --- a/ext/node/polyfills/stream/web.ts +++ b/ext/node/polyfills/stream/web.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { ByteLengthQueuingStrategy, diff --git a/ext/node/polyfills/string_decoder.ts b/ext/node/polyfills/string_decoder.ts index b4a422e4b6..f8dfe8d9a8 100644 --- a/ext/node/polyfills/string_decoder.ts +++ b/ext/node/polyfills/string_decoder.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/sys.ts b/ext/node/polyfills/sys.ts index 4ee112caea..e10f174b6d 100644 --- a/ext/node/polyfills/sys.ts +++ b/ext/node/polyfills/sys.ts @@ -1,3 +1,3 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export * from "node:util"; export { default } from "node:util"; diff --git a/ext/node/polyfills/testing.ts b/ext/node/polyfills/testing.ts index 901c38ed36..39014533a5 100644 --- a/ext/node/polyfills/testing.ts +++ b/ext/node/polyfills/testing.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { PromisePrototypeThen } = primordials; diff --git a/ext/node/polyfills/timers.ts b/ext/node/polyfills/timers.ts index fa5f7a2042..9ef704a0b3 100644 --- a/ext/node/polyfills/timers.ts +++ b/ext/node/polyfills/timers.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/timers/promises.ts b/ext/node/polyfills/timers/promises.ts index b2896fe809..2ec2327330 100644 --- a/ext/node/polyfills/timers/promises.ts +++ b/ext/node/polyfills/timers/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import timers from "node:timers"; export const setTimeout = timers.promises.setTimeout; diff --git a/ext/node/polyfills/tls.ts b/ext/node/polyfills/tls.ts index 4cfe9ebd63..345994236e 100644 --- a/ext/node/polyfills/tls.ts +++ b/ext/node/polyfills/tls.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/trace_events.ts b/ext/node/polyfills/trace_events.ts index bb3ea97459..4df631a4c5 100644 --- a/ext/node/polyfills/trace_events.ts +++ b/ext/node/polyfills/trace_events.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts"; diff --git a/ext/node/polyfills/tty.js b/ext/node/polyfills/tty.js index e906c5f677..1545ab12ec 100644 --- a/ext/node/polyfills/tty.js +++ b/ext/node/polyfills/tty.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_bootstrap_color_depth } from "ext:core/ops"; import { core, primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/url.ts b/ext/node/polyfills/url.ts index 4eeb0381f6..b2744ff6cc 100644 --- a/ext/node/polyfills/url.ts +++ b/ext/node/polyfills/url.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/util.ts b/ext/node/polyfills/util.ts index d82b288b03..06c75ef79a 100644 --- a/ext/node/polyfills/util.ts +++ b/ext/node/polyfills/util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/util/types.ts b/ext/node/polyfills/util/types.ts index 5f2ead19bb..fbf9092d9d 100644 --- a/ext/node/polyfills/util/types.ts +++ b/ext/node/polyfills/util/types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as types from "ext:deno_node/internal/util/types.ts"; export * from "ext:deno_node/internal/util/types.ts"; export default { ...types }; diff --git a/ext/node/polyfills/v8.ts b/ext/node/polyfills/v8.ts index e24a79ab5a..6d7892a724 100644 --- a/ext/node/polyfills/v8.ts +++ b/ext/node/polyfills/v8.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. /// diff --git a/ext/node/polyfills/vm.js b/ext/node/polyfills/vm.js index b64c847c58..72279abcf8 100644 --- a/ext/node/polyfills/vm.js +++ b/ext/node/polyfills/vm.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { Buffer } from "node:buffer"; diff --git a/ext/node/polyfills/wasi.ts b/ext/node/polyfills/wasi.ts index 4bdc776a4c..b948b0e35c 100644 --- a/ext/node/polyfills/wasi.ts +++ b/ext/node/polyfills/wasi.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/worker_threads.ts b/ext/node/polyfills/worker_threads.ts index 1b175fb1dd..87de9a5a5c 100644 --- a/ext/node/polyfills/worker_threads.ts +++ b/ext/node/polyfills/worker_threads.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; @@ -21,7 +21,7 @@ import { nodeWorkerThreadCloseCb, refMessagePort, serializeJsMessageData, - unrefPollForMessages, + unrefParentPort, } from "ext:deno_web/13_message_port.js"; import * as webidl from "ext:deno_webidl/00_webidl.js"; import { notImplemented } from "ext:deno_node/_utils.ts"; @@ -451,10 +451,10 @@ internals.__initWorkerThreads = ( parentPort.emit("close"); }); parentPort.unref = () => { - parentPort[unrefPollForMessages] = true; + parentPort[unrefParentPort] = true; }; parentPort.ref = () => { - parentPort[unrefPollForMessages] = false; + parentPort[unrefParentPort] = false; }; if (isWorkerThread) { diff --git a/ext/node/polyfills/zlib.ts b/ext/node/polyfills/zlib.ts index 6e5d02b5be..08a9238bd5 100644 --- a/ext/node/polyfills/zlib.ts +++ b/ext/node/polyfills/zlib.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; import { zlib as constants } from "ext:deno_node/internal_binding/constants.ts"; import { diff --git a/runtime/js/30_os.js b/ext/os/30_os.js similarity index 97% rename from runtime/js/30_os.js rename to ext/os/30_os.js index f3dfda886d..166b983f45 100644 --- a/runtime/js/30_os.js +++ b/ext/os/30_os.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/40_signals.js b/ext/os/40_signals.js similarity index 96% rename from runtime/js/40_signals.js rename to ext/os/40_signals.js index 41f25af677..161adfabbc 100644 --- a/runtime/js/40_signals.js +++ b/ext/os/40_signals.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_signal_bind, op_signal_poll, op_signal_unbind } from "ext:core/ops"; diff --git a/ext/os/Cargo.toml b/ext/os/Cargo.toml new file mode 100644 index 0000000000..fb553d1532 --- /dev/null +++ b/ext/os/Cargo.toml @@ -0,0 +1,33 @@ +# Copyright 2018-2025 the Deno authors. MIT license. + +[package] +name = "deno_os" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +readme = "README.md" +repository.workspace = true +description = "OS specific APIs for Deno" + +[lib] +path = "lib.rs" + +[dependencies] +deno_core.workspace = true +deno_error.workspace = true +deno_path_util.workspace = true +deno_permissions.workspace = true +deno_telemetry.workspace = true +libc.workspace = true +netif = "0.1.6" +once_cell.workspace = true +serde.workspace = true +signal-hook = "0.3.17" +signal-hook-registry = "1.4.0" +thiserror.workspace = true +tokio.workspace = true + +[target.'cfg(windows)'.dependencies] +winapi = { workspace = true, features = ["commapi", "knownfolders", "mswsock", "objbase", "psapi", "shlobj", "tlhelp32", "winbase", "winerror", "winuser", "winsock2"] } +ntapi = "0.4.0" diff --git a/runtime/ops/os/README.md b/ext/os/README.md similarity index 97% rename from runtime/ops/os/README.md rename to ext/os/README.md index ae1a5958e4..f308ed3d2b 100644 --- a/runtime/ops/os/README.md +++ b/ext/os/README.md @@ -1,4 +1,6 @@ -## `os` ops +# deno_os + +This crate implements OS specific APIs for Deno `loadavg` diff --git a/runtime/ops/os/mod.rs b/ext/os/lib.rs similarity index 82% rename from runtime/ops/os/mod.rs rename to ext/os/lib.rs index b8ebc88bed..f084601678 100644 --- a/runtime/ops/os/mod.rs +++ b/ext/os/lib.rs @@ -1,16 +1,54 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashMap; +use std::collections::HashSet; +use std::env; +use std::sync::atomic::AtomicI32; +use std::sync::atomic::Ordering; +use std::sync::Arc; -use crate::sys_info; -use crate::worker::ExitCode; use deno_core::op2; use deno_core::v8; use deno_core::OpState; -use deno_node::NODE_ENV_VAR_ALLOWLIST; use deno_path_util::normalize_path; +use deno_permissions::PermissionCheckError; use deno_permissions::PermissionsContainer; +use once_cell::sync::Lazy; use serde::Serialize; -use std::collections::HashMap; -use std::env; + +mod ops; +pub mod signal; +pub mod sys_info; + +pub use ops::signal::SignalError; + +pub static NODE_ENV_VAR_ALLOWLIST: Lazy> = Lazy::new(|| { + // The full list of environment variables supported by Node.js is available + // at https://nodejs.org/api/cli.html#environment-variables + let mut set = HashSet::new(); + set.insert("NODE_DEBUG".to_string()); + set.insert("NODE_OPTIONS".to_string()); + set +}); + +#[derive(Clone, Default)] +pub struct ExitCode(Arc); + +impl ExitCode { + pub fn get(&self) -> i32 { + self.0.load(Ordering::Relaxed) + } + + pub fn set(&mut self, code: i32) { + self.0.store(code, Ordering::Relaxed); + } +} + +pub fn exit(code: i32) -> ! { + deno_telemetry::flush(); + #[allow(clippy::disallowed_methods)] + std::process::exit(code); +} deno_core::extension!( deno_os, @@ -32,13 +70,21 @@ deno_core::extension!( op_system_memory_info, op_uid, op_runtime_memory_usage, + ops::signal::op_signal_bind, + ops::signal::op_signal_unbind, + ops::signal::op_signal_poll, ], + esm = ["30_os.js", "40_signals.js"], options = { exit_code: ExitCode, }, state = |state, options| { state.put::(options.exit_code); - }, + #[cfg(unix)] + { + state.put(ops::signal::SignalState::default()); + } + } ); deno_core::extension!( @@ -61,28 +107,39 @@ deno_core::extension!( op_system_memory_info, op_uid, op_runtime_memory_usage, + ops::signal::op_signal_bind, + ops::signal::op_signal_unbind, + ops::signal::op_signal_poll, ], + esm = ["30_os.js", "40_signals.js"], middleware = |op| match op.name { "op_exit" | "op_set_exit_code" | "op_get_exit_code" => op.with_implementation_from(&deno_core::op_void_sync()), _ => op, - }, + } ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum OsError { + #[class(inherit)] #[error(transparent)] - Permission(#[from] deno_permissions::PermissionCheckError), + Permission(#[from] PermissionCheckError), + #[class("InvalidData")] #[error("File name or path {0:?} is not valid UTF-8")] InvalidUtf8(std::ffi::OsString), + #[class(type)] #[error("Key is an empty string.")] EnvEmptyKey, + #[class(type)] #[error("Key contains invalid characters: {0:?}")] EnvInvalidKey(String), + #[class(type)] #[error("Value contains invalid characters: {0:?}")] EnvInvalidValue(String), + #[class(inherit)] #[error(transparent)] Var(#[from] env::VarError), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), } @@ -127,7 +184,7 @@ fn op_set_env( #[serde] fn op_env( state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +) -> Result, PermissionCheckError> { state.borrow_mut::().check_env_all()?; Ok(env::vars().collect()) } @@ -186,14 +243,14 @@ fn op_get_exit_code(state: &mut OpState) -> i32 { #[op2(fast)] fn op_exit(state: &mut OpState) { let code = state.borrow::().get(); - crate::exit(code) + exit(code) } #[op2(stack_trace)] #[serde] fn op_loadavg( state: &mut OpState, -) -> Result<(f64, f64, f64), deno_core::error::AnyError> { +) -> Result<(f64, f64, f64), PermissionCheckError> { state .borrow_mut::() .check_sys("loadavg", "Deno.loadavg()")?; @@ -202,9 +259,7 @@ fn op_loadavg( #[op2(stack_trace, stack_trace)] #[string] -fn op_hostname( - state: &mut OpState, -) -> Result { +fn op_hostname(state: &mut OpState) -> Result { state .borrow_mut::() .check_sys("hostname", "Deno.hostname()")?; @@ -213,9 +268,7 @@ fn op_hostname( #[op2(stack_trace)] #[string] -fn op_os_release( - state: &mut OpState, -) -> Result { +fn op_os_release(state: &mut OpState) -> Result { state .borrow_mut::() .check_sys("osRelease", "Deno.osRelease()")?; @@ -233,7 +286,7 @@ fn op_network_interfaces( Ok(netif::up()?.map(NetworkInterface::from).collect()) } -#[derive(serde::Serialize)] +#[derive(Serialize)] struct NetworkInterface { family: &'static str, name: String, @@ -278,7 +331,7 @@ impl From for NetworkInterface { #[serde] fn op_system_memory_info( state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("systemMemoryInfo", "Deno.systemMemoryInfo()")?; @@ -288,9 +341,7 @@ fn op_system_memory_info( #[cfg(not(windows))] #[op2(stack_trace)] #[smi] -fn op_gid( - state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +fn op_gid(state: &mut OpState) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("gid", "Deno.gid()")?; @@ -304,9 +355,7 @@ fn op_gid( #[cfg(windows)] #[op2(stack_trace)] #[smi] -fn op_gid( - state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +fn op_gid(state: &mut OpState) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("gid", "Deno.gid()")?; @@ -316,9 +365,7 @@ fn op_gid( #[cfg(not(windows))] #[op2(stack_trace)] #[smi] -fn op_uid( - state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +fn op_uid(state: &mut OpState) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("uid", "Deno.uid()")?; @@ -332,9 +379,7 @@ fn op_uid( #[cfg(windows)] #[op2(stack_trace)] #[smi] -fn op_uid( - state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +fn op_uid(state: &mut OpState) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("uid", "Deno.uid()")?; @@ -424,8 +469,11 @@ fn rss() -> usize { let mut count = libc::MACH_TASK_BASIC_INFO_COUNT; // SAFETY: libc calls let r = unsafe { + extern "C" { + static mut mach_task_self_: std::ffi::c_uint; + } libc::task_info( - libc::mach_task_self(), + mach_task_self_, libc::MACH_TASK_BASIC_INFO, task_info.as_mut_ptr() as libc::task_info_t, &mut count as *mut libc::mach_msg_type_number_t, @@ -512,7 +560,7 @@ fn rss() -> usize { } } -fn os_uptime(state: &mut OpState) -> Result { +fn os_uptime(state: &mut OpState) -> Result { state .borrow_mut::() .check_sys("osUptime", "Deno.osUptime()")?; @@ -521,8 +569,6 @@ fn os_uptime(state: &mut OpState) -> Result { #[op2(fast, stack_trace)] #[number] -fn op_os_uptime( - state: &mut OpState, -) -> Result { +fn op_os_uptime(state: &mut OpState) -> Result { os_uptime(state) } diff --git a/ext/os/ops/mod.rs b/ext/os/ops/mod.rs new file mode 100644 index 0000000000..857d4688c7 --- /dev/null +++ b/ext/os/ops/mod.rs @@ -0,0 +1,3 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +pub mod signal; diff --git a/runtime/ops/signal.rs b/ext/os/ops/signal.rs similarity index 92% rename from runtime/ops/signal.rs rename to ext/os/ops/signal.rs index ef87c37297..5b556e3a2c 100644 --- a/runtime/ops/signal.rs +++ b/ext/os/ops/signal.rs @@ -1,13 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::op2; -use deno_core::AsyncRefCell; -use deno_core::CancelFuture; -use deno_core::CancelHandle; -use deno_core::OpState; -use deno_core::RcRef; -use deno_core::Resource; -use deno_core::ResourceId; - +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; #[cfg(unix)] @@ -18,6 +9,15 @@ use std::sync::atomic::AtomicBool; #[cfg(unix)] use std::sync::Arc; +use deno_core::error::ResourceError; +use deno_core::op2; +use deno_core::AsyncRefCell; +use deno_core::CancelFuture; +use deno_core::CancelHandle; +use deno_core::OpState; +use deno_core::RcRef; +use deno_core::Resource; +use deno_core::ResourceId; #[cfg(unix)] use tokio::signal::unix::signal; #[cfg(unix)] @@ -33,32 +33,25 @@ use tokio::signal::windows::CtrlBreak; #[cfg(windows)] use tokio::signal::windows::CtrlC; -deno_core::extension!( - deno_signal, - ops = [op_signal_bind, op_signal_unbind, op_signal_poll], - state = |state| { - #[cfg(unix)] - { - state.put(SignalState::default()); - } - } -); - -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SignalError { + #[class(type)] #[error(transparent)] InvalidSignalStr(#[from] crate::signal::InvalidSignalStrError), + #[class(type)] #[error(transparent)] InvalidSignalInt(#[from] crate::signal::InvalidSignalIntError), + #[class(type)] #[error("Binding to signal '{0}' is not allowed")] SignalNotAllowed(String), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), } #[cfg(unix)] #[derive(Default)] -struct SignalState { +pub struct SignalState { enable_default_handlers: BTreeMap>, } @@ -160,7 +153,7 @@ impl Resource for SignalStreamResource { #[cfg(unix)] #[op2(fast)] #[smi] -fn op_signal_bind( +pub fn op_signal_bind( state: &mut OpState, #[string] sig: &str, ) -> Result { @@ -197,7 +190,7 @@ fn op_signal_bind( #[cfg(windows)] #[op2(fast)] #[smi] -fn op_signal_bind( +pub fn op_signal_bind( state: &mut OpState, #[string] sig: &str, ) -> Result { @@ -221,10 +214,10 @@ fn op_signal_bind( } #[op2(async)] -async fn op_signal_poll( +pub async fn op_signal_poll( state: Rc>, #[smi] rid: ResourceId, -) -> Result { +) -> Result { let resource = state .borrow_mut() .resource_table @@ -243,7 +236,7 @@ async fn op_signal_poll( pub fn op_signal_unbind( state: &mut OpState, #[smi] rid: ResourceId, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), ResourceError> { let resource = state.resource_table.take::(rid)?; #[cfg(unix)] diff --git a/runtime/signal.rs b/ext/os/signal.rs similarity index 98% rename from runtime/signal.rs rename to ext/os/signal.rs index 0ef83d7de8..b11353b3b2 100644 --- a/runtime/signal.rs +++ b/ext/os/signal.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[cfg(target_os = "windows")] #[derive(Debug, thiserror::Error)] diff --git a/runtime/sys_info.rs b/ext/os/sys_info.rs similarity index 98% rename from runtime/sys_info.rs rename to ext/os/sys_info.rs index cffc90e9da..e6bd16c0f3 100644 --- a/runtime/sys_info.rs +++ b/ext/os/sys_info.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[cfg(target_family = "windows")] use std::sync::Once; @@ -159,6 +159,7 @@ pub fn hostname() -> String { use std::ffi::OsString; use std::mem; use std::os::windows::ffi::OsStringExt; + use winapi::shared::minwindef::MAKEWORD; use winapi::um::winsock2::GetHostNameW; use winapi::um::winsock2::WSAStartup; @@ -278,11 +279,15 @@ pub fn mem_info() -> Option { mem_info.swap_total = xs.xsu_total; mem_info.swap_free = xs.xsu_avail; + extern "C" { + fn mach_host_self() -> std::ffi::c_uint; + } + let mut count: u32 = libc::HOST_VM_INFO64_COUNT as _; let mut stat = std::mem::zeroed::(); if libc::host_statistics64( // TODO(@littledivy): Put this in a once_cell. - libc::mach_host_self(), + mach_host_self(), libc::HOST_VM_INFO64, &mut stat as *mut libc::vm_statistics64 as *mut _, &mut count, @@ -291,11 +296,9 @@ pub fn mem_info() -> Option { // TODO(@littledivy): Put this in a once_cell let page_size = libc::sysconf(libc::_SC_PAGESIZE) as u64; mem_info.available = - (stat.free_count as u64 + stat.inactive_count as u64) * page_size - / 1024; + (stat.free_count as u64 + stat.inactive_count as u64) * page_size; mem_info.free = - (stat.free_count as u64 - stat.speculative_count as u64) * page_size - / 1024; + (stat.free_count as u64 - stat.speculative_count as u64) * page_size; } } } @@ -305,6 +308,7 @@ pub fn mem_info() -> Option { // - `dwLength` is set to the size of the struct. unsafe { use std::mem; + use winapi::shared::minwindef; use winapi::um::psapi::GetPerformanceInfo; use winapi::um::psapi::PERFORMANCE_INFORMATION; diff --git a/ext/telemetry/Cargo.toml b/ext/telemetry/Cargo.toml index f3d4bbd336..a83a08c3fe 100644 --- a/ext/telemetry/Cargo.toml +++ b/ext/telemetry/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_telemetry" -version = "0.4.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] async-trait.workspace = true deno_core.workspace = true +deno_error.workspace = true http-body-util.workspace = true hyper.workspace = true hyper-util.workspace = true @@ -28,4 +29,5 @@ opentelemetry-semantic-conventions.workspace = true opentelemetry_sdk.workspace = true pin-project.workspace = true serde.workspace = true +thiserror.workspace = true tokio.workspace = true diff --git a/ext/telemetry/lib.rs b/ext/telemetry/lib.rs index 816e838743..ce3f34a0af 100644 --- a/ext/telemetry/lib.rs +++ b/ext/telemetry/lib.rs @@ -1,21 +1,46 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +#![allow(clippy::too_many_arguments)] + +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; +use std::env; +use std::fmt::Debug; +use std::pin::Pin; +use std::rc::Rc; +use std::sync::atomic::AtomicU64; +use std::sync::Arc; +use std::sync::Mutex; +use std::task::Context; +use std::task::Poll; +use std::thread; +use std::time::Duration; +use std::time::SystemTime; -use deno_core::anyhow; -use deno_core::anyhow::anyhow; use deno_core::futures::channel::mpsc; use deno_core::futures::channel::mpsc::UnboundedSender; use deno_core::futures::future::BoxFuture; use deno_core::futures::stream; +use deno_core::futures::FutureExt; use deno_core::futures::Stream; use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::v8; +use deno_core::v8::DataError; +use deno_core::GarbageCollected; use deno_core::OpState; +use deno_error::JsError; +use deno_error::JsErrorBox; use once_cell::sync::Lazy; use once_cell::sync::OnceCell; use opentelemetry::logs::AnyValue; use opentelemetry::logs::LogRecord as LogRecordTrait; use opentelemetry::logs::Severity; +use opentelemetry::metrics::AsyncInstrumentBuilder; +use opentelemetry::metrics::InstrumentBuilder; +use opentelemetry::metrics::MeterProvider as _; +use opentelemetry::otel_debug; use opentelemetry::otel_error; use opentelemetry::trace::SpanContext; use opentelemetry::trace::SpanId; @@ -23,12 +48,13 @@ use opentelemetry::trace::SpanKind; use opentelemetry::trace::Status as SpanStatus; use opentelemetry::trace::TraceFlags; use opentelemetry::trace::TraceId; +use opentelemetry::trace::TraceState; +use opentelemetry::InstrumentationScope; use opentelemetry::Key; use opentelemetry::KeyValue; use opentelemetry::StringValue; use opentelemetry::Value; use opentelemetry_otlp::HttpExporterBuilder; -use opentelemetry_otlp::MetricExporter; use opentelemetry_otlp::Protocol; use opentelemetry_otlp::WithExportConfig; use opentelemetry_otlp::WithHttpConfig; @@ -36,13 +62,18 @@ use opentelemetry_sdk::export::trace::SpanData; use opentelemetry_sdk::logs::BatchLogProcessor; use opentelemetry_sdk::logs::LogProcessor; use opentelemetry_sdk::logs::LogRecord; -use opentelemetry_sdk::metrics::data::Metric; -use opentelemetry_sdk::metrics::data::ResourceMetrics; -use opentelemetry_sdk::metrics::data::ScopeMetrics; use opentelemetry_sdk::metrics::exporter::PushMetricExporter; +use opentelemetry_sdk::metrics::reader::MetricReader; +use opentelemetry_sdk::metrics::ManualReader; +use opentelemetry_sdk::metrics::MetricResult; +use opentelemetry_sdk::metrics::SdkMeterProvider; use opentelemetry_sdk::metrics::Temporality; use opentelemetry_sdk::trace::BatchSpanProcessor; -use opentelemetry_sdk::trace::SpanProcessor; +use opentelemetry_sdk::trace::IdGenerator; +use opentelemetry_sdk::trace::RandomIdGenerator; +use opentelemetry_sdk::trace::SpanEvents; +use opentelemetry_sdk::trace::SpanLinks; +use opentelemetry_sdk::trace::SpanProcessor as _; use opentelemetry_sdk::Resource; use opentelemetry_semantic_conventions::resource::PROCESS_RUNTIME_NAME; use opentelemetry_semantic_conventions::resource::PROCESS_RUNTIME_VERSION; @@ -51,48 +82,32 @@ use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_NAME; use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_VERSION; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::env; -use std::fmt::Debug; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; -use std::thread; -use std::time::Duration; -use std::time::SystemTime; +use thiserror::Error; +use tokio::sync::oneshot; +use tokio::task::JoinSet; deno_core::extension!( deno_telemetry, ops = [ op_otel_log, - op_otel_instrumentation_scope_create_and_enter, - op_otel_instrumentation_scope_enter, - op_otel_instrumentation_scope_enter_builtin, - op_otel_span_start, - op_otel_span_continue, - op_otel_span_attribute, + op_otel_log_foreign, + op_otel_span_attribute1, op_otel_span_attribute2, op_otel_span_attribute3, - op_otel_span_set_dropped, - op_otel_span_flush, - op_otel_metrics_resource_attribute, - op_otel_metrics_resource_attribute2, - op_otel_metrics_resource_attribute3, - op_otel_metrics_scope, - op_otel_metrics_sum, - op_otel_metrics_gauge, - op_otel_metrics_sum_or_gauge_data_point, - op_otel_metrics_histogram, - op_otel_metrics_histogram_data_point, - op_otel_metrics_histogram_data_point_entry_final, - op_otel_metrics_histogram_data_point_entry1, - op_otel_metrics_histogram_data_point_entry2, - op_otel_metrics_histogram_data_point_entry3, - op_otel_metrics_data_point_attribute, - op_otel_metrics_data_point_attribute2, - op_otel_metrics_data_point_attribute3, - op_otel_metrics_submit, + op_otel_span_update_name, + op_otel_metric_attribute3, + op_otel_metric_record0, + op_otel_metric_record1, + op_otel_metric_record2, + op_otel_metric_record3, + op_otel_metric_observable_record0, + op_otel_metric_observable_record1, + op_otel_metric_observable_record2, + op_otel_metric_observable_record3, + op_otel_metric_wait_to_observe, + op_otel_metric_observation_done, ], + objects = [OtelTracer, OtelMeter, OtelSpan], esm = ["telemetry.ts", "util.ts"], ); @@ -105,6 +120,7 @@ pub struct OtelRuntimeConfig { #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct OtelConfig { pub tracing_enabled: bool, + pub metrics_enabled: bool, pub console: OtelConsoleConfig, pub deterministic: bool, } @@ -113,6 +129,7 @@ impl OtelConfig { pub fn as_v8(&self) -> Box<[u8]> { Box::new([ self.tracing_enabled as u8, + self.metrics_enabled as u8, self.console as u8, self.deterministic as u8, ]) @@ -137,6 +154,10 @@ static OTEL_SHARED_RUNTIME_SPAWN_TASK_TX: Lazy< UnboundedSender>, > = Lazy::new(otel_create_shared_runtime); +static OTEL_PRE_COLLECT_CALLBACKS: Lazy< + Mutex>>>, +> = Lazy::new(Default::default); + fn otel_create_shared_runtime() -> UnboundedSender> { let (spawn_task_tx, mut spawn_task_rx) = mpsc::unbounded::>(); @@ -273,7 +294,187 @@ impl Stream for BatchMessageChannelReceiver { } } +enum DenoPeriodicReaderMessage { + Register(std::sync::Weak), + Export, + ForceFlush(oneshot::Sender>), + Shutdown(oneshot::Sender>), +} + +#[derive(Debug)] +struct DenoPeriodicReader { + tx: tokio::sync::mpsc::Sender, + temporality: Temporality, +} + +impl MetricReader for DenoPeriodicReader { + fn register_pipeline( + &self, + pipeline: std::sync::Weak, + ) { + let _ = self + .tx + .try_send(DenoPeriodicReaderMessage::Register(pipeline)); + } + + fn collect( + &self, + _rm: &mut opentelemetry_sdk::metrics::data::ResourceMetrics, + ) -> opentelemetry_sdk::metrics::MetricResult<()> { + unreachable!("collect should not be called on DenoPeriodicReader"); + } + + fn force_flush(&self) -> opentelemetry_sdk::metrics::MetricResult<()> { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.try_send(DenoPeriodicReaderMessage::ForceFlush(tx)); + deno_core::futures::executor::block_on(rx).unwrap()?; + Ok(()) + } + + fn shutdown(&self) -> opentelemetry_sdk::metrics::MetricResult<()> { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.try_send(DenoPeriodicReaderMessage::Shutdown(tx)); + deno_core::futures::executor::block_on(rx).unwrap()?; + Ok(()) + } + + fn temporality( + &self, + _kind: opentelemetry_sdk::metrics::InstrumentKind, + ) -> Temporality { + self.temporality + } +} + +const METRIC_EXPORT_INTERVAL_NAME: &str = "OTEL_METRIC_EXPORT_INTERVAL"; +const DEFAULT_INTERVAL: Duration = Duration::from_secs(60); + +impl DenoPeriodicReader { + fn new(exporter: opentelemetry_otlp::MetricExporter) -> Self { + let interval = env::var(METRIC_EXPORT_INTERVAL_NAME) + .ok() + .and_then(|v| v.parse().map(Duration::from_millis).ok()) + .unwrap_or(DEFAULT_INTERVAL); + + let (tx, mut rx) = tokio::sync::mpsc::channel(256); + + let temporality = PushMetricExporter::temporality(&exporter); + + let worker = async move { + let inner = ManualReader::builder() + .with_temporality(PushMetricExporter::temporality(&exporter)) + .build(); + + let collect_and_export = |collect_observed: bool| { + let inner = &inner; + let exporter = &exporter; + async move { + let mut resource_metrics = + opentelemetry_sdk::metrics::data::ResourceMetrics { + resource: Default::default(), + scope_metrics: Default::default(), + }; + if collect_observed { + let callbacks = { + let mut callbacks = OTEL_PRE_COLLECT_CALLBACKS.lock().unwrap(); + std::mem::take(&mut *callbacks) + }; + let mut futures = JoinSet::new(); + for callback in callbacks { + let (tx, rx) = oneshot::channel(); + if let Ok(()) = callback.send(tx) { + futures.spawn(rx); + } + } + while futures.join_next().await.is_some() {} + } + inner.collect(&mut resource_metrics)?; + if resource_metrics.scope_metrics.is_empty() { + return Ok(()); + } + exporter.export(&mut resource_metrics).await?; + Ok(()) + } + }; + + let mut ticker = tokio::time::interval(interval); + ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); + ticker.tick().await; + + loop { + let message = tokio::select! { + _ = ticker.tick() => DenoPeriodicReaderMessage::Export, + message = rx.recv() => if let Some(message) = message { + message + } else { + break; + }, + }; + + match message { + DenoPeriodicReaderMessage::Register(new_pipeline) => { + inner.register_pipeline(new_pipeline); + } + DenoPeriodicReaderMessage::Export => { + otel_debug!( + name: "DenoPeriodicReader.ExportTriggered", + message = "Export message received.", + ); + if let Err(err) = collect_and_export(true).await { + otel_error!( + name: "DenoPeriodicReader.ExportFailed", + message = "Failed to export metrics", + reason = format!("{}", err)); + } + } + DenoPeriodicReaderMessage::ForceFlush(sender) => { + otel_debug!( + name: "DenoPeriodicReader.ForceFlushCalled", + message = "Flush message received.", + ); + let res = collect_and_export(false).await; + if let Err(send_error) = sender.send(res) { + otel_debug!( + name: "DenoPeriodicReader.Flush.SendResultError", + message = "Failed to send flush result.", + reason = format!("{:?}", send_error), + ); + } + } + DenoPeriodicReaderMessage::Shutdown(sender) => { + otel_debug!( + name: "DenoPeriodicReader.ShutdownCalled", + message = "Shutdown message received", + ); + let res = collect_and_export(false).await; + let _ = exporter.shutdown(); + if let Err(send_error) = sender.send(res) { + otel_debug!( + name: "DenoPeriodicReader.Shutdown.SendResultError", + message = "Failed to send shutdown result", + reason = format!("{:?}", send_error), + ); + } + break; + } + } + } + }; + + (*OTEL_SHARED_RUNTIME_SPAWN_TASK_TX) + .unbounded_send(worker.boxed()) + .expect("failed to send task to shared OpenTelemetry runtime"); + + DenoPeriodicReader { tx, temporality } + } +} + mod hyper_client { + use std::fmt::Debug; + use std::pin::Pin; + use std::task::Poll; + use std::task::{self}; + use http_body_util::BodyExt; use http_body_util::Full; use hyper::body::Body as HttpBody; @@ -285,10 +486,6 @@ mod hyper_client { use opentelemetry_http::Request; use opentelemetry_http::Response; use opentelemetry_http::ResponseExt; - use std::fmt::Debug; - use std::pin::Pin; - use std::task::Poll; - use std::task::{self}; use super::OtelSharedRuntime; @@ -353,75 +550,20 @@ mod hyper_client { } } -enum MetricProcessorMessage { - ResourceMetrics(ResourceMetrics), - Flush(tokio::sync::oneshot::Sender<()>), +struct OtelGlobals { + span_processor: BatchSpanProcessor, + log_processor: BatchLogProcessor, + id_generator: DenoIdGenerator, + meter_provider: SdkMeterProvider, + builtin_instrumentation_scope: InstrumentationScope, } -struct MetricProcessor { - tx: tokio::sync::mpsc::Sender, -} +static OTEL_GLOBALS: OnceCell = OnceCell::new(); -impl MetricProcessor { - fn new(exporter: MetricExporter) -> Self { - let (tx, mut rx) = tokio::sync::mpsc::channel(2048); - let future = async move { - while let Some(message) = rx.recv().await { - match message { - MetricProcessorMessage::ResourceMetrics(mut rm) => { - if let Err(err) = exporter.export(&mut rm).await { - otel_error!( - name: "MetricProcessor.Export.Error", - error = format!("{}", err) - ); - } - } - MetricProcessorMessage::Flush(tx) => { - if let Err(()) = tx.send(()) { - otel_error!( - name: "MetricProcessor.Flush.SendResultError", - error = "()", - ); - } - } - } - } - }; - - (*OTEL_SHARED_RUNTIME_SPAWN_TASK_TX) - .unbounded_send(Box::pin(future)) - .expect("failed to send task to shared OpenTelemetry runtime"); - - Self { tx } - } - - fn submit(&self, rm: ResourceMetrics) { - let _ = self - .tx - .try_send(MetricProcessorMessage::ResourceMetrics(rm)); - } - - fn force_flush(&self) -> Result<(), anyhow::Error> { - let (tx, rx) = tokio::sync::oneshot::channel(); - self.tx.try_send(MetricProcessorMessage::Flush(tx))?; - deno_core::futures::executor::block_on(rx)?; - Ok(()) - } -} - -struct Processors { - spans: BatchSpanProcessor, - logs: BatchLogProcessor, - metrics: MetricProcessor, -} - -static OTEL_PROCESSORS: OnceCell = OnceCell::new(); - -static BUILT_IN_INSTRUMENTATION_SCOPE: OnceCell< - opentelemetry::InstrumentationScope, -> = OnceCell::new(); - -pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { +pub fn init( + rt_config: OtelRuntimeConfig, + config: &OtelConfig, +) -> deno_core::anyhow::Result<()> { // Parse the `OTEL_EXPORTER_OTLP_PROTOCOL` variable. The opentelemetry_* // crates don't do this automatically. // TODO(piscisaureus): enable GRPC support. @@ -430,13 +572,13 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { Ok("http/json") => Protocol::HttpJson, Ok("") | Err(env::VarError::NotPresent) => Protocol::HttpBinary, Ok(protocol) => { - return Err(anyhow!( + return Err(deno_core::anyhow::anyhow!( "Env var OTEL_EXPORTER_OTLP_PROTOCOL specifies an unsupported protocol: {}", protocol )); } Err(err) => { - return Err(anyhow!( + return Err(deno_core::anyhow::anyhow!( "Failed to read env var OTEL_EXPORTER_OTLP_PROTOCOL: {}", err )); @@ -454,8 +596,8 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { // Add the runtime name and version to the resource attributes. Also override // the `telemetry.sdk` attributes to include the Deno runtime. resource = resource.merge(&Resource::new(vec![ - KeyValue::new(PROCESS_RUNTIME_NAME, config.runtime_name), - KeyValue::new(PROCESS_RUNTIME_VERSION, config.runtime_version.clone()), + KeyValue::new(PROCESS_RUNTIME_NAME, rt_config.runtime_name), + KeyValue::new(PROCESS_RUNTIME_VERSION, rt_config.runtime_version.clone()), KeyValue::new( TELEMETRY_SDK_LANGUAGE, format!( @@ -474,7 +616,7 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { TELEMETRY_SDK_VERSION, format!( "{}-{}", - config.runtime_version, + rt_config.runtime_version, resource.get(Key::new(TELEMETRY_SDK_VERSION)).unwrap() ), ), @@ -494,11 +636,30 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { BatchSpanProcessor::builder(span_exporter, OtelSharedRuntime).build(); span_processor.set_resource(&resource); + let temporality_preference = + env::var("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE") + .ok() + .map(|s| s.to_lowercase()); + let temporality = match temporality_preference.as_deref() { + None | Some("cumulative") => Temporality::Cumulative, + Some("delta") => Temporality::Delta, + Some("lowmemory") => Temporality::LowMemory, + Some(other) => { + return Err(deno_core::anyhow::anyhow!( + "Invalid value for OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: {}", + other + )); + } + }; let metric_exporter = HttpExporterBuilder::default() .with_http_client(client.clone()) .with_protocol(protocol) - .build_metrics_exporter(Temporality::Cumulative)?; - let metric_processor = MetricProcessor::new(metric_exporter); + .build_metrics_exporter(temporality)?; + let metric_reader = DenoPeriodicReader::new(metric_exporter); + let meter_provider = SdkMeterProvider::builder() + .with_reader(metric_reader) + .with_resource(resource.clone()) + .build(); let log_exporter = HttpExporterBuilder::default() .with_http_client(client) @@ -508,21 +669,26 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { BatchLogProcessor::builder(log_exporter, OtelSharedRuntime).build(); log_processor.set_resource(&resource); - OTEL_PROCESSORS - .set(Processors { - spans: span_processor, - logs: log_processor, - metrics: metric_processor, - }) - .map_err(|_| anyhow!("failed to init otel"))?; - let builtin_instrumentation_scope = opentelemetry::InstrumentationScope::builder("deno") - .with_version(config.runtime_version.clone()) + .with_version(rt_config.runtime_version.clone()) .build(); - BUILT_IN_INSTRUMENTATION_SCOPE - .set(builtin_instrumentation_scope) - .map_err(|_| anyhow!("failed to init otel"))?; + + let id_generator = if config.deterministic { + DenoIdGenerator::deterministic() + } else { + DenoIdGenerator::random() + }; + + OTEL_GLOBALS + .set(OtelGlobals { + log_processor, + span_processor, + id_generator, + meter_provider, + builtin_instrumentation_scope, + }) + .map_err(|_| deno_core::anyhow::anyhow!("failed to set otel globals"))?; Ok(()) } @@ -531,22 +697,28 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { /// `process::exit()`, to ensure that all OpenTelemetry logs are properly /// flushed before the process terminates. pub fn flush() { - if let Some(Processors { - spans, - logs, - metrics, - }) = OTEL_PROCESSORS.get() + if let Some(OtelGlobals { + span_processor: spans, + log_processor: logs, + meter_provider, + .. + }) = OTEL_GLOBALS.get() { let _ = spans.force_flush(); let _ = logs.force_flush(); - let _ = metrics.force_flush(); + let _ = meter_provider.force_flush(); } } pub fn handle_log(record: &log::Record) { use log::Level; - let Some(Processors { logs, .. }) = OTEL_PROCESSORS.get() else { + let Some(OtelGlobals { + log_processor: logs, + builtin_instrumentation_scope, + .. + }) = OTEL_GLOBALS.get() + else { return; }; @@ -596,10 +768,58 @@ pub fn handle_log(record: &log::Record) { let _ = record.key_values().visit(&mut Visitor(&mut log_record)); - logs.emit( - &mut log_record, - BUILT_IN_INSTRUMENTATION_SCOPE.get().unwrap(), - ); + logs.emit(&mut log_record, builtin_instrumentation_scope); +} + +#[derive(Debug)] +enum DenoIdGenerator { + Random(RandomIdGenerator), + Deterministic { + next_trace_id: AtomicU64, + next_span_id: AtomicU64, + }, +} + +impl IdGenerator for DenoIdGenerator { + fn new_trace_id(&self) -> TraceId { + match self { + Self::Random(generator) => generator.new_trace_id(), + Self::Deterministic { next_trace_id, .. } => { + let id = + next_trace_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let bytes = id.to_be_bytes(); + let bytes = [ + 0, 0, 0, 0, 0, 0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + ]; + TraceId::from_bytes(bytes) + } + } + } + + fn new_span_id(&self) -> SpanId { + match self { + Self::Random(generator) => generator.new_span_id(), + Self::Deterministic { next_span_id, .. } => { + let id = + next_span_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + SpanId::from_bytes(id.to_be_bytes()) + } + } + } +} + +impl DenoIdGenerator { + fn random() -> Self { + Self::Random(RandomIdGenerator::default()) + } + + fn deterministic() -> Self { + Self::Deterministic { + next_trace_id: AtomicU64::new(1), + next_span_id: AtomicU64::new(1), + } + } } fn parse_trace_id( @@ -659,8 +879,8 @@ fn parse_span_id( } } -macro_rules! attr { - ($scope:ident, $attributes:expr $(=> $dropped_attributes_count:expr)?, $name:expr, $value:expr) => { +macro_rules! attr_raw { + ($scope:ident, $name:expr, $value:expr) => {{ let name = if let Ok(name) = $name.try_cast() { let view = v8::ValueView::new($scope, name); match view.data() { @@ -691,11 +911,25 @@ macro_rules! attr { } else if let Ok(bigint) = $value.try_cast::() { let (i64_value, _lossless) = bigint.i64_value(); Some(Value::I64(i64_value)) + } else if let Ok(_array) = $value.try_cast::() { + // TODO: implement array attributes + None } else { None }; if let (Some(name), Some(value)) = (name, value) { - $attributes.push(KeyValue::new(name, value)); + Some(KeyValue::new(name, value)) + } else { + None + } + }}; +} + +macro_rules! attr { + ($scope:ident, $attributes:expr $(=> $dropped_attributes_count:expr)?, $name:expr, $value:expr) => { + let attr = attr_raw!($scope, $name, $value); + if let Some(kv) = attr { + $attributes.push(kv); } $( else { @@ -705,48 +939,66 @@ macro_rules! attr { }; } -#[derive(Debug, Clone)] -struct InstrumentationScope(opentelemetry::InstrumentationScope); - -impl deno_core::GarbageCollected for InstrumentationScope {} - -#[op2] -#[cppgc] -fn op_otel_instrumentation_scope_create_and_enter( - state: &mut OpState, - #[string] name: String, - #[string] version: Option, - #[string] schema_url: Option, -) -> InstrumentationScope { - let mut builder = opentelemetry::InstrumentationScope::builder(name); - if let Some(version) = version { - builder = builder.with_version(version); - } - if let Some(schema_url) = schema_url { - builder = builder.with_schema_url(schema_url); - } - let scope = InstrumentationScope(builder.build()); - state.put(scope.clone()); - scope -} - #[op2(fast)] -fn op_otel_instrumentation_scope_enter( - state: &mut OpState, - #[cppgc] scope: &InstrumentationScope, +fn op_otel_log<'s>( + scope: &mut v8::HandleScope<'s>, + message: v8::Local<'s, v8::Value>, + #[smi] level: i32, + span: v8::Local<'s, v8::Value>, ) { - state.put(scope.clone()); -} + let Some(OtelGlobals { + log_processor, + builtin_instrumentation_scope, + .. + }) = OTEL_GLOBALS.get() + else { + return; + }; -#[op2(fast)] -fn op_otel_instrumentation_scope_enter_builtin(state: &mut OpState) { - if let Some(scope) = BUILT_IN_INSTRUMENTATION_SCOPE.get() { - state.put(InstrumentationScope(scope.clone())); + // Convert the integer log level that ext/console uses to the corresponding + // OpenTelemetry log severity. + let severity = match level { + ..=0 => Severity::Debug, + 1 => Severity::Info, + 2 => Severity::Warn, + 3.. => Severity::Error, + }; + + let mut log_record = LogRecord::default(); + log_record.set_observed_timestamp(SystemTime::now()); + let Ok(message) = message.try_cast() else { + return; + }; + log_record.set_body(owned_string(scope, message).into()); + log_record.set_severity_number(severity); + log_record.set_severity_text(severity.name()); + if let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + { + let state = span.0.borrow(); + match &**state { + OtelSpanState::Recording(span) => { + log_record.set_trace_context( + span.span_context.trace_id(), + span.span_context.span_id(), + Some(span.span_context.trace_flags()), + ); + } + OtelSpanState::Done(span_context) => { + log_record.set_trace_context( + span_context.trace_id(), + span_context.span_id(), + Some(span_context.trace_flags()), + ); + } + } } + + log_processor.emit(&mut log_record, builtin_instrumentation_scope); } #[op2(fast)] -fn op_otel_log( +fn op_otel_log_foreign( scope: &mut v8::HandleScope<'_>, #[string] message: String, #[smi] level: i32, @@ -754,10 +1006,12 @@ fn op_otel_log( span_id: v8::Local<'_, v8::Value>, #[smi] trace_flags: u8, ) { - let Some(Processors { logs, .. }) = OTEL_PROCESSORS.get() else { - return; - }; - let Some(instrumentation_scope) = BUILT_IN_INSTRUMENTATION_SCOPE.get() else { + let Some(OtelGlobals { + log_processor, + builtin_instrumentation_scope, + .. + }) = OTEL_GLOBALS.get() + else { return; }; @@ -787,7 +1041,7 @@ fn op_otel_log( ); } - logs.emit(&mut log_record, instrumentation_scope); + log_processor.emit(&mut log_record, builtin_instrumentation_scope); } fn owned_string<'s>( @@ -803,134 +1057,348 @@ fn owned_string<'s>( } } -struct TemporarySpan(SpanData); +struct OtelTracer(InstrumentationScope); -#[allow(clippy::too_many_arguments)] -#[op2(fast)] -fn op_otel_span_start<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - trace_id: v8::Local<'s, v8::Value>, - span_id: v8::Local<'s, v8::Value>, - parent_span_id: v8::Local<'s, v8::Value>, - #[smi] span_kind: u8, - name: v8::Local<'s, v8::Value>, - start_time: f64, - end_time: f64, -) -> Result<(), anyhow::Error> { - if let Some(temporary_span) = state.try_take::() { - let Some(Processors { spans, .. }) = OTEL_PROCESSORS.get() else { - return Ok(()); - }; - spans.on_end(temporary_span.0); - }; +impl deno_core::GarbageCollected for OtelTracer {} - let Some(InstrumentationScope(instrumentation_scope)) = - state.try_borrow::() - else { - return Err(anyhow!("instrumentation scope not available")); - }; - - let trace_id = parse_trace_id(scope, trace_id); - if trace_id == TraceId::INVALID { - return Err(anyhow!("invalid trace_id")); +#[op2] +impl OtelTracer { + #[constructor] + #[cppgc] + fn new( + #[string] name: String, + #[string] version: Option, + #[string] schema_url: Option, + ) -> OtelTracer { + let mut builder = opentelemetry::InstrumentationScope::builder(name); + if let Some(version) = version { + builder = builder.with_version(version); + } + if let Some(schema_url) = schema_url { + builder = builder.with_schema_url(schema_url); + } + let scope = builder.build(); + OtelTracer(scope) } - let span_id = parse_span_id(scope, span_id); - if span_id == SpanId::INVALID { - return Err(anyhow!("invalid span_id")); + #[static_method] + #[cppgc] + fn builtin() -> OtelTracer { + let OtelGlobals { + builtin_instrumentation_scope, + .. + } = OTEL_GLOBALS.get().unwrap(); + OtelTracer(builtin_instrumentation_scope.clone()) } - let parent_span_id = parse_span_id(scope, parent_span_id); - - let name = owned_string(scope, name.try_cast()?); - - let temporary_span = TemporarySpan(SpanData { - span_context: SpanContext::new( - trace_id, - span_id, - TraceFlags::SAMPLED, - false, - Default::default(), - ), - parent_span_id, - span_kind: match span_kind { + #[cppgc] + fn start_span<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + #[cppgc] parent: Option<&OtelSpan>, + name: v8::Local<'s, v8::Value>, + #[smi] span_kind: u8, + start_time: Option, + #[smi] attribute_count: usize, + ) -> Result { + let OtelGlobals { id_generator, .. } = OTEL_GLOBALS.get().unwrap(); + let span_context; + let parent_span_id; + match parent { + Some(parent) => { + let parent = parent.0.borrow(); + let parent_span_context = match &**parent { + OtelSpanState::Recording(span) => &span.span_context, + OtelSpanState::Done(span_context) => span_context, + }; + span_context = SpanContext::new( + parent_span_context.trace_id(), + id_generator.new_span_id(), + TraceFlags::SAMPLED, + false, + parent_span_context.trace_state().clone(), + ); + parent_span_id = parent_span_context.span_id(); + } + None => { + span_context = SpanContext::new( + id_generator.new_trace_id(), + id_generator.new_span_id(), + TraceFlags::SAMPLED, + false, + TraceState::NONE, + ); + parent_span_id = SpanId::INVALID; + } + } + let name = owned_string( + scope, + name + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); + let span_kind = match span_kind { 0 => SpanKind::Internal, 1 => SpanKind::Server, 2 => SpanKind::Client, 3 => SpanKind::Producer, 4 => SpanKind::Consumer, - _ => return Err(anyhow!("invalid span kind")), - }, - name: Cow::Owned(name), - start_time: SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(start_time)) - .ok_or_else(|| anyhow!("invalid start time"))?, - end_time: SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(end_time)) - .ok_or_else(|| anyhow!("invalid start time"))?, - attributes: Vec::new(), - dropped_attributes_count: 0, - events: Default::default(), - links: Default::default(), - status: SpanStatus::Unset, - instrumentation_scope: instrumentation_scope.clone(), - }); - state.put(temporary_span); + _ => return Err(JsErrorBox::generic("invalid span kind")), + }; + let start_time = start_time + .map(|start_time| { + SystemTime::UNIX_EPOCH + .checked_add(std::time::Duration::from_secs_f64(start_time)) + .ok_or_else(|| JsErrorBox::generic("invalid start time")) + }) + .unwrap_or_else(|| Ok(SystemTime::now()))?; + let span_data = SpanData { + span_context, + parent_span_id, + span_kind, + name: Cow::Owned(name), + start_time, + end_time: SystemTime::UNIX_EPOCH, + attributes: Vec::with_capacity(attribute_count), + dropped_attributes_count: 0, + status: SpanStatus::Unset, + events: SpanEvents::default(), + links: SpanLinks::default(), + instrumentation_scope: self.0.clone(), + }; + Ok(OtelSpan(RefCell::new(Box::new(OtelSpanState::Recording( + span_data, + ))))) + } - Ok(()) + #[cppgc] + fn start_span_foreign<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + parent_trace_id: v8::Local<'s, v8::Value>, + parent_span_id: v8::Local<'s, v8::Value>, + name: v8::Local<'s, v8::Value>, + #[smi] span_kind: u8, + start_time: Option, + #[smi] attribute_count: usize, + ) -> Result { + let parent_trace_id = parse_trace_id(scope, parent_trace_id); + if parent_trace_id == TraceId::INVALID { + return Err(JsErrorBox::generic("invalid trace id")); + }; + let parent_span_id = parse_span_id(scope, parent_span_id); + if parent_span_id == SpanId::INVALID { + return Err(JsErrorBox::generic("invalid span id")); + }; + let OtelGlobals { id_generator, .. } = OTEL_GLOBALS.get().unwrap(); + let span_context = SpanContext::new( + parent_trace_id, + id_generator.new_span_id(), + TraceFlags::SAMPLED, + false, + TraceState::NONE, + ); + let name = owned_string( + scope, + name + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); + let span_kind = match span_kind { + 0 => SpanKind::Internal, + 1 => SpanKind::Server, + 2 => SpanKind::Client, + 3 => SpanKind::Producer, + 4 => SpanKind::Consumer, + _ => return Err(JsErrorBox::generic("invalid span kind")), + }; + let start_time = start_time + .map(|start_time| { + SystemTime::UNIX_EPOCH + .checked_add(std::time::Duration::from_secs_f64(start_time)) + .ok_or_else(|| JsErrorBox::generic("invalid start time")) + }) + .unwrap_or_else(|| Ok(SystemTime::now()))?; + let span_data = SpanData { + span_context, + parent_span_id, + span_kind, + name: Cow::Owned(name), + start_time, + end_time: SystemTime::UNIX_EPOCH, + attributes: Vec::with_capacity(attribute_count), + dropped_attributes_count: 0, + status: SpanStatus::Unset, + events: SpanEvents::default(), + links: SpanLinks::default(), + instrumentation_scope: self.0.clone(), + }; + Ok(OtelSpan(RefCell::new(Box::new(OtelSpanState::Recording( + span_data, + ))))) + } } -#[op2(fast)] -fn op_otel_span_continue( - state: &mut OpState, - #[smi] status: u8, - #[string] error_description: Cow<'_, str>, -) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.status = match status { +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct JsSpanContext { + trace_id: Box, + span_id: Box, + trace_flags: u8, +} + +#[derive(Debug, Error, JsError)] +#[error("OtelSpan cannot be constructed.")] +#[class(type)] +struct OtelSpanCannotBeConstructedError; + +#[derive(Debug, Error, JsError)] +#[error("invalid span status code")] +#[class(type)] +struct InvalidSpanStatusCodeError; + +// boxed because of https://github.com/denoland/rusty_v8/issues/1676 +#[derive(Debug)] +struct OtelSpan(RefCell>); + +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +enum OtelSpanState { + Recording(SpanData), + Done(SpanContext), +} + +impl deno_core::GarbageCollected for OtelSpan {} + +#[op2] +impl OtelSpan { + #[constructor] + #[cppgc] + fn new() -> Result { + Err(OtelSpanCannotBeConstructedError) + } + + #[serde] + fn span_context(&self) -> JsSpanContext { + let state = self.0.borrow(); + let span_context = match &**state { + OtelSpanState::Recording(span) => &span.span_context, + OtelSpanState::Done(span_context) => span_context, + }; + JsSpanContext { + trace_id: format!("{:?}", span_context.trace_id()).into(), + span_id: format!("{:?}", span_context.span_id()).into(), + trace_flags: span_context.trace_flags().to_u8(), + } + } + + #[fast] + fn set_status<'s>( + &self, + #[smi] status: u8, + #[string] error_description: String, + ) -> Result<(), InvalidSpanStatusCodeError> { + let mut state = self.0.borrow_mut(); + let OtelSpanState::Recording(span) = &mut **state else { + return Ok(()); + }; + span.status = match status { 0 => SpanStatus::Unset, 1 => SpanStatus::Ok, 2 => SpanStatus::Error { - description: Cow::Owned(error_description.into_owned()), + description: Cow::Owned(error_description), }, - _ => return, + _ => return Err(InvalidSpanStatusCodeError), }; + Ok(()) + } + + #[fast] + fn drop_event(&self) { + let mut state = self.0.borrow_mut(); + match &mut **state { + OtelSpanState::Recording(span) => { + span.events.dropped_count += 1; + } + OtelSpanState::Done(_) => {} + } + } + + #[fast] + fn drop_link(&self) { + let mut state = self.0.borrow_mut(); + match &mut **state { + OtelSpanState::Recording(span) => { + span.links.dropped_count += 1; + } + OtelSpanState::Done(_) => {} + } + } + + #[fast] + fn end(&self, end_time: f64) { + let end_time = if end_time.is_nan() { + SystemTime::now() + } else { + SystemTime::UNIX_EPOCH + .checked_add(Duration::from_secs_f64(end_time)) + .unwrap() + }; + + let mut state = self.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + let span_context = span.span_context.clone(); + if let OtelSpanState::Recording(mut span) = *std::mem::replace( + &mut *state, + Box::new(OtelSpanState::Done(span_context)), + ) { + span.end_time = end_time; + let Some(OtelGlobals { span_processor, .. }) = OTEL_GLOBALS.get() + else { + return; + }; + span_processor.on_end(span); + } + } } } #[op2(fast)] -fn op_otel_span_attribute<'s>( +fn op_otel_span_attribute1<'s>( scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, + span: v8::Local<'_, v8::Value>, key: v8::Local<'s, v8::Value>, value: v8::Local<'s, v8::Value>, ) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.attributes.reserve_exact( - (capacity as usize) - temporary_span.0.attributes.capacity(), - ); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key, value); + let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + else { + return; + }; + let mut state = span.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + attr!(scope, span.attributes => span.dropped_attributes_count, key, value); } } #[op2(fast)] fn op_otel_span_attribute2<'s>( scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, + span: v8::Local<'_, v8::Value>, key1: v8::Local<'s, v8::Value>, value1: v8::Local<'s, v8::Value>, key2: v8::Local<'s, v8::Value>, value2: v8::Local<'s, v8::Value>, ) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.attributes.reserve_exact( - (capacity as usize) - temporary_span.0.attributes.capacity(), - ); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2); + let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + else { + return; + }; + let mut state = span.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + attr!(scope, span.attributes => span.dropped_attributes_count, key1, value1); + attr!(scope, span.attributes => span.dropped_attributes_count, key2, value2); } } @@ -938,8 +1406,7 @@ fn op_otel_span_attribute2<'s>( #[op2(fast)] fn op_otel_span_attribute3<'s>( scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, + span: v8::Local<'_, v8::Value>, key1: v8::Local<'s, v8::Value>, value1: v8::Local<'s, v8::Value>, key2: v8::Local<'s, v8::Value>, @@ -947,575 +1414,600 @@ fn op_otel_span_attribute3<'s>( key3: v8::Local<'s, v8::Value>, value3: v8::Local<'s, v8::Value>, ) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.attributes.reserve_exact( - (capacity as usize) - temporary_span.0.attributes.capacity(), - ); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key3, value3); + let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + else { + return; + }; + let mut state = span.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + attr!(scope, span.attributes => span.dropped_attributes_count, key1, value1); + attr!(scope, span.attributes => span.dropped_attributes_count, key2, value2); + attr!(scope, span.attributes => span.dropped_attributes_count, key3, value3); } } #[op2(fast)] -fn op_otel_span_set_dropped( - state: &mut OpState, - #[smi] dropped_attributes_count: u32, - #[smi] dropped_links_count: u32, - #[smi] dropped_events_count: u32, +fn op_otel_span_update_name<'s>( + scope: &mut v8::HandleScope<'s>, + span: v8::Local<'s, v8::Value>, + name: v8::Local<'s, v8::Value>, ) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.dropped_attributes_count += dropped_attributes_count; - temporary_span.0.links.dropped_count += dropped_links_count; - temporary_span.0.events.dropped_count += dropped_events_count; + let Ok(name) = name.try_cast() else { + return; + }; + let name = owned_string(scope, name); + let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + else { + return; + }; + let mut state = span.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + span.name = Cow::Owned(name) } } -#[op2(fast)] -fn op_otel_span_flush(state: &mut OpState) { - let Some(temporary_span) = state.try_take::() else { - return; - }; +struct OtelMeter(opentelemetry::metrics::Meter); - let Some(Processors { spans, .. }) = OTEL_PROCESSORS.get() else { - return; - }; +impl deno_core::GarbageCollected for OtelMeter {} - spans.on_end(temporary_span.0); -} +#[op2] +impl OtelMeter { + #[constructor] + #[cppgc] + fn new( + #[string] name: String, + #[string] version: Option, + #[string] schema_url: Option, + ) -> OtelMeter { + let mut builder = opentelemetry::InstrumentationScope::builder(name); + if let Some(version) = version { + builder = builder.with_version(version); + } + if let Some(schema_url) = schema_url { + builder = builder.with_schema_url(schema_url); + } + let scope = builder.build(); + let meter = OTEL_GLOBALS + .get() + .unwrap() + .meter_provider + .meter_with_scope(scope); + OtelMeter(meter) + } -// Holds data being built from JS before -// it is submitted to the rust processor. -struct TemporaryMetricsExport { - resource_attributes: Vec, - scope_metrics: Vec, - metric: Option, -} + #[cppgc] + fn create_counter<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_instrument( + |name| self.0.f64_counter(name), + |i| Instrument::Counter(i.build()), + scope, + name, + description, + unit, + ) + .map_err(|e| JsErrorBox::generic(e.to_string())) + } -struct TemporaryMetric { - name: String, - description: String, - unit: String, - data: TemporaryMetricData, -} + #[cppgc] + fn create_up_down_counter<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_instrument( + |name| self.0.f64_up_down_counter(name), + |i| Instrument::UpDownCounter(i.build()), + scope, + name, + description, + unit, + ) + .map_err(|e| JsErrorBox::generic(e.to_string())) + } -enum TemporaryMetricData { - Sum(opentelemetry_sdk::metrics::data::Sum), - Gauge(opentelemetry_sdk::metrics::data::Gauge), - Histogram(opentelemetry_sdk::metrics::data::Histogram), -} + #[cppgc] + fn create_gauge<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_instrument( + |name| self.0.f64_gauge(name), + |i| Instrument::Gauge(i.build()), + scope, + name, + description, + unit, + ) + .map_err(|e| JsErrorBox::generic(e.to_string())) + } -impl From for Metric { - fn from(value: TemporaryMetric) -> Self { - Metric { - name: Cow::Owned(value.name), - description: Cow::Owned(value.description), - unit: Cow::Owned(value.unit), - data: match value.data { - TemporaryMetricData::Sum(sum) => Box::new(sum), - TemporaryMetricData::Gauge(gauge) => Box::new(gauge), - TemporaryMetricData::Histogram(histogram) => Box::new(histogram), + #[cppgc] + fn create_histogram<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + #[serde] boundaries: Option>, + ) -> Result { + let name = owned_string( + scope, + name + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); + let mut builder = self.0.f64_histogram(name); + if !description.is_null_or_undefined() { + let description = owned_string( + scope, + description + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); + builder = builder.with_description(description); + }; + if !unit.is_null_or_undefined() { + let unit = owned_string( + scope, + unit + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); + builder = builder.with_unit(unit); + }; + if let Some(boundaries) = boundaries { + builder = builder.with_boundaries(boundaries); + } + + Ok(Instrument::Histogram(builder.build())) + } + + #[cppgc] + fn create_observable_counter<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_async_instrument( + |name| self.0.f64_observable_counter(name), + |i| { + i.build(); }, - } + scope, + name, + description, + unit, + ) + .map_err(|e| JsErrorBox::generic(e.to_string())) + } + + #[cppgc] + fn create_observable_up_down_counter<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_async_instrument( + |name| self.0.f64_observable_up_down_counter(name), + |i| { + i.build(); + }, + scope, + name, + description, + unit, + ) + .map_err(|e| JsErrorBox::generic(e.to_string())) + } + + #[cppgc] + fn create_observable_gauge<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_async_instrument( + |name| self.0.f64_observable_gauge(name), + |i| { + i.build(); + }, + scope, + name, + description, + unit, + ) + .map_err(|e| JsErrorBox::generic(e.to_string())) } } -#[op2(fast)] -fn op_otel_metrics_resource_attribute<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, - key: v8::Local<'s, v8::Value>, - value: v8::Local<'s, v8::Value>, -) { - let metrics_export = if let Some(metrics_export) = - state.try_borrow_mut::() - { - metrics_export.resource_attributes.reserve_exact( - (capacity as usize) - metrics_export.resource_attributes.capacity(), - ); - metrics_export - } else { - state.put(TemporaryMetricsExport { - resource_attributes: Vec::with_capacity(capacity as usize), - scope_metrics: vec![], - metric: None, - }); - state.borrow_mut() - }; - attr!(scope, metrics_export.resource_attributes, key, value); +enum Instrument { + Counter(opentelemetry::metrics::Counter), + UpDownCounter(opentelemetry::metrics::UpDownCounter), + Gauge(opentelemetry::metrics::Gauge), + Histogram(opentelemetry::metrics::Histogram), + Observable(Arc, f64>>>), } -#[op2(fast)] -fn op_otel_metrics_resource_attribute2<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, - key1: v8::Local<'s, v8::Value>, - value1: v8::Local<'s, v8::Value>, - key2: v8::Local<'s, v8::Value>, - value2: v8::Local<'s, v8::Value>, -) { - let metrics_export = if let Some(metrics_export) = - state.try_borrow_mut::() - { - metrics_export.resource_attributes.reserve_exact( - (capacity as usize) - metrics_export.resource_attributes.capacity(), - ); - metrics_export - } else { - state.put(TemporaryMetricsExport { - resource_attributes: Vec::with_capacity(capacity as usize), - scope_metrics: vec![], - metric: None, - }); - state.borrow_mut() +impl GarbageCollected for Instrument {} + +fn create_instrument<'a, 'b, T>( + cb: impl FnOnce(String) -> InstrumentBuilder<'b, T>, + cb2: impl FnOnce(InstrumentBuilder<'b, T>) -> Instrument, + scope: &mut v8::HandleScope<'a>, + name: v8::Local<'a, v8::Value>, + description: v8::Local<'a, v8::Value>, + unit: v8::Local<'a, v8::Value>, +) -> Result { + let name = owned_string(scope, name.try_cast()?); + let mut builder = cb(name); + if !description.is_null_or_undefined() { + let description = owned_string(scope, description.try_cast()?); + builder = builder.with_description(description); }; - attr!(scope, metrics_export.resource_attributes, key1, value1); - attr!(scope, metrics_export.resource_attributes, key2, value2); + if !unit.is_null_or_undefined() { + let unit = owned_string(scope, unit.try_cast()?); + builder = builder.with_unit(unit); + }; + + Ok(cb2(builder)) } -#[allow(clippy::too_many_arguments)] -#[op2(fast)] -fn op_otel_metrics_resource_attribute3<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, - key1: v8::Local<'s, v8::Value>, - value1: v8::Local<'s, v8::Value>, - key2: v8::Local<'s, v8::Value>, - value2: v8::Local<'s, v8::Value>, - key3: v8::Local<'s, v8::Value>, - value3: v8::Local<'s, v8::Value>, -) { - let metrics_export = if let Some(metrics_export) = - state.try_borrow_mut::() - { - metrics_export.resource_attributes.reserve_exact( - (capacity as usize) - metrics_export.resource_attributes.capacity(), - ); - metrics_export - } else { - state.put(TemporaryMetricsExport { - resource_attributes: Vec::with_capacity(capacity as usize), - scope_metrics: vec![], - metric: None, - }); - state.borrow_mut() +fn create_async_instrument<'a, 'b, T>( + cb: impl FnOnce(String) -> AsyncInstrumentBuilder<'b, T, f64>, + cb2: impl FnOnce(AsyncInstrumentBuilder<'b, T, f64>), + scope: &mut v8::HandleScope<'a>, + name: v8::Local<'a, v8::Value>, + description: v8::Local<'a, v8::Value>, + unit: v8::Local<'a, v8::Value>, +) -> Result { + let name = owned_string(scope, name.try_cast()?); + let mut builder = cb(name); + if !description.is_null_or_undefined() { + let description = owned_string(scope, description.try_cast()?); + builder = builder.with_description(description); }; - attr!(scope, metrics_export.resource_attributes, key1, value1); - attr!(scope, metrics_export.resource_attributes, key2, value2); - attr!(scope, metrics_export.resource_attributes, key3, value3); -} - -#[op2(fast)] -fn op_otel_metrics_scope<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - name: v8::Local<'s, v8::Value>, - schema_url: v8::Local<'s, v8::Value>, - version: v8::Local<'s, v8::Value>, -) { - let name = owned_string(scope, name.cast()); - - let scope_builder = opentelemetry::InstrumentationScope::builder(name); - let scope_builder = if schema_url.is_null_or_undefined() { - scope_builder - } else { - scope_builder.with_schema_url(owned_string(scope, schema_url.cast())) - }; - let scope_builder = if version.is_null_or_undefined() { - scope_builder - } else { - scope_builder.with_version(owned_string(scope, version.cast())) - }; - let scope = scope_builder.build(); - let scope_metric = ScopeMetrics { - scope, - metrics: vec![], + if !unit.is_null_or_undefined() { + let unit = owned_string(scope, unit.try_cast()?); + builder = builder.with_unit(unit); }; - match state.try_borrow_mut::() { - Some(temp) => { - if let Some(current_metric) = temp.metric.take() { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); - } - temp.scope_metrics.push(scope_metric); + let data_share = Arc::new(Mutex::new(HashMap::new())); + let data_share_: Arc, f64>>> = data_share.clone(); + builder = builder.with_callback(move |i| { + let data = { + let mut data = data_share_.lock().unwrap(); + std::mem::take(&mut *data) + }; + for (attributes, value) in data { + i.observe(value, &attributes); } - None => { - state.put(TemporaryMetricsExport { - resource_attributes: vec![], - scope_metrics: vec![scope_metric], - metric: None, - }); - } - } -} - -#[op2(fast)] -fn op_otel_metrics_sum<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, - #[smi] temporality: u8, - is_monotonic: bool, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(current_metric) = temp.metric.take() { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); - } - - let name = owned_string(scope, name.cast()); - let description = owned_string(scope, description.cast()); - let unit = owned_string(scope, unit.cast()); - let temporality = match temporality { - 0 => Temporality::Delta, - 1 => Temporality::Cumulative, - _ => return, - }; - let sum = opentelemetry_sdk::metrics::data::Sum { - data_points: vec![], - temporality, - is_monotonic, - }; - - temp.metric = Some(TemporaryMetric { - name, - description, - unit, - data: TemporaryMetricData::Sum(sum), }); + cb2(builder); + + Ok(Instrument::Observable(data_share)) +} + +struct MetricAttributes { + attributes: Vec, } #[op2(fast)] -fn op_otel_metrics_gauge<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(current_metric) = temp.metric.take() { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); - } - - let name = owned_string(scope, name.cast()); - let description = owned_string(scope, description.cast()); - let unit = owned_string(scope, unit.cast()); - - let gauge = opentelemetry_sdk::metrics::data::Gauge { - data_points: vec![], - }; - - temp.metric = Some(TemporaryMetric { - name, - description, - unit, - data: TemporaryMetricData::Gauge(gauge), - }); -} - -#[op2(fast)] -fn op_otel_metrics_sum_or_gauge_data_point( +fn op_otel_metric_record0( state: &mut OpState, + #[cppgc] instrument: &Instrument, value: f64, - start_time: f64, - time: f64, ) { - let Some(temp) = state.try_borrow_mut::() else { - return; + let values = state.try_take::(); + let attributes = match &values { + Some(values) => &*values.attributes, + None => &[], }; - - let start_time = SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(start_time)) - .unwrap(); - let time = SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(time)) - .unwrap(); - - let data_point = opentelemetry_sdk::metrics::data::DataPoint { - value, - start_time: Some(start_time), - time: Some(time), - attributes: vec![], - exemplars: vec![], - }; - - match &mut temp.metric { - Some(TemporaryMetric { - data: TemporaryMetricData::Sum(sum), - .. - }) => sum.data_points.push(data_point), - Some(TemporaryMetric { - data: TemporaryMetricData::Gauge(gauge), - .. - }) => gauge.data_points.push(data_point), + match instrument { + Instrument::Counter(counter) => counter.add(value, attributes), + Instrument::UpDownCounter(counter) => counter.add(value, attributes), + Instrument::Gauge(gauge) => gauge.record(value, attributes), + Instrument::Histogram(histogram) => histogram.record(value, attributes), _ => {} } } #[op2(fast)] -fn op_otel_metrics_histogram<'s>( - scope: &mut v8::HandleScope<'s>, +fn op_otel_metric_record1( state: &mut OpState, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, - #[smi] temporality: u8, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, ) { - let Some(temp) = state.try_borrow_mut::() else { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { return; }; - - if let Some(current_metric) = temp.metric.take() { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); - } - - let name = owned_string(scope, name.cast()); - let description = owned_string(scope, description.cast()); - let unit = owned_string(scope, unit.cast()); - - let temporality = match temporality { - 0 => Temporality::Delta, - 1 => Temporality::Cumulative, - _ => return, - }; - let histogram = opentelemetry_sdk::metrics::data::Histogram { - data_points: vec![], - temporality, - }; - - temp.metric = Some(TemporaryMetric { - name, - description, - unit, - data: TemporaryMetricData::Histogram(histogram), - }); -} - -#[allow(clippy::too_many_arguments)] -#[op2(fast)] -fn op_otel_metrics_histogram_data_point( - state: &mut OpState, - #[number] count: u64, - min: f64, - max: f64, - sum: f64, - start_time: f64, - time: f64, - #[smi] buckets: u32, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - let min = if min.is_nan() { None } else { Some(min) }; - let max = if max.is_nan() { None } else { Some(max) }; - - let start_time = SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(start_time)) - .unwrap(); - let time = SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(time)) - .unwrap(); - - let data_point = opentelemetry_sdk::metrics::data::HistogramDataPoint { - bounds: Vec::with_capacity(buckets as usize), - bucket_counts: Vec::with_capacity((buckets as usize) + 1), - count, - sum, - min, - max, - start_time, - time, - attributes: vec![], - exemplars: vec![], - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - histogram.data_points.push(data_point); - } -} - -#[op2(fast)] -fn op_otel_metrics_histogram_data_point_entry_final( - state: &mut OpState, - #[number] count1: u64, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - histogram - .data_points - .last_mut() - .unwrap() - .bucket_counts - .push(count1) - } -} - -#[op2(fast)] -fn op_otel_metrics_histogram_data_point_entry1( - state: &mut OpState, - #[number] count1: u64, - bound1: f64, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - let data_point = histogram.data_points.last_mut().unwrap(); - data_point.bucket_counts.push(count1); - data_point.bounds.push(bound1); - } -} - -#[op2(fast)] -fn op_otel_metrics_histogram_data_point_entry2( - state: &mut OpState, - #[number] count1: u64, - bound1: f64, - #[number] count2: u64, - bound2: f64, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - let data_point = histogram.data_points.last_mut().unwrap(); - data_point.bucket_counts.push(count1); - data_point.bounds.push(bound1); - data_point.bucket_counts.push(count2); - data_point.bounds.push(bound2); - } -} - -#[op2(fast)] -fn op_otel_metrics_histogram_data_point_entry3( - state: &mut OpState, - #[number] count1: u64, - bound1: f64, - #[number] count2: u64, - bound2: f64, - #[number] count3: u64, - bound3: f64, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - let data_point = histogram.data_points.last_mut().unwrap(); - data_point.bucket_counts.push(count1); - data_point.bounds.push(bound1); - data_point.bucket_counts.push(count2); - data_point.bounds.push(bound2); - data_point.bucket_counts.push(count3); - data_point.bounds.push(bound3); - } -} - -#[op2(fast)] -fn op_otel_metrics_data_point_attribute<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, - key: v8::Local<'s, v8::Value>, - value: v8::Local<'s, v8::Value>, -) { - if let Some(TemporaryMetricsExport { - metric: Some(metric), - .. - }) = state.try_borrow_mut::() - { - let attributes = match &mut metric.data { - TemporaryMetricData::Sum(sum) => { - &mut sum.data_points.last_mut().unwrap().attributes + let mut values = state.try_take::(); + let attr1 = attr_raw!(scope, key1, value1); + let attributes = match &mut values { + Some(values) => { + if let Some(kv) = attr1 { + values.attributes.reserve_exact(1); + values.attributes.push(kv); } - TemporaryMetricData::Gauge(gauge) => { - &mut gauge.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Histogram(histogram) => { - &mut histogram.data_points.last_mut().unwrap().attributes - } - }; - attributes.reserve_exact((capacity as usize) - attributes.capacity()); - attr!(scope, attributes, key, value); - } -} - -#[op2(fast)] -fn op_otel_metrics_data_point_attribute2<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, - key1: v8::Local<'s, v8::Value>, - value1: v8::Local<'s, v8::Value>, - key2: v8::Local<'s, v8::Value>, - value2: v8::Local<'s, v8::Value>, -) { - if let Some(TemporaryMetricsExport { - metric: Some(metric), - .. - }) = state.try_borrow_mut::() - { - let attributes = match &mut metric.data { - TemporaryMetricData::Sum(sum) => { - &mut sum.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Gauge(gauge) => { - &mut gauge.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Histogram(histogram) => { - &mut histogram.data_points.last_mut().unwrap().attributes - } - }; - attributes.reserve_exact((capacity as usize) - attributes.capacity()); - attr!(scope, attributes, key1, value1); - attr!(scope, attributes, key2, value2); + &*values.attributes + } + None => match attr1 { + Some(kv1) => &[kv1] as &[KeyValue], + None => &[], + }, + }; + match &*instrument { + Instrument::Counter(counter) => counter.add(value, attributes), + Instrument::UpDownCounter(counter) => counter.add(value, attributes), + Instrument::Gauge(gauge) => gauge.record(value, attributes), + Instrument::Histogram(histogram) => histogram.record(value, attributes), + _ => {} } } #[allow(clippy::too_many_arguments)] #[op2(fast)] -fn op_otel_metrics_data_point_attribute3<'s>( +fn op_otel_metric_record2( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, + key2: v8::Local<'_, v8::Value>, + value2: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let mut values = state.try_take::(); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + let attributes = match &mut values { + Some(values) => { + values.attributes.reserve_exact(2); + if let Some(kv1) = attr1 { + values.attributes.push(kv1); + } + if let Some(kv2) = attr2 { + values.attributes.push(kv2); + } + &*values.attributes + } + None => match (attr1, attr2) { + (Some(kv1), Some(kv2)) => &[kv1, kv2] as &[KeyValue], + (Some(kv1), None) => &[kv1], + (None, Some(kv2)) => &[kv2], + (None, None) => &[], + }, + }; + match &*instrument { + Instrument::Counter(counter) => counter.add(value, attributes), + Instrument::UpDownCounter(counter) => counter.add(value, attributes), + Instrument::Gauge(gauge) => gauge.record(value, attributes), + Instrument::Histogram(histogram) => histogram.record(value, attributes), + _ => {} + } +} + +#[allow(clippy::too_many_arguments)] +#[op2(fast)] +fn op_otel_metric_record3( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, + key2: v8::Local<'_, v8::Value>, + value2: v8::Local<'_, v8::Value>, + key3: v8::Local<'_, v8::Value>, + value3: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let mut values = state.try_take::(); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + let attr3 = attr_raw!(scope, key3, value3); + let attributes = match &mut values { + Some(values) => { + values.attributes.reserve_exact(3); + if let Some(kv1) = attr1 { + values.attributes.push(kv1); + } + if let Some(kv2) = attr2 { + values.attributes.push(kv2); + } + if let Some(kv3) = attr3 { + values.attributes.push(kv3); + } + &*values.attributes + } + None => match (attr1, attr2, attr3) { + (Some(kv1), Some(kv2), Some(kv3)) => &[kv1, kv2, kv3] as &[KeyValue], + (Some(kv1), Some(kv2), None) => &[kv1, kv2], + (Some(kv1), None, Some(kv3)) => &[kv1, kv3], + (None, Some(kv2), Some(kv3)) => &[kv2, kv3], + (Some(kv1), None, None) => &[kv1], + (None, Some(kv2), None) => &[kv2], + (None, None, Some(kv3)) => &[kv3], + (None, None, None) => &[], + }, + }; + match &*instrument { + Instrument::Counter(counter) => counter.add(value, attributes), + Instrument::UpDownCounter(counter) => counter.add(value, attributes), + Instrument::Gauge(gauge) => gauge.record(value, attributes), + Instrument::Histogram(histogram) => histogram.record(value, attributes), + _ => {} + } +} + +#[op2(fast)] +fn op_otel_metric_observable_record0( + state: &mut OpState, + #[cppgc] instrument: &Instrument, + value: f64, +) { + let values = state.try_take::(); + let attributes = values.map(|attr| attr.attributes).unwrap_or_default(); + if let Instrument::Observable(data_share) = instrument { + let mut data = data_share.lock().unwrap(); + data.insert(attributes, value); + } +} + +#[op2(fast)] +fn op_otel_metric_observable_record1( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let values = state.try_take::(); + let attr1 = attr_raw!(scope, key1, value1); + let mut attributes = values + .map(|mut attr| { + attr.attributes.reserve_exact(1); + attr.attributes + }) + .unwrap_or_else(|| Vec::with_capacity(1)); + if let Some(kv1) = attr1 { + attributes.push(kv1); + } + if let Instrument::Observable(data_share) = &*instrument { + let mut data = data_share.lock().unwrap(); + data.insert(attributes, value); + } +} + +#[allow(clippy::too_many_arguments)] +#[op2(fast)] +fn op_otel_metric_observable_record2( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, + key2: v8::Local<'_, v8::Value>, + value2: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let values = state.try_take::(); + let mut attributes = values + .map(|mut attr| { + attr.attributes.reserve_exact(2); + attr.attributes + }) + .unwrap_or_else(|| Vec::with_capacity(2)); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + if let Some(kv1) = attr1 { + attributes.push(kv1); + } + if let Some(kv2) = attr2 { + attributes.push(kv2); + } + if let Instrument::Observable(data_share) = &*instrument { + let mut data = data_share.lock().unwrap(); + data.insert(attributes, value); + } +} + +#[allow(clippy::too_many_arguments)] +#[op2(fast)] +fn op_otel_metric_observable_record3( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, + key2: v8::Local<'_, v8::Value>, + value2: v8::Local<'_, v8::Value>, + key3: v8::Local<'_, v8::Value>, + value3: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let values = state.try_take::(); + let mut attributes = values + .map(|mut attr| { + attr.attributes.reserve_exact(3); + attr.attributes + }) + .unwrap_or_else(|| Vec::with_capacity(3)); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + let attr3 = attr_raw!(scope, key3, value3); + if let Some(kv1) = attr1 { + attributes.push(kv1); + } + if let Some(kv2) = attr2 { + attributes.push(kv2); + } + if let Some(kv3) = attr3 { + attributes.push(kv3); + } + if let Instrument::Observable(data_share) = &*instrument { + let mut data = data_share.lock().unwrap(); + data.insert(attributes, value); + } +} + +#[allow(clippy::too_many_arguments)] +#[op2(fast)] +fn op_otel_metric_attribute3<'s>( scope: &mut v8::HandleScope<'s>, state: &mut OpState, #[smi] capacity: u32, @@ -1526,49 +2018,60 @@ fn op_otel_metrics_data_point_attribute3<'s>( key3: v8::Local<'s, v8::Value>, value3: v8::Local<'s, v8::Value>, ) { - if let Some(TemporaryMetricsExport { - metric: Some(metric), - .. - }) = state.try_borrow_mut::() + let mut values = state.try_borrow_mut::(); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + let attr3 = attr_raw!(scope, key3, value3); + if let Some(values) = &mut values { + values.attributes.reserve_exact( + (capacity as usize).saturating_sub(values.attributes.capacity()), + ); + if let Some(kv1) = attr1 { + values.attributes.push(kv1); + } + if let Some(kv2) = attr2 { + values.attributes.push(kv2); + } + if let Some(kv3) = attr3 { + values.attributes.push(kv3); + } + } else { + let mut attributes = Vec::with_capacity(capacity as usize); + if let Some(kv1) = attr1 { + attributes.push(kv1); + } + if let Some(kv2) = attr2 { + attributes.push(kv2); + } + if let Some(kv3) = attr3 { + attributes.push(kv3); + } + state.put(MetricAttributes { attributes }); + } +} + +struct ObservationDone(oneshot::Sender<()>); + +#[op2(async)] +async fn op_otel_metric_wait_to_observe(state: Rc>) -> bool { + let (tx, rx) = oneshot::channel(); { - let attributes = match &mut metric.data { - TemporaryMetricData::Sum(sum) => { - &mut sum.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Gauge(gauge) => { - &mut gauge.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Histogram(histogram) => { - &mut histogram.data_points.last_mut().unwrap().attributes - } - }; - attributes.reserve_exact((capacity as usize) - attributes.capacity()); - attr!(scope, attributes, key1, value1); - attr!(scope, attributes, key2, value2); - attr!(scope, attributes, key3, value3); + OTEL_PRE_COLLECT_CALLBACKS + .lock() + .expect("mutex poisoned") + .push(tx); + } + if let Ok(done) = rx.await { + state.borrow_mut().put(ObservationDone(done)); + true + } else { + false } } #[op2(fast)] -fn op_otel_metrics_submit(state: &mut OpState) { - let Some(mut temp) = state.try_take::() else { - return; - }; - - let Some(Processors { metrics, .. }) = OTEL_PROCESSORS.get() else { - return; - }; - - if let Some(current_metric) = temp.metric { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); +fn op_otel_metric_observation_done(state: &mut OpState) { + if let Some(ObservationDone(done)) = state.try_take::() { + let _ = done.send(()); } - - let resource = Resource::new(temp.resource_attributes); - let scope_metrics = temp.scope_metrics; - - metrics.submit(ResourceMetrics { - resource, - scope_metrics, - }); } diff --git a/ext/telemetry/telemetry.ts b/ext/telemetry/telemetry.ts index d1335f65b5..bea16f49d4 100644 --- a/ext/telemetry/telemetry.ts +++ b/ext/telemetry/telemetry.ts @@ -1,61 +1,53 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { - op_crypto_get_random_values, - op_otel_instrumentation_scope_create_and_enter, - op_otel_instrumentation_scope_enter, - op_otel_instrumentation_scope_enter_builtin, op_otel_log, - op_otel_metrics_data_point_attribute, - op_otel_metrics_data_point_attribute2, - op_otel_metrics_data_point_attribute3, - op_otel_metrics_gauge, - op_otel_metrics_histogram, - op_otel_metrics_histogram_data_point, - op_otel_metrics_histogram_data_point_entry1, - op_otel_metrics_histogram_data_point_entry2, - op_otel_metrics_histogram_data_point_entry3, - op_otel_metrics_histogram_data_point_entry_final, - op_otel_metrics_resource_attribute, - op_otel_metrics_resource_attribute2, - op_otel_metrics_resource_attribute3, - op_otel_metrics_scope, - op_otel_metrics_submit, - op_otel_metrics_sum, - op_otel_metrics_sum_or_gauge_data_point, - op_otel_span_attribute, + op_otel_log_foreign, + op_otel_metric_attribute3, + op_otel_metric_observable_record0, + op_otel_metric_observable_record1, + op_otel_metric_observable_record2, + op_otel_metric_observable_record3, + op_otel_metric_observation_done, + op_otel_metric_record0, + op_otel_metric_record1, + op_otel_metric_record2, + op_otel_metric_record3, + op_otel_metric_wait_to_observe, + op_otel_span_attribute1, op_otel_span_attribute2, op_otel_span_attribute3, - op_otel_span_continue, - op_otel_span_flush, - op_otel_span_set_dropped, - op_otel_span_start, + op_otel_span_update_name, + OtelMeter, + OtelSpan, + OtelTracer, } from "ext:core/ops"; import { Console } from "ext:deno_console/01_console.js"; -import { performance } from "ext:deno_web/15_performance.js"; const { - SafeWeakMap, - Array, - ObjectEntries, - ReflectApply, - SymbolFor, + ArrayIsArray, + ArrayPrototypePush, + DatePrototype, + DatePrototypeGetTime, Error, - Uint8Array, - TypedArrayPrototypeSubarray, - ObjectAssign, ObjectDefineProperty, - WeakRefPrototypeDeref, - String, - StringPrototypePadStart, + ObjectEntries, + ObjectKeys, ObjectPrototypeIsPrototypeOf, - SafeWeakRef, + ReflectApply, + SafeIterator, + SafeMap, + SafePromiseAll, + SafeSet, + SafeWeakSet, + SymbolFor, + TypeError, } = primordials; const { AsyncVariable, setAsyncContext } = core; export let TRACING_ENABLED = false; -let DETERMINISTIC = false; +export let METRICS_ENABLED = false; // Note: These start at 0 in the JS library, // but start at 1 when serialized with JSON. @@ -82,8 +74,6 @@ interface SpanContext { traceState?: TraceState; } -type HrTime = [number, number]; - enum SpanStatusCode { UNSET = 0, OK = 1, @@ -95,7 +85,7 @@ interface SpanStatus { message?: string; } -export type AttributeValue = +type AttributeValue = | string | number | boolean @@ -109,9 +99,14 @@ interface Attributes { type SpanAttributes = Attributes; +type TimeInput = [number, number] | number | Date; + interface SpanOptions { - attributes?: Attributes; kind?: SpanKind; + attributes?: Attributes; + links?: Link[]; + startTime?: TimeInput; + root?: boolean; } interface Link { @@ -120,13 +115,6 @@ interface Link { droppedAttributesCount?: number; } -interface TimedEvent { - time: HrTime; - name: string; - attributes?: SpanAttributes; - droppedAttributesCount?: number; -} - interface IArrayValue { values: IAnyValue[]; } @@ -149,477 +137,322 @@ interface IKeyValue { key: string; value: IAnyValue; } -interface IResource { - attributes: IKeyValue[]; - droppedAttributesCount: number; -} - -interface InstrumentationLibrary { - readonly name: string; - readonly version?: string; - readonly schemaUrl?: string; -} - -interface ReadableSpan { - readonly name: string; - readonly kind: SpanKind; - readonly spanContext: () => SpanContext; - readonly parentSpanId?: string; - readonly startTime: HrTime; - readonly endTime: HrTime; - readonly status: SpanStatus; - readonly attributes: SpanAttributes; - readonly links: Link[]; - readonly events: TimedEvent[]; - readonly duration: HrTime; - readonly ended: boolean; - readonly resource: IResource; - readonly instrumentationLibrary: InstrumentationLibrary; - readonly droppedAttributesCount: number; - readonly droppedEventsCount: number; - readonly droppedLinksCount: number; -} - -enum ExportResultCode { - SUCCESS = 0, - FAILED = 1, -} - -interface ExportResult { - code: ExportResultCode; - error?: Error; -} function hrToSecs(hr: [number, number]): number { - return ((hr[0] * 1e3 + hr[1] / 1e6) / 1000); + return (hr[0] * 1e3 + hr[1] / 1e6) / 1000; } -const TRACE_FLAG_SAMPLED = 1 << 0; - -const instrumentationScopes = new SafeWeakMap< - InstrumentationLibrary, - { __key: "instrumentation-library" } ->(); -let activeInstrumentationLibrary: WeakRef | null = null; - -function submitSpan( - spanId: string | Uint8Array, - traceId: string | Uint8Array, - traceFlags: number, - parentSpanId: string | Uint8Array | null, - span: Omit< - ReadableSpan, - | "spanContext" - | "startTime" - | "endTime" - | "parentSpanId" - | "duration" - | "ended" - | "resource" - >, - startTime: number, - endTime: number, -) { - if (!TRACING_ENABLED) return; - if (!(traceFlags & TRACE_FLAG_SAMPLED)) return; - - // TODO(@lucacasonato): `resource` is ignored for now, should we implement it? - - const instrumentationLibrary = span.instrumentationLibrary; - if ( - !activeInstrumentationLibrary || - WeakRefPrototypeDeref(activeInstrumentationLibrary) !== - instrumentationLibrary - ) { - activeInstrumentationLibrary = new SafeWeakRef(instrumentationLibrary); - if (instrumentationLibrary === BUILTIN_INSTRUMENTATION_LIBRARY) { - op_otel_instrumentation_scope_enter_builtin(); - } else { - let instrumentationScope = instrumentationScopes - .get(instrumentationLibrary); - - if (instrumentationScope === undefined) { - instrumentationScope = op_otel_instrumentation_scope_create_and_enter( - instrumentationLibrary.name, - instrumentationLibrary.version, - instrumentationLibrary.schemaUrl, - ) as { __key: "instrumentation-library" }; - instrumentationScopes.set( - instrumentationLibrary, - instrumentationScope, - ); - } else { - op_otel_instrumentation_scope_enter( - instrumentationScope, - ); - } - } - } - - op_otel_span_start( - traceId, - spanId, - parentSpanId, - span.kind, - span.name, - startTime, - endTime, - ); - - const status = span.status; - if (status !== null && status.code !== 0) { - op_otel_span_continue(status.code, status.message ?? ""); - } - - const attributeKvs = ObjectEntries(span.attributes); - let i = 0; - while (i < attributeKvs.length) { - if (i + 2 < attributeKvs.length) { - op_otel_span_attribute3( - attributeKvs.length, - attributeKvs[i][0], - attributeKvs[i][1], - attributeKvs[i + 1][0], - attributeKvs[i + 1][1], - attributeKvs[i + 2][0], - attributeKvs[i + 2][1], - ); - i += 3; - } else if (i + 1 < attributeKvs.length) { - op_otel_span_attribute2( - attributeKvs.length, - attributeKvs[i][0], - attributeKvs[i][1], - attributeKvs[i + 1][0], - attributeKvs[i + 1][1], - ); - i += 2; - } else { - op_otel_span_attribute( - attributeKvs.length, - attributeKvs[i][0], - attributeKvs[i][1], - ); - i += 1; - } - } - - // TODO(@lucacasonato): implement links - // TODO(@lucacasonato): implement events - - const droppedAttributesCount = span.droppedAttributesCount; - const droppedLinksCount = span.droppedLinksCount + span.links.length; - const droppedEventsCount = span.droppedEventsCount + span.events.length; - if ( - droppedAttributesCount > 0 || droppedLinksCount > 0 || - droppedEventsCount > 0 - ) { - op_otel_span_set_dropped( - droppedAttributesCount, - droppedLinksCount, - droppedEventsCount, - ); - } - - op_otel_span_flush(); +export function enterSpan(span: Span): Context | undefined { + if (!span.isRecording()) return undefined; + const context = (CURRENT.get() || ROOT_CONTEXT).setValue(SPAN_KEY, span); + return CURRENT.enter(context); } -const now = () => (performance.timeOrigin + performance.now()) / 1000; +export function restoreContext(context: Context): void { + setAsyncContext(context); +} -const SPAN_ID_BYTES = 8; -const TRACE_ID_BYTES = 16; +function isDate(value: unknown): value is Date { + return ObjectPrototypeIsPrototypeOf(value, DatePrototype); +} -const INVALID_TRACE_ID = new Uint8Array(TRACE_ID_BYTES); -const INVALID_SPAN_ID = new Uint8Array(SPAN_ID_BYTES); +interface OtelTracer { + __key: "tracer"; -const NO_ASYNC_CONTEXT = {}; + // deno-lint-ignore no-misused-new + new (name: string, version?: string, schemaUrl?: string): OtelTracer; -let otelLog: (message: string, level: number) => void; + startSpan( + parent: OtelSpan | undefined, + name: string, + spanKind: SpanKind, + startTime: number | undefined, + attributeCount: number, + ): OtelSpan; -const hexSliceLookupTable = (function () { - const alphabet = "0123456789abcdef"; - const table = new Array(256); - for (let i = 0; i < 16; ++i) { - const i16 = i * 16; - for (let j = 0; j < 16; ++j) { - table[i16 + j] = alphabet[i] + alphabet[j]; + startSpanForeign( + parentTraceId: string, + parentSpanId: string, + name: string, + spanKind: SpanKind, + startTime: number | undefined, + attributeCount: number, + ): OtelSpan; +} + +interface OtelSpan { + __key: "span"; + + spanContext(): SpanContext; + setStatus(status: SpanStatusCode, errorDescription: string): void; + dropEvent(): void; + dropLink(): void; + end(endTime: number): void; +} + +interface TracerOptions { + schemaUrl?: string; +} + +class TracerProvider { + constructor() { + throw new TypeError("TracerProvider can not be constructed"); + } + + static getTracer( + name: string, + version?: string, + options?: TracerOptions, + ): Tracer { + const tracer = new OtelTracer(name, version, options?.schemaUrl); + return new Tracer(tracer); + } +} + +class Tracer { + #tracer: OtelTracer; + + constructor(tracer: OtelTracer) { + this.#tracer = tracer; + } + + startActiveSpan unknown>( + name: string, + fn: F, + ): ReturnType; + startActiveSpan unknown>( + name: string, + options: SpanOptions, + fn: F, + ): ReturnType; + startActiveSpan unknown>( + name: string, + options: SpanOptions, + context: Context, + fn: F, + ): ReturnType; + startActiveSpan unknown>( + name: string, + optionsOrFn: SpanOptions | F, + fnOrContext?: F | Context, + maybeFn?: F, + ) { + let options; + let context; + let fn; + if (typeof optionsOrFn === "function") { + options = undefined; + fn = optionsOrFn; + } else if (typeof fnOrContext === "function") { + options = optionsOrFn; + fn = fnOrContext; + } else if (typeof maybeFn === "function") { + options = optionsOrFn; + context = fnOrContext; + fn = maybeFn; + } else { + throw new Error("startActiveSpan requires a function argument"); + } + if (options?.root) { + context = undefined; + } else { + context = context ?? CURRENT.get(); + } + const span = this.startSpan(name, options, context); + const ctx = CURRENT.enter(context.setValue(SPAN_KEY, span)); + try { + return ReflectApply(fn, undefined, [span]); + } finally { + setAsyncContext(ctx); } } - return table; -})(); -function bytesToHex(bytes: Uint8Array): string { - let out = ""; - for (let i = 0; i < bytes.length; i += 1) { - out += hexSliceLookupTable[bytes[i]]; + startSpan(name: string, options?: SpanOptions, context?: Context): Span { + if (options?.root) { + context = undefined; + } else { + context = context ?? CURRENT.get(); + } + + let startTime = options?.startTime; + if (startTime && ArrayIsArray(startTime)) { + startTime = hrToSecs(startTime); + } else if (startTime && isDate(startTime)) { + startTime = DatePrototypeGetTime(startTime); + } + + const parentSpan = context?.getValue(SPAN_KEY) as + | Span + | { spanContext(): SpanContext } + | undefined; + const attributesCount = options?.attributes + ? ObjectKeys(options.attributes).length + : 0; + const parentOtelSpan: OtelSpan | null | undefined = parentSpan !== undefined + ? getOtelSpan(parentSpan) ?? undefined + : undefined; + let otelSpan: OtelSpan; + if (parentOtelSpan || !parentSpan) { + otelSpan = this.#tracer.startSpan( + parentOtelSpan, + name, + options?.kind ?? 0, + startTime, + attributesCount, + ); + } else { + const spanContext = parentSpan.spanContext(); + otelSpan = this.#tracer.startSpanForeign( + spanContext.traceId, + spanContext.spanId, + name, + options?.kind ?? 0, + startTime, + attributesCount, + ); + } + const span = new Span(otelSpan); + if (options?.links) span.addLinks(options?.links); + if (options?.attributes) span.setAttributes(options?.attributes); + return span; } - return out; } const SPAN_KEY = SymbolFor("OpenTelemetry Context Key SPAN"); -const BUILTIN_INSTRUMENTATION_LIBRARY: InstrumentationLibrary = {} as never; +let getOtelSpan: (span: object) => OtelSpan | null | undefined; -let COUNTER = 1; - -export let enterSpan: (span: Span) => void; -export let exitSpan: (span: Span) => void; -export let endSpan: (span: Span) => void; - -export class Span { - #traceId: string | Uint8Array; - #spanId: Uint8Array; - #traceFlags = TRACE_FLAG_SAMPLED; - - #spanContext: SpanContext | null = null; - - #parentSpanId: string | Uint8Array | null = null; - #parentSpanIdString: string | null = null; - - #recording = TRACING_ENABLED; - - #kind: number = SpanKind.INTERNAL; - #name: string; - #startTime: number; - #status: { code: number; message?: string } | null = null; - #attributes: Attributes = { __proto__: null } as never; - - #droppedEventsCount = 0; - #droppedLinksCount = 0; - - #asyncContext = NO_ASYNC_CONTEXT; +class Span { + #otelSpan: OtelSpan | null; + #spanContext: SpanContext | undefined; static { - otelLog = function otelLog(message, level) { - let traceId = null; - let spanId = null; - let traceFlags = 0; - const span = CURRENT.get()?.getValue(SPAN_KEY); - if (span) { - // The lint is wrong, we can not use anything but `in` here because this - // is a private field. - // deno-lint-ignore prefer-primordials - if (#traceId in span) { - traceId = span.#traceId; - spanId = span.#spanId; - traceFlags = span.#traceFlags; - } else { - const context = span.spanContext(); - traceId = context.traceId; - spanId = context.spanId; - traceFlags = context.traceFlags; - } - } - return op_otel_log(message, level, traceId, spanId, traceFlags); - }; - - enterSpan = (span: Span) => { - if (!span.#recording) return; - const context = (CURRENT.get() || ROOT_CONTEXT).setValue(SPAN_KEY, span); - span.#asyncContext = CURRENT.enter(context); - }; - - exitSpan = (span: Span) => { - if (!span.#recording) return; - if (span.#asyncContext === NO_ASYNC_CONTEXT) return; - setAsyncContext(span.#asyncContext); - span.#asyncContext = NO_ASYNC_CONTEXT; - }; - - endSpan = (span: Span) => { - const endTime = now(); - submitSpan( - span.#spanId, - span.#traceId, - span.#traceFlags, - span.#parentSpanId, - { - name: span.#name, - kind: span.#kind, - status: span.#status ?? { code: 0 }, - attributes: span.#attributes, - events: [], - links: [], - droppedAttributesCount: 0, - droppedEventsCount: span.#droppedEventsCount, - droppedLinksCount: span.#droppedLinksCount, - instrumentationLibrary: BUILTIN_INSTRUMENTATION_LIBRARY, - }, - span.#startTime, - endTime, - ); - }; + // deno-lint-ignore prefer-primordials + getOtelSpan = (span) => (#otelSpan in span ? span.#otelSpan : undefined); } - constructor( - name: string, - options?: SpanOptions, - ) { - if (!this.isRecording) { - this.#name = ""; - this.#startTime = 0; - this.#traceId = INVALID_TRACE_ID; - this.#spanId = INVALID_SPAN_ID; - this.#traceFlags = 0; - return; - } - - this.#name = name; - this.#startTime = now(); - this.#attributes = options?.attributes ?? { __proto__: null } as never; - this.#kind = options?.kind ?? SpanKind.INTERNAL; - - const currentSpan: Span | { - spanContext(): { traceId: string; spanId: string }; - } = CURRENT.get()?.getValue(SPAN_KEY); - if (currentSpan) { - if (DETERMINISTIC) { - this.#spanId = StringPrototypePadStart(String(COUNTER++), 16, "0"); - } else { - this.#spanId = new Uint8Array(SPAN_ID_BYTES); - op_crypto_get_random_values(this.#spanId); - } - // deno-lint-ignore prefer-primordials - if (#traceId in currentSpan) { - this.#traceId = currentSpan.#traceId; - this.#parentSpanId = currentSpan.#spanId; - } else { - const context = currentSpan.spanContext(); - this.#traceId = context.traceId; - this.#parentSpanId = context.spanId; - } - } else { - if (DETERMINISTIC) { - this.#traceId = StringPrototypePadStart(String(COUNTER++), 32, "0"); - this.#spanId = StringPrototypePadStart(String(COUNTER++), 16, "0"); - } else { - const buffer = new Uint8Array(TRACE_ID_BYTES + SPAN_ID_BYTES); - op_crypto_get_random_values(buffer); - this.#traceId = TypedArrayPrototypeSubarray(buffer, 0, TRACE_ID_BYTES); - this.#spanId = TypedArrayPrototypeSubarray(buffer, TRACE_ID_BYTES); - } - } + constructor(otelSpan: OtelSpan | null) { + this.#otelSpan = otelSpan; } spanContext() { if (!this.#spanContext) { - this.#spanContext = { - traceId: typeof this.#traceId === "string" - ? this.#traceId - : bytesToHex(this.#traceId), - spanId: typeof this.#spanId === "string" - ? this.#spanId - : bytesToHex(this.#spanId), - traceFlags: this.#traceFlags, - }; + if (this.#otelSpan) { + this.#spanContext = this.#otelSpan.spanContext(); + } else { + this.#spanContext = { + traceId: "00000000000000000000000000000000", + spanId: "0000000000000000", + traceFlags: 0, + }; + } } return this.#spanContext; } - get parentSpanId() { - if (!this.#parentSpanIdString && this.#parentSpanId) { - if (typeof this.#parentSpanId === "string") { - this.#parentSpanIdString = this.#parentSpanId; - } else { - this.#parentSpanIdString = bytesToHex(this.#parentSpanId); - } - } - return this.#parentSpanIdString; - } - - setAttribute(name: string, value: AttributeValue) { - if (this.#recording) this.#attributes[name] = value; + addEvent( + _name: string, + _attributesOrStartTime?: Attributes | TimeInput, + _startTime?: TimeInput, + ): Span { + this.#otelSpan?.dropEvent(); return this; } - setAttributes(attributes: Attributes) { - if (this.#recording) ObjectAssign(this.#attributes, attributes); + addLink(_link: Link): Span { + this.#otelSpan?.dropLink(); return this; } - setStatus(status: { code: number; message?: string }) { - if (this.#recording) { - if (status.code === 0) { - this.#status = null; - } else if (status.code > 2) { - throw new Error("Invalid status code"); - } else { - this.#status = status; - } + addLinks(links: Link[]): Span { + for (let i = 0; i < links.length; i++) { + this.#otelSpan?.dropLink(); } return this; } - updateName(name: string) { - if (this.#recording) this.#name = name; + end(endTime?: TimeInput): void { + if (endTime && ArrayIsArray(endTime)) { + endTime = hrToSecs(endTime); + } else if (endTime && isDate(endTime)) { + endTime = DatePrototypeGetTime(endTime); + } + this.#otelSpan?.end(endTime || NaN); + } + + isRecording(): boolean { + return this.#otelSpan !== undefined; + } + + // deno-lint-ignore no-explicit-any + recordException(_exception: any, _time?: TimeInput): Span { + this.#otelSpan?.dropEvent(); return this; } - addEvent(_name: never) { - // TODO(@lucacasonato): implement events - if (this.#recording) this.#droppedEventsCount += 1; + setAttribute(key: string, value: AttributeValue): Span { + if (!this.#otelSpan) return this; + op_otel_span_attribute1(this.#otelSpan, key, value); return this; } - addLink(_link: never) { - // TODO(@lucacasonato): implement links - if (this.#recording) this.#droppedLinksCount += 1; - return this; - } - - addLinks(links: never[]) { - // TODO(@lucacasonato): implement links - if (this.#recording) this.#droppedLinksCount += links.length; - return this; - } - - isRecording() { - return this.#recording; - } -} - -// Exporter compatible with opentelemetry js library -class SpanExporter { - export( - spans: ReadableSpan[], - resultCallback: (result: ExportResult) => void, - ) { - try { - for (let i = 0; i < spans.length; i += 1) { - const span = spans[i]; - const context = span.spanContext(); - submitSpan( - context.spanId, - context.traceId, - context.traceFlags, - span.parentSpanId ?? null, - span, - hrToSecs(span.startTime), - hrToSecs(span.endTime), + setAttributes(attributes: Attributes): Span { + if (!this.#otelSpan) return this; + const attributeKvs = ObjectEntries(attributes); + let i = 0; + while (i < attributeKvs.length) { + if (i + 2 < attributeKvs.length) { + op_otel_span_attribute3( + this.#otelSpan, + attributeKvs[i][0], + attributeKvs[i][1], + attributeKvs[i + 1][0], + attributeKvs[i + 1][1], + attributeKvs[i + 2][0], + attributeKvs[i + 2][1], ); + i += 3; + } else if (i + 1 < attributeKvs.length) { + op_otel_span_attribute2( + this.#otelSpan, + attributeKvs[i][0], + attributeKvs[i][1], + attributeKvs[i + 1][0], + attributeKvs[i + 1][1], + ); + i += 2; + } else { + op_otel_span_attribute1( + this.#otelSpan, + attributeKvs[i][0], + attributeKvs[i][1], + ); + i += 1; } - resultCallback({ code: 0 }); - } catch (error) { - resultCallback({ - code: 1, - error: ObjectPrototypeIsPrototypeOf(error, Error) - ? error as Error - : new Error(String(error)), - }); } + return this; } - async shutdown() {} + setStatus(status: SpanStatus): Span { + this.#otelSpan?.setStatus(status.code, status.message ?? ""); + return this; + } - async forceFlush() {} + updateName(name: string): Span { + if (!this.#otelSpan) return this; + op_otel_span_update_name(this.#otelSpan, name); + return this; + } } const CURRENT = new AsyncVariable(); class Context { + // @ts-ignore __proto__ is not supported in TypeScript #data: Record = { __proto__: null }; constructor(data?: Record | null | undefined) { + // @ts-ignore __proto__ is not supported in TypeScript this.#data = { __proto__: null, ...data }; } @@ -645,11 +478,15 @@ const ROOT_CONTEXT = new Context(); // Context manager for opentelemetry js library class ContextManager { - active(): Context { + constructor() { + throw new TypeError("ContextManager can not be constructed"); + } + + static active(): Context { return CURRENT.get() ?? ROOT_CONTEXT; } - with ReturnType>( + static with ReturnType>( context: Context, fn: F, thisArg?: ThisParameterType, @@ -664,7 +501,7 @@ class ContextManager { } // deno-lint-ignore no-explicit-any - bind any>( + static bind any>( context: Context, target: T, ): T { @@ -678,269 +515,530 @@ class ContextManager { }) as T; } - enable() { + static enable() { return this; } - disable() { + static disable() { return this; } } -function attributeValue(value: IAnyValue) { - return value.boolValue ?? value.stringValue ?? value.doubleValue ?? - value.intValue; +// metrics + +interface MeterOptions { + schemaUrl?: string; } -function submitMetrics(resource, scopeMetrics) { - let i = 0; - while (i < resource.attributes.length) { - if (i + 2 < resource.attributes.length) { - op_otel_metrics_resource_attribute3( - resource.attributes.length, - resource.attributes[i].key, - attributeValue(resource.attributes[i].value), - resource.attributes[i + 1].key, - attributeValue(resource.attributes[i + 1].value), - resource.attributes[i + 2].key, - attributeValue(resource.attributes[i + 2].value), - ); - i += 3; - } else if (i + 1 < resource.attributes.length) { - op_otel_metrics_resource_attribute2( - resource.attributes.length, - resource.attributes[i].key, - attributeValue(resource.attributes[i].value), - resource.attributes[i + 1].key, - attributeValue(resource.attributes[i + 1].value), - ); - i += 2; - } else { - op_otel_metrics_resource_attribute( - resource.attributes.length, - resource.attributes[i].key, - attributeValue(resource.attributes[i].value), - ); - i += 1; +interface MetricOptions { + description?: string; + + unit?: string; + + valueType?: ValueType; + + advice?: MetricAdvice; +} + +enum ValueType { + INT = 0, + DOUBLE = 1, +} + +interface MetricAdvice { + /** + * Hint the explicit bucket boundaries for SDK if the metric is been + * aggregated with a HistogramAggregator. + */ + explicitBucketBoundaries?: number[]; +} + +interface OtelMeter { + __key: "meter"; + createCounter(name: string, description?: string, unit?: string): Instrument; + createUpDownCounter( + name: string, + description?: string, + unit?: string, + ): Instrument; + createGauge(name: string, description?: string, unit?: string): Instrument; + createHistogram( + name: string, + description?: string, + unit?: string, + explicitBucketBoundaries?: number[], + ): Instrument; + createObservableCounter( + name: string, + description?: string, + unit?: string, + ): Instrument; + createObservableUpDownCounter( + name: string, + description?: string, + unit?: string, + ): Instrument; + createObservableGauge( + name: string, + description?: string, + unit?: string, + ): Instrument; +} + +class MeterProvider { + constructor() { + throw new TypeError("MeterProvider can not be constructed"); + } + + static getMeter( + name: string, + version?: string, + options?: MeterOptions, + ): Meter { + const meter = new OtelMeter(name, version, options?.schemaUrl); + return new Meter(meter); + } +} + +type MetricAttributes = Attributes; + +type Instrument = { __key: "instrument" }; + +let batchResultHasObservables: ( + res: BatchObservableResult, + observables: Observable[], +) => boolean; + +class BatchObservableResult { + #observables: WeakSet; + + constructor(observables: WeakSet) { + this.#observables = observables; + } + + static { + batchResultHasObservables = (cb, observables) => { + for (const observable of new SafeIterator(observables)) { + if (!cb.#observables.has(observable)) return false; + } + return true; + }; + } + + observe( + metric: Observable, + value: number, + attributes?: MetricAttributes, + ): void { + if (!this.#observables.has(metric)) return; + getObservableResult(metric).observe(value, attributes); + } +} + +const BATCH_CALLBACKS = new SafeMap< + BatchObservableCallback, + BatchObservableResult +>(); +const INDIVIDUAL_CALLBACKS = new SafeMap>(); + +class Meter { + #meter: OtelMeter; + + constructor(meter: OtelMeter) { + this.#meter = meter; + } + + createCounter(name: string, options?: MetricOptions): Counter { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) return new Counter(null, false); + const instrument = this.#meter.createCounter( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Counter(instrument, false); + } + + createUpDownCounter(name: string, options?: MetricOptions): Counter { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) return new Counter(null, true); + const instrument = this.#meter.createUpDownCounter( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Counter(instrument, true); + } + + createGauge(name: string, options?: MetricOptions): Gauge { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) return new Gauge(null); + const instrument = this.#meter.createGauge( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Gauge(instrument); + } + + createHistogram(name: string, options?: MetricOptions): Histogram { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) return new Histogram(null); + const instrument = this.#meter.createHistogram( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + options?.advice?.explicitBucketBoundaries, + ) as Instrument; + return new Histogram(instrument); + } + + createObservableCounter(name: string, options?: MetricOptions): Observable { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) new Observable(new ObservableResult(null, true)); + const instrument = this.#meter.createObservableCounter( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Observable(new ObservableResult(instrument, true)); + } + + createObservableUpDownCounter( + name: string, + options?: MetricOptions, + ): Observable { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) new Observable(new ObservableResult(null, false)); + const instrument = this.#meter.createObservableUpDownCounter( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Observable(new ObservableResult(instrument, false)); + } + + createObservableGauge(name: string, options?: MetricOptions): Observable { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) new Observable(new ObservableResult(null, false)); + const instrument = this.#meter.createObservableGauge( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Observable(new ObservableResult(instrument, false)); + } + + addBatchObservableCallback( + callback: BatchObservableCallback, + observables: Observable[], + ): void { + if (!METRICS_ENABLED) return; + const result = new BatchObservableResult(new SafeWeakSet(observables)); + startObserving(); + BATCH_CALLBACKS.set(callback, result); + } + + removeBatchObservableCallback( + callback: BatchObservableCallback, + observables: Observable[], + ): void { + if (!METRICS_ENABLED) return; + const result = BATCH_CALLBACKS.get(callback); + if (result && batchResultHasObservables(result, observables)) { + BATCH_CALLBACKS.delete(callback); } } +} - for (let smi = 0; smi < scopeMetrics.length; smi += 1) { - const { scope, metrics } = scopeMetrics[smi]; +type BatchObservableCallback = ( + observableResult: BatchObservableResult, +) => void | Promise; - op_otel_metrics_scope(scope.name, scope.schemaUrl, scope.version); - - for (let mi = 0; mi < metrics.length; mi += 1) { - const metric = metrics[mi]; - switch (metric.dataPointType) { - case 3: - op_otel_metrics_sum( - metric.descriptor.name, - // deno-lint-ignore prefer-primordials - metric.descriptor.description, - metric.descriptor.unit, - metric.aggregationTemporality, - metric.isMonotonic, - ); - for (let di = 0; di < metric.dataPoints.length; di += 1) { - const dataPoint = metric.dataPoints[di]; - op_otel_metrics_sum_or_gauge_data_point( - dataPoint.value, - hrToSecs(dataPoint.startTime), - hrToSecs(dataPoint.endTime), - ); - const attributes = ObjectEntries(dataPoint.attributes); - let i = 0; - while (i < attributes.length) { - if (i + 2 < attributes.length) { - op_otel_metrics_data_point_attribute3( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - attributes[i + 2][0], - attributes[i + 2][1], - ); - i += 3; - } else if (i + 1 < attributes.length) { - op_otel_metrics_data_point_attribute2( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - ); - i += 2; - } else { - op_otel_metrics_data_point_attribute( - attributes.length, - attributes[i][0], - attributes[i][1], - ); - i += 1; - } - } - } - break; - case 2: - op_otel_metrics_gauge( - metric.descriptor.name, - // deno-lint-ignore prefer-primordials - metric.descriptor.description, - metric.descriptor.unit, - ); - for (let di = 0; di < metric.dataPoints.length; di += 1) { - const dataPoint = metric.dataPoints[di]; - op_otel_metrics_sum_or_gauge_data_point( - dataPoint.value, - hrToSecs(dataPoint.startTime), - hrToSecs(dataPoint.endTime), - ); - const attributes = ObjectEntries(dataPoint.attributes); - let i = 0; - while (i < attributes.length) { - if (i + 2 < attributes.length) { - op_otel_metrics_data_point_attribute3( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - attributes[i + 2][0], - attributes[i + 2][1], - ); - i += 3; - } else if (i + 1 < attributes.length) { - op_otel_metrics_data_point_attribute2( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - ); - i += 2; - } else { - op_otel_metrics_data_point_attribute( - attributes.length, - attributes[i][0], - attributes[i][1], - ); - i += 1; - } - } - } - break; - case 0: - op_otel_metrics_histogram( - metric.descriptor.name, - // deno-lint-ignore prefer-primordials - metric.descriptor.description, - metric.descriptor.unit, - metric.aggregationTemporality, - ); - for (let di = 0; di < metric.dataPoints.length; di += 1) { - const dataPoint = metric.dataPoints[di]; - const { boundaries, counts } = dataPoint.value.buckets; - op_otel_metrics_histogram_data_point( - dataPoint.value.count, - dataPoint.value.min ?? NaN, - dataPoint.value.max ?? NaN, - dataPoint.value.sum, - hrToSecs(dataPoint.startTime), - hrToSecs(dataPoint.endTime), - boundaries.length, - ); - let j = 0; - while (j < boundaries.length) { - if (j + 3 < boundaries.length) { - op_otel_metrics_histogram_data_point_entry3( - counts[j], - boundaries[j], - counts[j + 1], - boundaries[j + 1], - counts[j + 2], - boundaries[j + 2], - ); - j += 3; - } else if (j + 2 < boundaries.length) { - op_otel_metrics_histogram_data_point_entry2( - counts[j], - boundaries[j], - counts[j + 1], - boundaries[j + 1], - ); - j += 2; - } else { - op_otel_metrics_histogram_data_point_entry1( - counts[j], - boundaries[j], - ); - j += 1; - } - } - op_otel_metrics_histogram_data_point_entry_final(counts[j]); - const attributes = ObjectEntries(dataPoint.attributes); - let i = 0; - while (i < attributes.length) { - if (i + 2 < attributes.length) { - op_otel_metrics_data_point_attribute3( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - attributes[i + 2][0], - attributes[i + 2][1], - ); - i += 3; - } else if (i + 1 < attributes.length) { - op_otel_metrics_data_point_attribute2( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - ); - i += 2; - } else { - op_otel_metrics_data_point_attribute( - attributes.length, - attributes[i][0], - attributes[i][1], - ); - i += 1; - } - } - } - break; - default: - continue; +function record( + instrument: Instrument | null, + value: number, + attributes?: MetricAttributes, +) { + if (instrument === null) return; + if (attributes === undefined) { + op_otel_metric_record0(instrument, value); + } else { + const attrs = ObjectEntries(attributes); + if (attrs.length === 0) { + op_otel_metric_record0(instrument, value); + } + let i = 0; + while (i < attrs.length) { + const remaining = attrs.length - i; + if (remaining > 3) { + op_otel_metric_attribute3( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + attrs[i + 2][0], + attrs[i + 2][1], + ); + i += 3; + } else if (remaining === 3) { + op_otel_metric_record3( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + attrs[i + 2][0], + attrs[i + 2][1], + ); + i += 3; + } else if (remaining === 2) { + op_otel_metric_record2( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + ); + i += 2; + } else if (remaining === 1) { + op_otel_metric_record1(instrument, value, attrs[i][0], attrs[i][1]); + i += 1; } } } - - op_otel_metrics_submit(); } -class MetricExporter { - export(metrics, resultCallback: (result: ExportResult) => void) { - try { - submitMetrics(metrics.resource, metrics.scopeMetrics); - resultCallback({ code: 0 }); - } catch (error) { - resultCallback({ - code: 1, - error: ObjectPrototypeIsPrototypeOf(error, Error) - ? error as Error - : new Error(String(error)), - }); +function recordObservable( + instrument: Instrument | null, + value: number, + attributes?: MetricAttributes, +) { + if (instrument === null) return; + if (attributes === undefined) { + op_otel_metric_observable_record0(instrument, value); + } else { + const attrs = ObjectEntries(attributes); + if (attrs.length === 0) { + op_otel_metric_observable_record0(instrument, value); + } + let i = 0; + while (i < attrs.length) { + const remaining = attrs.length - i; + if (remaining > 3) { + op_otel_metric_attribute3( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + attrs[i + 2][0], + attrs[i + 2][1], + ); + i += 3; + } else if (remaining === 3) { + op_otel_metric_observable_record3( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + attrs[i + 2][0], + attrs[i + 2][1], + ); + i += 3; + } else if (remaining === 2) { + op_otel_metric_observable_record2( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + ); + i += 2; + } else if (remaining === 1) { + op_otel_metric_observable_record1( + instrument, + value, + attrs[i][0], + attrs[i][1], + ); + i += 1; + } } } +} - async forceFlush() {} +class Counter { + #instrument: Instrument | null; + #upDown: boolean; - async shutdown() {} + constructor(instrument: Instrument | null, upDown: boolean) { + this.#instrument = instrument; + this.#upDown = upDown; + } + + add(value: number, attributes?: MetricAttributes, _context?: Context): void { + if (value < 0 && !this.#upDown) { + throw new Error("Counter can only be incremented"); + } + record(this.#instrument, value, attributes); + } +} + +class Gauge { + #instrument: Instrument | null; + + constructor(instrument: Instrument | null) { + this.#instrument = instrument; + } + + record( + value: number, + attributes?: MetricAttributes, + _context?: Context, + ): void { + record(this.#instrument, value, attributes); + } +} + +class Histogram { + #instrument: Instrument | null; + + constructor(instrument: Instrument | null) { + this.#instrument = instrument; + } + + record( + value: number, + attributes?: MetricAttributes, + _context?: Context, + ): void { + record(this.#instrument, value, attributes); + } +} + +type ObservableCallback = ( + observableResult: ObservableResult, +) => void | Promise; + +let getObservableResult: (observable: Observable) => ObservableResult; + +class Observable { + #result: ObservableResult; + + constructor(result: ObservableResult) { + this.#result = result; + } + + static { + getObservableResult = (observable) => observable.#result; + } + + addCallback(callback: ObservableCallback): void { + const res = INDIVIDUAL_CALLBACKS.get(this); + if (res) res.add(callback); + else INDIVIDUAL_CALLBACKS.set(this, new SafeSet([callback])); + startObserving(); + } + + removeCallback(callback: ObservableCallback): void { + const res = INDIVIDUAL_CALLBACKS.get(this); + if (res) res.delete(callback); + if (res?.size === 0) INDIVIDUAL_CALLBACKS.delete(this); + } +} + +class ObservableResult { + #instrument: Instrument | null; + #isRegularCounter: boolean; + + constructor(instrument: Instrument | null, isRegularCounter: boolean) { + this.#instrument = instrument; + this.#isRegularCounter = isRegularCounter; + } + + observe( + this: ObservableResult, + value: number, + attributes?: MetricAttributes, + ): void { + if (this.#isRegularCounter) { + if (value < 0) { + throw new Error("Observable counters can only be incremented"); + } + } + recordObservable(this.#instrument, value, attributes); + } +} + +async function observe(): Promise { + const promises: Promise[] = []; + // Primordials are not needed, because this is a SafeMap. + // deno-lint-ignore prefer-primordials + for (const { 0: observable, 1: callbacks } of INDIVIDUAL_CALLBACKS) { + const result = getObservableResult(observable); + // Primordials are not needed, because this is a SafeSet. + // deno-lint-ignore prefer-primordials + for (const callback of callbacks) { + // PromiseTry is not in primordials? + // deno-lint-ignore prefer-primordials + ArrayPrototypePush(promises, Promise.try(callback, result)); + } + } + // Primordials are not needed, because this is a SafeMap. + // deno-lint-ignore prefer-primordials + for (const { 0: callback, 1: result } of BATCH_CALLBACKS) { + // PromiseTry is not in primordials? + // deno-lint-ignore prefer-primordials + ArrayPrototypePush(promises, Promise.try(callback, result)); + } + await SafePromiseAll(promises); +} + +let isObserving = false; +function startObserving() { + if (!isObserving) { + isObserving = true; + (async () => { + while (true) { + const promise = op_otel_metric_wait_to_observe(); + core.unrefOpPromise(promise); + const ok = await promise; + if (!ok) break; + await observe(); + op_otel_metric_observation_done(); + } + })(); + } } const otelConsoleConfig = { @@ -949,17 +1047,51 @@ const otelConsoleConfig = { replace: 2, }; +function otelLog(message: string, level: number) { + const currentSpan = CURRENT.get()?.getValue(SPAN_KEY); + const otelSpan = currentSpan !== undefined + ? getOtelSpan(currentSpan) + : undefined; + if (otelSpan || currentSpan === undefined) { + op_otel_log(message, level, otelSpan); + } else { + const spanContext = currentSpan.spanContext(); + op_otel_log_foreign( + message, + level, + spanContext.traceId, + spanContext.spanId, + spanContext.traceFlags, + ); + } +} + +let builtinTracerCache: Tracer; + +export function builtinTracer(): Tracer { + if (!builtinTracerCache) { + builtinTracerCache = new Tracer(OtelTracer.builtin()); + } + return builtinTracerCache; +} + +// We specify a very high version number, to allow any `@opentelemetry/api` +// version to load this module. This does cause @opentelemetry/api to not be +// able to register anything itself with the global registration methods. +const OTEL_API_COMPAT_VERSION = "1.999.999"; + export function bootstrap( config: [ 0 | 1, - typeof otelConsoleConfig[keyof typeof otelConsoleConfig], + 0 | 1, + (typeof otelConsoleConfig)[keyof typeof otelConsoleConfig], 0 | 1, ], ): void { - const { 0: tracingEnabled, 1: consoleConfig, 2: deterministic } = config; + const { 0: tracingEnabled, 1: metricsEnabled, 2: consoleConfig } = config; TRACING_ENABLED = tracingEnabled === 1; - DETERMINISTIC = deterministic === 1; + METRICS_ENABLED = metricsEnabled === 1; switch (consoleConfig) { case otelConsoleConfig.capture: @@ -975,10 +1107,23 @@ export function bootstrap( default: break; } + + if (TRACING_ENABLED || METRICS_ENABLED) { + const otel = globalThis[SymbolFor("opentelemetry.js.api.1")] ??= { + version: OTEL_API_COMPAT_VERSION, + }; + if (TRACING_ENABLED) { + otel.trace = TracerProvider; + otel.context = ContextManager; + } + if (METRICS_ENABLED) { + otel.metrics = MeterProvider; + } + } } export const telemetry = { - SpanExporter, - ContextManager, - MetricExporter, + tracerProvider: TracerProvider, + contextManager: ContextManager, + meterProvider: MeterProvider, }; diff --git a/ext/telemetry/util.ts b/ext/telemetry/util.ts index 7e30d5d859..ac233f7a9f 100644 --- a/ext/telemetry/util.ts +++ b/ext/telemetry/util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import type { Span } from "ext:deno_telemetry/telemetry.ts"; diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index fc75b0a05d..39b3d17c86 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_tls" -version = "0.169.0" +version = "0.172.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_native_certs = "0.3.0" rustls.workspace = true rustls-pemfile.workspace = true diff --git a/ext/tls/lib.rs b/ext/tls/lib.rs index 883d2995e4..a3e386052e 100644 --- a/ext/tls/lib.rs +++ b/ext/tls/lib.rs @@ -1,47 +1,54 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -pub use deno_native_certs; -pub use rustls; -use rustls::pki_types::CertificateDer; -use rustls::pki_types::PrivateKeyDer; -use rustls::pki_types::ServerName; -pub use rustls_pemfile; -pub use rustls_tokio_stream::*; -pub use webpki; -pub use webpki_roots; - -use rustls::client::danger::HandshakeSignatureValid; -use rustls::client::danger::ServerCertVerified; -use rustls::client::danger::ServerCertVerifier; -use rustls::client::WebPkiServerVerifier; -use rustls::ClientConfig; -use rustls::DigitallySignedStruct; -use rustls::RootCertStore; -use rustls_pemfile::certs; -use rustls_pemfile::ec_private_keys; -use rustls_pemfile::pkcs8_private_keys; -use rustls_pemfile::rsa_private_keys; -use serde::Deserialize; +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::BufRead; use std::io::BufReader; use std::io::Cursor; use std::net::IpAddr; use std::sync::Arc; +use deno_error::JsErrorBox; +pub use deno_native_certs; +pub use rustls; +use rustls::client::danger::HandshakeSignatureValid; +use rustls::client::danger::ServerCertVerified; +use rustls::client::danger::ServerCertVerifier; +use rustls::client::WebPkiServerVerifier; +use rustls::pki_types::CertificateDer; +use rustls::pki_types::PrivateKeyDer; +use rustls::pki_types::ServerName; +use rustls::ClientConfig; +use rustls::DigitallySignedStruct; +use rustls::RootCertStore; +pub use rustls_pemfile; +use rustls_pemfile::certs; +use rustls_pemfile::ec_private_keys; +use rustls_pemfile::pkcs8_private_keys; +use rustls_pemfile::rsa_private_keys; +pub use rustls_tokio_stream::*; +use serde::Deserialize; +pub use webpki; +pub use webpki_roots; + mod tls_key; pub use tls_key::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum TlsError { + #[class(generic)] #[error(transparent)] Rustls(#[from] rustls::Error), + #[class(inherit)] #[error("Unable to add pem file to certificate store: {0}")] UnableAddPemFileToCert(std::io::Error), + #[class("InvalidData")] #[error("Unable to decode certificate")] CertInvalid, + #[class("InvalidData")] #[error("No certificates found in certificate data")] CertsNotFound, + #[class("InvalidData")] #[error("No keys found in key data")] KeysNotFound, + #[class("InvalidData")] #[error("Unable to decode key")] KeyDecode, } @@ -51,9 +58,7 @@ pub enum TlsError { /// This was done because the root cert store is not needed in all cases /// and takes a bit of time to initialize. pub trait RootCertStoreProvider: Send + Sync { - fn get_or_try_init( - &self, - ) -> Result<&RootCertStore, deno_core::error::AnyError>; + fn get_or_try_init(&self) -> Result<&RootCertStore, JsErrorBox>; } // This extension has no runtime apis, it only exports some shared native functions. diff --git a/ext/tls/tls_key.rs b/ext/tls/tls_key.rs index b7baa604b9..dfd2863e5e 100644 --- a/ext/tls/tls_key.rs +++ b/ext/tls/tls_key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! These represent the various types of TLS keys we support for both client and server //! connections. @@ -11,12 +11,6 @@ //! key lookup can handle closing one end of the pair, in which case they will just //! attempt to clean up the associated resources. -use deno_core::futures::future::poll_fn; -use deno_core::futures::future::Either; -use deno_core::futures::FutureExt; -use deno_core::unsync::spawn; -use rustls::ServerConfig; -use rustls_tokio_stream::ServerConfigProvider; use std::cell::RefCell; use std::collections::HashMap; use std::fmt::Debug; @@ -25,6 +19,13 @@ use std::future::Future; use std::io::ErrorKind; use std::rc::Rc; use std::sync::Arc; + +use deno_core::futures::future::poll_fn; +use deno_core::futures::future::Either; +use deno_core::futures::FutureExt; +use deno_core::unsync::spawn; +use rustls::ServerConfig; +use rustls_tokio_stream::ServerConfigProvider; use tokio::sync::broadcast; use tokio::sync::mpsc; use tokio::sync::oneshot; @@ -269,9 +270,10 @@ impl TlsKeyLookup { #[cfg(test)] pub mod tests { - use super::*; use deno_core::unsync::spawn; + use super::*; + fn tls_key_for_test(sni: &str) -> TlsKey { let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); diff --git a/ext/url/00_url.js b/ext/url/00_url.js index ec875da768..c853430d1a 100644 --- a/ext/url/00_url.js +++ b/ext/url/00_url.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/url/01_urlpattern.js b/ext/url/01_urlpattern.js index 6e27563089..5febef3332 100644 --- a/ext/url/01_urlpattern.js +++ b/ext/url/01_urlpattern.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index d30332d0ff..2078571e39 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_url" -version = "0.182.0" +version = "0.185.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true thiserror.workspace = true urlpattern = "0.3.0" diff --git a/ext/url/benches/url_ops.rs b/ext/url/benches/url_ops.rs index 70afb96db2..9295a08d51 100644 --- a/ext/url/benches/url_ops.rs +++ b/ext/url/benches/url_ops.rs @@ -1,10 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::Bencher; - use deno_core::Extension; fn setup() -> Vec { diff --git a/ext/url/internal.d.ts b/ext/url/internal.d.ts index 11bacb0e1b..69e0472e8e 100644 --- a/ext/url/internal.d.ts +++ b/ext/url/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/ext/url/lib.deno_url.d.ts b/ext/url/lib.deno_url.d.ts index 946c70607f..08fe74cd66 100644 --- a/ext/url/lib.deno_url.d.ts +++ b/ext/url/lib.deno_url.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/url/lib.rs b/ext/url/lib.rs index f8946532ae..dd74239d93 100644 --- a/ext/url/lib.rs +++ b/ext/url/lib.rs @@ -1,22 +1,20 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod urlpattern; -use deno_core::error::type_error; -use deno_core::error::AnyError; +use std::path::PathBuf; + use deno_core::op2; use deno_core::url::form_urlencoded; use deno_core::url::quirks; use deno_core::url::Url; use deno_core::JsBuffer; use deno_core::OpState; -use std::path::PathBuf; +use deno_error::JsErrorBox; use crate::urlpattern::op_urlpattern_parse; use crate::urlpattern::op_urlpattern_process_match_input; -pub use urlpattern::UrlPatternError; - deno_core::extension!( deno_url, deps = [deno_webidl], @@ -220,7 +218,7 @@ pub fn op_url_reparse( pub fn op_url_parse_search_params( #[string] args: Option, #[buffer] zero_copy: Option, -) -> Result, AnyError> { +) -> Result, JsErrorBox> { let params = match (args, zero_copy) { (None, Some(zero_copy)) => form_urlencoded::parse(&zero_copy) .into_iter() @@ -230,7 +228,7 @@ pub fn op_url_parse_search_params( .into_iter() .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned())) .collect(), - _ => return Err(type_error("invalid parameters")), + _ => return Err(JsErrorBox::type_error("invalid parameters")), }; Ok(params) } diff --git a/ext/url/urlpattern.rs b/ext/url/urlpattern.rs index 7d4e8ee71b..02034332cf 100644 --- a/ext/url/urlpattern.rs +++ b/ext/url/urlpattern.rs @@ -1,15 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; - use urlpattern::quirks; use urlpattern::quirks::MatchInput; use urlpattern::quirks::StringOrInit; use urlpattern::quirks::UrlPattern; -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct UrlPatternError(urlpattern::Error); +deno_error::js_error_wrapper!(urlpattern::Error, UrlPatternError, "TypeError"); #[op2] #[serde] @@ -19,11 +16,9 @@ pub fn op_urlpattern_parse( #[serde] options: urlpattern::UrlPatternOptions, ) -> Result { let init = - quirks::process_construct_pattern_input(input, base_url.as_deref()) - .map_err(UrlPatternError)?; + quirks::process_construct_pattern_input(input, base_url.as_deref())?; - let pattern = - quirks::parse_pattern(init, options).map_err(UrlPatternError)?; + let pattern = quirks::parse_pattern(init, options)?; Ok(pattern) } @@ -34,8 +29,7 @@ pub fn op_urlpattern_process_match_input( #[serde] input: StringOrInit, #[string] base_url: Option, ) -> Result, UrlPatternError> { - let res = quirks::process_match_input(input, base_url.as_deref()) - .map_err(UrlPatternError)?; + let res = quirks::process_match_input(input, base_url.as_deref())?; let (input, inputs) = match res { Some((input, inputs)) => (input, inputs), diff --git a/ext/web/00_infra.js b/ext/web/00_infra.js index 9a75f8fa58..8ca42e86c2 100644 --- a/ext/web/00_infra.js +++ b/ext/web/00_infra.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/01_dom_exception.js b/ext/web/01_dom_exception.js index db2996e0c6..730fda860f 100644 --- a/ext/web/01_dom_exception.js +++ b/ext/web/01_dom_exception.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/01_mimesniff.js b/ext/web/01_mimesniff.js index e60783bbe1..9a687a8305 100644 --- a/ext/web/01_mimesniff.js +++ b/ext/web/01_mimesniff.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/02_event.js b/ext/web/02_event.js index f6351c4b9e..810e48537d 100644 --- a/ext/web/02_event.js +++ b/ext/web/02_event.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module follows most of the WHATWG Living Standard for the DOM logic. // Many parts of the DOM are not implemented in Deno, but the logic for those diff --git a/ext/web/02_structured_clone.js b/ext/web/02_structured_clone.js index 776a8ee96e..453700fc41 100644 --- a/ext/web/02_structured_clone.js +++ b/ext/web/02_structured_clone.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/02_timers.js b/ext/web/02_timers.js index 6058febd59..c74f9baff9 100644 --- a/ext/web/02_timers.js +++ b/ext/web/02_timers.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_defer } from "ext:core/ops"; diff --git a/ext/web/03_abort_signal.js b/ext/web/03_abort_signal.js index 93b3cf0522..1f9ce42e1e 100644 --- a/ext/web/03_abort_signal.js +++ b/ext/web/03_abort_signal.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/04_global_interfaces.js b/ext/web/04_global_interfaces.js index 7c7f83b431..bda695b530 100644 --- a/ext/web/04_global_interfaces.js +++ b/ext/web/04_global_interfaces.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/05_base64.js b/ext/web/05_base64.js index e6796e1dc3..155201f853 100644 --- a/ext/web/05_base64.js +++ b/ext/web/05_base64.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/06_streams.js b/ext/web/06_streams.js index e673ee2bb4..950d46e829 100644 --- a/ext/web/06_streams.js +++ b/ext/web/06_streams.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// @@ -908,8 +908,8 @@ const _original = Symbol("[[original]]"); * @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true. * @returns {ReadableStream} */ -function readableStreamForRid(rid, autoClose = true) { - const stream = new ReadableStream(_brand); +function readableStreamForRid(rid, autoClose = true, Super, onError) { + const stream = new (Super ?? ReadableStream)(_brand); stream[_resourceBacking] = { rid, autoClose }; const tryClose = () => { @@ -947,7 +947,11 @@ function readableStreamForRid(rid, autoClose = true) { controller.byobRequest.respond(bytesRead); } } catch (e) { - controller.error(e); + if (onError) { + onError(controller, e); + } else { + controller.error(e); + } tryClose(); } }, @@ -1130,8 +1134,8 @@ async function readableStreamCollectIntoUint8Array(stream) { * @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true. * @returns {ReadableStream} */ -function writableStreamForRid(rid, autoClose = true) { - const stream = new WritableStream(_brand); +function writableStreamForRid(rid, autoClose = true, Super) { + const stream = new (Super ?? WritableStream)(_brand); stream[_resourceBacking] = { rid, autoClose }; const tryClose = () => { diff --git a/ext/web/06_streams_types.d.ts b/ext/web/06_streams_types.d.ts index fe05ee6e65..0a6cb6503a 100644 --- a/ext/web/06_streams_types.d.ts +++ b/ext/web/06_streams_types.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // ** Internal Interfaces ** diff --git a/ext/web/08_text_encoding.js b/ext/web/08_text_encoding.js index 3163c96282..8988c06c36 100644 --- a/ext/web/08_text_encoding.js +++ b/ext/web/08_text_encoding.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/09_file.js b/ext/web/09_file.js index 7c1d79ce31..cdad0b7396 100644 --- a/ext/web/09_file.js +++ b/ext/web/09_file.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/10_filereader.js b/ext/web/10_filereader.js index 2718606380..cecc6484a6 100644 --- a/ext/web/10_filereader.js +++ b/ext/web/10_filereader.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/12_location.js b/ext/web/12_location.js index ba0c47e2d1..cc1afb3d05 100644 --- a/ext/web/12_location.js +++ b/ext/web/12_location.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/web/13_message_port.js b/ext/web/13_message_port.js index cf72c43e6f..f96cd193f4 100644 --- a/ext/web/13_message_port.js +++ b/ext/web/13_message_port.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// @@ -102,8 +102,8 @@ const nodeWorkerThreadCloseCb = Symbol("nodeWorkerThreadCloseCb"); const nodeWorkerThreadCloseCbInvoked = Symbol("nodeWorkerThreadCloseCbInvoked"); export const refMessagePort = Symbol("refMessagePort"); /** It is used by 99_main.js and worker_threads to - * unref/ref on the global pollForMessages promise. */ -export const unrefPollForMessages = Symbol("unrefPollForMessages"); + * unref/ref on the global message event handler count. */ +export const unrefParentPort = Symbol("unrefParentPort"); /** * @param {number} id diff --git a/ext/web/14_compression.js b/ext/web/14_compression.js index 1adb205b22..de49d7ad3d 100644 --- a/ext/web/14_compression.js +++ b/ext/web/14_compression.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/15_performance.js b/ext/web/15_performance.js index f23e851246..967cdda470 100644 --- a/ext/web/15_performance.js +++ b/ext/web/15_performance.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { op_now, op_time_origin } from "ext:core/ops"; diff --git a/ext/web/16_image_data.js b/ext/web/16_image_data.js index 13df0d07be..bd69da7903 100644 --- a/ext/web/16_image_data.js +++ b/ext/web/16_image_data.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import * as webidl from "ext:deno_webidl/00_webidl.js"; diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index ac2b14fbed..065f1a12b1 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_web" -version = "0.213.0" +version = "0.216.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -18,6 +18,7 @@ async-trait.workspace = true base64-simd = "0.8" bytes.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_permissions.workspace = true encoding_rs.workspace = true flate2 = { workspace = true, features = ["default"] } diff --git a/ext/web/benches/encoding.rs b/ext/web/benches/encoding.rs index d0738c6452..42497ef3ce 100644 --- a/ext/web/benches/encoding.rs +++ b/ext/web/benches/encoding.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; diff --git a/ext/web/benches/timers_ops.rs b/ext/web/benches/timers_ops.rs index d39ee4eeae..a8a52ad916 100644 --- a/ext/web/benches/timers_ops.rs +++ b/ext/web/benches/timers_ops.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_async; use deno_bench_util::bench_or_profile; diff --git a/ext/web/blob.rs b/ext/web/blob.rs index bc64a0f27e..555e6da1cf 100644 --- a/ext/web/blob.rs +++ b/ext/web/blob.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; @@ -17,14 +17,18 @@ use serde::Deserialize; use serde::Serialize; use uuid::Uuid; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BlobError { + #[class(type)] #[error("Blob part not found")] BlobPartNotFound, + #[class(type)] #[error("start + len can not be larger than blob part size")] SizeLargerThanBlobPart, + #[class(type)] #[error("Blob URLs are not supported in this context")] BlobURLsNotSupported, + #[class(generic)] #[error(transparent)] Url(#[from] deno_core::url::ParseError), } diff --git a/ext/web/compression.rs b/ext/web/compression.rs index 6967009915..66662de74a 100644 --- a/ext/web/compression.rs +++ b/ext/web/compression.rs @@ -1,4 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::cell::RefCell; +use std::io::Write; use deno_core::op2; use flate2::write::DeflateDecoder; @@ -8,17 +11,19 @@ use flate2::write::GzEncoder; use flate2::write::ZlibDecoder; use flate2::write::ZlibEncoder; use flate2::Compression; -use std::cell::RefCell; -use std::io::Write; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CompressionError { + #[class(type)] #[error("Unsupported format")] UnsupportedFormat, + #[class(type)] #[error("resource is closed")] ResourceClosed, + #[class(type)] #[error(transparent)] IoTypeError(std::io::Error), + #[class(inherit)] #[error(transparent)] Io(std::io::Error), } diff --git a/ext/web/internal.d.ts b/ext/web/internal.d.ts index b2aea80d9f..64a8633854 100644 --- a/ext/web/internal.d.ts +++ b/ext/web/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/ext/web/lib.deno_web.d.ts b/ext/web/lib.deno_web.d.ts index 8aafbad535..1fb003b66f 100644 --- a/ext/web/lib.deno_web.d.ts +++ b/ext/web/lib.deno_web.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/web/lib.rs b/ext/web/lib.rs index af0fc2c276..7d22fa3b2a 100644 --- a/ext/web/lib.rs +++ b/ext/web/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod blob; mod compression; @@ -6,17 +6,6 @@ mod message_port; mod stream_resource; mod timers; -use deno_core::op2; -use deno_core::url::Url; -use deno_core::v8; -use deno_core::ByteString; -use deno_core::ToJsBuffer; -use deno_core::U16String; - -use encoding_rs::CoderResult; -use encoding_rs::Decoder; -use encoding_rs::DecoderResult; -use encoding_rs::Encoding; use std::borrow::Cow; use std::cell::RefCell; use std::path::PathBuf; @@ -24,6 +13,16 @@ use std::sync::Arc; pub use blob::BlobError; pub use compression::CompressionError; +use deno_core::op2; +use deno_core::url::Url; +use deno_core::v8; +use deno_core::ByteString; +use deno_core::ToJsBuffer; +use deno_core::U16String; +use encoding_rs::CoderResult; +use encoding_rs::Decoder; +use encoding_rs::DecoderResult; +use encoding_rs::Encoding; pub use message_port::MessagePortError; pub use stream_resource::StreamResourceError; @@ -38,7 +37,6 @@ pub use crate::blob::Blob; pub use crate::blob::BlobPart; pub use crate::blob::BlobStore; pub use crate::blob::InMemoryBlobPart; - pub use crate::message_port::create_entangled_message_port; pub use crate::message_port::deserialize_js_transferables; use crate::message_port::op_message_port_create_entangled; @@ -49,7 +47,6 @@ pub use crate::message_port::serialize_transferables; pub use crate::message_port::JsMessageData; pub use crate::message_port::MessagePort; pub use crate::message_port::Transferable; - use crate::timers::op_defer; use crate::timers::op_now; use crate::timers::op_time_origin; @@ -129,20 +126,27 @@ deno_core::extension!(deno_web, } ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WebError { + #[class("DOMExceptionInvalidCharacterError")] #[error("Failed to decode base64")] Base64Decode, + #[class(range)] #[error("The encoding label provided ('{0}') is invalid.")] InvalidEncodingLabel(String), + #[class(type)] #[error("buffer exceeds maximum length")] BufferTooLong, + #[class(range)] #[error("Value too large to decode")] ValueTooLarge, + #[class(range)] #[error("Provided buffer too small")] BufferTooSmall, + #[class(type)] #[error("The encoded data is not valid")] DataInvalid, + #[class(generic)] #[error(transparent)] DataError(#[from] v8::DataError), } diff --git a/ext/web/message_port.rs b/ext/web/message_port.rs index 1a4a09073d..3d656fdea2 100644 --- a/ext/web/message_port.rs +++ b/ext/web/message_port.rs @@ -1,11 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; use deno_core::op2; - use deno_core::CancelFuture; use deno_core::CancelHandle; use deno_core::DetachedBuffer; @@ -21,18 +20,23 @@ use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum MessagePortError { + #[class(type)] #[error("Invalid message port transfer")] InvalidTransfer, + #[class(type)] #[error("Message port is not ready for transfer")] NotReady, + #[class(type)] #[error("Can not transfer self message port")] TransferSelf, + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(deno_core::error::ResourceError), } pub enum Transferable { diff --git a/ext/web/stream_resource.rs b/ext/web/stream_resource.rs index c44a385ea9..edc842ff4d 100644 --- a/ext/web/stream_resource.rs +++ b/ext/web/stream_resource.rs @@ -1,4 +1,17 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::cell::RefMut; +use std::ffi::c_void; +use std::future::Future; +use std::marker::PhantomData; +use std::mem::MaybeUninit; +use std::pin::Pin; +use std::rc::Rc; +use std::task::Context; +use std::task::Poll; +use std::task::Waker; + use bytes::BytesMut; use deno_core::external; use deno_core::op2; @@ -17,23 +30,13 @@ use deno_core::Resource; use deno_core::ResourceId; use futures::future::poll_fn; use futures::TryFutureExt; -use std::borrow::Cow; -use std::cell::RefCell; -use std::cell::RefMut; -use std::ffi::c_void; -use std::future::Future; -use std::marker::PhantomData; -use std::mem::MaybeUninit; -use std::pin::Pin; -use std::rc::Rc; -use std::task::Context; -use std::task::Poll; -use std::task::Waker; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum StreamResourceError { + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), + #[class(type)] #[error("{0}")] Js(String), } @@ -403,7 +406,10 @@ impl Resource for ReadableStreamResource { } fn read(self: Rc, limit: usize) -> AsyncResult { - Box::pin(ReadableStreamResource::read(self, limit).map_err(|e| e.into())) + Box::pin( + ReadableStreamResource::read(self, limit) + .map_err(deno_error::JsErrorBox::from_err), + ) } fn close(self: Rc) { @@ -607,13 +613,15 @@ impl Drop for ReadableStreamResourceData { #[cfg(test)] mod tests { - use super::*; - use deno_core::v8; use std::cell::OnceCell; use std::sync::atomic::AtomicUsize; use std::sync::OnceLock; use std::time::Duration; + use deno_core::v8; + + use super::*; + static V8_GLOBAL: OnceLock<()> = OnceLock::new(); thread_local! { diff --git a/ext/web/timers.rs b/ext/web/timers.rs index 06444ed34f..7929b6050e 100644 --- a/ext/web/timers.rs +++ b/ext/web/timers.rs @@ -1,14 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This module helps deno implement timers and performance APIs. -use deno_core::op2; -use deno_core::OpState; use std::time::Duration; use std::time::Instant; use std::time::SystemTime; use std::time::UNIX_EPOCH; +use deno_core::op2; +use deno_core::OpState; + pub trait TimersPermission { fn allow_hrtime(&mut self) -> bool; } diff --git a/ext/webgpu/00_init.js b/ext/webgpu/00_init.js index 0f10847cef..81bb27286d 100644 --- a/ext/webgpu/00_init.js +++ b/ext/webgpu/00_init.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core } from "ext:core/mod.js"; diff --git a/ext/webgpu/01_webgpu.js b/ext/webgpu/01_webgpu.js index d371f49ea1..5ce34a5e7b 100644 --- a/ext/webgpu/01_webgpu.js +++ b/ext/webgpu/01_webgpu.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/webgpu/02_surface.js b/ext/webgpu/02_surface.js index ce723b891f..b0561469a9 100644 --- a/ext/webgpu/02_surface.js +++ b/ext/webgpu/02_surface.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 9621c085e8..369fdb02e6 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_webgpu" -version = "0.149.0" +version = "0.152.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" @@ -21,6 +21,7 @@ vulkan-portability = [] # so the whole workspace can built as wasm. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] deno_core.workspace = true +deno_error.workspace = true serde = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["full"] } wgpu-types = { workspace = true, features = ["serde"] } diff --git a/ext/webgpu/binding.rs b/ext/webgpu/binding.rs index 41708acc7f..c8441c64af 100644 --- a/ext/webgpu/binding.rs +++ b/ext/webgpu/binding.rs @@ -1,13 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::error::AnyError; +use std::borrow::Cow; +use std::rc::Rc; + +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; @@ -169,7 +170,7 @@ pub fn op_webgpu_create_bind_group_layout( #[smi] device_rid: ResourceId, #[string] label: Cow, #[serde] entries: Vec, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -208,7 +209,7 @@ pub fn op_webgpu_create_pipeline_layout( #[smi] device_rid: ResourceId, #[string] label: Cow, #[serde] bind_group_layouts: Vec, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -222,7 +223,7 @@ pub fn op_webgpu_create_pipeline_layout( state.resource_table.get::(rid)?; Ok(bind_group_layout.1) }) - .collect::, AnyError>>()?; + .collect::, ResourceError>>()?; let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor { label: Some(label), @@ -255,7 +256,7 @@ pub fn op_webgpu_create_bind_group( #[string] label: Cow, #[smi] layout: ResourceId, #[serde] entries: Vec, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -303,7 +304,7 @@ pub fn op_webgpu_create_bind_group( }, }) }) - .collect::, AnyError>>()?; + .collect::, ResourceError>>()?; let bind_group_layout = state.resource_table.get::(layout)?; diff --git a/ext/webgpu/buffer.rs b/ext/webgpu/buffer.rs index c2b53890e0..25a5606e12 100644 --- a/ext/webgpu/buffer.rs +++ b/ext/webgpu/buffer.rs @@ -1,9 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; @@ -11,14 +7,26 @@ use std::sync::Arc; use std::sync::Mutex; use std::time::Duration; +use deno_core::op2; +use deno_core::OpState; +use deno_core::Resource; +use deno_core::ResourceId; + use super::error::WebGpuResult; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BufferError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(type)] #[error("usage is not valid")] InvalidUsage, + #[class("DOMExceptionOperationError")] #[error(transparent)] Access(wgpu_core::resource::BufferAccessError), } @@ -57,8 +65,7 @@ pub fn op_webgpu_create_buffer( let instance = state.borrow::(); let device_resource = state .resource_table - .get::(device_rid) - .map_err(BufferError::Resource)?; + .get::(device_rid)?; let device = device_resource.1; let descriptor = wgpu_core::resource::BufferDescriptor { @@ -91,15 +98,12 @@ pub async fn op_webgpu_buffer_get_map_async( { let state_ = state.borrow(); let instance = state_.borrow::(); - let buffer_resource = state_ - .resource_table - .get::(buffer_rid) - .map_err(BufferError::Resource)?; + let buffer_resource = + state_.resource_table.get::(buffer_rid)?; let buffer = buffer_resource.1; let device_resource = state_ .resource_table - .get::(device_rid) - .map_err(BufferError::Resource)?; + .get::(device_rid)?; device = device_resource.1; let done_ = done.clone(); @@ -154,10 +158,7 @@ pub fn op_webgpu_buffer_get_mapped_range( #[buffer] buf: &mut [u8], ) -> Result { let instance = state.borrow::(); - let buffer_resource = state - .resource_table - .get::(buffer_rid) - .map_err(BufferError::Resource)?; + let buffer_resource = state.resource_table.get::(buffer_rid)?; let buffer = buffer_resource.1; let (slice_pointer, range_size) = @@ -191,13 +192,9 @@ pub fn op_webgpu_buffer_unmap( ) -> Result { let mapped_resource = state .resource_table - .take::(mapped_rid) - .map_err(BufferError::Resource)?; + .take::(mapped_rid)?; let instance = state.borrow::(); - let buffer_resource = state - .resource_table - .get::(buffer_rid) - .map_err(BufferError::Resource)?; + let buffer_resource = state.resource_table.get::(buffer_rid)?; let buffer = buffer_resource.1; if let Some(buf) = buf { diff --git a/ext/webgpu/bundle.rs b/ext/webgpu/bundle.rs index d9a5b29539..73c3c9f221 100644 --- a/ext/webgpu/bundle.rs +++ b/ext/webgpu/bundle.rs @@ -1,20 +1,28 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; + +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::rc::Rc; use super::error::WebGpuResult; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BundleError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + ResourceError, + ), + #[class(type)] #[error("size must be larger than 0")] InvalidSize, } @@ -59,7 +67,7 @@ pub struct CreateRenderBundleEncoderArgs { pub fn op_webgpu_create_render_bundle_encoder( state: &mut OpState, #[serde] args: CreateRenderBundleEncoderArgs, -) -> Result { +) -> Result { let device_resource = state .resource_table .get::(args.device_rid)?; @@ -106,7 +114,7 @@ pub fn op_webgpu_render_bundle_encoder_finish( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, #[string] label: Cow, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -137,7 +145,7 @@ pub fn op_webgpu_render_bundle_encoder_set_bind_group( #[buffer] dynamic_offsets_data: &[u32], #[number] dynamic_offsets_data_start: usize, #[number] dynamic_offsets_data_length: usize, -) -> Result { +) -> Result { let bind_group_resource = state .resource_table @@ -177,7 +185,7 @@ pub fn op_webgpu_render_bundle_encoder_push_debug_group( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, #[string] group_label: &str, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -201,7 +209,7 @@ pub fn op_webgpu_render_bundle_encoder_push_debug_group( pub fn op_webgpu_render_bundle_encoder_pop_debug_group( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -220,7 +228,7 @@ pub fn op_webgpu_render_bundle_encoder_insert_debug_marker( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, #[string] marker_label: &str, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -245,7 +253,7 @@ pub fn op_webgpu_render_bundle_encoder_set_pipeline( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, #[smi] pipeline: ResourceId, -) -> Result { +) -> Result { let render_pipeline_resource = state .resource_table @@ -275,12 +283,11 @@ pub fn op_webgpu_render_bundle_encoder_set_index_buffer( ) -> Result { let buffer_resource = state .resource_table - .get::(buffer) - .map_err(BundleError::Resource)?; - let render_bundle_encoder_resource = state - .resource_table - .get::(render_bundle_encoder_rid) - .map_err(BundleError::Resource)?; + .get::(buffer)?; + let render_bundle_encoder_resource = + state + .resource_table + .get::(render_bundle_encoder_rid)?; let size = Some(std::num::NonZeroU64::new(size).ok_or(BundleError::InvalidSize)?); @@ -304,12 +311,11 @@ pub fn op_webgpu_render_bundle_encoder_set_vertex_buffer( ) -> Result { let buffer_resource = state .resource_table - .get::(buffer) - .map_err(BundleError::Resource)?; - let render_bundle_encoder_resource = state - .resource_table - .get::(render_bundle_encoder_rid) - .map_err(BundleError::Resource)?; + .get::(buffer)?; + let render_bundle_encoder_resource = + state + .resource_table + .get::(render_bundle_encoder_rid)?; let size = if let Some(size) = size { Some(std::num::NonZeroU64::new(size).ok_or(BundleError::InvalidSize)?) } else { @@ -336,7 +342,7 @@ pub fn op_webgpu_render_bundle_encoder_draw( instance_count: u32, first_vertex: u32, first_instance: u32, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -363,7 +369,7 @@ pub fn op_webgpu_render_bundle_encoder_draw_indexed( first_index: u32, base_vertex: i32, first_instance: u32, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -388,7 +394,7 @@ pub fn op_webgpu_render_bundle_encoder_draw_indirect( #[smi] render_bundle_encoder_rid: ResourceId, #[smi] indirect_buffer: ResourceId, #[number] indirect_offset: u64, -) -> Result { +) -> Result { let buffer_resource = state .resource_table .get::(indirect_buffer)?; diff --git a/ext/webgpu/byow.rs b/ext/webgpu/byow.rs index c9e1177b1e..e911e1402b 100644 --- a/ext/webgpu/byow.rs +++ b/ext/webgpu/byow.rs @@ -1,8 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::op2; -use deno_core::OpState; -use deno_core::ResourceId; use std::ffi::c_void; #[cfg(any( target_os = "linux", @@ -12,20 +9,29 @@ use std::ffi::c_void; ))] use std::ptr::NonNull; +use deno_core::op2; +use deno_core::OpState; +use deno_core::ResourceId; + use crate::surface::WebGpuSurface; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ByowError { + #[class(type)] #[error("Cannot create surface outside of WebGPU context. Did you forget to call `navigator.gpu.requestAdapter()`?")] WebGPUNotInitiated, + #[class(type)] #[error("Invalid parameters")] InvalidParameters, + #[class(generic)] #[error(transparent)] CreateSurface(wgpu_core::instance::CreateSurfaceError), #[cfg(target_os = "windows")] + #[class(type)] #[error("Invalid system on Windows")] InvalidSystem, #[cfg(target_os = "macos")] + #[class(type)] #[error("Invalid system on macOS")] InvalidSystem, #[cfg(any( @@ -33,6 +39,7 @@ pub enum ByowError { target_os = "freebsd", target_os = "openbsd" ))] + #[class(type)] #[error("Invalid system on Linux/BSD")] InvalidSystem, #[cfg(any( @@ -41,6 +48,7 @@ pub enum ByowError { target_os = "freebsd", target_os = "openbsd" ))] + #[class(type)] #[error("window is null")] NullWindow, #[cfg(any( @@ -48,9 +56,11 @@ pub enum ByowError { target_os = "freebsd", target_os = "openbsd" ))] + #[class(type)] #[error("display is null")] NullDisplay, #[cfg(target_os = "macos")] + #[class(type)] #[error("ns_view is null")] NSViewDisplay, } @@ -198,6 +208,6 @@ fn raw_window( _system: &str, _window: *const c_void, _display: *const c_void, -) -> Result { - Err(deno_core::error::type_error("Unsupported platform")) +) -> Result { + Err(deno_error::JsErrorBox::type_error("Unsupported platform")) } diff --git a/ext/webgpu/command_encoder.rs b/ext/webgpu/command_encoder.rs index 4bee7aac30..9b6bb44ae8 100644 --- a/ext/webgpu/command_encoder.rs +++ b/ext/webgpu/command_encoder.rs @@ -1,17 +1,18 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::WebGpuQuerySet; -use deno_core::error::AnyError; +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; + +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::rc::Rc; use super::error::WebGpuResult; +use crate::WebGpuQuerySet; pub(crate) struct WebGpuCommandEncoder( pub(crate) super::Instance, @@ -49,7 +50,7 @@ pub fn op_webgpu_create_command_encoder( state: &mut OpState, #[smi] device_rid: ResourceId, #[string] label: Cow, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -109,7 +110,7 @@ pub fn op_webgpu_command_encoder_begin_render_pass( >, #[smi] occlusion_query_set: Option, #[serde] timestamp_writes: Option, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .get::(command_encoder_rid)?; @@ -148,7 +149,7 @@ pub fn op_webgpu_command_encoder_begin_render_pass( }; Ok(rp_at) }) - .collect::, AnyError>>()?; + .collect::, ResourceError>>()?; let mut processed_depth_stencil_attachment = None; @@ -244,7 +245,7 @@ pub fn op_webgpu_command_encoder_begin_compute_pass( #[smi] command_encoder_rid: ResourceId, #[string] label: Cow, #[serde] timestamp_writes: Option, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .get::(command_encoder_rid)?; @@ -294,7 +295,7 @@ pub fn op_webgpu_command_encoder_copy_buffer_to_buffer( #[smi] destination: ResourceId, #[number] destination_offset: u64, #[number] size: u64, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -346,7 +347,7 @@ pub fn op_webgpu_command_encoder_copy_buffer_to_texture( #[serde] source: GpuImageCopyBuffer, #[serde] destination: GpuImageCopyTexture, #[serde] copy_size: wgpu_types::Extent3d, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -391,7 +392,7 @@ pub fn op_webgpu_command_encoder_copy_texture_to_buffer( #[serde] source: GpuImageCopyTexture, #[serde] destination: GpuImageCopyBuffer, #[serde] copy_size: wgpu_types::Extent3d, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -436,7 +437,7 @@ pub fn op_webgpu_command_encoder_copy_texture_to_texture( #[serde] source: GpuImageCopyTexture, #[serde] destination: GpuImageCopyTexture, #[serde] copy_size: wgpu_types::Extent3d, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -479,7 +480,7 @@ pub fn op_webgpu_command_encoder_clear_buffer( #[smi] buffer_rid: ResourceId, #[number] offset: u64, #[number] size: u64, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -503,7 +504,7 @@ pub fn op_webgpu_command_encoder_push_debug_group( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[string] group_label: &str, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -518,7 +519,7 @@ pub fn op_webgpu_command_encoder_push_debug_group( pub fn op_webgpu_command_encoder_pop_debug_group( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -534,7 +535,7 @@ pub fn op_webgpu_command_encoder_insert_debug_marker( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[string] marker_label: &str, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -554,7 +555,7 @@ pub fn op_webgpu_command_encoder_write_timestamp( #[smi] command_encoder_rid: ResourceId, #[smi] query_set: ResourceId, query_index: u32, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -581,7 +582,7 @@ pub fn op_webgpu_command_encoder_resolve_query_set( query_count: u32, #[smi] destination: ResourceId, #[number] destination_offset: u64, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -610,7 +611,7 @@ pub fn op_webgpu_command_encoder_finish( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[string] label: Cow, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .take::(command_encoder_rid)?; diff --git a/ext/webgpu/compute_pass.rs b/ext/webgpu/compute_pass.rs index 17043c7671..afa19b3fac 100644 --- a/ext/webgpu/compute_pass.rs +++ b/ext/webgpu/compute_pass.rs @@ -1,12 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::error::AnyError; +use std::borrow::Cow; +use std::cell::RefCell; + +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; -use std::borrow::Cow; -use std::cell::RefCell; use super::error::WebGpuResult; @@ -25,7 +26,7 @@ pub fn op_webgpu_compute_pass_set_pipeline( state: &mut OpState, #[smi] compute_pass_rid: ResourceId, #[smi] pipeline: ResourceId, -) -> Result { +) -> Result { let compute_pipeline_resource = state .resource_table @@ -50,7 +51,7 @@ pub fn op_webgpu_compute_pass_dispatch_workgroups( x: u32, y: u32, z: u32, -) -> Result { +) -> Result { let compute_pass_resource = state .resource_table .get::(compute_pass_rid)?; @@ -72,7 +73,7 @@ pub fn op_webgpu_compute_pass_dispatch_workgroups_indirect( #[smi] compute_pass_rid: ResourceId, #[smi] indirect_buffer: ResourceId, #[number] indirect_offset: u64, -) -> Result { +) -> Result { let buffer_resource = state .resource_table .get::(indirect_buffer)?; @@ -95,7 +96,7 @@ pub fn op_webgpu_compute_pass_end( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[smi] compute_pass_rid: ResourceId, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .get::( @@ -124,7 +125,7 @@ pub fn op_webgpu_compute_pass_set_bind_group( #[buffer] dynamic_offsets_data: &[u32], #[number] dynamic_offsets_data_start: usize, #[number] dynamic_offsets_data_length: usize, -) -> Result { +) -> Result { let bind_group_resource = state .resource_table @@ -158,7 +159,7 @@ pub fn op_webgpu_compute_pass_push_debug_group( state: &mut OpState, #[smi] compute_pass_rid: ResourceId, #[string] group_label: &str, -) -> Result { +) -> Result { let compute_pass_resource = state .resource_table .get::(compute_pass_rid)?; @@ -177,7 +178,7 @@ pub fn op_webgpu_compute_pass_push_debug_group( pub fn op_webgpu_compute_pass_pop_debug_group( state: &mut OpState, #[smi] compute_pass_rid: ResourceId, -) -> Result { +) -> Result { let compute_pass_resource = state .resource_table .get::(compute_pass_rid)?; @@ -195,7 +196,7 @@ pub fn op_webgpu_compute_pass_insert_debug_marker( state: &mut OpState, #[smi] compute_pass_rid: ResourceId, #[string] marker_label: &str, -) -> Result { +) -> Result { let compute_pass_resource = state .resource_table .get::(compute_pass_rid)?; diff --git a/ext/webgpu/error.rs b/ext/webgpu/error.rs index f08f765386..f022a56916 100644 --- a/ext/webgpu/error.rs +++ b/ext/webgpu/error.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::convert::From; +use std::error::Error; use deno_core::ResourceId; use serde::Serialize; -use std::convert::From; -use std::error::Error; use wgpu_core::binding_model::CreateBindGroupError; use wgpu_core::binding_model::CreateBindGroupLayoutError; use wgpu_core::binding_model::CreatePipelineLayoutError; diff --git a/ext/webgpu/lib.rs b/ext/webgpu/lib.rs index 5dc8278e41..afcd808f74 100644 --- a/ext/webgpu/lib.rs +++ b/ext/webgpu/lib.rs @@ -1,22 +1,22 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![cfg(not(target_arch = "wasm32"))] #![warn(unsafe_op_in_unsafe_fn)] +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashSet; +use std::rc::Rc; + use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +use error::WebGpuResult; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::HashSet; -use std::rc::Rc; pub use wgpu_core; pub use wgpu_types; -use error::WebGpuResult; - pub const UNSTABLE_FEATURE_NAME: &str = "webgpu"; #[macro_use] @@ -83,14 +83,22 @@ pub mod shader; pub mod surface; pub mod texture; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum InitError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(generic)] #[error(transparent)] InvalidAdapter(wgpu_core::instance::InvalidAdapter), + #[class("DOMExceptionOperationError")] #[error(transparent)] RequestDevice(wgpu_core::instance::RequestDeviceError), + #[class(generic)] #[error(transparent)] InvalidDevice(wgpu_core::device::InvalidDevice), } @@ -676,10 +684,8 @@ pub fn op_webgpu_request_device( #[serde] required_limits: Option, ) -> Result { let mut state = state.borrow_mut(); - let adapter_resource = state - .resource_table - .take::(adapter_rid) - .map_err(InitError::Resource)?; + let adapter_resource = + state.resource_table.take::(adapter_rid)?; let adapter = adapter_resource.1; let instance = state.borrow::(); @@ -738,10 +744,8 @@ pub fn op_webgpu_request_adapter_info( #[smi] adapter_rid: ResourceId, ) -> Result { let state = state.borrow_mut(); - let adapter_resource = state - .resource_table - .get::(adapter_rid) - .map_err(InitError::Resource)?; + let adapter_resource = + state.resource_table.get::(adapter_rid)?; let adapter = adapter_resource.1; let instance = state.borrow::(); @@ -788,10 +792,8 @@ pub fn op_webgpu_create_query_set( state: &mut OpState, #[serde] args: CreateQuerySetArgs, ) -> Result { - let device_resource = state - .resource_table - .get::(args.device_rid) - .map_err(InitError::Resource)?; + let device_resource = + state.resource_table.get::(args.device_rid)?; let device = device_resource.1; let instance = state.borrow::(); diff --git a/ext/webgpu/pipeline.rs b/ext/webgpu/pipeline.rs index a6b0cb8cec..07452a1caf 100644 --- a/ext/webgpu/pipeline.rs +++ b/ext/webgpu/pipeline.rs @@ -1,15 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::error::AnyError; +use std::borrow::Cow; +use std::collections::HashMap; +use std::rc::Rc; + +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::collections::HashMap; -use std::rc::Rc; use super::error::WebGpuError; use super::error::WebGpuResult; @@ -87,7 +88,7 @@ pub fn op_webgpu_create_compute_pipeline( #[string] label: Cow, #[serde] layout: GPUPipelineLayoutOrGPUAutoLayoutMode, #[serde] compute: GpuProgrammableStage, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -155,7 +156,7 @@ pub fn op_webgpu_compute_pipeline_get_bind_group_layout( state: &mut OpState, #[smi] compute_pipeline_rid: ResourceId, index: u32, -) -> Result { +) -> Result { let instance = state.borrow::(); let compute_pipeline_resource = state .resource_table @@ -334,7 +335,7 @@ pub struct CreateRenderPipelineArgs { pub fn op_webgpu_create_render_pipeline( state: &mut OpState, #[serde] args: CreateRenderPipelineArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -433,7 +434,7 @@ pub fn op_webgpu_render_pipeline_get_bind_group_layout( state: &mut OpState, #[smi] render_pipeline_rid: ResourceId, index: u32, -) -> Result { +) -> Result { let instance = state.borrow::(); let render_pipeline_resource = state .resource_table diff --git a/ext/webgpu/queue.rs b/ext/webgpu/queue.rs index 8c8bbec95e..51f4c4e009 100644 --- a/ext/webgpu/queue.rs +++ b/ext/webgpu/queue.rs @@ -1,17 +1,18 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::command_encoder::WebGpuCommandBuffer; -use crate::Instance; -use deno_core::error::AnyError; +use std::borrow::Cow; +use std::rc::Rc; + +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; +use crate::command_encoder::WebGpuCommandBuffer; +use crate::Instance; pub struct WebGpuQueue(pub Instance, pub wgpu_core::id::QueueId); impl Resource for WebGpuQueue { @@ -30,7 +31,7 @@ pub fn op_webgpu_queue_submit( state: &mut OpState, #[smi] queue_rid: ResourceId, #[serde] command_buffers: Vec, -) -> Result { +) -> Result { let instance = state.borrow::(); let queue_resource = state.resource_table.get::(queue_rid)?; let queue = queue_resource.1; @@ -43,7 +44,7 @@ pub fn op_webgpu_queue_submit( let mut id = buffer_resource.1.borrow_mut(); Ok(id.take().unwrap()) }) - .collect::, AnyError>>()?; + .collect::, ResourceError>>()?; let maybe_err = gfx_select!(queue => instance.queue_submit(queue, &ids)).err(); @@ -84,7 +85,7 @@ pub fn op_webgpu_write_buffer( #[number] data_offset: usize, #[number] size: Option, #[buffer] buf: &[u8], -) -> Result { +) -> Result { let instance = state.borrow::(); let buffer_resource = state .resource_table @@ -117,7 +118,7 @@ pub fn op_webgpu_write_texture( #[serde] data_layout: GpuImageDataLayout, #[serde] size: wgpu_types::Extent3d, #[buffer] buf: &[u8], -) -> Result { +) -> Result { let instance = state.borrow::(); let texture_resource = state .resource_table diff --git a/ext/webgpu/render_pass.rs b/ext/webgpu/render_pass.rs index 9b9d87d9fc..43c4cae846 100644 --- a/ext/webgpu/render_pass.rs +++ b/ext/webgpu/render_pass.rs @@ -1,19 +1,27 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; + +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; use super::error::WebGpuResult; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum RenderPassError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + ResourceError, + ), + #[class(type)] #[error("size must be larger than 0")] InvalidSize, } @@ -44,7 +52,7 @@ pub struct RenderPassSetViewportArgs { pub fn op_webgpu_render_pass_set_viewport( state: &mut OpState, #[serde] args: RenderPassSetViewportArgs, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(args.render_pass_rid)?; @@ -71,7 +79,7 @@ pub fn op_webgpu_render_pass_set_scissor_rect( y: u32, width: u32, height: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -93,7 +101,7 @@ pub fn op_webgpu_render_pass_set_blend_constant( state: &mut OpState, #[smi] render_pass_rid: ResourceId, #[serde] color: wgpu_types::Color, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -112,7 +120,7 @@ pub fn op_webgpu_render_pass_set_stencil_reference( state: &mut OpState, #[smi] render_pass_rid: ResourceId, reference: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -131,7 +139,7 @@ pub fn op_webgpu_render_pass_begin_occlusion_query( state: &mut OpState, #[smi] render_pass_rid: ResourceId, query_index: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -149,7 +157,7 @@ pub fn op_webgpu_render_pass_begin_occlusion_query( pub fn op_webgpu_render_pass_end_occlusion_query( state: &mut OpState, #[smi] render_pass_rid: ResourceId, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -167,7 +175,7 @@ pub fn op_webgpu_render_pass_execute_bundles( state: &mut OpState, #[smi] render_pass_rid: ResourceId, #[serde] bundles: Vec, -) -> Result { +) -> Result { let bundles = bundles .iter() .map(|rid| { @@ -177,7 +185,7 @@ pub fn op_webgpu_render_pass_execute_bundles( .get::(*rid)?; Ok(render_bundle_resource.1) }) - .collect::, deno_core::error::AnyError>>()?; + .collect::, ResourceError>>()?; let render_pass_resource = state .resource_table @@ -197,7 +205,7 @@ pub fn op_webgpu_render_pass_end( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[smi] render_pass_rid: ResourceId, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .get::( @@ -223,7 +231,7 @@ pub fn op_webgpu_render_pass_set_bind_group( #[buffer] dynamic_offsets_data: &[u32], #[number] dynamic_offsets_data_start: usize, #[number] dynamic_offsets_data_length: usize, -) -> Result { +) -> Result { let bind_group_resource = state .resource_table @@ -257,7 +265,7 @@ pub fn op_webgpu_render_pass_push_debug_group( state: &mut OpState, #[smi] render_pass_rid: ResourceId, #[string] group_label: &str, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -276,7 +284,7 @@ pub fn op_webgpu_render_pass_push_debug_group( pub fn op_webgpu_render_pass_pop_debug_group( state: &mut OpState, #[smi] render_pass_rid: ResourceId, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -294,7 +302,7 @@ pub fn op_webgpu_render_pass_insert_debug_marker( state: &mut OpState, #[smi] render_pass_rid: ResourceId, #[string] marker_label: &str, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -314,7 +322,7 @@ pub fn op_webgpu_render_pass_set_pipeline( state: &mut OpState, #[smi] render_pass_rid: ResourceId, pipeline: u32, -) -> Result { +) -> Result { let render_pipeline_resource = state .resource_table @@ -343,12 +351,10 @@ pub fn op_webgpu_render_pass_set_index_buffer( ) -> Result { let buffer_resource = state .resource_table - .get::(buffer) - .map_err(RenderPassError::Resource)?; + .get::(buffer)?; let render_pass_resource = state .resource_table - .get::(render_pass_rid) - .map_err(RenderPassError::Resource)?; + .get::(render_pass_rid)?; let size = if let Some(size) = size { Some(std::num::NonZeroU64::new(size).ok_or(RenderPassError::InvalidSize)?) @@ -378,12 +384,10 @@ pub fn op_webgpu_render_pass_set_vertex_buffer( ) -> Result { let buffer_resource = state .resource_table - .get::(buffer) - .map_err(RenderPassError::Resource)?; + .get::(buffer)?; let render_pass_resource = state .resource_table - .get::(render_pass_rid) - .map_err(RenderPassError::Resource)?; + .get::(render_pass_rid)?; let size = if let Some(size) = size { Some(std::num::NonZeroU64::new(size).ok_or(RenderPassError::InvalidSize)?) @@ -411,7 +415,7 @@ pub fn op_webgpu_render_pass_draw( instance_count: u32, first_vertex: u32, first_instance: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -437,7 +441,7 @@ pub fn op_webgpu_render_pass_draw_indexed( first_index: u32, base_vertex: i32, first_instance: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -461,7 +465,7 @@ pub fn op_webgpu_render_pass_draw_indirect( #[smi] render_pass_rid: ResourceId, indirect_buffer: u32, #[number] indirect_offset: u64, -) -> Result { +) -> Result { let buffer_resource = state .resource_table .get::(indirect_buffer)?; @@ -485,7 +489,7 @@ pub fn op_webgpu_render_pass_draw_indexed_indirect( #[smi] render_pass_rid: ResourceId, indirect_buffer: u32, #[number] indirect_offset: u64, -) -> Result { +) -> Result { let buffer_resource = state .resource_table .get::(indirect_buffer)?; diff --git a/ext/webgpu/sampler.rs b/ext/webgpu/sampler.rs index 9fc1269ea7..88c1947e63 100644 --- a/ext/webgpu/sampler.rs +++ b/ext/webgpu/sampler.rs @@ -1,12 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::rc::Rc; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; @@ -46,7 +47,7 @@ pub struct CreateSamplerArgs { pub fn op_webgpu_create_sampler( state: &mut OpState, #[serde] args: CreateSamplerArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table diff --git a/ext/webgpu/shader.rs b/ext/webgpu/shader.rs index 4653bd85bf..84615ea6f6 100644 --- a/ext/webgpu/shader.rs +++ b/ext/webgpu/shader.rs @@ -1,11 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::rc::Rc; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; @@ -30,7 +31,7 @@ pub fn op_webgpu_create_shader_module( #[smi] device_rid: ResourceId, #[string] label: Cow, #[string] code: Cow, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table diff --git a/ext/webgpu/surface.rs b/ext/webgpu/surface.rs index 297eaeb008..23e617c7de 100644 --- a/ext/webgpu/surface.rs +++ b/ext/webgpu/surface.rs @@ -1,21 +1,31 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use super::WebGpuResult; +use std::borrow::Cow; +use std::rc::Rc; + +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use wgpu_types::SurfaceStatus; -#[derive(Debug, thiserror::Error)] +use crate::error::WebGpuResult; + +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SurfaceError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + ResourceError, + ), + #[class(generic)] #[error("Invalid Surface Status")] InvalidStatus, + #[class(generic)] #[error(transparent)] Surface(wgpu_core::present::SurfaceError), } @@ -50,7 +60,7 @@ pub struct SurfaceConfigureArgs { pub fn op_webgpu_surface_configure( state: &mut OpState, #[serde] args: SurfaceConfigureArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -88,13 +98,10 @@ pub fn op_webgpu_surface_get_current_texture( let instance = state.borrow::(); let device_resource = state .resource_table - .get::(device_rid) - .map_err(SurfaceError::Resource)?; + .get::(device_rid)?; let device = device_resource.1; - let surface_resource = state - .resource_table - .get::(surface_rid) - .map_err(SurfaceError::Resource)?; + let surface_resource = + state.resource_table.get::(surface_rid)?; let surface = surface_resource.1; let output = @@ -124,13 +131,10 @@ pub fn op_webgpu_surface_present( let instance = state.borrow::(); let device_resource = state .resource_table - .get::(device_rid) - .map_err(SurfaceError::Resource)?; + .get::(device_rid)?; let device = device_resource.1; - let surface_resource = state - .resource_table - .get::(surface_rid) - .map_err(SurfaceError::Resource)?; + let surface_resource = + state.resource_table.get::(surface_rid)?; let surface = surface_resource.1; let _ = gfx_select!(device => instance.surface_present(surface)) diff --git a/ext/webgpu/texture.rs b/ext/webgpu/texture.rs index f8a5e05a3e..10c5d902ee 100644 --- a/ext/webgpu/texture.rs +++ b/ext/webgpu/texture.rs @@ -1,12 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::rc::Rc; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; pub(crate) struct WebGpuTexture { @@ -61,7 +62,7 @@ pub struct CreateTextureArgs { pub fn op_webgpu_create_texture( state: &mut OpState, #[serde] args: CreateTextureArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -110,7 +111,7 @@ pub struct CreateTextureViewArgs { pub fn op_webgpu_create_texture_view( state: &mut OpState, #[serde] args: CreateTextureViewArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let texture_resource = state .resource_table diff --git a/ext/webidl/00_webidl.js b/ext/webidl/00_webidl.js index eb18cbcc3e..b3c3b299f0 100644 --- a/ext/webidl/00_webidl.js +++ b/ext/webidl/00_webidl.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Adapted from https://github.com/jsdom/webidl-conversions. // Copyright Domenic Denicola. Licensed under BSD-2-Clause License. diff --git a/ext/webidl/Cargo.toml b/ext/webidl/Cargo.toml index 38eff7b66f..adb072ff45 100644 --- a/ext/webidl/Cargo.toml +++ b/ext/webidl/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_webidl" -version = "0.182.0" +version = "0.185.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webidl/benches/dict.js b/ext/webidl/benches/dict.js index 9e7367d62c..8aedc51235 100644 --- a/ext/webidl/benches/dict.js +++ b/ext/webidl/benches/dict.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/webidl/benches/dict.rs b/ext/webidl/benches/dict.rs index cfb658fa84..aea491cf12 100644 --- a/ext/webidl/benches/dict.rs +++ b/ext/webidl/benches/dict.rs @@ -1,10 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::Bencher; - use deno_core::Extension; fn setup() -> Vec { diff --git a/ext/webidl/internal.d.ts b/ext/webidl/internal.d.ts index 375d548d32..a884d982aa 100644 --- a/ext/webidl/internal.d.ts +++ b/ext/webidl/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any /// diff --git a/ext/webidl/lib.rs b/ext/webidl/lib.rs index 51dbed33a5..7a3608def4 100644 --- a/ext/webidl/lib.rs +++ b/ext/webidl/lib.rs @@ -1,3 +1,3 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. deno_core::extension!(deno_webidl, esm = ["00_webidl.js"],); diff --git a/ext/websocket/01_websocket.js b/ext/websocket/01_websocket.js index 78572f5f00..e0db51f4be 100644 --- a/ext/websocket/01_websocket.js +++ b/ext/websocket/01_websocket.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/websocket/02_websocketstream.js b/ext/websocket/02_websocketstream.js index 838ae3d4ce..a1b76fb6c0 100644 --- a/ext/websocket/02_websocketstream.js +++ b/ext/websocket/02_websocketstream.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index cb72618cad..a94da90484 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_websocket" -version = "0.187.0" +version = "0.190.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] bytes.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_net.workspace = true deno_permissions.workspace = true deno_tls.workspace = true diff --git a/ext/websocket/autobahn/autobahn_server.js b/ext/websocket/autobahn/autobahn_server.js index 7400c8d547..1c0d4bbaf6 100644 --- a/ext/websocket/autobahn/autobahn_server.js +++ b/ext/websocket/autobahn/autobahn_server.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { parseArgs } from "@std/cli/parse-args"; const { port } = parseArgs(Deno.args, { diff --git a/ext/websocket/autobahn/fuzzingclient.js b/ext/websocket/autobahn/fuzzingclient.js index fed0fd6aa9..8602319650 100644 --- a/ext/websocket/autobahn/fuzzingclient.js +++ b/ext/websocket/autobahn/fuzzingclient.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/websocket/lib.deno_websocket.d.ts b/ext/websocket/lib.deno_websocket.d.ts index fb7ea6070c..28cf8b8ab3 100644 --- a/ext/websocket/lib.deno_websocket.d.ts +++ b/ext/websocket/lib.deno_websocket.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index 5aef1a7a55..deb424c9be 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -1,5 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::stream::WebSocketStream; +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::cell::Cell; +use std::cell::RefCell; +use std::future::Future; +use std::num::NonZeroUsize; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + use bytes::Bytes; use deno_core::futures::TryFutureExt; use deno_core::op2; @@ -16,13 +24,22 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use deno_net::raw::NetworkStream; +use deno_permissions::PermissionCheckError; use deno_tls::create_client_config; use deno_tls::rustls::ClientConfig; use deno_tls::rustls::ClientConnection; use deno_tls::RootCertStoreProvider; use deno_tls::SocketUse; use deno_tls::TlsKeys; +use fastwebsockets::CloseCode; +use fastwebsockets::FragmentCollectorRead; +use fastwebsockets::Frame; +use fastwebsockets::OpCode; +use fastwebsockets::Role; +use fastwebsockets::WebSocket; +use fastwebsockets::WebSocketWrite; use http::header::CONNECTION; use http::header::UPGRADE; use http::HeaderName; @@ -36,28 +53,13 @@ use rustls_tokio_stream::rustls::pki_types::ServerName; use rustls_tokio_stream::rustls::RootCertStore; use rustls_tokio_stream::TlsStream; use serde::Serialize; -use std::borrow::Cow; -use std::cell::Cell; -use std::cell::RefCell; -use std::future::Future; -use std::num::NonZeroUsize; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; use tokio::io::AsyncRead; use tokio::io::AsyncWrite; use tokio::io::ReadHalf; use tokio::io::WriteHalf; use tokio::net::TcpStream; -use deno_permissions::PermissionCheckError; -use fastwebsockets::CloseCode; -use fastwebsockets::FragmentCollectorRead; -use fastwebsockets::Frame; -use fastwebsockets::OpCode; -use fastwebsockets::Role; -use fastwebsockets::WebSocket; -use fastwebsockets::WebSocketWrite; +use crate::stream::WebSocketStream; mod stream; @@ -71,22 +73,30 @@ static USE_WRITEV: Lazy = Lazy::new(|| { false }); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WebsocketError { + #[class(inherit)] #[error(transparent)] Url(url::ParseError), + #[class(inherit)] #[error(transparent)] Permission(#[from] PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(generic)] #[error(transparent)] Uri(#[from] http::uri::InvalidUri), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), + #[class(type)] #[error(transparent)] WebSocket(#[from] fastwebsockets::WebSocketError), + #[class("DOMExceptionNetworkError")] #[error("failed to connect to WebSocket: {0}")] ConnectionFailed(#[from] HandshakeError), + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), } @@ -95,9 +105,7 @@ pub enum WebsocketError { pub struct WsRootStoreProvider(Option>); impl WsRootStoreProvider { - pub fn get_or_try_init( - &self, - ) -> Result, deno_core::error::AnyError> { + pub fn get_or_try_init(&self) -> Result, JsErrorBox> { Ok(match &self.0 { Some(provider) => Some(provider.get_or_try_init()?.clone()), None => None, @@ -182,32 +190,45 @@ pub struct CreateResponse { extensions: String, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HandshakeError { + #[class(type)] #[error("Missing path in url")] MissingPath, + #[class(generic)] #[error("Invalid status code {0}")] InvalidStatusCode(StatusCode), + #[class(generic)] #[error(transparent)] Http(#[from] http::Error), + #[class(type)] #[error(transparent)] WebSocket(#[from] fastwebsockets::WebSocketError), + #[class(generic)] #[error("Didn't receive h2 alpn, aborting connection")] NoH2Alpn, + #[class(generic)] #[error(transparent)] Rustls(#[from] deno_tls::rustls::Error), + #[class(inherit)] #[error(transparent)] Io(#[from] std::io::Error), + #[class(generic)] #[error(transparent)] H2(#[from] h2::Error), + #[class(type)] #[error("Invalid hostname: '{0}'")] InvalidHostname(String), + #[class(inherit)] #[error(transparent)] - RootStoreError(deno_core::error::AnyError), + RootStoreError(JsErrorBox), + #[class(inherit)] #[error(transparent)] Tls(deno_tls::TlsError), + #[class(type)] #[error(transparent)] HeaderName(#[from] http::header::InvalidHeaderName), + #[class(type)] #[error(transparent)] HeaderValue(#[from] http::header::InvalidHeaderValue), } @@ -472,8 +493,7 @@ where let r = state .borrow_mut() .resource_table - .get::(cancel_rid) - .map_err(WebsocketError::Resource)?; + .get::(cancel_rid)?; Some(r.0.clone()) } else { None @@ -677,8 +697,7 @@ pub async fn op_ws_send_binary_async( let resource = state .borrow_mut() .resource_table - .get::(rid) - .map_err(WebsocketError::Resource)?; + .get::(rid)?; let data = data.to_vec(); let lock = resource.reserve_lock(); resource @@ -696,8 +715,7 @@ pub async fn op_ws_send_text_async( let resource = state .borrow_mut() .resource_table - .get::(rid) - .map_err(WebsocketError::Resource)?; + .get::(rid)?; let lock = resource.reserve_lock(); resource .write_frame( @@ -731,8 +749,7 @@ pub async fn op_ws_send_ping( let resource = state .borrow_mut() .resource_table - .get::(rid) - .map_err(WebsocketError::Resource)?; + .get::(rid)?; let lock = resource.reserve_lock(); resource .write_frame( @@ -757,9 +774,14 @@ pub async fn op_ws_close( return Ok(()); }; + const EMPTY_PAYLOAD: &[u8] = &[]; + let frame = reason .map(|reason| Frame::close(code.unwrap_or(1005), reason.as_bytes())) - .unwrap_or_else(|| Frame::close_raw(vec![].into())); + .unwrap_or_else(|| match code { + Some(code) => Frame::close(code, EMPTY_PAYLOAD), + _ => Frame::close_raw(EMPTY_PAYLOAD.into()), + }); resource.closed.set(true); let lock = resource.reserve_lock(); diff --git a/ext/websocket/stream.rs b/ext/websocket/stream.rs index 2cd2622175..c2ac4ee5d8 100644 --- a/ext/websocket/stream.rs +++ b/ext/websocket/stream.rs @@ -1,4 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::io::ErrorKind; +use std::pin::Pin; +use std::task::ready; +use std::task::Poll; + use bytes::Buf; use bytes::Bytes; use deno_net::raw::NetworkStream; @@ -6,10 +11,6 @@ use h2::RecvStream; use h2::SendStream; use hyper::upgrade::Upgraded; use hyper_util::rt::TokioIo; -use std::io::ErrorKind; -use std::pin::Pin; -use std::task::ready; -use std::task::Poll; use tokio::io::AsyncRead; use tokio::io::AsyncWrite; use tokio::io::ReadBuf; diff --git a/ext/webstorage/01_webstorage.js b/ext/webstorage/01_webstorage.js index 12abea8387..7adf190782 100644 --- a/ext/webstorage/01_webstorage.js +++ b/ext/webstorage/01_webstorage.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index 700a252016..8a900f662b 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_webstorage" -version = "0.177.0" +version = "0.180.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_web.workspace = true rusqlite.workspace = true thiserror.workspace = true diff --git a/ext/webstorage/lib.deno_webstorage.d.ts b/ext/webstorage/lib.deno_webstorage.d.ts index fa6d403dea..5f5aafa1b8 100644 --- a/ext/webstorage/lib.deno_webstorage.d.ts +++ b/ext/webstorage/lib.deno_webstorage.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/webstorage/lib.rs b/ext/webstorage/lib.rs index c3e4c46596..4653e1b948 100644 --- a/ext/webstorage/lib.rs +++ b/ext/webstorage/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // NOTE to all: use **cached** prepared statements when interfacing with SQLite. @@ -7,20 +7,23 @@ use std::path::PathBuf; use deno_core::op2; use deno_core::GarbageCollected; use deno_core::OpState; +pub use rusqlite; use rusqlite::params; use rusqlite::Connection; use rusqlite::OptionalExtension; -pub use rusqlite; - -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WebStorageError { + #[class("DOMExceptionNotSupportedError")] #[error("LocalStorage is not supported in this context.")] ContextNotSupported, + #[class(generic)] #[error(transparent)] Sqlite(#[from] rusqlite::Error), + #[class(inherit)] #[error(transparent)] Io(std::io::Error), + #[class("DOMExceptionQuotaExceededError")] #[error("Exceeded maximum storage size")] StorageExceeded, } diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 2966b5fef6..534a07ccf2 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_resolver" -version = "0.14.0" +version = "0.16.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -13,19 +13,27 @@ description = "Deno resolution algorithm" [lib] path = "lib.rs" +[features] +sync = ["dashmap", "deno_package_json/sync", "node_resolver/sync"] + [dependencies] anyhow.workspace = true +async-trait.workspace = true base32.workspace = true boxed_error.workspace = true -dashmap.workspace = true +dashmap = { workspace = true, optional = true } +deno_cache_dir.workspace = true deno_config.workspace = true +deno_error.workspace = true deno_media_type.workspace = true +deno_npm.workspace = true deno_package_json.workspace = true -deno_package_json.features = ["sync"] deno_path_util.workspace = true deno_semver.workspace = true +log.workspace = true node_resolver.workspace = true -node_resolver.features = ["sync"] +parking_lot.workspace = true +sys_traits.workspace = true thiserror.workspace = true url.workspace = true diff --git a/resolvers/deno/cjs.rs b/resolvers/deno/cjs.rs index 9ae60b6a15..4358b0ced2 100644 --- a/resolvers/deno/cjs.rs +++ b/resolvers/deno/cjs.rs @@ -1,31 +1,32 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use std::sync::Arc; - -use dashmap::DashMap; use deno_media_type::MediaType; -use node_resolver::env::NodeResolverEnv; use node_resolver::errors::ClosestPkgJsonError; use node_resolver::InNpmPackageChecker; -use node_resolver::PackageJsonResolver; +use node_resolver::PackageJsonResolverRc; use node_resolver::ResolutionMode; +use sys_traits::FsRead; use url::Url; +use crate::sync::MaybeDashMap; + /// Keeps track of what module specifiers were resolved as CJS. /// /// Modules that are `.js`, `.ts`, `.jsx`, and `tsx` are only known to /// be CJS or ESM after they're loaded based on their contents. So these /// files will be "maybe CJS" until they're loaded. #[derive(Debug)] -pub struct CjsTracker { - is_cjs_resolver: IsCjsResolver, - known: DashMap, +pub struct CjsTracker { + is_cjs_resolver: IsCjsResolver, + known: MaybeDashMap, } -impl CjsTracker { +impl + CjsTracker +{ pub fn new( - in_npm_pkg_checker: Arc, - pkg_json_resolver: Arc>, + in_npm_pkg_checker: TInNpmPackageChecker, + pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, ) -> Self { Self { @@ -126,16 +127,21 @@ pub enum IsCjsResolutionMode { /// Resolves whether a module is CJS or ESM. #[derive(Debug)] -pub struct IsCjsResolver { - in_npm_pkg_checker: Arc, - pkg_json_resolver: Arc>, +pub struct IsCjsResolver< + TInNpmPackageChecker: InNpmPackageChecker, + TSys: FsRead, +> { + in_npm_pkg_checker: TInNpmPackageChecker, + pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, } -impl IsCjsResolver { +impl + IsCjsResolver +{ pub fn new( - in_npm_pkg_checker: Arc, - pkg_json_resolver: Arc>, + in_npm_pkg_checker: TInNpmPackageChecker, + pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, ) -> Self { Self { @@ -185,7 +191,7 @@ impl IsCjsResolver { specifier: &Url, media_type: MediaType, is_script: Option, - known_cache: &DashMap, + known_cache: &MaybeDashMap, ) -> Option { if specifier.scheme() != "file" { return Some(ResolutionMode::Import); diff --git a/resolvers/deno/clippy.toml b/resolvers/deno/clippy.toml index 733ac83da1..886ba3fd1a 100644 --- a/resolvers/deno/clippy.toml +++ b/resolvers/deno/clippy.toml @@ -47,6 +47,5 @@ disallowed-methods = [ { path = "url::Url::from_directory_path", reason = "Use deno_path_util instead so it works in Wasm" }, ] disallowed-types = [ - # todo(dsherret): consider for the future - # { path = "std::sync::Arc", reason = "use crate::sync::MaybeArc instead" }, + { path = "std::sync::Arc", reason = "use crate::sync::MaybeArc instead" }, ] diff --git a/resolvers/deno/fs.rs b/resolvers/deno/fs.rs deleted file mode 100644 index f2021a73a9..0000000000 --- a/resolvers/deno/fs.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; - -pub struct DirEntry { - pub name: String, - pub is_file: bool, - pub is_directory: bool, -} - -pub trait DenoResolverFs { - fn read_to_string_lossy( - &self, - path: &Path, - ) -> std::io::Result>; - fn realpath_sync(&self, path: &Path) -> std::io::Result; - fn exists_sync(&self, path: &Path) -> bool; - fn is_dir_sync(&self, path: &Path) -> bool; - fn read_dir_sync(&self, dir_path: &Path) -> std::io::Result>; -} diff --git a/resolvers/deno/lib.rs b/resolvers/deno/lib.rs index a74ca614a3..454c7f12e2 100644 --- a/resolvers/deno/lib.rs +++ b/resolvers/deno/lib.rs @@ -1,45 +1,60 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![deny(clippy::print_stderr)] #![deny(clippy::print_stdout)] use std::path::PathBuf; -use std::sync::Arc; use boxed_error::Boxed; +use deno_cache_dir::npm::NpmCacheDir; use deno_config::workspace::MappedResolution; use deno_config::workspace::MappedResolutionDiagnostic; use deno_config::workspace::MappedResolutionError; use deno_config::workspace::WorkspaceResolvePkgJsonFolderError; use deno_config::workspace::WorkspaceResolver; +use deno_error::JsError; +use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValueParseError; use deno_semver::npm::NpmPackageReqReference; -use fs::DenoResolverFs; -use node_resolver::env::NodeResolverEnv; use node_resolver::errors::NodeResolveError; use node_resolver::errors::PackageSubpathResolveError; use node_resolver::InNpmPackageChecker; +use node_resolver::IsBuiltInNodeModuleChecker; use node_resolver::NodeResolution; use node_resolver::NodeResolutionKind; -use node_resolver::NodeResolver; +use node_resolver::NodeResolverRc; +use node_resolver::NpmPackageFolderResolver; use node_resolver::ResolutionMode; use npm::MissingPackageNodeModulesFolderError; use npm::NodeModulesOutOfDateError; -use npm::NpmReqResolver; +use npm::NpmReqResolverRc; use npm::ResolveIfForNpmPackageErrorKind; use npm::ResolvePkgFolderFromDenoReqError; use npm::ResolveReqWithSubPathErrorKind; use sloppy_imports::SloppyImportResolverFs; use sloppy_imports::SloppyImportsResolutionKind; -use sloppy_imports::SloppyImportsResolver; +use sloppy_imports::SloppyImportsResolverRc; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsRead; +use sys_traits::FsReadDir; use thiserror::Error; use url::Url; pub mod cjs; -pub mod fs; pub mod npm; pub mod sloppy_imports; +mod sync; + +#[allow(clippy::disallowed_types)] +pub type WorkspaceResolverRc = crate::sync::MaybeArc; + +#[allow(clippy::disallowed_types)] +pub(crate) type ResolvedNpmRcRc = crate::sync::MaybeArc; + +#[allow(clippy::disallowed_types)] +pub(crate) type NpmCacheDirRc = crate::sync::MaybeArc; #[derive(Debug, Clone)] pub struct DenoResolution { @@ -48,54 +63,84 @@ pub struct DenoResolution { pub found_package_json_dep: bool, } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct DenoResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum DenoResolveErrorKind { + #[class(type)] #[error("Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring.")] InvalidVendorFolderImport, + #[class(inherit)] #[error(transparent)] MappedResolution(#[from] MappedResolutionError), + #[class(inherit)] #[error(transparent)] MissingPackageNodeModulesFolder(#[from] MissingPackageNodeModulesFolderError), + #[class(inherit)] #[error(transparent)] Node(#[from] NodeResolveError), + #[class(inherit)] #[error(transparent)] NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError), + #[class(inherit)] #[error(transparent)] PackageJsonDepValueParse(#[from] PackageJsonDepValueParseError), + #[class(inherit)] #[error(transparent)] PackageJsonDepValueUrlParse(url::ParseError), + #[class(inherit)] #[error(transparent)] PackageSubpathResolve(#[from] PackageSubpathResolveError), + #[class(inherit)] #[error(transparent)] ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError), + #[class(inherit)] #[error(transparent)] WorkspaceResolvePkgJsonFolder(#[from] WorkspaceResolvePkgJsonFolderError), } #[derive(Debug)] pub struct NodeAndNpmReqResolver< - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - pub node_resolver: Arc>, - pub npm_req_resolver: Arc>, + pub node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + pub npm_req_resolver: NpmReqResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, } pub struct DenoResolverOptions< 'a, - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSloppyImportResolverFs: SloppyImportResolverFs, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - pub in_npm_pkg_checker: Arc, - pub node_and_req_resolver: - Option>, + pub in_npm_pkg_checker: TInNpmPackageChecker, + pub node_and_req_resolver: Option< + NodeAndNpmReqResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + >, pub sloppy_imports_resolver: - Option>>, - pub workspace_resolver: Arc, + Option>, + pub workspace_resolver: WorkspaceResolverRc, /// Whether "bring your own node_modules" is enabled where Deno does not /// setup the node_modules directories automatically, but instead uses /// what already exists on the file system. @@ -107,27 +152,51 @@ pub struct DenoResolverOptions< /// import map, JSX settings. #[derive(Debug)] pub struct DenoResolver< - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSloppyImportResolverFs: SloppyImportResolverFs, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - in_npm_pkg_checker: Arc, - node_and_npm_resolver: Option>, + in_npm_pkg_checker: TInNpmPackageChecker, + node_and_npm_resolver: Option< + NodeAndNpmReqResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + >, sloppy_imports_resolver: - Option>>, - workspace_resolver: Arc, + Option>, + workspace_resolver: WorkspaceResolverRc, is_byonm: bool, maybe_vendor_specifier: Option, } impl< - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSloppyImportResolverFs: SloppyImportResolverFs, - > DenoResolver + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, + > + DenoResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSloppyImportResolverFs, + TSys, + > { pub fn new( - options: DenoResolverOptions, + options: DenoResolverOptions< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSloppyImportResolverFs, + TSys, + >, ) -> Self { Self { in_npm_pkg_checker: options.in_npm_pkg_checker, diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index 6e1be35ca0..4f72692f8b 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -1,16 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; -use std::sync::Arc; use deno_package_json::PackageJson; use deno_package_json::PackageJsonDepValue; +use deno_package_json::PackageJsonRc; use deno_path_util::url_to_file_path; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::Version; -use node_resolver::env::NodeResolverEnv; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageJsonLoadError; @@ -18,73 +18,83 @@ use node_resolver::errors::PackageNotFoundError; use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; use node_resolver::PackageJsonResolverRc; +use sys_traits::FsCanonicalize; +use sys_traits::FsDirEntry; +use sys_traits::FsMetadata; +use sys_traits::FsRead; +use sys_traits::FsReadDir; use thiserror::Error; use url::Url; -use crate::fs::DenoResolverFs; - use super::local::normalize_pkg_name_for_node_modules_deno_folder; -use super::CliNpmReqResolver; -use super::ResolvePkgFolderFromDenoReqError; -#[derive(Debug, Error)] +#[derive(Debug, Error, deno_error::JsError)] pub enum ByonmResolvePkgFolderFromDenoReqError { + #[class(generic)] #[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), + MissingAlias(StackString), + #[class(inherit)] #[error(transparent)] PackageJson(#[from] PackageJsonLoadError), + #[class(generic)] #[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), + #[class(inherit)] #[error(transparent)] Io(#[from] std::io::Error), } -pub struct ByonmNpmResolverCreateOptions< - Fs: DenoResolverFs, - TEnv: NodeResolverEnv, -> { +pub struct ByonmNpmResolverCreateOptions { // todo(dsherret): investigate removing this pub root_node_modules_dir: Option, - pub fs: Fs, - pub pkg_json_resolver: PackageJsonResolverRc, + pub sys: TSys, + pub pkg_json_resolver: PackageJsonResolverRc, } +#[allow(clippy::disallowed_types)] +pub type ByonmNpmResolverRc = + crate::sync::MaybeArc>; + #[derive(Debug)] -pub struct ByonmNpmResolver { - fs: Fs, - pkg_json_resolver: PackageJsonResolverRc, +pub struct ByonmNpmResolver< + TSys: FsCanonicalize + FsRead + FsMetadata + FsReadDir, +> { + sys: TSys, + pkg_json_resolver: PackageJsonResolverRc, root_node_modules_dir: Option, } -impl Clone - for ByonmNpmResolver +impl Clone + for ByonmNpmResolver { fn clone(&self) -> Self { Self { - fs: self.fs.clone(), + sys: self.sys.clone(), pkg_json_resolver: self.pkg_json_resolver.clone(), root_node_modules_dir: self.root_node_modules_dir.clone(), } } } -impl ByonmNpmResolver { - pub fn new(options: ByonmNpmResolverCreateOptions) -> Self { +impl + ByonmNpmResolver +{ + pub fn new(options: ByonmNpmResolverCreateOptions) -> Self { Self { root_node_modules_dir: options.root_node_modules_dir, - fs: options.fs, + sys: options.sys, pkg_json_resolver: options.pkg_json_resolver, } } - pub fn root_node_modules_dir(&self) -> Option<&Path> { + pub fn root_node_modules_path(&self) -> Option<&Path> { self.root_node_modules_dir.as_deref() } fn load_pkg_json( &self, path: &Path, - ) -> Result>, PackageJsonLoadError> { + ) -> Result, PackageJsonLoadError> { self.pkg_json_resolver.load_package_json(path) } @@ -93,7 +103,7 @@ impl ByonmNpmResolver { &self, dep_name: &str, referrer: &Url, - ) -> Option> { + ) -> Option { let referrer_path = url_to_file_path(referrer).ok()?; let mut current_folder = referrer_path.parent()?; loop { @@ -124,19 +134,20 @@ impl ByonmNpmResolver { req: &PackageReq, referrer: &Url, ) -> Result { - fn node_resolve_dir( - fs: &Fs, + fn node_resolve_dir( + sys: &TSys, alias: &str, start_dir: &Path, ) -> 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); - if fs.is_dir_sync(&sub_dir) { - return Ok(Some(deno_path_util::canonicalize_path_maybe_not_exists( - &sub_dir, - &|path| fs.realpath_sync(path), - )?)); + if sys.fs_is_dir_no_err(&sub_dir) { + return Ok(Some( + deno_path_util::fs::canonicalize_path_maybe_not_exists( + sys, &sub_dir, + )?, + )); } } Ok(None) @@ -149,7 +160,7 @@ impl ByonmNpmResolver { Some((pkg_json, alias)) => { // now try node resolution if let Some(resolved) = - node_resolve_dir(&self.fs, &alias, pkg_json.dir_path())? + node_resolve_dir(&self.sys, &alias, pkg_json.dir_path())? { return Ok(resolved); } @@ -173,16 +184,14 @@ impl ByonmNpmResolver { &self, req: &PackageReq, referrer: &Url, - ) -> Result, String)>, PackageJsonLoadError> { + ) -> Result, PackageJsonLoadError> { fn resolve_alias_from_pkg_json( req: &PackageReq, pkg_json: &PackageJson, - ) -> Option { + ) -> Option { let deps = pkg_json.resolve_local_package_json_deps(); - for (key, value) in deps - .dependencies - .into_iter() - .chain(deps.dev_dependencies.into_iter()) + for (key, value) in + deps.dependencies.iter().chain(deps.dev_dependencies.iter()) { if let Ok(value) = value { match value { @@ -190,12 +199,14 @@ impl ByonmNpmResolver { if dep_req.name == req.name && dep_req.version_req.intersects(&req.version_req) { - return Some(key); + return Some(key.clone()); } } PackageJsonDepValue::Workspace(_workspace) => { - if key == req.name && req.version_req.tag() == Some("workspace") { - return Some(key); + if key.as_str() == req.name + && req.version_req.tag() == Some("workspace") + { + return Some(key.clone()); } } } @@ -242,7 +253,7 @@ impl ByonmNpmResolver { if let Ok(Some(dep_pkg_json)) = self.load_pkg_json(&pkg_folder.join("package.json")) { - if dep_pkg_json.name.as_ref() == Some(&req.name) { + if dep_pkg_json.name.as_deref() == Some(req.name.as_str()) { let matches_req = dep_pkg_json .version .as_ref() @@ -293,7 +304,7 @@ impl ByonmNpmResolver { // now check if node_modules/.deno/ matches this constraint let root_node_modules_dir = self.root_node_modules_dir.as_ref()?; let node_modules_deno_dir = root_node_modules_dir.join(".deno"); - let Ok(entries) = self.fs.read_dir_sync(&node_modules_deno_dir) else { + let Ok(entries) = self.sys.fs_read_dir(&node_modules_deno_dir) else { return None; }; let search_prefix = format!( @@ -306,10 +317,17 @@ impl ByonmNpmResolver { // - @denotest+add@1.0.0 // - @denotest+add@1.0.0_1 for entry in entries { - if !entry.is_directory { + let Ok(entry) = entry else { + continue; + }; + let Ok(file_type) = entry.file_type() else { + continue; + }; + if !file_type.is_dir() { continue; } - let Some(version_and_copy_idx) = entry.name.strip_prefix(&search_prefix) + let entry_name = entry.file_name().to_string_lossy().into_owned(); + let Some(version_and_copy_idx) = entry_name.strip_prefix(&search_prefix) else { continue; }; @@ -322,8 +340,8 @@ impl ByonmNpmResolver { }; 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) + node_modules_deno_dir.join(&entry_name).join(".initialized"); + let Ok(contents) = self.sys.fs_read_to_string_lossy(&initialized_file) else { continue; }; @@ -331,19 +349,19 @@ impl ByonmNpmResolver { 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)); + best_version = Some((version, entry_name)); } } else { - best_version = Some((version, entry.name)); + 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)); + best_version = Some((version, entry_name)); } } else { - best_version = Some((version, entry.name)); + best_version = Some((version, entry_name)); } } } @@ -357,35 +375,16 @@ impl ByonmNpmResolver { } } -impl< - Fs: DenoResolverFs + Send + Sync + std::fmt::Debug, - TEnv: NodeResolverEnv, - > CliNpmReqResolver for ByonmNpmResolver -{ - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - referrer: &Url, - ) -> Result { - ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req( - self, req, referrer, - ) - .map_err(ResolvePkgFolderFromDenoReqError::Byonm) - } -} - -impl< - Fs: DenoResolverFs + Send + Sync + std::fmt::Debug, - TEnv: NodeResolverEnv, - > NpmPackageFolderResolver for ByonmNpmResolver +impl + NpmPackageFolderResolver for ByonmNpmResolver { fn resolve_package_folder_from_package( &self, name: &str, referrer: &Url, ) -> Result { - fn inner( - fs: &Fs, + fn inner( + sys: &TSys, name: &str, referrer: &Url, ) -> Result { @@ -402,7 +401,7 @@ impl< }; let sub_dir = join_package_name(&node_modules_folder, name); - if fs.is_dir_sync(&sub_dir) { + if sys.fs_is_dir_no_err(&sub_dir) { return Ok(sub_dir); } } @@ -418,8 +417,8 @@ impl< ) } - let path = inner(&self.fs, name, referrer)?; - self.fs.realpath_sync(&path).map_err(|err| { + let path = inner(&self.sys, name, referrer)?; + self.sys.fs_canonicalize(&path).map_err(|err| { PackageFolderResolveIoError { package_name: name.to_string(), referrer: referrer.clone(), @@ -430,7 +429,7 @@ impl< } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ByonmInNpmPackageChecker; impl InNpmPackageChecker for ByonmInNpmPackageChecker { diff --git a/resolvers/deno/npm/local.rs b/resolvers/deno/npm/local.rs index aef476ad94..b05864b856 100644 --- a/resolvers/deno/npm/local.rs +++ b/resolvers/deno/npm/local.rs @@ -1,7 +1,59 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; +use deno_cache_dir::npm::mixed_case_package_name_decode; +use deno_npm::NpmPackageCacheFolderId; +use deno_semver::package::PackageNv; +use deno_semver::StackString; + +#[inline] +pub fn get_package_folder_id_folder_name( + folder_id: &NpmPackageCacheFolderId, +) -> String { + get_package_folder_id_folder_name_from_parts( + &folder_id.nv, + folder_id.copy_index, + ) +} + +pub fn get_package_folder_id_folder_name_from_parts( + nv: &PackageNv, + copy_index: u8, +) -> String { + let copy_str = if copy_index == 0 { + Cow::Borrowed("") + } else { + Cow::Owned(format!("_{}", copy_index)) + }; + let name = normalize_pkg_name_for_node_modules_deno_folder(&nv.name); + format!("{}@{}{}", name, nv.version, copy_str) +} + +pub fn get_package_folder_id_from_folder_name( + folder_name: &str, +) -> Option { + let folder_name = folder_name.replace('+', "/"); + let (name, ending) = folder_name.rsplit_once('@')?; + let name: StackString = if let Some(encoded_name) = name.strip_prefix('_') { + StackString::from_string(mixed_case_package_name_decode(encoded_name)?) + } else { + name.into() + }; + let (raw_version, copy_index) = match ending.split_once('_') { + Some((raw_version, copy_index)) => { + let copy_index = copy_index.parse::().ok()?; + (raw_version, copy_index) + } + None => (ending, 0), + }; + let version = deno_semver::Version::parse_from_npm(raw_version).ok()?; + Some(NpmPackageCacheFolderId { + nv: PackageNv { name, version }, + copy_index, + }) +} + /// Normalizes a package name for use at `node_modules/.deno/@[_]` pub fn normalize_pkg_name_for_node_modules_deno_folder(name: &str) -> Cow { let name = if name.to_lowercase() == name { @@ -25,3 +77,36 @@ fn mixed_case_package_name_encode(name: &str) -> String { ) .to_lowercase() } + +#[cfg(test)] +mod test { + use deno_npm::NpmPackageCacheFolderId; + use deno_semver::package::PackageNv; + + use super::*; + + #[test] + fn test_get_package_folder_id_folder_name() { + let cases = vec![ + ( + NpmPackageCacheFolderId { + nv: PackageNv::from_str("@types/foo@1.2.3").unwrap(), + copy_index: 1, + }, + "@types+foo@1.2.3_1".to_string(), + ), + ( + NpmPackageCacheFolderId { + nv: PackageNv::from_str("JSON@3.2.1").unwrap(), + copy_index: 0, + }, + "_jjju6tq@3.2.1".to_string(), + ), + ]; + for (input, output) in cases { + assert_eq!(get_package_folder_id_folder_name(&input), output); + let folder_id = get_package_folder_id_from_folder_name(&output).unwrap(); + assert_eq!(folder_id, input); + } + } +} diff --git a/resolvers/deno/npm/managed/common.rs b/resolvers/deno/npm/managed/common.rs new file mode 100644 index 0000000000..a92fe226dd --- /dev/null +++ b/resolvers/deno/npm/managed/common.rs @@ -0,0 +1,74 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::path::Path; +use std::path::PathBuf; + +use deno_npm::NpmPackageCacheFolderId; +use deno_npm::NpmPackageId; +use node_resolver::NpmPackageFolderResolver; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use url::Url; + +#[derive(Debug)] +pub enum NpmPackageFsResolver { + Local(super::local::LocalNpmPackageResolver), + Global(super::global::GlobalNpmPackageResolver), +} + +impl NpmPackageFsResolver { + /// The local node_modules folder (only for the local resolver). + pub fn node_modules_path(&self) -> Option<&Path> { + match self { + NpmPackageFsResolver::Local(resolver) => resolver.node_modules_path(), + NpmPackageFsResolver::Global(_) => None, + } + } + + pub fn maybe_package_folder( + &self, + package_id: &NpmPackageId, + ) -> Option { + match self { + NpmPackageFsResolver::Local(resolver) => { + resolver.maybe_package_folder(package_id) + } + NpmPackageFsResolver::Global(resolver) => { + resolver.maybe_package_folder(package_id) + } + } + } + + pub fn resolve_package_cache_folder_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, std::io::Error> { + match self { + NpmPackageFsResolver::Local(resolver) => { + resolver.resolve_package_cache_folder_id_from_specifier(specifier) + } + NpmPackageFsResolver::Global(resolver) => { + resolver.resolve_package_cache_folder_id_from_specifier(specifier) + } + } + } +} + +impl NpmPackageFolderResolver + for NpmPackageFsResolver +{ + fn resolve_package_folder_from_package( + &self, + specifier: &str, + referrer: &Url, + ) -> Result { + match self { + NpmPackageFsResolver::Local(r) => { + r.resolve_package_folder_from_package(specifier, referrer) + } + NpmPackageFsResolver::Global(r) => { + r.resolve_package_folder_from_package(specifier, referrer) + } + } + } +} diff --git a/resolvers/deno/npm/managed/global.rs b/resolvers/deno/npm/managed/global.rs new file mode 100644 index 0000000000..271851c79c --- /dev/null +++ b/resolvers/deno/npm/managed/global.rs @@ -0,0 +1,141 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +//! Code for global npm cache resolution. + +use std::path::PathBuf; + +use deno_npm::NpmPackageCacheFolderId; +use deno_npm::NpmPackageId; +use deno_semver::package::PackageNv; +use deno_semver::StackString; +use deno_semver::Version; +use node_resolver::errors::PackageFolderResolveError; +use node_resolver::errors::PackageNotFoundError; +use node_resolver::errors::ReferrerNotFoundError; +use node_resolver::NpmPackageFolderResolver; +use url::Url; + +use super::resolution::NpmResolutionCellRc; +use super::NpmCacheDirRc; +use crate::ResolvedNpmRcRc; + +/// Resolves packages from the global npm cache. +#[derive(Debug)] +pub struct GlobalNpmPackageResolver { + cache: NpmCacheDirRc, + npm_rc: ResolvedNpmRcRc, + resolution: NpmResolutionCellRc, +} + +impl GlobalNpmPackageResolver { + pub fn new( + cache: NpmCacheDirRc, + npm_rc: ResolvedNpmRcRc, + resolution: NpmResolutionCellRc, + ) -> Self { + Self { + cache, + npm_rc, + resolution, + } + } + + pub fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { + let folder_copy_index = self + .resolution + .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; + let registry_url = self.npm_rc.get_registry_url(&id.nv.name); + Some(self.cache.package_folder_for_id( + &id.nv.name, + &id.nv.version.to_string(), + folder_copy_index, + registry_url, + )) + } + + pub fn resolve_package_cache_folder_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, std::io::Error> { + Ok(self.resolve_package_cache_folder_id_from_specifier_inner(specifier)) + } + + fn resolve_package_cache_folder_id_from_specifier_inner( + &self, + specifier: &Url, + ) -> Option { + self + .cache + .resolve_package_folder_id_from_specifier(specifier) + .and_then(|cache_id| { + Some(NpmPackageCacheFolderId { + nv: PackageNv { + name: StackString::from_string(cache_id.name), + version: Version::parse_from_npm(&cache_id.version).ok()?, + }, + copy_index: cache_id.copy_index, + }) + }) + } +} + +impl NpmPackageFolderResolver for GlobalNpmPackageResolver { + fn resolve_package_folder_from_package( + &self, + name: &str, + referrer: &Url, + ) -> Result { + use deno_npm::resolution::PackageNotFoundFromReferrerError; + let Some(referrer_cache_folder_id) = + self.resolve_package_cache_folder_id_from_specifier_inner(referrer) + else { + return Err( + ReferrerNotFoundError { + referrer: referrer.clone(), + referrer_extra: None, + } + .into(), + ); + }; + let resolve_result = self + .resolution + .resolve_package_from_package(name, &referrer_cache_folder_id); + match resolve_result { + Ok(pkg) => match self.maybe_package_folder(&pkg.id) { + Some(folder) => Ok(folder), + None => Err( + PackageNotFoundError { + package_name: name.to_string(), + referrer: referrer.clone(), + referrer_extra: Some(format!( + "{} -> {}", + referrer_cache_folder_id, + pkg.id.as_serialized() + )), + } + .into(), + ), + }, + Err(err) => match *err { + PackageNotFoundFromReferrerError::Referrer(cache_folder_id) => Err( + ReferrerNotFoundError { + referrer: referrer.clone(), + referrer_extra: Some(cache_folder_id.to_string()), + } + .into(), + ), + PackageNotFoundFromReferrerError::Package { + name, + referrer: cache_folder_id_referrer, + } => Err( + PackageNotFoundError { + package_name: name, + referrer: referrer.clone(), + referrer_extra: Some(cache_folder_id_referrer.to_string()), + } + .into(), + ), + }, + } + } +} diff --git a/resolvers/deno/npm/managed/local.rs b/resolvers/deno/npm/managed/local.rs new file mode 100644 index 0000000000..e453101df4 --- /dev/null +++ b/resolvers/deno/npm/managed/local.rs @@ -0,0 +1,214 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +//! Code for local node_modules resolution. + +use std::borrow::Cow; +use std::path::Path; +use std::path::PathBuf; + +use deno_npm::NpmPackageCacheFolderId; +use deno_npm::NpmPackageId; +use deno_path_util::fs::canonicalize_path_maybe_not_exists; +use deno_path_util::url_from_directory_path; +use node_resolver::errors::PackageFolderResolveError; +use node_resolver::errors::PackageFolderResolveIoError; +use node_resolver::errors::PackageNotFoundError; +use node_resolver::errors::ReferrerNotFoundError; +use node_resolver::NpmPackageFolderResolver; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use url::Url; + +use super::resolution::NpmResolutionCellRc; +use crate::npm::local::get_package_folder_id_folder_name_from_parts; +use crate::npm::local::get_package_folder_id_from_folder_name; + +/// Resolver that creates a local node_modules directory +/// and resolves packages from it. +#[derive(Debug)] +pub struct LocalNpmPackageResolver { + resolution: NpmResolutionCellRc, + sys: TSys, + root_node_modules_path: PathBuf, + root_node_modules_url: Url, +} + +impl LocalNpmPackageResolver { + #[allow(clippy::too_many_arguments)] + pub fn new( + resolution: NpmResolutionCellRc, + sys: TSys, + node_modules_folder: PathBuf, + ) -> Self { + Self { + resolution, + sys, + root_node_modules_url: url_from_directory_path(&node_modules_folder) + .unwrap(), + root_node_modules_path: node_modules_folder, + } + } + + pub fn node_modules_path(&self) -> Option<&Path> { + Some(self.root_node_modules_path.as_ref()) + } + + pub fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { + let folder_copy_index = self + .resolution + .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; + // package is stored at: + // node_modules/.deno//node_modules/ + Some( + self + .root_node_modules_path + .join(".deno") + .join(get_package_folder_id_folder_name_from_parts( + &id.nv, + folder_copy_index, + )) + .join("node_modules") + .join(&id.nv.name), + ) + } + + pub fn resolve_package_cache_folder_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, std::io::Error> { + let Some(folder_path) = + self.resolve_package_folder_from_specifier(specifier)? + else { + return Ok(None); + }; + // ex. project/node_modules/.deno/preact@10.24.3/node_modules/preact/ + let Some(node_modules_ancestor) = folder_path + .ancestors() + .find(|ancestor| ancestor.ends_with("node_modules")) + else { + return Ok(None); + }; + let Some(folder_name) = + node_modules_ancestor.parent().and_then(|p| p.file_name()) + else { + return Ok(None); + }; + Ok(get_package_folder_id_from_folder_name( + &folder_name.to_string_lossy(), + )) + } + + fn resolve_package_root(&self, path: &Path) -> PathBuf { + let mut last_found = path; + loop { + let parent = last_found.parent().unwrap(); + if parent.file_name().unwrap() == "node_modules" { + return last_found.to_path_buf(); + } else { + last_found = parent; + } + } + } + + fn resolve_folder_for_specifier( + &self, + specifier: &Url, + ) -> Result, std::io::Error> { + let Some(relative_url) = + self.root_node_modules_url.make_relative(specifier) + else { + return Ok(None); + }; + if relative_url.starts_with("../") { + return Ok(None); + } + // it's within the directory, so use it + let Some(path) = deno_path_util::url_to_file_path(specifier).ok() else { + return Ok(None); + }; + // Canonicalize the path so it's not pointing to the symlinked directory + // in `node_modules` directory of the referrer. + canonicalize_path_maybe_not_exists(&self.sys, &path).map(Some) + } + + fn resolve_package_folder_from_specifier( + &self, + specifier: &Url, + ) -> Result, std::io::Error> { + let Some(local_path) = self.resolve_folder_for_specifier(specifier)? else { + return Ok(None); + }; + let package_root_path = self.resolve_package_root(&local_path); + Ok(Some(package_root_path)) + } +} + +impl NpmPackageFolderResolver + for LocalNpmPackageResolver +{ + fn resolve_package_folder_from_package( + &self, + name: &str, + referrer: &Url, + ) -> Result { + let maybe_local_path = self + .resolve_folder_for_specifier(referrer) + .map_err(|err| PackageFolderResolveIoError { + package_name: name.to_string(), + referrer: referrer.clone(), + source: err, + })?; + let Some(local_path) = maybe_local_path else { + return Err( + ReferrerNotFoundError { + referrer: referrer.clone(), + referrer_extra: None, + } + .into(), + ); + }; + let package_root_path = self.resolve_package_root(&local_path); + let mut current_folder = package_root_path.as_path(); + while let Some(parent_folder) = current_folder.parent() { + current_folder = parent_folder; + let node_modules_folder = if current_folder.ends_with("node_modules") { + Cow::Borrowed(current_folder) + } else { + Cow::Owned(current_folder.join("node_modules")) + }; + + let sub_dir = join_package_name(&node_modules_folder, name); + if self.sys.fs_is_dir_no_err(&sub_dir) { + return Ok(self.sys.fs_canonicalize(&sub_dir).map_err(|err| { + PackageFolderResolveIoError { + package_name: name.to_string(), + referrer: referrer.clone(), + source: err, + } + })?); + } + + if current_folder == self.root_node_modules_path { + break; + } + } + + Err( + PackageNotFoundError { + package_name: name.to_string(), + referrer: referrer.clone(), + referrer_extra: None, + } + .into(), + ) + } +} + +fn join_package_name(path: &Path, package_name: &str) -> PathBuf { + let mut path = path.to_path_buf(); + // ensure backslashes are used on windows + for part in package_name.split('/') { + path = path.join(part); + } + path +} diff --git a/resolvers/deno/npm/managed/mod.rs b/resolvers/deno/npm/managed/mod.rs new file mode 100644 index 0000000000..291e299bc8 --- /dev/null +++ b/resolvers/deno/npm/managed/mod.rs @@ -0,0 +1,287 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +mod common; +mod global; +mod local; +mod resolution; + +use std::path::Path; +use std::path::PathBuf; + +use deno_npm::resolution::PackageCacheFolderIdNotFoundError; +use deno_npm::resolution::PackageNvNotFoundError; +use deno_npm::resolution::PackageReqNotFoundError; +use deno_npm::NpmPackageCacheFolderId; +use deno_npm::NpmPackageId; +use deno_npm::NpmSystemInfo; +use deno_path_util::fs::canonicalize_path_maybe_not_exists; +use deno_semver::package::PackageNv; +use deno_semver::package::PackageReq; +use node_resolver::InNpmPackageChecker; +use node_resolver::NpmPackageFolderResolver; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use url::Url; + +use self::common::NpmPackageFsResolver; +use self::global::GlobalNpmPackageResolver; +use self::local::LocalNpmPackageResolver; +pub use self::resolution::NpmResolutionCell; +pub use self::resolution::NpmResolutionCellRc; +use crate::NpmCacheDirRc; +use crate::ResolvedNpmRcRc; + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolvePkgFolderFromDenoModuleError { + #[class(inherit)] + #[error(transparent)] + PackageNvNotFound(#[from] PackageNvNotFoundError), + #[class(inherit)] + #[error(transparent)] + ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[error(transparent)] +pub enum ResolvePkgFolderFromPkgIdError { + #[class(inherit)] + #[error(transparent)] + NotFound(#[from] NpmManagedPackageFolderNotFoundError), + #[class(inherit)] + #[error(transparent)] + FailedCanonicalizing(#[from] FailedCanonicalizingError), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +#[error("Package folder not found for '{0}'")] +pub struct NpmManagedPackageFolderNotFoundError(deno_semver::StackString); + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +#[error("Failed canonicalizing '{}'", path.display())] +pub struct FailedCanonicalizingError { + path: PathBuf, + #[source] + source: std::io::Error, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ManagedResolvePkgFolderFromDenoReqError { + #[class(inherit)] + #[error(transparent)] + PackageReqNotFound(#[from] PackageReqNotFoundError), + #[class(inherit)] + #[error(transparent)] + ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolvePkgIdFromSpecifierError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + NotFound(#[from] PackageCacheFolderIdNotFoundError), +} + +pub struct ManagedNpmResolverCreateOptions< + TSys: FsCanonicalize + FsMetadata + Clone, +> { + pub npm_cache_dir: NpmCacheDirRc, + pub sys: TSys, + pub maybe_node_modules_path: Option, + pub npm_system_info: NpmSystemInfo, + pub npmrc: ResolvedNpmRcRc, + pub npm_resolution: NpmResolutionCellRc, +} + +#[allow(clippy::disallowed_types)] +pub type ManagedNpmResolverRc = + crate::sync::MaybeArc>; + +#[derive(Debug)] +pub struct ManagedNpmResolver { + fs_resolver: NpmPackageFsResolver, + npm_cache_dir: NpmCacheDirRc, + resolution: NpmResolutionCellRc, + sys: TSys, +} + +impl ManagedNpmResolver { + pub fn new( + options: ManagedNpmResolverCreateOptions, + ) -> ManagedNpmResolver { + let fs_resolver = match options.maybe_node_modules_path { + Some(node_modules_folder) => { + NpmPackageFsResolver::Local(LocalNpmPackageResolver::new( + options.npm_resolution.clone(), + options.sys.clone(), + node_modules_folder, + )) + } + None => NpmPackageFsResolver::Global(GlobalNpmPackageResolver::new( + options.npm_cache_dir.clone(), + options.npmrc.clone(), + options.npm_resolution.clone(), + )), + }; + + ManagedNpmResolver { + fs_resolver, + npm_cache_dir: options.npm_cache_dir, + sys: options.sys, + resolution: options.npm_resolution, + } + } + + #[inline] + pub fn root_node_modules_path(&self) -> Option<&Path> { + self.fs_resolver.node_modules_path() + } + + pub fn global_cache_root_path(&self) -> &Path { + self.npm_cache_dir.root_dir() + } + + pub fn global_cache_root_url(&self) -> &Url { + self.npm_cache_dir.root_dir_url() + } + + pub fn resolution(&self) -> &NpmResolutionCell { + self.resolution.as_ref() + } + + /// Checks if the provided package req's folder is cached. + pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool { + self + .resolution + .resolve_pkg_id_from_pkg_req(req) + .ok() + .and_then(|id| self.resolve_pkg_folder_from_pkg_id(&id).ok()) + .map(|folder| self.sys.fs_exists_no_err(folder)) + .unwrap_or(false) + } + + pub fn resolve_pkg_folder_from_pkg_id( + &self, + package_id: &NpmPackageId, + ) -> Result { + let path = self + .fs_resolver + .maybe_package_folder(package_id) + .ok_or_else(|| { + NpmManagedPackageFolderNotFoundError(package_id.as_serialized()) + })?; + // todo(dsherret): investigate if this canonicalization is always + // necessary. For example, maybe it's not necessary for the global cache + let path = canonicalize_path_maybe_not_exists(&self.sys, &path).map_err( + |source| FailedCanonicalizingError { + path: path.to_path_buf(), + source, + }, + )?; + log::debug!( + "Resolved package folder of {} to {}", + package_id.as_serialized(), + path.display() + ); + Ok(path) + } + + pub fn resolve_pkg_folder_from_deno_module( + &self, + nv: &PackageNv, + ) -> Result { + let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?; + Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?) + } + + pub fn resolve_pkg_folder_from_deno_module_req( + &self, + req: &PackageReq, + _referrer: &Url, + ) -> Result { + let pkg_id = self.resolution.resolve_pkg_id_from_pkg_req(req)?; + Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?) + } + + #[inline] + pub fn resolve_package_cache_folder_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, std::io::Error> { + self + .fs_resolver + .resolve_package_cache_folder_id_from_specifier(specifier) + } + + /// Resolves the package id from the provided specifier. + pub fn resolve_pkg_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, ResolvePkgIdFromSpecifierError> { + let Some(cache_folder_id) = self + .fs_resolver + .resolve_package_cache_folder_id_from_specifier(specifier)? + else { + return Ok(None); + }; + Ok(Some( + self + .resolution + .resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?, + )) + } +} + +impl NpmPackageFolderResolver + for ManagedNpmResolver +{ + fn resolve_package_folder_from_package( + &self, + specifier: &str, + referrer: &Url, + ) -> Result { + let path = self + .fs_resolver + .resolve_package_folder_from_package(specifier, referrer)?; + log::debug!( + "Resolved {} from {} to {}", + specifier, + referrer, + path.display() + ); + Ok(path) + } +} + +#[derive(Debug, Clone)] +pub struct ManagedInNpmPackageChecker { + root_dir: Url, +} + +impl InNpmPackageChecker for ManagedInNpmPackageChecker { + fn in_npm_package(&self, specifier: &Url) -> bool { + specifier.as_ref().starts_with(self.root_dir.as_str()) + } +} + +pub struct ManagedInNpmPkgCheckerCreateOptions<'a> { + pub root_cache_dir_url: &'a Url, + pub maybe_node_modules_path: Option<&'a Path>, +} + +pub fn create_managed_in_npm_pkg_checker( + options: ManagedInNpmPkgCheckerCreateOptions, +) -> ManagedInNpmPackageChecker { + let root_dir = match options.maybe_node_modules_path { + Some(node_modules_folder) => { + deno_path_util::url_from_directory_path(node_modules_folder).unwrap() + } + None => options.root_cache_dir_url.clone(), + }; + debug_assert!(root_dir.as_str().ends_with('/')); + ManagedInNpmPackageChecker { root_dir } +} diff --git a/resolvers/deno/npm/managed/resolution.rs b/resolvers/deno/npm/managed/resolution.rs new file mode 100644 index 0000000000..faa0d43664 --- /dev/null +++ b/resolvers/deno/npm/managed/resolution.rs @@ -0,0 +1,188 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_npm::resolution::NpmPackagesPartitioned; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::resolution::PackageCacheFolderIdNotFoundError; +use deno_npm::resolution::PackageNotFoundFromReferrerError; +use deno_npm::resolution::PackageNvNotFoundError; +use deno_npm::resolution::PackageReqNotFoundError; +use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; +use deno_npm::NpmPackageCacheFolderId; +use deno_npm::NpmPackageId; +use deno_npm::NpmResolutionPackage; +use deno_npm::NpmSystemInfo; +use deno_semver::package::PackageNv; +use deno_semver::package::PackageReq; +use parking_lot::RwLock; + +#[allow(clippy::disallowed_types)] +pub type NpmResolutionCellRc = crate::sync::MaybeArc; + +/// Handles updating and storing npm resolution in memory. +/// +/// This does not interact with the file system. +#[derive(Default)] +pub struct NpmResolutionCell { + snapshot: RwLock, +} + +impl std::fmt::Debug for NpmResolutionCell { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let snapshot = self.snapshot.read(); + f.debug_struct("NpmResolution") + .field("snapshot", &snapshot.as_valid_serialized().as_serialized()) + .finish() + } +} + +impl NpmResolutionCell { + pub fn from_serialized( + initial_snapshot: Option, + ) -> Self { + let snapshot = + NpmResolutionSnapshot::new(initial_snapshot.unwrap_or_default()); + Self::new(snapshot) + } + + pub fn new(initial_snapshot: NpmResolutionSnapshot) -> Self { + Self { + snapshot: RwLock::new(initial_snapshot), + } + } + + pub fn resolve_pkg_cache_folder_copy_index_from_pkg_id( + &self, + id: &NpmPackageId, + ) -> Option { + self + .snapshot + .read() + .package_from_id(id) + .map(|p| p.copy_index) + } + + pub fn resolve_pkg_id_from_pkg_cache_folder_id( + &self, + id: &NpmPackageCacheFolderId, + ) -> Result { + self + .snapshot + .read() + .resolve_pkg_from_pkg_cache_folder_id(id) + .map(|pkg| pkg.id.clone()) + } + + pub fn resolve_package_from_package( + &self, + name: &str, + referrer: &NpmPackageCacheFolderId, + ) -> Result> { + self + .snapshot + .read() + .resolve_package_from_package(name, referrer) + .cloned() + } + + /// Resolve a node package from a deno module. + pub fn resolve_pkg_id_from_pkg_req( + &self, + req: &PackageReq, + ) -> Result { + self + .snapshot + .read() + .resolve_pkg_from_pkg_req(req) + .map(|pkg| pkg.id.clone()) + } + + pub fn resolve_pkg_reqs_from_pkg_id( + &self, + id: &NpmPackageId, + ) -> Vec { + let snapshot = self.snapshot.read(); + let mut pkg_reqs = snapshot + .package_reqs() + .iter() + .filter(|(_, nv)| *nv == &id.nv) + .map(|(req, _)| req.clone()) + .collect::>(); + pkg_reqs.sort(); // be deterministic + pkg_reqs + } + + pub fn resolve_pkg_id_from_deno_module( + &self, + id: &PackageNv, + ) -> Result { + self + .snapshot + .read() + .resolve_package_from_deno_module(id) + .map(|pkg| pkg.id.clone()) + } + + pub fn package_reqs(&self) -> Vec<(PackageReq, PackageNv)> { + self + .snapshot + .read() + .package_reqs() + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect() + } + + pub fn top_level_packages(&self) -> Vec { + self + .snapshot + .read() + .top_level_packages() + .cloned() + .collect::>() + } + + pub fn all_system_packages( + &self, + system_info: &NpmSystemInfo, + ) -> Vec { + self.snapshot.read().all_system_packages(system_info) + } + + pub fn all_system_packages_partitioned( + &self, + system_info: &NpmSystemInfo, + ) -> NpmPackagesPartitioned { + self + .snapshot + .read() + .all_system_packages_partitioned(system_info) + } + + pub fn snapshot(&self) -> NpmResolutionSnapshot { + self.snapshot.read().clone() + } + + pub fn serialized_valid_snapshot( + &self, + ) -> ValidSerializedNpmResolutionSnapshot { + self.snapshot.read().as_valid_serialized() + } + + pub fn serialized_valid_snapshot_for_system( + &self, + system_info: &NpmSystemInfo, + ) -> ValidSerializedNpmResolutionSnapshot { + self + .snapshot + .read() + .as_valid_serialized_for_system(system_info) + } + + pub fn subset(&self, package_reqs: &[PackageReq]) -> NpmResolutionSnapshot { + self.snapshot.read().subset(package_reqs) + } + + pub fn set_snapshot(&self, snapshot: NpmResolutionSnapshot) { + *self.snapshot.write() = snapshot; + } +} diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 83db04480a..fed3d388bf 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -1,13 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Debug; +use std::path::Path; use std::path::PathBuf; -use std::sync::Arc; use boxed_error::Boxed; +use deno_error::JsError; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; -use node_resolver::env::NodeResolverEnv; use node_resolver::errors::NodeResolveError; use node_resolver::errors::NodeResolveErrorKind; use node_resolver::errors::PackageFolderResolveErrorKind; @@ -16,115 +16,311 @@ use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::PackageResolveErrorKind; use node_resolver::errors::PackageSubpathResolveError; use node_resolver::InNpmPackageChecker; +use node_resolver::IsBuiltInNodeModuleChecker; use node_resolver::NodeResolution; use node_resolver::NodeResolutionKind; -use node_resolver::NodeResolver; +use node_resolver::NodeResolverRc; +use node_resolver::NpmPackageFolderResolver; use node_resolver::ResolutionMode; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsRead; +use sys_traits::FsReadDir; use thiserror::Error; use url::Url; -use crate::fs::DenoResolverFs; - -pub use byonm::ByonmInNpmPackageChecker; -pub use byonm::ByonmNpmResolver; -pub use byonm::ByonmNpmResolverCreateOptions; -pub use byonm::ByonmResolvePkgFolderFromDenoReqError; -pub use local::normalize_pkg_name_for_node_modules_deno_folder; +pub use self::byonm::ByonmInNpmPackageChecker; +pub use self::byonm::ByonmNpmResolver; +pub use self::byonm::ByonmNpmResolverCreateOptions; +pub use self::byonm::ByonmNpmResolverRc; +pub use self::byonm::ByonmResolvePkgFolderFromDenoReqError; +pub use self::local::get_package_folder_id_folder_name; +pub use self::local::normalize_pkg_name_for_node_modules_deno_folder; +use self::managed::create_managed_in_npm_pkg_checker; +use self::managed::ManagedInNpmPackageChecker; +use self::managed::ManagedInNpmPkgCheckerCreateOptions; +pub use self::managed::ManagedNpmResolver; +use self::managed::ManagedNpmResolverCreateOptions; +pub use self::managed::ManagedNpmResolverRc; +use crate::sync::new_rc; +use crate::sync::MaybeSend; +use crate::sync::MaybeSync; mod byonm; mod local; +pub mod managed; -#[derive(Debug, Error)] +pub enum CreateInNpmPkgCheckerOptions<'a> { + Managed(ManagedInNpmPkgCheckerCreateOptions<'a>), + Byonm, +} + +#[derive(Debug, Clone)] +pub enum DenoInNpmPackageChecker { + Managed(ManagedInNpmPackageChecker), + Byonm(ByonmInNpmPackageChecker), +} + +impl DenoInNpmPackageChecker { + pub fn new(options: CreateInNpmPkgCheckerOptions) -> Self { + match options { + CreateInNpmPkgCheckerOptions::Managed(options) => { + DenoInNpmPackageChecker::Managed(create_managed_in_npm_pkg_checker( + options, + )) + } + CreateInNpmPkgCheckerOptions::Byonm => { + DenoInNpmPackageChecker::Byonm(ByonmInNpmPackageChecker) + } + } + } +} + +impl InNpmPackageChecker for DenoInNpmPackageChecker { + fn in_npm_package(&self, specifier: &Url) -> bool { + match self { + DenoInNpmPackageChecker::Managed(c) => c.in_npm_package(specifier), + DenoInNpmPackageChecker::Byonm(c) => c.in_npm_package(specifier), + } + } +} + +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error("Could not resolve \"{}\", 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`?", specifier)] pub struct NodeModulesOutOfDateError { pub specifier: String, } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error("Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", package_json_path.display())] pub struct MissingPackageNodeModulesFolderError { pub package_json_path: PathBuf, } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct ResolveIfForNpmPackageError( pub Box, ); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolveIfForNpmPackageErrorKind { + #[class(inherit)] #[error(transparent)] NodeResolve(#[from] NodeResolveError), + #[class(inherit)] #[error(transparent)] NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError), } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct ResolveReqWithSubPathError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolveReqWithSubPathErrorKind { + #[class(inherit)] #[error(transparent)] MissingPackageNodeModulesFolder(#[from] MissingPackageNodeModulesFolderError), + #[class(inherit)] #[error(transparent)] ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError), + #[class(inherit)] #[error(transparent)] PackageSubpathResolve(#[from] PackageSubpathResolveError), } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolvePkgFolderFromDenoReqError { - // todo(dsherret): don't use anyhow here + #[class(inherit)] #[error(transparent)] - Managed(anyhow::Error), + Managed(managed::ManagedResolvePkgFolderFromDenoReqError), + #[class(inherit)] #[error(transparent)] - Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError), + Byonm(byonm::ByonmResolvePkgFolderFromDenoReqError), } -// todo(dsherret): a temporary trait until we extract -// out the CLI npm resolver into here -pub trait CliNpmReqResolver: Debug + Send + Sync { - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - referrer: &Url, - ) -> Result; -} - -pub struct NpmReqResolverOptions< - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, +pub enum NpmResolverCreateOptions< + TSys: FsRead + + FsCanonicalize + + FsMetadata + + std::fmt::Debug + + MaybeSend + + MaybeSync + + Clone + + 'static, > { + Managed(ManagedNpmResolverCreateOptions), + Byonm(ByonmNpmResolverCreateOptions), +} + +#[derive(Debug, Clone)] +pub enum NpmResolver { /// The resolver when "bring your own node_modules" is enabled where Deno /// does not setup the node_modules directories automatically, but instead /// uses what already exists on the file system. - pub byonm_resolver: Option>>, - pub fs: Fs, - pub in_npm_pkg_checker: Arc, - pub node_resolver: Arc>, - pub npm_req_resolver: Arc, + Byonm(ByonmNpmResolverRc), + Managed(ManagedNpmResolverRc), } +impl NpmResolver { + pub fn new< + TCreateSys: FsCanonicalize + + FsMetadata + + FsRead + + FsReadDir + + std::fmt::Debug + + MaybeSend + + MaybeSync + + Clone + + 'static, + >( + options: NpmResolverCreateOptions, + ) -> NpmResolver { + match options { + NpmResolverCreateOptions::Managed(options) => { + NpmResolver::Managed(new_rc(ManagedNpmResolver::::new::< + TCreateSys, + >(options))) + } + NpmResolverCreateOptions::Byonm(options) => { + NpmResolver::Byonm(new_rc(ByonmNpmResolver::new(options))) + } + } + } + + pub fn is_byonm(&self) -> bool { + matches!(self, NpmResolver::Byonm(_)) + } + + pub fn is_managed(&self) -> bool { + matches!(self, NpmResolver::Managed(_)) + } + + pub fn as_managed(&self) -> Option<&ManagedNpmResolver> { + match self { + NpmResolver::Managed(resolver) => Some(resolver), + NpmResolver::Byonm(_) => None, + } + } + + pub fn root_node_modules_path(&self) -> Option<&Path> { + match self { + NpmResolver::Byonm(resolver) => resolver.root_node_modules_path(), + NpmResolver::Managed(resolver) => resolver.root_node_modules_path(), + } + } + + pub fn resolve_pkg_folder_from_deno_module_req( + &self, + req: &PackageReq, + referrer: &Url, + ) -> Result { + match self { + NpmResolver::Byonm(byonm_resolver) => byonm_resolver + .resolve_pkg_folder_from_deno_module_req(req, referrer) + .map_err(ResolvePkgFolderFromDenoReqError::Byonm), + NpmResolver::Managed(managed_resolver) => managed_resolver + .resolve_pkg_folder_from_deno_module_req(req, referrer) + .map_err(ResolvePkgFolderFromDenoReqError::Managed), + } + } +} + +impl + NpmPackageFolderResolver for NpmResolver +{ + fn resolve_package_folder_from_package( + &self, + specifier: &str, + referrer: &Url, + ) -> Result { + match self { + NpmResolver::Byonm(byonm_resolver) => { + byonm_resolver.resolve_package_folder_from_package(specifier, referrer) + } + NpmResolver::Managed(managed_resolver) => managed_resolver + .resolve_package_folder_from_package(specifier, referrer), + } + } +} + +pub struct NpmReqResolverOptions< + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, +> { + pub in_npm_pkg_checker: TInNpmPackageChecker, + pub node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + pub npm_resolver: NpmResolver, + pub sys: TSys, +} + +#[allow(clippy::disallowed_types)] +pub type NpmReqResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, +> = crate::sync::MaybeArc< + NpmReqResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, +>; + #[derive(Debug)] -pub struct NpmReqResolver -{ - byonm_resolver: Option>>, - fs: Fs, - in_npm_pkg_checker: Arc, - node_resolver: Arc>, - npm_resolver: Arc, +pub struct NpmReqResolver< + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, +> { + sys: TSys, + in_npm_pkg_checker: TInNpmPackageChecker, + node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + npm_resolver: NpmResolver, } -impl - NpmReqResolver +impl< + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, + > + NpmReqResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + > { - pub fn new(options: NpmReqResolverOptions) -> Self { + pub fn new( + options: NpmReqResolverOptions< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + ) -> Self { Self { - byonm_resolver: options.byonm_resolver, - fs: options.fs, + sys: options.sys, in_npm_pkg_checker: options.in_npm_pkg_checker, node_resolver: options.node_resolver, - npm_resolver: options.npm_req_resolver, + npm_resolver: options.npm_resolver, } } @@ -166,9 +362,9 @@ impl match resolution_result { Ok(url) => Ok(url), Err(err) => { - if self.byonm_resolver.is_some() { + if matches!(self.npm_resolver, NpmResolver::Byonm(_)) { let package_json_path = package_folder.join("package.json"); - if !self.fs.exists_sync(&package_json_path) { + if !self.sys.fs_exists_no_err(&package_json_path) { return Err( MissingPackageNodeModulesFolderError { package_json_path }.into(), ); @@ -234,7 +430,9 @@ impl .into_box(), ); } - if let Some(byonm_npm_resolver) = &self.byonm_resolver { + if let NpmResolver::Byonm(byonm_npm_resolver) = + &self.npm_resolver + { if byonm_npm_resolver .find_ancestor_package_json_with_dep( package_name, diff --git a/resolvers/deno/sloppy_imports.rs b/resolvers/deno/sloppy_imports.rs index ccaa547435..486d2dab1e 100644 --- a/resolvers/deno/sloppy_imports.rs +++ b/resolvers/deno/sloppy_imports.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; @@ -7,8 +7,12 @@ use std::path::PathBuf; use deno_media_type::MediaType; use deno_path_util::url_from_file_path; use deno_path_util::url_to_file_path; +use sys_traits::FsMetadata; +use sys_traits::FsMetadataValue; use url::Url; +use crate::sync::MaybeDashMap; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SloppyImportsFsEntry { File, @@ -101,6 +105,10 @@ pub trait SloppyImportResolverFs { } } +#[allow(clippy::disallowed_types)] +pub type SloppyImportsResolverRc = + crate::sync::MaybeArc>; + #[derive(Debug)] pub struct SloppyImportsResolver { fs: Fs, @@ -364,6 +372,50 @@ impl SloppyImportsResolver { } } +#[derive(Debug)] +pub struct SloppyImportsCachedFs { + sys: TSys, + cache: Option>>, +} + +impl SloppyImportsCachedFs { + pub fn new(sys: TSys) -> Self { + Self { + sys, + cache: Some(Default::default()), + } + } + + pub fn new_without_stat_cache(sys: TSys) -> Self { + Self { sys, cache: None } + } +} + +impl SloppyImportResolverFs for SloppyImportsCachedFs { + fn stat_sync(&self, path: &Path) -> Option { + if let Some(cache) = &self.cache { + if let Some(entry) = cache.get(path) { + return *entry; + } + } + + let entry = self.sys.fs_metadata(path).ok().and_then(|stat| { + if stat.file_type().is_file() { + Some(SloppyImportsFsEntry::File) + } else if stat.file_type().is_dir() { + Some(SloppyImportsFsEntry::Dir) + } else { + None + } + }); + + if let Some(cache) = &self.cache { + cache.insert(path.to_owned(), entry); + } + entry + } +} + #[cfg(test)] mod test { use test_util::TestContext; diff --git a/resolvers/deno/sync.rs b/resolvers/deno/sync.rs new file mode 100644 index 0000000000..af3e290bb2 --- /dev/null +++ b/resolvers/deno/sync.rs @@ -0,0 +1,67 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +pub use inner::*; + +#[cfg(feature = "sync")] +mod inner { + #![allow(clippy::disallowed_types)] + + pub use core::marker::Send as MaybeSend; + pub use core::marker::Sync as MaybeSync; + pub use std::sync::Arc as MaybeArc; + + pub use dashmap::DashMap as MaybeDashMap; +} + +#[cfg(not(feature = "sync"))] +mod inner { + use std::cell::Ref; + use std::cell::RefCell; + use std::collections::HashMap; + use std::hash::BuildHasher; + use std::hash::Hash; + use std::hash::RandomState; + pub use std::rc::Rc as MaybeArc; + + pub trait MaybeSync {} + impl MaybeSync for T where T: ?Sized {} + pub trait MaybeSend {} + impl MaybeSend for T where T: ?Sized {} + + // Wrapper struct that exposes a subset of `DashMap` API. + #[derive(Debug)] + pub struct MaybeDashMap(RefCell>); + + impl Default for MaybeDashMap + where + K: Eq + Hash, + S: Default + BuildHasher + Clone, + { + fn default() -> Self { + Self(RefCell::new(Default::default())) + } + } + + impl MaybeDashMap { + pub fn get<'a, Q: Eq + Hash + ?Sized>( + &'a self, + key: &Q, + ) -> Option> + where + K: std::borrow::Borrow, + { + Ref::filter_map(self.0.borrow(), |map| map.get(key)).ok() + } + + pub fn insert(&self, key: K, value: V) -> Option { + let mut inner = self.0.borrow_mut(); + inner.insert(key, value) + } + } +} + +#[allow(clippy::disallowed_types)] +#[inline] +pub fn new_rc(value: T) -> MaybeArc { + MaybeArc::new(value) +} diff --git a/resolvers/node/Cargo.toml b/resolvers/node/Cargo.toml index e175bcfafa..1c2a342a9f 100644 --- a/resolvers/node/Cargo.toml +++ b/resolvers/node/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "node_resolver" -version = "0.21.0" +version = "0.23.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -20,6 +20,7 @@ sync = ["deno_package_json/sync"] anyhow.workspace = true async-trait.workspace = true boxed_error.workspace = true +deno_error.workspace = true deno_media_type.workspace = true deno_package_json.workspace = true deno_path_util.workspace = true @@ -29,6 +30,7 @@ once_cell.workspace = true path-clean = "=0.1.0" regex.workspace = true serde_json.workspace = true +sys_traits.workspace = true thiserror.workspace = true tokio.workspace = true url.workspace = true diff --git a/resolvers/node/analyze.rs b/resolvers/node/analyze.rs index a444f4d923..e144e2b8fb 100644 --- a/resolvers/node/analyze.rs +++ b/resolvers/node/analyze.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::BTreeSet; @@ -6,6 +6,8 @@ use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; +use anyhow::Context; +use anyhow::Error as AnyError; use deno_path_util::url_from_file_path; use deno_path_util::url_to_file_path; use futures::future::LocalBoxFuture; @@ -13,16 +15,16 @@ use futures::stream::FuturesUnordered; use futures::FutureExt; use futures::StreamExt; use once_cell::sync::Lazy; - -use anyhow::Context; -use anyhow::Error as AnyError; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsRead; use url::Url; -use crate::env::NodeResolverEnv; -use crate::npm::InNpmPackageCheckerRc; use crate::resolution::NodeResolverRc; +use crate::InNpmPackageChecker; +use crate::IsBuiltInNodeModuleChecker; use crate::NodeResolutionKind; -use crate::NpmPackageFolderResolverRc; +use crate::NpmPackageFolderResolver; use crate::PackageJsonResolverRc; use crate::PathClean; use crate::ResolutionMode; @@ -60,34 +62,59 @@ pub trait CjsCodeAnalyzer { pub struct NodeCodeTranslator< TCjsCodeAnalyzer: CjsCodeAnalyzer, - TNodeResolverEnv: NodeResolverEnv, + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: FsCanonicalize + FsMetadata + FsRead, > { cjs_code_analyzer: TCjsCodeAnalyzer, - env: TNodeResolverEnv, - in_npm_pkg_checker: InNpmPackageCheckerRc, - node_resolver: NodeResolverRc, - npm_resolver: NpmPackageFolderResolverRc, - pkg_json_resolver: PackageJsonResolverRc, + in_npm_pkg_checker: TInNpmPackageChecker, + node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + npm_resolver: TNpmPackageFolderResolver, + pkg_json_resolver: PackageJsonResolverRc, + sys: TSys, } -impl - NodeCodeTranslator +impl< + TCjsCodeAnalyzer: CjsCodeAnalyzer, + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: FsCanonicalize + FsMetadata + FsRead, + > + NodeCodeTranslator< + TCjsCodeAnalyzer, + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + > { pub fn new( cjs_code_analyzer: TCjsCodeAnalyzer, - env: TNodeResolverEnv, - in_npm_pkg_checker: InNpmPackageCheckerRc, - node_resolver: NodeResolverRc, - npm_resolver: NpmPackageFolderResolverRc, - pkg_json_resolver: PackageJsonResolverRc, + in_npm_pkg_checker: TInNpmPackageChecker, + node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + npm_resolver: TNpmPackageFolderResolver, + pkg_json_resolver: PackageJsonResolverRc, + sys: TSys, ) -> Self { Self { cjs_code_analyzer, - env, in_npm_pkg_checker, node_resolver, npm_resolver, pkg_json_resolver, + sys, } } @@ -162,7 +189,7 @@ impl add_export( &mut source, export, - &format!("mod[\"{}\"]", escape_for_double_quote_string(export)), + &format!("mod[{}]", to_double_quote_string(export)), &mut temp_var_count, ); } @@ -366,7 +393,7 @@ impl // old school if package_subpath != "." { let d = module_dir.join(package_subpath); - if self.env.is_dir_sync(&d) { + if self.sys.fs_is_dir_no_err(&d) { // subdir might have a package.json that specifies the entrypoint let package_json_path = d.join("package.json"); let maybe_package_json = self @@ -423,13 +450,13 @@ impl referrer: &Path, ) -> Result { let p = p.clean(); - if self.env.exists_sync(&p) { + if self.sys.fs_exists_no_err(&p) { let file_name = p.file_name().unwrap(); let p_js = p.with_file_name(format!("{}.js", file_name.to_str().unwrap())); - if self.env.is_file_sync(&p_js) { + if self.sys.fs_is_file_no_err(&p_js) { return Ok(p_js); - } else if self.env.is_dir_sync(&p) { + } else if self.sys.fs_is_dir_no_err(&p) { return Ok(p.join("index.js")); } else { return Ok(p); @@ -438,14 +465,14 @@ impl { let p_js = p.with_file_name(format!("{}.js", file_name.to_str().unwrap())); - if self.env.is_file_sync(&p_js) { + if self.sys.fs_is_file_no_err(&p_js) { return Ok(p_js); } } { let p_json = p.with_file_name(format!("{}.json", file_name.to_str().unwrap())); - if self.env.is_file_sync(&p_json) { + if self.sys.fs_is_file_no_err(&p_json) { return Ok(p_json); } } @@ -561,8 +588,8 @@ fn add_export( "const __deno_export_{temp_var_count}__ = {initializer};" )); source.push(format!( - "export {{ __deno_export_{temp_var_count}__ as \"{}\" }};", - escape_for_double_quote_string(name) + "export {{ __deno_export_{temp_var_count}__ as {} }};", + to_double_quote_string(name) )); } else { source.push(format!("export const {name} = {initializer};")); @@ -620,14 +647,9 @@ fn not_found(path: &str, referrer: &Path) -> AnyError { std::io::Error::new(std::io::ErrorKind::NotFound, msg).into() } -fn escape_for_double_quote_string(text: &str) -> Cow { - // this should be rare, so doing a scan first before allocating is ok - if text.chars().any(|c| matches!(c, '"' | '\\')) { - // don't bother making this more complex for perf because it's rare - Cow::Owned(text.replace('\\', "\\\\").replace('"', "\\\"")) - } else { - Cow::Borrowed(text) - } +fn to_double_quote_string(text: &str) -> String { + // serde can handle this for us + serde_json::to_string(text).unwrap() } #[cfg(test)] @@ -665,4 +687,13 @@ mod tests { Some(("@some-package/core".to_string(), "./actions".to_string())) ); } + + #[test] + fn test_to_double_quote_string() { + assert_eq!(to_double_quote_string("test"), "\"test\""); + assert_eq!( + to_double_quote_string("\r\n\t\"test"), + "\"\\r\\n\\t\\\"test\"" + ); + } } diff --git a/resolvers/node/env.rs b/resolvers/node/env.rs deleted file mode 100644 index b520ece0f8..0000000000 --- a/resolvers/node/env.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use std::path::Path; -use std::path::PathBuf; - -use crate::sync::MaybeSend; -use crate::sync::MaybeSync; - -pub struct NodeResolverFsStat { - pub is_file: bool, - pub is_dir: bool, - pub is_symlink: bool, -} - -pub trait NodeResolverEnv: std::fmt::Debug + MaybeSend + MaybeSync { - fn is_builtin_node_module(&self, specifier: &str) -> bool; - - fn realpath_sync(&self, path: &Path) -> std::io::Result; - - fn stat_sync(&self, path: &Path) -> std::io::Result; - - fn exists_sync(&self, path: &Path) -> bool; - - fn is_file_sync(&self, path: &Path) -> bool { - self - .stat_sync(path) - .map(|stat| stat.is_file) - .unwrap_or(false) - } - - fn is_dir_sync(&self, path: &Path) -> bool { - self - .stat_sync(path) - .map(|stat| stat.is_dir) - .unwrap_or(false) - } - - fn pkg_json_fs(&self) -> &dyn deno_package_json::fs::DenoPkgJsonFs; -} diff --git a/resolvers/node/errors.rs b/resolvers/node/errors.rs index 600a365a8f..1b4ce460d1 100644 --- a/resolvers/node/errors.rs +++ b/resolvers/node/errors.rs @@ -1,10 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::fmt::Write; use std::path::PathBuf; use boxed_error::Boxed; +use deno_error::JsError; use thiserror::Error; use url::Url; @@ -55,8 +56,7 @@ pub trait NodeJsErrorCoded { fn code(&self) -> NodeJsErrorCode; } -// todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError -#[derive(Debug, Clone, Error)] +#[derive(Debug, Clone, Error, JsError)] #[error( "[{}] Invalid module '{}' {}{}", self.code(), @@ -64,6 +64,7 @@ pub trait NodeJsErrorCoded { reason, maybe_referrer.as_ref().map(|referrer| format!(" imported from '{}'", referrer)).unwrap_or_default() )] +#[class(type)] pub struct InvalidModuleSpecifierError { pub request: String, pub reason: Cow<'static, str>, @@ -76,13 +77,15 @@ impl NodeJsErrorCoded for InvalidModuleSpecifierError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct LegacyResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum LegacyResolveErrorKind { + #[class(inherit)] #[error(transparent)] TypesNotFound(#[from] TypesNotFoundError), + #[class(inherit)] #[error(transparent)] ModuleNotFound(#[from] ModuleNotFoundError), } @@ -96,13 +99,14 @@ impl NodeJsErrorCoded for LegacyResolveError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] #[error( "Could not find package '{}' from referrer '{}'{}.", package_name, referrer, referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default() )] +#[class(generic)] pub struct PackageNotFoundError { pub package_name: String, pub referrer: Url, @@ -116,12 +120,13 @@ impl NodeJsErrorCoded for PackageNotFoundError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] #[error( "Could not find referrer npm package '{}'{}.", referrer, referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default() )] +#[class(generic)] pub struct ReferrerNotFoundError { pub referrer: Url, /// Extra information about the referrer. @@ -134,12 +139,14 @@ impl NodeJsErrorCoded for ReferrerNotFoundError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(inherit)] #[error("Failed resolving '{package_name}' from referrer '{referrer}'.")] pub struct PackageFolderResolveIoError { pub package_name: String, pub referrer: Url, #[source] + #[inherit] pub source: std::io::Error, } @@ -159,15 +166,18 @@ impl NodeJsErrorCoded for PackageFolderResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageFolderResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageFolderResolveErrorKind { + #[class(inherit)] #[error(transparent)] PackageNotFound(#[from] PackageNotFoundError), + #[class(inherit)] #[error(transparent)] ReferrerNotFound(#[from] ReferrerNotFoundError), + #[class(inherit)] #[error(transparent)] Io(#[from] PackageFolderResolveIoError), } @@ -182,20 +192,24 @@ impl NodeJsErrorCoded for PackageSubpathResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageSubpathResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageSubpathResolveErrorKind { + #[class(inherit)] #[error(transparent)] PkgJsonLoad(#[from] PackageJsonLoadError), + #[class(inherit)] #[error(transparent)] Exports(PackageExportsResolveError), + #[class(inherit)] #[error(transparent)] LegacyResolve(LegacyResolveError), } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error( "Target '{}' not found from '{}'{}{}.", target, @@ -241,19 +255,24 @@ impl NodeJsErrorCoded for PackageTargetResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageTargetResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageTargetResolveErrorKind { + #[class(inherit)] #[error(transparent)] NotFound(#[from] PackageTargetNotFoundError), + #[class(inherit)] #[error(transparent)] InvalidPackageTarget(#[from] InvalidPackageTargetError), + #[class(inherit)] #[error(transparent)] InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError), + #[class(inherit)] #[error(transparent)] PackageResolve(#[from] PackageResolveError), + #[class(inherit)] #[error(transparent)] TypesNotFound(#[from] TypesNotFoundError), } @@ -267,24 +286,27 @@ impl NodeJsErrorCoded for PackageExportsResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageExportsResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageExportsResolveErrorKind { + #[class(inherit)] #[error(transparent)] PackagePathNotExported(#[from] PackagePathNotExportedError), + #[class(inherit)] #[error(transparent)] PackageTargetResolve(#[from] PackageTargetResolveError), } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] #[error( "[{}] Could not find types for '{}'{}", self.code(), self.0.code_specifier, self.0.maybe_referrer.as_ref().map(|r| format!(" imported from '{}'", r)).unwrap_or_default(), )] +#[class(generic)] pub struct TypesNotFoundError(pub Box); #[derive(Debug)] @@ -299,7 +321,7 @@ impl NodeJsErrorCoded for TypesNotFoundError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] #[error( "[{}] Invalid package config. {}", self.code(), @@ -320,39 +342,23 @@ impl NodeJsErrorCoded for PackageJsonLoadError { impl NodeJsErrorCoded for ClosestPkgJsonError { fn code(&self) -> NodeJsErrorCode { match self.as_kind() { - ClosestPkgJsonErrorKind::CanonicalizingDir(e) => e.code(), ClosestPkgJsonErrorKind::Load(e) => e.code(), } } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct ClosestPkgJsonError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ClosestPkgJsonErrorKind { - #[error(transparent)] - CanonicalizingDir(#[from] CanonicalizingPkgJsonDirError), + #[class(inherit)] #[error(transparent)] Load(#[from] PackageJsonLoadError), } -#[derive(Debug, Error)] -#[error("[{}] Failed canonicalizing package.json directory '{}'.", self.code(), dir_path.display())] -pub struct CanonicalizingPkgJsonDirError { - pub dir_path: PathBuf, - #[source] - pub source: std::io::Error, -} - -impl NodeJsErrorCoded for CanonicalizingPkgJsonDirError { - fn code(&self) -> NodeJsErrorCode { - NodeJsErrorCode::ERR_MODULE_NOT_FOUND - } -} - -// todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(type)] #[error( "[{}] Package import specifier \"{}\" is not defined{}{}", self.code(), @@ -372,17 +378,21 @@ impl NodeJsErrorCoded for PackageImportNotDefinedError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageImportsResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageImportsResolveErrorKind { + #[class(inherit)] #[error(transparent)] ClosestPkgJson(ClosestPkgJsonError), + #[class(inherit)] #[error(transparent)] InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError), + #[class(inherit)] #[error(transparent)] NotDefined(#[from] PackageImportNotDefinedError), + #[class(inherit)] #[error(transparent)] Target(#[from] PackageTargetResolveError), } @@ -410,24 +420,30 @@ impl NodeJsErrorCoded for PackageResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageResolveErrorKind { + #[class(inherit)] #[error(transparent)] ClosestPkgJson(#[from] ClosestPkgJsonError), + #[class(inherit)] #[error(transparent)] InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError), + #[class(inherit)] #[error(transparent)] PackageFolderResolve(#[from] PackageFolderResolveError), + #[class(inherit)] #[error(transparent)] ExportsResolve(#[from] PackageExportsResolveError), + #[class(inherit)] #[error(transparent)] SubpathResolve(#[from] PackageSubpathResolveError), } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error("Failed joining '{path}' from '{base}'.")] pub struct NodeResolveRelativeJoinError { pub path: String, @@ -436,43 +452,54 @@ pub struct NodeResolveRelativeJoinError { pub source: url::ParseError, } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error("Failed resolving specifier from data url referrer.")] pub struct DataUrlReferrerError { #[source] pub source: url::ParseError, } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct NodeResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum NodeResolveErrorKind { + #[class(inherit)] #[error(transparent)] RelativeJoin(#[from] NodeResolveRelativeJoinError), + #[class(inherit)] #[error(transparent)] PackageImportsResolve(#[from] PackageImportsResolveError), + #[class(inherit)] #[error(transparent)] UnsupportedEsmUrlScheme(#[from] UnsupportedEsmUrlSchemeError), + #[class(inherit)] #[error(transparent)] DataUrlReferrer(#[from] DataUrlReferrerError), + #[class(inherit)] #[error(transparent)] PackageResolve(#[from] PackageResolveError), + #[class(inherit)] #[error(transparent)] TypesNotFound(#[from] TypesNotFoundError), + #[class(inherit)] #[error(transparent)] FinalizeResolution(#[from] FinalizeResolutionError), } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct FinalizeResolutionError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum FinalizeResolutionErrorKind { + #[class(inherit)] #[error(transparent)] InvalidModuleSpecifierError(#[from] InvalidModuleSpecifierError), + #[class(inherit)] #[error(transparent)] ModuleNotFound(#[from] ModuleNotFoundError), + #[class(inherit)] #[error(transparent)] UnsupportedDirImport(#[from] UnsupportedDirImportError), } @@ -487,7 +514,8 @@ impl NodeJsErrorCoded for FinalizeResolutionError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error( "[{}] Cannot find {} '{}'{}", self.code(), @@ -507,7 +535,8 @@ impl NodeJsErrorCoded for ModuleNotFoundError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error( "[{}] Directory import '{}' is not supported resolving ES modules{}", self.code(), @@ -525,7 +554,8 @@ impl NodeJsErrorCoded for UnsupportedDirImportError { } } -#[derive(Debug)] +#[derive(Debug, JsError)] +#[class(generic)] pub struct InvalidPackageTargetError { pub pkg_json_path: PathBuf, pub sub_path: String, @@ -581,7 +611,8 @@ impl NodeJsErrorCoded for InvalidPackageTargetError { } } -#[derive(Debug)] +#[derive(Debug, JsError)] +#[class(generic)] pub struct PackagePathNotExportedError { pub pkg_json_path: PathBuf, pub subpath: String, @@ -631,7 +662,8 @@ impl std::fmt::Display for PackagePathNotExportedError { } } -#[derive(Debug, Clone, Error)] +#[derive(Debug, Clone, Error, JsError)] +#[class(type)] #[error( "[{}] Only file and data URLs are supported by the default ESM loader.{} Received protocol '{}'", self.code(), @@ -648,20 +680,25 @@ impl NodeJsErrorCoded for UnsupportedEsmUrlSchemeError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolvePkgJsonBinExportError { + #[class(inherit)] #[error(transparent)] PkgJsonLoad(#[from] PackageJsonLoadError), + #[class(generic)] #[error("Failed resolving binary export. '{}' did not exist", pkg_json_path.display())] MissingPkgJson { pkg_json_path: PathBuf }, + #[class(generic)] #[error("Failed resolving binary export. {message}")] InvalidBinProperty { message: String }, } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolveBinaryCommandsError { + #[class(inherit)] #[error(transparent)] PkgJsonLoad(#[from] PackageJsonLoadError), + #[class(generic)] #[error("'{}' did not have a name", pkg_json_path.display())] MissingPkgJsonName { pkg_json_path: PathBuf }, } @@ -676,7 +713,7 @@ mod test { assert_eq!( PackagePathNotExportedError { pkg_json_path: PathBuf::from("test_path").join("package.json"), - subpath: "./jsx-runtime".to_string(), + subpath: "./jsx-runtime".to_string(), maybe_referrer: None, resolution_kind: NodeResolutionKind::Types }.to_string(), @@ -685,7 +722,7 @@ mod test { assert_eq!( PackagePathNotExportedError { pkg_json_path: PathBuf::from("test_path").join("package.json"), - subpath: ".".to_string(), + subpath: ".".to_string(), maybe_referrer: None, resolution_kind: NodeResolutionKind::Types }.to_string(), diff --git a/resolvers/node/lib.rs b/resolvers/node/lib.rs index 8da20c421e..c0e6383237 100644 --- a/resolvers/node/lib.rs +++ b/resolvers/node/lib.rs @@ -1,31 +1,32 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![deny(clippy::print_stderr)] #![deny(clippy::print_stdout)] pub mod analyze; -pub mod env; pub mod errors; mod npm; mod package_json; mod path; mod resolution; + mod sync; pub use deno_package_json::PackageJson; pub use npm::InNpmPackageChecker; -pub use npm::InNpmPackageCheckerRc; pub use npm::NpmPackageFolderResolver; -pub use npm::NpmPackageFolderResolverRc; pub use package_json::PackageJsonResolver; pub use package_json::PackageJsonResolverRc; pub use package_json::PackageJsonThreadLocalCache; pub use path::PathClean; pub use resolution::parse_npm_pkg_name; pub use resolution::resolve_specifier_into_node_modules; +pub use resolution::ConditionsFromResolutionMode; +pub use resolution::IsBuiltInNodeModuleChecker; pub use resolution::NodeResolution; pub use resolution::NodeResolutionKind; pub use resolution::NodeResolver; +pub use resolution::NodeResolverRc; pub use resolution::ResolutionMode; pub use resolution::DEFAULT_CONDITIONS; pub use resolution::REQUIRE_CONDITIONS; diff --git a/resolvers/node/npm.rs b/resolvers/node/npm.rs index ab3a179426..bb3de2a7f5 100644 --- a/resolvers/node/npm.rs +++ b/resolvers/node/npm.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; use std::path::PathBuf; @@ -9,16 +9,8 @@ use url::Url; use crate::errors; use crate::path::PathClean; -use crate::sync::MaybeSend; -use crate::sync::MaybeSync; -#[allow(clippy::disallowed_types)] -pub type NpmPackageFolderResolverRc = - crate::sync::MaybeArc; - -pub trait NpmPackageFolderResolver: - std::fmt::Debug + MaybeSend + MaybeSync -{ +pub trait NpmPackageFolderResolver { /// Resolves an npm package folder path from the specified referrer. fn resolve_package_folder_from_package( &self, @@ -27,11 +19,8 @@ pub trait NpmPackageFolderResolver: ) -> Result; } -#[allow(clippy::disallowed_types)] -pub type InNpmPackageCheckerRc = crate::sync::MaybeArc; - /// Checks if a provided specifier is in an npm package. -pub trait InNpmPackageChecker: std::fmt::Debug + MaybeSend + MaybeSync { +pub trait InNpmPackageChecker { fn in_npm_package(&self, specifier: &Url) -> bool; fn in_npm_package_at_dir_path(&self, path: &Path) -> bool { diff --git a/resolvers/node/package_json.rs b/resolvers/node/package_json.rs index e3793af84a..2e1d5110bd 100644 --- a/resolvers/node/package_json.rs +++ b/resolvers/node/package_json.rs @@ -1,17 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use deno_package_json::PackageJson; -use deno_package_json::PackageJsonRc; -use deno_path_util::strip_unc_prefix; use std::cell::RefCell; use std::collections::HashMap; use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; + +use deno_package_json::PackageJson; +use deno_package_json::PackageJsonRc; +use sys_traits::FsRead; use url::Url; -use crate::env::NodeResolverEnv; -use crate::errors::CanonicalizingPkgJsonDirError; use crate::errors::ClosestPkgJsonError; use crate::errors::PackageJsonLoadError; @@ -40,17 +39,17 @@ impl deno_package_json::PackageJsonCache for PackageJsonThreadLocalCache { } #[allow(clippy::disallowed_types)] -pub type PackageJsonResolverRc = - crate::sync::MaybeArc>; +pub type PackageJsonResolverRc = + crate::sync::MaybeArc>; #[derive(Debug)] -pub struct PackageJsonResolver { - env: TEnv, +pub struct PackageJsonResolver { + sys: TSys, } -impl PackageJsonResolver { - pub fn new(env: TEnv) -> Self { - Self { env } +impl PackageJsonResolver { + pub fn new(sys: TSys) -> Self { + Self { sys } } pub fn get_closest_package_json( @@ -67,37 +66,8 @@ impl PackageJsonResolver { &self, file_path: &Path, ) -> Result, ClosestPkgJsonError> { - // we use this for deno compile using byonm because the script paths - // won't be in virtual file system, but the package.json paths will be - fn canonicalize_first_ancestor_exists( - dir_path: &Path, - env: &TEnv, - ) -> Result, std::io::Error> { - for ancestor in dir_path.ancestors() { - match env.realpath_sync(ancestor) { - Ok(dir_path) => return Ok(Some(dir_path)), - Err(err) if err.kind() == std::io::ErrorKind::NotFound => { - // keep searching - } - Err(err) => return Err(err), - } - } - Ok(None) - } - let parent_dir = file_path.parent().unwrap(); - let Some(start_dir) = canonicalize_first_ancestor_exists( - parent_dir, &self.env, - ) - .map_err(|source| CanonicalizingPkgJsonDirError { - dir_path: parent_dir.to_path_buf(), - source, - })? - else { - return Ok(None); - }; - let start_dir = strip_unc_prefix(start_dir); - for current_dir in start_dir.ancestors() { + for current_dir in parent_dir.ancestors() { let package_json_path = current_dir.join("package.json"); if let Some(pkg_json) = self.load_package_json(&package_json_path)? { return Ok(Some(pkg_json)); @@ -112,9 +82,9 @@ impl PackageJsonResolver { path: &Path, ) -> Result, PackageJsonLoadError> { let result = PackageJson::load_from_path( - path, - self.env.pkg_json_fs(), + &self.sys, Some(&PackageJsonThreadLocalCache), + path, ); match result { Ok(pkg_json) => Ok(Some(pkg_json)), diff --git a/resolvers/node/path.rs b/resolvers/node/path.rs index 8c2d35fadf..525aeb36ef 100644 --- a/resolvers/node/path.rs +++ b/resolvers/node/path.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Component; use std::path::Path; diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs index 5f87698cd6..9ea5e17e41 100644 --- a/resolvers/node/resolution.rs +++ b/resolvers/node/resolution.rs @@ -1,17 +1,23 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; +use std::fmt::Debug; use std::path::Path; use std::path::PathBuf; use anyhow::bail; use anyhow::Error as AnyError; +use deno_package_json::PackageJson; use deno_path_util::url_from_file_path; use serde_json::Map; use serde_json::Value; +use sys_traits::FileType; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsMetadataValue; +use sys_traits::FsRead; use url::Url; -use crate::env::NodeResolverEnv; use crate::errors; use crate::errors::DataUrlReferrerError; use crate::errors::FinalizeResolutionError; @@ -40,22 +46,41 @@ use crate::errors::TypesNotFoundError; use crate::errors::TypesNotFoundErrorData; use crate::errors::UnsupportedDirImportError; use crate::errors::UnsupportedEsmUrlSchemeError; -use crate::npm::InNpmPackageCheckerRc; -use crate::NpmPackageFolderResolverRc; +use crate::InNpmPackageChecker; +use crate::NpmPackageFolderResolver; use crate::PackageJsonResolverRc; use crate::PathClean; -use deno_package_json::PackageJson; pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"]; pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"]; static TYPES_ONLY_CONDITIONS: &[&str] = &["types"]; -fn conditions_from_resolution_mode( - resolution_mode: ResolutionMode, -) -> &'static [&'static str] { - match resolution_mode { - ResolutionMode::Import => DEFAULT_CONDITIONS, - ResolutionMode::Require => REQUIRE_CONDITIONS, +type ConditionsFromResolutionModeFn = Box< + dyn Fn(ResolutionMode) -> &'static [&'static str] + Send + Sync + 'static, +>; + +#[derive(Default)] +pub struct ConditionsFromResolutionMode(Option); + +impl Debug for ConditionsFromResolutionMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ConditionsFromResolutionMode").finish() + } +} + +impl ConditionsFromResolutionMode { + pub fn new(func: ConditionsFromResolutionModeFn) -> Self { + Self(Some(func)) + } + + fn resolve( + &self, + resolution_mode: ResolutionMode, + ) -> &'static [&'static str] { + match &self.0 { + Some(func) => func(ResolutionMode::Import), + None => resolution_mode.default_conditions(), + } } } @@ -65,6 +90,15 @@ pub enum ResolutionMode { Require, } +impl ResolutionMode { + pub fn default_conditions(&self) -> &'static [&'static str] { + match self { + ResolutionMode::Import => DEFAULT_CONDITIONS, + ResolutionMode::Require => REQUIRE_CONDITIONS, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum NodeResolutionKind { Execution, @@ -98,29 +132,68 @@ impl NodeResolution { } } -#[allow(clippy::disallowed_types)] -pub type NodeResolverRc = crate::sync::MaybeArc>; - -#[derive(Debug)] -pub struct NodeResolver { - env: TEnv, - in_npm_pkg_checker: InNpmPackageCheckerRc, - npm_pkg_folder_resolver: NpmPackageFolderResolverRc, - pkg_json_resolver: PackageJsonResolverRc, +pub trait IsBuiltInNodeModuleChecker: std::fmt::Debug { + fn is_builtin_node_module(&self, specifier: &str) -> bool; } -impl NodeResolver { +#[allow(clippy::disallowed_types)] +pub type NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, +> = crate::sync::MaybeArc< + NodeResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, +>; + +#[derive(Debug)] +pub struct NodeResolver< + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: FsCanonicalize + FsMetadata + FsRead, +> { + in_npm_pkg_checker: TInNpmPackageChecker, + is_built_in_node_module_checker: TIsBuiltInNodeModuleChecker, + npm_pkg_folder_resolver: TNpmPackageFolderResolver, + pkg_json_resolver: PackageJsonResolverRc, + sys: TSys, + conditions_from_resolution_mode: ConditionsFromResolutionMode, +} + +impl< + TInNpmPackageChecker: InNpmPackageChecker, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: FsCanonicalize + FsMetadata + FsRead, + > + NodeResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + > +{ pub fn new( - env: TEnv, - in_npm_pkg_checker: InNpmPackageCheckerRc, - npm_pkg_folder_resolver: NpmPackageFolderResolverRc, - pkg_json_resolver: PackageJsonResolverRc, + in_npm_pkg_checker: TInNpmPackageChecker, + is_built_in_node_module_checker: TIsBuiltInNodeModuleChecker, + npm_pkg_folder_resolver: TNpmPackageFolderResolver, + pkg_json_resolver: PackageJsonResolverRc, + sys: TSys, + conditions_from_resolution_mode: ConditionsFromResolutionMode, ) -> Self { Self { - env, in_npm_pkg_checker, + is_built_in_node_module_checker, npm_pkg_folder_resolver, pkg_json_resolver, + sys, + conditions_from_resolution_mode, } } @@ -140,7 +213,10 @@ impl NodeResolver { // Note: if we are here, then the referrer is an esm module // TODO(bartlomieju): skipped "policy" part as we don't plan to support it - if self.env.is_builtin_node_module(specifier) { + if self + .is_built_in_node_module_checker + .is_builtin_node_module(specifier) + { return Ok(NodeResolution::BuiltIn(specifier.to_string())); } @@ -175,11 +251,14 @@ impl NodeResolver { } } + let conditions = self + .conditions_from_resolution_mode + .resolve(resolution_mode); let url = self.module_resolve( specifier, referrer, resolution_mode, - conditions_from_resolution_mode(resolution_mode), + conditions, resolution_kind, )?; @@ -189,6 +268,7 @@ impl NodeResolver { &file_path, Some(referrer), resolution_mode, + conditions, )? } else { url @@ -282,32 +362,25 @@ impl NodeResolver { p_str.to_string() }; - let (is_dir, is_file) = if let Ok(stats) = self.env.stat_sync(Path::new(&p)) - { - (stats.is_dir, stats.is_file) - } else { - (false, false) - }; - if is_dir { - return Err( + let maybe_file_type = self.sys.fs_metadata(p).map(|m| m.file_type()); + match maybe_file_type { + Ok(FileType::Dir) => Err( UnsupportedDirImportError { dir_url: resolved.clone(), maybe_referrer: maybe_referrer.map(ToOwned::to_owned), } .into(), - ); - } else if !is_file { - return Err( + ), + Ok(FileType::File) => Ok(resolved), + _ => Err( ModuleNotFoundError { specifier: resolved, maybe_referrer: maybe_referrer.map(ToOwned::to_owned), typ: "module", } .into(), - ); + ), } - - Ok(resolved) } pub fn resolve_package_subpath_from_deno_module( @@ -318,6 +391,8 @@ impl NodeResolver { resolution_mode: ResolutionMode, resolution_kind: NodeResolutionKind, ) -> Result { + // todo(dsherret): don't allocate a string here (maybe use an + // enum that says the subpath is not prefixed with a ./) let package_subpath = package_subpath .map(|s| format!("./{s}")) .unwrap_or_else(|| ".".to_string()); @@ -326,7 +401,9 @@ impl NodeResolver { &package_subpath, maybe_referrer, resolution_mode, - conditions_from_resolution_mode(resolution_mode), + self + .conditions_from_resolution_mode + .resolve(resolution_mode), resolution_kind, )?; // TODO(bartlomieju): skipped checking errors for commonJS resolution and @@ -388,15 +465,27 @@ impl NodeResolver { Ok(url) } + /// Resolves an npm package folder path from the specified referrer. + pub fn resolve_package_folder_from_package( + &self, + specifier: &str, + referrer: &Url, + ) -> Result { + self + .npm_pkg_folder_resolver + .resolve_package_folder_from_package(specifier, referrer) + } + /// Checks if the resolved file has a corresponding declaration file. fn path_to_declaration_url( &self, path: &Path, maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], ) -> Result { - fn probe_extensions( - fs: &TEnv, + fn probe_extensions( + sys: &TSys, path: &Path, lowercase_path: &str, resolution_mode: ResolutionMode, @@ -405,20 +494,20 @@ impl NodeResolver { let mut searched_for_d_cts = false; if lowercase_path.ends_with(".mjs") { let d_mts_path = with_known_extension(path, "d.mts"); - if fs.exists_sync(&d_mts_path) { + if sys.fs_exists_no_err(&d_mts_path) { return Some(d_mts_path); } searched_for_d_mts = true; } else if lowercase_path.ends_with(".cjs") { let d_cts_path = with_known_extension(path, "d.cts"); - if fs.exists_sync(&d_cts_path) { + if sys.fs_exists_no_err(&d_cts_path) { return Some(d_cts_path); } searched_for_d_cts = true; } let dts_path = with_known_extension(path, "d.ts"); - if fs.exists_sync(&dts_path) { + if sys.fs_exists_no_err(&dts_path) { return Some(dts_path); } @@ -432,7 +521,7 @@ impl NodeResolver { _ => None, // already searched above }; if let Some(specific_dts_path) = specific_dts_path { - if fs.exists_sync(&specific_dts_path) { + if sys.fs_exists_no_err(&specific_dts_path) { return Some(specific_dts_path); } } @@ -447,17 +536,17 @@ impl NodeResolver { return Ok(url_from_file_path(path).unwrap()); } if let Some(path) = - probe_extensions(&self.env, path, &lowercase_path, resolution_mode) + probe_extensions(&self.sys, path, &lowercase_path, resolution_mode) { return Ok(url_from_file_path(&path).unwrap()); } - if self.env.is_dir_sync(path) { + if self.sys.fs_is_dir_no_err(path) { let resolution_result = self.resolve_package_dir_subpath( path, /* sub path */ ".", maybe_referrer, resolution_mode, - conditions_from_resolution_mode(resolution_mode), + conditions, NodeResolutionKind::Types, ); if let Ok(resolution) = resolution_result { @@ -465,7 +554,7 @@ impl NodeResolver { } let index_path = path.join("index.js"); if let Some(path) = probe_extensions( - &self.env, + &self.sys, &index_path, &index_path.to_string_lossy().to_lowercase(), resolution_mode, @@ -669,7 +758,10 @@ impl NodeResolver { return match result { Ok(url) => Ok(url), Err(err) => { - if self.env.is_builtin_node_module(target) { + if self + .is_built_in_node_module_checker + .is_builtin_node_module(target) + { Ok(Url::parse(&format!("node:{}", target)).unwrap()) } else { Err(err) @@ -835,6 +927,7 @@ impl NodeResolver { &path, maybe_referrer, resolution_mode, + conditions, )?)); } else { return Ok(Some(url)); @@ -1192,6 +1285,7 @@ impl NodeResolver { package_subpath, maybe_referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1229,6 +1323,7 @@ impl NodeResolver { package_json, referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1248,6 +1343,7 @@ impl NodeResolver { package_json, referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1261,6 +1357,7 @@ impl NodeResolver { package_subpath, referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1274,12 +1371,18 @@ impl NodeResolver { package_subpath: &str, referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], resolution_kind: NodeResolutionKind, ) -> Result { assert_ne!(package_subpath, "."); let file_path = directory.join(package_subpath); if resolution_kind.is_types() { - Ok(self.path_to_declaration_url(&file_path, referrer, resolution_mode)?) + Ok(self.path_to_declaration_url( + &file_path, + referrer, + resolution_mode, + conditions, + )?) } else { Ok(url_from_file_path(&file_path).unwrap()) } @@ -1291,6 +1394,7 @@ impl NodeResolver { package_subpath: &str, maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], resolution_kind: NodeResolutionKind, ) -> Result { if package_subpath == "." { @@ -1307,6 +1411,7 @@ impl NodeResolver { package_subpath, maybe_referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| err.into()) @@ -1318,6 +1423,7 @@ impl NodeResolver { package_json: &PackageJson, maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], resolution_kind: NodeResolutionKind, ) -> Result { let pkg_json_kind = match resolution_mode { @@ -1336,6 +1442,7 @@ impl NodeResolver { &main, maybe_referrer, resolution_mode, + conditions, ); // don't surface errors, fallback to checking the index now if let Ok(url) = decl_url_result { @@ -1351,7 +1458,7 @@ impl NodeResolver { if let Some(main) = maybe_main { let guess = package_json.path.parent().unwrap().join(main).clean(); - if self.env.is_file_sync(&guess) { + if self.sys.fs_is_file_no_err(&guess) { return Ok(url_from_file_path(&guess).unwrap()); } @@ -1380,7 +1487,7 @@ impl NodeResolver { .unwrap() .join(format!("{main}{ending}")) .clean(); - if self.env.is_file_sync(&guess) { + if self.sys.fs_is_file_no_err(&guess) { // TODO(bartlomieju): emitLegacyIndexDeprecation() return Ok(url_from_file_path(&guess).unwrap()); } @@ -1415,7 +1522,7 @@ impl NodeResolver { }; for index_file_name in index_file_names { let guess = directory.join(index_file_name).clean(); - if self.env.is_file_sync(&guess) { + if self.sys.fs_is_file_no_err(&guess) { // TODO(bartlomieju): emitLegacyIndexDeprecation() return Ok(url_from_file_path(&guess).unwrap()); } @@ -1452,9 +1559,7 @@ impl NodeResolver { { // Specifiers in the node_modules directory are canonicalized // so canoncalize then check if it's in the node_modules directory. - let specifier = resolve_specifier_into_node_modules(specifier, &|path| { - self.env.realpath_sync(path) - }); + let specifier = resolve_specifier_into_node_modules(&self.sys, specifier); return Some(specifier); } @@ -1715,16 +1820,15 @@ pub fn parse_npm_pkg_name( /// not be fully resolved at the time deno_graph is analyzing it /// because the node_modules folder might not exist at that time. pub fn resolve_specifier_into_node_modules( + sys: &impl FsCanonicalize, specifier: &Url, - canonicalize: &impl Fn(&Path) -> std::io::Result, ) -> Url { deno_path_util::url_to_file_path(specifier) .ok() // this path might not exist at the time the graph is being created // because the node_modules folder might not yet exist .and_then(|path| { - deno_path_util::canonicalize_path_maybe_not_exists(&path, canonicalize) - .ok() + deno_path_util::fs::canonicalize_path_maybe_not_exists(sys, &path).ok() }) .and_then(|path| deno_path_util::url_from_file_path(&path).ok()) .unwrap_or_else(|| specifier.clone()) diff --git a/resolvers/node/sync.rs b/resolvers/node/sync.rs index 3c4729aa2c..218253b453 100644 --- a/resolvers/node/sync.rs +++ b/resolvers/node/sync.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use inner::*; @@ -7,17 +7,9 @@ mod inner { #![allow(clippy::disallowed_types)] pub use std::sync::Arc as MaybeArc; - - pub use core::marker::Send as MaybeSend; - pub use core::marker::Sync as MaybeSync; } #[cfg(not(feature = "sync"))] mod inner { pub use std::rc::Rc as MaybeArc; - - pub trait MaybeSync {} - impl MaybeSync for T where T: ?Sized {} - pub trait MaybeSend {} - impl MaybeSend for T where T: ?Sized {} } diff --git a/resolvers/npm_cache/Cargo.toml b/resolvers/npm_cache/Cargo.toml index 1cc7237025..d73cee9cd6 100644 --- a/resolvers/npm_cache/Cargo.toml +++ b/resolvers/npm_cache/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_npm_cache" -version = "0.2.0" +version = "0.4.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -14,16 +14,13 @@ description = "Helpers for downloading and caching npm dependencies for Deno" path = "lib.rs" [dependencies] -# todo(dsherret): remove this dependency -anyhow.workspace = true -# todo(dsherret): remove this dependency -deno_core.workspace = true - async-trait.workspace = true base64.workspace = true boxed_error.workspace = true deno_cache_dir.workspace = true +deno_error = { workspace = true, features = ["serde", "serde_json", "tokio"] } deno_npm.workspace = true +deno_path_util.workspace = true deno_semver.workspace = true deno_unsync = { workspace = true, features = ["tokio"] } faster-hex.workspace = true @@ -36,6 +33,7 @@ percent-encoding.workspace = true rand.workspace = true ring.workspace = true serde_json.workspace = true +sys_traits.workspace = true tar.workspace = true tempfile = "3.4.0" thiserror.workspace = true diff --git a/resolvers/npm_cache/fs_util.rs b/resolvers/npm_cache/fs_util.rs new file mode 100644 index 0000000000..77269ebe0b --- /dev/null +++ b/resolvers/npm_cache/fs_util.rs @@ -0,0 +1,153 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::io::ErrorKind; +use std::path::Path; +use std::path::PathBuf; +use std::time::Duration; + +use sys_traits::FsCreateDirAll; +use sys_traits::FsDirEntry; +use sys_traits::FsHardLink; +use sys_traits::FsReadDir; +use sys_traits::FsRemoveFile; +use sys_traits::ThreadSleep; + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum HardLinkDirRecursiveError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error("Creating {path}")] + Creating { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Creating {path}")] + Reading { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Dir {from} to {to}")] + Dir { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: Box, + }, + #[class(inherit)] + #[error("Removing file to hard link {from} to {to}")] + RemoveFileToHardLink { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Hard linking {from} to {to}")] + HardLinking { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, +} + +/// Hardlinks the files in one directory to another directory. +/// +/// Note: Does not handle symlinks. +pub fn hard_link_dir_recursive< + TSys: FsCreateDirAll + FsHardLink + FsReadDir + FsRemoveFile + ThreadSleep, +>( + sys: &TSys, + from: &Path, + to: &Path, +) -> Result<(), HardLinkDirRecursiveError> { + sys.fs_create_dir_all(to).map_err(|source| { + HardLinkDirRecursiveError::Creating { + path: to.to_path_buf(), + source, + } + })?; + let read_dir = sys.fs_read_dir(from).map_err(|source| { + HardLinkDirRecursiveError::Reading { + path: from.to_path_buf(), + source, + } + })?; + + for entry in read_dir { + let entry = entry?; + let file_type = entry.file_type()?; + let new_from = from.join(entry.file_name()); + let new_to = to.join(entry.file_name()); + + if file_type.is_dir() { + hard_link_dir_recursive(sys, &new_from, &new_to).map_err(|source| { + HardLinkDirRecursiveError::Dir { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: Box::new(source), + } + })?; + } else if file_type.is_file() { + // note: chance for race conditions here between attempting to create, + // then removing, then attempting to create. There doesn't seem to be + // a way to hard link with overwriting in Rust, but maybe there is some + // way with platform specific code. The workaround here is to handle + // scenarios where something else might create or remove files. + if let Err(err) = sys.fs_hard_link(&new_from, &new_to) { + if err.kind() == ErrorKind::AlreadyExists { + if let Err(err) = sys.fs_remove_file(&new_to) { + if err.kind() == ErrorKind::NotFound { + // Assume another process/thread created this hard link to the file we are wanting + // to remove then sleep a little bit to let the other process/thread move ahead + // faster to reduce contention. + sys.thread_sleep(Duration::from_millis(10)); + } else { + return Err(HardLinkDirRecursiveError::RemoveFileToHardLink { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: err, + }); + } + } + + // Always attempt to recreate the hardlink. In contention scenarios, the other process + // might have been killed or exited after removing the file, but before creating the hardlink + if let Err(err) = sys.fs_hard_link(&new_from, &new_to) { + // Assume another process/thread created this hard link to the file we are wanting + // to now create then sleep a little bit to let the other process/thread move ahead + // faster to reduce contention. + if err.kind() == ErrorKind::AlreadyExists { + sys.thread_sleep(Duration::from_millis(10)); + } else { + return Err(HardLinkDirRecursiveError::HardLinking { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: err, + }); + } + } + } else { + return Err(HardLinkDirRecursiveError::HardLinking { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: err, + }); + } + } + } + } + + Ok(()) +} diff --git a/resolvers/npm_cache/lib.rs b/resolvers/npm_cache/lib.rs index 9f5424dc46..f0de201b75 100644 --- a/resolvers/npm_cache/lib.rs +++ b/resolvers/npm_cache/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::io::ErrorKind; @@ -6,38 +6,51 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use anyhow::bail; -use anyhow::Context; -use anyhow::Error as AnyError; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::npm::NpmCacheDir; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_npm::NpmPackageCacheFolderId; +use deno_path_util::fs::atomic_write_file_with_retries; use deno_semver::package::PackageNv; +use deno_semver::StackString; use deno_semver::Version; use http::HeaderName; use http::HeaderValue; use http::StatusCode; use parking_lot::Mutex; +use sys_traits::FsCreateDirAll; +use sys_traits::FsHardLink; +use sys_traits::FsMetadata; +use sys_traits::FsOpen; +use sys_traits::FsReadDir; +use sys_traits::FsRemoveFile; +use sys_traits::FsRename; +use sys_traits::SystemRandom; +use sys_traits::ThreadSleep; use url::Url; +mod fs_util; mod registry_info; mod remote; mod tarball; mod tarball_extract; -pub use registry_info::RegistryInfoProvider; -pub use tarball::TarballCache; - +pub use fs_util::hard_link_dir_recursive; // todo(#27198): make both of these private and get the rest of the code // using RegistryInfoProvider. pub use registry_info::get_package_url; +pub use registry_info::RegistryInfoProvider; pub use remote::maybe_auth_header_for_npm_registry; +pub use tarball::EnsurePackageError; +pub use tarball::TarballCache; -#[derive(Debug)] +#[derive(Debug, deno_error::JsError)] +#[class(generic)] pub struct DownloadError { pub status_code: Option, - pub error: AnyError, + pub error: JsErrorBox, } impl std::error::Error for DownloadError { @@ -53,18 +66,7 @@ impl std::fmt::Display for DownloadError { } #[async_trait::async_trait(?Send)] -pub trait NpmCacheEnv: Send + Sync + 'static { - fn exists(&self, path: &Path) -> bool; - fn hard_link_dir_recursive( - &self, - from: &Path, - to: &Path, - ) -> Result<(), AnyError>; - fn atomic_write_file_with_retries( - &self, - file_path: &Path, - data: &[u8], - ) -> std::io::Result<()>; +pub trait NpmCacheHttpClient: Send + Sync + 'static { async fn download_with_retries_on_any_tokio_runtime( &self, url: Url, @@ -90,6 +92,27 @@ pub enum NpmCacheSetting { } impl NpmCacheSetting { + pub fn from_cache_setting(cache_setting: &CacheSetting) -> NpmCacheSetting { + match cache_setting { + CacheSetting::Only => NpmCacheSetting::Only, + CacheSetting::ReloadAll => NpmCacheSetting::ReloadAll, + CacheSetting::ReloadSome(values) => { + if values.iter().any(|v| v == "npm:") { + NpmCacheSetting::ReloadAll + } else { + NpmCacheSetting::ReloadSome { + npm_package_names: values + .iter() + .filter_map(|v| v.strip_prefix("npm:")) + .map(|n| n.to_string()) + .collect(), + } + } + } + CacheSetting::RespectHeaders => panic!("not supported"), + CacheSetting::Use => NpmCacheSetting::Use, + } + } pub fn should_use_for_npm_package(&self, package_name: &str) -> bool { match self { NpmCacheSetting::ReloadAll => false, @@ -103,27 +126,48 @@ impl NpmCacheSetting { /// Stores a single copy of npm packages in a cache. #[derive(Debug)] -pub struct NpmCache { - env: Arc, +pub struct NpmCache< + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom, +> { cache_dir: Arc, + sys: TSys, cache_setting: NpmCacheSetting, npmrc: Arc, previously_reloaded_packages: Mutex>, } -impl NpmCache { +impl< + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom, + > NpmCache +{ pub fn new( cache_dir: Arc, + sys: TSys, cache_setting: NpmCacheSetting, - env: Arc, npmrc: Arc, ) -> Self { Self { cache_dir, + sys, cache_setting, - env, - previously_reloaded_packages: Default::default(), npmrc, + previously_reloaded_packages: Default::default(), } } @@ -159,7 +203,7 @@ impl NpmCache { pub fn ensure_copy_package( &self, folder_id: &NpmPackageCacheFolderId, - ) -> Result<(), AnyError> { + ) -> Result<(), WithFolderSyncLockError> { let registry_url = self.npmrc.get_registry_url(&folder_id.nv.name); assert_ne!(folder_id.copy_index, 0); let package_folder = self.cache_dir.package_folder_for_id( @@ -188,9 +232,12 @@ impl NpmCache { // it seems Windows does an "AccessDenied" error when moving a // directory with hard links, so that's why this solution is done with_folder_sync_lock(&folder_id.nv, &package_folder, || { - self - .env - .hard_link_dir_recursive(&original_package_folder, &package_folder) + hard_link_dir_recursive( + &self.sys, + &original_package_folder, + &package_folder, + ) + .map_err(JsErrorBox::from_err) })?; Ok(()) } @@ -238,7 +285,7 @@ impl NpmCache { .and_then(|cache_id| { Some(NpmPackageCacheFolderId { nv: PackageNv { - name: cache_id.name, + name: StackString::from_string(cache_id.name), version: Version::parse_from_npm(&cache_id.version).ok()?, }, copy_index: cache_id.copy_index, @@ -249,27 +296,32 @@ impl NpmCache { pub fn load_package_info( &self, name: &str, - ) -> Result, AnyError> { + ) -> Result, serde_json::Error> { let file_cache_path = self.get_registry_package_info_file_cache_path(name); let file_text = match std::fs::read_to_string(file_cache_path) { Ok(file_text) => file_text, Err(err) if err.kind() == ErrorKind::NotFound => return Ok(None), - Err(err) => return Err(err.into()), + Err(err) => return Err(serde_json::Error::io(err)), }; - Ok(serde_json::from_str(&file_text)?) + serde_json::from_str(&file_text) } pub fn save_package_info( &self, name: &str, package_info: &NpmPackageInfo, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { let file_cache_path = self.get_registry_package_info_file_cache_path(name); - let file_text = serde_json::to_string(&package_info)?; - self - .env - .atomic_write_file_with_retries(&file_cache_path, file_text.as_bytes())?; + let file_text = + serde_json::to_string(&package_info).map_err(JsErrorBox::from_err)?; + atomic_write_file_with_retries( + &self.sys, + &file_cache_path, + file_text.as_bytes(), + 0o644, + ) + .map_err(JsErrorBox::from_err)?; Ok(()) } @@ -281,17 +333,52 @@ impl NpmCache { const NPM_PACKAGE_SYNC_LOCK_FILENAME: &str = ".deno_sync_lock"; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum WithFolderSyncLockError { + #[class(inherit)] + #[error("Error creating '{path}'")] + CreateDir { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Error creating package sync lock file at '{path}'. Maybe try manually deleting this folder.")] + CreateLockFile { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + Action(#[from] JsErrorBox), + #[class(generic)] + #[error("Failed setting up package cache directory for {package}, then failed cleaning it up.\n\nOriginal error:\n\n{error}\n\nRemove error:\n\n{remove_error}\n\nPlease manually delete this folder or you will run into issues using this package in the future:\n\n{output_folder}")] + SetUpPackageCacheDir { + package: Box, + error: Box, + remove_error: std::io::Error, + output_folder: PathBuf, + }, +} + +// todo(dsherret): use `sys` here instead of `std::fs`. fn with_folder_sync_lock( package: &PackageNv, output_folder: &Path, - action: impl FnOnce() -> Result<(), AnyError>, -) -> Result<(), AnyError> { + action: impl FnOnce() -> Result<(), JsErrorBox>, +) -> Result<(), WithFolderSyncLockError> { fn inner( output_folder: &Path, - action: impl FnOnce() -> Result<(), AnyError>, - ) -> Result<(), AnyError> { - std::fs::create_dir_all(output_folder).with_context(|| { - format!("Error creating '{}'.", output_folder.display()) + action: impl FnOnce() -> Result<(), JsErrorBox>, + ) -> Result<(), WithFolderSyncLockError> { + std::fs::create_dir_all(output_folder).map_err(|source| { + WithFolderSyncLockError::CreateDir { + path: output_folder.to_path_buf(), + source, + } })?; // This sync lock file is a way to ensure that partially created @@ -315,16 +402,10 @@ fn with_folder_sync_lock( let _ignore = std::fs::remove_file(&sync_lock_path); Ok(()) } - Err(err) => { - bail!( - concat!( - "Error creating package sync lock file at '{}'. ", - "Maybe try manually deleting this folder.\n\n{:#}", - ), - output_folder.display(), - err - ); - } + Err(err) => Err(WithFolderSyncLockError::CreateLockFile { + path: output_folder.to_path_buf(), + source: err, + }), } } @@ -333,19 +414,12 @@ fn with_folder_sync_lock( Err(err) => { if let Err(remove_err) = std::fs::remove_dir_all(output_folder) { if remove_err.kind() != std::io::ErrorKind::NotFound { - bail!( - concat!( - "Failed setting up package cache directory for {}, then ", - "failed cleaning it up.\n\nOriginal error:\n\n{}\n\n", - "Remove error:\n\n{}\n\nPlease manually ", - "delete this folder or you will run into issues using this ", - "package in the future:\n\n{}" - ), - package, - err, - remove_err, - output_folder.display(), - ); + return Err(WithFolderSyncLockError::SetUpPackageCacheDir { + package: Box::new(package.clone()), + error: Box::new(err), + remove_error: remove_err, + output_folder: output_folder.to_path_buf(), + }); } } Err(err) diff --git a/resolvers/npm_cache/registry_info.rs b/resolvers/npm_cache/registry_info.rs index 543ddadc5a..673f2ff445 100644 --- a/resolvers/npm_cache/registry_info.rs +++ b/resolvers/npm_cache/registry_info.rs @@ -1,14 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; use std::sync::Arc; -use anyhow::anyhow; -use anyhow::bail; -use anyhow::Context; -use anyhow::Error as AnyError; use async_trait::async_trait; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmRegistryApi; @@ -18,14 +15,23 @@ use deno_unsync::sync::MultiRuntimeAsyncValueCreator; use futures::future::LocalBoxFuture; use futures::FutureExt; use parking_lot::Mutex; +use sys_traits::FsCreateDirAll; +use sys_traits::FsHardLink; +use sys_traits::FsMetadata; +use sys_traits::FsOpen; +use sys_traits::FsReadDir; +use sys_traits::FsRemoveFile; +use sys_traits::FsRename; +use sys_traits::SystemRandom; +use sys_traits::ThreadSleep; use url::Url; use crate::remote::maybe_auth_header_for_npm_registry; use crate::NpmCache; -use crate::NpmCacheEnv; +use crate::NpmCacheHttpClient; use crate::NpmCacheSetting; -type LoadResult = Result>; +type LoadResult = Result>; type LoadFuture = LocalBoxFuture<'static, LoadResult>; #[derive(Debug, Clone)] @@ -45,7 +51,7 @@ enum MemoryCacheItem { FsCached, /// An item is memory cached when it fails saving to the file system cache /// or the package does not exist. - MemoryCached(Result>, Arc>), + MemoryCached(Result>, Arc>), } #[derive(Debug, Default)] @@ -90,31 +96,93 @@ impl MemoryCache { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +pub enum LoadFileCachedPackageInfoError { + #[error("Previously saved '{name}' from the npm cache, but now it fails to load: {err}")] + LoadPackageInfo { + err: serde_json::Error, + name: String, + }, + #[error("The package '{0}' previously saved its registry information to the file system cache, but that file no longer exists.")] + FileMissing(String), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(inherit)] +#[error("Failed loading {url} for package \"{name}\"")] +pub struct LoadPackageInfoError { + url: Url, + name: String, + #[inherit] + #[source] + inner: LoadPackageInfoInnerError, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum LoadPackageInfoInnerError { + #[class(inherit)] + #[error("{0}")] + LoadFileCachedPackageInfo(LoadFileCachedPackageInfoError), + #[class(inherit)] + #[error("{0}")] + Other(Arc), +} + // todo(#27198): refactor to store this only in the http cache /// Downloads packuments from the npm registry. /// /// This is shared amongst all the workers. #[derive(Debug)] -pub struct RegistryInfoProvider { +pub struct RegistryInfoProvider< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, +> { // todo(#27198): remove this - cache: Arc>, - env: Arc, + cache: Arc>, + http_client: Arc, npmrc: Arc, force_reload_flag: AtomicFlag, memory_cache: Mutex, previously_loaded_packages: Mutex>, } -impl RegistryInfoProvider { +impl< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, + > RegistryInfoProvider +{ pub fn new( - cache: Arc>, - env: Arc, + cache: Arc>, + http_client: Arc, npmrc: Arc, ) -> Self { Self { cache, - env, + http_client, npmrc, force_reload_flag: AtomicFlag::lowered(), memory_cache: Default::default(), @@ -144,7 +212,9 @@ impl RegistryInfoProvider { } } - pub fn as_npm_registry_api(self: &Arc) -> NpmRegistryApiAdapter { + pub fn as_npm_registry_api( + self: &Arc, + ) -> NpmRegistryApiAdapter { NpmRegistryApiAdapter(self.clone()) } @@ -157,29 +227,29 @@ impl RegistryInfoProvider { Ok(None) => Err(NpmRegistryPackageInfoLoadError::PackageNotExists { package_name: name.to_string(), }), - Err(err) => { - Err(NpmRegistryPackageInfoLoadError::LoadError(Arc::new(err))) - } + Err(err) => Err(NpmRegistryPackageInfoLoadError::LoadError(Arc::new( + JsErrorBox::from_err(err), + ))), } } pub async fn maybe_package_info( self: &Arc, name: &str, - ) -> Result>, AnyError> { - self.load_package_info_inner(name).await.with_context(|| { - format!( - "Failed loading {} for package \"{}\"", - get_package_url(&self.npmrc, name), - name - ) + ) -> Result>, LoadPackageInfoError> { + self.load_package_info_inner(name).await.map_err(|err| { + LoadPackageInfoError { + url: get_package_url(&self.npmrc, name), + name: name.to_string(), + inner: err, + } }) } async fn load_package_info_inner( self: &Arc, name: &str, - ) -> Result>, AnyError> { + ) -> Result>, LoadPackageInfoInnerError> { let (cache_item, clear_id) = { let mut mem_cache = self.memory_cache.lock(); let cache_item = if let Some(cache_item) = mem_cache.get(name) { @@ -204,9 +274,10 @@ impl RegistryInfoProvider { .load_file_cached_package_info(name) .await .map(|info| Some(Arc::new(info))) + .map_err(LoadPackageInfoInnerError::LoadFileCachedPackageInfo) } MemoryCacheItem::MemoryCached(maybe_info) => { - maybe_info.clone().map_err(|e| anyhow!("{}", e)) + maybe_info.clone().map_err(LoadPackageInfoInnerError::Other) } MemoryCacheItem::Pending(value_creator) => { match value_creator.get().await { @@ -238,13 +309,13 @@ impl RegistryInfoProvider { Ok(None) } Err(err) => { - let return_err = anyhow!("{:#}", err); + let return_err = err.clone(); self.memory_cache.lock().try_insert( clear_id, name, MemoryCacheItem::MemoryCached(Err(err)), ); - Err(return_err) + Err(LoadPackageInfoInnerError::Other(return_err)) } } } @@ -254,7 +325,7 @@ impl RegistryInfoProvider { async fn load_file_cached_package_info( &self, name: &str, - ) -> Result { + ) -> Result { // this scenario failing should be exceptionally rare so let's // deal with improving it only when anyone runs into an issue let maybe_package_info = deno_unsync::spawn_blocking({ @@ -264,17 +335,15 @@ impl RegistryInfoProvider { }) .await .unwrap() - .with_context(|| { - format!( - "Previously saved '{}' from the npm cache, but now it fails to load.", - name - ) + .map_err(|err| LoadFileCachedPackageInfoError::LoadPackageInfo { + err, + name: name.to_string(), })?; match maybe_package_info { Some(package_info) => Ok(package_info), - None => { - bail!("The package '{}' previously saved its registry information to the file system cache, but that file no longer exists.", name) - } + None => Err(LoadFileCachedPackageInfoError::FileMissing( + name.to_string(), + )), } } @@ -286,7 +355,8 @@ impl RegistryInfoProvider { match maybe_auth_header_for_npm_registry(registry_config) { Ok(maybe_auth_header) => maybe_auth_header, Err(err) => { - return std::future::ready(Err(Arc::new(err))).boxed_local() + return std::future::ready(Err(Arc::new(JsErrorBox::from_err(err)))) + .boxed_local() } }; let name = name.to_string(); @@ -297,14 +367,14 @@ impl RegistryInfoProvider { || downloader.previously_loaded_packages.lock().contains(&name) { // attempt to load from the file cache - if let Some(info) = downloader.cache.load_package_info(&name)? { + if let Some(info) = downloader.cache.load_package_info(&name).map_err(JsErrorBox::from_err)? { let result = Arc::new(info); return Ok(FutureResult::SavedFsCache(result)); } } if *downloader.cache.cache_setting() == NpmCacheSetting::Only { - return Err(deno_core::error::custom_error( + return Err(JsErrorBox::new( "NotCached", format!( "npm package not found in cache: \"{name}\", --cached-only is specified." @@ -315,17 +385,17 @@ impl RegistryInfoProvider { downloader.previously_loaded_packages.lock().insert(name.to_string()); let maybe_bytes = downloader - .env + .http_client .download_with_retries_on_any_tokio_runtime( package_url, maybe_auth_header, ) - .await?; + .await.map_err(JsErrorBox::from_err)?; match maybe_bytes { Some(bytes) => { let future_result = deno_unsync::spawn_blocking( - move || -> Result { - let package_info = serde_json::from_slice(&bytes)?; + move || -> Result { + let package_info = serde_json::from_slice(&bytes).map_err(JsErrorBox::from_err)?; match downloader.cache.save_package_info(&name, &package_info) { Ok(()) => { Ok(FutureResult::SavedFsCache(Arc::new(package_info))) @@ -341,7 +411,8 @@ impl RegistryInfoProvider { } }, ) - .await??; + .await + .map_err(JsErrorBox::from_err)??; Ok(future_result) } None => Ok(FutureResult::PackageNotExists), @@ -352,12 +423,39 @@ impl RegistryInfoProvider { } } -pub struct NpmRegistryApiAdapter( - Arc>, -); +pub struct NpmRegistryApiAdapter< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, +>(Arc>); #[async_trait(?Send)] -impl NpmRegistryApi for NpmRegistryApiAdapter { +impl< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, + > NpmRegistryApi for NpmRegistryApiAdapter +{ async fn package_info( &self, name: &str, diff --git a/resolvers/npm_cache/remote.rs b/resolvers/npm_cache/remote.rs index 538554612f..0e04d05502 100644 --- a/resolvers/npm_cache/remote.rs +++ b/resolvers/npm_cache/remote.rs @@ -1,17 +1,27 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use anyhow::bail; -use anyhow::Context; -use anyhow::Error as AnyError; use base64::prelude::BASE64_STANDARD; use base64::Engine; use deno_npm::npm_rc::RegistryConfig; use http::header; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum AuthHeaderForNpmRegistryError { + #[class(type)] + #[error("Both the username and password must be provided for basic auth")] + Both, + #[class(type)] + #[error("The password in npmrc is an invalid base64 string: {0}")] + Base64(base64::DecodeError), +} + // TODO(bartlomieju): support more auth methods besides token and basic auth pub fn maybe_auth_header_for_npm_registry( registry_config: &RegistryConfig, -) -> Result, AnyError> { +) -> Result< + Option<(header::HeaderName, header::HeaderValue)>, + AuthHeaderForNpmRegistryError, +> { if let Some(token) = registry_config.auth_token.as_ref() { return Ok(Some(( header::AUTHORIZATION, @@ -33,7 +43,7 @@ pub fn maybe_auth_header_for_npm_registry( if (username.is_some() && password.is_none()) || (username.is_none() && password.is_some()) { - bail!("Both the username and password must be provided for basic auth") + return Err(AuthHeaderForNpmRegistryError::Both); } if username.is_some() && password.is_some() { @@ -42,7 +52,7 @@ pub fn maybe_auth_header_for_npm_registry( // https://github.com/npm/cli/blob/780afc50e3a345feb1871a28e33fa48235bc3bd5/workspaces/config/lib/index.js#L846-L851 let pw_base64 = BASE64_STANDARD .decode(password.unwrap()) - .with_context(|| "The password in npmrc is an invalid base64 string")?; + .map_err(AuthHeaderForNpmRegistryError::Base64)?; let bearer = BASE64_STANDARD.encode(format!( "{}:{}", username.unwrap(), diff --git a/resolvers/npm_cache/tarball.rs b/resolvers/npm_cache/tarball.rs index 5c8e460fd6..7a9453e655 100644 --- a/resolvers/npm_cache/tarball.rs +++ b/resolvers/npm_cache/tarball.rs @@ -1,12 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::sync::Arc; -use anyhow::anyhow; -use anyhow::bail; -use anyhow::Context; -use anyhow::Error as AnyError; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageVersionDistInfo; use deno_semver::package::PackageNv; @@ -15,16 +12,25 @@ use futures::future::LocalBoxFuture; use futures::FutureExt; use http::StatusCode; use parking_lot::Mutex; +use sys_traits::FsCreateDirAll; +use sys_traits::FsHardLink; +use sys_traits::FsMetadata; +use sys_traits::FsOpen; +use sys_traits::FsReadDir; +use sys_traits::FsRemoveFile; +use sys_traits::FsRename; +use sys_traits::SystemRandom; +use sys_traits::ThreadSleep; use url::Url; use crate::remote::maybe_auth_header_for_npm_registry; use crate::tarball_extract::verify_and_extract_tarball; use crate::tarball_extract::TarballExtractionMode; use crate::NpmCache; -use crate::NpmCacheEnv; +use crate::NpmCacheHttpClient; use crate::NpmCacheSetting; -type LoadResult = Result<(), Arc>; +type LoadResult = Result<(), Arc>; type LoadFuture = LocalBoxFuture<'static, LoadResult>; #[derive(Debug, Clone)] @@ -32,7 +38,7 @@ enum MemoryCacheItem { /// The cache item hasn't finished yet. Pending(Arc>), /// The result errored. - Errored(Arc), + Errored(Arc), /// This package has already been cached. Cached, } @@ -42,22 +48,62 @@ enum MemoryCacheItem { /// /// This is shared amongst all the workers. #[derive(Debug)] -pub struct TarballCache { - cache: Arc>, - env: Arc, +pub struct TarballCache< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsRemoveFile + + FsReadDir + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, +> { + cache: Arc>, + http_client: Arc, + sys: TSys, npmrc: Arc, memory_cache: Mutex>, } -impl TarballCache { +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +#[error("Failed caching npm package '{package_nv}'")] +pub struct EnsurePackageError { + package_nv: Box, + #[source] + source: Arc, +} +impl< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsRemoveFile + + FsReadDir + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, + > TarballCache +{ pub fn new( - cache: Arc>, - env: Arc, + cache: Arc>, + http_client: Arc, + sys: TSys, npmrc: Arc, ) -> Self { Self { cache, - env, + http_client, + sys, npmrc, memory_cache: Default::default(), } @@ -67,18 +113,21 @@ impl TarballCache { self: &Arc, package_nv: &PackageNv, dist: &NpmPackageVersionDistInfo, - ) -> Result<(), AnyError> { + ) -> Result<(), EnsurePackageError> { self .ensure_package_inner(package_nv, dist) .await - .with_context(|| format!("Failed caching npm package '{}'.", package_nv)) + .map_err(|source| EnsurePackageError { + package_nv: Box::new(package_nv.clone()), + source, + }) } async fn ensure_package_inner( self: &Arc, package_nv: &PackageNv, dist: &NpmPackageVersionDistInfo, - ) -> Result<(), AnyError> { + ) -> Result<(), Arc> { let cache_item = { let mut mem_cache = self.memory_cache.lock(); if let Some(cache_item) = mem_cache.get(package_nv) { @@ -100,7 +149,7 @@ impl TarballCache { match cache_item { MemoryCacheItem::Cached => Ok(()), - MemoryCacheItem::Errored(err) => Err(anyhow!("{:#}", err)), + MemoryCacheItem::Errored(err) => Err(err), MemoryCacheItem::Pending(creator) => { let result = creator.get().await; match result { @@ -110,10 +159,9 @@ impl TarballCache { Ok(()) } Err(err) => { - let result_err = anyhow!("{:#}", err); *self.memory_cache.lock().get_mut(package_nv).unwrap() = - MemoryCacheItem::Errored(err); - Err(result_err) + MemoryCacheItem::Errored(err.clone()); + Err(err) } } } @@ -131,11 +179,11 @@ impl TarballCache { let package_folder = tarball_cache.cache.package_folder_for_nv_and_url(&package_nv, registry_url); let should_use_cache = tarball_cache.cache.should_use_cache_for_package(&package_nv); - let package_folder_exists = tarball_cache.env.exists(&package_folder); + let package_folder_exists = tarball_cache.sys.fs_exists_no_err(&package_folder); if should_use_cache && package_folder_exists { return Ok(()); } else if tarball_cache.cache.cache_setting() == &NpmCacheSetting::Only { - return Err(deno_core::error::custom_error( + return Err(JsErrorBox::new( "NotCached", format!( "npm package not found in cache: \"{}\", --cached-only is specified.", @@ -146,17 +194,17 @@ impl TarballCache { } if dist.tarball.is_empty() { - bail!("Tarball URL was empty."); + return Err(JsErrorBox::generic("Tarball URL was empty.")); } // IMPORTANT: npm registries may specify tarball URLs at different URLS than the // registry, so we MUST get the auth for the tarball URL and not the registry URL. - let tarball_uri = Url::parse(&dist.tarball)?; + let tarball_uri = Url::parse(&dist.tarball).map_err(JsErrorBox::from_err)?; let maybe_registry_config = tarball_cache.npmrc.tarball_config(&tarball_uri); let maybe_auth_header = maybe_registry_config.and_then(|c| maybe_auth_header_for_npm_registry(c).ok()?); - let result = tarball_cache.env + let result = tarball_cache.http_client .download_with_retries_on_any_tokio_runtime(tarball_uri, maybe_auth_header) .await; let maybe_bytes = match result { @@ -166,7 +214,7 @@ impl TarballCache { && maybe_registry_config.is_none() && tarball_cache.npmrc.get_registry_config(&package_nv.name).auth_token.is_some() { - bail!( + return Err(JsErrorBox::generic(format!( concat!( "No auth for tarball URI, but present for scoped registry.\n\n", "Tarball URI: {}\n", @@ -175,9 +223,9 @@ impl TarballCache { ), dist.tarball, registry_url, - ) + ))); } - return Err(err.into()) + return Err(JsErrorBox::from_err(err)) }, }; match maybe_bytes { @@ -206,10 +254,10 @@ impl TarballCache { extraction_mode, ) }) - .await? + .await.map_err(JsErrorBox::from_err)?.map_err(JsErrorBox::from_err) } None => { - bail!("Could not find npm package tarball at: {}", dist.tarball); + Err(JsErrorBox::generic(format!("Could not find npm package tarball at: {}", dist.tarball))) } } } diff --git a/resolvers/npm_cache/tarball_extract.rs b/resolvers/npm_cache/tarball_extract.rs index c4c614b35f..e53e4544d2 100644 --- a/resolvers/npm_cache/tarball_extract.rs +++ b/resolvers/npm_cache/tarball_extract.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashSet; @@ -7,9 +7,6 @@ use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; -use anyhow::bail; -use anyhow::Context; -use anyhow::Error as AnyError; use base64::prelude::BASE64_STANDARD; use base64::Engine; use deno_npm::registry::NpmPackageVersionDistInfo; @@ -31,23 +28,37 @@ pub enum TarballExtractionMode { SiblingTempDir, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum VerifyAndExtractTarballError { + #[class(inherit)] + #[error(transparent)] + TarballIntegrity(#[from] TarballIntegrityError), + #[class(inherit)] + #[error(transparent)] + ExtractTarball(#[from] ExtractTarballError), + #[class(inherit)] + #[error("Failed moving extracted tarball to final destination")] + MoveFailed(std::io::Error), +} + pub fn verify_and_extract_tarball( package_nv: &PackageNv, data: &[u8], dist_info: &NpmPackageVersionDistInfo, output_folder: &Path, extraction_mode: TarballExtractionMode, -) -> Result<(), AnyError> { +) -> Result<(), VerifyAndExtractTarballError> { verify_tarball_integrity(package_nv, data, &dist_info.integrity())?; match extraction_mode { - TarballExtractionMode::Overwrite => extract_tarball(data, output_folder), + TarballExtractionMode::Overwrite => { + extract_tarball(data, output_folder).map_err(Into::into) + } TarballExtractionMode::SiblingTempDir => { let temp_dir = get_atomic_dir_path(output_folder); extract_tarball(data, &temp_dir)?; rename_with_retries(&temp_dir, output_folder) - .map_err(AnyError::from) - .context("Failed moving extracted tarball to final destination.") + .map_err(VerifyAndExtractTarballError::MoveFailed) } } } @@ -89,11 +100,32 @@ fn rename_with_retries( } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +pub enum TarballIntegrityError { + #[error("Not implemented hash function for {package}: {hash_kind}")] + NotImplementedHashFunction { + package: Box, + hash_kind: String, + }, + #[error("Not implemented integrity kind for {package}: {integrity}")] + NotImplementedIntegrityKind { + package: Box, + integrity: String, + }, + #[error("Tarball checksum did not match what was provided by npm registry for {package}.\n\nExpected: {expected}\nActual: {actual}")] + MismatchedChecksum { + package: Box, + expected: String, + actual: String, + }, +} + fn verify_tarball_integrity( package: &PackageNv, data: &[u8], npm_integrity: &NpmPackageVersionDistInfoIntegrity, -) -> Result<(), AnyError> { +) -> Result<(), TarballIntegrityError> { use ring::digest::Context; let (tarball_checksum, expected_checksum) = match npm_integrity { NpmPackageVersionDistInfoIntegrity::Integrity { @@ -103,11 +135,12 @@ fn verify_tarball_integrity( let algo = match *algorithm { "sha512" => &ring::digest::SHA512, "sha1" => &ring::digest::SHA1_FOR_LEGACY_USE_ONLY, - hash_kind => bail!( - "Not implemented hash function for {}: {}", - package, - hash_kind - ), + hash_kind => { + return Err(TarballIntegrityError::NotImplementedHashFunction { + package: Box::new(package.clone()), + hash_kind: hash_kind.to_string(), + }); + } }; let mut hash_ctx = Context::new(algo); hash_ctx.update(data); @@ -123,26 +156,39 @@ fn verify_tarball_integrity( (tarball_checksum, hex) } NpmPackageVersionDistInfoIntegrity::UnknownIntegrity(integrity) => { - bail!( - "Not implemented integrity kind for {}: {}", - package, - integrity - ) + return Err(TarballIntegrityError::NotImplementedIntegrityKind { + package: Box::new(package.clone()), + integrity: integrity.to_string(), + }); } }; if tarball_checksum != *expected_checksum { - bail!( - "Tarball checksum did not match what was provided by npm registry for {}.\n\nExpected: {}\nActual: {}", - package, - expected_checksum, - tarball_checksum, - ) + return Err(TarballIntegrityError::MismatchedChecksum { + package: Box::new(package.clone()), + expected: expected_checksum.to_string(), + actual: tarball_checksum, + }); } Ok(()) } -fn extract_tarball(data: &[u8], output_folder: &Path) -> Result<(), AnyError> { +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ExtractTarballError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(generic)] + #[error( + "Extracted directory '{0}' of npm tarball was not in output directory." + )] + NotInOutputDirectory(PathBuf), +} + +fn extract_tarball( + data: &[u8], + output_folder: &Path, +) -> Result<(), ExtractTarballError> { fs::create_dir_all(output_folder)?; let output_folder = fs::canonicalize(output_folder)?; let tar = GzDecoder::new(data); @@ -174,10 +220,9 @@ fn extract_tarball(data: &[u8], output_folder: &Path) -> Result<(), AnyError> { fs::create_dir_all(dir_path)?; let canonicalized_dir = fs::canonicalize(dir_path)?; if !canonicalized_dir.starts_with(&output_folder) { - bail!( - "Extracted directory '{}' of npm tarball was not in output directory.", - canonicalized_dir.display() - ) + return Err(ExtractTarballError::NotInOutputDirectory( + canonicalized_dir.to_path_buf(), + )); } } @@ -236,7 +281,7 @@ mod test { #[test] pub fn test_verify_tarball() { let package = PackageNv { - name: "package".to_string(), + name: "package".into(), version: Version::parse_from_npm("1.0.0").unwrap(), }; let actual_checksum = diff --git a/resolvers/npm_cache/todo.md b/resolvers/npm_cache/todo.md index e10b1cfd89..e460fcae86 100644 --- a/resolvers/npm_cache/todo.md +++ b/resolvers/npm_cache/todo.md @@ -1,7 +1,5 @@ This crate is a work in progress: -1. Remove `deno_core` dependency. -1. Remove `anyhow` dependency. 1. Add a clippy.toml file that bans accessing the file system directory and instead does it through a trait. 1. Make this crate work in Wasm. diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index cb12abb141..808f79ab7b 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_runtime" -version = "0.191.0" +version = "0.193.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -49,6 +49,7 @@ deno_core.workspace = true deno_cron.workspace = true deno_crypto.workspace = true deno_fetch.workspace = true +deno_os.workspace = true deno_ffi.workspace = true deno_fs = { workspace = true, features = ["sync_fs"] } deno_http.workspace = true @@ -79,6 +80,7 @@ deno_console.workspace = true deno_core.workspace = true deno_cron.workspace = true deno_crypto.workspace = true +deno_error.workspace = true deno_fetch.workspace = true deno_ffi.workspace = true deno_fs = { workspace = true, features = ["sync_fs"] } @@ -88,8 +90,10 @@ deno_kv.workspace = true deno_napi.workspace = true deno_net.workspace = true deno_node.workspace = true +deno_os.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true +deno_resolver.workspace = true deno_telemetry.workspace = true deno_terminal.workspace = true deno_tls.workspace = true @@ -112,7 +116,6 @@ hyper-util.workspace = true hyper_v014 = { workspace = true, features = ["server", "stream", "http1", "http2", "runtime"] } libc.workspace = true log.workspace = true -netif = "0.1.6" notify.workspace = true once_cell.workspace = true percent-encoding.workspace = true @@ -120,8 +123,7 @@ regex.workspace = true rustyline = { workspace = true, features = ["custom-bindings"] } same-file = "1.0.6" serde.workspace = true -signal-hook = "0.3.17" -signal-hook-registry = "1.4.0" +sys_traits.workspace = true tempfile.workspace = true thiserror.workspace = true tokio.workspace = true diff --git a/runtime/code_cache.rs b/runtime/code_cache.rs index b4a7ce188f..b7ee8a9ed6 100644 --- a/runtime/code_cache.rs +++ b/runtime/code_cache.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::ModuleSpecifier; diff --git a/runtime/errors.rs b/runtime/errors.rs deleted file mode 100644 index 3f8e900851..0000000000 --- a/runtime/errors.rs +++ /dev/null @@ -1,1957 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -//! There are many types of errors in Deno: -//! - AnyError: a generic wrapper that can encapsulate any type of error. -//! - JsError: a container for the error message and stack trace for exceptions -//! thrown in JavaScript code. We use this to pretty-print stack traces. -//! - Diagnostic: these are errors that originate in TypeScript's compiler. -//! They're similar to JsError, in that they have line numbers. But -//! Diagnostics are compile-time type errors, whereas JsErrors are runtime -//! exceptions. - -use crate::ops::fs_events::FsEventsError; -use crate::ops::http::HttpStartError; -use crate::ops::os::OsError; -use crate::ops::permissions::PermissionError; -use crate::ops::process::CheckRunPermissionError; -use crate::ops::process::ProcessError; -use crate::ops::signal::SignalError; -use crate::ops::tty::TtyError; -use crate::ops::web_worker::SyncFetchError; -use crate::ops::worker_host::CreateWorkerError; -use deno_broadcast_channel::BroadcastChannelError; -use deno_cache::CacheError; -use deno_canvas::CanvasError; -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::url; -use deno_core::ModuleResolutionError; -use deno_cron::CronError; -use deno_crypto::DecryptError; -use deno_crypto::EncryptError; -use deno_crypto::ExportKeyError; -use deno_crypto::GenerateKeyError; -use deno_crypto::ImportKeyError; -use deno_fetch::FetchError; -use deno_fetch::HttpClientCreateError; -use deno_ffi::CallError; -use deno_ffi::CallbackError; -use deno_ffi::DlfcnError; -use deno_ffi::IRError; -use deno_ffi::ReprError; -use deno_ffi::StaticError; -use deno_fs::FsOpsError; -use deno_fs::FsOpsErrorKind; -use deno_http::HttpError; -use deno_http::HttpNextError; -use deno_http::WebSocketUpgradeError; -use deno_io::fs::FsError; -use deno_kv::KvCheckError; -use deno_kv::KvError; -use deno_kv::KvErrorKind; -use deno_kv::KvMutationError; -use deno_napi::NApiError; -use deno_net::ops::NetError; -use deno_permissions::ChildPermissionError; -use deno_permissions::NetDescriptorFromUrlParseError; -use deno_permissions::PathResolveError; -use deno_permissions::PermissionCheckError; -use deno_permissions::RunDescriptorParseError; -use deno_permissions::SysDescriptorParseError; -use deno_tls::TlsError; -use deno_web::BlobError; -use deno_web::CompressionError; -use deno_web::MessagePortError; -use deno_web::StreamResourceError; -use deno_web::WebError; -use deno_websocket::HandshakeError; -use deno_websocket::WebsocketError; -use deno_webstorage::WebStorageError; -use rustyline::error::ReadlineError; -use std::env; -use std::error::Error; -use std::io; -use std::sync::Arc; - -fn get_run_descriptor_parse_error(e: &RunDescriptorParseError) -> &'static str { - match e { - RunDescriptorParseError::Which(_) => "Error", - RunDescriptorParseError::PathResolve(e) => get_path_resolve_error(e), - RunDescriptorParseError::EmptyRunQuery => "Error", - } -} - -fn get_sys_descriptor_parse_error(e: &SysDescriptorParseError) -> &'static str { - match e { - SysDescriptorParseError::InvalidKind(_) => "TypeError", - SysDescriptorParseError::Empty => "Error", - } -} - -fn get_path_resolve_error(e: &PathResolveError) -> &'static str { - match e { - PathResolveError::CwdResolve(e) => get_io_error_class(e), - PathResolveError::EmptyPath => "Error", - } -} - -fn get_permission_error_class(e: &PermissionError) -> &'static str { - match e { - PermissionError::InvalidPermissionName(_) => "ReferenceError", - PermissionError::PathResolve(e) => get_path_resolve_error(e), - PermissionError::NetDescriptorParse(_) => "URIError", - PermissionError::SysDescriptorParse(e) => get_sys_descriptor_parse_error(e), - PermissionError::RunDescriptorParse(e) => get_run_descriptor_parse_error(e), - } -} - -fn get_permission_check_error_class(e: &PermissionCheckError) -> &'static str { - match e { - PermissionCheckError::PermissionDenied(_) => "NotCapable", - PermissionCheckError::InvalidFilePath(_) => "URIError", - PermissionCheckError::NetDescriptorForUrlParse(e) => match e { - NetDescriptorFromUrlParseError::MissingHost(_) => "TypeError", - NetDescriptorFromUrlParseError::Host(_) => "URIError", - }, - PermissionCheckError::SysDescriptorParse(e) => { - get_sys_descriptor_parse_error(e) - } - PermissionCheckError::PathResolve(e) => get_path_resolve_error(e), - PermissionCheckError::HostParse(_) => "URIError", - } -} - -fn get_dlopen_error_class(error: &dlopen2::Error) -> &'static str { - use dlopen2::Error::*; - match error { - NullCharacter(_) => "InvalidData", - OpeningLibraryError(ref e) => get_io_error_class(e), - SymbolGettingError(ref e) => get_io_error_class(e), - AddrNotMatchingDll(ref e) => get_io_error_class(e), - NullSymbol => "NotFound", - } -} - -fn get_env_var_error_class(error: &env::VarError) -> &'static str { - use env::VarError::*; - match error { - NotPresent => "NotFound", - NotUnicode(..) => "InvalidData", - } -} - -fn get_io_error_class(error: &io::Error) -> &'static str { - use io::ErrorKind::*; - match error.kind() { - NotFound => "NotFound", - PermissionDenied => "PermissionDenied", - ConnectionRefused => "ConnectionRefused", - ConnectionReset => "ConnectionReset", - ConnectionAborted => "ConnectionAborted", - NotConnected => "NotConnected", - AddrInUse => "AddrInUse", - AddrNotAvailable => "AddrNotAvailable", - BrokenPipe => "BrokenPipe", - AlreadyExists => "AlreadyExists", - InvalidInput => "TypeError", - InvalidData => "InvalidData", - TimedOut => "TimedOut", - Interrupted => "Interrupted", - WriteZero => "WriteZero", - UnexpectedEof => "UnexpectedEof", - Other => "Error", - WouldBlock => "WouldBlock", - // Non-exhaustive enum - might add new variants - // in the future - kind => { - let kind_str = kind.to_string(); - match kind_str.as_str() { - "FilesystemLoop" => "FilesystemLoop", - "IsADirectory" => "IsADirectory", - "NetworkUnreachable" => "NetworkUnreachable", - "NotADirectory" => "NotADirectory", - _ => "Error", - } - } - } -} - -fn get_module_resolution_error_class( - _: &ModuleResolutionError, -) -> &'static str { - "URIError" -} - -fn get_notify_error_class(error: ¬ify::Error) -> &'static str { - use notify::ErrorKind::*; - match error.kind { - Generic(_) => "Error", - Io(ref e) => get_io_error_class(e), - PathNotFound => "NotFound", - WatchNotFound => "NotFound", - InvalidConfig(_) => "InvalidData", - MaxFilesWatch => "Error", - } -} - -fn get_regex_error_class(error: ®ex::Error) -> &'static str { - use regex::Error::*; - match error { - Syntax(_) => "SyntaxError", - CompiledTooBig(_) => "RangeError", - _ => "Error", - } -} - -fn get_serde_json_error_class( - error: &serde_json::error::Error, -) -> &'static str { - use deno_core::serde_json::error::*; - match error.classify() { - Category::Io => error - .source() - .and_then(|e| e.downcast_ref::()) - .map(get_io_error_class) - .unwrap(), - Category::Syntax => "SyntaxError", - Category::Data => "InvalidData", - Category::Eof => "UnexpectedEof", - } -} - -fn get_url_parse_error_class(_error: &url::ParseError) -> &'static str { - "URIError" -} - -fn get_hyper_error_class(_error: &hyper::Error) -> &'static str { - "Http" -} - -fn get_hyper_util_error_class( - _error: &hyper_util::client::legacy::Error, -) -> &'static str { - "Http" -} - -fn get_hyper_v014_error_class(_error: &hyper_v014::Error) -> &'static str { - "Http" -} - -#[cfg(unix)] -pub fn get_nix_error_class(error: &nix::Error) -> &'static str { - match error { - nix::Error::ECHILD => "NotFound", - nix::Error::EINVAL => "TypeError", - nix::Error::ENOENT => "NotFound", - nix::Error::ENOTTY => "BadResource", - nix::Error::EPERM => "PermissionDenied", - nix::Error::ESRCH => "NotFound", - nix::Error::ELOOP => "FilesystemLoop", - nix::Error::ENOTDIR => "NotADirectory", - nix::Error::ENETUNREACH => "NetworkUnreachable", - nix::Error::EISDIR => "IsADirectory", - nix::Error::UnknownErrno => "Error", - &nix::Error::ENOTSUP => unreachable!(), - _ => "Error", - } -} - -fn get_webgpu_error_class(e: &deno_webgpu::InitError) -> &'static str { - match e { - deno_webgpu::InitError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::InitError::InvalidAdapter(_) => "Error", - deno_webgpu::InitError::RequestDevice(_) => "DOMExceptionOperationError", - deno_webgpu::InitError::InvalidDevice(_) => "Error", - } -} - -fn get_webgpu_buffer_error_class( - e: &deno_webgpu::buffer::BufferError, -) -> &'static str { - match e { - deno_webgpu::buffer::BufferError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::buffer::BufferError::InvalidUsage => "TypeError", - deno_webgpu::buffer::BufferError::Access(_) => "DOMExceptionOperationError", - } -} - -fn get_webgpu_bundle_error_class( - e: &deno_webgpu::bundle::BundleError, -) -> &'static str { - match e { - deno_webgpu::bundle::BundleError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::bundle::BundleError::InvalidSize => "TypeError", - } -} - -fn get_webgpu_byow_error_class( - e: &deno_webgpu::byow::ByowError, -) -> &'static str { - match e { - deno_webgpu::byow::ByowError::WebGPUNotInitiated => "TypeError", - deno_webgpu::byow::ByowError::InvalidParameters => "TypeError", - deno_webgpu::byow::ByowError::CreateSurface(_) => "Error", - deno_webgpu::byow::ByowError::InvalidSystem => "TypeError", - #[cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "freebsd", - target_os = "openbsd" - ))] - deno_webgpu::byow::ByowError::NullWindow => "TypeError", - #[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "openbsd" - ))] - deno_webgpu::byow::ByowError::NullDisplay => "TypeError", - #[cfg(target_os = "macos")] - deno_webgpu::byow::ByowError::NSViewDisplay => "TypeError", - } -} - -fn get_webgpu_render_pass_error_class( - e: &deno_webgpu::render_pass::RenderPassError, -) -> &'static str { - match e { - deno_webgpu::render_pass::RenderPassError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::render_pass::RenderPassError::InvalidSize => "TypeError", - } -} - -fn get_webgpu_surface_error_class( - e: &deno_webgpu::surface::SurfaceError, -) -> &'static str { - match e { - deno_webgpu::surface::SurfaceError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::surface::SurfaceError::Surface(_) => "Error", - deno_webgpu::surface::SurfaceError::InvalidStatus => "Error", - } -} - -fn get_crypto_decrypt_error_class(e: &DecryptError) -> &'static str { - match e { - DecryptError::General(e) => get_crypto_shared_error_class(e), - DecryptError::Pkcs1(_) => "Error", - DecryptError::Failed => "DOMExceptionOperationError", - DecryptError::InvalidLength => "TypeError", - DecryptError::InvalidCounterLength => "TypeError", - DecryptError::InvalidTagLength => "TypeError", - DecryptError::InvalidKeyOrIv => "DOMExceptionOperationError", - DecryptError::TooMuchData => "DOMExceptionOperationError", - DecryptError::InvalidIvLength => "TypeError", - DecryptError::Rsa(_) => "DOMExceptionOperationError", - } -} - -fn get_crypto_encrypt_error_class(e: &EncryptError) -> &'static str { - match e { - EncryptError::General(e) => get_crypto_shared_error_class(e), - EncryptError::InvalidKeyOrIv => "DOMExceptionOperationError", - EncryptError::Failed => "DOMExceptionOperationError", - EncryptError::InvalidLength => "TypeError", - EncryptError::InvalidIvLength => "TypeError", - EncryptError::InvalidCounterLength => "TypeError", - EncryptError::TooMuchData => "DOMExceptionOperationError", - } -} - -fn get_crypto_shared_error_class(e: &deno_crypto::SharedError) -> &'static str { - match e { - deno_crypto::SharedError::ExpectedValidPrivateKey => "TypeError", - deno_crypto::SharedError::ExpectedValidPublicKey => "TypeError", - deno_crypto::SharedError::ExpectedValidPrivateECKey => "TypeError", - deno_crypto::SharedError::ExpectedValidPublicECKey => "TypeError", - deno_crypto::SharedError::ExpectedPrivateKey => "TypeError", - deno_crypto::SharedError::ExpectedPublicKey => "TypeError", - deno_crypto::SharedError::ExpectedSecretKey => "TypeError", - deno_crypto::SharedError::FailedDecodePrivateKey => { - "DOMExceptionOperationError" - } - deno_crypto::SharedError::FailedDecodePublicKey => { - "DOMExceptionOperationError" - } - deno_crypto::SharedError::UnsupportedFormat => { - "DOMExceptionNotSupportedError" - } - } -} - -fn get_crypto_ed25519_error_class( - e: &deno_crypto::Ed25519Error, -) -> &'static str { - match e { - deno_crypto::Ed25519Error::FailedExport => "DOMExceptionOperationError", - deno_crypto::Ed25519Error::Der(_) => "Error", - deno_crypto::Ed25519Error::KeyRejected(_) => "Error", - } -} - -fn get_crypto_export_key_error_class(e: &ExportKeyError) -> &'static str { - match e { - ExportKeyError::General(e) => get_crypto_shared_error_class(e), - ExportKeyError::Der(_) => "Error", - ExportKeyError::UnsupportedNamedCurve => "DOMExceptionNotSupportedError", - } -} - -fn get_crypto_generate_key_error_class(e: &GenerateKeyError) -> &'static str { - match e { - GenerateKeyError::General(e) => get_crypto_shared_error_class(e), - GenerateKeyError::BadPublicExponent => "DOMExceptionOperationError", - GenerateKeyError::InvalidHMACKeyLength => "DOMExceptionOperationError", - GenerateKeyError::FailedRSAKeySerialization => "DOMExceptionOperationError", - GenerateKeyError::InvalidAESKeyLength => "DOMExceptionOperationError", - GenerateKeyError::FailedRSAKeyGeneration => "DOMExceptionOperationError", - GenerateKeyError::FailedECKeyGeneration => "DOMExceptionOperationError", - GenerateKeyError::FailedKeyGeneration => "DOMExceptionOperationError", - } -} - -fn get_crypto_import_key_error_class(e: &ImportKeyError) -> &'static str { - match e { - ImportKeyError::General(e) => get_crypto_shared_error_class(e), - ImportKeyError::InvalidModulus => "DOMExceptionDataError", - ImportKeyError::InvalidPublicExponent => "DOMExceptionDataError", - ImportKeyError::InvalidPrivateExponent => "DOMExceptionDataError", - ImportKeyError::InvalidFirstPrimeFactor => "DOMExceptionDataError", - ImportKeyError::InvalidSecondPrimeFactor => "DOMExceptionDataError", - ImportKeyError::InvalidFirstCRTExponent => "DOMExceptionDataError", - ImportKeyError::InvalidSecondCRTExponent => "DOMExceptionDataError", - ImportKeyError::InvalidCRTCoefficient => "DOMExceptionDataError", - ImportKeyError::InvalidB64Coordinate => "DOMExceptionDataError", - ImportKeyError::InvalidRSAPublicKey => "DOMExceptionDataError", - ImportKeyError::InvalidRSAPrivateKey => "DOMExceptionDataError", - ImportKeyError::UnsupportedAlgorithm => "DOMExceptionDataError", - ImportKeyError::PublicKeyTooLong => "DOMExceptionDataError", - ImportKeyError::PrivateKeyTooLong => "DOMExceptionDataError", - ImportKeyError::InvalidP256ECPoint => "DOMExceptionDataError", - ImportKeyError::InvalidP384ECPoint => "DOMExceptionDataError", - ImportKeyError::InvalidP521ECPoint => "DOMExceptionDataError", - ImportKeyError::UnsupportedNamedCurve => "DOMExceptionDataError", - ImportKeyError::CurveMismatch => "DOMExceptionDataError", - ImportKeyError::InvalidKeyData => "DOMExceptionDataError", - ImportKeyError::InvalidJWKPrivateKey => "DOMExceptionDataError", - ImportKeyError::EllipticCurve(_) => "DOMExceptionDataError", - ImportKeyError::ExpectedValidPkcs8Data => "DOMExceptionDataError", - ImportKeyError::MalformedParameters => "DOMExceptionDataError", - ImportKeyError::Spki(_) => "DOMExceptionDataError", - ImportKeyError::InvalidP256ECSPKIData => "DOMExceptionDataError", - ImportKeyError::InvalidP384ECSPKIData => "DOMExceptionDataError", - ImportKeyError::InvalidP521ECSPKIData => "DOMExceptionDataError", - ImportKeyError::Der(_) => "DOMExceptionDataError", - } -} - -fn get_crypto_x448_error_class(e: &deno_crypto::X448Error) -> &'static str { - match e { - deno_crypto::X448Error::FailedExport => "DOMExceptionOperationError", - deno_crypto::X448Error::Der(_) => "Error", - } -} - -fn get_crypto_x25519_error_class(e: &deno_crypto::X25519Error) -> &'static str { - match e { - deno_crypto::X25519Error::FailedExport => "DOMExceptionOperationError", - deno_crypto::X25519Error::Der(_) => "Error", - } -} - -fn get_crypto_error_class(e: &deno_crypto::Error) -> &'static str { - match e { - deno_crypto::Error::Der(_) => "Error", - deno_crypto::Error::JoinError(_) => "Error", - deno_crypto::Error::MissingArgumentHash => "TypeError", - deno_crypto::Error::MissingArgumentSaltLength => "TypeError", - deno_crypto::Error::Other(e) => get_error_class_name(e).unwrap_or("Error"), - deno_crypto::Error::UnsupportedAlgorithm => "TypeError", - deno_crypto::Error::KeyRejected(_) => "Error", - deno_crypto::Error::RSA(_) => "Error", - deno_crypto::Error::Pkcs1(_) => "Error", - deno_crypto::Error::Unspecified(_) => "Error", - deno_crypto::Error::InvalidKeyFormat => "TypeError", - deno_crypto::Error::MissingArgumentPublicKey => "TypeError", - deno_crypto::Error::P256Ecdsa(_) => "Error", - deno_crypto::Error::DecodePrivateKey => "TypeError", - deno_crypto::Error::MissingArgumentNamedCurve => "TypeError", - deno_crypto::Error::MissingArgumentInfo => "TypeError", - deno_crypto::Error::HKDFLengthTooLarge => "DOMExceptionOperationError", - deno_crypto::Error::General(e) => get_crypto_shared_error_class(e), - deno_crypto::Error::Base64Decode(_) => "Error", - deno_crypto::Error::DataInvalidSize => "TypeError", - deno_crypto::Error::InvalidKeyLength => "TypeError", - deno_crypto::Error::EncryptionError => "DOMExceptionOperationError", - deno_crypto::Error::DecryptionError => "DOMExceptionOperationError", - deno_crypto::Error::ArrayBufferViewLengthExceeded(_) => { - "DOMExceptionQuotaExceededError" - } - } -} - -fn get_napi_error_class(e: &NApiError) -> &'static str { - match e { - NApiError::InvalidPath - | NApiError::LibLoading(_) - | NApiError::ModuleNotFound(_) => "TypeError", - NApiError::Permission(e) => get_permission_check_error_class(e), - } -} - -fn get_web_error_class(e: &WebError) -> &'static str { - match e { - WebError::Base64Decode => "DOMExceptionInvalidCharacterError", - WebError::InvalidEncodingLabel(_) => "RangeError", - WebError::BufferTooLong => "TypeError", - WebError::ValueTooLarge => "RangeError", - WebError::BufferTooSmall => "RangeError", - WebError::DataInvalid => "TypeError", - WebError::DataError(_) => "Error", - } -} - -fn get_web_compression_error_class(e: &CompressionError) -> &'static str { - match e { - CompressionError::UnsupportedFormat => "TypeError", - CompressionError::ResourceClosed => "TypeError", - CompressionError::IoTypeError(_) => "TypeError", - CompressionError::Io(e) => get_io_error_class(e), - } -} - -fn get_web_message_port_error_class(e: &MessagePortError) -> &'static str { - match e { - MessagePortError::InvalidTransfer => "TypeError", - MessagePortError::NotReady => "TypeError", - MessagePortError::TransferSelf => "TypeError", - MessagePortError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - MessagePortError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_web_stream_resource_error_class( - e: &StreamResourceError, -) -> &'static str { - match e { - StreamResourceError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - StreamResourceError::Js(_) => "TypeError", - } -} - -fn get_web_blob_error_class(e: &BlobError) -> &'static str { - match e { - BlobError::BlobPartNotFound => "TypeError", - BlobError::SizeLargerThanBlobPart => "TypeError", - BlobError::BlobURLsNotSupported => "TypeError", - BlobError::Url(_) => "Error", - } -} - -fn get_ffi_repr_error_class(e: &ReprError) -> &'static str { - match e { - ReprError::InvalidOffset => "TypeError", - ReprError::InvalidArrayBuffer => "TypeError", - ReprError::DestinationLengthTooShort => "RangeError", - ReprError::InvalidCString => "TypeError", - ReprError::CStringTooLong => "TypeError", - ReprError::InvalidBool => "TypeError", - ReprError::InvalidU8 => "TypeError", - ReprError::InvalidI8 => "TypeError", - ReprError::InvalidU16 => "TypeError", - ReprError::InvalidI16 => "TypeError", - ReprError::InvalidU32 => "TypeError", - ReprError::InvalidI32 => "TypeError", - ReprError::InvalidU64 => "TypeError", - ReprError::InvalidI64 => "TypeError", - ReprError::InvalidF32 => "TypeError", - ReprError::InvalidF64 => "TypeError", - ReprError::InvalidPointer => "TypeError", - ReprError::Permission(e) => get_permission_check_error_class(e), - } -} - -fn get_ffi_dlfcn_error_class(e: &DlfcnError) -> &'static str { - match e { - DlfcnError::RegisterSymbol { .. } => "Error", - DlfcnError::Dlopen(_) => "Error", - DlfcnError::Permission(e) => get_permission_check_error_class(e), - DlfcnError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_ffi_static_error_class(e: &StaticError) -> &'static str { - match e { - StaticError::Dlfcn(e) => get_ffi_dlfcn_error_class(e), - StaticError::InvalidTypeVoid => "TypeError", - StaticError::InvalidTypeStruct => "TypeError", - StaticError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_ffi_callback_error_class(e: &CallbackError) -> &'static str { - match e { - CallbackError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - CallbackError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - CallbackError::Permission(e) => get_permission_check_error_class(e), - } -} - -fn get_ffi_call_error_class(e: &CallError) -> &'static str { - match e { - CallError::IR(_) => "TypeError", - CallError::NonblockingCallFailure(_) => "Error", - CallError::InvalidSymbol(_) => "TypeError", - CallError::Permission(e) => get_permission_check_error_class(e), - CallError::Callback(e) => get_ffi_callback_error_class(e), - CallError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_webstorage_class_name(e: &WebStorageError) -> &'static str { - match e { - WebStorageError::ContextNotSupported => "DOMExceptionNotSupportedError", - WebStorageError::Sqlite(_) => "Error", - WebStorageError::Io(e) => get_io_error_class(e), - WebStorageError::StorageExceeded => "DOMExceptionQuotaExceededError", - } -} - -fn get_tls_error_class(e: &TlsError) -> &'static str { - match e { - TlsError::Rustls(_) => "Error", - TlsError::UnableAddPemFileToCert(e) => get_io_error_class(e), - TlsError::CertInvalid - | TlsError::CertsNotFound - | TlsError::KeysNotFound - | TlsError::KeyDecode => "InvalidData", - } -} - -pub fn get_cron_error_class(e: &CronError) -> &'static str { - match e { - CronError::Resource(e) => { - deno_core::error::get_custom_error_class(e).unwrap_or("Error") - } - CronError::NameExceeded(_) => "TypeError", - CronError::NameInvalid => "TypeError", - CronError::AlreadyExists => "TypeError", - CronError::TooManyCrons => "TypeError", - CronError::InvalidCron => "TypeError", - CronError::InvalidBackoff => "TypeError", - CronError::AcquireError(_) => "Error", - CronError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_canvas_error(e: &CanvasError) -> &'static str { - match e { - CanvasError::UnsupportedColorType(_) => "TypeError", - CanvasError::Image(_) => "Error", - } -} - -pub fn get_cache_error(error: &CacheError) -> &'static str { - match error { - CacheError::Sqlite(_) => "Error", - CacheError::JoinError(_) => "Error", - CacheError::Resource(err) => { - deno_core::error::get_custom_error_class(err).unwrap_or("Error") - } - CacheError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - CacheError::Io(err) => get_io_error_class(err), - } -} - -fn get_broadcast_channel_error(error: &BroadcastChannelError) -> &'static str { - match error { - BroadcastChannelError::Resource(err) => { - deno_core::error::get_custom_error_class(err).unwrap() - } - BroadcastChannelError::MPSCSendError(_) => "Error", - BroadcastChannelError::BroadcastSendError(_) => "Error", - BroadcastChannelError::Other(err) => { - get_error_class_name(err).unwrap_or("Error") - } - } -} - -fn get_fetch_error(error: &FetchError) -> &'static str { - match error { - FetchError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - FetchError::Permission(e) => get_permission_check_error_class(e), - FetchError::NetworkError => "TypeError", - FetchError::FsNotGet(_) => "TypeError", - FetchError::PathToUrl(_) => "TypeError", - FetchError::InvalidUrl(_) => "TypeError", - FetchError::InvalidHeaderName(_) => "TypeError", - FetchError::InvalidHeaderValue(_) => "TypeError", - FetchError::DataUrl(_) => "TypeError", - FetchError::Base64(_) => "TypeError", - FetchError::BlobNotFound => "TypeError", - FetchError::SchemeNotSupported(_) => "TypeError", - FetchError::RequestCanceled => "TypeError", - FetchError::Http(_) => "Error", - FetchError::ClientCreate(e) => get_http_client_create_error(e), - FetchError::Url(e) => get_url_parse_error_class(e), - FetchError::Method(_) => "TypeError", - FetchError::ClientSend(_) => "TypeError", - FetchError::RequestBuilderHook(_) => "TypeError", - FetchError::Io(e) => get_io_error_class(e), - } -} - -fn get_http_client_create_error(error: &HttpClientCreateError) -> &'static str { - match error { - HttpClientCreateError::Tls(_) => "TypeError", - HttpClientCreateError::InvalidUserAgent(_) => "TypeError", - HttpClientCreateError::InvalidProxyUrl => "TypeError", - HttpClientCreateError::HttpVersionSelectionInvalid => "TypeError", - HttpClientCreateError::RootCertStore(_) => "TypeError", - } -} - -fn get_websocket_error(error: &WebsocketError) -> &'static str { - match error { - WebsocketError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - WebsocketError::Permission(e) => get_permission_check_error_class(e), - WebsocketError::Url(e) => get_url_parse_error_class(e), - WebsocketError::Io(e) => get_io_error_class(e), - WebsocketError::WebSocket(_) => "TypeError", - WebsocketError::ConnectionFailed(_) => "DOMExceptionNetworkError", - WebsocketError::Uri(_) => "Error", - WebsocketError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - } -} - -fn get_websocket_handshake_error(error: &HandshakeError) -> &'static str { - match error { - HandshakeError::RootStoreError(e) => { - get_error_class_name(e).unwrap_or("Error") - } - HandshakeError::Tls(e) => get_tls_error_class(e), - HandshakeError::MissingPath => "TypeError", - HandshakeError::Http(_) => "Error", - HandshakeError::InvalidHostname(_) => "TypeError", - HandshakeError::Io(e) => get_io_error_class(e), - HandshakeError::Rustls(_) => "Error", - HandshakeError::H2(_) => "Error", - HandshakeError::NoH2Alpn => "Error", - HandshakeError::InvalidStatusCode(_) => "Error", - HandshakeError::WebSocket(_) => "TypeError", - HandshakeError::HeaderName(_) => "TypeError", - HandshakeError::HeaderValue(_) => "TypeError", - } -} - -fn get_fs_ops_error(error: &FsOpsError) -> &'static str { - use FsOpsErrorKind::*; - match error.as_kind() { - Io(e) => get_io_error_class(e), - OperationError(e) => get_fs_error(&e.err), - Permission(e) => get_permission_check_error_class(e), - Resource(e) | Other(e) => get_error_class_name(e).unwrap_or("Error"), - InvalidUtf8(_) => "InvalidData", - StripPrefix(_) => "Error", - Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - InvalidSeekMode(_) => "TypeError", - InvalidControlCharacter(_) => "Error", - InvalidCharacter(_) => "Error", - #[cfg(windows)] - InvalidTrailingCharacter => "Error", - NotCapableAccess { .. } => "NotCapable", - NotCapable(_) => "NotCapable", - } -} - -fn get_kv_error(error: &KvError) -> &'static str { - use KvErrorKind::*; - match error.as_kind() { - DatabaseHandler(e) | Resource(e) | Kv(e) => { - get_error_class_name(e).unwrap_or("Error") - } - TooManyRanges(_) => "TypeError", - TooManyEntries(_) => "TypeError", - TooManyChecks(_) => "TypeError", - TooManyMutations(_) => "TypeError", - TooManyKeys(_) => "TypeError", - InvalidLimit => "TypeError", - InvalidBoundaryKey => "TypeError", - KeyTooLargeToRead(_) => "TypeError", - KeyTooLargeToWrite(_) => "TypeError", - TotalMutationTooLarge(_) => "TypeError", - TotalKeyTooLarge(_) => "TypeError", - Io(e) => get_io_error_class(e), - QueueMessageNotFound => "TypeError", - StartKeyNotInKeyspace => "TypeError", - EndKeyNotInKeyspace => "TypeError", - StartKeyGreaterThanEndKey => "TypeError", - InvalidCheck(e) => match e { - KvCheckError::InvalidVersionstamp => "TypeError", - KvCheckError::Io(e) => get_io_error_class(e), - }, - InvalidMutation(e) => match e { - KvMutationError::BigInt(_) => "Error", - KvMutationError::Io(e) => get_io_error_class(e), - KvMutationError::InvalidMutationWithValue(_) => "TypeError", - KvMutationError::InvalidMutationWithoutValue(_) => "TypeError", - }, - InvalidEnqueue(e) => get_io_error_class(e), - EmptyKey => "TypeError", - ValueTooLarge(_) => "TypeError", - EnqueuePayloadTooLarge(_) => "TypeError", - InvalidCursor => "TypeError", - CursorOutOfBounds => "TypeError", - InvalidRange => "TypeError", - } -} - -fn get_net_error(error: &NetError) -> &'static str { - match error { - NetError::ListenerClosed => "BadResource", - NetError::ListenerBusy => "Busy", - NetError::SocketClosed => "BadResource", - NetError::SocketClosedNotConnected => "NotConnected", - NetError::SocketBusy => "Busy", - NetError::Io(e) => get_io_error_class(e), - NetError::AcceptTaskOngoing => "Busy", - NetError::RootCertStore(e) | NetError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - NetError::Permission(e) => get_permission_check_error_class(e), - NetError::NoResolvedAddress => "Error", - NetError::AddrParse(_) => "Error", - NetError::Map(e) => get_net_map_error(e), - NetError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - NetError::DnsNotFound(_) => "NotFound", - NetError::DnsNotConnected(_) => "NotConnected", - NetError::DnsTimedOut(_) => "TimedOut", - NetError::Dns(_) => "Error", - NetError::UnsupportedRecordType => "NotSupported", - NetError::InvalidUtf8(_) => "InvalidData", - NetError::UnexpectedKeyType => "Error", - NetError::InvalidHostname(_) => "TypeError", - NetError::TcpStreamBusy => "Busy", - NetError::Rustls(_) => "Error", - NetError::Tls(e) => get_tls_error_class(e), - NetError::ListenTlsRequiresKey => "InvalidData", - NetError::Reunite(_) => "Error", - } -} - -fn get_net_map_error(error: &deno_net::io::MapError) -> &'static str { - match error { - deno_net::io::MapError::Io(e) => get_io_error_class(e), - deno_net::io::MapError::NoResources => "Error", - } -} - -fn get_child_permission_error(e: &ChildPermissionError) -> &'static str { - match e { - ChildPermissionError::Escalation => "NotCapable", - ChildPermissionError::PathResolve(e) => get_path_resolve_error(e), - ChildPermissionError::NetDescriptorParse(_) => "URIError", - ChildPermissionError::EnvDescriptorParse(_) => "Error", - ChildPermissionError::SysDescriptorParse(e) => { - get_sys_descriptor_parse_error(e) - } - ChildPermissionError::RunDescriptorParse(e) => { - get_run_descriptor_parse_error(e) - } - } -} - -fn get_create_worker_error(error: &CreateWorkerError) -> &'static str { - match error { - CreateWorkerError::ClassicWorkers => "DOMExceptionNotSupportedError", - CreateWorkerError::Permission(e) => get_child_permission_error(e), - CreateWorkerError::ModuleResolution(e) => { - get_module_resolution_error_class(e) - } - CreateWorkerError::Io(e) => get_io_error_class(e), - CreateWorkerError::MessagePort(e) => get_web_message_port_error_class(e), - } -} - -fn get_tty_error(error: &TtyError) -> &'static str { - match error { - TtyError::Resource(e) | TtyError::Other(e) => { - get_error_class_name(e).unwrap_or("Error") - } - TtyError::Io(e) => get_io_error_class(e), - #[cfg(unix)] - TtyError::Nix(e) => get_nix_error_class(e), - } -} - -fn get_readline_error(error: &ReadlineError) -> &'static str { - match error { - ReadlineError::Io(e) => get_io_error_class(e), - ReadlineError::Eof => "Error", - ReadlineError::Interrupted => "Error", - #[cfg(unix)] - ReadlineError::Errno(e) => get_nix_error_class(e), - ReadlineError::WindowResized => "Error", - #[cfg(windows)] - ReadlineError::Decode(_) => "Error", - #[cfg(windows)] - ReadlineError::SystemError(_) => "Error", - _ => "Error", - } -} - -fn get_signal_error(error: &SignalError) -> &'static str { - match error { - SignalError::InvalidSignalStr(_) => "TypeError", - SignalError::InvalidSignalInt(_) => "TypeError", - SignalError::SignalNotAllowed(_) => "TypeError", - SignalError::Io(e) => get_io_error_class(e), - } -} - -fn get_fs_events_error(error: &FsEventsError) -> &'static str { - match error { - FsEventsError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - FsEventsError::Permission(e) => get_permission_check_error_class(e), - FsEventsError::Notify(e) => get_notify_error_class(e), - FsEventsError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - } -} - -fn get_http_start_error(error: &HttpStartError) -> &'static str { - match error { - HttpStartError::TcpStreamInUse => "Busy", - HttpStartError::TlsStreamInUse => "Busy", - HttpStartError::UnixSocketInUse => "Busy", - HttpStartError::ReuniteTcp(_) => "Error", - #[cfg(unix)] - HttpStartError::ReuniteUnix(_) => "Error", - HttpStartError::Io(e) => get_io_error_class(e), - HttpStartError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_process_error(error: &ProcessError) -> &'static str { - match error { - ProcessError::SpawnFailed { error, .. } => get_process_error(error), - ProcessError::FailedResolvingCwd(e) | ProcessError::Io(e) => { - get_io_error_class(e) - } - ProcessError::Permission(e) => get_permission_check_error_class(e), - ProcessError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - ProcessError::BorrowMut(_) => "Error", - ProcessError::Which(_) => "Error", - ProcessError::ChildProcessAlreadyTerminated => "TypeError", - ProcessError::Signal(e) => get_signal_error(e), - ProcessError::MissingCmd => "Error", - ProcessError::InvalidPid => "TypeError", - #[cfg(unix)] - ProcessError::Nix(e) => get_nix_error_class(e), - ProcessError::RunPermission(e) => match e { - CheckRunPermissionError::Permission(e) => { - get_permission_check_error_class(e) - } - CheckRunPermissionError::Other(e) => { - get_error_class_name(e).unwrap_or("Error") - } - }, - } -} - -fn get_http_error(error: &HttpError) -> &'static str { - match error { - HttpError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - HttpError::HyperV014(e) => get_hyper_v014_error_class(e), - HttpError::InvalidHeaderName(_) => "Error", - HttpError::InvalidHeaderValue(_) => "Error", - HttpError::Http(_) => "Error", - HttpError::ResponseHeadersAlreadySent => "Http", - HttpError::ConnectionClosedWhileSendingResponse => "Http", - HttpError::AlreadyInUse => "Http", - HttpError::Io(e) => get_io_error_class(e), - HttpError::NoResponseHeaders => "Http", - HttpError::ResponseAlreadyCompleted => "Http", - HttpError::UpgradeBodyUsed => "Http", - HttpError::Resource(e) | HttpError::Other(e) => { - get_error_class_name(e).unwrap_or("Error") - } - } -} - -fn get_http_next_error(error: &HttpNextError) -> &'static str { - match error { - HttpNextError::Io(e) => get_io_error_class(e), - HttpNextError::WebSocketUpgrade(e) => get_websocket_upgrade_error(e), - HttpNextError::Hyper(e) => get_hyper_error_class(e), - HttpNextError::JoinError(_) => "Error", - HttpNextError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - HttpNextError::UpgradeUnavailable(_) => "Error", - HttpNextError::HttpPropertyExtractor(e) | HttpNextError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - } -} - -fn get_websocket_upgrade_error(error: &WebSocketUpgradeError) -> &'static str { - match error { - WebSocketUpgradeError::InvalidHeaders => "Http", - WebSocketUpgradeError::HttpParse(_) => "Error", - WebSocketUpgradeError::Http(_) => "Error", - WebSocketUpgradeError::Utf8(_) => "Error", - WebSocketUpgradeError::InvalidHeaderName(_) => "Error", - WebSocketUpgradeError::InvalidHeaderValue(_) => "Error", - WebSocketUpgradeError::InvalidHttpStatusLine => "Http", - WebSocketUpgradeError::UpgradeBufferAlreadyCompleted => "Http", - } -} - -fn get_fs_error(e: &FsError) -> &'static str { - match &e { - FsError::Io(e) => get_io_error_class(e), - FsError::FileBusy => "Busy", - FsError::NotSupported => "NotSupported", - FsError::NotCapable(_) => "NotCapable", - } -} - -mod node { - use super::get_error_class_name; - use super::get_io_error_class; - use super::get_permission_check_error_class; - use super::get_serde_json_error_class; - use super::get_url_parse_error_class; - pub use deno_node::ops::blocklist::BlocklistError; - pub use deno_node::ops::crypto::cipher::CipherContextError; - pub use deno_node::ops::crypto::cipher::CipherError; - pub use deno_node::ops::crypto::cipher::DecipherContextError; - pub use deno_node::ops::crypto::cipher::DecipherError; - pub use deno_node::ops::crypto::digest::HashError; - pub use deno_node::ops::crypto::keys::AsymmetricPrivateKeyDerError; - pub use deno_node::ops::crypto::keys::AsymmetricPrivateKeyError; - pub use deno_node::ops::crypto::keys::AsymmetricPublicKeyDerError; - pub use deno_node::ops::crypto::keys::AsymmetricPublicKeyError; - pub use deno_node::ops::crypto::keys::AsymmetricPublicKeyJwkError; - pub use deno_node::ops::crypto::keys::EcJwkError; - pub use deno_node::ops::crypto::keys::EdRawError; - pub use deno_node::ops::crypto::keys::ExportPrivateKeyPemError; - pub use deno_node::ops::crypto::keys::ExportPublicKeyPemError; - pub use deno_node::ops::crypto::keys::GenerateRsaPssError; - pub use deno_node::ops::crypto::keys::RsaJwkError; - pub use deno_node::ops::crypto::keys::RsaPssParamsParseError; - pub use deno_node::ops::crypto::keys::X509PublicKeyError; - pub use deno_node::ops::crypto::sign::KeyObjectHandlePrehashedSignAndVerifyError; - pub use deno_node::ops::crypto::x509::X509Error; - pub use deno_node::ops::crypto::DiffieHellmanError; - pub use deno_node::ops::crypto::EcdhEncodePubKey; - pub use deno_node::ops::crypto::HkdfError; - pub use deno_node::ops::crypto::Pbkdf2Error; - pub use deno_node::ops::crypto::PrivateEncryptDecryptError; - pub use deno_node::ops::crypto::ScryptAsyncError; - pub use deno_node::ops::crypto::SignEd25519Error; - pub use deno_node::ops::crypto::VerifyEd25519Error; - pub use deno_node::ops::fs::FsError; - pub use deno_node::ops::http::ConnError; - pub use deno_node::ops::http2::Http2Error; - pub use deno_node::ops::idna::IdnaError; - pub use deno_node::ops::ipc::IpcError; - pub use deno_node::ops::ipc::IpcJsonStreamError; - use deno_node::ops::os::priority::PriorityError; - pub use deno_node::ops::os::OsError; - pub use deno_node::ops::require::RequireError; - use deno_node::ops::require::RequireErrorKind; - pub use deno_node::ops::worker_threads::WorkerThreadsFilenameError; - pub use deno_node::ops::zlib::brotli::BrotliError; - pub use deno_node::ops::zlib::mode::ModeError; - pub use deno_node::ops::zlib::ZlibError; - - pub fn get_blocklist_error(error: &BlocklistError) -> &'static str { - match error { - BlocklistError::AddrParse(_) => "Error", - BlocklistError::IpNetwork(_) => "Error", - BlocklistError::InvalidAddress => "Error", - BlocklistError::IpVersionMismatch => "Error", - } - } - - pub fn get_fs_error(error: &FsError) -> &'static str { - match error { - FsError::Permission(e) => get_permission_check_error_class(e), - FsError::Io(e) => get_io_error_class(e), - #[cfg(windows)] - FsError::PathHasNoRoot => "Error", - #[cfg(not(any(unix, windows)))] - FsError::UnsupportedPlatform => "Error", - FsError::Fs(e) => super::get_fs_error(e), - } - } - - pub fn get_idna_error(error: &IdnaError) -> &'static str { - match error { - IdnaError::InvalidInput => "RangeError", - IdnaError::InputTooLong => "Error", - IdnaError::IllegalInput => "RangeError", - } - } - - pub fn get_ipc_json_stream_error(error: &IpcJsonStreamError) -> &'static str { - match error { - IpcJsonStreamError::Io(e) => get_io_error_class(e), - IpcJsonStreamError::SimdJson(_) => "Error", - } - } - - pub fn get_ipc_error(error: &IpcError) -> &'static str { - match error { - IpcError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - IpcError::IpcJsonStream(e) => get_ipc_json_stream_error(e), - IpcError::Canceled(e) => { - let io_err: std::io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - IpcError::SerdeJson(e) => get_serde_json_error_class(e), - } - } - - pub fn get_worker_threads_filename_error( - error: &WorkerThreadsFilenameError, - ) -> &'static str { - match error { - WorkerThreadsFilenameError::Permission(e) => { - get_error_class_name(e).unwrap_or("Error") - } - WorkerThreadsFilenameError::UrlParse(e) => get_url_parse_error_class(e), - WorkerThreadsFilenameError::InvalidRelativeUrl => "Error", - WorkerThreadsFilenameError::UrlFromPathString => "Error", - WorkerThreadsFilenameError::UrlToPathString => "Error", - WorkerThreadsFilenameError::UrlToPath => "Error", - WorkerThreadsFilenameError::FileNotFound(_) => "Error", - WorkerThreadsFilenameError::Fs(e) => super::get_fs_error(e), - } - } - - pub fn get_require_error(error: &RequireError) -> &'static str { - use RequireErrorKind::*; - match error.as_kind() { - UrlParse(e) => get_url_parse_error_class(e), - Permission(e) => get_error_class_name(e).unwrap_or("Error"), - PackageExportsResolve(_) - | PackageJsonLoad(_) - | ClosestPkgJson(_) - | FilePathConversion(_) - | UrlConversion(_) - | ReadModule(_) - | PackageImportsResolve(_) => "Error", - Fs(e) | UnableToGetCwd(e) => super::get_fs_error(e), - } - } - - pub fn get_http2_error(error: &Http2Error) -> &'static str { - match error { - Http2Error::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - Http2Error::UrlParse(e) => get_url_parse_error_class(e), - Http2Error::H2(_) => "Error", - } - } - - pub fn get_os_error(error: &OsError) -> &'static str { - match error { - OsError::Priority(e) => match e { - PriorityError::Io(e) => get_io_error_class(e), - #[cfg(windows)] - PriorityError::InvalidPriority => "TypeError", - }, - OsError::Permission(e) => get_permission_check_error_class(e), - OsError::FailedToGetCpuInfo => "TypeError", - OsError::FailedToGetUserInfo(e) => get_io_error_class(e), - } - } - - pub fn get_brotli_error(error: &BrotliError) -> &'static str { - match error { - BrotliError::InvalidEncoderMode => "TypeError", - BrotliError::CompressFailed => "TypeError", - BrotliError::DecompressFailed => "TypeError", - BrotliError::Join(_) => "Error", - BrotliError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - BrotliError::Io(e) => get_io_error_class(e), - } - } - - pub fn get_mode_error(_: &ModeError) -> &'static str { - "Error" - } - - pub fn get_zlib_error(e: &ZlibError) -> &'static str { - match e { - ZlibError::NotInitialized => "TypeError", - ZlibError::Mode(e) => get_mode_error(e), - ZlibError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } - } - - pub fn get_crypto_cipher_context_error( - e: &CipherContextError, - ) -> &'static str { - match e { - CipherContextError::ContextInUse => "TypeError", - CipherContextError::Cipher(e) => get_crypto_cipher_error(e), - CipherContextError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - } - } - - pub fn get_crypto_cipher_error(e: &CipherError) -> &'static str { - match e { - CipherError::InvalidIvLength => "TypeError", - CipherError::InvalidKeyLength => "RangeError", - CipherError::InvalidInitializationVector => "TypeError", - CipherError::CannotPadInputData => "TypeError", - CipherError::UnknownCipher(_) => "TypeError", - } - } - - pub fn get_crypto_decipher_context_error( - e: &DecipherContextError, - ) -> &'static str { - match e { - DecipherContextError::ContextInUse => "TypeError", - DecipherContextError::Decipher(e) => get_crypto_decipher_error(e), - DecipherContextError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - } - } - - pub fn get_crypto_decipher_error(e: &DecipherError) -> &'static str { - match e { - DecipherError::InvalidIvLength => "TypeError", - DecipherError::InvalidKeyLength => "RangeError", - DecipherError::InvalidInitializationVector => "TypeError", - DecipherError::CannotUnpadInputData => "TypeError", - DecipherError::DataAuthenticationFailed => "TypeError", - DecipherError::SetAutoPaddingFalseAes128GcmUnsupported => "TypeError", - DecipherError::SetAutoPaddingFalseAes256GcmUnsupported => "TypeError", - DecipherError::UnknownCipher(_) => "TypeError", - } - } - - pub fn get_x509_error(_: &X509Error) -> &'static str { - "Error" - } - - pub fn get_crypto_key_object_handle_prehashed_sign_and_verify_error( - e: &KeyObjectHandlePrehashedSignAndVerifyError, - ) -> &'static str { - match e { - KeyObjectHandlePrehashedSignAndVerifyError::InvalidDsaSignatureEncoding => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::KeyIsNotPrivate => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaSignature(_) => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithRsa => "Error", - KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaPssSignature(_) => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithRsaPss => "Error", - KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithDsa => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::RsaPssHashAlgorithmUnsupported => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::PrivateKeyDisallowsUsage { .. } => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigest => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::X25519KeyCannotBeUsedForSigning => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::Ed25519KeyCannotBeUsedForPrehashedSigning => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::DhKeyCannotBeUsedForSigning => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::KeyIsNotPublicOrPrivate => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::InvalidDsaSignature => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::X25519KeyCannotBeUsedForVerification => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::Ed25519KeyCannotBeUsedForPrehashedVerification => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::DhKeyCannotBeUsedForVerification => "TypeError", - } - } - - pub fn get_crypto_hash_error(_: &HashError) -> &'static str { - "Error" - } - - pub fn get_asymmetric_public_key_jwk_error( - e: &AsymmetricPublicKeyJwkError, - ) -> &'static str { - match e { - AsymmetricPublicKeyJwkError::UnsupportedJwkEcCurveP224 => "TypeError", - AsymmetricPublicKeyJwkError::JwkExportNotImplementedForKeyType => { - "TypeError" - } - AsymmetricPublicKeyJwkError::KeyIsNotAsymmetricPublicKey => "TypeError", - } - } - - pub fn get_generate_rsa_pss_error(_: &GenerateRsaPssError) -> &'static str { - "TypeError" - } - - pub fn get_asymmetric_private_key_der_error( - e: &AsymmetricPrivateKeyDerError, - ) -> &'static str { - match e { - AsymmetricPrivateKeyDerError::KeyIsNotAsymmetricPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::InvalidRsaPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::ExportingNonRsaPrivateKeyAsPkcs1Unsupported => "TypeError", - AsymmetricPrivateKeyDerError::InvalidEcPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::ExportingNonEcPrivateKeyAsSec1Unsupported => "TypeError", - AsymmetricPrivateKeyDerError::ExportingNonRsaPssPrivateKeyAsPkcs8Unsupported => "Error", - AsymmetricPrivateKeyDerError::InvalidDsaPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::InvalidX25519PrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::InvalidEd25519PrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::InvalidDhPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::UnsupportedKeyType(_) => "TypeError", - } - } - - pub fn get_asymmetric_public_key_der_error( - _: &AsymmetricPublicKeyDerError, - ) -> &'static str { - "TypeError" - } - - pub fn get_export_public_key_pem_error( - e: &ExportPublicKeyPemError, - ) -> &'static str { - match e { - ExportPublicKeyPemError::AsymmetricPublicKeyDer(e) => { - get_asymmetric_public_key_der_error(e) - } - ExportPublicKeyPemError::VeryLargeData => "TypeError", - ExportPublicKeyPemError::Der(_) => "Error", - } - } - - pub fn get_export_private_key_pem_error( - e: &ExportPrivateKeyPemError, - ) -> &'static str { - match e { - ExportPrivateKeyPemError::AsymmetricPublicKeyDer(e) => { - get_asymmetric_private_key_der_error(e) - } - ExportPrivateKeyPemError::VeryLargeData => "TypeError", - ExportPrivateKeyPemError::Der(_) => "Error", - } - } - - pub fn get_x509_public_key_error(e: &X509PublicKeyError) -> &'static str { - match e { - X509PublicKeyError::X509(_) => "Error", - X509PublicKeyError::Rsa(_) => "Error", - X509PublicKeyError::Asn1(_) => "Error", - X509PublicKeyError::Ec(_) => "Error", - X509PublicKeyError::UnsupportedEcNamedCurve => "TypeError", - X509PublicKeyError::MissingEcParameters => "TypeError", - X509PublicKeyError::MalformedDssPublicKey => "TypeError", - X509PublicKeyError::UnsupportedX509KeyType => "TypeError", - } - } - - pub fn get_rsa_jwk_error(e: &RsaJwkError) -> &'static str { - match e { - RsaJwkError::Base64(_) => "Error", - RsaJwkError::Rsa(_) => "Error", - RsaJwkError::MissingRsaPrivateComponent => "TypeError", - } - } - - pub fn get_ec_jwk_error(e: &EcJwkError) -> &'static str { - match e { - EcJwkError::Ec(_) => "Error", - EcJwkError::UnsupportedCurve(_) => "TypeError", - } - } - - pub fn get_ed_raw_error(e: &EdRawError) -> &'static str { - match e { - EdRawError::Ed25519Signature(_) => "Error", - EdRawError::InvalidEd25519Key => "TypeError", - EdRawError::UnsupportedCurve => "TypeError", - } - } - - pub fn get_pbkdf2_error(e: &Pbkdf2Error) -> &'static str { - match e { - Pbkdf2Error::UnsupportedDigest(_) => "TypeError", - Pbkdf2Error::Join(_) => "Error", - } - } - - pub fn get_scrypt_async_error(e: &ScryptAsyncError) -> &'static str { - match e { - ScryptAsyncError::Join(_) => "Error", - ScryptAsyncError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } - } - - pub fn get_hkdf_error_error(e: &HkdfError) -> &'static str { - match e { - HkdfError::ExpectedSecretKey => "TypeError", - HkdfError::HkdfExpandFailed => "TypeError", - HkdfError::UnsupportedDigest(_) => "TypeError", - HkdfError::Join(_) => "Error", - } - } - - pub fn get_rsa_pss_params_parse_error( - _: &RsaPssParamsParseError, - ) -> &'static str { - "TypeError" - } - - pub fn get_asymmetric_private_key_error( - e: &AsymmetricPrivateKeyError, - ) -> &'static str { - match e { - AsymmetricPrivateKeyError::InvalidPemPrivateKeyInvalidUtf8(_) => "TypeError", - AsymmetricPrivateKeyError::InvalidEncryptedPemPrivateKey => "TypeError", - AsymmetricPrivateKeyError::InvalidPemPrivateKey => "TypeError", - AsymmetricPrivateKeyError::EncryptedPrivateKeyRequiresPassphraseToDecrypt => "TypeError", - AsymmetricPrivateKeyError::InvalidPkcs1PrivateKey => "TypeError", - AsymmetricPrivateKeyError::InvalidSec1PrivateKey => "TypeError", - AsymmetricPrivateKeyError::UnsupportedPemLabel(_) => "TypeError", - AsymmetricPrivateKeyError::RsaPssParamsParse(e) => get_rsa_pss_params_parse_error(e), - AsymmetricPrivateKeyError::InvalidEncryptedPkcs8PrivateKey => "TypeError", - AsymmetricPrivateKeyError::InvalidPkcs8PrivateKey => "TypeError", - AsymmetricPrivateKeyError::Pkcs1PrivateKeyDoesNotSupportEncryptionWithPassphrase => "TypeError", - AsymmetricPrivateKeyError::Sec1PrivateKeyDoesNotSupportEncryptionWithPassphrase => "TypeError", - AsymmetricPrivateKeyError::UnsupportedEcNamedCurve => "TypeError", - AsymmetricPrivateKeyError::InvalidPrivateKey => "TypeError", - AsymmetricPrivateKeyError::InvalidDsaPrivateKey => "TypeError", - AsymmetricPrivateKeyError::MalformedOrMissingNamedCurveInEcParameters => "TypeError", - AsymmetricPrivateKeyError::UnsupportedKeyType(_) => "TypeError", - AsymmetricPrivateKeyError::UnsupportedKeyFormat(_) => "TypeError", - AsymmetricPrivateKeyError::InvalidX25519PrivateKey => "TypeError", - AsymmetricPrivateKeyError::X25519PrivateKeyIsWrongLength => "TypeError", - AsymmetricPrivateKeyError::InvalidEd25519PrivateKey => "TypeError", - AsymmetricPrivateKeyError::MissingDhParameters => "TypeError", - AsymmetricPrivateKeyError::UnsupportedPrivateKeyOid => "TypeError", - } - } - - pub fn get_asymmetric_public_key_error( - e: &AsymmetricPublicKeyError, - ) -> &'static str { - match e { - AsymmetricPublicKeyError::InvalidPemPrivateKeyInvalidUtf8(_) => { - "TypeError" - } - AsymmetricPublicKeyError::InvalidPemPublicKey => "TypeError", - AsymmetricPublicKeyError::InvalidPkcs1PublicKey => "TypeError", - AsymmetricPublicKeyError::AsymmetricPrivateKey(e) => { - get_asymmetric_private_key_error(e) - } - AsymmetricPublicKeyError::InvalidX509Certificate => "TypeError", - AsymmetricPublicKeyError::X509(_) => "Error", - AsymmetricPublicKeyError::X509PublicKey(e) => { - get_x509_public_key_error(e) - } - AsymmetricPublicKeyError::UnsupportedPemLabel(_) => "TypeError", - AsymmetricPublicKeyError::InvalidSpkiPublicKey => "TypeError", - AsymmetricPublicKeyError::UnsupportedKeyType(_) => "TypeError", - AsymmetricPublicKeyError::UnsupportedKeyFormat(_) => "TypeError", - AsymmetricPublicKeyError::Spki(_) => "Error", - AsymmetricPublicKeyError::Pkcs1(_) => "Error", - AsymmetricPublicKeyError::RsaPssParamsParse(_) => "TypeError", - AsymmetricPublicKeyError::MalformedDssPublicKey => "TypeError", - AsymmetricPublicKeyError::MalformedOrMissingNamedCurveInEcParameters => { - "TypeError" - } - AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInEcSpki => { - "TypeError" - } - AsymmetricPublicKeyError::Ec(_) => "Error", - AsymmetricPublicKeyError::UnsupportedEcNamedCurve => "TypeError", - AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInX25519Spki => { - "TypeError" - } - AsymmetricPublicKeyError::X25519PublicKeyIsTooShort => "TypeError", - AsymmetricPublicKeyError::InvalidEd25519PublicKey => "TypeError", - AsymmetricPublicKeyError::MissingDhParameters => "TypeError", - AsymmetricPublicKeyError::MalformedDhParameters => "TypeError", - AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInDhSpki => { - "TypeError" - } - AsymmetricPublicKeyError::UnsupportedPrivateKeyOid => "TypeError", - } - } - - pub fn get_private_encrypt_decrypt_error( - e: &PrivateEncryptDecryptError, - ) -> &'static str { - match e { - PrivateEncryptDecryptError::Pkcs8(_) => "Error", - PrivateEncryptDecryptError::Spki(_) => "Error", - PrivateEncryptDecryptError::Utf8(_) => "Error", - PrivateEncryptDecryptError::Rsa(_) => "Error", - PrivateEncryptDecryptError::UnknownPadding => "TypeError", - } - } - - pub fn get_ecdh_encode_pub_key_error(e: &EcdhEncodePubKey) -> &'static str { - match e { - EcdhEncodePubKey::InvalidPublicKey => "TypeError", - EcdhEncodePubKey::UnsupportedCurve => "TypeError", - EcdhEncodePubKey::Sec1(_) => "Error", - } - } - - pub fn get_diffie_hellman_error(_: &DiffieHellmanError) -> &'static str { - "TypeError" - } - - pub fn get_sign_ed25519_error(_: &SignEd25519Error) -> &'static str { - "TypeError" - } - - pub fn get_verify_ed25519_error(_: &VerifyEd25519Error) -> &'static str { - "TypeError" - } - - pub fn get_conn_error(e: &ConnError) -> &'static str { - match e { - ConnError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - ConnError::Permission(e) => get_permission_check_error_class(e), - ConnError::InvalidUrl(_) => "TypeError", - ConnError::InvalidHeaderName(_) => "TypeError", - ConnError::InvalidHeaderValue(_) => "TypeError", - ConnError::Url(e) => get_url_parse_error_class(e), - ConnError::Method(_) => "TypeError", - ConnError::Io(e) => get_io_error_class(e), - ConnError::Hyper(e) => super::get_hyper_error_class(e), - ConnError::TlsStreamBusy => "Busy", - ConnError::TcpStreamBusy => "Busy", - ConnError::ReuniteTcp(_) => "Error", - ConnError::Canceled(_) => "Error", - } - } -} - -fn get_os_error(error: &OsError) -> &'static str { - match error { - OsError::Permission(e) => get_permission_check_error_class(e), - OsError::InvalidUtf8(_) => "InvalidData", - OsError::EnvEmptyKey => "TypeError", - OsError::EnvInvalidKey(_) => "TypeError", - OsError::EnvInvalidValue(_) => "TypeError", - OsError::Io(e) => get_io_error_class(e), - OsError::Var(e) => get_env_var_error_class(e), - } -} - -fn get_sync_fetch_error(error: &SyncFetchError) -> &'static str { - match error { - SyncFetchError::BlobUrlsNotSupportedInContext => "TypeError", - SyncFetchError::Io(e) => get_io_error_class(e), - SyncFetchError::InvalidScriptUrl => "TypeError", - SyncFetchError::InvalidStatusCode(_) => "TypeError", - SyncFetchError::ClassicScriptSchemeUnsupportedInWorkers(_) => "TypeError", - SyncFetchError::InvalidUri(_) => "Error", - SyncFetchError::InvalidMimeType(_) => "DOMExceptionNetworkError", - SyncFetchError::MissingMimeType => "DOMExceptionNetworkError", - SyncFetchError::Fetch(e) => get_fetch_error(e), - SyncFetchError::Join(_) => "Error", - SyncFetchError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { - deno_core::error::get_custom_error_class(e) - .or_else(|| { - e.downcast_ref::() - .map(get_child_permission_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_permission_check_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_permission_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_fs_error)) - .or_else(|| { - e.downcast_ref::() - .map(node::get_blocklist_error) - }) - .or_else(|| e.downcast_ref::().map(node::get_fs_error)) - .or_else(|| { - e.downcast_ref::() - .map(node::get_idna_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_ipc_json_stream_error) - }) - .or_else(|| e.downcast_ref::().map(node::get_ipc_error)) - .or_else(|| { - e.downcast_ref::() - .map(node::get_worker_threads_filename_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_require_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_http2_error) - }) - .or_else(|| e.downcast_ref::().map(node::get_os_error)) - .or_else(|| { - e.downcast_ref::() - .map(node::get_brotli_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_mode_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_zlib_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_cipher_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_cipher_context_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_decipher_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_decipher_context_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_x509_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_key_object_handle_prehashed_sign_and_verify_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_hash_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_public_key_jwk_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_generate_rsa_pss_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_private_key_der_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_public_key_der_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_export_public_key_pem_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_export_private_key_pem_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_rsa_jwk_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_ec_jwk_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_ed_raw_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_pbkdf2_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_scrypt_async_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_hkdf_error_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_rsa_pss_params_parse_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_private_key_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_public_key_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_private_encrypt_decrypt_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_ecdh_encode_pub_key_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_diffie_hellman_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_sign_ed25519_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_verify_ed25519_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_conn_error) - }) - .or_else(|| e.downcast_ref::().map(get_napi_error_class)) - .or_else(|| e.downcast_ref::().map(get_web_error_class)) - .or_else(|| { - e.downcast_ref::() - .map(get_create_worker_error) - }) - .or_else(|| e.downcast_ref::().map(get_tty_error)) - .or_else(|| e.downcast_ref::().map(get_readline_error)) - .or_else(|| e.downcast_ref::().map(get_signal_error)) - .or_else(|| e.downcast_ref::().map(get_fs_events_error)) - .or_else(|| e.downcast_ref::().map(get_http_start_error)) - .or_else(|| e.downcast_ref::().map(get_process_error)) - .or_else(|| e.downcast_ref::().map(get_os_error)) - .or_else(|| e.downcast_ref::().map(get_sync_fetch_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_web_compression_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_web_message_port_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_web_stream_resource_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_web_blob_error_class)) - .or_else(|| e.downcast_ref::().map(|_| "TypeError")) - .or_else(|| e.downcast_ref::().map(get_ffi_repr_error_class)) - .or_else(|| e.downcast_ref::().map(get_http_error)) - .or_else(|| e.downcast_ref::().map(get_http_next_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_websocket_upgrade_error) - }) - .or_else(|| e.downcast_ref::().map(get_fs_ops_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_ffi_dlfcn_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_ffi_static_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_ffi_callback_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_ffi_call_error_class)) - .or_else(|| e.downcast_ref::().map(get_tls_error_class)) - .or_else(|| e.downcast_ref::().map(get_cron_error_class)) - .or_else(|| e.downcast_ref::().map(get_canvas_error)) - .or_else(|| e.downcast_ref::().map(get_cache_error)) - .or_else(|| e.downcast_ref::().map(get_websocket_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_websocket_handshake_error) - }) - .or_else(|| e.downcast_ref::().map(get_kv_error)) - .or_else(|| e.downcast_ref::().map(get_fetch_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_http_client_create_error) - }) - .or_else(|| e.downcast_ref::().map(get_net_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_net_map_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_broadcast_channel_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_buffer_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_bundle_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_byow_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_render_pass_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_surface_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_decrypt_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_encrypt_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_shared_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_ed25519_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_export_key_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_generate_key_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_import_key_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_x448_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_x25519_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webstorage_class_name) - }) - .or_else(|| { - e.downcast_ref::() - .map(|_| "TypeError") - }) - .or_else(|| { - e.downcast_ref::() - .map(get_dlopen_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_hyper_error_class)) - .or_else(|| { - e.downcast_ref::() - .map(get_hyper_util_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_hyper_v014_error_class) - }) - .or_else(|| { - e.downcast_ref::>() - .map(|e| get_hyper_v014_error_class(e)) - }) - .or_else(|| { - e.downcast_ref::().map(|e| { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - }) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_env_var_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_io_error_class)) - .or_else(|| { - e.downcast_ref::() - .map(get_module_resolution_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_notify_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_regex_error_class)) - .or_else(|| { - e.downcast_ref::() - .map(get_serde_json_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_url_parse_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(|_| "TypeError") - }) - .or_else(|| { - #[cfg(unix)] - let maybe_get_nix_error_class = - || e.downcast_ref::().map(get_nix_error_class); - #[cfg(not(unix))] - let maybe_get_nix_error_class = || Option::<&'static str>::None; - (maybe_get_nix_error_class)() - }) -} diff --git a/runtime/examples/extension/bootstrap.js b/runtime/examples/extension/bootstrap.js index 9461acb84a..78e0cb999f 100644 --- a/runtime/examples/extension/bootstrap.js +++ b/runtime/examples/extension/bootstrap.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_hello } from "ext:core/ops"; function hello() { op_hello("world"); diff --git a/runtime/examples/extension/main.js b/runtime/examples/extension/main.js index 4d6e4e3b7d..4a66aecf58 100644 --- a/runtime/examples/extension/main.js +++ b/runtime/examples/extension/main.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Hello world from JS!"); console.log(Deno.build); Extension.hello(); diff --git a/runtime/examples/extension/main.rs b/runtime/examples/extension/main.rs index 1ff16ec83f..e1538b8b75 100644 --- a/runtime/examples/extension/main.rs +++ b/runtime/examples/extension/main.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] @@ -12,6 +12,8 @@ use deno_core::op2; use deno_core::FsModuleLoader; use deno_core::ModuleSpecifier; use deno_fs::RealFs; +use deno_resolver::npm::DenoInNpmPackageChecker; +use deno_resolver::npm::NpmResolver; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::permissions::RuntimePermissionDescriptorParser; use deno_runtime::worker::MainWorker; @@ -37,11 +39,16 @@ async fn main() -> Result<(), AnyError> { let main_module = ModuleSpecifier::from_file_path(js_path).unwrap(); eprintln!("Running {main_module}..."); let fs = Arc::new(RealFs); - let permission_desc_parser = - Arc::new(RuntimePermissionDescriptorParser::new(fs.clone())); + let permission_desc_parser = Arc::new( + RuntimePermissionDescriptorParser::new(sys_traits::impls::RealSys), + ); let mut worker = MainWorker::bootstrap_from_options( main_module.clone(), - WorkerServiceOptions { + WorkerServiceOptions::< + DenoInNpmPackageChecker, + NpmResolver, + sys_traits::impls::RealSys, + > { module_loader: Rc::new(FsModuleLoader), permissions: PermissionsContainer::allow_all(permission_desc_parser), blob_store: Default::default(), diff --git a/runtime/fmt_errors.rs b/runtime/fmt_errors.rs index 6f120b5d46..6aa4765829 100644 --- a/runtime/fmt_errors.rs +++ b/runtime/fmt_errors.rs @@ -1,11 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This mod provides DenoError to unify errors across Deno. +use std::fmt::Write as _; + use color_print::cformat; use color_print::cstr; use deno_core::error::format_frame; use deno_core::error::JsError; use deno_terminal::colors; -use std::fmt::Write as _; #[derive(Debug, Clone)] struct ErrorReference<'a> { @@ -422,6 +423,20 @@ fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec { "Run again with `--unstable-webgpu` flag to enable this API.", ), ]; + } else if msg.contains("QuicEndpoint is not a constructor") { + return vec![ + FixSuggestion::info("listenQuic is an unstable API."), + FixSuggestion::hint( + "Run again with `--unstable-net` flag to enable this API.", + ), + ]; + } else if msg.contains("connectQuic is not a function") { + return vec![ + FixSuggestion::info("connectQuic is an unstable API."), + FixSuggestion::hint( + "Run again with `--unstable-net` flag to enable this API.", + ), + ]; // Try to capture errors like: // ``` // Uncaught Error: Cannot find module '../build/Release/canvas.node' @@ -476,9 +491,10 @@ pub fn format_js_error(js_error: &JsError) -> String { #[cfg(test)] mod tests { - use super::*; use test_util::strip_ansi_codes; + use super::*; + #[test] fn test_format_none_source_line() { let actual = format_maybe_source_line(None, None, false, 0); diff --git a/runtime/fs_util.rs b/runtime/fs_util.rs index a858e9770d..7788a97170 100644 --- a/runtime/fs_util.rs +++ b/runtime/fs_util.rs @@ -1,10 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::path::Path; +use std::path::PathBuf; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_path_util::normalize_path; -use std::path::Path; -use std::path::PathBuf; #[inline] pub fn resolve_from_cwd(path: &Path) -> Result { diff --git a/runtime/inspector_server.rs b/runtime/inspector_server.rs index a789dd3dca..75e9668db4 100644 --- a/runtime/inspector_server.rs +++ b/runtime/inspector_server.rs @@ -1,7 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Alias for the future `!` type. use core::convert::Infallible as Never; +use std::cell::RefCell; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::pin::pin; +use std::process; +use std::rc::Rc; +use std::thread; + use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::futures::channel::mpsc; @@ -28,13 +36,6 @@ use fastwebsockets::OpCode; use fastwebsockets::WebSocket; use hyper::body::Bytes; use hyper_util::rt::TokioIo; -use std::cell::RefCell; -use std::collections::HashMap; -use std::net::SocketAddr; -use std::pin::pin; -use std::process; -use std::rc::Rc; -use std::thread; use tokio::net::TcpListener; use tokio::sync::broadcast; use uuid::Uuid; diff --git a/runtime/js.rs b/runtime/js.rs index a8384ceacf..55ab75b66b 100644 --- a/runtime/js.rs +++ b/runtime/js.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[cfg(not(feature = "include_js_files_for_snapshotting"))] pub static SOURCE_CODE_FOR_99_MAIN_JS: &str = include_str!("js/99_main.js"); diff --git a/runtime/js/01_errors.js b/runtime/js/01_errors.js index ea567a5d08..09fd82e867 100644 --- a/runtime/js/01_errors.js +++ b/runtime/js/01_errors.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { BadResource, Interrupted, NotCapable } = core; diff --git a/runtime/js/01_version.ts b/runtime/js/01_version.ts index 33a8f50cdd..779a886897 100644 --- a/runtime/js/01_version.ts +++ b/runtime/js/01_version.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/runtime/js/06_util.js b/runtime/js/06_util.js index bf71c371b9..658cfa69aa 100644 --- a/runtime/js/06_util.js +++ b/runtime/js/06_util.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { op_bootstrap_log_level } from "ext:core/ops"; diff --git a/runtime/js/10_permissions.js b/runtime/js/10_permissions.js index 831b6bf2ae..c835cc1c5a 100644 --- a/runtime/js/10_permissions.js +++ b/runtime/js/10_permissions.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/11_workers.js b/runtime/js/11_workers.js index 3853761920..fd36866936 100644 --- a/runtime/js/11_workers.js +++ b/runtime/js/11_workers.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/40_fs_events.js b/runtime/js/40_fs_events.js index 322ee6b3ca..25f397b840 100644 --- a/runtime/js/40_fs_events.js +++ b/runtime/js/40_fs_events.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_fs_events_open, op_fs_events_poll } from "ext:core/ops"; diff --git a/runtime/js/40_process.js b/runtime/js/40_process.js index e2cb1d95b2..fde97fac64 100644 --- a/runtime/js/40_process.js +++ b/runtime/js/40_process.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/40_tty.js b/runtime/js/40_tty.js index 72e7b68846..56f8721eaf 100644 --- a/runtime/js/40_tty.js +++ b/runtime/js/40_tty.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_console_size } from "ext:core/ops"; const { diff --git a/runtime/js/41_prompt.js b/runtime/js/41_prompt.js index 8460862d2e..e300f69a3c 100644 --- a/runtime/js/41_prompt.js +++ b/runtime/js/41_prompt.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_read_line_prompt } from "ext:core/ops"; const { diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index a510ee33c4..b241028077 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -1,6 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -import { core } from "ext:core/mod.js"; +import { core, primordials } from "ext:core/mod.js"; import { op_net_listen_udp, op_net_listen_unixpacket, @@ -21,16 +21,20 @@ import * as version from "ext:runtime/01_version.ts"; import * as permissions from "ext:runtime/10_permissions.js"; import * as io from "ext:deno_io/12_io.js"; import * as fs from "ext:deno_fs/30_fs.js"; -import * as os from "ext:runtime/30_os.js"; +import * as os from "ext:deno_os/30_os.js"; import * as fsEvents from "ext:runtime/40_fs_events.js"; import * as process from "ext:runtime/40_process.js"; -import * as signals from "ext:runtime/40_signals.js"; +import * as signals from "ext:deno_os/40_signals.js"; import * as tty from "ext:runtime/40_tty.js"; import * as kv from "ext:deno_kv/01_db.ts"; import * as cron from "ext:deno_cron/01_cron.ts"; import * as webgpuSurface from "ext:deno_webgpu/02_surface.js"; import * as telemetry from "ext:deno_telemetry/telemetry.ts"; +const { ObjectDefineProperties } = primordials; + +const loadQuic = core.createLazyLoader("ext:deno_net/03_quic.js"); + const denoNs = { Process: process.Process, run: process.run, @@ -176,6 +180,26 @@ denoNsUnstableById[unstableIds.net] = { ), }; +ObjectDefineProperties(denoNsUnstableById[unstableIds.net], { + connectQuic: core.propWritableLazyLoaded((q) => q.connectQuic, loadQuic), + QuicEndpoint: core.propWritableLazyLoaded((q) => q.QuicEndpoint, loadQuic), + QuicBidirectionalStream: core.propWritableLazyLoaded( + (q) => q.QuicBidirectionalStream, + loadQuic, + ), + QuicConn: core.propWritableLazyLoaded((q) => q.QuicConn, loadQuic), + QuicListener: core.propWritableLazyLoaded((q) => q.QuicListener, loadQuic), + QuicReceiveStream: core.propWritableLazyLoaded( + (q) => q.QuicReceiveStream, + loadQuic, + ), + QuicSendStream: core.propWritableLazyLoaded( + (q) => q.QuicSendStream, + loadQuic, + ), + QuicIncoming: core.propWritableLazyLoaded((q) => q.QuicIncoming, loadQuic), +}); + // denoNsUnstableById[unstableIds.unsafeProto] = { __proto__: null } denoNsUnstableById[unstableIds.webgpu] = { diff --git a/runtime/js/98_global_scope_shared.js b/runtime/js/98_global_scope_shared.js index c01bde6fad..99bace7647 100644 --- a/runtime/js/98_global_scope_shared.js +++ b/runtime/js/98_global_scope_shared.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core } from "ext:core/mod.js"; diff --git a/runtime/js/98_global_scope_window.js b/runtime/js/98_global_scope_window.js index 098422f56f..c42a940a82 100644 --- a/runtime/js/98_global_scope_window.js +++ b/runtime/js/98_global_scope_window.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/98_global_scope_worker.js b/runtime/js/98_global_scope_worker.js index 4dc6157867..f10bb2830d 100644 --- a/runtime/js/98_global_scope_worker.js +++ b/runtime/js/98_global_scope_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 19432745d4..2971fd2c00 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Remove Intl.v8BreakIterator because it is a non-standard API. delete Intl.v8BreakIterator; @@ -55,7 +55,7 @@ import { registerDeclarativeServer } from "ext:deno_http/00_serve.ts"; import * as event from "ext:deno_web/02_event.js"; import * as location from "ext:deno_web/12_location.js"; import * as version from "ext:runtime/01_version.ts"; -import * as os from "ext:runtime/30_os.js"; +import * as os from "ext:deno_os/30_os.js"; import * as timers from "ext:deno_web/02_timers.js"; import { getDefaultInspectOptions, @@ -170,12 +170,14 @@ function postMessage(message, transferOrOptions = { __proto__: null }) { let isClosing = false; let globalDispatchEvent; +let closeOnIdle; 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 || + return (event.listenerCount(globalThis, "message") > 0 && + !globalThis[messagePort.unrefParentPort]) || messagePort.refedMessagePortsCount > 0; } @@ -188,7 +190,10 @@ async function pollForMessages() { } while (!isClosing) { const recvMessage = op_worker_recv_message(); - if (globalThis[messagePort.unrefPollForMessages] === true) { + // In a Node.js worker, unref() the op promise to prevent it from + // keeping the event loop alive. This avoids the need to explicitly + // call self.close() or worker.terminate(). + if (closeOnIdle) { core.unrefOpPromise(recvMessage); } const data = await recvMessage; @@ -521,6 +526,9 @@ const NOT_IMPORTED_OPS = [ // Used in jupyter API "op_base64_encode", + // Used in the lint API + "op_lint_create_serialized_ast", + // Related to `Deno.test()` API "op_test_event_step_result_failed", "op_test_event_step_result_ignored", @@ -915,6 +923,7 @@ function bootstrapWorkerRuntime( 6: argv0, 7: nodeDebug, 13: otelConfig, + 14: closeOnIdle_, } = runtimeOptions; performance.setTimeOrigin(); @@ -967,6 +976,7 @@ function bootstrapWorkerRuntime( globalThis.pollForMessages = pollForMessages; globalThis.hasMessageEventListener = hasMessageEventListener; + closeOnIdle = closeOnIdle_; for (let i = 0; i <= unstableFeatures.length; i++) { const id = unstableFeatures[i]; diff --git a/runtime/lib.rs b/runtime/lib.rs index 1f449dc69a..c104f5cd61 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use deno_broadcast_channel; pub use deno_cache; @@ -16,6 +16,7 @@ pub use deno_kv; pub use deno_napi; pub use deno_net; pub use deno_node; +pub use deno_os; pub use deno_permissions; pub use deno_terminal::colors; pub use deno_tls; @@ -27,16 +28,13 @@ pub use deno_websocket; pub use deno_webstorage; pub mod code_cache; -pub mod errors; pub mod fmt_errors; pub mod fs_util; pub mod inspector_server; pub mod js; pub mod ops; pub mod permissions; -pub mod signal; pub mod snapshot; -pub mod sys_info; pub mod tokio_util; pub mod web_worker; pub mod worker; @@ -47,6 +45,7 @@ pub use worker_bootstrap::WorkerExecutionMode; pub use worker_bootstrap::WorkerLogLevel; pub mod shared; +pub use deno_os::exit; pub use shared::runtime; pub struct UnstableGranularFlag { @@ -148,12 +147,6 @@ pub static UNSTABLE_GRANULAR_FLAGS: &[UnstableGranularFlag] = &[ }, ]; -pub fn exit(code: i32) -> ! { - deno_telemetry::flush(); - #[allow(clippy::disallowed_methods)] - std::process::exit(code); -} - #[cfg(test)] mod test { use super::*; diff --git a/runtime/ops/bootstrap.rs b/runtime/ops/bootstrap.rs index bbbddc61ba..b362217d2c 100644 --- a/runtime/ops/bootstrap.rs +++ b/runtime/ops/bootstrap.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::OpState; diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs index f6e5ceff5c..5336c232c9 100644 --- a/runtime/ops/fs_events.rs +++ b/runtime/ops/fs_events.rs @@ -1,5 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::convert::From; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + +use deno_core::op2; use deno_core::parking_lot::Mutex; use deno_core::AsyncRefCell; use deno_core::CancelFuture; @@ -8,9 +17,8 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; - -use deno_core::op2; - +use deno_error::builtin_classes::GENERIC_ERROR; +use deno_error::JsErrorClass; use deno_permissions::PermissionsContainer; use notify::event::Event as NotifyEvent; use notify::event::ModifyKind; @@ -20,13 +28,6 @@ use notify::RecommendedWatcher; use notify::RecursiveMode; use notify::Watcher; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::convert::From; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; use tokio::sync::mpsc; deno_core::extension!( @@ -117,14 +118,29 @@ fn is_file_removed(event_path: &PathBuf) -> bool { } } -#[derive(Debug, thiserror::Error)] +deno_error::js_error_wrapper!(NotifyError, JsNotifyError, |err| { + match &err.kind { + notify::ErrorKind::Generic(_) => GENERIC_ERROR.into(), + notify::ErrorKind::Io(e) => e.get_class(), + notify::ErrorKind::PathNotFound => "NotFound".into(), + notify::ErrorKind::WatchNotFound => "NotFound".into(), + notify::ErrorKind::InvalidConfig(_) => "InvalidData".into(), + notify::ErrorKind::MaxFilesWatch => GENERIC_ERROR.into(), + } +}); + +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum FsEventsError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Notify(#[from] NotifyError), + Notify(JsNotifyError), + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), } @@ -144,7 +160,9 @@ fn start_watcher( let sender_clone = senders.clone(); let watcher: RecommendedWatcher = Watcher::new( move |res: Result| { - let res2 = res.map(FsEvent::from).map_err(FsEventsError::Notify); + let res2 = res + .map(FsEvent::from) + .map_err(|e| FsEventsError::Notify(JsNotifyError(e))); for (paths, sender) in sender_clone.lock().iter() { // Ignore result, if send failed it means that watcher was already closed, // but not all messages have been flushed. @@ -170,7 +188,8 @@ fn start_watcher( } }, Default::default(), - )?; + ) + .map_err(|e| FsEventsError::Notify(JsNotifyError(e)))?; state.put::(WatcherState { watcher, senders }); @@ -199,7 +218,10 @@ fn op_fs_events_open( .check_read(path, "Deno.watchFs()")?; let watcher = state.borrow_mut::(); - watcher.watcher.watch(&path, recursive_mode)?; + watcher + .watcher + .watch(&path, recursive_mode) + .map_err(|e| FsEventsError::Notify(JsNotifyError(e)))?; } let resource = FsEventsResource { receiver: AsyncRefCell::new(receiver), @@ -215,17 +237,13 @@ async fn op_fs_events_poll( state: Rc>, #[smi] rid: ResourceId, ) -> Result, FsEventsError> { - let resource = state - .borrow() - .resource_table - .get::(rid) - .map_err(FsEventsError::Resource)?; + let resource = state.borrow().resource_table.get::(rid)?; let mut receiver = RcRef::map(&resource, |r| &r.receiver).borrow_mut().await; let cancel = RcRef::map(resource, |r| &r.cancel); let maybe_result = receiver.recv().or_cancel(cancel).await?; match maybe_result { Some(Ok(value)) => Ok(Some(value)), - Some(Err(err)) => Err(FsEventsError::Notify(err)), + Some(Err(err)) => Err(FsEventsError::Notify(JsNotifyError(err))), None => Ok(None), } } diff --git a/runtime/ops/http.rs b/runtime/ops/http.rs index 6e31576686..931b407779 100644 --- a/runtime/ops/http.rs +++ b/runtime/ops/http.rs @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::rc::Rc; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::ResourceId; @@ -13,23 +14,34 @@ pub const UNSTABLE_FEATURE_NAME: &str = "http"; deno_core::extension!(deno_http_runtime, ops = [op_http_start],); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HttpStartError { + #[class("Busy")] #[error("TCP stream is currently in use")] TcpStreamInUse, + #[class("Busy")] #[error("TLS stream is currently in use")] TlsStreamInUse, + #[class("Busy")] #[error("Unix socket is currently in use")] UnixSocketInUse, + #[class(generic)] #[error(transparent)] ReuniteTcp(#[from] tokio::net::tcp::ReuniteError), #[cfg(unix)] + #[class(generic)] #[error(transparent)] ReuniteUnix(#[from] tokio::net::unix::ReuniteError), + #[class(inherit)] #[error("{0}")] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + std::io::Error, + ), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Resource(#[inherit] ResourceError), } #[op2(fast)] @@ -89,5 +101,5 @@ fn op_http_start( )); } - Err(HttpStartError::Other(deno_core::error::bad_resource_id())) + Err(HttpStartError::Resource(ResourceError::BadResourceId)) } diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs index 67065b901b..d131e9aab5 100644 --- a/runtime/ops/mod.rs +++ b/runtime/ops/mod.rs @@ -1,13 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod bootstrap; pub mod fs_events; pub mod http; -pub mod os; pub mod permissions; pub mod process; pub mod runtime; -pub mod signal; pub mod tty; pub mod web_worker; pub mod worker_host; diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs index b5f9e284df..0ad14d433b 100644 --- a/runtime/ops/permissions.rs +++ b/runtime/ops/permissions.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use ::deno_permissions::PermissionState; use ::deno_permissions::PermissionsContainer; @@ -45,16 +45,21 @@ impl From for PermissionStatus { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PermissionError { + #[class(reference)] #[error("No such permission name: {0}")] InvalidPermissionName(String), + #[class(inherit)] #[error("{0}")] PathResolve(#[from] ::deno_permissions::PathResolveError), + #[class(uri)] #[error("{0}")] NetDescriptorParse(#[from] ::deno_permissions::NetDescriptorParseError), + #[class(inherit)] #[error("{0}")] SysDescriptorParse(#[from] ::deno_permissions::SysDescriptorParseError), + #[class(inherit)] #[error("{0}")] RunDescriptorParse(#[from] ::deno_permissions::RunDescriptorParseError), } diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index 422f229632..fc32f7e066 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -1,4 +1,20 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; +use std::ffi::OsString; +use std::io::Write; +#[cfg(unix)] +use std::os::unix::prelude::ExitStatusExt; +#[cfg(unix)] +use std::os::unix::process::CommandExt; +#[cfg(windows)] +use std::os::windows::process::CommandExt; +use std::path::Path; +use std::path::PathBuf; +use std::process::ExitStatus; +use std::rc::Rc; use deno_core::op2; use deno_core::serde_json; @@ -9,35 +25,19 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use deno_io::fs::FileResource; use deno_io::ChildStderrResource; use deno_io::ChildStdinResource; use deno_io::ChildStdoutResource; use deno_io::IntoRawIoHandle; +use deno_os::SignalError; use deno_permissions::PermissionsContainer; use deno_permissions::RunQueryDescriptor; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::HashMap; -use std::ffi::OsString; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; -use std::process::ExitStatus; -use std::rc::Rc; use tokio::process::Command; -#[cfg(windows)] -use std::os::windows::process::CommandExt; - -use crate::ops::signal::SignalError; -#[cfg(unix)] -use std::os::unix::prelude::ExitStatusExt; -#[cfg(unix)] -use std::os::unix::process::CommandExt; - pub const UNSTABLE_FEATURE_NAME: &str = "process"; #[derive(Copy, Clone, Eq, PartialEq, Deserialize)] @@ -107,8 +107,9 @@ impl StdioOrRid { match &self { StdioOrRid::Stdio(val) => Ok(val.as_stdio()), StdioOrRid::Rid(rid) => { - FileResource::with_file(state, *rid, |file| Ok(file.as_stdio()?)) - .map_err(ProcessError::Resource) + Ok(FileResource::with_file(state, *rid, |file| { + file.as_stdio().map_err(deno_error::JsErrorBox::from_err) + })?) } } } @@ -138,7 +139,9 @@ pub trait NpmProcessStateProvider: #[derive(Debug)] pub struct EmptyNpmProcessStateProvider; + impl NpmProcessStateProvider for EmptyNpmProcessStateProvider {} + deno_core::extension!( deno_process, ops = [ @@ -190,37 +193,73 @@ pub struct SpawnArgs { needs_npm_process_state: bool, } -#[derive(Debug, thiserror::Error)] +#[cfg(unix)] +deno_error::js_error_wrapper!(nix::Error, JsNixError, |err| { + match err { + nix::Error::ECHILD => "NotFound", + nix::Error::EINVAL => "TypeError", + nix::Error::ENOENT => "NotFound", + nix::Error::ENOTTY => "BadResource", + nix::Error::EPERM => "PermissionDenied", + nix::Error::ESRCH => "NotFound", + nix::Error::ELOOP => "FilesystemLoop", + nix::Error::ENOTDIR => "NotADirectory", + nix::Error::ENETUNREACH => "NetworkUnreachable", + nix::Error::EISDIR => "IsADirectory", + nix::Error::UnknownErrno => "Error", + &nix::Error::ENOTSUP => unreachable!(), + _ => "Error", + } +}); + +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ProcessError { + #[class(inherit)] #[error("Failed to spawn '{command}': {error}")] SpawnFailed { command: String, #[source] + #[inherit] error: Box, }, + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), #[cfg(unix)] + #[class(inherit)] #[error(transparent)] - Nix(nix::Error), + Nix(JsNixError), + #[class(inherit)] #[error("failed resolving cwd: {0}")] FailedResolvingCwd(#[source] std::io::Error), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] RunPermission(#[from] CheckRunPermissionError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(deno_core::error::ResourceError), + #[class(generic)] #[error(transparent)] BorrowMut(std::cell::BorrowMutError), + #[class(generic)] #[error(transparent)] Which(which::Error), + #[class(type)] #[error("Child process has already terminated.")] ChildProcessAlreadyTerminated, + #[class(type)] #[error("Invalid pid")] InvalidPid, + #[class(inherit)] #[error(transparent)] Signal(#[from] SignalError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), + #[class(type)] #[error("Missing cmd")] MissingCmd, // only for Deno.run } @@ -256,7 +295,7 @@ impl TryFrom for ChildStatus { success: false, code: 128 + signal, #[cfg(unix)] - signal: Some(crate::signal::signal_int_to_str(signal)?.to_string()), + signal: Some(deno_os::signal::signal_int_to_str(signal)?.to_string()), #[cfg(not(unix))] signal: None, } @@ -733,12 +772,14 @@ fn resolve_path(path: &str, cwd: &Path) -> PathBuf { deno_path_util::normalize_path(cwd.join(path)) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CheckRunPermissionError { + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error("{0}")] - Other(deno_core::error::AnyError), + Other(JsErrorBox), } fn check_run_permission( @@ -755,7 +796,7 @@ fn check_run_permission( // we don't allow users to launch subprocesses with any LD_ or DYLD_* // env vars set because this allows executing code (ex. LD_PRELOAD) return Err(CheckRunPermissionError::Other( - deno_core::error::custom_error( + JsErrorBox::new( "NotCapable", format!( "Requires --allow-run permissions to spawn subprocess with {0} environment variable{1}. Alternatively, spawn with {2} environment variable{1} unset.", @@ -1074,19 +1115,22 @@ mod deprecated { #[cfg(unix)] pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> { - let signo = crate::signal::signal_str_to_int(signal) + let signo = deno_os::signal::signal_str_to_int(signal) .map_err(SignalError::InvalidSignalStr)?; use nix::sys::signal::kill as unix_kill; use nix::sys::signal::Signal; use nix::unistd::Pid; - let sig = Signal::try_from(signo).map_err(ProcessError::Nix)?; - unix_kill(Pid::from_raw(pid), Some(sig)).map_err(ProcessError::Nix) + let sig = + Signal::try_from(signo).map_err(|e| ProcessError::Nix(JsNixError(e)))?; + unix_kill(Pid::from_raw(pid), Some(sig)) + .map_err(|e| ProcessError::Nix(JsNixError(e))) } #[cfg(not(unix))] pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> { use std::io::Error; use std::io::ErrorKind::NotFound; + use winapi::shared::minwindef::DWORD; use winapi::shared::minwindef::FALSE; use winapi::shared::minwindef::TRUE; @@ -1099,7 +1143,7 @@ mod deprecated { if !matches!(signal, "SIGKILL" | "SIGTERM") { Err( - SignalError::InvalidSignalStr(crate::signal::InvalidSignalStrError( + SignalError::InvalidSignalStr(deno_os::signal::InvalidSignalStrError( signal.to_string(), )) .into(), diff --git a/runtime/ops/runtime.rs b/runtime/ops/runtime.rs index 8d54783fc9..e95193167d 100644 --- a/runtime/ops/runtime.rs +++ b/runtime/ops/runtime.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::ModuleSpecifier; @@ -34,6 +34,7 @@ pub fn op_ppid() -> i64 { // - Apache License, Version 2.0 // - MIT license use std::mem; + use winapi::shared::minwindef::DWORD; use winapi::um::handleapi::CloseHandle; use winapi::um::handleapi::INVALID_HANDLE_VALUE; diff --git a/runtime/ops/tty.rs b/runtime/ops/tty.rs index 7849185faa..d9912839b8 100644 --- a/runtime/ops/tty.rs +++ b/runtime/ops/tty.rs @@ -1,9 +1,26 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +#[cfg(unix)] +use std::cell::RefCell; +#[cfg(unix)] +use std::collections::HashMap; use std::io::Error; +#[cfg(windows)] +use std::sync::Arc; use deno_core::op2; +#[cfg(windows)] +use deno_core::parking_lot::Mutex; use deno_core::OpState; +#[cfg(unix)] +use deno_core::ResourceId; +use deno_error::builtin_classes::GENERIC_ERROR; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; +#[cfg(windows)] +use deno_io::WinTtyState; +#[cfg(unix)] +use nix::sys::termios; use rustyline::config::Configurer; use rustyline::error::ReadlineError; use rustyline::Cmd; @@ -12,22 +29,6 @@ use rustyline::KeyCode; use rustyline::KeyEvent; use rustyline::Modifiers; -#[cfg(windows)] -use deno_core::parking_lot::Mutex; -#[cfg(windows)] -use deno_io::WinTtyState; -#[cfg(windows)] -use std::sync::Arc; - -#[cfg(unix)] -use deno_core::ResourceId; -#[cfg(unix)] -use nix::sys::termios; -#[cfg(unix)] -use std::cell::RefCell; -#[cfg(unix)] -use std::collections::HashMap; - #[cfg(unix)] #[derive(Default, Clone)] struct TtyModeStore( @@ -54,6 +55,9 @@ use winapi::shared::minwindef::DWORD; #[cfg(windows)] use winapi::um::wincon; +#[cfg(unix)] +use crate::ops::process::JsNixError; + deno_core::extension!( deno_tty, ops = [op_set_raw, op_console_size, op_read_line_prompt], @@ -63,17 +67,29 @@ deno_core::extension!( }, ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum TtyError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(inherit)] #[error("{0}")] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + Error, + ), #[cfg(unix)] + #[class(inherit)] #[error(transparent)] - Nix(nix::Error), + Nix(#[inherit] JsNixError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[inherit] JsErrorBox), } // ref: @@ -103,10 +119,7 @@ fn op_set_raw( is_raw: bool, cbreak: bool, ) -> Result<(), TtyError> { - let handle_or_fd = state - .resource_table - .get_fd(rid) - .map_err(TtyError::Resource)?; + let handle_or_fd = state.resource_table.get_fd(rid)?; // From https://github.com/kkawakam/rustyline/blob/master/src/tty/windows.rs // and https://github.com/kkawakam/rustyline/blob/master/src/tty/unix.rs @@ -115,14 +128,14 @@ fn op_set_raw( // Copyright (c) 2019 Timon. MIT license. #[cfg(windows)] { + use deno_error::JsErrorBox; use winapi::shared::minwindef::FALSE; - use winapi::um::consoleapi; let handle = handle_or_fd; if cbreak { - return Err(TtyError::Other(deno_core::error::not_supported())); + return Err(TtyError::Other(JsErrorBox::not_supported())); } let mut original_mode: DWORD = 0; @@ -267,8 +280,8 @@ fn op_set_raw( Some(mode) => mode, None => { // Save original mode. - let original_mode = - termios::tcgetattr(raw_fd).map_err(TtyError::Nix)?; + let original_mode = termios::tcgetattr(raw_fd) + .map_err(|e| TtyError::Nix(JsNixError(e)))?; tty_mode_store.set(rid, original_mode.clone()); original_mode } @@ -291,12 +304,12 @@ fn op_set_raw( raw.control_chars[termios::SpecialCharacterIndices::VMIN as usize] = 1; raw.control_chars[termios::SpecialCharacterIndices::VTIME as usize] = 0; termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &raw) - .map_err(TtyError::Nix)?; + .map_err(|e| TtyError::Nix(JsNixError(e)))?; } else { // Try restore saved mode. if let Some(mode) = tty_mode_store.take(rid) { termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &mode) - .map_err(TtyError::Nix)?; + .map_err(|e| TtyError::Nix(JsNixError(e)))?; } } @@ -314,10 +327,7 @@ fn op_console_size( result: &mut [u32], rid: u32, ) -> Result<(), TtyError> { - let fd = state - .resource_table - .get_fd(rid) - .map_err(TtyError::Resource)?; + let fd = state.resource_table.get_fd(rid)?; let size = console_size_from_fd(fd)?; result[0] = size.cols; result[1] = size.rows; @@ -435,12 +445,28 @@ mod tests { } } +deno_error::js_error_wrapper!(ReadlineError, JsReadlineError, |err| { + match err { + ReadlineError::Io(e) => e.get_class(), + ReadlineError::Eof => GENERIC_ERROR.into(), + ReadlineError::Interrupted => GENERIC_ERROR.into(), + #[cfg(unix)] + ReadlineError::Errno(e) => JsNixError(*e).get_class(), + ReadlineError::WindowResized => GENERIC_ERROR.into(), + #[cfg(windows)] + ReadlineError::Decode(_) => GENERIC_ERROR.into(), + #[cfg(windows)] + ReadlineError::SystemError(_) => GENERIC_ERROR.into(), + _ => GENERIC_ERROR.into(), + } +}); + #[op2] #[string] pub fn op_read_line_prompt( #[string] prompt_text: &str, #[string] default_value: &str, -) -> Result, ReadlineError> { +) -> Result, JsReadlineError> { let mut editor = Editor::<(), rustyline::history::DefaultHistory>::new() .expect("Failed to create editor."); @@ -460,6 +486,6 @@ pub fn op_read_line_prompt( Ok(None) } Err(ReadlineError::Eof) => Ok(None), - Err(err) => Err(err), + Err(err) => Err(JsReadlineError(err)), } } diff --git a/runtime/ops/web_worker.rs b/runtime/ops/web_worker.rs index d0c3eea668..5cde7d5373 100644 --- a/runtime/ops/web_worker.rs +++ b/runtime/ops/web_worker.rs @@ -1,19 +1,20 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod sync_fetch; -use crate::web_worker::WebWorkerInternalHandle; -use crate::web_worker::WebWorkerType; +use std::cell::RefCell; +use std::rc::Rc; + use deno_core::op2; use deno_core::CancelFuture; use deno_core::OpState; use deno_web::JsMessageData; use deno_web::MessagePortError; -use std::cell::RefCell; -use std::rc::Rc; +pub use sync_fetch::SyncFetchError; use self::sync_fetch::op_worker_sync_fetch; -pub use sync_fetch::SyncFetchError; +use crate::web_worker::WebWorkerInternalHandle; +use crate::web_worker::WebWorkerType; deno_core::extension!( deno_web_worker, diff --git a/runtime/ops/web_worker/sync_fetch.rs b/runtime/ops/web_worker/sync_fetch.rs index d1f133d3d2..4c5da428b2 100644 --- a/runtime/ops/web_worker/sync_fetch.rs +++ b/runtime/ops/web_worker/sync_fetch.rs @@ -1,9 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; -use crate::web_worker::WebWorkerInternalHandle; -use crate::web_worker::WebWorkerType; use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::url::Url; @@ -16,6 +14,9 @@ use hyper::body::Bytes; use serde::Deserialize; use serde::Serialize; +use crate::web_worker::WebWorkerInternalHandle; +use crate::web_worker::WebWorkerType; + // TODO(andreubotella) Properly parse the MIME type fn mime_type_essence(mime_type: &str) -> String { let essence = match mime_type.split_once(';') { @@ -25,30 +26,53 @@ fn mime_type_essence(mime_type: &str) -> String { essence.trim().to_ascii_lowercase() } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SyncFetchError { + #[class(type)] #[error("Blob URLs are not supported in this context.")] BlobUrlsNotSupportedInContext, + #[class(inherit)] #[error("{0}")] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + std::io::Error, + ), + #[class(type)] #[error("Invalid script URL")] InvalidScriptUrl, + #[class(type)] #[error("http status error: {0}")] InvalidStatusCode(http::StatusCode), + #[class(type)] #[error("Classic scripts with scheme {0}: are not supported in workers")] ClassicScriptSchemeUnsupportedInWorkers(String), + #[class(generic)] #[error("{0}")] InvalidUri(#[from] http::uri::InvalidUri), + #[class("DOMExceptionNetworkError")] #[error("Invalid MIME type {0:?}.")] InvalidMimeType(String), + #[class("DOMExceptionNetworkError")] #[error("Missing MIME type.")] MissingMimeType, + #[class(inherit)] #[error(transparent)] - Fetch(#[from] FetchError), + Fetch( + #[from] + #[inherit] + FetchError, + ), + #[class(inherit)] #[error(transparent)] - Join(#[from] tokio::task::JoinError), + Join( + #[from] + #[inherit] + tokio::task::JoinError, + ), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[inherit] deno_error::JsErrorBox), } #[derive(Serialize, Deserialize)] @@ -104,11 +128,7 @@ pub fn op_worker_sync_fetch( let (body, mime_type, res_url) = match script_url.scheme() { "http" | "https" => { - let mut req = http::Request::new( - http_body_util::Empty::new() - .map_err(|never| match never {}) - .boxed(), - ); + let mut req = http::Request::new(deno_fetch::ReqBody::empty()); *req.uri_mut() = script_url.as_str().parse()?; let resp = diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs index 131bad1962..c77b3af694 100644 --- a/runtime/ops/worker_host.rs +++ b/runtime/ops/worker_host.rs @@ -1,15 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; +use std::sync::Arc; -use crate::ops::TestingFeaturesEnabled; -use crate::web_worker::run_web_worker; -use crate::web_worker::SendableWebWorkerHandle; -use crate::web_worker::WebWorker; -use crate::web_worker::WebWorkerHandle; -use crate::web_worker::WebWorkerType; -use crate::web_worker::WorkerControlEvent; -use crate::web_worker::WorkerId; -use crate::web_worker::WorkerMetadata; -use crate::worker::FormatJsErrorFn; use deno_core::op2; use deno_core::serde::Deserialize; use deno_core::CancelFuture; @@ -22,10 +17,17 @@ use deno_web::deserialize_js_transferables; use deno_web::JsMessageData; use deno_web::MessagePortError; use log::debug; -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; -use std::sync::Arc; + +use crate::ops::TestingFeaturesEnabled; +use crate::web_worker::run_web_worker; +use crate::web_worker::SendableWebWorkerHandle; +use crate::web_worker::WebWorker; +use crate::web_worker::WebWorkerHandle; +use crate::web_worker::WebWorkerType; +use crate::web_worker::WorkerControlEvent; +use crate::web_worker::WorkerId; +use crate::web_worker::WorkerMetadata; +use crate::worker::FormatJsErrorFn; pub const UNSTABLE_FEATURE_NAME: &str = "worker-options"; @@ -118,16 +120,21 @@ pub struct CreateWorkerArgs { close_on_idle: bool, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CreateWorkerError { + #[class("DOMExceptionNotSupportedError")] #[error("Classic workers are not supported.")] ClassicWorkers, + #[class(inherit)] #[error(transparent)] Permission(deno_permissions::ChildPermissionError), + #[class(inherit)] #[error(transparent)] ModuleResolution(#[from] deno_core::ModuleResolutionError), + #[class(inherit)] #[error(transparent)] MessagePort(#[from] MessagePortError), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), } diff --git a/runtime/permissions.rs b/runtime/permissions.rs index e8460e03f8..8b65c6b1b0 100644 --- a/runtime/permissions.rs +++ b/runtime/permissions.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; use std::path::PathBuf; @@ -21,13 +21,17 @@ use deno_permissions::SysDescriptorParseError; use deno_permissions::WriteDescriptor; #[derive(Debug)] -pub struct RuntimePermissionDescriptorParser { - fs: deno_fs::FileSystemRc, +pub struct RuntimePermissionDescriptorParser< + TSys: sys_traits::EnvCurrentDir + Send + Sync, +> { + sys: TSys, } -impl RuntimePermissionDescriptorParser { - pub fn new(fs: deno_fs::FileSystemRc) -> Self { - Self { fs } +impl + RuntimePermissionDescriptorParser +{ + pub fn new(sys: TSys) -> Self { + Self { sys } } fn resolve_from_cwd(&self, path: &str) -> Result { @@ -45,14 +49,15 @@ impl RuntimePermissionDescriptorParser { fn resolve_cwd(&self) -> Result { self - .fs - .cwd() - .map_err(|e| PathResolveError::CwdResolve(e.into_io_error())) + .sys + .env_current_dir() + .map_err(PathResolveError::CwdResolve) } } -impl deno_permissions::PermissionDescriptorParser - for RuntimePermissionDescriptorParser +impl + deno_permissions::PermissionDescriptorParser + for RuntimePermissionDescriptorParser { fn parse_read_descriptor( &self, @@ -151,16 +156,14 @@ impl deno_permissions::PermissionDescriptorParser #[cfg(test)] mod test { - use std::sync::Arc; - - use deno_fs::RealFs; use deno_permissions::PermissionDescriptorParser; use super::*; #[test] fn test_handle_empty_value() { - let parser = RuntimePermissionDescriptorParser::new(Arc::new(RealFs)); + let parser = + RuntimePermissionDescriptorParser::new(sys_traits::impls::RealSys); assert!(parser.parse_read_descriptor("").is_err()); assert!(parser.parse_write_descriptor("").is_err()); assert!(parser.parse_env_descriptor("").is_err()); diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index dc46b03310..52501ee197 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_permissions" -version = "0.42.0" +version = "0.44.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] capacity_builder.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_path_util.workspace = true deno_terminal.workspace = true fqdn = "0.3.4" diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index a1a217738d..3a357d2d44 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -1,4 +1,17 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::collections::HashSet; +use std::ffi::OsStr; +use std::fmt; +use std::fmt::Debug; +use std::hash::Hash; +use std::net::IpAddr; +use std::net::Ipv6Addr; +use std::path::Path; +use std::path::PathBuf; +use std::string::ToString; +use std::sync::Arc; use capacity_builder::StringBuilder; use deno_core::parking_lot::Mutex; @@ -15,34 +28,22 @@ use deno_path_util::url_to_file_path; use deno_terminal::colors; use fqdn::FQDN; use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::collections::HashSet; -use std::ffi::OsStr; -use std::fmt; -use std::fmt::Debug; -use std::hash::Hash; -use std::net::IpAddr; -use std::net::Ipv6Addr; -use std::path::Path; -use std::path::PathBuf; -use std::string::ToString; -use std::sync::Arc; pub mod prompter; use prompter::permission_prompt; -use prompter::PERMISSION_EMOJI; - pub use prompter::set_prompt_callbacks; pub use prompter::set_prompter; pub use prompter::PermissionPrompter; pub use prompter::PromptCallback; pub use prompter::PromptResponse; +use prompter::PERMISSION_EMOJI; #[derive(Debug, thiserror::Error)] -#[error("Requires {access}, {}", format_permission_error(.name))] -pub struct PermissionDeniedError { - pub access: String, - pub name: &'static str, +pub enum PermissionDeniedError { + #[error("Requires {access}, {}", format_permission_error(.name))] + Retryable { access: String, name: &'static str }, + #[error("Requires {access}, which cannot be granted in this environment")] + Fatal { access: String }, } fn format_permission_error(name: &'static str) -> String { @@ -144,11 +145,11 @@ impl PermissionState { ) } - fn error( + fn retryable_error( name: &'static str, info: impl FnOnce() -> Option, ) -> PermissionDeniedError { - PermissionDeniedError { + PermissionDeniedError::Retryable { access: Self::fmt_access(name, info), name, } @@ -182,7 +183,7 @@ impl PermissionState { PermissionState::Prompt if prompt => { let msg = { let info = info(); - StringBuilder::build(|builder| { + StringBuilder::::build(|builder| { builder.append(name); builder.append(" access"); if let Some(info) = &info { @@ -201,10 +202,12 @@ impl PermissionState { Self::log_perm_access(name, info); (Ok(()), true, true) } - PromptResponse::Deny => (Err(Self::error(name, info)), true, false), + PromptResponse::Deny => { + (Err(Self::retryable_error(name, info)), true, false) + } } } - _ => (Err(Self::error(name, info)), false, false), + _ => (Err(Self::retryable_error(name, info)), false, false), } } } @@ -495,7 +498,7 @@ impl UnaryPermission { } let maybe_formatted_display_name = desc.map(|d| format_display_name(d.display_name())); - let message = StringBuilder::build(|builder| { + let message = StringBuilder::::build(|builder| { builder.append(TQuery::flag_name()); builder.append(" access"); if let Some(display_name) = &maybe_formatted_display_name { @@ -816,7 +819,8 @@ pub enum Host { Ip(IpAddr), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(uri)] pub enum HostParseError { #[error("invalid IPv6 address: '{0}'")] InvalidIpv6(String), @@ -951,10 +955,12 @@ pub enum NetDescriptorParseError { Host(#[from] HostParseError), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum NetDescriptorFromUrlParseError { + #[class(type)] #[error("Missing host in url: '{0}'")] MissingHost(Url), + #[class(inherit)] #[error("{0}")] Host(#[from] HostParseError), } @@ -1321,10 +1327,12 @@ pub enum RunQueryDescriptor { Name(String), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PathResolveError { + #[class(inherit)] #[error("failed resolving cwd: {0}")] CwdResolve(#[source] std::io::Error), + #[class(generic)] #[error("Empty path is not allowed")] EmptyPath, } @@ -1481,12 +1489,15 @@ pub enum AllowRunDescriptorParseResult { Descriptor(AllowRunDescriptor), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum RunDescriptorParseError { + #[class(generic)] #[error("{0}")] Which(#[from] which::Error), + #[class(inherit)] #[error("{0}")] PathResolve(#[from] PathResolveError), + #[class(generic)] #[error("Empty run query is not allowed")] EmptyRunQuery, } @@ -1507,11 +1518,10 @@ impl AllowRunDescriptor { match which::which_in(text, std::env::var_os("PATH"), cwd) { Ok(path) => path, Err(err) => match err { - which::Error::BadAbsolutePath | which::Error::BadRelativePath => { + which::Error::CannotGetCurrentDirAndPathListEmpty => { return Err(err); } which::Error::CannotFindBinaryPath - | which::Error::CannotGetCurrentDir | which::Error::CannotCanonicalize => { return Ok(AllowRunDescriptorParseResult::Unresolved(Box::new(err))) } @@ -1571,10 +1581,12 @@ fn denies_run_name(name: &str, cmd_path: &Path) -> bool { suffix.is_empty() || suffix.starts_with('.') } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SysDescriptorParseError { + #[class(type)] #[error("unknown system info kind \"{0}\"")] - InvalidKind(String), // TypeError + InvalidKind(String), + #[class(generic)] #[error("Empty sys not allowed")] Empty, // Error } @@ -2299,34 +2311,46 @@ pub enum CheckSpecifierKind { Dynamic, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ChildPermissionError { + #[class("NotCapable")] #[error("Can't escalate parent thread permissions")] Escalation, + #[class(inherit)] #[error("{0}")] PathResolve(#[from] PathResolveError), + #[class(uri)] #[error("{0}")] NetDescriptorParse(#[from] NetDescriptorParseError), + #[class(generic)] #[error("{0}")] EnvDescriptorParse(#[from] EnvDescriptorParseError), + #[class(inherit)] #[error("{0}")] SysDescriptorParse(#[from] SysDescriptorParseError), + #[class(inherit)] #[error("{0}")] RunDescriptorParse(#[from] RunDescriptorParseError), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PermissionCheckError { + #[class("NotCapable")] #[error(transparent)] PermissionDenied(#[from] PermissionDeniedError), + #[class(uri)] #[error("Invalid file path.\n Specifier: {0}")] InvalidFilePath(Url), + #[class(inherit)] #[error(transparent)] NetDescriptorForUrlParse(#[from] NetDescriptorFromUrlParseError), + #[class(inherit)] #[error(transparent)] SysDescriptorParse(#[from] SysDescriptorParseError), + #[class(inherit)] #[error(transparent)] PathResolve(#[from] PathResolveError), + #[class(uri)] #[error(transparent)] HostParse(#[from] HostParseError), } @@ -3688,11 +3712,13 @@ pub fn is_standalone() -> bool { #[cfg(test)] mod tests { - use super::*; + use std::net::Ipv4Addr; + use deno_core::serde_json::json; use fqdn::fqdn; use prompter::tests::*; - use std::net::Ipv4Addr; + + use super::*; // Creates vector of strings, Vec macro_rules! svec { diff --git a/runtime/permissions/prompter.rs b/runtime/permissions/prompter.rs index 0272744cc2..94384427d1 100644 --- a/runtime/permissions/prompter.rs +++ b/runtime/permissions/prompter.rs @@ -1,10 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::is_standalone; -use deno_core::error::JsStackFrame; -use deno_core::parking_lot::Mutex; -use deno_terminal::colors; -use once_cell::sync::Lazy; use std::fmt::Write; use std::io::BufRead; use std::io::IsTerminal; @@ -12,6 +7,13 @@ use std::io::StderrLock; use std::io::StdinLock; use std::io::Write as IoWrite; +use deno_core::error::JsStackFrame; +use deno_core::parking_lot::Mutex; +use deno_terminal::colors; +use once_cell::sync::Lazy; + +use crate::is_standalone; + /// Helper function to make control characters visible so users can see the underlying filename. fn escape_control_characters(s: &str) -> std::borrow::Cow { if !s.contains(|c: char| c.is_ascii_control() || c.is_control()) { @@ -489,10 +491,11 @@ impl PermissionPrompter for TtyPrompter { #[cfg(test)] pub mod tests { - use super::*; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; + use super::*; + pub struct TestPrompter; impl PermissionPrompter for TestPrompter { diff --git a/runtime/shared.rs b/runtime/shared.rs index f7d76f67a7..0e747b0565 100644 --- a/runtime/shared.rs +++ b/runtime/shared.rs @@ -1,16 +1,17 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Utilities shared between `build.rs` and the rest of the crate. +use std::path::Path; + use deno_ast::MediaType; use deno_ast::ParseParams; use deno_ast::SourceMapOption; -use deno_core::error::AnyError; use deno_core::extension; use deno_core::Extension; use deno_core::ModuleCodeString; use deno_core::ModuleName; use deno_core::SourceMapData; -use std::path::Path; +use deno_error::JsErrorBox; extension!(runtime, deps = [ @@ -41,10 +42,8 @@ extension!(runtime, "06_util.js", "10_permissions.js", "11_workers.js", - "30_os.js", "40_fs_events.js", "40_process.js", - "40_signals.js", "40_tty.js", "41_prompt.js", "90_deno_ns.js", @@ -63,10 +62,21 @@ extension!(runtime, } ); +deno_error::js_error_wrapper!( + deno_ast::ParseDiagnostic, + JsParseDiagnostic, + "Error" +); +deno_error::js_error_wrapper!( + deno_ast::TranspileError, + JsTranspileError, + "Error" +); + pub fn maybe_transpile_source( name: ModuleName, source: ModuleCodeString, -) -> Result<(ModuleCodeString, Option), AnyError> { +) -> Result<(ModuleCodeString, Option), JsErrorBox> { // Always transpile `node:` built-in modules, since they might be TypeScript. let media_type = if name.starts_with("node:") { MediaType::TypeScript @@ -91,7 +101,8 @@ pub fn maybe_transpile_source( capture_tokens: false, scope_analysis: false, maybe_syntax: None, - })?; + }) + .map_err(|e| JsErrorBox::from_err(JsParseDiagnostic(e)))?; let transpiled_source = parsed .transpile( &deno_ast::TranspileOptions { @@ -107,7 +118,8 @@ pub fn maybe_transpile_source( }, ..Default::default() }, - )? + ) + .map_err(|e| JsErrorBox::from_err(JsTranspileError(e)))? .into_source(); let maybe_source_map: Option = transpiled_source diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index 48c500ef74..08ea17a986 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -1,9 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::io::Write; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; -use crate::ops; -use crate::ops::bootstrap::SnapshotOptions; -use crate::shared::maybe_transpile_source; -use crate::shared::runtime; use deno_cache::SqliteBackedCache; use deno_core::snapshot::*; use deno_core::v8; @@ -11,12 +14,13 @@ use deno_core::Extension; use deno_http::DefaultHttpPropertyExtractor; use deno_io::fs::FsError; use deno_permissions::PermissionCheckError; -use std::borrow::Cow; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; +use deno_resolver::npm::DenoInNpmPackageChecker; +use deno_resolver::npm::NpmResolver; + +use crate::ops; +use crate::ops::bootstrap::SnapshotOptions; +use crate::shared::maybe_transpile_source; +use crate::shared::runtime; #[derive(Clone)] struct Permissions; @@ -306,7 +310,13 @@ pub fn create_runtime_snapshot( ), deno_io::deno_io::init_ops_and_esm(Default::default()), deno_fs::deno_fs::init_ops_and_esm::(fs.clone()), - deno_node::deno_node::init_ops_and_esm::(None, fs.clone()), + deno_os::deno_os::init_ops_and_esm(Default::default()), + deno_node::deno_node::init_ops_and_esm::< + Permissions, + DenoInNpmPackageChecker, + NpmResolver, + sys_traits::impls::RealSys, + >(None, fs.clone()), runtime::init_ops_and_esm(), ops::runtime::deno_runtime::init_ops("deno:runtime".parse().unwrap()), ops::worker_host::deno_worker_host::init_ops( @@ -314,10 +324,8 @@ pub fn create_runtime_snapshot( None, ), ops::fs_events::deno_fs_events::init_ops(), - ops::os::deno_os::init_ops(Default::default()), ops::permissions::deno_permissions::init_ops(), ops::process::deno_process::init_ops(None), - ops::signal::deno_signal::init_ops(), ops::tty::deno_tty::init_ops(), ops::http::deno_http_runtime::init_ops(), ops::bootstrap::deno_bootstrap::init_ops(Some(snapshot_options)), diff --git a/runtime/tokio_util.rs b/runtime/tokio_util.rs index aa0282ece8..370b8a6d92 100644 --- a/runtime/tokio_util.rs +++ b/runtime/tokio_util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Debug; use std::str::FromStr; diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index e3a69b39c0..e4ea42a2f7 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -1,10 +1,19 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::cell::RefCell; +use std::fmt; +use std::rc::Rc; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::task::Context; +use std::task::Poll; use deno_broadcast_channel::InMemoryBroadcastChannel; use deno_cache::CreateCache; use deno_cache::SqliteBackedCache; -use deno_core::error::AnyError; -use deno_core::error::JsError; +use deno_core::error::CoreError; use deno_core::futures::channel::mpsc; use deno_core::futures::future::poll_fn; use deno_core::futures::stream::StreamExt; @@ -19,7 +28,6 @@ use deno_core::CompiledWasmModuleStore; use deno_core::DetachedBuffer; use deno_core::Extension; use deno_core::FeatureChecker; -use deno_core::GetErrorClassFn; use deno_core::JsRuntime; use deno_core::ModuleCodeString; use deno_core::ModuleId; @@ -33,6 +41,7 @@ use deno_fs::FileSystem; use deno_http::DefaultHttpPropertyExtractor; use deno_io::Stdio; use deno_kv::dynamic::MultiBackendDbHandler; +use deno_node::ExtNodeSys; use deno_node::NodeExtInitServices; use deno_permissions::PermissionsContainer; use deno_terminal::colors; @@ -45,20 +54,12 @@ use deno_web::JsMessageData; use deno_web::MessagePort; use deno_web::Transferable; use log::debug; -use std::cell::RefCell; -use std::fmt; -use std::rc::Rc; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicU32; -use std::sync::atomic::Ordering; -use std::sync::Arc; -use std::task::Context; -use std::task::Poll; +use node_resolver::InNpmPackageChecker; +use node_resolver::NpmPackageFolderResolver; use crate::inspector_server::InspectorServer; use crate::ops; use crate::ops::process::NpmProcessStateProviderRc; -use crate::ops::worker_host::WorkersTable; use crate::shared::maybe_transpile_source; use crate::shared::runtime; use crate::tokio_util::create_and_run_current_thread; @@ -104,8 +105,7 @@ pub enum WebWorkerType { /// Events that are sent to host from child /// worker. pub enum WorkerControlEvent { - Error(AnyError), - TerminalError(AnyError), + TerminalError(CoreError), Close, } @@ -118,15 +118,13 @@ impl Serialize for WorkerControlEvent { { let type_id = match &self { WorkerControlEvent::TerminalError(_) => 1_i32, - WorkerControlEvent::Error(_) => 2_i32, WorkerControlEvent::Close => 3_i32, }; match self { - WorkerControlEvent::TerminalError(error) - | WorkerControlEvent::Error(error) => { - let value = match error.downcast_ref::() { - Some(js_error) => { + WorkerControlEvent::TerminalError(error) => { + let value = match error { + CoreError::Js(js_error) => { let frame = js_error.frames.iter().find(|f| match &f.file_name { Some(s) => !s.trim_start_matches('[').starts_with("ext:"), None => false, @@ -138,7 +136,7 @@ impl Serialize for WorkerControlEvent { "columnNumber": frame.map(|f| f.column_number.as_ref()), }) } - None => json!({ + _ => json!({ "message": error.to_string(), }), }; @@ -338,7 +336,11 @@ fn create_handles( (internal_handle, external_handle) } -pub struct WebWorkerServiceOptions { +pub struct WebWorkerServiceOptions< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, +> { pub blob_store: Arc, pub broadcast_channel: InMemoryBroadcastChannel, pub compiled_wasm_module_store: Option, @@ -346,7 +348,13 @@ pub struct WebWorkerServiceOptions { pub fs: Arc, pub maybe_inspector_server: Option>, pub module_loader: Rc, - pub node_services: Option, + pub node_services: Option< + NodeExtInitServices< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, + >, pub npm_process_state_provider: Option, pub permissions: PermissionsContainer, pub root_cert_store_provider: Option>, @@ -367,7 +375,6 @@ pub struct WebWorkerOptions { pub create_web_worker_cb: Arc, pub format_js_error_fn: Option>, pub worker_type: WebWorkerType, - pub get_error_class_fn: Option, pub cache_storage_dir: Option, pub stdio: Stdio, pub strace_ops: Option>, @@ -385,7 +392,6 @@ pub struct WebWorker { pub js_runtime: JsRuntime, pub name: String, close_on_idle: bool, - has_executed_main_module: bool, internal_handle: WebWorkerInternalHandle, pub worker_type: WebWorkerType, pub main_module: ModuleSpecifier, @@ -404,8 +410,16 @@ impl Drop for WebWorker { } impl WebWorker { - pub fn bootstrap_from_options( - services: WebWorkerServiceOptions, + pub fn bootstrap_from_options< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, + >( + services: WebWorkerServiceOptions< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, options: WebWorkerOptions, ) -> (Self, SendableWebWorkerHandle) { let (mut worker, handle, bootstrap_options) = @@ -414,8 +428,16 @@ impl WebWorker { (worker, handle) } - fn from_options( - services: WebWorkerServiceOptions, + fn from_options< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, + >( + services: WebWorkerServiceOptions< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, mut options: WebWorkerOptions, ) -> (Self, SendableWebWorkerHandle, BootstrapOptions) { deno_core::extension!(deno_permissions_web_worker, @@ -506,10 +528,13 @@ impl WebWorker { deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(), ), - deno_node::deno_node::init_ops_and_esm::( - services.node_services, - services.fs, - ), + deno_os::deno_os_worker::init_ops_and_esm(), + deno_node::deno_node::init_ops_and_esm::< + PermissionsContainer, + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >(services.node_services, services.fs), // Runtime ops that are always initialized for WebWorkers ops::runtime::deno_runtime::init_ops_and_esm(options.main_module.clone()), ops::worker_host::deno_worker_host::init_ops_and_esm( @@ -517,12 +542,10 @@ impl WebWorker { options.format_js_error_fn, ), ops::fs_events::deno_fs_events::init_ops_and_esm(), - ops::os::deno_os_worker::init_ops_and_esm(), ops::permissions::deno_permissions::init_ops_and_esm(), ops::process::deno_process::init_ops_and_esm( services.npm_process_state_provider, ), - ops::signal::deno_signal::init_ops_and_esm(), ops::tty::deno_tty::init_ops_and_esm(), ops::http::deno_http_runtime::init_ops_and_esm(), ops::bootstrap::deno_bootstrap::init_ops_and_esm( @@ -569,7 +592,6 @@ impl WebWorker { module_loader: Some(services.module_loader), startup_snapshot: options.startup_snapshot, create_params: options.create_params, - get_error_class_fn: options.get_error_class_fn, shared_array_buffer_store: services.shared_array_buffer_store, compiled_wasm_module_store: services.compiled_wasm_module_store, extensions, @@ -658,7 +680,6 @@ impl WebWorker { has_message_event_listener_fn: None, bootstrap_fn_global: Some(bootstrap_fn_global), close_on_idle: options.close_on_idle, - has_executed_main_module: false, maybe_worker_metadata: options.maybe_worker_metadata, }, external_handle, @@ -738,7 +759,7 @@ impl WebWorker { &mut self, name: &'static str, source_code: ModuleCodeString, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { self.js_runtime.execute_script(name, source_code)?; Ok(()) } @@ -747,7 +768,7 @@ impl WebWorker { pub async fn preload_main_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { self.js_runtime.load_main_es_module(module_specifier).await } @@ -755,7 +776,7 @@ impl WebWorker { pub async fn preload_side_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { self.js_runtime.load_side_es_module(module_specifier).await } @@ -766,7 +787,7 @@ impl WebWorker { pub async fn execute_side_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let id = self.preload_side_module(module_specifier).await?; let mut receiver = self.js_runtime.mod_evaluate(id); tokio::select! { @@ -790,7 +811,7 @@ impl WebWorker { pub async fn execute_main_module( &mut self, id: ModuleId, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let mut receiver = self.js_runtime.mod_evaluate(id); let poll_options = PollEventLoopOptions::default(); @@ -799,7 +820,6 @@ impl WebWorker { maybe_result = &mut receiver => { debug!("received worker module evaluate {:#?}", maybe_result); - self.has_executed_main_module = true; maybe_result } @@ -817,7 +837,7 @@ impl WebWorker { &mut self, cx: &mut Context, poll_options: PollEventLoopOptions, - ) -> Poll> { + ) -> Poll> { // If awakened because we are terminating, just return Ok if self.internal_handle.terminate_if_needed() { return Poll::Ready(Ok(())); @@ -837,6 +857,9 @@ impl WebWorker { } if self.close_on_idle { + if self.has_message_event_listener() { + return Poll::Pending; + } return Poll::Ready(Ok(())); } @@ -851,29 +874,14 @@ impl WebWorker { Poll::Ready(Ok(())) } } - Poll::Pending => { - // This is special code path for workers created from `node:worker_threads` - // module that have different semantics than Web workers. - // We want the worker thread to terminate automatically if we've done executing - // Top-Level await, there are no child workers spawned by that workers - // and there's no "message" event listener. - if self.close_on_idle - && self.has_executed_main_module - && !self.has_child_workers() - && !self.has_message_event_listener() - { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } + Poll::Pending => Poll::Pending, } } pub async fn run_event_loop( &mut self, poll_options: PollEventLoopOptions, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { poll_fn(|cx| self.poll_event_loop(cx, poll_options)).await } @@ -904,26 +912,17 @@ impl WebWorker { None => false, } } - - fn has_child_workers(&mut self) -> bool { - !self - .js_runtime - .op_state() - .borrow() - .borrow::() - .is_empty() - } } fn print_worker_error( - error: &AnyError, + error: &CoreError, name: &str, format_js_error_fn: Option<&FormatJsErrorFn>, ) { let error_str = match format_js_error_fn { - Some(format_js_error_fn) => match error.downcast_ref::() { - Some(js_error) => format_js_error_fn(js_error), - None => error.to_string(), + Some(format_js_error_fn) => match error { + CoreError::Js(js_error) => format_js_error_fn(js_error), + _ => error.to_string(), }, None => error.to_string(), }; @@ -942,7 +941,7 @@ pub fn run_web_worker( specifier: ModuleSpecifier, mut maybe_source_code: Option, format_js_error_fn: Option>, -) -> Result<(), AnyError> { +) -> Result<(), CoreError> { let name = worker.name.to_string(); // TODO(bartlomieju): run following block using "select!" diff --git a/runtime/worker.rs b/runtime/worker.rs index 46fbd7b43f..cfcadccc46 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -1,10 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; use std::rc::Rc; use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicI32; -use std::sync::atomic::Ordering::Relaxed; use std::sync::Arc; use std::time::Duration; use std::time::Instant; @@ -12,14 +10,13 @@ use std::time::Instant; use deno_broadcast_channel::InMemoryBroadcastChannel; use deno_cache::CreateCache; use deno_cache::SqliteBackedCache; -use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::error::JsError; use deno_core::merge_op_metrics; use deno_core::v8; use deno_core::CompiledWasmModuleStore; use deno_core::Extension; use deno_core::FeatureChecker; -use deno_core::GetErrorClassFn; use deno_core::InspectorSessionKind; use deno_core::InspectorSessionOptions; use deno_core::JsRuntime; @@ -39,12 +36,16 @@ use deno_fs::FileSystem; use deno_http::DefaultHttpPropertyExtractor; use deno_io::Stdio; use deno_kv::dynamic::MultiBackendDbHandler; +use deno_node::ExtNodeSys; use deno_node::NodeExtInitServices; +use deno_os::ExitCode; use deno_permissions::PermissionsContainer; use deno_tls::RootCertStoreProvider; use deno_tls::TlsKeys; use deno_web::BlobStore; use log::debug; +use node_resolver::InNpmPackageChecker; +use node_resolver::NpmPackageFolderResolver; use crate::code_cache::CodeCache; use crate::code_cache::CodeCacheType; @@ -58,10 +59,10 @@ use crate::BootstrapOptions; pub type FormatJsErrorFn = dyn Fn(&JsError) -> String + Sync + Send; pub fn import_meta_resolve_callback( - loader: &dyn deno_core::ModuleLoader, + loader: &dyn ModuleLoader, specifier: String, referrer: String, -) -> Result { +) -> Result { loader.resolve( &specifier, &referrer, @@ -95,19 +96,6 @@ pub fn validate_import_attributes_callback( } } -#[derive(Clone, Default)] -pub struct ExitCode(Arc); - -impl ExitCode { - pub fn get(&self) -> i32 { - self.0.load(Relaxed) - } - - pub fn set(&mut self, code: i32) { - self.0.store(code, Relaxed); - } -} - /// This worker is created and used by almost all /// subcommands in Deno executable. /// @@ -128,7 +116,11 @@ pub struct MainWorker { dispatch_process_exit_event_fn_global: v8::Global, } -pub struct WorkerServiceOptions { +pub struct WorkerServiceOptions< + TInNpmPackageChecker: InNpmPackageChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TExtNodeSys: ExtNodeSys, +> { pub blob_store: Arc, pub broadcast_channel: InMemoryBroadcastChannel, pub feature_checker: Arc, @@ -139,7 +131,13 @@ pub struct WorkerServiceOptions { /// If not provided runtime will error if code being /// executed tries to load modules. pub module_loader: Rc, - pub node_services: Option, + pub node_services: Option< + NodeExtInitServices< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, + >, pub npm_process_state_provider: Option, pub permissions: PermissionsContainer, pub root_cert_store_provider: Option>, @@ -201,9 +199,6 @@ pub struct WorkerOptions { /// If Some, print a low-level trace output for ops matching the given patterns. pub strace_ops: Option>, - /// Allows to map error type to a string "class" used to represent - /// error in JavaScript. - pub get_error_class_fn: Option, pub cache_storage_dir: Option, pub origin_storage_dir: Option, pub stdio: Stdio, @@ -224,7 +219,6 @@ impl Default for WorkerOptions { strace_ops: Default::default(), maybe_inspector_server: Default::default(), format_js_error_fn: Default::default(), - get_error_class_fn: Default::default(), origin_storage_dir: Default::default(), cache_storage_dir: Default::default(), extensions: Default::default(), @@ -304,9 +298,17 @@ pub fn create_op_metrics( } impl MainWorker { - pub fn bootstrap_from_options( + pub fn bootstrap_from_options< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, + >( main_module: ModuleSpecifier, - services: WorkerServiceOptions, + services: WorkerServiceOptions< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, options: WorkerOptions, ) -> Self { let (mut worker, bootstrap_options) = @@ -315,9 +317,17 @@ impl MainWorker { worker } - fn from_options( + fn from_options< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, + >( main_module: ModuleSpecifier, - services: WorkerServiceOptions, + services: WorkerServiceOptions< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, mut options: WorkerOptions, ) -> (Self, BootstrapOptions) { deno_core::extension!(deno_permissions_worker, @@ -339,7 +349,7 @@ impl MainWorker { // Permissions: many ops depend on this let enable_testing_features = options.bootstrap.enable_testing_features; - let exit_code = ExitCode(Arc::new(AtomicI32::new(0))); + let exit_code = ExitCode::default(); let create_cache = options.cache_storage_dir.map(|storage_dir| { let create_cache_fn = move || SqliteBackedCache::new(storage_dir.clone()); CreateCache(Arc::new(create_cache_fn)) @@ -417,10 +427,13 @@ impl MainWorker { deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(), ), - deno_node::deno_node::init_ops_and_esm::( - services.node_services, - services.fs, - ), + deno_os::deno_os::init_ops_and_esm(exit_code.clone()), + deno_node::deno_node::init_ops_and_esm::< + PermissionsContainer, + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >(services.node_services, services.fs), // Ops from this crate ops::runtime::deno_runtime::init_ops_and_esm(main_module.clone()), ops::worker_host::deno_worker_host::init_ops_and_esm( @@ -428,12 +441,10 @@ impl MainWorker { options.format_js_error_fn.clone(), ), ops::fs_events::deno_fs_events::init_ops_and_esm(), - ops::os::deno_os::init_ops_and_esm(exit_code.clone()), ops::permissions::deno_permissions::init_ops_and_esm(), ops::process::deno_process::init_ops_and_esm( services.npm_process_state_provider, ), - ops::signal::deno_signal::init_ops_and_esm(), ops::tty::deno_tty::init_ops_and_esm(), ops::http::deno_http_runtime::init_ops_and_esm(), ops::bootstrap::deno_bootstrap::init_ops_and_esm( @@ -488,7 +499,6 @@ impl MainWorker { startup_snapshot: options.startup_snapshot, create_params: options.create_params, skip_op_registration: options.skip_op_registration, - get_error_class_fn: options.get_error_class_fn, shared_array_buffer_store: services.shared_array_buffer_store.clone(), compiled_wasm_module_store: services.compiled_wasm_module_store.clone(), extensions, @@ -707,7 +717,7 @@ impl MainWorker { &mut self, script_name: &'static str, source_code: ModuleCodeString, - ) -> Result, AnyError> { + ) -> Result, CoreError> { self.js_runtime.execute_script(script_name, source_code) } @@ -715,7 +725,7 @@ impl MainWorker { pub async fn preload_main_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { self.js_runtime.load_main_es_module(module_specifier).await } @@ -723,7 +733,7 @@ impl MainWorker { pub async fn preload_side_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { self.js_runtime.load_side_es_module(module_specifier).await } @@ -731,7 +741,7 @@ impl MainWorker { pub async fn evaluate_module( &mut self, id: ModuleId, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { self.wait_for_inspector_session(); let mut receiver = self.js_runtime.mod_evaluate(id); tokio::select! { @@ -756,7 +766,7 @@ impl MainWorker { pub async fn run_up_to_duration( &mut self, duration: Duration, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { match tokio::time::timeout( duration, self @@ -775,7 +785,7 @@ impl MainWorker { pub async fn execute_side_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let id = self.preload_side_module(module_specifier).await?; self.evaluate_module(id).await } @@ -786,7 +796,7 @@ impl MainWorker { pub async fn execute_main_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let id = self.preload_main_module(module_specifier).await?; self.evaluate_module(id).await } @@ -817,10 +827,10 @@ impl MainWorker { pub async fn run_event_loop( &mut self, wait_for_inspector: bool, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { self .js_runtime - .run_event_loop(deno_core::PollEventLoopOptions { + .run_event_loop(PollEventLoopOptions { wait_for_inspector, ..Default::default() }) @@ -836,7 +846,7 @@ impl MainWorker { /// Dispatches "load" event to the JavaScript runtime. /// /// Does not poll event loop, and thus not await any of the "load" event handlers. - pub fn dispatch_load_event(&mut self) -> Result<(), AnyError> { + pub fn dispatch_load_event(&mut self) -> Result<(), JsError> { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_load_event_fn = @@ -845,7 +855,7 @@ impl MainWorker { dispatch_load_event_fn.call(tc_scope, undefined.into(), &[]); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } Ok(()) } @@ -853,7 +863,7 @@ impl MainWorker { /// Dispatches "unload" event to the JavaScript runtime. /// /// Does not poll event loop, and thus not await any of the "unload" event handlers. - pub fn dispatch_unload_event(&mut self) -> Result<(), AnyError> { + pub fn dispatch_unload_event(&mut self) -> Result<(), JsError> { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_unload_event_fn = @@ -862,13 +872,13 @@ impl MainWorker { dispatch_unload_event_fn.call(tc_scope, undefined.into(), &[]); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } Ok(()) } /// Dispatches process.emit("exit") event for node compat. - pub fn dispatch_process_exit_event(&mut self) -> Result<(), AnyError> { + pub fn dispatch_process_exit_event(&mut self) -> Result<(), JsError> { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_process_exit_event_fn = @@ -877,7 +887,7 @@ impl MainWorker { dispatch_process_exit_event_fn.call(tc_scope, undefined.into(), &[]); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } Ok(()) } @@ -885,7 +895,7 @@ impl MainWorker { /// Dispatches "beforeunload" event to the JavaScript runtime. Returns a boolean /// indicating if the event was prevented and thus event loop should continue /// running. - pub fn dispatch_beforeunload_event(&mut self) -> Result { + pub fn dispatch_beforeunload_event(&mut self) -> Result { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_beforeunload_event_fn = @@ -895,16 +905,14 @@ impl MainWorker { dispatch_beforeunload_event_fn.call(tc_scope, undefined.into(), &[]); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } let ret_val = ret_val.unwrap(); Ok(ret_val.is_false()) } /// Dispatches process.emit("beforeExit") event for node compat. - pub fn dispatch_process_beforeexit_event( - &mut self, - ) -> Result { + pub fn dispatch_process_beforeexit_event(&mut self) -> Result { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_process_beforeexit_event_fn = v8::Local::new( @@ -919,7 +927,7 @@ impl MainWorker { ); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } let ret_val = ret_val.unwrap(); Ok(ret_val.is_true()) diff --git a/runtime/worker_bootstrap.rs b/runtime/worker_bootstrap.rs index 2020c2bc8d..ff90804f3b 100644 --- a/runtime/worker_bootstrap.rs +++ b/runtime/worker_bootstrap.rs @@ -1,13 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::cell::RefCell; +use std::thread; use deno_core::v8; use deno_core::ModuleSpecifier; use deno_telemetry::OtelConfig; -use serde::Serialize; -use std::cell::RefCell; -use std::thread; - use deno_terminal::colors; +use serde::Serialize; /// The execution mode for this worker. Some modes may have implicit behaviour. #[derive(Copy, Clone)] @@ -120,6 +120,7 @@ pub struct BootstrapOptions { pub serve_port: Option, pub serve_host: Option, pub otel_config: OtelConfig, + pub close_on_idle: bool, } impl Default for BootstrapOptions { @@ -155,6 +156,7 @@ impl Default for BootstrapOptions { serve_port: Default::default(), serve_host: Default::default(), otel_config: Default::default(), + close_on_idle: false, } } } @@ -198,6 +200,8 @@ struct BootstrapV8<'a>( Option, // OTEL config Box<[u8]>, + // close on idle + bool, ); impl BootstrapOptions { @@ -225,6 +229,7 @@ impl BootstrapOptions { serve_is_main, serve_worker_count, self.otel_config.as_v8(), + self.close_on_idle, ); bootstrap.serialize(ser).unwrap() diff --git a/tests/Cargo.toml b/tests/Cargo.toml index fa51d7b77b..cff778b2de 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "cli_tests" @@ -60,6 +60,7 @@ pretty_assertions.workspace = true regex.workspace = true reqwest.workspace = true serde.workspace = true +sys_traits = { workspace = true, features = ["real", "getrandom", "libc", "winapi"] } test_util.workspace = true tokio.workspace = true tower-lsp.workspace = true diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml index a5d2883ef2..bae9aa6bb5 100644 --- a/tests/ffi/Cargo.toml +++ b/tests/ffi/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "test_ffi" diff --git a/tests/ffi/src/lib.rs b/tests/ffi/src/lib.rs index 09c2afb3de..ca416fb980 100644 --- a/tests/ffi/src/lib.rs +++ b/tests/ffi/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/tests/ffi/tests/bench.js b/tests/ffi/tests/bench.js index 2b4fbd55ba..c4b935398a 100644 --- a/tests/ffi/tests/bench.js +++ b/tests/ffi/tests/bench.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); diff --git a/tests/ffi/tests/event_loop_integration.ts b/tests/ffi/tests/event_loop_integration.ts index e3914d9662..17c2d17f0b 100644 --- a/tests/ffi/tests/event_loop_integration.ts +++ b/tests/ffi/tests/event_loop_integration.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/ffi/tests/ffi_callback_errors.ts b/tests/ffi/tests/ffi_callback_errors.ts index 797ff236c1..3f165158b4 100644 --- a/tests/ffi/tests/ffi_callback_errors.ts +++ b/tests/ffi/tests/ffi_callback_errors.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/ffi/tests/ffi_types.ts b/tests/ffi/tests/ffi_types.ts index a996195c69..0ae51c61fb 100644 --- a/tests/ffi/tests/ffi_types.ts +++ b/tests/ffi/tests/ffi_types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file // Only for testing types. Invoke with `deno install --entrypoint` diff --git a/tests/ffi/tests/integration_tests.rs b/tests/ffi/tests/integration_tests.rs index dbc0036bc2..7e92641ae5 100644 --- a/tests/ffi/tests/integration_tests.rs +++ b/tests/ffi/tests/integration_tests.rs @@ -1,10 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] -use pretty_assertions::assert_eq; use std::process::Command; + +use pretty_assertions::assert_eq; use test_util::deno_cmd; use test_util::deno_config_path; use test_util::ffi_tests_path; diff --git a/tests/ffi/tests/test.js b/tests/ffi/tests/test.js index 074db81882..a93b648a2e 100644 --- a/tests/ffi/tests/test.js +++ b/tests/ffi/tests/test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file // Run using cargo test or `--v8-flags=--allow-natives-syntax` diff --git a/tests/ffi/tests/thread_safe_test.js b/tests/ffi/tests/thread_safe_test.js index fffa61a04f..519b0bd4fd 100644 --- a/tests/ffi/tests/thread_safe_test.js +++ b/tests/ffi/tests/thread_safe_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); diff --git a/tests/ffi/tests/thread_safe_test_worker.js b/tests/ffi/tests/thread_safe_test_worker.js index fc4854436e..390b4d8a3f 100644 --- a/tests/ffi/tests/thread_safe_test_worker.js +++ b/tests/ffi/tests/thread_safe_test_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); diff --git a/tests/integration/bench_tests.rs b/tests/integration/bench_tests.rs index d588f5b437..b8d38a7f91 100644 --- a/tests/integration/bench_tests.rs +++ b/tests/integration/bench_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json::json; use deno_core::url::Url; @@ -43,7 +43,7 @@ fn conditionally_loads_type_graph() { .new_command() .args("bench --reload -L debug run/type_directives_js_main.js") .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow_with_options - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); + output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); let output = context .new_command() .args("bench --reload -L debug --no-check run/type_directives_js_main.js") diff --git a/tests/integration/cache_tests.rs b/tests/integration/cache_tests.rs index d9fb8e38e5..3fbed3cbd4 100644 --- a/tests/integration/cache_tests.rs +++ b/tests/integration/cache_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util::TestContext; use test_util::TestContextBuilder; @@ -107,5 +107,5 @@ fn loads_type_graph() { .new_command() .args("cache --reload -L debug run/type_directives_js_main.js") .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow_with_options - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); + output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); } diff --git a/tests/integration/check_tests.rs b/tests/integration/check_tests.rs index b98d719fca..15a2d96d04 100644 --- a/tests/integration/check_tests.rs +++ b/tests/integration/check_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_lockfile::NewLockfileOptions; use deno_semver::jsr::JsrDepPackageReq; @@ -218,7 +218,7 @@ fn npm_module_check_then_error() { "npm:@denotest/breaking-change-between-versions", ) .unwrap(), - "1.0.0".to_string(), + "1.0.0".into(), ); lockfile_path.write(lockfile.as_json_string()); temp_dir.write( @@ -236,7 +236,7 @@ fn npm_module_check_then_error() { "npm:@denotest/breaking-change-between-versions", ) .unwrap(), - "2.0.0".to_string(), + "2.0.0".into(), ); lockfile_path.write(lockfile.as_json_string()); diff --git a/tests/integration/compile_tests.rs b/tests/integration/compile_tests.rs index a34d2cdd1d..e61a1ed9eb 100644 --- a/tests/integration/compile_tests.rs +++ b/tests/integration/compile_tests.rs @@ -1,8 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json; use test_util as util; -use util::assert_contains; use util::assert_not_contains; use util::testdata_path; use util::TestContext; @@ -90,78 +89,6 @@ fn standalone_args() { .assert_exit_code(0); } -#[test] -fn standalone_error() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("error.exe") - } else { - dir.path().join("error") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_error.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - let output = context.new_command().name(&exe).split_output().run(); - output.assert_exit_code(1); - output.assert_stdout_matches_text(""); - let stderr = output.stderr(); - // On Windows, we cannot assert the file path (because '\'). - // Instead we just check for relevant output. - assert_contains!(stderr, "error: Uncaught (in promise) Error: boom!"); - assert_contains!(stderr, "\n at boom (file://"); - assert_contains!(stderr, "standalone_error.ts:2:9"); - assert_contains!(stderr, "at foo (file://"); - assert_contains!(stderr, "standalone_error.ts:5:3"); - assert_contains!(stderr, "standalone_error.ts:7:1"); -} - -#[test] -fn standalone_error_module_with_imports() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("error.exe") - } else { - dir.path().join("error") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_error_module_with_imports_1.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - let output = context - .new_command() - .name(&exe) - .env("NO_COLOR", "1") - .split_output() - .run(); - output.assert_stdout_matches_text("hello\n"); - let stderr = output.stderr(); - // On Windows, we cannot assert the file path (because '\'). - // Instead we just check for relevant output. - assert_contains!(stderr, "error: Uncaught (in promise) Error: boom!"); - assert_contains!(stderr, "\n at file://"); - assert_contains!(stderr, "standalone_error_module_with_imports_2.ts:2:7"); - output.assert_exit_code(1); -} - #[test] fn standalone_load_datauri() { let context = TestContextBuilder::new().build(); @@ -1091,7 +1018,7 @@ Warning Failed resolving symlink. Ignoring. Path: [WILDCARD] Message: [WILDCARD]) -Embedded File System +Embedded Files [WILDCARD] diff --git a/tests/integration/coverage_tests.rs b/tests/integration/coverage_tests.rs index ab18ef76d3..ab09d54267 100644 --- a/tests/integration/coverage_tests.rs +++ b/tests/integration/coverage_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json; use test_util as util; diff --git a/tests/integration/eval_tests.rs b/tests/integration/eval_tests.rs index 198be3a4e8..836cab900e 100644 --- a/tests/integration/eval_tests.rs +++ b/tests/integration/eval_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; diff --git a/tests/integration/flags_tests.rs b/tests/integration/flags_tests.rs index e233ca5baf..e819ea6c99 100644 --- a/tests/integration/flags_tests.rs +++ b/tests/integration/flags_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use util::assert_contains; diff --git a/tests/integration/fmt_tests.rs b/tests/integration/fmt_tests.rs index ccf54a4d0f..468c9cfbc8 100644 --- a/tests/integration/fmt_tests.rs +++ b/tests/integration/fmt_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json::json; use test_util as util; diff --git a/tests/integration/init_tests.rs b/tests/integration/init_tests.rs index a447e7bcef..5bbec687b0 100644 --- a/tests/integration/init_tests.rs +++ b/tests/integration/init_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use util::assert_contains; diff --git a/tests/integration/inspector_tests.rs b/tests/integration/inspector_tests.rs index fa1b3a9d83..7c1be193a1 100644 --- a/tests/integration/inspector_tests.rs +++ b/tests/integration/inspector_tests.rs @@ -1,4 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::io::BufRead; +use std::process::ChildStderr; +use std::time::Duration; use bytes::Bytes; use deno_core::anyhow::anyhow; @@ -6,7 +10,6 @@ use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::url; - use fastwebsockets::FragmentCollector; use fastwebsockets::Frame; use fastwebsockets::WebSocket; @@ -15,9 +18,6 @@ use hyper::upgrade::Upgraded; use hyper::Request; use hyper::Response; use hyper_util::rt::TokioIo; -use std::io::BufRead; -use std::process::ChildStderr; -use std::time::Duration; use test_util as util; use tokio::net::TcpStream; use tokio::time::timeout; diff --git a/tests/integration/install_tests.rs b/tests/integration/install_tests.rs index b0c1e44778..29a65a70b8 100644 --- a/tests/integration/install_tests.rs +++ b/tests/integration/install_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use test_util::assert_contains; diff --git a/tests/integration/js_unit_tests.rs b/tests/integration/js_unit_tests.rs index 577ca043ca..9ecec8b426 100644 --- a/tests/integration/js_unit_tests.rs +++ b/tests/integration/js_unit_tests.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::BufRead; use std::io::BufReader; use std::time::Duration; use std::time::Instant; + use test_util as util; util::unit_test_factory!( @@ -52,6 +53,8 @@ util::unit_test_factory!( kv_queue_test, kv_queue_undelivered_test, link_test, + lint_selectors_test, + lint_plugin_test, make_temp_test, message_channel_test, mkdir_test, @@ -66,6 +69,7 @@ util::unit_test_factory!( process_test, progressevent_test, promise_hooks_test, + quic_test, read_dir_test, read_file_test, read_link_test, diff --git a/tests/integration/jsr_tests.rs b/tests/integration/jsr_tests.rs index c4812e6bfb..0902b7d0a2 100644 --- a/tests/integration/jsr_tests.rs +++ b/tests/integration/jsr_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_cache_dir::HttpCache; use deno_core::serde_json; @@ -66,12 +66,11 @@ fn fast_check_cache() { // ensure cache works let output = check_debug_cmd.run(); assert_contains!(output.combined_output(), "Already type checked."); - let building_fast_check_msg = "Building fast check graph"; - assert_not_contains!(output.combined_output(), building_fast_check_msg); // now validated type_check_cache_path.remove_file(); let output = check_debug_cmd.run(); + let building_fast_check_msg = "Building fast check graph"; assert_contains!(output.combined_output(), building_fast_check_msg); assert_contains!( output.combined_output(), @@ -159,7 +158,7 @@ console.log(version);"#, .get_mut( &JsrDepPackageReq::from_str("jsr:@denotest/no-module-graph@0.1").unwrap(), ) - .unwrap() = "0.1.0".to_string(); + .unwrap() = "0.1.0".into(); lockfile_path.write(lockfile.as_json_string()); test_context @@ -191,8 +190,8 @@ fn reload_info_not_found_cache_but_exists_remote() { Url::parse(&format!("http://127.0.0.1:4250/{}/meta.json", package)) .unwrap(); let cache = deno_cache_dir::GlobalHttpCache::new( + sys_traits::impls::RealSys, deno_dir.path().join("remote").to_path_buf(), - deno_cache_dir::TestRealDenoCacheEnv, ); let entry = cache .get(&cache.cache_item_key(&specifier).unwrap(), None) diff --git a/tests/integration/jupyter_tests.rs b/tests/integration/jupyter_tests.rs index e99780a276..4ef993c157 100644 --- a/tests/integration/jupyter_tests.rs +++ b/tests/integration/jupyter_tests.rs @@ -1,15 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::process::Output; use std::sync::Arc; use std::time::Duration; use bytes::Bytes; -use test_util::assertions::assert_json_subset; -use test_util::DenoChild; -use test_util::TestContext; -use test_util::TestContextBuilder; - use chrono::DateTime; use chrono::Utc; use deno_core::anyhow::Result; @@ -18,6 +13,10 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use serde::Deserialize; use serde::Serialize; +use test_util::assertions::assert_json_subset; +use test_util::DenoChild; +use test_util::TestContext; +use test_util::TestContextBuilder; use tokio::sync::Mutex; use tokio::time::timeout; use uuid::Uuid; diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 13a3c0d69b..bbeb6e7c13 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -1,4 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::fs; +use std::str::FromStr; use deno_ast::ModuleSpecifier; use deno_core::serde::Deserialize; @@ -7,8 +10,6 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::url::Url; use pretty_assertions::assert_eq; -use std::fs; -use std::str::FromStr; use test_util::assert_starts_with; use test_util::assertions::assert_json_subset; use test_util::deno_cmd_with_deno_dir; @@ -2082,6 +2083,88 @@ fn lsp_inlay_hints_not_enabled() { client.shutdown(); } +#[test] +fn lsp_suggestion_actions_disabled() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.change_configuration(json!({ + "deno": { + "enable": true, + "lint": false, + }, + "typescript": { + "suggestionActions": { + "enabled": false, + }, + }, + })); + client.read_diagnostics(); + let diagnostics = client.did_open(json!({ + "textDocument": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": r#" + // The settings should disable the suggestion for this to be async. + function asyncLikeFunction() { + return new Promise((r) => r(null)).then((v) => v); + } + console.log(asyncLikeFunction); + + // Deprecated warnings should remain. + /** @deprecated */ + function deprecatedFunction() {} + console.log(deprecatedFunction); + + // Unused warnings should remain. + const unsusedVariable = 1; + "#, + }, + })); + assert_eq!( + json!(diagnostics.all()), + json!([ + { + "range": { + "start": { "line": 10, "character": 20 }, + "end": { "line": 10, "character": 38 }, + }, + "severity": 4, + "code": 6385, + "source": "deno-ts", + "message": "'deprecatedFunction' is deprecated.", + "relatedInformation": [ + { + "location": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "range": { + "start": { "line": 8, "character": 12 }, + "end": { "line": 8, "character": 24 }, + }, + }, + "message": "The declaration was marked as deprecated here.", + }, + ], + "tags": [2], + }, + { + "range": { + "start": { "line": 13, "character": 14 }, + "end": { "line": 13, "character": 29 }, + }, + "severity": 4, + "code": 6133, + "source": "deno-ts", + "message": "'unsusedVariable' is declared but its value is never read.", + "tags": [1], + }, + ]), + ); + client.shutdown(); +} + #[test] fn lsp_workspace_disable_enable_paths() { fn run_test(use_trailing_slash: bool) { @@ -5984,6 +6067,119 @@ fn lsp_jsr_code_action_missing_declaration() { ); } +#[test] +fn lsp_jsr_code_action_move_to_new_file() { + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); + let temp_dir = context.temp_dir(); + let file = source_file( + temp_dir.path().join("file.ts"), + r#" + import { someFunction } from "jsr:@denotest/types-file"; + export const someValue = someFunction(); + "#, + ); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.write_request( + "workspace/executeCommand", + json!({ + "command": "deno.cache", + "arguments": [[], file.url()], + }), + ); + client.did_open_file(&file); + let list = client + .write_request_with_res_as::>( + "textDocument/codeAction", + json!({ + "textDocument": { "uri": file.url() }, + "range": { + "start": { "line": 2, "character": 19 }, + "end": { "line": 2, "character": 28 }, + }, + "context": { "diagnostics": [] }, + }), + ) + .unwrap(); + let action = list + .iter() + .find_map(|c| match c { + lsp::CodeActionOrCommand::CodeAction(a) + if &a.title == "Move to a new file" => + { + Some(a) + } + _ => None, + }) + .unwrap(); + let res = client.write_request("codeAction/resolve", json!(action)); + assert_eq!( + res, + json!({ + "title": "Move to a new file", + "kind": "refactor.move.newFile", + "edit": { + "documentChanges": [ + { + "textDocument": { "uri": file.url(), "version": 1 }, + "edits": [ + { + "range": { + "start": { "line": 1, "character": 6 }, + "end": { "line": 2, "character": 0 }, + }, + "newText": "", + }, + { + "range": { + "start": { "line": 2, "character": 0 }, + "end": { "line": 3, "character": 4 }, + }, + "newText": "", + }, + ], + }, + { + "kind": "create", + "uri": file.url().join("someValue.ts").unwrap(), + "options": { + "ignoreIfExists": true, + }, + }, + { + "textDocument": { + "uri": file.url().join("someValue.ts").unwrap(), + "version": null, + }, + "edits": [ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 0 }, + }, + "newText": "import { someFunction } from \"jsr:@denotest/types-file\";\n\nexport const someValue = someFunction();\n", + }, + ], + }, + ], + }, + "isPreferred": false, + "data": { + "specifier": file.url(), + "range": { + "start": { "line": 2, "character": 19 }, + "end": { "line": 2, "character": 28 }, + }, + "refactorName": "Move to a new file", + "actionName": "Move to a new file", + }, + }), + ); +} + #[test] fn lsp_code_actions_deno_cache_npm() { let context = TestContextBuilder::new().use_temp_cwd().build(); @@ -7878,6 +8074,73 @@ fn lsp_completions_auto_import() { client.shutdown(); } +#[test] +fn lsp_completions_auto_import_node_builtin() { + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); + let temp_dir = context.temp_dir(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": r#" + import "npm:@types/node"; + pathToFileURL + "#, + } + })); + client.write_request( + "workspace/executeCommand", + json!({ + "command": "deno.cache", + "arguments": [[], temp_dir.url().join("file.ts").unwrap()], + }), + ); + let list = client.get_completion_list( + temp_dir.url().join("file.ts").unwrap(), + (2, 21), + json!({ "triggerKind": 2 }), + ); + assert!(!list.is_incomplete); + let item = list + .items + .iter() + .find(|item| item.label == "pathToFileURL") + .unwrap(); + let res = client.write_request("completionItem/resolve", json!(item)); + assert_eq!( + res, + json!({ + "label": "pathToFileURL", + "labelDetails": { + "description": "node:url", + }, + "kind": 3, + "detail": "function pathToFileURL(path: string, options?: PathToFileUrlOptions): URL", + "documentation": { + "kind": "markdown", + "value": "This function ensures that `path` is resolved absolutely, and that the URL\ncontrol characters are correctly encoded when converting into a File URL.\n\n```js\nimport { pathToFileURL } from 'node:url';\n\nnew URL('/foo#1', 'file:'); // Incorrect: file:///foo#1\npathToFileURL('/foo#1'); // Correct: file:///foo#1 (POSIX)\n\nnew URL('/some/path%.c', 'file:'); // Incorrect: file:///some/path%.c\npathToFileURL('/some/path%.c'); // Correct: file:///some/path%.c (POSIX)\n```\n\n*@since* - v10.12.0 \n\n*@param* - path The path to convert to a File URL. \n\n*@return* - The file URL object.", + }, + "sortText": "￿16_1", + "additionalTextEdits": [ + { + "range": { + "start": { "line": 2, "character": 0 }, + "end": { "line": 2, "character": 0 }, + }, + "newText": " import { pathToFileURL } from \"node:url\";\n", + }, + ], + }), + ); + client.shutdown(); +} + #[test] fn lsp_npm_completions_auto_import_and_quick_fix_no_import_map() { let context = TestContextBuilder::new() @@ -8450,6 +8713,7 @@ fn lsp_completions_node_specifier() { "node:http2", "node:https", "node:inspector", + "node:inspector/promises", "node:module", "node:net", "node:os", @@ -8460,9 +8724,9 @@ fn lsp_completions_node_specifier() { "node:process", "node:punycode", "node:querystring", - "node:repl", "node:readline", "node:readline/promises", + "node:repl", "node:stream", "node:stream/consumers", "node:stream/promises", @@ -9673,6 +9937,74 @@ fn lsp_auto_imports_npm_auto() { client.shutdown(); } +// Regression test for https://github.com/denoland/deno/issues/23869. +#[test] +fn lsp_auto_imports_remote_dts() { + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); + let temp_dir = context.temp_dir(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": r#" + import "http://localhost:4545/subdir/imports_declaration/imports_interface.ts"; + const a: SomeInterface + "#, + }, + })); + client.write_request( + "workspace/executeCommand", + json!({ + "command": "deno.cache", + "arguments": [[], temp_dir.url().join("file.ts").unwrap()], + }), + ); + let list = client.get_completion_list( + temp_dir.url().join("file.ts").unwrap(), + (2, 21), + json!({ "triggerKind": 2 }), + ); + assert!(!list.is_incomplete); + let item = list + .items + .iter() + .find(|item| item.label == "SomeInterface") + .unwrap(); + let res = client.write_request("completionItem/resolve", json!(item)); + assert_eq!( + res, + json!({ + "label": "SomeInterface", + "labelDetails": { + "description": "http://localhost:4545/subdir/imports_declaration/interface.d.ts", + }, + "kind": 8, + "detail": "interface SomeInterface", + "documentation": { + "kind": "markdown", + "value": "", + }, + "sortText": "￿16_1", + "additionalTextEdits": [ + { + "range": { + "start": { "line": 2, "character": 0 }, + "end": { "line": 2, "character": 0 }, + }, + "newText": " import { SomeInterface } from \"http://localhost:4545/subdir/imports_declaration/interface.d.ts\";\n", + }, + ], + }), + ); + client.shutdown(); +} + #[test] fn lsp_npm_specifier_unopened_file() { let context = TestContextBuilder::new() @@ -11358,6 +11690,46 @@ fn lsp_format_exclude_default_config() { client.shutdown(); } +#[test] +fn lsp_format_untitled() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": "untitled:Untitled-1", + "languageId": "typescript", + "version": 1, + "text": " console.log();\n", + }, + })); + let res = client.write_request( + "textDocument/formatting", + json!({ + "textDocument": { + "uri": "untitled:Untitled-1", + }, + "options": { + "tabSize": 2, + "insertSpaces": true, + }, + }), + ); + assert_eq!( + res, + json!([ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 2 }, + }, + "newText": "", + }, + ]) + ); + client.shutdown(); +} + #[test] fn lsp_format_json() { let context = TestContextBuilder::new().use_temp_cwd().build(); @@ -11625,13 +11997,22 @@ fn lsp_format_html() { fn lsp_format_css() { let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); - let file = source_file(temp_dir.path().join("file.css"), " foo {}"); + let css_file = source_file(temp_dir.path().join("file.css"), " foo {}\n"); + let scss_file = source_file(temp_dir.path().join("file.scss"), " $font-stack: Helvetica, sans-serif;\n\nbody {\n font: 100% $font-stack;\n}\n"); + let sass_file = source_file( + temp_dir.path().join("file.sass"), + " $font-stack: Helvetica, sans-serif\n\nbody\n font: 100% $font-stack\n", + ); + let less_file = source_file( + temp_dir.path().join("file.less"), + " @width: 10px;\n\n#header {\n width: @width;\n}\n", + ); let mut client = context.new_lsp_command().build(); client.initialize_default(); let res = client.write_request( "textDocument/formatting", json!({ - "textDocument": { "uri": file.url() }, + "textDocument": { "uri": css_file.url() }, "options": { "tabSize": 2, "insertSpaces": true, @@ -11648,12 +12029,71 @@ fn lsp_format_css() { }, "newText": "", }, + ]), + ); + let res = client.write_request( + "textDocument/formatting", + json!({ + "textDocument": { "uri": scss_file.url() }, + "options": { + "tabSize": 2, + "insertSpaces": true, + }, + }), + ); + assert_eq!( + res, + json!([ { "range": { - "start": { "line": 0, "character": 8 }, - "end": { "line": 0, "character": 8 }, + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 2 }, }, - "newText": "\n", + "newText": "", + }, + ]), + ); + let res = client.write_request( + "textDocument/formatting", + json!({ + "textDocument": { "uri": sass_file.url() }, + "options": { + "tabSize": 2, + "insertSpaces": true, + }, + }), + ); + assert_eq!( + res, + json!([ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 2 }, + }, + "newText": "", + }, + ]), + ); + let res = client.write_request( + "textDocument/formatting", + json!({ + "textDocument": { "uri": less_file.url() }, + "options": { + "tabSize": 2, + "insertSpaces": true, + }, + }), + ); + assert_eq!( + res, + json!([ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 2 }, + }, + "newText": "", }, ]), ); @@ -16821,3 +17261,38 @@ fn lsp_wasm_module() { ); client.shutdown(); } + +#[test] +fn wildcard_augment() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); + let temp_dir = context.temp_dir().path(); + let source = source_file( + temp_dir.join("index.ts"), + r#" + import styles from "./hello_world.scss"; + + function bar(v: string): string { + return v; + } + + bar(styles); + "#, + ); + temp_dir.join("index.d.ts").write( + r#" + declare module '*.scss' { + const content: string; + export default content; + } + "#, + ); + temp_dir + .join("hello_world.scss") + .write("body { color: red; }"); + + client.initialize_default(); + + let diagnostics = client.did_open_file(&source); + assert_eq!(diagnostics.all().len(), 0); +} diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index 37c7502284..a69bc65042 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/tests/integration/node_unit_tests.rs b/tests/integration/node_unit_tests.rs index 9cb1af9496..ef76e365a4 100644 --- a/tests/integration/node_unit_tests.rs +++ b/tests/integration/node_unit_tests.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::BufRead; use std::io::BufReader; use std::time::Duration; use std::time::Instant; + use test_util as util; use test_util::itest; use util::deno_config_path; diff --git a/tests/integration/npm_tests.rs b/tests/integration/npm_tests.rs index ffd3b817d4..033d948b2e 100644 --- a/tests/integration/npm_tests.rs +++ b/tests/integration/npm_tests.rs @@ -1,9 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; - use pretty_assertions::assert_eq; use test_util as util; use test_util::itest; diff --git a/tests/integration/pm_tests.rs b/tests/integration/pm_tests.rs index e3db9006fa..e8985ebfb1 100644 --- a/tests/integration/pm_tests.rs +++ b/tests/integration/pm_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json::json; use test_util::assert_contains; diff --git a/tests/integration/publish_tests.rs b/tests/integration/publish_tests.rs index b97479e78e..332b7c6fa6 100644 --- a/tests/integration/publish_tests.rs +++ b/tests/integration/publish_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::process::Command; diff --git a/tests/integration/repl_tests.rs b/tests/integration/repl_tests.rs index 9eceb2f05d..4faf629af5 100644 --- a/tests/integration/repl_tests.rs +++ b/tests/integration/repl_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use test_util::assert_contains; diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index f0b536aa22..abf1c200d9 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::BufReader; use std::io::Cursor; @@ -11,7 +11,6 @@ use std::sync::Arc; use bytes::Bytes; use deno_core::serde_json::json; use deno_core::url; - use deno_tls::rustls; use deno_tls::rustls::ClientConnection; use deno_tls::rustls_pemfile; @@ -446,7 +445,7 @@ fn permissions_trace() { test_util::assertions::assert_wildcard_match(&text, concat!( "┏ ⚠️ Deno requests sys access to \"hostname\".\r\n", "┠─ Requested by `Deno.hostname()` API.\r\n", - "┃ ├─ Object.hostname (ext:runtime/30_os.js:43:10)\r\n", + "┃ ├─ Object.hostname (ext:deno_os/30_os.js:43:10)\r\n", "┃ ├─ foo (file://[WILDCARD]/run/permissions_trace.ts:2:8)\r\n", "┃ ├─ bar (file://[WILDCARD]/run/permissions_trace.ts:6:3)\r\n", "┃ └─ file://[WILDCARD]/run/permissions_trace.ts:9:1\r\n", @@ -922,7 +921,7 @@ fn type_directives_js_main() { .new_command() .args("run --reload -L debug --check run/type_directives_js_main.js") .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow_with_options - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); + output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); let output = context .new_command() .args("run --reload -L debug run/type_directives_js_main.js") @@ -2215,15 +2214,16 @@ fn basic_auth_tokens() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_resolve_dns() { + use std::net::SocketAddr; + use std::str::FromStr; + use std::sync::Arc; + use std::time::Duration; + use hickory_server::authority::Catalog; use hickory_server::authority::ZoneType; use hickory_server::proto::rr::Name; use hickory_server::store::in_memory::InMemoryAuthority; use hickory_server::ServerFuture; - use std::net::SocketAddr; - use std::str::FromStr; - use std::sync::Arc; - use std::time::Duration; use tokio::net::TcpListener; use tokio::net::UdpSocket; use tokio::sync::oneshot; @@ -2663,7 +2663,6 @@ async fn websocket_server_multi_field_connection_header() { let message = socket.read_frame().await.unwrap(); assert_eq!(message.opcode, fastwebsockets::OpCode::Close); - assert!(message.payload.is_empty()); socket .write_frame(fastwebsockets::Frame::close_raw(vec![].into())) .await diff --git a/tests/integration/serve_tests.rs b/tests/integration/serve_tests.rs index f3c8a31d93..8771930847 100644 --- a/tests/integration/serve_tests.rs +++ b/tests/integration/serve_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; diff --git a/tests/integration/shared_library_tests.rs b/tests/integration/shared_library_tests.rs index 4d33e6584e..d7cdf5b426 100644 --- a/tests/integration/shared_library_tests.rs +++ b/tests/integration/shared_library_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[cfg(all(target_os = "linux", target_arch = "x86_64"))] #[test] diff --git a/tests/integration/task_tests.rs b/tests/integration/task_tests.rs index f2e901228a..1db5376cdd 100644 --- a/tests/integration/task_tests.rs +++ b/tests/integration/task_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Most of the tests for this are in deno_task_shell. // These tests are intended to only test integration. diff --git a/tests/integration/test_tests.rs b/tests/integration/test_tests.rs index 64857ae110..cfa9d9f2d6 100644 --- a/tests/integration/test_tests.rs +++ b/tests/integration/test_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use util::assert_contains; @@ -111,7 +111,7 @@ fn conditionally_loads_type_graph() { .new_command() .args("test --reload -L debug run/type_directives_js_main.js") .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow_with_options - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); + output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); let output = context .new_command() .args("test --reload -L debug --no-check run/type_directives_js_main.js") diff --git a/tests/integration/upgrade_tests.rs b/tests/integration/upgrade_tests.rs index 5132b4ca5b..7748f0ca1a 100644 --- a/tests/integration/upgrade_tests.rs +++ b/tests/integration/upgrade_tests.rs @@ -1,8 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::process::Command; use std::process::Stdio; use std::time::Instant; + use test_util as util; use test_util::assert_starts_with; use test_util::TestContext; diff --git a/tests/integration/watcher_tests.rs b/tests/integration/watcher_tests.rs index 055e46af9c..a180be2fb3 100644 --- a/tests/integration/watcher_tests.rs +++ b/tests/integration/watcher_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use flaky_test::flaky_test; use test_util as util; @@ -6,9 +6,8 @@ use test_util::assert_contains; use test_util::env_vars_for_npm_tests; use test_util::TempDir; use tokio::io::AsyncBufReadExt; -use util::DenoChild; - use util::assert_not_contains; +use util::DenoChild; /// Logs to stderr every time next_line() is called struct LoggingLines diff --git a/tests/lib.rs b/tests/lib.rs index 0a39b9f87d..d841ab9f3f 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1 +1 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. diff --git a/tests/napi/Cargo.toml b/tests/napi/Cargo.toml index e3de253683..e8a39a0cda 100644 --- a/tests/napi/Cargo.toml +++ b/tests/napi/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "test_napi" diff --git a/tests/napi/array_test.js b/tests/napi/array_test.js index 572d3a8994..c72e5d3849 100644 --- a/tests/napi/array_test.js +++ b/tests/napi/array_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/arraybuffer_test.js b/tests/napi/arraybuffer_test.js index f55b9a78c5..a11f761130 100644 --- a/tests/napi/arraybuffer_test.js +++ b/tests/napi/arraybuffer_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/async_test.js b/tests/napi/async_test.js index 4d9ad99c26..f3d258a543 100644 --- a/tests/napi/async_test.js +++ b/tests/napi/async_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/bigint_test.js b/tests/napi/bigint_test.js index 4a9ada2057..19c5317d75 100644 --- a/tests/napi/bigint_test.js +++ b/tests/napi/bigint_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/build.rs b/tests/napi/build.rs index c2fe86a531..73fce9172a 100644 --- a/tests/napi/build.rs +++ b/tests/napi/build.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. extern crate napi_build; diff --git a/tests/napi/callback_test.js b/tests/napi/callback_test.js index c132fefa18..3c56b11a28 100644 --- a/tests/napi/callback_test.js +++ b/tests/napi/callback_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/cleanup_hook_test.js b/tests/napi/cleanup_hook_test.js index 2c1f73e12b..8e4430eef7 100644 --- a/tests/napi/cleanup_hook_test.js +++ b/tests/napi/cleanup_hook_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/napi/coerce_test.js b/tests/napi/coerce_test.js index 64f0148016..91e8950980 100644 --- a/tests/napi/coerce_test.js +++ b/tests/napi/coerce_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/common.js b/tests/napi/common.js index 6dfdc873a8..7df63c4f90 100644 --- a/tests/napi/common.js +++ b/tests/napi/common.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export { assert, assertEquals, assertRejects, assertThrows } from "@std/assert"; export { fromFileUrl } from "@std/path"; diff --git a/tests/napi/date_test.js b/tests/napi/date_test.js index 49eec5cca1..2625010494 100644 --- a/tests/napi/date_test.js +++ b/tests/napi/date_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/env_test.js b/tests/napi/env_test.js index 72b868a668..0b9fad314b 100644 --- a/tests/napi/env_test.js +++ b/tests/napi/env_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/error_test.js b/tests/napi/error_test.js index 57acde5c16..cab36b0a30 100644 --- a/tests/napi/error_test.js +++ b/tests/napi/error_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/napi/init_test.js b/tests/napi/init_test.js index 9486824780..8cd5298dde 100644 --- a/tests/napi/init_test.js +++ b/tests/napi/init_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { assert, libSuffix } from "./common.js"; diff --git a/tests/napi/make_callback_test.js b/tests/napi/make_callback_test.js index b1f7912aea..60aa5ae4c7 100644 --- a/tests/napi/make_callback_test.js +++ b/tests/napi/make_callback_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/mem_test.js b/tests/napi/mem_test.js index bee8c194ea..2c8b0be75a 100644 --- a/tests/napi/mem_test.js +++ b/tests/napi/mem_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/module.c b/tests/napi/module.c index 1ae2ace5d3..9eb90887a8 100644 --- a/tests/napi/module.c +++ b/tests/napi/module.c @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. typedef struct napi_module { int nm_version; diff --git a/tests/napi/numbers_test.js b/tests/napi/numbers_test.js index 8a99c831d0..4914a24751 100644 --- a/tests/napi/numbers_test.js +++ b/tests/napi/numbers_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/object_test.js b/tests/napi/object_test.js index 6226b0138c..5ab81359c4 100644 --- a/tests/napi/object_test.js +++ b/tests/napi/object_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/napi/object_wrap_test.js b/tests/napi/object_wrap_test.js index ee6d4af86b..7c7eecbbd4 100644 --- a/tests/napi/object_wrap_test.js +++ b/tests/napi/object_wrap_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { assert, assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/promise_test.js b/tests/napi/promise_test.js index e4bbfee6b8..3b9ba15b67 100644 --- a/tests/napi/promise_test.js +++ b/tests/napi/promise_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/properties_test.js b/tests/napi/properties_test.js index 21a3555e8e..ee86eaa19b 100644 --- a/tests/napi/properties_test.js +++ b/tests/napi/properties_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/src/array.rs b/tests/napi/src/array.rs index 6df420eb57..b2aba94fff 100644 --- a/tests/napi/src/array.rs +++ b/tests/napi/src/array.rs @@ -1,12 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::ValueType::napi_number; +use napi_sys::ValueType::napi_object; +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_number; -use napi_sys::ValueType::napi_object; -use napi_sys::*; -use std::ptr; extern "C" fn test_array_new( env: napi_env, diff --git a/tests/napi/src/arraybuffer.rs b/tests/napi/src/arraybuffer.rs index 8402f5d861..0fe39b53cc 100644 --- a/tests/napi/src/arraybuffer.rs +++ b/tests/napi/src/arraybuffer.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; extern "C" fn test_detached( env: napi_env, diff --git a/tests/napi/src/async.rs b/tests/napi/src/async.rs index 367d2e9ef0..a91f7037b2 100644 --- a/tests/napi/src/async.rs +++ b/tests/napi/src/async.rs @@ -1,14 +1,16 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::os::raw::c_char; +use std::os::raw::c_void; +use std::ptr; + +use napi_sys::Status::napi_ok; +use napi_sys::ValueType::napi_function; +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::Status::napi_ok; -use napi_sys::ValueType::napi_function; -use napi_sys::*; -use std::os::raw::c_char; -use std::os::raw::c_void; -use std::ptr; pub struct Baton { called: bool, diff --git a/tests/napi/src/bigint.rs b/tests/napi/src/bigint.rs index d867823313..6e1d728b32 100644 --- a/tests/napi/src/bigint.rs +++ b/tests/napi/src/bigint.rs @@ -1,13 +1,15 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::Status::napi_pending_exception; +use napi_sys::ValueType::napi_bigint; +use napi_sys::*; use crate::assert_napi_ok; use crate::cstr; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::Status::napi_pending_exception; -use napi_sys::ValueType::napi_bigint; -use napi_sys::*; -use std::ptr; extern "C" fn is_lossless( env: napi_env, diff --git a/tests/napi/src/callback.rs b/tests/napi/src/callback.rs index 2512f6a38f..1ce1d688c2 100644 --- a/tests/napi/src/callback.rs +++ b/tests/napi/src/callback.rs @@ -1,15 +1,17 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; -use crate::assert_napi_ok; -use crate::napi_get_callback_info; -use crate::napi_new_property; use napi_sys::ValueType::napi_function; use napi_sys::ValueType::napi_object; use napi_sys::ValueType::napi_undefined; use napi_sys::*; -use std::ptr; use Status::napi_pending_exception; +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; + /// `test_callback_run((a, b) => a + b, [1, 2])` => 3 extern "C" fn test_callback_run( env: napi_env, diff --git a/tests/napi/src/coerce.rs b/tests/napi/src/coerce.rs index a405783843..e0fa50c89e 100644 --- a/tests/napi/src/coerce.rs +++ b/tests/napi/src/coerce.rs @@ -1,10 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn test_coerce_bool( env: napi_env, diff --git a/tests/napi/src/date.rs b/tests/napi/src/date.rs index 4d3c155c32..1db5bd088b 100644 --- a/tests/napi/src/date.rs +++ b/tests/napi/src/date.rs @@ -1,11 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::ValueType::napi_number; +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_number; -use napi_sys::*; -use std::ptr; extern "C" fn create_date( env: napi_env, diff --git a/tests/napi/src/env.rs b/tests/napi/src/env.rs index ebc6532a3c..b1b56191ec 100644 --- a/tests/napi/src/env.rs +++ b/tests/napi/src/env.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; extern "C" fn get_node_global( env: napi_env, diff --git a/tests/napi/src/error.rs b/tests/napi/src/error.rs index e0d79c836a..6de0d529b2 100644 --- a/tests/napi/src/error.rs +++ b/tests/napi/src/error.rs @@ -1,11 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::*; use crate::assert_napi_ok; use crate::cstr; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn check_error( env: napi_env, diff --git a/tests/napi/src/finalizer.rs b/tests/napi/src/finalizer.rs index 9769e775e2..56e0a326a7 100644 --- a/tests/napi/src/finalizer.rs +++ b/tests/napi/src/finalizer.rs @@ -1,11 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::ValueType::napi_object; +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_object; -use napi_sys::*; -use std::ptr; unsafe extern "C" fn finalize_cb( _env: napi_env, diff --git a/tests/napi/src/lib.rs b/tests/napi/src/lib.rs index 8c6190ad3e..6162feded6 100644 --- a/tests/napi/src/lib.rs +++ b/tests/napi/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::all)] #![allow(clippy::print_stdout)] diff --git a/tests/napi/src/make_callback.rs b/tests/napi/src/make_callback.rs index 945df34523..c19d34e50c 100644 --- a/tests/napi/src/make_callback.rs +++ b/tests/napi/src/make_callback.rs @@ -1,10 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::ValueType::napi_function; +use napi_sys::*; use crate::assert_napi_ok; use crate::cstr; -use napi_sys::ValueType::napi_function; -use napi_sys::*; -use std::ptr; extern "C" fn make_callback( env: napi_env, diff --git a/tests/napi/src/mem.rs b/tests/napi/src/mem.rs index ebb6a5c7ac..a05cd3abf4 100644 --- a/tests/napi/src/mem.rs +++ b/tests/napi/src/mem.rs @@ -1,9 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn adjust_external_memory( env: napi_env, diff --git a/tests/napi/src/numbers.rs b/tests/napi/src/numbers.rs index 777ccbfac7..9c4174f727 100644 --- a/tests/napi/src/numbers.rs +++ b/tests/napi/src/numbers.rs @@ -1,11 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::ValueType::napi_number; +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_number; -use napi_sys::*; -use std::ptr; extern "C" fn test_int32( env: napi_env, diff --git a/tests/napi/src/object.rs b/tests/napi/src/object.rs index 9876f4dae0..9014134803 100644 --- a/tests/napi/src/object.rs +++ b/tests/napi/src/object.rs @@ -1,10 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn test_object_new( env: napi_env, diff --git a/tests/napi/src/object_wrap.rs b/tests/napi/src/object_wrap.rs index 63e9e2e232..11844917b5 100644 --- a/tests/napi/src/object_wrap.rs +++ b/tests/napi/src/object_wrap.rs @@ -1,16 +1,18 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::assert_napi_ok; -use crate::napi_get_callback_info; -use crate::napi_new_property; -use napi_sys::ValueType::napi_number; -use napi_sys::*; use std::cell::RefCell; use std::collections::HashMap; use std::os::raw::c_char; use std::os::raw::c_void; use std::ptr; +use napi_sys::ValueType::napi_number; +use napi_sys::*; + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; + pub struct NapiObject { counter: i32, } diff --git a/tests/napi/src/primitives.rs b/tests/napi/src/primitives.rs index 28fb8ec3db..7717a0aaac 100644 --- a/tests/napi/src/primitives.rs +++ b/tests/napi/src/primitives.rs @@ -1,9 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn test_get_undefined( env: napi_env, diff --git a/tests/napi/src/promise.rs b/tests/napi/src/promise.rs index 1f1c31f1eb..eab379d617 100644 --- a/tests/napi/src/promise.rs +++ b/tests/napi/src/promise.rs @@ -1,11 +1,13 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; +use std::ptr::addr_of_mut; + +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; -use std::ptr::addr_of_mut; static mut CURRENT_DEFERRED: napi_deferred = ptr::null_mut(); diff --git a/tests/napi/src/properties.rs b/tests/napi/src/properties.rs index 43bef1949a..4244c7ba06 100644 --- a/tests/napi/src/properties.rs +++ b/tests/napi/src/properties.rs @@ -1,10 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::ptr; + +use napi_sys::PropertyAttributes::*; +use napi_sys::*; use crate::assert_napi_ok; use crate::cstr; -use napi_sys::PropertyAttributes::*; -use napi_sys::*; -use std::ptr; static NICE: i64 = 69; diff --git a/tests/napi/src/strings.rs b/tests/napi/src/strings.rs index 301ab23df2..027ae68176 100644 --- a/tests/napi/src/strings.rs +++ b/tests/napi/src/strings.rs @@ -1,10 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use napi_sys::ValueType::napi_string; +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_string; -use napi_sys::*; extern "C" fn test_utf8(env: napi_env, info: napi_callback_info) -> napi_value { let (args, argc, _) = napi_get_callback_info!(env, info, 1); diff --git a/tests/napi/src/symbol.rs b/tests/napi/src/symbol.rs index 6387d449f1..30c7cbde64 100644 --- a/tests/napi/src/symbol.rs +++ b/tests/napi/src/symbol.rs @@ -1,10 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use napi_sys::ValueType::napi_string; +use napi_sys::*; use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_string; -use napi_sys::*; extern "C" fn symbol_new( env: napi_env, diff --git a/tests/napi/src/tsfn.rs b/tests/napi/src/tsfn.rs index a3a231cec1..830145501d 100644 --- a/tests/napi/src/tsfn.rs +++ b/tests/napi/src/tsfn.rs @@ -1,13 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This test performs initialization similar to napi-rs. // https://github.com/napi-rs/napi-rs/commit/a5a04a4e545f268769cc78e2bd6c45af4336aac3 -use napi_sys as sys; use std::ffi::c_char; use std::ffi::c_void; use std::ptr; +use napi_sys as sys; + macro_rules! check_status_or_panic { ($code:expr, $msg:expr) => {{ let c = $code; diff --git a/tests/napi/src/typedarray.rs b/tests/napi/src/typedarray.rs index b512bd32fe..95adf957e4 100644 --- a/tests/napi/src/typedarray.rs +++ b/tests/napi/src/typedarray.rs @@ -1,16 +1,18 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::assert_napi_ok; -use crate::napi_get_callback_info; -use crate::napi_new_property; use core::ffi::c_void; +use std::os::raw::c_char; +use std::ptr; + use napi_sys::Status::napi_ok; use napi_sys::TypedarrayType; use napi_sys::ValueType::napi_number; use napi_sys::ValueType::napi_object; use napi_sys::*; -use std::os::raw::c_char; -use std::ptr; + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; extern "C" fn test_multiply( env: napi_env, diff --git a/tests/napi/src/uv.rs b/tests/napi/src/uv.rs index 555470c008..45ca114adc 100644 --- a/tests/napi/src/uv.rs +++ b/tests/napi/src/uv.rs @@ -1,8 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::mem::MaybeUninit; +use std::ptr; +use std::ptr::addr_of_mut; +use std::ptr::null_mut; +use std::time::Duration; -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; @@ -12,11 +15,10 @@ 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; + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; struct KeepAlive { tsfn: napi_threadsafe_function, diff --git a/tests/napi/strings_test.js b/tests/napi/strings_test.js index 45cb133b28..01fc873a74 100644 --- a/tests/napi/strings_test.js +++ b/tests/napi/strings_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/symbol_test.js b/tests/napi/symbol_test.js index d8edec0232..3f0f277647 100644 --- a/tests/napi/symbol_test.js +++ b/tests/napi/symbol_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/tests/napi_tests.rs b/tests/napi/tests/napi_tests.rs index 53d4258f93..f97fdce289 100644 --- a/tests/napi/tests/napi_tests.rs +++ b/tests/napi/tests/napi_tests.rs @@ -1,9 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] use std::process::Command; + use test_util::deno_cmd; use test_util::deno_config_path; use test_util::env_vars_for_npm_tests; diff --git a/tests/napi/typedarray_test.js b/tests/napi/typedarray_test.js index 25729754a5..f7887e4b1c 100644 --- a/tests/napi/typedarray_test.js +++ b/tests/napi/typedarray_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/uv_test.js b/tests/napi/uv_test.js index af20b26493..f0ff613f31 100644 --- a/tests/napi/uv_test.js +++ b/tests/napi/uv_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/node_compat/common.ts b/tests/node_compat/common.ts index 2982095a29..a86373d69f 100644 --- a/tests/node_compat/common.ts +++ b/tests/node_compat/common.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { partition } from "@std/collections/partition"; import { join } from "@std/path"; import * as JSONC from "@std/jsonc"; diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc index cda2923789..c2edc32ef6 100644 --- a/tests/node_compat/config.jsonc +++ b/tests/node_compat/config.jsonc @@ -368,9 +368,11 @@ "test-dgram-send-callback-multi-buffer.js", "test-dgram-send-callback-recursive.js", "test-dgram-send-default-host.js", - "test-dgram-send-empty-array.js", - "test-dgram-send-empty-buffer.js", - "test-dgram-send-empty-packet.js", + // TODO(kt3k): These tests are flaky on macOS CI. + // https://github.com/denoland/deno/issues/27316 + // "test-dgram-send-empty-array.js", + // "test-dgram-send-empty-buffer.js", + // "test-dgram-send-empty-packet.js", "test-dgram-send-error.js", "test-dgram-send-invalid-msg-type.js", "test-dgram-send-multi-buffer-copy.js", diff --git a/tests/node_compat/polyfill_globals.js b/tests/node_compat/polyfill_globals.js index f22143d9bd..8bbd5cc7df 100644 --- a/tests/node_compat/polyfill_globals.js +++ b/tests/node_compat/polyfill_globals.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { clearImmediate, diff --git a/tests/node_compat/runner.ts b/tests/node_compat/runner.ts index 56803fad44..6bc750447a 100644 --- a/tests/node_compat/runner.ts +++ b/tests/node_compat/runner.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import "./polyfill_globals.js"; import { createRequire } from "node:module"; import { toFileUrl } from "@std/path/to-file-url"; diff --git a/tests/node_compat/runner/TODO.md b/tests/node_compat/runner/TODO.md index 8ad00c9bfd..c885622d6f 100644 --- a/tests/node_compat/runner/TODO.md +++ b/tests/node_compat/runner/TODO.md @@ -1,7 +1,7 @@ # Remaining Node Tests -1155 tests out of 3681 have been ported from Node 20.11.1 (31.38% ported, 69.14% remaining). +1152 tests out of 3681 have been ported from Node 20.11.1 (31.30% ported, 69.22% remaining). NOTE: This file should not be manually edited. Please edit `tests/node_compat/config.json` and run `deno task setup` in `tests/node_compat/runner` dir instead. @@ -499,6 +499,9 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co - [parallel/test-dgram-multicast-set-interface.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-multicast-set-interface.js) - [parallel/test-dgram-multicast-setTTL.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-multicast-setTTL.js) - [parallel/test-dgram-send-address-types.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-address-types.js) +- [parallel/test-dgram-send-empty-array.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-empty-array.js) +- [parallel/test-dgram-send-empty-buffer.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-empty-buffer.js) +- [parallel/test-dgram-send-empty-packet.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-empty-packet.js) - [parallel/test-dgram-send-queue-info.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-queue-info.js) - [parallel/test-dgram-sendto.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-sendto.js) - [parallel/test-dgram-setBroadcast.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-setBroadcast.js) diff --git a/tests/node_compat/runner/challenge_new_test.ts b/tests/node_compat/runner/challenge_new_test.ts index 313bf60490..c95391d3e0 100644 --- a/tests/node_compat/runner/challenge_new_test.ts +++ b/tests/node_compat/runner/challenge_new_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console import { deadline } from "@std/async/deadline"; diff --git a/tests/node_compat/runner/setup.ts b/tests/node_compat/runner/setup.ts index b655633ead..d256842e14 100755 --- a/tests/node_compat/runner/setup.ts +++ b/tests/node_compat/runner/setup.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-read=. --allow-write=. --allow-run=git --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/node_compat/test.ts b/tests/node_compat/test.ts index 52d2b17169..fe6b2e879b 100644 --- a/tests/node_compat/test.ts +++ b/tests/node_compat/test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/node_compat/test/parallel/test-dgram-send-empty-array.js b/tests/node_compat/test/parallel/test-dgram-send-empty-array.js deleted file mode 100644 index cf2508d9ca..0000000000 --- a/tests/node_compat/test/parallel/test-dgram-send-empty-array.js +++ /dev/null @@ -1,32 +0,0 @@ -// deno-fmt-ignore-file -// deno-lint-ignore-file - -// Copyright Joyent and Node contributors. All rights reserved. MIT license. -// Taken from Node 20.11.1 -// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually. - -'use strict'; - -const common = require('../common'); - -const assert = require('assert'); -const dgram = require('dgram'); - -const client = dgram.createSocket('udp4'); - -let interval; - -client.on('message', common.mustCall(function onMessage(buf, info) { - const expected = Buffer.alloc(0); - assert.ok(buf.equals(expected), `Expected empty message but got ${buf}`); - clearInterval(interval); - client.close(); -})); - -client.on('listening', common.mustCall(function() { - interval = setInterval(function() { - client.send([], client.address().port, common.localhostIPv4); - }, 10); -})); - -client.bind(0); diff --git a/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js b/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js deleted file mode 100644 index de5101cbd2..0000000000 --- a/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js +++ /dev/null @@ -1,50 +0,0 @@ -// deno-fmt-ignore-file -// deno-lint-ignore-file - -// Copyright Joyent and Node contributors. All rights reserved. MIT license. -// Taken from Node 20.11.1 -// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually. - -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; -const common = require('../common'); - -const assert = require('assert'); -const dgram = require('dgram'); - -const client = dgram.createSocket('udp4'); - -client.bind(0, common.mustCall(function() { - const port = this.address().port; - - client.on('message', common.mustCall(function onMessage(buffer) { - assert.strictEqual(buffer.length, 0); - clearInterval(interval); - client.close(); - })); - - const buf = Buffer.alloc(0); - const interval = setInterval(function() { - client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall()); - }, 10); -})); diff --git a/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js b/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js deleted file mode 100644 index 01f7a459ce..0000000000 --- a/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js +++ /dev/null @@ -1,36 +0,0 @@ -// deno-fmt-ignore-file -// deno-lint-ignore-file - -// Copyright Joyent and Node contributors. All rights reserved. MIT license. -// Taken from Node 20.11.1 -// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually. - -'use strict'; -const common = require('../common'); - -const assert = require('assert'); -const dgram = require('dgram'); - -const client = dgram.createSocket('udp4'); - -client.bind(0, common.mustCall(function() { - - client.on('message', common.mustCall(callback)); - - const port = this.address().port; - const buf = Buffer.alloc(1); - - const interval = setInterval(function() { - client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall(callback)); - }, 10); - - function callback(firstArg) { - // If client.send() callback, firstArg should be null. - // If client.on('message') listener, firstArg should be a 0-length buffer. - if (firstArg instanceof Buffer) { - assert.strictEqual(firstArg.length, 0); - clearInterval(interval); - client.close(); - } - } -})); diff --git a/tests/node_compat/test_runner.rs b/tests/node_compat/test_runner.rs index 15749ca7fd..150b632b90 100644 --- a/tests/node_compat/test_runner.rs +++ b/tests/node_compat/test_runner.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use util::deno_config_path; diff --git a/tests/registry/jsr/@deno/otel/0.0.2/deno.json b/tests/registry/jsr/@deno/otel/0.0.2/deno.json deleted file mode 100644 index cfa44a7d07..0000000000 --- a/tests/registry/jsr/@deno/otel/0.0.2/deno.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "@deno/otel", - "version": "0.0.2", - "exports": { - ".": "./src/index.ts", - "./register": "./src/register.ts" - }, - "tasks": { - "check:license": "deno run -A tools/check_license.ts", - "check:docs": "deno doc --lint src/index.ts", - "check": "deno task check:license --check", - "ok": "deno fmt --check && deno lint && deno task check" - } -} diff --git a/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts b/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts deleted file mode 100644 index 9c44457832..0000000000 --- a/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2024-2024 the Deno authors. All rights reserved. MIT license. - -import { context } from "npm:@opentelemetry/api@1"; -import { - BasicTracerProvider, - SimpleSpanProcessor, -} from "npm:@opentelemetry/sdk-trace-base@1"; - -// @ts-ignore Deno.telemetry is not typed yet -const telemetry = Deno.telemetry ?? Deno.tracing; - -let COUNTER = 1; - -/** - * Register `Deno.telemetry` with the OpenTelemetry library. - */ -export function register() { - context.setGlobalContextManager( - new telemetry.ContextManager() ?? telemetry.ContextManager(), - ); - - const provider = new BasicTracerProvider({ - idGenerator: Deno.env.get("DENO_UNSTABLE_OTEL_DETERMINISTIC") === "1" ? { - generateSpanId() { - return "1" + String(COUNTER++).padStart(15, "0"); - }, - generateTraceId() { - return "1" + String(COUNTER++).padStart(31, "0"); - } - } : undefined - }); - - // @ts-ignore Deno.tracing is not typed yet - const exporter = new telemetry.SpanExporter(); - provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); - - provider.register(); -} diff --git a/tests/registry/jsr/@deno/otel/0.0.2/src/register.ts b/tests/registry/jsr/@deno/otel/0.0.2/src/register.ts deleted file mode 100644 index 5443707076..0000000000 --- a/tests/registry/jsr/@deno/otel/0.0.2/src/register.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2024 the Deno authors. All rights reserved. MIT license. - -import { register } from "./index.ts"; - -register(); diff --git a/tests/registry/jsr/@deno/otel/0.0.2_meta.json b/tests/registry/jsr/@deno/otel/0.0.2_meta.json deleted file mode 100644 index 79c28d61d1..0000000000 --- a/tests/registry/jsr/@deno/otel/0.0.2_meta.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "exports": { - ".": "./src/index.ts", - "./register": "./src/register.ts" - } -} \ No newline at end of file diff --git a/tests/registry/jsr/@deno/otel/meta.json b/tests/registry/jsr/@deno/otel/meta.json deleted file mode 100644 index 1cb49741a1..0000000000 --- a/tests/registry/jsr/@deno/otel/meta.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "scope": "deno", - "name": "otel", - "latest": "0.0.2", - "versions": { - "0.0.2": {} - } -} \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1/mod.ts b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1_meta.json b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1_meta.json new file mode 100644 index 0000000000..6c213a9c05 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2/mod.ts b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2_meta.json b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2_meta.json new file mode 100644 index 0000000000..6c213a9c05 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/meta.json b/tests/registry/jsr/@denotest/has-only-pre-release/meta.json new file mode 100644 index 0000000000..cce8d0954f --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/meta.json @@ -0,0 +1,6 @@ +{ + "versions": { + "2.0.0-beta.1": {}, + "2.0.0-beta.2": {} + } +} diff --git a/tests/registry/jsr/@denotest/has-pre-release/1.0.0/mod.ts b/tests/registry/jsr/@denotest/has-pre-release/1.0.0/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/1.0.0/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-pre-release/1.0.0_meta.json b/tests/registry/jsr/@denotest/has-pre-release/1.0.0_meta.json new file mode 100644 index 0000000000..c5807f588c --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/1.0.0_meta.json @@ -0,0 +1,3 @@ +{ + "exports": {} +} diff --git a/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1/mod.ts b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1_meta.json b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1_meta.json new file mode 100644 index 0000000000..6c213a9c05 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2/mod.ts b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2_meta.json b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2_meta.json new file mode 100644 index 0000000000..6c213a9c05 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/has-pre-release/meta.json b/tests/registry/jsr/@denotest/has-pre-release/meta.json new file mode 100644 index 0000000000..ba7bc452d7 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/meta.json @@ -0,0 +1,7 @@ +{ + "versions": { + "1.0.0": {}, + "2.0.0-beta.1": {}, + "2.0.0-beta.2": {} + } +} diff --git a/tests/registry/jsr/@std/assert/0.220.1/mod.ts b/tests/registry/jsr/@std/assert/0.220.1/mod.ts index fdcb56c8cf..44aa962030 100644 --- a/tests/registry/jsr/@std/assert/0.220.1/mod.ts +++ b/tests/registry/jsr/@std/assert/0.220.1/mod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** A library of assertion functions. * If the assertion is false an `AssertionError` will be thrown which will diff --git a/tests/registry/jsr/@std/assert/1.0.0/mod.ts b/tests/registry/jsr/@std/assert/1.0.0/mod.ts index fdcb56c8cf..44aa962030 100644 --- a/tests/registry/jsr/@std/assert/1.0.0/mod.ts +++ b/tests/registry/jsr/@std/assert/1.0.0/mod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** A library of assertion functions. * If the assertion is false an `AssertionError` will be thrown which will diff --git a/tests/registry/jsr/@std/http/1.0.0/mod.ts b/tests/registry/jsr/@std/http/1.0.0/mod.ts index 0a0e82847f..991765213a 100644 --- a/tests/registry/jsr/@std/http/1.0.0/mod.ts +++ b/tests/registry/jsr/@std/http/1.0.0/mod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** * Request handler for {@linkcode Route}. diff --git a/tests/registry/jsr/@std/path/0.220.1/_common/assert_path.ts b/tests/registry/jsr/@std/path/0.220.1/_common/assert_path.ts index 7033edcd1a..2d7f7f1b92 100644 --- a/tests/registry/jsr/@std/path/0.220.1/_common/assert_path.ts +++ b/tests/registry/jsr/@std/path/0.220.1/_common/assert_path.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright the Browserify authors. MIT License. export function assertPath(path?: string) { diff --git a/tests/registry/jsr/@std/path/0.220.1/_common/constants.ts b/tests/registry/jsr/@std/path/0.220.1/_common/constants.ts index 9bfd411b66..2dae0df89f 100644 --- a/tests/registry/jsr/@std/path/0.220.1/_common/constants.ts +++ b/tests/registry/jsr/@std/path/0.220.1/_common/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. diff --git a/tests/registry/jsr/@std/path/0.220.1/_common/normalize.ts b/tests/registry/jsr/@std/path/0.220.1/_common/normalize.ts index 3a1a162845..a3d0a0caee 100644 --- a/tests/registry/jsr/@std/path/0.220.1/_common/normalize.ts +++ b/tests/registry/jsr/@std/path/0.220.1/_common/normalize.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { assertPath } from "./assert_path.ts"; diff --git a/tests/registry/jsr/@std/path/0.220.1/_common/normalize_string.ts b/tests/registry/jsr/@std/path/0.220.1/_common/normalize_string.ts index d8f0e090a6..dbcf59029b 100644 --- a/tests/registry/jsr/@std/path/0.220.1/_common/normalize_string.ts +++ b/tests/registry/jsr/@std/path/0.220.1/_common/normalize_string.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. diff --git a/tests/registry/jsr/@std/path/0.220.1/posix/_util.ts b/tests/registry/jsr/@std/path/0.220.1/posix/_util.ts index b446155df5..ff4f87c2aa 100644 --- a/tests/registry/jsr/@std/path/0.220.1/posix/_util.ts +++ b/tests/registry/jsr/@std/path/0.220.1/posix/_util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. diff --git a/tests/registry/jsr/@std/path/0.220.1/posix/join.ts b/tests/registry/jsr/@std/path/0.220.1/posix/join.ts index 625762ab97..85bfb63794 100644 --- a/tests/registry/jsr/@std/path/0.220.1/posix/join.ts +++ b/tests/registry/jsr/@std/path/0.220.1/posix/join.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { assertPath } from "../_common/assert_path.ts"; diff --git a/tests/registry/jsr/@std/path/0.220.1/posix/normalize.ts b/tests/registry/jsr/@std/path/0.220.1/posix/normalize.ts index 8e88ad254b..40ccc59412 100644 --- a/tests/registry/jsr/@std/path/0.220.1/posix/normalize.ts +++ b/tests/registry/jsr/@std/path/0.220.1/posix/normalize.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { assertArg } from "../_common/normalize.ts"; diff --git a/tests/registry/jsr/@std/url/0.220.1/join.ts b/tests/registry/jsr/@std/url/0.220.1/join.ts index b9c8f19d31..b1f42a0a97 100644 --- a/tests/registry/jsr/@std/url/0.220.1/join.ts +++ b/tests/registry/jsr/@std/url/0.220.1/join.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { join as posixJoin } from "jsr:@std/path@^0.220.1/posix/join"; diff --git a/tests/registry/jsr/@std/url/0.220.1/normalize.ts b/tests/registry/jsr/@std/url/0.220.1/normalize.ts index e8d728435b..f9d89dd8f9 100644 --- a/tests/registry/jsr/@std/url/0.220.1/normalize.ts +++ b/tests/registry/jsr/@std/url/0.220.1/normalize.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { normalize as posixNormalize } from "jsr:@std/path@^0.220.1/posix/normalize"; diff --git a/tests/registry/npm/@denotest/has-pre-release/1.0.0/package.json b/tests/registry/npm/@denotest/has-pre-release/1.0.0/package.json new file mode 100644 index 0000000000..027b783d54 --- /dev/null +++ b/tests/registry/npm/@denotest/has-pre-release/1.0.0/package.json @@ -0,0 +1,7 @@ +{ + "name": "@denotest/has-pre-release", + "version": "1.0.0", + "publishConfig": { + "tag": "latest" + } +} \ No newline at end of file diff --git a/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.1/package.json b/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.1/package.json new file mode 100644 index 0000000000..b3d3b3e634 --- /dev/null +++ b/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.1/package.json @@ -0,0 +1,4 @@ +{ + "name": "@denotest/has-pre-release", + "version": "2.0.0-beta.1" +} \ No newline at end of file diff --git a/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.2/package.json b/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.2/package.json new file mode 100644 index 0000000000..b6f8ea4c76 --- /dev/null +++ b/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.2/package.json @@ -0,0 +1,4 @@ +{ + "name": "@denotest/has-pre-release", + "version": "2.0.0-beta.2" +} \ No newline at end of file diff --git a/tests/registry/npm/@opentelemetry/sdk-metrics/registry.json b/tests/registry/npm/@opentelemetry/sdk-metrics/registry.json deleted file mode 100644 index 1e55892f95..0000000000 --- a/tests/registry/npm/@opentelemetry/sdk-metrics/registry.json +++ /dev/null @@ -1 +0,0 @@ -{"_id":"@opentelemetry/sdk-metrics","_rev":"32-fd2f541de5aecbe413589147b6cc22fc","name":"@opentelemetry/sdk-metrics","dist-tags":{"next":"1.8.0","latest":"1.28.0"},"versions":{"0.32.0":{"name":"@opentelemetry/sdk-metrics","version":"0.32.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@0.32.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"463cd3a2b267f044db9aaab85887a171710345a0","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-0.32.0.tgz","fileCount":480,"integrity":"sha512-zC9RCOIsXRqOHWmWfcxArtDHbip2/jaIH1yu/OKau/shDZYFluAxY6zAEYIb4YEAzKKEF+fpaoRgpodDWNGVGA==","signatures":[{"sig":"MEUCIHBrzbDjk66vVi3mOQOqt4aIRr90QWqLXe7z6pqSOS6GAiEAxRW5Nt/5Uo5aJhfbXMPZUZSd1f5UYsLkrC5WVFl9u0o=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1364783,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjBmODACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqU5Q//X8u5PAiNpAJrFIXflu/338sR9oMsluNoiuoPba0zw6ikiPUv\r\nXCYlQMw1XBSE63X2CVpSDakAhG4WtkWXbsi2zRCfOLUze5CuQPf+TqikvwM/\r\nFOGeHl67KjYw20u1K0H0kxB1qAsBQNBV0lWIM8aF6Yty2J05kP7dACmT6o4v\r\nUn6n2+fjQFMlhRA0WeCrEk+6usmFQDBaZSieZofKGm9C3KCeasPZRfnAJZq3\r\nQvbINhGXDOfFdXrwQPNRqZEIDI5/9frr9dJuRsL+h59AEBizdkezofZGvJPr\r\n860MZqidZhyrzC8NBpodJK0sGxQutbssmSKDT2dptVEd9jjj7mj7iKhNPEFy\r\ntUQDgD/4ltCoLyqraDQi+twaE9gFDhol6EwwdRTIMV8pvazMXFuQ1ik6lsC8\r\nPMt4UDYjtOwDkOGqOjtK/AebAOkJWhoEOuz3znl4VAowL5+qPblVRUEOm7HY\r\nKM/I5DEJltVpWJymDlvRNyvN/ysah+p3b6QtAz5ZcXlipFBh/qFozY7GpemB\r\nIHNryYjSZiAvRCsbnWgHsNg2dIDHJQvN440q6cWfX49Hs03r1rYuKlJjkBpy\r\nFFxrzMAoUejzJ/4j9lVc0rfacQrIoUtQ+j4uLJVPCqy4BPk1LMTnyWY16zb4\r\nIvRTJJH4pe1bz0ucrcj7GawkrVRaoxvNO2U=\r\n=wJDK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"a5abee69119cc41d9d34f6beb5c1826eef1ac0dd","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../../","compile":"tsc --build tsconfig.all.json","version":"node ../../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"Work in progress OpenTelemetry metrics SDK","directories":{},"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.6.0","@opentelemetry/resources":"1.6.0","@opentelemetry/api-metrics":"0.32.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"9.1.1","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_0.32.0_1661363075429_0.18961817621798693","host":"s3://npm-registry-packages"}},"0.33.0":{"name":"@opentelemetry/sdk-metrics","version":"0.33.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@0.33.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"c4e51decc6e3bb0e1e97c7b081955d357e46c2fe","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-0.33.0.tgz","fileCount":489,"integrity":"sha512-ZXPixOlTd/FHLwpkmm5nTpJE7bZOPfmbSz8hBVFCEHkXE1aKEKaM38UFnZ+2xzOY1tDsDwyxEiiBiDX8y3039A==","signatures":[{"sig":"MEUCIBeHiozEczRdIpEbB1UwgCro3jj+tB2iGk+FQ+CZuc3LAiEA97t2teODixmkZuRZk4z+IPqaULq8SBNRrV8c3JVlTys=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1425603,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjJGjCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrNERAAjSfh3/c433lR4GMa9u1DjMIVy7MYOZe0YftxnQ9VPGs2jgfa\r\nWC4r/3Qbantsq49xoVj8GjmiZnIrhzm833P8L7lNLes8LDtI8TEbv+6SPfsq\r\nRLBvUdPHww4UlbfShEo5hnGXVIC3qudVRlvGv2DKxFD9AF2QZszGKsrNQ+P8\r\nB5A/lWp4dqY+IhB0xMk3y7asdEggiF/isxMsCGhNFpDiyL8iHQWuW3ylt5pq\r\nZmPLY23ax1kWkTSvj7Us9X7tgIjGuGgzU+cBSf29WI0L2gyVu5TBBRk5NY5x\r\nryXCnzi1MoNdY669ToCEVcCxJoWb4DY9Mg2ihhJq2argdUPzwEBThC1EecAP\r\nJ/786fw+PKmQAzKGVGEXRmDZljF4+3Zk/KIujlHZK7RP6h1kaMCRAKYwnBLT\r\nR9v3W4ljHnbPgV0OmX+W0suV8oEoYtIJxe/7yyznWsLxcndndZq6WFqzcJQN\r\nVdPk6kxtXdSx7hzcnn1CXxckq+fiG0EdZX32ZRGEi1X4EXmTeNL8s2CMiVUl\r\ndGQm+vbP517Nj2oQvOcnO+YSf55XGNtv49cbusZ8JHVGQhHE26HXCK6A2ci1\r\nhEgPOMtjRH43f3kncjdQXW/vTsS/vMiacwyM94686CkEOaJJNnTv8R2DlV2s\r\nkvO54QGc0KDggcsxEqzpfo9OuQEFKWUe0lU=\r\n=fhUv\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"ad88c3d9aa0100fe259b93f4b660e84417b757ac","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../../","compile":"tsc --build tsconfig.all.json","version":"node ../../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"Work in progress OpenTelemetry metrics SDK","directories":{},"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.7.0","@opentelemetry/resources":"1.7.0","@opentelemetry/api-metrics":"0.33.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"9.1.1","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_0.33.0_1663330498049_0.15542963522751552","host":"s3://npm-registry-packages"}},"1.8.0":{"name":"@opentelemetry/sdk-metrics","version":"1.8.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.8.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"d061060f03861ab3f345d0f924922bc1a6396157","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.8.0.tgz","fileCount":489,"integrity":"sha512-+KYb+uj0vHhl8xzJO+oChS4oP1e+/2Wl3SXoHoIdcEjd1TQfDV+lxOm4oqxWq6wykXvI35/JHyejxSoT+qxGmg==","signatures":[{"sig":"MEYCIQDsOqqaWHTqJVYLyeRb+ZNiGkJbd34UCyCqyHX6UgwbCAIhAMvGlF7I5klQng1omsJ/Nk8Nzz0TlqjJqpvJj76kcBV2","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1416962,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjbANeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqikQ/9EWJ03wBF//CwkzW46y4mBkSgukPe+k9sfS4+cd4/+nErVF2O\r\nMTKRhSpoDQcVLBdmTfeaObKRmgYg2p3PeOrGPphcfkEmZjrRv6342ubUWjRW\r\nm39DSPq8NmZp7J5hRf2hLYSg6tXKZnflCmj+T8upYC/KmW7kOKtx/B3tTy7c\r\nq6q1Kc6vBToqid7yNdFLsIYTjVHM8xSvtg0QVx84S6mtzR2iO8uAzL3ucu34\r\nfLXyN2MzbqJIO516Jt1vE0ix9q/xmt7TrbqCi5k5yZ8cIOianx1Yl7MFGOAX\r\nKwaGILDNoBnb7c8mQITWnP286rsO4NMNnBoXt7ZJCmIavKe8XlbxFZAwuM/q\r\nl0bC9nG8l4r+182xF5XKV2/wnRPQ3j8+uQyBx/8+7YGoIVbowgd4GvJEZGRm\r\n83hxM4/xGxmHiWbkngJlyspY7s86o1MTuwPgwns73cgAhNZsuti2SVgtdoFc\r\n4prfRtGRXUtHJkBQMItiPDEG+Mnfq9hBxHh2F1zuvDLzyN93nCs22gBY79jT\r\n33kticfFECGRLhuCIhZIuV+yzBu4ciYXk0fg9hMY6wagCqQ+tPRs4HPqO92f\r\nuVGhapMpKJPclhvOvlbY4d4Ixm4mo5rrnJx0BPkn445hV6JwJbUC3PAU9yPp\r\nJhIpEHNdZQc8ntThHRpYRCSBmecy8YkzSdo=\r\n=haC6\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"7972edf6659fb6e0d5928a5cf7a35f26683e168f","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"Work in progress OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.8.0","@opentelemetry/resources":"1.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.4.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.4.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.8.0_1668023134085_0.198580524230221","host":"s3://npm-registry-packages"}},"1.9.0":{"name":"@opentelemetry/sdk-metrics","version":"1.9.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.9.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"1c3a51abeb7f45ea25b91daf7e05e43d25ddd20a","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.9.0.tgz","fileCount":489,"integrity":"sha512-fSlJWhp86kCan1zuxdH6LTyUBYlohQwDKnxep5qHMnRTNErkYmdjgsmjZvSMdAfUFtQqfZmTXe2Lap7a5AIf9Q==","signatures":[{"sig":"MEUCIGoaNR3oyipKjYbn2fxyiZ4BKdIFiSOmx699LRzKo0vWAiEAyTvGUxH0WbB1bXUpv9AlkibTlQ51uAHPzOa9yFD7L7A=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1432686,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjvy41ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrSjg//czBDsjkHKhMQ+UP70UHKKTG+xRRSxL0WB8vcvSu2U1VFJldP\r\nBiEOUKKfXMNMTlwB6fuo8mdBnwT/Pn6rmbYyW30mkRtc0n9rpakFtdvASSUg\r\nSsLAROHI+NpaNG3lygIpwXu6wLNGzh1ejL8/1/sfRFKBZIGlb/4l9eHtLj8z\r\nNEZ2r0IAOVOQ2JWnMon0gSM1yjplES4pF30tt5j+UeJbNqgbu9CxD8mUOt/E\r\nV4KT3XawHrjMB5VmPlUhZ66OwbF4P7dZF3ukETA/ezK2XBQeUBBNCiJbW6C0\r\np6WdlJZ/1chyseR9hKiBm8bOrx9XQlNcKyM8brOL9G0joCa/YDX12z0lK5xg\r\nlv/As+8IcQUBkcMsBnzl/BVZUnVQ/qacyWXsYpuMmSCfCOCcCAfLXP/kkVDG\r\nNtNpb3RWySMTuqhYBNxQ2wSBmu0TnXxc3y7ubWfqVNh8SJu9kLZQo8fRFyrM\r\nrVATAIjhFPpZAGBeDca3YHuElfFhXdxtklvHX4ATh3yN0DlVqaaWxQXp6W/I\r\n671fbqRQWCy29YlLoS7k4WR8CGkvNFPYedOgIPinE/g3Qv4oTF0z8vcFYI9G\r\nP2PyfHSre1bHm/5rTIiB9xWEK+XUb+9uV/33gGfTkZqz+6oGd4E0pg9q16cy\r\nEZrSABE83nfpxgpbmLMjACClgyQWshGNr4I=\r\n=qh/2\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"08f597f3a3d71a4852b0afbba120af15ca038121","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.9.0","@opentelemetry/resources":"1.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"3.0.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.9.0_1673473589677_0.62979083795101","host":"s3://npm-registry-packages"}},"1.9.1":{"name":"@opentelemetry/sdk-metrics","version":"1.9.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.9.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"babc162a81df9884c16b1e67c2dd26ab478f3080","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.9.1.tgz","fileCount":543,"integrity":"sha512-AyhKDcA8NuV7o1+9KvzRMxNbATJ8AcrutKilJ6hWSo9R5utnzxgffV4y+Hp4mJn84iXxkv+CBb99GOJ2A5OMzA==","signatures":[{"sig":"MEYCIQCYUyFLpMVK/wDHg6lU87nZ3MQB9nQh8JvM/VryTCdksgIhAKZDK2iXOPbTaQ6GOK3qt6u560bXhXh+VmFjYH9uiaHo","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1556782,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj1+KFACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpp3w//cVciehkq5CKoOUjcEovwgleHJd+eCwOEc40/05fiVn6NmDuW\r\nHAs1UekoxG9cc+MHhvp/38o5o1WFK2sFGXO7ymvMluXheGXx7nI6U+qnqkb9\r\nWWvI53gNcfN5GUgJGarHX6kt+DoyV5+0ig0R/2U37hk6DWm89kZArd+X8xEI\r\nD+2GADx/m1uTUO/PJnaJ8LB1LLK2SJ2vQmViYTP8icykDzIJT5flZyBeb+hk\r\nRqgaFcx7Qa+XecZIjTUXfY5ZQCOc5IYf3JAVea/0kAosP63vSaK9TU+p4GG6\r\n4mYBEANc4Inn4ocav0UoYb5kDNntNo/wAusSI4gH/sByMaQhW/EGy9qmKTA+\r\nKpsIPqZO/FPXZEUJZsEUBOD7GxGp8rUB38JEvW3d3ACDFPfVl/AAuIJ4wipt\r\nM4cKsHtewTdDbDGrkO7o9EDG+OACTTBbvAQ/vxWoC8lDZ+vVWbc0pT+taLcs\r\nlimhIk0XDHJGZ+vxL/T7ib0nOCE1qgt6TL5F0Mmx/Juen4Go2NhOa9dbZG1Y\r\nSI6b/SXe5izJQR6vGhyx2mo3OOXrBQmQc/jjcJbwaRRzLshUY/GmZeqrsPLO\r\n91kJNQIUfFRZLhY0tLPlUaYoFjUY+HL4d0RHWKxLoenZ1POPTBwbrfTpwTRD\r\nOTqOB4P/zL3AutUyuX+FOPevSBguIKxq7wA=\r\n=AXfq\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"279458e7ddf16f7ddca5fe60c78672e05fafce66","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.9.1","@opentelemetry/resources":"1.9.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.9.1_1675092613192_0.7361446399809115","host":"s3://npm-registry-packages"}},"1.10.0":{"name":"@opentelemetry/sdk-metrics","version":"1.10.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.10.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"627f164036ad053551b3e75447adf9a902b066aa","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.10.0.tgz","fileCount":543,"integrity":"sha512-vOB3khvj894ppOwafNqKNavpChZYR2S2IZyy8PmL0DwNgFPGYwkWxZkv7YJduBNzcCd+Ao+ug93jdSFFhnpIhQ==","signatures":[{"sig":"MEUCIQDL/qHYi9VPBH3poMg/gvlnmTAxuK7zGkYo7FXRK1XAMgIgPAiYWDmawt1pshH1UtUsHcw9izgXdV8pWQMROlpkKEQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1571910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkD0cTACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpZ/w/9G8OISP56mEsFRcrjWT+zXdW4zgKR3RBzQcZ+RjfqhV2Gq51K\r\nkwhYJ/bIk/xXtSJkpZ+gHjQIy1lwNXU9ux62RcZXzO/EcDLEg1aDDBD+d7G4\r\naYN81H49JVfrstGQzxduGK1vamLm2EBg0PbQGK7r8oNiNkK/Rt+qRY0ZYi6f\r\neYFhc8Nvbh9MKquBU21z/En/IhrxlbUQyetJ0TKfoonVsoj9VK1F7BBf/7cD\r\nYirI9GZzNKfk+y3IPmQ3aalntJmoN+aWWGYFn3hbE2IIo48N1EOkv3ZPXLwm\r\nHIaEN4MbK1vIjbjaS0LHC6lWYDooa5GCvVEXSl1ZJimyIpdx3FV5z0mVgusJ\r\nyb2PuPPm2okM7FM4xg0sDOqajIBbTy253Q8F+dqEBiZah1ELX/HTXV+3TG8l\r\nUJjlP6oE1CQwW43F+o46Pmf9eF0qi3pPEPwFpuX6JQfnWgWrAgR4LAhAh1UA\r\nhbVTVTBixGvNCZFvMh8Irbj3DNz/6pj2h5akehMBbaENpKj8g8Ij/tFIH7ho\r\nVXKGbDTUrnJPAcm4q1lghjiuBOgvJ3mZxacCayI+5daZBpL1rzXp9TrMSZqq\r\nsk5NuYgpfwBunGAjyeH3J4BirEKMUQElOKUr0oPvGYz11Rz6cs2Xa3U679q6\r\nZ7ZlwIHa3hghQeF62mlagpDWNJ1MweZb3zU=\r\n=fjdK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"56e6b1bb890f844b8963a146780d0b9cfa8abd0d","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.10.0","@opentelemetry/resources":"1.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.10.0_1678722835367_0.34217119288271447","host":"s3://npm-registry-packages"}},"1.10.1":{"name":"@opentelemetry/sdk-metrics","version":"1.10.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.10.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"1389a8524ba59dc2e1d9cf627d504119c111fca1","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.10.1.tgz","fileCount":543,"integrity":"sha512-ARAD4e6lZhLwstwW+1HG2Q3XuYFA/t8vn10KK/mA4em1pZYKFn64c45RJZJcntxWp4wOZRbp9iL1RXsg7zIjow==","signatures":[{"sig":"MEQCIF7l8lYSyGXYbwwaiNKB4rSrfukNeC9FI7PrmRetWSCaAiAEvWmV3Mebg7p+TvQxAN44ZzolcH3+uz+10I6dLs+2BA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1571910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkGIV6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrjQg//V5N9A3eIarDzrRPXxYMuJ3TO2qYzZxGLjCSbFBab/UFBKK2g\r\n4KS7ImLvfoP7k9S5xKMEQlCgG+0dGWt3SFELbhpLmwjVPM7OUcfF+es8dfAE\r\nQC6aLfq5juKIz/hcNBUGPZfauUQqReb+KptJUlCfvIW1RUi12dCoXSLm89/r\r\nXOUbhsIo3v+Lj5HBvn4lIl/3iJ07SaB3ehaVtypnSmpcgl2sWyMv77USvagd\r\nENNV4IbUFYM9YRnMFCWgSxtZtFDKmkLypkvUyu9jepecvgbXT+J4Z8hl6zgo\r\nv2Sqbi5PjkY035ZgSHqQ09yHGg5azUwn8AN0Yecqq88Mrqt35As1kdJLvrXg\r\nDK5lDQaxPqtaMSzLAg34Ck/Cng8HANjiKGaRA5ITCuJhoVMRt9va+mHscMwv\r\ngdfkJHKyq3wlGPZAvroDkljKROic2cYZv35zSwHXAoQ1PxYgEvbtxYDxsxPk\r\nSv5azrVgUBpKBM33EE+MMQ3AeizxdjCHnl8mPgv5u4C26MfdEd5f8cOKt9BS\r\nlhvukjFyU9DjoyMypt+LZWjWz1WVxircRPkXwSQa7t2FIGSV5XHL5nHSrBt4\r\nam5WwdCTJntt5jdeiL5RHN0myvOBDPB10G9ZoaogPjGUIXJK/XERuVD4/e9w\r\nJMVSLW8zW0CgZaAGWoaA4KhhW2OYXC/OdUk=\r\n=O4i4\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"486df99906958de9b8b666839785b00160a6a229","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.10.1","@opentelemetry/resources":"1.10.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.10.1_1679328634706_0.9289571491726534","host":"s3://npm-registry-packages"}},"1.11.0":{"name":"@opentelemetry/sdk-metrics","version":"1.11.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.11.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"0fe347fb39a802ec270315cba0eba2e3ce64c4a2","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.11.0.tgz","fileCount":561,"integrity":"sha512-knuq3pwU0+46FEMdw9Ses+alXL9cbcLUUTdYBBBsaKkqKwoVMHfhBufW7u6YCu4i+47Wg6ZZTN/eGc4LbTbK5Q==","signatures":[{"sig":"MEUCICcPE7ow3GH80M4VwObQJPDkCO3SK4VTYmZkd1STJ+uZAiEA35ZtKgFt5GMZa3WnCxdCI7yWWkwAk9QYu88ktlLJYVk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1839857,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkJaswACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrzcg/9FdLBYGIFXRPNUEc+CsQRabaC90KOwOp+7HV39/LXozs0FSB1\r\niKe3uUkfFBCLhcRSCQns1EVifv+Xda02Wa/Y0r28uhKc1y/N7uGQWLFpR7Iu\r\ngVVKTxpLt2QPsTYyQuuj4a6XGpGk7frKzv+uss7ByMKXUsC7YmzMS1DUJt+4\r\nekB8LXK7MEMrsN1ze0pp9+r9KTPGMDQXT9I64a/AuWR7IhM4RuPqjpriq/7R\r\nV38kOjgp4I6uj1H4hAB3FQ1NwdVOSajrDQ2HHLUvT4lv22LKIABQr0TDtQsN\r\naJq9rK7IfWSAE5zj5q9t8VfpNkhph3pFPPgg/l1Mnryr+VYhvErInyIuuPcM\r\n6X3csId2wFKauJR4oSXTLDxE6IMfvPk1ln2aXe3u/bSDrjEkd8bJvAS45yhW\r\n8JxegGRcA3SMuTtJcy85S6mVc/rksLjDSGDGpBaL4eYnR1qQ1hGYLljdpbuy\r\nbDRNcL2ZERMu3CmmXsFEmDqfTdFaMIzGTXXN20+ti6HtcBL9WFsKgVudxD5O\r\nTU40gKMtkAc+kfQ7cheqj+WjbH6ED0dPA7BHCE8Mo7kQTuGCVR1pMZZHh50h\r\nb/CaOKc7PCzVltN0PRnNPouwHzSM00LFqU4O2dvfnDkM6yyNJ86Je01ls6Fu\r\nQooqG9QlA64TxWqtWwXfGRY/w5p7wZSrXrE=\r\n=KFj4\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"1328ee04ae78f9f6cf143af7050c00aaa6d2eb3b","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.11.0","@opentelemetry/resources":"1.11.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.11.0_1680190255856_0.4488883258029981","host":"s3://npm-registry-packages"}},"1.12.0":{"name":"@opentelemetry/sdk-metrics","version":"1.12.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.12.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"52c135b8ca6af677a3e93b6721bc866a74c98b4b","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.12.0.tgz","fileCount":561,"integrity":"sha512-zOy88Jfk88eTxqu+9ypHLs184dGydJocSWtvWMY10QKVVaxhC3SLKa0uxI/zBtD9S+x0LP65wxrTSfSoUNtCOA==","signatures":[{"sig":"MEQCIEAfZBHAMQVNInXN1E0ihfClv5NylKPFc0jrD34SRJMvAiBzpMnq2Iu5C00sNF1GPtmv10V2RrlKn7msJG8pBtnd5g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1839857,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkOEYtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmobXQ/6AsboU2mCpdhs/tPwnXUqcjmoBV3/aVWoXwM22/jxCszCX8vk\r\nRFoTQHvvMZOMjqOLOPW2S3U2sPumqm7tj7V2aT/tVoZ7En09afYCbKO1fClj\r\ndtm9zB4H/GeL9uT4j8E7mFzPGMkgl/Wo8xZzrifKGknDwoO5sITFwXYCb1XI\r\nVprJn7eNMQEeMuGNd8WG2SFiI+VwVDfRz9eCmaI6ozABBw+Xd/Ae2SzPRfqo\r\nlbV46b6MN47oJHPNNjvPDPpQ8PUu+usZ2CZZR0/DYvHryjJnsTEaBOEqefXl\r\nvL7ommVdZW6DNSzMEQ5k4DQIMIHrvYYL/j8Cwkl7UcpmzrXgWoHNfQLlP3zk\r\nOqHvW+AEAYeiAs1ZrLRwNId+SpdNdERohMR70BxyU1ZXXx6NcpzJv6V+a+4q\r\nrtfX4oazI7rldM1BHO3sJM3g0cCyUtllFGTb5Mg8EI1qXBZppQF33jLvj20n\r\n4Ulb6wpsteaDDlBOK1TmN0s8VftM4ekrz2b5+UvE5yYq7RX+FVRFWb8U5xiZ\r\nHRc/5H4txVGZXLFvmSCJm6WmzUy83njG0qSF3wS6dOetNRXlrJrhS0tnHBqM\r\nnC4RbTqtdrp4EDi4F521hcdm6uo2cAmCxHbVhwgL+3lM2me9VmAtxB/BJacp\r\nvupg+95/zDYgsEWSTaFE/0t7TGuFe0UJ9j4=\r\n=5rhT\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"a04090010ee18e17487b449984807cc2b7b6e3e6","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.12.0","@opentelemetry/resources":"1.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.12.0_1681409581531_0.31698151736882196","host":"s3://npm-registry-packages"}},"1.13.0":{"name":"@opentelemetry/sdk-metrics","version":"1.13.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.13.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"4e859107a7a4389bcda7b37d3952bc7dd34211d7","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.13.0.tgz","fileCount":561,"integrity":"sha512-MOjZX6AnSOqLliCcZUrb+DQKjAWXBiGeICGbHAGe5w0BB18PJIeIo995lO5JSaFfHpmUMgJButTPfJJD27W3Vg==","signatures":[{"sig":"MEYCIQCUeO+qPBpw+CVNlqlTfDO++08VfOb0W3UdO9pIYolUCAIhAJH6JOYZuj9SxAwhyK86XMhqrxGloVuk7E7pzmTPdjJr","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1839834},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"8fc76896595aac912bf9e15d4f19c167317844c8","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.13.0","@opentelemetry/resources":"1.13.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.13.0_1683811806809_0.022001801192962578","host":"s3://npm-registry-packages"}},"1.14.0":{"name":"@opentelemetry/sdk-metrics","version":"1.14.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.14.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"ee51d72eb32a74108e6632681ce2df46cddc0714","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.14.0.tgz","fileCount":561,"integrity":"sha512-F0JXmLqT4LmsaiaE28fl0qMtc5w0YuMWTHt1hnANTNX8hxW4IKSv9+wrYG7BZd61HEbPm032Re7fXyzzNA6nIw==","signatures":[{"sig":"MEUCIQDZy6AZC4gsrIqvrLyc0L21UL/+PakTzlDTNsuwjRpDhwIgMSf9ZJoCKDB8uD708TqJtggoPZ9cpVP/LIthheTWDWM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1839834},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"edebbcc757535bc88f01340409dbbecc0bb6ccf8","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.14.0","@opentelemetry/resources":"1.14.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.14.0_1686031255514_0.6205545095237606","host":"s3://npm-registry-packages"}},"1.15.0":{"name":"@opentelemetry/sdk-metrics","version":"1.15.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.15.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e47ad688882fc2daedcbbe3db16a5c110feb23e8","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.0.tgz","fileCount":561,"integrity":"sha512-fFUnAcPvlXO39nlIduGuaeCuiZyFtSLCn9gW/0djFRO5DFst4m4gcT6+llXvNWuUvtGB49s56NP10B9IZRN0Rw==","signatures":[{"sig":"MEQCIA6mPFnkdT47rCrHsCh4ePv+jy+q7abH27EHpIw2DKE0AiB9rZUo59MjzXY9Uih4EbPVSQZ5I/uFRmYTdzVsP7a68g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1803014},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"06e919d6c909e8cc8e28b6624d9843f401d9b059","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.1/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"tslib":"^2.3.1","lodash.merge":"^4.6.2","@opentelemetry/core":"1.15.0","@opentelemetry/resources":"1.15.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.1","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","cross-var":"1.1.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.15","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.15.0_1688642828881_0.612950022765163","host":"s3://npm-registry-packages"}},"1.15.1":{"name":"@opentelemetry/sdk-metrics","version":"1.15.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.15.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e0d2844191ecd7fce3fccf18ae50ed35389f0885","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.1.tgz","fileCount":561,"integrity":"sha512-ojcrzexOQfto83NvKfIvsJap4SHH3ZvLjsDGhQ04AfvWWGR7mPcqLSlLedoSkEdIe0k1H6uBEsHBtIprkMpTHA==","signatures":[{"sig":"MEYCIQCBwveTbaE79v4tJk8CdBffRh5H6Loc8hSu+ysnZzK1YAIhAMHsh5T/As5lzxnmYm6j7V704gjjvyvEm2CaH1bpx5cO","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1841179},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"9f71800fdc2a5ee5055684037a12498af71955f2","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.3/node@v18.4.0+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.4.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.15.1","@opentelemetry/resources":"1.15.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.3","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.9","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.1","@types/sinon":"10.0.15","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.15.1_1690209168814_0.4696021233708716","host":"s3://npm-registry-packages"}},"1.15.2":{"name":"@opentelemetry/sdk-metrics","version":"1.15.2","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.15.2","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"eadd0a049de9cd860e1e0d49eea01156469c4b60","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.2.tgz","fileCount":561,"integrity":"sha512-9aIlcX8GnhcsAHW/Wl8bzk4ZnWTpNlLtud+fxUfBtFATu6OZ6TrGrF4JkT9EVrnoxwtPIDtjHdEsSjOqisY/iA==","signatures":[{"sig":"MEUCIQCFKX9DaFzjXDXPu/N+lf5A+VZoJEIuX5BlwqR2YxqIPQIgTriFUfsIieNy4ajXosd23GlEnVapJ9nRDMUvRFJYIGk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1848194},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"48fb15862e801b742059a3e39dbcc8ef4c10b2e2","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.4/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.15.2","@opentelemetry/resources":"1.15.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.4","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.10","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.15.2_1691500878806_0.21964542929012487","host":"s3://npm-registry-packages"}},"1.16.0":{"name":"@opentelemetry/sdk-metrics","version":"1.16.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.16.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"6c413c6abb1d68dbfe59984384d4031feeccbe1e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.16.0.tgz","fileCount":561,"integrity":"sha512-58obaKzBY0CB6ZQS/sxcGvihqZk2zL2KDCQe734NofVfE7JpKMn/TtyzA8O4nw9sXIO2N9Wx2zzKRyGFXVGrcw==","signatures":[{"sig":"MEUCIGu4l4M/yMtx2D8TpJeVo1nvcoCzRr6o5+wZtJj2W23YAiEAzPdInj1ng6V1HXJhfyT/sRzxXuC3taYmiuKCASO4G2g=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1872545},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"5fcd8cf136e2235903dde3df9ba03ced594f0e95","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.16.0","@opentelemetry/resources":"1.16.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.17","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.6.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.6.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.16.0_1694434471320_0.42251452448444704","host":"s3://npm-registry-packages"}},"1.17.0":{"name":"@opentelemetry/sdk-metrics","version":"1.17.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.17.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e51d39e0bb749780d17f9b1df12f0490438dec1a","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.17.0.tgz","fileCount":561,"integrity":"sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==","signatures":[{"sig":"MEQCIEGFsgaNt88vPUiItoys14mI96KUmsfCU3V9/M9zEFrHAiAgibedup1xc0Uh7HFSGEe8a8IEZbYsRxYQhUM7mMJJmQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1872545},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"faf939c77591f709afbc23fadbe629c9d3607ef6","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.17.0","@opentelemetry/resources":"1.17.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.17","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.7.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.7.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.17.0_1694524354547_0.27289065421627656","host":"s3://npm-registry-packages"}},"1.17.1":{"name":"@opentelemetry/sdk-metrics","version":"1.17.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.17.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"9c4d13d845bcc82be8684050d9db7cce10f61580","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.17.1.tgz","fileCount":561,"integrity":"sha512-eHdpsMCKhKhwznxvEfls8Wv3y4ZBWkkXlD3m7vtHIiWBqsMHspWSfie1s07mM45i/bBCf6YBMgz17FUxIXwmZA==","signatures":[{"sig":"MEUCIQC6T+my/2xzI2vaedMangtbqdpdAHiTTnwC85I9zGxvdwIgJwgxV8Jz14/su/N16gkO8rU//itJPXSzxcx7YCPtM6U=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1883659},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"f8e187b473274cc2011e7385992f07d319d667dc","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.17.1","@opentelemetry/resources":"1.17.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.20","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.2","@types/sinon":"10.0.18","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.7.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.7.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.17.1_1696947498625_0.4798664177082872","host":"s3://npm-registry-packages"}},"1.18.0":{"name":"@opentelemetry/sdk-metrics","version":"1.18.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.18.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"f84fffcabdb0e9504e3b219635c1099aabc9e207","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.18.0.tgz","fileCount":561,"integrity":"sha512-wK5zdNCo5cJvZog/lsqXCg9/Dt9UeNXQsskgqX8Yz+40t13Kb5CKFFkAMU8tNUxkvidHnD6G6sT6xeVCHQYe4A==","signatures":[{"sig":"MEUCIQCMiT/6j2k3CLJNS9PVe/gMQM/shgZfXDwNLg9l7E3QdgIgFnmCSIgxrXYgrtIppJgyKJkGNUzsbBET1HwAZBRXfG8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1890396},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"73b446688f10fd8dc4cf403a085f0a39070df7b4","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.18.0","@opentelemetry/resources":"1.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.20","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.3","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.8","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.18.0_1699353886784_0.2402177673494048","host":"s3://npm-registry-packages"}},"1.18.1":{"name":"@opentelemetry/sdk-metrics","version":"1.18.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.18.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"1dd334744a1e5d2eec27e9e9765c73cd2f43aef3","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.18.1.tgz","fileCount":561,"integrity":"sha512-TEFgeNFhdULBYiCoHbz31Y4PDsfjjxRp8Wmdp6ybLQZPqMNEb+dRq+XN8Xw3ivIgTaf9gYsomgV5ensX99RuEQ==","signatures":[{"sig":"MEUCIQDZiuGHcrjzMGF/TJ452D+P3TvNuhbyNQDLJOkdYsDPFQIgKtcP2GNoTjs9raH1wfRgj93Kw4tqRy7FLSmt98YetyQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1892649},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"f665499096189390e691cf1a772e677fa67812d7","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.18.1","@opentelemetry/resources":"1.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.20","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.3","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.8","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.18.1_1699466949698_0.2731195093463463","host":"s3://npm-registry-packages"}},"1.19.0":{"name":"@opentelemetry/sdk-metrics","version":"1.19.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.19.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"fe8029af29402563eb8dba75a85fc02006ea92c4","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.19.0.tgz","fileCount":561,"integrity":"sha512-FiMii40zr0Fmys4F1i8gmuCvbinBnBsDeGBr4FQemOf0iPCLytYQm5AZJ/nn4xSc71IgKBQwTFQRAGJI7JvZ4Q==","signatures":[{"sig":"MEUCIQCQbl3KfCIEDQc8lHtdKlCRprXMXq1iqFxWHzS7fhRAMQIgOOpi/ti3SCJMstM0WHGr81bNA0QPpltVs/+OuHKDlyw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1892648},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"d3c311aec24137084dc820805a2597e120335672","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.19.0","@opentelemetry/resources":"1.19.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.19.0_1702557329935_0.360745159718469","host":"s3://npm-registry-packages"}},"1.20.0":{"name":"@opentelemetry/sdk-metrics","version":"1.20.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.20.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"472d723d986a0a0cc1ee1170ed086dc18269d7e0","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.20.0.tgz","fileCount":561,"integrity":"sha512-07bFOQUrpN/Q5biJ/cuBePztKwkc1VGkFblZxAcVkuvCLDAPJfsyr0NNWegWeYe0bpGt1jmXScpUWnVD+t8Q0w==","signatures":[{"sig":"MEUCIC3XY/+kezXMIGl3icXMEXdu0pKWhpKhJ7kpzNi6cW5lAiEAtKlujqgWTX99ep+IDoNQKi5a4BEjbPjw6yGUGzBa6hU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1892648},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"57008533aba7ccd51ea80f38ff4f29404d47eb9c","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.20.0","@opentelemetry/resources":"1.20.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.20.0_1705313747415_0.3264673460411951","host":"s3://npm-registry-packages"}},"1.21.0":{"name":"@opentelemetry/sdk-metrics","version":"1.21.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.21.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"40d71aaec5b696e58743889ce6d5bf2593f9a23d","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.21.0.tgz","fileCount":561,"integrity":"sha512-on1jTzIHc5DyWhRP+xpf+zrgrREXcHBH4EDAfaB5mIG7TWpKxNXooQ1JCylaPsswZUv4wGnVTinr4HrBdGARAQ==","signatures":[{"sig":"MEQCIGpBDMhU8lTH5TE6aUTa3/f+ZAAJNZyvJR4W+y34uKgyAiA9nbAm4W2G9iFlp1OSXMtF3+Qs3M8RTAz9dMzR/gPTXA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1898573},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"828f2ed730e4d26d71f92e220f96b60a552a673a","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.21.0","@opentelemetry/resources":"1.21.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.21.0_1706249469816_0.2681154592027921","host":"s3://npm-registry-packages"}},"1.22.0":{"name":"@opentelemetry/sdk-metrics","version":"1.22.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.22.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b94c62403013e4c72b96dc747d71d786073efafc","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.22.0.tgz","fileCount":561,"integrity":"sha512-k6iIx6H3TZ+BVMr2z8M16ri2OxWaljg5h8ihGJxi/KQWcjign6FEaEzuigXt5bK9wVEhqAcWLCfarSftaNWkkg==","signatures":[{"sig":"MEUCIQDNXgVv9Uxbxg3/33/6F66wijwbwDd7GOiqKURWpli14gIgE4NQsN1CjuPkQcHMyQ25CAKybwbYPJODQxdspPHt3+E=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1903538},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"7be35c7845e206b27b682e8ce1cee850b09cec04","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.22.0","@opentelemetry/resources":"1.22.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.9.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.22.0_1709198294535_0.21078722486314505","host":"s3://npm-registry-packages"}},"1.23.0":{"name":"@opentelemetry/sdk-metrics","version":"1.23.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.23.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b4cf3cc86b6dedf5c438c67c829df7399bf64be1","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.23.0.tgz","fileCount":561,"integrity":"sha512-4OkvW6+wST4h6LFG23rXSTf6nmTf201h9dzq7bE0z5R9ESEVLERZz6WXwE7PSgg1gdjlaznm1jLJf8GttypFDg==","signatures":[{"sig":"MEUCIQCRviOtBHY4cxAbFmlSe5/d1UlZwJANaHyzFmHvAstAfgIgcfcrCMSSgbzKnYncXB5RxmguAOFxcmRGxTgWETblVvM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914895},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"5231aa255047fbc6ee3d6a299f4423ab2f8a5fbc","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.23.0","@opentelemetry/resources":"1.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.9.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.23.0_1712131805787_0.6556898049242013","host":"s3://npm-registry-packages"}},"1.24.0":{"name":"@opentelemetry/sdk-metrics","version":"1.24.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.24.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"205c19b6d18e385039d0a261c784a203c644fc28","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.24.0.tgz","fileCount":561,"integrity":"sha512-4tJ+E6N019OZVB/nUW/LoK9xHxfeh88TCoaTqHeLBE9wLYfi6irWW6J9cphMav7J8Qk0D5b7/RM4VEY4dArWOA==","signatures":[{"sig":"MEUCIFw7C6N1PHTi2sUnPwYd6pb4MEYB0sdDPAfLyVHSEEL0AiEAhRBkWnU/RMsb1e2pd8JuT67Zi9eH87TbDc40JN74Plg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914895},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"3ab4f765d8d696327b7d139ae6a45e7bd7edd924","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.24.0","@opentelemetry/resources":"1.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.9.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.24.0_1713969585182_0.37633556794494494","host":"s3://npm-registry-packages"}},"1.24.1":{"name":"@opentelemetry/sdk-metrics","version":"1.24.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.24.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"82ee3069b2ca9bb7c1e91272ff81536dc2e9bc8d","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.24.1.tgz","fileCount":561,"integrity":"sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==","signatures":[{"sig":"MEUCIQCTMSUw6bs+C6f+azmvQBAAOS2F2zkdYmTaUA0WsSSESgIgVgEsIIBcrrCeUZGl8a4ziCe01KmMiEcyeTk5/btVJww=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914895},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"41c2626fe0ed03e2e83bd79ee43c9bdf0ffd80d8","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.24.1","@opentelemetry/resources":"1.24.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.9.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.24.1_1715093558847_0.03178144750682588","host":"s3://npm-registry-packages"}},"1.25.0":{"name":"@opentelemetry/sdk-metrics","version":"1.25.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.25.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"0c954d580c17821ae4385d29447718df09e80b79","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.0.tgz","fileCount":561,"integrity":"sha512-IF+Sv4VHgBr/BPMKabl+GouJIhEqAOexCHgXVTISdz3q9P9H/uA8ScCF+22gitQ69aFtESbdYOV+Fen5+avQng==","signatures":[{"sig":"MEUCIQCTGV0/dFktbxE5zP6XFH0Eu0/cZ7CGkU7c5BAAq4CVjAIgG3QIuMsMnEWURsoo1SS0GKQqtBcHiCbSGaaBzrbsXaE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914152},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"c4d3351b6b3f5593c8d7cbfec97b45cea9fe1511","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.25.0","@opentelemetry/resources":"1.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.3","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@babel/core":"7.24.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"17.0.3","karma-webpack":"5.0.1","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.24.6","@opentelemetry/api":">=1.3.0 <1.10.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.25.0_1717607758345_0.42159719696026543","host":"s3://npm-registry-packages"}},"1.25.1":{"name":"@opentelemetry/sdk-metrics","version":"1.25.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.25.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"50c985ec15557a9654334e7fa1018dc47a8a56b7","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz","fileCount":561,"integrity":"sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==","signatures":[{"sig":"MEUCIQCNZXU0MM/EDXNSq8ZV/6psCW97vGmYrurc/e/iYg7LOAIgS3/fl7W9LtXeIauAqM5ZL24hsmc39zaKuX+90I3Edn8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914152},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"0608f405573901e54db01e44c533009cf28be262","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.25.1","@opentelemetry/resources":"1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.3","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@babel/core":"7.24.7","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"17.0.3","karma-webpack":"5.0.1","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.24.7","@opentelemetry/api":">=1.3.0 <1.10.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.25.1_1718875163722_0.02638450999168529","host":"s3://npm-registry-packages"}},"1.26.0":{"name":"@opentelemetry/sdk-metrics","version":"1.26.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.26.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"37bb0afb1d4447f50aab9cdd05db6f2d8b86103e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.26.0.tgz","fileCount":561,"integrity":"sha512-0SvDXmou/JjzSDOjUmetAAvcKQW6ZrvosU0rkbDGpXvvZN+pQF6JbK/Kd4hNdK4q/22yeruqvukXEJyySTzyTQ==","signatures":[{"sig":"MEQCICUUc6/yySv2TiNLcq7AT6pbNR/Hi6zEZwTkoKabjKDXAiBJffQVgekh2F9ABx6OLgxEpstV/+ZyN55vBGwjKtK3bg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1922196},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"720bc8c70d47029cb6b41a34ffdc3d25cbaa2f80","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc mocha 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.20.4","dependencies":{"@opentelemetry/core":"1.26.0","@opentelemetry/resources":"1.26.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.4","lerna":"6.6.2","mocha":"10.7.3","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@babel/core":"7.25.2","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.7","@types/sinon":"17.0.3","karma-webpack":"5.0.1","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.25.3","@opentelemetry/api":">=1.3.0 <1.10.0","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"7.0.0","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.26.0_1724836642462_0.8363214110577704","host":"s3://npm-registry-packages"}},"1.27.0":{"name":"@opentelemetry/sdk-metrics","version":"1.27.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.27.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"fb4f55017dc95a95ee00260262952b18e3e7c25c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.27.0.tgz","fileCount":561,"integrity":"sha512-JzWgzlutoXCydhHWIbLg+r76m+m3ncqvkCcsswXAQ4gqKS+LOHKhq+t6fx1zNytvLuaOUBur7EvWxECc4jPQKg==","signatures":[{"sig":"MEQCICee8j7NTKwwuMDYrj9aQl8y/q4kq6aAAzlgQtPxV3QOAiBvOudBQcJ+lEOSc3vaMLEPFzNp4YjETGVxdyID8KO5GQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@opentelemetry%2fsdk-metrics@1.27.0","provenance":{"predicateType":"https://slsa.dev/provenance/v0.2"}},"unpackedSize":1920417},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"eb3ca4fb07ee31c62093f5fcec56575573c902ce","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc mocha 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.20.4","dependencies":{"@opentelemetry/core":"1.27.0","@opentelemetry/resources":"1.27.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.4","lerna":"6.6.2","mocha":"10.7.3","sinon":"15.1.2","webpack":"5.94.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@babel/core":"7.25.2","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.8","@types/sinon":"17.0.3","karma-webpack":"5.0.1","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.25.4","@opentelemetry/api":">=1.3.0 <1.10.0","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"7.0.0","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.27.0_1729695104075_0.2954021766849051","host":"s3://npm-registry-packages"}},"1.28.0":{"name":"@opentelemetry/sdk-metrics","version":"1.28.0","description":"OpenTelemetry metrics SDK","main":"build/src/index.js","module":"build/esm/index.js","esnext":"build/esnext/index.js","types":"build/src/index.d.ts","repository":{"type":"git","url":"git+https://github.com/open-telemetry/opentelemetry-js.git"},"scripts":{"prepublishOnly":"npm run compile","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","test":"nyc mocha 'test/**/*.test.ts'","test:browser":"karma start --single-run","tdd":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","lint":"eslint . --ext .ts","lint:fix":"eslint . --ext .ts --fix","version":"node ../../scripts/version-update.js","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","prewatch":"node ../../scripts/version-update.js","peer-api-check":"node ../../scripts/peer-api-check.js","align-api-deps":"node ../../scripts/align-api-deps.js"},"keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","engines":{"node":">=14"},"publishConfig":{"access":"public"},"devDependencies":{"@babel/core":"7.26.0","@babel/preset-env":"7.26.0","@opentelemetry/api":">=1.3.0 <1.10.0","@types/mocha":"10.0.9","@types/node":"18.6.5","@types/sinon":"17.0.3","babel-plugin-istanbul":"7.0.0","cross-var":"1.1.0","karma":"6.4.4","karma-chrome-launcher":"3.1.0","karma-coverage":"2.2.1","karma-mocha":"2.0.1","karma-spec-reporter":"0.0.36","karma-webpack":"5.0.1","lerna":"6.6.2","mocha":"10.8.2","nyc":"15.1.0","sinon":"15.1.2","ts-loader":"9.5.1","typescript":"4.4.4","webpack":"5.96.1","webpack-cli":"5.1.4","webpack-merge":"5.10.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"dependencies":{"@opentelemetry/core":"1.28.0","@opentelemetry/resources":"1.28.0"},"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","sideEffects":false,"gitHead":"4b1ad3fda0cde58907e30fab25c3c767546708e5","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"_id":"@opentelemetry/sdk-metrics@1.28.0","_nodeVersion":"18.20.4","_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","dist":{"integrity":"sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==","shasum":"257b5295bbe9de1ad31c5e8cb43a660c25911d20","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz","fileCount":561,"unpackedSize":1922324,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@opentelemetry%2fsdk-metrics@1.28.0","provenance":{"predicateType":"https://slsa.dev/provenance/v0.2"}},"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH/Z6d+HyDNtyNUtpuzVdAyqhlTCfvqGItbZ06UPzbGwAiEA1ZqqIIyQTA5ZM/DN/dHtZc0GZcZv5TtaAtdiIAF0eZI="}]},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"directories":{},"maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdk-metrics_1.28.0_1731926513320_0.5425586601271688"},"_hasShrinkwrap":false}},"time":{"created":"2022-08-24T17:44:35.369Z","modified":"2024-11-18T10:41:53.964Z","0.32.0":"2022-08-24T17:44:35.717Z","0.33.0":"2022-09-16T12:14:58.301Z","1.8.0":"2022-11-09T19:45:34.297Z","1.9.0":"2023-01-11T21:46:29.914Z","1.9.1":"2023-01-30T15:30:13.362Z","1.10.0":"2023-03-13T15:53:55.612Z","1.10.1":"2023-03-20T16:10:34.822Z","1.11.0":"2023-03-30T15:30:56.074Z","1.12.0":"2023-04-13T18:13:01.808Z","1.13.0":"2023-05-11T13:30:07.116Z","1.14.0":"2023-06-06T06:00:55.809Z","1.15.0":"2023-07-06T11:27:09.167Z","1.15.1":"2023-07-24T14:32:49.159Z","1.15.2":"2023-08-08T13:21:18.974Z","1.16.0":"2023-09-11T12:14:31.689Z","1.17.0":"2023-09-12T13:12:34.838Z","1.17.1":"2023-10-10T14:18:19.024Z","1.18.0":"2023-11-07T10:44:47.115Z","1.18.1":"2023-11-08T18:09:09.980Z","1.19.0":"2023-12-14T12:35:30.166Z","1.20.0":"2024-01-15T10:15:47.729Z","1.21.0":"2024-01-26T06:11:10.039Z","1.22.0":"2024-02-29T09:18:14.841Z","1.23.0":"2024-04-03T08:10:06.050Z","1.24.0":"2024-04-24T14:39:45.460Z","1.24.1":"2024-05-07T14:52:39.090Z","1.25.0":"2024-06-05T17:15:58.569Z","1.25.1":"2024-06-20T09:19:23.925Z","1.26.0":"2024-08-28T09:17:22.703Z","1.27.0":"2024-10-23T14:51:44.571Z","1.28.0":"2024-11-18T10:41:53.564Z"},"bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"repository":{"type":"git","url":"git+https://github.com/open-telemetry/opentelemetry-js.git"},"description":"OpenTelemetry metrics SDK","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"readme":"# OpenTelemetry Metrics SDK\n\n[![NPM Published Version][npm-img]][npm-url]\n[![Apache License][license-image]][license-image]\n\nThis module contains the Metrics SDK of [opentelemetry-js](https://github.com/open-telemetry/opentelemetry-js).\n\nUsed standalone, this module provides methods for manual instrumentation of code, offering full control over recording metrics for client-side JavaScript (browser) and Node.js.\n\nIt does **not** provide automated instrumentation of known libraries or host environment metrics out-of-the-box.\n\n## Installation\n\n```bash\nnpm install --save @opentelemetry/api\nnpm install --save @opentelemetry/sdk-metrics\n```\n\n## Usage\n\nThe basic setup of the SDK can be seen as followings:\n\n```js\nconst opentelemetry = require('@opentelemetry/api');\nconst { MeterProvider } = require('@opentelemetry/sdk-metrics');\n\n// To create an instrument, you first need to initialize the Meter provider.\n// NOTE: The default OpenTelemetry meter provider does not record any metric instruments.\n// Registering a working meter provider allows the API methods to record instruments.\nopentelemetry.metrics.setGlobalMeterProvider(new MeterProvider());\n\n// To record a metric event, we used the global singleton meter to create an instrument.\nconst counter = opentelemetry.metrics.getMeter('default').createCounter('foo');\n\n// record a metric event.\ncounter.add(1, { attributeKey: 'attribute-value' });\n```\n\nIn conditions, we may need to setup an async instrument to observe costly events:\n\n```js\n// Creating an async instrument, similar to synchronous instruments\nconst observableCounter = opentelemetry.metrics.getMeter('default')\n .createObservableCounter('observable-counter');\n\n// Register a single-instrument callback to the async instrument.\nobservableCounter.addCallback(async (observableResult) => {\n // ... do async stuff\n observableResult.observe(1, { attributeKey: 'attribute-value' });\n});\n\n// Register a multi-instrument callback and associate it with a set of async instruments.\nopentelemetry.metrics.getMeter('default')\n .addBatchObservableCallback(batchObservableCallback, [ observableCounter ]);\nasync function batchObservableCallback(batchObservableResult) {\n // ... do async stuff\n batchObservableResult.observe(observableCounter, 1, { attributeKey: 'attribute-value' });\n}\n```\n\nViews can be registered when instantiating a `MeterProvider`:\n\n```js\nconst meterProvider = new MeterProvider({\n views: [\n // override the bucket boundaries on `my.histogram` to [0, 50, 100]\n new View({ aggregation: new ExplicitBucketHistogramAggregation([0, 50, 100]), instrumentName: 'my.histogram'}),\n // rename 'my.counter' to 'my.renamed.counter'\n new View({ name: 'my.renamed.counter', instrumentName: 'my.counter'})\n ]\n})\n```\n\n## Example\n\nSee [examples/prometheus](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/examples/prometheus) for an end-to-end example, including exporting metrics.\n\n## Useful links\n\n- For more information on OpenTelemetry, visit: \n- For more about OpenTelemetry JavaScript: \n- For help or feedback on this project, join us in [GitHub Discussions][discussions-url]\n\n## License\n\nApache 2.0 - See [LICENSE][license-url] for more information.\n\n[discussions-url]: https://github.com/open-telemetry/opentelemetry-js/discussions\n[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/main/LICENSE\n[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat\n[npm-url]: https://www.npmjs.com/package/@opentelemetry/sdk-metrics\n[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fsdk%2Dmetrics.svg\n","readmeFilename":"README.md"} \ No newline at end of file diff --git a/tests/registry/npm/@opentelemetry/sdk-trace-base/registry.json b/tests/registry/npm/@opentelemetry/sdk-trace-base/registry.json deleted file mode 100644 index df5892a53b..0000000000 --- a/tests/registry/npm/@opentelemetry/sdk-trace-base/registry.json +++ /dev/null @@ -1 +0,0 @@ -{"_id":"@opentelemetry/sdk-trace-base","_rev":"65-20c7afee9d8d681944f1419799e365d9","name":"@opentelemetry/sdk-trace-base","dist-tags":{"canary":"0.25.1-alpha.23","next":"1.8.0","latest":"1.28.0"},"versions":{"0.24.1-alpha.4":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.4","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.4","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"8ad47864d4fd534b5a24f3d9b36aa91b348586f1","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.4.tgz","fileCount":147,"integrity":"sha512-dRMAseFliUOYuKoHha+3/qdNsU0JyY8085xzrRyJP7TvXo6KQKzotRCtMTMvXgv3UOufeZTMHyO13J3cuB+eSg==","signatures":[{"sig":"MEQCIA7Tcl+88PcVrNMkgkaAbIhBNLaDuB6ol0YEz/lQu/SPAiB9VqTx7RX1pJT9c2zbFfw0RPOaHeljtad8c+NBp9C5HA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":230456,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDDvUCRA9TVsSAnZWagAAwZMP/iLkavfZlJi3bwI7VOqY\n9dMwmFZXKZvBsrH2uGGD025BEAtA1p2NTARa1h2UTr+Zx5yI04ziJUkuZ7MH\n4t7QpMtHJCOAzavVzSFuy3WNFjG7qq8sK20loQL1NpmwVCC/Fz2+EohD5Wkj\nmxmizEdH+DkUNpKbsuLMlEYkPz3ZGW+h4b4nkeCt3MylOvuO0/1wW+NOvjk+\np8baQh3roC+JFZ445yQAQek90YylXzvtvhMuf4NYSedtfABAB3w08iy9I/sZ\nR+KlcC3px8zMsxUq2BdqV3EOiTwaqAZbCfaaPma1yjTSxHum1C2HuWjkUqMc\nxG/PctI398iEeAe0YtwKqK+BrjdV3RnlHNSS0rOZiRDqAqBKqPu8uvB43g5e\nhpEJW6QG34KGfO85mNgJwp8gMtrhQ30o5cetTJ6GBQBjySOgF5SUN4DCQmmb\nsZyR3UHNarAQIQWR5A2DTeqK60BTz2jjCKMIA+EGerhDbEm4kgc62Hml1O+a\nFzqycPereLxTLqRQybK0CSSe8IaZkdUQxir8FMH1UFkzM5jlQINpsa80u1fy\nDpVZ8QDFf2Yv74IqMmHjtOSrbh81IWx7GEAslvyNyvz8TxtpcVWpntd2Tveh\nzJwqF6xwYbFzc7kKYA7R9MpaPoQLeHvxFpPJjasjcswVDRxTEnonXXqBxHHu\nK/LE\r\n=dotJ\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"a8d39317b5daad727f2116ca314db0d1420ec488","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.4+a8d39317","@opentelemetry/resources":"^0.24.1-alpha.4+a8d39317","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.6","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.4_1628191700214_0.39586224494227173","host":"s3://npm-registry-packages"}},"0.24.1-alpha.5":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.5","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.5","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"5801c08684013d9cd72ce6ca781f0fea2f5eb776","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.5.tgz","fileCount":147,"integrity":"sha512-V5N8VEc6IUQP9CH5NPOPjdwhnX94Bboe+AKje/aQLyageVYoHFxkqLklI3EfbeuDCAoDQ1cSRC2PuqEQJGlBBg==","signatures":[{"sig":"MEQCIGoA1de87lUaFgMYQO6M31GSBowdvSJUcfMWYmZjl9WbAiBz4e7nYoJs/y8Eg/7vOVFT5uf6fpzxdgjqPpswdYAcLA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":230473,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDR2bCRA9TVsSAnZWagAAMmIP/iCQEezryq1P/10oM7l/\nQvh6jsYykBh9vZvXDvExS8TPKO49HZojjwFNBraqmJJSjQKgxIIB939N7EQw\n5Swfp9dUEMx71YaiigGF9r3pwQ15R+sN+HJPnTKEXIXvoTlutmTY1TDb1SGV\nhL+pviT97v9sUeT4NOphZ+eWkEzGepM/VM4No5O2w0KPFCsMu7cXFLC0UZPQ\n9DEHY+zAtpwJ9J0oSpQO4ci4cj7cp+6pKHGNxSzZPbkHwT9F8pCjM0CXx2O9\nnHs/HQhD99BIGyrmaQ45Q9lz2LOwIdFvtcPMZOfnfSYMM2yOvU7svHFugFCM\n3dbnPBp8U9gk7CMzDfR098vwQ+naW+1fovStECk/cLCzGPVTrJOMs6oknvnk\nuAHeyv/TI6THT+VyIphzlZu1R/NodGTkjV9oTOl4KodIfCIJ7nVm5mNlR2Ym\nlYvbKQWcLdrtbftq8Yzberd48DOYa3tgj7HKAZOZL/Op8jnElpK+M1gApkBa\nUOJ/TYyrAVhOXhPv4IItUKj9unTUn0ixT+zW41agKvOoEc0oNuj5SydsB5HZ\nUC88UQPNL+zWxtf3YEUYQh/cfgcFTzS0Oe3L9n1CeBYUi+j9JO9kvFMA49IC\no8a7MXMeANuxSynGSXQF8TH5VV7yLkiHEmbEadcal+GUpD0b/jhY6UjS1MAp\nwKmg\r\n=ogTt\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"b69ff35e90a33d20a0154dcd326f1467dfd39e2a","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.5+b69ff35e","@opentelemetry/resources":"^0.24.1-alpha.5+b69ff35e","@opentelemetry/semantic-conventions":"^0.24.1-alpha.5+b69ff35e"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.5_1628249499147_0.1555991669652872","host":"s3://npm-registry-packages"}},"0.24.1-alpha.7":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.7","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.7","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e1a60e391fdfaeb647b83318319f15e6b80fac73","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.7.tgz","fileCount":147,"integrity":"sha512-tenqp2xjKQqTxoO4+1S/VEDmdIJYIoWlOnCskibN86Am6UaSxryDtEaXzv4yVAoQJHF6c7QA308dgV45rAU4Ng==","signatures":[{"sig":"MEYCIQC0BMKRFFm4Xntj28BRWnEKOrdJMTRwty3ypoxV3T45+gIhAOyfi84y+2/dZdPwkzS8/GRgattYPgeoW6DoBy83c3eE","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":230473,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDouQCRA9TVsSAnZWagAAKWEP/1nv1RMnwNq8l2paxhGt\nMg2271/nrnAfCYD/H6+HSo/5I9AbLrHZKzN+QL+BDRC16OHnpCSEpjRqzuy3\nW8Gw38fSwWV5f72l5hs9pzSfqEpXGwobGlHLONHsrEnLgd6GIGNZhwNp/UjQ\nSEJ8Z93Fg/Yp5WwbsTuAHpiSaJ8IEHIlmTA15d93OJAZ8O1qOcAW2BszacIV\ngrWOzFtnuD29o36rQtO4Q8FMyTseCMfoxGqDcTXM9UweXq26A42QbPD2mx9W\nCaPNd5gA5RcA9KWI0pEM9SBtngmbVUA3wPh/T67eZ+xV5wUIxxc3zkz0Z8/2\n0XQDEG9Hd6vYFe2x9Sybj1bWCievDMMmrwjNCIMoBKCZJsBVLd0RsFSwb7/P\nYCxq7MmZhKAFkt7c5W2aUL9wOQEVlAmT7sxez5m6kgxNtxbC3kZn7+vEfEp6\nXPekhMdVHnIdeADWLEn1l1KWXghpE1eK0jUIzs4FNJzCBGvIRHBRVmVaglvH\nn1krQ+6kga6APLro4T+IwbrneSNMTKmmiKSVxW9dZAY0ECz9OWaDPvib8d7B\nSY8q0BHDcZCysnfwhMdmqDoGr0s0GPV2tvdhUv1k0fiFjd627uLh60TgwcH9\nic0sNdBBd2blhygx2TEtBXFurcnjScvNprRQCQQtgS6D7YOUVcAdQaIwAmgD\n3o9l\r\n=JzYM\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"365d502eaa0ba1f9799998f050d36073e943032e","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.7+365d502e","@opentelemetry/resources":"^0.24.1-alpha.7+365d502e","@opentelemetry/semantic-conventions":"^0.24.1-alpha.7+365d502e"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.7_1628343184139_0.5476851490956296","host":"s3://npm-registry-packages"}},"0.24.1-alpha.9":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.9","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.9","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"40822431d49ba09e480715241ce32a600ebbd2b5","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.9.tgz","fileCount":147,"integrity":"sha512-yU1c7NvvJMbNxlzL59OZ2mf1PYPcqNkxXcAL5D4UD04PfmS6d/NIVfzYmAs/leGhZukMiTofHn/KKw2RTs2cdA==","signatures":[{"sig":"MEUCICM6jKq78kih88ikV05KI25KywAyHyO6jp3PBZGmFbIqAiEA9UFwNt6nOazWH8/ZCGq8gGixg6P9FFMRuG3B8XiE62Y=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231300,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDpQ/CRA9TVsSAnZWagAAdfcP/RBEDRX9XNfxwhOEx3Xc\nZyDOEW/F9iWRJ6IMmUPB6xjThi6h3I8pdAeXqgRTCDEJdBLuT6Ja0dXJd26X\niBanP3KBWQAZubTDzSGlR31BLg2oeFPZvcuKHmfEHXCB7hyLCnI76WO/71P1\nL3lj60b3jw8/F0y9HKg0bKVhJSnCoNSTKIt5KcH9hLbOrPusRvWc8t9CRCfX\nAaHDlGz81M+AgIIr2ibMH8z5OrRK1U9Bd5lUD1V4pgxujH+tjeq27KXI//JX\npHtJlv/SbELg/0pU0JrqZKQpS5TfwV/L2Z8qWXQmDYMnxdYcUa+tjDOwo/zs\nFLIs4OecNpFpZAyEVeVzGMgbgcGgTOAGixZOzDGBqL7ZbOqKHn/njvA3WX70\nG/nZKBThXAl29+Kl2OHk5i0FfO4BDUZWN6KqpFX7zVnUJIlpVj/5DCDo7gc9\nOGCp8pNpN/kSqocaD8XeQ8U4CAoWUqQ5g2/fnLl4Mui191Rngtp7q8RiswIf\n2qAaWf1AJvRVr3nDycfrhw2FPU2sMgMSTHnWEVpS30mfkPwPaR68rkYQvzYx\na+eH7qvwXwnIBtUfb1Dal3njr8g34NfOp7rBUONHs6JFV07Jq/4IVLvkoGWI\nmZ0GqVIn8iqrAkfHef2fTPUQr+i89L6ItXiFdfrPXL29qpNwPd9CfdIfx8Qm\nbi/V\r\n=h0kW\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"46a42a18570da8a0b2ae027c80018ebfb6c8096f","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.0","@opentelemetry/resources":"^0.24.0","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.9_1628345406996_0.3167053382863443","host":"s3://npm-registry-packages"}},"0.24.1-alpha.14":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.14","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.14","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b670fd695883a066d7783c43af630c5e6b13d558","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.14.tgz","fileCount":147,"integrity":"sha512-98OBTMreg/uqFobh5u3A95fILVzaRDsixo4YR/QwIbjslLjyXt2+iJgUOtMN9W4O62tp4eeedrGqnINpOwfZPQ==","signatures":[{"sig":"MEUCIQC0zLhhndrpHsprXHc/NlLTppr59Plb6Uj3PNUl7lgN5gIgMwFK/IGoPqqcXjXWMAdWJqAA5OtrmqWa/MKWdnqdFwE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231337,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhE+POCRA9TVsSAnZWagAAgpUP+gKVAuimNG145WYxTnlQ\n2aMnt4P8TqAKtSToIaJtUZLCkQypCs+CM8EtQcJfbwh4xgQFLdqGxA6D4gPp\ntQ22hVMQlp4TyM56aepWk50Egh1niZaJJG8fzbIwHhEOiP54mD+2yP6wGE9S\nBZZO7ytJBOg6jlGum7KnGvgqVTF+h4h6kK+4rxmH+/HpyOwZLk+JGsABQLQl\nrnpglO6QzMocf0uuEkcK/BVHMd6cD8MLOaJ/ZSFD1UdvaVE+pyk3xuoK+3/u\nTtoUmgYZxObBRNBbHuhNfKzQCULFzMElnFpAZWk3dYo6AG/P5+N2ARpCjfZJ\n0jHyeeElYdK3j6lL0mjfTmYNYs+4DlFjU+Yz6jQevjLSJaGopWnXTaekVZwq\nSx010v3QPh2nOTfGci7eFwuILNrHQRy8MWq30rw99li55z2Uoa68kWIbDPsn\nfsmhTh8k4VWFVF3TSWDEZuLBbU/n+vVhVRkLk56bXsxWZ7xQOSYWAOQGbjJS\nmztmVq/5jC+4WGeZM9Z3geCbo/QlJrUampC6L5izl+3PIXanbuOw/PYISJ5b\nteE1wjWbdqTfA+8zABiI506kR6ssXZHi3w6TPJA4x3Jq80vIFfmv5h6rF9Oo\nEW4xBx0fyxpOruWeY6YZNOc0uGXOZpR6FXXiAzaSr6l256cJ/6rVGpPNRevn\n0vgT\r\n=lqQF\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"4553b29d4a04b5b7e4bf87cad64dc2fc8c740d8e","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.14+4553b29d","@opentelemetry/resources":"^0.24.1-alpha.14+4553b29d","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.14_1628693454833_0.767569219106649","host":"s3://npm-registry-packages"}},"0.24.1-alpha.18":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.18","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.18","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"d06ff14f49cea7c13b31bc3553c1315d9c434a9b","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.18.tgz","fileCount":147,"integrity":"sha512-QE0ZqebO4BJpm611b0165kvR40LkHvTfps406bv/wDO7SNz/nMXIv2sUKhJBqGSRK+2/jUG0vMvpOkBK7HxRZQ==","signatures":[{"sig":"MEUCIFToWHfCxokuNtSvaj4t8JR3GTRFdGOLf1iy8g33VW1vAiEAlSReugAeVdszYsfnrs+VI2Dd5XxVT4++YH7PJZnUMyU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231319,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhF3vJCRA9TVsSAnZWagAAiRwP/1YNDd+IH2bLR2ut+boE\n6PAa/4KiFKMpw4j4Go89kGpTfOBz5nTZWQu4+A9omScvVvBY7XBpQLqRTyRl\n1fmS3lSPELQtMv+102sIcGkz8zvnow5tVg2HNUBMG2xLXDSjR7LX9ruoMKgY\nAqMTCjSRxvTey6s+N0PBrpGZio+7oaQQocDqxKitiLkMX8EiHtcieY1zAskC\nElrtb2hg2najiDB3U1LKehREnNpbgjT3MWAiIJDWpyaL4SCG0zl127ERZfNO\ngv3y8m3S2ndB3MrKghC5bQ0xxfMYjBWGfVh5zPZAjiRwMAjXyXVeLgiHioH/\nwKWwQcWqEAcFOh3z/S0Xkx/MDNXZL+yQCXtE7WOzxvb+GT9errzYPFMzyBUq\nG+wShRLftqrEGzbo36fCHULYXGEaxfm2TF6HF/wTyR8JsNsNSzX2szdvUnG2\noWNE1owerG13zMQPqQqNk0H4qZbvLXE8F4MWPCVdsaWuyxX1Lhll9eknbBq4\nx5RQxfBQNGrI1Iw2sR8wGkMO7liZRqLBkAa/FlO0M0/n5ODxqT9QMBPk/I+2\nlT/fg1k6BuO7I3rgVAAYPDqv7nHLD1hCKbZ9dnkonZPssHxtY5ZLedoYk/G6\nfCSklauAiAjmvvZvVDzvxLtaYOfNnF0O+dyi5va77RMaaLu5AjHmG3Zv+9TR\nBNgS\r\n=gw2o\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"bdb12daeb2e4ca3761d1411125f5d883471709ce","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.0","@opentelemetry/resources":"^0.24.1-alpha.18+bdb12dae","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.18_1628928969813_0.9380634139248278","host":"s3://npm-registry-packages"}},"0.24.1-alpha.20":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.20","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.20","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"de349f8b90e4987906b14f064680403eefd73f40","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.20.tgz","fileCount":147,"integrity":"sha512-T4JgUpq3fRoHqL8RI6vghZ0Rf8Yusm7y5j+ZeS4gKq31KedB2AzZBNK6jRWprJc22IJ5/YP+0HTBA17euvlkCg==","signatures":[{"sig":"MEYCIQCVUqO1X93DZhqXTmIXc+aU8CiF0cDSHzoHjXz/1VsyRwIhAKBtjWBbfY4bKko6tzQDg1dDWc6N9gZQ7wecvsRn4PAO","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231337,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHCT7CRA9TVsSAnZWagAAL38P/RXZXrIkyb9DVgqFbcRH\nzNfnDhVtGTMNJbgXkZdVATXsWy4X6IAiKKZEc7rYIRjaYslOKdYyB5lgqTty\n8BL1J1LNRIXu2H/X/qviyGcV1P0+M1otFZ1v/edn8qCbO8iWjLHJ23Guweb8\nxQfkKeZEETeL13KzxZhm6Gy328e3o0Bd5fZBnyZ9jG0vWesS1HBkjHqBxLKd\ng0qADSfgPtSSlJxKweLGqrqQBKjHpZtBmOKJdXLgLHKEsSfPxgl8Si5tHRPa\nvLX8zYVDFCk8rryu5fqLpWe5d2o0RQRzWnVcw3FA56CZBxUpvNR3WlgFOgil\nWntcmJoGH1uUwWq3d6VqIPfVrapMiQknmGI3+HgzjzWFIphGaScawwnOjIVl\nWlXYR7CECUSrGqa1jwtspbfNO0SfIg922CBxWsC6iysJLsyfa6ziPyzwjJRB\nqnmc/bYyIpbEYiQ+4dOT1TJIK8ewtRJcTAVcDwl9/VSsn+GZ76/NdZldD+/p\n/tg8WZfPfp4cBjLhwBoBZ+a+rJ1naB64wc66J7aIfM2eRpUY0TexCGYx4USz\nzON5xTVvqlzpqvJKU+K+HctdzBsv0wxYs4/kQ4suR0NqfFVvpB2BZIe9KzH0\n/f4U9smqyocc0yHybZxG6HbF2HsbRR0vn+Yn1iQdieh8g16Wljk1vtq+QhDL\n6rYy\r\n=9wQp\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"f12913899ff5c588e10830e5ba7183d9115c3442","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.20+f1291389","@opentelemetry/resources":"^0.24.1-alpha.20+f1291389","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.20_1629234427062_0.7964990125133731","host":"s3://npm-registry-packages"}},"0.25.1-alpha.21":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.21","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.21","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"90130b88074239b003905ee68653a8a62aad8ca2","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.21.tgz","fileCount":147,"integrity":"sha512-W44WsBbrmkNKzHlbrjnoHlE8JLl419V5Vdhave+IJ6AmAdmsZQi7i99DgfcVVCyPiXDEXRGT1D6VI6d6MaRWHA==","signatures":[{"sig":"MEYCIQDNiLqpu8u+Cs1lTRYS6RSu8slzRafP58p5LM6Nx3wOzQIhAMhg8xKg2GTIQic/3j4xSjcKfJuZRhndoRhISTpOY7wh","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231355,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHWqqCRA9TVsSAnZWagAAOAoP/R+kSqbk2lLBLgSfS4tM\nW3P+ESNSTUKhzNGPD7zvhtFXa6p4lzLLUk6q8AIviCtXfHg8FryZL/94r0Sg\n2o9+PEY2MoR8dOdMw95ywLXJa4UfaY1QUOFaMYCV5fGigEfhLZXlniauYVEW\n2LQb5dTdiTsXGW3FLdnqLYPAtsnE7NQ/aoaDrHGIAThtSMnv97VMpz5lKk/Z\nEeVWdzLQ/VZjxtxxbgrnk8VqcjYq0bz49PC2HdzqeSq1EOT7hShW9cZ/BIkf\nKkD5vZZA90fvgduG0as7yc+KwMVeqGTkTcecdykS/cflls0ptJ9UDOvTpYgZ\nrSggs9J5/TW6UE62HNYyiSzNbKFC94OX+SactQ0zRp72yJs74wpD4UpQut8c\nHoeqLsmrWh7Xtz5uvgwt4100uhW2FV15jkwinhMMK4IYcIV+iN0M9CiouKbt\nA9wD0dOMlvC3rifnF8fn5D0PYe69QkEli6gJxrsiRfzOAXoO52KutFcDsQPW\n12U6XfLNhdgoL9kahXgEa+g/8U8s4IUXVYztF3DqIKRMBkGhf1R8cOwL0lo1\nIGYp3BmpyF7NQ3IKmV4mn/4d/qjXu1AetYErAIjcktgs8tCgR2Uz0+6A0pCR\nh6Qc1ZOlinidoiuzlLem7VUIQsuP26zaD6xJgf8BWg1Oekjroy3+KuL2S/XT\nwnjN\r\n=ah+v\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"0ef1fc28d366b74d98b73b5d6334ffdc75342fe2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.21+0ef1fc28","@opentelemetry/resources":"^0.25.1-alpha.21+0ef1fc28","@opentelemetry/semantic-conventions":"^0.25.1-alpha.21+0ef1fc28"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.21_1629317802400_0.8977428987171272","host":"s3://npm-registry-packages"}},"0.25.0":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.0","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"4393903a7db8a5ae81a99c4a34121df67e4fdfbe","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.0.tgz","fileCount":147,"integrity":"sha512-TInkLSF/ThM3GNVM+9tgnCVjyNLnRxvAkG585Fhu0HNwaEtCTUwI0r7AvMRIREOreeRWttBG6kvT0LOKdo8yjw==","signatures":[{"sig":"MEYCIQCti4VKK/PEN3BO7Ga65nTpCQWZUAuemjlsTmlgLCCXYQIhANG5iAycgkYjQtzcEkRDDSLBlDon1b+0nKg68xzEpiov","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231280,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHXi+CRA9TVsSAnZWagAAZnQP/2uVRo0am5dw3Xuz6WG4\nQhlmd9qcn6htXQYGAPH7eJ6ctlEl0xtSXDbkCf0IL2RU62nAhac794wKLdfp\ned9yI5B9HghS5LdapNWxeOVJbUhlfHUo3GKMagps1CTkpuElW/bPbWPgK5JM\nuRbHCx9O0MdY1iSzQm6yMd38xjI8EzA6RSo8xg0Z4AkH8OwANCsNvblW28OG\n9UZe+iAWYKOo0bJbe0WB1zKb41YMAhKczKMvo+yoHFPfsuyZYErktOsfje10\nMHuO/LmcSClwrX49MyDmXT3OKb6p6/bKubCqNvYWWQx/jDTua4Hjmj8OgSXh\nziD8rNObdGCLAqltz/buPhb/B7N27f6jzjB5jNkm/LVmL9Ujfkvwth9vfqk5\nE9g/x/pjHvyTaAX42UtJov8hDy9iEaQUqKIaUoDa5URlgzpYXaV4Hz5Cahc7\n7JyUi7M5cDK535/rbEfXUErpstOmpvvv0ATPhZuqNBzAOkAViyuLRgjFEVqO\nSOLCL/DCmhwcO3KOIzJRH0Z0h5CojstFXbCKsA0E0uLNLAl1UnzgAPFEbt/2\nmlhjPRIThzYOFwJysMXAOaJNtjk4ZBSlXan0t1sxj2eQplG+1gxpin29yDfB\n8gEKMkW0V6IZ0PVMwN/b0PSHvcnuOPEg1A1WKvW09xe/h44ww6z+Tf2VfeAu\nV/HP\r\n=aFb+\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"0ef1fc28d366b74d98b73b5d6334ffdc75342fe2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.8.0+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.8.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"0.25.0","@opentelemetry/resources":"0.25.0","@opentelemetry/semantic-conventions":"0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.0_1629321406548_0.048648051511765544","host":"s3://npm-registry-packages"}},"0.25.1-alpha.2":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.2","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.2","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"4de3a67970ff6eadf12b06710f5ac77e6c407d89","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.2.tgz","fileCount":147,"integrity":"sha512-OhXgn/tsEtvxOVZ5SQXIAV3d6pFd5rMMTPRsUK8QTHsSBVSOsKALygTvcRwPQJlUdSdJkxGA6NfUg8pOx1OjHw==","signatures":[{"sig":"MEYCIQCR/JgLC31LcnbSaR3Sn6LRjqitnzJq1b7eT4nBZ/apcAIhANLoKitgEA5BztqHIcLVhrvML97Bk2baz8xORcry+cG/","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238819,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJBX2CRA9TVsSAnZWagAAUUYP/1l0sLT+uC/JTj1TfINp\ndWHrPZhqeMMiEVARoPTILwDF2mZ6lWUn4OWe2m7waueZhQw1i9mlD6QKtNeA\nkps1z+L/WUHqlPzT2HKHG5JTi2OwkNfoO5lNoU9tpuvhd7t7/PWSg3O3S2rY\nBSPt+0pOne0mBD6uRi4yO9X4iasOS31QZJvNmkEdCBRy5K0yx2u1m7LzLzcA\nfrb49X5PJMsAywkJKEyExq0nbkcllod5fw36noniLe5uXiScWwrGohxWlnL2\nEzgRqYqcoWMZg6MRVoh3K/VwymhIyxfB/kailGv4dr6wU0dUpiBwVtBdADW8\nkoMx2LS/vDTva2sMp/W/4P5mt3Wqcy503X07N9V10vQolUvRR0hcoXV+OU41\neRbZVWdFWXo4Q5I4D3E5UTgIAloSlVNkanra66/p3nNWtTLakNcf9EfQRl/P\n34n5E8eTN3Wl0O8NiSimEVHexew26ac9aSEj74BuKBWEntPDQqGAWS5cg5/t\nzUCFSzDIjMNDlNRBH8bprcJKNWsG0V3ko9LsngyW8eA+CCwRhEOAcOqvdEwb\nfeBK3PC7TMNo4WuNufVgCm8ejH+yP9u7c3dTtU4Z60y/BGvvLdMZA9UMhABD\nFEUQ0lGoOVX2ALBNQh5KSMk/085WuGWj3dT5E83OKnVy1uD3mgslbdcNpj/Q\neOKC\r\n=tFMf\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"78a78c093c2df24b66c47af4e037da9a6098fedb","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.2+78a78c09","@opentelemetry/resources":"^0.25.1-alpha.2+78a78c09","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.2_1629754870123_0.17987001772703892","host":"s3://npm-registry-packages"}},"0.25.1-alpha.4":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.4","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.4","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"29ba973f0f357c64da5ece13e2565f2745500ef3","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.4.tgz","fileCount":147,"integrity":"sha512-xwUOlftFr1dN4Utp9Scu0+eDbQ9oEsQPJ5cUu6I5WW8wxURqV/d4f6UTL1bmTxm5x5FlS9ggQSVBiqHeWoAZEg==","signatures":[{"sig":"MEUCIDIO3X0Ip0Dqcxe7kAgyn4rie4xDhB062Pc/p6GnpywmAiEA0kK9ptzzM93v2jgnVtEt857r0cBWxnTxxDaE585k7vI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238837,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJUl9CRA9TVsSAnZWagAAsmQQAJp5bnBhFSjaYGNcLpjk\nB5rAh/7iqHO4cK/jNHYrlao+r6fp7rhFItt2PLoLrmuhl0TXEVAKbGp+GTIp\nf0Sz1A58zy+7dBlRK3X8VIYNpg+L0m+ujLNzk+DLbpSEHMLDXfhZ62DvYvpc\n0J6IPGhnTdrd/+HlHuhSzduClCAgmLsB/wZZo+GjR0nG52GKv2ZP/l5+ZAfN\n7FOiXgEp3xmYTyNrqfx6gRXpCXyjs4Ot4vAMf+DLsum0fkWtBvT8LyMJSuQs\nZHXEQGzbiBYCJELA7agamKBv5rxPIBYuELbDJXjDNwDL/pQDnB8xUTNoOjIL\nfG3B1m9bE2+6D8CZVI3ItfWmVZw9XhARocXZ6DYaQW9xuJ23ScSgN+CKUK2o\nyls8V5PwBadpJg59jQiAfOvWHCNPWSAGGttIXMbMfKTJ5v4iFv+sHHggS3h4\nvvmkvpUMPkBFHmlBMQLvQcqTuS+hffAylqelzHO445zEU3kDB7LfXy7v7Rx1\nH0zjn1cd0U1kkpGXrpIGW1czR5vChsVcDnUCmS8HzQmSU0+l42N8prjLPo73\nOHoHbGctUsX9j9IOhqIgc5GupPs6x4dJgzbYpQvtXygIra8kxl8bLlZLsJzy\n1CM+5GfaC8CeDreDh0/Y4fTutqMTV/s6ryMFupq43x+ONvyLU4Yf/e9O8bee\nn8f7\r\n=EjCd\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"3cbd530f2ee5c06376210402eb87ec9e362853c5","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.4+3cbd530f","@opentelemetry/resources":"^0.25.1-alpha.4+3cbd530f","@opentelemetry/semantic-conventions":"^0.25.1-alpha.4+3cbd530f"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.4_1629833597428_0.24765430996019488","host":"s3://npm-registry-packages"}},"0.25.1-alpha.7":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.7","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.7","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"2df18432d5a71f0e770a8f5bcc5c84eaf4c0c580","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.7.tgz","fileCount":147,"integrity":"sha512-3UrzA71WObun2L+NbrO6iJGQqSGnXqLKT04owUtWO1fVUVaYulTy45zu7caA4Y8GrO7BKiCglzICu8SfQ8bwxQ==","signatures":[{"sig":"MEUCIQCD4OSgZ7jj2n+j9BFZLCDxoe3drQp5qh+1CxEqKGC11QIgYQHLNxm/CR/1WEeJNhFs4v4oor/75XOXZdOmw0ln0oc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238786,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhKSzQCRA9TVsSAnZWagAAp5YP/jVVh5NPgHRzsGtznFca\no/xeMefN3jRoaXbhtbmQbLs14jL3/AuJ3yn0rSx2u62jFSchuEzGh9i13MBH\no4PrKBlzOwnvYtoDjlQ7ys1ACuusXh+ExTwiPO5X5bFnahDVXzfgTpjzK9GG\nb3bWA6O5DzmaX6UA/929snOdPssOBSFYCEBBWonwBmBBJ+IMEjSPy55RChHz\nw8GVt5A0IF5W2ZaLVdScdVuRCoPAZ6LRd8bpI/H5CnmFPxtfX1YW4zjZh89g\n3qWuXKGQM1wtsJT3oesgty/guyiCCrqmeS/fxbY0+L7F5FirWC6Aj0XsGy/j\n+iZYPl/Y41Oi4EixjREKMm/S4lckyMY0gv3EhBMrkg3jGBRZAYIMR3JKW9pN\n9VTGNpG3Fd32ZyViRXtDcfxMUr2P4+yaxHRm6ERFwtTeFuBTgEXWyHMsSWIc\nONmZ1iFb2gvRPh6E9UVpnf3mSjujXIBRzgyjAXNuStbeIXO9MZI+R57Q2PsN\n+vYY1qtHv2/ygw/2Wu985Ky1L0kqOKQwZ1HnX8hvvn0Dd5cPSUlHtA8Oi2HM\n+H+Jiz85aeXsHuFb2beINmoV3g4IO1HKsfrYXxRZOskLHD1BJ4SIZQ+ohhQV\ns+NIaNtRmCLgm81Kila2zo7sstnpVc/Dmv+EqS7KUjYFZFvqSk2RDvKexuIV\nHV7m\r\n=bXAb\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"dfb597def863b15b37b24d965018e8c92d2ee70c","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.0","@opentelemetry/resources":"^0.25.0","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.7_1630088400146_0.9518154290635452","host":"s3://npm-registry-packages"}},"0.25.1-alpha.12":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.12","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.12","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"690d9c2a6996aa8750fd0337ef74b5e30153e965","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.12.tgz","fileCount":147,"integrity":"sha512-L/bR58bGua4XoVFuBzOmh91UGrM5rhPF8n6LiWPe41EWJv4qyXAJGaCSAJvGjgM+QhKq10ovLIP3MBQnagZEcQ==","signatures":[{"sig":"MEUCIDcQU49VPQ2p1OxrCZnRJRihRmBNOGV76F9zIkvMPDmzAiEAnWJ1gaHQf3MPAi0itikgidfssSjLWuLlL6naPsd085s=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238817,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhLT3xCRA9TVsSAnZWagAAfm8P/iVFL3CAczgQ4gh3XANX\nxkvZ72MAb69BWMUzzZcUXgHg7h7mJtwFP6AiZtXjwqUt38mVicSTqAjkMxj3\n0v5KbSYCmj/TygAvdFjeGtUnohA+aTx+44GA4LGYMh7yCXwg05hM8kuL/Af1\nZO3PbVkW+zzM+BIoO2rrv2DR+gm8TMhQmT2842k1D8+IbgB98cKpBf7a5qVv\nmPM6fWCVa4WooTwOlJdBeMzfj44yNhhFdvEzOt8asaHtF+EiSzmm9fPi9woT\n1ALWnpy9V4fp21wcNNvbDzfoHqb7A/OTlu+MMntLFmEOzs18HPOL+AFJrpKu\nL+eP0trSPZJF+cMdlwmkxui3J/glPGAg6KEIjJnw2lz5H0w++6nbt91WXeWy\nPeof54TJ6zjjivE+Lukl1s/DcrHWh1oUaPbmvZesasB2AiEw2CeN9waFHSAt\nlSM0Z5W0d6PYzr0OxtTCzVgCQiEG3i5p9sWuBdDktdC/e1fYX8at9KB2amfw\nTHuadJ9LDZpsDPann/BpXw0KdYLPNMecKTwfLAoF91RXCoSJ9lRlGB+6LiX6\nrotDmt5uEpKWS9QkhfOaOZTnNErdWPKfbk8B+ZhdYQ/ACCbDBbHeB04iL7vZ\nNvAH0OQwIR0XmbPuxgaNSqgnuSyPDm2RVp9y86ZgliuEZu/Rv1bBUi/abbS8\nNuqF\r\n=egTX\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"7d2c4aaeb08e6c680f8b46cefcdfe955d7abe4b2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.12+7d2c4aae","@opentelemetry/resources":"^0.25.1-alpha.12+7d2c4aae","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.12_1630354928769_0.21178081484243938","host":"s3://npm-registry-packages"}},"0.25.1-alpha.13":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.13","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.13","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"df007e5f5653f164069be102c6bcdb5ced6669c1","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.13.tgz","fileCount":147,"integrity":"sha512-7PotUD3giX/eHFbNvp5UJaQ+YqZ0u3SyLneG7YKk9uyp3YYlU7UFfcBfh3x0z22FPdMArJBefZEHymbKTy3DRA==","signatures":[{"sig":"MEUCIQCpK5kc70OjUqwOaLJEQ20suyEP78oq1igPUb6imueg/wIgeJOCs8SP/YX3PR6BgVbVriWZkIC84r5jJDE1x7uWqN4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238972,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhLUJ6CRA9TVsSAnZWagAAnOMP/RLKjP8Dx0ueknV/3Hx9\naaZtc6QtAxqzRoSLIp09C9EHhbXHEu/VdCFKYnfWmqdhraU90m+RDULLepmN\ntXFJr/sK3hSE6rhgjPxsaZ+mPhE/FxBvfF4K/TPRFOVrdprNasw0KM5CjVFW\n+oM0rbgasDZk9h/ir030Fw5XxzxMFdWFGhOFV7TITIngA2l3aMQwdRWmm5Zx\nP2jiYTBw+JDkBO2wrB3c4nfQmuheKlvHx7trAmmBDktgKluMLsMMYagYlwt9\nqf+AArAUBn+R8NEcBoT14rtl+h8kfKeatJ2BOzgidACkDcSEAiKG/UhU08nn\n6XyFbVLBqb4pfLWN8jeu2WR1CFPvOtfppmV0fOPDPfSnDhnyXP+FpKKo32Bf\nCk7yhyCqGHmSfYD9EDCMed+9UEjTHY5dDjPOPO7z5nJIFgo+Fs9ax5HtNh9U\nnQQOzpKQlwWmrVOheENybm9slkFaQLvGzziZNGZMG62/X/D+fvRUCK/V8vo5\nz94icTsRxCZ4pSbIIb9+Bj9lh4Bx23DuUO+UaMma0FhVImsOhjWY1aCEiV3B\n+hAtC13RON4qT7pqqtrG/5rIKEET53U0gdoggJaV6aiULeoQD5LNWv7SOiEE\nEnnBXPqScP2/n3TtwzoupT2PumMmTFBPKyqel6htr1JhhoJPW5gA/Itq1eTR\npQRk\r\n=wdjK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"f0caa22ffcb26af2a2f05260f138a494e120a955","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.13+f0caa22f","@opentelemetry/resources":"^0.25.1-alpha.13+f0caa22f","@opentelemetry/semantic-conventions":"^0.25.1-alpha.13+f0caa22f"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.13_1630356089912_0.49133518504216656","host":"s3://npm-registry-packages"}},"0.25.1-alpha.16":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.16","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.16","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"3e59bd15213c7ed7a95407ac657190df9013f2bb","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.16.tgz","fileCount":147,"integrity":"sha512-xYPEm9jw1p9+Y7SPAHDcWIC6t4sewzOxfu5DqMxZE+Ze0MnQMatwCOvdO2EPMy3wkeFY7NJJjxlavVpzXCAVMw==","signatures":[{"sig":"MEUCIQDFwR8aIHNvMiRyvUYQQVlvKKFGnsfwCD+oVEztecBHbwIgFogvaGvTP+WztOJIkbiQukLk4FTcPJXQmxX1hSpbDNw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":239036,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMyi2CRA9TVsSAnZWagAAVkoP/iuad8ncSZJBEpyUK+Sy\nKYe4lSKqs2mQNAW//AUNRGAWbY95PLIliKCZvserpg1TQlcNJGou2aVsNiaA\nARU5FnQXb3VNgrVBlYGcbyJWmlZ8kGwwxFigb/q9vV6TmLdKUtaOBD+9eGOD\nnz3+5LXPV62uLGGs9L1eX6WgqAlEJRab0IsVGoSN6hqLu+8GnO1cE0zzuc6j\nlDces+kFs6UuYbADMSjnRmac70xkMDuDYWYBycW8Vu/LttXVklSemCtERjgL\nwjU5SIEqyGLmGyp5+YMvO4R31FYThwG2Vxumj10HocJoWi2nEHys+Hhu6fSL\nr6imKBDFxzAXNPtKm8dWpAtPYbSYr7w5D3lPmln45PRdoPXqqD5lR25ywGYp\nXZMi1UaQP/eWZ6kXFsWHwy+VyZT2aX/51BOHFYxlqhtlEFX4aGb4qwEzzP3/\nFnKltC+lEHykB/hSGiMjuXBcnqo47PDpIPSBNKtqZ/K+vnuDr9jAh/lBFdgk\nzNaE1ut507pNckfk77+QVnqyTH+abHmQZkjBUqPEY7Ljuq6IzGiQvPXmR9yq\nM1acS9Kbj2qxlmN8TmXRm0MD+Ncy7Ymayk4E5bkw6CI/mwLF6Ao7LsLGNWbJ\nvk8tOt3HCBm0z12FzQiEAZSZQMcb7ylznYMfNOFE71N4F+OhCP9cbjG4jfNE\nMw6a\r\n=sEuG\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"63f6701081e3e4a7eb964bb82cbd8cbc2eaad347","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.6+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.6","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.0","@opentelemetry/resources":"^0.25.0","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.16_1630742710731_0.6843385580327839","host":"s3://npm-registry-packages"}},"0.25.1-alpha.23":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.23","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.23","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"678ad69cfe2826f4f497f1fe0484576db5d23e88","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.23.tgz","fileCount":147,"integrity":"sha512-+nwTWJZoNWoc+zAE4yS+U9q1+qGD3sJGP3qUW9CmObVVW15ySP1tl3BDHiBFcB7ILQAhx/ObeS+pWZN9CL5JTw==","signatures":[{"sig":"MEUCIQCOW2HsF3Q8kw7GWYO+2Xm8AjoSiyYLsaMgzRUUWwVPNwIgFKJkmK5rUEfZxZM+R7Xg991DAuwoJWytg4sPsNJE6iQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":244906,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhOTXwCRA9TVsSAnZWagAAt4gQAJ4KGXGYny0wx+AEs5o+\nhpOUqvkyxY0FZBmS/XlF6Rx1MnH3zubA4925uZcK2giaQOMn3Ap5k3UkeDjP\nxT+btSm5hZvjnEjFBhA1zAo4Z1Vz2hqdj6Bc85RU5mUCFDNV8w899h8Xj/F/\nhwSLJwuPEL/MnwptLXf9NEvy9S+nMNy02gt7sbf/fK5dogJXxHAuZXkoHQst\nRl5U0pYESJtFCStYitzP9/vdsxjIv3pbPohrFxym1ifRKi7Is/YZf7Ege81R\nTLGX9aMH70AQnR75LwvPq2ApxQ2F2xbwDOhE6gbVKH+8F8jLBm7S5OBMzEwp\n6/vepagI7gU9xQRu+4j6XbYurnJf2ZmnulOFmARz98hEyWAvtEd9zN0E8zVP\nMjPO89/UVixzYUbXJz5e9EUP60/CoT8ETl4eHF3mmX0/FXmde0xG6KNKEI+u\nd950FsxHQg+WBfcC02eaNsjyYvavO+zxtAIkehnFOfXe1Mgb1/KdTKUhsm6d\nkKR2loyRyLVYxPYCq1YEq/a/NJCJZPofYqJ7nJA/0Y6gpgwKG1rXzPN1kcNQ\n2bZYQDwSCmAsVmBYFCb1fRTzM6w7v3a8/vFKcv4Mf+fZoaW8YrKlxm58lXqL\nOsdPIpiiH6ACsk4JvHTPCxoI+CpDwxCPDl7uu59VdF9ZgIv2M8RJr6EwQFKR\nBa93\r\n=b4/p\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"feea5167c15c41f0aeedc60959e36c18315c7ede","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.6+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.6","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.23+feea5167","@opentelemetry/resources":"^0.25.1-alpha.23+feea5167","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.23_1631139312167_0.5525742629314336","host":"s3://npm-registry-packages"}},"0.26.0":{"name":"@opentelemetry/sdk-trace-base","version":"0.26.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.26.0","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"9f8a0b7e290d63eee67c5a5be921ed21d293c70c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.26.0.tgz","fileCount":147,"integrity":"sha512-SqV5pccxJTekOXdE1jp5VWxZ7GYw8rrHy7g0LcxLzn+D8YJ9ZBhE481VrP9EsjyLlJRhicv+z2g1XFxpMkQdmA==","signatures":[{"sig":"MEYCIQDSU95sy8kG+hL60kiMOWHMrqvjMCzH1W9eKUABO3bf7wIhALdpLXNwZ59gj3Dz+YO1EUu6ZL6nTEr5VqN09jbTbkrV","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":244852},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"fa2e897587a2441205fd085772d80a0a225ee78e","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.8.0+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.8.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"0.26.0","@opentelemetry/resources":"0.26.0","@opentelemetry/semantic-conventions":"0.26.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.26.0_1633005332268_0.16476052719806322","host":"s3://npm-registry-packages"}},"1.0.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.0.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.0.0","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"f025d517fa2386ed2ccd534dfafa894ae323dc2e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.0.0.tgz","fileCount":147,"integrity":"sha512-/rXoyQlDlJTJ4SOVAbP0Gpj89B8oZ2hJApYG2Dq5klkgFAtDifN8271TIzwtM8/ET8HUhgx9eyoUJi42LhIesg==","signatures":[{"sig":"MEYCIQD0Zq61lp2goYcIDSNYrkSWaH2vcZkgym51d8Qv0RnyegIhAIP2T7OO8FtZ4nr9eNSN4CCmgB6oUCf+qdYOyzGHX0Cy","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":244844},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"69b925d142a4405c7c6bec7deadd8b4e96c7d5d6","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.8.0+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.8.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.0.0","@opentelemetry/resources":"1.0.0","@opentelemetry/semantic-conventions":"1.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.0.0_1633035223758_0.4156184977353712","host":"s3://npm-registry-packages"}},"1.0.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.0.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.0.1","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b88c72ac768eed58baab41552ce9070c57d5b7bf","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.0.1.tgz","fileCount":147,"integrity":"sha512-JVSAepTpW7dnqfV7XFN0zHj1jXGNd5OcvIGQl76buogqffdgJdgJWQNrOuUJaus56zrOtlzqFH+YtMA9RGEg8w==","signatures":[{"sig":"MEYCIQCC9fuVQZArSWRg+N89DDvJIHnMbymcyIIBAUzJymYEJAIhAKK+sjb0Wu3J5VvFZC39vqGlZ+/poFxV7Xoruc1qW6dd","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":375519,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh2UqlCRA9TVsSAnZWagAAatQP/iWXTWUBoGG4nIFmKkxS\n20Y1VIKi5N3udvMq+gwnTxYXD0sTjV01spACjY86atMx6ZHRn/0CfjaziMkU\nE4VdMnWOceoQlSzqWDLa9NNlAhmH6GV/+c36C9uhoVL8SDemHpX2wvIwv2cI\nyLCc5bdR82lnLePoFRAc34V/7jJHW3ryIbCe4eH/8Y23lNDaL1ofKXitYjfv\n1yOlvTK65xJsdQ+8CmBBQfdMqqpaw7ahrH6CxgXj//9Z3egZN7qiAhTjJWK9\n5midnB313D3jiaS2OKkb/MFrIfIuOpcL9Bd1TciKOTbDTlkFjgL/EpQ1MGSA\nAQJohsQeOYqvFIuouG+56qRqS094kMgvVYkQXLqTVHRvzxptc7Ai1H1YdKCh\nHEdWBIHzM/zdp/wKMDz1IjNrRefK24/yfWxv1YYPoCnPEQ2ZGHmfgMX4UiNH\nLiaVp8rVT6Lnl3hALEuJZrRP1Rxl0lbfPFb/ZxWv457LRJ2jsX58MsTrSScO\n0id/wDTYJcuzzX/auxRzTTfZy/vQtjQyy48tlAZbnJ8/EnTpNU+2whsWsSYR\nfUS8AeW4L5gXw1rfqJiz76QPND0ea7oa3eiiaXCqOHAvcalMzeYfdR/Eye8D\nCSky571jLOzqrHOaFsude/F9jvXgGEMUtqRbKafWNxmkjaFF6FXqyucYcLgh\nlZOZ\r\n=aKC/\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"9cf402ee4231ee1446884b5f59958ceafc1b5188","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.0.1","@opentelemetry/resources":"1.0.1","@opentelemetry/semantic-conventions":"1.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.7","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"~1.0.3","@types/webpack-env":"1.16.2","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.1.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.0.1_1636642284812_0.1678333829919234","host":"s3://npm-registry-packages"}},"1.1.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.1.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.1.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"78833810991679a634f440aa3055b22fab9c033e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.1.0.tgz","fileCount":75,"integrity":"sha512-L8WTUKVG+E3IVTTJ0FoDo5YwsEkSOGMFOCBJKFH8O7uV1bFny52ASyWaNrhhl5kRPGlkHtsUnWkgnQm9Ne3lRA==","signatures":[{"sig":"MEYCIQCxrsqZRfunV5YR0FGHmGWA6ok09L/bkBxVJhAyHioH2QIhAM4ZoSV///YZjO2/7AZIhJOQmcwMrLNQo/dE+EgbnRA9","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":194582,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiND6VACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrXIQ/9FtTOkR2g4MFiDgnehThC4HE6EDI3YhK5hVSeDlu8a5kEC/Dr\r\nfaNCFDev7W7G07hhnRYf6OaYMXtZHAMSJ3mjIUIJ8HE/R898GPYZi5udmgxY\r\ngPzF0B3GIKNT6fFTnvKQ+auoQUxfa96V5teMUAXWgWvQkgAAcPgtETbG4BlS\r\nGwuXmfxovxsYyI61VvNz3yUNA9MybAGN49Sv7RW13aA95ENT8nnQwBVRHRc+\r\nEViVyVe7cDHJvxvDuGOPcmtqxGTP6Q9JRq81K4VI1P3GQsS8vCWEND0gEE1q\r\nUJkU6QNwO7QuEsOMFklhS/bLruc7kvUpwr2y9+2lp+fHpCVPF1zLzMzyV8+9\r\nzemMXzG/afyRFAjwpYk1VLhRXL+dbwL+hFn31TrwBI+NqkFvB4Bndm/D7IsM\r\n8xNJZm/CsguwKj4oq2fJcCWpGxKZPw1u0a2RoqXabWoF8OoRE+b8DM8e79w0\r\n+ol/VgyOfBL8B8td7RuxN/xzIwCepSMZ5A7RWdkmhDJmHtdOxhtH8xDcekqq\r\n/1PEhvJeqBnqEvV2H0S9zCAZrAHPoVjJSSwPaDSc9guJeeKYSIg6wWcOrQzU\r\nYXU1Thvv4hILbfUK0tc3mdp9MDVSrMxbHnyRjV0B3Yyh/YUYWUddZ6T/imBP\r\ndjQxrAeNtrmBbkWmtXsflLikHSPU30Mqzgo=\r\n=scwK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"f384303ac469914d0dbafde0758ccdae473f336e","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.1.0","@opentelemetry/resources":"1.1.0","@opentelemetry/semantic-conventions":"1.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":"~1.1.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.1.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.1.0_1647591061708_0.7080112200023543","host":"s3://npm-registry-packages"}},"1.1.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.1.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.1.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"2277e44a8b90815bb3c23515cae9de57ce902595","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.1.1.tgz","fileCount":219,"integrity":"sha512-nj5kFly/d6V2UXZNi3jCaRBw44/7Z91xH0PcemXJTO3B6gyMx8zIHXdnECxrTVR1pglDWYCGs84uXPavu5SULw==","signatures":[{"sig":"MEUCIQDXcQWtqYJJV5tMuOB7wmN/al1uzJBLOH5LbMyGcrtHPwIgMBptxEBJsLebjv3XbdtlJ1TDGW/M5LwieOF/Xd6rpUE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":552814,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiOij/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpaKA//T0QbIUoelYuNPxDaBDM0ieilM+eh5Nyy8dP3LRocnfOfGVo6\r\n1+zkp9C58SOO2k0uMNgJ8BcMgXxBd+zcS5W+/J33LCvbP2ZpCMBQrOGa6kny\r\ndV5FZRE73sUBCDkY+/81ehyOiXXLW46313zGCBgpX9p/HwivTjT16PNYrr6k\r\n3ZZiQmb9Tm68VnVLOENIqTspRHzjq7T8+9c3fmdLmiNdrhP6k3y0GE2vdg0P\r\noHNrGD2NQCARK1RLMJNF9a/8/hK6dNERoQKWydjV5y6fTV8HrIHsVLzrNvXs\r\n0YF1N8ZJgHUoqYsTdKQf92kr/j8sgMsUFxx3cku2t9qcQHffY5GxmPgn/fTE\r\n8bZZ7mPhmsf8aXih/F24cInMD/Eu8mKktpov+agemmD5RFEMTUWeDV481din\r\nFOUCRJUDTSserEa580XBRkUsdUr/Gb6dm9JB8mxGQwIWJ6ZUW2K+RqAb+FdR\r\nMCDShq+C8YSwhSIaTeCIJce8NX+ca9NtjUO1sIPqVNIuisyO6NOz0keeVKXH\r\nO0xRTz5l8Jog/Gehka1hwbtUtfcVswEyZg4SvVKWmcrb0HYVW+e4dClEBr1d\r\nAp2QgpnUaIhcVNwjuMkSrGqiTvA+ehBvWMPk6r2zi60fHImod/+0d462yI9D\r\n2rgR//laEPnSI8sHJx7q33Yi1s5qXMUqzaA=\r\n=2PCq\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"b0f8a2d36e6d1d3090c3d2380608d2102c826e0b","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.1.1","@opentelemetry/resources":"1.1.1","@opentelemetry/semantic-conventions":"1.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":"~1.1.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.1.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.1.1_1647978750976_0.4451688237419007","host":"s3://npm-registry-packages"}},"1.2.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.2.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.2.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"aac5b79dbaced92a886fb2e348e54f5b681205ed","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.2.0.tgz","fileCount":219,"integrity":"sha512-eHrG9c9OhoDhUmMe63Qzgpcvlgxr2L7BFBbbj2DdZu3vGstayytTT6TDv6mz727lXBqR1HXMbqTGVafS07r3bg==","signatures":[{"sig":"MEQCIHxDeRJ1Y7SkEGT1p7MiTG4y2cOugUZZeZ+XhrikR+tTAiBWEgxMhwJy574hneXGgEi37Y37vhBQlNd/xvWTQr/SVw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":556102,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYsI/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmonHBAAj44NYQVANyFTg3YLzIsIJhityCIuLvyhLfar3YpD7IkGrrhZ\r\nT9U/D2fN2wdSfoeD/Z7dGu89Y5/AmmPWWEEe1SV4aPY9u3mXwiS6E27zX5sg\r\nyLkIHYphlU1bDlq/FHYpp5v9ptK6SJa4epgNBFrBNfyQg2vT9EWyyB9W5jOH\r\nQ10w9oEg3DY9MtH06PPh+VLIJqslxHFFTaEC5nDIz/+yfqXjJb2F11G7TL2E\r\nSoisfBXai9rNnUebsTLc8r2J+mWwlcZWHThTZQkeBw0eAw7BWMPIaWWnbl6k\r\nzNS686ldebx1dhQjD2qkuUabqHh4EuJoMvl7KCCYgFjWmRhXv5q9zog5HpsL\r\nkDSCxnLVP4uEjnKcQ7tw2Vru6+7AejaanVDeUVkVmW9MCNCZgyFQrDvq/I6e\r\nnVLTjrh+nLY1b6ulsliiwi0uUglQl6rgX8ei3oe1C/+fKg0H5n4ysL0Wdkak\r\nlEnaryQ9b5Mu+HYOLC4leatb+Ob+Tzre+aspQ8E+I+TbFRPqEYwAk9qTC/27\r\nltLXlSNb3sCzJXIrPg/fRQXDmi3Mq+j5Et3Xr9XiifQLwq+odpd7bh7tJ468\r\nzEiU+7v17c4vB/sq1diy3DL4H+qIcwVTffl5PRDY2p2Mon66Nruumx7vgxnd\r\nCYcdeAhqcXuo2O6XPltblnFADHHs56BOMCo=\r\n=MSTB\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.12.0"},"gitHead":"a0a670a03fd35b0799bee8cc466f79e93b5b6dd2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.2.0","@opentelemetry/resources":"1.2.0","@opentelemetry/semantic-conventions":"1.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.2.0_1650639423630_0.5047606523204053","host":"s3://npm-registry-packages"}},"1.3.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.3.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.3.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"eebf6f553e49ceb309d346b8de7c9257686bbec3","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.0.tgz","fileCount":219,"integrity":"sha512-BkJEVdx46Boumgy0u6v+pm7spjOq7wCmClDFnHvQs8BS3xjmfwdiReoJFcSjo0cSlxkTJh6el8Or2l2nE3kArw==","signatures":[{"sig":"MEUCIGz/0H3rGWRaHX0jlcn+6uukc3qzgIuKUgwQEgJ/3hoqAiEAzMR9u54IyMmxnMYkD/aoKGBz0H1UhWhby2nzZXGtqFw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":556534,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJikSlkACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpNxw/9HOqfRpUb4IKDqrn+hwiDh+sC4Nbwoz1NLjZVNN8lCqDA7Bt6\r\n7j+sgVk3xxbuTQAKxpIst+8vYkEbY3QiaYEU3Xs+inT92ky2J29s0NUE1u5j\r\n4G8g/vHIuAVb2q6KE1iWi+kTynYE+7LbO7Ao7nRfpqlorKVlMjIOQNCaiOAW\r\n6IwdU34TJXrT3Qya6Z/jWjFnC0e/gG+omiBOnv+UNemQqW5Psz60bRttRAQJ\r\nhY/9U7tgptiurrkCZbj4j5/DsBvLfJKuoXg0nE4jI1Nw5H0qIW0PP8ifZAtx\r\niyw9hoFAeOWb6eA6efQ/UJwXpXEBz/ViX5bIckA/QmDgZPIYrVT2/irowhYY\r\nR9/CAxkSu2UX6tz5lKmQZcYSCmpR2gIvtKV58MwAsH2g5SSkW2biqB0XdTtr\r\nAERTzqJtgrr/kKsSJXtGuBJQ6aRmIUSBGmQUHlU/mkfr+dMfCFxZvjGU5wwl\r\nTfIsifIGkVpytY4YUrQHDEiOA4FfbYNYIA5j8rQVhzcsXAe1kk3iL6+9wQsd\r\nDrI72fSs2KsxPjLhlEyu4LsLhJ51QPdDz9ml96zd2YzCuW1zUrYQ4cPiJuJU\r\nvN4LyaSB5VCMkgpgj9SmcIQsqB9X3JNBr/+2ajElF5HawwmI6NlzADfOQ4F9\r\nAozt+rNMEJKi4bnqab+pMfrux8B7nx3++Os=\r\n=7Hm/\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.12.0"},"gitHead":"eda0b092db484855ded8b4837ba7fc19a377c5a7","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.3.0","@opentelemetry/resources":"1.3.0","@opentelemetry/semantic-conventions":"1.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"9.0.2","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.3.0_1653680484654_0.37952356793618636","host":"s3://npm-registry-packages"}},"1.3.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.3.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.3.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"958083dbab928eefd17848959ac8810c787bec7f","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz","fileCount":219,"integrity":"sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ==","signatures":[{"sig":"MEQCIALi65Rm/zKG8ZBolugN7azramCkJZwJFqjYpR2IuUmHAiAtSSXm3hpjXUpwcBWKdCGjLnayhLgb27nqMu3p74uF9Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":556534,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJinmLqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo1lhAAiUCOLc4qxk934XKQhQW8My+vNgqPeT/tMdJiJ63yEvtbTwQ+\r\n8elNZzkUfc9a2n04eOKZTpcZBdLmdz8kr/L7+SGXVA3pGGRmvOFp29YTVUQ3\r\nN+/Ub3SVTDyyA9vTIss7XlJYUXvWXp3mEDwfgS2i9VVj5gCYyC5zt/LjYP6s\r\ndnb3OlASri2XaZPDzh/QOhkT4HZUbqhnqGCSJc+8fU6IhyrL7mOnS+oX4o+U\r\nWG2Y0wvx9MlkDX4+QwJdrRlDcG7KZjdoSn6p3ORgy8/UJVAGGoUHI4yPDnFC\r\nu+tEpRgnuzKRvtsyywrWx0h0c+fBxV1cCIrHBxPsWK/TlypEv9WbuD17vdz+\r\n4Jm2ljTlDgZ+K4xnYqIV6GFTZosmUiRbhiX76cRGL0PWQAueUwaBBjHhkloY\r\n4lGbLKWxYFBQglSYwYk/QJg1Cn4hfeMspRY2BbG1MgEdCK8hkOVOhq3Cl7Ic\r\nLDBaZ/H5pH1RrWcK/OPRFuta0lqgt65FkEHl/Xkh55v+l4N5yIOJD28CxXvl\r\nsJFWeWX1xluXfMXmtat6qMBZleIvDjtmLpY4G1VzwR85IKWURJb3ePVg5bvy\r\naf/JYHg8yO4mYcfGx7PmvPIH/CJryVK/kIr+KJmH0M5DYDYPMJD6gQbrFPon\r\n2LSUFlqN+LkjbZPXM29ovaaeO24sXaCEvbI=\r\n=lwEL\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.12.0"},"gitHead":"51afd54bd63e46d5d530266761144c7be2f6b3a7","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.3.1","@opentelemetry/resources":"1.3.1","@opentelemetry/semantic-conventions":"1.3.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"9.0.2","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.3.1_1654547177779_0.44314908261854846","host":"s3://npm-registry-packages"}},"1.4.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.4.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.4.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e54d09c1258cd53d3fe726053ed1cbda9d74f023","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.4.0.tgz","fileCount":219,"integrity":"sha512-l7EEjcOgYlKWK0hfxz4Jtkkk2DuGiqBDWmRZf7g2Is9RVneF1IgcrbYZTKGaVfBKA7lPuVtUiQ2qTv3R+dKJrw==","signatures":[{"sig":"MEYCIQCLqLk4sGs7ZlVm8eZVQO/gEw5B3TPiumDqB01dNJpsJgIhAI7a5bQFkGJly7Ar3CdIQul5vtwyWdO8IL4xekGMZz1g","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":557399,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJixe1qACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpkJQ/9E4JUmYpvdZvT8BmFGBAigK7zC7aBNaV3+jZoUnZBtxnS5pv2\r\nqdydK5nhXffwCWeFMEqdI6Ab5vWmUQJsBWk3PsV2FqtVM1ivv3aWeWLlTFAO\r\nR1wmFQVk9kIBDGVz21klCFauyhbHbj0OdYYlFFCjfMPZ1pfOHjyqNbE3HMYr\r\ny9dxnK9yffCB0U4yfB5GnEj99xC1AvXSkBDm6UFQLfhfioW3iCMfUi4kWPKd\r\nEwrrRg2XgsrNi++xM2iK2tLu3cgcmRnAbo1CriwE40ffSqohVMs6nohicdi7\r\n4V/j+buJJO6gCm9sRnrdmhvvHy6cExNqaE+qF9nJn2phL7YS3bIzjtq1hQyg\r\nvpwIe4nE8mZ7a/2cDCsIGK9uIcvsvgwbkHbH93q1J/l5MasVu2xTTn30bVjX\r\n1T0L0TE8b7193vjf41eki3YGPDC8pgdjdyzE+fB1WQGyogOpcNacwVbz+AmI\r\nZ+9pyjvBhLcMrZByLl6oKtMjosiJi+IAuoUnDDHOQ7GSFOnGKshg9/trbv3r\r\nkQP98YJcnIwc7aKSBYF59C1lSjbLJenJ8Ni5koiipFUQbnz53NLrshMlXq2V\r\nD71MkIfw9MrK15S7W/1uHY46t1B09zkdUC7ILQXXk1/9hi23D4qAn0YwPqNb\r\nRv34ZtYPfb/XaA4b4W05tdN5FqAxmeMuUrY=\r\n=DRGf\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"e39ab883b18636238ef0fd741df4ce5ed53e8d04","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.4.0","@opentelemetry/resources":"1.4.0","@opentelemetry/semantic-conventions":"1.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"9.0.2","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.4.0_1657138538116_0.08054665516123172","host":"s3://npm-registry-packages"}},"1.5.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.5.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.5.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"259439009fff5637e7a379ece7446ce5beb84b77","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.5.0.tgz","fileCount":219,"integrity":"sha512-6lx7YDf67HSQYuWnvq3XgSrWikDJLiGCbrpUP6UWJ5Z47HLcJvwZPRH+cQGJu1DFS3dT2cV3GpAR75/OofPNHQ==","signatures":[{"sig":"MEUCIQC1uiO4Uby5qJ5Q4RGt5KojX/NKiuQhrT6XzcGmN5YImAIgYNHGvd7qtO1rZ6/NgtNYF0j2DTITyPg1IWTufbasY3c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":561100,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi4FQAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrCfg//WNcotdRula30Z5YkzhkmwWHZyiBWnHIHOBEOgn36AolkGWE5\r\nM97A0rZ3cfW4DrbdCIjEaavWUjvHpAzER8ta+DiqigspFvT+ko8g5v0fMw2N\r\nbPOXkn0E0zVPCunYFPrcbiG8GEgETjvtPnOlofH6McdgXOyt5tUS/GEI4LwY\r\nObg5BzYs6hRc86X+aQvrLwCA9EroWzfLr4XbmeAkBE5ZEYq0bO/WTz3EujY8\r\nYxW8GnNaxJT70vYlczo5+6IGBASFg1bpALJyidUroNRHDRRQM/MQFZydXaKV\r\nWLEA/1fwTBEB6sfsKY+nzD0tMIRxpZzHvm+qqAO4wEnm8UVx5Y4Oe24baJaZ\r\nYw/Mpi3A8xj/5Glf4G6F0WAuKxEXfbNJ3/OUraE4IofrSKKvv/4JhNxOw+kA\r\nVKX+AnRCdBSPduR/xAyHMQke4Q3YCk50lwSLXYoZeo+VnEYPBe0/hgmFRqzK\r\ngXOsR9ZhxWJ65N3UuOiBYu69OlT31OTxlvDxDOd/SjSngpWeLFEbyq6NPxfF\r\nMkcsnYy6WSaythblJdUebz42u8CX//JSAfT40g2jidnniQgjE35EzrvgX6dp\r\nhB2UoTxrz44DD40jQLAivEMDxL9Sx+GZ1HafxBoxDnp5lgeRaj4r2IBNz4Lp\r\nhCXz5P1ZCR9Ut3YLnhqMRSfMZqvJDQnsD0E=\r\n=lvpO\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"776656b6e0cd0fc49a52b734702bc77110e483f1","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.5.0","@opentelemetry/resources":"1.5.0","@opentelemetry/semantic-conventions":"1.5.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"9.0.2","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.5.0_1658868735873_0.36948937296896256","host":"s3://npm-registry-packages"}},"1.6.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.6.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.6.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"8b1511c0b0f3e6015e345f5ed4a683adf03e3e3c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.6.0.tgz","fileCount":291,"integrity":"sha512-yx/uuzHdT0QNRSEbCgXHc0GONk90uvaFcPGaNowIFSl85rTp4or4uIIMkG7R8ckj8xWjDSjsaztH6yQxoZrl5g==","signatures":[{"sig":"MEUCIFGZxkPPycUwga6941/1+5vsL0g57z0IOs9srY9QVTFgAiEA4/3DbZJ+xNNnMd1k9gRasfX+VKzueDJPWlXaDvb6TLY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":703237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjBmODACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqCCA//UmDuN/9SBNkotpVIKMbNS/WfQYETlNXctdnZHBP88etrH+Ax\r\nQaM+cxOLvoO8VgneTZa2YcufJmZamgf0N5nnfUBABvNqOxDXfFlnXpgNR+S5\r\n6E1ldhx1sZ286kQqhQfrCSpENUdLxm3puzlJvnOzDrCUBqSAIL4vrE2pifuy\r\nngWrs2LCz+KBb0Oo9Zj+GvXOYKZaITZNVwR5y91aOzzyvhMj7aPumgWpwBOz\r\nnley7aO7CPXY8PyZ6FUUV/5YySSyLxtrGaUzTpwxkAmo8Rp9cB+xDyCKb5hd\r\n1XIZ53XH+XFUH9bK6LMW/NsANgcPvWT+gnXdK9OKg5orV3XEj7ftAWt/r9E4\r\nIWQsv1VO/k1aw4wikslDghIHd9DQGUq8f3qTbZDcIJhoSvfuUrIJWlE6zehv\r\nDWG4nGl9515eNnVvWFGuXLJUZPQPnZrCYS5ocD9viqOGj8Ix6Dp8yEkHA0vm\r\nNQttwwELXX03r0VeEQcFpxXwRzS7avmpRqDfedpl2TJrF/4Bo8TLBkBP1Ht6\r\nVFc408x3eSA3RvJ7gFdhvgqjqYLSLuVP1D/ps66pMi5SG7ykJ3Za05cBznbs\r\nJMp5L33qYYWHjAi2uOm/a0tVpmJMRO3QyLNF24Y4zbuV3apqsXk522MSWWPZ\r\nFsh1V6H5vRKAFEdP5XrBfq1qzaAkwPbw66k=\r\n=8qYD\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"a5abee69119cc41d9d34f6beb5c1826eef1ac0dd","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.6.0","@opentelemetry/resources":"1.6.0","@opentelemetry/semantic-conventions":"1.6.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"9.1.1","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.3.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.3.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.6.0_1661363075313_0.24925341140796742","host":"s3://npm-registry-packages"}},"1.7.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.7.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.7.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b498424e0c6340a9d80de63fd408c5c2130a60a5","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz","fileCount":291,"integrity":"sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg==","signatures":[{"sig":"MEQCICxQm5dNE2obRrewbmTbvDUPKjpEDqwe9DMSkjsL+ll7AiBBeL09XWESUAsZEFJqEb/czB9xRqxGSU7ZtYkIxFctxQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":710870,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjJGjCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmprZg//eVXy8Y9itH7B5owMNIWDUAuuXFpw5t4j4E3Eqsa8PL4MlfLz\r\n3Q8FGv/gMVPNSxTK+Q5olSgI/Dp4d5KuWEzTBPtU2kSSZFQ2hgeMdeLR5zkG\r\n/R0kcPL8z+iFxMsA6eO7uImf5Y9frntttn/el5jfw87X+qjXjV2jwqKJmADa\r\ngQ0TtRLb34gP/LReTYaE6P9pZXAgldTSLku65ztF74lHvPE5Q7HvHs+75uw5\r\nsAKGbVsA5yedjXZ+v8eIE8WL7aQer9VvtkPRXVlFScvim0CCChn5+45seH7c\r\nU+PMzcc97Y4Rg7ntxFy/lcW3OKQaO5yl+gss/WEDx0o7xwlNkITbePhNH0Rv\r\npTBpdcMNeZk1ITbmG1ri8DgjHFJWL5O7pao4yfgyzuFkM4SUYujEZB/Thfek\r\n//fZv8H+t6Ms+MFMciOE++GJS+GVXVgyNXFUWIUWF1eMeO4Y285LygJv453J\r\nN7sld0hrMn4fItZW61Vmj/sRaQuIM8NLUjHTY1jkBFRE39nsrIkWxNJRqjcv\r\n9DQOlzhSAOFOY1EINpYJeW9EAIQkwZ/w0UOd+HPmVDm5bGbCnvqEpiyluj3c\r\nClZr/yjpFoMm9Mx+FwhLaZjW5IXJe1ek9QFDDP61tGWQviV6jNHq5RxjOwm9\r\nOJ2F819hWrl66GKa02rO+gqNugRkAJxpgjE=\r\n=3Nls\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"ad88c3d9aa0100fe259b93f4b660e84417b757ac","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.7.0","@opentelemetry/resources":"1.7.0","@opentelemetry/semantic-conventions":"1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"9.1.1","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.3.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.3.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.7.0_1663330497999_0.322341921882334","host":"s3://npm-registry-packages"}},"1.8.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.8.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.8.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"70713aab90978a16dea188c8335209f857be7384","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.8.0.tgz","fileCount":291,"integrity":"sha512-iH41m0UTddnCKJzZx3M85vlhKzRcmT48pUeBbnzsGrq4nIay1oWVHKM5nhB5r8qRDGvd/n7f/YLCXClxwM0tvA==","signatures":[{"sig":"MEUCIGGgOVCWV9iMP+xpoW8qU84MMmAwLSfneZNUCzPnTNKKAiEAzWhHXdc3EDzlA7f34ajtUzAM95QQ/ampFalpD3c5vjw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":709861,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjbANeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrSvw//ewghTwxojDN+0h1tzWihXLoVTgDtKteumUYoytRWe8RqPLl7\r\nKuzxG5g7KLwqsojCfHp250ofMUwTVR36RuHZTarjpaJ1z3gC/EbROPfsCy2K\r\nIdxiZJLS+5rycgn+P/rqH69Ghqy+O6E1qxAVWNZ3S4px3wGivliDbpj4YlaN\r\nBoTVTuk8THoeg6us5RmM4WMx2lJNIliMtbOF1spyFEL8jfWrhfQFcMGftFAQ\r\nXSSn5fJ0iFWYDkD7psF9MQ/iJW7nEG4Cn4ITjRt0cRgIMUtDRGPJKtY9IwUj\r\nAtZudNs7KQYrZiT3WvaerL4yq6d0iY6lbDXiTvtKof/bRt2xciCaF+5CG3M5\r\nV4JxKS0/YwilsMKebUF9Uz7m5Y/bKr20uMFNgdSSp0vDeReOlcbSOqm1JGf5\r\nH1G1PUnZ7rvuhuYA512plL6sieLNmyhTgktMSL3nMPP9+phj6BEIdWI0eLfV\r\n0xz97XYI+YgKqS2U35vh01qwgJMDcHy1v20e+IVEFi/f6bUUd9NwVJ8y0IXG\r\n3hUikhaCFADqQHDHzNp6mfdNRkJSGzc9zD4mNKBQ+Dg8xEWNQwDq2pjCiKfI\r\nSey6HSU4D39eLnxPV/sB7+NQ/wav1w0vtFUg2GWQl7yQmI/OOROiBfyKYBeW\r\neL4oPyD0Beldz9ejMY4gT8aX6Y+KGU6mc9g=\r\n=fY0q\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"7972edf6659fb6e0d5928a5cf7a35f26683e168f","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.8.0","@opentelemetry/resources":"1.8.0","@opentelemetry/semantic-conventions":"1.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.4.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.4.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.8.0_1668023134059_0.3756471246154027","host":"s3://npm-registry-packages"}},"1.9.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.9.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.9.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"07aad8d3b484f24e45ad6347f74a66d12d69bf00","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.9.0.tgz","fileCount":291,"integrity":"sha512-glNgtJjxAIrDku8DG5Xu3nBK25rT+hkyg7yuXh8RUurp/4BcsXjMyVqpyJvb2kg+lxAX73VJBhncRKGHn9t8QQ==","signatures":[{"sig":"MEUCIQCk148UUg3KdpJvI14wehqmYDos3NaYmQqsaz+40gEBvAIgbGW9IAjfLk1D9uLL1eXth9ZQpdSfqpmZsYm/mJOGug4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":720397,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjvy41ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrHzw/+MRKqvOeTqf1fwQhfOP+OYxGNWn+/HFIV48ZraIw/eJnPNm9n\r\nrfPXgMjJwhMeRRR4veXg7NzMGhwDub7uMwjpxrFZzvWVLU5XHnuCu0boAGt1\r\nkWF4/KulBnxgOj/6hqJ/+M4/L4zoE+Pb4RlPe7lbqmIl56ZmfWhah28okgnC\r\nqNbR5vgAOAX1NEjrkA52kke1H/S+wTM+/D/IwaSTd9fReOauEEaGRX9M7eJf\r\ncMDSFpiwY3aqWgw8Qjc8XOOox0zi75avU56CjhN4P7SnlvLIQqCdiWa/INA2\r\nCYEFE8KFlwKBXai+WXUms6mTpXG/pSn16/mf44KHh2D0tmXG83FgsTRzex6O\r\nq0bKGX7+UnX1k6opShIWcZF1pekjU83nQwc8c5IKc+7NABXXCYy7bksVwsG3\r\nXW1w06RhJjQcLkydPwfXdSTD7P8zVHo7IlFrTrb9kc+biA2xqr0o7Jh+0nc+\r\n0cGsrohmazhmaRM9VIzhh22XdO+s9ajQgNNg6YEYPw8Tqtk1JIPE5Tj2VmA0\r\nduGpt2CQPeT3xrdnYtA4jJLBAVCCtNO+oBmhxvo9UWNcwf3BM+egVGZr2pDj\r\ng2BOlIUj96iuQ89lpGkrHTTsICxpiZo6XhKzvi0Pyz/kU7vLalYzOFX36Kp3\r\nfrVodW+vrNvDp2xCbRtezdDvCAVbM76VsO0=\r\n=rMZT\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"08f597f3a3d71a4852b0afbba120af15ca038121","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.9.0","@opentelemetry/resources":"1.9.0","@opentelemetry/semantic-conventions":"1.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.9.0_1673473589665_0.6742613306845624","host":"s3://npm-registry-packages"}},"1.9.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.9.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.9.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"c349491b432a7e0ae7316f0b48b2d454d79d2b84","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.9.1.tgz","fileCount":291,"integrity":"sha512-Y9gC5M1efhDLYHeeo2MWcDDMmR40z6QpqcWnPCm4Dmh+RHAMf4dnEBBntIe1dDpor686kyU6JV1D29ih1lZpsQ==","signatures":[{"sig":"MEQCIDCpHT2SvFd7SvoiA/2qcUtYH9zFjwJCGPBPCaE/O0/XAiAg9ObFHTNytJHMrIs2fZSuBwq6zkrVyW0zuSXKRpu3Cw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":721180,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj1+KDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoLHRAAg2YSezWIehGO+yqyz36zRSewcUwKhipwLyD79qHVT8biD3OZ\r\n+LouyTG+pKoIfA0ht/AxcwP/KPKd4HfApZX//J2F5up8Y2HXg4ttHF3ez/MI\r\n59pdZYzIHwvcV24CdShaviNcw1wVF5+fGBdP/AqDDHu+TzTqSyb8p0SLTKnU\r\nm6dqBu0O7/30a7lRuEacaE/RTN10fpXpQzseVyq+yY4fxnihW5WJ4wmKKgz3\r\nN/WcKOla4/hdeEpeQaBMsud+VxN1VTNor8Co+RyB7+T6pHFSIWcuUY4ED0QW\r\n8D9oHXPND8P8AqQG2V3oh/3J7EOsdfqaAhzbURnX/DYPw5GPwvEXHL2xg9SM\r\nEVnwf/amUsxp+KaLbLmJnLdUebv+YSYzU4Cq0b7lgF2Y3ETt4UHzC2aKP1f+\r\n2Qq7VM3XWobSfVQATnb9G7mT3ceET6hhWwx/2bCk8bL8z2nr1A/VFIcGR1/Z\r\njTKUuuf0tWndCgQcAiMgBqsY9gVc5k49BXILciDslFi/VAFPzPNLdgXTJLoc\r\nygkcbFFC/qshloubv3yjTyxbb4sPC8JHaJy7z+JneWFYZIVG1phNiRbUqUNv\r\neasg3/aSyGvuSXZSyE2tEKTvAt0wtOUpnk/LelZoD8xO11whUPlWz5bk5le/\r\nxuTo6bdKcBCPUftxCcET15ohn56htRaQssg=\r\n=4lA/\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"279458e7ddf16f7ddca5fe60c78672e05fafce66","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.9.1","@opentelemetry/resources":"1.9.1","@opentelemetry/semantic-conventions":"1.9.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.9.1_1675092611489_0.40379905656518345","host":"s3://npm-registry-packages"}},"1.10.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.10.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.10.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"abbffa0ae39234f4c441357edc3f4da45dc73ef5","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.10.0.tgz","fileCount":291,"integrity":"sha512-X4rRShtVQ893LCU4GNKS1TKFua9nSjVmo0VJvigfSFSOmyyOLfiyTWmVL9MKV7Ws0HqLOIWJixJY0x28fw3Tzg==","signatures":[{"sig":"MEUCIQCfH/YhGxM8fNPGzqv85ZBEv+TXK2xG4B3l4BxNeprrcwIgNgdeMoB45zsX6FCHz9OKjjZgeiZpELL7Pl8Jj8iVfsU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":753566,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkD0cLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrtJxAAjO1H0f8JKNzc9enF2Gb4+9qh/KuB+N6QinsJR5uIKghmG04L\r\nTyyCyEBIlrt5+C8U19xXZRbrtSTfyHD+lv7WRrSeV/fL6tOEV5oHwn0BhijS\r\nJB+Ng65UWSGtPiv9fnsczZN+pluUwzWcpn1eKdM/ev/7j0jFnxbGFJf8XfLb\r\nep+rdVtn2PBZfe66YZP9LtE7nCaHVYCNFdqkXl8PKeyYisl7FJefVRcxejOw\r\nIK1m8uChlMYtjDlMCkg3IrIfiEqlFoAoQinp2gPTEuSM9Z8R6WIW+rc5B680\r\n8MFyMQVy8U44vhZjnLb0fwIAzHpCEzXrsjQ6kwXU9ZhOOw6k6zq/F90W545+\r\nxc0yDu+EQLPLFwSYgVAD8Iutc2OkRWYDBUn2+QLWIc49HSq/XfMyUV36qDpM\r\nNa9JwV5Hzh9sfEPgvnKFDXDz1t/kYP1Y5D2h1Z0vvPYIGfxfpwnhKOtI4jHC\r\nwnnwBxFlBWbyxQ4TUWjV3R8HeChsO3KZ7Dcocjjkhr+dGZbRQwx5j/ZcRgb2\r\n1WS/kKpbXELlv7F16k+myxXVMSughmSmyTBvQesOMODviXrG9QvZ/k6hQDzn\r\n0aIdj7QFVsBqQ8QfLScLcQoGdxaD7PAImPFAJo8IwRIApGUFGoJIMkTAxQnB\r\n3An0ctyqyIqewKwSMDQ/kQkCAZo7nNMKF/k=\r\n=4uMa\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"56e6b1bb890f844b8963a146780d0b9cfa8abd0d","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.10.0","@opentelemetry/resources":"1.10.0","@opentelemetry/semantic-conventions":"1.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.10.0_1678722826858_0.7356588240446413","host":"s3://npm-registry-packages"}},"1.10.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.10.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.10.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"fce06a810f9052d3c1b935d134979f4254bc8ae2","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.10.1.tgz","fileCount":291,"integrity":"sha512-jutSP5t22wrPKReJKzI5uKht4mJ4cQdF/mGFJkN+emFFsDXru9CuFv/NfUrD0jEqoaaiqjcZtPSyTzMgu9LXvw==","signatures":[{"sig":"MEUCIC5TkRQU4u6vAJo35V80QPiCUtJjFb/AQ6lMRRW3pWVXAiEAvM5UwlwBdiTS/miwGRjsCy4RwNj1Vmv0E44uh035k4s=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":754653,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkGIV6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoD9Q/9HrqY+zXO5UB2Ajjrnffg1MgeYDLd+x0xsRVwooyC2HXp52/8\r\nmuP2SXq0JbSZOwbqQhSs6r/KqLt2avtTufh20YqFScGBvE0ajk+iPwdViznh\r\nBoWgA7m/cSTfazKwa/y01+rP7u/dHhMhUyp2VuX2Ml+6uytN3+ddvNgrPoB/\r\nV6V/V+G1StRlkrXj/yrI5kwtdbd2lDqsPErYhEWq/JnBamEckJYDUukJHftP\r\nOiYLvPEJGD8nDnZyFZM411BZzMi2c2eAXDTCgdBfczwdgHDq0hi2bBheMb84\r\nE9XDZO0roA+8qqwPOdII+ZhTM+Zy7AWVpgXYzYyn2H+zqt0kKYGAVum01B2P\r\nVwtG8UVJj/0/SXiKpzqaHNeRp1bG3Wwz/SgOdGWO078IoQVGOlQcldCbSc/9\r\nPU+OojbQ29jzwOOXRGq3Gr1S3CWshuDUM/V/gKPYJLQDL97VcP6qgmrGKYjw\r\n4phRWsFMpVtq4vLpciCThNZfdcCpIHKEW393pVNcpoYh8zyrucfR+UNt6gdB\r\nHOuK81d1sf67yI1lj6vuxYBL1Sg2PppgR+kuH8yat+XGV2PHdIp3g3gjaEx2\r\ndBp1tMLkrI8EW5TIgFusMyNuYq6+fH4VQ+6KL769f9zJi6wt5833VS1YjCK8\r\nh3pXzU0oMH3vOTOi+K+MgFdezKn399P7X3s=\r\n=ydap\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"486df99906958de9b8b666839785b00160a6a229","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.10.1","@opentelemetry/resources":"1.10.1","@opentelemetry/semantic-conventions":"1.10.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.10.1_1679328634722_0.677777763334195","host":"s3://npm-registry-packages"}},"1.11.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.11.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.11.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"a962dbe74ae28442501ce804d4a270892e70d862","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.11.0.tgz","fileCount":291,"integrity":"sha512-DV8e5/Qo42V8FMBlQ0Y0Liv6Hl/Pp5bAZ73s7r1euX8w4bpRes1B7ACiA4yujADbWMJxBgSo4fGbi4yjmTMG2A==","signatures":[{"sig":"MEUCIATNLGlPj7nprM/PqoK7yebouzxOOVHabs3pLCi2zF81AiEAnpwJz0aXZGwAS8cwf5tUXbH3Z5M719jhlqJKLHhclE4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":754653,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkJaswACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoBlg//cAe3wMtepVB6AkYMsdLNGEQdJNykDrEO4K0w2cJORD1OoOSw\r\nRKY69x4MsneZ7DoTy1Yyvrro4nFZVaXcMR4W2I2/VyCM9zuMYdfXfeYW8Xn2\r\nxlBmjjIvotMn3FMLwxd44Xc7RRXgqOYp+F+oYPfxxyB4bLtIjg9UCRWIUkfs\r\nxeHvkfxXhlSE/Ffl+o4+6S5JR8bPRcLKltwfM3sBlySUZ619M2t/3RdgXuN4\r\nKpZM8cj/+8w47iRnPEC5u+mxMgsUiJOPRdMNV8MLR9gAVrt+aycAGs0DveJ+\r\nW800yX76KLskPzXMOhLjY40uAfTmf7ZSMLySPTmjHB86Ao7a+VRof3Lp7F7W\r\nAl53WeR8ktsMkUgol6bYBUxQ6gpaUBcup8OVTq3UvIV0tc1nW9xGift4uyqV\r\n3Zx71db8n7DWQLx5djFNVcGxo4LGBnoac6yID7sAXZmFPKO5wgik9jcSvaes\r\nMefz5zOjllRyaggfDu0qzMl/pV8rP4U/HDyFsUBFkbVrs0H1gq7p4MWOzvA5\r\no+TIwiRqhkNVieFqBguPz6wC6R+xmGDfuMT1OVlErJ37xulLctcFR97cVvo8\r\nNhs/zg+VzSlLA2ffRMZJY8j2xFIIknTNW2XbxO0ynIuw2Qac/ubMyv3L607p\r\njN2vgVxGOy4ijQ4VOX7QzQTJOW9eOnjw9UQ=\r\n=Bvyk\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"1328ee04ae78f9f6cf143af7050c00aaa6d2eb3b","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.11.0","@opentelemetry/resources":"1.11.0","@opentelemetry/semantic-conventions":"1.11.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.11.0_1680190255784_0.000785890780287346","host":"s3://npm-registry-packages"}},"1.12.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.12.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.12.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"62b895dbb5900048a85e4899c38fec5585447d4b","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.12.0.tgz","fileCount":291,"integrity":"sha512-pfCOB3tNDlYVoWuz4D7Ji+Jmy9MHnATWHVpkERdCEiwUGEZ+4IvNPXUcPc37wJVmMpjGLeaWgPPrie0KIpWf1A==","signatures":[{"sig":"MEUCIQDGQNlFDI4aeratpSD5OxDMShZWpHdYTVjWCBnYfpqT7wIgS20kS7BJXFaKBMCcL57pG64sBXgcstHOb/QvJJbZc4c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":755384,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkOEYtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr1VxAAgK9Cgr/apKKrJ/8l0FYhazkWFDVijdj6ImBxtPgDYEUJgRCa\r\nXWPxQDsQLJ/n+ZSKLJ9e115m+2mfaLCg0ECNePS29s0tCwNiC0pS+jC5EJxm\r\nDAfJgqHg58HDE1ZwEvK2ssIgz66oCTBS59WwTMARovA4kBn9TyViR9k6wRGU\r\ndJX5LqLpt8E/1MymOyqo9/2HoqCyb1EiKzB/Ur8VdDnp4a/6yKC0r18d0ju3\r\nG6FfNyvgBwrdqOoIbUcut1bwbomwjplJZ63sIKUbC4h/DThjsYOd1jeek4N/\r\nR88hh4GEpZCWW9yHYrQ5oTYiVRtc8DKo+FstoKULy49suV9998uh50vlS+rR\r\nfDvRBInZIi1weDqJvjZVRw2rC24uoTOECiPulSKmrYsIF+jAREqJ1MwOLPdx\r\nvhnPsAmPF0gmRlWpHoXeYxG8NhfCjGycXJM3XcXIjX6+6FgSxKxDPGxiYIP0\r\nFHA7TP6N26re/AcKB1BhbjblKq9znqYbJ4vVG6xfFUEPC6akKwDwBNkwTPN/\r\n3Nq9B5fhF4IB02jHCu2NSGAyWeEXICD349DITHgpeY+WXRK/0Ch/FvKsAiQ2\r\n8DLal2y8VAMn1rX+18wbiJeDgUP0x88y8joyR+SRLlRb0BGbOgZarJ5t6rKY\r\nU8gfYC/v0GR0zNX8sPSWtuZxdQRabqIODas=\r\n=D7K4\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"a04090010ee18e17487b449984807cc2b7b6e3e6","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.12.0","@opentelemetry/resources":"1.12.0","@opentelemetry/semantic-conventions":"1.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.12.0_1681409581369_0.41803382504150455","host":"s3://npm-registry-packages"}},"1.13.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.13.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.13.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"096cc2759430d880c5d886e009df2605097403dc","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz","fileCount":291,"integrity":"sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==","signatures":[{"sig":"MEUCIQCUuJ4B96xwpSLT8DFKAofaJDlGrXekNXcbsCsQRyYcBQIgdFAq/sIQjSsvEbReUkhFeNBdPC5uB4GXp48ocXDFpTU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":755361},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"8fc76896595aac912bf9e15d4f19c167317844c8","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.13.0","@opentelemetry/resources":"1.13.0","@opentelemetry/semantic-conventions":"1.13.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.13.0_1683811806724_0.20967276776702293","host":"s3://npm-registry-packages"}},"1.14.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.14.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.14.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"831af08f002228a11e577ff860eb6059c8b80fb7","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.14.0.tgz","fileCount":291,"integrity":"sha512-NzRGt3PS+HPKfQYMb6Iy8YYc5OKA73qDwci/6ujOIvyW9vcqBJSWbjZ8FeLEAmuatUB5WrRhEKu9b0sIiIYTrQ==","signatures":[{"sig":"MEQCIGq2NLJF230csI91/HmEoC3vV+PDJAfR6T+ZfR1K5MhUAiBE0zHURoBg3oiLXEQFSYgZRPg4mT6sh7zOyIBPLwqTrQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":759415},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"edebbcc757535bc88f01340409dbbecc0bb6ccf8","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.14.0","@opentelemetry/resources":"1.14.0","@opentelemetry/semantic-conventions":"1.14.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.14.0_1686031254973_0.05353905101465628","host":"s3://npm-registry-packages"}},"1.15.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.15.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.15.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"92340ded8f9fec1aaa63afb40c6e7e01769c2852","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.0.tgz","fileCount":291,"integrity":"sha512-udt1c9VHipbZwvCPIQR1VLg25Z4AMR/g0X8KmcInbFruGWQ/lptVPkz3yvWAsGSta5yHNQ3uoPwcyCygGnQ6Lg==","signatures":[{"sig":"MEQCICYANwEi+W0pUvfm1ry5U2W2dfO2D8QKOxJ6ASiMd9fAAiAJVx8yq45ynFVPddGZXUIDiB+f460P0lH8/PukjsJa9Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":751257},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"06e919d6c909e8cc8e28b6624d9843f401d9b059","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.1/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"tslib":"^2.3.1","@opentelemetry/core":"1.15.0","@opentelemetry/resources":"1.15.0","@opentelemetry/semantic-conventions":"1.15.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.1","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.15","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.15.0_1688642828714_0.7763914571035175","host":"s3://npm-registry-packages"}},"1.15.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.15.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.15.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"8eabc0827769d91ac86cde8a86ebf0bf2a7d22ad","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.1.tgz","fileCount":291,"integrity":"sha512-5hccBe2yXzzXyExJNkTsIzDe1AM7HK0al+y/D2yEpslJqS1HUzsUSuCMY7Z4+Sfz5Gf0kTa6KYEt1QUQppnoBA==","signatures":[{"sig":"MEQCIAMA9wUFC89WxydgdbzsPHJAC6CJZrcaOKd6WZiMyomlAiAmC3lL7KJpsgZkvBN+kvQdR+/QBXU5aH853Mw0xGsxpQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":759437},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"9f71800fdc2a5ee5055684037a12498af71955f2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.3/node@v18.4.0+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.4.0","dependencies":{"@opentelemetry/core":"1.15.1","@opentelemetry/resources":"1.15.1","@opentelemetry/semantic-conventions":"1.15.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.3","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.15","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.15.1_1690209168495_0.9723185712242157","host":"s3://npm-registry-packages"}},"1.15.2":{"name":"@opentelemetry/sdk-trace-base","version":"1.15.2","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.15.2","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"4821f94033c55a6c8bbd35ae387b715b6108517a","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz","fileCount":291,"integrity":"sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ==","signatures":[{"sig":"MEUCIEIHVFRfdpxnv3EMAJ4ftYQ+AlgYc1/cZ62e/gv8tkELAiEAnsGkf0w1TCg+Nh+zO9gX/GmMEANl7JYCnR3A8+/VUTY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":759437},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"48fb15862e801b742059a3e39dbcc8ef4c10b2e2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.4/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.15.2","@opentelemetry/resources":"1.15.2","@opentelemetry/semantic-conventions":"1.15.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.4","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.15.2_1691500880621_0.5765565023863921","host":"s3://npm-registry-packages"}},"1.16.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.16.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.16.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"243d69767d44646e1d16baa425c35dbabd959c4e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.16.0.tgz","fileCount":291,"integrity":"sha512-UvV8v8cN0Bx5BI40IJ+sMWcbwWekPa9ngMHSOfCWtSAVKbzwFdDV4Jrs/ejC6uR/SI6CKFQB9ItHp/0nZzVbIQ==","signatures":[{"sig":"MEUCIE0CtdHOn5oxhEmmjk8QOgpa9uN1UcGaKdPZ4BPeSZHEAiEAxBrVmbuOmnEOtXfyBV6smxPj0Fzkzn/FiYy0SDpi+iI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":762780},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"5fcd8cf136e2235903dde3df9ba03ced594f0e95","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.16.0","@opentelemetry/resources":"1.16.0","@opentelemetry/semantic-conventions":"1.16.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.6.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.6.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.16.0_1694434471110_0.20239220544943004","host":"s3://npm-registry-packages"}},"1.17.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.17.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.17.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"05a21763c9efa72903c20b8930293cdde344b681","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.17.0.tgz","fileCount":291,"integrity":"sha512-2T5HA1/1iE36Q9eg6D4zYlC4Y4GcycI1J6NsHPKZY9oWfAxWsoYnRlkPfUqyY5XVtocCo/xHpnJvGNHwzT70oQ==","signatures":[{"sig":"MEUCIQDRl4V5Q9HD6NsW3DtOOe+96ou8MwSUKBYi1McLNudswwIgcqEqisIWT6U7qrLdIRH+kQFG05+JTW3HQaitOHqTY5A=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":759485},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"faf939c77591f709afbc23fadbe629c9d3607ef6","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.17.0","@opentelemetry/resources":"1.17.0","@opentelemetry/semantic-conventions":"1.17.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.7.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.7.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.17.0_1694524353106_0.9941234111275701","host":"s3://npm-registry-packages"}},"1.17.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.17.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.17.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"8ede213df8b0c957028a869c66964e535193a4fd","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.17.1.tgz","fileCount":291,"integrity":"sha512-pfSJJSjZj5jkCJUQZicSpzN8Iz9UKMryPWikZRGObPnJo6cUSoKkjZh6BM3j+D47G4olMBN+YZKYqkFM1L6zNA==","signatures":[{"sig":"MEQCICJ2bNHS5YUGFypz4AII9j8tSEQ9DlGT6kqLjtoOEsCGAiBvV/Vq4IZpDkaHq2O9HmyppmnPVxpIxrg+Jy1T5IiHDA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":763072},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"f8e187b473274cc2011e7385992f07d319d667dc","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.17.1","@opentelemetry/resources":"1.17.1","@opentelemetry/semantic-conventions":"1.17.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.2","@types/sinon":"10.0.18","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.7.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.7.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.17.1_1696947498602_0.8314715735448981","host":"s3://npm-registry-packages"}},"1.18.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.18.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.18.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"09e420d24465aaee8e21a8a9a3c4fa2e6f0fd08e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.18.0.tgz","fileCount":291,"integrity":"sha512-OThpwn8JeU4q7exo0e8kQqs5BZGKQ9NNkes66RCs7yhUKShHEKQIYl/A3+xnGzMrbrtgogcf84brH8XD4ahjMg==","signatures":[{"sig":"MEUCIFQOMg1gSyToxhdxU8T3AviTyjEEw2MbV3NIAaAUmbOCAiEA5XDBvAFEwa4c3eHldor7UtuSWc0QH6+9JPAUmc9laKE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":762996},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"73b446688f10fd8dc4cf403a085f0a39070df7b4","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"@opentelemetry/core":"1.18.0","@opentelemetry/resources":"1.18.0","@opentelemetry/semantic-conventions":"1.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.3","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.18.0_1699353886594_0.7186034490338462","host":"s3://npm-registry-packages"}},"1.18.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.18.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.18.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"256605d90b202002d5672305c66dbcf377132379","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.18.1.tgz","fileCount":291,"integrity":"sha512-tRHfDxN5dO+nop78EWJpzZwHsN1ewrZRVVwo03VJa3JQZxToRDH29/+MB24+yoa+IArerdr7INFJiX/iN4gjqg==","signatures":[{"sig":"MEUCIQDGgWjUUen2F48lorSLadmIOOAKIpssyEX1QX3AkPnEcgIgZ/6Bk9XnhJDqwrVWwaBzBowhdIMF0BlbAA4G9pfiu3s=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":762996},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"f665499096189390e691cf1a772e677fa67812d7","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"@opentelemetry/core":"1.18.1","@opentelemetry/resources":"1.18.1","@opentelemetry/semantic-conventions":"1.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.3","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.18.1_1699466949548_0.8982337926756658","host":"s3://npm-registry-packages"}},"1.19.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.19.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.19.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"87e629e7080945d955d53c2c12352915f5797cd3","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.19.0.tgz","fileCount":291,"integrity":"sha512-+IRvUm+huJn2KqfFW3yW/cjvRwJ8Q7FzYHoUNx5Fr0Lws0LxjMJG1uVB8HDpLwm7mg5XXH2M5MF+0jj5cM8BpQ==","signatures":[{"sig":"MEUCIQCvZye8IyirUNdQZpWpKxno3ZitChFbue9awFFtHELllgIgNyloI4Qyzwns38NU8u3WC0Li1KHoH9j2JOpghwv/it0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":764145},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"d3c311aec24137084dc820805a2597e120335672","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"@opentelemetry/core":"1.19.0","@opentelemetry/resources":"1.19.0","@opentelemetry/semantic-conventions":"1.19.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.19.0_1702557329886_0.8961281946707333","host":"s3://npm-registry-packages"}},"1.20.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.20.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.20.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"1771bf7a214924fe1f27ef50395f763b65aae220","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.20.0.tgz","fileCount":291,"integrity":"sha512-BAIZ0hUgnhdb3OBQjn1FKGz/Iwie4l+uOMKklP7FGh7PTqEAbbzDNMJKaZQh6KepF7Fq+CZDRKslD3yrYy2Tzw==","signatures":[{"sig":"MEYCIQCHh3POrnMKj+uhqmw18P6kbzsRGC80c1E4qJMfjLsMDQIhAMGITPFwWQeaDCcCWHsASv07JnvLZjRORCDpN/OnUc8V","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":765977},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"57008533aba7ccd51ea80f38ff4f29404d47eb9c","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.20.0","@opentelemetry/resources":"1.20.0","@opentelemetry/semantic-conventions":"1.20.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.20.0_1705313747279_0.982043777281284","host":"s3://npm-registry-packages"}},"1.21.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.21.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.21.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"ffad912e453a92044fb220bd5d2f6743bf37bb8a","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.21.0.tgz","fileCount":291,"integrity":"sha512-yrElGX5Fv0umzp8Nxpta/XqU71+jCAyaLk34GmBzNcrW43nqbrqvdPs4gj4MVy/HcTjr6hifCDCYA3rMkajxxA==","signatures":[{"sig":"MEUCIQCqCEvDM0Ok1Pc+0BKmVi/qGM49XKfMGKMFwmzj72uEbQIgQj2iOSZQ1GktYTNsHsyb8mpoMElXqAgH4R2tc1TYjzg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":765995},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"828f2ed730e4d26d71f92e220f96b60a552a673a","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.21.0","@opentelemetry/resources":"1.21.0","@opentelemetry/semantic-conventions":"1.21.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.21.0_1706249469569_0.5490245065158903","host":"s3://npm-registry-packages"}},"1.22.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.22.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.22.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"7833bf2493a7b49461915ca32aa2884c87afd78c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.22.0.tgz","fileCount":291,"integrity":"sha512-pfTuSIpCKONC6vkTpv6VmACxD+P1woZf4q0K46nSUvXFvOFqjBYKFaAMkKD3M1mlKUUh0Oajwj35qNjMl80m1Q==","signatures":[{"sig":"MEYCIQCIbs8fApIflGCI1DVtJx4Od7v+UAgxdvD1HYs3Knyn8wIhANZPfqD9CmErmsPHuf+xvLsoRdhkFg7m60o2ydsbiYlH","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":765995},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"7be35c7845e206b27b682e8ce1cee850b09cec04","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.22.0","@opentelemetry/resources":"1.22.0","@opentelemetry/semantic-conventions":"1.22.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.9.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.22.0_1709198294393_0.7751101530396496","host":"s3://npm-registry-packages"}},"1.23.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.23.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.23.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"ff0a0f8ec47205e0b14b3b765ea2a34de1ad01dd","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.23.0.tgz","fileCount":291,"integrity":"sha512-PzBmZM8hBomUqvCddF/5Olyyviayka44O5nDWq673np3ctnvwMOvNrsUORZjKja1zJbwEuD9niAGbnVrz3jwRQ==","signatures":[{"sig":"MEYCIQCkDxxFrTKMnaGS3uSnxIeqIYW9GUKUsumBHH/5FiMIQQIhAJl43qgE1ESEcqc99k140Qw3ZUyhYy98yujb5J2vrPVz","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":769572},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"5231aa255047fbc6ee3d6a299f4423ab2f8a5fbc","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.23.0","@opentelemetry/resources":"1.23.0","@opentelemetry/semantic-conventions":"1.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.9.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.23.0_1712131805476_0.8181787837823578","host":"s3://npm-registry-packages"}},"1.24.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.24.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.24.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e2de869e33fd224f6d9f39bafa4172074d1086c8","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.24.0.tgz","fileCount":291,"integrity":"sha512-H9sLETZ4jw9UJ3totV8oM5R0m4CW0ZIOLfp4NV3g0CM8HD5zGZcaW88xqzWDgiYRpctFxd+WmHtGX/Upoa2vRg==","signatures":[{"sig":"MEUCIQC+rEv+MmjVPnMGlU3wd57V5QFFVnW9EfX1PXPig1UglQIgdXMUaQB3puehbvSfKE7sDFk+XhwNKT3dOTs+Pe7BQaA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":770265},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"3ab4f765d8d696327b7d139ae6a45e7bd7edd924","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.24.0","@opentelemetry/resources":"1.24.0","@opentelemetry/semantic-conventions":"1.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.9.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.24.0_1713969581982_0.6686664599417391","host":"s3://npm-registry-packages"}},"1.24.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.24.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.24.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"dc2ab89126e75e442913fb5af98803fde67b2536","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.24.1.tgz","fileCount":291,"integrity":"sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==","signatures":[{"sig":"MEUCIQDTLWH2gw+yHzMuSStW+CjV5Qls++vE6j4ZyXWI8kIlXAIgcZvxmlQmkjw5cG6QCa7MTuRX04iBX07fk3qYK4/O4W0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":770265},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"41c2626fe0ed03e2e83bd79ee43c9bdf0ffd80d8","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.24.1","@opentelemetry/resources":"1.24.1","@opentelemetry/semantic-conventions":"1.24.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.9.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.24.1_1715093558704_0.0518702076931048","host":"s3://npm-registry-packages"}},"1.25.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.25.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.25.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"263f9ce19001c5cd7a814d0eb40ebc6469ae763d","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.0.tgz","fileCount":291,"integrity":"sha512-6+g2fiRQUG39guCsKVeY8ToeuUf3YUnPkN6DXRA1qDmFLprlLvZm9cS6+chgbW70cZJ406FTtSCDnJwxDC5sGQ==","signatures":[{"sig":"MEQCIHH4reSQvuavsAESwWCOjKs3174GY97NApvIwo8zkZdnAiAkX4evqPLOZGIcO2364Kma0IOukWxk2s4rZ7Q1/KWdIA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":772352},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"c4d3351b6b3f5593c8d7cbfec97b45cea9fe1511","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.25.0","@opentelemetry/resources":"1.25.0","@opentelemetry/semantic-conventions":"1.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.3","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"17.0.3","karma-webpack":"5.0.1","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.10.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.25.0_1717607756250_0.3809624034816559","host":"s3://npm-registry-packages"}},"1.25.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.25.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.25.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"cbc1e60af255655d2020aa14cde17b37bd13df37","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz","fileCount":291,"integrity":"sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==","signatures":[{"sig":"MEYCIQCTj2PAtWjS/RCNjK8Gf9jjrAGZC45q4Xe4UC7g9f7wQQIhAMVEucKOdO/kvVF98e1NxnacXHLACTuS62IDNJctBSeh","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":774722},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"0608f405573901e54db01e44c533009cf28be262","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.25.1","@opentelemetry/resources":"1.25.1","@opentelemetry/semantic-conventions":"1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.3","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"17.0.3","karma-webpack":"5.0.1","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.10.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.25.1_1718875161655_0.9029425309207557","host":"s3://npm-registry-packages"}},"1.26.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.26.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.26.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"0c913bc6d2cfafd901de330e4540952269ae579c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.26.0.tgz","fileCount":291,"integrity":"sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw==","signatures":[{"sig":"MEYCIQDBmAKg/eAuae/sOfGJddg2bp6XQnx7eLvjy23V8aEtGgIhAN8fMKmoqU5I46KS49TZSYtdGwxd/ucUS3lMOUNFbWO8","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":782749},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"720bc8c70d47029cb6b41a34ffdc3d25cbaa2f80","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc mocha 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.20.4","dependencies":{"@opentelemetry/core":"1.26.0","@opentelemetry/resources":"1.26.0","@opentelemetry/semantic-conventions":"1.27.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.4","lerna":"6.6.2","mocha":"10.7.3","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.7","@types/sinon":"17.0.3","karma-webpack":"5.0.1","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.10.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"7.0.0","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.26.0_1724836639726_0.4881073465309367","host":"s3://npm-registry-packages"}},"1.27.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.27.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.27.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"2276e4cd0d701a8faba77382b2938853a0907b54","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.27.0.tgz","fileCount":291,"integrity":"sha512-btz6XTQzwsyJjombpeqCX6LhiMQYpzt2pIYNPnw0IPO/3AhT6yjnf8Mnv3ZC2A4eRYOjqrg+bfaXg9XHDRJDWQ==","signatures":[{"sig":"MEUCIEZDRp9Rw7P7jVlsdY/bPk1oroAOlQqBPxMQtoLYLwDRAiEAyNSVcrfK4Enpjkh9EGRl4Eggqu0GYDvo0upx7fyeD2o=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@opentelemetry%2fsdk-trace-base@1.27.0","provenance":{"predicateType":"https://slsa.dev/provenance/v0.2"}},"unpackedSize":787865},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"eb3ca4fb07ee31c62093f5fcec56575573c902ce","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc mocha 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.20.4","dependencies":{"@opentelemetry/core":"1.27.0","@opentelemetry/resources":"1.27.0","@opentelemetry/semantic-conventions":"1.27.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.4","lerna":"6.6.2","mocha":"10.7.3","sinon":"15.1.2","webpack":"5.94.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.8","@types/sinon":"17.0.3","karma-webpack":"5.0.1","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.10.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"7.0.0","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.27.0_1729695101194_0.8545382921292937","host":"s3://npm-registry-packages"}},"1.28.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.28.0","description":"OpenTelemetry Tracing","main":"build/src/index.js","module":"build/esm/index.js","esnext":"build/esnext/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"types":"build/src/index.d.ts","repository":{"type":"git","url":"git+https://github.com/open-telemetry/opentelemetry-js.git"},"scripts":{"prepublishOnly":"npm run compile","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","test":"nyc mocha 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","test:browser":"karma start --single-run","test:webworker":"karma start karma.worker.js --single-run","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd":"npm run tdd:node","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","lint":"eslint . --ext .ts","lint:fix":"eslint . --ext .ts --fix","version":"node ../../scripts/version-update.js","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","prewatch":"npm run precompile","peer-api-check":"node ../../scripts/peer-api-check.js","align-api-deps":"node ../../scripts/align-api-deps.js"},"keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","engines":{"node":">=14"},"publishConfig":{"access":"public"},"devDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0","@types/mocha":"10.0.9","@types/node":"18.6.5","@types/sinon":"17.0.3","@types/webpack-env":"1.16.3","babel-plugin-istanbul":"7.0.0","cross-var":"1.1.0","karma":"6.4.4","karma-chrome-launcher":"3.1.0","karma-coverage":"2.2.1","karma-mocha":"2.0.1","karma-mocha-webworker":"1.3.0","karma-spec-reporter":"0.0.36","karma-webpack":"5.0.1","lerna":"6.6.2","mocha":"10.8.2","nyc":"15.1.0","sinon":"15.1.2","ts-loader":"9.5.1","typescript":"4.4.4","webpack":"5.96.1"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"dependencies":{"@opentelemetry/core":"1.28.0","@opentelemetry/resources":"1.28.0","@opentelemetry/semantic-conventions":"1.27.0"},"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","sideEffects":false,"gitHead":"4b1ad3fda0cde58907e30fab25c3c767546708e5","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"_id":"@opentelemetry/sdk-trace-base@1.28.0","_nodeVersion":"18.20.4","_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","dist":{"integrity":"sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==","shasum":"6195dc8cd78bd74394cf54c67c5cbd8d1528516c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz","fileCount":291,"unpackedSize":796305,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@opentelemetry%2fsdk-trace-base@1.28.0","provenance":{"predicateType":"https://slsa.dev/provenance/v0.2"}},"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEyv+TGfQvTgAsZnjL3L4p9Dh+f6tk6Ixvo16zD4UiDQAiEA5yYDGo+Hjd6DCfSj66EP/AB3HNzp6+ehs7BRV0DC9ws="}]},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"directories":{},"maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdk-trace-base_1.28.0_1731926510582_0.963577670721244"},"_hasShrinkwrap":false}},"time":{"created":"2021-08-05T19:28:20.129Z","modified":"2024-11-18T10:41:51.253Z","0.24.1-alpha.4":"2021-08-05T19:28:20.395Z","0.24.1-alpha.5":"2021-08-06T11:31:39.286Z","0.24.1-alpha.7":"2021-08-07T13:33:04.337Z","0.24.1-alpha.9":"2021-08-07T14:10:07.190Z","0.24.1-alpha.14":"2021-08-11T14:50:54.966Z","0.24.1-alpha.18":"2021-08-14T08:16:09.993Z","0.24.1-alpha.20":"2021-08-17T21:07:07.249Z","0.25.1-alpha.21":"2021-08-18T20:16:42.539Z","0.25.0":"2021-08-18T21:16:46.735Z","0.25.1-alpha.2":"2021-08-23T21:41:10.378Z","0.25.1-alpha.4":"2021-08-24T19:33:17.584Z","0.25.1-alpha.7":"2021-08-27T18:20:00.339Z","0.25.1-alpha.12":"2021-08-30T20:22:09.042Z","0.25.1-alpha.13":"2021-08-30T20:41:30.144Z","0.25.1-alpha.16":"2021-09-04T08:05:10.908Z","0.25.1-alpha.23":"2021-09-08T22:15:12.367Z","0.26.0":"2021-09-30T12:35:32.508Z","1.0.0":"2021-09-30T20:53:43.948Z","1.0.1":"2021-11-11T14:51:24.979Z","1.1.0":"2022-03-18T08:11:01.831Z","1.1.1":"2022-03-22T19:52:31.108Z","1.2.0":"2022-04-22T14:57:03.801Z","1.3.0":"2022-05-27T19:41:24.857Z","1.3.1":"2022-06-06T20:26:17.973Z","1.4.0":"2022-07-06T20:15:38.341Z","1.5.0":"2022-07-26T20:52:16.066Z","1.6.0":"2022-08-24T17:44:35.505Z","1.7.0":"2022-09-16T12:14:58.183Z","1.8.0":"2022-11-09T19:45:34.290Z","1.9.0":"2023-01-11T21:46:29.878Z","1.9.1":"2023-01-30T15:30:11.676Z","1.10.0":"2023-03-13T15:53:47.027Z","1.10.1":"2023-03-20T16:10:34.940Z","1.11.0":"2023-03-30T15:30:56.117Z","1.12.0":"2023-04-13T18:13:01.576Z","1.13.0":"2023-05-11T13:30:06.916Z","1.14.0":"2023-06-06T06:00:55.192Z","1.15.0":"2023-07-06T11:27:08.851Z","1.15.1":"2023-07-24T14:32:48.689Z","1.15.2":"2023-08-08T13:21:20.770Z","1.16.0":"2023-09-11T12:14:31.274Z","1.17.0":"2023-09-12T13:12:33.338Z","1.17.1":"2023-10-10T14:18:18.968Z","1.18.0":"2023-11-07T10:44:46.767Z","1.18.1":"2023-11-08T18:09:10.048Z","1.19.0":"2023-12-14T12:35:30.035Z","1.20.0":"2024-01-15T10:15:47.496Z","1.21.0":"2024-01-26T06:11:09.767Z","1.22.0":"2024-02-29T09:18:14.609Z","1.23.0":"2024-04-03T08:10:05.643Z","1.24.0":"2024-04-24T14:39:42.149Z","1.24.1":"2024-05-07T14:52:38.897Z","1.25.0":"2024-06-05T17:15:56.531Z","1.25.1":"2024-06-20T09:19:21.851Z","1.26.0":"2024-08-28T09:17:19.852Z","1.27.0":"2024-10-23T14:51:41.469Z","1.28.0":"2024-11-18T10:41:50.759Z"},"bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"repository":{"type":"git","url":"git+https://github.com/open-telemetry/opentelemetry-js.git"},"description":"OpenTelemetry Tracing","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"readme":"# OpenTelemetry Tracing SDK\n\n[![NPM Published Version][npm-img]][npm-url]\n[![Apache License][license-image]][license-image]\n\nThe `tracing` module contains the foundation for all tracing SDKs of [opentelemetry-js](https://github.com/open-telemetry/opentelemetry-js).\n\nUsed standalone, this module provides methods for manual instrumentation of code, offering full control over span creation for client-side JavaScript (browser) and Node.js.\n\nIt does **not** provide automated instrumentation of known libraries, context propagation for asynchronous invocations or distributed-context out-of-the-box.\n\nFor automated instrumentation for Node.js, please see\n[@opentelemetry/sdk-trace-node](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-node).\n\n## Installation\n\n```bash\nnpm install --save @opentelemetry/api\nnpm install --save @opentelemetry/sdk-trace-base\n```\n\n## Usage\n\n```js\nconst opentelemetry = require('@opentelemetry/api');\nconst { BasicTracerProvider } = require('@opentelemetry/sdk-trace-base');\n\n// To start a trace, you first need to initialize the Tracer provider.\n// NOTE: The default OpenTelemetry tracer provider does not record any tracing information.\n// Registering a working tracer provider allows the API methods to record traces.\nnew BasicTracerProvider().register();\n\n// To create a span in a trace, we used the global singleton tracer to start a new span.\nconst span = opentelemetry.trace.getTracer('default').startSpan('foo');\n\n// Set a span attribute\nspan.setAttribute('key', 'value');\n\n// We must end the spans so they become available for exporting.\nspan.end();\n```\n\n## Config\n\nTracing configuration is a merge of user supplied configuration with both the default\nconfiguration as specified in [config.ts](./src/config.ts) and an\nenvironmentally configurable sampling (via `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG`).\n\n## Built-in Samplers\n\nSampler is used to make decisions on `Span` sampling.\n\n### AlwaysOn Sampler\n\nSamples every trace regardless of upstream sampling decisions.\n\n> This is used as a default Sampler\n\n```js\nconst {\n AlwaysOnSampler,\n BasicTracerProvider,\n} = require(\"@opentelemetry/sdk-trace-base\");\n\nconst tracerProvider = new BasicTracerProvider({\n sampler: new AlwaysOnSampler()\n});\n```\n\n### AlwaysOff Sampler\n\nDoesn't sample any trace, regardless of upstream sampling decisions.\n\n```js\nconst {\n AlwaysOffSampler,\n BasicTracerProvider,\n} = require(\"@opentelemetry/sdk-trace-base\");\n\nconst tracerProvider = new BasicTracerProvider({\n sampler: new AlwaysOffSampler()\n});\n```\n\n### TraceIdRatioBased Sampler\n\nSamples some percentage of traces, calculated deterministically using the trace ID.\nAny trace that would be sampled at a given percentage will also be sampled at any higher percentage.\n\nThe `TraceIDRatioSampler` may be used with the `ParentBasedSampler` to respect the sampled flag of an incoming trace.\n\n```js\nconst {\n BasicTracerProvider,\n TraceIdRatioBasedSampler,\n} = require(\"@opentelemetry/sdk-trace-base\");\n\nconst tracerProvider = new BasicTracerProvider({\n // See details of ParentBasedSampler below\n sampler: new ParentBasedSampler({\n // Trace ID Ratio Sampler accepts a positional argument\n // which represents the percentage of traces which should\n // be sampled.\n root: new TraceIdRatioBasedSampler(0.5)\n });\n});\n```\n\n### ParentBased Sampler\n\n- This is a composite sampler. `ParentBased` helps distinguished between the\nfollowing cases:\n - No parent (root span).\n - Remote parent with `sampled` flag `true`\n - Remote parent with `sampled` flag `false`\n - Local parent with `sampled` flag `true`\n - Local parent with `sampled` flag `false`\n\nRequired parameters:\n\n- `root(Sampler)` - Sampler called for spans with no parent (root spans)\n\nOptional parameters:\n\n- `remoteParentSampled(Sampler)` (default: `AlwaysOn`)\n- `remoteParentNotSampled(Sampler)` (default: `AlwaysOff`)\n- `localParentSampled(Sampler)` (default: `AlwaysOn`)\n- `localParentNotSampled(Sampler)` (default: `AlwaysOff`)\n\n|Parent| parent.isRemote() | parent.isSampled()| Invoke sampler|\n|--|--|--|--|\n|absent| n/a | n/a |`root()`|\n|present|true|true|`remoteParentSampled()`|\n|present|true|false|`remoteParentNotSampled()`|\n|present|false|true|`localParentSampled()`|\n|present|false|false|`localParentNotSampled()`|\n\n```js\nconst {\n AlwaysOffSampler,\n BasicTracerProvider,\n ParentBasedSampler,\n TraceIdRatioBasedSampler,\n} = require(\"@opentelemetry/sdk-trace-base\");\n\nconst tracerProvider = new BasicTracerProvider({\n sampler: new ParentBasedSampler({\n // By default, the ParentBasedSampler will respect the parent span's sampling\n // decision. This is configurable by providing a different sampler to use\n // based on the situation. See configuration details above.\n //\n // This will delegate the sampling decision of all root traces (no parent)\n // to the TraceIdRatioBasedSampler.\n // See details of TraceIdRatioBasedSampler above.\n root: new TraceIdRatioBasedSampler(0.5)\n })\n});\n```\n\n## Example\n\nSee [examples/basic-tracer-node](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/basic-tracer-node) for an end-to-end example, including exporting created spans.\n\n## Useful links\n\n- For more information on OpenTelemetry, visit: \n- For more about OpenTelemetry JavaScript: \n- For help or feedback on this project, join us in [GitHub Discussions][discussions-url]\n\n## License\n\nApache 2.0 - See [LICENSE][license-url] for more information.\n\n[discussions-url]: https://github.com/open-telemetry/opentelemetry-js/discussions\n[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/main/LICENSE\n[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat\n[npm-url]: https://www.npmjs.com/package/@opentelemetry/sdk-trace-base\n[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fsdk-trace-base.svg\n","readmeFilename":"README.md"} \ No newline at end of file diff --git a/tests/registry/npm/npm-check-updates/npm-check-updates-17.1.13.tgz b/tests/registry/npm/npm-check-updates/npm-check-updates-17.1.13.tgz new file mode 100644 index 0000000000..4a95f26159 Binary files /dev/null and b/tests/registry/npm/npm-check-updates/npm-check-updates-17.1.13.tgz differ diff --git a/tests/registry/npm/npm-check-updates/registry.json b/tests/registry/npm/npm-check-updates/registry.json new file mode 100644 index 0000000000..71d96fd83d --- /dev/null +++ b/tests/registry/npm/npm-check-updates/registry.json @@ -0,0 +1,201 @@ +{ + "name": "npm-check-updates", + "dist-tags": { + "latest": "17.1.13" + }, + "versions": { + "17.1.13": { + "name": "npm-check-updates", + "version": "17.1.13", + "author": { + "name": "Tomas Junnonen", + "email": "tomas1@gmail.com" + }, + "license": "Apache-2.0", + "description": "Find newer versions of dependencies than what your package.json allows", + "engines": { + "node": "^18.18.0 || >=20.0.0", + "npm": ">=8.12.1" + }, + "main": "build/index.js", + "types": "build/index.d.ts", + "scripts": { + "build": "rimraf build && npm run build:options && vite build", + "build:options": "vite-node src/scripts/build-options.ts", + "build:analyze": "rimraf build && npm run build:options && ANALYZER=true vite build", + "lint": "cross-env FORCE_COLOR=1 npm-run-all --parallel --aggregate-output lint:*", + "lint:lockfile": "lockfile-lint", + "lint:markdown": "markdownlint \"**/*.md\" --ignore \"**/node_modules/**/*.md\" --ignore build --config .markdownlint.js", + "lint:src": "eslint --cache --cache-location node_modules/.cache/.eslintcache --ignore-path .gitignore --report-unused-disable-directives .", + "prepare": "src/scripts/install-hooks", + "prepublishOnly": "npm run build", + "prettier": "prettier . --check", + "test": "npm run test:unit && npm run test:e2e", + "test:bun": "test/bun-install.sh && mocha test/bun", + "test:unit": "mocha test test/package-managers/*", + "test:e2e": "./test/e2e.sh", + "ncu": "node build/cli.js" + }, + "bin": { + "npm-check-updates": "build/cli.js", + "ncu": "build/cli.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/raineorshine/npm-check-updates.git" + }, + "bugs": { + "url": "https://github.com/raineorshine/npm-check-updates/issues" + }, + "overrides": { + "ip": "2.0.1", + "jsonparse": "https://github.com/ARitz-Cracker/jsonparse/tree/patch-1", + "@yarnpkg/parsers": "2.6.0" + }, + "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@types/chai": "^4.3.19", + "@types/chai-as-promised": "^8.0.0", + "@types/chai-string": "^1.4.5", + "@types/cli-table": "^0.3.4", + "@types/hosted-git-info": "^3.0.5", + "@types/ini": "^4.1.1", + "@types/js-yaml": "^4.0.9", + "@types/json-parse-helpfulerror": "^1.0.3", + "@types/jsonlines": "^0.1.5", + "@types/lodash": "^4.17.10", + "@types/mocha": "^10.0.9", + "@types/node": "^22.7.5", + "@types/npm-registry-fetch": "^8.0.7", + "@types/parse-github-url": "^1.0.3", + "@types/picomatch": "^3.0.1", + "@types/progress": "^2.0.7", + "@types/prompts": "^2.4.9", + "@types/remote-git-tags": "^4.0.2", + "@types/semver": "^7.5.8", + "@types/semver-utils": "^1.1.3", + "@types/sinon": "^17.0.3", + "@types/update-notifier": "^6.0.8", + "@typescript-eslint/eslint-plugin": "^8.9.0", + "@typescript-eslint/parser": "^8.9.0", + "camelcase": "^6.3.0", + "chai": "^4.3.10", + "chai-as-promised": "^7.1.2", + "chai-string": "^1.5.0", + "chalk": "^5.3.0", + "cli-table3": "^0.6.5", + "commander": "^12.1.0", + "cross-env": "^7.0.3", + "dequal": "^2.0.3", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-config-raine": "^0.5.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.4.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.6.0", + "fast-glob": "^3.3.2", + "fast-memoize": "^2.5.2", + "find-up": "5.0.0", + "fp-and-or": "^1.0.2", + "hosted-git-info": "^8.0.0", + "ini": "^5.0.0", + "js-yaml": "^4.1.0", + "json-parse-helpfulerror": "^1.0.3", + "jsonlines": "^0.1.1", + "lockfile-lint": "^4.14.0", + "lodash": "^4.17.21", + "markdownlint-cli": "^0.42.0", + "mocha": "^10.7.3", + "npm-registry-fetch": "^18.0.2", + "npm-run-all": "^4.1.5", + "p-map": "^4.0.0", + "parse-github-url": "^1.0.3", + "picomatch": "^4.0.2", + "prettier": "^3.3.3", + "progress": "^2.0.3", + "prompts-ncu": "^3.0.2", + "rc-config-loader": "^4.1.3", + "remote-git-tags": "^3.0.0", + "rfdc": "^1.4.1", + "rimraf": "^6.0.1", + "rollup-plugin-node-externals": "^7.1.3", + "semver": "^7.6.3", + "semver-utils": "^1.1.4", + "should": "^13.2.3", + "sinon": "^19.0.2", + "source-map-support": "^0.5.21", + "spawn-please": "^3.0.0", + "strip-ansi": "^7.1.0", + "strip-json-comments": "^5.0.1", + "ts-node": "^10.9.2", + "typescript": "^5.6.3", + "typescript-json-schema": "^0.65.1", + "untildify": "^4.0.0", + "update-notifier": "^7.3.1", + "verdaccio": "^6.0.1", + "vite": "^5.4.9", + "vite-bundle-analyzer": "^0.12.1", + "vite-node": "^2.1.3", + "vite-plugin-dts": "^4.2.4", + "yarn": "^1.22.22" + }, + "lockfile-lint": { + "allowed-schemes": [ + "https:", + "git+ssh:" + ], + "allowed-hosts": [ + "npm", + "github.com" + ], + "empty-hostname": false, + "type": "npm ", + "path": "package-lock.json" + }, + "mocha": { + "check-leaks": true, + "extension": [ + "test.ts" + ], + "require": [ + "source-map-support/register", + "ts-node/register" + ], + "timeout": 60000, + "trace-deprecation": true, + "trace-warnings": true, + "use_strict": true + }, + "_id": "npm-check-updates@17.1.13", + "gitHead": "f514093647f1833c83653765493c694372d14fea", + "_nodeVersion": "22.0.0", + "_npmVersion": "10.9.1", + "dist": { + "integrity": "sha512-m9Woo2J5XVab0VcQpYvrQ0hx3ySI1mGbiHR595mc6Lr1/FIaTWvv+oU+T1WKSfXRiluKC/V5P6Bdk5agaYpqqg==", + "shasum": "93e1c5fa5b8e11bca0bd143650b14ffcf9fc6b5a", + "tarball": "http://localhost:4260/npm-check-updates/npm-check-updates-17.1.13.tgz", + "fileCount": 19, + "unpackedSize": 5336239 + }, + "directories": {}, + "_hasShrinkwrap": false + } + }, + "bugs": { + "url": "https://github.com/raineorshine/npm-check-updates/issues" + }, + "author": { + "name": "Tomas Junnonen", + "email": "tomas1@gmail.com" + }, + "license": "Apache-2.0", + "homepage": "https://github.com/raineorshine/npm-check-updates", + "repository": { + "type": "git", + "url": "git+https://github.com/raineorshine/npm-check-updates.git" + }, + "description": "Find newer versions of dependencies than what your package.json allows", + "readmeFilename": "README.md" +} diff --git a/tests/registry/npm/trim_registry_files.js b/tests/registry/npm/trim_registry_files.js index 608624b1d3..e6fbe1353f 100644 --- a/tests/registry/npm/trim_registry_files.js +++ b/tests/registry/npm/trim_registry_files.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-write=. --allow-read=. -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Run this to trim the registry.json files diff --git a/tests/specs/cert/localhost_unsafe_ssl/localhost_unsafe_ssl.ts.out b/tests/specs/cert/localhost_unsafe_ssl/localhost_unsafe_ssl.ts.out index c7bdfde0ed..ffb84ebfde 100644 --- a/tests/specs/cert/localhost_unsafe_ssl/localhost_unsafe_ssl.ts.out +++ b/tests/specs/cert/localhost_unsafe_ssl/localhost_unsafe_ssl.ts.out @@ -1,3 +1,6 @@ DANGER: TLS certificate validation is disabled for: deno.land -error: Import 'https://localhost:5545/subdir/mod2.ts' failed: error sending request for url (https://localhost:5545/subdir/mod2.ts): client error[WILDCARD] - at file:///[WILDCARD]/cafile_url_imports.ts:[WILDCARD] +error: Import 'https://localhost:5545/subdir/mod2.ts' failed. + 0: error sending request for url (https://localhost:5545/subdir/mod2.ts): client error (Connect): invalid peer certificate: UnknownIssuer + 1: client error (Connect) + 2: invalid peer certificate: UnknownIssuer + at file:///[WILDLINE]/cafile_url_imports.ts:[WILDLINE] diff --git a/tests/specs/check/css_import/__test__.jsonc b/tests/specs/check/css_import/__test__.jsonc index 629dcd3833..4e16560ec2 100644 --- a/tests/specs/check/css_import/__test__.jsonc +++ b/tests/specs/check/css_import/__test__.jsonc @@ -10,6 +10,10 @@ "args": "check not_exists.ts", "output": "not_exists.out", "exitCode": 1 + }, { + "args": "run --check not_exists.ts", + "output": "not_exists.out", + "exitCode": 1 }, { "args": "check exists_and_try_uses.ts", "output": "exists_and_try_uses.out", diff --git a/tests/specs/check/css_import/not_exists.out b/tests/specs/check/css_import/not_exists.out index 95fd14668e..1e9dce6b70 100644 --- a/tests/specs/check/css_import/not_exists.out +++ b/tests/specs/check/css_import/not_exists.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDLINE]/not_exists.css". +Check [WILDLINE]exists.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/not_exists.css'. at file:///[WILDLINE]/not_exists.ts:1:8 diff --git a/tests/specs/check/dts_importing_non_existent/check.out b/tests/specs/check/dts_importing_non_existent/check.out index 80ec9593b0..65e27bce83 100644 --- a/tests/specs/check/dts_importing_non_existent/check.out +++ b/tests/specs/check/dts_importing_non_existent/check.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDLINE]/test". +Check file:///[WILDLINE]/dts_importing_non_existent/index.js +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/test'. at file:///[WILDLINE]/index.d.ts:1:22 diff --git a/tests/specs/check/import_non_existent_in_remote/__test__.jsonc b/tests/specs/check/import_non_existent_in_remote/__test__.jsonc new file mode 100644 index 0000000000..39cd37ffc0 --- /dev/null +++ b/tests/specs/check/import_non_existent_in_remote/__test__.jsonc @@ -0,0 +1,14 @@ +{ + "tests": { + "not_all": { + "args": "check --allow-import import_remote.ts", + "output": "[WILDCARD]", + "exitCode": 0 + }, + "all": { + "args": "check --all --allow-import import_remote.ts", + "output": "check_all.out", + "exitCode": 1 + } + } +} diff --git a/tests/specs/check/import_non_existent_in_remote/check_all.out b/tests/specs/check/import_non_existent_in_remote/check_all.out new file mode 100644 index 0000000000..a3c3b1759c --- /dev/null +++ b/tests/specs/check/import_non_existent_in_remote/check_all.out @@ -0,0 +1,5 @@ +Download http://localhost:4545/check/import_non_existent.ts +Download http://localhost:4545/check/non-existent-module.ts +Check file:///[WILDLINE]/import_remote.ts +error: TS2307 [ERROR]: Cannot find module 'http://localhost:4545/check/non-existent-module.ts'. + at http://localhost:4545/check/import_non_existent.ts:1:22 diff --git a/tests/specs/check/import_non_existent_in_remote/import_remote.ts b/tests/specs/check/import_non_existent_in_remote/import_remote.ts new file mode 100644 index 0000000000..47c5c654b8 --- /dev/null +++ b/tests/specs/check/import_non_existent_in_remote/import_remote.ts @@ -0,0 +1,3 @@ +import { Other } from "http://localhost:4545/check/import_non_existent.ts"; + +console.log(Other); diff --git a/tests/specs/check/message_chain_formatting/__test__.jsonc b/tests/specs/check/message_chain_formatting/__test__.jsonc new file mode 100644 index 0000000000..1b5b49d8a9 --- /dev/null +++ b/tests/specs/check/message_chain_formatting/__test__.jsonc @@ -0,0 +1,6 @@ +// Regression test for https://github.com/denoland/deno/issues/27411. +{ + "args": "check --quiet message_chain_formatting.ts", + "output": "message_chain_formatting.out", + "exitCode": 1 +} diff --git a/tests/specs/check/message_chain_formatting/message_chain_formatting.out b/tests/specs/check/message_chain_formatting/message_chain_formatting.out new file mode 100644 index 0000000000..ca5c646ccb --- /dev/null +++ b/tests/specs/check/message_chain_formatting/message_chain_formatting.out @@ -0,0 +1,10 @@ +error: TS2769 [ERROR]: No overload matches this call. + Overload 1 of 3, '(s: string, b: boolean): void', gave the following error. + Argument of type 'number' is not assignable to parameter of type 'boolean'. + Overload 2 of 3, '(ss: string[], b: boolean): void', gave the following error. + Argument of type 'string' is not assignable to parameter of type 'string[]'. + Overload 3 of 3, '(ss: string[], b: Date): void', gave the following error. + Argument of type 'string' is not assignable to parameter of type 'string[]'. +foo("hello", 42); +~~~ + at [WILDLINE]/message_chain_formatting.ts:8:1 diff --git a/tests/specs/check/message_chain_formatting/message_chain_formatting.ts b/tests/specs/check/message_chain_formatting/message_chain_formatting.ts new file mode 100644 index 0000000000..ed342629ea --- /dev/null +++ b/tests/specs/check/message_chain_formatting/message_chain_formatting.ts @@ -0,0 +1,8 @@ +function foo(s: string, b: boolean): void; +function foo(ss: string[], b: boolean): void; +function foo(ss: string[], b: Date): void; +function foo(sOrSs: string | string[], b: boolean | Date): void { + console.log(sOrSs, b); +} + +foo("hello", 42); diff --git a/tests/specs/check/module_not_found/__test__.jsonc b/tests/specs/check/module_not_found/__test__.jsonc new file mode 100644 index 0000000000..5e7cfa2e59 --- /dev/null +++ b/tests/specs/check/module_not_found/__test__.jsonc @@ -0,0 +1,24 @@ +{ + "tests": { + "check": { + "args": "check --allow-import main.ts", + "output": "main.out", + "exitCode": 1 + }, + "run": { + "args": "run --check --allow-import main.ts", + "output": "main.out", + "exitCode": 1 + }, + "missing_local_root": { + "args": "check --allow-import non_existent.ts", + "output": "missing_local_root.out", + "exitCode": 1 + }, + "missing_remote_root": { + "args": "check --allow-import http://localhost:4545/missing_non_existent.ts", + "output": "missing_remote_root.out", + "exitCode": 1 + } + } +} diff --git a/tests/specs/check/module_not_found/main.out b/tests/specs/check/module_not_found/main.out new file mode 100644 index 0000000000..6c16183560 --- /dev/null +++ b/tests/specs/check/module_not_found/main.out @@ -0,0 +1,9 @@ +Download http://localhost:4545/remote.ts +Check file:///[WILDLINE]/module_not_found/main.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/other.js'. + at file:///[WILDLINE]/main.ts:1:22 + +TS2307 [ERROR]: Cannot find module 'http://localhost:4545/remote.ts'. + at file:///[WILDLINE]/main.ts:2:24 + +Found 2 errors. diff --git a/tests/specs/check/module_not_found/main.ts b/tests/specs/check/module_not_found/main.ts new file mode 100644 index 0000000000..cec9512569 --- /dev/null +++ b/tests/specs/check/module_not_found/main.ts @@ -0,0 +1,5 @@ +import { Test } from "./other.js"; +import { Remote } from "http://localhost:4545/remote.ts"; + +console.log(new Test()); +console.log(new Remote()); diff --git a/tests/specs/check/module_not_found/missing_local_root.out b/tests/specs/check/module_not_found/missing_local_root.out new file mode 100644 index 0000000000..34b150c9a3 --- /dev/null +++ b/tests/specs/check/module_not_found/missing_local_root.out @@ -0,0 +1,2 @@ +Check file:///[WILDLINE]/non_existent.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/non_existent.ts'. diff --git a/tests/specs/check/module_not_found/missing_remote_root.out b/tests/specs/check/module_not_found/missing_remote_root.out new file mode 100644 index 0000000000..e408938e41 --- /dev/null +++ b/tests/specs/check/module_not_found/missing_remote_root.out @@ -0,0 +1,3 @@ +Download http://localhost:4545/missing_non_existent.ts +Check http://localhost:4545/missing_non_existent.ts +error: TS2307 [ERROR]: Cannot find module 'http://localhost:4545/missing_non_existent.ts'. diff --git a/tests/specs/check/types_resolved_relative_config/main.out b/tests/specs/check/types_resolved_relative_config/main.out index 212e1224ca..5763d3298c 100644 --- a/tests/specs/check/types_resolved_relative_config/main.out +++ b/tests/specs/check/types_resolved_relative_config/main.out @@ -1,3 +1,4 @@ [# It should be resolving relative the config in sub_dir instead of the cwd] -error: Module not found "file:///[WILDLINE]/sub_dir/a.d.ts". +Check file:///[WILDLINE]/main.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/sub_dir/a.d.ts'. at file:///[WILDLINE]/sub_dir/deno.json:1:1 diff --git a/tests/specs/cli/otel_basic/__test__.jsonc b/tests/specs/cli/otel_basic/__test__.jsonc index e7f8d17c7a..f9826671e8 100644 --- a/tests/specs/cli/otel_basic/__test__.jsonc +++ b/tests/specs/cli/otel_basic/__test__.jsonc @@ -1,28 +1,27 @@ { - "steps": [ - { + "tests": { + "basic": { "args": "run -A main.ts basic.ts", "output": "basic.out" }, - { + "natural_exit": { "args": "run -A main.ts natural_exit.ts", "output": "natural_exit.out" }, - { + "deno_dot_exit": { "args": "run -A main.ts deno_dot_exit.ts", "output": "deno_dot_exit.out" }, - { + "uncaught": { "args": "run -A main.ts uncaught.ts", "output": "uncaught.out" }, - { + "metric": { + "envs": { + "OTEL_METRIC_EXPORT_INTERVAL": "1000" + }, "args": "run -A main.ts metric.ts", "output": "metric.out" - }, - { - "args": "run -A --unstable-otel context.ts", - "output": "" } - ] + } } diff --git a/tests/specs/cli/otel_basic/basic.out b/tests/specs/cli/otel_basic/basic.out index 1e82ba59b3..c16f57a8fc 100644 --- a/tests/specs/cli/otel_basic/basic.out +++ b/tests/specs/cli/otel_basic/basic.out @@ -2,7 +2,7 @@ "spans": [ { "traceId": "00000000000000000000000000000001", - "spanId": "0000000000000002", + "spanId": "0000000000000001", "traceState": "", "parentSpanId": "", "flags": 1, @@ -59,8 +59,8 @@ } }, { - "traceId": "00000000000000000000000000000003", - "spanId": "0000000000000004", + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000002", "traceState": "", "parentSpanId": "", "flags": 1, @@ -117,10 +117,10 @@ } }, { - "traceId": "00000000000000000000000000000003", - "spanId": "1000000000000001", + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000003", "traceState": "", - "parentSpanId": "0000000000000004", + "parentSpanId": "0000000000000002", "flags": 1, "name": "outer span", "kind": 1, @@ -138,10 +138,10 @@ } }, { - "traceId": "00000000000000000000000000000003", - "spanId": "1000000000000002", + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000004", "traceState": "", - "parentSpanId": "1000000000000001", + "parentSpanId": "0000000000000003", "flags": 1, "name": "inner span", "kind": 1, @@ -171,8 +171,8 @@ "attributes": [], "droppedAttributesCount": 0, "flags": 1, - "traceId": "00000000000000000000000000000003", - "spanId": "1000000000000002" + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000004" }, { "timeUnixNano": "0", @@ -185,8 +185,8 @@ "attributes": [], "droppedAttributesCount": 0, "flags": 1, - "traceId": "00000000000000000000000000000003", - "spanId": "1000000000000002" + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000004" } ], "metrics": [] diff --git a/tests/specs/cli/otel_basic/basic.ts b/tests/specs/cli/otel_basic/basic.ts index 5c4ae43cd8..6f19867d96 100644 --- a/tests/specs/cli/otel_basic/basic.ts +++ b/tests/specs/cli/otel_basic/basic.ts @@ -1,7 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { trace } from "npm:@opentelemetry/api@1.9.0"; -import "jsr:@deno/otel@0.0.2/register"; const tracer = trace.getTracer("example-tracer"); diff --git a/tests/specs/cli/otel_basic/context.ts b/tests/specs/cli/otel_basic/context.ts index cef0dbd81a..16b08835ff 100644 --- a/tests/specs/cli/otel_basic/context.ts +++ b/tests/specs/cli/otel_basic/context.ts @@ -2,9 +2,7 @@ import { assertEquals } from "@std/assert"; const { ContextManager } = Deno.telemetry; -const cm = new ContextManager(); - -const a = cm.active(); +const a = ContextManager.active(); const b = a.setValue("b", 1); const c = b.setValue("c", 2); diff --git a/tests/specs/cli/otel_basic/main.ts b/tests/specs/cli/otel_basic/main.ts index 634727cea7..921c39911b 100644 --- a/tests/specs/cli/otel_basic/main.ts +++ b/tests/specs/cli/otel_basic/main.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const data = { spans: [], @@ -21,8 +21,13 @@ const server = Deno.serve( stdout: "null", }); const child = command.spawn(); - child.output() - .then(() => server.shutdown()) + child.status + .then((status) => { + if (status.signal) { + throw new Error("child process failed: " + JSON.stringify(status)); + } + return server.shutdown(); + }) .then(() => { data.logs.sort((a, b) => Number( diff --git a/tests/specs/cli/otel_basic/metric.out b/tests/specs/cli/otel_basic/metric.out index 26ed4a23c6..dd53734230 100644 --- a/tests/specs/cli/otel_basic/metric.out +++ b/tests/specs/cli/otel_basic/metric.out @@ -56,6 +56,31 @@ "isMonotonic": false } }, + { + "name": "gauge", + "description": "Example of a Gauge", + "unit": "", + "metadata": [], + "gauge": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ] + } + }, { "name": "histogram", "description": "Example of a Histogram", @@ -119,6 +144,265 @@ ], "aggregationTemporality": 2 } + }, + { + "name": "observable_counter", + "description": "Example of a ObservableCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": true + } + }, + { + "name": "observable_up_down_counter", + "description": "Example of a ObservableUpDownCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": false + } + }, + { + "name": "observable_gauge", + "description": "Example of a ObservableGauge", + "unit": "", + "metadata": [], + "gauge": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ] + } + }, + { + "name": "counter", + "description": "Example of a Counter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": true + } + }, + { + "name": "up_down_counter", + "description": "Example of a UpDownCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": -1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": false + } + }, + { + "name": "gauge", + "description": "Example of a Gauge", + "unit": "", + "metadata": [], + "gauge": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ] + } + }, + { + "name": "histogram", + "description": "Example of a Histogram", + "unit": "", + "metadata": [], + "histogram": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "count": 1, + "sum": 1, + "bucketCounts": [ + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "explicitBounds": [ + 0, + 5, + 10, + 25, + 50, + 75, + 100, + 250, + 500, + 750, + 1000, + 2500, + 5000, + 7500, + 10000 + ], + "exemplars": [], + "flags": 0, + "min": 1, + "max": 1 + } + ], + "aggregationTemporality": 2 + } + }, + { + "name": "observable_counter", + "description": "Example of a ObservableCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": true + } + }, + { + "name": "observable_up_down_counter", + "description": "Example of a ObservableUpDownCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": false + } + }, + { + "name": "observable_gauge", + "description": "Example of a ObservableGauge", + "unit": "", + "metadata": [], + "gauge": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ] + } } ] } diff --git a/tests/specs/cli/otel_basic/metric.ts b/tests/specs/cli/otel_basic/metric.ts index 7d332f0432..f95c0cb802 100644 --- a/tests/specs/cli/otel_basic/metric.ts +++ b/tests/specs/cli/otel_basic/metric.ts @@ -1,18 +1,8 @@ -import { - MeterProvider, - PeriodicExportingMetricReader, -} from "npm:@opentelemetry/sdk-metrics@1.28.0"; +import { metrics } from "npm:@opentelemetry/api@1"; -const meterProvider = new MeterProvider(); +metrics.setGlobalMeterProvider(Deno.telemetry.meterProvider); -meterProvider.addMetricReader( - new PeriodicExportingMetricReader({ - exporter: new Deno.telemetry.MetricExporter(), - exportIntervalMillis: 100, - }), -); - -const meter = meterProvider.getMeter("m"); +const meter = metrics.getMeter("m"); const counter = meter.createCounter("counter", { description: "Example of a Counter", @@ -22,13 +12,82 @@ const upDownCounter = meter.createUpDownCounter("up_down_counter", { description: "Example of a UpDownCounter", }); +const gauge = meter.createGauge("gauge", { + description: "Example of a Gauge", +}); + const histogram = meter.createHistogram("histogram", { description: "Example of a Histogram", }); +const observableCounterPromise = Promise.withResolvers(); +const observableCounter = meter.createObservableCounter("observable_counter", { + description: "Example of a ObservableCounter", +}); +observableCounter.addCallback((res) => { + res.observe(1); + observableCounterPromise.resolve(); +}); + +const observableUpDownCounterPromise = Promise.withResolvers(); +const observableUpDownCounter = meter + .createObservableUpDownCounter("observable_up_down_counter", { + description: "Example of a ObservableUpDownCounter", + }); +observableUpDownCounter.addCallback((res) => { + res.observe(1); + observableUpDownCounterPromise.resolve(); +}); + +const observableGaugePromise = Promise.withResolvers(); +const observableGauge = meter.createObservableGauge("observable_gauge", { + description: "Example of a ObservableGauge", +}); +observableGauge.addCallback((res) => { + res.observe(1); + observableGaugePromise.resolve(); +}); + +const observableCounterBatch = meter.createObservableCounter( + "observable_counter_batch", + { description: "Example of a ObservableCounter, written in batch" }, +); +const observableUpDownCounterBatch = meter.createObservableUpDownCounter( + "observable_up_down_counter_batch", + { description: "Example of a ObservableUpDownCounter, written in batch" }, +); +const observableGaugeBatch = meter.createObservableGauge( + "observable_gauge_batch", + { + description: "Example of a ObservableGauge, written in batch", + }, +); + +const observableBatchPromise = Promise.withResolvers(); +meter.addBatchObservableCallback((observer) => { + observer.observe(observableCounter, 2); + observer.observe(observableUpDownCounter, 2); + observer.observe(observableGauge, 2); + observableBatchPromise.resolve(); +}, [ + observableCounterBatch, + observableUpDownCounterBatch, + observableGaugeBatch, +]); + const attributes = { attribute: 1 }; counter.add(1, attributes); upDownCounter.add(-1, attributes); +gauge.record(1, attributes); histogram.record(1, attributes); -await meterProvider.forceFlush(); +const timer = setTimeout(() => {}, 100000); + +await Promise.all([ + observableCounterPromise.promise, + observableUpDownCounterPromise.promise, + observableGaugePromise.promise, + observableBatchPromise.promise, +]); + +clearTimeout(timer); diff --git a/tests/specs/compile/case_insensitive_building/__test__.jsonc b/tests/specs/compile/case_insensitive_building/__test__.jsonc new file mode 100644 index 0000000000..38636dc273 --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/__test__.jsonc @@ -0,0 +1,24 @@ +{ + "tempDir": true, + "steps": [{ + "if": "mac", + "args": "compile --output main --include file.txt --include FILE.txt main.js", + "output": "compile.out" + }, { + "if": "mac", + "commandName": "./main", + "args": [], + "output": "main.out", + "exitCode": 0 + }, { + "if": "windows", + "args": "compile --output main.exe --include file.txt --include FILE.txt main.js", + "output": "compile.out" + }, { + "if": "windows", + "commandName": "./main.exe", + "args": [], + "output": "main.out", + "exitCode": 0 + }] +} diff --git a/tests/specs/compile/case_insensitive_building/compile.out b/tests/specs/compile/case_insensitive_building/compile.out new file mode 100644 index 0000000000..895c2f1d46 --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/compile.out @@ -0,0 +1,10 @@ +Compile file:///[WILDLINE]/main.js to main[WILDLINE] + +Embedded Files + +main[WILDLINE] +├── file.txt ([WILDLINE]) +└── main.js ([WILDLINE]) + +Size: [WILDLINE] + diff --git a/tests/specs/compile/case_insensitive_building/file.txt b/tests/specs/compile/case_insensitive_building/file.txt new file mode 100644 index 0000000000..40816a2b5a --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/file.txt @@ -0,0 +1 @@ +Hi \ No newline at end of file diff --git a/tests/specs/compile/case_insensitive_building/main.js b/tests/specs/compile/case_insensitive_building/main.js new file mode 100644 index 0000000000..0fafe8a4dd --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/main.js @@ -0,0 +1 @@ +console.log(Deno.readTextFileSync(import.meta.dirname + "/file.txt")); diff --git a/tests/specs/compile/case_insensitive_building/main.out b/tests/specs/compile/case_insensitive_building/main.out new file mode 100644 index 0000000000..b14df6442e --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/main.out @@ -0,0 +1 @@ +Hi diff --git a/tests/specs/compile/code_cache/__test__.jsonc b/tests/specs/compile/code_cache/__test__.jsonc index 72353e27da..f1c3461adc 100644 --- a/tests/specs/compile/code_cache/__test__.jsonc +++ b/tests/specs/compile/code_cache/__test__.jsonc @@ -1,6 +1,9 @@ { "tempDir": true, "steps": [{ + "args": "run -A cleanup.ts", + "output": "[WILDCARD]" + }, { "if": "unix", "args": "compile --output using_code_cache --log-level=debug main.ts", "output": "[WILDCARD]" diff --git a/tests/specs/compile/code_cache/cleanup.ts b/tests/specs/compile/code_cache/cleanup.ts new file mode 100644 index 0000000000..d9e7c805f8 --- /dev/null +++ b/tests/specs/compile/code_cache/cleanup.ts @@ -0,0 +1,11 @@ +import { tmpdir } from "node:os"; + +// cleanup the code cache file from a previous run +try { + if (Deno.build.os === "windows") { + Deno.removeSync(tmpdir() + "\\deno-compile-using_code_cache.exe.cache"); + } else { + Deno.removeSync(tmpdir() + "\\deno-compile-using_code_cache.cache"); + } +} catch { +} diff --git a/tests/specs/compile/determinism/__test__.jsonc b/tests/specs/compile/determinism/__test__.jsonc index 97045744f1..b84a1fdf18 100644 --- a/tests/specs/compile/determinism/__test__.jsonc +++ b/tests/specs/compile/determinism/__test__.jsonc @@ -1,28 +1,31 @@ { "tempDir": true, "steps": [{ - "if": "unix", - "args": "compile --output main1 main.ts", + "args": "run -A setup.ts", "output": "[WILDCARD]" }, { "if": "unix", - "args": "compile --output main2 main.ts", + "args": "compile --no-config --output a/main a/main.ts", "output": "[WILDCARD]" }, { "if": "unix", - "args": "run --allow-read=. assert_equal.ts main1 main2", + "args": "compile --no-config --output b/main b/main.ts", + "output": "[WILDCARD]" + }, { + "if": "unix", + "args": "run --allow-read=. assert_equal.ts a/main b/main", "output": "Same\n" }, { "if": "windows", - "args": "compile --output main1.exe main.ts", + "args": "compile --no-config --output a/main.exe a/main.ts", "output": "[WILDCARD]" }, { "if": "windows", - "args": "compile --output main2.exe main.ts", + "args": "compile --no-config --output b/main.exe b/main.ts", "output": "[WILDCARD]" }, { "if": "windows", - "args": "run --allow-read=. assert_equal.ts main1.exe main2.exe", + "args": "run --allow-read=. assert_equal.ts a/main.exe b/main.exe", "output": "Same\n" }] } diff --git a/tests/specs/compile/determinism/setup.ts b/tests/specs/compile/determinism/setup.ts new file mode 100644 index 0000000000..8bb5753079 --- /dev/null +++ b/tests/specs/compile/determinism/setup.ts @@ -0,0 +1,10 @@ +// for setup, we create two directories with the same file in each +// and then when compiling we ensure this directory name has no +// effect on the output +makeCopyDir("a"); +makeCopyDir("b"); + +function makeCopyDir(dirName) { + Deno.mkdirSync(dirName); + Deno.copyFileSync("main.ts", `${dirName}/main.ts`); +} diff --git a/tests/specs/compile/env_vars_support/compile.out b/tests/specs/compile/env_vars_support/compile.out index 02332ea0eb..cba114b1f7 100644 --- a/tests/specs/compile/env_vars_support/compile.out +++ b/tests/specs/compile/env_vars_support/compile.out @@ -3,8 +3,10 @@ Check [WILDCARD]main.ts Compile [WILDCARD]main.ts to out[WILDCARD] Warning Environment variables from the file "environment.env" were embedded in the generated executable file -Embedded File System +Embedded Files out[WILDLINE] -└── main.ts +└── main.ts ([WILDLINE]) + +Size: [WILDLINE] diff --git a/tests/specs/compile/error/local/__test__.jsonc b/tests/specs/compile/error/local/__test__.jsonc new file mode 100644 index 0000000000..8d6a015a51 --- /dev/null +++ b/tests/specs/compile/error/local/__test__.jsonc @@ -0,0 +1,24 @@ +{ + "tempDir": true, + "steps": [{ + "if": "unix", + "args": "compile --output main standalone_error.ts", + "output": "[WILDCARD]" + }, { + "if": "unix", + "commandName": "./main", + "args": [], + "output": "output.out", + "exitCode": 1 + }, { + "if": "windows", + "args": "compile --output main.exe standalone_error.ts", + "output": "[WILDCARD]" + }, { + "if": "windows", + "commandName": "./main.exe", + "args": [], + "output": "output.out", + "exitCode": 1 + }] +} diff --git a/tests/specs/compile/error/local/output.out b/tests/specs/compile/error/local/output.out new file mode 100644 index 0000000000..b346734ae6 --- /dev/null +++ b/tests/specs/compile/error/local/output.out @@ -0,0 +1,6 @@ +error: Uncaught (in promise) Error: boom! + throw new Error("boom!"); + ^ + at boom (file:///[WILDLINE]standalone_error.ts:2:9) + at foo (file:///[WILDLINE]standalone_error.ts:6:3) + at file:///[WILDLINE]standalone_error.ts:9:1 diff --git a/tests/testdata/compile/standalone_error.ts b/tests/specs/compile/error/local/standalone_error.ts similarity index 100% rename from tests/testdata/compile/standalone_error.ts rename to tests/specs/compile/error/local/standalone_error.ts diff --git a/tests/specs/compile/error/remote/__test__.jsonc b/tests/specs/compile/error/remote/__test__.jsonc new file mode 100644 index 0000000000..9ad9091ec6 --- /dev/null +++ b/tests/specs/compile/error/remote/__test__.jsonc @@ -0,0 +1,24 @@ +{ + "tempDir": true, + "steps": [{ + "if": "unix", + "args": "compile -A --output main main.ts", + "output": "[WILDCARD]" + }, { + "if": "unix", + "commandName": "./main", + "args": [], + "output": "output.out", + "exitCode": 1 + }, { + "if": "windows", + "args": "compile -A --output main.exe main.ts", + "output": "[WILDCARD]" + }, { + "if": "windows", + "commandName": "./main.exe", + "args": [], + "output": "output.out", + "exitCode": 1 + }] +} diff --git a/tests/specs/compile/error/remote/main.ts b/tests/specs/compile/error/remote/main.ts new file mode 100644 index 0000000000..7a27276dd8 --- /dev/null +++ b/tests/specs/compile/error/remote/main.ts @@ -0,0 +1 @@ +import "http://localhost:4545/compile/standalone_error_module_with_imports_1.ts"; diff --git a/tests/specs/compile/error/remote/output.out b/tests/specs/compile/error/remote/output.out new file mode 100644 index 0000000000..3e23694c16 --- /dev/null +++ b/tests/specs/compile/error/remote/output.out @@ -0,0 +1,5 @@ +hello +error: Uncaught (in promise) Error: boom! +throw new Error(value); + ^ + at http://localhost:4545/compile/standalone_error_module_with_imports_2.ts:7:7 diff --git a/tests/specs/compile/global_npm_cache_implicit_read_permission/compile.out b/tests/specs/compile/global_npm_cache_implicit_read_permission/compile.out index c29c878593..fa1dd2bf9f 100644 --- a/tests/specs/compile/global_npm_cache_implicit_read_permission/compile.out +++ b/tests/specs/compile/global_npm_cache_implicit_read_permission/compile.out @@ -1,47 +1,49 @@ [WILDCARD] Compile file:///[WILDLINE]/main.ts to [WILDLINE] -Embedded File System +Embedded Files main[WILDLINE] -├─┬ .deno_compile_node_modules -│ └─┬ localhost -│ ├─┬ ansi-regex -│ │ ├── 3.0.1/* -│ │ └── 5.0.1/* -│ ├── ansi-styles/4.3.0/* -│ ├── camelcase/5.3.1/* -│ ├── cliui/6.0.0/* -│ ├── color-convert/2.0.1/* -│ ├── color-name/1.1.4/* -│ ├── cowsay/1.5.0/* -│ ├── decamelize/1.2.0/* -│ ├── emoji-regex/8.0.0/* -│ ├── find-up/4.1.0/* -│ ├── get-caller-file/2.0.5/* -│ ├── get-stdin/8.0.0/* -│ ├─┬ is-fullwidth-code-point -│ │ ├── 2.0.0/* -│ │ └── 3.0.0/* -│ ├── locate-path/5.0.0/* -│ ├── p-limit/2.3.0/* -│ ├── p-locate/4.1.0/* -│ ├── p-try/2.2.0/* -│ ├── path-exists/4.0.0/* -│ ├── require-directory/2.1.1/* -│ ├── require-main-filename/2.0.0/* -│ ├── set-blocking/2.0.0/* -│ ├─┬ string-width -│ │ ├── 2.1.1/* -│ │ └── 4.2.3/* -│ ├─┬ strip-ansi -│ │ ├── 4.0.0/* -│ │ └── 6.0.1/* -│ ├── strip-final-newline/2.0.0/* -│ ├── which-module/2.0.0/* -│ ├── wrap-ansi/6.2.0/* -│ ├── y18n/4.0.3/* -│ ├── yargs/15.4.1/* -│ └── yargs-parser/18.1.3/* -└── main.ts +├─┬ .deno_compile_node_modules ([WILDLINE]) +│ └─┬ localhost ([WILDLINE]) +│ ├─┬ ansi-regex ([WILDLINE]) +│ │ ├── 3.0.1/* ([WILDLINE]) +│ │ └── 5.0.1/* ([WILDLINE]) +│ ├── ansi-styles/4.3.0/* ([WILDLINE]) +│ ├── camelcase/5.3.1/* ([WILDLINE]) +│ ├── cliui/6.0.0/* ([WILDLINE]) +│ ├── color-convert/2.0.1/* ([WILDLINE]) +│ ├── color-name/1.1.4/* ([WILDLINE]) +│ ├── cowsay/1.5.0/* ([WILDLINE]) +│ ├── decamelize/1.2.0/* ([WILDLINE]) +│ ├── emoji-regex/8.0.0/* ([WILDLINE]) +│ ├── find-up/4.1.0/* ([WILDLINE]) +│ ├── get-caller-file/2.0.5/* ([WILDLINE]) +│ ├── get-stdin/8.0.0/* ([WILDLINE]) +│ ├─┬ is-fullwidth-code-point ([WILDLINE]) +│ │ ├── 2.0.0/* ([WILDLINE]) +│ │ └── 3.0.0/* ([WILDLINE]) +│ ├── locate-path/5.0.0/* ([WILDLINE]) +│ ├── p-limit/2.3.0/* ([WILDLINE]) +│ ├── p-locate/4.1.0/* ([WILDLINE]) +│ ├── p-try/2.2.0/* ([WILDLINE]) +│ ├── path-exists/4.0.0/* ([WILDLINE]) +│ ├── require-directory/2.1.1/* ([WILDLINE]) +│ ├── require-main-filename/2.0.0/* ([WILDLINE]) +│ ├── set-blocking/2.0.0/* ([WILDLINE]) +│ ├─┬ string-width ([WILDLINE]) +│ │ ├── 2.1.1/* ([WILDLINE]) +│ │ └── 4.2.3/* ([WILDLINE]) +│ ├─┬ strip-ansi ([WILDLINE]) +│ │ ├── 4.0.0/* ([WILDLINE]) +│ │ └── 6.0.1/* ([WILDLINE]) +│ ├── strip-final-newline/2.0.0/* ([WILDLINE]) +│ ├── which-module/2.0.0/* ([WILDLINE]) +│ ├── wrap-ansi/6.2.0/* ([WILDLINE]) +│ ├── y18n/4.0.3/* ([WILDLINE]) +│ ├── yargs/15.4.1/* ([WILDLINE]) +│ └── yargs-parser/18.1.3/* ([WILDLINE]) +└── main.ts ([WILDLINE]) + +Size: [WILDLINE] diff --git a/tests/specs/compile/include/symlink_twice/compile.out b/tests/specs/compile/include/symlink_twice/compile.out index c57eb9b2f1..6ae93bf1cb 100644 --- a/tests/specs/compile/include/symlink_twice/compile.out +++ b/tests/specs/compile/include/symlink_twice/compile.out @@ -1,9 +1,11 @@ Compile [WILDLINE] -Embedded File System +Embedded Files main[WILDLINE] -├── index.js +├── index.js ([WILDLINE]) ├── link.js --> index.js -└── setup.js +└── setup.js ([WILDLINE]) + +Size: [WILDLINE] diff --git a/tests/specs/compile/npm_fs/compile.out b/tests/specs/compile/npm_fs/compile.out index 4944146788..c2ecef4015 100644 --- a/tests/specs/compile/npm_fs/compile.out +++ b/tests/specs/compile/npm_fs/compile.out @@ -1,8 +1,10 @@ [WILDCARD] -Embedded File System +Embedded Files main[WILDLINE] -├── main.ts -└── node_modules/* +├── main.ts ([WILDLINE]) +└── node_modules/* ([WILDLINE]) + +Size: [WILDLINE] diff --git a/tests/specs/compile/package_json_type/compile.out b/tests/specs/compile/package_json_type/compile.out index 0370192809..c03e63b71a 100644 --- a/tests/specs/compile/package_json_type/compile.out +++ b/tests/specs/compile/package_json_type/compile.out @@ -1,6 +1,6 @@ Check file:///[WILDLINE]/main.js Compile file:///[WILDLINE] -Embedded File System +Embedded Files [WILDCARD] diff --git a/tests/specs/info/import_map/__test__.jsonc b/tests/specs/info/import_map/__test__.jsonc index 7aba603e0b..725276925e 100644 --- a/tests/specs/info/import_map/__test__.jsonc +++ b/tests/specs/info/import_map/__test__.jsonc @@ -1,9 +1,5 @@ { - "steps": [ - { - "args": "info preact/debug", - "output": "with_import_map.out", - "exitCode": 0 - } - ] + "args": "info preact/debug", + "output": "with_import_map.out", + "exitCode": 0 } diff --git a/tests/specs/lint/with_malformed_config/with_malformed_config.out b/tests/specs/lint/with_malformed_config/with_malformed_config.out index 1c0f0fff6e..928ec02fb1 100644 --- a/tests/specs/lint/with_malformed_config/with_malformed_config.out +++ b/tests/specs/lint/with_malformed_config/with_malformed_config.out @@ -1,4 +1,4 @@ error: Failed to parse "lint" configuration Caused by: - unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report` + unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`, `plugins` diff --git a/tests/specs/lint/with_malformed_config2/with_malformed_config2.out b/tests/specs/lint/with_malformed_config2/with_malformed_config2.out index 1c0f0fff6e..928ec02fb1 100644 --- a/tests/specs/lint/with_malformed_config2/with_malformed_config2.out +++ b/tests/specs/lint/with_malformed_config2/with_malformed_config2.out @@ -1,4 +1,4 @@ error: Failed to parse "lint" configuration Caused by: - unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report` + unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`, `plugins` diff --git a/tests/specs/mod.rs b/tests/specs/mod.rs index 985a6c7c40..4da5a87d14 100644 --- a/tests/specs/mod.rs +++ b/tests/specs/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::BTreeMap; diff --git a/tests/specs/node/cjs_key_escaped_whitespace/__test__.jsonc b/tests/specs/node/cjs_key_escaped_whitespace/__test__.jsonc new file mode 100644 index 0000000000..ebaae5bfd6 --- /dev/null +++ b/tests/specs/node/cjs_key_escaped_whitespace/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "run -A main.js", + "output": "output.out" +} diff --git a/tests/specs/node/cjs_key_escaped_whitespace/main.js b/tests/specs/node/cjs_key_escaped_whitespace/main.js new file mode 100644 index 0000000000..9d4f2ee26c --- /dev/null +++ b/tests/specs/node/cjs_key_escaped_whitespace/main.js @@ -0,0 +1,2 @@ +const bang = await import("./module.cjs"); +console.log("imported:", bang); diff --git a/tests/specs/node/cjs_key_escaped_whitespace/module.cjs b/tests/specs/node/cjs_key_escaped_whitespace/module.cjs new file mode 100644 index 0000000000..5accc6196a --- /dev/null +++ b/tests/specs/node/cjs_key_escaped_whitespace/module.cjs @@ -0,0 +1,6 @@ +module.exports = { + "\nx": "test", + "\ty": "test", + "\rz": "test", + '"a': "test", +}; diff --git a/tests/specs/node/cjs_key_escaped_whitespace/output.out b/tests/specs/node/cjs_key_escaped_whitespace/output.out new file mode 100644 index 0000000000..49e92abdec --- /dev/null +++ b/tests/specs/node/cjs_key_escaped_whitespace/output.out @@ -0,0 +1,7 @@ +imported: [Module: null prototype] { + "\ty": "test", + "\nx": "test", + "\rz": "test", + '"a': "test", + default: { "\nx": "test", "\ty": "test", "\rz": "test", '"a': "test" } +} diff --git a/tests/specs/npm/npm_check_updates/__test__.jsonc b/tests/specs/npm/npm_check_updates/__test__.jsonc new file mode 100644 index 0000000000..27b84c5f05 --- /dev/null +++ b/tests/specs/npm/npm_check_updates/__test__.jsonc @@ -0,0 +1,7 @@ +{ + "tempDir": true, + "steps": [{ + "args": "run -A npm:npm-check-updates", + "output": "output.out" + }] +} diff --git a/tests/specs/npm/npm_check_updates/output.out b/tests/specs/npm/npm_check_updates/output.out new file mode 100644 index 0000000000..b8c1ae4957 --- /dev/null +++ b/tests/specs/npm/npm_check_updates/output.out @@ -0,0 +1,2 @@ +[WILDCARD] +All dependencies match the latest package versions[WILDCARD] \ No newline at end of file diff --git a/tests/specs/npm/npm_check_updates/package.json b/tests/specs/npm/npm_check_updates/package.json new file mode 100644 index 0000000000..82afc391cd --- /dev/null +++ b/tests/specs/npm/npm_check_updates/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "chalk": "^5.0.1" + } +} diff --git a/tests/specs/npm/npmrc_tarball_other_server/fail/main.out b/tests/specs/npm/npmrc_tarball_other_server/fail/main.out index 2c68dba54e..d49bc148ea 100644 --- a/tests/specs/npm/npmrc_tarball_other_server/fail/main.out +++ b/tests/specs/npm/npmrc_tarball_other_server/fail/main.out @@ -1,6 +1,6 @@ Download http://localhost:4261/@denotest%2ftarballs-privateserver2 Download http://localhost:4262/@denotest/tarballs-privateserver2/1.0.0.tgz -error: Failed caching npm package '@denotest/tarballs-privateserver2@1.0.0'. +error: Failed caching npm package '@denotest/tarballs-privateserver2@1.0.0' Caused by: No auth for tarball URI, but present for scoped registry. diff --git a/tests/specs/npm/npmrc_tarball_other_server/success/main.out b/tests/specs/npm/npmrc_tarball_other_server/success/main.out index 5322a1a17d..239f1d525b 100644 --- a/tests/specs/npm/npmrc_tarball_other_server/success/main.out +++ b/tests/specs/npm/npmrc_tarball_other_server/success/main.out @@ -4,7 +4,7 @@ Download http://localhost:4262/@denotest/tarballs-privateserver2/1.0.0.tgz [# to serve proper checksums for a package at another registry. That's fine] [# though because this shows us that we're making it to this step instead of] [# failing sooner on an auth issue.] -error: Failed caching npm package '@denotest/tarballs-privateserver2@1.0.0'. +error: Failed caching npm package '@denotest/tarballs-privateserver2@1.0.0' Caused by: Tarball checksum did not match [WILDCARD] diff --git a/tests/specs/permission/allow_import_cached_only/__test__.jsonc b/tests/specs/permission/allow_import_cached_only/__test__.jsonc new file mode 100644 index 0000000000..a86a8796c2 --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/__test__.jsonc @@ -0,0 +1,22 @@ +{ + "tempDir": true, + "tests": { + "no_flag": { + // ensure what we're testing will fail without the flags + "args": "run main.ts", + "output": "fail.out", + "exitCode": 1 + }, + "with_flags": { + "steps": [{ + "args": "cache --allow-import main.ts", + "output": "[WILDLINE]", + "exitCode": 0 + }, { + "args": "run --cached-only main.ts", + "output": "success.out", + "exitCode": 0 + }] + } + } +} diff --git a/tests/specs/permission/allow_import_cached_only/deno.jsonc b/tests/specs/permission/allow_import_cached_only/deno.jsonc new file mode 100644 index 0000000000..090481af96 --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/deno.jsonc @@ -0,0 +1,3 @@ +{ + "lock": true +} diff --git a/tests/specs/permission/allow_import_cached_only/fail.out b/tests/specs/permission/allow_import_cached_only/fail.out new file mode 100644 index 0000000000..517f53b9f4 --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/fail.out @@ -0,0 +1,2 @@ +error: Requires import access to "localhost:4545", run again with the --allow-import flag + at file:///[WILDLINE]/main.ts:1:8 diff --git a/tests/specs/permission/allow_import_cached_only/main.ts b/tests/specs/permission/allow_import_cached_only/main.ts new file mode 100644 index 0000000000..7556f22667 --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/main.ts @@ -0,0 +1 @@ +import "http://localhost:4545/welcome.ts"; diff --git a/tests/specs/permission/allow_import_cached_only/success.out b/tests/specs/permission/allow_import_cached_only/success.out new file mode 100644 index 0000000000..8432170eee --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/success.out @@ -0,0 +1 @@ +Welcome to Deno! diff --git a/tests/specs/permission/allow_import_info/__test__.jsonc b/tests/specs/permission/allow_import_info/__test__.jsonc new file mode 100644 index 0000000000..adcd0f2190 --- /dev/null +++ b/tests/specs/permission/allow_import_info/__test__.jsonc @@ -0,0 +1,22 @@ +{ + "envs": { + "JSR_URL": "" + }, + "tests": { + "implicit": { + "args": "info http://localhost:4545/welcome.ts", + "output": "success.out", + "exitCode": 0 + }, + "via_import_not_allowed": { + "args": "info main.ts", + "output": "import_not_allowed.out", + "exitCode": 0 + }, + "via_import_allowed": { + "args": "info --allow-import main.ts", + "output": "import_allowed.out", + "exitCode": 0 + } + } +} diff --git a/tests/specs/permission/allow_import_info/import_allowed.out b/tests/specs/permission/allow_import_info/import_allowed.out new file mode 100644 index 0000000000..95b61b27ef --- /dev/null +++ b/tests/specs/permission/allow_import_info/import_allowed.out @@ -0,0 +1,8 @@ +Download http://localhost:4545/welcome.ts +local: [WILDLINE] +type: TypeScript +dependencies: [WILDLINE] +size: [WILDLINE] + +file:///[WILDLINE]/main.ts ([WILDLINE]) +└── http://localhost:4545/welcome.ts ([WILDLINE]B) diff --git a/tests/specs/permission/allow_import_info/import_not_allowed.out b/tests/specs/permission/allow_import_info/import_not_allowed.out new file mode 100644 index 0000000000..c73eced93a --- /dev/null +++ b/tests/specs/permission/allow_import_info/import_not_allowed.out @@ -0,0 +1,7 @@ +local: [WILDLINE] +type: TypeScript +dependencies: [WILDLINE] +size: [WILDLINE] + +file:///[WILDLINE]/allow_import_info/main.ts ([WILDLINE]) +└── http://localhost:4545/welcome.ts (not capable, requires --allow-import) diff --git a/tests/specs/permission/allow_import_info/main.ts b/tests/specs/permission/allow_import_info/main.ts new file mode 100644 index 0000000000..7556f22667 --- /dev/null +++ b/tests/specs/permission/allow_import_info/main.ts @@ -0,0 +1 @@ +import "http://localhost:4545/welcome.ts"; diff --git a/tests/specs/permission/allow_import_info/success.out b/tests/specs/permission/allow_import_info/success.out new file mode 100644 index 0000000000..1b43d71444 --- /dev/null +++ b/tests/specs/permission/allow_import_info/success.out @@ -0,0 +1,7 @@ +Download http://localhost:4545/welcome.ts +local: [WILDLINE] +type: TypeScript +dependencies: [WILDLINE] +size: [WILDLINE] + +http://localhost:4545/welcome.ts ([WILDLINE]B) diff --git a/tests/specs/permission/allow_import_worker/denied.out b/tests/specs/permission/allow_import_worker/denied.out index 6e4dcaee09..af44ae21ee 100644 --- a/tests/specs/permission/allow_import_worker/denied.out +++ b/tests/specs/permission/allow_import_worker/denied.out @@ -3,5 +3,4 @@ await import(specifier); ^ at async file:///[WILDLINE] error: Uncaught (in promise) Error: Unhandled error in child worker. - at [WILDLINE] - at [WILDLINE] \ No newline at end of file + at [WILDCARD] \ No newline at end of file diff --git a/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out b/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out index 4eacbea655..8388e4751e 100644 --- a/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out +++ b/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out @@ -1,2 +1,3 @@ -error: [WILDCARD] Maybe specify path to 'index.ts' file in directory instead or run with --unstable-sloppy-imports - at file:///[WILDCARD]/mod.ts:1:20 +Check file:///[WILDLINE]/mod.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/b'. Maybe specify path to 'index.ts' file in directory instead or run with --unstable-sloppy-imports + at file:///[WILDLINE]/mod.ts:1:20 diff --git a/tests/specs/repl/console_log/093_console_log_format.js b/tests/specs/repl/console_log/093_console_log_format.js index 15022411ca..3ac9988023 100644 --- a/tests/specs/repl/console_log/093_console_log_format.js +++ b/tests/specs/repl/console_log/093_console_log_format.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. class Frac { constructor(num, den) { this.num = num; diff --git a/tests/specs/run/045_proxy/programmatic_proxy_client.ts b/tests/specs/run/045_proxy/programmatic_proxy_client.ts index 73af590c71..c238440755 100644 --- a/tests/specs/run/045_proxy/programmatic_proxy_client.ts +++ b/tests/specs/run/045_proxy/programmatic_proxy_client.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const client = Deno.createHttpClient({ proxy: { diff --git a/tests/specs/run/045_proxy/proxy_client.ts b/tests/specs/run/045_proxy/proxy_client.ts index 41deae2a5d..13fcc134ba 100644 --- a/tests/specs/run/045_proxy/proxy_client.ts +++ b/tests/specs/run/045_proxy/proxy_client.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const res = await fetch( "http://localhost:4545/run/045_mod.ts", ); diff --git a/tests/specs/run/045_proxy/proxy_test.ts b/tests/specs/run/045_proxy/proxy_test.ts index 22115a3aa8..04f4c9d06d 100644 --- a/tests/specs/run/045_proxy/proxy_test.ts +++ b/tests/specs/run/045_proxy/proxy_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const addr = Deno.args[1] || "localhost:4555"; function proxyServer() { diff --git a/tests/specs/run/finalization_registry/finalization_registry.js b/tests/specs/run/finalization_registry/finalization_registry.js index ee9dc384f5..76d68d6df5 100644 --- a/tests/specs/run/finalization_registry/finalization_registry.js +++ b/tests/specs/run/finalization_registry/finalization_registry.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. "use strict"; function assertEquals(a, b) { diff --git a/tests/specs/run/heapstats/heapstats.js b/tests/specs/run/heapstats/heapstats.js index b93c9c120d..c0dc0da81c 100644 --- a/tests/specs/run/heapstats/heapstats.js +++ b/tests/specs/run/heapstats/heapstats.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. "use strict"; function allocTest(alloc, allocAssert, deallocAssert) { diff --git a/tests/specs/run/jsx_import_source/__test__.jsonc b/tests/specs/run/jsx_import_source/__test__.jsonc index cbda2dd32e..0350df7f17 100644 --- a/tests/specs/run/jsx_import_source/__test__.jsonc +++ b/tests/specs/run/jsx_import_source/__test__.jsonc @@ -19,6 +19,7 @@ "output": "jsx_import_source_dev.out" }, "jsx_import_source_pragma_with_config_vendor_dir": { + "tempDir": true, "args": "run --allow-import --reload --config jsx/deno-jsx.jsonc --no-lock --vendor jsx_import_source_pragma.tsx", "output": "jsx_import_source.out" }, diff --git a/tests/specs/run/jsx_import_source/jsx_import_source_error.out b/tests/specs/run/jsx_import_source/jsx_import_source_error.out index 634a5b09ba..cb673c6bc9 100644 --- a/tests/specs/run/jsx_import_source/jsx_import_source_error.out +++ b/tests/specs/run/jsx_import_source/jsx_import_source_error.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDCARD]/nonexistent/jsx-runtime". - at file:///[WILDCARD]/jsx_import_source_no_pragma.tsx:1:1 +Check file:///[WILDLINE]/jsx_import_source_no_pragma.tsx +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDCARD]/nonexistent/jsx-runtime'. + at file:///[WILDLINE]/jsx_import_source_no_pragma.tsx:1:1 diff --git a/tests/specs/run/node_prefix_missing/__test__.jsonc b/tests/specs/run/node_prefix_missing/__test__.jsonc index 305020ed97..fa4504fdc2 100644 --- a/tests/specs/run/node_prefix_missing/__test__.jsonc +++ b/tests/specs/run/node_prefix_missing/__test__.jsonc @@ -5,6 +5,11 @@ "output": "main.ts.out", "exitCode": 1 }, + "basic_no_config": { + "args": "run --quiet --no-config main.ts", + "output": "main_no_config.out", + "exitCode": 1 + }, "unstable_bare_node_builtins_enabled": { "args": "run --unstable-bare-node-builtins main.ts", "output": "feature_enabled.out" diff --git a/tests/specs/run/node_prefix_missing/config.json b/tests/specs/run/node_prefix_missing/config.json index 72f40aaf36..52f5f4d4f4 100644 --- a/tests/specs/run/node_prefix_missing/config.json +++ b/tests/specs/run/node_prefix_missing/config.json @@ -1,3 +1,4 @@ { + "imports": {}, "unstable": ["bare-node-builtins"] } diff --git a/tests/specs/run/node_prefix_missing/deno.json b/tests/specs/run/node_prefix_missing/deno.json new file mode 100644 index 0000000000..f6ca8454c5 --- /dev/null +++ b/tests/specs/run/node_prefix_missing/deno.json @@ -0,0 +1,3 @@ +{ + "imports": {} +} diff --git a/tests/specs/run/node_prefix_missing/main.ts.out b/tests/specs/run/node_prefix_missing/main.ts.out index 48b4e37e27..c7067c6026 100644 --- a/tests/specs/run/node_prefix_missing/main.ts.out +++ b/tests/specs/run/node_prefix_missing/main.ts.out @@ -1,3 +1,3 @@ -error: Relative import path "fs" not prefixed with / or ./ or ../ +error: Relative import path "fs" not prefixed with / or ./ or ../ and not in import map from "[WILDLINE]/main.ts" hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs"). at file:///[WILDCARD]/main.ts:1:16 diff --git a/tests/specs/run/node_prefix_missing/main_no_config.out b/tests/specs/run/node_prefix_missing/main_no_config.out new file mode 100644 index 0000000000..48b4e37e27 --- /dev/null +++ b/tests/specs/run/node_prefix_missing/main_no_config.out @@ -0,0 +1,3 @@ +error: Relative import path "fs" not prefixed with / or ./ or ../ + hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs"). + at file:///[WILDCARD]/main.ts:1:16 diff --git a/tests/specs/run/reference_types_error/reference_types_error.js.out b/tests/specs/run/reference_types_error/reference_types_error.js.out index 86055f3ac3..3f22354915 100644 --- a/tests/specs/run/reference_types_error/reference_types_error.js.out +++ b/tests/specs/run/reference_types_error/reference_types_error.js.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDCARD]/nonexistent.d.ts". - at file:///[WILDCARD]/reference_types_error.js:1:22 +Check file:///[WILDLINE]/reference_types_error.js +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/nonexistent.d.ts'. + at file:///[WILDLINE]/reference_types_error.js:1:22 diff --git a/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out b/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out index 86055f3ac3..3f22354915 100644 --- a/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out +++ b/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDCARD]/nonexistent.d.ts". - at file:///[WILDCARD]/reference_types_error.js:1:22 +Check file:///[WILDLINE]/reference_types_error.js +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/nonexistent.d.ts'. + at file:///[WILDLINE]/reference_types_error.js:1:22 diff --git a/tests/specs/run/sloppy_imports/no_sloppy.out b/tests/specs/run/sloppy_imports/no_sloppy.out index d3a205e990..f28d9181ff 100644 --- a/tests/specs/run/sloppy_imports/no_sloppy.out +++ b/tests/specs/run/sloppy_imports/no_sloppy.out @@ -1,2 +1,26 @@ -error: Module not found "file:///[WILDCARD]/a.js". Maybe change the extension to '.ts' or run with --unstable-sloppy-imports +Check file:///[WILDLINE]/main.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/a.js'. Maybe change the extension to '.ts' or run with --unstable-sloppy-imports at file:///[WILDLINE]/main.ts:1:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/b'. Maybe add a '.js' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:2:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/c'. Maybe add a '.mts' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:3:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/d'. Maybe add a '.mjs' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:4:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/e'. Maybe add a '.tsx' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:5:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/e.js'. Maybe change the extension to '.tsx' or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:6:21 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/f'. Maybe add a '.jsx' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:7:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/dir'. Maybe specify path to 'index.tsx' file in directory instead or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:8:20 + +Found 8 errors. diff --git a/tests/specs/run/tls_connecttls/textproto.ts b/tests/specs/run/tls_connecttls/textproto.ts index 9e0f5f5f0a..6b8ac92ecb 100644 --- a/tests/specs/run/tls_connecttls/textproto.ts +++ b/tests/specs/run/tls_connecttls/textproto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/tests/specs/run/tls_starttls/textproto.ts b/tests/specs/run/tls_starttls/textproto.ts index 9e0f5f5f0a..6b8ac92ecb 100644 --- a/tests/specs/run/tls_starttls/textproto.ts +++ b/tests/specs/run/tls_starttls/textproto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc b/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc index a27fcfa82b..0141f9828c 100644 --- a/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc +++ b/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc @@ -1,5 +1,14 @@ { - "args": "--allow-import main.js", - "output": "main.out", - "exitCode": 1 + "tests": { + "run": { + "args": "--allow-import main.js", + "output": "main.out", + "exitCode": 1 + }, + "check": { + "args": "check --all --allow-import main.js", + "output": "check.out", + "exitCode": 1 + } + } } diff --git a/tests/specs/run/wasm_module/import_file_not_found/check.out b/tests/specs/run/wasm_module/import_file_not_found/check.out new file mode 100644 index 0000000000..59c052297c --- /dev/null +++ b/tests/specs/run/wasm_module/import_file_not_found/check.out @@ -0,0 +1,4 @@ +Download http://localhost:4545/wasm/math_with_import.wasm +Check file:///[WILDLINE]/main.js +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/local_math.ts'. + at http://localhost:4545/wasm/math_with_import.wasm:1:87 diff --git a/tests/specs/run/wasm_module/import_file_not_found/main.js b/tests/specs/run/wasm_module/import_file_not_found/main.js index 9ad66df35b..b55405bd31 100644 --- a/tests/specs/run/wasm_module/import_file_not_found/main.js +++ b/tests/specs/run/wasm_module/import_file_not_found/main.js @@ -1,3 +1,4 @@ +// @ts-check import { add, subtract, diff --git a/tests/specs/run/wasm_module/import_file_not_found/main.out b/tests/specs/run/wasm_module/import_file_not_found/main.out index 54343673f1..ed021b9a21 100644 --- a/tests/specs/run/wasm_module/import_file_not_found/main.out +++ b/tests/specs/run/wasm_module/import_file_not_found/main.out @@ -1,3 +1,3 @@ Download http://localhost:4545/wasm/math_with_import.wasm error: Module not found "file:///[WILDLINE]/local_math.ts". - at http://localhost:4545/wasm/math_with_import.wasm:1:8 + at http://localhost:4545/wasm/math_with_import.wasm:1:87 diff --git a/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc b/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc index a27fcfa82b..0141f9828c 100644 --- a/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc +++ b/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc @@ -1,5 +1,14 @@ { - "args": "--allow-import main.js", - "output": "main.out", - "exitCode": 1 + "tests": { + "run": { + "args": "--allow-import main.js", + "output": "main.out", + "exitCode": 1 + }, + "check": { + "args": "check --all --allow-import main.js", + "output": "check.out", + "exitCode": 1 + } + } } diff --git a/tests/specs/run/wasm_module/import_named_export_not_found/check.out b/tests/specs/run/wasm_module/import_named_export_not_found/check.out new file mode 100644 index 0000000000..d7cc2ea0fb --- /dev/null +++ b/tests/specs/run/wasm_module/import_named_export_not_found/check.out @@ -0,0 +1,9 @@ +Download http://localhost:4545/wasm/math_with_import.wasm +Check file:///[WILDLINE]/main.js +error: TS2305 [ERROR]: Module '"file:///[WILDLINE]/local_math.ts"' has no exported member '"add"'. + at http://localhost:4545/wasm/math_with_import.wasm:1:1 + +TS2305 [ERROR]: Module '"file:///[WILDLINE]/local_math.ts"' has no exported member '"subtract"'. + at http://localhost:4545/wasm/math_with_import.wasm:1:1 + +Found 2 errors. diff --git a/tests/specs/run/wasm_module/import_named_export_not_found/main.js b/tests/specs/run/wasm_module/import_named_export_not_found/main.js index 9ad66df35b..b55405bd31 100644 --- a/tests/specs/run/wasm_module/import_named_export_not_found/main.js +++ b/tests/specs/run/wasm_module/import_named_export_not_found/main.js @@ -1,3 +1,4 @@ +// @ts-check import { add, subtract, diff --git a/tests/specs/run/worker_close_in_wasm_reactions/worker_close_in_wasm_reactions.js b/tests/specs/run/worker_close_in_wasm_reactions/worker_close_in_wasm_reactions.js index 2f62707eff..38508c31c4 100644 --- a/tests/specs/run/worker_close_in_wasm_reactions/worker_close_in_wasm_reactions.js +++ b/tests/specs/run/worker_close_in_wasm_reactions/worker_close_in_wasm_reactions.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // https://github.com/denoland/deno/issues/12263 // Test for a panic that happens when a worker is closed in the reactions of a diff --git a/tests/specs/run/worker_close_nested/close_nested_child.js b/tests/specs/run/worker_close_nested/close_nested_child.js index 97980c689e..1f2b2091a6 100644 --- a/tests/specs/run/worker_close_nested/close_nested_child.js +++ b/tests/specs/run/worker_close_nested/close_nested_child.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Starting the child worker"); diff --git a/tests/specs/run/worker_close_nested/close_nested_parent.js b/tests/specs/run/worker_close_nested/close_nested_parent.js index d1fe47553e..ddb9aec26d 100644 --- a/tests/specs/run/worker_close_nested/close_nested_parent.js +++ b/tests/specs/run/worker_close_nested/close_nested_parent.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Starting the parent worker"); diff --git a/tests/specs/run/worker_close_nested/worker_close_nested.js b/tests/specs/run/worker_close_nested/worker_close_nested.js index 8d9c88d1cf..179eb48fa1 100644 --- a/tests/specs/run/worker_close_nested/worker_close_nested.js +++ b/tests/specs/run/worker_close_nested/worker_close_nested.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Test that closing a worker which has living child workers will automatically // close the children. diff --git a/tests/specs/run/worker_close_race/close_race_worker.js b/tests/specs/run/worker_close_race/close_race_worker.js index 6964be34a0..945fed9dd0 100644 --- a/tests/specs/run/worker_close_race/close_race_worker.js +++ b/tests/specs/run/worker_close_race/close_race_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. setTimeout(() => { self.postMessage(""); diff --git a/tests/specs/run/worker_close_race/worker_close_race.js b/tests/specs/run/worker_close_race/worker_close_race.js index 188cd9ed88..b39062df50 100644 --- a/tests/specs/run/worker_close_race/worker_close_race.js +++ b/tests/specs/run/worker_close_race/worker_close_race.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // https://github.com/denoland/deno/issues/11416 // Test for a race condition between a worker's `close()` and the main thread's diff --git a/tests/specs/run/worker_drop_handle_race/worker_drop_handle_race.js b/tests/specs/run/worker_drop_handle_race/worker_drop_handle_race.js index ef9bcbe072..bfacc332ac 100644 --- a/tests/specs/run/worker_drop_handle_race/worker_drop_handle_race.js +++ b/tests/specs/run/worker_drop_handle_race/worker_drop_handle_race.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // https://github.com/denoland/deno/issues/11342 // Test for a panic that happens when the main thread's event loop finishes diff --git a/tests/specs/run/worker_drop_handle_race_terminate/worker_drop_handle_race_terminate.js b/tests/specs/run/worker_drop_handle_race_terminate/worker_drop_handle_race_terminate.js index 7c4e0b1099..85a3c51072 100644 --- a/tests/specs/run/worker_drop_handle_race_terminate/worker_drop_handle_race_terminate.js +++ b/tests/specs/run/worker_drop_handle_race_terminate/worker_drop_handle_race_terminate.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Test that the panic in https://github.com/denoland/deno/issues/11342 does not // happen when calling worker.terminate() after fixing diff --git a/tests/specs/task/dependencies/__test__.jsonc b/tests/specs/task/dependencies/__test__.jsonc index 84c98f11a4..c9032153b3 100644 --- a/tests/specs/task/dependencies/__test__.jsonc +++ b/tests/specs/task/dependencies/__test__.jsonc @@ -61,6 +61,18 @@ "cwd": "arg_task_with_deps", "args": "task a a", "output": "./arg_task_with_deps.out" + }, + "no_command": { + "cwd": "no_command", + "args": "task a", + "output": "./no_command.out", + "exitCode": 0 + }, + "no_command_list": { + "cwd": "no_command", + "args": "task", + "output": "./no_command_list.out", + "exitCode": 0 } } } diff --git a/tests/specs/task/dependencies/no_command.out b/tests/specs/task/dependencies/no_command.out new file mode 100644 index 0000000000..521b3541df --- /dev/null +++ b/tests/specs/task/dependencies/no_command.out @@ -0,0 +1,5 @@ +Task b echo 'b' +b +Task c echo 'c' +c +Task a (no command) diff --git a/tests/specs/task/dependencies/no_command/deno.json b/tests/specs/task/dependencies/no_command/deno.json new file mode 100644 index 0000000000..5588365a92 --- /dev/null +++ b/tests/specs/task/dependencies/no_command/deno.json @@ -0,0 +1,13 @@ +{ + "tasks": { + "a": { + "dependencies": ["b", "c"] + }, + "b": { + "command": "echo 'b'" + }, + "c": { + "command": "echo 'c'" + } + } +} diff --git a/tests/specs/task/dependencies/no_command_list.out b/tests/specs/task/dependencies/no_command_list.out new file mode 100644 index 0000000000..3d58c1cb06 --- /dev/null +++ b/tests/specs/task/dependencies/no_command_list.out @@ -0,0 +1,7 @@ +Available tasks: +- a + depends on: b, c +- b + echo 'b' +- c + echo 'c' diff --git a/tests/specs/task/workspace_regex_match/__test__.jsonc b/tests/specs/task/workspace_regex_match/__test__.jsonc new file mode 100644 index 0000000000..258c288d44 --- /dev/null +++ b/tests/specs/task/workspace_regex_match/__test__.jsonc @@ -0,0 +1,11 @@ +{ + "tempDir": true, + "tests": { + // Regression test for https://github.com/denoland/deno/issues/27370 + "root": { + "args": "task test-all", + "output": "root.out", + "exitCode": 0 + } + } +} diff --git a/tests/specs/task/workspace_regex_match/deno.json b/tests/specs/task/workspace_regex_match/deno.json new file mode 100644 index 0000000000..ce040ba5ab --- /dev/null +++ b/tests/specs/task/workspace_regex_match/deno.json @@ -0,0 +1,6 @@ +{ + "workspace": ["./subdir"], + "tasks": { + "test-all": "deno task --recursive test" + } +} diff --git a/tests/specs/task/workspace_regex_match/root.out b/tests/specs/task/workspace_regex_match/root.out new file mode 100644 index 0000000000..9da724a5c0 --- /dev/null +++ b/tests/specs/task/workspace_regex_match/root.out @@ -0,0 +1,3 @@ +Task test-all deno task --recursive test +Task test echo 'ok' +ok diff --git a/tests/specs/task/workspace_regex_match/subdir/deno.json b/tests/specs/task/workspace_regex_match/subdir/deno.json new file mode 100644 index 0000000000..78d768e396 --- /dev/null +++ b/tests/specs/task/workspace_regex_match/subdir/deno.json @@ -0,0 +1,5 @@ +{ + "tasks": { + "test": "echo 'ok'" + } +} diff --git a/tests/specs/update/pre_release/__test__.jsonc b/tests/specs/update/pre_release/__test__.jsonc new file mode 100644 index 0000000000..7e2ea39dab --- /dev/null +++ b/tests/specs/update/pre_release/__test__.jsonc @@ -0,0 +1,21 @@ +{ + "tempDir": true, + "steps": [ + { + "args": "i", + "output": "[WILDCARD]" + }, + { + "args": "outdated", + "output": "outdated.out" + }, + { + "args": "outdated --compatible", + "output": "outdated.out" + }, + { + "args": "outdated --update --latest", + "output": "update.out" + } + ] +} diff --git a/tests/specs/update/pre_release/deno.json b/tests/specs/update/pre_release/deno.json new file mode 100644 index 0000000000..07646b5059 --- /dev/null +++ b/tests/specs/update/pre_release/deno.json @@ -0,0 +1,7 @@ +{ + "imports": { + "@denotest/npm-has-pre-release": "npm:@denotest/has-pre-release@^2.0.0-beta.1", + "@denotest/jsr-has-pre-release": "jsr:@denotest/has-pre-release@^2.0.0-beta.1", + "@denotest/has-only-pre-release": "jsr:@denotest/has-only-pre-release@^2.0.0-beta.1" + } +} diff --git a/tests/specs/update/pre_release/deno.lock b/tests/specs/update/pre_release/deno.lock new file mode 100644 index 0000000000..33b136dd53 --- /dev/null +++ b/tests/specs/update/pre_release/deno.lock @@ -0,0 +1,28 @@ +{ + "version": "4", + "specifiers": { + "jsr:@denotest/has-only-pre-release@^2.0.0-beta.1": "2.0.0-beta.1", + "jsr:@denotest/has-pre-release@^2.0.0-beta.1": "2.0.0-beta.1", + "npm:@denotest/has-pre-release@^2.0.0-beta.1": "2.0.0-beta.1" + }, + "jsr": { + "@denotest/has-only-pre-release@2.0.0-beta.1": { + "integrity": "43fd680ea94bb5db5fe1a2d86101c47d0e2cc77323b881755cea9a0372e49537" + }, + "@denotest/has-pre-release@2.0.0-beta.1": { + "integrity": "43fd680ea94bb5db5fe1a2d86101c47d0e2cc77323b881755cea9a0372e49537" + } + }, + "npm": { + "@denotest/has-pre-release@2.0.0-beta.1": { + "integrity": "sha512-K1fHe1L2EUSLgijtzzALNpkkIO0SrX3z+IXvVjjOIE8HKd4T7lkpzDdoUp+WllwS3KXmuJh+9vIfY5lFp38pew==" + } + }, + "workspace": { + "dependencies": [ + "jsr:@denotest/has-only-pre-release@^2.0.0-beta.1", + "jsr:@denotest/has-pre-release@^2.0.0-beta.1", + "npm:@denotest/has-pre-release@^2.0.0-beta.1" + ] + } +} diff --git a/tests/specs/update/pre_release/outdated.out b/tests/specs/update/pre_release/outdated.out new file mode 100644 index 0000000000..b8369b25f7 --- /dev/null +++ b/tests/specs/update/pre_release/outdated.out @@ -0,0 +1,11 @@ +┌────────────────────────────────────┬──────────────┬──────────────┬──────────────┐ +│ Package │ Current │ Update │ Latest │ +├────────────────────────────────────┼──────────────┼──────────────┼──────────────┤ +│ jsr:@denotest/has-only-pre-release │ 2.0.0-beta.1 │ 2.0.0-beta.2 │ 2.0.0-beta.2 │ +├────────────────────────────────────┼──────────────┼──────────────┼──────────────┤ +│ jsr:@denotest/has-pre-release │ 2.0.0-beta.1 │ 2.0.0-beta.2 │ 2.0.0-beta.2 │ +├────────────────────────────────────┼──────────────┼──────────────┼──────────────┤ +│ npm:@denotest/has-pre-release │ 2.0.0-beta.1 │ 2.0.0-beta.2 │ 2.0.0-beta.2 │ +└────────────────────────────────────┴──────────────┴──────────────┴──────────────┘ + +[WILDCARD] diff --git a/tests/specs/update/pre_release/update.out b/tests/specs/update/pre_release/update.out new file mode 100644 index 0000000000..d019457f0a --- /dev/null +++ b/tests/specs/update/pre_release/update.out @@ -0,0 +1,5 @@ +[WILDCARD] +Updated 3 dependencies: + - jsr:@denotest/has-only-pre-release 2.0.0-beta.1 -> 2.0.0-beta.2 + - jsr:@denotest/has-pre-release 2.0.0-beta.1 -> 2.0.0-beta.2 + - npm:@denotest/has-pre-release 2.0.0-beta.1 -> 2.0.0-beta.2 diff --git a/tests/testdata/check/import_non_existent.ts b/tests/testdata/check/import_non_existent.ts new file mode 100644 index 0000000000..ae511bca8a --- /dev/null +++ b/tests/testdata/check/import_non_existent.ts @@ -0,0 +1,5 @@ +import { Test } from "./non-existent-module.ts"; + +console.log(Test); + +export class Other {} diff --git a/tests/testdata/commonjs/example.js b/tests/testdata/commonjs/example.js index d2f89d3f02..0feedb12d4 100644 --- a/tests/testdata/commonjs/example.js +++ b/tests/testdata/commonjs/example.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore no-undef const processMod = require("process"); const osMod = require("node:os"); diff --git a/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out b/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out index 633c2cca62..a96f1f71e1 100644 --- a/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out +++ b/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out @@ -1,5 +1,5 @@ Compile file:///[WILDCARD]/node_modules_symlink_outside/main.ts to [WILDCARD] -Embedded File System +Embedded Files [WILDCARD] diff --git a/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out b/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out index 61f0a2456a..538aaa414c 100644 --- a/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out +++ b/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out @@ -4,12 +4,14 @@ Initialize @denotest/esm-basic@1.0.0 Check file:///[WILDCARD]/node_modules_symlink_outside/main.ts Compile file:///[WILDCARD]/node_modules_symlink_outside/main.ts to [WILDLINE] -Embedded File System +Embedded Files bin[WILDLINE] -├─┬ compile -│ └─┬ node_modules_symlink_outside -│ ├── main.ts -│ └── node_modules/* -└── some_folder/* +├─┬ compile ([WILDLINE]) +│ └─┬ node_modules_symlink_outside ([WILDLINE]) +│ ├── main.ts ([WILDLINE]) +│ └── node_modules/* ([WILDLINE]) +└── some_folder/* ([WILDLINE]) + +Size: [WILDLINE] diff --git a/tests/testdata/compile/standalone_error_module_with_imports_2.ts b/tests/testdata/compile/standalone_error_module_with_imports_2.ts index ef052b512e..c83d7ceea6 100644 --- a/tests/testdata/compile/standalone_error_module_with_imports_2.ts +++ b/tests/testdata/compile/standalone_error_module_with_imports_2.ts @@ -1,2 +1,7 @@ +// file has blank lines to make the input line +// different than the output console.log("hello"); -throw new Error("boom!"); + +const value: string = "boom!"; + +throw new Error(value); diff --git a/tests/testdata/run/textproto.ts b/tests/testdata/run/textproto.ts index 9e0f5f5f0a..6b8ac92ecb 100644 --- a/tests/testdata/run/textproto.ts +++ b/tests/testdata/run/textproto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/tests/testdata/subdir/imports_declaration/imports_interface.ts b/tests/testdata/subdir/imports_declaration/imports_interface.ts new file mode 100644 index 0000000000..5eb2e64d51 --- /dev/null +++ b/tests/testdata/subdir/imports_declaration/imports_interface.ts @@ -0,0 +1,3 @@ +import type { SomeInterface } from "./interface.d.ts"; + +export const someObject: SomeInterface = { someField: "someValue" }; diff --git a/tests/testdata/subdir/imports_declaration/interface.d.ts b/tests/testdata/subdir/imports_declaration/interface.d.ts new file mode 100644 index 0000000000..e1531905b9 --- /dev/null +++ b/tests/testdata/subdir/imports_declaration/interface.d.ts @@ -0,0 +1,3 @@ +export interface SomeInterface { + someField: string; +} diff --git a/tests/testdata/workers/close_nested_child.js b/tests/testdata/workers/close_nested_child.js index 97980c689e..1f2b2091a6 100644 --- a/tests/testdata/workers/close_nested_child.js +++ b/tests/testdata/workers/close_nested_child.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Starting the child worker"); diff --git a/tests/testdata/workers/close_nested_parent.js b/tests/testdata/workers/close_nested_parent.js index d1fe47553e..ddb9aec26d 100644 --- a/tests/testdata/workers/close_nested_parent.js +++ b/tests/testdata/workers/close_nested_parent.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Starting the parent worker"); diff --git a/tests/testdata/workers/close_race_worker.js b/tests/testdata/workers/close_race_worker.js index 6964be34a0..945fed9dd0 100644 --- a/tests/testdata/workers/close_race_worker.js +++ b/tests/testdata/workers/close_race_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. setTimeout(() => { self.postMessage(""); diff --git a/tests/testdata/workers/http_worker.js b/tests/testdata/workers/http_worker.js index 27bc9c038c..fd11641feb 100644 --- a/tests/testdata/workers/http_worker.js +++ b/tests/testdata/workers/http_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-deprecated-deno-api diff --git a/tests/unit/__snapshots__/lint_plugin_test.ts.snap b/tests/unit/__snapshots__/lint_plugin_test.ts.snap new file mode 100644 index 0000000000..337fcecc8f --- /dev/null +++ b/tests/unit/__snapshots__/lint_plugin_test.ts.snap @@ -0,0 +1,8608 @@ +export const snapshot = {}; + +snapshot[`Plugin - Program 1`] = ` +{ + body: [], + range: [ + 1, + 1, + ], + sourceType: "script", + type: "Program", +} +`; + +snapshot[`Plugin - ImportDeclaration 1`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 14, + ], + source: { + range: [ + 8, + 13, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 2`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 23, + ], + source: { + range: [ + 17, + 22, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + local: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 11, + ], + type: "ImportDefaultSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 3`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 28, + ], + source: { + range: [ + 22, + 27, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + local: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 16, + ], + type: "ImportNamespaceSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 4`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 39, + ], + source: { + range: [ + 33, + 38, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + importKind: "value", + imported: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + type: "ImportSpecifier", + }, + { + importKind: "value", + imported: { + name: "bar", + optional: false, + range: [ + 15, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "baz", + optional: false, + range: [ + 22, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 15, + 25, + ], + type: "ImportSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 5`] = ` +{ + attributes: [ + { + key: { + name: "type", + optional: false, + range: [ + 30, + 34, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 30, + 42, + ], + type: "ImportAttribute", + value: { + range: [ + 36, + 42, + ], + raw: '"json"', + type: "Literal", + value: "json", + }, + }, + ], + importKind: "value", + range: [ + 1, + 45, + ], + source: { + range: [ + 17, + 22, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + local: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 11, + ], + type: "ImportDefaultSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ExportNamedDeclaration 1`] = ` +{ + attributes: [], + range: [ + 1, + 27, + ], + source: { + range: [ + 21, + 26, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + exportKind: "value", + exported: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + type: "ExportSpecifier", + }, + ], + type: "ExportNamedDeclaration", +} +`; + +snapshot[`Plugin - ExportNamedDeclaration 2`] = ` +{ + attributes: [], + range: [ + 1, + 34, + ], + source: { + range: [ + 28, + 33, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + exportKind: "value", + exported: { + name: "baz", + optional: false, + range: [ + 17, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "bar", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 20, + ], + type: "ExportSpecifier", + }, + ], + type: "ExportNamedDeclaration", +} +`; + +snapshot[`Plugin - ExportNamedDeclaration 3`] = ` +{ + attributes: [ + { + key: { + name: "type", + optional: false, + range: [ + 34, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 34, + 46, + ], + type: "ImportAttribute", + value: { + range: [ + 40, + 46, + ], + raw: '"json"', + type: "Literal", + value: "json", + }, + }, + ], + range: [ + 1, + 49, + ], + source: { + range: [ + 21, + 26, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + exportKind: "value", + exported: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + type: "ExportSpecifier", + }, + ], + type: "ExportNamedDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 1`] = ` +{ + declaration: { + async: false, + body: { + body: [], + range: [ + 31, + 33, + ], + type: "BlockStatement", + }, + declare: false, + generator: false, + id: { + name: "foo", + optional: false, + range: [ + 25, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + params: [], + range: [ + 16, + 33, + ], + returnType: null, + type: "FunctionDeclaration", + typeParameters: null, + }, + exportKind: "value", + range: [ + 1, + 33, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 2`] = ` +{ + declaration: { + async: false, + body: { + body: [], + range: [ + 28, + 30, + ], + type: "BlockStatement", + }, + declare: false, + generator: false, + id: null, + params: [], + range: [ + 16, + 30, + ], + returnType: null, + type: "FunctionDeclaration", + typeParameters: null, + }, + exportKind: "value", + range: [ + 1, + 30, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 3`] = ` +{ + declaration: { + abstract: false, + body: { + body: [], + range: [ + 16, + 28, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 22, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 16, + 28, + ], + superClass: null, + type: "ClassDeclaration", + }, + exportKind: "value", + range: [ + 1, + 28, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 4`] = ` +{ + declaration: { + abstract: false, + body: { + body: [], + range: [ + 16, + 24, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 16, + 24, + ], + superClass: null, + type: "ClassDeclaration", + }, + exportKind: "value", + range: [ + 1, + 24, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 5`] = ` +{ + declaration: { + name: "bar", + optional: false, + range: [ + 16, + 19, + ], + type: "Identifier", + typeAnnotation: null, + }, + exportKind: "value", + range: [ + 1, + 20, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 6`] = ` +{ + declaration: { + body: { + body: [], + range: [ + 30, + 32, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "Foo", + optional: false, + range: [ + 26, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 16, + 32, + ], + type: "TSInterfaceDeclaration", + typeParameters: [], + }, + exportKind: "type", + range: [ + 1, + 32, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportAllDeclaration 1`] = ` +{ + attributes: [], + exportKind: "value", + exported: null, + range: [ + 1, + 21, + ], + source: { + range: [ + 15, + 20, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + type: "ExportAllDeclaration", +} +`; + +snapshot[`Plugin - ExportAllDeclaration 2`] = ` +{ + attributes: [], + exportKind: "value", + exported: { + range: [ + 22, + 27, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 1, + 28, + ], + source: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ExportAllDeclaration", +} +`; + +snapshot[`Plugin - ExportAllDeclaration 3`] = ` +{ + attributes: [ + { + key: { + name: "type", + optional: false, + range: [ + 28, + 32, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 28, + 40, + ], + type: "ImportAttribute", + value: { + range: [ + 34, + 40, + ], + raw: '"json"', + type: "Literal", + value: "json", + }, + }, + ], + exportKind: "value", + exported: null, + range: [ + 1, + 43, + ], + source: { + range: [ + 15, + 20, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + type: "ExportAllDeclaration", +} +`; + +snapshot[`Plugin - TSExportAssignment 1`] = ` +{ + expression: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 14, + ], + type: "TSExportAssignment", +} +`; + +snapshot[`Plugin - TSNamespaceExportDeclaration 1`] = ` +{ + id: { + name: "A", + optional: false, + range: [ + 21, + 22, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 23, + ], + type: "TSNamespaceExportDeclaration", +} +`; + +snapshot[`Plugin - TSImportEqualsDeclaration 1`] = ` +{ + id: { + name: "a", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + importKind: "value", + moduleReference: { + name: "b", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 13, + ], + type: "TSImportEqualsDeclaration", +} +`; + +snapshot[`Plugin - TSImportEqualsDeclaration 2`] = ` +{ + id: { + name: "a", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + importKind: "value", + moduleReference: { + expression: { + range: [ + 20, + 25, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 12, + 26, + ], + type: "TSExternalModuleReference", + }, + range: [ + 1, + 26, + ], + type: "TSImportEqualsDeclaration", +} +`; + +snapshot[`Plugin - BlockStatement 1`] = ` +{ + body: [ + { + expression: { + name: "foo", + optional: false, + range: [ + 3, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 3, + 7, + ], + type: "ExpressionStatement", + }, + ], + range: [ + 1, + 9, + ], + type: "BlockStatement", +} +`; + +snapshot[`Plugin - BreakStatement 1`] = ` +{ + label: null, + range: [ + 15, + 21, + ], + type: "BreakStatement", +} +`; + +snapshot[`Plugin - BreakStatement 2`] = ` +{ + label: { + name: "foo", + optional: false, + range: [ + 26, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 20, + 30, + ], + type: "BreakStatement", +} +`; + +snapshot[`Plugin - ContinueStatement 1`] = ` +{ + label: null, + range: [ + 1, + 10, + ], + type: "ContinueStatement", +} +`; + +snapshot[`Plugin - ContinueStatement 2`] = ` +{ + label: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 14, + ], + type: "ContinueStatement", +} +`; + +snapshot[`Plugin - DebuggerStatement 1`] = ` +{ + range: [ + 1, + 10, + ], + type: "DebuggerStatement", +} +`; + +snapshot[`Plugin - DoWhileStatement 1`] = ` +{ + body: { + body: [], + range: [ + 4, + 6, + ], + type: "BlockStatement", + }, + range: [ + 1, + 19, + ], + test: { + name: "foo", + optional: false, + range: [ + 14, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "DoWhileStatement", +} +`; + +snapshot[`Plugin - ExpressionStatement 1`] = ` +{ + expression: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 5, + ], + type: "ExpressionStatement", +} +`; + +snapshot[`Plugin - ForInStatement 1`] = ` +{ + body: { + body: [], + range: [ + 14, + 16, + ], + type: "BlockStatement", + }, + left: { + name: "a", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 16, + ], + right: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForInStatement", +} +`; + +snapshot[`Plugin - ForOfStatement 1`] = ` +{ + await: false, + body: { + body: [], + range: [ + 14, + 16, + ], + type: "BlockStatement", + }, + left: { + name: "a", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 16, + ], + right: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForOfStatement", +} +`; + +snapshot[`Plugin - ForOfStatement 2`] = ` +{ + await: true, + body: { + body: [], + range: [ + 20, + 22, + ], + type: "BlockStatement", + }, + left: { + name: "a", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 22, + ], + right: { + name: "b", + optional: false, + range: [ + 17, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForOfStatement", +} +`; + +snapshot[`Plugin - ForStatement 1`] = ` +{ + body: { + body: [], + range: [ + 10, + 12, + ], + type: "BlockStatement", + }, + init: null, + range: [ + 1, + 12, + ], + test: null, + type: "ForStatement", + update: null, +} +`; + +snapshot[`Plugin - ForStatement 2`] = ` +{ + body: { + body: [], + range: [ + 15, + 17, + ], + type: "BlockStatement", + }, + init: { + name: "a", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 17, + ], + test: { + name: "b", + optional: false, + range: [ + 9, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForStatement", + update: { + name: "c", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, +} +`; + +snapshot[`Plugin - IfStatement 1`] = ` +{ + alternate: null, + consequent: { + body: [], + range: [ + 10, + 12, + ], + type: "BlockStatement", + }, + range: [ + 1, + 12, + ], + test: { + name: "foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "IfStatement", +} +`; + +snapshot[`Plugin - IfStatement 2`] = ` +{ + alternate: { + body: [], + range: [ + 18, + 20, + ], + type: "BlockStatement", + }, + consequent: { + body: [], + range: [ + 10, + 12, + ], + type: "BlockStatement", + }, + range: [ + 1, + 20, + ], + test: { + name: "foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "IfStatement", +} +`; + +snapshot[`Plugin - LabeledStatement 1`] = ` +{ + body: { + body: [], + range: [ + 6, + 8, + ], + type: "BlockStatement", + }, + label: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 8, + ], + type: "LabeledStatement", +} +`; + +snapshot[`Plugin - ReturnStatement 1`] = ` +{ + argument: null, + range: [ + 1, + 7, + ], + type: "ReturnStatement", +} +`; + +snapshot[`Plugin - ReturnStatement 2`] = ` +{ + argument: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "ReturnStatement", +} +`; + +snapshot[`Plugin - SwitchStatement 1`] = ` +{ + cases: [ + { + consequent: [], + range: [ + 22, + 31, + ], + test: { + name: "foo", + optional: false, + range: [ + 27, + 30, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "SwitchCase", + }, + { + consequent: [ + { + label: null, + range: [ + 56, + 62, + ], + type: "BreakStatement", + }, + ], + range: [ + 38, + 62, + ], + test: { + name: "bar", + optional: false, + range: [ + 43, + 46, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "SwitchCase", + }, + { + consequent: [ + { + body: [], + range: [ + 86, + 88, + ], + type: "BlockStatement", + }, + ], + range: [ + 69, + 88, + ], + test: null, + type: "SwitchCase", + }, + ], + discriminant: { + name: "foo", + optional: false, + range: [ + 9, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 94, + ], + type: "SwitchStatement", +} +`; + +snapshot[`Plugin - ThrowStatement 1`] = ` +{ + argument: { + name: "foo", + optional: false, + range: [ + 7, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 11, + ], + type: "ThrowStatement", +} +`; + +snapshot[`Plugin - TryStatement 1`] = ` +{ + block: { + body: [], + range: [ + 5, + 7, + ], + type: "BlockStatement", + }, + finalizer: null, + handler: { + body: { + body: [], + range: [ + 14, + 16, + ], + type: "BlockStatement", + }, + param: null, + range: [ + 8, + 16, + ], + type: "CatchClause", + }, + range: [ + 1, + 16, + ], + type: "TryStatement", +} +`; + +snapshot[`Plugin - TryStatement 2`] = ` +{ + block: { + body: [], + range: [ + 5, + 7, + ], + type: "BlockStatement", + }, + finalizer: null, + handler: { + body: { + body: [], + range: [ + 18, + 20, + ], + type: "BlockStatement", + }, + param: { + name: "e", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 20, + ], + type: "CatchClause", + }, + range: [ + 1, + 20, + ], + type: "TryStatement", +} +`; + +snapshot[`Plugin - TryStatement 3`] = ` +{ + block: { + body: [], + range: [ + 5, + 7, + ], + type: "BlockStatement", + }, + finalizer: { + body: [], + range: [ + 16, + 18, + ], + type: "BlockStatement", + }, + handler: null, + range: [ + 1, + 18, + ], + type: "TryStatement", +} +`; + +snapshot[`Plugin - WhileStatement 1`] = ` +{ + body: { + body: [], + range: [ + 13, + 15, + ], + type: "BlockStatement", + }, + range: [ + 1, + 15, + ], + test: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "WhileStatement", +} +`; + +snapshot[`Plugin - WithStatement 1`] = ` +{ + body: { + body: [], + range: [ + 11, + 13, + ], + type: "BlockStatement", + }, + object: { + elements: [], + range: [ + 7, + 9, + ], + type: "ArrayExpression", + }, + range: [ + 1, + 13, + ], + type: "WithStatement", +} +`; + +snapshot[`Plugin - ArrayExpression 1`] = ` +{ + elements: [ + { + elements: [], + range: [ + 2, + 4, + ], + type: "ArrayExpression", + }, + ], + range: [ + 1, + 9, + ], + type: "ArrayExpression", +} +`; + +snapshot[`Plugin - ArrowFunctionExpression 1`] = ` +{ + async: false, + body: { + body: [], + range: [ + 7, + 9, + ], + type: "BlockStatement", + }, + generator: false, + params: [], + range: [ + 1, + 9, + ], + returnType: null, + type: "ArrowFunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - ArrowFunctionExpression 2`] = ` +{ + async: true, + body: { + body: [], + range: [ + 13, + 15, + ], + type: "BlockStatement", + }, + generator: false, + params: [], + range: [ + 1, + 15, + ], + returnType: null, + type: "ArrowFunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - ArrowFunctionExpression 3`] = ` +{ + async: false, + body: { + body: [], + range: [ + 34, + 36, + ], + type: "BlockStatement", + }, + generator: false, + params: [ + { + name: "a", + optional: false, + range: [ + 2, + 11, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 3, + 11, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 5, + 11, + ], + type: "TSNumberKeyword", + }, + }, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 13, + 24, + ], + type: "RestElement", + typeAnnotation: { + range: [ + 17, + 24, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + elementType: { + range: [ + 19, + 22, + ], + type: "TSAnyKeyword", + }, + range: [ + 19, + 24, + ], + type: "TSArrayType", + }, + }, + }, + ], + range: [ + 1, + 36, + ], + returnType: { + range: [ + 25, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 27, + 30, + ], + type: "TSAnyKeyword", + }, + }, + type: "ArrowFunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - AssignmentExpression 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "=", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "AssignmentExpression", +} +`; + +snapshot[`Plugin - AssignmentExpression 2`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "=", + range: [ + 1, + 12, + ], + right: { + left: { + name: "a", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "??=", + range: [ + 5, + 12, + ], + right: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "AssignmentExpression", + }, + type: "AssignmentExpression", +} +`; + +snapshot[`Plugin - AwaitExpression 1`] = ` +{ + argument: { + name: "foo", + optional: false, + range: [ + 7, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 10, + ], + type: "AwaitExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 2`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">=", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 3`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "<", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 4`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "<=", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 5`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "==", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 6`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "===", + range: [ + 1, + 8, + ], + right: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 7`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "!=", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 8`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "!=", + range: [ + 1, + 8, + ], + right: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 9`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "<<", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 10`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">>", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 11`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">>>", + range: [ + 1, + 8, + ], + right: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 12`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "+", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 13`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "-", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 14`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "*", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 15`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "/", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 16`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "%", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 17`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "|", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 18`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "^", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 19`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "&", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 20`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "in", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 21`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "**", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - CallExpression 1`] = ` +{ + arguments: [], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 1, + 6, + ], + type: "CallExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - CallExpression 2`] = ` +{ + arguments: [ + { + name: "a", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 11, + ], + type: "SpreadElement", + }, + ], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 1, + 13, + ], + type: "CallExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - CallExpression 3`] = ` +{ + arguments: [], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: true, + range: [ + 1, + 8, + ], + type: "CallExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - CallExpression 4`] = ` +{ + arguments: [], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 1, + 9, + ], + type: "CallExpression", + typeArguments: { + params: [ + { + range: [ + 5, + 6, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 4, + 7, + ], + type: "TSTypeParameterInstantiation", + }, +} +`; + +snapshot[`Plugin - ChainExpression 1`] = ` +{ + expression: { + computed: false, + object: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: true, + property: { + name: "b", + optional: false, + range: [ + 4, + 5, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 5, + ], + type: "MemberExpression", + }, + range: [ + 1, + 5, + ], + type: "ChainExpression", +} +`; + +snapshot[`Plugin - ClassExpression 1`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 13, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 13, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 2`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 17, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 5, + 17, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 3`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 29, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 5, + 29, + ], + superClass: { + name: "Bar", + optional: false, + range: [ + 23, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 4`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 50, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [ + { + expression: { + name: "Baz", + optional: false, + range: [ + 38, + 41, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 38, + 41, + ], + type: "TSClassImplements", + typeArguments: null, + }, + { + expression: { + name: "Baz2", + optional: false, + range: [ + 43, + 47, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 43, + 47, + ], + type: "TSClassImplements", + typeArguments: null, + }, + ], + range: [ + 5, + 50, + ], + superClass: { + name: "Bar", + optional: false, + range: [ + 23, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 5`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 20, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 5, + 20, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 6`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + key: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "method", + optional: false, + override: false, + range: [ + 13, + 21, + ], + static: false, + type: "MethodDefinition", + value: { + async: false, + body: { + body: [], + range: [ + 19, + 21, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [], + range: [ + 13, + 21, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, + }, + }, + ], + range: [ + 5, + 23, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 23, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 7`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + key: { + name: "foo", + range: [ + 13, + 17, + ], + type: "PrivateIdentifier", + }, + kind: "method", + optional: false, + override: false, + range: [ + 13, + 22, + ], + static: false, + type: "MethodDefinition", + value: { + async: false, + body: { + body: [], + range: [ + 20, + 22, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [], + range: [ + 13, + 22, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, + }, + }, + ], + range: [ + 5, + 24, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 24, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 8`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 24, + ], + readonly: false, + static: false, + type: "PropertyDefinition", + value: null, + }, + ], + range: [ + 5, + 26, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 26, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 9`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 22, + ], + readonly: false, + static: false, + type: "PropertyDefinition", + value: { + name: "bar", + optional: false, + range: [ + 19, + 22, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 24, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 24, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 10`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + key: { + name: "constructor", + optional: false, + range: [ + 13, + 24, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "constructor", + optional: false, + override: false, + range: [ + 13, + 47, + ], + static: false, + type: "MethodDefinition", + value: { + async: false, + body: { + body: [], + range: [ + 45, + 47, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [ + { + accessibility: undefined, + decorators: [], + override: false, + parameter: { + name: "foo", + optional: false, + range: [ + 32, + 35, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 35, + 43, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 37, + 43, + ], + type: "TSStringKeyword", + }, + }, + }, + range: [ + 25, + 43, + ], + readonly: false, + static: false, + type: "TSParameterProperty", + }, + ], + range: [ + 13, + 47, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, + }, + }, + ], + range: [ + 5, + 49, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 49, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 11`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + range: [ + 13, + 17, + ], + type: "PrivateIdentifier", + }, + optional: false, + override: false, + range: [ + 13, + 31, + ], + readonly: false, + static: false, + type: "PropertyDefinition", + value: { + name: "bar", + optional: false, + range: [ + 28, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 33, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 33, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 12`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 20, + 23, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 29, + ], + readonly: false, + static: true, + type: "PropertyDefinition", + value: { + name: "bar", + optional: false, + range: [ + 26, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 31, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 31, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 13`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 20, + 23, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 24, + ], + readonly: false, + static: true, + type: "PropertyDefinition", + value: null, + }, + { + body: { + body: [ + { + expression: { + left: { + name: "foo", + optional: false, + range: [ + 34, + 37, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "=", + range: [ + 34, + 43, + ], + right: { + name: "bar", + optional: false, + range: [ + 40, + 43, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "AssignmentExpression", + }, + range: [ + 34, + 43, + ], + type: "ExpressionStatement", + }, + ], + range: [ + 32, + 45, + ], + type: "BlockStatement", + }, + range: [ + 25, + 45, + ], + type: "StaticBlock", + }, + ], + range: [ + 5, + 47, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 47, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ConditionalExpression 1`] = ` +{ + alternate: { + name: "c", + optional: false, + range: [ + 9, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + consequent: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 10, + ], + test: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ConditionalExpression", +} +`; + +snapshot[`Plugin - FunctionExpression 1`] = ` +{ + async: false, + body: { + body: [], + range: [ + 17, + 19, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [], + range: [ + 5, + 19, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - FunctionExpression 2`] = ` +{ + async: false, + body: { + body: [], + range: [ + 20, + 22, + ], + type: "BlockStatement", + }, + generator: false, + id: { + name: "foo", + optional: false, + range: [ + 14, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + params: [], + range: [ + 5, + 22, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - FunctionExpression 3`] = ` +{ + async: false, + body: { + body: [], + range: [ + 45, + 47, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [ + { + name: "a", + optional: true, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 17, + 25, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 19, + 25, + ], + type: "TSNumberKeyword", + }, + }, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 30, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 27, + 38, + ], + type: "RestElement", + typeAnnotation: { + range: [ + 31, + 38, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + elementType: { + range: [ + 33, + 36, + ], + type: "TSAnyKeyword", + }, + range: [ + 33, + 38, + ], + type: "TSArrayType", + }, + }, + }, + ], + range: [ + 5, + 47, + ], + returnType: { + range: [ + 39, + 44, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 41, + 44, + ], + type: "TSAnyKeyword", + }, + }, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - FunctionExpression 4`] = ` +{ + async: true, + body: { + body: [], + range: [ + 24, + 26, + ], + type: "BlockStatement", + }, + generator: true, + id: null, + params: [], + range: [ + 5, + 26, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - Identifier 1`] = ` +{ + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, +} +`; + +snapshot[`Plugin - ImportExpression 1`] = ` +{ + options: { + properties: [ + { + computed: false, + key: { + name: "with", + optional: false, + range: [ + 17, + 21, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 17, + 39, + ], + shorthand: false, + type: "Property", + value: { + properties: [ + { + computed: false, + key: { + name: "type", + optional: false, + range: [ + 25, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 25, + 37, + ], + shorthand: false, + type: "Property", + value: { + range: [ + 31, + 37, + ], + raw: "'json'", + type: "Literal", + value: "json", + }, + }, + ], + range: [ + 23, + 39, + ], + type: "ObjectExpression", + }, + }, + ], + range: [ + 15, + 41, + ], + type: "ObjectExpression", + }, + range: [ + 1, + 42, + ], + source: { + range: [ + 8, + 13, + ], + raw: "'foo'", + type: "Literal", + value: "foo", + }, + type: "ImportExpression", +} +`; + +snapshot[`Plugin - LogicalExpression 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "&&", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "LogicalExpression", +} +`; + +snapshot[`Plugin - LogicalExpression 2`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "||", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "LogicalExpression", +} +`; + +snapshot[`Plugin - LogicalExpression 3`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "??", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "LogicalExpression", +} +`; + +snapshot[`Plugin - MemberExpression 1`] = ` +{ + computed: false, + object: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + property: { + name: "b", + optional: false, + range: [ + 3, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 4, + ], + type: "MemberExpression", +} +`; + +snapshot[`Plugin - MemberExpression 2`] = ` +{ + computed: true, + object: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + property: { + range: [ + 3, + 6, + ], + raw: "'b'", + type: "Literal", + value: "b", + }, + range: [ + 1, + 7, + ], + type: "MemberExpression", +} +`; + +snapshot[`Plugin - MetaProperty 1`] = ` +{ + property: { + name: "meta", + optional: false, + range: [ + 1, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "MetaProperty", +} +`; + +snapshot[`Plugin - NewExpression 1`] = ` +{ + arguments: [], + callee: { + name: "Foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 10, + ], + type: "NewExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - NewExpression 2`] = ` +{ + arguments: [ + { + name: "a", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 18, + 19, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 15, + 18, + ], + type: "SpreadElement", + }, + ], + callee: { + name: "Foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 20, + ], + type: "NewExpression", + typeArguments: { + params: [ + { + range: [ + 9, + 10, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 9, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 8, + 11, + ], + type: "TSTypeParameterInstantiation", + }, +} +`; + +snapshot[`Plugin - ObjectExpression 1`] = ` +{ + properties: [], + range: [ + 5, + 7, + ], + type: "ObjectExpression", +} +`; + +snapshot[`Plugin - ObjectExpression 2`] = ` +{ + properties: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 7, + 8, + ], + shorthand: true, + type: "Property", + value: { + name: "a", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 10, + ], + type: "ObjectExpression", +} +`; + +snapshot[`Plugin - ObjectExpression 3`] = ` +{ + properties: [ + { + computed: false, + key: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 7, + 11, + ], + shorthand: false, + type: "Property", + value: { + name: "c", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + { + computed: true, + key: { + name: "c", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 13, + 19, + ], + shorthand: false, + type: "Property", + value: { + name: "d", + optional: false, + range: [ + 18, + 19, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 21, + ], + type: "ObjectExpression", +} +`; + +snapshot[`Plugin - PrivateIdentifier 1`] = ` +{ + name: "foo", + range: [ + 13, + 17, + ], + type: "PrivateIdentifier", +} +`; + +snapshot[`Plugin - SequenceExpression 1`] = ` +{ + expressions: [ + { + name: "a", + optional: false, + range: [ + 2, + 3, + ], + type: "Identifier", + typeAnnotation: null, + }, + { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + ], + range: [ + 2, + 6, + ], + type: "SequenceExpression", +} +`; + +snapshot[`Plugin - Super 1`] = ` +{ + range: [ + 41, + 46, + ], + type: "Super", +} +`; + +snapshot[`Plugin - TaggedTemplateExpression 1`] = ` +{ + quasi: { + expressions: [ + { + name: "bar", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + ], + quasis: [ + { + cooked: "foo ", + range: [ + 5, + 9, + ], + raw: "foo ", + tail: false, + type: "TemplateElement", + }, + { + cooked: " baz", + range: [ + 15, + 19, + ], + raw: " baz", + tail: true, + type: "TemplateElement", + }, + ], + range: [ + 4, + 20, + ], + type: "TemplateLiteral", + }, + range: [ + 1, + 20, + ], + tag: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "TaggedTemplateExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - TemplateLiteral 1`] = ` +{ + expressions: [ + { + name: "bar", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + ], + quasis: [ + { + cooked: "foo ", + range: [ + 2, + 6, + ], + raw: "foo ", + tail: false, + type: "TemplateElement", + }, + { + cooked: " baz", + range: [ + 12, + 16, + ], + raw: " baz", + tail: true, + type: "TemplateElement", + }, + ], + range: [ + 1, + 17, + ], + type: "TemplateLiteral", +} +`; + +snapshot[`Plugin - ThisExpression 1`] = ` +{ + range: [ + 1, + 5, + ], + type: "ThisExpression", +} +`; + +snapshot[`Plugin - TSAsExpression 1`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 7, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 6, + 7, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSAsExpression 2`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 11, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 1, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "const", + optional: false, + range: [ + 1, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSNonNullExpression 1`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 3, + ], + type: "TSNonNullExpression", +} +`; + +snapshot[`Plugin - TSSatisfiesExpression 1`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 14, + ], + type: "TSSatisfiesExpression", + typeAnnotation: { + range: [ + 13, + 14, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "b", + optional: false, + range: [ + 13, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - UnaryExpression 1`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "typeof", + range: [ + 1, + 9, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UnaryExpression 2`] = ` +{ + argument: { + range: [ + 6, + 7, + ], + raw: "0", + type: "Literal", + value: 0, + }, + operator: "void", + range: [ + 1, + 7, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UnaryExpression 3`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 2, + 3, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "-", + range: [ + 1, + 3, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UnaryExpression 4`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 2, + 3, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "+", + range: [ + 1, + 3, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 1`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "++", + prefix: false, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 2`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 3, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "++", + prefix: true, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 3`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "--", + prefix: false, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 4`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 3, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "--", + prefix: true, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - YieldExpression 1`] = ` +{ + argument: { + name: "bar", + optional: false, + range: [ + 25, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + delegate: false, + range: [ + 19, + 28, + ], + type: "YieldExpression", +} +`; + +snapshot[`Plugin - Literal 1`] = ` +{ + range: [ + 1, + 2, + ], + raw: "1", + type: "Literal", + value: 1, +} +`; + +snapshot[`Plugin - Literal 2`] = ` +{ + range: [ + 1, + 6, + ], + raw: "'foo'", + type: "Literal", + value: "foo", +} +`; + +snapshot[`Plugin - Literal 3`] = ` +{ + range: [ + 1, + 6, + ], + raw: '"foo"', + type: "Literal", + value: "foo", +} +`; + +snapshot[`Plugin - Literal 4`] = ` +{ + range: [ + 1, + 5, + ], + raw: "true", + type: "Literal", + value: true, +} +`; + +snapshot[`Plugin - Literal 5`] = ` +{ + range: [ + 1, + 6, + ], + raw: "false", + type: "Literal", + value: false, +} +`; + +snapshot[`Plugin - Literal 6`] = ` +{ + range: [ + 1, + 5, + ], + raw: "null", + type: "Literal", + value: null, +} +`; + +snapshot[`Plugin - Literal 7`] = ` +{ + bigint: "1", + range: [ + 1, + 3, + ], + raw: "1n", + type: "Literal", + value: 1n, +} +`; + +snapshot[`Plugin - Literal 8`] = ` +{ + range: [ + 1, + 7, + ], + raw: "/foo/g", + regex: { + flags: "g", + pattern: "foo", + }, + type: "Literal", + value: /foo/g, +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 1`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 8, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 8, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 2`] = ` +{ + children: [], + closingElement: { + name: { + name: "div", + range: [ + 8, + 11, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 12, + ], + type: "JSXClosingElement", + }, + openingElement: { + attributes: [], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 6, + ], + selfClosing: false, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 12, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 3`] = ` +{ + children: [], + closingElement: { + name: { + name: "div", + range: [ + 10, + 13, + ], + type: "JSXIdentifier", + }, + range: [ + 8, + 14, + ], + type: "JSXClosingElement", + }, + openingElement: { + attributes: [ + { + name: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 7, + ], + type: "JSXAttribute", + value: null, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 8, + ], + selfClosing: false, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 14, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 4`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [ + { + name: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 11, + ], + type: "JSXAttribute", + value: { + range: [ + 8, + 11, + ], + raw: '"b"', + type: "Literal", + value: "b", + }, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 14, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 14, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 5`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [ + { + name: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 11, + ], + type: "JSXAttribute", + value: { + expression: { + range: [ + 9, + 10, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 8, + 11, + ], + type: "JSXExpressionContainer", + }, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 14, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 14, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 6`] = ` +{ + children: [ + { + range: [ + 6, + 9, + ], + raw: "foo", + type: "JSXText", + value: "foo", + }, + { + expression: { + range: [ + 10, + 11, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 9, + 12, + ], + type: "JSXExpressionContainer", + }, + ], + closingElement: { + name: { + name: "div", + range: [ + 14, + 17, + ], + type: "JSXIdentifier", + }, + range: [ + 12, + 18, + ], + type: "JSXClosingElement", + }, + openingElement: { + attributes: [], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 6, + ], + selfClosing: false, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 18, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 7`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + object: { + name: "a", + range: [ + 2, + 3, + ], + type: "JSXIdentifier", + }, + property: { + name: "b", + range: [ + 4, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 2, + 5, + ], + type: "JSXMemberExpression", + }, + range: [ + 1, + 8, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 8, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 8`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [ + { + name: { + name: { + name: "b", + range: [ + 8, + 9, + ], + type: "JSXIdentifier", + }, + namespace: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 9, + ], + type: "JSXNamespacedName", + }, + range: [ + 6, + 13, + ], + type: "JSXAttribute", + value: { + expression: { + range: [ + 11, + 12, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 10, + 13, + ], + type: "JSXExpressionContainer", + }, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 16, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 16, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 9`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + name: "Foo", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 8, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 8, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 10`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + name: "Foo", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 11, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: { + params: [ + { + range: [ + 6, + 7, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 8, + ], + type: "TSTypeParameterInstantiation", + }, + }, + range: [ + 1, + 11, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment 1`] = ` +{ + children: [], + closingFragment: { + range: [ + 3, + 6, + ], + type: "JSXClosingFragment", + }, + openingFragment: { + range: [ + 1, + 3, + ], + type: "JSXOpeningFragment", + }, + range: [ + 1, + 6, + ], + type: "JSXFragment", +} +`; + +snapshot[`Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment 2`] = ` +{ + children: [ + { + range: [ + 3, + 6, + ], + raw: "foo", + type: "JSXText", + value: "foo", + }, + { + expression: { + range: [ + 7, + 8, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 6, + 9, + ], + type: "JSXExpressionContainer", + }, + ], + closingFragment: { + range: [ + 9, + 12, + ], + type: "JSXClosingFragment", + }, + openingFragment: { + range: [ + 1, + 3, + ], + type: "JSXOpeningFragment", + }, + range: [ + 1, + 12, + ], + type: "JSXFragment", +} +`; + +snapshot[`Plugin - TSAsExpression 3`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 9, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 6, + 9, + ], + type: "TSAnyKeyword", + }, +} +`; + +snapshot[`Plugin - TSAsExpression 4`] = ` +{ + expression: { + range: [ + 1, + 6, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 1, + 15, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 1, + 15, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "const", + optional: false, + range: [ + 1, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSEnumDeclaration 1`] = ` +{ + body: { + members: [], + range: [ + 1, + 12, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 2`] = ` +{ + body: { + members: [], + range: [ + 1, + 18, + ], + type: "TSEnumBody", + }, + const: true, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 12, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 18, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 3`] = ` +{ + body: { + members: [ + { + id: { + name: "A", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: null, + range: [ + 12, + 13, + ], + type: "TSEnumMember", + }, + { + id: { + name: "B", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: null, + range: [ + 15, + 16, + ], + type: "TSEnumMember", + }, + ], + range: [ + 1, + 18, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 18, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 4`] = ` +{ + body: { + members: [ + { + id: { + range: [ + 12, + 17, + ], + raw: '"a-b"', + type: "Literal", + value: "a-b", + }, + initializer: null, + range: [ + 12, + 17, + ], + type: "TSEnumMember", + }, + ], + range: [ + 1, + 19, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 19, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 5`] = ` +{ + body: { + members: [ + { + id: { + name: "A", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: { + range: [ + 16, + 17, + ], + raw: "1", + type: "Literal", + value: 1, + }, + range: [ + 12, + 17, + ], + type: "TSEnumMember", + }, + { + id: { + name: "B", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: { + range: [ + 23, + 24, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 19, + 24, + ], + type: "TSEnumMember", + }, + { + id: { + name: "C", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: { + left: { + name: "A", + optional: false, + range: [ + 30, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "|", + range: [ + 30, + 35, + ], + right: { + name: "B", + optional: false, + range: [ + 34, + 35, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", + }, + range: [ + 26, + 35, + ], + type: "TSEnumMember", + }, + ], + range: [ + 1, + 37, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 37, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSInterface 1`] = ` +{ + body: { + body: [], + range: [ + 13, + 15, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 15, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 2`] = ` +{ + body: { + body: [], + range: [ + 16, + 18, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 13, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 13, + 14, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 12, + 15, + ], + type: "TSTypeParameterDeclaration", + }, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 18, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 3`] = ` +{ + body: { + body: [], + range: [ + 36, + 38, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 38, + ], + type: "TSInterface", + typeParameters: [ + { + expression: { + name: "Foo", + optional: false, + range: [ + 21, + 24, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 21, + 27, + ], + type: "TSInterfaceHeritage", + typeArguments: { + params: [ + { + range: [ + 25, + 26, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 25, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 24, + 27, + ], + type: "TSTypeParameterInstantiation", + }, + }, + { + expression: { + name: "Bar", + optional: false, + range: [ + 29, + 32, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 29, + 35, + ], + type: "TSInterfaceHeritage", + typeArguments: { + params: [ + { + range: [ + 33, + 34, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 33, + 34, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 32, + 35, + ], + type: "TSTypeParameterInstantiation", + }, + }, + ], +} +`; + +snapshot[`Plugin - TSInterface 4`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "foo", + optional: false, + range: [ + 15, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 15, + 24, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 18, + 23, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 20, + 23, + ], + type: "TSAnyKeyword", + }, + }, + }, + { + computed: false, + key: { + name: "bar", + optional: false, + range: [ + 25, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: true, + range: [ + 25, + 34, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 29, + 34, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 31, + 34, + ], + type: "TSAnyKeyword", + }, + }, + }, + ], + range: [ + 13, + 36, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 36, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 5`] = ` +{ + body: { + body: [ + { + parameters: [ + { + name: "key", + optional: false, + range: [ + 25, + 36, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 28, + 36, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 30, + 36, + ], + type: "TSStringKeyword", + }, + }, + }, + ], + range: [ + 15, + 42, + ], + readonly: true, + type: "TSIndexSignature", + typeAnnotation: { + range: [ + 37, + 42, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 39, + 42, + ], + type: "TSAnyKeyword", + }, + }, + }, + ], + range: [ + 13, + 44, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 44, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 6`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 24, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 15, + 30, + ], + readonly: true, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 25, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 27, + 30, + ], + type: "TSAnyKeyword", + }, + }, + }, + ], + range: [ + 13, + 32, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 32, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 7`] = ` +{ + body: { + body: [ + { + params: [ + { + name: "a", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 20, + 23, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 22, + 23, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 22, + 23, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + }, + ], + range: [ + 15, + 27, + ], + returnType: { + range: [ + 24, + 27, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 26, + 27, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + type: "TSCallSignatureDeclaration", + typeAnnotation: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 17, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 15, + 18, + ], + type: "TSTypeParameterDeclaration", + }, + }, + ], + range: [ + 13, + 29, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 29, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 8`] = ` +{ + body: { + body: [ + { + params: [ + { + name: "a", + optional: false, + range: [ + 23, + 24, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 24, + 27, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 26, + 27, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + }, + ], + range: [ + 15, + 31, + ], + returnType: { + range: [ + 28, + 31, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 30, + 31, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 30, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + type: "TSConstructSignatureDeclaration", + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 20, + 21, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 20, + 21, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 19, + 22, + ], + type: "TSTypeParameterDeclaration", + }, + }, + ], + range: [ + 13, + 33, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 33, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 9`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 15, + 36, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 16, + 36, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + params: [ + { + name: "a", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 27, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 29, + 30, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 29, + 30, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + }, + ], + range: [ + 18, + 36, + ], + returnType: { + range: [ + 32, + 36, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 35, + 36, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 35, + 36, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + type: "TSConstructSignatureDeclaration", + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 23, + 24, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 23, + 24, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 22, + 25, + ], + type: "TSTypeParameterDeclaration", + }, + }, + }, + }, + ], + range: [ + 13, + 38, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 38, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 10`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "getter", + optional: false, + range: [ + 15, + 30, + ], + readonly: false, + returnType: { + range: [ + 22, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 24, + 30, + ], + type: "TSStringKeyword", + }, + }, + static: false, + type: "TSMethodSignature", + }, + ], + range: [ + 13, + 32, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 32, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 11`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "setter", + optional: false, + params: [ + { + name: "v", + optional: false, + range: [ + 21, + 22, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 22, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 24, + 30, + ], + type: "TSStringKeyword", + }, + }, + }, + ], + range: [ + 15, + 31, + ], + readonly: false, + static: false, + type: "TSMethodSignature", + }, + ], + range: [ + 13, + 33, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 33, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 12`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "method", + optional: false, + params: [ + { + name: "arg", + optional: true, + range: [ + 20, + 23, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 24, + 29, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 26, + 29, + ], + type: "TSAnyKeyword", + }, + }, + }, + { + argument: { + name: "args", + optional: false, + range: [ + 34, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 31, + 45, + ], + type: "RestElement", + typeAnnotation: { + range: [ + 38, + 45, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + elementType: { + range: [ + 40, + 43, + ], + type: "TSAnyKeyword", + }, + range: [ + 40, + 45, + ], + type: "TSArrayType", + }, + }, + }, + ], + range: [ + 15, + 51, + ], + readonly: false, + returnType: { + range: [ + 46, + 51, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 48, + 51, + ], + type: "TSAnyKeyword", + }, + }, + static: false, + type: "TSMethodSignature", + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 17, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 17, + 18, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 16, + 19, + ], + type: "TSTypeParameterDeclaration", + }, + }, + ], + range: [ + 13, + 53, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 53, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSSatisfiesExpression 2`] = ` +{ + expression: { + properties: [], + range: [ + 11, + 13, + ], + type: "ObjectExpression", + }, + range: [ + 11, + 25, + ], + type: "TSSatisfiesExpression", + typeAnnotation: { + range: [ + 24, + 25, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "A", + optional: false, + range: [ + 24, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSTypeAliasDeclaration 1`] = ` +{ + declare: false, + id: { + name: "A", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 13, + ], + type: "TSTypeAliasDeclaration", + typeAnnotation: { + range: [ + 10, + 13, + ], + type: "TSAnyKeyword", + }, + typeParameters: null, +} +`; + +snapshot[`Plugin - TSTypeAliasDeclaration 2`] = ` +{ + declare: false, + id: { + name: "A", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 16, + ], + type: "TSTypeAliasDeclaration", + typeAnnotation: { + range: [ + 13, + 16, + ], + type: "TSAnyKeyword", + }, + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 8, + 9, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 7, + 10, + ], + type: "TSTypeParameterDeclaration", + }, +} +`; + +snapshot[`Plugin - TSTypeAliasDeclaration 3`] = ` +{ + declare: true, + id: { + name: "A", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 24, + ], + type: "TSTypeAliasDeclaration", + typeAnnotation: { + range: [ + 21, + 24, + ], + type: "TSAnyKeyword", + }, + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 17, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 15, + 18, + ], + type: "TSTypeParameterDeclaration", + }, +} +`; + +snapshot[`Plugin - TSNonNullExpression 2`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 3, + ], + type: "TSNonNullExpression", +} +`; + +snapshot[`Plugin - TSUnionType 1`] = ` +{ + range: [ + 10, + 15, + ], + type: "TSUnionType", + types: [ + { + range: [ + 10, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + { + range: [ + 14, + 15, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "C", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], +} +`; + +snapshot[`Plugin - TSIntersectionType 1`] = ` +{ + range: [ + 10, + 15, + ], + type: "TSIntersectionType", + types: [ + { + range: [ + 10, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + { + range: [ + 14, + 15, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "C", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], +} +`; + +snapshot[`Plugin - TSModuleDeclaration 1`] = ` +{ + body: { + body: [], + range: [ + 10, + 12, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "A", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSModuleDeclaration 2`] = ` +{ + body: { + body: [ + { + declaration: { + async: false, + body: null, + declare: false, + generator: false, + id: { + name: "A", + optional: false, + range: [ + 36, + 37, + ], + type: "Identifier", + typeAnnotation: null, + }, + params: [], + range: [ + 27, + 45, + ], + returnType: { + range: [ + 39, + 45, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 41, + 45, + ], + type: "TSVoidKeyword", + }, + }, + type: "FunctionDeclaration", + typeParameters: null, + }, + range: [ + 20, + 45, + ], + type: "ExportNamedDeclaration", + }, + ], + range: [ + 18, + 47, + ], + type: "TSModuleBlock", + }, + declare: true, + global: false, + id: { + name: "A", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 47, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSModuleDeclaration + TSModuleBlock 1`] = ` +{ + body: { + body: [], + range: [ + 10, + 12, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "A", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSModuleDeclaration + TSModuleBlock 2`] = ` +{ + body: { + body: [ + { + body: { + body: [], + range: [ + 27, + 29, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "B", + optional: false, + range: [ + 25, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 15, + 29, + ], + type: "TSModuleDeclaration", + }, + ], + range: [ + 13, + 31, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 31, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSQualifiedName 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + right: { + name: "b", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "TSQualifiedName", +} +`; + +snapshot[`Plugin - TSTypeLiteral 1`] = ` +{ + members: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 12, + 16, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 13, + 16, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + literal: { + range: [ + 15, + 16, + ], + raw: "1", + type: "Literal", + value: 1, + }, + range: [ + 15, + 16, + ], + type: "TSLiteralType", + }, + }, + }, + ], + range: [ + 10, + 18, + ], + type: "TSTypeLiteral", +} +`; + +snapshot[`Plugin - TSOptionalType 1`] = ` +{ + range: [ + 11, + 18, + ], + type: "TSOptionalType", + typeAnnotation: { + range: [ + 11, + 17, + ], + type: "TSNumberKeyword", + }, +} +`; + +snapshot[`Plugin - TSRestType 1`] = ` +{ + range: [ + 11, + 22, + ], + type: "TSRestType", + typeAnnotation: { + elementType: { + range: [ + 14, + 20, + ], + type: "TSNumberKeyword", + }, + range: [ + 14, + 22, + ], + type: "TSArrayType", + }, +} +`; + +snapshot[`Plugin - TSConditionalType 1`] = ` +{ + checkType: { + range: [ + 10, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + extendsType: { + range: [ + 20, + 21, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "C", + optional: false, + range: [ + 20, + 21, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + falseType: { + range: [ + 33, + 39, + ], + type: "TSStringKeyword", + }, + range: [ + 10, + 39, + ], + trueType: { + range: [ + 24, + 30, + ], + type: "TSNumberKeyword", + }, + type: "TSConditionalType", +} +`; + +snapshot[`Plugin - TSInferType 1`] = ` +{ + range: [ + 29, + 39, + ], + type: "TSInferType", + typeParameter: { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "Item", + optional: false, + range: [ + 35, + 39, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 35, + 39, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSTypeOperator 1`] = ` +{ + operator: "keyof", + range: [ + 10, + 17, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 16, + 17, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSTypeOperator 2`] = ` +{ + operator: "unique", + range: [ + 21, + 34, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 28, + 34, + ], + type: "TSSymbolKeyword", + }, +} +`; + +snapshot[`Plugin - TSTypeOperator 3`] = ` +{ + operator: "readonly", + range: [ + 10, + 21, + ], + type: "TSTypeOperator", + typeAnnotation: { + elementTypes: [], + range: [ + 19, + 21, + ], + type: "TSTupleType", + }, +} +`; + +snapshot[`Plugin - TSMappedType 1`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 41, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 31, + 38, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 2`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 45, + ], + readonly: true, + type: "TSMappedType", + typeAnnotation: { + elementTypes: [], + range: [ + 40, + 42, + ], + type: "TSTupleType", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 30, + 37, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 36, + 37, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 36, + 37, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 25, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 25, + 37, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 3`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 46, + ], + readonly: "-", + type: "TSMappedType", + typeAnnotation: { + elementTypes: [], + range: [ + 41, + 43, + ], + type: "TSTupleType", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 31, + 38, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 37, + 38, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 37, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 26, + 38, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 4`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 46, + ], + readonly: "+", + type: "TSMappedType", + typeAnnotation: { + elementTypes: [], + range: [ + 41, + 43, + ], + type: "TSTupleType", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 31, + 38, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 37, + 38, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 37, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 26, + 38, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 5`] = ` +{ + nameType: null, + optional: true, + range: [ + 13, + 42, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 32, + 39, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 6`] = ` +{ + nameType: null, + optional: "-", + range: [ + 13, + 43, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 33, + 40, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 7`] = ` +{ + nameType: null, + optional: "+", + range: [ + 13, + 43, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 33, + 40, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSLiteralType 1`] = ` +{ + literal: { + range: [ + 10, + 14, + ], + raw: "true", + type: "Literal", + value: true, + }, + range: [ + 10, + 14, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSLiteralType 2`] = ` +{ + literal: { + range: [ + 10, + 15, + ], + raw: "false", + type: "Literal", + value: false, + }, + range: [ + 10, + 15, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSLiteralType 3`] = ` +{ + literal: { + range: [ + 10, + 11, + ], + raw: "1", + type: "Literal", + value: 1, + }, + range: [ + 10, + 11, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSLiteralType 4`] = ` +{ + literal: { + range: [ + 10, + 15, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 10, + 15, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSTemplateLiteralType 1`] = ` +{ + quasis: [ + { + cooked: "a ", + range: [ + 11, + 13, + ], + raw: "a ", + tail: false, + type: "TemplateElement", + }, + { + cooked: "", + range: [ + 22, + 22, + ], + raw: "", + tail: true, + type: "TemplateElement", + }, + ], + range: [ + 10, + 23, + ], + type: "TSTemplateLiteralType", + types: [ + { + range: [ + 15, + 21, + ], + type: "TSStringKeyword", + }, + ], +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 1`] = ` +{ + elementTypes: [ + { + range: [ + 11, + 17, + ], + type: "TSNumberKeyword", + }, + ], + range: [ + 10, + 18, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 2`] = ` +{ + elementTypes: [ + { + elementType: { + range: [ + 14, + 20, + ], + type: "TSNumberKeyword", + }, + label: { + name: "x", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 11, + 20, + ], + type: "TSNamedTupleMember", + }, + ], + range: [ + 10, + 21, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 3`] = ` +{ + elementTypes: [ + { + elementType: { + range: [ + 14, + 20, + ], + type: "TSNumberKeyword", + }, + label: { + name: "x", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 11, + 20, + ], + type: "TSNamedTupleMember", + }, + ], + range: [ + 10, + 21, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 4`] = ` +{ + elementTypes: [ + { + elementType: { + elementType: { + range: [ + 17, + 23, + ], + type: "TSNumberKeyword", + }, + range: [ + 17, + 25, + ], + type: "TSArrayType", + }, + label: { + argument: { + name: "x", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 11, + 16, + ], + type: "RestElement", + typeAnnotation: null, + }, + range: [ + 11, + 25, + ], + type: "TSNamedTupleMember", + }, + ], + range: [ + 10, + 26, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSArrayType 1`] = ` +{ + elementType: { + range: [ + 10, + 16, + ], + type: "TSNumberKeyword", + }, + range: [ + 10, + 18, + ], + type: "TSArrayType", +} +`; + +snapshot[`Plugin - TSTypeQuery 1`] = ` +{ + exprName: { + name: "B", + optional: false, + range: [ + 17, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 18, + ], + type: "TSTypeQuery", + typeArguments: null, +} +`; + +snapshot[`Plugin - TS keywords 1`] = ` +{ + range: [ + 10, + 13, + ], + type: "TSAnyKeyword", +} +`; + +snapshot[`Plugin - TS keywords 2`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSBigIntKeyword", +} +`; + +snapshot[`Plugin - TS keywords 3`] = ` +{ + range: [ + 10, + 17, + ], + type: "TSBooleanKeyword", +} +`; + +snapshot[`Plugin - TS keywords 4`] = ` +{ + range: [ + 10, + 19, + ], + type: "TSIntrinsicKeyword", +} +`; + +snapshot[`Plugin - TS keywords 5`] = ` +{ + range: [ + 10, + 15, + ], + type: "TSNeverKeyword", +} +`; + +snapshot[`Plugin - TS keywords 6`] = ` +{ + range: [ + 10, + 14, + ], + type: "TSNullKeyword", +} +`; + +snapshot[`Plugin - TS keywords 7`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSNumberKeyword", +} +`; + +snapshot[`Plugin - TS keywords 8`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSObjectKeyword", +} +`; + +snapshot[`Plugin - TS keywords 9`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSStringKeyword", +} +`; + +snapshot[`Plugin - TS keywords 10`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSSymbolKeyword", +} +`; + +snapshot[`Plugin - TS keywords 11`] = ` +{ + range: [ + 10, + 19, + ], + type: "TSUndefinedKeyword", +} +`; + +snapshot[`Plugin - TS keywords 12`] = ` +{ + range: [ + 10, + 17, + ], + type: "TSUnknownKeyword", +} +`; + +snapshot[`Plugin - TS keywords 13`] = ` +{ + range: [ + 10, + 14, + ], + type: "TSVoidKeyword", +} +`; diff --git a/tests/unit/abort_controller_test.ts b/tests/unit/abort_controller_test.ts index 60ea6aa245..52a1bad803 100644 --- a/tests/unit/abort_controller_test.ts +++ b/tests/unit/abort_controller_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; diff --git a/tests/unit/blob_test.ts b/tests/unit/blob_test.ts index b578253142..b89f4703e2 100644 --- a/tests/unit/blob_test.ts +++ b/tests/unit/blob_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertStringIncludes } from "./test_util.ts"; import { concat } from "@std/bytes/concat"; diff --git a/tests/unit/body_test.ts b/tests/unit/body_test.ts index 18cdb22be0..28a1c697b1 100644 --- a/tests/unit/body_test.ts +++ b/tests/unit/body_test.ts @@ -1,5 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "./test_util.ts"; +// Copyright 2018-2025 the Deno authors. MIT license. +import { assert, assertEquals, assertRejects } from "./test_util.ts"; // just a hack to get a body object // deno-lint-ignore no-explicit-any @@ -187,3 +187,14 @@ Deno.test( assertEquals(file.size, 1); }, ); + +Deno.test(async function bodyBadResourceError() { + const file = await Deno.open("README.md"); + file.close(); + const body = buildBody(file.readable); + await assertRejects( + () => body.arrayBuffer(), + Deno.errors.BadResource, + "Cannot read body as underlying resource unavailable", + ); +}); diff --git a/tests/unit/broadcast_channel_test.ts b/tests/unit/broadcast_channel_test.ts index dce5f10867..f4efe68c4f 100644 --- a/tests/unit/broadcast_channel_test.ts +++ b/tests/unit/broadcast_channel_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; Deno.test("BroadcastChannel worker", async () => { diff --git a/tests/unit/build_test.ts b/tests/unit/build_test.ts index f697b64d39..9f4e3a5cbf 100644 --- a/tests/unit/build_test.ts +++ b/tests/unit/build_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; Deno.test(function buildInfo() { diff --git a/tests/unit/cache_api_test.ts b/tests/unit/cache_api_test.ts index 08f768e334..ec84e9c908 100644 --- a/tests/unit/cache_api_test.ts +++ b/tests/unit/cache_api_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/chmod_test.ts b/tests/unit/chmod_test.ts index 9ff6301e28..6d815ea1eb 100644 --- a/tests/unit/chmod_test.ts +++ b/tests/unit/chmod_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/chown_test.ts b/tests/unit/chown_test.ts index eda4d34037..99e7dd445a 100644 --- a/tests/unit/chown_test.ts +++ b/tests/unit/chown_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; // chown on Windows is noop for now, so ignore its testing on Windows diff --git a/tests/unit/command_test.ts b/tests/unit/command_test.ts index 8345548f85..268b0c25ba 100644 --- a/tests/unit/command_test.ts +++ b/tests/unit/command_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/console_test.ts b/tests/unit/console_test.ts index 06f5dd7e61..7b19e49cbd 100644 --- a/tests/unit/console_test.ts +++ b/tests/unit/console_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(ry) The unit test functions in this module are too coarse. They should // be broken up into smaller bits. diff --git a/tests/unit/copy_file_test.ts b/tests/unit/copy_file_test.ts index 9405184e33..fa4a9d837c 100644 --- a/tests/unit/copy_file_test.ts +++ b/tests/unit/copy_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; function readFileString(filename: string | URL): string { diff --git a/tests/unit/cron_test.ts b/tests/unit/cron_test.ts index 5f14f6c784..325ee5535d 100644 --- a/tests/unit/cron_test.ts +++ b/tests/unit/cron_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "./test_util.ts"; // @ts-ignore This is not publicly typed namespace, but it's there for sure. diff --git a/tests/unit/custom_event_test.ts b/tests/unit/custom_event_test.ts index b72084eb23..ddefd4cce6 100644 --- a/tests/unit/custom_event_test.ts +++ b/tests/unit/custom_event_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test(function customEventInitializedWithDetail() { diff --git a/tests/unit/dir_test.ts b/tests/unit/dir_test.ts index 1e702f549a..dbf88609c2 100644 --- a/tests/unit/dir_test.ts +++ b/tests/unit/dir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "./test_util.ts"; Deno.test({ permissions: { read: true } }, function dirCwdNotNull() { diff --git a/tests/unit/dom_exception_test.ts b/tests/unit/dom_exception_test.ts index 4e894f5fc8..2e0c9f71e1 100644 --- a/tests/unit/dom_exception_test.ts +++ b/tests/unit/dom_exception_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/error_stack_test.ts b/tests/unit/error_stack_test.ts index 7188b9f53d..fea8913494 100644 --- a/tests/unit/error_stack_test.ts +++ b/tests/unit/error_stack_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertMatch } from "./test_util.ts"; Deno.test(function errorStackMessageLine() { diff --git a/tests/unit/error_test.ts b/tests/unit/error_test.ts index bf0ef50627..48e1f7879b 100644 --- a/tests/unit/error_test.ts +++ b/tests/unit/error_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertThrows, fail } from "./test_util.ts"; Deno.test("Errors work", () => { diff --git a/tests/unit/esnext_test.ts b/tests/unit/esnext_test.ts index 1d5759aafa..2c597ef7ba 100644 --- a/tests/unit/esnext_test.ts +++ b/tests/unit/esnext_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; // TODO(@kitsonk) remove when we are no longer patching TypeScript to have diff --git a/tests/unit/event_source_test.ts b/tests/unit/event_source_test.ts index 242c12d6e8..2ce7178b00 100644 --- a/tests/unit/event_source_test.ts +++ b/tests/unit/event_source_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertStrictEquals } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/event_target_test.ts b/tests/unit/event_target_test.ts index 3f7d8ee24a..ca1c496143 100644 --- a/tests/unit/event_target_test.ts +++ b/tests/unit/event_target_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/event_test.ts b/tests/unit/event_test.ts index bd398fd410..d022e1c1d1 100644 --- a/tests/unit/event_test.ts +++ b/tests/unit/event_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertStringIncludes } from "./test_util.ts"; Deno.test(function eventInitializedWithType() { diff --git a/tests/unit/fetch_test.ts b/tests/unit/fetch_test.ts index 298a266903..094b963e19 100644 --- a/tests/unit/fetch_test.ts +++ b/tests/unit/fetch_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit/ffi_test.ts b/tests/unit/ffi_test.ts index 98338b15e6..9db0ad0c26 100644 --- a/tests/unit/ffi_test.ts +++ b/tests/unit/ffi_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/file_test.ts b/tests/unit/file_test.ts index 1af3a3f84d..38e4dce64a 100644 --- a/tests/unit/file_test.ts +++ b/tests/unit/file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; // deno-lint-ignore no-explicit-any diff --git a/tests/unit/filereader_test.ts b/tests/unit/filereader_test.ts index 158cf53835..21ca25ed48 100644 --- a/tests/unit/filereader_test.ts +++ b/tests/unit/filereader_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test(function fileReaderConstruct() { diff --git a/tests/unit/files_test.ts b/tests/unit/files_test.ts index c22d3deb77..aa211b224d 100644 --- a/tests/unit/files_test.ts +++ b/tests/unit/files_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/fs_events_test.ts b/tests/unit/fs_events_test.ts index 7489626b9f..d9bdf454d5 100644 --- a/tests/unit/fs_events_test.ts +++ b/tests/unit/fs_events_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows, delay } from "./test_util.ts"; diff --git a/tests/unit/get_random_values_test.ts b/tests/unit/get_random_values_test.ts index 75aaf4c1b2..47ba78a325 100644 --- a/tests/unit/get_random_values_test.ts +++ b/tests/unit/get_random_values_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertNotEquals, assertStrictEquals } from "./test_util.ts"; Deno.test(function getRandomValuesInt8Array() { diff --git a/tests/unit/globals_test.ts b/tests/unit/globals_test.ts index 6de228e1c9..84773dbf47 100644 --- a/tests/unit/globals_test.ts +++ b/tests/unit/globals_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-node-globals import { diff --git a/tests/unit/headers_test.ts b/tests/unit/headers_test.ts index ea72f784b5..969b2a36f4 100644 --- a/tests/unit/headers_test.ts +++ b/tests/unit/headers_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "./test_util.ts"; const { inspectArgs, diff --git a/tests/unit/http_test.ts b/tests/unit/http_test.ts index 355b155afd..809b36227b 100644 --- a/tests/unit/http_test.ts +++ b/tests/unit/http_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-nocheck `Deno.serveHttp()` was soft-removed in Deno 2. // deno-lint-ignore-file no-deprecated-deno-api diff --git a/tests/unit/image_bitmap_test.ts b/tests/unit/image_bitmap_test.ts index 0066311820..ca5b85c178 100644 --- a/tests/unit/image_bitmap_test.ts +++ b/tests/unit/image_bitmap_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; diff --git a/tests/unit/image_data_test.ts b/tests/unit/image_data_test.ts index 7156301a05..c6dfb0d372 100644 --- a/tests/unit/image_data_test.ts +++ b/tests/unit/image_data_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; diff --git a/tests/unit/internals_test.ts b/tests/unit/internals_test.ts index bb4c21793e..7ca9b6d336 100644 --- a/tests/unit/internals_test.ts +++ b/tests/unit/internals_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; Deno.test(function internalsExists() { diff --git a/tests/unit/intl_test.ts b/tests/unit/intl_test.ts index 6e4de378c9..e177dc45b0 100644 --- a/tests/unit/intl_test.ts +++ b/tests/unit/intl_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test("Intl.v8BreakIterator should be undefined", () => { diff --git a/tests/unit/jupyter_test.ts b/tests/unit/jupyter_test.ts index 07defe2305..e29bb2b300 100644 --- a/tests/unit/jupyter_test.ts +++ b/tests/unit/jupyter_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/kv_queue_test.ts b/tests/unit/kv_queue_test.ts index d92977169f..569eb92800 100644 --- a/tests/unit/kv_queue_test.ts +++ b/tests/unit/kv_queue_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertFalse } from "./test_util.ts"; Deno.test({}, async function queueTestDbClose() { diff --git a/tests/unit/kv_queue_test_no_db_close.ts b/tests/unit/kv_queue_test_no_db_close.ts index 947e1c5e62..7c4bbf271e 100644 --- a/tests/unit/kv_queue_test_no_db_close.ts +++ b/tests/unit/kv_queue_test_no_db_close.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertNotEquals } from "./test_util.ts"; Deno.test({ diff --git a/tests/unit/kv_queue_undelivered_test.ts b/tests/unit/kv_queue_undelivered_test.ts index 1fcefe7e26..2fbfefbd69 100644 --- a/tests/unit/kv_queue_undelivered_test.ts +++ b/tests/unit/kv_queue_undelivered_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; const sleep = (time: number) => new Promise((r) => setTimeout(r, time)); diff --git a/tests/unit/kv_test.ts b/tests/unit/kv_test.ts index e603bc196d..47e1305c94 100644 --- a/tests/unit/kv_test.ts +++ b/tests/unit/kv_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, @@ -1951,14 +1951,14 @@ dbTest("Invalid backoffSchedule", async (db) => { await db.enqueue("foo", { backoffSchedule: [1, 1, 1, 1, 1, 1] }); }, TypeError, - "Invalid backoffSchedule", + "Invalid backoffSchedule, max 5 intervals allowed", ); await assertRejects( async () => { await db.enqueue("foo", { backoffSchedule: [3600001] }); }, TypeError, - "Invalid backoffSchedule", + "Invalid backoffSchedule, interval at index 0 is invalid", ); }); diff --git a/tests/unit/link_test.ts b/tests/unit/link_test.ts index dfa72479c5..5093c5720d 100644 --- a/tests/unit/link_test.ts +++ b/tests/unit/link_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/lint_plugin_test.ts b/tests/unit/lint_plugin_test.ts new file mode 100644 index 0000000000..6c71501c46 --- /dev/null +++ b/tests/unit/lint_plugin_test.ts @@ -0,0 +1,926 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +import { assertEquals } from "./test_util.ts"; +import { assertSnapshot } from "@std/testing/snapshot"; + +// TODO(@marvinhagemeister) Remove once we land "official" types +export interface LintReportData { + // deno-lint-ignore no-explicit-any + node: any; + message: string; +} +// TODO(@marvinhagemeister) Remove once we land "official" types +interface LintContext { + id: string; +} +// TODO(@marvinhagemeister) Remove once we land "official" types +// deno-lint-ignore no-explicit-any +type LintVisitor = Record void>; + +// TODO(@marvinhagemeister) Remove once we land "official" types +interface LintRule { + create(ctx: LintContext): LintVisitor; + destroy?(): void; +} + +// TODO(@marvinhagemeister) Remove once we land "official" types +interface LintPlugin { + name: string; + rules: Record; +} + +function runLintPlugin(plugin: LintPlugin, fileName: string, source: string) { + // deno-lint-ignore no-explicit-any + return (Deno as any)[(Deno as any).internal].runLintPlugin( + plugin, + fileName, + source, + ); +} + +function testPlugin( + source: string, + rule: LintRule, +) { + const plugin = { + name: "test-plugin", + rules: { + testRule: rule, + }, + }; + + return runLintPlugin(plugin, "source.tsx", source); +} + +interface VisitResult { + selector: string; + kind: "enter" | "exit"; + // deno-lint-ignore no-explicit-any + node: any; +} + +function testVisit( + source: string, + ...selectors: string[] +): VisitResult[] { + const result: VisitResult[] = []; + + testPlugin(source, { + create() { + const visitor: LintVisitor = {}; + + for (const s of selectors) { + visitor[s] = (node) => { + result.push({ + kind: s.endsWith(":exit") ? "exit" : "enter", + selector: s, + node, + }); + }; + } + + return visitor; + }, + }); + + return result; +} + +async function testSnapshot( + t: Deno.TestContext, + source: string, + ...selectors: string[] +) { + const log: unknown[] = []; + + testPlugin(source, { + create() { + const visitor: LintVisitor = {}; + + for (const s of selectors) { + visitor[s] = (node) => { + log.push(node[Symbol.for("Deno.lint.toJsValue")]()); + }; + } + + return visitor; + }, + }); + + assertEquals(log.length > 0, true); + await assertSnapshot(t, log[0]); +} + +Deno.test("Plugin - visitor enter/exit", () => { + const enter = testVisit( + "foo", + "Identifier", + ); + assertEquals(enter[0].node.type, "Identifier"); + + const exit = testVisit( + "foo", + "Identifier:exit", + ); + assertEquals(exit[0].node.type, "Identifier"); + + const both = testVisit("foo", "Identifier", "Identifier:exit"); + assertEquals(both.map((t) => t.selector), ["Identifier", "Identifier:exit"]); +}); + +Deno.test("Plugin - visitor descendant", () => { + let result = testVisit( + "if (false) foo; if (false) bar()", + "IfStatement CallExpression", + ); + assertEquals(result[0].node.type, "CallExpression"); + assertEquals(result[0].node.callee.name, "bar"); + + result = testVisit( + "if (false) foo; foo()", + "IfStatement IfStatement", + ); + assertEquals(result, []); + + result = testVisit( + "if (false) foo; foo()", + "* CallExpression", + ); + assertEquals(result[0].node.type, "CallExpression"); +}); + +Deno.test("Plugin - visitor child combinator", () => { + let result = testVisit( + "if (false) foo; if (false) { bar; }", + "IfStatement > ExpressionStatement > Identifier", + ); + assertEquals(result[0].node.name, "foo"); + + result = testVisit( + "if (false) foo; foo()", + "IfStatement IfStatement", + ); + assertEquals(result, []); +}); + +Deno.test("Plugin - visitor next sibling", () => { + const result = testVisit( + "if (false) foo; if (false) bar;", + "IfStatement + IfStatement Identifier", + ); + assertEquals(result[0].node.name, "bar"); +}); + +Deno.test("Plugin - visitor subsequent sibling", () => { + const result = testVisit( + "if (false) foo; if (false) bar; if (false) baz;", + "IfStatement ~ IfStatement Identifier", + ); + assertEquals(result.map((r) => r.node.name), ["bar", "baz"]); +}); + +Deno.test("Plugin - visitor attr", () => { + let result = testVisit( + "for (const a of b) {}", + "[await]", + ); + assertEquals(result[0].node.await, false); + + result = testVisit( + "for await (const a of b) {}", + "[await=true]", + ); + assertEquals(result[0].node.await, true); + + result = testVisit( + "for await (const a of b) {}", + "ForOfStatement[await=true]", + ); + assertEquals(result[0].node.await, true); + + result = testVisit( + "for (const a of b) {}", + "ForOfStatement[await != true]", + ); + assertEquals(result[0].node.await, false); + + result = testVisit( + "async function *foo() {}", + "FunctionDeclaration[async=true][generator=true]", + ); + assertEquals(result[0].node.type, "FunctionDeclaration"); + + result = testVisit( + "foo", + "[name='foo']", + ); + assertEquals(result[0].node.name, "foo"); +}); + +Deno.test("Plugin - visitor attr to check type", () => { + let result = testVisit( + "foo", + "Identifier[type]", + ); + assertEquals(result[0].node.type, "Identifier"); + + result = testVisit( + "foo", + "Identifier[type='Identifier']", + ); + assertEquals(result[0].node.type, "Identifier"); +}); + +Deno.test("Plugin - visitor attr non-existing", () => { + const result = testVisit( + "foo", + "[non-existing]", + ); + assertEquals(result, []); +}); + +Deno.test("Plugin - visitor attr length special case", () => { + let result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length=2]", + ); + assertEquals(result[0].node.arguments.length, 2); + + result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length>1]", + ); + assertEquals(result[0].node.arguments.length, 2); + + result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length<2]", + ); + assertEquals(result[0].node.arguments.length, 1); + + result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length<=3]", + ); + assertEquals(result[0].node.arguments.length, 1); + assertEquals(result[1].node.arguments.length, 2); + + result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length>=1]", + ); + assertEquals(result[0].node.arguments.length, 1); + assertEquals(result[1].node.arguments.length, 2); +}); + +Deno.test("Plugin - visitor :first-child", () => { + const result = testVisit( + "{ foo; bar }", + "BlockStatement ExpressionStatement:first-child Identifier", + ); + assertEquals(result[0].node.name, "foo"); +}); + +Deno.test("Plugin - visitor :last-child", () => { + const result = testVisit( + "{ foo; bar }", + "BlockStatement ExpressionStatement:last-child Identifier", + ); + assertEquals(result[0].node.name, "bar"); +}); + +Deno.test("Plugin - visitor :nth-child", () => { + let result = testVisit( + "{ foo; bar; baz; foobar; }", + "BlockStatement ExpressionStatement:nth-child(2) Identifier", + ); + assertEquals(result[0].node.name, "bar"); + + result = testVisit( + "{ foo; bar; baz; foobar; }", + "BlockStatement ExpressionStatement:nth-child(2n) Identifier", + ); + assertEquals(result[0].node.name, "foo"); + assertEquals(result[1].node.name, "baz"); + + result = testVisit( + "{ foo; bar; baz; foobar; }", + "BlockStatement ExpressionStatement:nth-child(2n + 1) Identifier", + ); + assertEquals(result[0].node.name, "bar"); + assertEquals(result[1].node.name, "foobar"); + + result = testVisit( + "{ foo; bar; baz; foobar; }", + "BlockStatement *:nth-child(2n + 1 of ExpressionStatement) Identifier", + ); + assertEquals(result[0].node.name, "bar"); + assertEquals(result[1].node.name, "foobar"); +}); + +Deno.test("Plugin - Program", async (t) => { + await testSnapshot(t, "", "Program"); +}); + +Deno.test("Plugin - ImportDeclaration", async (t) => { + await testSnapshot(t, 'import "foo";', "ImportDeclaration"); + await testSnapshot(t, 'import foo from "foo";', "ImportDeclaration"); + await testSnapshot(t, 'import * as foo from "foo";', "ImportDeclaration"); + await testSnapshot( + t, + 'import { foo, bar as baz } from "foo";', + "ImportDeclaration", + ); + await testSnapshot( + t, + 'import foo from "foo" with { type: "json" };', + "ImportDeclaration", + ); +}); + +Deno.test("Plugin - ExportNamedDeclaration", async (t) => { + await testSnapshot(t, 'export { foo } from "foo";', "ExportNamedDeclaration"); + await testSnapshot( + t, + 'export { bar as baz } from "foo";', + "ExportNamedDeclaration", + ); + await testSnapshot( + t, + 'export { foo } from "foo" with { type: "json" };', + "ExportNamedDeclaration", + ); +}); + +Deno.test("Plugin - ExportDefaultDeclaration", async (t) => { + await testSnapshot( + t, + "export default function foo() {}", + "ExportDefaultDeclaration", + ); + await testSnapshot( + t, + "export default function () {}", + "ExportDefaultDeclaration", + ); + await testSnapshot( + t, + "export default class Foo {}", + "ExportDefaultDeclaration", + ); + await testSnapshot( + t, + "export default class {}", + "ExportDefaultDeclaration", + ); + await testSnapshot(t, "export default bar;", "ExportDefaultDeclaration"); + await testSnapshot( + t, + "export default interface Foo {};", + "ExportDefaultDeclaration", + ); +}); + +Deno.test("Plugin - ExportAllDeclaration", async (t) => { + await testSnapshot(t, 'export * from "foo";', "ExportAllDeclaration"); + await testSnapshot(t, 'export * as foo from "foo";', "ExportAllDeclaration"); + await testSnapshot( + t, + 'export * from "foo" with { type: "json" };', + "ExportAllDeclaration", + ); +}); + +Deno.test("Plugin - TSExportAssignment", async (t) => { + await testSnapshot(t, "export = foo;", "TSExportAssignment"); +}); + +Deno.test("Plugin - TSNamespaceExportDeclaration", async (t) => { + await testSnapshot( + t, + "export as namespace A;", + "TSNamespaceExportDeclaration", + ); +}); + +Deno.test("Plugin - TSImportEqualsDeclaration", async (t) => { + await testSnapshot(t, "import a = b", "TSImportEqualsDeclaration"); + await testSnapshot( + t, + 'import a = require("foo")', + "TSImportEqualsDeclaration", + ); +}); + +Deno.test("Plugin - BlockStatement", async (t) => { + await testSnapshot(t, "{ foo; }", "BlockStatement"); +}); + +Deno.test("Plugin - BreakStatement", async (t) => { + await testSnapshot(t, "while (false) break;", "BreakStatement"); + await testSnapshot(t, "foo: while (false) break foo;", "BreakStatement"); +}); + +Deno.test("Plugin - ContinueStatement", async (t) => { + await testSnapshot(t, "continue;", "ContinueStatement"); + await testSnapshot(t, "continue foo;", "ContinueStatement"); +}); + +Deno.test("Plugin - DebuggerStatement", async (t) => { + await testSnapshot(t, "debugger;", "DebuggerStatement"); +}); + +Deno.test("Plugin - DoWhileStatement", async (t) => { + await testSnapshot(t, "do {} while (foo);", "DoWhileStatement"); +}); + +Deno.test("Plugin - ExpressionStatement", async (t) => { + await testSnapshot(t, "foo;", "ExpressionStatement"); +}); + +Deno.test("Plugin - ForInStatement", async (t) => { + await testSnapshot(t, "for (a in b) {}", "ForInStatement"); +}); + +Deno.test("Plugin - ForOfStatement", async (t) => { + await testSnapshot(t, "for (a of b) {}", "ForOfStatement"); + await testSnapshot(t, "for await (a of b) {}", "ForOfStatement"); +}); + +Deno.test("Plugin - ForStatement", async (t) => { + await testSnapshot(t, "for (;;) {}", "ForStatement"); + await testSnapshot(t, "for (a; b; c) {}", "ForStatement"); +}); + +Deno.test("Plugin - IfStatement", async (t) => { + await testSnapshot(t, "if (foo) {}", "IfStatement"); + await testSnapshot(t, "if (foo) {} else {}", "IfStatement"); +}); + +Deno.test("Plugin - LabeledStatement", async (t) => { + await testSnapshot(t, "foo: {};", "LabeledStatement"); +}); + +Deno.test("Plugin - ReturnStatement", async (t) => { + await testSnapshot(t, "return", "ReturnStatement"); + await testSnapshot(t, "return foo;", "ReturnStatement"); +}); + +Deno.test("Plugin - SwitchStatement", async (t) => { + await testSnapshot( + t, + `switch (foo) { + case foo: + case bar: + break; + default: + {} + }`, + "SwitchStatement", + ); +}); + +Deno.test("Plugin - ThrowStatement", async (t) => { + await testSnapshot(t, "throw foo;", "ThrowStatement"); +}); + +Deno.test("Plugin - TryStatement", async (t) => { + await testSnapshot(t, "try {} catch {};", "TryStatement"); + await testSnapshot(t, "try {} catch (e) {};", "TryStatement"); + await testSnapshot(t, "try {} finally {};", "TryStatement"); +}); + +Deno.test("Plugin - WhileStatement", async (t) => { + await testSnapshot(t, "while (foo) {}", "WhileStatement"); +}); + +Deno.test("Plugin - WithStatement", async (t) => { + await testSnapshot(t, "with ([]) {}", "WithStatement"); +}); + +Deno.test("Plugin - ArrayExpression", async (t) => { + await testSnapshot(t, "[[],,[]]", "ArrayExpression"); +}); + +Deno.test("Plugin - ArrowFunctionExpression", async (t) => { + await testSnapshot(t, "() => {}", "ArrowFunctionExpression"); + await testSnapshot(t, "async () => {}", "ArrowFunctionExpression"); + await testSnapshot( + t, + "(a: number, ...b: any[]): any => {}", + "ArrowFunctionExpression", + ); +}); + +Deno.test("Plugin - AssignmentExpression", async (t) => { + await testSnapshot(t, "a = b", "AssignmentExpression"); + await testSnapshot(t, "a = a ??= b", "AssignmentExpression"); +}); + +Deno.test("Plugin - AwaitExpression", async (t) => { + await testSnapshot(t, "await foo;", "AwaitExpression"); +}); + +Deno.test("Plugin - BinaryExpression", async (t) => { + await testSnapshot(t, "a > b", "BinaryExpression"); + await testSnapshot(t, "a >= b", "BinaryExpression"); + await testSnapshot(t, "a < b", "BinaryExpression"); + await testSnapshot(t, "a <= b", "BinaryExpression"); + await testSnapshot(t, "a == b", "BinaryExpression"); + await testSnapshot(t, "a === b", "BinaryExpression"); + await testSnapshot(t, "a != b", "BinaryExpression"); + await testSnapshot(t, "a !== b", "BinaryExpression"); + await testSnapshot(t, "a << b", "BinaryExpression"); + await testSnapshot(t, "a >> b", "BinaryExpression"); + await testSnapshot(t, "a >>> b", "BinaryExpression"); + await testSnapshot(t, "a + b", "BinaryExpression"); + await testSnapshot(t, "a - b", "BinaryExpression"); + await testSnapshot(t, "a * b", "BinaryExpression"); + await testSnapshot(t, "a / b", "BinaryExpression"); + await testSnapshot(t, "a % b", "BinaryExpression"); + await testSnapshot(t, "a | b", "BinaryExpression"); + await testSnapshot(t, "a ^ b", "BinaryExpression"); + await testSnapshot(t, "a & b", "BinaryExpression"); + await testSnapshot(t, "a in b", "BinaryExpression"); + await testSnapshot(t, "a ** b", "BinaryExpression"); +}); + +Deno.test("Plugin - CallExpression", async (t) => { + await testSnapshot(t, "foo();", "CallExpression"); + await testSnapshot(t, "foo(a, ...b);", "CallExpression"); + await testSnapshot(t, "foo?.();", "CallExpression"); + await testSnapshot(t, "foo();", "CallExpression"); +}); + +Deno.test("Plugin - ChainExpression", async (t) => { + await testSnapshot(t, "a?.b", "ChainExpression"); +}); + +Deno.test("Plugin - ClassExpression", async (t) => { + await testSnapshot(t, "a = class {}", "ClassExpression"); + await testSnapshot(t, "a = class Foo {}", "ClassExpression"); + await testSnapshot(t, "a = class Foo extends Bar {}", "ClassExpression"); + await testSnapshot( + t, + "a = class Foo extends Bar implements Baz, Baz2 {}", + "ClassExpression", + ); + await testSnapshot(t, "a = class Foo {}", "ClassExpression"); + await testSnapshot(t, "a = class { foo() {} }", "ClassExpression"); + await testSnapshot(t, "a = class { #foo() {} }", "ClassExpression"); + await testSnapshot(t, "a = class { foo: number }", "ClassExpression"); + await testSnapshot(t, "a = class { foo = bar }", "ClassExpression"); + await testSnapshot( + t, + "a = class { constructor(public foo: string) {} }", + "ClassExpression", + ); + await testSnapshot(t, "a = class { #foo: number = bar }", "ClassExpression"); + await testSnapshot(t, "a = class { static foo = bar }", "ClassExpression"); + await testSnapshot( + t, + "a = class { static foo; static { foo = bar } }", + "ClassExpression", + ); +}); + +Deno.test("Plugin - ConditionalExpression", async (t) => { + await testSnapshot(t, "a ? b : c", "ConditionalExpression"); +}); + +Deno.test("Plugin - FunctionExpression", async (t) => { + await testSnapshot(t, "a = function () {}", "FunctionExpression"); + await testSnapshot(t, "a = function foo() {}", "FunctionExpression"); + await testSnapshot( + t, + "a = function (a?: number, ...b: any[]): any {}", + "FunctionExpression", + ); + await testSnapshot(t, "a = async function* () {}", "FunctionExpression"); +}); + +Deno.test("Plugin - Identifier", async (t) => { + await testSnapshot(t, "a", "Identifier"); +}); + +Deno.test("Plugin - ImportExpression", async (t) => { + await testSnapshot( + t, + "import('foo', { with: { type: 'json' } })", + "ImportExpression", + ); +}); + +Deno.test("Plugin - LogicalExpression", async (t) => { + await testSnapshot(t, "a && b", "LogicalExpression"); + await testSnapshot(t, "a || b", "LogicalExpression"); + await testSnapshot(t, "a ?? b", "LogicalExpression"); +}); + +Deno.test("Plugin - MemberExpression", async (t) => { + await testSnapshot(t, "a.b", "MemberExpression"); + await testSnapshot(t, "a['b']", "MemberExpression"); +}); + +Deno.test("Plugin - MetaProperty", async (t) => { + await testSnapshot(t, "import.meta", "MetaProperty"); +}); + +Deno.test("Plugin - NewExpression", async (t) => { + await testSnapshot(t, "new Foo()", "NewExpression"); + await testSnapshot(t, "new Foo(a, ...b)", "NewExpression"); +}); + +Deno.test("Plugin - ObjectExpression", async (t) => { + await testSnapshot(t, "a = {}", "ObjectExpression"); + await testSnapshot(t, "a = { a }", "ObjectExpression"); + await testSnapshot(t, "a = { b: c, [c]: d }", "ObjectExpression"); +}); + +Deno.test("Plugin - PrivateIdentifier", async (t) => { + await testSnapshot(t, "class Foo { #foo = foo }", "PrivateIdentifier"); +}); + +Deno.test("Plugin - SequenceExpression", async (t) => { + await testSnapshot(t, "(a, b)", "SequenceExpression"); +}); + +Deno.test("Plugin - Super", async (t) => { + await testSnapshot( + t, + "class Foo extends Bar { constructor() { super(); } }", + "Super", + ); +}); + +Deno.test("Plugin - TaggedTemplateExpression", async (t) => { + await testSnapshot(t, "foo`foo ${bar} baz`", "TaggedTemplateExpression"); +}); + +Deno.test("Plugin - TemplateLiteral", async (t) => { + await testSnapshot(t, "`foo ${bar} baz`", "TemplateLiteral"); +}); + +Deno.test("Plugin - ThisExpression", async (t) => { + await testSnapshot(t, "this", "ThisExpression"); +}); + +Deno.test("Plugin - TSAsExpression", async (t) => { + await testSnapshot(t, "a as b", "TSAsExpression"); + await testSnapshot(t, "a as const", "TSAsExpression"); +}); + +Deno.test("Plugin - TSNonNullExpression", async (t) => { + await testSnapshot(t, "a!", "TSNonNullExpression"); +}); + +Deno.test("Plugin - TSSatisfiesExpression", async (t) => { + await testSnapshot(t, "a satisfies b", "TSSatisfiesExpression"); +}); + +Deno.test("Plugin - UnaryExpression", async (t) => { + await testSnapshot(t, "typeof a", "UnaryExpression"); + await testSnapshot(t, "void 0", "UnaryExpression"); + await testSnapshot(t, "-a", "UnaryExpression"); + await testSnapshot(t, "+a", "UnaryExpression"); +}); + +Deno.test("Plugin - UpdateExpression", async (t) => { + await testSnapshot(t, "a++", "UpdateExpression"); + await testSnapshot(t, "++a", "UpdateExpression"); + await testSnapshot(t, "a--", "UpdateExpression"); + await testSnapshot(t, "--a", "UpdateExpression"); +}); + +Deno.test("Plugin - YieldExpression", async (t) => { + await testSnapshot(t, "function* foo() { yield bar; }", "YieldExpression"); +}); + +Deno.test("Plugin - Literal", async (t) => { + await testSnapshot(t, "1", "Literal"); + await testSnapshot(t, "'foo'", "Literal"); + await testSnapshot(t, '"foo"', "Literal"); + await testSnapshot(t, "true", "Literal"); + await testSnapshot(t, "false", "Literal"); + await testSnapshot(t, "null", "Literal"); + await testSnapshot(t, "1n", "Literal"); + await testSnapshot(t, "/foo/g", "Literal"); +}); + +Deno.test("Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr", async (t) => { + await testSnapshot(t, "

", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, '
', "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "
foo{2}
", "JSXElement"); + await testSnapshot(t, "", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "", "JSXElement"); + await testSnapshot(t, " />", "JSXElement"); +}); + +Deno.test("Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment", async (t) => { + await testSnapshot(t, "<>", "JSXFragment"); + await testSnapshot(t, "<>foo{2}", "JSXFragment"); +}); + +Deno.test("Plugin - TSAsExpression", async (t) => { + await testSnapshot(t, "a as any", "TSAsExpression"); + await testSnapshot(t, '"foo" as const', "TSAsExpression"); +}); + +Deno.test("Plugin - TSEnumDeclaration", async (t) => { + await testSnapshot(t, "enum Foo {}", "TSEnumDeclaration"); + await testSnapshot(t, "const enum Foo {}", "TSEnumDeclaration"); + await testSnapshot(t, "enum Foo { A, B }", "TSEnumDeclaration"); + await testSnapshot(t, 'enum Foo { "a-b" }', "TSEnumDeclaration"); + await testSnapshot( + t, + "enum Foo { A = 1, B = 2, C = A | B }", + "TSEnumDeclaration", + ); +}); + +Deno.test("Plugin - TSInterface", async (t) => { + await testSnapshot(t, "interface A {}", "TSInterface"); + await testSnapshot(t, "interface A {}", "TSInterface"); + await testSnapshot(t, "interface A extends Foo, Bar {}", "TSInterface"); + await testSnapshot(t, "interface A { foo: any, bar?: any }", "TSInterface"); + await testSnapshot( + t, + "interface A { readonly [key: string]: any }", + "TSInterface", + ); + + await testSnapshot(t, "interface A { readonly a: any }", "TSInterface"); + await testSnapshot(t, "interface A { (a: T): T }", "TSInterface"); + await testSnapshot(t, "interface A { new (a: T): T }", "TSInterface"); + await testSnapshot(t, "interface A { a: new (a: T) => T }", "TSInterface"); + await testSnapshot(t, "interface A { get a(): string }", "TSInterface"); + await testSnapshot(t, "interface A { set a(v: string) }", "TSInterface"); + + await testSnapshot( + t, + "interface A { a(arg?: any, ...args: any[]): any }", + "TSInterface", + ); +}); + +Deno.test("Plugin - TSSatisfiesExpression", async (t) => { + await testSnapshot(t, "const a = {} satisfies A", "TSSatisfiesExpression"); +}); + +Deno.test("Plugin - TSTypeAliasDeclaration", async (t) => { + await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); + await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); + await testSnapshot(t, "declare type A = any", "TSTypeAliasDeclaration"); +}); + +Deno.test("Plugin - TSNonNullExpression", async (t) => { + await testSnapshot(t, "a!", "TSNonNullExpression"); +}); + +Deno.test("Plugin - TSUnionType", async (t) => { + await testSnapshot(t, "type A = B | C", "TSUnionType"); +}); + +Deno.test("Plugin - TSIntersectionType", async (t) => { + await testSnapshot(t, "type A = B & C", "TSIntersectionType"); +}); + +Deno.test("Plugin - TSModuleDeclaration", async (t) => { + await testSnapshot(t, "module A {}", "TSModuleDeclaration"); + await testSnapshot( + t, + "declare module A { export function A(): void }", + "TSModuleDeclaration", + ); +}); + +Deno.test("Plugin - TSModuleDeclaration + TSModuleBlock", async (t) => { + await testSnapshot(t, "module A {}", "TSModuleDeclaration"); + await testSnapshot( + t, + "namespace A { namespace B {} }", + "TSModuleDeclaration", + ); +}); + +Deno.test("Plugin - TSQualifiedName", async (t) => { + await testSnapshot(t, "type A = a.b;", "TSQualifiedName"); +}); + +Deno.test("Plugin - TSTypeLiteral", async (t) => { + await testSnapshot(t, "type A = { a: 1 };", "TSTypeLiteral"); +}); + +Deno.test("Plugin - TSOptionalType", async (t) => { + await testSnapshot(t, "type A = [number?]", "TSOptionalType"); +}); + +Deno.test("Plugin - TSRestType", async (t) => { + await testSnapshot(t, "type A = [...number[]]", "TSRestType"); +}); + +Deno.test("Plugin - TSConditionalType", async (t) => { + await testSnapshot( + t, + "type A = B extends C ? number : string;", + "TSConditionalType", + ); +}); + +Deno.test("Plugin - TSInferType", async (t) => { + await testSnapshot( + t, + "type A = T extends Array ? Item : T;", + "TSInferType", + ); +}); + +Deno.test("Plugin - TSTypeOperator", async (t) => { + await testSnapshot(t, "type A = keyof B", "TSTypeOperator"); + await testSnapshot(t, "declare const sym1: unique symbol;", "TSTypeOperator"); + await testSnapshot(t, "type A = readonly []", "TSTypeOperator"); +}); + +Deno.test("Plugin - TSMappedType", async (t) => { + await testSnapshot( + t, + "type A = { [P in keyof T]: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { -readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { +readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]?: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]-?: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]+?: boolean; };", + "TSMappedType", + ); +}); + +Deno.test("Plugin - TSLiteralType", async (t) => { + await testSnapshot(t, "type A = true", "TSLiteralType"); + await testSnapshot(t, "type A = false", "TSLiteralType"); + await testSnapshot(t, "type A = 1", "TSLiteralType"); + await testSnapshot(t, 'type A = "foo"', "TSLiteralType"); +}); + +Deno.test("Plugin - TSTemplateLiteralType", async (t) => { + await testSnapshot(t, "type A = `a ${string}`", "TSTemplateLiteralType"); +}); + +Deno.test("Plugin - TSTupleType + TSArrayType", async (t) => { + await testSnapshot(t, "type A = [number]", "TSTupleType"); + await testSnapshot(t, "type A = [x: number]", "TSTupleType"); + await testSnapshot(t, "type A = [x: number]", "TSTupleType"); + await testSnapshot(t, "type A = [...x: number[]]", "TSTupleType"); +}); + +Deno.test("Plugin - TSArrayType", async (t) => { + await testSnapshot(t, "type A = number[]", "TSArrayType"); +}); + +Deno.test("Plugin - TSTypeQuery", async (t) => { + await testSnapshot(t, "type A = typeof B", "TSTypeQuery"); +}); + +Deno.test("Plugin - TS keywords", async (t) => { + await testSnapshot(t, "type A = any", "TSAnyKeyword"); + await testSnapshot(t, "type A = bigint", "TSBigIntKeyword"); + await testSnapshot(t, "type A = boolean", "TSBooleanKeyword"); + await testSnapshot(t, "type A = intrinsic", "TSIntrinsicKeyword"); + await testSnapshot(t, "type A = never", "TSNeverKeyword"); + await testSnapshot(t, "type A = null", "TSNullKeyword"); + await testSnapshot(t, "type A = number", "TSNumberKeyword"); + await testSnapshot(t, "type A = object", "TSObjectKeyword"); + await testSnapshot(t, "type A = string", "TSStringKeyword"); + await testSnapshot(t, "type A = symbol", "TSSymbolKeyword"); + await testSnapshot(t, "type A = undefined", "TSUndefinedKeyword"); + await testSnapshot(t, "type A = unknown", "TSUnknownKeyword"); + await testSnapshot(t, "type A = void", "TSVoidKeyword"); +}); diff --git a/tests/unit/lint_selectors_test.ts b/tests/unit/lint_selectors_test.ts new file mode 100644 index 0000000000..e9e4dacb4e --- /dev/null +++ b/tests/unit/lint_selectors_test.ts @@ -0,0 +1,613 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +import { assertEquals } from "@std/assert/equals"; +import { + ATTR_BIN_NODE, + ATTR_EXISTS_NODE, + BinOp, + ELEM_NODE, + Lexer, + parseSelector, + PSEUDO_FIRST_CHILD, + PSEUDO_HAS, + PSEUDO_LAST_CHILD, + PSEUDO_NOT, + PSEUDO_NTH_CHILD, + RELATION_NODE, + splitSelectors, + Token, +} from "../../cli/js/40_lint_selector.js"; +import { assertThrows } from "@std/assert"; + +Deno.test("splitSelectors", () => { + assertEquals(splitSelectors("*"), ["*"]); + assertEquals(splitSelectors("*,*"), ["*", "*"]); + assertEquals(splitSelectors("*,* "), ["*", "*"]); + assertEquals(splitSelectors("foo"), ["foo"]); + assertEquals(splitSelectors("foo, bar"), ["foo", "bar"]); + assertEquals(splitSelectors("foo:f(bar, baz)"), ["foo:f(bar, baz)"]); + assertEquals(splitSelectors("foo:f(bar, baz), foobar"), [ + "foo:f(bar, baz)", + "foobar", + ]); +}); + +interface LexState { + token: number; + value: string; +} + +function testLexer(input: string): LexState[] { + const out: LexState[] = []; + const l = new Lexer(input); + + while (l.token !== Token.EOF) { + out.push({ token: l.token, value: l.value }); + l.next(); + } + + return out; +} + +const Tags: Record = { Foo: 1, Bar: 2, FooBar: 3 }; +const Attrs: Record = { foo: 1, bar: 2, foobar: 3, attr: 4 }; +const toTag = (name: string): number => Tags[name]; +const toAttr = (name: string): number => Attrs[name]; + +const testParse = (input: string) => parseSelector(input, toTag, toAttr); + +Deno.test("Lexer - Elem", () => { + assertEquals(testLexer("Foo"), [ + { token: Token.Word, value: "Foo" }, + ]); + assertEquals(testLexer("foo-bar"), [ + { token: Token.Word, value: "foo-bar" }, + ]); + assertEquals(testLexer("foo_bar"), [ + { token: Token.Word, value: "foo_bar" }, + ]); + assertEquals(testLexer("Foo Bar Baz"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Bar" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Baz" }, + ]); + assertEquals(testLexer("Foo Bar Baz"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Bar" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Baz" }, + ]); +}); + +Deno.test("Lexer - Relation >", () => { + assertEquals(testLexer("Foo > Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: ">" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("Foo>Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: ">" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer(">Bar"), [ + { token: Token.Op, value: ">" }, + { token: Token.Word, value: "Bar" }, + ]); +}); + +Deno.test("Lexer - Relation +", () => { + assertEquals(testLexer("Foo + Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: "+" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("Foo+Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: "+" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("+Bar"), [ + { token: Token.Op, value: "+" }, + { token: Token.Word, value: "Bar" }, + ]); +}); + +Deno.test("Lexer - Relation ~", () => { + assertEquals(testLexer("Foo ~ Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: "~" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("Foo~Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: "~" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("~Bar"), [ + { token: Token.Op, value: "~" }, + { token: Token.Word, value: "Bar" }, + ]); + + assertEquals(testLexer("Foo Bar ~ Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Bar" }, + { token: Token.Op, value: "~" }, + { token: Token.Word, value: "Bar" }, + ]); +}); + +Deno.test("Lexer - Attr", () => { + assertEquals(testLexer("[attr]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr=1]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "=" }, + { token: Token.Word, value: "1" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr='foo']"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "=" }, + { token: Token.String, value: "foo" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr>=2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: ">=" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr<=2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "<=" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr>2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: ">" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr<2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "<" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr!=2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "!=" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr.foo=1]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Dot, value: "" }, + { token: Token.Word, value: "foo" }, + { token: Token.Op, value: "=" }, + { token: Token.Word, value: "1" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr] [attr]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.BracketClose, value: "" }, + { token: Token.Space, value: "" }, + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("Foo[attr][attr2=1]"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.BracketClose, value: "" }, + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr2" }, + { token: Token.Op, value: "=" }, + { token: Token.Word, value: "1" }, + { token: Token.BracketClose, value: "" }, + ]); +}); + +Deno.test("Lexer - Pseudo", () => { + assertEquals(testLexer(":foo-bar"), [ + { token: Token.Colon, value: "" }, + { token: Token.Word, value: "foo-bar" }, + ]); + assertEquals(testLexer("Foo:foo-bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Colon, value: "" }, + { token: Token.Word, value: "foo-bar" }, + ]); + assertEquals(testLexer(":foo-bar(baz)"), [ + { token: Token.Colon, value: "" }, + { token: Token.Word, value: "foo-bar" }, + { token: Token.BraceOpen, value: "" }, + { token: Token.Word, value: "baz" }, + { token: Token.BraceClose, value: "" }, + ]); + assertEquals(testLexer(":foo-bar(2n + 1)"), [ + { token: Token.Colon, value: "" }, + { token: Token.Word, value: "foo-bar" }, + { token: Token.BraceOpen, value: "" }, + { token: Token.Word, value: "2n" }, + { token: Token.Op, value: "+" }, + { token: Token.Word, value: "1" }, + { token: Token.BraceClose, value: "" }, + ]); +}); + +Deno.test("Parser - Elem", () => { + assertEquals(testParse("Foo"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + ]]); +}); + +Deno.test("Parser - Relation (descendant)", () => { + assertEquals(testParse("Foo Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { + type: RELATION_NODE, + op: BinOp.Space, + }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); +}); + +Deno.test("Parser - Relation", () => { + assertEquals(testParse("Foo > Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { + type: RELATION_NODE, + op: BinOp.Greater, + }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); + + assertEquals(testParse("Foo ~ Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { + type: RELATION_NODE, + op: BinOp.Tilde, + }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); + + assertEquals(testParse("Foo + Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { + type: RELATION_NODE, + op: BinOp.Plus, + }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); +}); + +Deno.test("Parser - Attr", () => { + assertEquals(testParse("[foo]"), [[ + { + type: ATTR_EXISTS_NODE, + prop: [1], + }, + ]]); + + assertEquals(testParse("[foo][bar]"), [[ + { + type: ATTR_EXISTS_NODE, + prop: [1], + }, + { + type: ATTR_EXISTS_NODE, + prop: [2], + }, + ]]); + + assertEquals(testParse("[foo=1]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: 1, + }, + ]]); + assertEquals(testParse("[foo=true]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: true, + }, + ]]); + assertEquals(testParse("[foo=false]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: false, + }, + ]]); + assertEquals(testParse("[foo=null]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: null, + }, + ]]); + assertEquals(testParse("[foo='str']"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: "str", + }, + ]]); + assertEquals(testParse('[foo="str"]'), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: "str", + }, + ]]); + assertEquals(testParse("[foo=/str/]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: /str/, + }, + ]]); + assertEquals(testParse("[foo=/str/g]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: /str/g, + }, + ]]); +}); + +Deno.test("Parser - Attr nested", () => { + assertEquals(testParse("[foo.bar]"), [[ + { + type: ATTR_EXISTS_NODE, + prop: [1, 2], + }, + ]]); + + assertEquals(testParse("[foo.bar = 2]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1, 2], + value: 2, + }, + ]]); +}); + +Deno.test("Parser - Pseudo no value", () => { + assertEquals(testParse(":first-child"), [[ + { + type: PSEUDO_FIRST_CHILD, + }, + ]]); + assertEquals(testParse(":last-child"), [[ + { + type: PSEUDO_LAST_CHILD, + }, + ]]); +}); + +Deno.test("Parser - Pseudo nth-child", () => { + assertEquals(testParse(":nth-child(2)"), [[ + { + type: PSEUDO_NTH_CHILD, + of: null, + op: null, + step: 0, + stepOffset: 1, + repeat: false, + }, + ]]); + assertEquals(testParse(":nth-child(2n)"), [[ + { + type: PSEUDO_NTH_CHILD, + of: null, + op: null, + step: 2, + stepOffset: 0, + repeat: true, + }, + ]]); + assertEquals(testParse(":nth-child(-2n)"), [[ + { + type: PSEUDO_NTH_CHILD, + of: null, + op: null, + step: -2, + stepOffset: 0, + repeat: true, + }, + ]]); + assertEquals(testParse(":nth-child(2n + 1)"), [[ + { + type: PSEUDO_NTH_CHILD, + of: null, + op: "+", + step: 2, + stepOffset: 1, + repeat: true, + }, + ]]); + assertEquals(testParse(":nth-child(2n + 1 of Foo[attr])"), [[ + { + type: PSEUDO_NTH_CHILD, + of: [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { type: ATTR_EXISTS_NODE, prop: [4] }, + ], + op: "+", + step: 2, + stepOffset: 1, + repeat: true, + }, + ]]); + + // Invalid selectors + assertThrows(() => testParse(":nth-child(2n + 1 of Foo[attr], Bar)")); + assertThrows(() => testParse(":nth-child(2n - 1 foo)")); +}); + +Deno.test("Parser - Pseudo has/is/where", () => { + assertEquals(testParse(":has(Foo:has(Foo), Bar)"), [[ + { + type: PSEUDO_HAS, + selectors: [ + [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { + type: PSEUDO_HAS, + selectors: [ + [{ type: ELEM_NODE, elem: 1, wildcard: false }], + ], + }, + ], + [ + { type: ELEM_NODE, elem: 2, wildcard: false }, + ], + ], + }, + ]]); + assertEquals(testParse(":where(Foo:where(Foo), Bar)"), [[ + { + type: PSEUDO_HAS, + selectors: [ + [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { + type: PSEUDO_HAS, + selectors: [ + [{ type: ELEM_NODE, elem: 1, wildcard: false }], + ], + }, + ], + [ + { type: ELEM_NODE, elem: 2, wildcard: false }, + ], + ], + }, + ]]); + assertEquals(testParse(":is(Foo:is(Foo), Bar)"), [[ + { + type: PSEUDO_HAS, + selectors: [ + [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { + type: PSEUDO_HAS, + selectors: [ + [{ type: ELEM_NODE, elem: 1, wildcard: false }], + ], + }, + ], + [ + { type: ELEM_NODE, elem: 2, wildcard: false }, + ], + ], + }, + ]]); +}); + +Deno.test("Parser - Pseudo not", () => { + assertEquals(testParse(":not(Foo:not(Foo), Bar)"), [[ + { + type: PSEUDO_NOT, + selectors: [ + [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { + type: PSEUDO_NOT, + selectors: [ + [{ type: ELEM_NODE, elem: 1, wildcard: false }], + ], + }, + ], + [ + { type: ELEM_NODE, elem: 2, wildcard: false }, + ], + ], + }, + ]]); +}); + +Deno.test("Parser - mixed", () => { + assertEquals(testParse("Foo[foo=true] Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { type: ATTR_BIN_NODE, op: BinOp.Equal, prop: [1], value: true }, + { type: RELATION_NODE, op: BinOp.Space }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); +}); diff --git a/tests/unit/make_temp_test.ts b/tests/unit/make_temp_test.ts index 32383387b5..d22b633091 100644 --- a/tests/unit/make_temp_test.ts +++ b/tests/unit/make_temp_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/message_channel_test.ts b/tests/unit/message_channel_test.ts index 0fcbc5e95d..bf7bb6443a 100644 --- a/tests/unit/message_channel_test.ts +++ b/tests/unit/message_channel_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // NOTE: these are just sometests to test the TypeScript types. Real coverage is // provided by WPT. import { assert, assertEquals } from "@std/assert"; diff --git a/tests/unit/mkdir_test.ts b/tests/unit/mkdir_test.ts index def77cd3e4..7b71e4a799 100644 --- a/tests/unit/mkdir_test.ts +++ b/tests/unit/mkdir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/navigator_test.ts b/tests/unit/navigator_test.ts index 5dcc423fa5..955c34dcc4 100644 --- a/tests/unit/navigator_test.ts +++ b/tests/unit/navigator_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; Deno.test(function navigatorNumCpus() { diff --git a/tests/unit/net_test.ts b/tests/unit/net_test.ts index cfa42b3d36..9c2819c30a 100644 --- a/tests/unit/net_test.ts +++ b/tests/unit/net_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/network_interfaces_test.ts b/tests/unit/network_interfaces_test.ts index 160efbfe60..675400365f 100644 --- a/tests/unit/network_interfaces_test.ts +++ b/tests/unit/network_interfaces_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; diff --git a/tests/unit/ops_test.ts b/tests/unit/ops_test.ts index 6de55f8b66..60f67cdca6 100644 --- a/tests/unit/ops_test.ts +++ b/tests/unit/ops_test.ts @@ -1,6 +1,6 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -const EXPECTED_OP_COUNT = 12; +const EXPECTED_OP_COUNT = 13; Deno.test(function checkExposedOps() { // @ts-ignore TS doesn't allow to index with symbol diff --git a/tests/unit/os_test.ts b/tests/unit/os_test.ts index a70796505f..36b6daad2f 100644 --- a/tests/unit/os_test.ts +++ b/tests/unit/os_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/path_from_url_test.ts b/tests/unit/path_from_url_test.ts index b3a6406bcb..3785f2bbb5 100644 --- a/tests/unit/path_from_url_test.ts +++ b/tests/unit/path_from_url_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/performance_test.ts b/tests/unit/performance_test.ts index fa056c7f54..22f51a4ce6 100644 --- a/tests/unit/performance_test.ts +++ b/tests/unit/performance_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/permissions_test.ts b/tests/unit/permissions_test.ts index f981b10fd6..8bb1633423 100644 --- a/tests/unit/permissions_test.ts +++ b/tests/unit/permissions_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/process_test.ts b/tests/unit/process_test.ts index 5cbab3b4c5..17589d8581 100644 --- a/tests/unit/process_test.ts +++ b/tests/unit/process_test.ts @@ -1,5 +1,5 @@ // deno-lint-ignore-file no-deprecated-deno-api -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/progressevent_test.ts b/tests/unit/progressevent_test.ts index 809c2ad391..3e7aa9ec15 100644 --- a/tests/unit/progressevent_test.ts +++ b/tests/unit/progressevent_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test(function progressEventConstruct() { diff --git a/tests/unit/promise_hooks_test.ts b/tests/unit/promise_hooks_test.ts index f7c44155d1..7fd7a8f51d 100644 --- a/tests/unit/promise_hooks_test.ts +++ b/tests/unit/promise_hooks_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; diff --git a/tests/unit/quic_test.ts b/tests/unit/quic_test.ts new file mode 100644 index 0000000000..f87a38cecc --- /dev/null +++ b/tests/unit/quic_test.ts @@ -0,0 +1,228 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +import { assert, assertEquals } from "./test_util.ts"; + +const cert = Deno.readTextFileSync("tests/testdata/tls/localhost.crt"); +const key = Deno.readTextFileSync("tests/testdata/tls/localhost.key"); +const caCerts = [Deno.readTextFileSync("tests/testdata/tls/RootCA.pem")]; + +interface Pair { + server: Deno.QuicConn; + client: Deno.QuicConn; + endpoint: Deno.QuicEndpoint; +} + +async function pair(opt?: Deno.QuicTransportOptions): Promise { + const endpoint = new Deno.QuicEndpoint({ hostname: "localhost" }); + const listener = endpoint.listen({ + cert, + key, + alpnProtocols: ["deno-test"], + ...opt, + }); + assertEquals(endpoint, listener.endpoint); + + const [server, client] = await Promise.all([ + listener.accept(), + Deno.connectQuic({ + hostname: "localhost", + port: endpoint.addr.port, + caCerts, + alpnProtocols: ["deno-test"], + ...opt, + }), + ]); + + assertEquals(server.protocol, "deno-test"); + assertEquals(client.protocol, "deno-test"); + assertEquals(client.remoteAddr, endpoint.addr); + assertEquals(server.serverName, "localhost"); + + return { server, client, endpoint }; +} + +Deno.test("bidirectional stream", async () => { + const { server, client, endpoint } = await pair(); + + const encoded = (new TextEncoder()).encode("hi!"); + + { + const bi = await server.createBidirectionalStream({ sendOrder: 42 }); + assertEquals(bi.writable.sendOrder, 42); + bi.writable.sendOrder = 0; + assertEquals(bi.writable.sendOrder, 0); + await bi.writable.getWriter().write(encoded); + } + + { + const { value: bi } = await client.incomingBidirectionalStreams + .getReader() + .read(); + const { value: data } = await bi!.readable.getReader().read(); + assertEquals(data, encoded); + } + + client.close(); + endpoint.close(); +}); + +Deno.test("unidirectional stream", async () => { + const { server, client, endpoint } = await pair(); + + const encoded = (new TextEncoder()).encode("hi!"); + + { + const uni = await server.createUnidirectionalStream({ sendOrder: 42 }); + assertEquals(uni.sendOrder, 42); + uni.sendOrder = 0; + assertEquals(uni.sendOrder, 0); + await uni.getWriter().write(encoded); + } + + { + const { value: uni } = await client.incomingUnidirectionalStreams + .getReader() + .read(); + const { value: data } = await uni!.getReader().read(); + assertEquals(data, encoded); + } + + endpoint.close(); + client.close(); +}); + +Deno.test("datagrams", async () => { + const { server, client, endpoint } = await pair(); + + const encoded = (new TextEncoder()).encode("hi!"); + + await server.sendDatagram(encoded); + + const data = await client.readDatagram(); + assertEquals(data, encoded); + + endpoint.close(); + client.close(); +}); + +Deno.test("closing", async () => { + const { server, client } = await pair(); + + server.close({ closeCode: 42, reason: "hi!" }); + + assertEquals(await client.closed, { closeCode: 42, reason: "hi!" }); +}); + +Deno.test("max concurrent streams", async () => { + const { server, client, endpoint } = await pair({ + maxConcurrentBidirectionalStreams: 1, + maxConcurrentUnidirectionalStreams: 1, + }); + + { + await server.createBidirectionalStream(); + await server.createBidirectionalStream() + .then(() => { + throw new Error("expected failure"); + }, () => { + // success! + }); + } + + { + await server.createUnidirectionalStream(); + await server.createUnidirectionalStream() + .then(() => { + throw new Error("expected failure"); + }, () => { + // success! + }); + } + + endpoint.close(); + client.close(); +}); + +Deno.test("incoming", async () => { + const endpoint = new Deno.QuicEndpoint({ hostname: "localhost" }); + const listener = endpoint.listen({ + cert, + key, + alpnProtocols: ["deno-test"], + }); + + const connect = () => + Deno.connectQuic({ + hostname: "localhost", + port: endpoint.addr.port, + caCerts, + alpnProtocols: ["deno-test"], + }); + + const c1p = connect(); + const i1 = await listener.incoming(); + const server = await i1.accept(); + const client = await c1p; + + assertEquals(server.protocol, "deno-test"); + assertEquals(client.protocol, "deno-test"); + assertEquals(client.remoteAddr, endpoint.addr); + + endpoint.close(); + client.close(); +}); + +Deno.test("0rtt", async () => { + const sEndpoint = new Deno.QuicEndpoint({ hostname: "localhost" }); + const listener = sEndpoint.listen({ + cert, + key, + alpnProtocols: ["deno-test"], + }); + + (async () => { + while (true) { + let incoming; + try { + incoming = await listener.incoming(); + } catch (e) { + if (e instanceof Deno.errors.BadResource) { + break; + } + throw e; + } + const conn = incoming.accept({ zeroRtt: true }); + conn.handshake.then(() => { + conn.close(); + }); + } + })(); + + const endpoint = new Deno.QuicEndpoint(); + + const c1 = await Deno.connectQuic({ + hostname: "localhost", + port: sEndpoint.addr.port, + caCerts, + alpnProtocols: ["deno-test"], + endpoint, + }); + + await c1.closed; + + const c2 = Deno.connectQuic({ + hostname: "localhost", + port: sEndpoint.addr.port, + caCerts, + alpnProtocols: ["deno-test"], + zeroRtt: true, + endpoint, + }); + + assert(!(c2 instanceof Promise), "0rtt should be accepted"); + + await c2.closed; + + sEndpoint.close(); + endpoint.close(); +}); diff --git a/tests/unit/read_dir_test.ts b/tests/unit/read_dir_test.ts index b00495eb45..9c5e6da79f 100644 --- a/tests/unit/read_dir_test.ts +++ b/tests/unit/read_dir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/read_file_test.ts b/tests/unit/read_file_test.ts index 7123833e9c..5716816b37 100644 --- a/tests/unit/read_file_test.ts +++ b/tests/unit/read_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/read_link_test.ts b/tests/unit/read_link_test.ts index c89ffe4927..7ecee2d567 100644 --- a/tests/unit/read_link_test.ts +++ b/tests/unit/read_link_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, diff --git a/tests/unit/read_text_file_test.ts b/tests/unit/read_text_file_test.ts index 1ec57bde35..39cd3e798c 100644 --- a/tests/unit/read_text_file_test.ts +++ b/tests/unit/read_text_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/real_path_test.ts b/tests/unit/real_path_test.ts index 7832846308..79dd0c084f 100644 --- a/tests/unit/real_path_test.ts +++ b/tests/unit/real_path_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/ref_unref_test.ts b/tests/unit/ref_unref_test.ts index 6f5bcf0a77..751f2af387 100644 --- a/tests/unit/ref_unref_test.ts +++ b/tests/unit/ref_unref_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertNotEquals, execCode } from "./test_util.ts"; diff --git a/tests/unit/remove_test.ts b/tests/unit/remove_test.ts index 261ff6bd05..38421f8610 100644 --- a/tests/unit/remove_test.ts +++ b/tests/unit/remove_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertRejects, assertThrows } from "./test_util.ts"; const REMOVE_METHODS = ["remove", "removeSync"] as const; diff --git a/tests/unit/rename_test.ts b/tests/unit/rename_test.ts index 3162c699c6..bacd54c675 100644 --- a/tests/unit/rename_test.ts +++ b/tests/unit/rename_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/request_test.ts b/tests/unit/request_test.ts index fe34c20a50..fa7619cd93 100644 --- a/tests/unit/request_test.ts +++ b/tests/unit/request_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertStringIncludes } from "./test_util.ts"; Deno.test(async function fromInit() { diff --git a/tests/unit/response_test.ts b/tests/unit/response_test.ts index bbdd5f481d..383790af3a 100644 --- a/tests/unit/response_test.ts +++ b/tests/unit/response_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/serve_test.ts b/tests/unit/serve_test.ts index f5896bc64b..09616b0151 100644 --- a/tests/unit/serve_test.ts +++ b/tests/unit/serve_test.ts @@ -1,8 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console -import { assertMatch, assertRejects } from "@std/assert"; +import { assertIsError, assertMatch, assertRejects } from "@std/assert"; import { Buffer, BufReader, BufWriter, type Reader } from "@std/io"; import { TextProtoReader } from "../testdata/run/textproto.ts"; import { @@ -387,7 +387,7 @@ Deno.test(async function httpServerCanResolveHostnames() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_req) => new Response("ok"), hostname: "localhost", port: servePort, @@ -410,7 +410,7 @@ Deno.test(async function httpServerRejectsOnAddrInUse() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_req) => new Response("ok"), hostname: "localhost", port: servePort, @@ -441,7 +441,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() { const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request, { remoteAddr }) => { // FIXME(bartlomieju): // make sure that request can be inspected @@ -483,7 +483,7 @@ Deno.test( const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); const listener = Deno.listen({ port: servePort }); - const server = serveHttpOnListener( + await using server = serveHttpOnListener( listener, ac.signal, async ( @@ -532,7 +532,7 @@ Deno.test( headers: { "connection": "close" }, }); - const server = serveHttpOnConnection( + await using server = serveHttpOnConnection( await acceptPromise, ac.signal, async ( @@ -572,7 +572,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerOnError() { const { promise, resolve } = Promise.withResolvers(); let requestStash: Request | null; - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request: Request) => { requestStash = request; await new Promise((r) => setTimeout(r, 100)); @@ -607,7 +607,7 @@ Deno.test( // deno-lint-ignore no-unused-vars let requestStash: Request | null; - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request: Request) => { requestStash = request; await new Promise((r) => setTimeout(r, 100)); @@ -640,7 +640,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const response = new Response("Hello World"); let hadError = false; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { return response; }, @@ -684,7 +684,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); let hadError = false; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { return Response.error(); }, @@ -717,7 +717,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload1() { const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ port: servePort, signal: ac.signal, onListen: onListen(listeningDeferred.resolve), @@ -752,7 +752,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload2() { const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ port: servePort, signal: ac.signal, onListen: onListen(listeningDeferred.resolve), @@ -807,7 +807,7 @@ Deno.test( Deno.test({ permissions: { net: true } }, async function httpServerPort0() { const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler() { return new Response("Hello World"); }, @@ -841,7 +841,7 @@ Deno.test( }; try { - const server = Deno.serve({ + await using server = Deno.serve({ handler() { return new Response("Hello World"); }, @@ -866,7 +866,7 @@ Deno.test( const ac = new AbortController(); let headers: Headers; - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { await request.text(); headers = request.headers; @@ -896,7 +896,7 @@ Deno.test( ); Deno.test({ permissions: { net: true } }, async function validPortString() { - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_request) => new Response(), port: "4501" as unknown as number, }); @@ -921,7 +921,7 @@ Deno.test({ permissions: { net: true } }, async function ipv6Hostname() { }; try { - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response(), hostname: "::1", port: 0, @@ -1017,7 +1017,7 @@ function createUrlTest( const listeningDeferred = Promise.withResolvers(); const urlDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request: Request) => { urlDeferred.resolve(request.url); return new Response(""); @@ -1117,7 +1117,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.body, null); deferred.resolve(); @@ -1157,7 +1157,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { await assertRejects(async () => { await request.text(); @@ -1221,7 +1221,7 @@ function createStreamTest(count: number, delay: number, action: string) { Deno.test(`httpServerStreamCount${count}Delay${delay}${action}`, async () => { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_request) => { return new Response(makeStream(count, delay)); }, @@ -1275,7 +1275,7 @@ Deno.test( writer.close(); const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { const reqBody = await request.text(); assertEquals("hello world", reqBody); @@ -1303,7 +1303,7 @@ Deno.test( Deno.test({ permissions: { net: true } }, async function httpServerClose() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response("ok"), port: servePort, signal: ac.signal, @@ -1323,7 +1323,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerCloseGet() { const listeningDeferred = Promise.withResolvers(); const requestDeferred = Promise.withResolvers(); const responseDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async () => { requestDeferred.resolve(); await new Promise((r) => setTimeout(r, 500)); @@ -1349,13 +1349,12 @@ Deno.test({ permissions: { net: true } }, async function httpServerCloseGet() { await server.finished; }); -// FIXME: Deno.test( { permissions: { net: true } }, async function httpServerEmptyBlobResponse() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response(new Blob([])), port: servePort, signal: ac.signal, @@ -1380,7 +1379,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); const errorDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { const body = new ReadableStream({ start(controller) { @@ -1421,7 +1420,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response("韓國".repeat(10)), port: servePort, signal: ac.signal, @@ -1456,7 +1455,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() { const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); const doneDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, @@ -1501,7 +1500,7 @@ Deno.test( async function httpServerWebSocketRaw() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { const { conn, response } = upgradeHttpRaw(request); const buf = new Uint8Array(1024); @@ -1581,7 +1580,7 @@ Deno.test( const ac = new AbortController(); const done = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, @@ -1635,7 +1634,7 @@ Deno.test( const ac = new AbortController(); const done = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, @@ -1673,7 +1672,7 @@ Deno.test( const ac = new AbortController(); const done = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, @@ -1723,7 +1722,7 @@ Deno.test( const ac = new AbortController(); let headers: Headers; - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { headers = request.headers; deferred.resolve(); @@ -1762,7 +1761,7 @@ Deno.test( let headers: Headers; let text: string; - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { headers = request.headers; text = await request.text(); @@ -1807,7 +1806,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response(""); @@ -1858,7 +1857,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, signal: ac.signal }, (request: Request) => { assert(request.body); @@ -1889,7 +1888,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, signal: ac.signal }, (request: Request) => { assert(request.body); @@ -2005,7 +2004,7 @@ Deno.test( }).pipeThrough(new TextEncoderStream()); } - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response(periodicStream()); @@ -2037,7 +2036,7 @@ Deno.test( { permissions: { net: true } }, async function httpLargeReadableStreamChunk() { const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler() { return new Response( new ReadableStream({ @@ -2077,7 +2076,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const deferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.headers.get("X-Header-Test"), "á"); deferred.resolve(); @@ -2123,7 +2122,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { // FIXME: // assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`); @@ -2177,7 +2176,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(await request.text(), ""); assertEquals(request.headers.get("cookie"), "foo=bar; bar=foo"); @@ -2221,7 +2220,7 @@ Deno.test( const hostname = "localhost"; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response("ok"); @@ -2256,7 +2255,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.body, null); deferred.resolve(); @@ -2292,7 +2291,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.method, "GET"); assertEquals(request.headers.get("host"), "deno.land"); @@ -2326,7 +2325,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.method, "GET"); assertEquals(request.headers.get("server"), "hello\tworld"); @@ -2360,7 +2359,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(request.method, "GET"); assertEquals(await request.text(), ""); @@ -2396,7 +2395,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(request.method, "POST"); assertEquals(await request.text(), "I'm a good request."); @@ -2443,7 +2442,7 @@ function createServerLengthTest(name: string, testCase: TestCase) { const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.method, "GET"); deferred.resolve(); @@ -2575,7 +2574,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(request.method, "POST"); assertEquals(request.headers.get("content-length"), "5"); @@ -2611,7 +2610,7 @@ Deno.test( async function httpServerPostWithInvalidPrefixContentLength() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { throw new Error("unreachable"); }, @@ -2651,7 +2650,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(request.method, "POST"); assertEquals(await request.text(), "qwert"); @@ -2688,7 +2687,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (r) => { deferred.resolve(); assertEquals(await r.text(), "12345"); @@ -2724,7 +2723,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response("NaN".repeat(100)); @@ -2867,7 +2866,7 @@ for (const testCase of compressionTestCases) { const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (_request) => { const f = await makeTempFile(testCase.length); deferred.resolve(); @@ -2923,7 +2922,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals( await request.bytes(), @@ -2971,7 +2970,7 @@ for (const delay of ["delay", "nodelay"]) { const listeningDeferred = Promise.withResolvers(); const waitForAbort = Promise.withResolvers(); const waitForRequest = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ port: servePort, signal: ac.signal, onListen: onListen(listeningDeferred.resolve), @@ -3121,7 +3120,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const hostname = "127.0.0.1"; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response("Hello World"), hostname, port: servePort, @@ -3151,7 +3150,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const hostname = "127.0.0.1"; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response("Hello World"), hostname, port: servePort, @@ -3186,7 +3185,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const deferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { assertEquals(await req.text(), ""); deferred.resolve(); @@ -3221,7 +3220,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { throw new Error("oops"); }, @@ -3268,7 +3267,7 @@ Deno.test( async function httpServer204ResponseDoesntSendContentLength() { const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_request) => new Response(null, { status: 204 }), port: servePort, signal: ac.signal, @@ -3298,7 +3297,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response(null, { status: 304 }); @@ -3343,7 +3342,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { deferred.resolve(); assertEquals(await req.text(), "hello"); @@ -3404,7 +3403,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { deferred.resolve(); assertEquals(await req.text(), ""); @@ -3458,7 +3457,7 @@ for (const [name, req] of badRequests) { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { throw new Error("oops"); }, @@ -3505,7 +3504,7 @@ Deno.test( let reqCount = -1; let timerId: number | undefined; - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_req) => { reqCount++; if (reqCount === 0) { @@ -3600,7 +3599,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); let count = 0; - const server = Deno.serve({ + await using server = Deno.serve({ async onListen({ port }: { port: number }) { const res1 = await fetch(`http://localhost:${port}/`); assertEquals(await res1.text(), "hello world 1"); @@ -3630,7 +3629,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { const cloned = req.clone(); assertEquals(req.headers, cloned.headers); @@ -3684,7 +3683,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { await req.text(); @@ -3733,7 +3732,7 @@ Deno.test({ const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { const _reader = req.body?.getReader(); @@ -3780,7 +3779,7 @@ Deno.test( async function testIssue16567() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ async onListen({ port }) { const res1 = await fetch(`http://localhost:${port}/`); assertEquals((await res1.text()).length, 40 * 50_000); @@ -3947,7 +3946,7 @@ Deno.test( }, async function httpServeCurlH2C() { const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, signal: ac.signal }, () => new Response("hello world!"), ); @@ -3982,7 +3981,7 @@ Deno.test( const ac = new AbortController(); const { resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { const response = new Response("Hello World", { headers: { @@ -4025,7 +4024,7 @@ Deno.test( }, async function httpsServeCurlH2C() { const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { signal: ac.signal, port: servePort, @@ -4082,7 +4081,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); const filePath = tmpUnixSocketPath(); - const server = Deno.serve( + await using server = Deno.serve( { signal: ac.signal, path: filePath, @@ -4115,7 +4114,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, onListen: onListen(listeningDeferred.resolve), @@ -4151,7 +4150,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, onListen: onListen(resolve), @@ -4187,7 +4186,7 @@ Deno.test( let timer: number | undefined = undefined; let _controller; - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, onListen: onListen(resolve), @@ -4237,7 +4236,7 @@ Deno.test({ await assertRejects( async () => { const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ path: "path/to/socket", handler: (_req) => new Response("Hello, world"), signal: ac.signal, @@ -4260,7 +4259,7 @@ Deno.test({ }, async () => { const { promise, resolve } = Promise.withResolvers<{ hostname: string }>(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_) => new Response("ok"), hostname: "0.0.0.0", port: 0, @@ -4278,7 +4277,7 @@ Deno.test({ let cancelled = false; - const server = Deno.serve({ + await using server = Deno.serve({ hostname: "0.0.0.0", port: servePort, onListen: () => resolve(), @@ -4305,7 +4304,7 @@ Deno.test({ }, async () => { const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ hostname: "0.0.0.0", port: servePort, onListen: () => resolve(), @@ -4335,7 +4334,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); const doneDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, @@ -4379,3 +4378,46 @@ Deno.test( await server.finished; }, ); + +Deno.test({ + name: + "req.body.getReader().read() throws the error with reasonable error message", +}, async () => { + const { promise, resolve, reject } = Promise.withResolvers(); + const server = Deno.serve({ onListen, port: 0 }, async (req) => { + const reader = req.body!.getReader(); + + try { + while (true) { + const { done } = await reader.read(); + if (done) break; + } + } catch (e) { + // deno-lint-ignore no-explicit-any + resolve(e as any); + } + + reject(new Error("Should not reach here")); + server.shutdown(); + return new Response(); + }); + + async function onListen({ port }: { port: number }) { + const body = "a".repeat(1000); + const request = `POST / HTTP/1.1\r\n` + + `Host: 127.0.0.1:${port}\r\n` + + `Content-Length: 1000\r\n` + + "\r\n" + body; + + const connection = await Deno.connect({ hostname: "127.0.0.1", port }); + await connection.write(new TextEncoder().encode(request)); + connection.close(); + } + await server.finished; + const e = await promise; + assertIsError( + e, + Deno.errors.BadResource, + "Cannot read request body as underlying resource unavailable", + ); +}); diff --git a/tests/unit/signal_test.ts b/tests/unit/signal_test.ts index 8923aa75bf..bfaa9aac90 100644 --- a/tests/unit/signal_test.ts +++ b/tests/unit/signal_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, delay } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/stat_test.ts b/tests/unit/stat_test.ts index 0609035b41..0f10f6e2d6 100644 --- a/tests/unit/stat_test.ts +++ b/tests/unit/stat_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/stdio_test.ts b/tests/unit/stdio_test.ts index d24fdc8efa..c605ccb9ad 100644 --- a/tests/unit/stdio_test.ts +++ b/tests/unit/stdio_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test(async function stdioStdinRead() { diff --git a/tests/unit/streams_test.ts b/tests/unit/streams_test.ts index 53225a1553..84a87d166d 100644 --- a/tests/unit/streams_test.ts +++ b/tests/unit/streams_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, diff --git a/tests/unit/structured_clone_test.ts b/tests/unit/structured_clone_test.ts index 6e0473f9a9..fc5d68e9da 100644 --- a/tests/unit/structured_clone_test.ts +++ b/tests/unit/structured_clone_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/symbol_test.ts b/tests/unit/symbol_test.ts index 54db7f5bae..2dca022b14 100644 --- a/tests/unit/symbol_test.ts +++ b/tests/unit/symbol_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; // Test that `Symbol.metadata` is defined. This file can be removed when V8 diff --git a/tests/unit/symlink_test.ts b/tests/unit/symlink_test.ts index 47a685ec61..cdf0ea5bbd 100644 --- a/tests/unit/symlink_test.ts +++ b/tests/unit/symlink_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertRejects, diff --git a/tests/unit/test_util.ts b/tests/unit/test_util.ts index a987cb5427..6e7865ea7a 100644 --- a/tests/unit/test_util.ts +++ b/tests/unit/test_util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as colors from "@std/fmt/colors"; import { assert } from "@std/assert"; diff --git a/tests/unit/testing_test.ts b/tests/unit/testing_test.ts index 51372c42b0..96104df9c4 100644 --- a/tests/unit/testing_test.ts +++ b/tests/unit/testing_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; Deno.test(function testWrongOverloads() { diff --git a/tests/unit/text_encoding_test.ts b/tests/unit/text_encoding_test.ts index 719e5907e4..1f378b2125 100644 --- a/tests/unit/text_encoding_test.ts +++ b/tests/unit/text_encoding_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/timers_test.ts b/tests/unit/timers_test.ts index 580d8c524e..29d338c761 100644 --- a/tests/unit/timers_test.ts +++ b/tests/unit/timers_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit/tls_sni_test.ts b/tests/unit/tls_sni_test.ts index a8d51108e7..3874753ded 100644 --- a/tests/unit/tls_sni_test.ts +++ b/tests/unit/tls_sni_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects } from "./test_util.ts"; // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol const { resolverSymbol, serverNameSymbol } = Deno[Deno.internal]; diff --git a/tests/unit/tls_test.ts b/tests/unit/tls_test.ts index 219f4a4508..53db347a13 100644 --- a/tests/unit/tls_test.ts +++ b/tests/unit/tls_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/truncate_test.ts b/tests/unit/truncate_test.ts index cebd6e8ee1..ae41b82271 100644 --- a/tests/unit/truncate_test.ts +++ b/tests/unit/truncate_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/tty_color_test.ts b/tests/unit/tty_color_test.ts index 6f26891e37..a1ca43951c 100644 --- a/tests/unit/tty_color_test.ts +++ b/tests/unit/tty_color_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; // Note tests for Deno.stdin.setRaw is in integration tests. diff --git a/tests/unit/tty_test.ts b/tests/unit/tty_test.ts index 1d29a0b706..352c313b43 100644 --- a/tests/unit/tty_test.ts +++ b/tests/unit/tty_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-deprecated-deno-api diff --git a/tests/unit/umask_test.ts b/tests/unit/umask_test.ts index 0e97f0d353..c7cdd68e4c 100644 --- a/tests/unit/umask_test.ts +++ b/tests/unit/umask_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/url_search_params_test.ts b/tests/unit/url_search_params_test.ts index d682c291a9..9846c7a91c 100644 --- a/tests/unit/url_search_params_test.ts +++ b/tests/unit/url_search_params_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; Deno.test(function urlSearchParamsWithMultipleSpaces() { diff --git a/tests/unit/url_test.ts b/tests/unit/url_test.ts index b0dc86232b..5643fbd258 100644 --- a/tests/unit/url_test.ts +++ b/tests/unit/url_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/urlpattern_test.ts b/tests/unit/urlpattern_test.ts index 3c1fb0cf19..4d815cd042 100644 --- a/tests/unit/urlpattern_test.ts +++ b/tests/unit/urlpattern_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; import { assertType, IsExact } from "@std/testing/types"; diff --git a/tests/unit/utime_test.ts b/tests/unit/utime_test.ts index 7a1fee74eb..5669499aaf 100644 --- a/tests/unit/utime_test.ts +++ b/tests/unit/utime_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, diff --git a/tests/unit/version_test.ts b/tests/unit/version_test.ts index 307295ad87..fd570fb16d 100644 --- a/tests/unit/version_test.ts +++ b/tests/unit/version_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; diff --git a/tests/unit/wasm_test.ts b/tests/unit/wasm_test.ts index 8ee9392f93..80bc468f7d 100644 --- a/tests/unit/wasm_test.ts +++ b/tests/unit/wasm_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertRejects } from "./test_util.ts"; diff --git a/tests/unit/webcrypto_test.ts b/tests/unit/webcrypto_test.ts index 09552a0587..bc5b307de5 100644 --- a/tests/unit/webcrypto_test.ts +++ b/tests/unit/webcrypto_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/webgpu_test.ts b/tests/unit/webgpu_test.ts index aac75d3420..f7bfc0bbc2 100644 --- a/tests/unit/webgpu_test.ts +++ b/tests/unit/webgpu_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/websocket_test.ts b/tests/unit/websocket_test.ts index d9878828db..e5e4b1a7a7 100644 --- a/tests/unit/websocket_test.ts +++ b/tests/unit/websocket_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows, fail } from "./test_util.ts"; const servePort = 4248; @@ -262,7 +262,7 @@ Deno.test({ socket.onopen = () => socket.send("Hello"); socket.onmessage = () => { socket.send("Bye"); - socket.close(); + socket.close(1000); }; socket.onclose = () => ac.abort(); socket.onerror = () => fail(); @@ -288,7 +288,8 @@ Deno.test({ seenBye = true; } }; - ws.onclose = () => { + ws.onclose = (e) => { + assertEquals(e.code, 1000); deferred.resolve(); }; await Promise.all([deferred.promise, server.finished]); diff --git a/tests/unit/websocketstream_test.ts.disabled b/tests/unit/websocketstream_test.ts.disabled index 4f8c321c92..7a64318d47 100644 --- a/tests/unit/websocketstream_test.ts.disabled +++ b/tests/unit/websocketstream_test.ts.disabled @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, diff --git a/tests/unit/webstorage_test.ts b/tests/unit/webstorage_test.ts index aa832b1c4b..1c2f9cfa34 100644 --- a/tests/unit/webstorage_test.ts +++ b/tests/unit/webstorage_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any import { assert, assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/worker_permissions_test.ts b/tests/unit/worker_permissions_test.ts index 28bf9f92af..20736357f5 100644 --- a/tests/unit/worker_permissions_test.ts +++ b/tests/unit/worker_permissions_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/worker_test.ts b/tests/unit/worker_test.ts index 42c257282c..c0777bb78b 100644 --- a/tests/unit/worker_test.ts +++ b/tests/unit/worker_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit/write_file_test.ts b/tests/unit/write_file_test.ts index 15e462cca9..53a5434e04 100644 --- a/tests/unit/write_file_test.ts +++ b/tests/unit/write_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/write_text_file_test.ts b/tests/unit/write_text_file_test.ts index 9e1b75326b..f8209250a1 100644 --- a/tests/unit/write_text_file_test.ts +++ b/tests/unit/write_text_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit_node/_fs/_fs_access_test.ts b/tests/unit_node/_fs/_fs_access_test.ts index f8010b0b8b..1333d9b7c8 100644 --- a/tests/unit_node/_fs/_fs_access_test.ts +++ b/tests/unit_node/_fs/_fs_access_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as fs from "node:fs"; import { assertRejects, assertThrows } from "@std/assert"; @@ -28,6 +28,8 @@ Deno.test( try { await fs.promises.access(file, fs.constants.R_OK); await fs.promises.access(file, fs.constants.W_OK); + await fs.promises.access(file, fs.constants.X_OK); + await fs.promises.access(file, fs.constants.F_OK); } finally { await Deno.remove(file); } @@ -60,6 +62,8 @@ Deno.test( try { fs.accessSync(file, fs.constants.R_OK); fs.accessSync(file, fs.constants.W_OK); + fs.accessSync(file, fs.constants.X_OK); + fs.accessSync(file, fs.constants.F_OK); } finally { Deno.removeSync(file); } diff --git a/tests/unit_node/_fs/_fs_appendFile_test.ts b/tests/unit_node/_fs/_fs_appendFile_test.ts index 5ee8eabed4..d6f45786cb 100644 --- a/tests/unit_node/_fs/_fs_appendFile_test.ts +++ b/tests/unit_node/_fs/_fs_appendFile_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { appendFile, appendFileSync } from "node:fs"; import { fromFileUrl } from "@std/path"; diff --git a/tests/unit_node/_fs/_fs_chmod_test.ts b/tests/unit_node/_fs/_fs_chmod_test.ts index 97dc9ecdda..db097397e2 100644 --- a/tests/unit_node/_fs/_fs_chmod_test.ts +++ b/tests/unit_node/_fs/_fs_chmod_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertRejects, assertThrows, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { chmod, chmodSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_chown_test.ts b/tests/unit_node/_fs/_fs_chown_test.ts index 6cb2f576ad..d9e6870ac6 100644 --- a/tests/unit_node/_fs/_fs_chown_test.ts +++ b/tests/unit_node/_fs/_fs_chown_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { chown, chownSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_close_test.ts b/tests/unit_node/_fs/_fs_close_test.ts index 8880bc0461..62f2d4f05e 100644 --- a/tests/unit_node/_fs/_fs_close_test.ts +++ b/tests/unit_node/_fs/_fs_close_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertThrows, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { close, closeSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_copy_test.ts b/tests/unit_node/_fs/_fs_copy_test.ts index 22f30ea373..b52f1951c6 100644 --- a/tests/unit_node/_fs/_fs_copy_test.ts +++ b/tests/unit_node/_fs/_fs_copy_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { assert } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_dir_test.ts b/tests/unit_node/_fs/_fs_dir_test.ts index 0f41543e92..7da76e50fb 100644 --- a/tests/unit_node/_fs/_fs_dir_test.ts +++ b/tests/unit_node/_fs/_fs_dir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { Dir as DirOrig, type Dirent } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_dirent_test.ts b/tests/unit_node/_fs/_fs_dirent_test.ts index 093617fafd..a709163f4a 100644 --- a/tests/unit_node/_fs/_fs_dirent_test.ts +++ b/tests/unit_node/_fs/_fs_dirent_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "@std/assert"; import { Dirent as Dirent_ } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_exists_test.ts b/tests/unit_node/_fs/_fs_exists_test.ts index 7ed7584ae6..3de7afdc18 100644 --- a/tests/unit_node/_fs/_fs_exists_test.ts +++ b/tests/unit_node/_fs/_fs_exists_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertStringIncludes } from "@std/assert"; import { exists, existsSync } from "node:fs"; import { promisify } from "node:util"; diff --git a/tests/unit_node/_fs/_fs_fdatasync_test.ts b/tests/unit_node/_fs/_fs_fdatasync_test.ts index 40ca2969f9..59c2b5657f 100644 --- a/tests/unit_node/_fs/_fs_fdatasync_test.ts +++ b/tests/unit_node/_fs/_fs_fdatasync_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { fdatasync, fdatasyncSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_fstat_test.ts b/tests/unit_node/_fs/_fs_fstat_test.ts index afcac836f6..79e5106525 100644 --- a/tests/unit_node/_fs/_fs_fstat_test.ts +++ b/tests/unit_node/_fs/_fs_fstat_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { fstat, fstatSync } from "node:fs"; import { fail } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_fsync_test.ts b/tests/unit_node/_fs/_fs_fsync_test.ts index 3c1509410e..cd01786a2f 100644 --- a/tests/unit_node/_fs/_fs_fsync_test.ts +++ b/tests/unit_node/_fs/_fs_fsync_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { fsync, fsyncSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_ftruncate_test.ts b/tests/unit_node/_fs/_fs_ftruncate_test.ts index 974f8f168e..b51ac6c2ca 100644 --- a/tests/unit_node/_fs/_fs_ftruncate_test.ts +++ b/tests/unit_node/_fs/_fs_ftruncate_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { ftruncate, ftruncateSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_futimes_test.ts b/tests/unit_node/_fs/_fs_futimes_test.ts index 9b0c64efd6..207610abed 100644 --- a/tests/unit_node/_fs/_fs_futimes_test.ts +++ b/tests/unit_node/_fs/_fs_futimes_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { futimes, futimesSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_handle_test.ts b/tests/unit_node/_fs/_fs_handle_test.ts index e26b82aa06..9134c2cb0a 100644 --- a/tests/unit_node/_fs/_fs_handle_test.ts +++ b/tests/unit_node/_fs/_fs_handle_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { Buffer } from "node:buffer"; import * as fs from "node:fs/promises"; @@ -117,3 +117,159 @@ Deno.test("[node/fs filehandle.writeFile] Write to file", async function () { assertEquals(decoder.decode(data), "hello world"); }); + +Deno.test( + "[node/fs filehandle.writev] Write array of buffers to file", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w"); + + const buffer1 = Buffer.from("hello "); + const buffer2 = Buffer.from("world"); + const res = await fileHandle.writev([buffer1, buffer2]); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(res.bytesWritten, 11); + assertEquals(decoder.decode(data), "hello world"); + }, +); + +Deno.test( + "[node/fs filehandle.writev] Write array of buffers to file with position", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w"); + + const buffer1 = Buffer.from("hello "); + const buffer2 = Buffer.from("world"); + await fileHandle.writev([buffer1, buffer2], 0); + const buffer3 = Buffer.from("lorem ipsum"); + await fileHandle.writev([buffer3], 6); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(decoder.decode(data), "hello lorem ipsum"); + }, +); + +Deno.test( + "[node/fs filehandle.truncate] Truncate file with length", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w+"); + + await fileHandle.writeFile("hello world"); + + await fileHandle.truncate(5); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(decoder.decode(data), "hello"); + }, +); + +Deno.test( + "[node/fs filehandle.truncate] Truncate file without length", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w+"); + + await fileHandle.writeFile("hello world"); + + await fileHandle.truncate(); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(decoder.decode(data), ""); + }, +); + +Deno.test( + "[node/fs filehandle.truncate] Truncate file with extension", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w+"); + + await fileHandle.writeFile("hi"); + + await fileHandle.truncate(5); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + const expected = new Uint8Array(5); + expected.set(new TextEncoder().encode("hi")); + + assertEquals(data, expected); + assertEquals(data.length, 5); + assertEquals(decoder.decode(data.subarray(0, 2)), "hi"); + // Verify null bytes + assertEquals(data[2], 0); + assertEquals(data[3], 0); + assertEquals(data[4], 0); + }, +); + +Deno.test( + "[node/fs filehandle.truncate] Truncate file with negative length", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w+"); + + await fileHandle.writeFile("hello world"); + + await fileHandle.truncate(-1); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(decoder.decode(data), ""); + assertEquals(data.length, 0); + }, +); + +Deno.test({ + name: "[node/fs filehandle.chmod] Change the permissions of the file", + ignore: Deno.build.os === "windows", + async fn() { + const fileHandle = await fs.open(testData); + + const readOnly = 0o444; + await fileHandle.chmod(readOnly.toString(8)); + assertEquals(Deno.statSync(testData).mode! & 0o777, readOnly); + + const readWrite = 0o666; + await fileHandle.chmod(readWrite.toString(8)); + assertEquals(Deno.statSync(testData).mode! & 0o777, readWrite); + + await fileHandle.close(); + }, +}); + +Deno.test({ + name: + "[node/fs filehandle.utimes] Change the file system timestamps of the file", + async fn() { + const fileHandle = await fs.open(testData); + + const atime = new Date(); + const mtime = new Date(0); + + await fileHandle.utimes(atime, mtime); + assertEquals(Deno.statSync(testData).atime!, atime); + assertEquals(Deno.statSync(testData).mtime!, mtime); + + await fileHandle.close(); + }, +}); diff --git a/tests/unit_node/_fs/_fs_link_test.ts b/tests/unit_node/_fs/_fs_link_test.ts index 3062db7a67..fca1dfe869 100644 --- a/tests/unit_node/_fs/_fs_link_test.ts +++ b/tests/unit_node/_fs/_fs_link_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { assert, assertEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_lstat_test.ts b/tests/unit_node/_fs/_fs_lstat_test.ts index 6a42443452..196d5f23b7 100644 --- a/tests/unit_node/_fs/_fs_lstat_test.ts +++ b/tests/unit_node/_fs/_fs_lstat_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { lstat, lstatSync } from "node:fs"; import { fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_mkdir_test.ts b/tests/unit_node/_fs/_fs_mkdir_test.ts index b9f6adf44c..d3476a23a7 100644 --- a/tests/unit_node/_fs/_fs_mkdir_test.ts +++ b/tests/unit_node/_fs/_fs_mkdir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { assert } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_mkdtemp_test.ts b/tests/unit_node/_fs/_fs_mkdtemp_test.ts index 47530b495a..4e130d4b9d 100644 --- a/tests/unit_node/_fs/_fs_mkdtemp_test.ts +++ b/tests/unit_node/_fs/_fs_mkdtemp_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertRejects, assertThrows } from "@std/assert"; import { EncodingOption, existsSync, mkdtemp, mkdtempSync } from "node:fs"; import { env } from "node:process"; diff --git a/tests/unit_node/_fs/_fs_open_test.ts b/tests/unit_node/_fs/_fs_open_test.ts index b4679cbab6..cd8cdf791b 100644 --- a/tests/unit_node/_fs/_fs_open_test.ts +++ b/tests/unit_node/_fs/_fs_open_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { O_APPEND, O_CREAT, diff --git a/tests/unit_node/_fs/_fs_opendir_test.ts b/tests/unit_node/_fs/_fs_opendir_test.ts index e22a47a702..37200d364f 100644 --- a/tests/unit_node/_fs/_fs_opendir_test.ts +++ b/tests/unit_node/_fs/_fs_opendir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit_node/_fs/_fs_readFile_test.ts b/tests/unit_node/_fs/_fs_readFile_test.ts index a75f12d1f6..0903777bca 100644 --- a/tests/unit_node/_fs/_fs_readFile_test.ts +++ b/tests/unit_node/_fs/_fs_readFile_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { promises, readFile, readFileSync } from "node:fs"; import * as path from "@std/path"; diff --git a/tests/unit_node/_fs/_fs_read_test.ts b/tests/unit_node/_fs/_fs_read_test.ts index 867ec01c5c..6def1ff71d 100644 --- a/tests/unit_node/_fs/_fs_read_test.ts +++ b/tests/unit_node/_fs/_fs_read_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// import { assert, diff --git a/tests/unit_node/_fs/_fs_readdir_test.ts b/tests/unit_node/_fs/_fs_readdir_test.ts index 3e36b1dc2d..b5071b5777 100644 --- a/tests/unit_node/_fs/_fs_readdir_test.ts +++ b/tests/unit_node/_fs/_fs_readdir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertNotEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { readdir, readdirSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_readlink_test.ts b/tests/unit_node/_fs/_fs_readlink_test.ts index 23c5e3896e..9443f2903f 100644 --- a/tests/unit_node/_fs/_fs_readlink_test.ts +++ b/tests/unit_node/_fs/_fs_readlink_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { readlink, readlinkSync } from "node:fs"; import { assert, assertEquals } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_realpath_test.ts b/tests/unit_node/_fs/_fs_realpath_test.ts index b0285cf545..25d266c4bf 100644 --- a/tests/unit_node/_fs/_fs_realpath_test.ts +++ b/tests/unit_node/_fs/_fs_realpath_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { assertEquals } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_rename_test.ts b/tests/unit_node/_fs/_fs_rename_test.ts index 76d2e13141..91185e02a2 100644 --- a/tests/unit_node/_fs/_fs_rename_test.ts +++ b/tests/unit_node/_fs/_fs_rename_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { rename, renameSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_rm_test.ts b/tests/unit_node/_fs/_fs_rm_test.ts index 6ded2e5645..da02b60f30 100644 --- a/tests/unit_node/_fs/_fs_rm_test.ts +++ b/tests/unit_node/_fs/_fs_rm_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows, fail } from "@std/assert"; import { rm, rmSync } from "node:fs"; import { existsSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_rmdir_test.ts b/tests/unit_node/_fs/_fs_rmdir_test.ts index db0c692a40..90d2c7c8a8 100644 --- a/tests/unit_node/_fs/_fs_rmdir_test.ts +++ b/tests/unit_node/_fs/_fs_rmdir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { rmdir, rmdirSync } from "node:fs"; import { existsSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_stat_test.ts b/tests/unit_node/_fs/_fs_stat_test.ts index 3cbbe940b0..d10521d0cd 100644 --- a/tests/unit_node/_fs/_fs_stat_test.ts +++ b/tests/unit_node/_fs/_fs_stat_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { BigIntStats, stat, Stats, statSync } from "node:fs"; import { assert, assertEquals, fail } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_statfs_test.ts b/tests/unit_node/_fs/_fs_statfs_test.ts index fc85bcf174..51cbb0e552 100644 --- a/tests/unit_node/_fs/_fs_statfs_test.ts +++ b/tests/unit_node/_fs/_fs_statfs_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as fs from "node:fs"; import { assertEquals, assertRejects } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_symlink_test.ts b/tests/unit_node/_fs/_fs_symlink_test.ts index dfd6adb51a..d4a86bfc0f 100644 --- a/tests/unit_node/_fs/_fs_symlink_test.ts +++ b/tests/unit_node/_fs/_fs_symlink_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertThrows, fail } from "@std/assert"; import { symlink, symlinkSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_truncate_test.ts b/tests/unit_node/_fs/_fs_truncate_test.ts index 67bd021e45..ebb33a0c29 100644 --- a/tests/unit_node/_fs/_fs_truncate_test.ts +++ b/tests/unit_node/_fs/_fs_truncate_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { truncate, truncateSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_unlink_test.ts b/tests/unit_node/_fs/_fs_unlink_test.ts index f96217f8ef..a48ef58bc7 100644 --- a/tests/unit_node/_fs/_fs_unlink_test.ts +++ b/tests/unit_node/_fs/_fs_unlink_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { existsSync } from "node:fs"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_utimes_test.ts b/tests/unit_node/_fs/_fs_utimes_test.ts index 717d511c06..56233973ac 100644 --- a/tests/unit_node/_fs/_fs_utimes_test.ts +++ b/tests/unit_node/_fs/_fs_utimes_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { utimes, utimesSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_watch_test.ts b/tests/unit_node/_fs/_fs_watch_test.ts index 963e0889f1..d611202066 100644 --- a/tests/unit_node/_fs/_fs_watch_test.ts +++ b/tests/unit_node/_fs/_fs_watch_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { unwatchFile, watch, watchFile } from "node:fs"; import { watch as watchPromise } from "node:fs/promises"; import { assert, assertEquals } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_writeFile_test.ts b/tests/unit_node/_fs/_fs_writeFile_test.ts index 2733a2df0b..503faf8f4c 100644 --- a/tests/unit_node/_fs/_fs_writeFile_test.ts +++ b/tests/unit_node/_fs/_fs_writeFile_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit_node/_fs/_fs_write_test.ts b/tests/unit_node/_fs/_fs_write_test.ts index 400fce73aa..6a5d4c9c26 100644 --- a/tests/unit_node/_fs/_fs_write_test.ts +++ b/tests/unit_node/_fs/_fs_write_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit_node/_test_utils.ts b/tests/unit_node/_test_utils.ts index c451ccd84b..a78444fd4a 100644 --- a/tests/unit_node/_test_utils.ts +++ b/tests/unit_node/_test_utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertStringIncludes } from "@std/assert"; diff --git a/tests/unit_node/assert_test.ts b/tests/unit_node/assert_test.ts index dc565458f6..f6dc18836d 100644 --- a/tests/unit_node/assert_test.ts +++ b/tests/unit_node/assert_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as assert from "node:assert"; Deno.test("[node/assert] .throws() compares Error instance", () => { diff --git a/tests/unit_node/assertion_error_test.ts b/tests/unit_node/assertion_error_test.ts index 4f8fe70ba5..60b0020d90 100644 --- a/tests/unit_node/assertion_error_test.ts +++ b/tests/unit_node/assertion_error_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { stripAnsiCode } from "@std/fmt/colors"; import { assert, assertStrictEquals } from "@std/assert"; import { AssertionError } from "node:assert"; diff --git a/tests/unit_node/async_hooks_test.ts b/tests/unit_node/async_hooks_test.ts index edad57bf76..d76898bcf5 100644 --- a/tests/unit_node/async_hooks_test.ts +++ b/tests/unit_node/async_hooks_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { AsyncLocalStorage, AsyncResource } from "node:async_hooks"; import process from "node:process"; import { setImmediate } from "node:timers"; diff --git a/tests/unit_node/buffer_test.ts b/tests/unit_node/buffer_test.ts index bd7f6edb17..3929769ffa 100644 --- a/tests/unit_node/buffer_test.ts +++ b/tests/unit_node/buffer_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { assertEquals, assertThrows } from "@std/assert"; diff --git a/tests/unit_node/child_process_test.ts b/tests/unit_node/child_process_test.ts index 0ea3c46cf0..1732b9d2bf 100644 --- a/tests/unit_node/child_process_test.ts +++ b/tests/unit_node/child_process_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import CP from "node:child_process"; import { Buffer } from "node:buffer"; @@ -656,6 +656,73 @@ Deno.test({ }, }); +Deno.test({ + name: + "[node/child_process spawn] child inherits Deno.env when options.env is not provided", + async fn() { + const deferred = withTimeout(); + Deno.env.set("BAR", "BAR"); + const env = spawn( + `"${Deno.execPath()}" eval -p "Deno.env.toObject().BAR"`, + { + shell: true, + }, + ); + try { + let envOutput = ""; + + assert(env.stdout); + env.on("error", (err: Error) => deferred.reject(err)); + env.stdout.on("data", (data) => { + envOutput += data; + }); + env.on("close", () => { + deferred.resolve(envOutput.trim()); + }); + await deferred.promise; + } finally { + env.kill(); + Deno.env.delete("BAR"); + } + const value = await deferred.promise; + assertEquals(value, "BAR"); + }, +}); + +Deno.test({ + name: + "[node/child_process spawn] child doesn't inherit Deno.env when options.env is provided", + async fn() { + const deferred = withTimeout(); + Deno.env.set("BAZ", "BAZ"); + const env = spawn( + `"${Deno.execPath()}" eval -p "Deno.env.toObject().BAZ"`, + { + env: {}, + shell: true, + }, + ); + try { + let envOutput = ""; + + assert(env.stdout); + env.on("error", (err: Error) => deferred.reject(err)); + env.stdout.on("data", (data) => { + envOutput += data; + }); + env.on("close", () => { + deferred.resolve(envOutput.trim()); + }); + await deferred.promise; + } finally { + env.kill(); + Deno.env.delete("BAZ"); + } + const value = await deferred.promise; + assertEquals(value, "undefined"); + }, +}); + // Regression test for https://github.com/denoland/deno/issues/20373 Deno.test(async function undefinedValueInEnvVar() { const deferred = withTimeout(); diff --git a/tests/unit_node/cluster_test.ts b/tests/unit_node/cluster_test.ts index d9a59ae63a..e0335c4824 100644 --- a/tests/unit_node/cluster_test.ts +++ b/tests/unit_node/cluster_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import cluster from "node:cluster"; import * as clusterNamed from "node:cluster"; diff --git a/tests/unit_node/console_test.ts b/tests/unit_node/console_test.ts index 25d4a78e57..a9e3551074 100644 --- a/tests/unit_node/console_test.ts +++ b/tests/unit_node/console_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import vm from "node:vm"; import { stripAnsiCode } from "@std/fmt/colors"; diff --git a/tests/unit_node/crypto/crypto_cipher_gcm_test.ts b/tests/unit_node/crypto/crypto_cipher_gcm_test.ts index b379a43696..dd02ee5e32 100644 --- a/tests/unit_node/crypto/crypto_cipher_gcm_test.ts +++ b/tests/unit_node/crypto/crypto_cipher_gcm_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import crypto from "node:crypto"; import { Buffer } from "node:buffer"; @@ -119,3 +119,23 @@ Deno.test({ ); }, }); + +// Issue #27441 +// https://github.com/denoland/deno/issues/27441 +Deno.test({ + name: "aes-256-gcm supports IV of non standard length", + fn() { + const decipher = crypto.createDecipheriv( + "aes-256-gcm", + Buffer.from("eYLEiLFQnpjYksWTiKpwv2sKhw+WJb5Fo/aY2YqXswc=", "base64"), + Buffer.from("k5oP3kb8tTbZaL3PxbFWN8ToOb8vfv2b1EuPz1LbmYU=", "base64"), // 256 bits IV + ); + const decrypted = decipher.update( + "s0/KBsFec29XLrGbAnLiNA==", + "base64", + "utf-8", + ); + assertEquals(decrypted, "this is a secret"); + decipher.final(); + }, +}); diff --git a/tests/unit_node/crypto/crypto_cipher_test.ts b/tests/unit_node/crypto/crypto_cipher_test.ts index 65a5b29eeb..bc001c8d0f 100644 --- a/tests/unit_node/crypto/crypto_cipher_test.ts +++ b/tests/unit_node/crypto/crypto_cipher_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import crypto from "node:crypto"; import { Buffer } from "node:buffer"; import { Readable } from "node:stream"; @@ -361,6 +361,19 @@ Deno.test({ name: "getCiphers", fn() { assertEquals(crypto.getCiphers().includes("aes-128-cbc"), true); + + const getZeroKey = (cipher: string) => zeros(+cipher.match(/\d+/)![0] / 8); + const getZeroIv = (cipher: string) => { + if (cipher.includes("gcm") || cipher.includes("ecb")) { + return zeros(12); + } + return zeros(16); + }; + + for (const cipher of crypto.getCiphers()) { + crypto.createCipheriv(cipher, getZeroKey(cipher), getZeroIv(cipher)) + .final(); + } }, }); diff --git a/tests/unit_node/crypto/crypto_hash_test.ts b/tests/unit_node/crypto/crypto_hash_test.ts index 0d4e307096..0a3fd2245e 100644 --- a/tests/unit_node/crypto/crypto_hash_test.ts +++ b/tests/unit_node/crypto/crypto_hash_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { createHash, createHmac, getHashes } from "node:crypto"; import { Buffer } from "node:buffer"; import { Readable } from "node:stream"; diff --git a/tests/unit_node/crypto/crypto_hkdf_test.ts b/tests/unit_node/crypto/crypto_hkdf_test.ts index 34102fd03d..dc128db584 100644 --- a/tests/unit_node/crypto/crypto_hkdf_test.ts +++ b/tests/unit_node/crypto/crypto_hkdf_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { hkdfSync } from "node:crypto"; import { assertEquals } from "@std/assert"; import { Buffer } from "node:buffer"; diff --git a/tests/unit_node/crypto/crypto_import_export.ts b/tests/unit_node/crypto/crypto_import_export.ts index fc41cbacc2..458178d578 100644 --- a/tests/unit_node/crypto/crypto_import_export.ts +++ b/tests/unit_node/crypto/crypto_import_export.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import crypto, { KeyFormat } from "node:crypto"; import path from "node:path"; import { Buffer } from "node:buffer"; diff --git a/tests/unit_node/crypto/crypto_key_test.ts b/tests/unit_node/crypto/crypto_key_test.ts index 5d206acc72..ac54a35419 100644 --- a/tests/unit_node/crypto/crypto_key_test.ts +++ b/tests/unit_node/crypto/crypto_key_test.ts @@ -1,6 +1,6 @@ // deno-lint-ignore-file no-explicit-any -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { createECDH, createHmac, @@ -700,3 +700,19 @@ Deno.test("generateKeyPair promisify", async () => { assert(publicKey.startsWith("-----BEGIN PUBLIC KEY-----")); assert(privateKey.startsWith("-----BEGIN PRIVATE KEY-----")); }); + +Deno.test("RSA export private JWK", function () { + // @ts-ignore @types/node broken + const { privateKey, publicKey } = generateKeyPairSync("rsa", { + modulusLength: 4096, + publicKeyEncoding: { + format: "jwk", + }, + privateKeyEncoding: { + format: "jwk", + }, + }); + + assertEquals((privateKey as any).kty, "RSA"); + assertEquals((privateKey as any).n, (publicKey as any).n); +}); diff --git a/tests/unit_node/crypto/crypto_misc_test.ts b/tests/unit_node/crypto/crypto_misc_test.ts index 007009339d..3c49edd388 100644 --- a/tests/unit_node/crypto/crypto_misc_test.ts +++ b/tests/unit_node/crypto/crypto_misc_test.ts @@ -1,7 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { randomFillSync, randomUUID, timingSafeEqual } from "node:crypto"; import { Buffer } from "node:buffer"; -import { assert, assertEquals } from "../../unit/test_util.ts"; +import { assert, assertEquals, assertThrows } from "../../unit/test_util.ts"; import { assertNotEquals } from "@std/assert"; Deno.test("[node/crypto.getRandomUUID] works the same way as Web Crypto API", () => { @@ -36,3 +36,10 @@ Deno.test("[node/crypto.timingSafeEqual] compares equal Buffer with different by assert(timingSafeEqual(a, b)); }); + +Deno.test("[node/crypto.timingSafeEqual] RangeError on Buffer with different byteLength", () => { + const a = Buffer.from([212, 213]); + const b = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 212, 213, 0]); + + assertThrows(() => timingSafeEqual(a, b), RangeError); +}); diff --git a/tests/unit_node/crypto/crypto_pbkdf2_test.ts b/tests/unit_node/crypto/crypto_pbkdf2_test.ts index c937eb0c47..5eb4b82210 100644 --- a/tests/unit_node/crypto/crypto_pbkdf2_test.ts +++ b/tests/unit_node/crypto/crypto_pbkdf2_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { pbkdf2, pbkdf2Sync } from "node:crypto"; import { assert, assertEquals } from "@std/assert"; import nodeFixtures from "../testdata/crypto_digest_fixtures.json" with { diff --git a/tests/unit_node/crypto/crypto_scrypt_test.ts b/tests/unit_node/crypto/crypto_scrypt_test.ts index 03c08909ea..3f4d1437f0 100644 --- a/tests/unit_node/crypto/crypto_scrypt_test.ts +++ b/tests/unit_node/crypto/crypto_scrypt_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { scrypt, scryptSync } from "node:crypto"; import { Buffer } from "node:buffer"; import { assertEquals } from "@std/assert"; diff --git a/tests/unit_node/crypto/crypto_sign_test.ts b/tests/unit_node/crypto/crypto_sign_test.ts index 97c80b28af..24b049ca36 100644 --- a/tests/unit_node/crypto/crypto_sign_test.ts +++ b/tests/unit_node/crypto/crypto_sign_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "@std/assert"; import { diff --git a/tests/unit_node/crypto/gcmEncryptExtIV128.json b/tests/unit_node/crypto/gcmEncryptExtIV128.json index 64896642d4..f0b4bca1f1 100644 --- a/tests/unit_node/crypto/gcmEncryptExtIV128.json +++ b/tests/unit_node/crypto/gcmEncryptExtIV128.json @@ -51373,5 +51373,322 @@ 102, 238 ] + }, + { + "key": [ + 131, + 249, + 217, + 125, + 74, + 183, + 89, + 253, + 220, + 195, + 239, + 84, + 160, + 226, + 168, + 236 + ], + "nonce": [ + 207 + ], + "aad": [ + 109, + 212, + 158, + 174, + 180, + 16, + 61, + 172, + 143, + 151, + 227, + 35, + 73, + 70, + 221, + 45 + ], + "plaintext": [ + 119, + 230, + 50, + 156, + 249, + 66, + 79, + 113, + 200, + 8, + 223, + 145, + 112, + 191, + 210, + 152 + ], + "ciphertext": [ + 80, + 222, + 134, + 167, + 169, + 42, + 138, + 94, + 163, + 61, + 181, + 105, + 107, + 150, + 205, + 119 + ], + "tag": [ + 170, + 24, + 30, + 132, + 188, + 139, + 75, + 245, + 166, + 137, + 39, + 196, + 9, + 212, + 34, + 203 + ] + }, + { + "key": [ + 202, + 145, + 226, + 65, + 68, + 9, + 164, + 57, + 176, + 101, + 115, + 215, + 114, + 249, + 10, + 251 + ], + "nonce": [ + 23, + 112, + 8, + 249, + 32, + 160, + 97, + 105, + 204, + 223, + 117, + 58, + 51, + 133, + 83, + 254, + 253, + 70, + 132, + 88, + 105, + 201, + 36, + 77, + 164, + 73, + 151, + 248, + 61, + 76, + 232, + 5, + 161, + 135, + 7, + 200, + 77, + 17, + 79, + 156, + 104, + 66, + 123, + 34, + 132, + 21, + 145, + 230, + 202, + 236, + 245, + 195, + 231, + 42, + 37, + 22, + 122, + 168, + 96, + 197, + 27, + 220, + 26, + 165, + 109, + 205, + 105, + 242, + 154, + 47, + 53, + 231, + 10, + 50, + 43, + 158, + 186, + 9, + 42, + 152, + 214, + 106, + 149, + 107, + 77, + 41, + 67, + 131, + 160, + 235, + 171, + 38, + 247, + 196, + 223, + 26, + 93, + 64, + 96, + 223, + 196, + 90, + 20, + 21, + 81, + 0, + 234, + 125, + 158, + 50, + 222, + 187, + 101, + 55, + 64, + 107, + 117, + 114, + 145, + 113, + 5, + 5, + 20, + 46, + 118, + 89, + 252, + 119 + ], + "aad": [ + 191, + 235, + 21, + 252, + 247, + 177, + 95, + 14, + 20, + 192, + 68, + 57, + 182, + 121, + 80, + 189 + ], + "plaintext": [ + 40, + 0, + 62, + 48, + 196, + 164, + 202, + 158, + 65, + 170, + 254, + 250, + 193, + 225, + 195, + 222 + ], + "ciphertext": [ + 0, + 228, + 114, + 151, + 31, + 58, + 119, + 112, + 170, + 113, + 88, + 253, + 146, + 241, + 123, + 183 + ], + "tag": [ + 22, + 102, + 27, + 133, + 235, + 81, + 100, + 108, + 148, + 207, + 43, + 228, + 228, + 45, + 122, + 142 + ] } ] diff --git a/tests/unit_node/crypto/gcmEncryptExtIV256.json b/tests/unit_node/crypto/gcmEncryptExtIV256.json index cb8ba30869..808c47ec6e 100644 --- a/tests/unit_node/crypto/gcmEncryptExtIV256.json +++ b/tests/unit_node/crypto/gcmEncryptExtIV256.json @@ -57373,5 +57373,354 @@ 246, 57 ] + }, + { + "key": [ + 187, + 70, + 53, + 215, + 102, + 221, + 14, + 74, + 112, + 25, + 209, + 114, + 76, + 115, + 110, + 31, + 44, + 1, + 106, + 249, + 226, + 158, + 125, + 58, + 162, + 192, + 222, + 35, + 231, + 128, + 175, + 38 + ], + "nonce": [ + 171 + ], + "aad": [ + 15, + 133, + 199, + 219, + 235, + 103, + 75, + 122, + 112, + 195, + 81, + 37, + 211, + 97, + 147, + 80 + ], + "plaintext": [ + 208, + 92, + 232, + 120, + 217, + 70, + 98, + 209, + 82, + 11, + 24, + 75, + 75, + 239, + 60, + 69 + ], + "ciphertext": [ + 81, + 186, + 162, + 106, + 106, + 113, + 156, + 22, + 0, + 100, + 95, + 243, + 191, + 223, + 165, + 59 + ], + "tag": [ + 107, + 213, + 78, + 81, + 132, + 235, + 48, + 9, + 52, + 179, + 146, + 195, + 43, + 124, + 26, + 110 + ] + }, + { + "key": [ + 252, + 188, + 126, + 182, + 39, + 22, + 220, + 127, + 121, + 43, + 97, + 148, + 210, + 109, + 109, + 86, + 158, + 174, + 224, + 122, + 157, + 60, + 55, + 202, + 66, + 133, + 64, + 144, + 102, + 30, + 24, + 69 + ], + "nonce": [ + 76, + 140, + 70, + 36, + 39, + 155, + 35, + 180, + 149, + 199, + 136, + 132, + 76, + 118, + 210, + 37, + 235, + 242, + 56, + 38, + 89, + 156, + 62, + 28, + 244, + 219, + 29, + 162, + 214, + 90, + 127, + 117, + 68, + 216, + 232, + 111, + 204, + 51, + 251, + 17, + 61, + 49, + 116, + 184, + 199, + 144, + 49, + 34, + 203, + 89, + 103, + 246, + 16, + 115, + 130, + 204, + 90, + 198, + 231, + 160, + 228, + 202, + 79, + 8, + 222, + 62, + 145, + 29, + 72, + 62, + 104, + 37, + 61, + 63, + 136, + 108, + 254, + 52, + 155, + 249, + 50, + 153, + 162, + 142, + 102, + 91, + 192, + 150, + 165, + 28, + 232, + 76, + 230, + 148, + 11, + 52, + 160, + 55, + 114, + 36, + 131, + 185, + 106, + 123, + 37, + 80, + 127, + 90, + 4, + 100, + 60, + 103, + 48, + 250, + 170, + 182, + 24, + 230, + 35, + 26, + 114, + 119, + 20, + 214, + 243, + 102, + 250, + 155 + ], + "aad": [ + 60, + 24, + 42, + 241, + 156, + 70, + 255, + 74, + 203, + 218, + 206, + 207, + 112, + 180, + 47, + 181 + ], + "plaintext": [ + 34, + 20, + 79, + 193, + 47, + 123, + 197, + 82, + 43, + 136, + 183, + 108, + 141, + 237, + 28, + 118 + ], + "ciphertext": [ + 200, + 217, + 129, + 7, + 192, + 203, + 60, + 15, + 210, + 24, + 154, + 233, + 114, + 128, + 213, + 98 + ], + "tag": [ + 41, + 6, + 119, + 35, + 48, + 236, + 217, + 163, + 184, + 168, + 40, + 118, + 164, + 235, + 222, + 234 + ] } ] diff --git a/tests/unit_node/crypto/generate_fixture.mjs b/tests/unit_node/crypto/generate_fixture.mjs index 3724fe4aff..3fb78e82c4 100644 --- a/tests/unit_node/crypto/generate_fixture.mjs +++ b/tests/unit_node/crypto/generate_fixture.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Run this file with `node` to regenerate the testdata/crypto_digest_fixtures.json file. import { readFileSync, writeFileSync } from "node:fs"; diff --git a/tests/unit_node/crypto/generate_keys.mjs b/tests/unit_node/crypto/generate_keys.mjs index 29d9f570f3..d422729a29 100644 --- a/tests/unit_node/crypto/generate_keys.mjs +++ b/tests/unit_node/crypto/generate_keys.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { writeFileSync } from "node:fs"; import { join } from "node:path"; diff --git a/tests/unit_node/dgram_test.ts b/tests/unit_node/dgram_test.ts index 2ccdfbfb7a..521b2448ad 100644 --- a/tests/unit_node/dgram_test.ts +++ b/tests/unit_node/dgram_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import { execCode } from "../unit/test_util.ts"; diff --git a/tests/unit_node/domain_test.ts b/tests/unit_node/domain_test.ts index ddc56efae3..59b2c57b77 100644 --- a/tests/unit_node/domain_test.ts +++ b/tests/unit_node/domain_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright © Benjamin Lupton // This code has been forked by https://github.com/bevry/domain-browser/commit/8bce7f4a093966ca850da75b024239ad5d0b33c6 diff --git a/tests/unit_node/events_test.ts b/tests/unit_node/events_test.ts index 8cfef6319a..152ace787b 100644 --- a/tests/unit_node/events_test.ts +++ b/tests/unit_node/events_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import events, { addAbortListener, EventEmitter } from "node:events"; diff --git a/tests/unit_node/fetch_test.ts b/tests/unit_node/fetch_test.ts index 399d6052a5..d005fd1d6e 100644 --- a/tests/unit_node/fetch_test.ts +++ b/tests/unit_node/fetch_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import { createReadStream } from "node:fs"; diff --git a/tests/unit_node/fs_test.ts b/tests/unit_node/fs_test.ts index 32bea40e75..2dcd45529f 100644 --- a/tests/unit_node/fs_test.ts +++ b/tests/unit_node/fs_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// import { assert, assertEquals, assertRejects, assertThrows } from "@std/assert"; diff --git a/tests/unit_node/http2_test.ts b/tests/unit_node/http2_test.ts index 90f2388124..698e6b4ebe 100644 --- a/tests/unit_node/http2_test.ts +++ b/tests/unit_node/http2_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit_node/http_test.ts b/tests/unit_node/http_test.ts index e6c36eea19..7478546617 100644 --- a/tests/unit_node/http_test.ts +++ b/tests/unit_node/http_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console @@ -10,6 +10,7 @@ import http, { } from "node:http"; import url from "node:url"; import https from "node:https"; +import zlib from "node:zlib"; import net, { Socket } from "node:net"; import fs from "node:fs"; import { text } from "node:stream/consumers"; @@ -1823,3 +1824,60 @@ Deno.test("[node/http] ServerResponse socket", async () => { await promise; }); + +Deno.test("[node/http] decompress brotli response", { + permissions: { net: true }, +}, async () => { + let received = false; + const ac = new AbortController(); + const server = Deno.serve({ port: 5928, signal: ac.signal }, (_req) => { + received = true; + return Response.json([ + ["accept-language", "*"], + ["host", "localhost:3000"], + ["user-agent", "Deno/2.1.1"], + ], {}); + }); + const { promise, resolve, reject } = Promise.withResolvers(); + let body = ""; + + const request = http.get( + "http://localhost:5928/", + { + headers: { + "accept-encoding": "gzip, deflate, br, zstd", + }, + }, + (resp) => { + const decompress = zlib.createBrotliDecompress(); + resp.on("data", (chunk) => { + decompress.write(chunk); + }); + + resp.on("end", () => { + decompress.end(); + }); + + decompress.on("data", (chunk) => { + body += chunk; + }); + + decompress.on("end", () => { + resolve(); + }); + }, + ); + request.on("error", reject); + request.end(() => { + assert(received); + }); + + await promise; + ac.abort(); + await server.finished; + + assertEquals(JSON.parse(body), [["accept-language", "*"], [ + "host", + "localhost:3000", + ], ["user-agent", "Deno/2.1.1"]]); +}); diff --git a/tests/unit_node/inspector_test.ts b/tests/unit_node/inspector_test.ts index a53e977bb6..ed94332108 100644 --- a/tests/unit_node/inspector_test.ts +++ b/tests/unit_node/inspector_test.ts @@ -1,5 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import inspector, { Session } from "node:inspector"; +import inspectorPromises, { + Session as SessionPromise, +} from "node:inspector/promises"; import { assertEquals } from "@std/assert/equals"; Deno.test("[node/inspector] - importing inspector works", () => { @@ -9,3 +12,11 @@ Deno.test("[node/inspector] - importing inspector works", () => { Deno.test("[node/inspector] - Session constructor should not throw", () => { new Session(); }); + +Deno.test("[node/inspector/promises] - importing inspector works", () => { + assertEquals(typeof inspectorPromises.open, "function"); +}); + +Deno.test("[node/inspector/promises] - Session constructor should not throw", () => { + new SessionPromise(); +}); diff --git a/tests/unit_node/internal/_randomBytes_test.ts b/tests/unit_node/internal/_randomBytes_test.ts index ff3acf5e38..d479f44e76 100644 --- a/tests/unit_node/internal/_randomBytes_test.ts +++ b/tests/unit_node/internal/_randomBytes_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertRejects, assertThrows } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { pseudoRandomBytes, randomBytes } from "node:crypto"; diff --git a/tests/unit_node/internal/_randomFill_test.ts b/tests/unit_node/internal/_randomFill_test.ts index 7b590666cd..d865b75f39 100644 --- a/tests/unit_node/internal/_randomFill_test.ts +++ b/tests/unit_node/internal/_randomFill_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { randomFill, randomFillSync } from "node:crypto"; import { assertEquals, assertNotEquals, assertThrows } from "@std/assert"; diff --git a/tests/unit_node/internal/_randomInt_test.ts b/tests/unit_node/internal/_randomInt_test.ts index b0d9d771ee..ec2f869551 100644 --- a/tests/unit_node/internal/_randomInt_test.ts +++ b/tests/unit_node/internal/_randomInt_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { randomInt } from "node:crypto"; import { assert, assertThrows } from "@std/assert"; diff --git a/tests/unit_node/module_test.ts b/tests/unit_node/module_test.ts index 96c3504bd2..728a339fb6 100644 --- a/tests/unit_node/module_test.ts +++ b/tests/unit_node/module_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { builtinModules, diff --git a/tests/unit_node/net_test.ts b/tests/unit_node/net_test.ts index 83d751866f..1142e6201d 100644 --- a/tests/unit_node/net_test.ts +++ b/tests/unit_node/net_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit_node/os_test.ts b/tests/unit_node/os_test.ts index 78636e755d..99ad82c031 100644 --- a/tests/unit_node/os_test.ts +++ b/tests/unit_node/os_test.ts @@ -1,5 +1,5 @@ // deno-lint-ignore-file no-undef -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import os from "node:os"; import { diff --git a/tests/unit_node/path_test.ts b/tests/unit_node/path_test.ts index bd0711334d..63aa5ee2c1 100644 --- a/tests/unit_node/path_test.ts +++ b/tests/unit_node/path_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import path from "node:path"; import posix from "node:path/posix"; diff --git a/tests/unit_node/perf_hooks_test.ts b/tests/unit_node/perf_hooks_test.ts index 83d0062228..a77dd08538 100644 --- a/tests/unit_node/perf_hooks_test.ts +++ b/tests/unit_node/perf_hooks_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as perfHooks from "node:perf_hooks"; import { monitorEventLoopDelay, diff --git a/tests/unit_node/process_test.ts b/tests/unit_node/process_test.ts index 49de2dce1d..592bd6497f 100644 --- a/tests/unit_node/process_test.ts +++ b/tests/unit_node/process_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-undef no-console diff --git a/tests/unit_node/punycode_test.ts b/tests/unit_node/punycode_test.ts index fffa26164f..d4fa5b50a0 100644 --- a/tests/unit_node/punycode_test.ts +++ b/tests/unit_node/punycode_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as punycode from "node:punycode"; import { assertEquals } from "@std/assert"; diff --git a/tests/unit_node/querystring_test.ts b/tests/unit_node/querystring_test.ts index 09672df370..d57941264c 100644 --- a/tests/unit_node/querystring_test.ts +++ b/tests/unit_node/querystring_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import { parse, stringify } from "node:querystring"; diff --git a/tests/unit_node/readline_test.ts b/tests/unit_node/readline_test.ts index 68f0cd7c92..02fd6958bf 100644 --- a/tests/unit_node/readline_test.ts +++ b/tests/unit_node/readline_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { createInterface, Interface } from "node:readline"; import { assertInstanceOf } from "@std/assert"; import { Readable, Writable } from "node:stream"; diff --git a/tests/unit_node/repl_test.ts b/tests/unit_node/repl_test.ts index 693fdcc9eb..d0759f442f 100644 --- a/tests/unit_node/repl_test.ts +++ b/tests/unit_node/repl_test.ts @@ -1,5 +1,5 @@ // deno-lint-ignore-file no-undef -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import repl from "node:repl"; import { assert } from "@std/assert"; diff --git a/tests/unit_node/stream_test.ts b/tests/unit_node/stream_test.ts index b8542f6cfd..9c58ae03d6 100644 --- a/tests/unit_node/stream_test.ts +++ b/tests/unit_node/stream_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "@std/assert"; import { fromFileUrl, relative } from "@std/path"; diff --git a/tests/unit_node/string_decoder_test.ts b/tests/unit_node/string_decoder_test.ts index 34fbdd8810..1062373422 100644 --- a/tests/unit_node/string_decoder_test.ts +++ b/tests/unit_node/string_decoder_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import { Buffer } from "node:buffer"; import { StringDecoder } from "node:string_decoder"; diff --git a/tests/unit_node/timers_test.ts b/tests/unit_node/timers_test.ts index ecff32e763..43a3338096 100644 --- a/tests/unit_node/timers_test.ts +++ b/tests/unit_node/timers_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, fail } from "@std/assert"; import * as timers from "node:timers"; diff --git a/tests/unit_node/tls_test.ts b/tests/unit_node/tls_test.ts index 627b948cd1..f34d9efb5b 100644 --- a/tests/unit_node/tls_test.ts +++ b/tests/unit_node/tls_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit_node/tty_test.ts b/tests/unit_node/tty_test.ts index df2888ddd0..9acf9be614 100644 --- a/tests/unit_node/tty_test.ts +++ b/tests/unit_node/tty_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any import { assert } from "@std/assert"; diff --git a/tests/unit_node/util_test.ts b/tests/unit_node/util_test.ts index af174b0f4d..0f83bb1b9a 100644 --- a/tests/unit_node/util_test.ts +++ b/tests/unit_node/util_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit_node/v8_test.ts b/tests/unit_node/v8_test.ts index f7208fb0ef..7262ccd1fb 100644 --- a/tests/unit_node/v8_test.ts +++ b/tests/unit_node/v8_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { cachedDataVersionTag, deserialize, diff --git a/tests/unit_node/vm_test.ts b/tests/unit_node/vm_test.ts index 85b9556637..2876208e2f 100644 --- a/tests/unit_node/vm_test.ts +++ b/tests/unit_node/vm_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "@std/assert"; import { createContext, diff --git a/tests/unit_node/wasi_test.ts b/tests/unit_node/wasi_test.ts index 6af2d4b1db..ae174e961b 100644 --- a/tests/unit_node/wasi_test.ts +++ b/tests/unit_node/wasi_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import wasi from "node:wasi"; import { assertThrows } from "@std/assert"; diff --git a/tests/unit_node/worker_threads_test.ts b/tests/unit_node/worker_threads_test.ts index 808fd6116e..c994b91eb1 100644 --- a/tests/unit_node/worker_threads_test.ts +++ b/tests/unit_node/worker_threads_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, @@ -841,3 +841,26 @@ Deno.test({ assertEquals(result, true); }, }); + +Deno.test("[node/worker_threads] Worker runs async ops correctly", async () => { + const recvMessage = Promise.withResolvers(); + const timer = setTimeout(() => recvMessage.reject(), 1000); + const worker = new workerThreads.Worker( + ` + import { parentPort } from "node:worker_threads"; + setTimeout(() => { + parentPort.postMessage("Hello from worker"); + }, 10); + `, + { eval: true }, + ); + + worker.on("message", (msg) => { + assertEquals(msg, "Hello from worker"); + worker.terminate(); + recvMessage.resolve(); + clearTimeout(timer); + }); + + await recvMessage.promise; +}); diff --git a/tests/unit_node/zlib_test.ts b/tests/unit_node/zlib_test.ts index de2d2450d1..fb066a30d1 100644 --- a/tests/unit_node/zlib_test.ts +++ b/tests/unit_node/zlib_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "@std/assert"; import { fromFileUrl, relative } from "@std/path"; diff --git a/tests/util/server/Cargo.toml b/tests/util/server/Cargo.toml index efc81da17c..aec9f6e91c 100644 --- a/tests/util/server/Cargo.toml +++ b/tests/util/server/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "test_server" diff --git a/tests/util/server/src/assertions.rs b/tests/util/server/src/assertions.rs index c8b8845f4c..9eb39fb4a8 100644 --- a/tests/util/server/src/assertions.rs +++ b/tests/util/server/src/assertions.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::Write; diff --git a/tests/util/server/src/builders.rs b/tests/util/server/src/builders.rs index 1cc1af2812..d5df8d09c8 100644 --- a/tests/util/server/src/builders.rs +++ b/tests/util/server/src/builders.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; diff --git a/tests/util/server/src/factory.rs b/tests/util/server/src/factory.rs index 5b796fbc1d..a13d673971 100644 --- a/tests/util/server/src/factory.rs +++ b/tests/util/server/src/factory.rs @@ -1,8 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use glob::glob; +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::path::PathBuf; +use glob::glob; + /// Generate a unit test factory verified and backed by a glob. #[macro_export] macro_rules! unit_test_factory { diff --git a/tests/util/server/src/fs.rs b/tests/util/server/src/fs.rs index 7feb0799ae..19b94731eb 100644 --- a/tests/util/server/src/fs.rs +++ b/tests/util/server/src/fs.rs @@ -1,7 +1,5 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use lsp_types::Uri; -use pretty_assertions::assert_eq; use std::borrow::Cow; use std::collections::HashSet; use std::ffi::OsStr; @@ -15,6 +13,8 @@ use std::str::FromStr; use std::sync::Arc; use anyhow::Context; +use lsp_types::Uri; +use pretty_assertions::assert_eq; use serde::de::DeserializeOwned; use serde::Serialize; use url::Url; diff --git a/tests/util/server/src/https.rs b/tests/util/server/src/https.rs index 617fd5cae2..f3fc1291fe 100644 --- a/tests/util/server/src/https.rs +++ b/tests/util/server/src/https.rs @@ -1,4 +1,9 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. +use std::io; +use std::num::NonZeroUsize; +use std::result::Result; +use std::sync::Arc; + use anyhow::anyhow; use futures::Stream; use futures::StreamExt; @@ -6,10 +11,6 @@ use rustls_tokio_stream::rustls; use rustls_tokio_stream::rustls::pki_types::CertificateDer; use rustls_tokio_stream::rustls::pki_types::PrivateKeyDer; use rustls_tokio_stream::TlsStream; -use std::io; -use std::num::NonZeroUsize; -use std::result::Result; -use std::sync::Arc; use tokio::net::TcpStream; use crate::get_tcp_listener_stream; diff --git a/tests/util/server/src/lib.rs b/tests/util/server/src/lib.rs index 531944bf6a..477568ab1b 100644 --- a/tests/util/server/src/lib.rs +++ b/tests/util/server/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::env; @@ -1296,9 +1296,10 @@ pub(crate) mod colors { #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; + use super::*; + #[test] fn parse_wrk_output_1() { const TEXT: &str = include_str!("./testdata/wrk1.txt"); diff --git a/tests/util/server/src/lsp.rs b/tests/util/server/src/lsp.rs index 92169ee644..12593578c3 100644 --- a/tests/util/server/src/lsp.rs +++ b/tests/util/server/src/lsp.rs @@ -1,11 +1,24 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::deno_exe_path; -use crate::jsr_registry_url; -use crate::npm_registry_url; -use crate::PathRef; - -use super::TempDir; +use std::collections::HashMap; +use std::collections::HashSet; +use std::ffi::OsStr; +use std::ffi::OsString; +use std::io; +use std::io::BufRead; +use std::io::BufReader; +use std::io::Write; +use std::path::Path; +use std::process::Child; +use std::process::ChildStdin; +use std::process::ChildStdout; +use std::process::Command; +use std::process::Stdio; +use std::str::FromStr; +use std::sync::mpsc; +use std::sync::Arc; +use std::time::Duration; +use std::time::Instant; use anyhow::Result; use lsp_types as lsp; @@ -32,27 +45,14 @@ use serde::Serialize; use serde_json::json; use serde_json::to_value; use serde_json::Value; -use std::collections::HashMap; -use std::collections::HashSet; -use std::ffi::OsStr; -use std::ffi::OsString; -use std::io; -use std::io::BufRead; -use std::io::BufReader; -use std::io::Write; -use std::path::Path; -use std::process::Child; -use std::process::ChildStdin; -use std::process::ChildStdout; -use std::process::Command; -use std::process::Stdio; -use std::str::FromStr; -use std::sync::mpsc; -use std::sync::Arc; -use std::time::Duration; -use std::time::Instant; use url::Url; +use super::TempDir; +use crate::deno_exe_path; +use crate::jsr_registry_url; +use crate::npm_registry_url; +use crate::PathRef; + static CONTENT_TYPE_REG: Lazy = lazy_regex::lazy_regex!(r"(?i)^content-length:\s+(\d+)"); @@ -1289,6 +1289,9 @@ impl SourceFile { "md" => "markdown", "html" => "html", "css" => "css", + "scss" => "scss", + "sass" => "sass", + "less" => "less", "yaml" => "yaml", "sql" => "sql", "svelte" => "svelte", diff --git a/tests/util/server/src/macros.rs b/tests/util/server/src/macros.rs index e076583f19..52c84acf00 100644 --- a/tests/util/server/src/macros.rs +++ b/tests/util/server/src/macros.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[macro_export] // https://stackoverflow.com/questions/38088067/equivalent-of-func-or-function-in-rust diff --git a/tests/util/server/src/npm.rs b/tests/util/server/src/npm.rs index 081989ddb5..da9a59cf2f 100644 --- a/tests/util/server/src/npm.rs +++ b/tests/util/server/src/npm.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::fs; @@ -267,6 +267,7 @@ fn get_npm_package( let mut tarballs = HashMap::new(); let mut versions = serde_json::Map::new(); let mut latest_version = semver::Version::parse("0.0.0").unwrap(); + let mut dist_tags = serde_json::Map::new(); for entry in fs::read_dir(&package_folder)? { let entry = entry?; let file_type = entry.file_type()?; @@ -345,6 +346,14 @@ fn get_npm_package( } } + if let Some(publish_config) = version_info.get("publishConfig") { + if let Some(tag) = publish_config.get("tag") { + if let Some(tag) = tag.as_str() { + dist_tags.insert(tag.to_string(), version.clone().into()); + } + } + } + versions.insert(version.clone(), version_info.into()); let version = semver::Version::parse(&version)?; if version.cmp(&latest_version).is_gt() { @@ -352,8 +361,9 @@ fn get_npm_package( } } - let mut dist_tags = serde_json::Map::new(); - dist_tags.insert("latest".to_string(), latest_version.to_string().into()); + if !dist_tags.contains_key("latest") { + dist_tags.insert("latest".to_string(), latest_version.to_string().into()); + } // create the registry file for this package let mut registry_file = serde_json::Map::new(); diff --git a/tests/util/server/src/pty.rs b/tests/util/server/src/pty.rs index 07659262cf..d72617cd5e 100644 --- a/tests/util/server/src/pty.rs +++ b/tests/util/server/src/pty.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; @@ -323,9 +323,10 @@ fn create_pty( cwd: &Path, env_vars: Option>, ) -> Box { - use crate::pty::unix::UnixPty; use std::os::unix::process::CommandExt; + use crate::pty::unix::UnixPty; + // Manually open pty main/secondary sides in the test process. Since we're not actually // changing uid/gid here, this is the easiest way to do it. diff --git a/tests/util/server/src/servers/grpc.rs b/tests/util/server/src/servers/grpc.rs index 144afc06a3..f097fbd595 100644 --- a/tests/util/server/src/servers/grpc.rs +++ b/tests/util/server/src/servers/grpc.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use futures::StreamExt; use h2; diff --git a/tests/util/server/src/servers/hyper_utils.rs b/tests/util/server/src/servers/hyper_utils.rs index 8e01151ed4..a8b30ac24e 100644 --- a/tests/util/server/src/servers/hyper_utils.rs +++ b/tests/util/server/src/servers/hyper_utils.rs @@ -1,4 +1,10 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::convert::Infallible; +use std::io; +use std::net::SocketAddr; +use std::pin::Pin; +use std::result::Result; use bytes::Bytes; use futures::Future; @@ -10,11 +16,6 @@ use http::Request; use http::Response; use http_body_util::combinators::UnsyncBoxBody; use hyper_util::rt::TokioIo; -use std::convert::Infallible; -use std::io; -use std::net::SocketAddr; -use std::pin::Pin; -use std::result::Result; use tokio::net::TcpListener; #[derive(Debug, Clone, Copy)] diff --git a/tests/util/server/src/servers/jsr_registry.rs b/tests/util/server/src/servers/jsr_registry.rs index 8970750a28..b4b045087c 100644 --- a/tests/util/server/src/servers/jsr_registry.rs +++ b/tests/util/server/src/servers/jsr_registry.rs @@ -1,10 +1,12 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::tests_path; +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::convert::Infallible; +use std::net::SocketAddr; +use std::path::Path; +use std::sync::Mutex; -use super::run_server; -use super::ServerKind; -use super::ServerOptions; use base64::engine::general_purpose::STANDARD_NO_PAD; use base64::Engine as _; use bytes::Bytes; @@ -17,12 +19,11 @@ use hyper::Response; use hyper::StatusCode; use once_cell::sync::Lazy; use serde_json::json; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::convert::Infallible; -use std::net::SocketAddr; -use std::path::Path; -use std::sync::Mutex; + +use super::run_server; +use super::ServerKind; +use super::ServerOptions; +use crate::tests_path; pub async fn registry_server(port: u16) { let registry_server_addr = SocketAddr::from(([127, 0, 0, 1], port)); diff --git a/tests/util/server/src/servers/mod.rs b/tests/util/server/src/servers/mod.rs index 0b1d99aeb9..03f327319c 100644 --- a/tests/util/server/src/servers/mod.rs +++ b/tests/util/server/src/servers/mod.rs @@ -1,7 +1,14 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Usage: provide a port as argument to run hyper_hello benchmark server // otherwise this starts multiple servers on many ports for test endpoints. +use std::collections::HashMap; +use std::convert::Infallible; +use std::env; +use std::net::SocketAddr; +use std::result::Result; +use std::time::Duration; + use base64::prelude::BASE64_STANDARD; use base64::Engine; use bytes::Bytes; @@ -27,12 +34,6 @@ use http_body_util::Empty; use http_body_util::Full; use pretty_assertions::assert_eq; use prost::Message; -use std::collections::HashMap; -use std::convert::Infallible; -use std::env; -use std::net::SocketAddr; -use std::result::Result; -use std::time::Duration; use tokio::io::AsyncWriteExt; use tokio::net::TcpStream; @@ -48,12 +49,11 @@ use hyper_utils::run_server_with_acceptor; use hyper_utils::ServerKind; use hyper_utils::ServerOptions; -use crate::TEST_SERVERS_COUNT; - use super::https::get_tls_listener_stream; use super::https::SupportedHttpVersions; use super::std_path; use super::testdata_path; +use crate::TEST_SERVERS_COUNT; pub(crate) const PORT: u16 = 4545; const TEST_AUTH_TOKEN: &str = "abcdef123456789"; @@ -577,11 +577,6 @@ async fn main_server( ); Ok(res) } - (_, "/bad_redirect") => { - let mut res = Response::new(empty_body()); - *res.status_mut() = StatusCode::FOUND; - Ok(res) - } (_, "/server_error") => { let mut res = Response::new(empty_body()); *res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; diff --git a/tests/util/server/src/servers/nodejs_org_mirror.rs b/tests/util/server/src/servers/nodejs_org_mirror.rs index 521e79d3c4..e3e94757c0 100644 --- a/tests/util/server/src/servers/nodejs_org_mirror.rs +++ b/tests/util/server/src/servers/nodejs_org_mirror.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! Server for NodeJS header tarballs, used by `node-gyp` in tests to download headers //! diff --git a/tests/util/server/src/servers/npm_registry.rs b/tests/util/server/src/servers/npm_registry.rs index 4ada468fac..c814b83340 100644 --- a/tests/util/server/src/servers/npm_registry.rs +++ b/tests/util/server/src/servers/npm_registry.rs @@ -1,14 +1,11 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. -use crate::npm; +use std::convert::Infallible; +use std::net::Ipv6Addr; +use std::net::SocketAddr; +use std::net::SocketAddrV6; +use std::path::PathBuf; -use super::custom_headers; -use super::empty_body; -use super::hyper_utils::HandlerOutput; -use super::run_server; -use super::string_body; -use super::ServerKind; -use super::ServerOptions; use bytes::Bytes; use futures::future::LocalBoxFuture; use futures::Future; @@ -18,11 +15,15 @@ use hyper::body::Incoming; use hyper::Request; use hyper::Response; use hyper::StatusCode; -use std::convert::Infallible; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::net::SocketAddrV6; -use std::path::PathBuf; + +use super::custom_headers; +use super::empty_body; +use super::hyper_utils::HandlerOutput; +use super::run_server; +use super::string_body; +use super::ServerKind; +use super::ServerOptions; +use crate::npm; pub fn public_npm_registry(port: u16) -> Vec> { run_npm_server(port, "npm registry server error", { diff --git a/tests/util/server/src/servers/ws.rs b/tests/util/server/src/servers/ws.rs index dd4efbf659..2b9189f3e1 100644 --- a/tests/util/server/src/servers/ws.rs +++ b/tests/util/server/src/servers/ws.rs @@ -1,4 +1,7 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::pin::Pin; +use std::result::Result; use anyhow::anyhow; use bytes::Bytes; @@ -22,8 +25,6 @@ use hyper::Response; use hyper::StatusCode; use hyper_util::rt::TokioIo; use pretty_assertions::assert_eq; -use std::pin::Pin; -use std::result::Result; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; diff --git a/tests/util/server/src/spawn.rs b/tests/util/server/src/spawn.rs index bfd83e9b26..936da7c785 100644 --- a/tests/util/server/src/spawn.rs +++ b/tests/util/server/src/spawn.rs @@ -1,7 +1,8 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use anyhow::Error; +// Copyright 2018-2025 the Deno authors. MIT license. use std::convert::Infallible; +use anyhow::Error; + /// For unix targets, we just replace our current process with the desired cargo process. #[cfg(unix)] pub fn exec_replace_inner( @@ -30,6 +31,7 @@ pub fn exec_replace_inner( ) -> Result { use std::os::windows::io::AsRawHandle; use std::process::Command; + use win32job::ExtendedLimitInfo; use win32job::Job; diff --git a/tests/util/server/src/test_server.rs b/tests/util/server/src/test_server.rs index 3ae3eaa7d9..a118318e6f 100644 --- a/tests/util/server/src/test_server.rs +++ b/tests/util/server/src/test_server.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/tests/wpt/runner/runner.ts b/tests/wpt/runner/runner.ts index 6e654fd334..e124d28c7a 100644 --- a/tests/wpt/runner/runner.ts +++ b/tests/wpt/runner/runner.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { delay, join, diff --git a/tests/wpt/runner/testharnessreport.js b/tests/wpt/runner/testharnessreport.js index 7cc6a9e2dd..f5fce1efe3 100644 --- a/tests/wpt/runner/testharnessreport.js +++ b/tests/wpt/runner/testharnessreport.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. window.add_result_callback(({ message, name, stack, status }) => { const data = new TextEncoder().encode( diff --git a/tests/wpt/runner/utils.ts b/tests/wpt/runner/utils.ts index 140c388ec2..1d9426aed9 100644 --- a/tests/wpt/runner/utils.ts +++ b/tests/wpt/runner/utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// FLAGS import { parseArgs } from "@std/cli/parse-args"; diff --git a/tests/wpt/wpt.ts b/tests/wpt/wpt.ts index b13a10cf4c..ec4a77fba1 100755 --- a/tests/wpt/wpt.ts +++ b/tests/wpt/wpt.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-write --allow-read --allow-net --allow-env --allow-run --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/build_bench.ts b/tools/build_bench.ts index 400737561b..ccc72410ba 100755 --- a/tools/build_bench.ts +++ b/tools/build_bench.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-env --allow-read --allow-write --allow-run=git,cargo -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import $ from "https://deno.land/x/dax@0.32.0/mod.ts"; diff --git a/tools/build_benchmark_jsons.js b/tools/build_benchmark_jsons.js index 64310f75a7..6ed7cb0d83 100755 --- a/tools/build_benchmark_jsons.js +++ b/tools/build_benchmark_jsons.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { buildPath, existsSync, join } from "./util.js"; const currentDataFile = join(buildPath(), "bench.json"); diff --git a/tools/copyright_checker.js b/tools/copyright_checker.js index d7d196bc44..9ac84e3ec5 100755 --- a/tools/copyright_checker.js +++ b/tools/copyright_checker.js @@ -1,11 +1,11 @@ #!/usr/bin/env -S deno run --allow-read=. --allow-run=git --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console import { getSources, ROOT_PATH } from "./util.js"; -const copyrightYear = 2024; +const copyrightYear = 2025; const buffer = new Uint8Array(1024); const textDecoder = new TextDecoder(); @@ -63,7 +63,7 @@ export async function checkCopyright() { const ACCEPTABLE_LINES = /^(\/\/ deno-lint-.*|\/\/ Copyright.*|\/\/ Ported.*|\s*|#!\/.*)\n/; const COPYRIGHT_LINE = - `Copyright 2018-${copyrightYear} the Deno authors. All rights reserved. MIT license.`; + `Copyright 2018-${copyrightYear} the Deno authors. MIT license.`; const TOML_COPYRIGHT_LINE = "# " + COPYRIGHT_LINE; const C_STYLE_COPYRIGHT_LINE = "// " + COPYRIGHT_LINE; diff --git a/tools/core_import_map.json b/tools/core_import_map.json index bc0674277e..4843884015 100644 --- a/tools/core_import_map.json +++ b/tools/core_import_map.json @@ -38,7 +38,7 @@ "ext:deno_node/_fs/_fs_stat.ts": "../ext/node/polyfills/_fs/_fs_stat.ts", "ext:deno_node/_fs/_fs_watch.ts": "../ext/node/polyfills/_fs/_fs_watch.ts", "ext:deno_node/_fs/_fs_write.mjs": "../ext/node/polyfills/_fs/_fs_write.mjs", - "ext:deno_node/_fs/_fs_writev.mjs": "../ext/node/polyfills/_fs/_fs_writev.mjs", + "ext:deno_node/_fs/_fs_writev.ts": "../ext/node/polyfills/_fs/_fs_writev.ts", "ext:deno_node/_global.d.ts": "../ext/node/polyfills/_global.d.ts", "node:_http_agent": "../ext/node/polyfills/_http_agent.mjs", "node:_http_common": "../ext/node/polyfills/_http_common.ts", @@ -239,10 +239,10 @@ "ext:runtime/06_util.js": "../runtime/js/06_util.js", "ext:runtime/10_permissions.js": "../runtime/js/10_permissions.js", "ext:runtime/11_workers.js": "../runtime/js/11_workers.js", - "ext:runtime/30_os.js": "../runtime/js/30_os.js", + "ext:deno_os/30_os.js": "../ext/os/30_os.js", "ext:runtime/40_fs_events.js": "../runtime/js/40_fs_events.js", "ext:runtime/40_process.js": "../runtime/js/40_process.js", - "ext:runtime/40_signals.js": "../runtime/js/40_signals.js", + "ext:deno_os/40_signals.js": "../ext/os/40_signals.js", "ext:runtime/40_tty.js": "../runtime/js/40_tty.js", "ext:runtime/41_prompt.js": "../runtime/js/41_prompt.js", "ext:runtime/90_deno_ns.js": "../runtime/js/90_deno_ns.js", @@ -250,6 +250,7 @@ "ext:deno_node/_util/std_fmt_colors.ts": "../ext/node/polyfills/_util/std_fmt_colors.ts", "ext:deno_telemetry/telemetry.ts": "../ext/deno_telemetry/telemetry.ts", "ext:deno_telemetry/util.ts": "../ext/deno_telemetry/util.ts", + "ext:cli/40_lint_selector.js": "../cli/js/40_lint_selector.js", "@std/archive": "../tests/util/std/archive/mod.ts", "@std/archive/tar": "../tests/util/std/archive/tar.ts", "@std/archive/untar": "../tests/util/std/archive/untar.ts", diff --git a/tools/format.js b/tools/format.js index b29667ca77..02211cdaf8 100755 --- a/tools/format.js +++ b/tools/format.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-all --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { join, ROOT_PATH } from "./util.js"; const subcommand = Deno.args.includes("--check") ? "check" : "fmt"; diff --git a/tools/generate_types_deno.ts b/tools/generate_types_deno.ts index 265b6f5371..fa60f51a4b 100755 --- a/tools/generate_types_deno.ts +++ b/tools/generate_types_deno.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This script is used to generate the @types/deno package on DefinitelyTyped. @@ -76,7 +76,7 @@ async function createDenoDtsFile() { file.insertStatements( 0, - "// Copyright 2018-2024 the Deno authors. MIT license.\n\n", + "// Copyright 2018-2025 the Deno authors. MIT license.\n\n", ); file.saveSync(); diff --git a/tools/install_prebuilt.js b/tools/install_prebuilt.js index 0c983d405d..c7a58c4691 100755 --- a/tools/install_prebuilt.js +++ b/tools/install_prebuilt.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --unstable --allow-write --allow-read --allow-net --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { getPrebuilt } from "./util.js"; const args = Deno.args.slice(); diff --git a/tools/jsdoc_checker.js b/tools/jsdoc_checker.js index 241d04273b..034782136c 100755 --- a/tools/jsdoc_checker.js +++ b/tools/jsdoc_checker.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-read --allow-env --allow-sys --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Node, Project, ts } from "npm:ts-morph@22.0.0"; import { join, ROOT_PATH } from "./util.js"; diff --git a/tools/lint.js b/tools/lint.js index 5bc3f2654f..3f548d5c3a 100755 --- a/tools/lint.js +++ b/tools/lint.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-all --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/napi/generate_symbols_lists.js b/tools/napi/generate_symbols_lists.js index efb0edc043..42942777f6 100755 --- a/tools/napi/generate_symbols_lists.js +++ b/tools/napi/generate_symbols_lists.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-read --allow-write -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import exports from "../../ext/napi/sym/symbol_exports.json" with { type: "json", diff --git a/tools/ops.d.ts b/tools/ops.d.ts index 8acb1e5883..1d6dc8304a 100644 --- a/tools/ops.d.ts +++ b/tools/ops.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file is intentionally empty - that puts this file into script mode, // which then allows all symbols to be imported from the file. diff --git a/tools/release/00_start_release.ts b/tools/release/00_start_release.ts index 125a76af66..a7a5d22a40 100755 --- a/tools/release/00_start_release.ts +++ b/tools/release/00_start_release.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --quiet --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/release/01_bump_crate_versions.ts b/tools/release/01_bump_crate_versions.ts index bef8011ba1..ddbc785c75 100755 --- a/tools/release/01_bump_crate_versions.ts +++ b/tools/release/01_bump_crate_versions.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { DenoWorkspace } from "./deno_workspace.ts"; import { $, GitLogOutput, semver } from "./deps.ts"; diff --git a/tools/release/02_create_pr.ts b/tools/release/02_create_pr.ts index 5ef64cd144..97f50f0d44 100755 --- a/tools/release/02_create_pr.ts +++ b/tools/release/02_create_pr.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { DenoWorkspace } from "./deno_workspace.ts"; import { $, createOctoKit, getGitHubRepository } from "./deps.ts"; diff --git a/tools/release/03_publish_crates.ts b/tools/release/03_publish_crates.ts index ecfb75e796..17ab2f71f7 100755 --- a/tools/release/03_publish_crates.ts +++ b/tools/release/03_publish_crates.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/release/04_post_publish.ts b/tools/release/04_post_publish.ts index 2fa9e73561..42bf2dbafd 100755 --- a/tools/release/04_post_publish.ts +++ b/tools/release/04_post_publish.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { DenoWorkspace } from "./deno_workspace.ts"; import { $, createOctoKit, getGitHubRepository } from "./deps.ts"; diff --git a/tools/release/05_create_release_notes.ts b/tools/release/05_create_release_notes.ts index 9ea9ade31f..383f5ba089 100755 --- a/tools/release/05_create_release_notes.ts +++ b/tools/release/05_create_release_notes.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { $ } from "./deps.ts"; import { DenoWorkspace } from "./deno_workspace.ts"; diff --git a/tools/release/deno_workspace.ts b/tools/release/deno_workspace.ts index e55a02b73f..e5d9f1b928 100644 --- a/tools/release/deno_workspace.ts +++ b/tools/release/deno_workspace.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { $, ReleasesMdFile, Repo } from "./deps.ts"; diff --git a/tools/release/deps.ts b/tools/release/deps.ts index 568830a74b..17177870d7 100644 --- a/tools/release/deps.ts +++ b/tools/release/deps.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export * from "https://raw.githubusercontent.com/denoland/automation/0.19.0/mod.ts"; export * from "https://raw.githubusercontent.com/denoland/automation/0.19.0/github_actions.ts"; diff --git a/tools/release/npm/bin.cjs b/tools/release/npm/bin.cjs index 984aa350f1..aaa9dd343c 100644 --- a/tools/release/npm/bin.cjs +++ b/tools/release/npm/bin.cjs @@ -1,5 +1,5 @@ #!/usr/bin/env node -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check const path = require("path"); diff --git a/tools/release/npm/build.ts b/tools/release/npm/build.ts index b1f1c45cbf..606ff6dbe8 100755 --- a/tools/release/npm/build.ts +++ b/tools/release/npm/build.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // NOTICE: This deployment/npm folder was lifted from https://github.com/dprint/dprint/blob/0ba79811cc96d2dee8e0cf766a8c8c0fc44879c2/deployment/npm/ // with permission (Copyright 2019-2023 David Sherret) import $ from "jsr:@david/dax@^0.42.0"; diff --git a/tools/release/npm/install.cjs b/tools/release/npm/install.cjs index 8bf9aabe47..5546818941 100644 --- a/tools/release/npm/install.cjs +++ b/tools/release/npm/install.cjs @@ -1,5 +1,5 @@ // @ts-check -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. "use strict"; require("./install_api.cjs").runInstall(); diff --git a/tools/release/npm/install_api.cjs b/tools/release/npm/install_api.cjs index 026d8ccc45..d2571839bc 100644 --- a/tools/release/npm/install_api.cjs +++ b/tools/release/npm/install_api.cjs @@ -1,5 +1,5 @@ // @ts-check -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. "use strict"; const fs = require("fs"); diff --git a/tools/release/promote_to_release.ts b/tools/release/promote_to_release.ts index 046f4d33a8..bca798d22c 100755 --- a/tools/release/promote_to_release.ts +++ b/tools/release/promote_to_release.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/upload_wptfyi.js b/tools/upload_wptfyi.js index 23dd4c6602..b08f352329 100644 --- a/tools/upload_wptfyi.js +++ b/tools/upload_wptfyi.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This script pushes new WPT results to wpt.fyi. When the `--ghstatus` flag is // passed, will automatically add a status check to the commit with a link to diff --git a/tools/util.js b/tools/util.js index 8669337bff..07f405098c 100644 --- a/tools/util.js +++ b/tools/util.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/verify_pr_title.js b/tools/verify_pr_title.js index 90a045d3f4..dd623bb630 100644 --- a/tools/verify_pr_title.js +++ b/tools/verify_pr_title.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/wgpu_sync.js b/tools/wgpu_sync.js index 1d9e180d52..995a4b5975 100755 --- a/tools/wgpu_sync.js +++ b/tools/wgpu_sync.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --unstable --allow-read --allow-write --allow-run --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { join, ROOT_PATH } from "./util.js";