1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

Merge branch 'main' into open-flag-on-serve

This commit is contained in:
HasanAlrimawi 2024-11-12 22:47:28 +02:00 committed by GitHub
commit 8e63dea5ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 1410 additions and 244 deletions

View file

@ -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 = 24;
const cacheVersion = 25;
const ubuntuX86Runner = "ubuntu-24.04";
const ubuntuX86XlRunner = "ubuntu-24.04-xl";

View file

@ -361,8 +361,8 @@ jobs:
path: |-
~/.cargo/registry/index
~/.cargo/registry/cache
key: '24-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '24-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
key: '25-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '25-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: '24-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
restore-keys: '25-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
- name: Apply and update mtime cache
if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))'
uses: ./.github/mtime_cache
@ -685,7 +685,7 @@ jobs:
!./target/*/*.zip
!./target/*/*.sha256sum
!./target/*/*.tar.gz
key: '24-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
key: '25-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
publish-canary:
name: publish canary
runs-on: ubuntu-24.04

62
Cargo.lock generated
View file

@ -1154,7 +1154,7 @@ dependencies = [
[[package]]
name = "deno"
version = "2.0.5"
version = "2.0.6"
dependencies = [
"anstream",
"async-trait",
@ -1323,7 +1323,7 @@ dependencies = [
[[package]]
name = "deno_bench_util"
version = "0.170.0"
version = "0.171.0"
dependencies = [
"bencher",
"deno_core",
@ -1332,7 +1332,7 @@ dependencies = [
[[package]]
name = "deno_broadcast_channel"
version = "0.170.0"
version = "0.171.0"
dependencies = [
"async-trait",
"deno_core",
@ -1343,7 +1343,7 @@ dependencies = [
[[package]]
name = "deno_cache"
version = "0.108.0"
version = "0.109.0"
dependencies = [
"async-trait",
"deno_core",
@ -1376,7 +1376,7 @@ dependencies = [
[[package]]
name = "deno_canvas"
version = "0.45.0"
version = "0.46.0"
dependencies = [
"deno_core",
"deno_webgpu",
@ -1411,7 +1411,7 @@ dependencies = [
[[package]]
name = "deno_console"
version = "0.176.0"
version = "0.177.0"
dependencies = [
"deno_core",
]
@ -1456,7 +1456,7 @@ checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1"
[[package]]
name = "deno_cron"
version = "0.56.0"
version = "0.57.0"
dependencies = [
"anyhow",
"async-trait",
@ -1469,7 +1469,7 @@ dependencies = [
[[package]]
name = "deno_crypto"
version = "0.190.0"
version = "0.191.0"
dependencies = [
"aes",
"aes-gcm",
@ -1531,7 +1531,7 @@ dependencies = [
[[package]]
name = "deno_fetch"
version = "0.200.0"
version = "0.201.0"
dependencies = [
"base64 0.21.7",
"bytes",
@ -1564,7 +1564,7 @@ dependencies = [
[[package]]
name = "deno_ffi"
version = "0.163.0"
version = "0.164.0"
dependencies = [
"deno_core",
"deno_permissions",
@ -1584,7 +1584,7 @@ dependencies = [
[[package]]
name = "deno_fs"
version = "0.86.0"
version = "0.87.0"
dependencies = [
"async-trait",
"base32",
@ -1635,7 +1635,7 @@ dependencies = [
[[package]]
name = "deno_http"
version = "0.174.0"
version = "0.175.0"
dependencies = [
"async-compression",
"async-trait",
@ -1674,7 +1674,7 @@ dependencies = [
[[package]]
name = "deno_io"
version = "0.86.0"
version = "0.87.0"
dependencies = [
"async-trait",
"deno_core",
@ -1695,7 +1695,7 @@ dependencies = [
[[package]]
name = "deno_kv"
version = "0.84.0"
version = "0.85.0"
dependencies = [
"anyhow",
"async-trait",
@ -1767,7 +1767,7 @@ dependencies = [
[[package]]
name = "deno_napi"
version = "0.107.0"
version = "0.108.0"
dependencies = [
"deno_core",
"deno_permissions",
@ -1795,7 +1795,7 @@ dependencies = [
[[package]]
name = "deno_net"
version = "0.168.0"
version = "0.169.0"
dependencies = [
"deno_core",
"deno_permissions",
@ -1812,7 +1812,7 @@ dependencies = [
[[package]]
name = "deno_node"
version = "0.113.0"
version = "0.114.0"
dependencies = [
"aead-gcm-stream",
"aes",
@ -1961,7 +1961,7 @@ dependencies = [
[[package]]
name = "deno_permissions"
version = "0.36.0"
version = "0.37.0"
dependencies = [
"deno_core",
"deno_path_util",
@ -1979,7 +1979,7 @@ dependencies = [
[[package]]
name = "deno_resolver"
version = "0.8.0"
version = "0.9.0"
dependencies = [
"anyhow",
"base32",
@ -1995,7 +1995,7 @@ dependencies = [
[[package]]
name = "deno_runtime"
version = "0.185.0"
version = "0.186.0"
dependencies = [
"color-print",
"deno_ast",
@ -2113,7 +2113,7 @@ dependencies = [
[[package]]
name = "deno_tls"
version = "0.163.0"
version = "0.164.0"
dependencies = [
"deno_core",
"deno_native_certs",
@ -2162,7 +2162,7 @@ dependencies = [
[[package]]
name = "deno_url"
version = "0.176.0"
version = "0.177.0"
dependencies = [
"deno_bench_util",
"deno_console",
@ -2174,7 +2174,7 @@ dependencies = [
[[package]]
name = "deno_web"
version = "0.207.0"
version = "0.208.0"
dependencies = [
"async-trait",
"base64-simd 0.8.0",
@ -2196,7 +2196,7 @@ dependencies = [
[[package]]
name = "deno_webgpu"
version = "0.143.0"
version = "0.144.0"
dependencies = [
"deno_core",
"raw-window-handle",
@ -2209,7 +2209,7 @@ dependencies = [
[[package]]
name = "deno_webidl"
version = "0.176.0"
version = "0.177.0"
dependencies = [
"deno_bench_util",
"deno_core",
@ -2217,7 +2217,7 @@ dependencies = [
[[package]]
name = "deno_websocket"
version = "0.181.0"
version = "0.182.0"
dependencies = [
"bytes",
"deno_core",
@ -2239,7 +2239,7 @@ dependencies = [
[[package]]
name = "deno_webstorage"
version = "0.171.0"
version = "0.172.0"
dependencies = [
"deno_core",
"deno_web",
@ -4561,7 +4561,7 @@ dependencies = [
[[package]]
name = "napi_sym"
version = "0.106.0"
version = "0.107.0"
dependencies = [
"quote",
"serde",
@ -4616,7 +4616,7 @@ dependencies = [
[[package]]
name = "node_resolver"
version = "0.15.0"
version = "0.16.0"
dependencies = [
"anyhow",
"async-trait",
@ -8485,9 +8485,9 @@ dependencies = [
[[package]]
name = "zeromq"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb0560d00172817b7f7c2265060783519c475702ae290b154115ca75e976d4d0"
checksum = "6a4528179201f6eecf211961a7d3276faa61554c82651ecc66387f68fc3004bd"
dependencies = [
"async-trait",
"asynchronous-codec",

View file

@ -48,16 +48,16 @@ repository = "https://github.com/denoland/deno"
deno_ast = { version = "=0.43.3", features = ["transpiling"] }
deno_core = { version = "0.318.0" }
deno_bench_util = { version = "0.170.0", path = "./bench_util" }
deno_bench_util = { version = "0.171.0", path = "./bench_util" }
deno_lockfile = "=0.23.1"
deno_media_type = { version = "0.2.0", features = ["module_specifier"] }
deno_npm = "=0.25.4"
deno_path_util = "=0.2.1"
deno_permissions = { version = "0.36.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.185.0", path = "./runtime" }
deno_permissions = { version = "0.37.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.186.0", path = "./runtime" }
deno_semver = "=0.5.16"
deno_terminal = "0.2.0"
napi_sym = { version = "0.106.0", path = "./ext/napi/sym" }
napi_sym = { version = "0.107.0", path = "./ext/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.170.0", path = "./ext/broadcast_channel" }
deno_cache = { version = "0.108.0", path = "./ext/cache" }
deno_canvas = { version = "0.45.0", path = "./ext/canvas" }
deno_console = { version = "0.176.0", path = "./ext/console" }
deno_cron = { version = "0.56.0", path = "./ext/cron" }
deno_crypto = { version = "0.190.0", path = "./ext/crypto" }
deno_fetch = { version = "0.200.0", path = "./ext/fetch" }
deno_ffi = { version = "0.163.0", path = "./ext/ffi" }
deno_fs = { version = "0.86.0", path = "./ext/fs" }
deno_http = { version = "0.174.0", path = "./ext/http" }
deno_io = { version = "0.86.0", path = "./ext/io" }
deno_kv = { version = "0.84.0", path = "./ext/kv" }
deno_napi = { version = "0.107.0", path = "./ext/napi" }
deno_net = { version = "0.168.0", path = "./ext/net" }
deno_node = { version = "0.113.0", path = "./ext/node" }
deno_tls = { version = "0.163.0", path = "./ext/tls" }
deno_url = { version = "0.176.0", path = "./ext/url" }
deno_web = { version = "0.207.0", path = "./ext/web" }
deno_webgpu = { version = "0.143.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.176.0", path = "./ext/webidl" }
deno_websocket = { version = "0.181.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.171.0", path = "./ext/webstorage" }
deno_broadcast_channel = { version = "0.171.0", path = "./ext/broadcast_channel" }
deno_cache = { version = "0.109.0", path = "./ext/cache" }
deno_canvas = { version = "0.46.0", path = "./ext/canvas" }
deno_console = { version = "0.177.0", path = "./ext/console" }
deno_cron = { version = "0.57.0", path = "./ext/cron" }
deno_crypto = { version = "0.191.0", path = "./ext/crypto" }
deno_fetch = { version = "0.201.0", path = "./ext/fetch" }
deno_ffi = { version = "0.164.0", path = "./ext/ffi" }
deno_fs = { version = "0.87.0", path = "./ext/fs" }
deno_http = { version = "0.175.0", path = "./ext/http" }
deno_io = { version = "0.87.0", path = "./ext/io" }
deno_kv = { version = "0.85.0", path = "./ext/kv" }
deno_napi = { version = "0.108.0", path = "./ext/napi" }
deno_net = { version = "0.169.0", path = "./ext/net" }
deno_node = { version = "0.114.0", path = "./ext/node" }
deno_tls = { version = "0.164.0", path = "./ext/tls" }
deno_url = { version = "0.177.0", path = "./ext/url" }
deno_web = { version = "0.208.0", path = "./ext/web" }
deno_webgpu = { version = "0.144.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.177.0", path = "./ext/webidl" }
deno_websocket = { version = "0.182.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.172.0", path = "./ext/webstorage" }
# resolvers
deno_resolver = { version = "0.8.0", path = "./resolvers/deno" }
node_resolver = { version = "0.15.0", path = "./resolvers/node" }
deno_resolver = { version = "0.9.0", path = "./resolvers/deno" }
node_resolver = { version = "0.16.0", path = "./resolvers/node" }
aes = "=0.8.3"
anyhow = "1.0.57"
@ -204,7 +204,7 @@ webpki-root-certs = "0.26.5"
webpki-roots = "0.26"
which = "4.2.5"
yoke = { version = "0.7.4", features = ["derive"] }
zeromq = { version = "=0.4.0", default-features = false, features = ["tcp-transport", "tokio-runtime"] }
zeromq = { version = "=0.4.1", default-features = false, features = ["tcp-transport", "tokio-runtime"] }
zstd = "=0.12.4"
# crypto

View file

@ -6,6 +6,18 @@ https://github.com/denoland/deno/releases
We also have one-line install commands at:
https://github.com/denoland/deno_install
### 2.0.6 / 2024.11.10
- feat(ext/http): abort event when request is cancelled (#26781)
- feat(ext/http): abort signal when request is cancelled (#26761)
- feat(lsp): auto-import completions from byonm dependencies (#26680)
- fix(ext/cache): don't panic when creating cache (#26780)
- fix(ext/node): better inspector support (#26471)
- fix(fmt): don't use self-closing tags in HTML (#26754)
- fix(install): cache jsr deps from all workspace config files (#26779)
- fix(node:zlib): gzip & gzipSync should accept ArrayBuffer (#26762)
- fix: performance.timeOrigin (#26787)
### 2.0.5 / 2024.11.05
- fix(add): better error message when adding package that only has pre-release

View file

@ -2,7 +2,7 @@
[package]
name = "deno_bench_util"
version = "0.170.0"
version = "0.171.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno"
version = "2.0.5"
version = "2.0.6"
authors.workspace = true
default-run = "deno"
edition.workspace = true

View file

@ -10,6 +10,7 @@ use super::tsc;
use super::urls::url_to_uri;
use crate::args::jsr_url;
use crate::lsp::logging::lsp_warn;
use crate::lsp::search::PackageSearchApi;
use crate::tools::lint::CliLinter;
use crate::util::path::relative_specifier;
@ -747,8 +748,14 @@ pub fn ts_changes_to_edit(
) -> Result<Option<lsp::WorkspaceEdit>, AnyError> {
let mut text_document_edits = Vec::new();
for change in changes {
let text_document_edit = change.to_text_document_edit(language_server)?;
text_document_edits.push(text_document_edit);
let edit = match change.to_text_document_edit(language_server) {
Ok(e) => e,
Err(err) => {
lsp_warn!("Couldn't covert text document edit: {:#}", err);
continue;
}
};
text_document_edits.push(edit);
}
Ok(Some(lsp::WorkspaceEdit {
changes: None,

View file

@ -18,6 +18,7 @@ pub struct BinEntries<'a> {
seen_names: HashMap<&'a str, &'a NpmPackageId>,
/// The bin entries
entries: Vec<(&'a NpmResolutionPackage, PathBuf)>,
sorted: bool,
}
/// Returns the name of the default binary for the given package.
@ -31,6 +32,20 @@ fn default_bin_name(package: &NpmResolutionPackage) -> &str {
.map_or(package.id.nv.name.as_str(), |(_, name)| name)
}
pub fn warn_missing_entrypoint(
bin_name: &str,
package_path: &Path,
entrypoint: &Path,
) {
log::warn!(
"{} Trying to set up '{}' bin for \"{}\", but the entry point \"{}\" doesn't exist.",
deno_terminal::colors::yellow("Warning"),
bin_name,
package_path.display(),
entrypoint.display()
);
}
impl<'a> BinEntries<'a> {
pub fn new() -> Self {
Self::default()
@ -42,6 +57,7 @@ impl<'a> BinEntries<'a> {
package: &'a NpmResolutionPackage,
package_path: PathBuf,
) {
self.sorted = false;
// check for a new collision, if we haven't already
// found one
match package.bin.as_ref().unwrap() {
@ -79,16 +95,21 @@ impl<'a> BinEntries<'a> {
&str, // bin name
&str, // bin script
) -> Result<(), AnyError>,
mut filter: impl FnMut(&NpmResolutionPackage) -> bool,
) -> Result<(), AnyError> {
if !self.collisions.is_empty() {
if !self.collisions.is_empty() && !self.sorted {
// walking the dependency tree to find out the depth of each package
// is sort of expensive, so we only do it if there's a collision
sort_by_depth(snapshot, &mut self.entries, &mut self.collisions);
self.sorted = true;
}
let mut seen = HashSet::new();
for (package, package_path) in &self.entries {
if !filter(package) {
continue;
}
if let Some(bin_entries) = &package.bin {
match bin_entries {
deno_npm::registry::NpmPackageVersionBinEntry::String(script) => {
@ -118,8 +139,8 @@ impl<'a> BinEntries<'a> {
}
/// Collect the bin entries into a vec of (name, script path)
pub fn into_bin_files(
mut self,
pub fn collect_bin_files(
&mut self,
snapshot: &NpmResolutionSnapshot,
) -> Vec<(String, PathBuf)> {
let mut bins = Vec::new();
@ -131,17 +152,18 @@ impl<'a> BinEntries<'a> {
bins.push((name.to_string(), package_path.join(script)));
Ok(())
},
|_| true,
)
.unwrap();
bins
}
/// Finish setting up the bin entries, writing the necessary files
/// to disk.
pub fn finish(
fn set_up_entries_filtered(
mut self,
snapshot: &NpmResolutionSnapshot,
bin_node_modules_dir_path: &Path,
filter: impl FnMut(&NpmResolutionPackage) -> bool,
mut handler: impl FnMut(&EntrySetupOutcome<'_>),
) -> Result<(), AnyError> {
if !self.entries.is_empty() && !bin_node_modules_dir_path.exists() {
std::fs::create_dir_all(bin_node_modules_dir_path).with_context(
@ -160,18 +182,54 @@ impl<'a> BinEntries<'a> {
Ok(())
},
|package, package_path, name, script| {
set_up_bin_entry(
let outcome = set_up_bin_entry(
package,
name,
script,
package_path,
bin_node_modules_dir_path,
)
)?;
handler(&outcome);
Ok(())
},
filter,
)?;
Ok(())
}
/// Finish setting up the bin entries, writing the necessary files
/// to disk.
pub fn finish(
self,
snapshot: &NpmResolutionSnapshot,
bin_node_modules_dir_path: &Path,
handler: impl FnMut(&EntrySetupOutcome<'_>),
) -> Result<(), AnyError> {
self.set_up_entries_filtered(
snapshot,
bin_node_modules_dir_path,
|_| true,
handler,
)
}
/// Finish setting up the bin entries, writing the necessary files
/// to disk.
pub fn finish_only(
self,
snapshot: &NpmResolutionSnapshot,
bin_node_modules_dir_path: &Path,
handler: impl FnMut(&EntrySetupOutcome<'_>),
only: &HashSet<&NpmPackageId>,
) -> Result<(), AnyError> {
self.set_up_entries_filtered(
snapshot,
bin_node_modules_dir_path,
|package| only.contains(&package.id),
handler,
)
}
}
// walk the dependency tree to find out the depth of each package
@ -233,16 +291,17 @@ fn sort_by_depth(
});
}
pub fn set_up_bin_entry(
package: &NpmResolutionPackage,
bin_name: &str,
pub fn set_up_bin_entry<'a>(
package: &'a NpmResolutionPackage,
bin_name: &'a str,
#[allow(unused_variables)] bin_script: &str,
#[allow(unused_variables)] package_path: &Path,
#[allow(unused_variables)] package_path: &'a Path,
bin_node_modules_dir_path: &Path,
) -> Result<(), AnyError> {
) -> Result<EntrySetupOutcome<'a>, AnyError> {
#[cfg(windows)]
{
set_up_bin_shim(package, bin_name, bin_node_modules_dir_path)?;
Ok(EntrySetupOutcome::Success)
}
#[cfg(unix)]
{
@ -252,9 +311,8 @@ pub fn set_up_bin_entry(
bin_script,
package_path,
bin_node_modules_dir_path,
)?;
)
}
Ok(())
}
#[cfg(windows)]
@ -301,14 +359,39 @@ fn make_executable_if_exists(path: &Path) -> Result<bool, AnyError> {
Ok(true)
}
pub enum EntrySetupOutcome<'a> {
#[cfg_attr(windows, allow(dead_code))]
MissingEntrypoint {
bin_name: &'a str,
package_path: &'a Path,
entrypoint: PathBuf,
package: &'a NpmResolutionPackage,
},
Success,
}
impl<'a> EntrySetupOutcome<'a> {
pub fn warn_if_failed(&self) {
match self {
EntrySetupOutcome::MissingEntrypoint {
bin_name,
package_path,
entrypoint,
..
} => warn_missing_entrypoint(bin_name, package_path, entrypoint),
EntrySetupOutcome::Success => {}
}
}
}
#[cfg(unix)]
fn symlink_bin_entry(
_package: &NpmResolutionPackage,
bin_name: &str,
fn symlink_bin_entry<'a>(
package: &'a NpmResolutionPackage,
bin_name: &'a str,
bin_script: &str,
package_path: &Path,
package_path: &'a Path,
bin_node_modules_dir_path: &Path,
) -> Result<(), AnyError> {
) -> Result<EntrySetupOutcome<'a>, AnyError> {
use std::io;
use std::os::unix::fs::symlink;
let link = bin_node_modules_dir_path.join(bin_name);
@ -318,14 +401,12 @@ fn symlink_bin_entry(
format!("Can't set up '{}' bin at {}", bin_name, original.display())
})?;
if !found {
log::warn!(
"{} Trying to set up '{}' bin for \"{}\", but the entry point \"{}\" doesn't exist.",
deno_terminal::colors::yellow("Warning"),
return Ok(EntrySetupOutcome::MissingEntrypoint {
bin_name,
package_path.display(),
original.display()
);
return Ok(());
package_path,
entrypoint: original,
package,
});
}
let original_relative =
@ -348,7 +429,7 @@ fn symlink_bin_entry(
original_relative.display()
)
})?;
return Ok(());
return Ok(EntrySetupOutcome::Success);
}
return Err(err).with_context(|| {
format!(
@ -359,5 +440,5 @@ fn symlink_bin_entry(
});
}
Ok(())
Ok(EntrySetupOutcome::Success)
}

View file

@ -10,6 +10,7 @@ use deno_runtime::deno_io::FromRawIoHandle;
use deno_semver::package::PackageNv;
use deno_semver::Version;
use std::borrow::Cow;
use std::collections::HashSet;
use std::rc::Rc;
use std::path::Path;
@ -61,7 +62,7 @@ impl<'a> LifecycleScripts<'a> {
}
}
fn has_lifecycle_scripts(
pub fn has_lifecycle_scripts(
package: &NpmResolutionPackage,
package_path: &Path,
) -> bool {
@ -83,7 +84,7 @@ fn is_broken_default_install_script(script: &str, package_path: &Path) -> bool {
}
impl<'a> LifecycleScripts<'a> {
fn can_run_scripts(&self, package_nv: &PackageNv) -> bool {
pub fn can_run_scripts(&self, package_nv: &PackageNv) -> bool {
if !self.strategy.can_run_scripts() {
return false;
}
@ -98,6 +99,9 @@ impl<'a> LifecycleScripts<'a> {
PackagesAllowedScripts::None => false,
}
}
pub fn has_run_scripts(&self, package: &NpmResolutionPackage) -> bool {
self.strategy.has_run(package)
}
/// Register a package for running lifecycle scripts, if applicable.
///
/// `package_path` is the path containing the package's code (its root dir).
@ -110,12 +114,12 @@ impl<'a> LifecycleScripts<'a> {
) {
if has_lifecycle_scripts(package, &package_path) {
if self.can_run_scripts(&package.id.nv) {
if !self.strategy.has_run(package) {
if !self.has_run_scripts(package) {
self
.packages_with_scripts
.push((package, package_path.into_owned()));
}
} else if !self.strategy.has_run(package)
} else if !self.has_run_scripts(package)
&& (self.config.explicit_install || !self.strategy.has_warned(package))
{
// Skip adding `esbuild` as it is known that it can work properly without lifecycle script
@ -149,22 +153,32 @@ impl<'a> LifecycleScripts<'a> {
self,
snapshot: &NpmResolutionSnapshot,
packages: &[NpmResolutionPackage],
root_node_modules_dir_path: Option<&Path>,
root_node_modules_dir_path: &Path,
progress_bar: &ProgressBar,
) -> Result<(), AnyError> {
self.warn_not_run_scripts()?;
let get_package_path =
|p: &NpmResolutionPackage| self.strategy.package_path(p);
let mut failed_packages = Vec::new();
let mut bin_entries = BinEntries::new();
if !self.packages_with_scripts.is_empty() {
let package_ids = self
.packages_with_scripts
.iter()
.map(|(p, _)| &p.id)
.collect::<HashSet<_>>();
// get custom commands for each bin available in the node_modules dir (essentially
// the scripts that are in `node_modules/.bin`)
let base =
resolve_baseline_custom_commands(snapshot, packages, get_package_path)?;
let base = resolve_baseline_custom_commands(
&mut bin_entries,
snapshot,
packages,
get_package_path,
)?;
let init_cwd = &self.config.initial_cwd;
let process_state = crate::npm::managed::npm_process_state(
snapshot.as_valid_serialized(),
root_node_modules_dir_path,
Some(root_node_modules_dir_path),
);
let mut env_vars = crate::task_runner::real_env_vars();
@ -221,7 +235,7 @@ impl<'a> LifecycleScripts<'a> {
custom_commands: custom_commands.clone(),
init_cwd,
argv: &[],
root_node_modules_dir: root_node_modules_dir_path,
root_node_modules_dir: Some(root_node_modules_dir_path),
stdio: Some(crate::task_runner::TaskIo {
stderr: TaskStdio::piped(),
stdout: TaskStdio::piped(),
@ -262,6 +276,17 @@ impl<'a> LifecycleScripts<'a> {
}
self.strategy.did_run_scripts(package)?;
}
// re-set up bin entries for the packages which we've run scripts for.
// lifecycle scripts can create files that are linked to by bin entries,
// and the only reliable way to handle this is to re-link bin entries
// (this is what PNPM does as well)
bin_entries.finish_only(
snapshot,
&root_node_modules_dir_path.join(".bin"),
|outcome| outcome.warn_if_failed(),
&package_ids,
)?;
}
if failed_packages.is_empty() {
Ok(())
@ -281,9 +306,10 @@ impl<'a> LifecycleScripts<'a> {
// take in all (non copy) packages from snapshot,
// and resolve the set of available binaries to create
// custom commands available to the task runner
fn resolve_baseline_custom_commands(
snapshot: &NpmResolutionSnapshot,
packages: &[NpmResolutionPackage],
fn resolve_baseline_custom_commands<'a>(
bin_entries: &mut BinEntries<'a>,
snapshot: &'a NpmResolutionSnapshot,
packages: &'a [NpmResolutionPackage],
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
let mut custom_commands = crate::task_runner::TaskCustomCommands::new();
@ -306,6 +332,7 @@ fn resolve_baseline_custom_commands(
// doing it for packages that are set up already.
// realistically, scripts won't be run very often so it probably isn't too big of an issue.
resolve_custom_commands_from_packages(
bin_entries,
custom_commands,
snapshot,
packages,
@ -320,12 +347,12 @@ fn resolve_custom_commands_from_packages<
'a,
P: IntoIterator<Item = &'a NpmResolutionPackage>,
>(
bin_entries: &mut BinEntries<'a>,
mut commands: crate::task_runner::TaskCustomCommands,
snapshot: &'a NpmResolutionSnapshot,
packages: P,
get_package_path: impl Fn(&'a NpmResolutionPackage) -> PathBuf,
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
let mut bin_entries = BinEntries::new();
for package in packages {
let package_path = get_package_path(package);
@ -333,7 +360,7 @@ fn resolve_custom_commands_from_packages<
bin_entries.add(package, package_path);
}
}
let bins = bin_entries.into_bin_files(snapshot);
let bins: Vec<(String, PathBuf)> = bin_entries.collect_bin_files(snapshot);
for (bin_name, script_path) in bins {
commands.insert(
bin_name.clone(),
@ -356,7 +383,9 @@ fn resolve_custom_commands_from_deps(
snapshot: &NpmResolutionSnapshot,
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
let mut bin_entries = BinEntries::new();
resolve_custom_commands_from_packages(
&mut bin_entries,
baseline,
snapshot,
package

View file

@ -55,6 +55,7 @@ use crate::util::progress_bar::ProgressMessagePrompt;
use super::super::cache::NpmCache;
use super::super::cache::TarballCache;
use super::super::resolution::NpmResolution;
use super::common::bin_entries;
use super::common::NpmPackageFsResolver;
use super::common::RegistryReadPermissionChecker;
@ -329,8 +330,7 @@ async fn sync_resolution_with_fs(
let mut cache_futures = FuturesUnordered::new();
let mut newest_packages_by_name: HashMap<&String, &NpmResolutionPackage> =
HashMap::with_capacity(package_partitions.packages.len());
let bin_entries =
Rc::new(RefCell::new(super::common::bin_entries::BinEntries::new()));
let bin_entries = Rc::new(RefCell::new(bin_entries::BinEntries::new()));
let mut lifecycle_scripts =
super::common::lifecycle_scripts::LifecycleScripts::new(
lifecycle_scripts,
@ -658,7 +658,28 @@ async fn sync_resolution_with_fs(
// 7. Set up `node_modules/.bin` entries for packages that need it.
{
let bin_entries = std::mem::take(&mut *bin_entries.borrow_mut());
bin_entries.finish(snapshot, &bin_node_modules_dir_path)?;
bin_entries.finish(
snapshot,
&bin_node_modules_dir_path,
|setup_outcome| {
match setup_outcome {
bin_entries::EntrySetupOutcome::MissingEntrypoint {
package,
package_path,
..
} if super::common::lifecycle_scripts::has_lifecycle_scripts(
package,
package_path,
) && lifecycle_scripts.can_run_scripts(&package.id.nv)
&& !lifecycle_scripts.has_run_scripts(package) =>
{
// ignore, it might get fixed when the lifecycle scripts run.
// if not, we'll warn then
}
outcome => outcome.warn_if_failed(),
}
},
)?;
}
// 8. Create symlinks for the workspace packages
@ -708,7 +729,7 @@ async fn sync_resolution_with_fs(
.finish(
snapshot,
&package_partitions.packages,
Some(root_node_modules_dir_path),
root_node_modules_dir_path,
progress_bar,
)
.await?;

View file

@ -2,7 +2,6 @@
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::time;
use deno_core::error::generic_error;
use deno_core::error::type_error;
@ -13,6 +12,7 @@ use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_runtime::deno_permissions::ChildPermissionsArg;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_web::StartTime;
use tokio::sync::mpsc::UnboundedSender;
use uuid::Uuid;
@ -148,7 +148,7 @@ fn op_dispatch_bench_event(state: &mut OpState, #[serde] event: BenchEvent) {
#[op2(fast)]
#[number]
fn op_bench_now(state: &mut OpState) -> Result<u64, std::num::TryFromIntError> {
let ns = state.borrow::<time::Instant>().elapsed().as_nanos();
let ns = state.borrow::<StartTime>().elapsed().as_nanos();
let ns_u64 = u64::try_from(ns)?;
Ok(ns_u64)
}

View file

@ -44,7 +44,11 @@ pub async fn cache_top_level_deps(
let mut seen_reqs = std::collections::HashSet::new();
for entry in import_map.imports().entries() {
for entry in import_map.imports().entries().chain(
import_map
.scopes()
.flat_map(|scope| scope.imports.entries()),
) {
let Some(specifier) = entry.value else {
continue;
};

View file

@ -2,7 +2,7 @@
[package]
name = "deno_broadcast_channel"
version = "0.170.0"
version = "0.171.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_cache"
version = "0.108.0"
version = "0.109.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

6
ext/cache/lib.rs vendored
View file

@ -33,7 +33,9 @@ pub enum CacheError {
}
#[derive(Clone)]
pub struct CreateCache<C: Cache + 'static>(pub Arc<dyn Fn() -> C>);
pub struct CreateCache<C: Cache + 'static>(
pub Arc<dyn Fn() -> Result<C, CacheError>>,
);
deno_core::extension!(deno_cache,
deps = [ deno_webidl, deno_web, deno_url, deno_fetch ],
@ -231,7 +233,7 @@ where
if let Some(cache) = state.try_borrow::<CA>() {
Ok(cache.clone())
} else if let Some(create_cache) = state.try_borrow::<CreateCache<CA>>() {
let cache = create_cache.0();
let cache = create_cache.0()?;
state.put(cache);
Ok(state.borrow::<CA>().clone())
} else {

23
ext/cache/sqlite.rs vendored
View file

@ -42,7 +42,7 @@ pub struct SqliteBackedCache {
}
impl SqliteBackedCache {
pub fn new(cache_storage_dir: PathBuf) -> Self {
pub fn new(cache_storage_dir: PathBuf) -> Result<Self, CacheError> {
{
std::fs::create_dir_all(&cache_storage_dir)
.expect("failed to create cache dir");
@ -57,18 +57,14 @@ impl SqliteBackedCache {
PRAGMA synchronous=NORMAL;
PRAGMA optimize;
";
connection
.execute_batch(initial_pragmas)
.expect("failed to execute pragmas");
connection
.execute(
"CREATE TABLE IF NOT EXISTS cache_storage (
connection.execute_batch(initial_pragmas)?;
connection.execute(
"CREATE TABLE IF NOT EXISTS cache_storage (
id INTEGER PRIMARY KEY,
cache_name TEXT NOT NULL UNIQUE
)",
(),
)
.expect("failed to create cache_storage table");
(),
)?;
connection
.execute(
"CREATE TABLE IF NOT EXISTS request_response_list (
@ -86,12 +82,11 @@ impl SqliteBackedCache {
UNIQUE (cache_id, request_url)
)",
(),
)
.expect("failed to create request_response_list table");
SqliteBackedCache {
)?;
Ok(SqliteBackedCache {
connection: Arc::new(Mutex::new(connection)),
cache_storage_dir,
}
})
}
}
}

View file

@ -2,7 +2,7 @@
[package]
name = "deno_canvas"
version = "0.45.0"
version = "0.46.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_console"
version = "0.176.0"
version = "0.177.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_cron"
version = "0.56.0"
version = "0.57.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_crypto"
version = "0.190.0"
version = "0.191.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -281,11 +281,11 @@ class Request {
if (signal === undefined) {
const signal = newSignal();
this[_signalCache] = signal;
return signal;
}
this[_request].onCancel?.(() => {
signal[signalAbort](signalAbortError);
});
if (!signal.aborted && this[_request].isCancelled) {
signal[signalAbort](signalAbortError);
return signal;
}
return signal;

View file

@ -2,7 +2,7 @@
[package]
name = "deno_fetch"
version = "0.200.0"
version = "0.201.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_ffi"
version = "0.163.0"
version = "0.164.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_fs"
version = "0.86.0"
version = "0.87.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -11,10 +11,10 @@ import {
op_http_cancel,
op_http_close,
op_http_close_after_finish,
op_http_get_request_cancelled,
op_http_get_request_headers,
op_http_get_request_method_and_url,
op_http_read_request_body,
op_http_request_on_cancel,
op_http_serve,
op_http_serve_on,
op_http_set_promise_complete,
@ -375,11 +375,16 @@ class InnerRequest {
return this.#external;
}
get isCancelled() {
onCancel(callback) {
if (this.#external === null) {
return true;
callback();
return;
}
return op_http_get_request_cancelled(this.#external);
PromisePrototypeThen(
op_http_request_on_cancel(this.#external),
callback,
);
}
}

View file

@ -2,7 +2,7 @@
[package]
name = "deno_http"
version = "0.174.0"
version = "0.175.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -564,6 +564,7 @@ fn is_request_compressible(
match accept_encoding.to_str() {
// Firefox and Chrome send this -- no need to parse
Ok("gzip, deflate, br") => return Compression::Brotli,
Ok("gzip, deflate, br, zstd") => return Compression::Brotli,
Ok("gzip") => return Compression::GZip,
Ok("br") => return Compression::Brotli,
_ => (),
@ -708,6 +709,19 @@ pub fn op_http_get_request_cancelled(external: *const c_void) -> bool {
http.cancelled()
}
#[op2(async)]
pub async fn op_http_request_on_cancel(external: *const c_void) {
let http =
// SAFETY: op is called with external.
unsafe { clone_external!(external, "op_http_request_on_cancel") };
let (tx, rx) = tokio::sync::oneshot::channel();
http.on_cancel(tx);
drop(http);
rx.await.ok();
}
/// Returned promise resolves when body streaming finishes.
/// Call [`op_http_close_after_finish`] when done with the external.
#[op2(async)]

View file

@ -112,6 +112,7 @@ deno_core::extension!(
http_next::op_http_close_after_finish,
http_next::op_http_get_request_header,
http_next::op_http_get_request_headers,
http_next::op_http_request_on_cancel,
http_next::op_http_get_request_method_and_url<HTTP>,
http_next::op_http_get_request_cancelled,
http_next::op_http_read_request_body,

View file

@ -27,6 +27,7 @@ use std::rc::Rc;
use std::task::Context;
use std::task::Poll;
use std::task::Waker;
use tokio::sync::oneshot;
pub type Request = hyper::Request<Incoming>;
pub type Response = hyper::Response<HttpRecordResponse>;
@ -211,6 +212,7 @@ pub struct UpgradeUnavailableError;
struct HttpRecordInner {
server_state: SignallingRc<HttpServerState>,
closed_channel: Option<oneshot::Sender<()>>,
request_info: HttpConnectionProperties,
request_parts: http::request::Parts,
request_body: Option<RequestBodyState>,
@ -276,6 +278,7 @@ impl HttpRecord {
response_body_finished: false,
response_body_waker: None,
trailers: None,
closed_channel: None,
been_dropped: false,
finished: false,
needs_close_after_finish: false,
@ -312,6 +315,10 @@ impl HttpRecord {
RefMut::map(self.self_mut(), |inner| &mut inner.needs_close_after_finish)
}
pub fn on_cancel(&self, sender: oneshot::Sender<()>) {
self.self_mut().closed_channel = Some(sender);
}
fn recycle(self: Rc<Self>) {
assert!(
Rc::strong_count(&self) == 1,
@ -390,6 +397,9 @@ impl HttpRecord {
inner.been_dropped = true;
// The request body might include actual resources.
inner.request_body.take();
if let Some(closed_channel) = inner.closed_channel.take() {
let _ = closed_channel.send(());
}
}
/// Complete this record, potentially expunging it if it is fully complete (ie: cancelled as well).

View file

@ -2,7 +2,7 @@
[package]
name = "deno_io"
version = "0.86.0"
version = "0.87.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_kv"
version = "0.84.0"
version = "0.85.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -1,16 +1,17 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
#[derive(Clone, Copy, Debug)]
pub struct KvConfig {
pub(crate) max_write_key_size_bytes: usize,
pub(crate) max_read_key_size_bytes: usize,
pub(crate) max_value_size_bytes: usize,
pub(crate) max_read_ranges: usize,
pub(crate) max_read_entries: usize,
pub(crate) max_checks: usize,
pub(crate) max_mutations: usize,
pub(crate) max_watched_keys: usize,
pub(crate) max_total_mutation_size_bytes: usize,
pub(crate) max_total_key_size_bytes: usize,
pub max_write_key_size_bytes: usize,
pub max_read_key_size_bytes: usize,
pub max_value_size_bytes: usize,
pub max_read_ranges: usize,
pub max_read_entries: usize,
pub max_checks: usize,
pub max_mutations: usize,
pub max_watched_keys: usize,
pub max_total_mutation_size_bytes: usize,
pub max_total_key_size_bytes: usize,
}
impl KvConfig {

View file

@ -2,7 +2,7 @@
[package]
name = "deno_napi"
version = "0.107.0"
version = "0.108.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "napi_sym"
version = "0.106.0"
version = "0.107.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_net"
version = "0.168.0"
version = "0.169.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_node"
version = "0.113.0"
version = "0.114.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -18,7 +18,7 @@
*/
import { primordials } from "ext:core/mod.js";
const { JSONStringify, SymbolFor } = primordials;
const { JSONStringify, SafeArrayIterator, SymbolFor } = primordials;
import { format, inspect } from "ext:deno_node/internal/util/inspect.mjs";
import { codes } from "ext:deno_node/internal/error_codes.ts";
import {
@ -1874,6 +1874,11 @@ export class ERR_SOCKET_CLOSED extends NodeError {
super("ERR_SOCKET_CLOSED", `Socket is closed`);
}
}
export class ERR_SOCKET_CONNECTION_TIMEOUT extends NodeError {
constructor() {
super("ERR_SOCKET_CONNECTION_TIMEOUT", `Socket connection timeout`);
}
}
export class ERR_SOCKET_DGRAM_IS_CONNECTED extends NodeError {
constructor() {
super("ERR_SOCKET_DGRAM_IS_CONNECTED", `Already connected`);
@ -2633,11 +2638,30 @@ export function aggregateTwoErrors(
}
return innerError || outerError;
}
export class NodeAggregateError extends AggregateError {
code: string;
constructor(errors, message) {
super(new SafeArrayIterator(errors), message);
this.code = errors[0]?.code;
}
get [kIsNodeError]() {
return true;
}
// deno-lint-ignore adjacent-overload-signatures
get ["constructor"]() {
return AggregateError;
}
}
codes.ERR_IPC_CHANNEL_CLOSED = ERR_IPC_CHANNEL_CLOSED;
codes.ERR_INVALID_ARG_TYPE = ERR_INVALID_ARG_TYPE;
codes.ERR_INVALID_ARG_VALUE = ERR_INVALID_ARG_VALUE;
codes.ERR_OUT_OF_RANGE = ERR_OUT_OF_RANGE;
codes.ERR_SOCKET_BAD_PORT = ERR_SOCKET_BAD_PORT;
codes.ERR_SOCKET_CONNECTION_TIMEOUT = ERR_SOCKET_CONNECTION_TIMEOUT;
codes.ERR_BUFFER_OUT_OF_BOUNDS = ERR_BUFFER_OUT_OF_BOUNDS;
codes.ERR_UNKNOWN_ENCODING = ERR_UNKNOWN_ENCODING;
codes.ERR_PARSE_ARGS_INVALID_OPTION_VALUE = ERR_PARSE_ARGS_INVALID_OPTION_VALUE;

View file

@ -95,4 +95,5 @@ export function makeSyncWrite(fd: number) {
};
}
export const kReinitializeHandle = Symbol("kReinitializeHandle");
export const normalizedArgsSymbol = Symbol("normalizedArgs");

View file

@ -530,10 +530,12 @@ export function mapSysErrnoToUvErrno(sysErrno: number): number {
export const UV_EAI_MEMORY = codeMap.get("EAI_MEMORY")!;
export const UV_EBADF = codeMap.get("EBADF")!;
export const UV_ECANCELED = codeMap.get("ECANCELED")!;
export const UV_EEXIST = codeMap.get("EEXIST");
export const UV_EINVAL = codeMap.get("EINVAL")!;
export const UV_ENOENT = codeMap.get("ENOENT");
export const UV_ENOTSOCK = codeMap.get("ENOTSOCK")!;
export const UV_ETIMEDOUT = codeMap.get("ETIMEDOUT")!;
export const UV_UNKNOWN = codeMap.get("UNKNOWN")!;
export function errname(errno: number): string {

View file

@ -31,6 +31,7 @@ import {
isIP,
isIPv4,
isIPv6,
kReinitializeHandle,
normalizedArgsSymbol,
} from "ext:deno_node/internal/net.ts";
import { Duplex } from "node:stream";
@ -50,9 +51,11 @@ import {
ERR_SERVER_ALREADY_LISTEN,
ERR_SERVER_NOT_RUNNING,
ERR_SOCKET_CLOSED,
ERR_SOCKET_CONNECTION_TIMEOUT,
errnoException,
exceptionWithHostPort,
genericNodeError,
NodeAggregateError,
uvExceptionWithHostPort,
} from "ext:deno_node/internal/errors.ts";
import type { ErrnoException } from "ext:deno_node/internal/errors.ts";
@ -80,6 +83,7 @@ import { Buffer } from "node:buffer";
import type { LookupOneOptions } from "ext:deno_node/internal/dns/utils.ts";
import {
validateAbortSignal,
validateBoolean,
validateFunction,
validateInt32,
validateNumber,
@ -100,13 +104,25 @@ import { ShutdownWrap } from "ext:deno_node/internal_binding/stream_wrap.ts";
import { assert } from "ext:deno_node/_util/asserts.ts";
import { isWindows } from "ext:deno_node/_util/os.ts";
import { ADDRCONFIG, lookup as dnsLookup } from "node:dns";
import { codeMap } from "ext:deno_node/internal_binding/uv.ts";
import {
codeMap,
UV_ECANCELED,
UV_ETIMEDOUT,
} from "ext:deno_node/internal_binding/uv.ts";
import { guessHandleType } from "ext:deno_node/internal_binding/util.ts";
import { debuglog } from "ext:deno_node/internal/util/debuglog.ts";
import type { DuplexOptions } from "ext:deno_node/_stream.d.ts";
import type { BufferEncoding } from "ext:deno_node/_global.d.ts";
import type { Abortable } from "ext:deno_node/_events.d.ts";
import { channel } from "node:diagnostics_channel";
import { primordials } from "ext:core/mod.js";
const {
ArrayPrototypeIncludes,
ArrayPrototypePush,
FunctionPrototypeBind,
MathMax,
} = primordials;
let debug = debuglog("net", (fn) => {
debug = fn;
@ -120,6 +136,9 @@ const kBytesWritten = Symbol("kBytesWritten");
const DEFAULT_IPV4_ADDR = "0.0.0.0";
const DEFAULT_IPV6_ADDR = "::";
let autoSelectFamilyDefault = true;
let autoSelectFamilyAttemptTimeoutDefault = 250;
type Handle = TCP | Pipe;
interface HandleOptions {
@ -214,6 +233,8 @@ interface TcpSocketConnectOptions extends ConnectOptions {
hints?: number;
family?: number;
lookup?: LookupFunction;
autoSelectFamily?: boolean | undefined;
autoSelectFamilyAttemptTimeout?: number | undefined;
}
interface IpcSocketConnectOptions extends ConnectOptions {
@ -316,12 +337,6 @@ export function _normalizeArgs(args: unknown[]): NormalizedArgs {
return arr;
}
function _isTCPConnectWrap(
req: TCPConnectWrap | PipeConnectWrap,
): req is TCPConnectWrap {
return "localAddress" in req && "localPort" in req;
}
function _afterConnect(
status: number,
// deno-lint-ignore no-explicit-any
@ -372,7 +387,7 @@ function _afterConnect(
socket.connecting = false;
let details;
if (_isTCPConnectWrap(req)) {
if (req.localAddress && req.localPort) {
details = req.localAddress + ":" + req.localPort;
}
@ -384,7 +399,7 @@ function _afterConnect(
details,
);
if (_isTCPConnectWrap(req)) {
if (details) {
ex.localAddress = req.localAddress;
ex.localPort = req.localPort;
}
@ -393,6 +408,107 @@ function _afterConnect(
}
}
function _createConnectionError(req, status) {
let details;
if (req.localAddress && req.localPort) {
details = req.localAddress + ":" + req.localPort;
}
const ex = exceptionWithHostPort(
status,
"connect",
req.address,
req.port,
details,
);
if (details) {
ex.localAddress = req.localAddress;
ex.localPort = req.localPort;
}
return ex;
}
function _afterConnectMultiple(
context,
current,
status,
handle,
req,
readable,
writable,
) {
debug(
"connect/multiple: connection attempt to %s:%s completed with status %s",
req.address,
req.port,
status,
);
// Make sure another connection is not spawned
clearTimeout(context[kTimeout]);
// One of the connection has completed and correctly dispatched but after timeout, ignore this one
if (status === 0 && current !== context.current - 1) {
debug(
"connect/multiple: ignoring successful but timedout connection to %s:%s",
req.address,
req.port,
);
handle.close();
return;
}
const self = context.socket;
// Some error occurred, add to the list of exceptions
if (status !== 0) {
const ex = _createConnectionError(req, status);
ArrayPrototypePush(context.errors, ex);
self.emit(
"connectionAttemptFailed",
req.address,
req.port,
req.addressType,
ex,
);
// Try the next address, unless we were aborted
if (context.socket.connecting) {
_internalConnectMultiple(context, status === UV_ECANCELED);
}
return;
}
_afterConnect(status, self._handle, req, readable, writable);
}
function _internalConnectMultipleTimeout(context, req, handle) {
debug(
"connect/multiple: connection to %s:%s timed out",
req.address,
req.port,
);
context.socket.emit(
"connectionAttemptTimeout",
req.address,
req.port,
req.addressType,
);
req.oncomplete = undefined;
ArrayPrototypePush(context.errors, _createConnectionError(req, UV_ETIMEDOUT));
handle.close();
// Try the next address, unless we were aborted
if (context.socket.connecting) {
_internalConnectMultiple(context);
}
}
function _checkBindError(err: number, port: number, handle: TCP) {
// EADDRINUSE may not be reported until we call `listen()` or `connect()`.
// To complicate matters, a failed `bind()` followed by `listen()` or `connect()`
@ -495,6 +611,131 @@ function _internalConnect(
}
}
function _internalConnectMultiple(context, canceled?: boolean) {
clearTimeout(context[kTimeout]);
const self = context.socket;
// We were requested to abort. Stop all operations
if (self._aborted) {
return;
}
// All connections have been tried without success, destroy with error
if (canceled || context.current === context.addresses.length) {
if (context.errors.length === 0) {
self.destroy(new ERR_SOCKET_CONNECTION_TIMEOUT());
return;
}
self.destroy(new NodeAggregateError(context.errors));
return;
}
assert(self.connecting);
const current = context.current++;
if (current > 0) {
self[kReinitializeHandle](new TCP(TCPConstants.SOCKET));
}
const { localPort, port, flags } = context;
const { address, family: addressType } = context.addresses[current];
let localAddress;
let err;
if (localPort) {
if (addressType === 4) {
localAddress = DEFAULT_IPV4_ADDR;
err = self._handle.bind(localAddress, localPort);
} else { // addressType === 6
localAddress = DEFAULT_IPV6_ADDR;
err = self._handle.bind6(localAddress, localPort, flags);
}
debug(
"connect/multiple: binding to localAddress: %s and localPort: %d (addressType: %d)",
localAddress,
localPort,
addressType,
);
err = _checkBindError(err, localPort, self._handle);
if (err) {
ArrayPrototypePush(
context.errors,
exceptionWithHostPort(err, "bind", localAddress, localPort),
);
_internalConnectMultiple(context);
return;
}
}
debug(
"connect/multiple: attempting to connect to %s:%d (addressType: %d)",
address,
port,
addressType,
);
self.emit("connectionAttempt", address, port, addressType);
const req = new TCPConnectWrap();
req.oncomplete = FunctionPrototypeBind(
_afterConnectMultiple,
undefined,
context,
current,
);
req.address = address;
req.port = port;
req.localAddress = localAddress;
req.localPort = localPort;
req.addressType = addressType;
ArrayPrototypePush(
self.autoSelectFamilyAttemptedAddresses,
`${address}:${port}`,
);
if (addressType === 4) {
err = self._handle.connect(req, address, port);
} else {
err = self._handle.connect6(req, address, port);
}
if (err) {
const sockname = self._getsockname();
let details;
if (sockname) {
details = sockname.address + ":" + sockname.port;
}
const ex = exceptionWithHostPort(err, "connect", address, port, details);
ArrayPrototypePush(context.errors, ex);
self.emit("connectionAttemptFailed", address, port, addressType, ex);
_internalConnectMultiple(context);
return;
}
if (current < context.addresses.length - 1) {
debug(
"connect/multiple: setting the attempt timeout to %d ms",
context.timeout,
);
// If the attempt has not returned an error, start the connection timer
context[kTimeout] = setTimeout(
_internalConnectMultipleTimeout,
context.timeout,
context,
req,
self._handle,
);
}
}
// Provide a better error message when we call end() as a result
// of the other side sending a FIN. The standard "write after end"
// is overly vague, and makes it seem like the user's code is to blame.
@ -597,7 +838,7 @@ function _lookupAndConnect(
) {
const { localAddress, localPort } = options;
const host = options.host || "localhost";
let { port } = options;
let { port, autoSelectFamilyAttemptTimeout, autoSelectFamily } = options;
if (localAddress && !isIP(localAddress)) {
throw new ERR_INVALID_IP_ADDRESS(localAddress);
@ -621,6 +862,22 @@ function _lookupAndConnect(
port |= 0;
if (autoSelectFamily != null) {
validateBoolean(autoSelectFamily, "options.autoSelectFamily");
} else {
autoSelectFamily = autoSelectFamilyDefault;
}
if (autoSelectFamilyAttemptTimeout !== undefined) {
validateInt32(autoSelectFamilyAttemptTimeout);
if (autoSelectFamilyAttemptTimeout < 10) {
autoSelectFamilyAttemptTimeout = 10;
}
} else {
autoSelectFamilyAttemptTimeout = autoSelectFamilyAttemptTimeoutDefault;
}
// If host is an IP, skip performing a lookup
const addressType = isIP(host);
if (addressType) {
@ -649,6 +906,7 @@ function _lookupAndConnect(
const dnsOpts = {
family: options.family,
hints: options.hints || 0,
all: false,
};
if (
@ -665,6 +923,31 @@ function _lookupAndConnect(
self._host = host;
const lookup = options.lookup || dnsLookup;
if (
dnsOpts.family !== 4 && dnsOpts.family !== 6 && !localAddress &&
autoSelectFamily
) {
debug("connect: autodetecting");
dnsOpts.all = true;
defaultTriggerAsyncIdScope(self[asyncIdSymbol], function () {
_lookupAndConnectMultiple(
self,
asyncIdSymbol,
lookup,
host,
options,
dnsOpts,
port,
localAddress,
localPort,
autoSelectFamilyAttemptTimeout,
);
});
return;
}
defaultTriggerAsyncIdScope(self[asyncIdSymbol], function () {
lookup(
host,
@ -719,6 +1002,143 @@ function _lookupAndConnect(
});
}
function _lookupAndConnectMultiple(
self: Socket,
asyncIdSymbol: number,
// deno-lint-ignore no-explicit-any
lookup: any,
host: string,
options: TcpSocketConnectOptions,
dnsopts,
port: number,
localAddress: string,
localPort: number,
timeout: number | undefined,
) {
defaultTriggerAsyncIdScope(self[asyncIdSymbol], function emitLookup() {
lookup(host, dnsopts, function emitLookup(err, addresses) {
// It's possible we were destroyed while looking this up.
// XXX it would be great if we could cancel the promise returned by
// the look up.
if (!self.connecting) {
return;
} else if (err) {
self.emit("lookup", err, undefined, undefined, host);
// net.createConnection() creates a net.Socket object and immediately
// calls net.Socket.connect() on it (that's us). There are no event
// listeners registered yet so defer the error event to the next tick.
nextTick(_connectErrorNT, self, err);
return;
}
// Filter addresses by only keeping the one which are either IPv4 or IPV6.
// The first valid address determines which group has preference on the
// alternate family sorting which happens later.
const validAddresses = [[], []];
const validIps = [[], []];
let destinations;
for (let i = 0, l = addresses.length; i < l; i++) {
const address = addresses[i];
const { address: ip, family: addressType } = address;
self.emit("lookup", err, ip, addressType, host);
// It's possible we were destroyed while looking this up.
if (!self.connecting) {
return;
}
if (isIP(ip) && (addressType === 4 || addressType === 6)) {
destinations ||= addressType === 6 ? { 6: 0, 4: 1 } : { 4: 0, 6: 1 };
const destination = destinations[addressType];
// Only try an address once
if (!ArrayPrototypeIncludes(validIps[destination], ip)) {
ArrayPrototypePush(validAddresses[destination], address);
ArrayPrototypePush(validIps[destination], ip);
}
}
}
// When no AAAA or A records are available, fail on the first one
if (!validAddresses[0].length && !validAddresses[1].length) {
const { address: firstIp, family: firstAddressType } = addresses[0];
if (!isIP(firstIp)) {
err = new ERR_INVALID_IP_ADDRESS(firstIp);
nextTick(_connectErrorNT, self, err);
} else if (firstAddressType !== 4 && firstAddressType !== 6) {
err = new ERR_INVALID_ADDRESS_FAMILY(
firstAddressType,
options.host,
options.port,
);
nextTick(_connectErrorNT, self, err);
}
return;
}
// Sort addresses alternating families
const toAttempt = [];
for (
let i = 0,
l = MathMax(validAddresses[0].length, validAddresses[1].length);
i < l;
i++
) {
if (i in validAddresses[0]) {
ArrayPrototypePush(toAttempt, validAddresses[0][i]);
}
if (i in validAddresses[1]) {
ArrayPrototypePush(toAttempt, validAddresses[1][i]);
}
}
if (toAttempt.length === 1) {
debug(
"connect/multiple: only one address found, switching back to single connection",
);
const { address: ip, family: addressType } = toAttempt[0];
self._unrefTimer();
defaultTriggerAsyncIdScope(
self[asyncIdSymbol],
_internalConnect,
self,
ip,
port,
addressType,
localAddress,
localPort,
);
return;
}
self.autoSelectFamilyAttemptedAddresses = [];
debug("connect/multiple: will try the following addresses", toAttempt);
const context = {
socket: self,
addresses: toAttempt,
current: 0,
port,
localPort,
timeout,
[kTimeout]: null,
errors: [],
};
self._unrefTimer();
defaultTriggerAsyncIdScope(
self[asyncIdSymbol],
_internalConnectMultiple,
context,
);
});
});
}
function _afterShutdown(this: ShutdownWrap<TCP>) {
// deno-lint-ignore no-explicit-any
const self: any = this.handle[ownerSymbol];
@ -777,6 +1197,7 @@ export class Socket extends Duplex {
_host: string | null = null;
// deno-lint-ignore no-explicit-any
_parent: any = null;
autoSelectFamilyAttemptedAddresses: AddressInfo[] | undefined = undefined;
constructor(options: SocketOptions | number) {
if (typeof options === "number") {
@ -1546,6 +1967,16 @@ export class Socket extends Duplex {
set _handle(v: Handle | null) {
this[kHandle] = v;
}
// deno-lint-ignore no-explicit-any
[kReinitializeHandle](handle: any) {
this._handle?.close();
this._handle = handle;
this._handle[ownerSymbol] = this;
_initSocketHandle(this);
}
}
export const Stream = Socket;
@ -1593,6 +2024,33 @@ export function connect(...args: unknown[]) {
export const createConnection = connect;
/** https://docs.deno.com/api/node/net/#namespace_getdefaultautoselectfamily */
export function getDefaultAutoSelectFamily() {
return autoSelectFamilyDefault;
}
/** https://docs.deno.com/api/node/net/#namespace_setdefaultautoselectfamily */
export function setDefaultAutoSelectFamily(value: boolean) {
validateBoolean(value, "value");
autoSelectFamilyDefault = value;
}
/** https://docs.deno.com/api/node/net/#namespace_getdefaultautoselectfamilyattempttimeout */
export function getDefaultAutoSelectFamilyAttemptTimeout() {
return autoSelectFamilyAttemptTimeoutDefault;
}
/** https://docs.deno.com/api/node/net/#namespace_setdefaultautoselectfamilyattempttimeout */
export function setDefaultAutoSelectFamilyAttemptTimeout(value: number) {
validateInt32(value, "value", 1);
if (value < 10) {
value = 10;
}
autoSelectFamilyAttemptTimeoutDefault = value;
}
export interface ListenOptions extends Abortable {
fd?: number;
port?: number | undefined;
@ -2478,15 +2936,19 @@ export { BlockList, isIP, isIPv4, isIPv6, SocketAddress };
export default {
_createServerHandle,
_normalizeArgs,
isIP,
isIPv4,
isIPv6,
BlockList,
SocketAddress,
connect,
createConnection,
createServer,
getDefaultAutoSelectFamily,
getDefaultAutoSelectFamilyAttemptTimeout,
isIP,
isIPv4,
isIPv6,
Server,
setDefaultAutoSelectFamily,
setDefaultAutoSelectFamilyAttemptTimeout,
Socket,
SocketAddress,
Stream,
};

View file

@ -2,7 +2,7 @@
[package]
name = "deno_tls"
version = "0.163.0"
version = "0.164.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_url"
version = "0.176.0"
version = "0.177.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -1,12 +1,9 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { core, primordials } from "ext:core/mod.js";
import { op_defer, op_now } from "ext:core/ops";
import { op_defer } from "ext:core/ops";
const {
Uint8Array,
Uint32Array,
PromisePrototypeThen,
TypedArrayPrototypeGetBuffer,
TypeError,
indirectEval,
ReflectApply,
@ -18,13 +15,6 @@ const {
import * as webidl from "ext:deno_webidl/00_webidl.js";
const hrU8 = new Uint8Array(8);
const hr = new Uint32Array(TypedArrayPrototypeGetBuffer(hrU8));
function opNow() {
op_now(hrU8);
return (hr[0] * 1000 + hr[1] / 1e6);
}
// ---------------------------------------------------------------------------
function checkThis(thisArg) {
@ -151,7 +141,6 @@ export {
clearInterval,
clearTimeout,
defer,
opNow,
refTimer,
setImmediate,
setInterval,

View file

@ -1,6 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { primordials } from "ext:core/mod.js";
import { op_now, op_time_origin } from "ext:core/ops";
const {
ArrayPrototypeFilter,
ArrayPrototypePush,
@ -10,19 +11,34 @@ const {
Symbol,
SymbolFor,
TypeError,
TypedArrayPrototypeGetBuffer,
Uint8Array,
Uint32Array,
} = primordials;
import * as webidl from "ext:deno_webidl/00_webidl.js";
import { structuredClone } from "./02_structured_clone.js";
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
import { EventTarget } from "./02_event.js";
import { opNow } from "./02_timers.js";
import { DOMException } from "./01_dom_exception.js";
const illegalConstructorKey = Symbol("illegalConstructorKey");
let performanceEntries = [];
let timeOrigin;
const hrU8 = new Uint8Array(8);
const hr = new Uint32Array(TypedArrayPrototypeGetBuffer(hrU8));
function setTimeOrigin() {
op_time_origin(hrU8);
timeOrigin = hr[0] * 1000 + hr[1] / 1e6;
}
function now() {
op_now(hrU8);
return hr[0] * 1000 + hr[1] / 1e6;
}
webidl.converters["PerformanceMarkOptions"] = webidl
.createDictionaryConverter(
"PerformanceMarkOptions",
@ -90,10 +106,6 @@ webidl.converters["DOMString or PerformanceMeasureOptions"] = (
return webidl.converters.DOMString(V, prefix, context, opts);
};
function setTimeOrigin(origin) {
timeOrigin = origin;
}
function findMostRecent(
name,
type,
@ -135,8 +147,6 @@ function filterByNameType(
);
}
const now = opNow;
const _name = Symbol("[[name]]");
const _entryType = Symbol("[[entryType]]");
const _startTime = Symbol("[[startTime]]");

View file

@ -2,7 +2,7 @@
[package]
name = "deno_web"
version = "0.207.0"
version = "0.208.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -52,7 +52,8 @@ pub use crate::message_port::Transferable;
use crate::timers::op_defer;
use crate::timers::op_now;
use crate::timers::StartTime;
use crate::timers::op_time_origin;
pub use crate::timers::StartTime;
pub use crate::timers::TimersPermission;
deno_core::extension!(deno_web,
@ -84,6 +85,7 @@ deno_core::extension!(deno_web,
compression::op_compression_write,
compression::op_compression_finish,
op_now<P>,
op_time_origin<P>,
op_defer,
stream_resource::op_readable_stream_resource_allocate,
stream_resource::op_readable_stream_resource_allocate_sized,
@ -123,7 +125,7 @@ deno_core::extension!(deno_web,
if let Some(location) = options.maybe_location {
state.put(Location(location));
}
state.put(StartTime::now());
state.put(StartTime::default());
}
);

View file

@ -4,7 +4,10 @@
use deno_core::op2;
use deno_core::OpState;
use std::time::Duration;
use std::time::Instant;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
pub trait TimersPermission {
fn allow_hrtime(&mut self) -> bool;
@ -17,21 +20,28 @@ impl TimersPermission for deno_permissions::PermissionsContainer {
}
}
pub type StartTime = Instant;
pub struct StartTime(Instant);
// Returns a milliseconds and nanoseconds subsec
// since the start time of the deno runtime.
// If the High precision flag is not set, the
// nanoseconds are rounded on 2ms.
#[op2(fast)]
pub fn op_now<TP>(state: &mut OpState, #[buffer] buf: &mut [u8])
impl Default for StartTime {
fn default() -> Self {
Self(Instant::now())
}
}
impl std::ops::Deref for StartTime {
type Target = Instant;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn expose_time<TP>(state: &mut OpState, duration: Duration, out: &mut [u8])
where
TP: TimersPermission + 'static,
{
let start_time = state.borrow::<StartTime>();
let elapsed = start_time.elapsed();
let seconds = elapsed.as_secs();
let mut subsec_nanos = elapsed.subsec_nanos();
let seconds = duration.as_secs() as u32;
let mut subsec_nanos = duration.subsec_nanos();
// If the permission is not enabled
// Round the nano result on 2 milliseconds
@ -40,14 +50,33 @@ where
let reduced_time_precision = 2_000_000; // 2ms in nanoseconds
subsec_nanos -= subsec_nanos % reduced_time_precision;
}
if buf.len() < 8 {
return;
if out.len() >= 8 {
out[0..4].copy_from_slice(&seconds.to_ne_bytes());
out[4..8].copy_from_slice(&subsec_nanos.to_ne_bytes());
}
let buf: &mut [u32] =
// SAFETY: buffer is at least 8 bytes long.
unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as _, 2) };
buf[0] = seconds as u32;
buf[1] = subsec_nanos;
}
#[op2(fast)]
pub fn op_now<TP>(state: &mut OpState, #[buffer] buf: &mut [u8])
where
TP: TimersPermission + 'static,
{
let start_time = state.borrow::<StartTime>();
let elapsed = start_time.elapsed();
expose_time::<TP>(state, elapsed, buf);
}
#[op2(fast)]
pub fn op_time_origin<TP>(state: &mut OpState, #[buffer] buf: &mut [u8])
where
TP: TimersPermission + 'static,
{
// https://w3c.github.io/hr-time/#dfn-estimated-monotonic-time-of-the-unix-epoch
let wall_time = SystemTime::now();
let monotonic_time = state.borrow::<StartTime>().elapsed();
let epoch = wall_time.duration_since(UNIX_EPOCH).unwrap() - monotonic_time;
expose_time::<TP>(state, epoch, buf);
}
#[allow(clippy::unused_async)]

View file

@ -2,7 +2,7 @@
[package]
name = "deno_webgpu"
version = "0.143.0"
version = "0.144.0"
authors = ["the Deno authors"]
edition.workspace = true
license = "MIT"

View file

@ -2,7 +2,7 @@
[package]
name = "deno_webidl"
version = "0.176.0"
version = "0.177.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -28,6 +28,7 @@ const {
ArrayPrototypePush,
ArrayPrototypeShift,
ArrayPrototypeSome,
Error,
ErrorPrototypeToString,
ObjectDefineProperties,
ObjectPrototypeIsPrototypeOf,
@ -488,8 +489,11 @@ class WebSocket extends EventTarget {
/* error */
this[_readyState] = CLOSED;
const message = op_ws_get_error(rid);
const error = new Error(message);
const errorEv = new ErrorEvent("error", {
message: op_ws_get_error(rid),
error,
message,
});
this.dispatchEvent(errorEv);

View file

@ -2,7 +2,7 @@
[package]
name = "deno_websocket"
version = "0.181.0"
version = "0.182.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_webstorage"
version = "0.171.0"
version = "0.172.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_resolver"
version = "0.8.0"
version = "0.9.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "node_resolver"
version = "0.15.0"
version = "0.16.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_runtime"
version = "0.185.0"
version = "0.186.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -623,7 +623,7 @@ fn get_ffi_call_error_class(e: &CallError) -> &'static str {
fn get_webstorage_class_name(e: &WebStorageError) -> &'static str {
match e {
WebStorageError::ContextNotSupported => "DOMExceptionNotSupportedError",
WebStorageError::Sqlite(_) => todo!(),
WebStorageError::Sqlite(_) => "Error",
WebStorageError::Io(e) => get_io_error_class(e),
WebStorageError::StorageExceeded => "DOMExceptionQuotaExceededError",
}

View file

@ -27,7 +27,6 @@ const {
ArrayPrototypeForEach,
ArrayPrototypeIncludes,
ArrayPrototypeMap,
DateNow,
Error,
ErrorPrototype,
FunctionPrototypeBind,
@ -642,7 +641,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) {
removeImportedOps();
performance.setTimeOrigin(DateNow());
performance.setTimeOrigin();
globalThis_ = globalThis;
// Remove bootstrapping data from the global scope
@ -858,7 +857,7 @@ function bootstrapWorkerRuntime(
7: nodeDebug,
} = runtimeOptions;
performance.setTimeOrigin(DateNow());
performance.setTimeOrigin();
globalThis_ = globalThis;
// Remove bootstrapping data from the global scope

View file

@ -2,7 +2,7 @@
[package]
name = "deno_permissions"
version = "0.36.0"
version = "0.37.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -40,8 +40,8 @@ pub use prompter::PromptResponse;
#[derive(Debug, thiserror::Error)]
#[error("Requires {access}, {}", format_permission_error(.name))]
pub struct PermissionDeniedError {
access: String,
name: &'static str,
pub access: String,
pub name: &'static str,
}
fn format_permission_error(name: &'static str) -> String {
@ -1461,7 +1461,7 @@ pub struct SysDescriptor(String);
impl SysDescriptor {
pub fn parse(kind: String) -> Result<Self, SysDescriptorParseError> {
match kind.as_str() {
"hostname" | "osRelease" | "osUptime" | "loadavg"
"hostname" | "inspector" | "osRelease" | "osUptime" | "loadavg"
| "networkInterfaces" | "systemMemoryInfo" | "uid" | "gid" | "cpus"
| "homedir" | "getegid" | "statfs" | "getPriority" | "setPriority"
| "userInfo" => Ok(Self(kind)),

View file

@ -77,6 +77,7 @@
"test-fs-rmdir-recursive.js",
"test-fs-write-file.js",
"test-http-url.parse-https.request.js",
"test-net-autoselectfamily.js",
"test-net-better-error-messages-path.js",
"test-net-connect-buffer.js",
"test-net-connect-buffer2.js",
@ -404,6 +405,7 @@
"test-http-url.parse-only-support-http-https-protocol.js",
"test-icu-transcode.js",
"test-net-access-byteswritten.js",
"test-net-autoselectfamily.js",
"test-net-better-error-messages-listen-path.js",
"test-net-better-error-messages-path.js",
"test-net-better-error-messages-port-hostname.js",

View file

@ -1767,7 +1767,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
- [parallel/test-net-autoselectfamily-commandline-option.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-autoselectfamily-commandline-option.js)
- [parallel/test-net-autoselectfamily-default.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-autoselectfamily-default.js)
- [parallel/test-net-autoselectfamily-ipv4first.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-autoselectfamily-ipv4first.js)
- [parallel/test-net-autoselectfamily.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-autoselectfamily.js)
- [parallel/test-net-better-error-messages-listen.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-better-error-messages-listen.js)
- [parallel/test-net-binary.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-binary.js)
- [parallel/test-net-bind-twice.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-net-bind-twice.js)

View file

@ -19,6 +19,7 @@ import { magenta } from "@std/fmt/colors";
import { pooledMap } from "@std/async/pool";
import { dirname, fromFileUrl, join } from "@std/path";
import { assertEquals, fail } from "@std/assert";
import { distinct } from "@std/collections";
import {
config,
getPathsFromTestSuites,
@ -36,6 +37,9 @@ const testPaths = partitionParallelTestPaths(
getPathsFromTestSuites(config.ignore),
),
);
testPaths.sequential = distinct(testPaths.sequential);
testPaths.parallel = distinct(testPaths.parallel);
const cwd = new URL(".", import.meta.url);
const windowsIgnorePaths = new Set(
getPathsFromTestSuites(config.windowsIgnore),

View file

@ -473,6 +473,7 @@ const pwdCommand = isWindows ?
module.exports = {
allowGlobals,
defaultAutoSelectFamilyAttemptTimeout: 2500,
expectsError,
expectWarning,
getArrayBufferViews,

View file

@ -0,0 +1,312 @@
// deno-fmt-ignore-file
// deno-lint-ignore-file
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node 18.12.1
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
'use strict';
const common = require('../common');
const { parseDNSPacket, writeDNSPacket } = require('../common/dns');
const assert = require('assert');
const dgram = require('dgram');
const { Resolver } = require('dns');
const { createConnection, createServer } = require('net');
// Test that happy eyeballs algorithm is properly implemented.
// Purposely not using setDefaultAutoSelectFamilyAttemptTimeout here to test the
// parameter is correctly used in options.
//
// Some of the machines in the CI need more time to establish connection
const autoSelectFamilyAttemptTimeout = common.defaultAutoSelectFamilyAttemptTimeout;
function _lookup(resolver, hostname, options, cb) {
resolver.resolve(hostname, 'ANY', (err, replies) => {
assert.notStrictEqual(options.family, 4);
if (err) {
return cb(err);
}
const hosts = replies
.map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 }))
.sort((a, b) => b.family - a.family);
if (options.all === true) {
return cb(null, hosts);
}
return cb(null, hosts[0].address, hosts[0].family);
});
}
function createDnsServer(ipv6Addrs, ipv4Addrs, cb) {
if (!Array.isArray(ipv6Addrs)) {
ipv6Addrs = [ipv6Addrs];
}
if (!Array.isArray(ipv4Addrs)) {
ipv4Addrs = [ipv4Addrs];
}
// Create a DNS server which replies with a AAAA and a A record for the same host
const socket = dgram.createSocket('udp4');
// TODO(kt3k): We use common.mustCallAtLeast instead of common.mustCall
// because Deno sends multiple requests to the DNS server.
// This can be addressed if Deno.resolveDns supports ANY record type.
// See https://github.com/denoland/deno/issues/14492
socket.on('message', common.mustCallAtLeast((msg, { address, port }) => {
const parsed = parseDNSPacket(msg);
const domain = parsed.questions[0].domain;
assert.strictEqual(domain, 'example.org');
socket.send(writeDNSPacket({
id: parsed.id,
questions: parsed.questions,
answers: [
...ipv6Addrs.map((address) => ({ type: 'AAAA', address, ttl: 123, domain: 'example.org' })),
...ipv4Addrs.map((address) => ({ type: 'A', address, ttl: 123, domain: 'example.org' })),
]
}), port, address);
}));
socket.bind(0, () => {
const resolver = new Resolver();
resolver.setServers([`127.0.0.1:${socket.address().port}`]);
cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) });
});
}
// Test that IPV4 is reached if IPV6 is not reachable
{
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
const ipv4Server = createServer((socket) => {
socket.on('data', common.mustCall(() => {
socket.write('response-ipv4');
socket.end();
}));
});
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
const port = ipv4Server.address().port;
const connection = createConnection({
host: 'example.org',
port: port,
lookup,
autoSelectFamily: true,
autoSelectFamilyAttemptTimeout,
});
let response = '';
connection.setEncoding('utf-8');
connection.on('ready', common.mustCall(() => {
assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`, `127.0.0.1:${port}`]);
}));
connection.on('data', (chunk) => {
response += chunk;
});
connection.on('end', common.mustCall(() => {
assert.strictEqual(response, 'response-ipv4');
ipv4Server.close();
dnsServer.close();
}));
connection.write('request');
}));
}));
}
// Test that only the last successful connection is established.
{
createDnsServer(
['2606:4700::6810:85e5', '2606:4700::6810:84e5', "::1"],
// TODO(kt3k): Comment out ipv4 addresses to make the test pass faster.
// Enable this when Deno.connect() call becomes cancellable.
// See https://github.com/denoland/deno/issues/26819
// ['104.20.22.46', '104.20.23.46', '127.0.0.1'],
['127.0.0.1'],
common.mustCall(function({ dnsServer, lookup }) {
const ipv4Server = createServer((socket) => {
socket.on('data', common.mustCall(() => {
socket.write('response-ipv4');
socket.end();
}));
});
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
const port = ipv4Server.address().port;
const connection = createConnection({
host: 'example.org',
port: port,
lookup,
autoSelectFamily: true,
autoSelectFamilyAttemptTimeout,
});
let response = '';
connection.setEncoding('utf-8');
connection.on('ready', common.mustCall(() => {
assert.deepStrictEqual(
connection.autoSelectFamilyAttemptedAddresses,
[
`2606:4700::6810:85e5:${port}`,
`104.20.22.46:${port}`,
`2606:4700::6810:84e5:${port}`,
`104.20.23.46:${port}`,
`::1:${port}`,
`127.0.0.1:${port}`,
]
);
}));
connection.on('data', (chunk) => {
response += chunk;
});
connection.on('end', common.mustCall(() => {
assert.strictEqual(response, 'response-ipv4');
ipv4Server.close();
dnsServer.close();
}));
connection.write('request');
}));
})
);
}
// Test that IPV4 is NOT reached if IPV6 is reachable
if (common.hasIPv6) {
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
const ipv4Server = createServer((socket) => {
socket.on('data', common.mustNotCall(() => {
socket.write('response-ipv4');
socket.end();
}));
});
const ipv6Server = createServer((socket) => {
socket.on('data', common.mustCall(() => {
socket.write('response-ipv6');
socket.end();
}));
});
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
const port = ipv4Server.address().port;
ipv6Server.listen(port, '::1', common.mustCall(() => {
const connection = createConnection({
host: 'example.org',
port,
lookup,
autoSelectFamily: true,
autoSelectFamilyAttemptTimeout,
});
let response = '';
connection.setEncoding('utf-8');
connection.on('ready', common.mustCall(() => {
assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`]);
}));
connection.on('data', (chunk) => {
response += chunk;
});
connection.on('end', common.mustCall(() => {
assert.strictEqual(response, 'response-ipv6');
ipv4Server.close();
ipv6Server.close();
dnsServer.close();
}));
connection.write('request');
}));
}));
}));
}
// Test that when all errors are returned when no connections succeeded
{
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
const connection = createConnection({
host: 'example.org',
port: 10,
lookup,
autoSelectFamily: true,
autoSelectFamilyAttemptTimeout,
});
connection.on('ready', common.mustNotCall());
connection.on('error', common.mustCall((error) => {
assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, ['::1:10', '127.0.0.1:10']);
assert.strictEqual(error.constructor.name, 'AggregateError');
assert.strictEqual(error.errors.length, 2);
const errors = error.errors.map((e) => e.message);
assert.ok(errors.includes('connect ECONNREFUSED 127.0.0.1:10'));
if (common.hasIPv6) {
assert.ok(errors.includes('connect ECONNREFUSED ::1:10'));
}
dnsServer.close();
}));
}));
}
// Test that the option can be disabled
{
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
const ipv4Server = createServer((socket) => {
socket.on('data', common.mustCall(() => {
socket.write('response-ipv4');
socket.end();
}));
});
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
const port = ipv4Server.address().port;
const connection = createConnection({
host: 'example.org',
port,
lookup,
autoSelectFamily: false,
});
connection.on('ready', common.mustNotCall());
connection.on('error', common.mustCall((error) => {
assert.strictEqual(connection.autoSelectFamilyAttemptedAddresses, undefined);
if (common.hasIPv6) {
assert.strictEqual(error.code, 'ECONNREFUSED');
assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
} else if (error.code === 'EAFNOSUPPORT') {
assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
} else if (error.code === 'EUNATCH') {
assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
} else {
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
}
ipv4Server.close();
dnsServer.close();
}));
}));
}));
}

View file

@ -0,0 +1,3 @@
import * as fs from "node:fs";
fs.writeFileSync("./testbin.js", "#!/usr/bin/env node\nconsole.log('run testbin');");

View file

@ -0,0 +1,10 @@
{
"name": "@denotest/bin-created-by-lifecycle",
"version": "1.0.0",
"scripts": {
"install": "node install.mjs"
},
"bin": {
"testbin": "testbin.js"
}
}

View file

@ -0,0 +1,9 @@
{
"tempDir": true,
"steps": [
{
"args": "install",
"output": "install.out"
}
]
}

View file

@ -0,0 +1,6 @@
{
"workspace": ["packages/foo", "packages/bar"],
"imports": {
"@denotest/subtract": "jsr:@denotest/subtract@^1.0.0"
}
}

View file

@ -0,0 +1,10 @@
[UNORDERED_START]
Download http://localhost:4260/@denotest%2fesm-basic
Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
Download http://127.0.0.1:4250/@denotest/subtract/meta.json
Download http://127.0.0.1:4250/@denotest/add/meta.json
Download http://127.0.0.1:4250/@denotest/add/1.0.0_meta.json
Download http://127.0.0.1:4250/@denotest/subtract/1.0.0_meta.json
Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts
Download http://127.0.0.1:4250/@denotest/subtract/1.0.0/mod.ts
[UNORDERED_END]

View file

@ -0,0 +1,5 @@
{
"imports": {
"@denotest/esm-basic": "npm:@denotest/esm-basic@^1.0.0"
}
}

View file

@ -0,0 +1,5 @@
{
"imports": {
"@denotest/add": "jsr:@denotest/add@^1.0.0"
}
}

View file

@ -0,0 +1,29 @@
{
"tempDir": true,
"tests": {
"all_at_once": {
"steps": [
{
"args": "install --allow-scripts",
"output": "all_at_once_install.out"
},
{ "args": "task run-testbin", "output": "run_testbin.out" }
]
},
"separate_steps": {
"steps": [
{ "if": "unix", "args": "install", "output": "install_warn.out" },
{
"if": "windows",
"args": "install",
"output": "install_warn_windows.out"
},
{
"args": "install --allow-scripts",
"output": "Initialize @denotest/bin-created-by-lifecycle@1.0.0: running 'install' script\n"
},
{ "args": "task run-testbin", "output": "run_testbin.out" }
]
}
}
}

View file

@ -0,0 +1,4 @@
Download http://localhost:4260/@denotest%2fbin-created-by-lifecycle
Download http://localhost:4260/@denotest/bin-created-by-lifecycle/1.0.0.tgz
Initialize @denotest/bin-created-by-lifecycle@1.0.0
Initialize @denotest/bin-created-by-lifecycle@1.0.0: running 'install' script

View file

@ -0,0 +1,10 @@
Download http://localhost:4260/@denotest%2fbin-created-by-lifecycle
Download http://localhost:4260/@denotest/bin-created-by-lifecycle/1.0.0.tgz
Initialize @denotest/bin-created-by-lifecycle@1.0.0
Warning Trying to set up 'testbin' bin for "[WILDCARD]bin-created-by-lifecycle", but the entry point "[WILDCARD]testbin.js" doesn't exist.
Warning The following packages contained npm lifecycle scripts (preinstall/install/postinstall) that were not executed:
┠─ npm:@denotest/bin-created-by-lifecycle@1.0.0
┠─ This may cause the packages to not work correctly.
┖─ To run lifecycle scripts, use the `--allow-scripts` flag with `deno install`:
deno install --allow-scripts=npm:@denotest/bin-created-by-lifecycle@1.0.0

View file

@ -0,0 +1,9 @@
Download http://localhost:4260/@denotest%2fbin-created-by-lifecycle
Download http://localhost:4260/@denotest/bin-created-by-lifecycle/1.0.0.tgz
Initialize @denotest/bin-created-by-lifecycle@1.0.0
Warning The following packages contained npm lifecycle scripts (preinstall/install/postinstall) that were not executed:
┠─ npm:@denotest/bin-created-by-lifecycle@1.0.0
┠─ This may cause the packages to not work correctly.
┖─ To run lifecycle scripts, use the `--allow-scripts` flag with `deno install`:
deno install --allow-scripts=npm:@denotest/bin-created-by-lifecycle@1.0.0

View file

@ -0,0 +1,8 @@
{
"dependencies": {
"@denotest/bin-created-by-lifecycle": "1.0.0"
},
"scripts": {
"run-testbin": "testbin"
}
}

View file

@ -0,0 +1,2 @@
Task run-testbin testbin
run testbin

View file

@ -4299,3 +4299,31 @@ Deno.test({
assert(cancelled);
});
Deno.test({
name: "AbortSignal event aborted when request is cancelled",
}, async () => {
const { promise, resolve } = Promise.withResolvers<void>();
const server = Deno.serve({
hostname: "0.0.0.0",
port: servePort,
onListen: () => resolve(),
}, async (request) => {
const { promise: promiseAbort, resolve: resolveAbort } = Promise
.withResolvers<void>();
request.signal.addEventListener("abort", () => resolveAbort());
assert(!request.signal.aborted);
await promiseAbort;
return new Response("Ok");
});
await promise;
await fetch(`http://localhost:${servePort}/`, {
signal: AbortSignal.timeout(100),
}).catch(() => {});
await server.shutdown();
});

View file

@ -453,7 +453,8 @@ Deno.test("invalid server", async () => {
const { promise, resolve } = Promise.withResolvers<void>();
const ws = new WebSocket("ws://localhost:2121");
let err = false;
ws.onerror = () => {
ws.onerror = (e) => {
assert("error" in e);
err = true;
};
ws.onclose = () => {

View file

@ -10,6 +10,11 @@ import * as net from "node:net";
import { assert, assertEquals } from "@std/assert";
import { curlRequest } from "../unit/test_util.ts";
// Increase the timeout for the auto select family to avoid flakiness
net.setDefaultAutoSelectFamilyAttemptTimeout(
net.getDefaultAutoSelectFamilyAttemptTimeout() * 30,
);
for (const url of ["http://localhost:4246", "https://localhost:4247"]) {
Deno.test(`[node/http2 client] ${url}`, {
ignore: Deno.build.os === "windows",