mirror of
https://github.com/denoland/deno.git
synced 2024-11-28 16:20:57 -05:00
Merge branch 'main' into Add-Dynamic-Device-Path-Handling-for-Windows-File-Access
This commit is contained in:
commit
5eb63928a7
461 changed files with 8150 additions and 4250 deletions
5
.github/workflows/cargo_publish.yml
vendored
5
.github/workflows/cargo_publish.yml
vendored
|
@ -2,6 +2,11 @@ name: cargo_publish
|
|||
|
||||
on: workflow_dispatch
|
||||
|
||||
# Ensures only one publish is running at a time
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: cargo publish
|
||||
|
|
34
.github/workflows/ci.generate.ts
vendored
34
.github/workflows/ci.generate.ts
vendored
|
@ -5,7 +5,7 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify";
|
|||
// Bump this number when you want to purge the cache.
|
||||
// Note: the tools/release/01_bump_crate_versions.ts script will update this version
|
||||
// automatically via regex, so ensure that this line maintains this format.
|
||||
const cacheVersion = 18;
|
||||
const cacheVersion = 21;
|
||||
|
||||
const ubuntuX86Runner = "ubuntu-22.04";
|
||||
const ubuntuX86XlRunner = "ubuntu-22.04-xl";
|
||||
|
@ -751,11 +751,11 @@ const ci = {
|
|||
].join("\n"),
|
||||
run: [
|
||||
"cd target/release",
|
||||
"shasum -a 256 deno > deno-${{ matrix.arch }}-unknown-linux-gnu.sha256sum",
|
||||
"zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno",
|
||||
"shasum -a 256 deno-${{ matrix.arch }}-unknown-linux-gnu.zip > deno-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum",
|
||||
"strip denort",
|
||||
"shasum -a 256 denort > denort-${{ matrix.arch }}-unknown-linux-gnu.sha256sum",
|
||||
"zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort",
|
||||
"shasum -a 256 denort-${{ matrix.arch }}-unknown-linux-gnu.zip > denort-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum",
|
||||
"./deno types > lib.deno.d.ts",
|
||||
].join("\n"),
|
||||
},
|
||||
|
@ -779,11 +779,11 @@ const ci = {
|
|||
"--p12-file=<(echo $APPLE_CODESIGN_KEY | base64 -d) " +
|
||||
"--entitlements-xml-file=cli/entitlements.plist",
|
||||
"cd target/release",
|
||||
"shasum -a 256 deno > deno-${{ matrix.arch }}-apple-darwin.sha256sum",
|
||||
"zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno",
|
||||
"shasum -a 256 deno-${{ matrix.arch }}-apple-darwin.zip > deno-${{ matrix.arch }}-apple-darwin.zip.sha256sum",
|
||||
"strip denort",
|
||||
"shasum -a 256 denort > denort-${{ matrix.arch }}-apple-darwin.sha256sum",
|
||||
"zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort",
|
||||
"shasum -a 256 denort-${{ matrix.arch }}-apple-darwin.zip > denort-${{ matrix.arch }}-apple-darwin.zip.sha256sum",
|
||||
]
|
||||
.join("\n"),
|
||||
},
|
||||
|
@ -797,10 +797,10 @@ const ci = {
|
|||
].join("\n"),
|
||||
shell: "pwsh",
|
||||
run: [
|
||||
"Get-FileHash target/release/deno.exe -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.sha256sum",
|
||||
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip",
|
||||
"Get-FileHash target/release/denort.exe -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.sha256sum",
|
||||
"Get-FileHash target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum",
|
||||
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip",
|
||||
"Get-FileHash target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum",
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
|
@ -1045,25 +1045,25 @@ const ci = {
|
|||
with: {
|
||||
files: [
|
||||
"target/release/deno-x86_64-pc-windows-msvc.zip",
|
||||
"target/release/deno-x86_64-pc-windows-msvc.sha256sum",
|
||||
"target/release/deno-x86_64-pc-windows-msvc.zip.sha256sum",
|
||||
"target/release/denort-x86_64-pc-windows-msvc.zip",
|
||||
"target/release/denort-x86_64-pc-windows-msvc.sha256sum",
|
||||
"target/release/denort-x86_64-pc-windows-msvc.zip.sha256sum",
|
||||
"target/release/deno-x86_64-unknown-linux-gnu.zip",
|
||||
"target/release/deno-x86_64-unknown-linux-gnu.sha256sum",
|
||||
"target/release/deno-x86_64-unknown-linux-gnu.zip.sha256sum",
|
||||
"target/release/denort-x86_64-unknown-linux-gnu.zip",
|
||||
"target/release/denort-x86_64-unknown-linux-gnu.sha256sum",
|
||||
"target/release/denort-x86_64-unknown-linux-gnu.zip.sha256sum",
|
||||
"target/release/deno-x86_64-apple-darwin.zip",
|
||||
"target/release/deno-x86_64-apple-darwin.sha256sum",
|
||||
"target/release/deno-x86_64-apple-darwin.zip.sha256sum",
|
||||
"target/release/denort-x86_64-apple-darwin.zip",
|
||||
"target/release/denort-x86_64-apple-darwin.sha256sum",
|
||||
"target/release/denort-x86_64-apple-darwin.zip.sha256sum",
|
||||
"target/release/deno-aarch64-unknown-linux-gnu.zip",
|
||||
"target/release/deno-aarch64-unknown-linux-gnu.sha256sum",
|
||||
"target/release/deno-aarch64-unknown-linux-gnu.zip.sha256sum",
|
||||
"target/release/denort-aarch64-unknown-linux-gnu.zip",
|
||||
"target/release/denort-aarch64-unknown-linux-gnu.sha256sum",
|
||||
"target/release/denort-aarch64-unknown-linux-gnu.zip.sha256sum",
|
||||
"target/release/deno-aarch64-apple-darwin.zip",
|
||||
"target/release/deno-aarch64-apple-darwin.sha256sum",
|
||||
"target/release/deno-aarch64-apple-darwin.zip.sha256sum",
|
||||
"target/release/denort-aarch64-apple-darwin.zip",
|
||||
"target/release/denort-aarch64-apple-darwin.sha256sum",
|
||||
"target/release/denort-aarch64-apple-darwin.zip.sha256sum",
|
||||
"target/release/deno_src.tar.gz",
|
||||
"target/release/lib.deno.d.ts",
|
||||
].join("\n"),
|
||||
|
|
40
.github/workflows/ci.yml
vendored
40
.github/workflows/ci.yml
vendored
|
@ -361,8 +361,8 @@ jobs:
|
|||
path: |-
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
key: '18-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '18-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
|
||||
key: '21-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '21-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
|
||||
if: '!(matrix.skip)'
|
||||
- name: Restore cache build output (PR)
|
||||
uses: actions/cache/restore@v4
|
||||
|
@ -375,7 +375,7 @@ jobs:
|
|||
!./target/*/*.zip
|
||||
!./target/*/*.tar.gz
|
||||
key: never_saved
|
||||
restore-keys: '18-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
|
||||
restore-keys: '21-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
|
||||
|
@ -442,11 +442,11 @@ jobs:
|
|||
github.repository == 'denoland/deno')
|
||||
run: |-
|
||||
cd target/release
|
||||
shasum -a 256 deno > deno-${{ matrix.arch }}-unknown-linux-gnu.sha256sum
|
||||
zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno
|
||||
shasum -a 256 deno-${{ matrix.arch }}-unknown-linux-gnu.zip > deno-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum
|
||||
strip denort
|
||||
shasum -a 256 denort > denort-${{ matrix.arch }}-unknown-linux-gnu.sha256sum
|
||||
zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort
|
||||
shasum -a 256 denort-${{ matrix.arch }}-unknown-linux-gnu.zip > denort-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum
|
||||
./deno types > lib.deno.d.ts
|
||||
- name: Pre-release (mac)
|
||||
if: |-
|
||||
|
@ -461,11 +461,11 @@ jobs:
|
|||
echo "Key is $(echo $APPLE_CODESIGN_KEY | base64 -d | wc -c) bytes"
|
||||
rcodesign sign target/release/deno --code-signature-flags=runtime --p12-password="$APPLE_CODESIGN_PASSWORD" --p12-file=<(echo $APPLE_CODESIGN_KEY | base64 -d) --entitlements-xml-file=cli/entitlements.plist
|
||||
cd target/release
|
||||
shasum -a 256 deno > deno-${{ matrix.arch }}-apple-darwin.sha256sum
|
||||
zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno
|
||||
shasum -a 256 deno-${{ matrix.arch }}-apple-darwin.zip > deno-${{ matrix.arch }}-apple-darwin.zip.sha256sum
|
||||
strip denort
|
||||
shasum -a 256 denort > denort-${{ matrix.arch }}-apple-darwin.sha256sum
|
||||
zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort
|
||||
shasum -a 256 denort-${{ matrix.arch }}-apple-darwin.zip > denort-${{ matrix.arch }}-apple-darwin.zip.sha256sum
|
||||
- name: Pre-release (windows)
|
||||
if: |-
|
||||
!(matrix.skip) && (matrix.os == 'windows' &&
|
||||
|
@ -474,10 +474,10 @@ jobs:
|
|||
github.repository == 'denoland/deno')
|
||||
shell: pwsh
|
||||
run: |-
|
||||
Get-FileHash target/release/deno.exe -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.sha256sum
|
||||
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip
|
||||
Get-FileHash target/release/denort.exe -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.sha256sum
|
||||
Get-FileHash target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum
|
||||
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip
|
||||
Get-FileHash target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum
|
||||
- name: Upload canary to dl.deno.land
|
||||
if: |-
|
||||
!(matrix.skip) && (matrix.job == 'test' &&
|
||||
|
@ -652,25 +652,25 @@ jobs:
|
|||
with:
|
||||
files: |-
|
||||
target/release/deno-x86_64-pc-windows-msvc.zip
|
||||
target/release/deno-x86_64-pc-windows-msvc.sha256sum
|
||||
target/release/deno-x86_64-pc-windows-msvc.zip.sha256sum
|
||||
target/release/denort-x86_64-pc-windows-msvc.zip
|
||||
target/release/denort-x86_64-pc-windows-msvc.sha256sum
|
||||
target/release/denort-x86_64-pc-windows-msvc.zip.sha256sum
|
||||
target/release/deno-x86_64-unknown-linux-gnu.zip
|
||||
target/release/deno-x86_64-unknown-linux-gnu.sha256sum
|
||||
target/release/deno-x86_64-unknown-linux-gnu.zip.sha256sum
|
||||
target/release/denort-x86_64-unknown-linux-gnu.zip
|
||||
target/release/denort-x86_64-unknown-linux-gnu.sha256sum
|
||||
target/release/denort-x86_64-unknown-linux-gnu.zip.sha256sum
|
||||
target/release/deno-x86_64-apple-darwin.zip
|
||||
target/release/deno-x86_64-apple-darwin.sha256sum
|
||||
target/release/deno-x86_64-apple-darwin.zip.sha256sum
|
||||
target/release/denort-x86_64-apple-darwin.zip
|
||||
target/release/denort-x86_64-apple-darwin.sha256sum
|
||||
target/release/denort-x86_64-apple-darwin.zip.sha256sum
|
||||
target/release/deno-aarch64-unknown-linux-gnu.zip
|
||||
target/release/deno-aarch64-unknown-linux-gnu.sha256sum
|
||||
target/release/deno-aarch64-unknown-linux-gnu.zip.sha256sum
|
||||
target/release/denort-aarch64-unknown-linux-gnu.zip
|
||||
target/release/denort-aarch64-unknown-linux-gnu.sha256sum
|
||||
target/release/denort-aarch64-unknown-linux-gnu.zip.sha256sum
|
||||
target/release/deno-aarch64-apple-darwin.zip
|
||||
target/release/deno-aarch64-apple-darwin.sha256sum
|
||||
target/release/deno-aarch64-apple-darwin.zip.sha256sum
|
||||
target/release/denort-aarch64-apple-darwin.zip
|
||||
target/release/denort-aarch64-apple-darwin.sha256sum
|
||||
target/release/denort-aarch64-apple-darwin.zip.sha256sum
|
||||
target/release/deno_src.tar.gz
|
||||
target/release/lib.deno.d.ts
|
||||
body_path: target/release/release-notes.md
|
||||
|
@ -685,7 +685,7 @@ jobs:
|
|||
!./target/*/*.zip
|
||||
!./target/*/*.sha256sum
|
||||
!./target/*/*.tar.gz
|
||||
key: '18-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
key: '21-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
publish-canary:
|
||||
name: publish canary
|
||||
runs-on: ubuntu-22.04
|
||||
|
|
350
Cargo.lock
generated
350
Cargo.lock
generated
|
@ -769,7 +769,7 @@ dependencies = [
|
|||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"nix 0.26.2",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"os_pipe",
|
||||
"pretty_assertions",
|
||||
|
@ -1154,7 +1154,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno"
|
||||
version = "2.0.0"
|
||||
version = "2.0.2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"async-trait",
|
||||
|
@ -1188,6 +1188,7 @@ dependencies = [
|
|||
"deno_task_shell",
|
||||
"deno_terminal 0.2.0",
|
||||
"deno_tower_lsp",
|
||||
"dhat",
|
||||
"dissimilar",
|
||||
"dotenvy",
|
||||
"dprint-plugin-json",
|
||||
|
@ -1222,7 +1223,7 @@ dependencies = [
|
|||
"memmem",
|
||||
"monch",
|
||||
"napi_sym",
|
||||
"nix 0.26.2",
|
||||
"nix",
|
||||
"node_resolver",
|
||||
"notify",
|
||||
"once_cell",
|
||||
|
@ -1327,7 +1328,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_bench_util"
|
||||
version = "0.165.0"
|
||||
version = "0.167.0"
|
||||
dependencies = [
|
||||
"bencher",
|
||||
"deno_core",
|
||||
|
@ -1336,23 +1337,25 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_broadcast_channel"
|
||||
version = "0.165.0"
|
||||
version = "0.167.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"deno_core",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_cache"
|
||||
version = "0.103.0"
|
||||
version = "0.105.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"deno_core",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -1378,12 +1381,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_canvas"
|
||||
version = "0.40.0"
|
||||
version = "0.42.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"deno_webgpu",
|
||||
"image",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1412,16 +1416,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_console"
|
||||
version = "0.171.0"
|
||||
version = "0.173.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_core"
|
||||
version = "0.311.0"
|
||||
version = "0.314.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e09bd55da542fa1fde753aff617c355b5d782e763ab2a19e4371a56d7844cac"
|
||||
checksum = "83138917579676069b423c3eb9be3c1e579f60dc022d85f6ded4c792456255ff"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
@ -1434,7 +1438,7 @@ dependencies = [
|
|||
"deno_unsync",
|
||||
"futures",
|
||||
"libc",
|
||||
"memoffset 0.9.1",
|
||||
"memoffset",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
|
@ -1457,19 +1461,20 @@ checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1"
|
|||
|
||||
[[package]]
|
||||
name = "deno_cron"
|
||||
version = "0.51.0"
|
||||
version = "0.53.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"deno_core",
|
||||
"saffron",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_crypto"
|
||||
version = "0.185.0"
|
||||
version = "0.187.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-gcm",
|
||||
|
@ -1498,15 +1503,17 @@ dependencies = [
|
|||
"sha2",
|
||||
"signature",
|
||||
"spki",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"uuid",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_doc"
|
||||
version = "0.152.0"
|
||||
version = "0.154.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d237256ad99d6064b271324485120028e843329fd0fa0e93175d5e98f17033"
|
||||
checksum = "17e204e45b0d79750880114e37b34abe19ad0710d8435a8da8f23a528fe98de4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
|
@ -1523,23 +1530,13 @@ dependencies = [
|
|||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syntect",
|
||||
"termcolor",
|
||||
"tree-sitter-bash",
|
||||
"tree-sitter-css",
|
||||
"tree-sitter-highlight",
|
||||
"tree-sitter-html",
|
||||
"tree-sitter-javascript",
|
||||
"tree-sitter-json",
|
||||
"tree-sitter-md",
|
||||
"tree-sitter-regex",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-typescript",
|
||||
"tree-sitter-xml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_fetch"
|
||||
version = "0.195.0"
|
||||
version = "0.197.0"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
|
@ -1560,6 +1557,7 @@ dependencies = [
|
|||
"rustls-webpki",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-socks",
|
||||
|
@ -1571,7 +1569,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_ffi"
|
||||
version = "0.158.0"
|
||||
version = "0.160.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"deno_permissions",
|
||||
|
@ -1583,12 +1581,14 @@ dependencies = [
|
|||
"serde",
|
||||
"serde-value",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_fs"
|
||||
version = "0.81.0"
|
||||
version = "0.83.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base32",
|
||||
|
@ -1599,10 +1599,11 @@ dependencies = [
|
|||
"filetime",
|
||||
"junction",
|
||||
"libc",
|
||||
"nix 0.26.2",
|
||||
"nix",
|
||||
"rand",
|
||||
"rayon",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"winapi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
@ -1638,7 +1639,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_http"
|
||||
version = "0.169.0"
|
||||
version = "0.171.0"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"async-trait",
|
||||
|
@ -1677,7 +1678,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_io"
|
||||
version = "0.81.0"
|
||||
version = "0.83.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"deno_core",
|
||||
|
@ -1698,7 +1699,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_kv"
|
||||
version = "0.79.0"
|
||||
version = "0.81.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -1723,6 +1724,7 @@ dependencies = [
|
|||
"rand",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
@ -1769,11 +1771,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_napi"
|
||||
version = "0.102.0"
|
||||
version = "0.104.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"deno_permissions",
|
||||
"libloading 0.7.4",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1791,7 +1794,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_net"
|
||||
version = "0.163.0"
|
||||
version = "0.165.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"deno_permissions",
|
||||
|
@ -1800,6 +1803,7 @@ dependencies = [
|
|||
"rustls-tokio-stream",
|
||||
"serde",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"trust-dns-proto",
|
||||
"trust-dns-resolver",
|
||||
|
@ -1807,7 +1811,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_node"
|
||||
version = "0.108.0"
|
||||
version = "0.110.0"
|
||||
dependencies = [
|
||||
"aead-gcm-stream",
|
||||
"aes",
|
||||
|
@ -1897,9 +1901,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_npm"
|
||||
version = "0.25.3"
|
||||
version = "0.25.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8050bcc2513046cbc0134ae1bc0f3b251a58b95012f3b81e0ea09a7f069c301b"
|
||||
checksum = "e6b4dc4a9f1cff63d5638e7d93042f24f46300d1cc77b86f3caaa699a7ddccf7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -1916,9 +1920,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_ops"
|
||||
version = "0.187.0"
|
||||
version = "0.190.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e040fd4def8a67538fe38c9955fd970efc9f44284bd69d44f8992a456afd665d"
|
||||
checksum = "26f46d4e4f52f26c882b74a9b58810ea75252b807cf0966166ec333077cdfd85"
|
||||
dependencies = [
|
||||
"proc-macro-rules",
|
||||
"proc-macro2",
|
||||
|
@ -1956,7 +1960,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_permissions"
|
||||
version = "0.31.0"
|
||||
version = "0.33.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"deno_path_util",
|
||||
|
@ -1973,7 +1977,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_resolver"
|
||||
version = "0.3.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base32",
|
||||
|
@ -1989,8 +1993,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_runtime"
|
||||
version = "0.180.0"
|
||||
version = "0.182.0"
|
||||
dependencies = [
|
||||
"color-print",
|
||||
"deno_ast",
|
||||
"deno_broadcast_channel",
|
||||
"deno_cache",
|
||||
|
@ -2030,7 +2035,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"netif",
|
||||
"nix 0.26.2",
|
||||
"nix",
|
||||
"node_resolver",
|
||||
"notify",
|
||||
"ntapi",
|
||||
|
@ -2104,7 +2109,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_tls"
|
||||
version = "0.158.0"
|
||||
version = "0.160.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"deno_native_certs",
|
||||
|
@ -2113,6 +2118,7 @@ dependencies = [
|
|||
"rustls-tokio-stream",
|
||||
"rustls-webpki",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
@ -2152,18 +2158,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_url"
|
||||
version = "0.171.0"
|
||||
version = "0.173.0"
|
||||
dependencies = [
|
||||
"deno_bench_util",
|
||||
"deno_console",
|
||||
"deno_core",
|
||||
"deno_webidl",
|
||||
"thiserror",
|
||||
"urlpattern",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_web"
|
||||
version = "0.202.0"
|
||||
version = "0.204.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64-simd 0.8.0",
|
||||
|
@ -2178,17 +2185,19 @@ dependencies = [
|
|||
"flate2",
|
||||
"futures",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_webgpu"
|
||||
version = "0.138.0"
|
||||
version = "0.140.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"wgpu-core",
|
||||
"wgpu-types",
|
||||
|
@ -2196,7 +2205,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_webidl"
|
||||
version = "0.171.0"
|
||||
version = "0.173.0"
|
||||
dependencies = [
|
||||
"deno_bench_util",
|
||||
"deno_core",
|
||||
|
@ -2204,7 +2213,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_websocket"
|
||||
version = "0.176.0"
|
||||
version = "0.178.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"deno_core",
|
||||
|
@ -2220,16 +2229,18 @@ dependencies = [
|
|||
"once_cell",
|
||||
"rustls-tokio-stream",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_webstorage"
|
||||
version = "0.166.0"
|
||||
version = "0.168.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"deno_web",
|
||||
"rusqlite",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2415,6 +2426,22 @@ version = "1.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6e854126756c496b8c81dec88f9a706b15b875c5849d4097a3854476b9fdf94"
|
||||
|
||||
[[package]]
|
||||
name = "dhat"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98cd11d84628e233de0ce467de10b8633f4ddaecafadefc86e13b84b8739b827"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"lazy_static",
|
||||
"mintex",
|
||||
"parking_lot",
|
||||
"rustc-hash 1.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thousands",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
|
@ -4370,15 +4397,6 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
|
@ -4425,6 +4443,12 @@ dependencies = [
|
|||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mintex"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
|
@ -4488,7 +4512,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "napi_sym"
|
||||
version = "0.101.0"
|
||||
version = "0.103.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
|
@ -4530,20 +4554,6 @@ dependencies = [
|
|||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.7.1",
|
||||
"pin-utils",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
|
@ -4557,7 +4567,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "node_resolver"
|
||||
version = "0.10.0"
|
||||
version = "0.12.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -4733,6 +4743,28 @@ version = "1.19.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "onig"
|
||||
version = "6.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"onig_sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "onig_sys"
|
||||
version = "69.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
|
@ -5721,7 +5753,7 @@ checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
|
|||
dependencies = [
|
||||
"countme",
|
||||
"hashbrown",
|
||||
"memoffset 0.9.1",
|
||||
"memoffset",
|
||||
"rustc-hash 1.1.0",
|
||||
"text-size",
|
||||
]
|
||||
|
@ -5929,7 +5961,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix 0.27.1",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
|
@ -6166,9 +6198,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_v8"
|
||||
version = "0.220.0"
|
||||
version = "0.223.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e7a65d91d79acc82aa229aeb084f4a39bda269069bc1520df40f679495388e4"
|
||||
checksum = "9cf3d859dda87ee96423c01244f10af864fa6d6a9fcdc2b77e0595078ea0ea11"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"serde",
|
||||
|
@ -7021,6 +7053,26 @@ dependencies = [
|
|||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntect"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags 1.3.2",
|
||||
"flate2",
|
||||
"fnv",
|
||||
"once_cell",
|
||||
"onig",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
|
@ -7112,7 +7164,7 @@ dependencies = [
|
|||
"libc",
|
||||
"lsp-types",
|
||||
"monch",
|
||||
"nix 0.26.2",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"os_pipe",
|
||||
"parking_lot",
|
||||
|
@ -7153,24 +7205,30 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thousands"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
|
@ -7452,128 +7510,6 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-bash"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5244703ad2e08a616d859a0557d7aa290adcd5e0990188a692e628ffe9dce40"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-css"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e08e324b1cf60fd3291774b49724c66de2ce8fcf4d358d0b4b82e37b41b1c9b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-highlight"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaca0fe34fa96eec6aaa8e63308dbe1bafe65a6317487c287f93938959b21907"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"thiserror",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-html"
|
||||
version = "0.20.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8766b5ad3721517f8259e6394aefda9c686aebf7a8c74ab8624f2c3b46902fd5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-javascript"
|
||||
version = "0.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8710a71bc6779e33811a8067bdda3ed08bed1733296ff915e44faf60f8c533d7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-json"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b737dcb73c35d74b7d64a5f3dde158113c86a012bf3cee2bfdf2150d23b05db"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-md"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c3cfd068f2527250bbd8ff407431164e12b17863e7eafb76e311dd3f96965a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-regex"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ff1286fe9651b2797484839ffa37aa76c8618d4ccb6836d7e31765dfd60c0d5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-rust"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "277690f420bf90741dea984f3da038ace46c4fe6047cba57a66822226cde1c93"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-typescript"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecb35d98a688378e56c18c9c159824fd16f730ccbea19aacf4f206e5d5438ed9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-xml"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65c3a1b08e9842143f84fde1a18ac40ee77ca80a80b14077e4ca67a3b4808b8b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "triomphe"
|
||||
version = "0.1.13"
|
||||
|
|
63
Cargo.toml
63
Cargo.toml
|
@ -46,18 +46,18 @@ repository = "https://github.com/denoland/deno"
|
|||
|
||||
[workspace.dependencies]
|
||||
deno_ast = { version = "=0.42.2", features = ["transpiling"] }
|
||||
deno_core = { version = "0.311.0" }
|
||||
deno_core = { version = "0.314.2" }
|
||||
|
||||
deno_bench_util = { version = "0.165.0", path = "./bench_util" }
|
||||
deno_bench_util = { version = "0.167.0", path = "./bench_util" }
|
||||
deno_lockfile = "=0.23.1"
|
||||
deno_media_type = { version = "0.1.4", features = ["module_specifier"] }
|
||||
deno_npm = "=0.25.3"
|
||||
deno_npm = "=0.25.4"
|
||||
deno_path_util = "=0.2.1"
|
||||
deno_permissions = { version = "0.31.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.180.0", path = "./runtime" }
|
||||
deno_permissions = { version = "0.33.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.182.0", path = "./runtime" }
|
||||
deno_semver = "=0.5.14"
|
||||
deno_terminal = "0.2.0"
|
||||
napi_sym = { version = "0.101.0", path = "./cli/napi/sym" }
|
||||
napi_sym = { version = "0.103.0", path = "./cli/napi/sym" }
|
||||
test_util = { package = "test_server", path = "./tests/util/server" }
|
||||
|
||||
denokv_proto = "0.8.1"
|
||||
|
@ -66,32 +66,32 @@ denokv_remote = "0.8.1"
|
|||
denokv_sqlite = { default-features = false, version = "0.8.2" }
|
||||
|
||||
# exts
|
||||
deno_broadcast_channel = { version = "0.165.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.103.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.40.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.171.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.51.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.185.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.195.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.158.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.81.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.169.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.81.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.79.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.102.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.163.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.108.0", path = "./ext/node" }
|
||||
deno_tls = { version = "0.158.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.171.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.202.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.138.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.171.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.176.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.166.0", path = "./ext/webstorage" }
|
||||
deno_broadcast_channel = { version = "0.167.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.105.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.42.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.173.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.53.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.187.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.197.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.160.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.83.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.171.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.83.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.81.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.104.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.165.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.110.0", path = "./ext/node" }
|
||||
deno_tls = { version = "0.160.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.173.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.204.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.140.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.173.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.178.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.168.0", path = "./ext/webstorage" }
|
||||
|
||||
# resolvers
|
||||
deno_resolver = { version = "0.3.0", path = "./resolvers/deno" }
|
||||
node_resolver = { version = "0.10.0", path = "./resolvers/node" }
|
||||
deno_resolver = { version = "0.5.0", path = "./resolvers/deno" }
|
||||
node_resolver = { version = "0.12.0", path = "./resolvers/node" }
|
||||
|
||||
aes = "=0.8.3"
|
||||
anyhow = "1.0.57"
|
||||
|
@ -106,6 +106,7 @@ 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()
|
||||
chrono = { version = "0.4", default-features = false, features = ["std", "serde"] }
|
||||
color-print = "0.3.5"
|
||||
console_static_text = "=0.8.1"
|
||||
dashmap = "5.5.3"
|
||||
data-encoding = "2.3.3"
|
||||
|
@ -220,7 +221,7 @@ quote = "1"
|
|||
syn = { version = "2", features = ["full", "extra-traits"] }
|
||||
|
||||
# unix
|
||||
nix = "=0.26.2"
|
||||
nix = "=0.27.1"
|
||||
|
||||
# windows deps
|
||||
junction = "=0.2.0"
|
||||
|
|
71
Releases.md
71
Releases.md
|
@ -6,6 +6,77 @@ https://github.com/denoland/deno/releases
|
|||
We also have one-line install commands at:
|
||||
https://github.com/denoland/deno_install
|
||||
|
||||
### 2.0.2 / 2024.10.17
|
||||
|
||||
- fix(cli): set napi object property properly (#26344)
|
||||
- fix(ext/node): add null check for kStreamBaseField (#26368)
|
||||
- fix(install): don't attempt to cache specifiers that point to directories
|
||||
(#26369)
|
||||
- fix(jupyter): fix panics for overslow subtraction (#26371)
|
||||
- fix(jupyter): update to the new logo (#26353)
|
||||
- fix(net): don't try to set nodelay on upgrade streams (#26342)
|
||||
- fix(node/fs): copyFile with `COPYFILE_EXCL` should not throw if the
|
||||
destination doesn't exist (#26360)
|
||||
- fix(node/http): normalize header names in `ServerResponse` (#26339)
|
||||
- fix(runtime): send ws ping frames from inspector server (#26352)
|
||||
- fix: don't warn on ignored signals on windows (#26332)
|
||||
|
||||
### 2.0.1 / 2024.10.16
|
||||
|
||||
- feat(lsp): "deno/didRefreshDenoConfigurationTree" notifications (#26215)
|
||||
- feat(unstable): `--unstable-detect-cjs` for respecting explicit
|
||||
`"type": "commonjs"` (#26149)
|
||||
- fix(add): create deno.json when running `deno add jsr:<pkg>` (#26275)
|
||||
- fix(add): exact version should not have range `^` specifier (#26302)
|
||||
- fix(child_process): map node `--no-warnings` flag to `--quiet` (#26288)
|
||||
- fix(cli): add prefix to install commands in help (#26318)
|
||||
- fix(cli): consolidate pkg parser for install & remove (#26298)
|
||||
- fix(cli): named export takes precedence over default export in doc testing
|
||||
(#26112)
|
||||
- fix(cli): improve deno info output for npm packages (#25906)
|
||||
- fix(console/ext/repl): support using parseFloat() (#25900)
|
||||
- fix(ext/console): apply coloring for console.table (#26280)
|
||||
- fix(ext/napi): pass user context to napi_threadsafe_fn finalizers (#26229)
|
||||
- fix(ext/node): allow writing to tty columns (#26201)
|
||||
- fix(ext/node): compute pem length (upper bound) for key exports (#26231)
|
||||
- fix(ext/node): fix dns.lookup result ordering (#26264)
|
||||
- fix(ext/node): handle http2 server ending stream (#26235)
|
||||
- fix(ext/node): implement TCP.setNoDelay (#26263)
|
||||
- fix(ext/node): timingSafeEqual account for AB byteOffset (#26292)
|
||||
- fix(ext/node): use primordials in `ext/node/polyfills/internal/buffer.mjs`
|
||||
(#24993)
|
||||
- fix(ext/webgpu): allow GL backend on Windows (#26206)
|
||||
- fix(install): duplicate dependencies in `package.json` (#26128)
|
||||
- fix(install): handle pkg with dep on self when pkg part of peer dep resolution
|
||||
(#26277)
|
||||
- fix(install): retry downloads of registry info / tarballs (#26278)
|
||||
- fix(install): support installing npm package with alias (#26246)
|
||||
- fix(jupyter): copy kernels icons to the kernel directory (#26084)
|
||||
- fix(jupyter): keep running event loop when waiting for messages (#26049)
|
||||
- fix(lsp): relative completions for bare import-mapped specifiers (#26137)
|
||||
- fix(node): make `process.stdout.isTTY` writable (#26130)
|
||||
- fix(node/util): export `styleText` from `node:util` (#26194)
|
||||
- fix(npm): support `--allow-scripts` on `deno run` (and `deno add`,
|
||||
`deno test`, etc) (#26075)
|
||||
- fix(repl): importing json files (#26053)
|
||||
- fix(repl): remove check flags (#26140)
|
||||
- fix(unstable/worker): ensure import permissions are passed (#26101)
|
||||
- fix: add hint for missing `document` global in terminal error (#26218)
|
||||
- fix: do not panic on wsl share file paths on windows (#26081)
|
||||
- fix: do not panic running remote cjs module (#26259)
|
||||
- fix: do not panic when using methods on classes and interfaces in deno doc
|
||||
html output (#26100)
|
||||
- fix: improve suggestions and hints when using CommonJS modules (#26287)
|
||||
- fix: node-api function call should use preamble (#26297)
|
||||
- fix: panic in `prepare_stack_trace_callback` when global interceptor throws
|
||||
(#26241)
|
||||
- fix: use syntect for deno doc html generation (#26322)
|
||||
- perf(http): avoid clone getting request method and url (#26250)
|
||||
- perf(http): cache webidl.converters lookups in ext/fetch/23_response.js
|
||||
(#26256)
|
||||
- perf(http): make heap allocation for path conditional (#26289)
|
||||
- perf: use fast calls for microtask ops (#26236)
|
||||
|
||||
### 2.0.0 / 2024.10.09
|
||||
|
||||
Read announcement blog post at: https://deno.com/blog/v2
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_bench_util"
|
||||
version = "0.165.0"
|
||||
version = "0.167.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno"
|
||||
version = "2.0.0"
|
||||
version = "2.0.2"
|
||||
authors.workspace = true
|
||||
default-run = "deno"
|
||||
edition.workspace = true
|
||||
|
@ -38,6 +38,11 @@ path = "./bench/lsp_bench_standalone.rs"
|
|||
|
||||
[features]
|
||||
default = ["upgrade", "__vendored_zlib_ng"]
|
||||
# A feature that enables heap profiling with dhat on Linux.
|
||||
# 1. Compile with `cargo build --profile=release-with-debug --features=dhat-heap`
|
||||
# 2. Run the executable. It will output a dhat-heap.json file.
|
||||
# 3. Open the json file in https://nnethercote.github.io/dh_view/dh_view.html
|
||||
dhat-heap = ["dhat"]
|
||||
# A feature that enables the upgrade subcommand and the background check for
|
||||
# available updates (of deno binary). This is typically disabled for (Linux)
|
||||
# distribution packages.
|
||||
|
@ -67,7 +72,7 @@ deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposa
|
|||
deno_cache_dir = { workspace = true }
|
||||
deno_config = { version = "=0.37.1", features = ["workspace", "sync"] }
|
||||
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_doc = { version = "0.152.0", features = ["html"] }
|
||||
deno_doc = { version = "0.154.0", default-features = false, features = ["rust", "html", "syntect"] }
|
||||
deno_graph = { version = "=0.83.3" }
|
||||
deno_lint = { version = "=0.67.0", features = ["docs"] }
|
||||
deno_lockfile.workspace = true
|
||||
|
@ -94,10 +99,11 @@ chrono = { workspace = true, features = ["now"] }
|
|||
clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] }
|
||||
clap_complete = "=4.5.24"
|
||||
clap_complete_fig = "=4.5.2"
|
||||
color-print = "0.3.5"
|
||||
color-print.workspace = true
|
||||
console_static_text.workspace = true
|
||||
dashmap.workspace = true
|
||||
data-encoding.workspace = true
|
||||
dhat = { version = "0.3.3", optional = true }
|
||||
dissimilar = "=1.0.4"
|
||||
dotenvy = "0.15.7"
|
||||
dprint-plugin-json = "=0.19.3"
|
||||
|
|
|
@ -575,7 +575,8 @@ fn parse_packages_allowed_scripts(s: &str) -> Result<String, AnyError> {
|
|||
pub struct UnstableConfig {
|
||||
// TODO(bartlomieju): remove in Deno 2.5
|
||||
pub legacy_flag_enabled: bool, // --unstable
|
||||
pub bare_node_builtins: bool, // --unstable-bare-node-builts
|
||||
pub bare_node_builtins: bool,
|
||||
pub detect_cjs: bool,
|
||||
pub sloppy_imports: bool,
|
||||
pub features: Vec<String>, // --unstabe-kv --unstable-cron
|
||||
}
|
||||
|
@ -1177,7 +1178,7 @@ static DENO_HELP: &str = cstr!(
|
|||
|
||||
<y>Dependency management:</>
|
||||
<g>add</> Add dependencies
|
||||
<p(245)>deno add @std/assert | deno add npm:express</>
|
||||
<p(245)>deno add jsr:@std/assert | deno add npm:express</>
|
||||
<g>install</> Install script as an executable
|
||||
<g>uninstall</> Uninstall a script previously installed with deno install
|
||||
<g>remove</> Remove dependencies from the configuration file
|
||||
|
@ -1342,7 +1343,7 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
|
|||
}
|
||||
|
||||
match subcommand.as_str() {
|
||||
"add" => add_parse(&mut flags, &mut m),
|
||||
"add" => add_parse(&mut flags, &mut m)?,
|
||||
"remove" => remove_parse(&mut flags, &mut m),
|
||||
"bench" => bench_parse(&mut flags, &mut m)?,
|
||||
"bundle" => bundle_parse(&mut flags, &mut m),
|
||||
|
@ -1528,7 +1529,7 @@ pub fn clap_root() -> Command {
|
|||
);
|
||||
|
||||
run_args(Command::new("deno"), true)
|
||||
.args(unstable_args(UnstableArgsConfig::ResolutionAndRuntime))
|
||||
.with_unstable_args(UnstableArgsConfig::ResolutionAndRuntime)
|
||||
.next_line_help(false)
|
||||
.bin_name("deno")
|
||||
.styles(
|
||||
|
@ -1630,7 +1631,7 @@ fn command(
|
|||
) -> Command {
|
||||
Command::new(name)
|
||||
.about(about)
|
||||
.args(unstable_args(unstable_args_config))
|
||||
.with_unstable_args(unstable_args_config)
|
||||
}
|
||||
|
||||
fn help_subcommand(app: &Command) -> Command {
|
||||
|
@ -1658,10 +1659,10 @@ fn add_subcommand() -> Command {
|
|||
"add",
|
||||
cstr!(
|
||||
"Add dependencies to your configuration file.
|
||||
<p(245)>deno add @std/path</>
|
||||
<p(245)>deno add jsr:@std/path</>
|
||||
|
||||
You can add multiple dependencies at once:
|
||||
<p(245)>deno add @std/path @std/assert</>"
|
||||
<p(245)>deno add jsr:@std/path jsr:@std/assert</>"
|
||||
),
|
||||
UnstableArgsConfig::None,
|
||||
)
|
||||
|
@ -1675,6 +1676,7 @@ You can add multiple dependencies at once:
|
|||
.action(ArgAction::Append),
|
||||
)
|
||||
.arg(add_dev_arg())
|
||||
.arg(allow_scripts_arg())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1717,7 +1719,7 @@ If you specify a directory instead of a file, the path is expanded to all contai
|
|||
UnstableArgsConfig::ResolutionAndRuntime,
|
||||
)
|
||||
.defer(|cmd| {
|
||||
runtime_args(cmd, true, false)
|
||||
runtime_args(cmd, true, false, true)
|
||||
.arg(check_arg(true))
|
||||
.arg(
|
||||
Arg::new("json")
|
||||
|
@ -1881,7 +1883,7 @@ On the first invocation with deno will download the proper binary and cache it i
|
|||
UnstableArgsConfig::ResolutionAndRuntime,
|
||||
)
|
||||
.defer(|cmd| {
|
||||
runtime_args(cmd, true, false)
|
||||
runtime_args(cmd, true, false, true)
|
||||
.arg(check_arg(true))
|
||||
.arg(
|
||||
Arg::new("include")
|
||||
|
@ -2202,7 +2204,7 @@ This command has implicit access to all permissions.
|
|||
UnstableArgsConfig::ResolutionAndRuntime,
|
||||
)
|
||||
.defer(|cmd| {
|
||||
runtime_args(cmd, false, true)
|
||||
runtime_args(cmd, false, true, true)
|
||||
.arg(check_arg(false))
|
||||
.arg(executable_ext_arg())
|
||||
.arg(
|
||||
|
@ -2468,7 +2470,7 @@ in the package cache. If no dependency is specified, installs all dependencies l
|
|||
If the <p(245)>--entrypoint</> flag is passed, installs the dependencies of the specified entrypoint(s).
|
||||
|
||||
<p(245)>deno install</>
|
||||
<p(245)>deno install @std/bytes</>
|
||||
<p(245)>deno install jsr:@std/bytes</>
|
||||
<p(245)>deno install npm:chalk</>
|
||||
<p(245)>deno install --entrypoint entry1.ts entry2.ts</>
|
||||
|
||||
|
@ -2501,7 +2503,7 @@ The installation root is determined, in order of precedence:
|
|||
These must be added to the path manually if required."), UnstableArgsConfig::ResolutionAndRuntime)
|
||||
.visible_alias("i")
|
||||
.defer(|cmd| {
|
||||
permission_args(runtime_args(cmd, false, true), Some("global"))
|
||||
permission_args(runtime_args(cmd, false, true, false), Some("global"))
|
||||
.arg(check_arg(true))
|
||||
.arg(allow_scripts_arg())
|
||||
.arg(
|
||||
|
@ -2767,8 +2769,13 @@ It is especially useful for quick prototyping and checking snippets of code.
|
|||
|
||||
TypeScript is supported, however it is not type-checked, only transpiled."
|
||||
), UnstableArgsConfig::ResolutionAndRuntime)
|
||||
.defer(|cmd| runtime_args(cmd, true, true)
|
||||
.arg(check_arg(false))
|
||||
.defer(|cmd| {
|
||||
let cmd = compile_args_without_check_args(cmd);
|
||||
let cmd = inspect_args(cmd);
|
||||
let cmd = permission_args(cmd, None);
|
||||
let cmd = runtime_misc_args(cmd);
|
||||
|
||||
cmd
|
||||
.arg(
|
||||
Arg::new("eval-file")
|
||||
.long("eval-file")
|
||||
|
@ -2787,7 +2794,7 @@ TypeScript is supported, however it is not type-checked, only transpiled."
|
|||
.after_help(cstr!("<y>Environment variables:</>
|
||||
<g>DENO_REPL_HISTORY</> Set REPL history file path. History file is disabled when the value is empty.
|
||||
<p(245)>[default: $DENO_DIR/deno_history.txt]</>"))
|
||||
)
|
||||
})
|
||||
.arg(env_file_arg())
|
||||
.arg(
|
||||
Arg::new("args")
|
||||
|
@ -2799,7 +2806,7 @@ TypeScript is supported, however it is not type-checked, only transpiled."
|
|||
}
|
||||
|
||||
fn run_args(command: Command, top_level: bool) -> Command {
|
||||
runtime_args(command, true, true)
|
||||
runtime_args(command, true, true, true)
|
||||
.arg(check_arg(false))
|
||||
.arg(watch_arg(true))
|
||||
.arg(hmr_arg(true))
|
||||
|
@ -2855,7 +2862,7 @@ Start a server defined in server.ts:
|
|||
Start a server defined in server.ts, watching for changes and running on port 5050:
|
||||
<p(245)>deno serve --watch --port 5050 server.ts</>
|
||||
|
||||
<y>Read more:</> <c>https://docs.deno.com/go/serve</>"), UnstableArgsConfig::ResolutionAndRuntime), true, true)
|
||||
<y>Read more:</> <c>https://docs.deno.com/go/serve</>"), UnstableArgsConfig::ResolutionAndRuntime), true, true, true)
|
||||
.arg(
|
||||
Arg::new("port")
|
||||
.long("port")
|
||||
|
@ -2929,7 +2936,7 @@ or <c>**/__tests__/**</>:
|
|||
UnstableArgsConfig::ResolutionAndRuntime
|
||||
)
|
||||
.defer(|cmd|
|
||||
runtime_args(cmd, true, true)
|
||||
runtime_args(cmd, true, true, true)
|
||||
.arg(check_arg(true))
|
||||
.arg(
|
||||
Arg::new("ignore")
|
||||
|
@ -3642,6 +3649,7 @@ fn runtime_args(
|
|||
app: Command,
|
||||
include_perms: bool,
|
||||
include_inspector: bool,
|
||||
include_allow_scripts: bool,
|
||||
) -> Command {
|
||||
let app = compile_args(app);
|
||||
let app = if include_perms {
|
||||
|
@ -3654,6 +3662,15 @@ fn runtime_args(
|
|||
} else {
|
||||
app
|
||||
};
|
||||
let app = if include_allow_scripts {
|
||||
app.arg(allow_scripts_arg())
|
||||
} else {
|
||||
app
|
||||
};
|
||||
runtime_misc_args(app)
|
||||
}
|
||||
|
||||
fn runtime_misc_args(app: Command) -> Command {
|
||||
app
|
||||
.arg(frozen_lockfile_arg())
|
||||
.arg(cached_only_arg())
|
||||
|
@ -4135,23 +4152,29 @@ enum UnstableArgsConfig {
|
|||
ResolutionAndRuntime,
|
||||
}
|
||||
|
||||
struct UnstableArgsIter {
|
||||
idx: usize,
|
||||
cfg: UnstableArgsConfig,
|
||||
trait CommandExt {
|
||||
fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self;
|
||||
}
|
||||
|
||||
impl Iterator for UnstableArgsIter {
|
||||
type Item = Arg;
|
||||
impl CommandExt for Command {
|
||||
fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self {
|
||||
let mut next_display_order = {
|
||||
let mut value = 1000;
|
||||
move || {
|
||||
value += 1;
|
||||
value
|
||||
}
|
||||
};
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let arg = if self.idx == 0 {
|
||||
let mut cmd = self.arg(
|
||||
Arg::new("unstable")
|
||||
.long("unstable")
|
||||
.help(cstr!("Enable all unstable features and APIs. Instead of using this flag, consider enabling individual unstable features
|
||||
<p(245)>To view the list of individual unstable feature flags, run this command again with --help=unstable</>"))
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(matches!(self.cfg, UnstableArgsConfig::None))
|
||||
} else if self.idx == 1 {
|
||||
.hide(matches!(cfg, UnstableArgsConfig::None))
|
||||
.display_order(next_display_order())
|
||||
).arg(
|
||||
Arg::new("unstable-bare-node-builtins")
|
||||
.long("unstable-bare-node-builtins")
|
||||
.help("Enable unstable bare node builtins feature")
|
||||
|
@ -4159,20 +4182,36 @@ impl Iterator for UnstableArgsIter {
|
|||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.long_help(match self.cfg {
|
||||
.long_help(match cfg {
|
||||
UnstableArgsConfig::None => None,
|
||||
UnstableArgsConfig::ResolutionOnly
|
||||
| UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
|
||||
})
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
} else if self.idx == 2 {
|
||||
.display_order(next_display_order()),
|
||||
).arg(
|
||||
Arg::new("unstable-detect-cjs")
|
||||
.long("unstable-detect-cjs")
|
||||
.help("Reads the package.json type field in a project to treat .js files as .cjs")
|
||||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.long_help(match cfg {
|
||||
UnstableArgsConfig::None => None,
|
||||
UnstableArgsConfig::ResolutionOnly
|
||||
| UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
|
||||
})
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
.display_order(next_display_order())
|
||||
).arg(
|
||||
Arg::new("unstable-byonm")
|
||||
.long("unstable-byonm")
|
||||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
} else if self.idx == 3 {
|
||||
.display_order(next_display_order()),
|
||||
).arg(
|
||||
Arg::new("unstable-sloppy-imports")
|
||||
.long("unstable-sloppy-imports")
|
||||
.help("Enable unstable resolving of specifiers by extension probing, .js to .ts, and directory probing")
|
||||
|
@ -4180,13 +4219,16 @@ impl Iterator for UnstableArgsIter {
|
|||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.long_help(match self.cfg {
|
||||
.long_help(match cfg {
|
||||
UnstableArgsConfig::None => None,
|
||||
UnstableArgsConfig::ResolutionOnly | UnstableArgsConfig::ResolutionAndRuntime => Some("true")
|
||||
})
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
} else if self.idx > 3 {
|
||||
let granular_flag = crate::UNSTABLE_GRANULAR_FLAGS.get(self.idx - 4)?;
|
||||
.display_order(next_display_order())
|
||||
);
|
||||
|
||||
for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() {
|
||||
cmd = cmd.arg(
|
||||
Arg::new(format!("unstable-{}", granular_flag.name))
|
||||
.long(format!("unstable-{}", granular_flag.name))
|
||||
.help(granular_flag.help_text)
|
||||
|
@ -4195,7 +4237,7 @@ impl Iterator for UnstableArgsIter {
|
|||
.help_heading(UNSTABLE_HEADING)
|
||||
// we don't render long help, so using it here as a sort of metadata
|
||||
.long_help(if granular_flag.show_in_help {
|
||||
match self.cfg {
|
||||
match cfg {
|
||||
UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => {
|
||||
None
|
||||
}
|
||||
|
@ -4204,16 +4246,12 @@ impl Iterator for UnstableArgsIter {
|
|||
} else {
|
||||
None
|
||||
})
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
self.idx += 1;
|
||||
Some(arg.display_order(self.idx + 1000))
|
||||
}
|
||||
.display_order(next_display_order()),
|
||||
);
|
||||
}
|
||||
|
||||
fn unstable_args(cfg: UnstableArgsConfig) -> impl IntoIterator<Item = Arg> {
|
||||
UnstableArgsIter { idx: 0, cfg }
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
||||
fn allow_scripts_arg_parse(
|
||||
|
@ -4235,8 +4273,13 @@ fn allow_scripts_arg_parse(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn add_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||
fn add_parse(
|
||||
flags: &mut Flags,
|
||||
matches: &mut ArgMatches,
|
||||
) -> clap::error::Result<()> {
|
||||
allow_scripts_arg_parse(flags, matches)?;
|
||||
flags.subcommand = DenoSubcommand::Add(add_parse_inner(matches, None));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_parse_inner(
|
||||
|
@ -4262,7 +4305,7 @@ fn bench_parse(
|
|||
) -> clap::error::Result<()> {
|
||||
flags.type_check_mode = TypeCheckMode::Local;
|
||||
|
||||
runtime_args_parse(flags, matches, true, false)?;
|
||||
runtime_args_parse(flags, matches, true, false, true)?;
|
||||
ext_arg_parse(flags, matches);
|
||||
|
||||
// NOTE: `deno bench` always uses `--no-prompt`, tests shouldn't ever do
|
||||
|
@ -4352,7 +4395,7 @@ fn compile_parse(
|
|||
matches: &mut ArgMatches,
|
||||
) -> clap::error::Result<()> {
|
||||
flags.type_check_mode = TypeCheckMode::Local;
|
||||
runtime_args_parse(flags, matches, true, false)?;
|
||||
runtime_args_parse(flags, matches, true, false, true)?;
|
||||
|
||||
let mut script = matches.remove_many::<String>("script_arg").unwrap();
|
||||
let source_file = script.next().unwrap();
|
||||
|
@ -4527,7 +4570,7 @@ fn eval_parse(
|
|||
flags: &mut Flags,
|
||||
matches: &mut ArgMatches,
|
||||
) -> clap::error::Result<()> {
|
||||
runtime_args_parse(flags, matches, false, true)?;
|
||||
runtime_args_parse(flags, matches, false, true, false)?;
|
||||
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
|
||||
flags.allow_all();
|
||||
|
||||
|
@ -4620,7 +4663,7 @@ fn install_parse(
|
|||
flags: &mut Flags,
|
||||
matches: &mut ArgMatches,
|
||||
) -> clap::error::Result<()> {
|
||||
runtime_args_parse(flags, matches, true, true)?;
|
||||
runtime_args_parse(flags, matches, true, true, false)?;
|
||||
|
||||
let global = matches.get_flag("global");
|
||||
if global {
|
||||
|
@ -4846,8 +4889,18 @@ fn repl_parse(
|
|||
flags: &mut Flags,
|
||||
matches: &mut ArgMatches,
|
||||
) -> clap::error::Result<()> {
|
||||
runtime_args_parse(flags, matches, true, true)?;
|
||||
unsafely_ignore_certificate_errors_parse(flags, matches);
|
||||
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
|
||||
compile_args_without_check_parse(flags, matches)?;
|
||||
cached_only_arg_parse(flags, matches);
|
||||
frozen_lockfile_arg_parse(flags, matches);
|
||||
permission_args_parse(flags, matches)?;
|
||||
inspect_arg_parse(flags, matches);
|
||||
location_arg_parse(flags, matches);
|
||||
v8_flags_arg_parse(flags, matches);
|
||||
seed_arg_parse(flags, matches);
|
||||
enable_testing_features_arg_parse(flags, matches);
|
||||
env_file_arg_parse(flags, matches);
|
||||
strace_ops_parse(flags, matches);
|
||||
|
||||
let eval_files = matches
|
||||
.remove_many::<String>("eval-file")
|
||||
|
@ -4879,7 +4932,7 @@ fn run_parse(
|
|||
mut app: Command,
|
||||
bare: bool,
|
||||
) -> clap::error::Result<()> {
|
||||
runtime_args_parse(flags, matches, true, true)?;
|
||||
runtime_args_parse(flags, matches, true, true, true)?;
|
||||
ext_arg_parse(flags, matches);
|
||||
|
||||
flags.code_cache_enabled = !matches.get_flag("no-code-cache");
|
||||
|
@ -4920,7 +4973,7 @@ fn serve_parse(
|
|||
|
||||
let worker_count = parallel_arg_parse(matches).map(|v| v.get());
|
||||
|
||||
runtime_args_parse(flags, matches, true, true)?;
|
||||
runtime_args_parse(flags, matches, true, true, true)?;
|
||||
// If the user didn't pass --allow-net, add this port to the network
|
||||
// allowlist. If the host is 0.0.0.0, we add :{port} and allow the same network perms
|
||||
// as if it was passed to --allow-net directly.
|
||||
|
@ -5015,7 +5068,7 @@ fn test_parse(
|
|||
matches: &mut ArgMatches,
|
||||
) -> clap::error::Result<()> {
|
||||
flags.type_check_mode = TypeCheckMode::Local;
|
||||
runtime_args_parse(flags, matches, true, true)?;
|
||||
runtime_args_parse(flags, matches, true, true, true)?;
|
||||
ext_arg_parse(flags, matches);
|
||||
|
||||
// NOTE: `deno test` always uses `--no-prompt`, tests shouldn't ever do
|
||||
|
@ -5380,6 +5433,7 @@ fn runtime_args_parse(
|
|||
matches: &mut ArgMatches,
|
||||
include_perms: bool,
|
||||
include_inspector: bool,
|
||||
include_allow_scripts: bool,
|
||||
) -> clap::error::Result<()> {
|
||||
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
|
||||
compile_args_parse(flags, matches)?;
|
||||
|
@ -5391,6 +5445,9 @@ fn runtime_args_parse(
|
|||
if include_inspector {
|
||||
inspect_arg_parse(flags, matches);
|
||||
}
|
||||
if include_allow_scripts {
|
||||
allow_scripts_arg_parse(flags, matches)?;
|
||||
}
|
||||
location_arg_parse(flags, matches);
|
||||
v8_flags_arg_parse(flags, matches);
|
||||
seed_arg_parse(flags, matches);
|
||||
|
@ -5662,6 +5719,7 @@ fn unstable_args_parse(
|
|||
|
||||
flags.unstable_config.bare_node_builtins =
|
||||
matches.get_flag("unstable-bare-node-builtins");
|
||||
flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs");
|
||||
flags.unstable_config.sloppy_imports =
|
||||
matches.get_flag("unstable-sloppy-imports");
|
||||
|
||||
|
@ -7390,7 +7448,7 @@ mod tests {
|
|||
#[test]
|
||||
fn repl_with_flags() {
|
||||
#[rustfmt::skip]
|
||||
let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]);
|
||||
let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
|
@ -7438,7 +7496,6 @@ mod tests {
|
|||
allow_write: Some(vec![]),
|
||||
..Default::default()
|
||||
},
|
||||
type_check_mode: TypeCheckMode::None,
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
@ -7460,7 +7517,6 @@ mod tests {
|
|||
eval: None,
|
||||
is_default_command: false,
|
||||
}),
|
||||
type_check_mode: TypeCheckMode::None,
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
@ -8862,8 +8918,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_no_colon_in_value_name() {
|
||||
let app =
|
||||
runtime_args(Command::new("test_inspect_completion_value"), true, true);
|
||||
let app = runtime_args(
|
||||
Command::new("test_inspect_completion_value"),
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
let inspect_args = app
|
||||
.get_arguments()
|
||||
.filter(|arg| arg.get_id() == "inspect")
|
||||
|
|
|
@ -1576,6 +1576,11 @@ impl CliOptions {
|
|||
|| self.workspace().has_unstable("bare-node-builtins")
|
||||
}
|
||||
|
||||
pub fn unstable_detect_cjs(&self) -> bool {
|
||||
self.flags.unstable_config.detect_cjs
|
||||
|| self.workspace().has_unstable("detect-cjs")
|
||||
}
|
||||
|
||||
fn byonm_enabled(&self) -> bool {
|
||||
// check if enabled via unstable
|
||||
self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual)
|
||||
|
@ -1620,21 +1625,17 @@ impl CliOptions {
|
|||
});
|
||||
|
||||
if !from_config_file.is_empty() {
|
||||
// collect unstable granular flags
|
||||
let mut all_valid_unstable_flags: Vec<&str> =
|
||||
crate::UNSTABLE_GRANULAR_FLAGS
|
||||
let all_valid_unstable_flags: Vec<&str> = crate::UNSTABLE_GRANULAR_FLAGS
|
||||
.iter()
|
||||
.map(|granular_flag| granular_flag.name)
|
||||
.collect();
|
||||
|
||||
let mut another_unstable_flags = Vec::from([
|
||||
.chain([
|
||||
"sloppy-imports",
|
||||
"byonm",
|
||||
"bare-node-builtins",
|
||||
"fmt-component",
|
||||
]);
|
||||
// add more unstable flags to the same vector holding granular flags
|
||||
all_valid_unstable_flags.append(&mut another_unstable_flags);
|
||||
"detect-cjs",
|
||||
])
|
||||
.collect();
|
||||
|
||||
// check and warn if the unstable flag of config file isn't supported, by
|
||||
// iterating through the vector holding the unstable flags
|
||||
|
|
|
@ -150,7 +150,11 @@ fn bench_big_file_edits(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
@ -206,6 +210,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": {
|
||||
"enable": true,
|
||||
"codeLens": {
|
||||
|
@ -214,6 +220,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
|
|||
"test": true,
|
||||
},
|
||||
} }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
@ -257,7 +265,11 @@ fn bench_find_replace(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
for i in 0..10 {
|
||||
client.write_notification(
|
||||
|
@ -341,7 +353,11 @@ fn bench_startup_shutdown(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
|
|
@ -13,7 +13,11 @@ use test_util::lsp::LspClientBuilder;
|
|||
fn incremental_change_wait(bench: &mut Bencher) {
|
||||
let mut client = LspClientBuilder::new().use_diagnostic_sync(false).build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
|
57
cli/cache/mod.rs
vendored
57
cli/cache/mod.rs
vendored
|
@ -9,10 +9,13 @@ use crate::file_fetcher::FetchPermissionsOptionRef;
|
|||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::FileOrRedirect;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
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 crate::util::path::specifier_has_extension;
|
||||
use crate::util::text_encoding::arc_str_to_bytes;
|
||||
use crate::util::text_encoding::from_utf8_lossy_owned;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::futures;
|
||||
|
@ -57,6 +60,7 @@ pub use fast_check::FastCheckCache;
|
|||
pub use incremental::IncrementalCache;
|
||||
pub use module_info::ModuleInfoCache;
|
||||
pub use node::NodeAnalysisCache;
|
||||
pub use parsed_source::EsmOrCjsChecker;
|
||||
pub use parsed_source::LazyGraphSourceParser;
|
||||
pub use parsed_source::ParsedSourceCache;
|
||||
|
||||
|
@ -177,37 +181,46 @@ pub struct FetchCacherOptions {
|
|||
pub permissions: PermissionsContainer,
|
||||
/// If we're publishing for `deno publish`.
|
||||
pub is_deno_publish: bool,
|
||||
pub unstable_detect_cjs: bool,
|
||||
}
|
||||
|
||||
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
|
||||
/// a concise interface to the DENO_DIR when building module graphs.
|
||||
pub struct FetchCacher {
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
permissions: PermissionsContainer,
|
||||
cache_info_enabled: bool,
|
||||
is_deno_publish: bool,
|
||||
unstable_detect_cjs: bool,
|
||||
cache_info_enabled: bool,
|
||||
}
|
||||
|
||||
impl FetchCacher {
|
||||
pub fn new(
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
options: FetchCacherOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
file_fetcher,
|
||||
esm_or_cjs_checker,
|
||||
global_http_cache,
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
module_info_cache,
|
||||
file_header_overrides: options.file_header_overrides,
|
||||
permissions: options.permissions,
|
||||
is_deno_publish: options.is_deno_publish,
|
||||
unstable_detect_cjs: options.unstable_detect_cjs,
|
||||
cache_info_enabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -282,6 +295,46 @@ impl Loader for FetchCacher {
|
|||
},
|
||||
))));
|
||||
}
|
||||
|
||||
if self.unstable_detect_cjs && specifier_has_extension(specifier, "js") {
|
||||
if let Ok(Some(pkg_json)) =
|
||||
self.node_resolver.get_closest_package_json(specifier)
|
||||
{
|
||||
if pkg_json.typ == "commonjs" {
|
||||
if let Ok(path) = specifier.to_file_path() {
|
||||
if let Ok(bytes) = std::fs::read(&path) {
|
||||
let text: Arc<str> = from_utf8_lossy_owned(bytes).into();
|
||||
let is_es_module = match self.esm_or_cjs_checker.is_esm(
|
||||
specifier,
|
||||
text.clone(),
|
||||
MediaType::JavaScript,
|
||||
) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return Box::pin(futures::future::ready(Err(err.into())));
|
||||
}
|
||||
};
|
||||
if !is_es_module {
|
||||
self.node_resolver.mark_cjs_resolution(specifier.clone());
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::External {
|
||||
specifier: specifier.clone(),
|
||||
},
|
||||
))));
|
||||
} else {
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::Module {
|
||||
specifier: specifier.clone(),
|
||||
content: arc_str_to_bytes(text),
|
||||
maybe_headers: None,
|
||||
},
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.is_deno_publish
|
||||
|
|
40
cli/cache/parsed_source.rs
vendored
40
cli/cache/parsed_source.rs
vendored
|
@ -5,6 +5,7 @@ use std::sync::Arc;
|
|||
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_ast::ParseDiagnostic;
|
||||
use deno_ast::ParsedSource;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_graph::CapturingModuleParser;
|
||||
|
@ -149,3 +150,42 @@ impl deno_graph::ParsedSourceStore for ParsedSourceCache {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EsmOrCjsChecker {
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
}
|
||||
|
||||
impl EsmOrCjsChecker {
|
||||
pub fn new(parsed_source_cache: Arc<ParsedSourceCache>) -> Self {
|
||||
Self {
|
||||
parsed_source_cache,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_esm(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
source: Arc<str>,
|
||||
media_type: MediaType,
|
||||
) -> Result<bool, ParseDiagnostic> {
|
||||
// todo(dsherret): add a file cache here to avoid parsing with swc on each run
|
||||
let source = match self.parsed_source_cache.get_parsed_source(specifier) {
|
||||
Some(source) => source.clone(),
|
||||
None => {
|
||||
let source = deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier: specifier.clone(),
|
||||
text: source,
|
||||
media_type,
|
||||
capture_tokens: true, // capture because it's used for cjs export analysis
|
||||
scope_analysis: false,
|
||||
maybe_syntax: None,
|
||||
})?;
|
||||
self
|
||||
.parsed_source_cache
|
||||
.set_parsed_source(specifier.clone(), source.clone());
|
||||
source
|
||||
}
|
||||
};
|
||||
Ok(source.is_module())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::cache::CodeCache;
|
|||
use crate::cache::DenoDir;
|
||||
use crate::cache::DenoDirProvider;
|
||||
use crate::cache::EmitCache;
|
||||
use crate::cache::EsmOrCjsChecker;
|
||||
use crate::cache::GlobalHttpCache;
|
||||
use crate::cache::HttpCache;
|
||||
use crate::cache::LocalHttpCache;
|
||||
|
@ -171,6 +172,7 @@ struct CliFactoryServices {
|
|||
http_client_provider: Deferred<Arc<HttpClientProvider>>,
|
||||
emit_cache: Deferred<Arc<EmitCache>>,
|
||||
emitter: Deferred<Arc<Emitter>>,
|
||||
esm_or_cjs_checker: Deferred<Arc<EsmOrCjsChecker>>,
|
||||
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
|
||||
main_graph_container: Deferred<Arc<MainModuleGraphContainer>>,
|
||||
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
|
||||
|
@ -298,6 +300,12 @@ impl CliFactory {
|
|||
.get_or_init(|| ProgressBar::new(ProgressBarStyle::TextOnly))
|
||||
}
|
||||
|
||||
pub fn esm_or_cjs_checker(&self) -> &Arc<EsmOrCjsChecker> {
|
||||
self.services.esm_or_cjs_checker.get_or_init(|| {
|
||||
Arc::new(EsmOrCjsChecker::new(self.parsed_source_cache().clone()))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn global_http_cache(&self) -> Result<&Arc<GlobalHttpCache>, AnyError> {
|
||||
self.services.global_http_cache.get_or_try_init(|| {
|
||||
Ok(Arc::new(GlobalHttpCache::new(
|
||||
|
@ -579,6 +587,7 @@ impl CliFactory {
|
|||
node_analysis_cache,
|
||||
self.fs().clone(),
|
||||
node_resolver,
|
||||
Some(self.parsed_source_cache().clone()),
|
||||
);
|
||||
|
||||
Ok(Arc::new(NodeCodeTranslator::new(
|
||||
|
@ -619,8 +628,10 @@ impl CliFactory {
|
|||
Ok(Arc::new(ModuleGraphBuilder::new(
|
||||
cli_options.clone(),
|
||||
self.caches()?.clone(),
|
||||
self.esm_or_cjs_checker().clone(),
|
||||
self.fs().clone(),
|
||||
self.resolver().await?.clone(),
|
||||
self.cli_node_resolver().await?.clone(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
self.module_info_cache()?.clone(),
|
||||
self.parsed_source_cache().clone(),
|
||||
|
@ -792,6 +803,7 @@ impl CliFactory {
|
|||
|
||||
Ok(CliMainWorkerFactory::new(
|
||||
self.blob_store().clone(),
|
||||
self.cjs_resolutions().clone(),
|
||||
if cli_options.code_cache_enabled() {
|
||||
Some(self.code_cache()?.clone())
|
||||
} else {
|
||||
|
@ -896,6 +908,7 @@ impl CliFactory {
|
|||
node_ipc: cli_options.node_ipc_fd(),
|
||||
serve_port: cli_options.serve_port(),
|
||||
serve_host: cli_options.serve_host(),
|
||||
unstable_detect_cjs: cli_options.unstable_detect_cjs(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -333,7 +333,7 @@ impl FileFetcher {
|
|||
)
|
||||
})?;
|
||||
|
||||
let bytes = blob.read_all().await?;
|
||||
let bytes = blob.read_all().await;
|
||||
let headers =
|
||||
HashMap::from([("content-type".to_string(), blob.media_type.clone())]);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::args::CliLockfile;
|
|||
use crate::args::CliOptions;
|
||||
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
||||
use crate::cache;
|
||||
use crate::cache::EsmOrCjsChecker;
|
||||
use crate::cache::GlobalHttpCache;
|
||||
use crate::cache::ModuleInfoCache;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
|
@ -14,6 +15,7 @@ use crate::errors::get_error_class_name;
|
|||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CliGraphResolver;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::SloppyImportsCachedFs;
|
||||
use crate::tools::check;
|
||||
|
@ -379,8 +381,10 @@ pub struct BuildFastCheckGraphOptions<'a> {
|
|||
pub struct ModuleGraphBuilder {
|
||||
options: Arc<CliOptions>,
|
||||
caches: Arc<cache::Caches>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
|
@ -396,8 +400,10 @@ impl ModuleGraphBuilder {
|
|||
pub fn new(
|
||||
options: Arc<CliOptions>,
|
||||
caches: Arc<cache::Caches>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
|
@ -410,8 +416,10 @@ impl ModuleGraphBuilder {
|
|||
Self {
|
||||
options,
|
||||
caches,
|
||||
esm_or_cjs_checker,
|
||||
fs,
|
||||
resolver,
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
module_info_cache,
|
||||
parsed_source_cache,
|
||||
|
@ -691,8 +699,10 @@ impl ModuleGraphBuilder {
|
|||
permissions: PermissionsContainer,
|
||||
) -> cache::FetchCacher {
|
||||
cache::FetchCacher::new(
|
||||
self.esm_or_cjs_checker.clone(),
|
||||
self.file_fetcher.clone(),
|
||||
self.global_http_cache.clone(),
|
||||
self.node_resolver.clone(),
|
||||
self.npm_resolver.clone(),
|
||||
self.module_info_cache.clone(),
|
||||
cache::FetchCacherOptions {
|
||||
|
@ -702,6 +712,7 @@ impl ModuleGraphBuilder {
|
|||
self.options.sub_command(),
|
||||
crate::args::DenoSubcommand::Publish { .. }
|
||||
),
|
||||
unstable_detect_cjs: self.options.unstable_detect_cjs(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -470,14 +470,22 @@ impl HttpClient {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn download_with_progress(
|
||||
pub async fn download_with_progress_and_retries(
|
||||
&self,
|
||||
url: Url,
|
||||
maybe_header: Option<(HeaderName, HeaderValue)>,
|
||||
progress_guard: &UpdateGuard,
|
||||
) -> Result<Option<Vec<u8>>, DownloadError> {
|
||||
self
|
||||
.download_inner(url, maybe_header, Some(progress_guard))
|
||||
crate::util::retry::retry(
|
||||
|| {
|
||||
self.download_inner(
|
||||
url.clone(),
|
||||
maybe_header.clone(),
|
||||
Some(progress_guard),
|
||||
)
|
||||
},
|
||||
|e| matches!(e, DownloadError::BadResponse(_) | DownloadError::Fetch(_)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
|
|
|
@ -148,10 +148,10 @@ pub fn server_capabilities(
|
|||
experimental: Some(json!({
|
||||
"denoConfigTasks": true,
|
||||
"testingApi": true,
|
||||
"didRefreshDenoConfigurationTreeNotifications": true,
|
||||
})),
|
||||
inlay_hint_provider: Some(OneOf::Left(true)),
|
||||
position_encoding: None,
|
||||
// TODO(nayeemrmn): Support pull-based diagnostics.
|
||||
diagnostic_provider: None,
|
||||
inline_value_provider: None,
|
||||
inline_completion_provider: None,
|
||||
|
|
|
@ -92,6 +92,19 @@ impl Client {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
) {
|
||||
// do on a task in case the caller currently is in the lsp lock
|
||||
let client = self.0.clone();
|
||||
spawn(async move {
|
||||
client
|
||||
.send_did_refresh_deno_configuration_tree_notification(params)
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -169,6 +182,10 @@ trait ClientTrait: Send + Sync {
|
|||
params: lsp_custom::DiagnosticBatchNotificationParams,
|
||||
);
|
||||
async fn send_test_notification(&self, params: TestingNotification);
|
||||
async fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
);
|
||||
async fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -249,6 +266,18 @@ impl ClientTrait for TowerClient {
|
|||
}
|
||||
}
|
||||
|
||||
async fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
) {
|
||||
self
|
||||
.0
|
||||
.send_notification::<lsp_custom::DidRefreshDenoConfigurationTreeNotification>(
|
||||
params,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -366,6 +395,12 @@ impl ClientTrait for ReplClient {
|
|||
|
||||
async fn send_test_notification(&self, _params: TestingNotification) {}
|
||||
|
||||
async fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
_params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
) {
|
||||
}
|
||||
|
||||
async fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
_params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
|
|
@ -200,15 +200,11 @@ pub async fn get_import_completions(
|
|||
{
|
||||
// completions for import map specifiers
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
} else if text.starts_with("./")
|
||||
|| text.starts_with("../")
|
||||
|| text.starts_with('/')
|
||||
} else if let Some(completion_list) =
|
||||
get_local_completions(specifier, &text, &range, resolver)
|
||||
{
|
||||
// completions for local relative modules
|
||||
Some(lsp::CompletionResponse::List(CompletionList {
|
||||
is_incomplete: false,
|
||||
items: get_local_completions(specifier, &text, &range, resolver)?,
|
||||
}))
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
} else if !text.is_empty() {
|
||||
// completion of modules from a module registry or cache
|
||||
check_auto_config_registry(
|
||||
|
@ -363,15 +359,15 @@ fn get_local_completions(
|
|||
text: &str,
|
||||
range: &lsp::Range,
|
||||
resolver: &LspResolver,
|
||||
) -> Option<Vec<lsp::CompletionItem>> {
|
||||
) -> Option<CompletionList> {
|
||||
if base.scheme() != "file" {
|
||||
return None;
|
||||
}
|
||||
let parent = base.join(text).ok()?.join(".").ok()?;
|
||||
let parent = &text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
|
||||
let resolved_parent = resolver
|
||||
.as_graph_resolver(Some(base))
|
||||
.resolve(
|
||||
parent.as_str(),
|
||||
parent,
|
||||
&Range {
|
||||
specifier: base.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
|
@ -381,13 +377,10 @@ fn get_local_completions(
|
|||
)
|
||||
.ok()?;
|
||||
let resolved_parent_path = url_to_file_path(&resolved_parent).ok()?;
|
||||
let raw_parent =
|
||||
&text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
|
||||
if resolved_parent_path.is_dir() {
|
||||
let cwd = std::env::current_dir().ok()?;
|
||||
let items = std::fs::read_dir(resolved_parent_path).ok()?;
|
||||
Some(
|
||||
items
|
||||
let entries = std::fs::read_dir(resolved_parent_path).ok()?;
|
||||
let items = entries
|
||||
.filter_map(|de| {
|
||||
let de = de.ok()?;
|
||||
let label = de.path().file_name()?.to_string_lossy().to_string();
|
||||
|
@ -395,7 +388,7 @@ fn get_local_completions(
|
|||
if entry_specifier == *base {
|
||||
return None;
|
||||
}
|
||||
let full_text = format!("{raw_parent}{label}");
|
||||
let full_text = format!("{parent}{label}");
|
||||
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
|
||||
range: *range,
|
||||
new_text: full_text.clone(),
|
||||
|
@ -435,8 +428,11 @@ fn get_local_completions(
|
|||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.collect();
|
||||
Some(CompletionList {
|
||||
is_incomplete: false,
|
||||
items,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -921,11 +917,11 @@ mod tests {
|
|||
},
|
||||
},
|
||||
&Default::default(),
|
||||
);
|
||||
assert!(actual.is_some());
|
||||
let actual = actual.unwrap();
|
||||
assert_eq!(actual.len(), 3);
|
||||
for item in actual {
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!actual.is_incomplete);
|
||||
assert_eq!(actual.items.len(), 3);
|
||||
for item in actual.items {
|
||||
match item.text_edit {
|
||||
Some(lsp::CompletionTextEdit::Edit(text_edit)) => {
|
||||
assert!(["./b", "./f.mjs", "./g.json"]
|
||||
|
|
|
@ -50,6 +50,8 @@ use std::sync::Arc;
|
|||
use tower_lsp::lsp_types as lsp;
|
||||
|
||||
use super::logging::lsp_log;
|
||||
use super::lsp_custom;
|
||||
use super::urls::url_to_uri;
|
||||
use crate::args::discover_npmrc_from_workspace;
|
||||
use crate::args::has_flag_env_var;
|
||||
use crate::args::CliLockfile;
|
||||
|
@ -1716,14 +1718,14 @@ impl ConfigTree {
|
|||
.unwrap_or_else(|| Arc::new(FmtConfig::new_with_base(PathBuf::from("/"))))
|
||||
}
|
||||
|
||||
/// Returns (scope_uri, type).
|
||||
/// Returns (scope_url, type).
|
||||
pub fn watched_file_type(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<(&ModuleSpecifier, ConfigWatchedFileType)> {
|
||||
for (scope_uri, data) in self.scopes.iter() {
|
||||
for (scope_url, data) in self.scopes.iter() {
|
||||
if let Some(typ) = data.watched_files.get(specifier) {
|
||||
return Some((scope_uri, *typ));
|
||||
return Some((scope_url, *typ));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -1747,6 +1749,46 @@ impl ConfigTree {
|
|||
.any(|data| data.watched_files.contains_key(specifier))
|
||||
}
|
||||
|
||||
pub fn to_did_refresh_params(
|
||||
&self,
|
||||
) -> lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams {
|
||||
let data = self
|
||||
.scopes
|
||||
.values()
|
||||
.filter_map(|data| {
|
||||
let workspace_root_scope_uri =
|
||||
Some(data.member_dir.workspace.root_dir())
|
||||
.filter(|s| *s != data.member_dir.dir_url())
|
||||
.and_then(|s| url_to_uri(s).ok());
|
||||
Some(lsp_custom::DenoConfigurationData {
|
||||
scope_uri: url_to_uri(&data.scope).ok()?,
|
||||
deno_json: data.maybe_deno_json().and_then(|c| {
|
||||
if workspace_root_scope_uri.is_some()
|
||||
&& Some(&c.specifier)
|
||||
== data
|
||||
.member_dir
|
||||
.workspace
|
||||
.root_deno_json()
|
||||
.map(|c| &c.specifier)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&c.specifier).ok()?,
|
||||
})
|
||||
}),
|
||||
package_json: data.maybe_pkg_json().and_then(|p| {
|
||||
Some(lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&p.specifier()).ok()?,
|
||||
})
|
||||
}),
|
||||
workspace_root_scope_uri,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams { data }
|
||||
}
|
||||
|
||||
pub async fn refresh(
|
||||
&mut self,
|
||||
settings: &Settings,
|
||||
|
|
|
@ -963,6 +963,11 @@ impl Inner {
|
|||
.tree
|
||||
.refresh(&self.config.settings, &self.workspace_files, &file_fetcher)
|
||||
.await;
|
||||
self
|
||||
.client
|
||||
.send_did_refresh_deno_configuration_tree_notification(
|
||||
self.config.tree.to_did_refresh_params(),
|
||||
);
|
||||
for config_file in self.config.tree.config_files() {
|
||||
(|| {
|
||||
let compiler_options = config_file.to_compiler_options().ok()?.options;
|
||||
|
|
|
@ -46,6 +46,30 @@ pub struct DiagnosticBatchNotificationParams {
|
|||
pub messages_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DenoConfigurationData {
|
||||
pub scope_uri: lsp::Uri,
|
||||
pub workspace_root_scope_uri: Option<lsp::Uri>,
|
||||
pub deno_json: Option<lsp::TextDocumentIdentifier>,
|
||||
pub package_json: Option<lsp::TextDocumentIdentifier>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DidRefreshDenoConfigurationTreeNotificationParams {
|
||||
pub data: Vec<DenoConfigurationData>,
|
||||
}
|
||||
|
||||
pub enum DidRefreshDenoConfigurationTreeNotification {}
|
||||
|
||||
impl lsp::notification::Notification
|
||||
for DidRefreshDenoConfigurationTreeNotification
|
||||
{
|
||||
type Params = DidRefreshDenoConfigurationTreeNotificationParams;
|
||||
const METHOD: &'static str = "deno/didRefreshDenoConfigurationTree";
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum DenoConfigurationChangeType {
|
||||
|
@ -88,13 +112,15 @@ pub struct DidChangeDenoConfigurationNotificationParams {
|
|||
pub changes: Vec<DenoConfigurationChangeEvent>,
|
||||
}
|
||||
|
||||
// TODO(nayeemrmn): This is being replaced by
|
||||
// `DidRefreshDenoConfigurationTreeNotification` for Deno > v2.0.0. Remove it
|
||||
// soon.
|
||||
pub enum DidChangeDenoConfigurationNotification {}
|
||||
|
||||
impl lsp::notification::Notification
|
||||
for DidChangeDenoConfigurationNotification
|
||||
{
|
||||
type Params = DidChangeDenoConfigurationNotificationParams;
|
||||
|
||||
const METHOD: &'static str = "deno/didChangeDenoConfiguration";
|
||||
}
|
||||
|
||||
|
@ -102,7 +128,6 @@ pub enum DidUpgradeCheckNotification {}
|
|||
|
||||
impl lsp::notification::Notification for DidUpgradeCheckNotification {
|
||||
type Params = DidUpgradeCheckNotificationParams;
|
||||
|
||||
const METHOD: &'static str = "deno/didUpgradeCheck";
|
||||
}
|
||||
|
||||
|
@ -125,6 +150,5 @@ pub enum DiagnosticBatchNotification {}
|
|||
|
||||
impl lsp::notification::Notification for DiagnosticBatchNotification {
|
||||
type Params = DiagnosticBatchNotificationParams;
|
||||
|
||||
const METHOD: &'static str = "deno/internalTestDiagnosticBatch";
|
||||
}
|
||||
|
|
|
@ -3939,7 +3939,7 @@ pub struct OutliningSpan {
|
|||
kind: OutliningSpanKind,
|
||||
}
|
||||
|
||||
const FOLD_END_PAIR_CHARACTERS: &[u8] = &[b'}', b']', b')', b'`'];
|
||||
const FOLD_END_PAIR_CHARACTERS: &[u8] = b"}])`";
|
||||
|
||||
impl OutliningSpan {
|
||||
pub fn to_folding_range(
|
||||
|
|
111
cli/main.rs
111
cli/main.rs
|
@ -47,8 +47,7 @@ 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_with_suggestions;
|
||||
use deno_runtime::fmt_errors::FixSuggestion;
|
||||
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;
|
||||
|
@ -62,6 +61,10 @@ use std::ops::Deref;
|
|||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
#[global_allocator]
|
||||
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||
|
||||
/// Ensures that all subcommands return an i32 exit code and an [`AnyError`] error type.
|
||||
trait SubcommandOutput {
|
||||
fn output(self) -> Result<i32, AnyError>;
|
||||
|
@ -362,104 +365,12 @@ fn exit_with_message(message: &str, code: i32) -> ! {
|
|||
std::process::exit(code);
|
||||
}
|
||||
|
||||
fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec<FixSuggestion> {
|
||||
if let Some(msg) = &e.message {
|
||||
if msg.contains("module is not defined")
|
||||
|| msg.contains("exports is not defined")
|
||||
{
|
||||
return vec![
|
||||
FixSuggestion::info(
|
||||
"Deno does not support CommonJS modules without `.cjs` extension.",
|
||||
),
|
||||
FixSuggestion::hint(
|
||||
"Rewrite this module to ESM or change the file extension to `.cjs`.",
|
||||
),
|
||||
];
|
||||
} else if msg.contains("openKv is not a function") {
|
||||
return vec![
|
||||
FixSuggestion::info("Deno.openKv() is an unstable API."),
|
||||
FixSuggestion::hint(
|
||||
"Run again with `--unstable-kv` flag to enable this API.",
|
||||
),
|
||||
];
|
||||
} else if msg.contains("cron is not a function") {
|
||||
return vec![
|
||||
FixSuggestion::info("Deno.cron() is an unstable API."),
|
||||
FixSuggestion::hint(
|
||||
"Run again with `--unstable-cron` flag to enable this API.",
|
||||
),
|
||||
];
|
||||
} else if msg.contains("WebSocketStream is not defined") {
|
||||
return vec![
|
||||
FixSuggestion::info("new WebSocketStream() is an unstable API."),
|
||||
FixSuggestion::hint(
|
||||
"Run again with `--unstable-net` flag to enable this API.",
|
||||
),
|
||||
];
|
||||
} else if msg.contains("Temporal is not defined") {
|
||||
return vec![
|
||||
FixSuggestion::info("Temporal is an unstable API."),
|
||||
FixSuggestion::hint(
|
||||
"Run again with `--unstable-temporal` flag to enable this API.",
|
||||
),
|
||||
];
|
||||
} else if msg.contains("BroadcastChannel is not defined") {
|
||||
return vec![
|
||||
FixSuggestion::info("BroadcastChannel is an unstable API."),
|
||||
FixSuggestion::hint(
|
||||
"Run again with `--unstable-broadcast-channel` flag to enable this API.",
|
||||
),
|
||||
];
|
||||
} else if msg.contains("window is not defined") {
|
||||
return vec![
|
||||
FixSuggestion::info("window global is not available in Deno 2."),
|
||||
FixSuggestion::hint("Replace `window` with `globalThis`."),
|
||||
];
|
||||
} else if msg.contains("UnsafeWindowSurface is not a constructor") {
|
||||
return vec![
|
||||
FixSuggestion::info("Deno.UnsafeWindowSurface is an unstable API."),
|
||||
FixSuggestion::hint(
|
||||
"Run again with `--unstable-webgpu` flag to enable this API.",
|
||||
),
|
||||
];
|
||||
// Try to capture errors like:
|
||||
// ```
|
||||
// Uncaught Error: Cannot find module '../build/Release/canvas.node'
|
||||
// Require stack:
|
||||
// - /.../deno/npm/registry.npmjs.org/canvas/2.11.2/lib/bindings.js
|
||||
// - /.../.cache/deno/npm/registry.npmjs.org/canvas/2.11.2/lib/canvas.js
|
||||
// ```
|
||||
} else if msg.contains("Cannot find module")
|
||||
&& msg.contains("Require stack")
|
||||
&& msg.contains(".node'")
|
||||
{
|
||||
return vec![
|
||||
FixSuggestion::info_multiline(
|
||||
&[
|
||||
"Trying to execute an npm package using Node-API addons,",
|
||||
"these packages require local `node_modules` directory to be present."
|
||||
]
|
||||
),
|
||||
FixSuggestion::hint_multiline(
|
||||
&[
|
||||
"Add `\"nodeModulesDir\": \"auto\" option to `deno.json`, and then run",
|
||||
"`deno install --allow-scripts=npm:<package> --entrypoint <script>` to setup `node_modules` directory."
|
||||
]
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn exit_for_error(error: AnyError) -> ! {
|
||||
let mut error_string = format!("{error:?}");
|
||||
let mut error_code = 1;
|
||||
|
||||
if let Some(e) = error.downcast_ref::<JsError>() {
|
||||
let suggestions = get_suggestions_for_terminal_errors(e);
|
||||
error_string = format_js_error_with_suggestions(e, suggestions);
|
||||
error_string = format_js_error(e);
|
||||
} else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) =
|
||||
error.downcast_ref::<SnapshotFromLockfileError>()
|
||||
{
|
||||
|
@ -480,6 +391,9 @@ pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) {
|
|||
}
|
||||
|
||||
pub fn main() {
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
let profiler = dhat::Profiler::new_heap();
|
||||
|
||||
setup_panic_hook();
|
||||
|
||||
util::unix::raise_fd_limit();
|
||||
|
@ -500,7 +414,12 @@ pub fn main() {
|
|||
run_subcommand(Arc::new(flags)).await
|
||||
};
|
||||
|
||||
match create_and_run_current_thread_with_maybe_metrics(future) {
|
||||
let result = create_and_run_current_thread_with_maybe_metrics(future);
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
drop(profiler);
|
||||
|
||||
match result {
|
||||
Ok(exit_code) => std::process::exit(exit_code),
|
||||
Err(err) => exit_for_error(err),
|
||||
}
|
||||
|
|
|
@ -331,15 +331,23 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
requested_module_type: RequestedModuleType,
|
||||
) -> Result<ModuleSource, AnyError> {
|
||||
let code_source = if let Some(result) = self
|
||||
let code_source = match self.load_prepared_module(specifier).await? {
|
||||
Some(code_source) => code_source,
|
||||
None => {
|
||||
if self.shared.npm_module_loader.if_in_npm_package(specifier) {
|
||||
self
|
||||
.shared
|
||||
.npm_module_loader
|
||||
.load_if_in_npm_package(specifier, maybe_referrer)
|
||||
.await
|
||||
{
|
||||
result?
|
||||
.load(specifier, maybe_referrer)
|
||||
.await?
|
||||
} else {
|
||||
self.load_prepared_module(specifier, maybe_referrer).await?
|
||||
let mut msg = format!("Loading unprepared module: {specifier}");
|
||||
if let Some(referrer) = maybe_referrer {
|
||||
msg = format!("{}, imported from: {}", msg, referrer.as_str());
|
||||
}
|
||||
return Err(anyhow!(msg));
|
||||
}
|
||||
}
|
||||
};
|
||||
let code = if self.shared.is_inspecting {
|
||||
// we need the code with the source map in order for
|
||||
|
@ -514,17 +522,12 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
async fn load_prepared_module(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<ModuleCodeStringSource, AnyError> {
|
||||
) -> Result<Option<ModuleCodeStringSource>, AnyError> {
|
||||
// Note: keep this in sync with the sync version below
|
||||
let graph = self.graph_container.graph();
|
||||
match self.load_prepared_module_or_defer_emit(
|
||||
&graph,
|
||||
specifier,
|
||||
maybe_referrer,
|
||||
) {
|
||||
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
|
||||
Ok(CodeOrDeferredEmit::DeferredEmit {
|
||||
match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
|
||||
Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
|
||||
Some(CodeOrDeferredEmit::DeferredEmit {
|
||||
specifier,
|
||||
media_type,
|
||||
source,
|
||||
|
@ -537,30 +540,25 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
// at this point, we no longer need the parsed source in memory, so free it
|
||||
self.parsed_source_cache.free(specifier);
|
||||
|
||||
Ok(ModuleCodeStringSource {
|
||||
Ok(Some(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::Bytes(transpile_result),
|
||||
found_url: specifier.clone(),
|
||||
media_type,
|
||||
})
|
||||
}))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_prepared_module_sync(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<ModuleCodeStringSource, AnyError> {
|
||||
) -> Result<Option<ModuleCodeStringSource>, AnyError> {
|
||||
// Note: keep this in sync with the async version above
|
||||
let graph = self.graph_container.graph();
|
||||
match self.load_prepared_module_or_defer_emit(
|
||||
&graph,
|
||||
specifier,
|
||||
maybe_referrer,
|
||||
) {
|
||||
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
|
||||
Ok(CodeOrDeferredEmit::DeferredEmit {
|
||||
match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
|
||||
Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
|
||||
Some(CodeOrDeferredEmit::DeferredEmit {
|
||||
specifier,
|
||||
media_type,
|
||||
source,
|
||||
|
@ -572,13 +570,13 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
// at this point, we no longer need the parsed source in memory, so free it
|
||||
self.parsed_source_cache.free(specifier);
|
||||
|
||||
Ok(ModuleCodeStringSource {
|
||||
Ok(Some(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::Bytes(transpile_result),
|
||||
found_url: specifier.clone(),
|
||||
media_type,
|
||||
})
|
||||
}))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,8 +584,7 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
&self,
|
||||
graph: &'graph ModuleGraph,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<CodeOrDeferredEmit<'graph>, AnyError> {
|
||||
) -> Result<Option<CodeOrDeferredEmit<'graph>>, AnyError> {
|
||||
if specifier.scheme() == "node" {
|
||||
// Node built-in modules should be handled internally.
|
||||
unreachable!("Deno bug. {} was misconfigured internally.", specifier);
|
||||
|
@ -599,11 +596,11 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
media_type,
|
||||
specifier,
|
||||
..
|
||||
})) => Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
})) => Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::String(source.clone().into()),
|
||||
found_url: specifier.clone(),
|
||||
media_type: *media_type,
|
||||
})),
|
||||
}))),
|
||||
Some(deno_graph::Module::Js(JsModule {
|
||||
source,
|
||||
media_type,
|
||||
|
@ -624,11 +621,11 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
| MediaType::Cts
|
||||
| MediaType::Jsx
|
||||
| MediaType::Tsx => {
|
||||
return Ok(CodeOrDeferredEmit::DeferredEmit {
|
||||
return Ok(Some(CodeOrDeferredEmit::DeferredEmit {
|
||||
specifier,
|
||||
media_type: *media_type,
|
||||
source,
|
||||
});
|
||||
}));
|
||||
}
|
||||
MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => {
|
||||
panic!("Unexpected media type {media_type} for {specifier}")
|
||||
|
@ -638,24 +635,18 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
// at this point, we no longer need the parsed source in memory, so free it
|
||||
self.parsed_source_cache.free(specifier);
|
||||
|
||||
Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::String(code),
|
||||
found_url: specifier.clone(),
|
||||
media_type: *media_type,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
Some(
|
||||
deno_graph::Module::External(_)
|
||||
| deno_graph::Module::Node(_)
|
||||
| deno_graph::Module::Npm(_),
|
||||
)
|
||||
| None => {
|
||||
let mut msg = format!("Loading unprepared module: {specifier}");
|
||||
if let Some(referrer) = maybe_referrer {
|
||||
msg = format!("{}, imported from: {}", msg, referrer.as_str());
|
||||
}
|
||||
Err(anyhow!(msg))
|
||||
}
|
||||
| None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -828,7 +819,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
"wasm" | "file" | "http" | "https" | "data" | "blob" => (),
|
||||
_ => return None,
|
||||
}
|
||||
let source = self.0.load_prepared_module_sync(&specifier, None).ok()?;
|
||||
let source = self.0.load_prepared_module_sync(&specifier).ok()??;
|
||||
source_map_from_code(source.code.as_bytes())
|
||||
}
|
||||
|
||||
|
|
|
@ -264,6 +264,16 @@ fn napi_define_class<'s>(
|
|||
Err(status) => return status,
|
||||
};
|
||||
|
||||
let mut accessor_property = v8::PropertyAttribute::NONE;
|
||||
|
||||
if p.attributes & napi_enumerable == 0 {
|
||||
accessor_property = accessor_property | v8::PropertyAttribute::DONT_ENUM;
|
||||
}
|
||||
if p.attributes & napi_configurable == 0 {
|
||||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::DONT_DELETE;
|
||||
}
|
||||
|
||||
if p.getter.is_some() || p.setter.is_some() {
|
||||
let getter = p.getter.map(|g| {
|
||||
create_function_template(&mut env.scope(), env_ptr, None, g, p.data)
|
||||
|
@ -271,8 +281,6 @@ fn napi_define_class<'s>(
|
|||
let setter = p.setter.map(|s| {
|
||||
create_function_template(&mut env.scope(), env_ptr, None, s, p.data)
|
||||
});
|
||||
|
||||
let mut accessor_property = v8::PropertyAttribute::NONE;
|
||||
if getter.is_some()
|
||||
&& setter.is_some()
|
||||
&& (p.attributes & napi_writable) == 0
|
||||
|
@ -280,15 +288,6 @@ fn napi_define_class<'s>(
|
|||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::READ_ONLY;
|
||||
}
|
||||
if p.attributes & napi_enumerable == 0 {
|
||||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::DONT_ENUM;
|
||||
}
|
||||
if p.attributes & napi_configurable == 0 {
|
||||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::DONT_DELETE;
|
||||
}
|
||||
|
||||
let proto = tpl.prototype_template(&mut env.scope());
|
||||
proto.set_accessor_property(name, getter, setter, accessor_property);
|
||||
} else if let Some(method) = p.method {
|
||||
|
@ -300,10 +299,14 @@ fn napi_define_class<'s>(
|
|||
p.data,
|
||||
);
|
||||
let proto = tpl.prototype_template(&mut env.scope());
|
||||
proto.set(name, function.into());
|
||||
proto.set_with_attr(name, function.into(), accessor_property);
|
||||
} else {
|
||||
let proto = tpl.prototype_template(&mut env.scope());
|
||||
proto.set(name, p.value.unwrap().into());
|
||||
if (p.attributes & napi_writable) == 0 {
|
||||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::READ_ONLY;
|
||||
}
|
||||
proto.set_with_attr(name, p.value.unwrap().into(), accessor_property);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1694,15 +1697,14 @@ fn napi_get_new_target(
|
|||
}
|
||||
|
||||
#[napi_sym]
|
||||
fn napi_call_function(
|
||||
env_ptr: *mut Env,
|
||||
recv: napi_value,
|
||||
func: napi_value,
|
||||
fn napi_call_function<'s>(
|
||||
env: &'s mut Env,
|
||||
recv: napi_value<'s>,
|
||||
func: napi_value<'s>,
|
||||
argc: usize,
|
||||
argv: *const napi_value,
|
||||
result: *mut napi_value,
|
||||
argv: *const napi_value<'s>,
|
||||
result: *mut napi_value<'s>,
|
||||
) -> napi_status {
|
||||
let env = check_env!(env_ptr);
|
||||
check_arg!(env, recv);
|
||||
let args = if argc > 0 {
|
||||
check_arg!(env, argv);
|
||||
|
@ -1716,11 +1718,11 @@ fn napi_call_function(
|
|||
let Some(func) =
|
||||
func.and_then(|f| v8::Local::<v8::Function>::try_from(f).ok())
|
||||
else {
|
||||
return napi_set_last_error(env, napi_function_expected);
|
||||
return napi_function_expected;
|
||||
};
|
||||
|
||||
let Some(v) = func.call(&mut env.scope(), recv.unwrap(), args) else {
|
||||
return napi_set_last_error(env_ptr, napi_generic_failure);
|
||||
return napi_generic_failure;
|
||||
};
|
||||
|
||||
if !result.is_null() {
|
||||
|
@ -1729,7 +1731,7 @@ fn napi_call_function(
|
|||
}
|
||||
}
|
||||
|
||||
return napi_clear_last_error(env_ptr);
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym]
|
||||
|
|
|
@ -692,7 +692,7 @@ impl Drop for TsFn {
|
|||
|
||||
if let Some(finalizer) = self.thread_finalize_cb {
|
||||
unsafe {
|
||||
(finalizer)(self.env as _, self.thread_finalize_data, ptr::null_mut());
|
||||
(finalizer)(self.env as _, self.thread_finalize_data, self.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "napi_sym"
|
||||
version = "0.101.0"
|
||||
version = "0.103.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
15
cli/node.rs
15
cli/node.rs
|
@ -5,6 +5,7 @@ use std::sync::Arc;
|
|||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_graph::ParsedSourceStore;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
|
||||
|
@ -16,6 +17,7 @@ use serde::Serialize;
|
|||
|
||||
use crate::cache::CacheDBHash;
|
||||
use crate::cache::NodeAnalysisCache;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
|
||||
|
@ -56,6 +58,7 @@ pub struct CliCjsCodeAnalyzer {
|
|||
cache: NodeAnalysisCache,
|
||||
fs: deno_fs::FileSystemRc,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
|
||||
}
|
||||
|
||||
impl CliCjsCodeAnalyzer {
|
||||
|
@ -63,11 +66,13 @@ impl CliCjsCodeAnalyzer {
|
|||
cache: NodeAnalysisCache,
|
||||
fs: deno_fs::FileSystemRc,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cache,
|
||||
fs,
|
||||
node_resolver,
|
||||
parsed_source_cache,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,17 +112,25 @@ impl CliCjsCodeAnalyzer {
|
|||
}
|
||||
}
|
||||
|
||||
let maybe_parsed_source = self
|
||||
.parsed_source_cache
|
||||
.as_ref()
|
||||
.and_then(|c| c.remove_parsed_source(specifier));
|
||||
|
||||
let analysis = deno_core::unsync::spawn_blocking({
|
||||
let specifier = specifier.clone();
|
||||
let source: Arc<str> = source.into();
|
||||
move || -> Result<_, deno_ast::ParseDiagnostic> {
|
||||
let parsed_source = deno_ast::parse_program(deno_ast::ParseParams {
|
||||
let parsed_source =
|
||||
maybe_parsed_source.map(Ok).unwrap_or_else(|| {
|
||||
deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier,
|
||||
text: source,
|
||||
media_type,
|
||||
capture_tokens: true,
|
||||
scope_analysis: false,
|
||||
maybe_syntax: None,
|
||||
})
|
||||
})?;
|
||||
if parsed_source.is_script() {
|
||||
let analysis = parsed_source.analyze_cjs();
|
||||
|
|
|
@ -40,7 +40,7 @@ pub fn maybe_auth_header_for_npm_registry(
|
|||
header::AUTHORIZATION,
|
||||
header::HeaderValue::from_str(&format!(
|
||||
"Basic {}",
|
||||
BASE64_STANDARD.encode(&format!(
|
||||
BASE64_STANDARD.encode(format!(
|
||||
"{}:{}",
|
||||
username.unwrap(),
|
||||
password.unwrap()
|
||||
|
|
26
cli/npm/managed/cache/registry_info.rs
vendored
26
cli/npm/managed/cache/registry_info.rs
vendored
|
@ -202,10 +202,13 @@ impl RegistryInfoDownloader {
|
|||
let guard = self.progress_bar.update(package_url.as_str());
|
||||
let name = name.to_string();
|
||||
async move {
|
||||
let maybe_bytes = downloader
|
||||
.http_client_provider
|
||||
.get_or_create()?
|
||||
.download_with_progress(package_url, maybe_auth_header, &guard)
|
||||
let client = downloader.http_client_provider.get_or_create()?;
|
||||
let maybe_bytes = client
|
||||
.download_with_progress_and_retries(
|
||||
package_url,
|
||||
maybe_auth_header,
|
||||
&guard,
|
||||
)
|
||||
.await?;
|
||||
match maybe_bytes {
|
||||
Some(bytes) => {
|
||||
|
@ -239,6 +242,14 @@ impl RegistryInfoDownloader {
|
|||
|
||||
fn get_package_url(&self, name: &str) -> Url {
|
||||
let registry_url = self.npmrc.get_registry_url(name);
|
||||
// The '/' character in scoped package names "@scope/name" must be
|
||||
// encoded for older third party registries. Newer registries and
|
||||
// npm itself support both ways
|
||||
// - encoded: https://registry.npmjs.org/@rollup%2fplugin-json
|
||||
// - non-ecoded: https://registry.npmjs.org/@rollup/plugin-json
|
||||
// To support as many third party registries as possible we'll
|
||||
// always encode the '/' character.
|
||||
|
||||
// list of all characters used in npm packages:
|
||||
// !, ', (, ), *, -, ., /, [0-9], @, [A-Za-z], _, ~
|
||||
const ASCII_SET: percent_encoding::AsciiSet =
|
||||
|
@ -250,11 +261,14 @@ impl RegistryInfoDownloader {
|
|||
.remove(b'*')
|
||||
.remove(b'-')
|
||||
.remove(b'.')
|
||||
.remove(b'/')
|
||||
.remove(b'@')
|
||||
.remove(b'_')
|
||||
.remove(b'~');
|
||||
let name = percent_encoding::utf8_percent_encode(name, &ASCII_SET);
|
||||
registry_url.join(&name.to_string()).unwrap()
|
||||
registry_url
|
||||
// Ensure that scoped package name percent encoding is lower cased
|
||||
// to match npm.
|
||||
.join(&name.to_string().replace("%2F", "%2f"))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
|
2
cli/npm/managed/cache/tarball.rs
vendored
2
cli/npm/managed/cache/tarball.rs
vendored
|
@ -172,7 +172,7 @@ impl TarballCache {
|
|||
let guard = tarball_cache.progress_bar.update(&dist.tarball);
|
||||
let result = tarball_cache.http_client_provider
|
||||
.get_or_create()?
|
||||
.download_with_progress(tarball_uri, maybe_auth_header, &guard)
|
||||
.download_with_progress_and_retries(tarball_uri, maybe_auth_header, &guard)
|
||||
.await;
|
||||
let maybe_bytes = match result {
|
||||
Ok(maybe_bytes) => maybe_bytes,
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
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;
|
||||
|
@ -148,6 +150,7 @@ impl<'a> LifecycleScripts<'a> {
|
|||
snapshot: &NpmResolutionSnapshot,
|
||||
packages: &[NpmResolutionPackage],
|
||||
root_node_modules_dir_path: Option<&Path>,
|
||||
progress_bar: &ProgressBar,
|
||||
) -> Result<(), AnyError> {
|
||||
self.warn_not_run_scripts()?;
|
||||
let get_package_path =
|
||||
|
@ -201,7 +204,15 @@ impl<'a> LifecycleScripts<'a> {
|
|||
{
|
||||
continue;
|
||||
}
|
||||
let exit_code = crate::task_runner::run_task(
|
||||
let _guard = progress_bar.update_with_prompt(
|
||||
crate::util::progress_bar::ProgressMessagePrompt::Initialize,
|
||||
&format!("{}: running '{script_name}' script", package.id.nv),
|
||||
);
|
||||
let crate::task_runner::TaskResult {
|
||||
exit_code,
|
||||
stderr,
|
||||
stdout,
|
||||
} = crate::task_runner::run_task(
|
||||
crate::task_runner::RunTaskOptions {
|
||||
task_name: script_name,
|
||||
script,
|
||||
|
@ -211,15 +222,37 @@ impl<'a> LifecycleScripts<'a> {
|
|||
init_cwd,
|
||||
argv: &[],
|
||||
root_node_modules_dir: root_node_modules_dir_path,
|
||||
stdio: Some(crate::task_runner::TaskIo {
|
||||
stderr: TaskStdio::piped(),
|
||||
stdout: TaskStdio::piped(),
|
||||
}),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let stdout = stdout.unwrap();
|
||||
let stderr = stderr.unwrap();
|
||||
if exit_code != 0 {
|
||||
log::warn!(
|
||||
"error: script '{}' in '{}' failed with exit code {}",
|
||||
"error: script '{}' in '{}' failed with exit code {}{}{}",
|
||||
script_name,
|
||||
package.id.nv,
|
||||
exit_code,
|
||||
if !stdout.trim_ascii().is_empty() {
|
||||
format!(
|
||||
"\nstdout:\n{}\n",
|
||||
String::from_utf8_lossy(&stdout).trim()
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
if !stderr.trim_ascii().is_empty() {
|
||||
format!(
|
||||
"\nstderr:\n{}\n",
|
||||
String::from_utf8_lossy(&stderr).trim()
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
);
|
||||
failed_packages.push(&package.id.nv);
|
||||
// assume if earlier script fails, later ones will fail too
|
||||
|
|
|
@ -713,6 +713,7 @@ async fn sync_resolution_with_fs(
|
|||
snapshot,
|
||||
&package_partitions.packages,
|
||||
Some(root_node_modules_dir_path),
|
||||
progress_bar,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ use node_resolver::NodeModuleKind;
|
|||
use node_resolver::NodeResolution;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::PackageJson;
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -53,7 +52,9 @@ use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
|||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::InnerCliNpmResolverRef;
|
||||
use crate::util::path::specifier_has_extension;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
use crate::util::text_encoding::from_utf8_lossy_owned;
|
||||
|
||||
pub struct ModuleCodeStringSource {
|
||||
pub code: ModuleSourceCode,
|
||||
|
@ -215,7 +216,7 @@ impl CliNodeResolver {
|
|||
referrer: &ModuleSpecifier,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<NodeResolution, NodeResolveError> {
|
||||
let referrer_kind = if self.cjs_resolutions.contains(referrer) {
|
||||
let referrer_kind = if self.cjs_resolutions.is_known_cjs(referrer) {
|
||||
NodeModuleKind::Cjs
|
||||
} else {
|
||||
NodeModuleKind::Esm
|
||||
|
@ -310,9 +311,7 @@ impl CliNodeResolver {
|
|||
if self.in_npm_package(&specifier) {
|
||||
let resolution =
|
||||
self.node_resolver.url_to_node_resolution(specifier)?;
|
||||
if let NodeResolution::CommonJs(specifier) = &resolution {
|
||||
self.cjs_resolutions.insert(specifier.clone());
|
||||
}
|
||||
let resolution = self.handle_node_resolution(resolution);
|
||||
return Ok(Some(resolution.into_url()));
|
||||
}
|
||||
}
|
||||
|
@ -333,12 +332,17 @@ impl CliNodeResolver {
|
|||
) -> NodeResolution {
|
||||
if let NodeResolution::CommonJs(specifier) = &resolution {
|
||||
// remember that this was a common js resolution
|
||||
self.cjs_resolutions.insert(specifier.clone());
|
||||
self.mark_cjs_resolution(specifier.clone());
|
||||
}
|
||||
resolution
|
||||
}
|
||||
|
||||
pub fn mark_cjs_resolution(&self, specifier: ModuleSpecifier) {
|
||||
self.cjs_resolutions.insert(specifier);
|
||||
}
|
||||
}
|
||||
|
||||
// todo(dsherret): move to module_loader.rs
|
||||
#[derive(Clone)]
|
||||
pub struct NpmModuleLoader {
|
||||
cjs_resolutions: Arc<CjsResolutionStore>,
|
||||
|
@ -362,18 +366,9 @@ impl NpmModuleLoader {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn load_if_in_npm_package(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<Result<ModuleCodeStringSource, AnyError>> {
|
||||
if self.node_resolver.in_npm_package(specifier)
|
||||
|| (specifier.scheme() == "file" && specifier.path().ends_with(".cjs"))
|
||||
{
|
||||
Some(self.load(specifier, maybe_referrer).await)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn if_in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
self.node_resolver.in_npm_package(specifier)
|
||||
|| self.cjs_resolutions.is_known_cjs(specifier)
|
||||
}
|
||||
|
||||
pub async fn load(
|
||||
|
@ -418,16 +413,9 @@ impl NpmModuleLoader {
|
|||
}
|
||||
})?;
|
||||
|
||||
let code = if self.cjs_resolutions.contains(specifier)
|
||||
|| (specifier.scheme() == "file" && specifier.path().ends_with(".cjs"))
|
||||
{
|
||||
let code = if self.cjs_resolutions.is_known_cjs(specifier) {
|
||||
// translate cjs to esm if it's cjs and inject node globals
|
||||
let code = match String::from_utf8_lossy(&code) {
|
||||
Cow::Owned(code) => code,
|
||||
// SAFETY: `String::from_utf8_lossy` guarantees that the result is valid
|
||||
// UTF-8 if `Cow::Borrowed` is returned.
|
||||
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(code) },
|
||||
};
|
||||
let code = from_utf8_lossy_owned(code);
|
||||
ModuleSourceCode::String(
|
||||
self
|
||||
.node_code_translator
|
||||
|
@ -452,8 +440,12 @@ impl NpmModuleLoader {
|
|||
pub struct CjsResolutionStore(DashSet<ModuleSpecifier>);
|
||||
|
||||
impl CjsResolutionStore {
|
||||
pub fn contains(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
self.0.contains(specifier)
|
||||
pub fn is_known_cjs(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
if specifier.scheme() != "file" {
|
||||
return false;
|
||||
}
|
||||
|
||||
specifier_has_extension(specifier, "cjs") || self.0.contains(specifier)
|
||||
}
|
||||
|
||||
pub fn insert(&self, specifier: ModuleSpecifier) {
|
||||
|
|
|
@ -528,6 +528,7 @@
|
|||
"bare-node-builtins",
|
||||
"byonm",
|
||||
"cron",
|
||||
"detect-cjs",
|
||||
"ffi",
|
||||
"fs",
|
||||
"http",
|
||||
|
|
|
@ -468,7 +468,11 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
self
|
||||
.http_client_provider
|
||||
.get_or_create()?
|
||||
.download_with_progress(download_url.parse()?, None, &progress)
|
||||
.download_with_progress_and_retries(
|
||||
download_url.parse()?,
|
||||
None,
|
||||
&progress,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
let bytes = match maybe_bytes {
|
||||
|
@ -622,6 +626,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
unstable_config: UnstableConfig {
|
||||
legacy_flag_enabled: false,
|
||||
bare_node_builtins: cli_options.unstable_bare_node_builtins(),
|
||||
detect_cjs: cli_options.unstable_detect_cjs(),
|
||||
sloppy_imports: cli_options.unstable_sloppy_imports(),
|
||||
features: cli_options.unstable_features(),
|
||||
},
|
||||
|
|
|
@ -586,6 +586,7 @@ pub async fn run(
|
|||
node_analysis_cache,
|
||||
fs.clone(),
|
||||
cli_node_resolver.clone(),
|
||||
None,
|
||||
);
|
||||
let node_code_translator = Arc::new(NodeCodeTranslator::new(
|
||||
cjs_esm_code_analyzer,
|
||||
|
@ -651,7 +652,7 @@ pub async fn run(
|
|||
workspace_resolver,
|
||||
node_resolver: cli_node_resolver.clone(),
|
||||
npm_module_loader: Arc::new(NpmModuleLoader::new(
|
||||
cjs_resolutions,
|
||||
cjs_resolutions.clone(),
|
||||
node_code_translator,
|
||||
fs.clone(),
|
||||
cli_node_resolver,
|
||||
|
@ -696,6 +697,7 @@ pub async fn run(
|
|||
});
|
||||
let worker_factory = CliMainWorkerFactory::new(
|
||||
Arc::new(BlobStore::default()),
|
||||
cjs_resolutions,
|
||||
// Code cache is not supported for standalone binary yet.
|
||||
None,
|
||||
feature_checker,
|
||||
|
@ -738,6 +740,7 @@ pub async fn run(
|
|||
node_ipc: None,
|
||||
serve_port: None,
|
||||
serve_host: None,
|
||||
unstable_detect_cjs: metadata.unstable_config.detect_cjs,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -16,8 +16,11 @@ use deno_task_shell::ExecutableCommand;
|
|||
use deno_task_shell::ExecuteResult;
|
||||
use deno_task_shell::ShellCommand;
|
||||
use deno_task_shell::ShellCommandContext;
|
||||
use deno_task_shell::ShellPipeReader;
|
||||
use deno_task_shell::ShellPipeWriter;
|
||||
use lazy_regex::Lazy;
|
||||
use regex::Regex;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use crate::npm::CliNpmResolver;
|
||||
|
@ -36,6 +39,35 @@ pub fn get_script_with_args(script: &str, argv: &[String]) -> String {
|
|||
script.trim().to_owned()
|
||||
}
|
||||
|
||||
pub struct TaskStdio(Option<ShellPipeReader>, ShellPipeWriter);
|
||||
|
||||
impl TaskStdio {
|
||||
pub fn stdout() -> Self {
|
||||
Self(None, ShellPipeWriter::stdout())
|
||||
}
|
||||
pub fn stderr() -> Self {
|
||||
Self(None, ShellPipeWriter::stderr())
|
||||
}
|
||||
pub fn piped() -> Self {
|
||||
let (r, w) = deno_task_shell::pipe();
|
||||
Self(Some(r), w)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TaskIo {
|
||||
pub stdout: TaskStdio,
|
||||
pub stderr: TaskStdio,
|
||||
}
|
||||
|
||||
impl Default for TaskIo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
stderr: TaskStdio::stderr(),
|
||||
stdout: TaskStdio::stdout(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunTaskOptions<'a> {
|
||||
pub task_name: &'a str,
|
||||
pub script: &'a str,
|
||||
|
@ -45,24 +77,69 @@ pub struct RunTaskOptions<'a> {
|
|||
pub argv: &'a [String],
|
||||
pub custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
|
||||
pub root_node_modules_dir: Option<&'a Path>,
|
||||
pub stdio: Option<TaskIo>,
|
||||
}
|
||||
|
||||
pub type TaskCustomCommands = HashMap<String, Rc<dyn ShellCommand>>;
|
||||
|
||||
pub async fn run_task(opts: RunTaskOptions<'_>) -> Result<i32, AnyError> {
|
||||
pub struct TaskResult {
|
||||
pub exit_code: i32,
|
||||
pub stdout: Option<Vec<u8>>,
|
||||
pub stderr: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
pub async fn run_task(
|
||||
opts: RunTaskOptions<'_>,
|
||||
) -> Result<TaskResult, AnyError> {
|
||||
let script = get_script_with_args(opts.script, opts.argv);
|
||||
let seq_list = deno_task_shell::parser::parse(&script)
|
||||
.with_context(|| format!("Error parsing script '{}'.", opts.task_name))?;
|
||||
let env_vars =
|
||||
prepare_env_vars(opts.env_vars, opts.init_cwd, opts.root_node_modules_dir);
|
||||
let state =
|
||||
deno_task_shell::ShellState::new(env_vars, opts.cwd, opts.custom_commands);
|
||||
let stdio = opts.stdio.unwrap_or_default();
|
||||
let (
|
||||
TaskStdio(stdout_read, stdout_write),
|
||||
TaskStdio(stderr_read, stderr_write),
|
||||
) = (stdio.stdout, stdio.stderr);
|
||||
|
||||
fn read(reader: ShellPipeReader) -> JoinHandle<Result<Vec<u8>, AnyError>> {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut buf = Vec::new();
|
||||
reader.pipe_to(&mut buf)?;
|
||||
Ok(buf)
|
||||
})
|
||||
}
|
||||
|
||||
let stdout = stdout_read.map(read);
|
||||
let stderr = stderr_read.map(read);
|
||||
|
||||
let local = LocalSet::new();
|
||||
let future = deno_task_shell::execute(
|
||||
let future = async move {
|
||||
let exit_code = deno_task_shell::execute_with_pipes(
|
||||
seq_list,
|
||||
env_vars,
|
||||
opts.cwd,
|
||||
opts.custom_commands,
|
||||
);
|
||||
Ok(local.run_until(future).await)
|
||||
state,
|
||||
ShellPipeReader::stdin(),
|
||||
stdout_write,
|
||||
stderr_write,
|
||||
)
|
||||
.await;
|
||||
Ok::<_, AnyError>(TaskResult {
|
||||
exit_code,
|
||||
stdout: if let Some(stdout) = stdout {
|
||||
Some(stdout.await??)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
stderr: if let Some(stderr) = stderr {
|
||||
Some(stderr.await??)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
};
|
||||
local.run_until(future).await
|
||||
}
|
||||
|
||||
fn prepare_env_vars(
|
||||
|
|
|
@ -54,6 +54,16 @@ pub async fn compile(
|
|||
);
|
||||
}
|
||||
|
||||
if cli_options.unstable_detect_cjs() {
|
||||
log::warn!(
|
||||
concat!(
|
||||
"{} --unstable-detect-cjs is not properly supported in deno compile. ",
|
||||
"The compiled executable may encounter runtime errors.",
|
||||
),
|
||||
crate::colors::yellow("Warning"),
|
||||
);
|
||||
}
|
||||
|
||||
let output_path = resolve_compile_executable_output_path(
|
||||
http_client,
|
||||
&compile_flags,
|
||||
|
|
|
@ -11,12 +11,14 @@ use deno_core::anyhow::bail;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url;
|
||||
use deno_graph::Dependency;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_graph::Module;
|
||||
use deno_graph::ModuleError;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::Resolution;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
|
@ -47,13 +49,16 @@ pub async fn info(
|
|||
let module_graph_creator = factory.module_graph_creator().await?;
|
||||
let npm_resolver = factory.npm_resolver().await?;
|
||||
let maybe_lockfile = cli_options.maybe_lockfile();
|
||||
let npmrc = cli_options.npmrc();
|
||||
let resolver = factory.workspace_resolver().await?;
|
||||
|
||||
let maybe_import_specifier =
|
||||
if let Some(import_map) = resolver.maybe_import_map() {
|
||||
if let Ok(imports_specifier) =
|
||||
import_map.resolve(&specifier, import_map.base_url())
|
||||
let cwd_url =
|
||||
url::Url::from_directory_path(cli_options.initial_cwd()).unwrap();
|
||||
|
||||
let maybe_import_specifier = if let Some(import_map) =
|
||||
resolver.maybe_import_map()
|
||||
{
|
||||
if let Ok(imports_specifier) = import_map.resolve(&specifier, &cwd_url) {
|
||||
Some(imports_specifier)
|
||||
} else {
|
||||
None
|
||||
|
@ -88,7 +93,8 @@ pub async fn info(
|
|||
JSON_SCHEMA_VERSION.into(),
|
||||
);
|
||||
}
|
||||
add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref());
|
||||
|
||||
add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref(), npmrc);
|
||||
display::write_json_to_stdout(&json_graph)?;
|
||||
} else {
|
||||
let mut output = String::new();
|
||||
|
@ -185,6 +191,7 @@ fn print_cache_info(
|
|||
fn add_npm_packages_to_json(
|
||||
json: &mut serde_json::Value,
|
||||
npm_resolver: &dyn CliNpmResolver,
|
||||
npmrc: &ResolvedNpmRc,
|
||||
) {
|
||||
let Some(npm_resolver) = npm_resolver.as_managed() else {
|
||||
return; // does not include byonm to deno info's output
|
||||
|
@ -195,13 +202,11 @@ fn add_npm_packages_to_json(
|
|||
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 {
|
||||
if modules.len() == 1
|
||||
&& modules[0].get("kind").and_then(|k| k.as_str()) == Some("npm")
|
||||
{
|
||||
for module in modules.iter_mut() {
|
||||
if matches!(module.get("kind").and_then(|k| k.as_str()), Some("npm")) {
|
||||
// If there is only one module and it's "external", then that means
|
||||
// someone provided an npm specifier as a cli argument. In this case,
|
||||
// we want to show which npm package the cli argument resolved to.
|
||||
let module = &mut modules[0];
|
||||
let maybe_package = module
|
||||
.get("specifier")
|
||||
.and_then(|k| k.as_str())
|
||||
|
@ -217,23 +222,8 @@ fn add_npm_packages_to_json(
|
|||
.insert("npmPackage".to_string(), pkg.id.as_serialized().into());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Filter out npm package references from the modules and instead
|
||||
// have them only listed as dependencies. This is done because various
|
||||
// npm specifiers modules in the graph are really just unresolved
|
||||
// references. So there could be listed multiple npm specifiers
|
||||
// that would resolve to a single npm package.
|
||||
for i in (0..modules.len()).rev() {
|
||||
if matches!(
|
||||
modules[i].get("kind").and_then(|k| k.as_str()),
|
||||
Some("npm") | Some("external")
|
||||
) {
|
||||
modules.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for module in modules.iter_mut() {
|
||||
let dependencies = module
|
||||
.get_mut("dependencies")
|
||||
.and_then(|d| d.as_array_mut());
|
||||
|
@ -265,7 +255,7 @@ fn add_npm_packages_to_json(
|
|||
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.to_string().into());
|
||||
kv.insert("name".to_string(), pkg.id.nv.name.clone().into());
|
||||
kv.insert("version".to_string(), pkg.id.nv.version.to_string().into());
|
||||
let mut deps = pkg.dependencies.values().collect::<Vec<_>>();
|
||||
deps.sort();
|
||||
|
@ -274,6 +264,8 @@ fn add_npm_packages_to_json(
|
|||
.map(|id| serde_json::Value::String(id.as_serialized()))
|
||||
.collect::<Vec<_>>();
|
||||
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());
|
||||
}
|
||||
|
|
|
@ -58,9 +58,9 @@ pub fn install() -> Result<(), AnyError> {
|
|||
|
||||
let f = std::fs::File::create(kernel_json_path)?;
|
||||
serde_json::to_writer_pretty(f, &json_data)?;
|
||||
install_icon(&user_data_dir, "logo-32x32.png", DENO_ICON_32)?;
|
||||
install_icon(&user_data_dir, "logo-64x64.png", DENO_ICON_64)?;
|
||||
install_icon(&user_data_dir, "logo-svg.svg", DENO_ICON_SVG)?;
|
||||
install_icon(&kernel_dir, "logo-32x32.png", DENO_ICON_32)?;
|
||||
install_icon(&kernel_dir, "logo-64x64.png", DENO_ICON_64)?;
|
||||
install_icon(&kernel_dir, "logo-svg.svg", DENO_ICON_SVG)?;
|
||||
|
||||
log::info!("✅ Deno kernelspec installed successfully.");
|
||||
Ok(())
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.8 KiB |
|
@ -1 +1,17 @@
|
|||
<svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_29_599)"><path d="M15 0C23.2843 0 30 6.71572 30 15C30 23.2843 23.2843 30 15 30C6.71572 30 0 23.2843 0 15C0 6.71572 6.71572 0 15 0Z" fill="currentColor"></path><path d="M14.6635 22.3394C14.2788 22.2357 13.8831 22.4584 13.7705 22.8381L13.7655 22.8558L12.7694 26.5472L12.7649 26.565C12.6711 26.9498 12.9011 27.3414 13.2858 27.4451C13.6704 27.549 14.0661 27.3263 14.1787 26.9465L14.1837 26.9289L15.1797 23.2375L15.1843 23.2196C15.1911 23.1919 15.1962 23.164 15.1997 23.1362L15.2026 23.1084L15.179 22.9888L15.1445 22.8166L15.1227 22.7091C15.076 22.619 15.0111 22.5396 14.932 22.4759C14.853 22.4123 14.7615 22.3658 14.6635 22.3394ZM7.7224 18.5379C7.70424 18.5741 7.68883 18.6123 7.67658 18.6522L7.66967 18.6763L6.67358 22.3677L6.669 22.3856C6.57525 22.7704 6.80524 23.1619 7.1899 23.2657C7.57451 23.3695 7.97026 23.1469 8.08287 22.7671L8.08779 22.7494L8.99096 19.4023C8.51793 19.1518 8.09336 18.8628 7.7224 18.5379ZM5.34707 14.2929C4.9624 14.1891 4.56666 14.4117 4.4541 14.7915L4.44912 14.8092L3.45303 18.5006L3.44846 18.5184C3.35471 18.9032 3.58469 19.2947 3.96936 19.3985C4.35397 19.5023 4.74971 19.2797 4.86232 18.8999L4.86725 18.8822L5.86334 15.1908L5.86791 15.173C5.96166 14.7882 5.73174 14.3967 5.34707 14.2929ZM27.682 13.4546C27.2973 13.3508 26.9015 13.5734 26.789 13.9532L26.784 13.9709L25.7879 17.6623L25.7833 17.6801C25.6896 18.0649 25.9196 18.4564 26.3042 18.5602C26.6889 18.664 27.0846 18.4414 27.1972 18.0616L27.2021 18.0439L28.1982 14.3525L28.2028 14.3347C28.2965 13.9499 28.0666 13.5584 27.682 13.4546ZM3.17781 8.52527C2.34361 10.0444 1.81243 11.7112 1.61377 13.4329C1.7088 13.5412 1.83381 13.619 1.97301 13.6563C2.35768 13.7602 2.75342 13.5375 2.86598 13.1577L2.87096 13.1401L3.86705 9.44865L3.87162 9.43084C3.96537 9.04599 3.73539 8.65447 3.35072 8.5507C3.2943 8.53547 3.23623 8.52694 3.17781 8.52527ZM25.159 8.5507C24.7744 8.44687 24.3786 8.66953 24.266 9.04933L24.2611 9.06697L23.265 12.7584L23.2604 12.7762C23.1667 13.161 23.3966 13.5526 23.7813 13.6563C24.1659 13.7602 24.5617 13.5375 24.6743 13.1577L24.6792 13.1401L25.6753 9.44865L25.6799 9.43084C25.7736 9.04599 25.5436 8.65447 25.159 8.5507Z" fill="white"></path><path d="M7.51285 5.04065C7.12824 4.93682 6.73249 5.15948 6.61988 5.53929L6.61495 5.55692L5.61886 9.24833L5.61429 9.26614C5.52054 9.65098 5.75052 10.0425 6.13519 10.1463C6.5198 10.2501 6.91554 10.0274 7.02816 9.64764L7.03308 9.63001L8.02917 5.9386L8.03374 5.92079C8.12749 5.53595 7.89751 5.14442 7.51285 5.04065ZM20.3116 5.73845C19.9269 5.63462 19.5312 5.85727 19.4186 6.23708L19.4136 6.25471L18.7443 8.73499C19.1779 8.94915 19.5917 9.20126 19.9809 9.48839L20.0453 9.53643L20.8279 6.63639L20.8324 6.61858C20.9262 6.23374 20.6963 5.84221 20.3116 5.73845ZM13.7968 1.57642C13.3296 1.61771 12.8647 1.68338 12.4043 1.77317L12.3066 1.79263L11.3782 5.23419L11.3736 5.252C11.2799 5.63684 11.5099 6.02837 11.8945 6.13214C12.2792 6.23596 12.6749 6.01331 12.7875 5.6335L12.7924 5.61587L13.7885 1.92446L13.7931 1.90665C13.8196 1.79831 13.8209 1.68533 13.7968 1.57642ZM22.9626 4.1263L22.7669 4.85169L22.7623 4.86944C22.6686 5.25429 22.8986 5.64581 23.2832 5.74958C23.6678 5.85341 24.0636 5.63075 24.1762 5.25095L24.1811 5.23331L24.2025 5.15462C23.8362 4.81205 23.4511 4.49009 23.0491 4.19022L22.9626 4.1263ZM17.1672 1.69677L16.8139 3.00593L16.8094 3.02374C16.7156 3.40858 16.9456 3.80011 17.3303 3.90388C17.7149 4.0077 18.1106 3.78505 18.2233 3.40524L18.2282 3.38761L18.6 2.00966C18.1624 1.88867 17.719 1.79001 17.2714 1.71405L17.1672 1.69677Z" fill="white"></path><path d="M9.69085 24.6253C9.80341 24.2455 10.1992 24.0229 10.5838 24.1266C10.9685 24.2303 11.1984 24.6219 11.1047 25.0068L11.1001 25.0246L10.3872 27.6664L10.2876 27.6297C9.85836 27.4694 9.43765 27.2873 9.0271 27.0839L9.68587 24.6429L9.69085 24.6253Z" fill="white"></path><path d="M14.4141 8.49082C10.0522 8.49082 6.65918 11.2368 6.65918 14.6517C6.65918 17.8769 9.78123 19.9362 14.6211 19.8331C15.0327 19.8243 15.1517 20.1008 15.2856 20.4734C15.4196 20.846 15.7796 22.8097 16.0665 24.3117C16.3233 25.656 16.5842 27.0052 16.7834 28.3596C19.9439 27.9418 22.8663 26.3807 25.0076 24.0261L22.7237 15.5088C22.1544 13.4518 21.489 11.5564 19.7283 10.1794C18.3118 9.07166 16.5122 8.49082 14.4141 8.49082Z" fill="white"></path><path d="M15.3516 10.957C15.8694 10.957 16.2891 11.3767 16.2891 11.8945C16.2891 12.4123 15.8694 12.832 15.3516 12.832C14.8338 12.832 14.4141 12.4123 14.4141 11.8945C14.4141 11.3767 14.8338 10.957 15.3516 10.957Z" fill="currentColor"></path></g><defs><clipPath id="clip0_29_599"><rect width="30" height="30" fill="white"></rect></clipPath></defs></svg>
|
||||
<svg width="100%" height="100%" viewBox="0 0 441 441" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1.02631,-2.08167e-17,2.08167e-17,1.02631,-0.525826,-0.525138)">
|
||||
<path
|
||||
d="M37.965,296.635C26.441,271.766 20.009,244.065 20.009,214.873C20.009,207.318 20.439,199.863 21.278,192.531C22.129,185.123 23.39,177.852 25.036,170.742C34.286,130.852 55.801,95.64 85.384,69.301C110.233,47.207 140.674,31.444 174.043,24.299C187.212,21.486 200.872,20.006 214.875,20.006C219.783,20.011 224.727,20.2 229.701,20.579C253.285,22.38 275.571,28.317 295.904,37.625C312.305,45.143 327.486,54.87 341.064,66.426C375.17,95.48 398.957,135.953 406.867,181.369C408.757,192.255 409.742,203.45 409.742,214.873C409.738,219.789 409.548,224.74 409.168,229.721C407.731,248.545 403.659,266.542 397.34,283.379C388.521,306.83 375.308,328.136 358.706,346.294C337.113,368.342 309.673,378.152 286.755,377.744C270.09,377.447 253.784,370.816 242.516,361.114C226.42,347.253 219.918,331.409 217.69,313.729C217.136,309.334 217.461,297.358 219.748,289.066C221.453,282.885 225.777,270.948 232.1,265.727C224.703,262.541 215.183,255.604 212.182,252.274C211.445,251.455 211.54,250.174 212.2,249.292C212.861,248.41 214.02,248.062 215.057,248.435C221.416,250.618 229.161,252.771 237.327,254.137C248.067,255.932 261.424,258.194 274.955,258.859C307.946,260.479 342.407,245.67 353.103,216.207C363.798,186.744 359.649,157.602 321.279,140.121C282.909,122.64 265.185,101.856 234.183,89.32C213.934,81.131 191.396,85.992 168.257,98.78C105.931,133.223 50.092,242.048 75.833,342.873C76.201,344.252 75.58,345.705 74.328,346.392C73.156,347.036 71.713,346.852 70.741,345.962C63.25,337.731 56.454,328.857 50.445,319.433C45.796,312.139 41.623,304.524 37.965,296.635Z" />
|
||||
</g>
|
||||
<g transform="matrix(0.0920293,0.00428099,-0.00428099,0.0920293,-28.1272,-500.301)">
|
||||
<path
|
||||
d="M3053.7,5296.9C4371.65,5296.9 5441.66,6366.91 5441.66,7684.86C5441.66,9002.81 4371.65,10072.8 3053.7,10072.8C1735.75,10072.8 665.74,9002.81 665.74,7684.86C665.74,6366.91 1735.75,5296.9 3053.7,5296.9ZM3745.03,8143.22C3594.12,8142.82 3444.31,8124.57 3323.87,8110.15C3232.29,8099.18 3144.99,8079.23 3073.1,8058.23C3061.36,8054.62 3048.65,8059.09 3041.75,8069.24C3034.86,8079.4 3034.46,8093.71 3043.09,8102.44C3078.21,8137.94 3187.74,8210.21 3271.7,8241.83C3204.04,8303.2 3162.1,8438.28 3146.33,8507.94C3125.17,8601.4 3127.75,8734.83 3136.19,8783.45C3170.14,8979.04 3250.69,9151.99 3436.99,9297.9C3567.4,9400.03 3752.28,9465.38 3937.88,9460.06C4194.01,9452.71 4495.48,9328.51 4724.65,9070.17C5023.25,8710.58 5208.52,8252.45 5223.47,7749.5C5259.08,6551.9 4315.7,5550.69 3118.1,5515.08C1920.51,5479.47 919.301,6422.86 883.689,7620.45C865.246,8240.66 1109.37,8808.21 1515.43,9216.2C1526.73,9227.39 1544.21,9229.43 1557.78,9221.14C1571.35,9212.85 1577.51,9196.36 1572.7,9181.2C1234.07,8072.55 1799.11,6832.64 2474.84,6417.1C2725.71,6262.82 2973.99,6197.06 3203.56,6277.7C3555.04,6401.15 3763.03,6623.26 4199.06,6797.93C4635.09,6972.59 4696.35,7294.74 4592.58,7628.14C4488.81,7961.54 4113,8144.17 3745.03,8143.22ZM2917.17,6442.51C2777.75,6459.97 2693.93,6637.44 2687.08,6749.42C2680.18,6861.39 2744.03,7042.7 2926.19,7030.63C3139.52,7016.49 3195.89,6830.7 3164.24,6654.94C3140.48,6522.94 3033.73,6427.9 2917.17,6442.51Z"
|
||||
style="fill:white;" />
|
||||
</g>
|
||||
<g transform="matrix(7.12289,0.543899,-0.543899,7.12289,-4867.49,-1040.55)">
|
||||
<path
|
||||
d="M721.316,105.751C722.813,105.518 724.225,106.703 724.582,108.395C725.058,110.649 724.402,113.065 721.658,113.329C719.314,113.555 718.422,111.242 718.468,109.796C718.513,108.35 719.525,106.03 721.316,105.751Z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 3.7 KiB |
|
@ -329,7 +329,12 @@ impl JupyterServer {
|
|||
})
|
||||
.collect();
|
||||
|
||||
if prop_name.len() > cursor_pos {
|
||||
// TODO(bartlomieju): most likely not correct, but better than panicking because of sub with overflow
|
||||
(candidates, cursor_pos)
|
||||
} else {
|
||||
(candidates, cursor_pos - prop_name.len())
|
||||
}
|
||||
} else {
|
||||
// combine results of declarations and globalThis properties
|
||||
let mut candidates = get_expression_property_names(
|
||||
|
@ -349,7 +354,12 @@ impl JupyterServer {
|
|||
candidates.sort();
|
||||
candidates.dedup(); // make sure to sort first
|
||||
|
||||
if expr.len() > cursor_pos {
|
||||
// TODO(bartlomieju): most likely not correct, but better than panicking because of sub with overflow
|
||||
(candidates, cursor_pos)
|
||||
} else {
|
||||
(candidates, cursor_pos - expr.len())
|
||||
}
|
||||
};
|
||||
|
||||
connection
|
||||
|
|
|
@ -130,8 +130,10 @@ impl NpmConfig {
|
|||
fn add(&mut self, selected: SelectedPackage, dev: bool) {
|
||||
let (name, version) = package_json_dependency_entry(selected);
|
||||
if dev {
|
||||
self.dependencies.swap_remove(&name);
|
||||
self.dev_dependencies.insert(name, version);
|
||||
} else {
|
||||
self.dev_dependencies.swap_remove(&name);
|
||||
self.dependencies.insert(name, version);
|
||||
}
|
||||
}
|
||||
|
@ -361,7 +363,14 @@ fn package_json_dependency_entry(
|
|||
selected: SelectedPackage,
|
||||
) -> (String, String) {
|
||||
if let Some(npm_package) = selected.package_name.strip_prefix("npm:") {
|
||||
if selected.import_name == npm_package {
|
||||
(npm_package.into(), selected.version_req)
|
||||
} else {
|
||||
(
|
||||
selected.import_name,
|
||||
format!("npm:{}@{}", npm_package, selected.version_req),
|
||||
)
|
||||
}
|
||||
} else if let Some(jsr_package) = selected.package_name.strip_prefix("jsr:") {
|
||||
let jsr_package = jsr_package.strip_prefix('@').unwrap_or(jsr_package);
|
||||
let scope_replaced = jsr_package.replace('/', "__");
|
||||
|
@ -391,14 +400,17 @@ impl std::fmt::Display for AddCommandName {
|
|||
|
||||
fn load_configs(
|
||||
flags: &Arc<Flags>,
|
||||
has_jsr_specifiers: impl FnOnce() -> bool,
|
||||
) -> Result<(CliFactory, Option<NpmConfig>, Option<DenoConfig>), AnyError> {
|
||||
let cli_factory = CliFactory::from_flags(flags.clone());
|
||||
let options = cli_factory.cli_options()?;
|
||||
let npm_config = NpmConfig::from_options(options)?;
|
||||
let (cli_factory, deno_config) = match DenoConfig::from_options(options)? {
|
||||
Some(config) => (cli_factory, Some(config)),
|
||||
None if npm_config.is_some() => (cli_factory, None),
|
||||
None => {
|
||||
None if npm_config.is_some() && !has_jsr_specifiers() => {
|
||||
(cli_factory, None)
|
||||
}
|
||||
_ => {
|
||||
let factory = create_deno_json(flags, options)?;
|
||||
let options = factory.cli_options()?.clone();
|
||||
(
|
||||
|
@ -418,7 +430,9 @@ pub async fn add(
|
|||
add_flags: AddFlags,
|
||||
cmd_name: AddCommandName,
|
||||
) -> Result<(), AnyError> {
|
||||
let (cli_factory, npm_config, deno_config) = load_configs(&flags)?;
|
||||
let (cli_factory, npm_config, deno_config) = load_configs(&flags, || {
|
||||
add_flags.packages.iter().any(|s| s.starts_with("jsr:"))
|
||||
})?;
|
||||
let mut npm_config = ConfigUpdater::maybe_new(npm_config).await?;
|
||||
let mut deno_config = ConfigUpdater::maybe_new(deno_config).await?;
|
||||
|
||||
|
@ -456,7 +470,7 @@ pub async fn add(
|
|||
let mut package_reqs = Vec::with_capacity(add_flags.packages.len());
|
||||
|
||||
for entry_text in add_flags.packages.iter() {
|
||||
let req = AddPackageReq::parse(entry_text).with_context(|| {
|
||||
let req = AddRmPackageReq::parse(entry_text).with_context(|| {
|
||||
format!("Failed to parse package required: {}", entry_text)
|
||||
})?;
|
||||
|
||||
|
@ -582,10 +596,10 @@ enum PackageAndVersion {
|
|||
async fn find_package_and_select_version_for_req(
|
||||
jsr_resolver: Arc<JsrFetchResolver>,
|
||||
npm_resolver: Arc<NpmFetchResolver>,
|
||||
add_package_req: AddPackageReq,
|
||||
add_package_req: AddRmPackageReq,
|
||||
) -> Result<PackageAndVersion, AnyError> {
|
||||
match add_package_req.value {
|
||||
AddPackageReqValue::Jsr(req) => {
|
||||
AddRmPackageReqValue::Jsr(req) => {
|
||||
let jsr_prefixed_name = format!("jsr:{}", &req.name);
|
||||
let Some(nv) = jsr_resolver.req_to_nv(&req).await else {
|
||||
if npm_resolver.req_to_nv(&req).await.is_some() {
|
||||
|
@ -603,9 +617,11 @@ async fn find_package_and_select_version_for_req(
|
|||
});
|
||||
};
|
||||
let range_symbol = if req.version_req.version_text().starts_with('~') {
|
||||
'~'
|
||||
"~"
|
||||
} else if req.version_req.version_text() == nv.version.to_string() {
|
||||
""
|
||||
} else {
|
||||
'^'
|
||||
"^"
|
||||
};
|
||||
Ok(PackageAndVersion::Selected(SelectedPackage {
|
||||
import_name: add_package_req.alias,
|
||||
|
@ -614,7 +630,7 @@ async fn find_package_and_select_version_for_req(
|
|||
selected_version: nv.version.to_string(),
|
||||
}))
|
||||
}
|
||||
AddPackageReqValue::Npm(req) => {
|
||||
AddRmPackageReqValue::Npm(req) => {
|
||||
let npm_prefixed_name = format!("npm:{}", &req.name);
|
||||
let Some(nv) = npm_resolver.req_to_nv(&req).await else {
|
||||
return Ok(PackageAndVersion::NotFound {
|
||||
|
@ -623,11 +639,15 @@ async fn find_package_and_select_version_for_req(
|
|||
package_req: req,
|
||||
});
|
||||
};
|
||||
|
||||
let range_symbol = if req.version_req.version_text().starts_with('~') {
|
||||
'~'
|
||||
"~"
|
||||
} else if req.version_req.version_text() == nv.version.to_string() {
|
||||
""
|
||||
} else {
|
||||
'^'
|
||||
"^"
|
||||
};
|
||||
|
||||
Ok(PackageAndVersion::Selected(SelectedPackage {
|
||||
import_name: add_package_req.alias,
|
||||
package_name: npm_prefixed_name,
|
||||
|
@ -639,18 +659,18 @@ async fn find_package_and_select_version_for_req(
|
|||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum AddPackageReqValue {
|
||||
enum AddRmPackageReqValue {
|
||||
Jsr(PackageReq),
|
||||
Npm(PackageReq),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct AddPackageReq {
|
||||
struct AddRmPackageReq {
|
||||
alias: String,
|
||||
value: AddPackageReqValue,
|
||||
value: AddRmPackageReqValue,
|
||||
}
|
||||
|
||||
impl AddPackageReq {
|
||||
impl AddRmPackageReq {
|
||||
pub fn parse(entry_text: &str) -> Result<Result<Self, PackageReq>, AnyError> {
|
||||
enum Prefix {
|
||||
Jsr,
|
||||
|
@ -705,9 +725,9 @@ impl AddPackageReq {
|
|||
let req_ref =
|
||||
JsrPackageReqReference::from_str(&format!("jsr:{}", entry_text))?;
|
||||
let package_req = req_ref.into_inner().req;
|
||||
Ok(Ok(AddPackageReq {
|
||||
Ok(Ok(AddRmPackageReq {
|
||||
alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()),
|
||||
value: AddPackageReqValue::Jsr(package_req),
|
||||
value: AddRmPackageReqValue::Jsr(package_req),
|
||||
}))
|
||||
}
|
||||
Prefix::Npm => {
|
||||
|
@ -725,9 +745,9 @@ impl AddPackageReq {
|
|||
deno_semver::RangeSetOrTag::Tag("latest".into()),
|
||||
);
|
||||
}
|
||||
Ok(Ok(AddPackageReq {
|
||||
Ok(Ok(AddRmPackageReq {
|
||||
alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()),
|
||||
value: AddPackageReqValue::Npm(package_req),
|
||||
value: AddRmPackageReqValue::Npm(package_req),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -739,6 +759,9 @@ fn generate_imports(mut packages_to_version: Vec<(String, String)>) -> String {
|
|||
let mut contents = vec![];
|
||||
let len = packages_to_version.len();
|
||||
for (index, (package, version)) in packages_to_version.iter().enumerate() {
|
||||
if index == 0 {
|
||||
contents.push(String::new()); // force a newline at the start
|
||||
}
|
||||
// TODO(bartlomieju): fix it, once we start support specifying version on the cli
|
||||
contents.push(format!("\"{}\": \"{}\"", package, version));
|
||||
if index != len - 1 {
|
||||
|
@ -752,7 +775,7 @@ pub async fn remove(
|
|||
flags: Arc<Flags>,
|
||||
remove_flags: RemoveFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let (_, npm_config, deno_config) = load_configs(&flags)?;
|
||||
let (_, npm_config, deno_config) = load_configs(&flags, || false)?;
|
||||
|
||||
let mut configs = [
|
||||
ConfigUpdater::maybe_new(npm_config).await?,
|
||||
|
@ -762,12 +785,28 @@ pub async fn remove(
|
|||
let mut removed_packages = vec![];
|
||||
|
||||
for package in &remove_flags.packages {
|
||||
let mut removed = false;
|
||||
let req = AddRmPackageReq::parse(package).with_context(|| {
|
||||
format!("Failed to parse package required: {}", package)
|
||||
})?;
|
||||
let mut parsed_pkg_name = None;
|
||||
for config in configs.iter_mut().flatten() {
|
||||
removed |= config.remove(package);
|
||||
match &req {
|
||||
Ok(rm_pkg) => {
|
||||
if config.remove(&rm_pkg.alias) && parsed_pkg_name.is_none() {
|
||||
parsed_pkg_name = Some(rm_pkg.alias.clone());
|
||||
}
|
||||
if removed {
|
||||
removed_packages.push(package.clone());
|
||||
}
|
||||
Err(pkg) => {
|
||||
// An alias or a package name without registry/version
|
||||
// constraints. Try to remove the package anyway.
|
||||
if config.remove(&pkg.name) && parsed_pkg_name.is_none() {
|
||||
parsed_pkg_name = Some(pkg.name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(pkg) = parsed_pkg_name {
|
||||
removed_packages.push(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -896,48 +935,52 @@ mod test {
|
|||
#[test]
|
||||
fn test_parse_add_package_req() {
|
||||
assert_eq!(
|
||||
AddPackageReq::parse("jsr:foo").unwrap().unwrap(),
|
||||
AddPackageReq {
|
||||
AddRmPackageReq::parse("jsr:foo").unwrap().unwrap(),
|
||||
AddRmPackageReq {
|
||||
alias: "foo".to_string(),
|
||||
value: AddPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
|
||||
value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
AddPackageReq::parse("alias@jsr:foo").unwrap().unwrap(),
|
||||
AddPackageReq {
|
||||
AddRmPackageReq::parse("alias@jsr:foo").unwrap().unwrap(),
|
||||
AddRmPackageReq {
|
||||
alias: "alias".to_string(),
|
||||
value: AddPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
|
||||
value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
AddPackageReq::parse("@alias/pkg@npm:foo").unwrap().unwrap(),
|
||||
AddPackageReq {
|
||||
AddRmPackageReq::parse("@alias/pkg@npm:foo")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
AddRmPackageReq {
|
||||
alias: "@alias/pkg".to_string(),
|
||||
value: AddPackageReqValue::Npm(
|
||||
value: AddRmPackageReqValue::Npm(
|
||||
PackageReq::from_str("foo@latest").unwrap()
|
||||
)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
AddPackageReq::parse("@alias/pkg@jsr:foo").unwrap().unwrap(),
|
||||
AddPackageReq {
|
||||
AddRmPackageReq::parse("@alias/pkg@jsr:foo")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
AddRmPackageReq {
|
||||
alias: "@alias/pkg".to_string(),
|
||||
value: AddPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
|
||||
value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
AddPackageReq::parse("alias@jsr:foo@^1.5.0")
|
||||
AddRmPackageReq::parse("alias@jsr:foo@^1.5.0")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
AddPackageReq {
|
||||
AddRmPackageReq {
|
||||
alias: "alias".to_string(),
|
||||
value: AddPackageReqValue::Jsr(
|
||||
value: AddRmPackageReqValue::Jsr(
|
||||
PackageReq::from_str("foo@^1.5.0").unwrap()
|
||||
)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
AddPackageReq::parse("@scope/pkg@tag")
|
||||
AddRmPackageReq::parse("@scope/pkg@tag")
|
||||
.unwrap()
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
|
|
|
@ -75,6 +75,13 @@ pub async fn cache_top_level_deps(
|
|||
if entry.key.ends_with('/') && specifier.as_str().ends_with('/') {
|
||||
continue;
|
||||
}
|
||||
if specifier.scheme() == "file" {
|
||||
if let Ok(path) = specifier.to_file_path() {
|
||||
if !path.is_file() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
roots.push(specifier.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,6 +182,7 @@ async fn run_task(opts: RunTaskOptions<'_>) -> Result<i32, AnyError> {
|
|||
&task_runner::get_script_with_args(script, cli_options.argv()),
|
||||
);
|
||||
|
||||
Ok(
|
||||
task_runner::run_task(task_runner::RunTaskOptions {
|
||||
task_name,
|
||||
script,
|
||||
|
@ -191,8 +192,11 @@ async fn run_task(opts: RunTaskOptions<'_>) -> Result<i32, AnyError> {
|
|||
init_cwd: opts.cli_options.initial_cwd(),
|
||||
argv: cli_options.argv(),
|
||||
root_node_modules_dir: npm_resolver.root_node_modules_path(),
|
||||
stdio: None,
|
||||
})
|
||||
.await
|
||||
.await?
|
||||
.exit_code,
|
||||
)
|
||||
}
|
||||
|
||||
fn output_task(task_name: &str, script: &str) {
|
||||
|
|
|
@ -913,7 +913,7 @@ async fn download_package(
|
|||
// text above which will stay alive after the progress bars are complete
|
||||
let progress = progress_bar.update("");
|
||||
let maybe_bytes = client
|
||||
.download_with_progress(download_url.clone(), None, &progress)
|
||||
.download_with_progress_and_retries(download_url.clone(), None, &progress)
|
||||
.await
|
||||
.with_context(|| format!("Failed downloading {download_url}. The version you requested may not have been built for the current architecture."))?;
|
||||
Ok(maybe_bytes)
|
||||
|
|
|
@ -254,7 +254,11 @@ impl ExportCollector {
|
|||
let mut import_specifiers = vec![];
|
||||
|
||||
if let Some(default_export) = &self.default_export {
|
||||
if !symbols_to_exclude.contains(default_export) {
|
||||
// If the default export conflicts with a named export, a named one
|
||||
// takes precedence.
|
||||
if !symbols_to_exclude.contains(default_export)
|
||||
&& !self.named_exports.contains(default_export)
|
||||
{
|
||||
import_specifiers.push(ast::ImportSpecifier::Default(
|
||||
ast::ImportDefaultSpecifier {
|
||||
span: DUMMY_SP,
|
||||
|
@ -1137,6 +1141,30 @@ Deno.test("file:///README.md$6-12.js", async ()=>{
|
|||
media_type: MediaType::JavaScript,
|
||||
}],
|
||||
},
|
||||
// https://github.com/denoland/deno/issues/26009
|
||||
Test {
|
||||
input: Input {
|
||||
source: r#"
|
||||
/**
|
||||
* ```ts
|
||||
* console.log(Foo)
|
||||
* ```
|
||||
*/
|
||||
export class Foo {}
|
||||
export default Foo
|
||||
"#,
|
||||
specifier: "file:///main.ts",
|
||||
},
|
||||
expected: vec![Expected {
|
||||
source: r#"import { Foo } from "file:///main.ts";
|
||||
Deno.test("file:///main.ts$3-6.ts", async ()=>{
|
||||
console.log(Foo);
|
||||
});
|
||||
"#,
|
||||
specifier: "file:///main.ts$3-6.ts",
|
||||
media_type: MediaType::TypeScript,
|
||||
}],
|
||||
},
|
||||
];
|
||||
|
||||
for test in tests {
|
||||
|
@ -1326,6 +1354,28 @@ assertEquals(add(1, 2), 3);
|
|||
media_type: MediaType::JavaScript,
|
||||
}],
|
||||
},
|
||||
// https://github.com/denoland/deno/issues/26009
|
||||
Test {
|
||||
input: Input {
|
||||
source: r#"
|
||||
/**
|
||||
* ```ts
|
||||
* console.log(Foo)
|
||||
* ```
|
||||
*/
|
||||
export class Foo {}
|
||||
export default Foo
|
||||
"#,
|
||||
specifier: "file:///main.ts",
|
||||
},
|
||||
expected: vec![Expected {
|
||||
source: r#"import { Foo } from "file:///main.ts";
|
||||
console.log(Foo);
|
||||
"#,
|
||||
specifier: "file:///main.ts$3-6.ts",
|
||||
media_type: MediaType::TypeScript,
|
||||
}],
|
||||
},
|
||||
];
|
||||
|
||||
for test in tests {
|
||||
|
@ -1581,6 +1631,16 @@ declare global {
|
|||
named_expected: atom_set!(),
|
||||
default_expected: None,
|
||||
},
|
||||
// The identifier `Foo` conflicts, but `ExportCollector` doesn't do
|
||||
// anything about it. It is handled by `to_import_specifiers` method.
|
||||
Test {
|
||||
input: r#"
|
||||
export class Foo {}
|
||||
export default Foo
|
||||
"#,
|
||||
named_expected: atom_set!("Foo"),
|
||||
default_expected: Some("Foo".into()),
|
||||
},
|
||||
];
|
||||
|
||||
for test in tests {
|
||||
|
|
|
@ -160,10 +160,10 @@ fn atomic_write_file(
|
|||
data: &[u8],
|
||||
) -> std::io::Result<()> {
|
||||
fs.write_file(temp_file_path, data)?;
|
||||
fs.rename_file(temp_file_path, file_path).map_err(|err| {
|
||||
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);
|
||||
err
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -277,7 +277,7 @@ pub fn write_file_2<T: AsRef<[u8]>>(
|
|||
|
||||
/// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows.
|
||||
pub fn canonicalize_path(path: &Path) -> Result<PathBuf, Error> {
|
||||
Ok(deno_core::strip_unc_prefix(path.canonicalize()?))
|
||||
Ok(deno_path_util::strip_unc_prefix(path.canonicalize()?))
|
||||
}
|
||||
|
||||
/// Canonicalizes a path which might be non-existent by going up the
|
||||
|
|
|
@ -14,6 +14,7 @@ pub mod logger;
|
|||
pub mod path;
|
||||
pub mod progress_bar;
|
||||
pub mod result;
|
||||
pub mod retry;
|
||||
pub mod sync;
|
||||
pub mod text_encoding;
|
||||
pub mod unix;
|
||||
|
|
41
cli/util/retry.rs
Normal file
41
cli/util/retry.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::future::Future;
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn retry<
|
||||
F: FnMut() -> Fut,
|
||||
T,
|
||||
E,
|
||||
Fut: Future<Output = Result<T, E>>,
|
||||
ShouldRetry: FnMut(&E) -> bool,
|
||||
>(
|
||||
mut f: F,
|
||||
mut should_retry: ShouldRetry,
|
||||
) -> impl Future<Output = Result<T, E>> {
|
||||
const WAITS: [Duration; 3] = [
|
||||
Duration::from_millis(100),
|
||||
Duration::from_millis(250),
|
||||
Duration::from_millis(500),
|
||||
];
|
||||
|
||||
let mut waits = WAITS.into_iter();
|
||||
async move {
|
||||
let mut first_result = None;
|
||||
loop {
|
||||
let result = f().await;
|
||||
match result {
|
||||
Ok(r) => return Ok(r),
|
||||
Err(e) if !should_retry(&e) => return Err(e),
|
||||
_ => {}
|
||||
}
|
||||
if first_result.is_none() {
|
||||
first_result = Some(result);
|
||||
}
|
||||
let Some(wait) = waits.next() else {
|
||||
return first_result.unwrap();
|
||||
};
|
||||
tokio::time::sleep(wait).await;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
use base64::Engine;
|
||||
|
@ -9,6 +11,15 @@ use deno_core::ModuleSourceCode;
|
|||
static SOURCE_MAP_PREFIX: &[u8] =
|
||||
b"//# sourceMappingURL=data:application/json;base64,";
|
||||
|
||||
pub fn from_utf8_lossy_owned(bytes: Vec<u8>) -> String {
|
||||
match String::from_utf8_lossy(&bytes) {
|
||||
Cow::Owned(code) => code,
|
||||
// SAFETY: `String::from_utf8_lossy` guarantees that the result is valid
|
||||
// UTF-8 if `Cow::Borrowed` is returned.
|
||||
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(bytes) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_map_from_code(code: &[u8]) -> Option<Vec<u8>> {
|
||||
let range = find_source_map_range(code)?;
|
||||
let source_map_range = &code[range];
|
||||
|
@ -85,6 +96,13 @@ fn find_source_map_range(code: &[u8]) -> Option<Range<usize>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts an `Arc<str>` to an `Arc<[u8]>`.
|
||||
pub fn arc_str_to_bytes(arc_str: Arc<str>) -> Arc<[u8]> {
|
||||
let raw = Arc::into_raw(arc_str);
|
||||
// SAFETY: This is safe because they have the same memory layout.
|
||||
unsafe { Arc::from_raw(raw as *const [u8]) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
|
|
@ -51,9 +51,11 @@ use crate::args::DenoSubcommand;
|
|||
use crate::args::StorageKeyResolver;
|
||||
use crate::errors;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CjsResolutionStore;
|
||||
use crate::util::checksum;
|
||||
use crate::util::file_watcher::WatcherCommunicator;
|
||||
use crate::util::file_watcher::WatcherRestartMode;
|
||||
use crate::util::path::specifier_has_extension;
|
||||
use crate::version;
|
||||
|
||||
pub struct ModuleLoaderAndSourceMapGetter {
|
||||
|
@ -120,11 +122,13 @@ pub struct CliMainWorkerOptions {
|
|||
pub node_ipc: Option<i64>,
|
||||
pub serve_port: Option<u16>,
|
||||
pub serve_host: Option<String>,
|
||||
pub unstable_detect_cjs: bool,
|
||||
}
|
||||
|
||||
struct SharedWorkerState {
|
||||
blob_store: Arc<BlobStore>,
|
||||
broadcast_channel: InMemoryBroadcastChannel,
|
||||
cjs_resolution_store: Arc<CjsResolutionStore>,
|
||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
||||
compiled_wasm_module_store: CompiledWasmModuleStore,
|
||||
feature_checker: Arc<FeatureChecker>,
|
||||
|
@ -422,6 +426,7 @@ impl CliMainWorkerFactory {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
blob_store: Arc<BlobStore>,
|
||||
cjs_resolution_store: Arc<CjsResolutionStore>,
|
||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
||||
feature_checker: Arc<FeatureChecker>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
|
@ -441,6 +446,7 @@ impl CliMainWorkerFactory {
|
|||
shared: Arc::new(SharedWorkerState {
|
||||
blob_store,
|
||||
broadcast_channel: Default::default(),
|
||||
cjs_resolution_store,
|
||||
code_cache,
|
||||
compiled_wasm_module_store: Default::default(),
|
||||
feature_checker,
|
||||
|
@ -486,6 +492,9 @@ impl CliMainWorkerFactory {
|
|||
stdio: deno_runtime::deno_io::Stdio,
|
||||
) -> Result<CliMainWorker, AnyError> {
|
||||
let shared = &self.shared;
|
||||
let ModuleLoaderAndSourceMapGetter { module_loader } = shared
|
||||
.module_loader_factory
|
||||
.create_for_main(permissions.clone());
|
||||
let (main_module, is_main_cjs) = if let Ok(package_ref) =
|
||||
NpmPackageReqReference::from_specifier(&main_module)
|
||||
{
|
||||
|
@ -526,13 +535,29 @@ impl CliMainWorkerFactory {
|
|||
let is_main_cjs = matches!(node_resolution, NodeResolution::CommonJs(_));
|
||||
(node_resolution.into_url(), is_main_cjs)
|
||||
} else {
|
||||
let is_cjs = main_module.path().ends_with(".cjs");
|
||||
let is_maybe_cjs_js_ext = self.shared.options.unstable_detect_cjs
|
||||
&& specifier_has_extension(&main_module, "js")
|
||||
&& self
|
||||
.shared
|
||||
.node_resolver
|
||||
.get_closest_package_json(&main_module)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|pkg_json| pkg_json.typ == "commonjs")
|
||||
.unwrap_or(false);
|
||||
let is_cjs = if is_maybe_cjs_js_ext {
|
||||
// fill the cjs resolution store by preparing the module load
|
||||
module_loader
|
||||
.prepare_load(&main_module, None, false)
|
||||
.await?;
|
||||
self.shared.cjs_resolution_store.is_known_cjs(&main_module)
|
||||
} else {
|
||||
main_module.scheme() == "file"
|
||||
&& specifier_has_extension(&main_module, "cjs")
|
||||
};
|
||||
(main_module, is_cjs)
|
||||
};
|
||||
|
||||
let ModuleLoaderAndSourceMapGetter { module_loader } = shared
|
||||
.module_loader_factory
|
||||
.create_for_main(permissions.clone());
|
||||
let maybe_inspector_server = shared.maybe_inspector_server.clone();
|
||||
|
||||
let create_web_worker_cb =
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_broadcast_channel"
|
||||
version = "0.165.0"
|
||||
version = "0.167.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -16,5 +16,6 @@ path = "lib.rs"
|
|||
[dependencies]
|
||||
async-trait.workspace = true
|
||||
deno_core.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
uuid.workspace = true
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use tokio::sync::broadcast;
|
||||
use tokio::sync::mpsc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::BroadcastChannel;
|
||||
use crate::BroadcastChannelError;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InMemoryBroadcastChannel(Arc<Mutex<broadcast::Sender<Message>>>);
|
||||
|
@ -41,7 +41,7 @@ impl Default for InMemoryBroadcastChannel {
|
|||
impl BroadcastChannel for InMemoryBroadcastChannel {
|
||||
type Resource = InMemoryBroadcastChannelResource;
|
||||
|
||||
fn subscribe(&self) -> Result<Self::Resource, AnyError> {
|
||||
fn subscribe(&self) -> Result<Self::Resource, BroadcastChannelError> {
|
||||
let (cancel_tx, cancel_rx) = mpsc::unbounded_channel();
|
||||
let broadcast_rx = self.0.lock().subscribe();
|
||||
let rx = tokio::sync::Mutex::new((broadcast_rx, cancel_rx));
|
||||
|
@ -53,7 +53,10 @@ impl BroadcastChannel for InMemoryBroadcastChannel {
|
|||
})
|
||||
}
|
||||
|
||||
fn unsubscribe(&self, resource: &Self::Resource) -> Result<(), AnyError> {
|
||||
fn unsubscribe(
|
||||
&self,
|
||||
resource: &Self::Resource,
|
||||
) -> Result<(), BroadcastChannelError> {
|
||||
Ok(resource.cancel_tx.send(())?)
|
||||
}
|
||||
|
||||
|
@ -62,7 +65,7 @@ impl BroadcastChannel for InMemoryBroadcastChannel {
|
|||
resource: &Self::Resource,
|
||||
name: String,
|
||||
data: Vec<u8>,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), BroadcastChannelError> {
|
||||
let name = Arc::new(name);
|
||||
let data = Arc::new(data);
|
||||
let uuid = resource.uuid;
|
||||
|
@ -73,7 +76,7 @@ impl BroadcastChannel for InMemoryBroadcastChannel {
|
|||
async fn recv(
|
||||
&self,
|
||||
resource: &Self::Resource,
|
||||
) -> Result<Option<crate::Message>, AnyError> {
|
||||
) -> Result<Option<crate::Message>, BroadcastChannelError> {
|
||||
let mut g = resource.rx.lock().await;
|
||||
let (broadcast_rx, cancel_rx) = &mut *g;
|
||||
loop {
|
||||
|
|
|
@ -10,34 +10,69 @@ use std::path::PathBuf;
|
|||
use std::rc::Rc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::JsBuffer;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
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)]
|
||||
pub enum BroadcastChannelError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
MPSCSendError(MpscSendError<Box<dyn std::fmt::Debug + Send + Sync>>),
|
||||
#[error(transparent)]
|
||||
BroadcastSendError(
|
||||
BroadcastSendError<Box<dyn std::fmt::Debug + Send + Sync>>,
|
||||
),
|
||||
#[error(transparent)]
|
||||
Other(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug + Send + Sync + 'static> From<MpscSendError<T>>
|
||||
for BroadcastChannelError
|
||||
{
|
||||
fn from(value: MpscSendError<T>) -> Self {
|
||||
BroadcastChannelError::MPSCSendError(MpscSendError(Box::new(value.0)))
|
||||
}
|
||||
}
|
||||
impl<T: std::fmt::Debug + Send + Sync + 'static> From<BroadcastSendError<T>>
|
||||
for BroadcastChannelError
|
||||
{
|
||||
fn from(value: BroadcastSendError<T>) -> Self {
|
||||
BroadcastChannelError::BroadcastSendError(BroadcastSendError(Box::new(
|
||||
value.0,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait BroadcastChannel: Clone {
|
||||
type Resource: Resource;
|
||||
|
||||
fn subscribe(&self) -> Result<Self::Resource, AnyError>;
|
||||
fn subscribe(&self) -> Result<Self::Resource, BroadcastChannelError>;
|
||||
|
||||
fn unsubscribe(&self, resource: &Self::Resource) -> Result<(), AnyError>;
|
||||
fn unsubscribe(
|
||||
&self,
|
||||
resource: &Self::Resource,
|
||||
) -> Result<(), BroadcastChannelError>;
|
||||
|
||||
async fn send(
|
||||
&self,
|
||||
resource: &Self::Resource,
|
||||
name: String,
|
||||
data: Vec<u8>,
|
||||
) -> Result<(), AnyError>;
|
||||
) -> Result<(), BroadcastChannelError>;
|
||||
|
||||
async fn recv(
|
||||
&self,
|
||||
resource: &Self::Resource,
|
||||
) -> Result<Option<Message>, AnyError>;
|
||||
) -> Result<Option<Message>, BroadcastChannelError>;
|
||||
}
|
||||
|
||||
pub type Message = (String, Vec<u8>);
|
||||
|
@ -46,7 +81,7 @@ pub type Message = (String, Vec<u8>);
|
|||
#[smi]
|
||||
pub fn op_broadcast_subscribe<BC>(
|
||||
state: &mut OpState,
|
||||
) -> Result<ResourceId, AnyError>
|
||||
) -> Result<ResourceId, BroadcastChannelError>
|
||||
where
|
||||
BC: BroadcastChannel + 'static,
|
||||
{
|
||||
|
@ -62,11 +97,14 @@ where
|
|||
pub fn op_broadcast_unsubscribe<BC>(
|
||||
state: &mut OpState,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<(), AnyError>
|
||||
) -> Result<(), BroadcastChannelError>
|
||||
where
|
||||
BC: BroadcastChannel + 'static,
|
||||
{
|
||||
let resource = state.resource_table.get::<BC::Resource>(rid)?;
|
||||
let resource = state
|
||||
.resource_table
|
||||
.get::<BC::Resource>(rid)
|
||||
.map_err(BroadcastChannelError::Resource)?;
|
||||
let bc = state.borrow::<BC>();
|
||||
bc.unsubscribe(&resource)
|
||||
}
|
||||
|
@ -77,11 +115,15 @@ pub async fn op_broadcast_send<BC>(
|
|||
#[smi] rid: ResourceId,
|
||||
#[string] name: String,
|
||||
#[buffer] buf: JsBuffer,
|
||||
) -> Result<(), AnyError>
|
||||
) -> Result<(), BroadcastChannelError>
|
||||
where
|
||||
BC: BroadcastChannel + 'static,
|
||||
{
|
||||
let resource = state.borrow().resource_table.get::<BC::Resource>(rid)?;
|
||||
let resource = state
|
||||
.borrow()
|
||||
.resource_table
|
||||
.get::<BC::Resource>(rid)
|
||||
.map_err(BroadcastChannelError::Resource)?;
|
||||
let bc = state.borrow().borrow::<BC>().clone();
|
||||
bc.send(&resource, name, buf.to_vec()).await
|
||||
}
|
||||
|
@ -91,11 +133,15 @@ where
|
|||
pub async fn op_broadcast_recv<BC>(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<Option<Message>, AnyError>
|
||||
) -> Result<Option<Message>, BroadcastChannelError>
|
||||
where
|
||||
BC: BroadcastChannel + 'static,
|
||||
{
|
||||
let resource = state.borrow().resource_table.get::<BC::Resource>(rid)?;
|
||||
let resource = state
|
||||
.borrow()
|
||||
.resource_table
|
||||
.get::<BC::Resource>(rid)
|
||||
.map_err(BroadcastChannelError::Resource)?;
|
||||
let bc = state.borrow().borrow::<BC>().clone();
|
||||
bc.recv(&resource).await
|
||||
}
|
||||
|
|
3
ext/cache/Cargo.toml
vendored
3
ext/cache/Cargo.toml
vendored
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_cache"
|
||||
version = "0.103.0"
|
||||
version = "0.105.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -19,4 +19,5 @@ deno_core.workspace = true
|
|||
rusqlite.workspace = true
|
||||
serde.workspace = true
|
||||
sha2.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
|
|
60
ext/cache/lib.rs
vendored
60
ext/cache/lib.rs
vendored
|
@ -7,7 +7,6 @@ use std::sync::Arc;
|
|||
|
||||
use async_trait::async_trait;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::serde::Deserialize;
|
||||
use deno_core::serde::Serialize;
|
||||
|
@ -19,6 +18,20 @@ use deno_core::ResourceId;
|
|||
mod sqlite;
|
||||
pub use sqlite::SqliteBackedCache;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CacheError {
|
||||
#[error(transparent)]
|
||||
Sqlite(#[from] rusqlite::Error),
|
||||
#[error(transparent)]
|
||||
JoinError(#[from] tokio::task::JoinError),
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Other(deno_core::error::AnyError),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CreateCache<C: Cache + 'static>(pub Arc<dyn Fn() -> C>);
|
||||
|
||||
|
@ -92,26 +105,31 @@ pub struct CacheDeleteRequest {
|
|||
pub trait Cache: Clone + 'static {
|
||||
type CacheMatchResourceType: Resource;
|
||||
|
||||
async fn storage_open(&self, cache_name: String) -> Result<i64, AnyError>;
|
||||
async fn storage_has(&self, cache_name: String) -> Result<bool, AnyError>;
|
||||
async fn storage_delete(&self, cache_name: String) -> Result<bool, AnyError>;
|
||||
async fn storage_open(&self, cache_name: String) -> Result<i64, CacheError>;
|
||||
async fn storage_has(&self, cache_name: String) -> Result<bool, CacheError>;
|
||||
async fn storage_delete(
|
||||
&self,
|
||||
cache_name: String,
|
||||
) -> Result<bool, CacheError>;
|
||||
|
||||
/// Put a resource into the cache.
|
||||
async fn put(
|
||||
&self,
|
||||
request_response: CachePutRequest,
|
||||
resource: Option<Rc<dyn Resource>>,
|
||||
) -> Result<(), AnyError>;
|
||||
) -> Result<(), CacheError>;
|
||||
|
||||
async fn r#match(
|
||||
&self,
|
||||
request: CacheMatchRequest,
|
||||
) -> Result<
|
||||
Option<(CacheMatchResponseMeta, Option<Self::CacheMatchResourceType>)>,
|
||||
AnyError,
|
||||
CacheError,
|
||||
>;
|
||||
async fn delete(&self, request: CacheDeleteRequest)
|
||||
-> Result<bool, AnyError>;
|
||||
async fn delete(
|
||||
&self,
|
||||
request: CacheDeleteRequest,
|
||||
) -> Result<bool, CacheError>;
|
||||
}
|
||||
|
||||
#[op2(async)]
|
||||
|
@ -119,7 +137,7 @@ pub trait Cache: Clone + 'static {
|
|||
pub async fn op_cache_storage_open<CA>(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[string] cache_name: String,
|
||||
) -> Result<i64, AnyError>
|
||||
) -> Result<i64, CacheError>
|
||||
where
|
||||
CA: Cache,
|
||||
{
|
||||
|
@ -131,7 +149,7 @@ where
|
|||
pub async fn op_cache_storage_has<CA>(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[string] cache_name: String,
|
||||
) -> Result<bool, AnyError>
|
||||
) -> Result<bool, CacheError>
|
||||
where
|
||||
CA: Cache,
|
||||
{
|
||||
|
@ -143,7 +161,7 @@ where
|
|||
pub async fn op_cache_storage_delete<CA>(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[string] cache_name: String,
|
||||
) -> Result<bool, AnyError>
|
||||
) -> Result<bool, CacheError>
|
||||
where
|
||||
CA: Cache,
|
||||
{
|
||||
|
@ -155,13 +173,19 @@ where
|
|||
pub async fn op_cache_put<CA>(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[serde] request_response: CachePutRequest,
|
||||
) -> Result<(), AnyError>
|
||||
) -> Result<(), CacheError>
|
||||
where
|
||||
CA: Cache,
|
||||
{
|
||||
let cache = get_cache::<CA>(&state)?;
|
||||
let resource = match request_response.response_rid {
|
||||
Some(rid) => Some(state.borrow_mut().resource_table.take_any(rid)?),
|
||||
Some(rid) => Some(
|
||||
state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.take_any(rid)
|
||||
.map_err(CacheError::Resource)?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
cache.put(request_response, resource).await
|
||||
|
@ -172,7 +196,7 @@ where
|
|||
pub async fn op_cache_match<CA>(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[serde] request: CacheMatchRequest,
|
||||
) -> Result<Option<CacheMatchResponse>, AnyError>
|
||||
) -> Result<Option<CacheMatchResponse>, CacheError>
|
||||
where
|
||||
CA: Cache,
|
||||
{
|
||||
|
@ -191,7 +215,7 @@ where
|
|||
pub async fn op_cache_delete<CA>(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[serde] request: CacheDeleteRequest,
|
||||
) -> Result<bool, AnyError>
|
||||
) -> Result<bool, CacheError>
|
||||
where
|
||||
CA: Cache,
|
||||
{
|
||||
|
@ -199,7 +223,7 @@ where
|
|||
cache.delete(request).await
|
||||
}
|
||||
|
||||
pub fn get_cache<CA>(state: &Rc<RefCell<OpState>>) -> Result<CA, AnyError>
|
||||
pub fn get_cache<CA>(state: &Rc<RefCell<OpState>>) -> Result<CA, CacheError>
|
||||
where
|
||||
CA: Cache,
|
||||
{
|
||||
|
@ -211,7 +235,9 @@ where
|
|||
state.put(cache);
|
||||
Ok(state.borrow::<CA>().clone())
|
||||
} else {
|
||||
Err(type_error("CacheStorage is not available in this context"))
|
||||
Err(CacheError::Other(type_error(
|
||||
"CacheStorage is not available in this context",
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
50
ext/cache/sqlite.rs
vendored
50
ext/cache/sqlite.rs
vendored
|
@ -30,6 +30,7 @@ use crate::serialize_headers;
|
|||
use crate::vary_header_matches;
|
||||
use crate::Cache;
|
||||
use crate::CacheDeleteRequest;
|
||||
use crate::CacheError;
|
||||
use crate::CacheMatchRequest;
|
||||
use crate::CacheMatchResponseMeta;
|
||||
use crate::CachePutRequest;
|
||||
|
@ -102,7 +103,7 @@ impl Cache for SqliteBackedCache {
|
|||
/// Open a cache storage. Internally, this creates a row in the
|
||||
/// sqlite db if the cache doesn't exist and returns the internal id
|
||||
/// of the cache.
|
||||
async fn storage_open(&self, cache_name: String) -> Result<i64, AnyError> {
|
||||
async fn storage_open(&self, cache_name: String) -> Result<i64, CacheError> {
|
||||
let db = self.connection.clone();
|
||||
let cache_storage_dir = self.cache_storage_dir.clone();
|
||||
spawn_blocking(move || {
|
||||
|
@ -121,14 +122,14 @@ impl Cache for SqliteBackedCache {
|
|||
)?;
|
||||
let responses_dir = get_responses_dir(cache_storage_dir, cache_id);
|
||||
std::fs::create_dir_all(responses_dir)?;
|
||||
Ok::<i64, AnyError>(cache_id)
|
||||
Ok::<i64, CacheError>(cache_id)
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
/// Check if a cache with the provided name exists.
|
||||
/// Note: this doesn't check the disk, it only checks the sqlite db.
|
||||
async fn storage_has(&self, cache_name: String) -> Result<bool, AnyError> {
|
||||
async fn storage_has(&self, cache_name: String) -> Result<bool, CacheError> {
|
||||
let db = self.connection.clone();
|
||||
spawn_blocking(move || {
|
||||
let db = db.lock();
|
||||
|
@ -140,13 +141,16 @@ impl Cache for SqliteBackedCache {
|
|||
Ok(count > 0)
|
||||
},
|
||||
)?;
|
||||
Ok::<bool, AnyError>(cache_exists)
|
||||
Ok::<bool, CacheError>(cache_exists)
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
/// Delete a cache storage. Internally, this deletes the row in the sqlite db.
|
||||
async fn storage_delete(&self, cache_name: String) -> Result<bool, AnyError> {
|
||||
async fn storage_delete(
|
||||
&self,
|
||||
cache_name: String,
|
||||
) -> Result<bool, CacheError> {
|
||||
let db = self.connection.clone();
|
||||
let cache_storage_dir = self.cache_storage_dir.clone();
|
||||
spawn_blocking(move || {
|
||||
|
@ -167,7 +171,7 @@ impl Cache for SqliteBackedCache {
|
|||
std::fs::remove_dir_all(cache_dir)?;
|
||||
}
|
||||
}
|
||||
Ok::<bool, AnyError>(maybe_cache_id.is_some())
|
||||
Ok::<bool, CacheError>(maybe_cache_id.is_some())
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
@ -176,10 +180,12 @@ impl Cache for SqliteBackedCache {
|
|||
&self,
|
||||
request_response: CachePutRequest,
|
||||
resource: Option<Rc<dyn Resource>>,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), CacheError> {
|
||||
let db = self.connection.clone();
|
||||
let cache_storage_dir = self.cache_storage_dir.clone();
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("SystemTime is before unix epoch");
|
||||
|
||||
if let Some(resource) = resource {
|
||||
let body_key = hash(&format!(
|
||||
|
@ -193,7 +199,11 @@ impl Cache for SqliteBackedCache {
|
|||
let mut file = tokio::fs::File::create(response_path).await?;
|
||||
let mut buf = BufMutView::new(64 * 1024);
|
||||
loop {
|
||||
let (size, buf2) = resource.clone().read_byob(buf).await?;
|
||||
let (size, buf2) = resource
|
||||
.clone()
|
||||
.read_byob(buf)
|
||||
.await
|
||||
.map_err(CacheError::Other)?;
|
||||
if size == 0 {
|
||||
break;
|
||||
}
|
||||
|
@ -224,7 +234,7 @@ impl Cache for SqliteBackedCache {
|
|||
request: CacheMatchRequest,
|
||||
) -> Result<
|
||||
Option<(CacheMatchResponseMeta, Option<CacheResponseResource>)>,
|
||||
AnyError,
|
||||
CacheError,
|
||||
> {
|
||||
let db = self.connection.clone();
|
||||
let cache_storage_dir = self.cache_storage_dir.clone();
|
||||
|
@ -290,19 +300,17 @@ impl Cache for SqliteBackedCache {
|
|||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
return Ok(Some((cache_meta, Some(CacheResponseResource::new(file)))));
|
||||
Ok(Some((cache_meta, Some(CacheResponseResource::new(file)))))
|
||||
}
|
||||
Some((cache_meta, None)) => {
|
||||
return Ok(Some((cache_meta, None)));
|
||||
}
|
||||
None => return Ok(None),
|
||||
Some((cache_meta, None)) => Ok(Some((cache_meta, None))),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete(
|
||||
&self,
|
||||
request: CacheDeleteRequest,
|
||||
) -> Result<bool, AnyError> {
|
||||
) -> Result<bool, CacheError> {
|
||||
let db = self.connection.clone();
|
||||
spawn_blocking(move || {
|
||||
// TODO(@satyarohith): remove the response body from disk if one exists
|
||||
|
@ -311,17 +319,17 @@ impl Cache for SqliteBackedCache {
|
|||
"DELETE FROM request_response_list WHERE cache_id = ?1 AND request_url = ?2",
|
||||
(request.cache_id, &request.request_url),
|
||||
)?;
|
||||
Ok::<bool, AnyError>(rows_effected > 0)
|
||||
Ok::<bool, CacheError>(rows_effected > 0)
|
||||
})
|
||||
.await?
|
||||
}
|
||||
}
|
||||
|
||||
async fn insert_cache_asset(
|
||||
db: Arc<Mutex<rusqlite::Connection>>,
|
||||
db: Arc<Mutex<Connection>>,
|
||||
put: CachePutRequest,
|
||||
response_body_key: Option<String>,
|
||||
) -> Result<Option<String>, deno_core::anyhow::Error> {
|
||||
) -> Result<Option<String>, CacheError> {
|
||||
spawn_blocking(move || {
|
||||
let maybe_response_body = {
|
||||
let db = db.lock();
|
||||
|
@ -339,7 +347,7 @@ async fn insert_cache_asset(
|
|||
response_body_key,
|
||||
put.response_status,
|
||||
put.response_status_text,
|
||||
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(),
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime is before unix epoch").as_secs(),
|
||||
),
|
||||
|row| {
|
||||
let response_body_key: Option<String> = row.get(0)?;
|
||||
|
@ -347,7 +355,7 @@ async fn insert_cache_asset(
|
|||
},
|
||||
)?
|
||||
};
|
||||
Ok::<Option<String>, AnyError>(maybe_response_body)
|
||||
Ok::<Option<String>, CacheError>(maybe_response_body)
|
||||
}).await?
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_canvas"
|
||||
version = "0.40.0"
|
||||
version = "0.42.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -18,3 +18,4 @@ deno_core.workspace = true
|
|||
deno_webgpu.workspace = true
|
||||
image = { version = "0.24.7", default-features = false, features = ["png"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror.workspace = true
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::ToJsBuffer;
|
||||
use image::imageops::FilterType;
|
||||
|
@ -13,6 +11,14 @@ use serde::Deserialize;
|
|||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CanvasError {
|
||||
#[error("Color type '{0:?}' not supported")]
|
||||
UnsupportedColorType(ColorType),
|
||||
#[error(transparent)]
|
||||
Image(#[from] image::ImageError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum ImageResizeQuality {
|
||||
|
@ -43,7 +49,7 @@ struct ImageProcessArgs {
|
|||
fn op_image_process(
|
||||
#[buffer] buf: &[u8],
|
||||
#[serde] args: ImageProcessArgs,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> ToJsBuffer {
|
||||
let view =
|
||||
RgbaImage::from_vec(args.width, args.height, buf.to_vec()).unwrap();
|
||||
|
||||
|
@ -105,7 +111,7 @@ fn op_image_process(
|
|||
}
|
||||
}
|
||||
|
||||
Ok(image_out.to_vec().into())
|
||||
image_out.to_vec().into()
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
@ -117,17 +123,16 @@ struct DecodedPng {
|
|||
|
||||
#[op2]
|
||||
#[serde]
|
||||
fn op_image_decode_png(#[buffer] buf: &[u8]) -> Result<DecodedPng, AnyError> {
|
||||
fn op_image_decode_png(
|
||||
#[buffer] buf: &[u8],
|
||||
) -> Result<DecodedPng, CanvasError> {
|
||||
let png = image::codecs::png::PngDecoder::new(buf)?;
|
||||
|
||||
let (width, height) = png.dimensions();
|
||||
|
||||
// TODO(@crowlKats): maybe use DynamicImage https://docs.rs/image/0.24.7/image/enum.DynamicImage.html ?
|
||||
if png.color_type() != ColorType::Rgba8 {
|
||||
return Err(type_error(format!(
|
||||
"Color type '{:?}' not supported",
|
||||
png.color_type()
|
||||
)));
|
||||
return Err(CanvasError::UnsupportedColorType(png.color_type()));
|
||||
}
|
||||
|
||||
// read_image will assert that the buffer is the correct size, so we need to fill it with zeros
|
||||
|
|
|
@ -84,6 +84,7 @@ const {
|
|||
NumberIsInteger,
|
||||
NumberIsNaN,
|
||||
NumberParseInt,
|
||||
NumberParseFloat,
|
||||
NumberPrototypeToFixed,
|
||||
NumberPrototypeToString,
|
||||
NumberPrototypeValueOf,
|
||||
|
@ -3010,20 +3011,18 @@ function inspectArgs(args, inspectOptions = { __proto__: null }) {
|
|||
} else if (ArrayPrototypeIncludes(["d", "i"], char)) {
|
||||
// Format as an integer.
|
||||
const value = args[a++];
|
||||
if (typeof value == "bigint") {
|
||||
formattedArg = `${value}n`;
|
||||
} else if (typeof value == "number") {
|
||||
formattedArg = `${NumberParseInt(String(value))}`;
|
||||
} else {
|
||||
if (typeof value === "symbol") {
|
||||
formattedArg = "NaN";
|
||||
} else {
|
||||
formattedArg = `${NumberParseInt(value)}`;
|
||||
}
|
||||
} else if (char == "f") {
|
||||
// Format as a floating point value.
|
||||
const value = args[a++];
|
||||
if (typeof value == "number") {
|
||||
formattedArg = `${value}`;
|
||||
} else {
|
||||
if (typeof value === "symbol") {
|
||||
formattedArg = "NaN";
|
||||
} else {
|
||||
formattedArg = `${NumberParseFloat(value)}`;
|
||||
}
|
||||
} else if (ArrayPrototypeIncludes(["O", "o"], char)) {
|
||||
// Format as an object.
|
||||
|
@ -3257,7 +3256,7 @@ class Console {
|
|||
|
||||
const stringifyValue = (value) =>
|
||||
inspectValueWithQuotes(value, {
|
||||
...getDefaultInspectOptions(),
|
||||
...getConsoleInspectOptions(noColorStdout()),
|
||||
depth: 1,
|
||||
compact: true,
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_console"
|
||||
version = "0.171.0"
|
||||
version = "0.173.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_cron"
|
||||
version = "0.51.0"
|
||||
version = "0.53.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -19,4 +19,5 @@ async-trait.workspace = true
|
|||
chrono = { workspace = true, features = ["now"] }
|
||||
deno_core.workspace = true
|
||||
saffron.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::CronError;
|
||||
use async_trait::async_trait;
|
||||
use deno_core::error::AnyError;
|
||||
|
||||
pub trait CronHandler {
|
||||
type EH: CronHandle + 'static;
|
||||
|
||||
fn create(&self, spec: CronSpec) -> Result<Self::EH, AnyError>;
|
||||
fn create(&self, spec: CronSpec) -> Result<Self::EH, CronError>;
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
pub trait CronHandle {
|
||||
async fn next(&self, prev_success: bool) -> Result<bool, AnyError>;
|
||||
async fn next(&self, prev_success: bool) -> Result<bool, CronError>;
|
||||
fn close(&self);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,16 +7,13 @@ 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::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
|
||||
pub use crate::interface::*;
|
||||
|
||||
pub const UNSTABLE_FEATURE_NAME: &str = "cron";
|
||||
|
||||
deno_core::extension!(deno_cron,
|
||||
|
@ -49,6 +46,28 @@ impl<EH: CronHandle + 'static> Resource for CronResource<EH> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CronError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error("Cron name cannot exceed 64 characters: current length {0}")]
|
||||
NameExceeded(usize),
|
||||
#[error("Invalid cron name: only alphanumeric characters, whitespace, hyphens, and underscores are allowed")]
|
||||
NameInvalid,
|
||||
#[error("Cron with this name already exists")]
|
||||
AlreadyExists,
|
||||
#[error("Too many crons")]
|
||||
TooManyCrons,
|
||||
#[error("Invalid cron schedule")]
|
||||
InvalidCron,
|
||||
#[error("Invalid backoff schedule")]
|
||||
InvalidBackoff,
|
||||
#[error(transparent)]
|
||||
AcquireError(#[from] tokio::sync::AcquireError),
|
||||
#[error(transparent)]
|
||||
Other(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[smi]
|
||||
fn op_cron_create<C>(
|
||||
|
@ -56,7 +75,7 @@ fn op_cron_create<C>(
|
|||
#[string] name: String,
|
||||
#[string] cron_schedule: String,
|
||||
#[serde] backoff_schedule: Option<Vec<u32>>,
|
||||
) -> Result<ResourceId, AnyError>
|
||||
) -> Result<ResourceId, CronError>
|
||||
where
|
||||
C: CronHandler + 'static,
|
||||
{
|
||||
|
@ -90,7 +109,7 @@ async fn op_cron_next<C>(
|
|||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] rid: ResourceId,
|
||||
prev_success: bool,
|
||||
) -> Result<bool, AnyError>
|
||||
) -> Result<bool, CronError>
|
||||
where
|
||||
C: CronHandler + 'static,
|
||||
{
|
||||
|
@ -102,7 +121,7 @@ where
|
|||
if get_custom_error_class(&err) == Some("BadResource") {
|
||||
return Ok(false);
|
||||
} else {
|
||||
return Err(err);
|
||||
return Err(CronError::Resource(err));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -112,17 +131,14 @@ where
|
|||
cron_handler.next(prev_success).await
|
||||
}
|
||||
|
||||
fn validate_cron_name(name: &str) -> Result<(), AnyError> {
|
||||
fn validate_cron_name(name: &str) -> Result<(), CronError> {
|
||||
if name.len() > 64 {
|
||||
return Err(type_error(format!(
|
||||
"Cron name cannot exceed 64 characters: current length {}",
|
||||
name.len()
|
||||
)));
|
||||
return Err(CronError::NameExceeded(name.len()));
|
||||
}
|
||||
if !name.chars().all(|c| {
|
||||
c.is_ascii_whitespace() || c.is_ascii_alphanumeric() || c == '_' || c == '-'
|
||||
}) {
|
||||
return Err(type_error("Invalid cron name: only alphanumeric characters, whitespace, hyphens, and underscores are allowed"));
|
||||
return Err(CronError::NameInvalid);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ use std::rc::Weak;
|
|||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::unsync::spawn;
|
||||
|
@ -21,6 +19,7 @@ use tokio::sync::mpsc::WeakSender;
|
|||
use tokio::sync::OwnedSemaphorePermit;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::CronError;
|
||||
use crate::CronHandle;
|
||||
use crate::CronHandler;
|
||||
use crate::CronSpec;
|
||||
|
@ -81,7 +80,7 @@ impl LocalCronHandler {
|
|||
async fn cron_loop(
|
||||
runtime_state: Rc<RefCell<RuntimeState>>,
|
||||
mut cron_schedule_rx: mpsc::Receiver<(String, bool)>,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), CronError> {
|
||||
loop {
|
||||
let earliest_deadline = runtime_state
|
||||
.borrow()
|
||||
|
@ -154,7 +153,7 @@ impl LocalCronHandler {
|
|||
impl RuntimeState {
|
||||
fn get_ready_crons(
|
||||
&mut self,
|
||||
) -> Result<Vec<(String, WeakSender<()>)>, AnyError> {
|
||||
) -> Result<Vec<(String, WeakSender<()>)>, CronError> {
|
||||
let now = chrono::Utc::now().timestamp_millis() as u64;
|
||||
|
||||
let ready = {
|
||||
|
@ -191,7 +190,7 @@ impl RuntimeState {
|
|||
impl CronHandler for LocalCronHandler {
|
||||
type EH = CronExecutionHandle;
|
||||
|
||||
fn create(&self, spec: CronSpec) -> Result<Self::EH, AnyError> {
|
||||
fn create(&self, spec: CronSpec) -> Result<Self::EH, CronError> {
|
||||
// Ensure that the cron loop is started.
|
||||
self.cron_loop_join_handle.get_or_init(|| {
|
||||
let (cron_schedule_tx, cron_schedule_rx) =
|
||||
|
@ -208,17 +207,17 @@ impl CronHandler for LocalCronHandler {
|
|||
let mut runtime_state = self.runtime_state.borrow_mut();
|
||||
|
||||
if runtime_state.crons.len() > MAX_CRONS {
|
||||
return Err(type_error("Too many crons"));
|
||||
return Err(CronError::TooManyCrons);
|
||||
}
|
||||
if runtime_state.crons.contains_key(&spec.name) {
|
||||
return Err(type_error("Cron with this name already exists"));
|
||||
return Err(CronError::AlreadyExists);
|
||||
}
|
||||
|
||||
// Validate schedule expression.
|
||||
spec
|
||||
.cron_schedule
|
||||
.parse::<saffron::Cron>()
|
||||
.map_err(|_| type_error("Invalid cron schedule"))?;
|
||||
.map_err(|_| CronError::InvalidCron)?;
|
||||
|
||||
// Validate backoff_schedule.
|
||||
if let Some(backoff_schedule) = &spec.backoff_schedule {
|
||||
|
@ -263,7 +262,7 @@ struct Inner {
|
|||
|
||||
#[async_trait(?Send)]
|
||||
impl CronHandle for CronExecutionHandle {
|
||||
async fn next(&self, prev_success: bool) -> Result<bool, AnyError> {
|
||||
async fn next(&self, prev_success: bool) -> Result<bool, CronError> {
|
||||
self.inner.borrow_mut().permit.take();
|
||||
|
||||
if self
|
||||
|
@ -300,7 +299,7 @@ impl CronHandle for CronExecutionHandle {
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_next_deadline(cron_expression: &str) -> Result<u64, AnyError> {
|
||||
fn compute_next_deadline(cron_expression: &str) -> Result<u64, CronError> {
|
||||
let now = chrono::Utc::now();
|
||||
|
||||
if let Ok(test_schedule) = env::var("DENO_CRON_TEST_SCHEDULE_OFFSET") {
|
||||
|
@ -311,19 +310,21 @@ fn compute_next_deadline(cron_expression: &str) -> Result<u64, AnyError> {
|
|||
|
||||
let cron = cron_expression
|
||||
.parse::<saffron::Cron>()
|
||||
.map_err(|_| anyhow::anyhow!("invalid cron expression"))?;
|
||||
.map_err(|_| CronError::InvalidCron)?;
|
||||
let Some(next_deadline) = cron.next_after(now) else {
|
||||
return Err(anyhow::anyhow!("invalid cron expression"));
|
||||
return Err(CronError::InvalidCron);
|
||||
};
|
||||
Ok(next_deadline.timestamp_millis() as u64)
|
||||
}
|
||||
|
||||
fn validate_backoff_schedule(backoff_schedule: &[u32]) -> Result<(), AnyError> {
|
||||
fn validate_backoff_schedule(
|
||||
backoff_schedule: &[u32],
|
||||
) -> Result<(), CronError> {
|
||||
if backoff_schedule.len() > MAX_BACKOFF_COUNT {
|
||||
return Err(type_error("Invalid backoff schedule"));
|
||||
return Err(CronError::InvalidBackoff);
|
||||
}
|
||||
if backoff_schedule.iter().any(|s| *s > MAX_BACKOFF_MS) {
|
||||
return Err(type_error("Invalid backoff schedule"));
|
||||
return Err(CronError::InvalidBackoff);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_crypto"
|
||||
version = "0.185.0"
|
||||
version = "0.187.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -41,5 +41,7 @@ sha1.workspace = true
|
|||
sha2.workspace = true
|
||||
signature.workspace = true
|
||||
spki.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
uuid.workspace = true
|
||||
x25519-dalek = "2.0.0"
|
||||
|
|
|
@ -16,9 +16,6 @@ use ctr::cipher::StreamCipher;
|
|||
use ctr::Ctr128BE;
|
||||
use ctr::Ctr32BE;
|
||||
use ctr::Ctr64BE;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::unsync::spawn_blocking;
|
||||
use deno_core::JsBuffer;
|
||||
|
@ -73,12 +70,36 @@ pub enum DecryptAlgorithm {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DecryptError {
|
||||
#[error(transparent)]
|
||||
General(#[from] SharedError),
|
||||
#[error(transparent)]
|
||||
Pkcs1(#[from] rsa::pkcs1::Error),
|
||||
#[error("Decryption failed")]
|
||||
Failed,
|
||||
#[error("invalid length")]
|
||||
InvalidLength,
|
||||
#[error("invalid counter length. Currently supported 32/64/128 bits")]
|
||||
InvalidCounterLength,
|
||||
#[error("tag length not equal to 128")]
|
||||
InvalidTagLength,
|
||||
#[error("invalid key or iv")]
|
||||
InvalidKeyOrIv,
|
||||
#[error("tried to decrypt too much data")]
|
||||
TooMuchData,
|
||||
#[error("iv length not equal to 12 or 16")]
|
||||
InvalidIvLength,
|
||||
#[error("{0}")]
|
||||
Rsa(rsa::Error),
|
||||
}
|
||||
|
||||
#[op2(async)]
|
||||
#[serde]
|
||||
pub async fn op_crypto_decrypt(
|
||||
#[serde] opts: DecryptOptions,
|
||||
#[buffer] data: JsBuffer,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, DecryptError> {
|
||||
let key = opts.key;
|
||||
let fun = move || match opts.algorithm {
|
||||
DecryptAlgorithm::RsaOaep { hash, label } => {
|
||||
|
@ -108,7 +129,7 @@ fn decrypt_rsa_oaep(
|
|||
hash: ShaHash,
|
||||
label: Vec<u8>,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, deno_core::anyhow::Error> {
|
||||
) -> Result<Vec<u8>, DecryptError> {
|
||||
let key = key.as_rsa_private_key()?;
|
||||
|
||||
let private_key = rsa::RsaPrivateKey::from_pkcs1_der(key)?;
|
||||
|
@ -139,7 +160,7 @@ fn decrypt_rsa_oaep(
|
|||
|
||||
private_key
|
||||
.decrypt(padding, data)
|
||||
.map_err(|e| custom_error("DOMExceptionOperationError", e.to_string()))
|
||||
.map_err(DecryptError::Rsa)
|
||||
}
|
||||
|
||||
fn decrypt_aes_cbc(
|
||||
|
@ -147,7 +168,7 @@ fn decrypt_aes_cbc(
|
|||
length: usize,
|
||||
iv: Vec<u8>,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, deno_core::anyhow::Error> {
|
||||
) -> Result<Vec<u8>, DecryptError> {
|
||||
let key = key.as_secret_key()?;
|
||||
|
||||
// 2.
|
||||
|
@ -155,53 +176,32 @@ fn decrypt_aes_cbc(
|
|||
128 => {
|
||||
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
|
||||
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
|
||||
let cipher = Aes128CbcDec::new_from_slices(key, &iv).map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"Invalid key or iv".to_string(),
|
||||
)
|
||||
})?;
|
||||
let cipher = Aes128CbcDec::new_from_slices(key, &iv)
|
||||
.map_err(|_| DecryptError::InvalidKeyOrIv)?;
|
||||
|
||||
cipher.decrypt_padded_vec_mut::<Pkcs7>(data).map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"Decryption failed".to_string(),
|
||||
)
|
||||
})?
|
||||
cipher
|
||||
.decrypt_padded_vec_mut::<Pkcs7>(data)
|
||||
.map_err(|_| DecryptError::Failed)?
|
||||
}
|
||||
192 => {
|
||||
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
|
||||
type Aes192CbcDec = cbc::Decryptor<aes::Aes192>;
|
||||
let cipher = Aes192CbcDec::new_from_slices(key, &iv).map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"Invalid key or iv".to_string(),
|
||||
)
|
||||
})?;
|
||||
let cipher = Aes192CbcDec::new_from_slices(key, &iv)
|
||||
.map_err(|_| DecryptError::InvalidKeyOrIv)?;
|
||||
|
||||
cipher.decrypt_padded_vec_mut::<Pkcs7>(data).map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"Decryption failed".to_string(),
|
||||
)
|
||||
})?
|
||||
cipher
|
||||
.decrypt_padded_vec_mut::<Pkcs7>(data)
|
||||
.map_err(|_| DecryptError::Failed)?
|
||||
}
|
||||
256 => {
|
||||
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
|
||||
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
|
||||
let cipher = Aes256CbcDec::new_from_slices(key, &iv).map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"Invalid key or iv".to_string(),
|
||||
)
|
||||
})?;
|
||||
let cipher = Aes256CbcDec::new_from_slices(key, &iv)
|
||||
.map_err(|_| DecryptError::InvalidKeyOrIv)?;
|
||||
|
||||
cipher.decrypt_padded_vec_mut::<Pkcs7>(data).map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"Decryption failed".to_string(),
|
||||
)
|
||||
})?
|
||||
cipher
|
||||
.decrypt_padded_vec_mut::<Pkcs7>(data)
|
||||
.map_err(|_| DecryptError::Failed)?
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
@ -214,7 +214,7 @@ fn decrypt_aes_ctr_gen<B>(
|
|||
key: &[u8],
|
||||
counter: &[u8],
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, AnyError>
|
||||
) -> Result<Vec<u8>, DecryptError>
|
||||
where
|
||||
B: KeyIvInit + StreamCipher,
|
||||
{
|
||||
|
@ -223,7 +223,7 @@ where
|
|||
let mut plaintext = data.to_vec();
|
||||
cipher
|
||||
.try_apply_keystream(&mut plaintext)
|
||||
.map_err(|_| operation_error("tried to decrypt too much data"))?;
|
||||
.map_err(|_| DecryptError::TooMuchData)?;
|
||||
|
||||
Ok(plaintext)
|
||||
}
|
||||
|
@ -235,12 +235,12 @@ fn decrypt_aes_gcm_gen<N: ArrayLength<u8>>(
|
|||
length: usize,
|
||||
additional_data: Vec<u8>,
|
||||
plaintext: &mut [u8],
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), DecryptError> {
|
||||
let nonce = Nonce::from_slice(nonce);
|
||||
match length {
|
||||
128 => {
|
||||
let cipher = aes_gcm::AesGcm::<Aes128, N>::new_from_slice(key)
|
||||
.map_err(|_| operation_error("Decryption failed"))?;
|
||||
.map_err(|_| DecryptError::Failed)?;
|
||||
cipher
|
||||
.decrypt_in_place_detached(
|
||||
nonce,
|
||||
|
@ -248,11 +248,11 @@ fn decrypt_aes_gcm_gen<N: ArrayLength<u8>>(
|
|||
plaintext,
|
||||
tag,
|
||||
)
|
||||
.map_err(|_| operation_error("Decryption failed"))?
|
||||
.map_err(|_| DecryptError::Failed)?
|
||||
}
|
||||
192 => {
|
||||
let cipher = aes_gcm::AesGcm::<Aes192, N>::new_from_slice(key)
|
||||
.map_err(|_| operation_error("Decryption failed"))?;
|
||||
.map_err(|_| DecryptError::Failed)?;
|
||||
cipher
|
||||
.decrypt_in_place_detached(
|
||||
nonce,
|
||||
|
@ -260,11 +260,11 @@ fn decrypt_aes_gcm_gen<N: ArrayLength<u8>>(
|
|||
plaintext,
|
||||
tag,
|
||||
)
|
||||
.map_err(|_| operation_error("Decryption failed"))?
|
||||
.map_err(|_| DecryptError::Failed)?
|
||||
}
|
||||
256 => {
|
||||
let cipher = aes_gcm::AesGcm::<Aes256, N>::new_from_slice(key)
|
||||
.map_err(|_| operation_error("Decryption failed"))?;
|
||||
.map_err(|_| DecryptError::Failed)?;
|
||||
cipher
|
||||
.decrypt_in_place_detached(
|
||||
nonce,
|
||||
|
@ -272,9 +272,9 @@ fn decrypt_aes_gcm_gen<N: ArrayLength<u8>>(
|
|||
plaintext,
|
||||
tag,
|
||||
)
|
||||
.map_err(|_| operation_error("Decryption failed"))?
|
||||
.map_err(|_| DecryptError::Failed)?
|
||||
}
|
||||
_ => return Err(type_error("invalid length")),
|
||||
_ => return Err(DecryptError::InvalidLength),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
@ -286,7 +286,7 @@ fn decrypt_aes_ctr(
|
|||
counter: &[u8],
|
||||
ctr_length: usize,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, deno_core::anyhow::Error> {
|
||||
) -> Result<Vec<u8>, DecryptError> {
|
||||
let key = key.as_secret_key()?;
|
||||
|
||||
match ctr_length {
|
||||
|
@ -294,23 +294,21 @@ fn decrypt_aes_ctr(
|
|||
128 => decrypt_aes_ctr_gen::<Ctr32BE<aes::Aes128>>(key, counter, data),
|
||||
192 => decrypt_aes_ctr_gen::<Ctr32BE<aes::Aes192>>(key, counter, data),
|
||||
256 => decrypt_aes_ctr_gen::<Ctr32BE<aes::Aes256>>(key, counter, data),
|
||||
_ => Err(type_error("invalid length")),
|
||||
_ => Err(DecryptError::InvalidLength),
|
||||
},
|
||||
64 => match key_length {
|
||||
128 => decrypt_aes_ctr_gen::<Ctr64BE<aes::Aes128>>(key, counter, data),
|
||||
192 => decrypt_aes_ctr_gen::<Ctr64BE<aes::Aes192>>(key, counter, data),
|
||||
256 => decrypt_aes_ctr_gen::<Ctr64BE<aes::Aes256>>(key, counter, data),
|
||||
_ => Err(type_error("invalid length")),
|
||||
_ => Err(DecryptError::InvalidLength),
|
||||
},
|
||||
128 => match key_length {
|
||||
128 => decrypt_aes_ctr_gen::<Ctr128BE<aes::Aes128>>(key, counter, data),
|
||||
192 => decrypt_aes_ctr_gen::<Ctr128BE<aes::Aes192>>(key, counter, data),
|
||||
256 => decrypt_aes_ctr_gen::<Ctr128BE<aes::Aes256>>(key, counter, data),
|
||||
_ => Err(type_error("invalid length")),
|
||||
_ => Err(DecryptError::InvalidLength),
|
||||
},
|
||||
_ => Err(type_error(
|
||||
"invalid counter length. Currently supported 32/64/128 bits",
|
||||
)),
|
||||
_ => Err(DecryptError::InvalidCounterLength),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,7 +319,7 @@ fn decrypt_aes_gcm(
|
|||
iv: Vec<u8>,
|
||||
additional_data: Option<Vec<u8>>,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
) -> Result<Vec<u8>, DecryptError> {
|
||||
let key = key.as_secret_key()?;
|
||||
let additional_data = additional_data.unwrap_or_default();
|
||||
|
||||
|
@ -330,7 +328,7 @@ fn decrypt_aes_gcm(
|
|||
// Note that encryption won't fail, it instead truncates the tag
|
||||
// to the specified tag length as specified in the spec.
|
||||
if tag_length != 128 {
|
||||
return Err(type_error("tag length not equal to 128"));
|
||||
return Err(DecryptError::InvalidTagLength);
|
||||
}
|
||||
|
||||
let sep = data.len() - (tag_length / 8);
|
||||
|
@ -357,7 +355,7 @@ fn decrypt_aes_gcm(
|
|||
additional_data,
|
||||
&mut plaintext,
|
||||
)?,
|
||||
_ => return Err(type_error("iv length not equal to 12 or 16")),
|
||||
_ => return Err(DecryptError::InvalidIvLength),
|
||||
}
|
||||
|
||||
Ok(plaintext)
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
|
||||
use base64::Engine;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::ToJsBuffer;
|
||||
use elliptic_curve::pkcs8::PrivateKeyInfo;
|
||||
|
@ -15,6 +13,16 @@ use spki::der::asn1::BitString;
|
|||
use spki::der::Decode;
|
||||
use spki::der::Encode;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Ed25519Error {
|
||||
#[error("Failed to export key")]
|
||||
FailedExport,
|
||||
#[error(transparent)]
|
||||
Der(#[from] rsa::pkcs1::der::Error),
|
||||
#[error(transparent)]
|
||||
KeyRejected(#[from] ring::error::KeyRejected),
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
pub fn op_crypto_generate_ed25519_keypair(
|
||||
#[buffer] pkey: &mut [u8],
|
||||
|
@ -116,7 +124,7 @@ pub fn op_crypto_import_pkcs8_ed25519(
|
|||
#[serde]
|
||||
pub fn op_crypto_export_spki_ed25519(
|
||||
#[buffer] pubkey: &[u8],
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, Ed25519Error> {
|
||||
let key_info = spki::SubjectPublicKeyInfo {
|
||||
algorithm: spki::AlgorithmIdentifierOwned {
|
||||
// id-Ed25519
|
||||
|
@ -128,9 +136,7 @@ pub fn op_crypto_export_spki_ed25519(
|
|||
Ok(
|
||||
key_info
|
||||
.to_der()
|
||||
.map_err(|_| {
|
||||
custom_error("DOMExceptionOperationError", "Failed to export key")
|
||||
})?
|
||||
.map_err(|_| Ed25519Error::FailedExport)?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
@ -139,7 +145,7 @@ pub fn op_crypto_export_spki_ed25519(
|
|||
#[serde]
|
||||
pub fn op_crypto_export_pkcs8_ed25519(
|
||||
#[buffer] pkey: &[u8],
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, Ed25519Error> {
|
||||
use rsa::pkcs1::der::Encode;
|
||||
|
||||
// This should probably use OneAsymmetricKey instead
|
||||
|
@ -164,7 +170,7 @@ pub fn op_crypto_export_pkcs8_ed25519(
|
|||
#[string]
|
||||
pub fn op_crypto_jwk_x_ed25519(
|
||||
#[buffer] pkey: &[u8],
|
||||
) -> Result<String, AnyError> {
|
||||
) -> Result<String, Ed25519Error> {
|
||||
let pair = Ed25519KeyPair::from_seed_unchecked(pkey)?;
|
||||
Ok(BASE64_URL_SAFE_NO_PAD.encode(pair.public_key().as_ref()))
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@ use aes_gcm::Nonce;
|
|||
use ctr::Ctr128BE;
|
||||
use ctr::Ctr32BE;
|
||||
use ctr::Ctr64BE;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::unsync::spawn_blocking;
|
||||
use deno_core::JsBuffer;
|
||||
|
@ -73,12 +71,30 @@ pub enum EncryptAlgorithm {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum EncryptError {
|
||||
#[error(transparent)]
|
||||
General(#[from] SharedError),
|
||||
#[error("invalid length")]
|
||||
InvalidLength,
|
||||
#[error("invalid key or iv")]
|
||||
InvalidKeyOrIv,
|
||||
#[error("iv length not equal to 12 or 16")]
|
||||
InvalidIvLength,
|
||||
#[error("invalid counter length. Currently supported 32/64/128 bits")]
|
||||
InvalidCounterLength,
|
||||
#[error("tried to encrypt too much data")]
|
||||
TooMuchData,
|
||||
#[error("Encryption failed")]
|
||||
Failed,
|
||||
}
|
||||
|
||||
#[op2(async)]
|
||||
#[serde]
|
||||
pub async fn op_crypto_encrypt(
|
||||
#[serde] opts: EncryptOptions,
|
||||
#[buffer] data: JsBuffer,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, EncryptError> {
|
||||
let key = opts.key;
|
||||
let fun = move || match opts.algorithm {
|
||||
EncryptAlgorithm::RsaOaep { hash, label } => {
|
||||
|
@ -108,12 +124,12 @@ fn encrypt_rsa_oaep(
|
|||
hash: ShaHash,
|
||||
label: Vec<u8>,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
) -> Result<Vec<u8>, EncryptError> {
|
||||
let label = String::from_utf8_lossy(&label).to_string();
|
||||
|
||||
let public_key = key.as_rsa_public_key()?;
|
||||
let public_key = rsa::RsaPublicKey::from_pkcs1_der(&public_key)
|
||||
.map_err(|_| operation_error("failed to decode public key"))?;
|
||||
.map_err(|_| SharedError::FailedDecodePublicKey)?;
|
||||
let mut rng = OsRng;
|
||||
let padding = match hash {
|
||||
ShaHash::Sha1 => rsa::Oaep {
|
||||
|
@ -139,7 +155,7 @@ fn encrypt_rsa_oaep(
|
|||
};
|
||||
let encrypted = public_key
|
||||
.encrypt(&mut rng, padding, data)
|
||||
.map_err(|_| operation_error("Encryption failed"))?;
|
||||
.map_err(|_| EncryptError::Failed)?;
|
||||
Ok(encrypted)
|
||||
}
|
||||
|
||||
|
@ -148,7 +164,7 @@ fn encrypt_aes_cbc(
|
|||
length: usize,
|
||||
iv: Vec<u8>,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
) -> Result<Vec<u8>, EncryptError> {
|
||||
let key = key.as_secret_key()?;
|
||||
let ciphertext = match length {
|
||||
128 => {
|
||||
|
@ -156,7 +172,7 @@ fn encrypt_aes_cbc(
|
|||
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
|
||||
|
||||
let cipher = Aes128CbcEnc::new_from_slices(key, &iv)
|
||||
.map_err(|_| operation_error("invalid key or iv".to_string()))?;
|
||||
.map_err(|_| EncryptError::InvalidKeyOrIv)?;
|
||||
cipher.encrypt_padded_vec_mut::<Pkcs7>(data)
|
||||
}
|
||||
192 => {
|
||||
|
@ -164,7 +180,7 @@ fn encrypt_aes_cbc(
|
|||
type Aes192CbcEnc = cbc::Encryptor<aes::Aes192>;
|
||||
|
||||
let cipher = Aes192CbcEnc::new_from_slices(key, &iv)
|
||||
.map_err(|_| operation_error("invalid key or iv".to_string()))?;
|
||||
.map_err(|_| EncryptError::InvalidKeyOrIv)?;
|
||||
cipher.encrypt_padded_vec_mut::<Pkcs7>(data)
|
||||
}
|
||||
256 => {
|
||||
|
@ -172,10 +188,10 @@ fn encrypt_aes_cbc(
|
|||
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
||||
|
||||
let cipher = Aes256CbcEnc::new_from_slices(key, &iv)
|
||||
.map_err(|_| operation_error("invalid key or iv".to_string()))?;
|
||||
.map_err(|_| EncryptError::InvalidKeyOrIv)?;
|
||||
cipher.encrypt_padded_vec_mut::<Pkcs7>(data)
|
||||
}
|
||||
_ => return Err(type_error("invalid length")),
|
||||
_ => return Err(EncryptError::InvalidLength),
|
||||
};
|
||||
Ok(ciphertext)
|
||||
}
|
||||
|
@ -186,31 +202,31 @@ fn encrypt_aes_gcm_general<N: ArrayLength<u8>>(
|
|||
length: usize,
|
||||
ciphertext: &mut [u8],
|
||||
additional_data: Vec<u8>,
|
||||
) -> Result<aes_gcm::Tag, AnyError> {
|
||||
) -> Result<aes_gcm::Tag, EncryptError> {
|
||||
let nonce = Nonce::<N>::from_slice(&iv);
|
||||
let tag = match length {
|
||||
128 => {
|
||||
let cipher = aes_gcm::AesGcm::<Aes128, N>::new_from_slice(key)
|
||||
.map_err(|_| operation_error("Encryption failed"))?;
|
||||
.map_err(|_| EncryptError::Failed)?;
|
||||
cipher
|
||||
.encrypt_in_place_detached(nonce, &additional_data, ciphertext)
|
||||
.map_err(|_| operation_error("Encryption failed"))?
|
||||
.map_err(|_| EncryptError::Failed)?
|
||||
}
|
||||
192 => {
|
||||
let cipher = aes_gcm::AesGcm::<Aes192, N>::new_from_slice(key)
|
||||
.map_err(|_| operation_error("Encryption failed"))?;
|
||||
.map_err(|_| EncryptError::Failed)?;
|
||||
cipher
|
||||
.encrypt_in_place_detached(nonce, &additional_data, ciphertext)
|
||||
.map_err(|_| operation_error("Encryption failed"))?
|
||||
.map_err(|_| EncryptError::Failed)?
|
||||
}
|
||||
256 => {
|
||||
let cipher = aes_gcm::AesGcm::<Aes256, N>::new_from_slice(key)
|
||||
.map_err(|_| operation_error("Encryption failed"))?;
|
||||
.map_err(|_| EncryptError::Failed)?;
|
||||
cipher
|
||||
.encrypt_in_place_detached(nonce, &additional_data, ciphertext)
|
||||
.map_err(|_| operation_error("Encryption failed"))?
|
||||
.map_err(|_| EncryptError::Failed)?
|
||||
}
|
||||
_ => return Err(type_error("invalid length")),
|
||||
_ => return Err(EncryptError::InvalidLength),
|
||||
};
|
||||
|
||||
Ok(tag)
|
||||
|
@ -223,7 +239,7 @@ fn encrypt_aes_gcm(
|
|||
iv: Vec<u8>,
|
||||
additional_data: Option<Vec<u8>>,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
) -> Result<Vec<u8>, EncryptError> {
|
||||
let key = key.as_secret_key()?;
|
||||
let additional_data = additional_data.unwrap_or_default();
|
||||
|
||||
|
@ -244,7 +260,7 @@ fn encrypt_aes_gcm(
|
|||
&mut ciphertext,
|
||||
additional_data,
|
||||
)?,
|
||||
_ => return Err(type_error("iv length not equal to 12 or 16")),
|
||||
_ => return Err(EncryptError::InvalidIvLength),
|
||||
};
|
||||
|
||||
// Truncated tag to the specified tag length.
|
||||
|
@ -261,7 +277,7 @@ fn encrypt_aes_ctr_gen<B>(
|
|||
key: &[u8],
|
||||
counter: &[u8],
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, AnyError>
|
||||
) -> Result<Vec<u8>, EncryptError>
|
||||
where
|
||||
B: KeyIvInit + StreamCipher,
|
||||
{
|
||||
|
@ -270,7 +286,7 @@ where
|
|||
let mut ciphertext = data.to_vec();
|
||||
cipher
|
||||
.try_apply_keystream(&mut ciphertext)
|
||||
.map_err(|_| operation_error("tried to encrypt too much data"))?;
|
||||
.map_err(|_| EncryptError::TooMuchData)?;
|
||||
|
||||
Ok(ciphertext)
|
||||
}
|
||||
|
@ -281,7 +297,7 @@ fn encrypt_aes_ctr(
|
|||
counter: &[u8],
|
||||
ctr_length: usize,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
) -> Result<Vec<u8>, EncryptError> {
|
||||
let key = key.as_secret_key()?;
|
||||
|
||||
match ctr_length {
|
||||
|
@ -289,22 +305,20 @@ fn encrypt_aes_ctr(
|
|||
128 => encrypt_aes_ctr_gen::<Ctr32BE<aes::Aes128>>(key, counter, data),
|
||||
192 => encrypt_aes_ctr_gen::<Ctr32BE<aes::Aes192>>(key, counter, data),
|
||||
256 => encrypt_aes_ctr_gen::<Ctr32BE<aes::Aes256>>(key, counter, data),
|
||||
_ => Err(type_error("invalid length")),
|
||||
_ => Err(EncryptError::InvalidLength),
|
||||
},
|
||||
64 => match key_length {
|
||||
128 => encrypt_aes_ctr_gen::<Ctr64BE<aes::Aes128>>(key, counter, data),
|
||||
192 => encrypt_aes_ctr_gen::<Ctr64BE<aes::Aes192>>(key, counter, data),
|
||||
256 => encrypt_aes_ctr_gen::<Ctr64BE<aes::Aes256>>(key, counter, data),
|
||||
_ => Err(type_error("invalid length")),
|
||||
_ => Err(EncryptError::InvalidLength),
|
||||
},
|
||||
128 => match key_length {
|
||||
128 => encrypt_aes_ctr_gen::<Ctr128BE<aes::Aes128>>(key, counter, data),
|
||||
192 => encrypt_aes_ctr_gen::<Ctr128BE<aes::Aes192>>(key, counter, data),
|
||||
256 => encrypt_aes_ctr_gen::<Ctr128BE<aes::Aes256>>(key, counter, data),
|
||||
_ => Err(type_error("invalid length")),
|
||||
_ => Err(EncryptError::InvalidLength),
|
||||
},
|
||||
_ => Err(type_error(
|
||||
"invalid counter length. Currently supported 32/64/128 bits",
|
||||
)),
|
||||
_ => Err(EncryptError::InvalidCounterLength),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ use base64::prelude::BASE64_URL_SAFE_NO_PAD;
|
|||
use base64::Engine;
|
||||
use const_oid::AssociatedOid;
|
||||
use const_oid::ObjectIdentifier;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::ToJsBuffer;
|
||||
use elliptic_curve::sec1::ToEncodedPoint;
|
||||
|
@ -22,6 +20,16 @@ use spki::AlgorithmIdentifierOwned;
|
|||
|
||||
use crate::shared::*;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ExportKeyError {
|
||||
#[error(transparent)]
|
||||
General(#[from] SharedError),
|
||||
#[error(transparent)]
|
||||
Der(#[from] spki::der::Error),
|
||||
#[error("Unsupported named curve")]
|
||||
UnsupportedNamedCurve,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExportKeyOptions {
|
||||
|
@ -99,7 +107,7 @@ pub enum ExportKeyResult {
|
|||
pub fn op_crypto_export_key(
|
||||
#[serde] opts: ExportKeyOptions,
|
||||
#[serde] key_data: V8RawKeyData,
|
||||
) -> Result<ExportKeyResult, AnyError> {
|
||||
) -> Result<ExportKeyResult, ExportKeyError> {
|
||||
match opts.algorithm {
|
||||
ExportKeyAlgorithm::RsassaPkcs1v15 {}
|
||||
| ExportKeyAlgorithm::RsaPss {}
|
||||
|
@ -125,7 +133,7 @@ fn bytes_to_b64(bytes: &[u8]) -> String {
|
|||
fn export_key_rsa(
|
||||
format: ExportKeyFormat,
|
||||
key_data: V8RawKeyData,
|
||||
) -> Result<ExportKeyResult, deno_core::anyhow::Error> {
|
||||
) -> Result<ExportKeyResult, ExportKeyError> {
|
||||
match format {
|
||||
ExportKeyFormat::Spki => {
|
||||
let subject_public_key = &key_data.as_rsa_public_key()?;
|
||||
|
@ -181,12 +189,7 @@ fn export_key_rsa(
|
|||
ExportKeyFormat::JwkPublic => {
|
||||
let public_key = key_data.as_rsa_public_key()?;
|
||||
let public_key = rsa::pkcs1::RsaPublicKey::from_der(&public_key)
|
||||
.map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"failed to decode public key",
|
||||
)
|
||||
})?;
|
||||
.map_err(|_| SharedError::FailedDecodePublicKey)?;
|
||||
|
||||
Ok(ExportKeyResult::JwkPublicRsa {
|
||||
n: uint_to_b64(public_key.modulus),
|
||||
|
@ -196,12 +199,7 @@ fn export_key_rsa(
|
|||
ExportKeyFormat::JwkPrivate => {
|
||||
let private_key = key_data.as_rsa_private_key()?;
|
||||
let private_key = rsa::pkcs1::RsaPrivateKey::from_der(private_key)
|
||||
.map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"failed to decode private key",
|
||||
)
|
||||
})?;
|
||||
.map_err(|_| SharedError::FailedDecodePrivateKey)?;
|
||||
|
||||
Ok(ExportKeyResult::JwkPrivateRsa {
|
||||
n: uint_to_b64(private_key.modulus),
|
||||
|
@ -214,14 +212,14 @@ fn export_key_rsa(
|
|||
qi: uint_to_b64(private_key.coefficient),
|
||||
})
|
||||
}
|
||||
_ => Err(unsupported_format()),
|
||||
_ => Err(SharedError::UnsupportedFormat.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn export_key_symmetric(
|
||||
format: ExportKeyFormat,
|
||||
key_data: V8RawKeyData,
|
||||
) -> Result<ExportKeyResult, deno_core::anyhow::Error> {
|
||||
) -> Result<ExportKeyResult, ExportKeyError> {
|
||||
match format {
|
||||
ExportKeyFormat::JwkSecret => {
|
||||
let bytes = key_data.as_secret_key()?;
|
||||
|
@ -230,7 +228,7 @@ fn export_key_symmetric(
|
|||
k: bytes_to_b64(bytes),
|
||||
})
|
||||
}
|
||||
_ => Err(unsupported_format()),
|
||||
_ => Err(SharedError::UnsupportedFormat.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,7 +237,7 @@ fn export_key_ec(
|
|||
key_data: V8RawKeyData,
|
||||
algorithm: ExportKeyAlgorithm,
|
||||
named_curve: EcNamedCurve,
|
||||
) -> Result<ExportKeyResult, deno_core::anyhow::Error> {
|
||||
) -> Result<ExportKeyResult, ExportKeyError> {
|
||||
match format {
|
||||
ExportKeyFormat::Raw => {
|
||||
let subject_public_key = match named_curve {
|
||||
|
@ -332,10 +330,7 @@ fn export_key_ec(
|
|||
y: bytes_to_b64(y),
|
||||
})
|
||||
} else {
|
||||
Err(custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"failed to decode public key",
|
||||
))
|
||||
Err(SharedError::FailedDecodePublicKey.into())
|
||||
}
|
||||
}
|
||||
EcNamedCurve::P384 => {
|
||||
|
@ -350,10 +345,7 @@ fn export_key_ec(
|
|||
y: bytes_to_b64(y),
|
||||
})
|
||||
} else {
|
||||
Err(custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"failed to decode public key",
|
||||
))
|
||||
Err(SharedError::FailedDecodePublicKey.into())
|
||||
}
|
||||
}
|
||||
EcNamedCurve::P521 => {
|
||||
|
@ -368,10 +360,7 @@ fn export_key_ec(
|
|||
y: bytes_to_b64(y),
|
||||
})
|
||||
} else {
|
||||
Err(custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"failed to decode public key",
|
||||
))
|
||||
Err(SharedError::FailedDecodePublicKey.into())
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -380,13 +369,8 @@ fn export_key_ec(
|
|||
|
||||
match named_curve {
|
||||
EcNamedCurve::P256 => {
|
||||
let ec_key =
|
||||
p256::SecretKey::from_pkcs8_der(private_key).map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"failed to decode private key",
|
||||
)
|
||||
})?;
|
||||
let ec_key = p256::SecretKey::from_pkcs8_der(private_key)
|
||||
.map_err(|_| SharedError::FailedDecodePrivateKey)?;
|
||||
|
||||
let point = ec_key.public_key().to_encoded_point(false);
|
||||
if let elliptic_curve::sec1::Coordinates::Uncompressed { x, y } =
|
||||
|
@ -398,18 +382,13 @@ fn export_key_ec(
|
|||
d: bytes_to_b64(&ec_key.to_bytes()),
|
||||
})
|
||||
} else {
|
||||
Err(data_error("expected valid public EC key"))
|
||||
Err(SharedError::ExpectedValidPublicECKey.into())
|
||||
}
|
||||
}
|
||||
|
||||
EcNamedCurve::P384 => {
|
||||
let ec_key =
|
||||
p384::SecretKey::from_pkcs8_der(private_key).map_err(|_| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"failed to decode private key",
|
||||
)
|
||||
})?;
|
||||
let ec_key = p384::SecretKey::from_pkcs8_der(private_key)
|
||||
.map_err(|_| SharedError::FailedDecodePrivateKey)?;
|
||||
|
||||
let point = ec_key.public_key().to_encoded_point(false);
|
||||
if let elliptic_curve::sec1::Coordinates::Uncompressed { x, y } =
|
||||
|
@ -421,12 +400,12 @@ fn export_key_ec(
|
|||
d: bytes_to_b64(&ec_key.to_bytes()),
|
||||
})
|
||||
} else {
|
||||
Err(data_error("expected valid public EC key"))
|
||||
Err(SharedError::ExpectedValidPublicECKey.into())
|
||||
}
|
||||
}
|
||||
_ => Err(not_supported_error("Unsupported namedCurve")),
|
||||
_ => Err(ExportKeyError::UnsupportedNamedCurve),
|
||||
}
|
||||
}
|
||||
ExportKeyFormat::JwkSecret => Err(unsupported_format()),
|
||||
ExportKeyFormat::JwkSecret => Err(SharedError::UnsupportedFormat.into()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::unsync::spawn_blocking;
|
||||
use deno_core::ToJsBuffer;
|
||||
|
@ -16,6 +15,26 @@ use serde::Deserialize;
|
|||
|
||||
use crate::shared::*;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum GenerateKeyError {
|
||||
#[error(transparent)]
|
||||
General(#[from] SharedError),
|
||||
#[error("Bad public exponent")]
|
||||
BadPublicExponent,
|
||||
#[error("Invalid HMAC key length")]
|
||||
InvalidHMACKeyLength,
|
||||
#[error("Failed to serialize RSA key")]
|
||||
FailedRSAKeySerialization,
|
||||
#[error("Invalid AES key length")]
|
||||
InvalidAESKeyLength,
|
||||
#[error("Failed to generate RSA key")]
|
||||
FailedRSAKeyGeneration,
|
||||
#[error("Failed to generate EC key")]
|
||||
FailedECKeyGeneration,
|
||||
#[error("Failed to generate key")]
|
||||
FailedKeyGeneration,
|
||||
}
|
||||
|
||||
// Allowlist for RSA public exponents.
|
||||
static PUB_EXPONENT_1: Lazy<BigUint> =
|
||||
Lazy::new(|| BigUint::from_u64(3).unwrap());
|
||||
|
@ -46,7 +65,7 @@ pub enum GenerateKeyOptions {
|
|||
#[serde]
|
||||
pub async fn op_crypto_generate_key(
|
||||
#[serde] opts: GenerateKeyOptions,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, GenerateKeyError> {
|
||||
let fun = || match opts {
|
||||
GenerateKeyOptions::Rsa {
|
||||
modulus_length,
|
||||
|
@ -65,21 +84,21 @@ pub async fn op_crypto_generate_key(
|
|||
fn generate_key_rsa(
|
||||
modulus_length: u32,
|
||||
public_exponent: &[u8],
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
) -> Result<Vec<u8>, GenerateKeyError> {
|
||||
let exponent = BigUint::from_bytes_be(public_exponent);
|
||||
if exponent != *PUB_EXPONENT_1 && exponent != *PUB_EXPONENT_2 {
|
||||
return Err(operation_error("Bad public exponent"));
|
||||
return Err(GenerateKeyError::BadPublicExponent);
|
||||
}
|
||||
|
||||
let mut rng = OsRng;
|
||||
|
||||
let private_key =
|
||||
RsaPrivateKey::new_with_exp(&mut rng, modulus_length as usize, &exponent)
|
||||
.map_err(|_| operation_error("Failed to generate RSA key"))?;
|
||||
.map_err(|_| GenerateKeyError::FailedRSAKeyGeneration)?;
|
||||
|
||||
let private_key = private_key
|
||||
.to_pkcs1_der()
|
||||
.map_err(|_| operation_error("Failed to serialize RSA key"))?;
|
||||
.map_err(|_| GenerateKeyError::FailedRSAKeySerialization)?;
|
||||
|
||||
Ok(private_key.as_bytes().to_vec())
|
||||
}
|
||||
|
@ -90,7 +109,9 @@ fn generate_key_ec_p521() -> Vec<u8> {
|
|||
key.to_nonzero_scalar().to_bytes().to_vec()
|
||||
}
|
||||
|
||||
fn generate_key_ec(named_curve: EcNamedCurve) -> Result<Vec<u8>, AnyError> {
|
||||
fn generate_key_ec(
|
||||
named_curve: EcNamedCurve,
|
||||
) -> Result<Vec<u8>, GenerateKeyError> {
|
||||
let curve = match named_curve {
|
||||
EcNamedCurve::P256 => &ring::signature::ECDSA_P256_SHA256_FIXED_SIGNING,
|
||||
EcNamedCurve::P384 => &ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING,
|
||||
|
@ -100,21 +121,21 @@ fn generate_key_ec(named_curve: EcNamedCurve) -> Result<Vec<u8>, AnyError> {
|
|||
let rng = ring::rand::SystemRandom::new();
|
||||
|
||||
let pkcs8 = EcdsaKeyPair::generate_pkcs8(curve, &rng)
|
||||
.map_err(|_| operation_error("Failed to generate EC key"))?;
|
||||
.map_err(|_| GenerateKeyError::FailedECKeyGeneration)?;
|
||||
|
||||
Ok(pkcs8.as_ref().to_vec())
|
||||
}
|
||||
|
||||
fn generate_key_aes(length: usize) -> Result<Vec<u8>, AnyError> {
|
||||
fn generate_key_aes(length: usize) -> Result<Vec<u8>, GenerateKeyError> {
|
||||
if length % 8 != 0 || length > 256 {
|
||||
return Err(operation_error("Invalid AES key length"));
|
||||
return Err(GenerateKeyError::InvalidAESKeyLength);
|
||||
}
|
||||
|
||||
let mut key = vec![0u8; length / 8];
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
rng
|
||||
.fill(&mut key)
|
||||
.map_err(|_| operation_error("Failed to generate key"))?;
|
||||
.map_err(|_| GenerateKeyError::FailedKeyGeneration)?;
|
||||
|
||||
Ok(key)
|
||||
}
|
||||
|
@ -122,7 +143,7 @@ fn generate_key_aes(length: usize) -> Result<Vec<u8>, AnyError> {
|
|||
fn generate_key_hmac(
|
||||
hash: ShaHash,
|
||||
length: Option<usize>,
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
) -> Result<Vec<u8>, GenerateKeyError> {
|
||||
let hash = match hash {
|
||||
ShaHash::Sha1 => &ring::hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
|
||||
ShaHash::Sha256 => &ring::hmac::HMAC_SHA256,
|
||||
|
@ -132,12 +153,12 @@ fn generate_key_hmac(
|
|||
|
||||
let length = if let Some(length) = length {
|
||||
if length % 8 != 0 {
|
||||
return Err(operation_error("Invalid HMAC key length"));
|
||||
return Err(GenerateKeyError::InvalidHMACKeyLength);
|
||||
}
|
||||
|
||||
let length = length / 8;
|
||||
if length > ring::digest::MAX_BLOCK_LEN {
|
||||
return Err(operation_error("Invalid HMAC key length"));
|
||||
return Err(GenerateKeyError::InvalidHMACKeyLength);
|
||||
}
|
||||
|
||||
length
|
||||
|
@ -149,7 +170,7 @@ fn generate_key_hmac(
|
|||
let mut key = vec![0u8; length];
|
||||
rng
|
||||
.fill(&mut key)
|
||||
.map_err(|_| operation_error("Failed to generate key"))?;
|
||||
.map_err(|_| GenerateKeyError::FailedKeyGeneration)?;
|
||||
|
||||
Ok(key)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use base64::Engine;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::JsBuffer;
|
||||
use deno_core::ToJsBuffer;
|
||||
|
@ -15,6 +14,70 @@ use spki::der::Decode;
|
|||
|
||||
use crate::shared::*;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ImportKeyError {
|
||||
#[error(transparent)]
|
||||
General(#[from] SharedError),
|
||||
#[error("invalid modulus")]
|
||||
InvalidModulus,
|
||||
#[error("invalid public exponent")]
|
||||
InvalidPublicExponent,
|
||||
#[error("invalid private exponent")]
|
||||
InvalidPrivateExponent,
|
||||
#[error("invalid first prime factor")]
|
||||
InvalidFirstPrimeFactor,
|
||||
#[error("invalid second prime factor")]
|
||||
InvalidSecondPrimeFactor,
|
||||
#[error("invalid first CRT exponent")]
|
||||
InvalidFirstCRTExponent,
|
||||
#[error("invalid second CRT exponent")]
|
||||
InvalidSecondCRTExponent,
|
||||
#[error("invalid CRT coefficient")]
|
||||
InvalidCRTCoefficient,
|
||||
#[error("invalid b64 coordinate")]
|
||||
InvalidB64Coordinate,
|
||||
#[error("invalid RSA public key")]
|
||||
InvalidRSAPublicKey,
|
||||
#[error("invalid RSA private key")]
|
||||
InvalidRSAPrivateKey,
|
||||
#[error("unsupported algorithm")]
|
||||
UnsupportedAlgorithm,
|
||||
#[error("public key is invalid (too long)")]
|
||||
PublicKeyTooLong,
|
||||
#[error("private key is invalid (too long)")]
|
||||
PrivateKeyTooLong,
|
||||
#[error("invalid P-256 elliptic curve point")]
|
||||
InvalidP256ECPoint,
|
||||
#[error("invalid P-384 elliptic curve point")]
|
||||
InvalidP384ECPoint,
|
||||
#[error("invalid P-521 elliptic curve point")]
|
||||
InvalidP521ECPoint,
|
||||
#[error("invalid P-256 elliptic curve SPKI data")]
|
||||
InvalidP256ECSPKIData,
|
||||
#[error("invalid P-384 elliptic curve SPKI data")]
|
||||
InvalidP384ECSPKIData,
|
||||
#[error("invalid P-521 elliptic curve SPKI data")]
|
||||
InvalidP521ECSPKIData,
|
||||
#[error("curve mismatch")]
|
||||
CurveMismatch,
|
||||
#[error("Unsupported named curve")]
|
||||
UnsupportedNamedCurve,
|
||||
#[error("invalid key data")]
|
||||
InvalidKeyData,
|
||||
#[error("invalid JWK private key")]
|
||||
InvalidJWKPrivateKey,
|
||||
#[error(transparent)]
|
||||
EllipticCurve(#[from] elliptic_curve::Error),
|
||||
#[error("expected valid PKCS#8 data")]
|
||||
ExpectedValidPkcs8Data,
|
||||
#[error("malformed parameters")]
|
||||
MalformedParameters,
|
||||
#[error(transparent)]
|
||||
Spki(#[from] spki::Error),
|
||||
#[error(transparent)]
|
||||
Der(#[from] rsa::pkcs1::der::Error),
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum KeyData {
|
||||
|
@ -93,7 +156,7 @@ pub enum ImportKeyResult {
|
|||
pub fn op_crypto_import_key(
|
||||
#[serde] opts: ImportKeyOptions,
|
||||
#[serde] key_data: KeyData,
|
||||
) -> Result<ImportKeyResult, AnyError> {
|
||||
) -> Result<ImportKeyResult, ImportKeyError> {
|
||||
match opts {
|
||||
ImportKeyOptions::RsassaPkcs1v15 {} => import_key_rsassa(key_data),
|
||||
ImportKeyOptions::RsaPss {} => import_key_rsapss(key_data),
|
||||
|
@ -117,21 +180,21 @@ const BASE64_URL_SAFE_FORGIVING:
|
|||
);
|
||||
|
||||
macro_rules! jwt_b64_int_or_err {
|
||||
($name:ident, $b64:expr, $err:expr) => {
|
||||
($name:ident, $b64:expr, $err:tt) => {
|
||||
let bytes = BASE64_URL_SAFE_FORGIVING
|
||||
.decode($b64)
|
||||
.map_err(|_| data_error($err))?;
|
||||
let $name = UintRef::new(&bytes).map_err(|_| data_error($err))?;
|
||||
.map_err(|_| ImportKeyError::$err)?;
|
||||
let $name = UintRef::new(&bytes).map_err(|_| ImportKeyError::$err)?;
|
||||
};
|
||||
}
|
||||
|
||||
fn import_key_rsa_jwk(
|
||||
key_data: KeyData,
|
||||
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
||||
) -> Result<ImportKeyResult, ImportKeyError> {
|
||||
match key_data {
|
||||
KeyData::JwkPublicRsa { n, e } => {
|
||||
jwt_b64_int_or_err!(modulus, &n, "invalid modulus");
|
||||
jwt_b64_int_or_err!(public_exponent, &e, "invalid public exponent");
|
||||
jwt_b64_int_or_err!(modulus, &n, InvalidModulus);
|
||||
jwt_b64_int_or_err!(public_exponent, &e, InvalidPublicExponent);
|
||||
|
||||
let public_key = rsa::pkcs1::RsaPublicKey {
|
||||
modulus,
|
||||
|
@ -141,7 +204,7 @@ fn import_key_rsa_jwk(
|
|||
let mut data = Vec::new();
|
||||
public_key
|
||||
.encode_to_vec(&mut data)
|
||||
.map_err(|_| data_error("invalid rsa public key"))?;
|
||||
.map_err(|_| ImportKeyError::InvalidRSAPublicKey)?;
|
||||
|
||||
let public_exponent =
|
||||
public_key.public_exponent.as_bytes().to_vec().into();
|
||||
|
@ -163,14 +226,14 @@ fn import_key_rsa_jwk(
|
|||
dq,
|
||||
qi,
|
||||
} => {
|
||||
jwt_b64_int_or_err!(modulus, &n, "invalid modulus");
|
||||
jwt_b64_int_or_err!(public_exponent, &e, "invalid public exponent");
|
||||
jwt_b64_int_or_err!(private_exponent, &d, "invalid private exponent");
|
||||
jwt_b64_int_or_err!(prime1, &p, "invalid first prime factor");
|
||||
jwt_b64_int_or_err!(prime2, &q, "invalid second prime factor");
|
||||
jwt_b64_int_or_err!(exponent1, &dp, "invalid first CRT exponent");
|
||||
jwt_b64_int_or_err!(exponent2, &dq, "invalid second CRT exponent");
|
||||
jwt_b64_int_or_err!(coefficient, &qi, "invalid CRT coefficient");
|
||||
jwt_b64_int_or_err!(modulus, &n, InvalidModulus);
|
||||
jwt_b64_int_or_err!(public_exponent, &e, InvalidPublicExponent);
|
||||
jwt_b64_int_or_err!(private_exponent, &d, InvalidPrivateExponent);
|
||||
jwt_b64_int_or_err!(prime1, &p, InvalidFirstPrimeFactor);
|
||||
jwt_b64_int_or_err!(prime2, &q, InvalidSecondPrimeFactor);
|
||||
jwt_b64_int_or_err!(exponent1, &dp, InvalidFirstCRTExponent);
|
||||
jwt_b64_int_or_err!(exponent2, &dq, InvalidSecondCRTExponent);
|
||||
jwt_b64_int_or_err!(coefficient, &qi, InvalidCRTCoefficient);
|
||||
|
||||
let private_key = rsa::pkcs1::RsaPrivateKey {
|
||||
modulus,
|
||||
|
@ -187,7 +250,7 @@ fn import_key_rsa_jwk(
|
|||
let mut data = Vec::new();
|
||||
private_key
|
||||
.encode_to_vec(&mut data)
|
||||
.map_err(|_| data_error("invalid rsa private key"))?;
|
||||
.map_err(|_| ImportKeyError::InvalidRSAPrivateKey)?;
|
||||
|
||||
let public_exponent =
|
||||
private_key.public_exponent.as_bytes().to_vec().into();
|
||||
|
@ -205,37 +268,33 @@ fn import_key_rsa_jwk(
|
|||
|
||||
fn import_key_rsassa(
|
||||
key_data: KeyData,
|
||||
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
||||
) -> Result<ImportKeyResult, ImportKeyError> {
|
||||
match key_data {
|
||||
KeyData::Spki(data) => {
|
||||
// 2-3.
|
||||
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)?;
|
||||
|
||||
// 4-5.
|
||||
let alg = pk_info.algorithm.oid;
|
||||
|
||||
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
||||
if alg != RSA_ENCRYPTION_OID {
|
||||
return Err(data_error("unsupported algorithm"));
|
||||
return Err(ImportKeyError::UnsupportedAlgorithm);
|
||||
}
|
||||
|
||||
// 8-9.
|
||||
let public_key = rsa::pkcs1::RsaPublicKey::from_der(
|
||||
pk_info.subject_public_key.raw_bytes(),
|
||||
)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
)?;
|
||||
|
||||
let bytes_consumed = public_key
|
||||
.encoded_len()
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let bytes_consumed = public_key.encoded_len()?;
|
||||
|
||||
if bytes_consumed
|
||||
!= rsa::pkcs1::der::Length::new(
|
||||
pk_info.subject_public_key.raw_bytes().len() as u16,
|
||||
)
|
||||
{
|
||||
return Err(data_error("public key is invalid (too long)"));
|
||||
return Err(ImportKeyError::PublicKeyTooLong);
|
||||
}
|
||||
|
||||
let data = pk_info.subject_public_key.raw_bytes().to_vec().into();
|
||||
|
@ -251,30 +310,26 @@ fn import_key_rsassa(
|
|||
}
|
||||
KeyData::Pkcs8(data) => {
|
||||
// 2-3.
|
||||
let pk_info = PrivateKeyInfo::from_der(&data)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let pk_info = PrivateKeyInfo::from_der(&data)?;
|
||||
|
||||
// 4-5.
|
||||
let alg = pk_info.algorithm.oid;
|
||||
|
||||
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
||||
if alg != RSA_ENCRYPTION_OID {
|
||||
return Err(data_error("unsupported algorithm"));
|
||||
return Err(ImportKeyError::UnsupportedAlgorithm);
|
||||
}
|
||||
|
||||
// 8-9.
|
||||
let private_key =
|
||||
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)?;
|
||||
|
||||
let bytes_consumed = private_key
|
||||
.encoded_len()
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let bytes_consumed = private_key.encoded_len()?;
|
||||
|
||||
if bytes_consumed
|
||||
!= rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
|
||||
{
|
||||
return Err(data_error("private key is invalid (too long)"));
|
||||
return Err(ImportKeyError::PrivateKeyTooLong);
|
||||
}
|
||||
|
||||
let data = pk_info.private_key.to_vec().into();
|
||||
|
@ -291,43 +346,39 @@ fn import_key_rsassa(
|
|||
KeyData::JwkPublicRsa { .. } | KeyData::JwkPrivateRsa { .. } => {
|
||||
import_key_rsa_jwk(key_data)
|
||||
}
|
||||
_ => Err(unsupported_format()),
|
||||
_ => Err(SharedError::UnsupportedFormat.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn import_key_rsapss(
|
||||
key_data: KeyData,
|
||||
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
||||
) -> Result<ImportKeyResult, ImportKeyError> {
|
||||
match key_data {
|
||||
KeyData::Spki(data) => {
|
||||
// 2-3.
|
||||
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)?;
|
||||
|
||||
// 4-5.
|
||||
let alg = pk_info.algorithm.oid;
|
||||
|
||||
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
||||
if alg != RSA_ENCRYPTION_OID {
|
||||
return Err(data_error("unsupported algorithm"));
|
||||
return Err(ImportKeyError::UnsupportedAlgorithm);
|
||||
}
|
||||
|
||||
// 8-9.
|
||||
let public_key = rsa::pkcs1::RsaPublicKey::from_der(
|
||||
pk_info.subject_public_key.raw_bytes(),
|
||||
)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
)?;
|
||||
|
||||
let bytes_consumed = public_key
|
||||
.encoded_len()
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let bytes_consumed = public_key.encoded_len()?;
|
||||
|
||||
if bytes_consumed
|
||||
!= rsa::pkcs1::der::Length::new(
|
||||
pk_info.subject_public_key.raw_bytes().len() as u16,
|
||||
)
|
||||
{
|
||||
return Err(data_error("public key is invalid (too long)"));
|
||||
return Err(ImportKeyError::PublicKeyTooLong);
|
||||
}
|
||||
|
||||
let data = pk_info.subject_public_key.raw_bytes().to_vec().into();
|
||||
|
@ -343,30 +394,26 @@ fn import_key_rsapss(
|
|||
}
|
||||
KeyData::Pkcs8(data) => {
|
||||
// 2-3.
|
||||
let pk_info = PrivateKeyInfo::from_der(&data)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let pk_info = PrivateKeyInfo::from_der(&data)?;
|
||||
|
||||
// 4-5.
|
||||
let alg = pk_info.algorithm.oid;
|
||||
|
||||
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
||||
if alg != RSA_ENCRYPTION_OID {
|
||||
return Err(data_error("unsupported algorithm"));
|
||||
return Err(ImportKeyError::UnsupportedAlgorithm);
|
||||
}
|
||||
|
||||
// 8-9.
|
||||
let private_key =
|
||||
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)?;
|
||||
|
||||
let bytes_consumed = private_key
|
||||
.encoded_len()
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let bytes_consumed = private_key.encoded_len()?;
|
||||
|
||||
if bytes_consumed
|
||||
!= rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
|
||||
{
|
||||
return Err(data_error("private key is invalid (too long)"));
|
||||
return Err(ImportKeyError::PrivateKeyTooLong);
|
||||
}
|
||||
|
||||
let data = pk_info.private_key.to_vec().into();
|
||||
|
@ -383,43 +430,39 @@ fn import_key_rsapss(
|
|||
KeyData::JwkPublicRsa { .. } | KeyData::JwkPrivateRsa { .. } => {
|
||||
import_key_rsa_jwk(key_data)
|
||||
}
|
||||
_ => Err(unsupported_format()),
|
||||
_ => Err(SharedError::UnsupportedFormat.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn import_key_rsaoaep(
|
||||
key_data: KeyData,
|
||||
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
||||
) -> Result<ImportKeyResult, ImportKeyError> {
|
||||
match key_data {
|
||||
KeyData::Spki(data) => {
|
||||
// 2-3.
|
||||
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)?;
|
||||
|
||||
// 4-5.
|
||||
let alg = pk_info.algorithm.oid;
|
||||
|
||||
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
||||
if alg != RSA_ENCRYPTION_OID {
|
||||
return Err(data_error("unsupported algorithm"));
|
||||
return Err(ImportKeyError::UnsupportedAlgorithm);
|
||||
}
|
||||
|
||||
// 8-9.
|
||||
let public_key = rsa::pkcs1::RsaPublicKey::from_der(
|
||||
pk_info.subject_public_key.raw_bytes(),
|
||||
)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
)?;
|
||||
|
||||
let bytes_consumed = public_key
|
||||
.encoded_len()
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let bytes_consumed = public_key.encoded_len()?;
|
||||
|
||||
if bytes_consumed
|
||||
!= rsa::pkcs1::der::Length::new(
|
||||
pk_info.subject_public_key.raw_bytes().len() as u16,
|
||||
)
|
||||
{
|
||||
return Err(data_error("public key is invalid (too long)"));
|
||||
return Err(ImportKeyError::PublicKeyTooLong);
|
||||
}
|
||||
|
||||
let data = pk_info.subject_public_key.raw_bytes().to_vec().into();
|
||||
|
@ -435,30 +478,26 @@ fn import_key_rsaoaep(
|
|||
}
|
||||
KeyData::Pkcs8(data) => {
|
||||
// 2-3.
|
||||
let pk_info = PrivateKeyInfo::from_der(&data)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let pk_info = PrivateKeyInfo::from_der(&data)?;
|
||||
|
||||
// 4-5.
|
||||
let alg = pk_info.algorithm.oid;
|
||||
|
||||
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
||||
if alg != RSA_ENCRYPTION_OID {
|
||||
return Err(data_error("unsupported algorithm"));
|
||||
return Err(ImportKeyError::UnsupportedAlgorithm);
|
||||
}
|
||||
|
||||
// 8-9.
|
||||
let private_key =
|
||||
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)?;
|
||||
|
||||
let bytes_consumed = private_key
|
||||
.encoded_len()
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let bytes_consumed = private_key.encoded_len()?;
|
||||
|
||||
if bytes_consumed
|
||||
!= rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
|
||||
{
|
||||
return Err(data_error("private key is invalid (too long)"));
|
||||
return Err(ImportKeyError::PrivateKeyTooLong);
|
||||
}
|
||||
|
||||
let data = pk_info.private_key.to_vec().into();
|
||||
|
@ -475,14 +514,14 @@ fn import_key_rsaoaep(
|
|||
KeyData::JwkPublicRsa { .. } | KeyData::JwkPrivateRsa { .. } => {
|
||||
import_key_rsa_jwk(key_data)
|
||||
}
|
||||
_ => Err(unsupported_format()),
|
||||
_ => Err(SharedError::UnsupportedFormat.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_b64url_to_field_bytes<C: elliptic_curve::Curve>(
|
||||
b64: &str,
|
||||
) -> Result<elliptic_curve::FieldBytes<C>, deno_core::anyhow::Error> {
|
||||
jwt_b64_int_or_err!(val, b64, "invalid b64 coordinate");
|
||||
) -> Result<elliptic_curve::FieldBytes<C>, ImportKeyError> {
|
||||
jwt_b64_int_or_err!(val, b64, InvalidB64Coordinate);
|
||||
|
||||
let mut bytes = elliptic_curve::FieldBytes::<C>::default();
|
||||
let original_bytes = val.as_bytes();
|
||||
|
@ -495,7 +534,7 @@ fn decode_b64url_to_field_bytes<C: elliptic_curve::Curve>(
|
|||
let val = new_bytes.as_slice();
|
||||
|
||||
if val.len() != bytes.len() {
|
||||
return Err(data_error("invalid b64 coordinate"));
|
||||
return Err(ImportKeyError::InvalidB64Coordinate);
|
||||
}
|
||||
bytes.copy_from_slice(val);
|
||||
|
||||
|
@ -506,7 +545,7 @@ fn import_key_ec_jwk_to_point(
|
|||
x: String,
|
||||
y: String,
|
||||
named_curve: EcNamedCurve,
|
||||
) -> Result<Vec<u8>, deno_core::anyhow::Error> {
|
||||
) -> Result<Vec<u8>, ImportKeyError> {
|
||||
let point_bytes = match named_curve {
|
||||
EcNamedCurve::P256 => {
|
||||
let x = decode_b64url_to_field_bytes::<p256::NistP256>(&x)?;
|
||||
|
@ -534,7 +573,7 @@ fn import_key_ec_jwk_to_point(
|
|||
fn import_key_ec_jwk(
|
||||
key_data: KeyData,
|
||||
named_curve: EcNamedCurve,
|
||||
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
||||
) -> Result<ImportKeyResult, ImportKeyError> {
|
||||
match key_data {
|
||||
KeyData::JwkPublicEc { x, y } => {
|
||||
let point_bytes = import_key_ec_jwk_to_point(x, y, named_curve)?;
|
||||
|
@ -550,21 +589,21 @@ fn import_key_ec_jwk(
|
|||
let pk = p256::SecretKey::from_bytes(&d)?;
|
||||
|
||||
pk.to_pkcs8_der()
|
||||
.map_err(|_| data_error("invalid JWK private key"))?
|
||||
.map_err(|_| ImportKeyError::InvalidJWKPrivateKey)?
|
||||
}
|
||||
EcNamedCurve::P384 => {
|
||||
let d = decode_b64url_to_field_bytes::<p384::NistP384>(&d)?;
|
||||
let pk = p384::SecretKey::from_bytes(&d)?;
|
||||
|
||||
pk.to_pkcs8_der()
|
||||
.map_err(|_| data_error("invalid JWK private key"))?
|
||||
.map_err(|_| ImportKeyError::InvalidJWKPrivateKey)?
|
||||
}
|
||||
EcNamedCurve::P521 => {
|
||||
let d = decode_b64url_to_field_bytes::<p521::NistP521>(&d)?;
|
||||
let pk = p521::SecretKey::from_bytes(&d)?;
|
||||
|
||||
pk.to_pkcs8_der()
|
||||
.map_err(|_| data_error("invalid JWK private key"))?
|
||||
.map_err(|_| ImportKeyError::InvalidJWKPrivateKey)?
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -595,7 +634,7 @@ impl<'a> TryFrom<spki::der::asn1::AnyRef<'a>> for ECParametersSpki {
|
|||
fn import_key_ec(
|
||||
key_data: KeyData,
|
||||
named_curve: EcNamedCurve,
|
||||
) -> Result<ImportKeyResult, AnyError> {
|
||||
) -> Result<ImportKeyResult, ImportKeyError> {
|
||||
match key_data {
|
||||
KeyData::Raw(data) => {
|
||||
// The point is parsed and validated, ultimately the original data is
|
||||
|
@ -604,28 +643,28 @@ fn import_key_ec(
|
|||
EcNamedCurve::P256 => {
|
||||
// 1-2.
|
||||
let point = p256::EncodedPoint::from_bytes(&data)
|
||||
.map_err(|_| data_error("invalid P-256 elliptic curve point"))?;
|
||||
.map_err(|_| ImportKeyError::InvalidP256ECPoint)?;
|
||||
// 3.
|
||||
if point.is_identity() {
|
||||
return Err(data_error("invalid P-256 elliptic curve point"));
|
||||
return Err(ImportKeyError::InvalidP256ECPoint);
|
||||
}
|
||||
}
|
||||
EcNamedCurve::P384 => {
|
||||
// 1-2.
|
||||
let point = p384::EncodedPoint::from_bytes(&data)
|
||||
.map_err(|_| data_error("invalid P-384 elliptic curve point"))?;
|
||||
.map_err(|_| ImportKeyError::InvalidP384ECPoint)?;
|
||||
// 3.
|
||||
if point.is_identity() {
|
||||
return Err(data_error("invalid P-384 elliptic curve point"));
|
||||
return Err(ImportKeyError::InvalidP384ECPoint);
|
||||
}
|
||||
}
|
||||
EcNamedCurve::P521 => {
|
||||
// 1-2.
|
||||
let point = p521::EncodedPoint::from_bytes(&data)
|
||||
.map_err(|_| data_error("invalid P-521 elliptic curve point"))?;
|
||||
.map_err(|_| ImportKeyError::InvalidP521ECPoint)?;
|
||||
// 3.
|
||||
if point.is_identity() {
|
||||
return Err(data_error("invalid P-521 elliptic curve point"));
|
||||
return Err(ImportKeyError::InvalidP521ECPoint);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -635,11 +674,11 @@ fn import_key_ec(
|
|||
}
|
||||
KeyData::Pkcs8(data) => {
|
||||
let pk = PrivateKeyInfo::from_der(data.as_ref())
|
||||
.map_err(|_| data_error("expected valid PKCS#8 data"))?;
|
||||
.map_err(|_| ImportKeyError::ExpectedValidPkcs8Data)?;
|
||||
let named_curve_alg = pk
|
||||
.algorithm
|
||||
.parameters
|
||||
.ok_or_else(|| data_error("malformed parameters"))?
|
||||
.ok_or(ImportKeyError::MalformedParameters)?
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
|
@ -654,7 +693,7 @@ fn import_key_ec(
|
|||
};
|
||||
|
||||
if pk_named_curve != Some(named_curve) {
|
||||
return Err(data_error("curve mismatch"));
|
||||
return Err(ImportKeyError::CurveMismatch);
|
||||
}
|
||||
|
||||
Ok(ImportKeyResult::Ec {
|
||||
|
@ -663,14 +702,13 @@ fn import_key_ec(
|
|||
}
|
||||
KeyData::Spki(data) => {
|
||||
// 2-3.
|
||||
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)
|
||||
.map_err(|e| data_error(e.to_string()))?;
|
||||
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)?;
|
||||
|
||||
// 4.
|
||||
let alg = pk_info.algorithm.oid;
|
||||
// id-ecPublicKey
|
||||
if alg != elliptic_curve::ALGORITHM_OID {
|
||||
return Err(data_error("unsupported algorithm"));
|
||||
return Err(ImportKeyError::UnsupportedAlgorithm);
|
||||
}
|
||||
|
||||
// 5-7.
|
||||
|
@ -678,9 +716,9 @@ fn import_key_ec(
|
|||
pk_info
|
||||
.algorithm
|
||||
.parameters
|
||||
.ok_or_else(|| data_error("malformed parameters"))?,
|
||||
.ok_or(ImportKeyError::MalformedParameters)?,
|
||||
)
|
||||
.map_err(|_| data_error("malformed parameters"))?;
|
||||
.map_err(|_| ImportKeyError::MalformedParameters)?;
|
||||
|
||||
// 8-9.
|
||||
let named_curve_alg = params.named_curve_alg;
|
||||
|
@ -704,36 +742,30 @@ fn import_key_ec(
|
|||
|
||||
let bytes_consumed = match named_curve {
|
||||
EcNamedCurve::P256 => {
|
||||
let point =
|
||||
p256::EncodedPoint::from_bytes(&*encoded_key).map_err(|_| {
|
||||
data_error("invalid P-256 elliptic curve SPKI data")
|
||||
})?;
|
||||
let point = p256::EncodedPoint::from_bytes(&*encoded_key)
|
||||
.map_err(|_| ImportKeyError::InvalidP256ECSPKIData)?;
|
||||
if point.is_identity() {
|
||||
return Err(data_error("invalid P-256 elliptic curve point"));
|
||||
return Err(ImportKeyError::InvalidP256ECPoint);
|
||||
}
|
||||
|
||||
point.as_bytes().len()
|
||||
}
|
||||
EcNamedCurve::P384 => {
|
||||
let point =
|
||||
p384::EncodedPoint::from_bytes(&*encoded_key).map_err(|_| {
|
||||
data_error("invalid P-384 elliptic curve SPKI data")
|
||||
})?;
|
||||
let point = p384::EncodedPoint::from_bytes(&*encoded_key)
|
||||
.map_err(|_| ImportKeyError::InvalidP384ECSPKIData)?;
|
||||
|
||||
if point.is_identity() {
|
||||
return Err(data_error("invalid P-384 elliptic curve point"));
|
||||
return Err(ImportKeyError::InvalidP384ECPoint);
|
||||
}
|
||||
|
||||
point.as_bytes().len()
|
||||
}
|
||||
EcNamedCurve::P521 => {
|
||||
let point =
|
||||
p521::EncodedPoint::from_bytes(&*encoded_key).map_err(|_| {
|
||||
data_error("invalid P-521 elliptic curve SPKI data")
|
||||
})?;
|
||||
let point = p521::EncodedPoint::from_bytes(&*encoded_key)
|
||||
.map_err(|_| ImportKeyError::InvalidP521ECSPKIData)?;
|
||||
|
||||
if point.is_identity() {
|
||||
return Err(data_error("invalid P-521 elliptic curve point"));
|
||||
return Err(ImportKeyError::InvalidP521ECPoint);
|
||||
}
|
||||
|
||||
point.as_bytes().len()
|
||||
|
@ -741,15 +773,15 @@ fn import_key_ec(
|
|||
};
|
||||
|
||||
if bytes_consumed != pk_info.subject_public_key.raw_bytes().len() {
|
||||
return Err(data_error("public key is invalid (too long)"));
|
||||
return Err(ImportKeyError::PublicKeyTooLong);
|
||||
}
|
||||
|
||||
// 11.
|
||||
if named_curve != pk_named_curve {
|
||||
return Err(data_error("curve mismatch"));
|
||||
return Err(ImportKeyError::CurveMismatch);
|
||||
}
|
||||
} else {
|
||||
return Err(data_error("Unsupported named curve"));
|
||||
return Err(ImportKeyError::UnsupportedNamedCurve);
|
||||
}
|
||||
|
||||
Ok(ImportKeyResult::Ec {
|
||||
|
@ -759,34 +791,38 @@ fn import_key_ec(
|
|||
KeyData::JwkPublicEc { .. } | KeyData::JwkPrivateEc { .. } => {
|
||||
import_key_ec_jwk(key_data, named_curve)
|
||||
}
|
||||
_ => Err(unsupported_format()),
|
||||
_ => Err(SharedError::UnsupportedFormat.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn import_key_aes(key_data: KeyData) -> Result<ImportKeyResult, AnyError> {
|
||||
fn import_key_aes(
|
||||
key_data: KeyData,
|
||||
) -> Result<ImportKeyResult, ImportKeyError> {
|
||||
Ok(match key_data {
|
||||
KeyData::JwkSecret { k } => {
|
||||
let data = BASE64_URL_SAFE_FORGIVING
|
||||
.decode(k)
|
||||
.map_err(|_| data_error("invalid key data"))?;
|
||||
.map_err(|_| ImportKeyError::InvalidKeyData)?;
|
||||
ImportKeyResult::Hmac {
|
||||
raw_data: RustRawKeyData::Secret(data.into()),
|
||||
}
|
||||
}
|
||||
_ => return Err(unsupported_format()),
|
||||
_ => return Err(SharedError::UnsupportedFormat.into()),
|
||||
})
|
||||
}
|
||||
|
||||
fn import_key_hmac(key_data: KeyData) -> Result<ImportKeyResult, AnyError> {
|
||||
fn import_key_hmac(
|
||||
key_data: KeyData,
|
||||
) -> Result<ImportKeyResult, ImportKeyError> {
|
||||
Ok(match key_data {
|
||||
KeyData::JwkSecret { k } => {
|
||||
let data = BASE64_URL_SAFE_FORGIVING
|
||||
.decode(k)
|
||||
.map_err(|_| data_error("invalid key data"))?;
|
||||
.map_err(|_| ImportKeyError::InvalidKeyData)?;
|
||||
ImportKeyResult::Hmac {
|
||||
raw_data: RustRawKeyData::Secret(data.into()),
|
||||
}
|
||||
}
|
||||
_ => return Err(unsupported_format()),
|
||||
_ => return Err(SharedError::UnsupportedFormat.into()),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,10 +6,7 @@ use aes_kw::KekAes256;
|
|||
|
||||
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
|
||||
use base64::Engine;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::not_supported;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::ToJsBuffer;
|
||||
|
||||
|
@ -17,7 +14,6 @@ use deno_core::unsync::spawn_blocking;
|
|||
use deno_core::JsBuffer;
|
||||
use deno_core::OpState;
|
||||
use serde::Deserialize;
|
||||
use shared::operation_error;
|
||||
|
||||
use p256::elliptic_curve::sec1::FromEncodedPoint;
|
||||
use p256::pkcs8::DecodePrivateKey;
|
||||
|
@ -67,15 +63,24 @@ mod x25519;
|
|||
mod x448;
|
||||
|
||||
pub use crate::decrypt::op_crypto_decrypt;
|
||||
pub use crate::decrypt::DecryptError;
|
||||
pub use crate::ed25519::Ed25519Error;
|
||||
pub use crate::encrypt::op_crypto_encrypt;
|
||||
pub use crate::encrypt::EncryptError;
|
||||
pub use crate::export_key::op_crypto_export_key;
|
||||
pub use crate::export_key::ExportKeyError;
|
||||
pub use crate::generate_key::op_crypto_generate_key;
|
||||
pub use crate::generate_key::GenerateKeyError;
|
||||
pub use crate::import_key::op_crypto_import_key;
|
||||
pub use crate::import_key::ImportKeyError;
|
||||
use crate::key::Algorithm;
|
||||
use crate::key::CryptoHash;
|
||||
use crate::key::CryptoNamedCurve;
|
||||
use crate::key::HkdfOutput;
|
||||
pub use crate::shared::SharedError;
|
||||
use crate::shared::V8RawKeyData;
|
||||
pub use crate::x25519::X25519Error;
|
||||
pub use crate::x448::X448Error;
|
||||
|
||||
deno_core::extension!(deno_crypto,
|
||||
deps = [ deno_webidl, deno_web ],
|
||||
|
@ -127,11 +132,63 @@ deno_core::extension!(deno_crypto,
|
|||
},
|
||||
);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
General(#[from] SharedError),
|
||||
#[error(transparent)]
|
||||
JoinError(#[from] tokio::task::JoinError),
|
||||
#[error(transparent)]
|
||||
Der(#[from] rsa::pkcs1::der::Error),
|
||||
#[error("Missing argument hash")]
|
||||
MissingArgumentHash,
|
||||
#[error("Missing argument saltLength")]
|
||||
MissingArgumentSaltLength,
|
||||
#[error("unsupported algorithm")]
|
||||
UnsupportedAlgorithm,
|
||||
#[error(transparent)]
|
||||
KeyRejected(#[from] ring::error::KeyRejected),
|
||||
#[error(transparent)]
|
||||
RSA(#[from] rsa::Error),
|
||||
#[error(transparent)]
|
||||
Pkcs1(#[from] rsa::pkcs1::Error),
|
||||
#[error(transparent)]
|
||||
Unspecified(#[from] ring::error::Unspecified),
|
||||
#[error("Invalid key format")]
|
||||
InvalidKeyFormat,
|
||||
#[error(transparent)]
|
||||
P256Ecdsa(#[from] p256::ecdsa::Error),
|
||||
#[error("Unexpected error decoding private key")]
|
||||
DecodePrivateKey,
|
||||
#[error("Missing argument publicKey")]
|
||||
MissingArgumentPublicKey,
|
||||
#[error("Missing argument namedCurve")]
|
||||
MissingArgumentNamedCurve,
|
||||
#[error("Missing argument info")]
|
||||
MissingArgumentInfo,
|
||||
#[error("The length provided for HKDF is too large")]
|
||||
HKDFLengthTooLarge,
|
||||
#[error(transparent)]
|
||||
Base64Decode(#[from] base64::DecodeError),
|
||||
#[error("Data must be multiple of 8 bytes")]
|
||||
DataInvalidSize,
|
||||
#[error("Invalid key length")]
|
||||
InvalidKeyLength,
|
||||
#[error("encryption error")]
|
||||
EncryptionError,
|
||||
#[error("decryption error - integrity check failed")]
|
||||
DecryptionError,
|
||||
#[error("The ArrayBufferView's byte length ({0}) exceeds the number of bytes of entropy available via this API (65536)")]
|
||||
ArrayBufferViewLengthExceeded(usize),
|
||||
#[error(transparent)]
|
||||
Other(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_crypto_base64url_decode(
|
||||
#[string] data: String,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, Error> {
|
||||
let data: Vec<u8> = BASE64_URL_SAFE_NO_PAD.decode(data)?;
|
||||
Ok(data.into())
|
||||
}
|
||||
|
@ -147,12 +204,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<(), AnyError> {
|
||||
) -> Result<(), Error> {
|
||||
if out.len() > 65536 {
|
||||
return Err(
|
||||
deno_web::DomExceptionQuotaExceededError::new(&format!("The ArrayBufferView's byte length ({}) exceeds the number of bytes of entropy available via this API (65536)", out.len()))
|
||||
.into(),
|
||||
);
|
||||
return Err(Error::ArrayBufferViewLengthExceeded(out.len()));
|
||||
}
|
||||
|
||||
let maybe_seeded_rng = state.try_borrow_mut::<StdRng>();
|
||||
|
@ -204,7 +258,7 @@ pub struct SignArg {
|
|||
pub async fn op_crypto_sign_key(
|
||||
#[serde] args: SignArg,
|
||||
#[buffer] zero_copy: JsBuffer,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, Error> {
|
||||
deno_core::unsync::spawn_blocking(move || {
|
||||
let data = &*zero_copy;
|
||||
let algorithm = args.algorithm;
|
||||
|
@ -213,10 +267,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(|| type_error("Missing argument hash".to_string()))?
|
||||
{
|
||||
match args.hash.ok_or_else(|| Error::MissingArgumentHash)? {
|
||||
CryptoHash::Sha1 => {
|
||||
let signing_key = SigningKey::<Sha1>::new(private_key);
|
||||
signing_key.sign(data)
|
||||
|
@ -239,15 +290,13 @@ pub async fn op_crypto_sign_key(
|
|||
Algorithm::RsaPss => {
|
||||
let private_key = RsaPrivateKey::from_pkcs1_der(&args.key.data)?;
|
||||
|
||||
let salt_len = args.salt_length.ok_or_else(|| {
|
||||
type_error("Missing argument saltLength".to_string())
|
||||
})? as usize;
|
||||
let salt_len = args
|
||||
.salt_length
|
||||
.ok_or_else(|| Error::MissingArgumentSaltLength)?
|
||||
as usize;
|
||||
|
||||
let mut rng = OsRng;
|
||||
match args
|
||||
.hash
|
||||
.ok_or_else(|| type_error("Missing argument hash".to_string()))?
|
||||
{
|
||||
match args.hash.ok_or_else(|| Error::MissingArgumentHash)? {
|
||||
CryptoHash::Sha1 => {
|
||||
let signing_key = Pss::new_with_salt::<Sha1>(salt_len);
|
||||
let hashed = Sha1::digest(data);
|
||||
|
@ -272,8 +321,10 @@ pub async fn op_crypto_sign_key(
|
|||
.to_vec()
|
||||
}
|
||||
Algorithm::Ecdsa => {
|
||||
let curve: &EcdsaSigningAlgorithm =
|
||||
args.named_curve.ok_or_else(not_supported)?.into();
|
||||
let curve: &EcdsaSigningAlgorithm = args
|
||||
.named_curve
|
||||
.ok_or_else(|| Error::Other(not_supported()))?
|
||||
.into();
|
||||
|
||||
let rng = RingRand::SystemRandom::new();
|
||||
let key_pair = EcdsaKeyPair::from_pkcs8(curve, &args.key.data, &rng)?;
|
||||
|
@ -282,7 +333,7 @@ pub async fn op_crypto_sign_key(
|
|||
if let Some(hash) = args.hash {
|
||||
match hash {
|
||||
CryptoHash::Sha256 | CryptoHash::Sha384 => (),
|
||||
_ => return Err(type_error("Unsupported algorithm")),
|
||||
_ => return Err(Error::UnsupportedAlgorithm),
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -292,14 +343,17 @@ pub async fn op_crypto_sign_key(
|
|||
signature.as_ref().to_vec()
|
||||
}
|
||||
Algorithm::Hmac => {
|
||||
let hash: HmacAlgorithm = args.hash.ok_or_else(not_supported)?.into();
|
||||
let hash: HmacAlgorithm = args
|
||||
.hash
|
||||
.ok_or_else(|| Error::Other(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(type_error("Unsupported algorithm".to_string())),
|
||||
_ => return Err(Error::UnsupportedAlgorithm),
|
||||
};
|
||||
|
||||
Ok(signature.into())
|
||||
|
@ -322,7 +376,7 @@ pub struct VerifyArg {
|
|||
pub async fn op_crypto_verify_key(
|
||||
#[serde] args: VerifyArg,
|
||||
#[buffer] zero_copy: JsBuffer,
|
||||
) -> Result<bool, AnyError> {
|
||||
) -> Result<bool, Error> {
|
||||
deno_core::unsync::spawn_blocking(move || {
|
||||
let data = &*zero_copy;
|
||||
let algorithm = args.algorithm;
|
||||
|
@ -333,10 +387,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(|| type_error("Missing argument hash".to_string()))?
|
||||
{
|
||||
match args.hash.ok_or_else(|| Error::MissingArgumentHash)? {
|
||||
CryptoHash::Sha1 => {
|
||||
let verifying_key = VerifyingKey::<Sha1>::new(public_key);
|
||||
verifying_key.verify(data, &signature).is_ok()
|
||||
|
@ -359,14 +410,12 @@ pub async fn op_crypto_verify_key(
|
|||
let public_key = read_rsa_public_key(args.key)?;
|
||||
let signature = args.signature.as_ref();
|
||||
|
||||
let salt_len = args.salt_length.ok_or_else(|| {
|
||||
type_error("Missing argument saltLength".to_string())
|
||||
})? as usize;
|
||||
let salt_len = args
|
||||
.salt_length
|
||||
.ok_or_else(|| Error::MissingArgumentSaltLength)?
|
||||
as usize;
|
||||
|
||||
match args
|
||||
.hash
|
||||
.ok_or_else(|| type_error("Missing argument hash".to_string()))?
|
||||
{
|
||||
match args.hash.ok_or_else(|| Error::MissingArgumentHash)? {
|
||||
CryptoHash::Sha1 => {
|
||||
let pss = Pss::new_with_salt::<Sha1>(salt_len);
|
||||
let hashed = Sha1::digest(data);
|
||||
|
@ -390,15 +439,22 @@ pub async fn op_crypto_verify_key(
|
|||
}
|
||||
}
|
||||
Algorithm::Hmac => {
|
||||
let hash: HmacAlgorithm = args.hash.ok_or_else(not_supported)?.into();
|
||||
let hash: HmacAlgorithm = args
|
||||
.hash
|
||||
.ok_or_else(|| Error::Other(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(not_supported)?.into();
|
||||
let verify_alg: &EcdsaVerificationAlgorithm =
|
||||
args.named_curve.ok_or_else(not_supported)?.into();
|
||||
let signing_alg: &EcdsaSigningAlgorithm = args
|
||||
.named_curve
|
||||
.ok_or_else(|| Error::Other(not_supported()))?
|
||||
.into();
|
||||
let verify_alg: &EcdsaVerificationAlgorithm = args
|
||||
.named_curve
|
||||
.ok_or_else(|| Error::Other(not_supported()))?
|
||||
.into();
|
||||
|
||||
let private_key;
|
||||
|
||||
|
@ -411,7 +467,7 @@ pub async fn op_crypto_verify_key(
|
|||
private_key.public_key().as_ref()
|
||||
}
|
||||
KeyType::Public => &*args.key.data,
|
||||
_ => return Err(type_error("Invalid Key format".to_string())),
|
||||
_ => return Err(Error::InvalidKeyFormat),
|
||||
};
|
||||
|
||||
let public_key =
|
||||
|
@ -419,7 +475,7 @@ pub async fn op_crypto_verify_key(
|
|||
|
||||
public_key.verify(data, &args.signature).is_ok()
|
||||
}
|
||||
_ => return Err(type_error("Unsupported algorithm".to_string())),
|
||||
_ => return Err(Error::UnsupportedAlgorithm),
|
||||
};
|
||||
|
||||
Ok(verification)
|
||||
|
@ -447,18 +503,20 @@ pub struct DeriveKeyArg {
|
|||
pub async fn op_crypto_derive_bits(
|
||||
#[serde] args: DeriveKeyArg,
|
||||
#[buffer] zero_copy: Option<JsBuffer>,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, Error> {
|
||||
deno_core::unsync::spawn_blocking(move || {
|
||||
let algorithm = args.algorithm;
|
||||
match algorithm {
|
||||
Algorithm::Pbkdf2 => {
|
||||
let zero_copy = zero_copy.ok_or_else(not_supported)?;
|
||||
let zero_copy =
|
||||
zero_copy.ok_or_else(|| Error::Other(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(not_supported)? {
|
||||
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,
|
||||
|
@ -466,51 +524,47 @@ pub async fn op_crypto_derive_bits(
|
|||
};
|
||||
|
||||
// This will never panic. We have already checked length earlier.
|
||||
let iterations =
|
||||
NonZeroU32::new(args.iterations.ok_or_else(not_supported)?).unwrap();
|
||||
let iterations = NonZeroU32::new(
|
||||
args
|
||||
.iterations
|
||||
.ok_or_else(|| Error::Other(not_supported()))?,
|
||||
)
|
||||
.unwrap();
|
||||
let secret = args.key.data;
|
||||
let mut out = vec![0; args.length / 8];
|
||||
pbkdf2::derive(algorithm, iterations, salt, &secret, &mut out);
|
||||
Ok(out.into())
|
||||
}
|
||||
Algorithm::Ecdh => {
|
||||
let named_curve = args.named_curve.ok_or_else(|| {
|
||||
type_error("Missing argument namedCurve".to_string())
|
||||
})?;
|
||||
let named_curve = args
|
||||
.named_curve
|
||||
.ok_or_else(|| Error::MissingArgumentNamedCurve)?;
|
||||
|
||||
let public_key = args
|
||||
.public_key
|
||||
.ok_or_else(|| type_error("Missing argument publicKey"))?;
|
||||
.ok_or_else(|| Error::MissingArgumentPublicKey)?;
|
||||
|
||||
match named_curve {
|
||||
CryptoNamedCurve::P256 => {
|
||||
let secret_key = p256::SecretKey::from_pkcs8_der(&args.key.data)
|
||||
.map_err(|_| {
|
||||
type_error("Unexpected error decoding private key")
|
||||
})?;
|
||||
.map_err(|_| Error::DecodePrivateKey)?;
|
||||
|
||||
let public_key = match public_key.r#type {
|
||||
KeyType::Private => {
|
||||
p256::SecretKey::from_pkcs8_der(&public_key.data)
|
||||
.map_err(|_| {
|
||||
type_error("Unexpected error decoding private key")
|
||||
})?
|
||||
.map_err(|_| Error::DecodePrivateKey)?
|
||||
.public_key()
|
||||
}
|
||||
KeyType::Public => {
|
||||
let point = p256::EncodedPoint::from_bytes(public_key.data)
|
||||
.map_err(|_| {
|
||||
type_error("Unexpected error decoding private key")
|
||||
})?;
|
||||
.map_err(|_| Error::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(type_error(
|
||||
"Unexpected error decoding private key",
|
||||
));
|
||||
return Err(Error::DecodePrivateKey);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -526,32 +580,24 @@ pub async fn op_crypto_derive_bits(
|
|||
}
|
||||
CryptoNamedCurve::P384 => {
|
||||
let secret_key = p384::SecretKey::from_pkcs8_der(&args.key.data)
|
||||
.map_err(|_| {
|
||||
type_error("Unexpected error decoding private key")
|
||||
})?;
|
||||
.map_err(|_| Error::DecodePrivateKey)?;
|
||||
|
||||
let public_key = match public_key.r#type {
|
||||
KeyType::Private => {
|
||||
p384::SecretKey::from_pkcs8_der(&public_key.data)
|
||||
.map_err(|_| {
|
||||
type_error("Unexpected error decoding private key")
|
||||
})?
|
||||
.map_err(|_| Error::DecodePrivateKey)?
|
||||
.public_key()
|
||||
}
|
||||
KeyType::Public => {
|
||||
let point = p384::EncodedPoint::from_bytes(public_key.data)
|
||||
.map_err(|_| {
|
||||
type_error("Unexpected error decoding private key")
|
||||
})?;
|
||||
.map_err(|_| Error::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(type_error(
|
||||
"Unexpected error decoding private key",
|
||||
));
|
||||
return Err(Error::DecodePrivateKey);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -568,18 +614,18 @@ pub async fn op_crypto_derive_bits(
|
|||
}
|
||||
}
|
||||
Algorithm::Hkdf => {
|
||||
let zero_copy = zero_copy.ok_or_else(not_supported)?;
|
||||
let zero_copy =
|
||||
zero_copy.ok_or_else(|| Error::Other(not_supported()))?;
|
||||
let salt = &*zero_copy;
|
||||
let algorithm = match args.hash.ok_or_else(not_supported)? {
|
||||
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 info = args
|
||||
.info
|
||||
.ok_or_else(|| type_error("Missing argument info".to_string()))?;
|
||||
let info = args.info.ok_or_else(|| Error::MissingArgumentInfo)?;
|
||||
// IKM
|
||||
let secret = args.key.data;
|
||||
// L
|
||||
|
@ -588,23 +634,20 @@ pub async fn op_crypto_derive_bits(
|
|||
let salt = hkdf::Salt::new(algorithm, salt);
|
||||
let prk = salt.extract(&secret);
|
||||
let info = &[&*info];
|
||||
let okm = prk.expand(info, HkdfOutput(length)).map_err(|_e| {
|
||||
custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"The length provided for HKDF is too large",
|
||||
)
|
||||
})?;
|
||||
let okm = prk
|
||||
.expand(info, HkdfOutput(length))
|
||||
.map_err(|_e| Error::HKDFLengthTooLarge)?;
|
||||
let mut r = vec![0u8; length];
|
||||
okm.fill(&mut r)?;
|
||||
Ok(r.into())
|
||||
}
|
||||
_ => Err(type_error("Unsupported algorithm".to_string())),
|
||||
_ => Err(Error::UnsupportedAlgorithm),
|
||||
}
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, AnyError> {
|
||||
fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, Error> {
|
||||
let public_key = match key_data.r#type {
|
||||
KeyType::Private => {
|
||||
RsaPrivateKey::from_pkcs1_der(&key_data.data)?.to_public_key()
|
||||
|
@ -617,7 +660,7 @@ fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, AnyError> {
|
|||
|
||||
#[op2]
|
||||
#[string]
|
||||
pub fn op_crypto_random_uuid(state: &mut OpState) -> Result<String, AnyError> {
|
||||
pub fn op_crypto_random_uuid(state: &mut OpState) -> Result<String, Error> {
|
||||
let maybe_seeded_rng = state.try_borrow_mut::<StdRng>();
|
||||
let uuid = if let Some(seeded_rng) = maybe_seeded_rng {
|
||||
let mut bytes = [0u8; 16];
|
||||
|
@ -638,7 +681,7 @@ pub fn op_crypto_random_uuid(state: &mut OpState) -> Result<String, AnyError> {
|
|||
pub async fn op_crypto_subtle_digest(
|
||||
#[serde] algorithm: CryptoHash,
|
||||
#[buffer] data: JsBuffer,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, Error> {
|
||||
let output = spawn_blocking(move || {
|
||||
digest::digest(algorithm.into(), &data)
|
||||
.as_ref()
|
||||
|
@ -662,7 +705,7 @@ pub struct WrapUnwrapKeyArg {
|
|||
pub fn op_crypto_wrap_key(
|
||||
#[serde] args: WrapUnwrapKeyArg,
|
||||
#[buffer] data: JsBuffer,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, Error> {
|
||||
let algorithm = args.algorithm;
|
||||
|
||||
match algorithm {
|
||||
|
@ -670,20 +713,20 @@ pub fn op_crypto_wrap_key(
|
|||
let key = args.key.as_secret_key()?;
|
||||
|
||||
if data.len() % 8 != 0 {
|
||||
return Err(type_error("Data must be multiple of 8 bytes"));
|
||||
return Err(Error::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(type_error("Invalid key length")),
|
||||
_ => return Err(Error::InvalidKeyLength),
|
||||
}
|
||||
.map_err(|_| operation_error("encryption error"))?;
|
||||
.map_err(|_| Error::EncryptionError)?;
|
||||
|
||||
Ok(wrapped_key.into())
|
||||
}
|
||||
_ => Err(type_error("Unsupported algorithm")),
|
||||
_ => Err(Error::UnsupportedAlgorithm),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -692,29 +735,27 @@ pub fn op_crypto_wrap_key(
|
|||
pub fn op_crypto_unwrap_key(
|
||||
#[serde] args: WrapUnwrapKeyArg,
|
||||
#[buffer] data: JsBuffer,
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, Error> {
|
||||
let algorithm = args.algorithm;
|
||||
match algorithm {
|
||||
Algorithm::AesKw => {
|
||||
let key = args.key.as_secret_key()?;
|
||||
|
||||
if data.len() % 8 != 0 {
|
||||
return Err(type_error("Data must be multiple of 8 bytes"));
|
||||
return Err(Error::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(type_error("Invalid key length")),
|
||||
_ => return Err(Error::InvalidKeyLength),
|
||||
}
|
||||
.map_err(|_| {
|
||||
operation_error("decryption error - integrity check failed")
|
||||
})?;
|
||||
.map_err(|_| Error::DecryptionError)?;
|
||||
|
||||
Ok(unwrapped_key.into())
|
||||
}
|
||||
_ => Err(type_error("Unsupported algorithm")),
|
||||
_ => Err(Error::UnsupportedAlgorithm),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::JsBuffer;
|
||||
use deno_core::ToJsBuffer;
|
||||
use elliptic_curve::sec1::ToEncodedPoint;
|
||||
|
@ -63,47 +60,73 @@ pub enum RustRawKeyData {
|
|||
Public(ToJsBuffer),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SharedError {
|
||||
#[error("expected valid private key")]
|
||||
ExpectedValidPrivateKey,
|
||||
#[error("expected valid public key")]
|
||||
ExpectedValidPublicKey,
|
||||
#[error("expected valid private EC key")]
|
||||
ExpectedValidPrivateECKey,
|
||||
#[error("expected valid public EC key")]
|
||||
ExpectedValidPublicECKey,
|
||||
#[error("expected private key")]
|
||||
ExpectedPrivateKey,
|
||||
#[error("expected public key")]
|
||||
ExpectedPublicKey,
|
||||
#[error("expected secret key")]
|
||||
ExpectedSecretKey,
|
||||
#[error("failed to decode private key")]
|
||||
FailedDecodePrivateKey,
|
||||
#[error("failed to decode public key")]
|
||||
FailedDecodePublicKey,
|
||||
#[error("unsupported format")]
|
||||
UnsupportedFormat,
|
||||
}
|
||||
|
||||
impl V8RawKeyData {
|
||||
pub fn as_rsa_public_key(&self) -> Result<Cow<'_, [u8]>, AnyError> {
|
||||
pub fn as_rsa_public_key(&self) -> Result<Cow<'_, [u8]>, SharedError> {
|
||||
match self {
|
||||
V8RawKeyData::Public(data) => Ok(Cow::Borrowed(data)),
|
||||
V8RawKeyData::Private(data) => {
|
||||
let private_key = RsaPrivateKey::from_pkcs1_der(data)
|
||||
.map_err(|_| type_error("expected valid private key"))?;
|
||||
.map_err(|_| SharedError::ExpectedValidPrivateKey)?;
|
||||
|
||||
let public_key_doc = private_key
|
||||
.to_public_key()
|
||||
.to_pkcs1_der()
|
||||
.map_err(|_| type_error("expected valid public key"))?;
|
||||
.map_err(|_| SharedError::ExpectedValidPublicKey)?;
|
||||
|
||||
Ok(Cow::Owned(public_key_doc.as_bytes().into()))
|
||||
}
|
||||
_ => Err(type_error("expected public key")),
|
||||
_ => Err(SharedError::ExpectedPublicKey),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_rsa_private_key(&self) -> Result<&[u8], AnyError> {
|
||||
pub fn as_rsa_private_key(&self) -> Result<&[u8], SharedError> {
|
||||
match self {
|
||||
V8RawKeyData::Private(data) => Ok(data),
|
||||
_ => Err(type_error("expected private key")),
|
||||
_ => Err(SharedError::ExpectedPrivateKey),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_secret_key(&self) -> Result<&[u8], AnyError> {
|
||||
pub fn as_secret_key(&self) -> Result<&[u8], SharedError> {
|
||||
match self {
|
||||
V8RawKeyData::Secret(data) => Ok(data),
|
||||
_ => Err(type_error("expected secret key")),
|
||||
_ => Err(SharedError::ExpectedSecretKey),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ec_public_key_p256(&self) -> Result<p256::EncodedPoint, AnyError> {
|
||||
pub fn as_ec_public_key_p256(
|
||||
&self,
|
||||
) -> Result<p256::EncodedPoint, SharedError> {
|
||||
match self {
|
||||
V8RawKeyData::Public(data) => p256::PublicKey::from_sec1_bytes(data)
|
||||
.map(|p| p.to_encoded_point(false))
|
||||
.map_err(|_| type_error("expected valid public EC key")),
|
||||
.map_err(|_| SharedError::ExpectedValidPublicECKey),
|
||||
V8RawKeyData::Private(data) => {
|
||||
let signing_key = p256::SecretKey::from_pkcs8_der(data)
|
||||
.map_err(|_| type_error("expected valid private EC key"))?;
|
||||
.map_err(|_| SharedError::ExpectedValidPrivateECKey)?;
|
||||
Ok(signing_key.public_key().to_encoded_point(false))
|
||||
}
|
||||
// Should never reach here.
|
||||
|
@ -111,14 +134,16 @@ impl V8RawKeyData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_ec_public_key_p384(&self) -> Result<p384::EncodedPoint, AnyError> {
|
||||
pub fn as_ec_public_key_p384(
|
||||
&self,
|
||||
) -> Result<p384::EncodedPoint, SharedError> {
|
||||
match self {
|
||||
V8RawKeyData::Public(data) => p384::PublicKey::from_sec1_bytes(data)
|
||||
.map(|p| p.to_encoded_point(false))
|
||||
.map_err(|_| type_error("expected valid public EC key")),
|
||||
.map_err(|_| SharedError::ExpectedValidPublicECKey),
|
||||
V8RawKeyData::Private(data) => {
|
||||
let signing_key = p384::SecretKey::from_pkcs8_der(data)
|
||||
.map_err(|_| type_error("expected valid private EC key"))?;
|
||||
.map_err(|_| SharedError::ExpectedValidPrivateECKey)?;
|
||||
Ok(signing_key.public_key().to_encoded_point(false))
|
||||
}
|
||||
// Should never reach here.
|
||||
|
@ -126,16 +151,18 @@ impl V8RawKeyData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_ec_public_key_p521(&self) -> Result<p521::EncodedPoint, AnyError> {
|
||||
pub fn as_ec_public_key_p521(
|
||||
&self,
|
||||
) -> Result<p521::EncodedPoint, SharedError> {
|
||||
match self {
|
||||
V8RawKeyData::Public(data) => {
|
||||
// public_key is a serialized EncodedPoint
|
||||
p521::EncodedPoint::from_bytes(data)
|
||||
.map_err(|_| type_error("expected valid public EC key"))
|
||||
.map_err(|_| SharedError::ExpectedValidPublicECKey)
|
||||
}
|
||||
V8RawKeyData::Private(data) => {
|
||||
let signing_key = p521::SecretKey::from_pkcs8_der(data)
|
||||
.map_err(|_| type_error("expected valid private EC key"))?;
|
||||
.map_err(|_| SharedError::ExpectedValidPrivateECKey)?;
|
||||
Ok(signing_key.public_key().to_encoded_point(false))
|
||||
}
|
||||
// Should never reach here.
|
||||
|
@ -143,26 +170,10 @@ impl V8RawKeyData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_ec_private_key(&self) -> Result<&[u8], AnyError> {
|
||||
pub fn as_ec_private_key(&self) -> Result<&[u8], SharedError> {
|
||||
match self {
|
||||
V8RawKeyData::Private(data) => Ok(data),
|
||||
_ => Err(type_error("expected private key")),
|
||||
_ => Err(SharedError::ExpectedPrivateKey),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data_error(msg: impl Into<Cow<'static, str>>) -> AnyError {
|
||||
custom_error("DOMExceptionDataError", msg)
|
||||
}
|
||||
|
||||
pub fn not_supported_error(msg: impl Into<Cow<'static, str>>) -> AnyError {
|
||||
custom_error("DOMExceptionNotSupportedError", msg)
|
||||
}
|
||||
|
||||
pub fn operation_error(msg: impl Into<Cow<'static, str>>) -> AnyError {
|
||||
custom_error("DOMExceptionOperationError", msg)
|
||||
}
|
||||
|
||||
pub fn unsupported_format() -> AnyError {
|
||||
not_supported_error("unsupported format")
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use curve25519_dalek::montgomery::MontgomeryPoint;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::ToJsBuffer;
|
||||
use elliptic_curve::pkcs8::PrivateKeyInfo;
|
||||
|
@ -13,6 +11,14 @@ use spki::der::asn1::BitString;
|
|||
use spki::der::Decode;
|
||||
use spki::der::Encode;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum X25519Error {
|
||||
#[error("Failed to export key")]
|
||||
FailedExport,
|
||||
#[error(transparent)]
|
||||
Der(#[from] spki::der::Error),
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
pub fn op_crypto_generate_x25519_keypair(
|
||||
#[buffer] pkey: &mut [u8],
|
||||
|
@ -113,7 +119,7 @@ pub fn op_crypto_import_pkcs8_x25519(
|
|||
#[serde]
|
||||
pub fn op_crypto_export_spki_x25519(
|
||||
#[buffer] pubkey: &[u8],
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, X25519Error> {
|
||||
let key_info = spki::SubjectPublicKeyInfo {
|
||||
algorithm: spki::AlgorithmIdentifierRef {
|
||||
// id-X25519
|
||||
|
@ -125,9 +131,7 @@ pub fn op_crypto_export_spki_x25519(
|
|||
Ok(
|
||||
key_info
|
||||
.to_der()
|
||||
.map_err(|_| {
|
||||
custom_error("DOMExceptionOperationError", "Failed to export key")
|
||||
})?
|
||||
.map_err(|_| X25519Error::FailedExport)?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
@ -136,7 +140,7 @@ pub fn op_crypto_export_spki_x25519(
|
|||
#[serde]
|
||||
pub fn op_crypto_export_pkcs8_x25519(
|
||||
#[buffer] pkey: &[u8],
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, X25519Error> {
|
||||
use rsa::pkcs1::der::Encode;
|
||||
|
||||
// This should probably use OneAsymmetricKey instead
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::ToJsBuffer;
|
||||
use ed448_goldilocks::curve::MontgomeryPoint;
|
||||
|
@ -13,6 +12,14 @@ use spki::der::asn1::BitString;
|
|||
use spki::der::Decode;
|
||||
use spki::der::Encode;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum X448Error {
|
||||
#[error("Failed to export key")]
|
||||
FailedExport,
|
||||
#[error(transparent)]
|
||||
Der(#[from] spki::der::Error),
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
pub fn op_crypto_generate_x448_keypair(
|
||||
#[buffer] pkey: &mut [u8],
|
||||
|
@ -56,7 +63,7 @@ const X448_OID: const_oid::ObjectIdentifier =
|
|||
#[serde]
|
||||
pub fn op_crypto_export_spki_x448(
|
||||
#[buffer] pubkey: &[u8],
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, X448Error> {
|
||||
let key_info = spki::SubjectPublicKeyInfo {
|
||||
algorithm: spki::AlgorithmIdentifierRef {
|
||||
oid: X448_OID,
|
||||
|
@ -67,9 +74,7 @@ pub fn op_crypto_export_spki_x448(
|
|||
Ok(
|
||||
key_info
|
||||
.to_der()
|
||||
.map_err(|_| {
|
||||
custom_error("DOMExceptionOperationError", "Failed to export key")
|
||||
})?
|
||||
.map_err(|_| X448Error::FailedExport)?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
@ -78,7 +83,7 @@ pub fn op_crypto_export_spki_x448(
|
|||
#[serde]
|
||||
pub fn op_crypto_export_pkcs8_x448(
|
||||
#[buffer] pkey: &[u8],
|
||||
) -> Result<ToJsBuffer, AnyError> {
|
||||
) -> Result<ToJsBuffer, X448Error> {
|
||||
use rsa::pkcs1::der::Encode;
|
||||
|
||||
let pk_info = rsa::pkcs8::PrivateKeyInfo {
|
||||
|
|
|
@ -61,6 +61,15 @@ const _mimeType = Symbol("mime type");
|
|||
const _body = Symbol("body");
|
||||
const _brand = webidl.brand;
|
||||
|
||||
// it's slightly faster to cache these
|
||||
const webidlConvertersBodyInitDomString =
|
||||
webidl.converters["BodyInit_DOMString?"];
|
||||
const webidlConvertersUSVString = webidl.converters["USVString"];
|
||||
const webidlConvertersUnsignedShort = webidl.converters["unsigned short"];
|
||||
const webidlConvertersAny = webidl.converters["any"];
|
||||
const webidlConvertersByteString = webidl.converters["ByteString"];
|
||||
const webidlConvertersHeadersInit = webidl.converters["HeadersInit"];
|
||||
|
||||
/**
|
||||
* @typedef InnerResponse
|
||||
* @property {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"} type
|
||||
|
@ -259,8 +268,8 @@ class Response {
|
|||
*/
|
||||
static redirect(url, status = 302) {
|
||||
const prefix = "Failed to execute 'Response.redirect'";
|
||||
url = webidl.converters["USVString"](url, prefix, "Argument 1");
|
||||
status = webidl.converters["unsigned short"](status, prefix, "Argument 2");
|
||||
url = webidlConvertersUSVString(url, prefix, "Argument 1");
|
||||
status = webidlConvertersUnsignedShort(status, prefix, "Argument 2");
|
||||
|
||||
const baseURL = getLocationHref();
|
||||
const parsedURL = new URL(url, baseURL);
|
||||
|
@ -286,8 +295,8 @@ class Response {
|
|||
*/
|
||||
static json(data = undefined, init = { __proto__: null }) {
|
||||
const prefix = "Failed to execute 'Response.json'";
|
||||
data = webidl.converters.any(data);
|
||||
init = webidl.converters["ResponseInit_fast"](init, prefix, "Argument 2");
|
||||
data = webidlConvertersAny(data);
|
||||
init = webidlConvertersResponseInitFast(init, prefix, "Argument 2");
|
||||
|
||||
const str = serializeJSValueToJSONString(data);
|
||||
const res = extractBody(str);
|
||||
|
@ -313,8 +322,8 @@ class Response {
|
|||
}
|
||||
|
||||
const prefix = "Failed to construct 'Response'";
|
||||
body = webidl.converters["BodyInit_DOMString?"](body, prefix, "Argument 1");
|
||||
init = webidl.converters["ResponseInit_fast"](init, prefix, "Argument 2");
|
||||
body = webidlConvertersBodyInitDomString(body, prefix, "Argument 1");
|
||||
init = webidlConvertersResponseInitFast(init, prefix, "Argument 2");
|
||||
|
||||
this[_response] = newInnerResponse();
|
||||
this[_headers] = headersFromHeaderList(
|
||||
|
@ -443,22 +452,24 @@ webidl.converters["Response"] = webidl.createInterfaceConverter(
|
|||
"Response",
|
||||
ResponsePrototype,
|
||||
);
|
||||
webidl.converters["ResponseInit"] = webidl.createDictionaryConverter(
|
||||
const webidlConvertersResponseInit = webidl.converters["ResponseInit"] = webidl
|
||||
.createDictionaryConverter(
|
||||
"ResponseInit",
|
||||
[{
|
||||
key: "status",
|
||||
defaultValue: 200,
|
||||
converter: webidl.converters["unsigned short"],
|
||||
converter: webidlConvertersUnsignedShort,
|
||||
}, {
|
||||
key: "statusText",
|
||||
defaultValue: "",
|
||||
converter: webidl.converters["ByteString"],
|
||||
converter: webidlConvertersByteString,
|
||||
}, {
|
||||
key: "headers",
|
||||
converter: webidl.converters["HeadersInit"],
|
||||
converter: webidlConvertersHeadersInit,
|
||||
}],
|
||||
);
|
||||
webidl.converters["ResponseInit_fast"] = function (
|
||||
const webidlConvertersResponseInitFast = webidl
|
||||
.converters["ResponseInit_fast"] = function (
|
||||
init,
|
||||
prefix,
|
||||
context,
|
||||
|
@ -471,18 +482,18 @@ webidl.converters["ResponseInit_fast"] = function (
|
|||
if (typeof init === "object" && !core.isProxy(init)) {
|
||||
// Not a proxy fast path
|
||||
const status = init.status !== undefined
|
||||
? webidl.converters["unsigned short"](init.status)
|
||||
? webidlConvertersUnsignedShort(init.status)
|
||||
: 200;
|
||||
const statusText = init.statusText !== undefined
|
||||
? webidl.converters["ByteString"](init.statusText)
|
||||
? webidlConvertersByteString(init.statusText)
|
||||
: "";
|
||||
const headers = init.headers !== undefined
|
||||
? webidl.converters["HeadersInit"](init.headers)
|
||||
? webidlConvertersHeadersInit(init.headers)
|
||||
: undefined;
|
||||
return { status, statusText, headers };
|
||||
}
|
||||
// Slow default path
|
||||
return webidl.converters["ResponseInit"](init, prefix, context, opts);
|
||||
return webidlConvertersResponseInit(init, prefix, context, opts);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_fetch"
|
||||
version = "0.195.0"
|
||||
version = "0.197.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -32,6 +32,7 @@ percent-encoding.workspace = true
|
|||
rustls-webpki.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-rustls.workspace = true
|
||||
tokio-socks.workspace = true
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::CancelHandle;
|
|||
use crate::CancelableResponseFuture;
|
||||
use crate::FetchHandler;
|
||||
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::futures::TryFutureExt;
|
||||
use deno_core::futures::TryStreamExt;
|
||||
|
@ -42,9 +41,7 @@ impl FetchHandler for FsFetchHandler {
|
|||
.map_err(|_| ())?;
|
||||
Ok::<_, ()>(response)
|
||||
}
|
||||
.map_err(move |_| {
|
||||
type_error("NetworkError when attempting to fetch resource")
|
||||
})
|
||||
.map_err(move |_| super::FetchError::NetworkError)
|
||||
.or_cancel(&cancel_handle)
|
||||
.boxed_local();
|
||||
|
||||
|
|
224
ext/fetch/lib.rs
224
ext/fetch/lib.rs
|
@ -17,10 +17,6 @@ use std::sync::Arc;
|
|||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::anyhow::Error;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::stream::Peekable;
|
||||
use deno_core::futures::Future;
|
||||
use deno_core::futures::FutureExt;
|
||||
|
@ -28,6 +24,7 @@ use deno_core::futures::Stream;
|
|||
use deno_core::futures::StreamExt;
|
||||
use deno_core::futures::TryFutureExt;
|
||||
use deno_core::op2;
|
||||
use deno_core::url;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::AsyncRefCell;
|
||||
use deno_core::AsyncResult;
|
||||
|
@ -87,15 +84,18 @@ pub struct Options {
|
|||
pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
|
||||
pub proxy: Option<Proxy>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub request_builder_hook:
|
||||
Option<fn(&mut http::Request<ReqBody>) -> Result<(), AnyError>>,
|
||||
pub request_builder_hook: Option<
|
||||
fn(&mut http::Request<ReqBody>) -> Result<(), deno_core::error::AnyError>,
|
||||
>,
|
||||
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
|
||||
pub client_cert_chain_and_key: TlsKeys,
|
||||
pub file_fetch_handler: Rc<dyn FetchHandler>,
|
||||
}
|
||||
|
||||
impl Options {
|
||||
pub fn root_cert_store(&self) -> Result<Option<RootCertStore>, AnyError> {
|
||||
pub fn root_cert_store(
|
||||
&self,
|
||||
) -> Result<Option<RootCertStore>, deno_core::error::AnyError> {
|
||||
Ok(match &self.root_cert_store_provider {
|
||||
Some(provider) => Some(provider.get_or_try_init()?.clone()),
|
||||
None => None,
|
||||
|
@ -144,6 +144,51 @@ deno_core::extension!(deno_fetch,
|
|||
},
|
||||
);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum FetchError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Permission(deno_core::error::AnyError),
|
||||
#[error("NetworkError when attempting to fetch resource")]
|
||||
NetworkError,
|
||||
#[error("Fetching files only supports the GET method: received {0}")]
|
||||
FsNotGet(Method),
|
||||
#[error("Invalid URL {0}")]
|
||||
InvalidUrl(Url),
|
||||
#[error(transparent)]
|
||||
InvalidHeaderName(#[from] http::header::InvalidHeaderName),
|
||||
#[error(transparent)]
|
||||
InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
|
||||
#[error("{0:?}")]
|
||||
DataUrl(data_url::DataUrlError),
|
||||
#[error("{0:?}")]
|
||||
Base64(data_url::forgiving_base64::InvalidBase64),
|
||||
#[error("Blob for the given URL not found.")]
|
||||
BlobNotFound,
|
||||
#[error("Url scheme '{0}' not supported")]
|
||||
SchemeNotSupported(String),
|
||||
#[error("Request was cancelled")]
|
||||
RequestCanceled,
|
||||
#[error(transparent)]
|
||||
Http(#[from] http::Error),
|
||||
#[error(transparent)]
|
||||
ClientCreate(#[from] HttpClientCreateError),
|
||||
#[error(transparent)]
|
||||
Url(#[from] url::ParseError),
|
||||
#[error(transparent)]
|
||||
Method(#[from] http::method::InvalidMethod),
|
||||
#[error(transparent)]
|
||||
ClientSend(#[from] ClientSendError),
|
||||
#[error(transparent)]
|
||||
RequestBuilderHook(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
// Only used for node upgrade
|
||||
#[error(transparent)]
|
||||
Hyper(#[from] hyper::Error),
|
||||
}
|
||||
|
||||
pub type CancelableResponseFuture =
|
||||
Pin<Box<dyn Future<Output = CancelableResponseResult>>>;
|
||||
|
||||
|
@ -170,11 +215,7 @@ impl FetchHandler for DefaultFileFetchHandler {
|
|||
_state: &mut OpState,
|
||||
_url: &Url,
|
||||
) -> (CancelableResponseFuture, Option<Rc<CancelHandle>>) {
|
||||
let fut = async move {
|
||||
Ok(Err(type_error(
|
||||
"NetworkError when attempting to fetch resource",
|
||||
)))
|
||||
};
|
||||
let fut = async move { Ok(Err(FetchError::NetworkError)) };
|
||||
(Box::pin(fut), None)
|
||||
}
|
||||
}
|
||||
|
@ -191,7 +232,7 @@ pub struct FetchReturn {
|
|||
|
||||
pub fn get_or_create_client_from_state(
|
||||
state: &mut OpState,
|
||||
) -> Result<Client, AnyError> {
|
||||
) -> Result<Client, HttpClientCreateError> {
|
||||
if let Some(client) = state.try_borrow::<Client>() {
|
||||
Ok(client.clone())
|
||||
} else {
|
||||
|
@ -204,11 +245,13 @@ pub fn get_or_create_client_from_state(
|
|||
|
||||
pub fn create_client_from_options(
|
||||
options: &Options,
|
||||
) -> Result<Client, AnyError> {
|
||||
) -> Result<Client, HttpClientCreateError> {
|
||||
create_http_client(
|
||||
&options.user_agent,
|
||||
CreateHttpClientOptions {
|
||||
root_cert_store: options.root_cert_store()?,
|
||||
root_cert_store: options
|
||||
.root_cert_store()
|
||||
.map_err(HttpClientCreateError::RootCertStore)?,
|
||||
ca_certs: vec![],
|
||||
proxy: options.proxy.clone(),
|
||||
unsafely_ignore_certificate_errors: options
|
||||
|
@ -230,7 +273,9 @@ pub fn create_client_from_options(
|
|||
#[allow(clippy::type_complexity)]
|
||||
pub struct ResourceToBodyAdapter(
|
||||
Rc<dyn Resource>,
|
||||
Option<Pin<Box<dyn Future<Output = Result<BufView, Error>>>>>,
|
||||
Option<
|
||||
Pin<Box<dyn Future<Output = Result<BufView, deno_core::error::AnyError>>>>,
|
||||
>,
|
||||
);
|
||||
|
||||
impl ResourceToBodyAdapter {
|
||||
|
@ -246,7 +291,7 @@ unsafe impl Send for ResourceToBodyAdapter {}
|
|||
unsafe impl Sync for ResourceToBodyAdapter {}
|
||||
|
||||
impl Stream for ResourceToBodyAdapter {
|
||||
type Item = Result<Bytes, Error>;
|
||||
type Item = Result<Bytes, deno_core::error::AnyError>;
|
||||
|
||||
fn poll_next(
|
||||
self: Pin<&mut Self>,
|
||||
|
@ -276,7 +321,7 @@ impl Stream for ResourceToBodyAdapter {
|
|||
|
||||
impl hyper::body::Body for ResourceToBodyAdapter {
|
||||
type Data = Bytes;
|
||||
type Error = Error;
|
||||
type Error = deno_core::error::AnyError;
|
||||
|
||||
fn poll_frame(
|
||||
self: Pin<&mut Self>,
|
||||
|
@ -301,13 +346,13 @@ pub trait FetchPermissions {
|
|||
&mut self,
|
||||
url: &Url,
|
||||
api_name: &str,
|
||||
) -> Result<(), AnyError>;
|
||||
) -> Result<(), deno_core::error::AnyError>;
|
||||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
fn check_read<'a>(
|
||||
&mut self,
|
||||
p: &'a Path,
|
||||
api_name: &str,
|
||||
) -> Result<Cow<'a, Path>, AnyError>;
|
||||
) -> Result<Cow<'a, Path>, deno_core::error::AnyError>;
|
||||
}
|
||||
|
||||
impl FetchPermissions for deno_permissions::PermissionsContainer {
|
||||
|
@ -316,7 +361,7 @@ impl FetchPermissions for deno_permissions::PermissionsContainer {
|
|||
&mut self,
|
||||
url: &Url,
|
||||
api_name: &str,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
deno_permissions::PermissionsContainer::check_net_url(self, url, api_name)
|
||||
}
|
||||
|
||||
|
@ -325,7 +370,7 @@ impl FetchPermissions for deno_permissions::PermissionsContainer {
|
|||
&mut self,
|
||||
path: &'a Path,
|
||||
api_name: &str,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
) -> Result<Cow<'a, Path>, deno_core::error::AnyError> {
|
||||
deno_permissions::PermissionsContainer::check_read_path(
|
||||
self,
|
||||
path,
|
||||
|
@ -346,12 +391,15 @@ pub fn op_fetch<FP>(
|
|||
has_body: bool,
|
||||
#[buffer] data: Option<JsBuffer>,
|
||||
#[smi] resource: Option<ResourceId>,
|
||||
) -> Result<FetchReturn, AnyError>
|
||||
) -> Result<FetchReturn, FetchError>
|
||||
where
|
||||
FP: FetchPermissions + 'static,
|
||||
{
|
||||
let (client, allow_host) = if let Some(rid) = client_rid {
|
||||
let r = state.resource_table.get::<HttpClientResource>(rid)?;
|
||||
let r = state
|
||||
.resource_table
|
||||
.get::<HttpClientResource>(rid)
|
||||
.map_err(FetchError::Resource)?;
|
||||
(r.client.clone(), r.allow_host)
|
||||
} else {
|
||||
(get_or_create_client_from_state(state)?, false)
|
||||
|
@ -364,20 +412,18 @@ where
|
|||
let scheme = url.scheme();
|
||||
let (request_rid, cancel_handle_rid) = match scheme {
|
||||
"file" => {
|
||||
let path = url.to_file_path().map_err(|_| {
|
||||
type_error("NetworkError when attempting to fetch resource")
|
||||
})?;
|
||||
let path = url.to_file_path().map_err(|_| FetchError::NetworkError)?;
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
let path = permissions.check_read(&path, "fetch()")?;
|
||||
let path = permissions
|
||||
.check_read(&path, "fetch()")
|
||||
.map_err(FetchError::Permission)?;
|
||||
let url = match path {
|
||||
Cow::Owned(path) => Url::from_file_path(path).unwrap(),
|
||||
Cow::Borrowed(_) => url,
|
||||
};
|
||||
|
||||
if method != Method::GET {
|
||||
return Err(type_error(format!(
|
||||
"Fetching files only supports the GET method: received {method}"
|
||||
)));
|
||||
return Err(FetchError::FsNotGet(method));
|
||||
}
|
||||
|
||||
let Options {
|
||||
|
@ -396,13 +442,15 @@ where
|
|||
}
|
||||
"http" | "https" => {
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_net_url(&url, "fetch()")?;
|
||||
permissions
|
||||
.check_net_url(&url, "fetch()")
|
||||
.map_err(FetchError::Resource)?;
|
||||
|
||||
let maybe_authority = extract_authority(&mut url);
|
||||
let uri = url
|
||||
.as_str()
|
||||
.parse::<Uri>()
|
||||
.map_err(|_| type_error(format!("Invalid URL {url}")))?;
|
||||
.map_err(|_| FetchError::InvalidUrl(url.clone()))?;
|
||||
|
||||
let mut con_len = None;
|
||||
let body = if has_body {
|
||||
|
@ -416,7 +464,10 @@ where
|
|||
.boxed()
|
||||
}
|
||||
(_, Some(resource)) => {
|
||||
let resource = state.resource_table.take_any(resource)?;
|
||||
let resource = state
|
||||
.resource_table
|
||||
.take_any(resource)
|
||||
.map_err(FetchError::Resource)?;
|
||||
match resource.size_hint() {
|
||||
(body_size, Some(n)) if body_size == n && body_size > 0 => {
|
||||
con_len = Some(body_size);
|
||||
|
@ -453,10 +504,8 @@ where
|
|||
}
|
||||
|
||||
for (key, value) in headers {
|
||||
let name = HeaderName::from_bytes(&key)
|
||||
.map_err(|err| type_error(err.to_string()))?;
|
||||
let v = HeaderValue::from_bytes(&value)
|
||||
.map_err(|err| type_error(err.to_string()))?;
|
||||
let name = HeaderName::from_bytes(&key)?;
|
||||
let v = HeaderValue::from_bytes(&value)?;
|
||||
|
||||
if (name != HOST || allow_host) && name != CONTENT_LENGTH {
|
||||
request.headers_mut().append(name, v);
|
||||
|
@ -474,20 +523,18 @@ where
|
|||
let options = state.borrow::<Options>();
|
||||
if let Some(request_builder_hook) = options.request_builder_hook {
|
||||
request_builder_hook(&mut request)
|
||||
.map_err(|err| type_error(err.to_string()))?;
|
||||
.map_err(FetchError::RequestBuilderHook)?;
|
||||
}
|
||||
|
||||
let cancel_handle = CancelHandle::new_rc();
|
||||
let cancel_handle_ = cancel_handle.clone();
|
||||
|
||||
let fut = {
|
||||
async move {
|
||||
let fut = async move {
|
||||
client
|
||||
.send(request)
|
||||
.map_err(Into::into)
|
||||
.or_cancel(cancel_handle_)
|
||||
.await
|
||||
}
|
||||
};
|
||||
|
||||
let request_rid = state.resource_table.add(FetchRequestResource {
|
||||
|
@ -501,12 +548,10 @@ where
|
|||
(request_rid, Some(cancel_handle_rid))
|
||||
}
|
||||
"data" => {
|
||||
let data_url = DataUrl::process(url.as_str())
|
||||
.map_err(|e| type_error(format!("{e:?}")))?;
|
||||
let data_url =
|
||||
DataUrl::process(url.as_str()).map_err(FetchError::DataUrl)?;
|
||||
|
||||
let (body, _) = data_url
|
||||
.decode_to_vec()
|
||||
.map_err(|e| type_error(format!("{e:?}")))?;
|
||||
let (body, _) = data_url.decode_to_vec().map_err(FetchError::Base64)?;
|
||||
let body = http_body_util::Full::new(body.into())
|
||||
.map_err(|never| match never {})
|
||||
.boxed();
|
||||
|
@ -528,11 +573,9 @@ where
|
|||
"blob" => {
|
||||
// Blob URL resolution happens in the JS side of fetch. If we got here is
|
||||
// because the URL isn't an object URL.
|
||||
return Err(type_error("Blob for the given URL not found."));
|
||||
}
|
||||
_ => {
|
||||
return Err(type_error(format!("Url scheme '{scheme}' not supported")))
|
||||
return Err(FetchError::BlobNotFound);
|
||||
}
|
||||
_ => return Err(FetchError::SchemeNotSupported(scheme.to_string())),
|
||||
};
|
||||
|
||||
Ok(FetchReturn {
|
||||
|
@ -564,11 +607,12 @@ pub struct FetchResponse {
|
|||
pub async fn op_fetch_send(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<FetchResponse, AnyError> {
|
||||
) -> Result<FetchResponse, FetchError> {
|
||||
let request = state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.take::<FetchRequestResource>(rid)?;
|
||||
.take::<FetchRequestResource>(rid)
|
||||
.map_err(FetchError::Resource)?;
|
||||
|
||||
let request = Rc::try_unwrap(request)
|
||||
.ok()
|
||||
|
@ -581,9 +625,10 @@ pub async fn op_fetch_send(
|
|||
// If any error in the chain is a hyper body error, return that as a special result we can use to
|
||||
// reconstruct an error chain (eg: `new TypeError(..., { cause: new Error(...) })`).
|
||||
// TODO(mmastrac): it would be a lot easier if we just passed a v8::Global through here instead
|
||||
let mut err_ref: &dyn std::error::Error = err.as_ref();
|
||||
while let Some(err_src) = std::error::Error::source(err_ref) {
|
||||
if let Some(err_src) = err_src.downcast_ref::<hyper::Error>() {
|
||||
|
||||
if let FetchError::ClientSend(err_src) = &err {
|
||||
if let Some(client_err) = std::error::Error::source(&err_src.source) {
|
||||
if let Some(err_src) = client_err.downcast_ref::<hyper::Error>() {
|
||||
if let Some(err_src) = std::error::Error::source(err_src) {
|
||||
return Ok(FetchResponse {
|
||||
error: Some((err.to_string(), err_src.to_string())),
|
||||
|
@ -591,12 +636,12 @@ pub async fn op_fetch_send(
|
|||
});
|
||||
}
|
||||
}
|
||||
err_ref = err_src;
|
||||
}
|
||||
}
|
||||
|
||||
return Err(type_error(err.to_string()));
|
||||
return Err(err);
|
||||
}
|
||||
Err(_) => return Err(type_error("Request was cancelled")),
|
||||
Err(_) => return Err(FetchError::RequestCanceled),
|
||||
};
|
||||
|
||||
let status = res.status();
|
||||
|
@ -636,7 +681,7 @@ pub async fn op_fetch_send(
|
|||
}
|
||||
|
||||
type CancelableResponseResult =
|
||||
Result<Result<http::Response<ResBody>, AnyError>, Canceled>;
|
||||
Result<Result<http::Response<ResBody>, FetchError>, Canceled>;
|
||||
|
||||
pub struct FetchRequestResource {
|
||||
pub future: Pin<Box<dyn Future<Output = CancelableResponseResult>>>,
|
||||
|
@ -691,7 +736,7 @@ impl FetchResponseResource {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn upgrade(self) -> Result<hyper::upgrade::Upgraded, AnyError> {
|
||||
pub async fn upgrade(self) -> Result<hyper::upgrade::Upgraded, hyper::Error> {
|
||||
let reader = self.response_reader.into_inner();
|
||||
match reader {
|
||||
FetchResponseReader::Start(resp) => Ok(hyper::upgrade::on(resp).await?),
|
||||
|
@ -746,7 +791,9 @@ 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(type_error(err.to_string())),
|
||||
Err(err) => {
|
||||
break Err(deno_core::error::type_error(err.to_string()))
|
||||
}
|
||||
},
|
||||
None => break Ok(BufView::empty()),
|
||||
}
|
||||
|
@ -809,14 +856,16 @@ pub fn op_fetch_custom_client<FP>(
|
|||
state: &mut OpState,
|
||||
#[serde] args: CreateHttpClientArgs,
|
||||
#[cppgc] tls_keys: &TlsKeysHolder,
|
||||
) -> Result<ResourceId, AnyError>
|
||||
) -> Result<ResourceId, FetchError>
|
||||
where
|
||||
FP: FetchPermissions + 'static,
|
||||
{
|
||||
if let Some(proxy) = args.proxy.clone() {
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
let url = Url::parse(&proxy.url)?;
|
||||
permissions.check_net_url(&url, "Deno.createHttpClient()")?;
|
||||
permissions
|
||||
.check_net_url(&url, "Deno.createHttpClient()")
|
||||
.map_err(FetchError::Permission)?;
|
||||
}
|
||||
|
||||
let options = state.borrow::<Options>();
|
||||
|
@ -829,7 +878,9 @@ where
|
|||
let client = create_http_client(
|
||||
&options.user_agent,
|
||||
CreateHttpClientOptions {
|
||||
root_cert_store: options.root_cert_store()?,
|
||||
root_cert_store: options
|
||||
.root_cert_store()
|
||||
.map_err(HttpClientCreateError::RootCertStore)?,
|
||||
ca_certs,
|
||||
proxy: args.proxy,
|
||||
unsafely_ignore_certificate_errors: options
|
||||
|
@ -887,19 +938,34 @@ impl Default for CreateHttpClientOptions {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum HttpClientCreateError {
|
||||
#[error(transparent)]
|
||||
Tls(deno_tls::TlsError),
|
||||
#[error("Illegal characters in User-Agent: received {0}")]
|
||||
InvalidUserAgent(String),
|
||||
#[error("invalid proxy url")]
|
||||
InvalidProxyUrl,
|
||||
#[error("Cannot create Http Client: either `http1` or `http2` needs to be set to true")]
|
||||
HttpVersionSelectionInvalid,
|
||||
#[error(transparent)]
|
||||
RootCertStore(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
/// Create new instance of async Client. This client supports
|
||||
/// proxies and doesn't follow redirects.
|
||||
pub fn create_http_client(
|
||||
user_agent: &str,
|
||||
options: CreateHttpClientOptions,
|
||||
) -> Result<Client, AnyError> {
|
||||
) -> Result<Client, HttpClientCreateError> {
|
||||
let mut tls_config = deno_tls::create_client_config(
|
||||
options.root_cert_store,
|
||||
options.ca_certs,
|
||||
options.unsafely_ignore_certificate_errors,
|
||||
options.client_cert_chain_and_key.into(),
|
||||
deno_tls::SocketUse::Http,
|
||||
)?;
|
||||
)
|
||||
.map_err(HttpClientCreateError::Tls)?;
|
||||
|
||||
// Proxy TLS should not send ALPN
|
||||
tls_config.alpn_protocols.clear();
|
||||
|
@ -919,9 +985,7 @@ pub fn create_http_client(
|
|||
http_connector.enforce_http(false);
|
||||
|
||||
let user_agent = user_agent.parse::<HeaderValue>().map_err(|_| {
|
||||
type_error(format!(
|
||||
"Illegal characters in User-Agent: received {user_agent}"
|
||||
))
|
||||
HttpClientCreateError::InvalidUserAgent(user_agent.to_string())
|
||||
})?;
|
||||
|
||||
let mut builder =
|
||||
|
@ -932,7 +996,7 @@ pub fn create_http_client(
|
|||
let mut proxies = proxy::from_env();
|
||||
if let Some(proxy) = options.proxy {
|
||||
let mut intercept = proxy::Intercept::all(&proxy.url)
|
||||
.ok_or_else(|| type_error("invalid proxy url"))?;
|
||||
.ok_or_else(|| HttpClientCreateError::InvalidProxyUrl)?;
|
||||
if let Some(basic_auth) = &proxy.basic_auth {
|
||||
intercept.set_auth(&basic_auth.username, &basic_auth.password);
|
||||
}
|
||||
|
@ -964,7 +1028,7 @@ pub fn create_http_client(
|
|||
}
|
||||
(true, true) => {}
|
||||
(false, false) => {
|
||||
return Err(type_error("Cannot create Http Client: either `http1` or `http2` needs to be set to true"))
|
||||
return Err(HttpClientCreateError::HttpVersionSelectionInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -980,10 +1044,8 @@ pub fn create_http_client(
|
|||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_utf8_to_byte_string(
|
||||
#[string] input: String,
|
||||
) -> Result<ByteString, AnyError> {
|
||||
Ok(input.into())
|
||||
pub fn op_utf8_to_byte_string(#[string] input: String) -> ByteString {
|
||||
input.into()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -1003,7 +1065,7 @@ const STAR_STAR: HeaderValue = HeaderValue::from_static("*/*");
|
|||
#[derive(Debug)]
|
||||
pub struct ClientSendError {
|
||||
uri: Uri,
|
||||
source: hyper_util::client::legacy::Error,
|
||||
pub source: hyper_util::client::legacy::Error,
|
||||
}
|
||||
|
||||
impl ClientSendError {
|
||||
|
@ -1075,12 +1137,14 @@ impl Client {
|
|||
.oneshot(req)
|
||||
.await
|
||||
.map_err(|e| ClientSendError { uri, source: e })?;
|
||||
Ok(resp.map(|b| b.map_err(|e| anyhow!(e)).boxed()))
|
||||
Ok(resp.map(|b| b.map_err(|e| deno_core::anyhow::anyhow!(e)).boxed()))
|
||||
}
|
||||
}
|
||||
|
||||
pub type ReqBody = http_body_util::combinators::BoxBody<Bytes, Error>;
|
||||
pub type ResBody = http_body_util::combinators::BoxBody<Bytes, Error>;
|
||||
pub type ReqBody =
|
||||
http_body_util::combinators::BoxBody<Bytes, deno_core::error::AnyError>;
|
||||
pub type ResBody =
|
||||
http_body_util::combinators::BoxBody<Bytes, deno_core::error::AnyError>;
|
||||
|
||||
/// 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
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_ffi"
|
||||
version = "0.158.0"
|
||||
version = "0.160.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -24,6 +24,8 @@ log.workspace = true
|
|||
serde.workspace = true
|
||||
serde-value = "0.7"
|
||||
serde_json = "1.0"
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { workspace = true, features = ["errhandlingapi", "minwindef", "ntdef", "winbase", "winnt"] }
|
||||
|
|
|
@ -7,9 +7,6 @@ use crate::symbol::NativeType;
|
|||
use crate::symbol::Symbol;
|
||||
use crate::FfiPermissions;
|
||||
use crate::ForeignFunction;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::serde_v8::ExternalPointer;
|
||||
|
@ -24,6 +21,20 @@ use std::ffi::c_void;
|
|||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CallError {
|
||||
#[error(transparent)]
|
||||
IR(#[from] IRError),
|
||||
#[error("Nonblocking FFI call failed: {0}")]
|
||||
NonblockingCallFailure(#[source] tokio::task::JoinError),
|
||||
#[error("Invalid FFI symbol name: '{0}'")]
|
||||
InvalidSymbol(String),
|
||||
#[error(transparent)]
|
||||
Permission(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Callback(#[from] super::CallbackError),
|
||||
}
|
||||
|
||||
// SAFETY: Makes an FFI call
|
||||
unsafe fn ffi_call_rtype_struct(
|
||||
cif: &libffi::middle::Cif,
|
||||
|
@ -45,7 +56,7 @@ pub(crate) fn ffi_call_sync<'scope>(
|
|||
args: v8::FunctionCallbackArguments,
|
||||
symbol: &Symbol,
|
||||
out_buffer: Option<OutBuffer>,
|
||||
) -> Result<NativeValue, AnyError>
|
||||
) -> Result<NativeValue, CallError>
|
||||
where
|
||||
'scope: 'scope,
|
||||
{
|
||||
|
@ -201,7 +212,7 @@ fn ffi_call(
|
|||
parameter_types: &[NativeType],
|
||||
result_type: NativeType,
|
||||
out_buffer: Option<OutBuffer>,
|
||||
) -> Result<FfiValue, AnyError> {
|
||||
) -> FfiValue {
|
||||
let call_args: Vec<Arg> = call_args
|
||||
.iter()
|
||||
.enumerate()
|
||||
|
@ -214,7 +225,7 @@ fn ffi_call(
|
|||
// SAFETY: types in the `Cif` match the actual calling convention and
|
||||
// types of symbol.
|
||||
unsafe {
|
||||
Ok(match result_type {
|
||||
match result_type {
|
||||
NativeType::Void => {
|
||||
cif.call::<()>(fun_ptr, &call_args);
|
||||
FfiValue::Value(Value::from(()))
|
||||
|
@ -267,7 +278,7 @@ fn ffi_call(
|
|||
ffi_call_rtype_struct(cif, &fun_ptr, call_args, out_buffer.unwrap().0);
|
||||
FfiValue::Value(Value::Null)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,14 +291,16 @@ pub fn op_ffi_call_ptr_nonblocking<FP>(
|
|||
#[serde] def: ForeignFunction,
|
||||
parameters: v8::Local<v8::Array>,
|
||||
out_buffer: Option<v8::Local<v8::TypedArray>>,
|
||||
) -> Result<impl Future<Output = Result<FfiValue, AnyError>>, AnyError>
|
||||
) -> Result<impl Future<Output = Result<FfiValue, CallError>>, CallError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(CallError::Permission)?;
|
||||
};
|
||||
|
||||
let symbol = PtrSymbol::new(pointer, &def)?;
|
||||
|
@ -309,7 +322,7 @@ where
|
|||
Ok(async move {
|
||||
let result = join_handle
|
||||
.await
|
||||
.map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??;
|
||||
.map_err(CallError::NonblockingCallFailure)?;
|
||||
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
|
||||
Ok(result)
|
||||
})
|
||||
|
@ -325,16 +338,17 @@ pub fn op_ffi_call_nonblocking(
|
|||
#[string] symbol: String,
|
||||
parameters: v8::Local<v8::Array>,
|
||||
out_buffer: Option<v8::Local<v8::TypedArray>>,
|
||||
) -> Result<impl Future<Output = Result<FfiValue, AnyError>>, AnyError> {
|
||||
) -> Result<impl Future<Output = Result<FfiValue, CallError>>, CallError> {
|
||||
let symbol = {
|
||||
let state = state.borrow();
|
||||
let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?;
|
||||
let resource = state
|
||||
.resource_table
|
||||
.get::<DynamicLibraryResource>(rid)
|
||||
.map_err(CallError::Permission)?;
|
||||
let symbols = &resource.symbols;
|
||||
*symbols
|
||||
.get(&symbol)
|
||||
.ok_or_else(|| {
|
||||
type_error(format!("Invalid FFI symbol name: '{symbol}'"))
|
||||
})?
|
||||
.ok_or_else(|| CallError::InvalidSymbol(symbol))?
|
||||
.clone()
|
||||
};
|
||||
|
||||
|
@ -362,7 +376,7 @@ pub fn op_ffi_call_nonblocking(
|
|||
Ok(async move {
|
||||
let result = join_handle
|
||||
.await
|
||||
.map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??;
|
||||
.map_err(CallError::NonblockingCallFailure)?;
|
||||
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
|
||||
Ok(result)
|
||||
})
|
||||
|
@ -377,14 +391,16 @@ pub fn op_ffi_call_ptr<FP>(
|
|||
#[serde] def: ForeignFunction,
|
||||
parameters: v8::Local<v8::Array>,
|
||||
out_buffer: Option<v8::Local<v8::TypedArray>>,
|
||||
) -> Result<FfiValue, AnyError>
|
||||
) -> Result<FfiValue, CallError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(CallError::Permission)?;
|
||||
};
|
||||
|
||||
let symbol = PtrSymbol::new(pointer, &def)?;
|
||||
|
@ -399,7 +415,7 @@ where
|
|||
&def.parameters,
|
||||
def.result.clone(),
|
||||
out_buffer_ptr,
|
||||
)?;
|
||||
);
|
||||
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
|
||||
Ok(result)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use crate::symbol::NativeType;
|
||||
use crate::FfiPermissions;
|
||||
use crate::ForeignFunction;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::v8::TryCatch;
|
||||
|
@ -34,6 +33,16 @@ thread_local! {
|
|||
static LOCAL_THREAD_ID: RefCell<u32> = const { RefCell::new(0) };
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CallbackError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Permission(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Other(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PtrSymbol {
|
||||
pub cif: libffi::middle::Cif,
|
||||
|
@ -44,7 +53,7 @@ impl PtrSymbol {
|
|||
pub fn new(
|
||||
fn_ptr: *mut c_void,
|
||||
def: &ForeignFunction,
|
||||
) -> Result<Self, AnyError> {
|
||||
) -> Result<Self, CallbackError> {
|
||||
let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _);
|
||||
let cif = libffi::middle::Cif::new(
|
||||
def
|
||||
|
@ -52,8 +61,13 @@ impl PtrSymbol {
|
|||
.clone()
|
||||
.into_iter()
|
||||
.map(libffi::middle::Type::try_from)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
def.result.clone().try_into()?,
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(CallbackError::Other)?,
|
||||
def
|
||||
.result
|
||||
.clone()
|
||||
.try_into()
|
||||
.map_err(CallbackError::Other)?,
|
||||
);
|
||||
|
||||
Ok(Self { cif, ptr })
|
||||
|
@ -522,10 +536,12 @@ unsafe fn do_ffi_callback(
|
|||
pub fn op_ffi_unsafe_callback_ref(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<impl Future<Output = Result<(), AnyError>>, AnyError> {
|
||||
) -> Result<impl Future<Output = ()>, CallbackError> {
|
||||
let state = state.borrow();
|
||||
let callback_resource =
|
||||
state.resource_table.get::<UnsafeCallbackResource>(rid)?;
|
||||
let callback_resource = state
|
||||
.resource_table
|
||||
.get::<UnsafeCallbackResource>(rid)
|
||||
.map_err(CallbackError::Resource)?;
|
||||
|
||||
Ok(async move {
|
||||
let info: &mut CallbackInfo =
|
||||
|
@ -536,7 +552,6 @@ pub fn op_ffi_unsafe_callback_ref(
|
|||
.into_future()
|
||||
.or_cancel(callback_resource.cancel.clone())
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -552,12 +567,14 @@ pub fn op_ffi_unsafe_callback_create<FP, 'scope>(
|
|||
scope: &mut v8::HandleScope<'scope>,
|
||||
#[serde] args: RegisterCallbackArgs,
|
||||
cb: v8::Local<v8::Function>,
|
||||
) -> Result<v8::Local<'scope, v8::Value>, AnyError>
|
||||
) -> Result<v8::Local<'scope, v8::Value>, CallbackError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(CallbackError::Permission)?;
|
||||
|
||||
let thread_id: u32 = LOCAL_THREAD_ID.with(|s| {
|
||||
let value = *s.borrow();
|
||||
|
@ -593,8 +610,10 @@ where
|
|||
.parameters
|
||||
.into_iter()
|
||||
.map(libffi::middle::Type::try_from)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
libffi::middle::Type::try_from(args.result)?,
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(CallbackError::Other)?,
|
||||
libffi::middle::Type::try_from(args.result)
|
||||
.map_err(CallbackError::Other)?,
|
||||
);
|
||||
|
||||
// SAFETY: CallbackInfo is leaked, is not null and stays valid as long as the callback exists.
|
||||
|
@ -624,14 +643,16 @@ pub fn op_ffi_unsafe_callback_close(
|
|||
state: &mut OpState,
|
||||
scope: &mut v8::HandleScope,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), CallbackError> {
|
||||
// SAFETY: This drops the closure and the callback info associated with it.
|
||||
// Any retained function pointers to the closure become dangling pointers.
|
||||
// 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::<UnsafeCallbackResource>(rid)?;
|
||||
let callback_resource = state
|
||||
.resource_table
|
||||
.take::<UnsafeCallbackResource>(rid)
|
||||
.map_err(CallbackError::Resource)?;
|
||||
let info = Box::from_raw(callback_resource.info);
|
||||
let _ = v8::Global::from_raw(scope, info.callback);
|
||||
let _ = v8::Global::from_raw(scope, info.context);
|
||||
|
|
|
@ -6,8 +6,6 @@ use crate::symbol::Symbol;
|
|||
use crate::turbocall;
|
||||
use crate::turbocall::Turbocall;
|
||||
use crate::FfiPermissions;
|
||||
use deno_core::error::generic_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::GarbageCollected;
|
||||
|
@ -21,6 +19,22 @@ use std::collections::HashMap;
|
|||
use std::ffi::c_void;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DlfcnError {
|
||||
#[error("Failed to register symbol {symbol}: {error}")]
|
||||
RegisterSymbol {
|
||||
symbol: String,
|
||||
#[source]
|
||||
error: dlopen2::Error,
|
||||
},
|
||||
#[error(transparent)]
|
||||
Dlopen(#[from] dlopen2::Error),
|
||||
#[error(transparent)]
|
||||
Permission(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Other(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
pub struct DynamicLibraryResource {
|
||||
lib: Library,
|
||||
pub symbols: HashMap<String, Box<Symbol>>,
|
||||
|
@ -37,7 +51,7 @@ impl Resource for DynamicLibraryResource {
|
|||
}
|
||||
|
||||
impl DynamicLibraryResource {
|
||||
pub fn get_static(&self, symbol: String) -> Result<*mut c_void, AnyError> {
|
||||
pub fn get_static(&self, symbol: String) -> Result<*mut c_void, DlfcnError> {
|
||||
// By default, Err returned by this function does not tell
|
||||
// which symbol wasn't exported. So we'll modify the error
|
||||
// message to include the name of symbol.
|
||||
|
@ -45,9 +59,7 @@ impl DynamicLibraryResource {
|
|||
// SAFETY: The obtained T symbol is the size of a pointer.
|
||||
match unsafe { self.lib.symbol::<*mut c_void>(&symbol) } {
|
||||
Ok(value) => Ok(Ok(value)),
|
||||
Err(err) => Err(generic_error(format!(
|
||||
"Failed to register symbol {symbol}: {err}"
|
||||
))),
|
||||
Err(error) => Err(DlfcnError::RegisterSymbol { symbol, error }),
|
||||
}?
|
||||
}
|
||||
}
|
||||
|
@ -116,12 +128,14 @@ pub fn op_ffi_load<'scope, FP>(
|
|||
scope: &mut v8::HandleScope<'scope>,
|
||||
state: &mut OpState,
|
||||
#[serde] args: FfiLoadArgs,
|
||||
) -> Result<v8::Local<'scope, v8::Value>, AnyError>
|
||||
) -> Result<v8::Local<'scope, v8::Value>, DlfcnError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
let path = permissions.check_partial_with_path(&args.path)?;
|
||||
let path = permissions
|
||||
.check_partial_with_path(&args.path)
|
||||
.map_err(DlfcnError::Permission)?;
|
||||
|
||||
let lib = Library::open(&path).map_err(|e| {
|
||||
dlopen2::Error::OpeningLibraryError(std::io::Error::new(
|
||||
|
@ -152,15 +166,16 @@ where
|
|||
// SAFETY: The obtained T symbol is the size of a pointer.
|
||||
match unsafe { resource.lib.symbol::<*const c_void>(symbol) } {
|
||||
Ok(value) => Ok(value),
|
||||
Err(err) => if foreign_fn.optional {
|
||||
Err(error) => if foreign_fn.optional {
|
||||
let null: v8::Local<v8::Value> = v8::null(scope).into();
|
||||
let func_key = v8::String::new(scope, &symbol_key).unwrap();
|
||||
obj.set(scope, func_key.into(), null);
|
||||
break 'register_symbol;
|
||||
} else {
|
||||
Err(generic_error(format!(
|
||||
"Failed to register symbol {symbol}: {err}"
|
||||
)))
|
||||
Err(DlfcnError::RegisterSymbol {
|
||||
symbol: symbol.to_owned(),
|
||||
error,
|
||||
})
|
||||
},
|
||||
}?;
|
||||
|
||||
|
@ -171,8 +186,13 @@ where
|
|||
.clone()
|
||||
.into_iter()
|
||||
.map(libffi::middle::Type::try_from)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
foreign_fn.result.clone().try_into()?,
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(DlfcnError::Other)?,
|
||||
foreign_fn
|
||||
.result
|
||||
.clone()
|
||||
.try_into()
|
||||
.map_err(DlfcnError::Other)?,
|
||||
);
|
||||
|
||||
let func_key = v8::String::new(scope, &symbol_key).unwrap();
|
||||
|
|
142
ext/ffi/ir.rs
142
ext/ffi/ir.rs
|
@ -1,13 +1,55 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::symbol::NativeType;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::v8;
|
||||
use libffi::middle::Arg;
|
||||
use std::ffi::c_void;
|
||||
use std::ptr;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum IRError {
|
||||
#[error("Invalid FFI u8 type, expected boolean")]
|
||||
InvalidU8ExpectedBoolean,
|
||||
#[error("Invalid FFI u8 type, expected unsigned integer")]
|
||||
InvalidU8ExpectedUnsignedInteger,
|
||||
#[error("Invalid FFI i8 type, expected integer")]
|
||||
InvalidI8,
|
||||
#[error("Invalid FFI u16 type, expected unsigned integer")]
|
||||
InvalidU16,
|
||||
#[error("Invalid FFI i16 type, expected integer")]
|
||||
InvalidI16,
|
||||
#[error("Invalid FFI u32 type, expected unsigned integer")]
|
||||
InvalidU32,
|
||||
#[error("Invalid FFI i32 type, expected integer")]
|
||||
InvalidI32,
|
||||
#[error("Invalid FFI u64 type, expected unsigned integer")]
|
||||
InvalidU64,
|
||||
#[error("Invalid FFI i64 type, expected integer")]
|
||||
InvalidI64,
|
||||
#[error("Invalid FFI usize type, expected unsigned integer")]
|
||||
InvalidUsize,
|
||||
#[error("Invalid FFI isize type, expected integer")]
|
||||
InvalidIsize,
|
||||
#[error("Invalid FFI f32 type, expected number")]
|
||||
InvalidF32,
|
||||
#[error("Invalid FFI f64 type, expected number")]
|
||||
InvalidF64,
|
||||
#[error("Invalid FFI pointer type, expected null, or External")]
|
||||
InvalidPointerType,
|
||||
#[error(
|
||||
"Invalid FFI buffer type, expected null, ArrayBuffer, or ArrayBufferView"
|
||||
)]
|
||||
InvalidBufferType,
|
||||
#[error("Invalid FFI ArrayBufferView, expected data in the buffer")]
|
||||
InvalidArrayBufferView,
|
||||
#[error("Invalid FFI ArrayBuffer, expected data in buffer")]
|
||||
InvalidArrayBuffer,
|
||||
#[error("Invalid FFI struct type, expected ArrayBuffer, or ArrayBufferView")]
|
||||
InvalidStructType,
|
||||
#[error("Invalid FFI function type, expected null, or External")]
|
||||
InvalidFunctionType,
|
||||
}
|
||||
|
||||
pub struct OutBuffer(pub *mut u8);
|
||||
|
||||
// SAFETY: OutBuffer is allocated by us in 00_ffi.js and is guaranteed to be
|
||||
|
@ -126,9 +168,9 @@ unsafe impl Send for NativeValue {}
|
|||
#[inline]
|
||||
pub fn ffi_parse_bool_arg(
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let bool_value = v8::Local::<v8::Boolean>::try_from(arg)
|
||||
.map_err(|_| type_error("Invalid FFI u8 type, expected boolean"))?
|
||||
.map_err(|_| IRError::InvalidU8ExpectedBoolean)?
|
||||
.is_true();
|
||||
Ok(NativeValue { bool_value })
|
||||
}
|
||||
|
@ -136,9 +178,9 @@ pub fn ffi_parse_bool_arg(
|
|||
#[inline]
|
||||
pub fn ffi_parse_u8_arg(
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let u8_value = v8::Local::<v8::Uint32>::try_from(arg)
|
||||
.map_err(|_| type_error("Invalid FFI u8 type, expected unsigned integer"))?
|
||||
.map_err(|_| IRError::InvalidU8ExpectedUnsignedInteger)?
|
||||
.value() as u8;
|
||||
Ok(NativeValue { u8_value })
|
||||
}
|
||||
|
@ -146,9 +188,9 @@ pub fn ffi_parse_u8_arg(
|
|||
#[inline]
|
||||
pub fn ffi_parse_i8_arg(
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let i8_value = v8::Local::<v8::Int32>::try_from(arg)
|
||||
.map_err(|_| type_error("Invalid FFI i8 type, expected integer"))?
|
||||
.map_err(|_| IRError::InvalidI8)?
|
||||
.value() as i8;
|
||||
Ok(NativeValue { i8_value })
|
||||
}
|
||||
|
@ -156,9 +198,9 @@ pub fn ffi_parse_i8_arg(
|
|||
#[inline]
|
||||
pub fn ffi_parse_u16_arg(
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let u16_value = v8::Local::<v8::Uint32>::try_from(arg)
|
||||
.map_err(|_| type_error("Invalid FFI u16 type, expected unsigned integer"))?
|
||||
.map_err(|_| IRError::InvalidU16)?
|
||||
.value() as u16;
|
||||
Ok(NativeValue { u16_value })
|
||||
}
|
||||
|
@ -166,9 +208,9 @@ pub fn ffi_parse_u16_arg(
|
|||
#[inline]
|
||||
pub fn ffi_parse_i16_arg(
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let i16_value = v8::Local::<v8::Int32>::try_from(arg)
|
||||
.map_err(|_| type_error("Invalid FFI i16 type, expected integer"))?
|
||||
.map_err(|_| IRError::InvalidI16)?
|
||||
.value() as i16;
|
||||
Ok(NativeValue { i16_value })
|
||||
}
|
||||
|
@ -176,9 +218,9 @@ pub fn ffi_parse_i16_arg(
|
|||
#[inline]
|
||||
pub fn ffi_parse_u32_arg(
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let u32_value = v8::Local::<v8::Uint32>::try_from(arg)
|
||||
.map_err(|_| type_error("Invalid FFI u32 type, expected unsigned integer"))?
|
||||
.map_err(|_| IRError::InvalidU32)?
|
||||
.value();
|
||||
Ok(NativeValue { u32_value })
|
||||
}
|
||||
|
@ -186,9 +228,9 @@ pub fn ffi_parse_u32_arg(
|
|||
#[inline]
|
||||
pub fn ffi_parse_i32_arg(
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let i32_value = v8::Local::<v8::Int32>::try_from(arg)
|
||||
.map_err(|_| type_error("Invalid FFI i32 type, expected integer"))?
|
||||
.map_err(|_| IRError::InvalidI32)?
|
||||
.value();
|
||||
Ok(NativeValue { i32_value })
|
||||
}
|
||||
|
@ -197,7 +239,7 @@ pub fn ffi_parse_i32_arg(
|
|||
pub fn ffi_parse_u64_arg(
|
||||
scope: &mut v8::HandleScope,
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
// Order of checking:
|
||||
// 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case.
|
||||
// 2. Number: Common, supported by Fast API, so let that be the optimal case.
|
||||
|
@ -207,9 +249,7 @@ pub fn ffi_parse_u64_arg(
|
|||
} else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
|
||||
value.integer_value(scope).unwrap() as u64
|
||||
} else {
|
||||
return Err(type_error(
|
||||
"Invalid FFI u64 type, expected unsigned integer",
|
||||
));
|
||||
return Err(IRError::InvalidU64);
|
||||
};
|
||||
Ok(NativeValue { u64_value })
|
||||
}
|
||||
|
@ -218,7 +258,7 @@ pub fn ffi_parse_u64_arg(
|
|||
pub fn ffi_parse_i64_arg(
|
||||
scope: &mut v8::HandleScope,
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
// Order of checking:
|
||||
// 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case.
|
||||
// 2. Number: Common, supported by Fast API, so let that be the optimal case.
|
||||
|
@ -228,7 +268,7 @@ pub fn ffi_parse_i64_arg(
|
|||
} else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
|
||||
value.integer_value(scope).unwrap()
|
||||
} else {
|
||||
return Err(type_error("Invalid FFI i64 type, expected integer"));
|
||||
return Err(IRError::InvalidI64);
|
||||
};
|
||||
Ok(NativeValue { i64_value })
|
||||
}
|
||||
|
@ -237,7 +277,7 @@ pub fn ffi_parse_i64_arg(
|
|||
pub fn ffi_parse_usize_arg(
|
||||
scope: &mut v8::HandleScope,
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
// Order of checking:
|
||||
// 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case.
|
||||
// 2. Number: Common, supported by Fast API, so let that be the optimal case.
|
||||
|
@ -247,7 +287,7 @@ pub fn ffi_parse_usize_arg(
|
|||
} else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
|
||||
value.integer_value(scope).unwrap() as usize
|
||||
} else {
|
||||
return Err(type_error("Invalid FFI usize type, expected integer"));
|
||||
return Err(IRError::InvalidUsize);
|
||||
};
|
||||
Ok(NativeValue { usize_value })
|
||||
}
|
||||
|
@ -256,7 +296,7 @@ pub fn ffi_parse_usize_arg(
|
|||
pub fn ffi_parse_isize_arg(
|
||||
scope: &mut v8::HandleScope,
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
// Order of checking:
|
||||
// 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case.
|
||||
// 2. Number: Common, supported by Fast API, so let that be the optimal case.
|
||||
|
@ -266,7 +306,7 @@ pub fn ffi_parse_isize_arg(
|
|||
} else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
|
||||
value.integer_value(scope).unwrap() as isize
|
||||
} else {
|
||||
return Err(type_error("Invalid FFI isize type, expected integer"));
|
||||
return Err(IRError::InvalidIsize);
|
||||
};
|
||||
Ok(NativeValue { isize_value })
|
||||
}
|
||||
|
@ -274,9 +314,9 @@ pub fn ffi_parse_isize_arg(
|
|||
#[inline]
|
||||
pub fn ffi_parse_f32_arg(
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let f32_value = v8::Local::<v8::Number>::try_from(arg)
|
||||
.map_err(|_| type_error("Invalid FFI f32 type, expected number"))?
|
||||
.map_err(|_| IRError::InvalidF32)?
|
||||
.value() as f32;
|
||||
Ok(NativeValue { f32_value })
|
||||
}
|
||||
|
@ -284,9 +324,9 @@ pub fn ffi_parse_f32_arg(
|
|||
#[inline]
|
||||
pub fn ffi_parse_f64_arg(
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let f64_value = v8::Local::<v8::Number>::try_from(arg)
|
||||
.map_err(|_| type_error("Invalid FFI f64 type, expected number"))?
|
||||
.map_err(|_| IRError::InvalidF64)?
|
||||
.value();
|
||||
Ok(NativeValue { f64_value })
|
||||
}
|
||||
|
@ -295,15 +335,13 @@ pub fn ffi_parse_f64_arg(
|
|||
pub fn ffi_parse_pointer_arg(
|
||||
_scope: &mut v8::HandleScope,
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let pointer = if let Ok(value) = v8::Local::<v8::External>::try_from(arg) {
|
||||
value.value()
|
||||
} else if arg.is_null() {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
return Err(type_error(
|
||||
"Invalid FFI pointer type, expected null, or External",
|
||||
));
|
||||
return Err(IRError::InvalidPointerType);
|
||||
};
|
||||
Ok(NativeValue { pointer })
|
||||
}
|
||||
|
@ -312,7 +350,7 @@ pub fn ffi_parse_pointer_arg(
|
|||
pub fn ffi_parse_buffer_arg(
|
||||
scope: &mut v8::HandleScope,
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
// Order of checking:
|
||||
// 1. ArrayBuffer: Fairly common and not supported by Fast API, optimise this case.
|
||||
// 2. ArrayBufferView: Common and supported by Fast API
|
||||
|
@ -328,9 +366,7 @@ pub fn ffi_parse_buffer_arg(
|
|||
let byte_offset = value.byte_offset();
|
||||
let pointer = value
|
||||
.buffer(scope)
|
||||
.ok_or_else(|| {
|
||||
type_error("Invalid FFI ArrayBufferView, expected data in the buffer")
|
||||
})?
|
||||
.ok_or(IRError::InvalidArrayBufferView)?
|
||||
.data();
|
||||
if let Some(non_null) = pointer {
|
||||
// SAFETY: Pointer is non-null, and V8 guarantees that the byte_offset
|
||||
|
@ -342,9 +378,7 @@ pub fn ffi_parse_buffer_arg(
|
|||
} else if arg.is_null() {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
return Err(type_error(
|
||||
"Invalid FFI buffer type, expected null, ArrayBuffer, or ArrayBufferView",
|
||||
));
|
||||
return Err(IRError::InvalidBufferType);
|
||||
};
|
||||
Ok(NativeValue { pointer })
|
||||
}
|
||||
|
@ -353,7 +387,7 @@ pub fn ffi_parse_buffer_arg(
|
|||
pub fn ffi_parse_struct_arg(
|
||||
scope: &mut v8::HandleScope,
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
// Order of checking:
|
||||
// 1. ArrayBuffer: Fairly common and not supported by Fast API, optimise this case.
|
||||
// 2. ArrayBufferView: Common and supported by Fast API
|
||||
|
@ -362,31 +396,23 @@ pub fn ffi_parse_struct_arg(
|
|||
if let Some(non_null) = value.data() {
|
||||
non_null.as_ptr()
|
||||
} else {
|
||||
return Err(type_error(
|
||||
"Invalid FFI ArrayBuffer, expected data in buffer",
|
||||
));
|
||||
return Err(IRError::InvalidArrayBuffer);
|
||||
}
|
||||
} else if let Ok(value) = v8::Local::<v8::ArrayBufferView>::try_from(arg) {
|
||||
let byte_offset = value.byte_offset();
|
||||
let pointer = value
|
||||
.buffer(scope)
|
||||
.ok_or_else(|| {
|
||||
type_error("Invalid FFI ArrayBufferView, expected data in the buffer")
|
||||
})?
|
||||
.ok_or(IRError::InvalidArrayBufferView)?
|
||||
.data();
|
||||
if let Some(non_null) = pointer {
|
||||
// SAFETY: Pointer is non-null, and V8 guarantees that the byte_offset
|
||||
// is within the buffer backing store.
|
||||
unsafe { non_null.as_ptr().add(byte_offset) }
|
||||
} else {
|
||||
return Err(type_error(
|
||||
"Invalid FFI ArrayBufferView, expected data in buffer",
|
||||
));
|
||||
return Err(IRError::InvalidArrayBufferView);
|
||||
}
|
||||
} else {
|
||||
return Err(type_error(
|
||||
"Invalid FFI struct type, expected ArrayBuffer, or ArrayBufferView",
|
||||
));
|
||||
return Err(IRError::InvalidStructType);
|
||||
};
|
||||
Ok(NativeValue { pointer })
|
||||
}
|
||||
|
@ -395,15 +421,13 @@ pub fn ffi_parse_struct_arg(
|
|||
pub fn ffi_parse_function_arg(
|
||||
_scope: &mut v8::HandleScope,
|
||||
arg: v8::Local<v8::Value>,
|
||||
) -> Result<NativeValue, AnyError> {
|
||||
) -> Result<NativeValue, IRError> {
|
||||
let pointer = if let Ok(value) = v8::Local::<v8::External>::try_from(arg) {
|
||||
value.value()
|
||||
} else if arg.is_null() {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
return Err(type_error(
|
||||
"Invalid FFI function type, expected null, or External",
|
||||
));
|
||||
return Err(IRError::InvalidFunctionType);
|
||||
};
|
||||
Ok(NativeValue { pointer })
|
||||
}
|
||||
|
@ -412,7 +436,7 @@ pub fn ffi_parse_args<'scope>(
|
|||
scope: &mut v8::HandleScope<'scope>,
|
||||
args: v8::Local<v8::Array>,
|
||||
parameter_types: &[NativeType],
|
||||
) -> Result<Vec<NativeValue>, AnyError>
|
||||
) -> Result<Vec<NativeValue>, IRError>
|
||||
where
|
||||
'scope: 'scope,
|
||||
{
|
||||
|
|
|
@ -29,6 +29,13 @@ use repr::*;
|
|||
use symbol::NativeType;
|
||||
use symbol::Symbol;
|
||||
|
||||
pub use call::CallError;
|
||||
pub use callback::CallbackError;
|
||||
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");
|
||||
|
||||
|
|
211
ext/ffi/repr.rs
211
ext/ffi/repr.rs
|
@ -1,9 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::FfiPermissions;
|
||||
use deno_core::error::range_error;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::OpState;
|
||||
|
@ -12,16 +9,58 @@ use std::ffi::c_void;
|
|||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ReprError {
|
||||
#[error("Invalid pointer to offset, pointer is null")]
|
||||
InvalidOffset,
|
||||
#[error("Invalid ArrayBuffer pointer, pointer is null")]
|
||||
InvalidArrayBuffer,
|
||||
#[error("Destination length is smaller than source length")]
|
||||
DestinationLengthTooShort,
|
||||
#[error("Invalid CString pointer, pointer is null")]
|
||||
InvalidCString,
|
||||
#[error("Invalid CString pointer, string exceeds max length")]
|
||||
CStringTooLong,
|
||||
#[error("Invalid bool pointer, pointer is null")]
|
||||
InvalidBool,
|
||||
#[error("Invalid u8 pointer, pointer is null")]
|
||||
InvalidU8,
|
||||
#[error("Invalid i8 pointer, pointer is null")]
|
||||
InvalidI8,
|
||||
#[error("Invalid u16 pointer, pointer is null")]
|
||||
InvalidU16,
|
||||
#[error("Invalid i16 pointer, pointer is null")]
|
||||
InvalidI16,
|
||||
#[error("Invalid u32 pointer, pointer is null")]
|
||||
InvalidU32,
|
||||
#[error("Invalid i32 pointer, pointer is null")]
|
||||
InvalidI32,
|
||||
#[error("Invalid u64 pointer, pointer is null")]
|
||||
InvalidU64,
|
||||
#[error("Invalid i64 pointer, pointer is null")]
|
||||
InvalidI64,
|
||||
#[error("Invalid f32 pointer, pointer is null")]
|
||||
InvalidF32,
|
||||
#[error("Invalid f64 pointer, pointer is null")]
|
||||
InvalidF64,
|
||||
#[error("Invalid pointer pointer, pointer is null")]
|
||||
InvalidPointer,
|
||||
#[error(transparent)]
|
||||
Permission(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
pub fn op_ffi_ptr_create<FP>(
|
||||
state: &mut OpState,
|
||||
#[bigint] ptr_number: usize,
|
||||
) -> Result<*mut c_void, AnyError>
|
||||
) -> Result<*mut c_void, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
Ok(ptr_number as *mut c_void)
|
||||
}
|
||||
|
@ -31,12 +70,14 @@ pub fn op_ffi_ptr_equals<FP>(
|
|||
state: &mut OpState,
|
||||
a: *const c_void,
|
||||
b: *const c_void,
|
||||
) -> Result<bool, AnyError>
|
||||
) -> Result<bool, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
Ok(a == b)
|
||||
}
|
||||
|
@ -45,12 +86,14 @@ where
|
|||
pub fn op_ffi_ptr_of<FP>(
|
||||
state: &mut OpState,
|
||||
#[anybuffer] buf: *const u8,
|
||||
) -> Result<*mut c_void, AnyError>
|
||||
) -> Result<*mut c_void, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
Ok(buf as *mut c_void)
|
||||
}
|
||||
|
@ -59,12 +102,14 @@ where
|
|||
pub fn op_ffi_ptr_of_exact<FP>(
|
||||
state: &mut OpState,
|
||||
buf: v8::Local<v8::ArrayBufferView>,
|
||||
) -> Result<*mut c_void, AnyError>
|
||||
) -> Result<*mut c_void, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
let Some(buf) = buf.get_backing_store() else {
|
||||
return Ok(0 as _);
|
||||
|
@ -80,15 +125,17 @@ pub fn op_ffi_ptr_offset<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<*mut c_void, AnyError>
|
||||
) -> Result<*mut c_void, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid pointer to offset, pointer is null"));
|
||||
return Err(ReprError::InvalidOffset);
|
||||
}
|
||||
|
||||
// TODO(mmastrac): Create a RawPointer that can safely do pointer math.
|
||||
|
@ -110,12 +157,14 @@ unsafe extern "C" fn noop_deleter_callback(
|
|||
pub fn op_ffi_ptr_value<FP>(
|
||||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
) -> Result<usize, AnyError>
|
||||
) -> Result<usize, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
Ok(ptr as usize)
|
||||
}
|
||||
|
@ -127,15 +176,17 @@ pub fn op_ffi_get_buf<FP, 'scope>(
|
|||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
#[number] len: usize,
|
||||
) -> Result<v8::Local<'scope, v8::ArrayBuffer>, AnyError>
|
||||
) -> Result<v8::Local<'scope, v8::ArrayBuffer>, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid ArrayBuffer pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidArrayBuffer);
|
||||
}
|
||||
|
||||
// SAFETY: Trust the user to have provided a real pointer, offset, and a valid matching size to it. Since this is a foreign pointer, we should not do any deletion.
|
||||
|
@ -144,7 +195,7 @@ where
|
|||
ptr.offset(offset),
|
||||
len,
|
||||
noop_deleter_callback,
|
||||
std::ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
}
|
||||
.make_shared();
|
||||
|
@ -159,19 +210,19 @@ pub fn op_ffi_buf_copy_into<FP>(
|
|||
#[number] offset: isize,
|
||||
#[anybuffer] dst: &mut [u8],
|
||||
#[number] len: usize,
|
||||
) -> Result<(), AnyError>
|
||||
) -> Result<(), ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if src.is_null() {
|
||||
Err(type_error("Invalid ArrayBuffer pointer, pointer is null"))
|
||||
Err(ReprError::InvalidArrayBuffer)
|
||||
} else if dst.len() < len {
|
||||
Err(range_error(
|
||||
"Destination length is smaller than source length",
|
||||
))
|
||||
Err(ReprError::DestinationLengthTooShort)
|
||||
} else {
|
||||
let src = src as *const c_void;
|
||||
|
||||
|
@ -190,24 +241,24 @@ pub fn op_ffi_cstr_read<FP, 'scope>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<v8::Local<'scope, v8::String>, AnyError>
|
||||
) -> Result<v8::Local<'scope, v8::String>, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid CString pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidCString);
|
||||
}
|
||||
|
||||
let cstr =
|
||||
// SAFETY: Pointer and offset are user provided.
|
||||
unsafe { CStr::from_ptr(ptr.offset(offset) as *const c_char) }.to_bytes();
|
||||
let value = v8::String::new_from_utf8(scope, cstr, v8::NewStringType::Normal)
|
||||
.ok_or_else(|| {
|
||||
type_error("Invalid CString pointer, string exceeds max length")
|
||||
})?;
|
||||
.ok_or_else(|| ReprError::CStringTooLong)?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
|
@ -216,15 +267,17 @@ pub fn op_ffi_read_bool<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<bool, AnyError>
|
||||
) -> Result<bool, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid bool pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidBool);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
@ -236,15 +289,17 @@ pub fn op_ffi_read_u8<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<u32, AnyError>
|
||||
) -> Result<u32, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid u8 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidU8);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
@ -258,15 +313,17 @@ pub fn op_ffi_read_i8<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<i32, AnyError>
|
||||
) -> Result<i32, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid i8 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidI8);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
@ -280,15 +337,17 @@ pub fn op_ffi_read_u16<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<u32, AnyError>
|
||||
) -> Result<u32, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid u16 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidU16);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
@ -302,15 +361,17 @@ pub fn op_ffi_read_i16<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<i32, AnyError>
|
||||
) -> Result<i32, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid i16 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidI16);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
@ -324,15 +385,17 @@ pub fn op_ffi_read_u32<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<u32, AnyError>
|
||||
) -> Result<u32, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid u32 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidU32);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
@ -344,15 +407,17 @@ pub fn op_ffi_read_i32<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<i32, AnyError>
|
||||
) -> Result<i32, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid i32 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidI32);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
@ -367,15 +432,17 @@ pub fn op_ffi_read_u64<FP>(
|
|||
// Note: The representation of 64-bit integers is function-wide. We cannot
|
||||
// choose to take this parameter as a number while returning a bigint.
|
||||
#[bigint] offset: isize,
|
||||
) -> Result<u64, AnyError>
|
||||
) -> Result<u64, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid u64 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidU64);
|
||||
}
|
||||
|
||||
let value =
|
||||
|
@ -393,15 +460,17 @@ pub fn op_ffi_read_i64<FP>(
|
|||
// Note: The representation of 64-bit integers is function-wide. We cannot
|
||||
// choose to take this parameter as a number while returning a bigint.
|
||||
#[bigint] offset: isize,
|
||||
) -> Result<i64, AnyError>
|
||||
) -> Result<i64, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid i64 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidI64);
|
||||
}
|
||||
|
||||
let value =
|
||||
|
@ -416,15 +485,17 @@ pub fn op_ffi_read_f32<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<f32, AnyError>
|
||||
) -> Result<f32, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid f32 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidF32);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
@ -436,15 +507,17 @@ pub fn op_ffi_read_f64<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<f64, AnyError>
|
||||
) -> Result<f64, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid f64 pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidF64);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
@ -456,15 +529,17 @@ pub fn op_ffi_read_ptr<FP>(
|
|||
state: &mut OpState,
|
||||
ptr: *mut c_void,
|
||||
#[number] offset: isize,
|
||||
) -> Result<*mut c_void, AnyError>
|
||||
) -> Result<*mut c_void, ReprError>
|
||||
where
|
||||
FP: FfiPermissions + 'static,
|
||||
{
|
||||
let permissions = state.borrow_mut::<FP>();
|
||||
permissions.check_partial_no_path()?;
|
||||
permissions
|
||||
.check_partial_no_path()
|
||||
.map_err(ReprError::Permission)?;
|
||||
|
||||
if ptr.is_null() {
|
||||
return Err(type_error("Invalid pointer pointer, pointer is null"));
|
||||
return Err(ReprError::InvalidPointer);
|
||||
}
|
||||
|
||||
// SAFETY: ptr and offset are user provided.
|
||||
|
|
|
@ -2,14 +2,24 @@
|
|||
|
||||
use crate::dlfcn::DynamicLibraryResource;
|
||||
use crate::symbol::NativeType;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::OpState;
|
||||
use deno_core::ResourceId;
|
||||
use std::ptr;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum StaticError {
|
||||
#[error(transparent)]
|
||||
Dlfcn(super::DlfcnError),
|
||||
#[error("Invalid FFI static type 'void'")]
|
||||
InvalidTypeVoid,
|
||||
#[error("Invalid FFI static type 'struct'")]
|
||||
InvalidTypeStruct,
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
#[op2]
|
||||
pub fn op_ffi_get_static<'scope>(
|
||||
scope: &mut v8::HandleScope<'scope>,
|
||||
|
@ -18,24 +28,27 @@ pub fn op_ffi_get_static<'scope>(
|
|||
#[string] name: String,
|
||||
#[serde] static_type: NativeType,
|
||||
optional: bool,
|
||||
) -> Result<v8::Local<'scope, v8::Value>, AnyError> {
|
||||
let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?;
|
||||
) -> Result<v8::Local<'scope, v8::Value>, StaticError> {
|
||||
let resource = state
|
||||
.resource_table
|
||||
.get::<DynamicLibraryResource>(rid)
|
||||
.map_err(StaticError::Resource)?;
|
||||
|
||||
let data_ptr = match resource.get_static(name) {
|
||||
Ok(data_ptr) => Ok(data_ptr),
|
||||
Ok(data_ptr) => data_ptr,
|
||||
Err(err) => {
|
||||
if optional {
|
||||
let null: v8::Local<v8::Value> = v8::null(scope).into();
|
||||
return Ok(null);
|
||||
} else {
|
||||
Err(err)
|
||||
return Err(StaticError::Dlfcn(err));
|
||||
}
|
||||
}
|
||||
}?;
|
||||
};
|
||||
|
||||
Ok(match static_type {
|
||||
NativeType::Void => {
|
||||
return Err(type_error("Invalid FFI static type 'void'"));
|
||||
return Err(StaticError::InvalidTypeVoid);
|
||||
}
|
||||
NativeType::Bool => {
|
||||
// SAFETY: ptr is user provided
|
||||
|
@ -132,7 +145,7 @@ pub fn op_ffi_get_static<'scope>(
|
|||
external
|
||||
}
|
||||
NativeType::Struct(_) => {
|
||||
return Err(type_error("Invalid FFI static type 'struct'"));
|
||||
return Err(StaticError::InvalidTypeStruct);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_fs"
|
||||
version = "0.81.0"
|
||||
version = "0.83.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -28,9 +28,10 @@ libc.workspace = true
|
|||
rand.workspace = true
|
||||
rayon = "1.8.0"
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix.workspace = true
|
||||
nix = { workspace = true, features = ["user"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { workspace = true, features = ["winbase"] }
|
||||
|
|
|
@ -14,6 +14,8 @@ pub use crate::interface::FileSystemRc;
|
|||
pub use crate::interface::FsDirEntry;
|
||||
pub use crate::interface::FsFileType;
|
||||
pub use crate::interface::OpenOptions;
|
||||
pub use crate::ops::FsOpsError;
|
||||
pub use crate::ops::OperationError;
|
||||
pub use crate::std_fs::RealFs;
|
||||
pub use crate::sync::MaybeSend;
|
||||
pub use crate::sync::MaybeSync;
|
||||
|
|
575
ext/fs/ops.rs
575
ext/fs/ops.rs
File diff suppressed because it is too large
Load diff
|
@ -929,7 +929,7 @@ fn exists(path: &Path) -> bool {
|
|||
}
|
||||
|
||||
fn realpath(path: &Path) -> FsResult<PathBuf> {
|
||||
Ok(deno_core::strip_unc_prefix(path.canonicalize()?))
|
||||
Ok(deno_path_util::strip_unc_prefix(path.canonicalize()?))
|
||||
}
|
||||
|
||||
fn read_dir(path: &Path) -> FsResult<Vec<FsDirEntry>> {
|
||||
|
|
|
@ -76,7 +76,11 @@ import {
|
|||
ReadableStreamPrototype,
|
||||
resourceForReadableStream,
|
||||
} from "ext:deno_web/06_streams.js";
|
||||
import { listen, listenOptionApiName, TcpConn } from "ext:deno_net/01_net.js";
|
||||
import {
|
||||
listen,
|
||||
listenOptionApiName,
|
||||
UpgradedConn,
|
||||
} from "ext:deno_net/01_net.js";
|
||||
import { hasTlsKeyPairOptions, listenTls } from "ext:deno_net/02_tls.js";
|
||||
import { SymbolAsyncDispose } from "ext:deno_web/00_infra.js";
|
||||
|
||||
|
@ -189,7 +193,7 @@ class InnerRequest {
|
|||
|
||||
const upgradeRid = op_http_upgrade_raw(external);
|
||||
|
||||
const conn = new TcpConn(
|
||||
const conn = new UpgradedConn(
|
||||
upgradeRid,
|
||||
underlyingConn?.remoteAddr,
|
||||
underlyingConn?.localAddr,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue