From 39f7845d4e25e22d3bffdc2fb11f7583fd04bbc9 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Sat, 14 Dec 2024 11:38:19 -0500 Subject: [PATCH 001/107] ci: restore cargo home cache before rust install (#27356) I think this makes more sense. We'll see if it makes it faster. It was taking 1m 22s to install rust. --- .github/workflows/ci.generate.ts | 38 ++++++++++++++++++-------------- .github/workflows/ci.yml | 22 ++++++++++-------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index 5c2de96006..c22f87a861 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -484,6 +484,27 @@ const ci = { " -czvf target/release/deno_src.tar.gz -C .. deno", ].join("\n"), }, + { + name: "Cache Cargo home", + uses: "actions/cache@v4", + with: { + // See https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci + // Note that with the new sparse registry format, we no longer have to cache a `.git` dir + path: [ + "~/.cargo/.crates.toml", + "~/.cargo/.crates2.json", + "~/.cargo/bin", + "~/.cargo/registry/index", + "~/.cargo/registry/cache", + "~/.cargo/git/db", + ].join("\n"), + key: + `${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ hashFiles('Cargo.lock') }}`, + // We will try to restore from the closest cargo-home we can find + "restore-keys": + `${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-`, + }, + }, installRustStep, { if: @@ -607,23 +628,6 @@ const ci = { installBenchTools, ].join("\n"), }, - { - name: "Cache Cargo home", - uses: "actions/cache@v4", - with: { - // See https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci - // Note that with the new sparse registry format, we no longer have to cache a `.git` dir - path: [ - "~/.cargo/registry/index", - "~/.cargo/registry/cache", - ].join("\n"), - key: - `${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ hashFiles('Cargo.lock') }}`, - // We will try to restore from the closest cargo-home we can find - "restore-keys": - `${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-`, - }, - }, { // Restore cache from the latest 'main' branch build. name: "Restore cache build output (PR)", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44eb15cb95..aa7500f7c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -174,6 +174,19 @@ jobs: mkdir -p target/release tar --exclude=".git*" --exclude=target --exclude=third_party/prebuilt \ -czvf target/release/deno_src.tar.gz -C .. deno + - name: Cache Cargo home + uses: actions/cache@v4 + with: + path: |- + ~/.cargo/.crates.toml + ~/.cargo/.crates2.json + ~/.cargo/bin + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + key: '30-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '30-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' + if: '!(matrix.skip)' - uses: dsherret/rust-toolchain-file@v1 if: '!(matrix.skip)' - if: '!(matrix.skip) && (matrix.job == ''lint'' || matrix.job == ''test'' || matrix.job == ''bench'')' @@ -355,15 +368,6 @@ jobs: - name: Install benchmark tools if: '!(matrix.skip) && (matrix.job == ''bench'')' run: ./tools/install_prebuilt.js wrk hyperfine - - name: Cache Cargo home - uses: actions/cache@v4 - with: - path: |- - ~/.cargo/registry/index - ~/.cargo/registry/cache - key: '30-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '30-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' - if: '!(matrix.skip)' - name: Restore cache build output (PR) uses: actions/cache/restore@v4 if: '!(matrix.skip) && (github.ref != ''refs/heads/main'' && !startsWith(github.ref, ''refs/tags/''))' From 7949f53cabb912e24b27596b9e0840e20232be8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sun, 15 Dec 2024 08:18:04 +0000 Subject: [PATCH 002/107] refactor: add 'sync' feature to deno_resolver crate (#27357) --- cli/Cargo.toml | 2 +- resolvers/deno/Cargo.toml | 5 ++++- resolvers/deno/cjs.rs | 24 +++++++++++----------- resolvers/deno/clippy.toml | 3 +-- resolvers/deno/lib.rs | 29 +++++++++++++++------------ resolvers/deno/npm/byonm.rs | 12 +++++++---- resolvers/deno/npm/mod.rs | 29 ++++++++++++++++----------- resolvers/deno/sloppy_imports.rs | 4 ++++ resolvers/deno/sync.rs | 34 ++++++++++++++++++++++++++++++++ resolvers/node/lib.rs | 1 + 10 files changed, 98 insertions(+), 45 deletions(-) create mode 100644 resolvers/deno/sync.rs diff --git a/cli/Cargo.toml b/cli/Cargo.toml index cf76dfe69d..6395910ccd 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -80,7 +80,7 @@ deno_npm.workspace = true deno_npm_cache.workspace = true deno_package_json.workspace = true deno_path_util.workspace = true -deno_resolver.workspace = true +deno_resolver = { workspace = true, features = ["sync"] } deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_semver.workspace = true deno_task_shell = "=0.20.2" diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 2966b5fef6..4dca044377 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -13,11 +13,14 @@ description = "Deno resolution algorithm" [lib] path = "lib.rs" +[features] +sync = ["dashmap"] + [dependencies] anyhow.workspace = true base32.workspace = true boxed_error.workspace = true -dashmap.workspace = true +dashmap = { workspace = true, optional = true } deno_config.workspace = true deno_media_type.workspace = true deno_package_json.workspace = true diff --git a/resolvers/deno/cjs.rs b/resolvers/deno/cjs.rs index 9ae60b6a15..6ae648deab 100644 --- a/resolvers/deno/cjs.rs +++ b/resolvers/deno/cjs.rs @@ -1,13 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use std::sync::Arc; - -use dashmap::DashMap; +use crate::sync::MaybeDashMap; use deno_media_type::MediaType; use node_resolver::env::NodeResolverEnv; use node_resolver::errors::ClosestPkgJsonError; -use node_resolver::InNpmPackageChecker; -use node_resolver::PackageJsonResolver; +use node_resolver::InNpmPackageCheckerRc; +use node_resolver::PackageJsonResolverRc; use node_resolver::ResolutionMode; use url::Url; @@ -19,13 +17,13 @@ use url::Url; #[derive(Debug)] pub struct CjsTracker { is_cjs_resolver: IsCjsResolver, - known: DashMap, + known: MaybeDashMap, } impl CjsTracker { pub fn new( - in_npm_pkg_checker: Arc, - pkg_json_resolver: Arc>, + in_npm_pkg_checker: InNpmPackageCheckerRc, + pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, ) -> Self { Self { @@ -127,15 +125,15 @@ pub enum IsCjsResolutionMode { /// Resolves whether a module is CJS or ESM. #[derive(Debug)] pub struct IsCjsResolver { - in_npm_pkg_checker: Arc, - pkg_json_resolver: Arc>, + in_npm_pkg_checker: InNpmPackageCheckerRc, + pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, } impl IsCjsResolver { pub fn new( - in_npm_pkg_checker: Arc, - pkg_json_resolver: Arc>, + in_npm_pkg_checker: InNpmPackageCheckerRc, + pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, ) -> Self { Self { @@ -185,7 +183,7 @@ impl IsCjsResolver { specifier: &Url, media_type: MediaType, is_script: Option, - known_cache: &DashMap, + known_cache: &MaybeDashMap, ) -> Option { if specifier.scheme() != "file" { return Some(ResolutionMode::Import); diff --git a/resolvers/deno/clippy.toml b/resolvers/deno/clippy.toml index 733ac83da1..886ba3fd1a 100644 --- a/resolvers/deno/clippy.toml +++ b/resolvers/deno/clippy.toml @@ -47,6 +47,5 @@ disallowed-methods = [ { path = "url::Url::from_directory_path", reason = "Use deno_path_util instead so it works in Wasm" }, ] disallowed-types = [ - # todo(dsherret): consider for the future - # { path = "std::sync::Arc", reason = "use crate::sync::MaybeArc instead" }, + { path = "std::sync::Arc", reason = "use crate::sync::MaybeArc instead" }, ] diff --git a/resolvers/deno/lib.rs b/resolvers/deno/lib.rs index a74ca614a3..05fa416da1 100644 --- a/resolvers/deno/lib.rs +++ b/resolvers/deno/lib.rs @@ -4,7 +4,6 @@ #![deny(clippy::print_stdout)] use std::path::PathBuf; -use std::sync::Arc; use boxed_error::Boxed; use deno_config::workspace::MappedResolution; @@ -19,20 +18,20 @@ use fs::DenoResolverFs; use node_resolver::env::NodeResolverEnv; use node_resolver::errors::NodeResolveError; use node_resolver::errors::PackageSubpathResolveError; -use node_resolver::InNpmPackageChecker; +use node_resolver::InNpmPackageCheckerRc; use node_resolver::NodeResolution; use node_resolver::NodeResolutionKind; -use node_resolver::NodeResolver; +use node_resolver::NodeResolverRc; use node_resolver::ResolutionMode; use npm::MissingPackageNodeModulesFolderError; use npm::NodeModulesOutOfDateError; -use npm::NpmReqResolver; +use npm::NpmReqResolverRc; use npm::ResolveIfForNpmPackageErrorKind; use npm::ResolvePkgFolderFromDenoReqError; use npm::ResolveReqWithSubPathErrorKind; use sloppy_imports::SloppyImportResolverFs; use sloppy_imports::SloppyImportsResolutionKind; -use sloppy_imports::SloppyImportsResolver; +use sloppy_imports::SloppyImportsResolverRc; use thiserror::Error; use url::Url; @@ -40,6 +39,10 @@ pub mod cjs; pub mod fs; pub mod npm; pub mod sloppy_imports; +mod sync; + +#[allow(clippy::disallowed_types)] +pub type WorkspaceResolverRc = crate::sync::MaybeArc; #[derive(Debug, Clone)] pub struct DenoResolution { @@ -80,8 +83,8 @@ pub struct NodeAndNpmReqResolver< Fs: DenoResolverFs, TNodeResolverEnv: NodeResolverEnv, > { - pub node_resolver: Arc>, - pub npm_req_resolver: Arc>, + pub node_resolver: NodeResolverRc, + pub npm_req_resolver: NpmReqResolverRc, } pub struct DenoResolverOptions< @@ -90,12 +93,12 @@ pub struct DenoResolverOptions< TNodeResolverEnv: NodeResolverEnv, TSloppyImportResolverFs: SloppyImportResolverFs, > { - pub in_npm_pkg_checker: Arc, + pub in_npm_pkg_checker: InNpmPackageCheckerRc, pub node_and_req_resolver: Option>, pub sloppy_imports_resolver: - Option>>, - pub workspace_resolver: Arc, + Option>, + pub workspace_resolver: WorkspaceResolverRc, /// Whether "bring your own node_modules" is enabled where Deno does not /// setup the node_modules directories automatically, but instead uses /// what already exists on the file system. @@ -111,11 +114,11 @@ pub struct DenoResolver< TNodeResolverEnv: NodeResolverEnv, TSloppyImportResolverFs: SloppyImportResolverFs, > { - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: InNpmPackageCheckerRc, node_and_npm_resolver: Option>, sloppy_imports_resolver: - Option>>, - workspace_resolver: Arc, + Option>, + workspace_resolver: WorkspaceResolverRc, is_byonm: bool, maybe_vendor_specifier: Option, } diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index 6e1be35ca0..08d06f9cac 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -3,10 +3,10 @@ use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; -use std::sync::Arc; use deno_package_json::PackageJson; use deno_package_json::PackageJsonDepValue; +use deno_package_json::PackageJsonRc; use deno_path_util::url_to_file_path; use deno_semver::package::PackageReq; use deno_semver::Version; @@ -49,6 +49,10 @@ pub struct ByonmNpmResolverCreateOptions< pub pkg_json_resolver: PackageJsonResolverRc, } +#[allow(clippy::disallowed_types)] +pub type ByonmNpmResolverRc = + crate::sync::MaybeArc>; + #[derive(Debug)] pub struct ByonmNpmResolver { fs: Fs, @@ -84,7 +88,7 @@ impl ByonmNpmResolver { fn load_pkg_json( &self, path: &Path, - ) -> Result>, PackageJsonLoadError> { + ) -> Result, PackageJsonLoadError> { self.pkg_json_resolver.load_package_json(path) } @@ -93,7 +97,7 @@ impl ByonmNpmResolver { &self, dep_name: &str, referrer: &Url, - ) -> Option> { + ) -> Option { let referrer_path = url_to_file_path(referrer).ok()?; let mut current_folder = referrer_path.parent()?; loop { @@ -173,7 +177,7 @@ impl ByonmNpmResolver { &self, req: &PackageReq, referrer: &Url, - ) -> Result, String)>, PackageJsonLoadError> { + ) -> Result, PackageJsonLoadError> { fn resolve_alias_from_pkg_json( req: &PackageReq, pkg_json: &PackageJson, diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 83db04480a..64ec86fe3f 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -2,7 +2,6 @@ use std::fmt::Debug; use std::path::PathBuf; -use std::sync::Arc; use boxed_error::Boxed; use deno_semver::npm::NpmPackageReqReference; @@ -15,10 +14,10 @@ use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::PackageResolveErrorKind; use node_resolver::errors::PackageSubpathResolveError; -use node_resolver::InNpmPackageChecker; +use node_resolver::InNpmPackageCheckerRc; use node_resolver::NodeResolution; use node_resolver::NodeResolutionKind; -use node_resolver::NodeResolver; +use node_resolver::NodeResolverRc; use node_resolver::ResolutionMode; use thiserror::Error; use url::Url; @@ -28,6 +27,7 @@ use crate::fs::DenoResolverFs; pub use byonm::ByonmInNpmPackageChecker; pub use byonm::ByonmNpmResolver; pub use byonm::ByonmNpmResolverCreateOptions; +pub use byonm::ByonmNpmResolverRc; pub use byonm::ByonmResolvePkgFolderFromDenoReqError; pub use local::normalize_pkg_name_for_node_modules_deno_folder; @@ -81,6 +81,9 @@ pub enum ResolvePkgFolderFromDenoReqError { Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError), } +#[allow(clippy::disallowed_types)] +pub type CliNpmReqResolverRc = crate::sync::MaybeArc; + // todo(dsherret): a temporary trait until we extract // out the CLI npm resolver into here pub trait CliNpmReqResolver: Debug + Send + Sync { @@ -98,21 +101,25 @@ pub struct NpmReqResolverOptions< /// The resolver when "bring your own node_modules" is enabled where Deno /// does not setup the node_modules directories automatically, but instead /// uses what already exists on the file system. - pub byonm_resolver: Option>>, + pub byonm_resolver: Option>, pub fs: Fs, - pub in_npm_pkg_checker: Arc, - pub node_resolver: Arc>, - pub npm_req_resolver: Arc, + pub in_npm_pkg_checker: InNpmPackageCheckerRc, + pub node_resolver: NodeResolverRc, + pub npm_req_resolver: CliNpmReqResolverRc, } +#[allow(clippy::disallowed_types)] +pub type NpmReqResolverRc = + crate::sync::MaybeArc>; + #[derive(Debug)] pub struct NpmReqResolver { - byonm_resolver: Option>>, + byonm_resolver: Option>, fs: Fs, - in_npm_pkg_checker: Arc, - node_resolver: Arc>, - npm_resolver: Arc, + in_npm_pkg_checker: InNpmPackageCheckerRc, + node_resolver: NodeResolverRc, + npm_resolver: CliNpmReqResolverRc, } impl diff --git a/resolvers/deno/sloppy_imports.rs b/resolvers/deno/sloppy_imports.rs index ccaa547435..6644222a8b 100644 --- a/resolvers/deno/sloppy_imports.rs +++ b/resolvers/deno/sloppy_imports.rs @@ -101,6 +101,10 @@ pub trait SloppyImportResolverFs { } } +#[allow(clippy::disallowed_types)] +pub type SloppyImportsResolverRc = + crate::sync::MaybeArc>; + #[derive(Debug)] pub struct SloppyImportsResolver { fs: Fs, diff --git a/resolvers/deno/sync.rs b/resolvers/deno/sync.rs new file mode 100644 index 0000000000..6e62336901 --- /dev/null +++ b/resolvers/deno/sync.rs @@ -0,0 +1,34 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +pub use inner::*; + +#[cfg(feature = "sync")] +mod inner { + #![allow(clippy::disallowed_types)] + + pub use std::sync::Arc as MaybeArc; + + pub use dashmap::DashMap as MaybeDashMap; +} + +#[cfg(not(feature = "sync"))] +mod inner { + use std::hash::RandomState; + pub use std::rc::Rc as MaybeArc; + + // Wrapper struct that exposes a subset of `DashMap` API. + #[derive(Default)] + struct MaybeDashMap(RefCell>); + + impl MaybeDashMap { + pub fn get(&'a self, key: &K) -> Option<&'a V> { + let inner = self.0.borrow(); + inner.get(key) + } + + pub fn insert(&self, key: K, value: V) -> Option { + let inner = self.0.borrow_mut(); + inner.insert(key, value) + } + } +} diff --git a/resolvers/node/lib.rs b/resolvers/node/lib.rs index 8da20c421e..c73c395dfc 100644 --- a/resolvers/node/lib.rs +++ b/resolvers/node/lib.rs @@ -26,6 +26,7 @@ pub use resolution::resolve_specifier_into_node_modules; pub use resolution::NodeResolution; pub use resolution::NodeResolutionKind; pub use resolution::NodeResolver; +pub use resolution::NodeResolverRc; pub use resolution::ResolutionMode; pub use resolution::DEFAULT_CONDITIONS; pub use resolution::REQUIRE_CONDITIONS; From 50871b2aa3728c34d5ce45bb8052c7bbdf1d0255 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 16 Dec 2024 05:51:49 -0800 Subject: [PATCH 003/107] fix: FastString v8_string() should error when cannot allocated (#27375) Upgrades deno_core to 0.326.0 --- Cargo.lock | 17 +++++++++-------- Cargo.toml | 2 +- cli/lsp/tsc.rs | 1 + ext/fs/ops.rs | 8 +------- ext/node/ops/v8.rs | 7 ++++++- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 077f1e1e18..b0ea574b2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1483,9 +1483,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.324.0" +version = "0.326.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24503eda646f246aa6eb0f794909f9a857c8f05095fed66f36e0eaef92edce23" +checksum = "ed157162dc5320a2b46ffeeaec24788339df0f2437cfaea78a8d82696715ad7f" dependencies = [ "anyhow", "az", @@ -1493,6 +1493,7 @@ dependencies = [ "bit-set", "bit-vec", "bytes", + "capacity_builder", "cooked-waker", "deno_core_icudata", "deno_ops", @@ -2052,9 +2053,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.200.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a529a2c488cd3042f12f35666569ebe5b3cf89d2b7d1cafc1a652f6d7bcc8f" +checksum = "4dd8ac1af251e292388e516dd339b9a3b982a6d1e7f8644c08e34671ca39003c" dependencies = [ "proc-macro-rules", "proc-macro2", @@ -6694,9 +6695,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.233.0" +version = "0.235.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307f176b7475480cee690c34c7118f96fe564d1f2a974bf990294b8310ae4983" +checksum = "d07afd8b67b4a442ecc2823038473ac0e9e5682de93c213323b60661afdd7eb4" dependencies = [ "num-bigint", "serde", @@ -8281,9 +8282,9 @@ dependencies = [ [[package]] name = "v8" -version = "130.0.1" +version = "130.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c23b5c2caff00209b03a716609b275acae94b02dd3b63c4648e7232a84a8402f" +checksum = "2ee0be58935708fa4d7efb970c6cf9f2d9511d24ee24246481a65b6ee167348d" dependencies = [ "bindgen", "bitflags 2.6.0", diff --git a/Cargo.toml b/Cargo.toml index 27038110d3..984cb187ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ repository = "https://github.com/denoland/deno" [workspace.dependencies] deno_ast = { version = "=0.44.0", features = ["transpiling"] } -deno_core = { version = "0.324.0" } +deno_core = { version = "0.326.0" } deno_bench_util = { version = "0.176.0", path = "./bench_util" } deno_config = { version = "=0.39.3", features = ["workspace", "sync"] } diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 931e008a72..957c3a6859 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -4496,6 +4496,7 @@ impl<'a> ToV8<'a> for TscRequestArray { let method_name = deno_core::FastString::from_static(method_name) .v8_string(scope) + .unwrap() .into(); let args = args.unwrap_or_else(|| v8::Array::new(scope, 0).into()); let scope_url = serde_v8::to_v8(scope, self.scope) diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index 5e64585e0c..521ff65471 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -1411,19 +1411,13 @@ impl<'s> ToV8<'s> for V8MaybeStaticStr { self, scope: &mut v8::HandleScope<'s>, ) -> Result, Self::Error> { - // todo(https://github.com/denoland/deno_core/pull/986): remove this check - // when upgrading deno_core - const MAX_V8_STRING_LENGTH: usize = 536870888; - if self.0.len() > MAX_V8_STRING_LENGTH { - return Err(FastStringV8AllocationError); - } - Ok( match self.0 { Cow::Borrowed(text) => FastString::from_static(text), Cow::Owned(value) => value.into(), } .v8_string(scope) + .map_err(|_| FastStringV8AllocationError)? .into(), ) } diff --git a/ext/node/ops/v8.rs b/ext/node/ops/v8.rs index 61f67f11f7..8f09314d1d 100644 --- a/ext/node/ops/v8.rs +++ b/ext/node/ops/v8.rs @@ -68,6 +68,7 @@ impl v8::ValueSerializerImpl for SerializerDelegate { let obj = self.obj(scope); let key = FastString::from_static("_getSharedArrayBufferId") .v8_string(scope) + .unwrap() .into(); if let Some(v) = obj.get(scope, key) { if let Ok(fun) = v.try_cast::() { @@ -89,6 +90,7 @@ impl v8::ValueSerializerImpl for SerializerDelegate { let obj = self.obj(scope); let key = FastString::from_static("_getDataCloneError") .v8_string(scope) + .unwrap() .into(); if let Some(v) = obj.get(scope, key) { let fun = v @@ -112,6 +114,7 @@ impl v8::ValueSerializerImpl for SerializerDelegate { let obj = self.obj(scope); let key = FastString::from_static("_writeHostObject") .v8_string(scope) + .unwrap() .into(); if let Some(v) = obj.get(scope, key) { if let Ok(v) = v.try_cast::() { @@ -240,6 +243,7 @@ impl v8::ValueDeserializerImpl for DeserializerDelegate { let obj = v8::Local::new(scope, &self.obj); let key = FastString::from_static("_readHostObject") .v8_string(scope) + .unwrap() .into(); let scope = &mut v8::AllowJavascriptExecutionScope::new(scope); if let Some(v) = obj.get(scope, key) { @@ -250,7 +254,8 @@ impl v8::ValueDeserializerImpl for DeserializerDelegate { Err(_) => { let msg = FastString::from_static("readHostObject must return an object") - .v8_string(scope); + .v8_string(scope) + .unwrap(); let error = v8::Exception::type_error(scope, msg); scope.throw_exception(error); return None; From 75945cbb86c25300a69ae1da47751affa3465929 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 16 Dec 2024 09:37:39 -0500 Subject: [PATCH 004/107] fix(compile): display embedded file sizes and total (#27360) Merging as a fix so that LTS gets this as it's a useful diagnostic tool. The 1MB unique is because we deduplicate files that we store (ex. some packages have the same file multiple times so we store that once). --- cli/standalone/virtual_fs.rs | 299 ++++++++++++++---- tests/integration/compile_tests.rs | 2 +- .../compile/env_vars_support/compile.out | 6 +- .../compile.out | 84 ++--- .../compile/include/symlink_twice/compile.out | 8 +- tests/specs/compile/npm_fs/compile.out | 8 +- .../compile/package_json_type/compile.out | 2 +- .../main_compile_file.out | 2 +- .../main_compile_folder.out | 14 +- 9 files changed, 299 insertions(+), 126 deletions(-) diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index 8ddd179c7a..04e66d680e 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -35,6 +35,7 @@ use serde::Serialize; use thiserror::Error; use crate::util; +use crate::util::display::human_size; use crate::util::display::DisplayTreeNode; use crate::util::fs::canonicalize_path; @@ -512,96 +513,238 @@ pub fn output_vfs(vfs: &BuiltVfs, executable_name: &str) { let mut text = String::new(); let display_tree = vfs_as_display_tree(vfs, executable_name); display_tree.print(&mut text).unwrap(); // unwrap ok because it's writing to a string - log::info!( - "\n{}\n", - deno_terminal::colors::bold("Embedded File System") - ); + log::info!("\n{}\n", deno_terminal::colors::bold("Embedded Files")); log::info!("{}\n", text.trim()); + log::info!( + "Size: {}\n", + human_size(vfs.files.iter().map(|f| f.len() as f64).sum()) + ); } fn vfs_as_display_tree( vfs: &BuiltVfs, executable_name: &str, ) -> DisplayTreeNode { + /// The VFS only stores duplicate files once, so track that and display + /// it to the user so that it's not confusing. + #[derive(Debug, Default, Copy, Clone)] + struct Size { + unique: u64, + total: u64, + } + + impl std::ops::Add for Size { + type Output = Self; + + fn add(self, other: Self) -> Self { + Self { + unique: self.unique + other.unique, + total: self.total + other.total, + } + } + } + + impl std::iter::Sum for Size { + fn sum>(iter: I) -> Self { + iter.fold(Self::default(), std::ops::Add::add) + } + } + enum EntryOutput<'a> { - All, + All(Size), Subset(Vec>), - File, + File(Size), Symlink(&'a [String]), } impl<'a> EntryOutput<'a> { - pub fn as_display_tree(&self, name: String) -> DisplayTreeNode { - let mut children = match self { - EntryOutput::Subset(vec) => vec - .iter() - .map(|e| e.output.as_display_tree(e.name.to_string())) - .collect(), - EntryOutput::All | EntryOutput::File | EntryOutput::Symlink(_) => { - vec![] + pub fn size(&self) -> Size { + match self { + EntryOutput::All(size) => *size, + EntryOutput::Subset(children) => { + children.iter().map(|c| c.output.size()).sum() } - }; - // we only want to collapse leafs so that nodes of the - // same depth have the same indentation - let collapse_single_child = - children.len() == 1 && children[0].children.is_empty(); + EntryOutput::File(size) => *size, + EntryOutput::Symlink(_) => Size { + unique: 0, + total: 0, + }, + } + } + } + + impl<'a> EntryOutput<'a> { + pub fn as_display_tree(&self, name: String) -> DisplayTreeNode { + fn format_size(size: Size) -> String { + if size.unique == size.total { + human_size(size.unique as f64) + } else { + format!( + "{}{}", + human_size(size.total as f64), + deno_terminal::colors::gray(format!( + " - {} unique", + human_size(size.unique as f64) + )) + ) + } + } + DisplayTreeNode { text: match self { - EntryOutput::All => format!("{}/*", name), - EntryOutput::Subset(_) => { - if collapse_single_child { - format!("{}/{}", name, children[0].text) - } else { - name - } + EntryOutput::All(size) => { + format!("{}/* ({})", name, format_size(*size)) + } + EntryOutput::Subset(children) => { + let size = children.iter().map(|c| c.output.size()).sum::(); + format!("{} ({})", name, format_size(size)) + } + EntryOutput::File(size) => { + format!("{} ({})", name, format_size(*size)) } - EntryOutput::File => name, EntryOutput::Symlink(parts) => { format!("{} --> {}", name, parts.join("/")) } }, - children: if collapse_single_child { - children.remove(0).children - } else { - children + children: match self { + EntryOutput::All(_) => Vec::new(), + EntryOutput::Subset(children) => children + .iter() + .map(|entry| entry.output.as_display_tree(entry.name.to_string())) + .collect(), + EntryOutput::File(_) => Vec::new(), + EntryOutput::Symlink(_) => Vec::new(), }, } } } pub struct DirEntryOutput<'a> { - name: &'a str, + name: Cow<'a, str>, output: EntryOutput<'a>, } - fn show_global_node_modules_dir( - vfs_dir: &VirtualDirectory, - ) -> Vec { - fn show_subset_deep( - vfs_dir: &VirtualDirectory, - depth: usize, - ) -> EntryOutput { - if depth == 0 { - EntryOutput::All + impl<'a> DirEntryOutput<'a> { + /// Collapses leaf nodes so they don't take up so much space when being + /// displayed. + /// + /// We only want to collapse leafs so that nodes of the same depth have + /// the same indentation. + pub fn collapse_leaf_nodes(&mut self) { + let EntryOutput::Subset(vec) = &mut self.output else { + return; + }; + for dir_entry in vec.iter_mut() { + dir_entry.collapse_leaf_nodes(); + } + if vec.len() != 1 { + return; + } + let child = &mut vec[0]; + let child_name = &child.name; + match &mut child.output { + EntryOutput::All(size) => { + self.name = Cow::Owned(format!("{}/{}", self.name, child_name)); + self.output = EntryOutput::All(*size); + } + EntryOutput::Subset(children) => { + if children.is_empty() { + self.name = Cow::Owned(format!("{}/{}", self.name, child_name)); + self.output = EntryOutput::Subset(vec![]); + } + } + EntryOutput::File(size) => { + self.name = Cow::Owned(format!("{}/{}", self.name, child_name)); + self.output = EntryOutput::File(*size); + } + EntryOutput::Symlink(parts) => { + let new_name = format!("{}/{}", self.name, child_name); + self.output = EntryOutput::Symlink(parts); + self.name = Cow::Owned(new_name); + } + } + } + } + + fn file_size(file: &VirtualFile, seen_offsets: &mut HashSet) -> Size { + fn add_offset_to_size( + offset: OffsetWithLength, + size: &mut Size, + seen_offsets: &mut HashSet, + ) { + if offset.len == 0 { + // some empty files have a dummy offset, so don't + // insert them into the seen offsets + return; + } + + if seen_offsets.insert(offset.offset) { + size.total += offset.len; + size.unique += offset.len; } else { - EntryOutput::Subset(show_subset(vfs_dir, depth)) + size.total += offset.len; } } - fn show_subset( - vfs_dir: &VirtualDirectory, + let mut size = Size::default(); + add_offset_to_size(file.offset, &mut size, seen_offsets); + if file.module_graph_offset.offset != file.offset.offset { + add_offset_to_size(file.module_graph_offset, &mut size, seen_offsets); + } + size + } + + fn dir_size(dir: &VirtualDirectory, seen_offsets: &mut HashSet) -> Size { + let mut size = Size::default(); + for entry in &dir.entries { + match entry { + VfsEntry::Dir(virtual_directory) => { + size = size + dir_size(virtual_directory, seen_offsets); + } + VfsEntry::File(file) => { + size = size + file_size(file, seen_offsets); + } + VfsEntry::Symlink(_) => { + // ignore + } + } + } + size + } + + fn show_global_node_modules_dir<'a>( + vfs_dir: &'a VirtualDirectory, + seen_offsets: &mut HashSet, + ) -> Vec> { + fn show_subset_deep<'a>( + vfs_dir: &'a VirtualDirectory, depth: usize, - ) -> Vec { + seen_offsets: &mut HashSet, + ) -> EntryOutput<'a> { + if depth == 0 { + EntryOutput::All(dir_size(vfs_dir, seen_offsets)) + } else { + EntryOutput::Subset(show_subset(vfs_dir, depth, seen_offsets)) + } + } + + fn show_subset<'a>( + vfs_dir: &'a VirtualDirectory, + depth: usize, + seen_offsets: &mut HashSet, + ) -> Vec> { vfs_dir .entries .iter() .map(|entry| DirEntryOutput { - name: entry.name(), + name: Cow::Borrowed(entry.name()), output: match entry { VfsEntry::Dir(virtual_directory) => { - show_subset_deep(virtual_directory, depth - 1) + show_subset_deep(virtual_directory, depth - 1, seen_offsets) + } + VfsEntry::File(file) => { + EntryOutput::File(file_size(file, seen_offsets)) } - VfsEntry::File(_) => EntryOutput::File, VfsEntry::Symlink(virtual_symlink) => { EntryOutput::Symlink(&virtual_symlink.dest_parts.0) } @@ -612,40 +755,54 @@ fn vfs_as_display_tree( // in this scenario, we want to show // .deno_compile_node_modules/localhost///* - show_subset(vfs_dir, 3) + show_subset(vfs_dir, 3, seen_offsets) } fn include_all_entries<'a>( dir_path: &WindowsSystemRootablePath, vfs_dir: &'a VirtualDirectory, + seen_offsets: &mut HashSet, ) -> Vec> { if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME { - return show_global_node_modules_dir(vfs_dir); + return show_global_node_modules_dir(vfs_dir, seen_offsets); } vfs_dir .entries .iter() .map(|entry| DirEntryOutput { - name: entry.name(), - output: analyze_entry(dir_path.join(entry.name()), entry), + name: Cow::Borrowed(entry.name()), + output: analyze_entry(dir_path.join(entry.name()), entry, seen_offsets), }) .collect() } - fn analyze_entry(path: PathBuf, entry: &VfsEntry) -> EntryOutput { + fn analyze_entry<'a>( + path: PathBuf, + entry: &'a VfsEntry, + seen_offsets: &mut HashSet, + ) -> EntryOutput<'a> { match entry { - VfsEntry::Dir(virtual_directory) => analyze_dir(path, virtual_directory), - VfsEntry::File(_) => EntryOutput::File, + VfsEntry::Dir(virtual_directory) => { + analyze_dir(path, virtual_directory, seen_offsets) + } + VfsEntry::File(file) => EntryOutput::File(file_size(file, seen_offsets)), VfsEntry::Symlink(virtual_symlink) => { EntryOutput::Symlink(&virtual_symlink.dest_parts.0) } } } - fn analyze_dir(dir: PathBuf, vfs_dir: &VirtualDirectory) -> EntryOutput { + fn analyze_dir<'a>( + dir: PathBuf, + vfs_dir: &'a VirtualDirectory, + seen_offsets: &mut HashSet, + ) -> EntryOutput<'a> { if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME { - return EntryOutput::Subset(show_global_node_modules_dir(vfs_dir)); + return EntryOutput::Subset(show_global_node_modules_dir( + vfs_dir, + seen_offsets, + )); } let real_entry_count = std::fs::read_dir(&dir) @@ -657,15 +814,15 @@ fn vfs_as_display_tree( .entries .iter() .map(|entry| DirEntryOutput { - name: entry.name(), - output: analyze_entry(dir.join(entry.name()), entry), + name: Cow::Borrowed(entry.name()), + output: analyze_entry(dir.join(entry.name()), entry, seen_offsets), }) .collect::>(); if children .iter() - .all(|c| !matches!(c.output, EntryOutput::Subset(_))) + .all(|c| !matches!(c.output, EntryOutput::Subset { .. })) { - EntryOutput::All + EntryOutput::All(children.iter().map(|c| c.output.size()).sum()) } else { EntryOutput::Subset(children) } @@ -673,13 +830,19 @@ fn vfs_as_display_tree( EntryOutput::Subset(include_all_entries( &WindowsSystemRootablePath::Path(dir), vfs_dir, + seen_offsets, )) } } // always include all the entries for the root directory, otherwise the // user might not have context about what's being shown - let child_entries = include_all_entries(&vfs.root_path, &vfs.root); + let mut seen_offsets = HashSet::with_capacity(vfs.files.len()); + let mut child_entries = + include_all_entries(&vfs.root_path, &vfs.root, &mut seen_offsets); + for child_entry in &mut child_entries { + child_entry.collapse_leaf_nodes(); + } DisplayTreeNode { text: deno_terminal::colors::italic(executable_name).to_string(), children: child_entries @@ -1637,8 +1800,8 @@ mod test { let temp_dir = TempDir::new(); temp_dir.write("root.txt", ""); temp_dir.create_dir_all("a"); - temp_dir.write("a/a.txt", ""); - temp_dir.write("a/b.txt", ""); + temp_dir.write("a/a.txt", "data"); + temp_dir.write("a/b.txt", "other data"); temp_dir.create_dir_all("b"); temp_dir.write("b/a.txt", ""); temp_dir.write("b/b.txt", ""); @@ -1666,10 +1829,10 @@ mod test { assert_eq!( strip_ansi_codes(&text), r#"executable -├── a/* -├── b/a.txt -└─┬ c - ├── a.txt +├── a/* (14B) +├── b/a.txt (0B) +└─┬ c (8B) + ├── a.txt (8B) └── b.txt --> c/a.txt "# ); diff --git a/tests/integration/compile_tests.rs b/tests/integration/compile_tests.rs index a34d2cdd1d..a69e873ab4 100644 --- a/tests/integration/compile_tests.rs +++ b/tests/integration/compile_tests.rs @@ -1091,7 +1091,7 @@ Warning Failed resolving symlink. Ignoring. Path: [WILDCARD] Message: [WILDCARD]) -Embedded File System +Embedded Files [WILDCARD] diff --git a/tests/specs/compile/env_vars_support/compile.out b/tests/specs/compile/env_vars_support/compile.out index 02332ea0eb..cba114b1f7 100644 --- a/tests/specs/compile/env_vars_support/compile.out +++ b/tests/specs/compile/env_vars_support/compile.out @@ -3,8 +3,10 @@ Check [WILDCARD]main.ts Compile [WILDCARD]main.ts to out[WILDCARD] Warning Environment variables from the file "environment.env" were embedded in the generated executable file -Embedded File System +Embedded Files out[WILDLINE] -└── main.ts +└── main.ts ([WILDLINE]) + +Size: [WILDLINE] diff --git a/tests/specs/compile/global_npm_cache_implicit_read_permission/compile.out b/tests/specs/compile/global_npm_cache_implicit_read_permission/compile.out index c29c878593..fa1dd2bf9f 100644 --- a/tests/specs/compile/global_npm_cache_implicit_read_permission/compile.out +++ b/tests/specs/compile/global_npm_cache_implicit_read_permission/compile.out @@ -1,47 +1,49 @@ [WILDCARD] Compile file:///[WILDLINE]/main.ts to [WILDLINE] -Embedded File System +Embedded Files main[WILDLINE] -├─┬ .deno_compile_node_modules -│ └─┬ localhost -│ ├─┬ ansi-regex -│ │ ├── 3.0.1/* -│ │ └── 5.0.1/* -│ ├── ansi-styles/4.3.0/* -│ ├── camelcase/5.3.1/* -│ ├── cliui/6.0.0/* -│ ├── color-convert/2.0.1/* -│ ├── color-name/1.1.4/* -│ ├── cowsay/1.5.0/* -│ ├── decamelize/1.2.0/* -│ ├── emoji-regex/8.0.0/* -│ ├── find-up/4.1.0/* -│ ├── get-caller-file/2.0.5/* -│ ├── get-stdin/8.0.0/* -│ ├─┬ is-fullwidth-code-point -│ │ ├── 2.0.0/* -│ │ └── 3.0.0/* -│ ├── locate-path/5.0.0/* -│ ├── p-limit/2.3.0/* -│ ├── p-locate/4.1.0/* -│ ├── p-try/2.2.0/* -│ ├── path-exists/4.0.0/* -│ ├── require-directory/2.1.1/* -│ ├── require-main-filename/2.0.0/* -│ ├── set-blocking/2.0.0/* -│ ├─┬ string-width -│ │ ├── 2.1.1/* -│ │ └── 4.2.3/* -│ ├─┬ strip-ansi -│ │ ├── 4.0.0/* -│ │ └── 6.0.1/* -│ ├── strip-final-newline/2.0.0/* -│ ├── which-module/2.0.0/* -│ ├── wrap-ansi/6.2.0/* -│ ├── y18n/4.0.3/* -│ ├── yargs/15.4.1/* -│ └── yargs-parser/18.1.3/* -└── main.ts +├─┬ .deno_compile_node_modules ([WILDLINE]) +│ └─┬ localhost ([WILDLINE]) +│ ├─┬ ansi-regex ([WILDLINE]) +│ │ ├── 3.0.1/* ([WILDLINE]) +│ │ └── 5.0.1/* ([WILDLINE]) +│ ├── ansi-styles/4.3.0/* ([WILDLINE]) +│ ├── camelcase/5.3.1/* ([WILDLINE]) +│ ├── cliui/6.0.0/* ([WILDLINE]) +│ ├── color-convert/2.0.1/* ([WILDLINE]) +│ ├── color-name/1.1.4/* ([WILDLINE]) +│ ├── cowsay/1.5.0/* ([WILDLINE]) +│ ├── decamelize/1.2.0/* ([WILDLINE]) +│ ├── emoji-regex/8.0.0/* ([WILDLINE]) +│ ├── find-up/4.1.0/* ([WILDLINE]) +│ ├── get-caller-file/2.0.5/* ([WILDLINE]) +│ ├── get-stdin/8.0.0/* ([WILDLINE]) +│ ├─┬ is-fullwidth-code-point ([WILDLINE]) +│ │ ├── 2.0.0/* ([WILDLINE]) +│ │ └── 3.0.0/* ([WILDLINE]) +│ ├── locate-path/5.0.0/* ([WILDLINE]) +│ ├── p-limit/2.3.0/* ([WILDLINE]) +│ ├── p-locate/4.1.0/* ([WILDLINE]) +│ ├── p-try/2.2.0/* ([WILDLINE]) +│ ├── path-exists/4.0.0/* ([WILDLINE]) +│ ├── require-directory/2.1.1/* ([WILDLINE]) +│ ├── require-main-filename/2.0.0/* ([WILDLINE]) +│ ├── set-blocking/2.0.0/* ([WILDLINE]) +│ ├─┬ string-width ([WILDLINE]) +│ │ ├── 2.1.1/* ([WILDLINE]) +│ │ └── 4.2.3/* ([WILDLINE]) +│ ├─┬ strip-ansi ([WILDLINE]) +│ │ ├── 4.0.0/* ([WILDLINE]) +│ │ └── 6.0.1/* ([WILDLINE]) +│ ├── strip-final-newline/2.0.0/* ([WILDLINE]) +│ ├── which-module/2.0.0/* ([WILDLINE]) +│ ├── wrap-ansi/6.2.0/* ([WILDLINE]) +│ ├── y18n/4.0.3/* ([WILDLINE]) +│ ├── yargs/15.4.1/* ([WILDLINE]) +│ └── yargs-parser/18.1.3/* ([WILDLINE]) +└── main.ts ([WILDLINE]) + +Size: [WILDLINE] diff --git a/tests/specs/compile/include/symlink_twice/compile.out b/tests/specs/compile/include/symlink_twice/compile.out index c57eb9b2f1..6ae93bf1cb 100644 --- a/tests/specs/compile/include/symlink_twice/compile.out +++ b/tests/specs/compile/include/symlink_twice/compile.out @@ -1,9 +1,11 @@ Compile [WILDLINE] -Embedded File System +Embedded Files main[WILDLINE] -├── index.js +├── index.js ([WILDLINE]) ├── link.js --> index.js -└── setup.js +└── setup.js ([WILDLINE]) + +Size: [WILDLINE] diff --git a/tests/specs/compile/npm_fs/compile.out b/tests/specs/compile/npm_fs/compile.out index 4944146788..c2ecef4015 100644 --- a/tests/specs/compile/npm_fs/compile.out +++ b/tests/specs/compile/npm_fs/compile.out @@ -1,8 +1,10 @@ [WILDCARD] -Embedded File System +Embedded Files main[WILDLINE] -├── main.ts -└── node_modules/* +├── main.ts ([WILDLINE]) +└── node_modules/* ([WILDLINE]) + +Size: [WILDLINE] diff --git a/tests/specs/compile/package_json_type/compile.out b/tests/specs/compile/package_json_type/compile.out index 0370192809..c03e63b71a 100644 --- a/tests/specs/compile/package_json_type/compile.out +++ b/tests/specs/compile/package_json_type/compile.out @@ -1,6 +1,6 @@ Check file:///[WILDLINE]/main.js Compile file:///[WILDLINE] -Embedded File System +Embedded Files [WILDCARD] diff --git a/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out b/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out index 633c2cca62..a96f1f71e1 100644 --- a/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out +++ b/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out @@ -1,5 +1,5 @@ Compile file:///[WILDCARD]/node_modules_symlink_outside/main.ts to [WILDCARD] -Embedded File System +Embedded Files [WILDCARD] diff --git a/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out b/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out index 61f0a2456a..538aaa414c 100644 --- a/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out +++ b/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out @@ -4,12 +4,14 @@ Initialize @denotest/esm-basic@1.0.0 Check file:///[WILDCARD]/node_modules_symlink_outside/main.ts Compile file:///[WILDCARD]/node_modules_symlink_outside/main.ts to [WILDLINE] -Embedded File System +Embedded Files bin[WILDLINE] -├─┬ compile -│ └─┬ node_modules_symlink_outside -│ ├── main.ts -│ └── node_modules/* -└── some_folder/* +├─┬ compile ([WILDLINE]) +│ └─┬ node_modules_symlink_outside ([WILDLINE]) +│ ├── main.ts ([WILDLINE]) +│ └── node_modules/* ([WILDLINE]) +└── some_folder/* ([WILDLINE]) + +Size: [WILDLINE] From f9f5a12a16f5e98726664cc0e8eada94e4c6304d Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 16 Dec 2024 07:12:07 -0800 Subject: [PATCH 005/107] fix: upgrade deno_doc to 0.161.3 (#27377) upgrades itoa requirement to `1.0.14`. needed for #27308 --- Cargo.lock | 4 ++-- cli/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0ea574b2e..0a8c28ceb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1575,9 +1575,9 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.161.2" +version = "0.161.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af787319136f3e7f73ef551c618aeec70794522e36cd75ae35132a3bad983ef" +checksum = "353a39c70d248af04600928cefc8066a9e4535fb6e7d7c518411e5efc822819f" dependencies = [ "anyhow", "cfg-if", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 6395910ccd..253ea80f19 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -72,7 +72,7 @@ deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposa deno_cache_dir.workspace = true deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } -deno_doc = { version = "=0.161.2", features = ["rust", "comrak"] } +deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] } deno_graph = { version = "=0.86.3" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true From 358bf566c28b918c471dc1396c4ac2e8129af198 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Mon, 16 Dec 2024 17:25:49 +0000 Subject: [PATCH 006/107] fix(lsp): respect "typescript.suggestionActions.enabled" setting (#27373) --- cli/lsp/config.rs | 17 +++++++ cli/lsp/diagnostics.rs | 17 ++++++- cli/lsp/tsc.rs | 2 + cli/tsc/diagnostics.rs | 6 +++ cli/tsc/mod.rs | 3 ++ tests/integration/lsp_tests.rs | 82 ++++++++++++++++++++++++++++++++++ 6 files changed, 126 insertions(+), 1 deletion(-) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index a629757788..47e36d1328 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -459,6 +459,19 @@ impl Default for LanguagePreferences { } } +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct SuggestionActionsSettings { + #[serde(default = "is_true")] + pub enabled: bool, +} + +impl Default for SuggestionActionsSettings { + fn default() -> Self { + SuggestionActionsSettings { enabled: true } + } +} + #[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct UpdateImportsOnFileMoveOptions { @@ -490,6 +503,8 @@ pub struct LanguageWorkspaceSettings { #[serde(default)] pub suggest: CompletionSettings, #[serde(default)] + pub suggestion_actions: SuggestionActionsSettings, + #[serde(default)] pub update_imports_on_file_move: UpdateImportsOnFileMoveOptions, } @@ -2292,6 +2307,7 @@ mod tests { enabled: true, }, }, + suggestion_actions: SuggestionActionsSettings { enabled: true }, update_imports_on_file_move: UpdateImportsOnFileMoveOptions { enabled: UpdateImportsOnFileMoveEnabled::Prompt } @@ -2338,6 +2354,7 @@ mod tests { enabled: true, }, }, + suggestion_actions: SuggestionActionsSettings { enabled: true }, update_imports_on_file_move: UpdateImportsOnFileMoveOptions { enabled: UpdateImportsOnFileMoveEnabled::Prompt } diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 01fc3bf69e..e99e5a19b0 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -24,6 +24,7 @@ use crate::resolver::SloppyImportsCachedFs; use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinterOptions; use crate::tools::lint::LintRuleProvider; +use crate::tsc::DiagnosticCategory; use crate::util::path::to_percent_decoded_str; use deno_ast::MediaType; @@ -906,8 +907,22 @@ async fn generate_ts_diagnostics( } else { Default::default() }; - for (specifier_str, ts_json_diagnostics) in ts_diagnostics_map { + for (specifier_str, mut ts_json_diagnostics) in ts_diagnostics_map { let specifier = resolve_url(&specifier_str)?; + let suggestion_actions_settings = snapshot + .config + .language_settings_for_specifier(&specifier) + .map(|s| s.suggestion_actions.clone()) + .unwrap_or_default(); + if !suggestion_actions_settings.enabled { + ts_json_diagnostics.retain(|d| { + d.category != DiagnosticCategory::Suggestion + // Still show deprecated and unused diagnostics. + // https://github.com/microsoft/vscode/blob/ce50bd4876af457f64d83cfd956bc916535285f4/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts#L113-L114 + || d.reports_deprecated == Some(true) + || d.reports_unnecessary == Some(true) + }); + } let version = snapshot .documents .get(&specifier) diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 957c3a6859..fddcd6e738 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -5746,6 +5746,7 @@ mod tests { "sourceLine": " import { A } from \".\";", "category": 2, "code": 6133, + "reportsUnnecessary": true, }] }) ); @@ -5828,6 +5829,7 @@ mod tests { "sourceLine": " import {", "category": 2, "code": 6192, + "reportsUnnecessary": true, }, { "start": { "line": 8, diff --git a/cli/tsc/diagnostics.rs b/cli/tsc/diagnostics.rs index d3795706eb..e4cc80723f 100644 --- a/cli/tsc/diagnostics.rs +++ b/cli/tsc/diagnostics.rs @@ -133,6 +133,12 @@ pub struct Diagnostic { pub file_name: Option, #[serde(skip_serializing_if = "Option::is_none")] pub related_information: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub reports_deprecated: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub reports_unnecessary: Option, + #[serde(flatten)] + pub other: deno_core::serde_json::Map, } impl Diagnostic { diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index a8e8d73b68..4c18d1a2b0 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -1444,6 +1444,9 @@ mod tests { source_line: None, file_name: None, related_information: None, + reports_deprecated: None, + reports_unnecessary: None, + other: Default::default(), }]), stats: Stats(vec![("a".to_string(), 12)]) }) diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 13a3c0d69b..92cefb98f0 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -2082,6 +2082,88 @@ fn lsp_inlay_hints_not_enabled() { client.shutdown(); } +#[test] +fn lsp_suggestion_actions_disabled() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.change_configuration(json!({ + "deno": { + "enable": true, + "lint": false, + }, + "typescript": { + "suggestionActions": { + "enabled": false, + }, + }, + })); + client.read_diagnostics(); + let diagnostics = client.did_open(json!({ + "textDocument": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": r#" + // The settings should disable the suggestion for this to be async. + function asyncLikeFunction() { + return new Promise((r) => r(null)).then((v) => v); + } + console.log(asyncLikeFunction); + + // Deprecated warnings should remain. + /** @deprecated */ + function deprecatedFunction() {} + console.log(deprecatedFunction); + + // Unused warnings should remain. + const unsusedVariable = 1; + "#, + }, + })); + assert_eq!( + json!(diagnostics.all()), + json!([ + { + "range": { + "start": { "line": 10, "character": 20 }, + "end": { "line": 10, "character": 38 }, + }, + "severity": 4, + "code": 6385, + "source": "deno-ts", + "message": "'deprecatedFunction' is deprecated.", + "relatedInformation": [ + { + "location": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "range": { + "start": { "line": 8, "character": 12 }, + "end": { "line": 8, "character": 24 }, + }, + }, + "message": "The declaration was marked as deprecated here.", + }, + ], + "tags": [2], + }, + { + "range": { + "start": { "line": 13, "character": 14 }, + "end": { "line": 13, "character": 29 }, + }, + "severity": 4, + "code": 6133, + "source": "deno-ts", + "message": "'unsusedVariable' is declared but its value is never read.", + "tags": [1], + }, + ]), + ); + client.shutdown(); +} + #[test] fn lsp_workspace_disable_enable_paths() { fn run_test(use_trailing_slash: bool) { From 95928c46eb83024551e4ede49ea00c4f4b21afef Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 16 Dec 2024 18:39:40 -0500 Subject: [PATCH 007/107] refactor: extract out `FileFetcher` to `deno_cache_dir` (#27263) --- Cargo.lock | 34 +- Cargo.toml | 9 +- cli/Cargo.toml | 2 + cli/args/import_map.rs | 12 +- cli/args/mod.rs | 52 +- cli/auth_tokens.rs | 369 ----- cli/cache/mod.rs | 93 +- cli/factory.rs | 13 +- cli/file_fetcher.rs | 1387 ++++++++--------- cli/graph_util.rs | 6 +- cli/http_util.rs | 895 ++--------- cli/jsr.rs | 6 +- cli/lsp/config.rs | 8 +- cli/lsp/jsr.rs | 13 +- cli/lsp/language_server.rs | 12 +- cli/lsp/npm.rs | 13 +- cli/lsp/registries.rs | 61 +- cli/lsp/resolver.rs | 2 +- cli/lsp/tsc.rs | 5 +- cli/main.rs | 1 - cli/mainrt.rs | 1 - cli/npm/managed/mod.rs | 5 +- cli/npm/mod.rs | 12 +- cli/standalone/binary.rs | 6 +- cli/standalone/mod.rs | 2 +- cli/tools/check.rs | 2 +- cli/tools/coverage/mod.rs | 28 +- cli/tools/installer.rs | 12 +- cli/tools/registry/pm.rs | 12 +- cli/tools/registry/pm/outdated.rs | 12 +- cli/tools/repl/mod.rs | 7 +- cli/tools/run/mod.rs | 6 +- cli/tools/test/mod.rs | 10 +- cli/util/extract.rs | 19 +- resolvers/npm_cache/lib.rs | 22 + tests/integration/bench_tests.rs | 2 +- tests/integration/cache_tests.rs | 2 +- tests/integration/run_tests.rs | 2 +- tests/integration/test_tests.rs | 2 +- .../localhost_unsafe_ssl.ts.out | 7 +- .../run/jsx_import_source/__test__.jsonc | 1 + tests/util/server/src/servers/mod.rs | 5 - 42 files changed, 1018 insertions(+), 2152 deletions(-) delete mode 100644 cli/auth_tokens.rs diff --git a/Cargo.lock b/Cargo.lock index 0a8c28ceb5..850ab038f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -596,9 +596,9 @@ dependencies = [ [[package]] name = "boxed_error" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69aae56aaf59d1994b902ed5c0c79024012bdc2426741def75a635999a030e7e" +checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" dependencies = [ "quote", "syn 2.0.87", @@ -1193,9 +1193,9 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "data-url" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b319d1b62ffbd002e057f36bebd1f42b9f97927c9577461d855f3513c4289f" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "debug-ignore" @@ -1221,6 +1221,7 @@ dependencies = [ "async-trait", "base64 0.21.7", "bincode", + "boxed_error", "bytes", "cache_control", "chrono", @@ -1237,6 +1238,7 @@ dependencies = [ "deno_config", "deno_core", "deno_doc", + "deno_error", "deno_graph", "deno_lint", "deno_lockfile", @@ -1421,13 +1423,21 @@ dependencies = [ [[package]] name = "deno_cache_dir" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca43605c8cbce6c6787e0daf227864487c07c2b31d438c0bf43d1b38da94b7f" +checksum = "54df1c5177ace01d92b872584ab9af8290681bb150fd9b423c37a494ad5ddbdc" dependencies = [ + "async-trait", "base32", + "base64 0.21.7", + "boxed_error", + "cache_control", + "chrono", + "data-url", + "deno_error", "deno_media_type", "deno_path_util", + "http 1.1.0", "indexmap 2.3.0", "log", "once_cell", @@ -1612,6 +1622,7 @@ dependencies = [ "libc", "serde", "serde_json", + "url", ] [[package]] @@ -1857,9 +1868,9 @@ dependencies = [ [[package]] name = "deno_media_type" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf552fbdedbe81c89705349d7d2485c7051382b000dfddbdbf7fc25931cf83" +checksum = "eaa135b8a9febc9a51c16258e294e268a1276750780d69e46edb31cced2826e4" dependencies = [ "data-url", "serde", @@ -2085,12 +2096,13 @@ dependencies = [ [[package]] name = "deno_path_util" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99" +checksum = "b02c7d341e1b2cf089daff0f4fb2b4be8f3b5511b1d96040b3f7ed63a66c737b" dependencies = [ + "deno_error", "percent-encoding", - "thiserror 1.0.64", + "thiserror 2.0.3", "url", ] diff --git a/Cargo.toml b/Cargo.toml index 984cb187ef..0ab9c93376 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ deno_config = { version = "=0.39.3", features = ["workspace", "sync"] } deno_lockfile = "=0.23.2" deno_media_type = { version = "0.2.0", features = ["module_specifier"] } deno_npm = "=0.26.0" -deno_path_util = "=0.2.1" +deno_path_util = "=0.2.2" deno_permissions = { version = "0.42.0", path = "./runtime/permissions" } deno_runtime = { version = "0.191.0", path = "./runtime" } deno_semver = "=0.6.1" @@ -104,7 +104,7 @@ async-trait = "0.1.73" base32 = "=0.5.1" base64 = "0.21.7" bencher = "0.1" -boxed_error = "0.2.2" +boxed_error = "0.2.3" brotli = "6.0.0" bytes = "1.4.0" cache_control = "=0.2.0" @@ -117,8 +117,9 @@ color-print = "0.3.5" console_static_text = "=0.8.1" dashmap = "5.5.3" data-encoding = "2.3.3" -data-url = "=0.3.0" -deno_cache_dir = "=0.14.0" +data-url = "=0.3.1" +deno_cache_dir = "=0.15.0" +deno_error = "=0.5.2" deno_package_json = { version = "0.2.1", default-features = false } deno_unsync = "0.4.2" dlopen2 = "0.6.1" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 253ea80f19..84464bb01f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -73,6 +73,7 @@ deno_cache_dir.workspace = true deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] } +deno_error.workspace = true deno_graph = { version = "=0.86.3" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true @@ -93,6 +94,7 @@ anstream = "0.6.14" async-trait.workspace = true base64.workspace = true bincode = "=1.3.3" +boxed_error.workspace = true bytes.workspace = true cache_control.workspace = true chrono = { workspace = true, features = ["now"] } diff --git a/cli/args/import_map.rs b/cli/args/import_map.rs index ff2f158715..d6434ed46a 100644 --- a/cli/args/import_map.rs +++ b/cli/args/import_map.rs @@ -4,21 +4,21 @@ use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::TextDecodedFile; pub async fn resolve_import_map_value_from_specifier( specifier: &Url, - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, ) -> Result { if specifier.scheme() == "data" { let data_url_text = deno_graph::source::RawDataUrl::parse(specifier)?.decode()?; Ok(serde_json::from_str(&data_url_text)?) } else { - let file = file_fetcher - .fetch_bypass_permissions(specifier) - .await? - .into_text_decoded()?; + let file = TextDecodedFile::decode( + file_fetcher.fetch_bypass_permissions(specifier).await?, + )?; Ok(serde_json::from_str(&file.source)?) } } diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 450aa11652..ddf990fcab 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -9,6 +9,7 @@ mod package_json; use deno_ast::MediaType; use deno_ast::SourceMapOption; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_config::deno_json::NodeModulesDirMode; use deno_config::workspace::CreateResolverOptions; use deno_config::workspace::FolderConfigs; @@ -27,7 +28,6 @@ use deno_npm::npm_rc::NpmRc; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmSystemInfo; -use deno_npm_cache::NpmCacheSetting; use deno_path_util::normalize_path; use deno_semver::npm::NpmPackageReqReference; use deno_telemetry::OtelConfig; @@ -85,7 +85,7 @@ use thiserror::Error; use crate::cache; use crate::cache::DenoDirProvider; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::version; @@ -217,52 +217,6 @@ pub fn ts_config_to_transpile_and_emit_options( )) } -/// Indicates how cached source files should be handled. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum CacheSetting { - /// Only the cached files should be used. Any files not in the cache will - /// error. This is the equivalent of `--cached-only` in the CLI. - Only, - /// No cached source files should be used, and all files should be reloaded. - /// This is the equivalent of `--reload` in the CLI. - ReloadAll, - /// Only some cached resources should be used. This is the equivalent of - /// `--reload=jsr:@std/http/file-server` or - /// `--reload=jsr:@std/http/file-server,jsr:@std/assert/assert-equals`. - ReloadSome(Vec), - /// The usability of a cached value is determined by analyzing the cached - /// headers and other metadata associated with a cached response, reloading - /// any cached "non-fresh" cached responses. - RespectHeaders, - /// The cached source files should be used for local modules. This is the - /// default behavior of the CLI. - Use, -} - -impl CacheSetting { - pub fn as_npm_cache_setting(&self) -> NpmCacheSetting { - match self { - CacheSetting::Only => NpmCacheSetting::Only, - CacheSetting::ReloadAll => NpmCacheSetting::ReloadAll, - CacheSetting::ReloadSome(values) => { - if values.iter().any(|v| v == "npm:") { - NpmCacheSetting::ReloadAll - } else { - NpmCacheSetting::ReloadSome { - npm_package_names: values - .iter() - .filter_map(|v| v.strip_prefix("npm:")) - .map(|n| n.to_string()) - .collect(), - } - } - } - CacheSetting::RespectHeaders => unreachable!(), // not supported - CacheSetting::Use => NpmCacheSetting::Use, - } - } -} - pub struct WorkspaceBenchOptions { pub filter: Option, pub json: bool, @@ -1091,7 +1045,7 @@ impl CliOptions { pub async fn create_workspace_resolver( &self, - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, pkg_json_dep_resolution: PackageJsonDepResolution, ) -> Result { let overrode_no_import_map: bool = self diff --git a/cli/auth_tokens.rs b/cli/auth_tokens.rs deleted file mode 100644 index ef9f9d0746..0000000000 --- a/cli/auth_tokens.rs +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use base64::prelude::BASE64_STANDARD; -use base64::Engine; -use deno_core::ModuleSpecifier; -use log::debug; -use log::error; -use std::borrow::Cow; -use std::fmt; -use std::net::IpAddr; -use std::net::Ipv4Addr; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::str::FromStr; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum AuthTokenData { - Bearer(String), - Basic { username: String, password: String }, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AuthToken { - host: AuthDomain, - token: AuthTokenData, -} - -impl fmt::Display for AuthToken { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match &self.token { - AuthTokenData::Bearer(token) => write!(f, "Bearer {token}"), - AuthTokenData::Basic { username, password } => { - let credentials = format!("{username}:{password}"); - write!(f, "Basic {}", BASE64_STANDARD.encode(credentials)) - } - } - } -} - -/// A structure which contains bearer tokens that can be used when sending -/// requests to websites, intended to authorize access to private resources -/// such as remote modules. -#[derive(Debug, Clone)] -pub struct AuthTokens(Vec); - -/// An authorization domain, either an exact or suffix match. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum AuthDomain { - Ip(IpAddr), - IpPort(SocketAddr), - /// Suffix match, no dot. May include a port. - Suffix(Cow<'static, str>), -} - -impl From for AuthDomain { - fn from(value: T) -> Self { - let s = value.to_string().to_lowercase(); - if let Ok(ip) = SocketAddr::from_str(&s) { - return AuthDomain::IpPort(ip); - }; - if s.starts_with('[') && s.ends_with(']') { - if let Ok(ip) = Ipv6Addr::from_str(&s[1..s.len() - 1]) { - return AuthDomain::Ip(ip.into()); - } - } else if let Ok(ip) = Ipv4Addr::from_str(&s) { - return AuthDomain::Ip(ip.into()); - } - if let Some(s) = s.strip_prefix('.') { - AuthDomain::Suffix(Cow::Owned(s.to_owned())) - } else { - AuthDomain::Suffix(Cow::Owned(s)) - } - } -} - -impl AuthDomain { - pub fn matches(&self, specifier: &ModuleSpecifier) -> bool { - let Some(host) = specifier.host_str() else { - return false; - }; - match *self { - Self::Ip(ip) => { - let AuthDomain::Ip(parsed) = AuthDomain::from(host) else { - return false; - }; - ip == parsed && specifier.port().is_none() - } - Self::IpPort(ip) => { - let AuthDomain::Ip(parsed) = AuthDomain::from(host) else { - return false; - }; - ip.ip() == parsed && specifier.port() == Some(ip.port()) - } - Self::Suffix(ref suffix) => { - let hostname = if let Some(port) = specifier.port() { - Cow::Owned(format!("{}:{}", host, port)) - } else { - Cow::Borrowed(host) - }; - - if suffix.len() == hostname.len() { - return suffix == &hostname; - } - - // If it's a suffix match, ensure a dot - if hostname.ends_with(suffix.as_ref()) - && hostname.ends_with(&format!(".{suffix}")) - { - return true; - } - - false - } - } - } -} - -impl AuthTokens { - /// Create a new set of tokens based on the provided string. It is intended - /// that the string be the value of an environment variable and the string is - /// parsed for token values. The string is expected to be a semi-colon - /// separated string, where each value is `{token}@{hostname}`. - pub fn new(maybe_tokens_str: Option) -> Self { - let mut tokens = Vec::new(); - if let Some(tokens_str) = maybe_tokens_str { - for token_str in tokens_str.trim().split(';') { - if token_str.contains('@') { - let mut iter = token_str.rsplitn(2, '@'); - let host = AuthDomain::from(iter.next().unwrap()); - let token = iter.next().unwrap(); - if token.contains(':') { - let mut iter = token.rsplitn(2, ':'); - let password = iter.next().unwrap().to_owned(); - let username = iter.next().unwrap().to_owned(); - tokens.push(AuthToken { - host, - token: AuthTokenData::Basic { username, password }, - }); - } else { - tokens.push(AuthToken { - host, - token: AuthTokenData::Bearer(token.to_string()), - }); - } - } else { - error!("Badly formed auth token discarded."); - } - } - debug!("Parsed {} auth token(s).", tokens.len()); - } - - Self(tokens) - } - - /// Attempt to match the provided specifier to the tokens in the set. The - /// matching occurs from the right of the hostname plus port, irrespective of - /// scheme. For example `https://www.deno.land:8080/` would match a token - /// with a host value of `deno.land:8080` but not match `www.deno.land`. The - /// matching is case insensitive. - pub fn get(&self, specifier: &ModuleSpecifier) -> Option { - self.0.iter().find_map(|t| { - if t.host.matches(specifier) { - Some(t.clone()) - } else { - None - } - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use deno_core::resolve_url; - - #[test] - fn test_auth_token() { - let auth_tokens = AuthTokens::new(Some("abc123@deno.land".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123" - ); - let fixture = resolve_url("https://www.deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - let fixture = resolve_url("http://127.0.0.1:8080/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = - resolve_url("https://deno.land.example.com/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = resolve_url("https://deno.land:8080/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - } - - #[test] - fn test_auth_tokens_multiple() { - let auth_tokens = - AuthTokens::new(Some("abc123@deno.land;def456@example.com".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - let fixture = resolve_url("http://example.com/a/file.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer def456".to_string() - ); - } - - #[test] - fn test_auth_tokens_space() { - let auth_tokens = AuthTokens::new(Some( - " abc123@deno.land;def456@example.com\t".to_string(), - )); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - let fixture = resolve_url("http://example.com/a/file.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer def456".to_string() - ); - } - - #[test] - fn test_auth_tokens_newline() { - let auth_tokens = AuthTokens::new(Some( - "\nabc123@deno.land;def456@example.com\n".to_string(), - )); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - let fixture = resolve_url("http://example.com/a/file.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer def456".to_string() - ); - } - - #[test] - fn test_auth_tokens_port() { - let auth_tokens = - AuthTokens::new(Some("abc123@deno.land:8080".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = resolve_url("http://deno.land:8080/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc123".to_string() - ); - } - - #[test] - fn test_auth_tokens_contain_at() { - let auth_tokens = AuthTokens::new(Some("abc@123@deno.land".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Bearer abc@123".to_string() - ); - } - - #[test] - fn test_auth_token_basic() { - let auth_tokens = AuthTokens::new(Some("abc:123@deno.land".to_string())); - let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Basic YWJjOjEyMw==" - ); - let fixture = resolve_url("https://www.deno.land/x/mod.ts").unwrap(); - assert_eq!( - auth_tokens.get(&fixture).unwrap().to_string(), - "Basic YWJjOjEyMw==".to_string() - ); - let fixture = resolve_url("http://127.0.0.1:8080/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = - resolve_url("https://deno.land.example.com/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - let fixture = resolve_url("https://deno.land:8080/x/mod.ts").unwrap(); - assert_eq!(auth_tokens.get(&fixture), None); - } - - #[test] - fn test_parse_ip() { - let ip = AuthDomain::from("[2001:db8:a::123]"); - assert_eq!("Ip(2001:db8:a::123)", format!("{ip:?}")); - let ip = AuthDomain::from("[2001:db8:a::123]:8080"); - assert_eq!("IpPort([2001:db8:a::123]:8080)", format!("{ip:?}")); - let ip = AuthDomain::from("1.1.1.1"); - assert_eq!("Ip(1.1.1.1)", format!("{ip:?}")); - } - - #[test] - fn test_case_insensitive() { - let domain = AuthDomain::from("EXAMPLE.com"); - assert!( - domain.matches(&ModuleSpecifier::parse("http://example.com").unwrap()) - ); - assert!( - domain.matches(&ModuleSpecifier::parse("http://example.COM").unwrap()) - ); - } - - #[test] - fn test_matches() { - let candidates = [ - "example.com", - "www.example.com", - "1.1.1.1", - "[2001:db8:a::123]", - // These will never match - "example.com.evil.com", - "1.1.1.1.evil.com", - "notexample.com", - "www.notexample.com", - ]; - let domains = [ - ("example.com", vec!["example.com", "www.example.com"]), - (".example.com", vec!["example.com", "www.example.com"]), - ("www.example.com", vec!["www.example.com"]), - ("1.1.1.1", vec!["1.1.1.1"]), - ("[2001:db8:a::123]", vec!["[2001:db8:a::123]"]), - ]; - let url = |c: &str| ModuleSpecifier::parse(&format!("http://{c}")).unwrap(); - let url_port = - |c: &str| ModuleSpecifier::parse(&format!("http://{c}:8080")).unwrap(); - - // Generate each candidate with and without a port - let candidates = candidates - .into_iter() - .flat_map(|c| [url(c), url_port(c)]) - .collect::>(); - - for (domain, expected_domain) in domains { - // Test without a port -- all candidates return without a port - let auth_domain = AuthDomain::from(domain); - let actual = candidates - .iter() - .filter(|c| auth_domain.matches(c)) - .cloned() - .collect::>(); - let expected = expected_domain.iter().map(|u| url(u)).collect::>(); - assert_eq!(actual, expected); - - // Test with a port, all candidates return with a port - let auth_domain = AuthDomain::from(&format!("{domain}:8080")); - let actual = candidates - .iter() - .filter(|c| auth_domain.matches(c)) - .cloned() - .collect::>(); - let expected = expected_domain - .iter() - .map(|u| url_port(u)) - .collect::>(); - assert_eq!(actual, expected); - } - } -} diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index e3e242e975..31968be0c2 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -1,18 +1,19 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use crate::args::jsr_url; -use crate::args::CacheSetting; -use crate::errors::get_error_class_name; +use crate::file_fetcher::CliFetchNoFollowErrorKind; +use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::FetchNoFollowOptions; -use crate::file_fetcher::FetchOptions; use crate::file_fetcher::FetchPermissionsOptionRef; -use crate::file_fetcher::FileFetcher; -use crate::file_fetcher::FileOrRedirect; use crate::util::fs::atomic_write_file_with_retries; use crate::util::fs::atomic_write_file_with_retries_and_fs; use crate::util::fs::AtomicWriteFileFsAdapter; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::CacheSetting; +use deno_cache_dir::file_fetcher::FetchNoFollowErrorKind; +use deno_cache_dir::file_fetcher::FileOrRedirect; +use deno_core::error::AnyError; use deno_core::futures; use deno_core::futures::FutureExt; use deno_core::ModuleSpecifier; @@ -190,7 +191,7 @@ pub struct FetchCacherOptions { /// a concise interface to the DENO_DIR when building module graphs. pub struct FetchCacher { pub file_header_overrides: HashMap>, - file_fetcher: Arc, + file_fetcher: Arc, fs: Arc, global_http_cache: Arc, in_npm_pkg_checker: Arc, @@ -202,7 +203,7 @@ pub struct FetchCacher { impl FetchCacher { pub fn new( - file_fetcher: Arc, + file_fetcher: Arc, fs: Arc, global_http_cache: Arc, in_npm_pkg_checker: Arc, @@ -320,18 +321,18 @@ impl Loader for FetchCacher { LoaderCacheSetting::Only => Some(CacheSetting::Only), }; file_fetcher - .fetch_no_follow_with_options(FetchNoFollowOptions { - fetch_options: FetchOptions { - specifier: &specifier, - permissions: if is_statically_analyzable { - FetchPermissionsOptionRef::StaticContainer(&permissions) - } else { - FetchPermissionsOptionRef::DynamicContainer(&permissions) - }, - maybe_auth: None, - maybe_accept: None, - maybe_cache_setting: maybe_cache_setting.as_ref(), - }, + .fetch_no_follow( + &specifier, + FetchPermissionsOptionRef::Restricted(&permissions, + if is_statically_analyzable { + deno_runtime::deno_permissions::CheckSpecifierKind::Static + } else { + deno_runtime::deno_permissions::CheckSpecifierKind::Dynamic + }), + FetchNoFollowOptions { + maybe_auth: None, + maybe_accept: None, + maybe_cache_setting: maybe_cache_setting.as_ref(), maybe_checksum: options.maybe_checksum.as_ref(), }) .await @@ -348,7 +349,7 @@ impl Loader for FetchCacher { (None, None) => None, }; Ok(Some(LoadResponse::Module { - specifier: file.specifier, + specifier: file.url, maybe_headers, content: file.source, })) @@ -361,18 +362,46 @@ impl Loader for FetchCacher { } }) .unwrap_or_else(|err| { - if let Some(io_err) = err.downcast_ref::() { - if io_err.kind() == std::io::ErrorKind::NotFound { - return Ok(None); - } else { - return Err(err); - } - } - let error_class_name = get_error_class_name(&err); - match error_class_name { - "NotFound" => Ok(None), - "NotCached" if options.cache_setting == LoaderCacheSetting::Only => Ok(None), - _ => Err(err), + let err = err.into_kind(); + match err { + CliFetchNoFollowErrorKind::FetchNoFollow(err) => { + let err = err.into_kind(); + match err { + FetchNoFollowErrorKind::NotFound(_) => Ok(None), + FetchNoFollowErrorKind::UrlToFilePath { .. } | + FetchNoFollowErrorKind::ReadingBlobUrl { .. } | + FetchNoFollowErrorKind::ReadingFile { .. } | + FetchNoFollowErrorKind::FetchingRemote { .. } | + FetchNoFollowErrorKind::ClientError { .. } | + FetchNoFollowErrorKind::NoRemote { .. } | + FetchNoFollowErrorKind::DataUrlDecode { .. } | + FetchNoFollowErrorKind::RedirectResolution { .. } | + FetchNoFollowErrorKind::CacheRead { .. } | + FetchNoFollowErrorKind::CacheSave { .. } | + FetchNoFollowErrorKind::UnsupportedScheme { .. } | + FetchNoFollowErrorKind::RedirectHeaderParse { .. } | + FetchNoFollowErrorKind::InvalidHeader { .. } => Err(AnyError::from(err)), + FetchNoFollowErrorKind::NotCached { .. } => { + if options.cache_setting == LoaderCacheSetting::Only { + Ok(None) + } else { + Err(AnyError::from(err)) + } + }, + FetchNoFollowErrorKind::ChecksumIntegrity(err) => { + // convert to the equivalent deno_graph error so that it + // enhances it if this is passed to deno_graph + Err( + deno_graph::source::ChecksumIntegrityError { + actual: err.actual, + expected: err.expected, + } + .into(), + ) + } + } + }, + CliFetchNoFollowErrorKind::PermissionCheck(permission_check_error) => Err(AnyError::from(permission_check_error)), } }) } diff --git a/cli/factory.rs b/cli/factory.rs index f08bf7e4b1..d6940d6df1 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -22,7 +22,7 @@ use crate::cache::ModuleInfoCache; use crate::cache::NodeAnalysisCache; use crate::cache::ParsedSourceCache; use crate::emit::Emitter; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::graph_container::MainModuleGraphContainer; use crate::graph_util::FileWatcherReporter; use crate::graph_util::ModuleGraphBuilder; @@ -185,7 +185,7 @@ struct CliFactoryServices { emit_cache: Deferred>, emitter: Deferred>, feature_checker: Deferred>, - file_fetcher: Deferred>, + file_fetcher: Deferred>, fs: Deferred>, global_http_cache: Deferred>, http_cache: Deferred>, @@ -350,16 +350,17 @@ impl CliFactory { }) } - pub fn file_fetcher(&self) -> Result<&Arc, AnyError> { + pub fn file_fetcher(&self) -> Result<&Arc, AnyError> { self.services.file_fetcher.get_or_try_init(|| { let cli_options = self.cli_options()?; - Ok(Arc::new(FileFetcher::new( + Ok(Arc::new(CliFileFetcher::new( self.http_cache()?.clone(), - cli_options.cache_setting(), - !cli_options.no_remote(), self.http_client_provider().clone(), self.blob_store().clone(), Some(self.text_only_progress_bar().clone()), + !cli_options.no_remote(), + cli_options.cache_setting(), + log::Level::Info, ))) }) } diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 29f9c6ba3f..1b286c76b7 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -1,41 +1,45 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::CacheSetting; -use crate::auth_tokens::AuthTokens; use crate::cache::HttpCache; +use crate::cache::RealDenoCacheEnv; use crate::colors; -use crate::http_util::CacheSemantics; -use crate::http_util::FetchOnceArgs; -use crate::http_util::FetchOnceResult; +use crate::http_util::get_response_body_with_progress; use crate::http_util::HttpClientProvider; use crate::util::progress_bar::ProgressBar; +use boxed_error::Boxed; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::AuthTokens; +use deno_cache_dir::file_fetcher::BlobData; +use deno_cache_dir::file_fetcher::CacheSetting; +use deno_cache_dir::file_fetcher::FetchNoFollowError; +use deno_cache_dir::file_fetcher::File; +use deno_cache_dir::file_fetcher::FileFetcherOptions; +use deno_cache_dir::file_fetcher::FileOrRedirect; +use deno_cache_dir::file_fetcher::SendError; +use deno_cache_dir::file_fetcher::SendResponse; +use deno_cache_dir::file_fetcher::TooManyRedirectsError; +use deno_cache_dir::file_fetcher::UnsupportedSchemeError; use deno_core::anyhow::Context; -use deno_core::error::custom_error; -use deno_core::error::generic_error; -use deno_core::error::uri_error; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::url::Url; use deno_core::ModuleSpecifier; +use deno_error::JsError; use deno_graph::source::LoaderChecksum; -use deno_path_util::url_to_file_path; +use deno_runtime::deno_permissions::CheckSpecifierKind; +use deno_runtime::deno_permissions::PermissionCheckError; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_web::BlobStore; use http::header; -use log::debug; +use http::HeaderMap; +use http::StatusCode; use std::borrow::Cow; use std::collections::HashMap; use std::env; -use std::fs; -use std::path::PathBuf; use std::sync::Arc; -use std::time::SystemTime; - -pub const SUPPORTED_SCHEMES: [&str; 5] = - ["data", "blob", "file", "http", "https"]; +use thiserror::Error; #[derive(Debug, Clone, Eq, PartialEq)] pub struct TextDecodedFile { @@ -47,62 +51,19 @@ pub struct TextDecodedFile { pub source: Arc, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum FileOrRedirect { - File(File), - Redirect(ModuleSpecifier), -} - -impl FileOrRedirect { - fn from_deno_cache_entry( - specifier: &ModuleSpecifier, - cache_entry: deno_cache_dir::CacheEntry, - ) -> Result { - if let Some(redirect_to) = cache_entry.metadata.headers.get("location") { - let redirect = specifier.join(redirect_to)?; - Ok(FileOrRedirect::Redirect(redirect)) - } else { - Ok(FileOrRedirect::File(File { - specifier: specifier.clone(), - maybe_headers: Some(cache_entry.metadata.headers), - source: Arc::from(cache_entry.content), - })) - } - } -} - -/// A structure representing a source file. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct File { - /// The _final_ specifier for the file. The requested specifier and the final - /// specifier maybe different for remote files that have been redirected. - pub specifier: ModuleSpecifier, - pub maybe_headers: Option>, - /// The source of the file. - pub source: Arc<[u8]>, -} - -impl File { - pub fn resolve_media_type_and_charset(&self) -> (MediaType, Option<&str>) { - deno_graph::source::resolve_media_type_and_charset_from_headers( - &self.specifier, - self.maybe_headers.as_ref(), - ) - } - +impl TextDecodedFile { /// Decodes the source bytes into a string handling any encoding rules /// for local vs remote files and dealing with the charset. - pub fn into_text_decoded(self) -> Result { - // lots of borrow checker fighting here + pub fn decode(file: File) -> Result { let (media_type, maybe_charset) = deno_graph::source::resolve_media_type_and_charset_from_headers( - &self.specifier, - self.maybe_headers.as_ref(), + &file.url, + file.maybe_headers.as_ref(), ); - let specifier = self.specifier; + let specifier = file.url; match deno_graph::source::decode_source( &specifier, - self.source, + file.source, maybe_charset, ) { Ok(source) => Ok(TextDecodedFile { @@ -117,14 +78,146 @@ impl File { } } -#[derive(Debug, Clone, Default)] -struct MemoryFiles(Arc>>); +#[derive(Debug)] +struct BlobStoreAdapter(Arc); + +#[async_trait::async_trait(?Send)] +impl deno_cache_dir::file_fetcher::BlobStore for BlobStoreAdapter { + async fn get(&self, specifier: &Url) -> std::io::Result> { + let Some(blob) = self.0.get_object_url(specifier.clone()) else { + return Ok(None); + }; + Ok(Some(BlobData { + media_type: blob.media_type.clone(), + bytes: blob.read_all().await, + })) + } +} + +#[derive(Debug)] +struct HttpClientAdapter { + http_client_provider: Arc, + download_log_level: log::Level, + progress_bar: Option, +} + +#[async_trait::async_trait(?Send)] +impl deno_cache_dir::file_fetcher::HttpClient for HttpClientAdapter { + async fn send_no_follow( + &self, + url: &Url, + headers: HeaderMap, + ) -> Result { + async fn handle_request_or_server_error( + retried: &mut bool, + specifier: &Url, + err_str: String, + ) -> Result<(), ()> { + // Retry once, and bail otherwise. + if !*retried { + *retried = true; + log::debug!("Import '{}' failed: {}. Retrying...", specifier, err_str); + tokio::time::sleep(std::time::Duration::from_millis(50)).await; + Ok(()) + } else { + Err(()) + } + } + + let mut maybe_progress_guard = None; + if let Some(pb) = self.progress_bar.as_ref() { + maybe_progress_guard = Some(pb.update(url.as_str())); + } else { + log::log!( + self.download_log_level, + "{} {}", + colors::green("Download"), + url + ); + } + + let mut retried = false; // retry intermittent failures + loop { + let response = match self + .http_client_provider + .get_or_create() + .map_err(|err| SendError::Failed(err.into()))? + .send(url, headers.clone()) + .await + { + Ok(response) => response, + Err(crate::http_util::SendError::Send(err)) => { + if err.is_connect_error() { + handle_request_or_server_error(&mut retried, url, err.to_string()) + .await + .map_err(|()| SendError::Failed(err.into()))?; + continue; + } else { + return Err(SendError::Failed(err.into())); + } + } + Err(crate::http_util::SendError::InvalidUri(err)) => { + return Err(SendError::Failed(err.into())); + } + }; + if response.status() == StatusCode::NOT_MODIFIED { + return Ok(SendResponse::NotModified); + } + + if let Some(warning) = response.headers().get("X-Deno-Warning") { + log::warn!( + "{} {}", + crate::colors::yellow("Warning"), + warning.to_str().unwrap() + ); + } + + if response.status().is_redirection() { + return Ok(SendResponse::Redirect(response.into_parts().0.headers)); + } + + if response.status().is_server_error() { + handle_request_or_server_error( + &mut retried, + url, + response.status().to_string(), + ) + .await + .map_err(|()| SendError::StatusCode(response.status()))?; + } else if response.status().is_client_error() { + let err = if response.status() == StatusCode::NOT_FOUND { + SendError::NotFound + } else { + SendError::StatusCode(response.status()) + }; + return Err(err); + } else { + let body_result = get_response_body_with_progress( + response, + maybe_progress_guard.as_ref(), + ) + .await; + + match body_result { + Ok((headers, body)) => { + return Ok(SendResponse::Success(headers, body)); + } + Err(err) => { + handle_request_or_server_error(&mut retried, url, err.to_string()) + .await + .map_err(|()| SendError::Failed(err.into()))?; + continue; + } + } + } + } + } +} + +#[derive(Debug, Default)] +struct MemoryFiles(Mutex>); impl MemoryFiles { - pub fn get(&self, specifier: &ModuleSpecifier) -> Option { - self.0.lock().get(specifier).cloned() - } - pub fn insert(&self, specifier: ModuleSpecifier, file: File) -> Option { self.0.lock().insert(specifier, file) } @@ -134,416 +227,93 @@ impl MemoryFiles { } } -/// Fetch a source file from the local file system. -fn fetch_local(specifier: &ModuleSpecifier) -> Result { - let local = url_to_file_path(specifier).map_err(|_| { - uri_error(format!("Invalid file path.\n Specifier: {specifier}")) - })?; - // If it doesnt have a extension, we want to treat it as typescript by default - let headers = if local.extension().is_none() { - Some(HashMap::from([( - "content-type".to_string(), - "application/typescript".to_string(), - )])) - } else { - None - }; - let bytes = fs::read(local)?; - - Ok(File { - specifier: specifier.clone(), - maybe_headers: headers, - source: bytes.into(), - }) +impl deno_cache_dir::file_fetcher::MemoryFiles for MemoryFiles { + fn get(&self, specifier: &ModuleSpecifier) -> Option { + self.0.lock().get(specifier).cloned() + } } -/// Return a validated scheme for a given module specifier. -fn get_validated_scheme( - specifier: &ModuleSpecifier, -) -> Result { - let scheme = specifier.scheme(); - if !SUPPORTED_SCHEMES.contains(&scheme) { - // NOTE(bartlomieju): this message list additional `npm` and `jsr` schemes, but they should actually be handled - // before `file_fetcher.rs` APIs are even hit. - let mut all_supported_schemes = SUPPORTED_SCHEMES.to_vec(); - all_supported_schemes.extend_from_slice(&["npm", "jsr"]); - all_supported_schemes.sort(); - let scheme_list = all_supported_schemes - .iter() - .map(|scheme| format!(" - \"{}\"", scheme)) - .collect::>() - .join("\n"); - Err(generic_error(format!( - "Unsupported scheme \"{scheme}\" for module \"{specifier}\". Supported schemes:\n{}", - scheme_list - ))) - } else { - Ok(scheme.to_string()) - } +#[derive(Debug, Boxed, JsError)] +pub struct CliFetchNoFollowError(pub Box); + +#[derive(Debug, Error, JsError)] +pub enum CliFetchNoFollowErrorKind { + #[error(transparent)] + #[class(inherit)] + FetchNoFollow(#[from] FetchNoFollowError), + #[error(transparent)] + #[class(generic)] + PermissionCheck(#[from] PermissionCheckError), } #[derive(Debug, Copy, Clone)] pub enum FetchPermissionsOptionRef<'a> { AllowAll, - DynamicContainer(&'a PermissionsContainer), - StaticContainer(&'a PermissionsContainer), + Restricted(&'a PermissionsContainer, CheckSpecifierKind), } +#[derive(Debug, Default)] pub struct FetchOptions<'a> { - pub specifier: &'a ModuleSpecifier, - pub permissions: FetchPermissionsOptionRef<'a>, pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, pub maybe_accept: Option<&'a str>, pub maybe_cache_setting: Option<&'a CacheSetting>, } pub struct FetchNoFollowOptions<'a> { - pub fetch_options: FetchOptions<'a>, - /// This setting doesn't make sense to provide for `FetchOptions` - /// since the required checksum may change for a redirect. + pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, + pub maybe_accept: Option<&'a str>, + pub maybe_cache_setting: Option<&'a CacheSetting>, pub maybe_checksum: Option<&'a LoaderChecksum>, } +type DenoCacheDirFileFetcher = deno_cache_dir::file_fetcher::FileFetcher< + BlobStoreAdapter, + RealDenoCacheEnv, + HttpClientAdapter, +>; + /// A structure for resolving, fetching and caching source files. #[derive(Debug)] -pub struct FileFetcher { - auth_tokens: AuthTokens, - allow_remote: bool, - memory_files: MemoryFiles, - cache_setting: CacheSetting, - http_cache: Arc, - http_client_provider: Arc, - blob_store: Arc, - download_log_level: log::Level, - progress_bar: Option, +pub struct CliFileFetcher { + file_fetcher: DenoCacheDirFileFetcher, + memory_files: Arc, } -impl FileFetcher { +impl CliFileFetcher { pub fn new( http_cache: Arc, - cache_setting: CacheSetting, - allow_remote: bool, http_client_provider: Arc, blob_store: Arc, progress_bar: Option, + allow_remote: bool, + cache_setting: CacheSetting, + download_log_level: log::Level, ) -> Self { - Self { - auth_tokens: AuthTokens::new(env::var("DENO_AUTH_TOKENS").ok()), - allow_remote, - memory_files: Default::default(), - cache_setting, + let memory_files = Arc::new(MemoryFiles::default()); + let file_fetcher = DenoCacheDirFileFetcher::new( + BlobStoreAdapter(blob_store), + RealDenoCacheEnv, http_cache, - http_client_provider, - blob_store, - download_log_level: log::Level::Info, - progress_bar, + HttpClientAdapter { + http_client_provider: http_client_provider.clone(), + download_log_level, + progress_bar, + }, + memory_files.clone(), + FileFetcherOptions { + allow_remote, + cache_setting, + auth_tokens: AuthTokens::new(env::var("DENO_AUTH_TOKENS").ok()), + }, + ); + Self { + file_fetcher, + memory_files, } } pub fn cache_setting(&self) -> &CacheSetting { - &self.cache_setting - } - - /// Sets the log level to use when outputting the download message. - pub fn set_download_log_level(&mut self, level: log::Level) { - self.download_log_level = level; - } - - /// Fetch cached remote file. - /// - /// This is a recursive operation if source file has redirections. - pub fn fetch_cached( - &self, - specifier: &ModuleSpecifier, - redirect_limit: i64, - ) -> Result, AnyError> { - let mut specifier = Cow::Borrowed(specifier); - for _ in 0..=redirect_limit { - match self.fetch_cached_no_follow(&specifier, None)? { - Some(FileOrRedirect::File(file)) => { - return Ok(Some(file)); - } - Some(FileOrRedirect::Redirect(redirect_specifier)) => { - specifier = Cow::Owned(redirect_specifier); - } - None => { - return Ok(None); - } - } - } - Err(custom_error("Http", "Too many redirects.")) - } - - fn fetch_cached_no_follow( - &self, - specifier: &ModuleSpecifier, - maybe_checksum: Option<&LoaderChecksum>, - ) -> Result, AnyError> { - debug!( - "FileFetcher::fetch_cached_no_follow - specifier: {}", - specifier - ); - - let cache_key = self.http_cache.cache_item_key(specifier)?; // compute this once - let result = self.http_cache.get( - &cache_key, - maybe_checksum - .as_ref() - .map(|c| deno_cache_dir::Checksum::new(c.as_str())), - ); - match result { - Ok(Some(cache_data)) => Ok(Some(FileOrRedirect::from_deno_cache_entry( - specifier, cache_data, - )?)), - Ok(None) => Ok(None), - Err(err) => match err { - deno_cache_dir::CacheReadFileError::Io(err) => Err(err.into()), - deno_cache_dir::CacheReadFileError::ChecksumIntegrity(err) => { - // convert to the equivalent deno_graph error so that it - // enhances it if this is passed to deno_graph - Err( - deno_graph::source::ChecksumIntegrityError { - actual: err.actual, - expected: err.expected, - } - .into(), - ) - } - }, - } - } - - /// Convert a data URL into a file, resulting in an error if the URL is - /// invalid. - fn fetch_data_url( - &self, - specifier: &ModuleSpecifier, - ) -> Result { - debug!("FileFetcher::fetch_data_url() - specifier: {}", specifier); - let data_url = deno_graph::source::RawDataUrl::parse(specifier)?; - let (bytes, headers) = data_url.into_bytes_and_headers(); - Ok(File { - specifier: specifier.clone(), - maybe_headers: Some(headers), - source: Arc::from(bytes), - }) - } - - /// Get a blob URL. - async fn fetch_blob_url( - &self, - specifier: &ModuleSpecifier, - ) -> Result { - debug!("FileFetcher::fetch_blob_url() - specifier: {}", specifier); - let blob = self - .blob_store - .get_object_url(specifier.clone()) - .ok_or_else(|| { - custom_error( - "NotFound", - format!("Blob URL not found: \"{specifier}\"."), - ) - })?; - - let bytes = blob.read_all().await; - let headers = - HashMap::from([("content-type".to_string(), blob.media_type.clone())]); - - Ok(File { - specifier: specifier.clone(), - maybe_headers: Some(headers), - source: Arc::from(bytes), - }) - } - - async fn fetch_remote_no_follow( - &self, - specifier: &ModuleSpecifier, - maybe_accept: Option<&str>, - cache_setting: &CacheSetting, - maybe_checksum: Option<&LoaderChecksum>, - maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, - ) -> Result { - debug!( - "FileFetcher::fetch_remote_no_follow - specifier: {}", - specifier - ); - - if self.should_use_cache(specifier, cache_setting) { - if let Some(file_or_redirect) = - self.fetch_cached_no_follow(specifier, maybe_checksum)? - { - return Ok(file_or_redirect); - } - } - - if *cache_setting == CacheSetting::Only { - return Err(custom_error( - "NotCached", - format!( - "Specifier not found in cache: \"{specifier}\", --cached-only is specified." - ), - )); - } - - let mut maybe_progress_guard = None; - if let Some(pb) = self.progress_bar.as_ref() { - maybe_progress_guard = Some(pb.update(specifier.as_str())); - } else { - log::log!( - self.download_log_level, - "{} {}", - colors::green("Download"), - specifier - ); - } - - let maybe_etag_cache_entry = self - .http_cache - .cache_item_key(specifier) - .ok() - .and_then(|key| { - self - .http_cache - .get( - &key, - maybe_checksum - .as_ref() - .map(|c| deno_cache_dir::Checksum::new(c.as_str())), - ) - .ok() - .flatten() - }) - .and_then(|cache_entry| { - cache_entry - .metadata - .headers - .get("etag") - .cloned() - .map(|etag| (cache_entry, etag)) - }); - let maybe_auth_token = self.auth_tokens.get(specifier); - - async fn handle_request_or_server_error( - retried: &mut bool, - specifier: &Url, - err_str: String, - ) -> Result<(), AnyError> { - // Retry once, and bail otherwise. - if !*retried { - *retried = true; - log::debug!("Import '{}' failed: {}. Retrying...", specifier, err_str); - tokio::time::sleep(std::time::Duration::from_millis(50)).await; - Ok(()) - } else { - Err(generic_error(format!( - "Import '{}' failed: {}", - specifier, err_str - ))) - } - } - - let mut retried = false; // retry intermittent failures - let result = loop { - let result = match self - .http_client_provider - .get_or_create()? - .fetch_no_follow(FetchOnceArgs { - url: specifier.clone(), - maybe_accept: maybe_accept.map(ToOwned::to_owned), - maybe_etag: maybe_etag_cache_entry - .as_ref() - .map(|(_, etag)| etag.clone()), - maybe_auth_token: maybe_auth_token.clone(), - maybe_auth: maybe_auth.clone(), - maybe_progress_guard: maybe_progress_guard.as_ref(), - }) - .await? - { - FetchOnceResult::NotModified => { - let (cache_entry, _) = maybe_etag_cache_entry.unwrap(); - FileOrRedirect::from_deno_cache_entry(specifier, cache_entry) - } - FetchOnceResult::Redirect(redirect_url, headers) => { - self.http_cache.set(specifier, headers, &[])?; - Ok(FileOrRedirect::Redirect(redirect_url)) - } - FetchOnceResult::Code(bytes, headers) => { - self.http_cache.set(specifier, headers.clone(), &bytes)?; - if let Some(checksum) = &maybe_checksum { - checksum.check_source(&bytes)?; - } - Ok(FileOrRedirect::File(File { - specifier: specifier.clone(), - maybe_headers: Some(headers), - source: Arc::from(bytes), - })) - } - FetchOnceResult::RequestError(err) => { - handle_request_or_server_error(&mut retried, specifier, err).await?; - continue; - } - FetchOnceResult::ServerError(status) => { - handle_request_or_server_error( - &mut retried, - specifier, - status.to_string(), - ) - .await?; - continue; - } - }; - break result; - }; - - drop(maybe_progress_guard); - result - } - - /// Returns if the cache should be used for a given specifier. - fn should_use_cache( - &self, - specifier: &ModuleSpecifier, - cache_setting: &CacheSetting, - ) -> bool { - match cache_setting { - CacheSetting::ReloadAll => false, - CacheSetting::Use | CacheSetting::Only => true, - CacheSetting::RespectHeaders => { - let Ok(cache_key) = self.http_cache.cache_item_key(specifier) else { - return false; - }; - let Ok(Some(headers)) = self.http_cache.read_headers(&cache_key) else { - return false; - }; - let Ok(Some(download_time)) = - self.http_cache.read_download_time(&cache_key) - else { - return false; - }; - let cache_semantics = - CacheSemantics::new(headers, download_time, SystemTime::now()); - cache_semantics.should_use() - } - CacheSetting::ReloadSome(list) => { - let mut url = specifier.clone(); - url.set_fragment(None); - if list.iter().any(|x| x == url.as_str()) { - return false; - } - url.set_query(None); - let mut path = PathBuf::from(url.as_str()); - loop { - if list.contains(&path.to_str().unwrap().to_string()) { - return false; - } - if !path.pop() { - break; - } - } - true - } - } + self.file_fetcher.cache_setting() } #[inline(always)] @@ -578,7 +348,10 @@ impl FileFetcher { .fetch_inner( specifier, None, - FetchPermissionsOptionRef::StaticContainer(permissions), + FetchPermissionsOptionRef::Restricted( + permissions, + CheckSpecifierKind::Static, + ), ) .await } @@ -590,42 +363,50 @@ impl FileFetcher { permissions: FetchPermissionsOptionRef<'_>, ) -> Result { self - .fetch_with_options(FetchOptions { + .fetch_with_options( specifier, permissions, - maybe_auth, - maybe_accept: None, - maybe_cache_setting: None, - }) + FetchOptions { + maybe_auth, + maybe_accept: None, + maybe_cache_setting: None, + }, + ) .await } pub async fn fetch_with_options( &self, + specifier: &ModuleSpecifier, + permissions: FetchPermissionsOptionRef<'_>, options: FetchOptions<'_>, ) -> Result { - self.fetch_with_options_and_max_redirect(options, 10).await + self + .fetch_with_options_and_max_redirect(specifier, permissions, options, 10) + .await } async fn fetch_with_options_and_max_redirect( &self, + specifier: &ModuleSpecifier, + permissions: FetchPermissionsOptionRef<'_>, options: FetchOptions<'_>, max_redirect: usize, ) -> Result { - let mut specifier = Cow::Borrowed(options.specifier); - let mut maybe_auth = options.maybe_auth.clone(); + let mut specifier = Cow::Borrowed(specifier); + let mut maybe_auth = options.maybe_auth; for _ in 0..=max_redirect { match self - .fetch_no_follow_with_options(FetchNoFollowOptions { - fetch_options: FetchOptions { - specifier: &specifier, - permissions: options.permissions, + .fetch_no_follow( + &specifier, + permissions, + FetchNoFollowOptions { maybe_auth: maybe_auth.clone(), maybe_accept: options.maybe_accept, maybe_cache_setting: options.maybe_cache_setting, + maybe_checksum: None, }, - maybe_checksum: None, - }) + ) .await? { FileOrRedirect::File(file) => { @@ -641,92 +422,61 @@ impl FileFetcher { } } - Err(custom_error("Http", "Too many redirects.")) + Err(TooManyRedirectsError(specifier.into_owned()).into()) } /// Fetches without following redirects. - pub async fn fetch_no_follow_with_options( + pub async fn fetch_no_follow( &self, + specifier: &ModuleSpecifier, + permissions: FetchPermissionsOptionRef<'_>, options: FetchNoFollowOptions<'_>, - ) -> Result { - let maybe_checksum = options.maybe_checksum; - let options = options.fetch_options; - let specifier = options.specifier; - // note: this debug output is used by the tests - debug!( - "FileFetcher::fetch_no_follow_with_options - specifier: {}", - specifier - ); - let scheme = get_validated_scheme(specifier)?; - match options.permissions { + ) -> Result { + validate_scheme(specifier).map_err(|err| { + CliFetchNoFollowErrorKind::FetchNoFollow(err.into()).into_box() + })?; + match permissions { FetchPermissionsOptionRef::AllowAll => { // allow } - FetchPermissionsOptionRef::StaticContainer(permissions) => { - permissions.check_specifier( - specifier, - deno_runtime::deno_permissions::CheckSpecifierKind::Static, - )?; - } - FetchPermissionsOptionRef::DynamicContainer(permissions) => { - permissions.check_specifier( - specifier, - deno_runtime::deno_permissions::CheckSpecifierKind::Dynamic, - )?; + FetchPermissionsOptionRef::Restricted(permissions, kind) => { + permissions.check_specifier(specifier, kind)?; } } - if let Some(file) = self.memory_files.get(specifier) { - Ok(FileOrRedirect::File(file)) - } else if scheme == "file" { - // we do not in memory cache files, as this would prevent files on the - // disk changing effecting things like workers and dynamic imports. - fetch_local(specifier).map(FileOrRedirect::File) - } else if scheme == "data" { - self.fetch_data_url(specifier).map(FileOrRedirect::File) - } else if scheme == "blob" { - self - .fetch_blob_url(specifier) - .await - .map(FileOrRedirect::File) - } else if !self.allow_remote { - Err(custom_error( - "NoRemote", - format!("A remote specifier was requested: \"{specifier}\", but --no-remote is specified."), - )) - } else { - self - .fetch_remote_no_follow( - specifier, - options.maybe_accept, - options.maybe_cache_setting.unwrap_or(&self.cache_setting), - maybe_checksum, - options.maybe_auth, - ) - .await - } + self + .file_fetcher + .fetch_no_follow( + specifier, + deno_cache_dir::file_fetcher::FetchNoFollowOptions { + maybe_auth: options.maybe_auth, + maybe_checksum: options + .maybe_checksum + .map(|c| deno_cache_dir::Checksum::new(c.as_str())), + maybe_accept: options.maybe_accept, + maybe_cache_setting: options.maybe_cache_setting, + }, + ) + .await + .map_err(|err| CliFetchNoFollowErrorKind::FetchNoFollow(err).into_box()) } /// A synchronous way to retrieve a source file, where if the file has already /// been cached in memory it will be returned, otherwise for local files will /// be read from disk. - pub fn get_source(&self, specifier: &ModuleSpecifier) -> Option { - let maybe_file = self.memory_files.get(specifier); - if maybe_file.is_none() { - let is_local = specifier.scheme() == "file"; - if is_local { - if let Ok(file) = fetch_local(specifier) { - return Some(file); - } - } - None + pub fn get_cached_source_or_local( + &self, + specifier: &ModuleSpecifier, + ) -> Result, AnyError> { + if specifier.scheme() == "file" { + Ok(self.file_fetcher.fetch_local(specifier)?) } else { - maybe_file + Ok(self.file_fetcher.fetch_cached(specifier, 10)?) } } /// Insert a temporary module for the file fetcher. pub fn insert_memory_files(&self, file: File) -> Option { - self.memory_files.insert(file.specifier.clone(), file) + self.memory_files.insert(file.url.clone(), file) } pub fn clear_memory_files(&self) { @@ -734,6 +484,16 @@ impl FileFetcher { } } +fn validate_scheme(specifier: &Url) -> Result<(), UnsupportedSchemeError> { + match deno_cache_dir::file_fetcher::is_valid_scheme(specifier.scheme()) { + true => Ok(()), + false => Err(UnsupportedSchemeError { + scheme: specifier.scheme().to_string(), + url: specifier.clone(), + }), + } +} + #[cfg(test)] mod tests { use crate::cache::GlobalHttpCache; @@ -741,7 +501,8 @@ mod tests { use crate::http_util::HttpClientProvider; use super::*; - use deno_core::error::get_custom_error_class; + use deno_cache_dir::file_fetcher::FetchNoFollowErrorKind; + use deno_cache_dir::file_fetcher::HttpClient; use deno_core::resolve_url; use deno_runtime::deno_web::Blob; use deno_runtime::deno_web::InMemoryBlobPart; @@ -750,7 +511,7 @@ mod tests { fn setup( cache_setting: CacheSetting, maybe_temp_dir: Option, - ) -> (FileFetcher, TempDir) { + ) -> (CliFileFetcher, TempDir) { let (file_fetcher, temp_dir, _) = setup_with_blob_store(cache_setting, maybe_temp_dir); (file_fetcher, temp_dir) @@ -759,22 +520,38 @@ mod tests { fn setup_with_blob_store( cache_setting: CacheSetting, maybe_temp_dir: Option, - ) -> (FileFetcher, TempDir, Arc) { - let temp_dir = maybe_temp_dir.unwrap_or_default(); - let location = temp_dir.path().join("remote").to_path_buf(); - let blob_store: Arc = Default::default(); - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)), - cache_setting, - true, - Arc::new(HttpClientProvider::new(None, None)), - blob_store.clone(), - None, - ); + ) -> (CliFileFetcher, TempDir, Arc) { + let (file_fetcher, temp_dir, blob_store, _) = + setup_with_blob_store_and_cache(cache_setting, maybe_temp_dir); (file_fetcher, temp_dir, blob_store) } - async fn test_fetch(specifier: &ModuleSpecifier) -> (File, FileFetcher) { + fn setup_with_blob_store_and_cache( + cache_setting: CacheSetting, + maybe_temp_dir: Option, + ) -> ( + CliFileFetcher, + TempDir, + Arc, + Arc, + ) { + let temp_dir = maybe_temp_dir.unwrap_or_default(); + let location = temp_dir.path().join("remote").to_path_buf(); + let blob_store: Arc = Default::default(); + let cache = Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)); + let file_fetcher = CliFileFetcher::new( + cache.clone(), + Arc::new(HttpClientProvider::new(None, None)), + blob_store.clone(), + None, + true, + cache_setting, + log::Level::Info, + ); + (file_fetcher, temp_dir, blob_store, cache) + } + + async fn test_fetch(specifier: &ModuleSpecifier) -> (File, CliFileFetcher) { let (file_fetcher, _) = setup(CacheSetting::ReloadAll, None); let result = file_fetcher.fetch_bypass_permissions(specifier).await; assert!(result.is_ok()); @@ -785,27 +562,20 @@ mod tests { specifier: &ModuleSpecifier, ) -> (File, HashMap) { let _http_server_guard = test_util::http_server(); - let (file_fetcher, _) = setup(CacheSetting::ReloadAll, None); + let (file_fetcher, _, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::ReloadAll, None); let result: Result = file_fetcher .fetch_with_options_and_max_redirect( - FetchOptions { - specifier, - permissions: FetchPermissionsOptionRef::AllowAll, - maybe_auth: None, - maybe_accept: None, - maybe_cache_setting: Some(&file_fetcher.cache_setting), - }, + specifier, + FetchPermissionsOptionRef::AllowAll, + Default::default(), 1, ) .await; - let cache_key = file_fetcher.http_cache.cache_item_key(specifier).unwrap(); + let cache_key = http_cache.cache_item_key(specifier).unwrap(); ( result.unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), ) } @@ -850,28 +620,6 @@ mod tests { ); } - #[test] - fn test_get_validated_scheme() { - let fixtures = vec![ - ("https://deno.land/x/mod.ts", true, "https"), - ("http://deno.land/x/mod.ts", true, "http"), - ("file:///a/b/c.ts", true, "file"), - ("file:///C:/a/b/c.ts", true, "file"), - ("data:,some%20text", true, "data"), - ("ftp://a/b/c.ts", false, ""), - ("mailto:dino@deno.land", false, ""), - ]; - - for (specifier, is_ok, expected) in fixtures { - let specifier = ModuleSpecifier::parse(specifier).unwrap(); - let actual = get_validated_scheme(&specifier); - assert_eq!(actual.is_ok(), is_ok); - if is_ok { - assert_eq!(actual.unwrap(), expected); - } - } - } - #[tokio::test] async fn test_insert_cached() { let (file_fetcher, temp_dir) = setup(CacheSetting::Use, None); @@ -879,7 +627,7 @@ mod tests { let specifier = ModuleSpecifier::from_file_path(&local).unwrap(); let file = File { source: Arc::from("some source code".as_bytes()), - specifier: specifier.clone(), + url: specifier.clone(), maybe_headers: Some(HashMap::from([( "content-type".to_string(), "application/javascript".to_string(), @@ -900,7 +648,7 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export const a = \"a\";\n\nexport enum A {\n A,\n B,\n C,\n}\n" @@ -929,7 +677,7 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export const a = \"a\";\n\nexport enum A {\n A,\n B,\n C,\n}\n" @@ -941,33 +689,36 @@ mod tests { #[tokio::test] async fn test_fetch_complex() { let _http_server_guard = test_util::http_server(); - let (file_fetcher, temp_dir) = setup(CacheSetting::Use, None); + let (file_fetcher, temp_dir, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::Use, None); let (file_fetcher_01, _) = setup(CacheSetting::Use, Some(temp_dir.clone())); - let (file_fetcher_02, _) = setup(CacheSetting::Use, Some(temp_dir.clone())); + let (file_fetcher_02, _, _, http_cache_02) = + setup_with_blob_store_and_cache( + CacheSetting::Use, + Some(temp_dir.clone()), + ); let specifier = ModuleSpecifier::parse("http://localhost:4545/subdir/mod2.ts").unwrap(); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export { printHello } from \"./print_hello.ts\";\n" ); assert_eq!(file.media_type, MediaType::TypeScript); - let cache_item_key = - file_fetcher.http_cache.cache_item_key(&specifier).unwrap(); + let cache_item_key = http_cache.cache_item_key(&specifier).unwrap(); let mut headers = HashMap::new(); headers.insert("content-type".to_string(), "text/javascript".to_string()); - file_fetcher - .http_cache + http_cache .set(&specifier, headers.clone(), file.source.as_bytes()) .unwrap(); let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export { printHello } from \"./print_hello.ts\";\n" @@ -976,22 +727,20 @@ mod tests { // the value above. assert_eq!(file.media_type, MediaType::JavaScript); - let headers2 = file_fetcher_02 - .http_cache + let headers2 = http_cache_02 .read_headers(&cache_item_key) .unwrap() .unwrap(); assert_eq!(headers2.get("content-type").unwrap(), "text/javascript"); headers = HashMap::new(); headers.insert("content-type".to_string(), "application/json".to_string()); - file_fetcher_02 - .http_cache + http_cache_02 .set(&specifier, headers.clone(), file.source.as_bytes()) .unwrap(); let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export { printHello } from \"./print_hello.ts\";\n" @@ -1001,20 +750,21 @@ mod tests { // This creates a totally new instance, simulating another Deno process // invocation and indicates to "cache bust". let location = temp_dir.path().join("remote").to_path_buf(); - let file_fetcher = FileFetcher::new( + let file_fetcher = CliFileFetcher::new( Arc::new(GlobalHttpCache::new( location, crate::cache::RealDenoCacheEnv, )), - CacheSetting::ReloadAll, - true, Arc::new(HttpClientProvider::new(None, None)), Default::default(), None, + true, + CacheSetting::ReloadAll, + log::Level::Info, ); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!( &*file.source, "export { printHello } from \"./print_hello.ts\";\n" @@ -1030,73 +780,52 @@ mod tests { let specifier = resolve_url("http://localhost:4545/subdir/mismatch_ext.ts").unwrap(); + let http_cache = Arc::new(GlobalHttpCache::new( + location.clone(), + crate::cache::RealDenoCacheEnv, + )); let file_modified_01 = { - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new( - location.clone(), - crate::cache::RealDenoCacheEnv, - )), - CacheSetting::Use, - true, + let file_fetcher = CliFileFetcher::new( + http_cache.clone(), Arc::new(HttpClientProvider::new(None, None)), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let cache_key = - file_fetcher.http_cache.cache_item_key(&specifier).unwrap(); + let cache_key = http_cache.cache_item_key(&specifier).unwrap(); ( - file_fetcher - .http_cache - .read_modified_time(&cache_key) - .unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), - file_fetcher - .http_cache - .read_download_time(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_modified_time(&cache_key).unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), + http_cache.read_download_time(&cache_key).unwrap().unwrap(), ) }; let file_modified_02 = { - let file_fetcher = FileFetcher::new( + let file_fetcher = CliFileFetcher::new( Arc::new(GlobalHttpCache::new( location, crate::cache::RealDenoCacheEnv, )), - CacheSetting::Use, - true, Arc::new(HttpClientProvider::new(None, None)), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let cache_key = - file_fetcher.http_cache.cache_item_key(&specifier).unwrap(); + let cache_key = http_cache.cache_item_key(&specifier).unwrap(); ( - file_fetcher - .http_cache - .read_modified_time(&cache_key) - .unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), - file_fetcher - .http_cache - .read_download_time(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_modified_time(&cache_key).unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), + http_cache.read_download_time(&cache_key).unwrap().unwrap(), ) }; @@ -1106,7 +835,8 @@ mod tests { #[tokio::test] async fn test_fetch_redirected() { let _http_server_guard = test_util::http_server(); - let (file_fetcher, _) = setup(CacheSetting::Use, None); + let (file_fetcher, _, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::Use, None); let specifier = resolve_url("http://localhost:4546/subdir/redirects/redirect1.js") .unwrap(); @@ -1117,24 +847,27 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); let file = result.unwrap(); - assert_eq!(file.specifier, redirected_specifier); + assert_eq!(file.url, redirected_specifier); assert_eq!( - get_text_from_cache(&file_fetcher, &specifier), + get_text_from_cache(http_cache.as_ref(), &specifier), "", "redirected files should have empty cached contents" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &specifier), + get_location_header_from_cache(http_cache.as_ref(), &specifier), Some("http://localhost:4545/subdir/redirects/redirect1.js".to_string()), ); assert_eq!( - get_text_from_cache(&file_fetcher, &redirected_specifier), + get_text_from_cache(http_cache.as_ref(), &redirected_specifier), "export const redirect = 1;\n" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &redirected_specifier), + get_location_header_from_cache( + http_cache.as_ref(), + &redirected_specifier + ), None, ); } @@ -1142,7 +875,8 @@ mod tests { #[tokio::test] async fn test_fetch_multiple_redirects() { let _http_server_guard = test_util::http_server(); - let (file_fetcher, _) = setup(CacheSetting::Use, None); + let (file_fetcher, _, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::Use, None); let specifier = resolve_url("http://localhost:4548/subdir/redirects/redirect1.js") .unwrap(); @@ -1156,34 +890,40 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); let file = result.unwrap(); - assert_eq!(file.specifier, redirected_02_specifier); + assert_eq!(file.url, redirected_02_specifier); assert_eq!( - get_text_from_cache(&file_fetcher, &specifier), + get_text_from_cache(http_cache.as_ref(), &specifier), "", "redirected files should have empty cached contents" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &specifier), + get_location_header_from_cache(http_cache.as_ref(), &specifier), Some("http://localhost:4546/subdir/redirects/redirect1.js".to_string()), ); assert_eq!( - get_text_from_cache(&file_fetcher, &redirected_01_specifier), + get_text_from_cache(http_cache.as_ref(), &redirected_01_specifier), "", "redirected files should have empty cached contents" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &redirected_01_specifier), + get_location_header_from_cache( + http_cache.as_ref(), + &redirected_01_specifier + ), Some("http://localhost:4545/subdir/redirects/redirect1.js".to_string()), ); assert_eq!( - get_text_from_cache(&file_fetcher, &redirected_02_specifier), + get_text_from_cache(http_cache.as_ref(), &redirected_02_specifier), "export const redirect = 1;\n" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &redirected_02_specifier), + get_location_header_from_cache( + http_cache.as_ref(), + &redirected_02_specifier + ), None, ); } @@ -1197,81 +937,53 @@ mod tests { resolve_url("http://localhost:4548/subdir/mismatch_ext.ts").unwrap(); let redirected_specifier = resolve_url("http://localhost:4546/subdir/mismatch_ext.ts").unwrap(); + let http_cache = Arc::new(GlobalHttpCache::new( + location.clone(), + crate::cache::RealDenoCacheEnv, + )); let metadata_file_modified_01 = { - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new( - location.clone(), - crate::cache::RealDenoCacheEnv, - )), - CacheSetting::Use, - true, + let file_fetcher = CliFileFetcher::new( + http_cache.clone(), Arc::new(HttpClientProvider::new(None, None)), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let cache_key = file_fetcher - .http_cache - .cache_item_key(&redirected_specifier) - .unwrap(); + let cache_key = http_cache.cache_item_key(&redirected_specifier).unwrap(); ( - file_fetcher - .http_cache - .read_modified_time(&cache_key) - .unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), - file_fetcher - .http_cache - .read_download_time(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_modified_time(&cache_key).unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), + http_cache.read_download_time(&cache_key).unwrap().unwrap(), ) }; let metadata_file_modified_02 = { - let file_fetcher = FileFetcher::new( - Arc::new(GlobalHttpCache::new( - location, - crate::cache::RealDenoCacheEnv, - )), - CacheSetting::Use, - true, + let file_fetcher = CliFileFetcher::new( + http_cache.clone(), Arc::new(HttpClientProvider::new(None, None)), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let result = file_fetcher .fetch_bypass_permissions(&redirected_specifier) .await; assert!(result.is_ok()); - let cache_key = file_fetcher - .http_cache - .cache_item_key(&redirected_specifier) - .unwrap(); + let cache_key = http_cache.cache_item_key(&redirected_specifier).unwrap(); ( - file_fetcher - .http_cache - .read_modified_time(&cache_key) - .unwrap(), - file_fetcher - .http_cache - .read_headers(&cache_key) - .unwrap() - .unwrap(), - file_fetcher - .http_cache - .read_download_time(&cache_key) - .unwrap() - .unwrap(), + http_cache.read_modified_time(&cache_key).unwrap(), + http_cache.read_headers(&cache_key).unwrap().unwrap(), + http_cache.read_download_time(&cache_key).unwrap().unwrap(), ) }; @@ -1288,13 +1000,9 @@ mod tests { let result = file_fetcher .fetch_with_options_and_max_redirect( - FetchOptions { - specifier: &specifier, - permissions: FetchPermissionsOptionRef::AllowAll, - maybe_auth: None, - maybe_accept: None, - maybe_cache_setting: Some(&file_fetcher.cache_setting), - }, + &specifier, + FetchPermissionsOptionRef::AllowAll, + Default::default(), 2, ) .await; @@ -1302,29 +1010,26 @@ mod tests { let result = file_fetcher .fetch_with_options_and_max_redirect( - FetchOptions { - specifier: &specifier, - permissions: FetchPermissionsOptionRef::AllowAll, - maybe_auth: None, - maybe_accept: None, - maybe_cache_setting: Some(&file_fetcher.cache_setting), - }, + &specifier, + FetchPermissionsOptionRef::AllowAll, + Default::default(), 1, ) .await; assert!(result.is_err()); - let result = file_fetcher.fetch_cached(&specifier, 2); + let result = file_fetcher.file_fetcher.fetch_cached(&specifier, 2); assert!(result.is_ok()); - let result = file_fetcher.fetch_cached(&specifier, 1); + let result = file_fetcher.file_fetcher.fetch_cached(&specifier, 1); assert!(result.is_err()); } #[tokio::test] async fn test_fetch_same_host_redirect() { let _http_server_guard = test_util::http_server(); - let (file_fetcher, _) = setup(CacheSetting::Use, None); + let (file_fetcher, _, _, http_cache) = + setup_with_blob_store_and_cache(CacheSetting::Use, None); let specifier = resolve_url( "http://localhost:4550/REDIRECT/subdir/redirects/redirect1.js", ) @@ -1336,24 +1041,27 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); let file = result.unwrap(); - assert_eq!(file.specifier, redirected_specifier); + assert_eq!(file.url, redirected_specifier); assert_eq!( - get_text_from_cache(&file_fetcher, &specifier), + get_text_from_cache(http_cache.as_ref(), &specifier), "", "redirected files should have empty cached contents" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &specifier), + get_location_header_from_cache(http_cache.as_ref(), &specifier), Some("/subdir/redirects/redirect1.js".to_string()), ); assert_eq!( - get_text_from_cache(&file_fetcher, &redirected_specifier), + get_text_from_cache(http_cache.as_ref(), &redirected_specifier), "export const redirect = 1;\n" ); assert_eq!( - get_location_header_from_cache(&file_fetcher, &redirected_specifier), + get_location_header_from_cache( + http_cache.as_ref(), + &redirected_specifier + ), None ); } @@ -1363,16 +1071,17 @@ mod tests { let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new(); let location = temp_dir.path().join("remote").to_path_buf(); - let file_fetcher = FileFetcher::new( + let file_fetcher = CliFileFetcher::new( Arc::new(GlobalHttpCache::new( location, crate::cache::RealDenoCacheEnv, )), - CacheSetting::Use, - false, Arc::new(HttpClientProvider::new(None, None)), Default::default(), None, + false, + CacheSetting::Use, + log::Level::Info, ); let specifier = resolve_url("http://localhost:4545/run/002_hello.ts").unwrap(); @@ -1380,8 +1089,19 @@ mod tests { let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_err()); let err = result.unwrap_err(); - assert_eq!(get_custom_error_class(&err), Some("NoRemote")); - assert_eq!(err.to_string(), "A remote specifier was requested: \"http://localhost:4545/run/002_hello.ts\", but --no-remote is specified."); + let err = err.downcast::().unwrap().into_kind(); + match err { + CliFetchNoFollowErrorKind::FetchNoFollow(err) => { + let err = err.into_kind(); + match &err { + FetchNoFollowErrorKind::NoRemote { .. } => { + assert_eq!(err.to_string(), "A remote specifier was requested: \"http://localhost:4545/run/002_hello.ts\", but --no-remote is specified."); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } } #[tokio::test] @@ -1389,21 +1109,23 @@ mod tests { let _http_server_guard = test_util::http_server(); let temp_dir = TempDir::new(); let location = temp_dir.path().join("remote").to_path_buf(); - let file_fetcher_01 = FileFetcher::new( + let file_fetcher_01 = CliFileFetcher::new( Arc::new(GlobalHttpCache::new(location.clone(), RealDenoCacheEnv)), + Arc::new(HttpClientProvider::new(None, None)), + Default::default(), + None, + true, CacheSetting::Only, - true, - Arc::new(HttpClientProvider::new(None, None)), - Default::default(), - None, + log::Level::Info, ); - let file_fetcher_02 = FileFetcher::new( + let file_fetcher_02 = CliFileFetcher::new( Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)), - CacheSetting::Use, - true, Arc::new(HttpClientProvider::new(None, None)), Default::default(), None, + true, + CacheSetting::Use, + log::Level::Info, ); let specifier = resolve_url("http://localhost:4545/run/002_hello.ts").unwrap(); @@ -1411,8 +1133,19 @@ mod tests { let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await; assert!(result.is_err()); let err = result.unwrap_err(); - assert_eq!(err.to_string(), "Specifier not found in cache: \"http://localhost:4545/run/002_hello.ts\", --cached-only is specified."); - assert_eq!(get_custom_error_class(&err), Some("NotCached")); + let err = err.downcast::().unwrap().into_kind(); + match err { + CliFetchNoFollowErrorKind::FetchNoFollow(err) => { + let err = err.into_kind(); + match &err { + FetchNoFollowErrorKind::NotCached { .. } => { + assert_eq!(err.to_string(), "Specifier not found in cache: \"http://localhost:4545/run/002_hello.ts\", --cached-only is specified."); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); @@ -1426,16 +1159,16 @@ mod tests { let (file_fetcher, temp_dir) = setup(CacheSetting::Use, None); let fixture_path = temp_dir.path().join("mod.ts"); let specifier = ModuleSpecifier::from_file_path(&fixture_path).unwrap(); - fs::write(fixture_path.clone(), r#"console.log("hello deno");"#).unwrap(); + fixture_path.write(r#"console.log("hello deno");"#); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!(&*file.source, r#"console.log("hello deno");"#); - fs::write(fixture_path, r#"console.log("goodbye deno");"#).unwrap(); + fixture_path.write(r#"console.log("goodbye deno");"#); let result = file_fetcher.fetch_bypass_permissions(&specifier).await; assert!(result.is_ok()); - let file = result.unwrap().into_text_decoded().unwrap(); + let file = TextDecodedFile::decode(result.unwrap()).unwrap(); assert_eq!(&*file.source, r#"console.log("goodbye deno");"#); } @@ -1527,29 +1260,169 @@ mod tests { test_fetch_remote_encoded("windows-1255", "windows-1255", expected).await; } + fn create_http_client_adapter() -> HttpClientAdapter { + HttpClientAdapter { + http_client_provider: Arc::new(HttpClientProvider::new(None, None)), + download_log_level: log::Level::Info, + progress_bar: None, + } + } + + #[tokio::test] + async fn test_fetch_string() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/assets/fixture.json").unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, HeaderMap::new()).await; + if let Ok(SendResponse::Success(headers, body)) = result { + assert!(!body.is_empty()); + assert_eq!(headers.get("content-type").unwrap(), "application/json"); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); + } else { + panic!(); + } + } + + #[tokio::test] + async fn test_fetch_gzip() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/run/import_compression/gziped") + .unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, HeaderMap::new()).await; + if let Ok(SendResponse::Success(headers, body)) = result { + assert_eq!(String::from_utf8(body).unwrap(), "console.log('gzip')"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/javascript" + ); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); + } else { + panic!(); + } + } + + #[tokio::test] + async fn test_fetch_with_etag() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, HeaderMap::new()).await; + if let Ok(SendResponse::Success(headers, body)) = result { + assert!(!body.is_empty()); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('etag')"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/typescript" + ); + assert_eq!(headers.get("etag").unwrap(), "33a64df551425fcc55e"); + } else { + panic!(); + } + + let mut headers = HeaderMap::new(); + headers.insert("if-none-match", "33a64df551425fcc55e".parse().unwrap()); + let res = client.send_no_follow(&url, headers).await; + assert_eq!(res.unwrap(), SendResponse::NotModified); + } + + #[tokio::test] + async fn test_fetch_brotli() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/run/import_compression/brotli") + .unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, HeaderMap::new()).await; + if let Ok(SendResponse::Success(headers, body)) = result { + assert!(!body.is_empty()); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('brotli');"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/javascript" + ); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); + } else { + panic!(); + } + } + + #[tokio::test] + async fn test_fetch_accept() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4545/echo_accept").unwrap(); + let client = create_http_client_adapter(); + let mut headers = HeaderMap::new(); + headers.insert("accept", "application/json".parse().unwrap()); + let result = client.send_no_follow(&url, headers).await; + if let Ok(SendResponse::Success(_, body)) = result { + assert_eq!(body, r#"{"accept":"application/json"}"#.as_bytes()); + } else { + panic!(); + } + } + + #[tokio::test] + async fn test_fetch_no_follow_with_redirect() { + let _http_server_guard = test_util::http_server(); + let url = Url::parse("http://127.0.0.1:4546/assets/fixture.json").unwrap(); + // Dns resolver substitutes `127.0.0.1` with `localhost` + let target_url = + Url::parse("http://localhost:4545/assets/fixture.json").unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, Default::default()).await; + if let Ok(SendResponse::Redirect(headers)) = result { + assert_eq!(headers.get("location").unwrap(), target_url.as_str()); + } else { + panic!(); + } + } + + #[tokio::test] + async fn server_error() { + let _g = test_util::http_server(); + let url_str = "http://127.0.0.1:4545/server_error"; + let url = Url::parse(url_str).unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, Default::default()).await; + + if let Err(SendError::StatusCode(status)) = result { + assert_eq!(status, 500); + } else { + panic!("{:?}", result); + } + } + + #[tokio::test] + async fn request_error() { + let _g = test_util::http_server(); + let url_str = "http://127.0.0.1:9999/"; + let url = Url::parse(url_str).unwrap(); + let client = create_http_client_adapter(); + let result = client.send_no_follow(&url, Default::default()).await; + + assert!(matches!(result, Err(SendError::Failed(_)))); + } + #[track_caller] fn get_text_from_cache( - file_fetcher: &FileFetcher, + http_cache: &dyn HttpCache, url: &ModuleSpecifier, ) -> String { - let cache_key = file_fetcher.http_cache.cache_item_key(url).unwrap(); - let bytes = file_fetcher - .http_cache - .get(&cache_key, None) - .unwrap() - .unwrap() - .content; + let cache_key = http_cache.cache_item_key(url).unwrap(); + let bytes = http_cache.get(&cache_key, None).unwrap().unwrap().content; String::from_utf8(bytes.into_owned()).unwrap() } #[track_caller] fn get_location_header_from_cache( - file_fetcher: &FileFetcher, + http_cache: &dyn HttpCache, url: &ModuleSpecifier, ) -> Option { - let cache_key = file_fetcher.http_cache.cache_item_key(url).unwrap(); - file_fetcher - .http_cache + let cache_key = http_cache.cache_item_key(url).unwrap(); + http_cache .read_headers(&cache_key) .unwrap() .unwrap() diff --git a/cli/graph_util.rs b/cli/graph_util.rs index b655dda0f6..6abdbe247a 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -13,7 +13,7 @@ use crate::cache::ModuleInfoCache; use crate::cache::ParsedSourceCache; use crate::colors; use crate::errors::get_error_class_name; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::npm::CliNpmResolver; use crate::resolver::CjsTracker; use crate::resolver::CliResolver; @@ -431,7 +431,7 @@ pub struct ModuleGraphBuilder { caches: Arc, cjs_tracker: Arc, cli_options: Arc, - file_fetcher: Arc, + file_fetcher: Arc, fs: Arc, global_http_cache: Arc, in_npm_pkg_checker: Arc, @@ -450,7 +450,7 @@ impl ModuleGraphBuilder { caches: Arc, cjs_tracker: Arc, cli_options: Arc, - file_fetcher: Arc, + file_fetcher: Arc, fs: Arc, global_http_cache: Arc, in_npm_pkg_checker: Arc, diff --git a/cli/http_util.rs b/cli/http_util.rs index 4b17936d68..ce05d66b78 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -1,14 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::auth_tokens::AuthToken; use crate::util::progress_bar::UpdateGuard; use crate::version; -use cache_control::Cachability; -use cache_control::CacheControl; -use chrono::DateTime; +use boxed_error::Boxed; +use deno_cache_dir::file_fetcher::RedirectHeaderParseError; use deno_core::error::custom_error; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::futures::StreamExt; use deno_core::parking_lot::Mutex; @@ -18,195 +15,26 @@ use deno_core::url::Url; use deno_runtime::deno_fetch; use deno_runtime::deno_fetch::create_http_client; use deno_runtime::deno_fetch::CreateHttpClientOptions; +use deno_runtime::deno_fetch::ResBody; use deno_runtime::deno_tls::RootCertStoreProvider; -use http::header; use http::header::HeaderName; use http::header::HeaderValue; -use http::header::ACCEPT; -use http::header::AUTHORIZATION; use http::header::CONTENT_LENGTH; -use http::header::IF_NONE_MATCH; -use http::header::LOCATION; +use http::HeaderMap; use http::StatusCode; use http_body_util::BodyExt; use std::collections::HashMap; use std::sync::Arc; use std::thread::ThreadId; -use std::time::Duration; -use std::time::SystemTime; use thiserror::Error; -// TODO(ry) HTTP headers are not unique key, value pairs. There may be more than -// one header line with the same key. This should be changed to something like -// Vec<(String, String)> -pub type HeadersMap = HashMap; - -/// A structure used to determine if a entity in the http cache can be used. -/// -/// This is heavily influenced by -/// which is BSD -/// 2-Clause Licensed and copyright Kornel Lesiński -pub struct CacheSemantics { - cache_control: CacheControl, - cached: SystemTime, - headers: HashMap, - now: SystemTime, -} - -impl CacheSemantics { - pub fn new( - headers: HashMap, - cached: SystemTime, - now: SystemTime, - ) -> Self { - let cache_control = headers - .get("cache-control") - .map(|v| CacheControl::from_value(v).unwrap_or_default()) - .unwrap_or_default(); - Self { - cache_control, - cached, - headers, - now, - } - } - - fn age(&self) -> Duration { - let mut age = self.age_header_value(); - - if let Ok(resident_time) = self.now.duration_since(self.cached) { - age += resident_time; - } - - age - } - - fn age_header_value(&self) -> Duration { - Duration::from_secs( - self - .headers - .get("age") - .and_then(|v| v.parse().ok()) - .unwrap_or(0), - ) - } - - fn is_stale(&self) -> bool { - self.max_age() <= self.age() - } - - fn max_age(&self) -> Duration { - if self.cache_control.cachability == Some(Cachability::NoCache) { - return Duration::from_secs(0); - } - - if self.headers.get("vary").map(|s| s.trim()) == Some("*") { - return Duration::from_secs(0); - } - - if let Some(max_age) = self.cache_control.max_age { - return max_age; - } - - let default_min_ttl = Duration::from_secs(0); - - let server_date = self.raw_server_date(); - if let Some(expires) = self.headers.get("expires") { - return match DateTime::parse_from_rfc2822(expires) { - Err(_) => Duration::from_secs(0), - Ok(expires) => { - let expires = SystemTime::UNIX_EPOCH - + Duration::from_secs(expires.timestamp().max(0) as _); - return default_min_ttl - .max(expires.duration_since(server_date).unwrap_or_default()); - } - }; - } - - if let Some(last_modified) = self.headers.get("last-modified") { - if let Ok(last_modified) = DateTime::parse_from_rfc2822(last_modified) { - let last_modified = SystemTime::UNIX_EPOCH - + Duration::from_secs(last_modified.timestamp().max(0) as _); - if let Ok(diff) = server_date.duration_since(last_modified) { - let secs_left = diff.as_secs() as f64 * 0.1; - return default_min_ttl.max(Duration::from_secs(secs_left as _)); - } - } - } - - default_min_ttl - } - - fn raw_server_date(&self) -> SystemTime { - self - .headers - .get("date") - .and_then(|d| DateTime::parse_from_rfc2822(d).ok()) - .and_then(|d| { - SystemTime::UNIX_EPOCH - .checked_add(Duration::from_secs(d.timestamp() as _)) - }) - .unwrap_or(self.cached) - } - - /// Returns true if the cached value is "fresh" respecting cached headers, - /// otherwise returns false. - pub fn should_use(&self) -> bool { - if self.cache_control.cachability == Some(Cachability::NoCache) { - return false; - } - - if let Some(max_age) = self.cache_control.max_age { - if self.age() > max_age { - return false; - } - } - - if let Some(min_fresh) = self.cache_control.min_fresh { - if self.time_to_live() < min_fresh { - return false; - } - } - - if self.is_stale() { - let has_max_stale = self.cache_control.max_stale.is_some(); - let allows_stale = has_max_stale - && self - .cache_control - .max_stale - .map(|val| val > self.age() - self.max_age()) - .unwrap_or(true); - if !allows_stale { - return false; - } - } - - true - } - - fn time_to_live(&self) -> Duration { - self.max_age().checked_sub(self.age()).unwrap_or_default() - } -} - -#[derive(Debug, Eq, PartialEq)] -pub enum FetchOnceResult { - Code(Vec, HeadersMap), - NotModified, - Redirect(Url, HeadersMap), - RequestError(String), - ServerError(StatusCode), -} - -#[derive(Debug)] -pub struct FetchOnceArgs<'a> { - pub url: Url, - pub maybe_accept: Option, - pub maybe_etag: Option, - pub maybe_auth_token: Option, - pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, - pub maybe_progress_guard: Option<&'a UpdateGuard>, +#[derive(Debug, Error)] +pub enum SendError { + #[error(transparent)] + Send(#[from] deno_fetch::ClientSendError), + #[error(transparent)] + InvalidUri(#[from] http::uri::InvalidUri), } pub struct HttpClientProvider { @@ -273,8 +101,11 @@ pub struct BadResponseError { pub response_text: Option, } +#[derive(Debug, Boxed)] +pub struct DownloadError(pub Box); + #[derive(Debug, Error)] -pub enum DownloadError { +pub enum DownloadErrorKind { #[error(transparent)] Fetch(AnyError), #[error(transparent)] @@ -285,8 +116,8 @@ pub enum DownloadError { Json(#[from] serde_json::Error), #[error(transparent)] ToStr(#[from] http::header::ToStrError), - #[error("Redirection from '{}' did not provide location header", .request_url)] - NoRedirectHeader { request_url: Url }, + #[error(transparent)] + RedirectHeaderParse(RedirectHeaderParseError), #[error("Too many redirects.")] TooManyRedirects, #[error(transparent)] @@ -358,107 +189,24 @@ impl HttpClient { )) } - /// Asynchronously fetches the given HTTP URL one pass only. - /// If no redirect is present and no error occurs, - /// yields Code(ResultPayload). - /// If redirect occurs, does not follow and - /// yields Redirect(url). - pub async fn fetch_no_follow<'a>( + pub async fn send( &self, - args: FetchOnceArgs<'a>, - ) -> Result { + url: &Url, + headers: HeaderMap, + ) -> Result, SendError> { let body = http_body_util::Empty::new() .map_err(|never| match never {}) .boxed(); let mut request = http::Request::new(body); - *request.uri_mut() = args.url.as_str().parse()?; + *request.uri_mut() = http::Uri::try_from(url.as_str())?; + *request.headers_mut() = headers; - if let Some(etag) = args.maybe_etag { - let if_none_match_val = HeaderValue::from_str(&etag)?; - request - .headers_mut() - .insert(IF_NONE_MATCH, if_none_match_val); - } - if let Some(auth_token) = args.maybe_auth_token { - let authorization_val = HeaderValue::from_str(&auth_token.to_string())?; - request - .headers_mut() - .insert(AUTHORIZATION, authorization_val); - } else if let Some((header, value)) = args.maybe_auth { - request.headers_mut().insert(header, value); - } - if let Some(accept) = args.maybe_accept { - let accepts_val = HeaderValue::from_str(&accept)?; - request.headers_mut().insert(ACCEPT, accepts_val); - } - let response = match self.client.clone().send(request).await { - Ok(resp) => resp, - Err(err) => { - if err.is_connect_error() { - return Ok(FetchOnceResult::RequestError(err.to_string())); - } - return Err(err.into()); - } - }; - - if response.status() == StatusCode::NOT_MODIFIED { - return Ok(FetchOnceResult::NotModified); - } - - let mut result_headers = HashMap::new(); - let response_headers = response.headers(); - - if let Some(warning) = response_headers.get("X-Deno-Warning") { - log::warn!( - "{} {}", - crate::colors::yellow("Warning"), - warning.to_str().unwrap() - ); - } - - for key in response_headers.keys() { - let key_str = key.to_string(); - let values = response_headers.get_all(key); - let values_str = values - .iter() - .map(|e| e.to_str().unwrap().to_string()) - .collect::>() - .join(","); - result_headers.insert(key_str, values_str); - } - - if response.status().is_redirection() { - let new_url = resolve_redirect_from_response(&args.url, &response)?; - return Ok(FetchOnceResult::Redirect(new_url, result_headers)); - } - - let status = response.status(); - - if status.is_server_error() { - return Ok(FetchOnceResult::ServerError(status)); - } - - if status.is_client_error() { - let err = if response.status() == StatusCode::NOT_FOUND { - custom_error( - "NotFound", - format!("Import '{}' failed, not found.", args.url), - ) - } else { - generic_error(format!( - "Import '{}' failed: {}", - args.url, - response.status() - )) - }; - return Err(err); - } - - let body = - get_response_body_with_progress(response, args.maybe_progress_guard) - .await?; - - Ok(FetchOnceResult::Code(body, result_headers)) + self + .client + .clone() + .send(request) + .await + .map_err(SendError::Send) } pub async fn download_text(&self, url: Url) -> Result { @@ -488,7 +236,12 @@ impl HttpClient { Some(progress_guard), ) }, - |e| matches!(e, DownloadError::BadResponse(_) | DownloadError::Fetch(_)), + |e| { + matches!( + e.as_kind(), + DownloadErrorKind::BadResponse(_) | DownloadErrorKind::Fetch(_) + ) + }, ) .await } @@ -515,18 +268,21 @@ impl HttpClient { } else if !response.status().is_success() { let status = response.status(); let maybe_response_text = body_to_string(response).await.ok(); - return Err(DownloadError::BadResponse(BadResponseError { - status_code: status, - response_text: maybe_response_text - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()), - })); + return Err( + DownloadErrorKind::BadResponse(BadResponseError { + status_code: status, + response_text: maybe_response_text + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()), + }) + .into_box(), + ); } get_response_body_with_progress(response, progress_guard) .await - .map(Some) - .map_err(DownloadError::Fetch) + .map(|(_, body)| Some(body)) + .map_err(|err| DownloadErrorKind::Fetch(err).into_box()) } async fn get_redirected_response( @@ -543,7 +299,7 @@ impl HttpClient { .clone() .send(req) .await - .map_err(|e| DownloadError::Fetch(e.into()))?; + .map_err(|e| DownloadErrorKind::Fetch(e.into()).into_box())?; let status = response.status(); if status.is_redirection() { for _ in 0..5 { @@ -563,7 +319,7 @@ impl HttpClient { .clone() .send(req) .await - .map_err(|e| DownloadError::Fetch(e.into()))?; + .map_err(|e| DownloadErrorKind::Fetch(e.into()).into_box())?; let status = new_response.status(); if status.is_redirection() { response = new_response; @@ -572,17 +328,17 @@ impl HttpClient { return Ok((new_response, new_url)); } } - Err(DownloadError::TooManyRedirects) + Err(DownloadErrorKind::TooManyRedirects.into_box()) } else { Ok((response, url)) } } } -async fn get_response_body_with_progress( +pub async fn get_response_body_with_progress( response: http::Response, progress_guard: Option<&UpdateGuard>, -) -> Result, AnyError> { +) -> Result<(HeaderMap, Vec), AnyError> { use http_body::Body as _; if let Some(progress_guard) = progress_guard { let mut total_size = response.body().size_hint().exact(); @@ -597,45 +353,21 @@ async fn get_response_body_with_progress( progress_guard.set_total_size(total_size); let mut current_size = 0; let mut data = Vec::with_capacity(total_size as usize); - let mut stream = response.into_body().into_data_stream(); + let (parts, body) = response.into_parts(); + let mut stream = body.into_data_stream(); while let Some(item) = stream.next().await { let bytes = item?; current_size += bytes.len() as u64; progress_guard.set_position(current_size); data.extend(bytes.into_iter()); } - return Ok(data); + return Ok((parts.headers, data)); } } - let bytes = response.collect().await?.to_bytes(); - Ok(bytes.into()) -} -/// Construct the next uri based on base uri and location header fragment -/// See -fn resolve_url_from_location(base_url: &Url, location: &str) -> Url { - if location.starts_with("http://") || location.starts_with("https://") { - // absolute uri - Url::parse(location).expect("provided redirect url should be a valid url") - } else if location.starts_with("//") { - // "//" authority path-abempty - Url::parse(&format!("{}:{}", base_url.scheme(), location)) - .expect("provided redirect url should be a valid url") - } else if location.starts_with('/') { - // path-absolute - base_url - .join(location) - .expect("provided redirect url should be a valid url") - } else { - // assuming path-noscheme | path-empty - let base_url_path_str = base_url.path().to_owned(); - // Pop last part or url (after last slash) - let segs: Vec<&str> = base_url_path_str.rsplitn(2, '/').collect(); - let new_path = format!("{}/{}", segs.last().unwrap_or(&""), location); - base_url - .join(&new_path) - .expect("provided redirect url should be a valid url") - } + let (parts, body) = response.into_parts(); + let bytes = body.collect().await?.to_bytes(); + Ok((parts.headers, bytes.into())) } fn resolve_redirect_from_response( @@ -643,16 +375,11 @@ fn resolve_redirect_from_response( response: &http::Response, ) -> Result { debug_assert!(response.status().is_redirection()); - if let Some(location) = response.headers().get(LOCATION) { - let location_string = location.to_str()?; - log::debug!("Redirecting to {:?}...", &location_string); - let new_url = resolve_url_from_location(request_url, location_string); - Ok(new_url) - } else { - Err(DownloadError::NoRedirectHeader { - request_url: request_url.clone(), - }) - } + deno_cache_dir::file_fetcher::resolve_redirect_from_headers( + request_url, + response.headers(), + ) + .map_err(|err| DownloadErrorKind::RedirectHeaderParse(*err).into_box()) } pub async fn body_to_string(body: B) -> Result @@ -707,8 +434,6 @@ mod test { use deno_runtime::deno_tls::rustls::RootCertStore; - use crate::version; - use super::*; #[tokio::test] @@ -738,231 +463,9 @@ mod test { assert_eq!(err.to_string(), "Too many redirects."); } - #[test] - fn test_resolve_url_from_location_full_1() { - let url = "http://deno.land".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "http://golang.org"); - assert_eq!(new_uri.host_str().unwrap(), "golang.org"); - } - - #[test] - fn test_resolve_url_from_location_full_2() { - let url = "https://deno.land".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "https://golang.org"); - assert_eq!(new_uri.host_str().unwrap(), "golang.org"); - } - - #[test] - fn test_resolve_url_from_location_relative_1() { - let url = "http://deno.land/x".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "//rust-lang.org/en-US"); - assert_eq!(new_uri.host_str().unwrap(), "rust-lang.org"); - assert_eq!(new_uri.path(), "/en-US"); - } - - #[test] - fn test_resolve_url_from_location_relative_2() { - let url = "http://deno.land/x".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "/y"); - assert_eq!(new_uri.host_str().unwrap(), "deno.land"); - assert_eq!(new_uri.path(), "/y"); - } - - #[test] - fn test_resolve_url_from_location_relative_3() { - let url = "http://deno.land/x".parse::().unwrap(); - let new_uri = resolve_url_from_location(&url, "z"); - assert_eq!(new_uri.host_str().unwrap(), "deno.land"); - assert_eq!(new_uri.path(), "/z"); - } - - fn create_test_client() -> HttpClient { - HttpClient::new( - create_http_client("test_client", CreateHttpClientOptions::default()) - .unwrap(), - ) - } - - #[tokio::test] - async fn test_fetch_string() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4545/assets/fixture.json").unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(headers.get("content-type").unwrap(), "application/json"); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } - } - - #[tokio::test] - async fn test_fetch_gzip() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4545/run/import_compression/gziped") - .unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert_eq!(String::from_utf8(body).unwrap(), "console.log('gzip')"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/javascript" - ); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } - } - - #[tokio::test] - async fn test_fetch_with_etag() { - let _http_server_guard = test_util::http_server(); - let url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url: url.clone(), - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(String::from_utf8(body).unwrap(), "console.log('etag')"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/typescript" - ); - assert_eq!(headers.get("etag").unwrap(), "33a64df551425fcc55e"); - } else { - panic!(); - } - - let res = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: Some("33a64df551425fcc55e".to_string()), - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - assert_eq!(res.unwrap(), FetchOnceResult::NotModified); - } - - #[tokio::test] - async fn test_fetch_brotli() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4545/run/import_compression/brotli") - .unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(String::from_utf8(body).unwrap(), "console.log('brotli');"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/javascript" - ); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } - } - - #[tokio::test] - async fn test_fetch_accept() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4545/echo_accept").unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: Some("application/json".to_string()), - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, _)) = result { - assert_eq!(body, r#"{"accept":"application/json"}"#.as_bytes()); - } else { - panic!(); - } - } - - #[tokio::test] - async fn test_fetch_no_follow_with_redirect() { - let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server - let url = Url::parse("http://127.0.0.1:4546/assets/fixture.json").unwrap(); - // Dns resolver substitutes `127.0.0.1` with `localhost` - let target_url = - Url::parse("http://localhost:4545/assets/fixture.json").unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Redirect(url, _)) = result { - assert_eq!(url, target_url); - } else { - panic!(); - } - } - #[tokio::test] async fn test_fetch_with_cafile_string() { let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server let url = Url::parse("https://localhost:5545/assets/fixture.json").unwrap(); let client = HttpClient::new( @@ -978,24 +481,15 @@ mod test { ) .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(headers.get("content-type").unwrap(), "application/json"); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } + let response = client.send(&url, Default::default()).await.unwrap(); + assert!(response.status().is_success()); + let (parts, body) = response.into_parts(); + let headers = parts.headers; + let body = body.collect().await.unwrap().to_bytes(); + assert!(!body.is_empty()); + assert_eq!(headers.get("content-type").unwrap(), "application/json"); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); } static PUBLIC_HTTPS_URLS: &[&str] = &[ @@ -1026,34 +520,15 @@ mod test { .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - + let result = client.send(&url, Default::default()).await; match result { - Err(_) => { - eprintln!("Fetch error: {result:?}"); - continue; + Ok(response) if response.status().is_success() => { + return; // success } - Ok( - FetchOnceResult::Code(..) - | FetchOnceResult::NotModified - | FetchOnceResult::Redirect(..), - ) => return, - Ok( - FetchOnceResult::RequestError(_) | FetchOnceResult::ServerError(_), - ) => { - eprintln!("HTTP error: {result:?}"); - continue; + _ => { + // keep going } - }; + } } // Use 1.1.1.1 and 8.8.8.8 as our last-ditch internet check @@ -1089,42 +564,13 @@ mod test { .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - - match result { - Err(_) => { - eprintln!("Fetch error (expected): {result:?}"); - return; - } - Ok( - FetchOnceResult::Code(..) - | FetchOnceResult::NotModified - | FetchOnceResult::Redirect(..), - ) => { - panic!("Should not have successfully fetched a URL"); - } - Ok( - FetchOnceResult::RequestError(_) | FetchOnceResult::ServerError(_), - ) => { - eprintln!("HTTP error (expected): {result:?}"); - return; - } - }; + let result = client.send(&url, HeaderMap::new()).await; + assert!(result.is_err() || !result.unwrap().status().is_success()); } #[tokio::test] async fn test_fetch_with_cafile_gzip() { let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server let url = Url::parse("https://localhost:5545/run/import_compression/gziped") .unwrap(); @@ -1143,27 +589,18 @@ mod test { ) .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert_eq!(String::from_utf8(body).unwrap(), "console.log('gzip')"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/javascript" - ); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } + let response = client.send(&url, Default::default()).await.unwrap(); + assert!(response.status().is_success()); + let (parts, body) = response.into_parts(); + let headers = parts.headers; + let body = body.collect().await.unwrap().to_bytes().to_vec(); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('gzip')"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/javascript" + ); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); } #[tokio::test] @@ -1185,46 +622,29 @@ mod test { ) .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url: url.clone(), - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(String::from_utf8(body).unwrap(), "console.log('etag')"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/typescript" - ); - assert_eq!(headers.get("etag").unwrap(), "33a64df551425fcc55e"); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } + let response = client.send(&url, Default::default()).await.unwrap(); + assert!(response.status().is_success()); + let (parts, body) = response.into_parts(); + let headers = parts.headers; + let body = body.collect().await.unwrap().to_bytes().to_vec(); + assert!(!body.is_empty()); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('etag')"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/typescript" + ); + assert_eq!(headers.get("etag").unwrap(), "33a64df551425fcc55e"); + assert_eq!(headers.get("x-typescript-types"), None); - let res = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: Some("33a64df551425fcc55e".to_string()), - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - assert_eq!(res.unwrap(), FetchOnceResult::NotModified); + let mut headers = HeaderMap::new(); + headers.insert("If-None-Match", "33a64df551425fcc55e".parse().unwrap()); + let res = client.send(&url, headers).await.unwrap(); + assert_eq!(res.status(), StatusCode::NOT_MODIFIED); } #[tokio::test] async fn test_fetch_with_cafile_brotli() { let _http_server_guard = test_util::http_server(); - // Relies on external http server. See target/debug/test_server let url = Url::parse("https://localhost:5545/run/import_compression/brotli") .unwrap(); @@ -1243,93 +663,18 @@ mod test { ) .unwrap(), ); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - if let Ok(FetchOnceResult::Code(body, headers)) = result { - assert!(!body.is_empty()); - assert_eq!(String::from_utf8(body).unwrap(), "console.log('brotli');"); - assert_eq!( - headers.get("content-type").unwrap(), - "application/javascript" - ); - assert_eq!(headers.get("etag"), None); - assert_eq!(headers.get("x-typescript-types"), None); - } else { - panic!(); - } - } - - #[tokio::test] - async fn bad_redirect() { - let _g = test_util::http_server(); - let url_str = "http://127.0.0.1:4545/bad_redirect"; - let url = Url::parse(url_str).unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - assert!(result.is_err()); - let err = result.unwrap_err(); - // Check that the error message contains the original URL - assert!(err.to_string().contains(url_str)); - } - - #[tokio::test] - async fn server_error() { - let _g = test_util::http_server(); - let url_str = "http://127.0.0.1:4545/server_error"; - let url = Url::parse(url_str).unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - - if let Ok(FetchOnceResult::ServerError(status)) = result { - assert_eq!(status, 500); - } else { - panic!(); - } - } - - #[tokio::test] - async fn request_error() { - let _g = test_util::http_server(); - let url_str = "http://127.0.0.1:9999/"; - let url = Url::parse(url_str).unwrap(); - let client = create_test_client(); - let result = client - .fetch_no_follow(FetchOnceArgs { - url, - maybe_accept: None, - maybe_etag: None, - maybe_auth_token: None, - maybe_progress_guard: None, - maybe_auth: None, - }) - .await; - - assert!(matches!(result, Ok(FetchOnceResult::RequestError(_)))); + let response = client.send(&url, Default::default()).await.unwrap(); + assert!(response.status().is_success()); + let (parts, body) = response.into_parts(); + let headers = parts.headers; + let body = body.collect().await.unwrap().to_bytes().to_vec(); + assert!(!body.is_empty()); + assert_eq!(String::from_utf8(body).unwrap(), "console.log('brotli');"); + assert_eq!( + headers.get("content-type").unwrap(), + "application/javascript" + ); + assert_eq!(headers.get("etag"), None); + assert_eq!(headers.get("x-typescript-types"), None); } } diff --git a/cli/jsr.rs b/cli/jsr.rs index 767d304d60..acfbb1c8e2 100644 --- a/cli/jsr.rs +++ b/cli/jsr.rs @@ -1,7 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use crate::args::jsr_url; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use dashmap::DashMap; use deno_core::serde_json; use deno_graph::packages::JsrPackageInfo; @@ -19,11 +19,11 @@ pub struct JsrFetchResolver { /// It can be large and we don't want to store it. info_by_nv: DashMap>>, info_by_name: DashMap>>, - file_fetcher: Arc, + file_fetcher: Arc, } impl JsrFetchResolver { - pub fn new(file_fetcher: Arc) -> Self { + pub fn new(file_fetcher: Arc) -> Self { Self { nv_by_req: Default::default(), info_by_nv: Default::default(), diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 47e36d1328..3efebe63b1 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -63,7 +63,7 @@ use crate::args::ConfigFile; use crate::args::LintFlags; use crate::args::LintOptions; use crate::cache::FastInsecureHasher; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::lsp::logging::lsp_warn; use crate::resolver::CliSloppyImportsResolver; use crate::resolver::SloppyImportsCachedFs; @@ -1218,7 +1218,7 @@ impl ConfigData { specified_config: Option<&Path>, scope: &ModuleSpecifier, settings: &Settings, - file_fetcher: &Arc, + file_fetcher: &Arc, // sync requirement is because the lsp requires sync cached_deno_config_fs: &(dyn DenoConfigFs + Sync), deno_json_cache: &(dyn DenoJsonCache + Sync), @@ -1313,7 +1313,7 @@ impl ConfigData { member_dir: Arc, scope: Arc, settings: &Settings, - file_fetcher: Option<&Arc>, + file_fetcher: Option<&Arc>, ) -> Self { let (settings, workspace_folder) = settings.get_for_specifier(&scope); let mut watched_files = HashMap::with_capacity(10); @@ -1834,7 +1834,7 @@ impl ConfigTree { &mut self, settings: &Settings, workspace_files: &IndexSet, - file_fetcher: &Arc, + file_fetcher: &Arc, ) { lsp_log!("Refreshing configuration tree..."); // since we're resolving a workspace multiple times in different diff --git a/cli/lsp/jsr.rs b/cli/lsp/jsr.rs index ab570f6348..1d012b42f0 100644 --- a/cli/lsp/jsr.rs +++ b/cli/lsp/jsr.rs @@ -2,7 +2,8 @@ use crate::args::jsr_api_url; use crate::args::jsr_url; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::TextDecodedFile; use crate::jsr::partial_jsr_package_version_info_from_slice; use crate::jsr::JsrFetchResolver; use dashmap::DashMap; @@ -267,7 +268,7 @@ fn read_cached_url( #[derive(Debug)] pub struct CliJsrSearchApi { - file_fetcher: Arc, + file_fetcher: Arc, resolver: JsrFetchResolver, search_cache: DashMap>>, versions_cache: DashMap>>, @@ -275,7 +276,7 @@ pub struct CliJsrSearchApi { } impl CliJsrSearchApi { - pub fn new(file_fetcher: Arc) -> Self { + pub fn new(file_fetcher: Arc) -> Self { let resolver = JsrFetchResolver::new(file_fetcher.clone()); Self { file_fetcher, @@ -309,10 +310,8 @@ impl PackageSearchApi for CliJsrSearchApi { let file_fetcher = self.file_fetcher.clone(); // spawn due to the lsp's `Send` requirement let file = deno_core::unsync::spawn(async move { - file_fetcher - .fetch_bypass_permissions(&search_url) - .await? - .into_text_decoded() + let file = file_fetcher.fetch_bypass_permissions(&search_url).await?; + TextDecodedFile::decode(file) }) .await??; let names = Arc::new(parse_jsr_search_response(&file.source)?); diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 839d28469e..3ffe4491e0 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1,6 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_config::workspace::WorkspaceDirectory; use deno_config::workspace::WorkspaceDiscoverOptions; use deno_core::anyhow::anyhow; @@ -95,13 +96,12 @@ use crate::args::create_default_npmrc; use crate::args::get_root_cert_store; use crate::args::has_flag_env_var; use crate::args::CaData; -use crate::args::CacheSetting; use crate::args::CliOptions; use crate::args::Flags; use crate::args::InternalFlags; use crate::args::UnstableFmtOptions; use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::graph_util; use crate::http_util::HttpClientProvider; use crate::lsp::config::ConfigWatchedFileType; @@ -958,15 +958,15 @@ impl Inner { } async fn refresh_config_tree(&mut self) { - let mut file_fetcher = FileFetcher::new( + let file_fetcher = CliFileFetcher::new( self.cache.global().clone(), - CacheSetting::RespectHeaders, - true, self.http_client_provider.clone(), Default::default(), None, + true, + CacheSetting::RespectHeaders, + super::logging::lsp_log_level(), ); - file_fetcher.set_download_log_level(super::logging::lsp_log_level()); let file_fetcher = Arc::new(file_fetcher); self .config diff --git a/cli/lsp/npm.rs b/cli/lsp/npm.rs index 2decfc3429..18c7e2fccf 100644 --- a/cli/lsp/npm.rs +++ b/cli/lsp/npm.rs @@ -11,21 +11,22 @@ use serde::Deserialize; use std::sync::Arc; use crate::args::npm_registry_url; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::TextDecodedFile; use crate::npm::NpmFetchResolver; use super::search::PackageSearchApi; #[derive(Debug)] pub struct CliNpmSearchApi { - file_fetcher: Arc, + file_fetcher: Arc, resolver: NpmFetchResolver, search_cache: DashMap>>, versions_cache: DashMap>>, } impl CliNpmSearchApi { - pub fn new(file_fetcher: Arc) -> Self { + pub fn new(file_fetcher: Arc) -> Self { let resolver = NpmFetchResolver::new( file_fetcher.clone(), Arc::new(NpmRc::default().as_resolved(npm_registry_url()).unwrap()), @@ -57,10 +58,8 @@ impl PackageSearchApi for CliNpmSearchApi { .append_pair("text", &format!("{} boost-exact:false", query)); let file_fetcher = self.file_fetcher.clone(); let file = deno_core::unsync::spawn(async move { - file_fetcher - .fetch_bypass_permissions(&search_url) - .await? - .into_text_decoded() + let file = file_fetcher.fetch_bypass_permissions(&search_url).await?; + TextDecodedFile::decode(file) }) .await??; let names = Arc::new(parse_npm_search_response(&file.source)?); diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index ade353e683..067f201829 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -12,14 +12,15 @@ use super::path_to_regex::StringOrNumber; use super::path_to_regex::StringOrVec; use super::path_to_regex::Token; -use crate::args::CacheSetting; use crate::cache::GlobalHttpCache; use crate::cache::HttpCache; +use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::FetchOptions; use crate::file_fetcher::FetchPermissionsOptionRef; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::TextDecodedFile; use crate::http_util::HttpClientProvider; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; use deno_core::serde::Deserialize; @@ -418,7 +419,7 @@ enum VariableItems { pub struct ModuleRegistry { origins: HashMap>, pub location: PathBuf, - pub file_fetcher: Arc, + pub file_fetcher: Arc, http_cache: Arc, } @@ -432,15 +433,15 @@ impl ModuleRegistry { location.clone(), crate::cache::RealDenoCacheEnv, )); - let mut file_fetcher = FileFetcher::new( + let file_fetcher = CliFileFetcher::new( http_cache.clone(), - CacheSetting::RespectHeaders, - true, http_client_provider, Default::default(), None, + true, + CacheSetting::RespectHeaders, + super::logging::lsp_log_level(), ); - file_fetcher.set_download_log_level(super::logging::lsp_log_level()); Self { origins: HashMap::new(), @@ -479,13 +480,15 @@ impl ModuleRegistry { let specifier = specifier.clone(); async move { file_fetcher - .fetch_with_options(FetchOptions { - specifier: &specifier, - permissions: FetchPermissionsOptionRef::AllowAll, - maybe_auth: None, - maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"), - maybe_cache_setting: None, - }) + .fetch_with_options( + &specifier, +FetchPermissionsOptionRef::AllowAll, + FetchOptions { + maybe_auth: None, + maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"), + maybe_cache_setting: None, + } + ) .await } }).await?; @@ -500,7 +503,7 @@ impl ModuleRegistry { ); self.http_cache.set(specifier, headers_map, &[])?; } - let file = fetch_result?.into_text_decoded()?; + let file = TextDecodedFile::decode(fetch_result?)?; let config: RegistryConfigurationJson = serde_json::from_str(&file.source)?; validate_config(&config)?; Ok(config.registries) @@ -584,12 +587,11 @@ impl ModuleRegistry { // spawn due to the lsp's `Send` requirement let file = deno_core::unsync::spawn({ async move { - file_fetcher + let file = file_fetcher .fetch_bypass_permissions(&endpoint) .await - .ok()? - .into_text_decoded() - .ok() + .ok()?; + TextDecodedFile::decode(file).ok() } }) .await @@ -983,12 +985,11 @@ impl ModuleRegistry { let file_fetcher = self.file_fetcher.clone(); // spawn due to the lsp's `Send` requirement let file = deno_core::unsync::spawn(async move { - file_fetcher + let file = file_fetcher .fetch_bypass_permissions(&specifier) .await - .ok()? - .into_text_decoded() - .ok() + .ok()?; + TextDecodedFile::decode(file).ok() }) .await .ok()??; @@ -1049,7 +1050,7 @@ impl ModuleRegistry { let file_fetcher = self.file_fetcher.clone(); let specifier = specifier.clone(); async move { - file_fetcher + let file = file_fetcher .fetch_bypass_permissions(&specifier) .await .map_err(|err| { @@ -1058,9 +1059,8 @@ impl ModuleRegistry { specifier, err ); }) - .ok()? - .into_text_decoded() - .ok() + .ok()?; + TextDecodedFile::decode(file).ok() } }) .await @@ -1095,7 +1095,7 @@ impl ModuleRegistry { let file_fetcher = self.file_fetcher.clone(); let specifier = specifier.clone(); async move { - file_fetcher + let file = file_fetcher .fetch_bypass_permissions(&specifier) .await .map_err(|err| { @@ -1104,9 +1104,8 @@ impl ModuleRegistry { specifier, err ); }) - .ok()? - .into_text_decoded() - .ok() + .ok()?; + TextDecodedFile::decode(file).ok() } }) .await diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 28c7b04fc9..482f2ddb40 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -2,6 +2,7 @@ use dashmap::DashMap; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::npm::NpmCacheDir; use deno_cache_dir::HttpCache; use deno_config::deno_json::JsxImportSourceConfig; @@ -39,7 +40,6 @@ use std::sync::Arc; use super::cache::LspCache; use super::jsr::JsrCacheResolver; use crate::args::create_default_npmrc; -use crate::args::CacheSetting; use crate::args::CliLockfile; use crate::args::NpmInstallDepsProvider; use crate::cache::DenoCacheEnvFsAdapter; diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index fddcd6e738..6ae6265688 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -5516,7 +5516,6 @@ impl TscRequest { mod tests { use super::*; use crate::cache::HttpCache; - use crate::http_util::HeadersMap; use crate::lsp::cache::LspCache; use crate::lsp::config::Config; use crate::lsp::config::WorkspaceSettings; @@ -5953,7 +5952,7 @@ mod tests { .global() .set( &specifier_dep, - HeadersMap::default(), + Default::default(), b"export const b = \"b\";\n", ) .unwrap(); @@ -5992,7 +5991,7 @@ mod tests { .global() .set( &specifier_dep, - HeadersMap::default(), + Default::default(), b"export const b = \"b\";\n\nexport const a = \"b\";\n", ) .unwrap(); diff --git a/cli/main.rs b/cli/main.rs index 0594739fd8..d68f27146b 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -1,7 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. mod args; -mod auth_tokens; mod cache; mod cdp; mod emit; diff --git a/cli/mainrt.rs b/cli/mainrt.rs index 18142bd0e7..cba54b044c 100644 --- a/cli/mainrt.rs +++ b/cli/mainrt.rs @@ -8,7 +8,6 @@ mod standalone; mod args; -mod auth_tokens; mod cache; mod emit; mod errors; diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 2c6e6d318a..4545800e99 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -20,6 +20,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; +use deno_npm_cache::NpmCacheSetting; use deno_resolver::npm::CliNpmReqResolver; use deno_runtime::colors; use deno_runtime::deno_fs::FileSystem; @@ -70,7 +71,7 @@ pub struct CliManagedNpmResolverCreateOptions { pub fs: Arc, pub http_client_provider: Arc, pub npm_cache_dir: Arc, - pub cache_setting: crate::args::CacheSetting, + pub cache_setting: deno_cache_dir::file_fetcher::CacheSetting, pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, pub maybe_node_modules_path: Option, pub npm_system_info: NpmSystemInfo, @@ -203,7 +204,7 @@ fn create_cache( ) -> Arc { Arc::new(CliNpmCache::new( options.npm_cache_dir.clone(), - options.cache_setting.as_npm_cache_setting(), + NpmCacheSetting::from_cache_setting(&options.cache_setting), env, options.npmrc.clone(), )) diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index b39e0a340d..312ea2055b 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -28,7 +28,7 @@ use managed::create_managed_in_npm_pkg_checker; use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::http_util::HttpClientProvider; use crate::util::fs::atomic_write_file_with_retries_and_fs; use crate::util::fs::hard_link_dir_recursive; @@ -115,14 +115,14 @@ impl deno_npm_cache::NpmCacheEnv for CliNpmCacheEnv { .download_with_progress_and_retries(url, maybe_auth_header, &guard) .await .map_err(|err| { - use crate::http_util::DownloadError::*; - let status_code = match &err { + use crate::http_util::DownloadErrorKind::*; + let status_code = match err.as_kind() { Fetch { .. } | UrlParse { .. } | HttpParse { .. } | Json { .. } | ToStr { .. } - | NoRedirectHeader { .. } + | RedirectHeaderParse { .. } | TooManyRedirects => None, BadResponse(bad_response_error) => { Some(bad_response_error.status_code) @@ -232,13 +232,13 @@ pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver { pub struct NpmFetchResolver { nv_by_req: DashMap>, info_by_name: DashMap>>, - file_fetcher: Arc, + file_fetcher: Arc, npmrc: Arc, } impl NpmFetchResolver { pub fn new( - file_fetcher: Arc, + file_fetcher: Arc, npmrc: Arc, ) -> Self { Self { diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 85a22cf837..2ed52010fb 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -71,7 +71,7 @@ use crate::args::UnstableConfig; use crate::cache::DenoDir; use crate::cache::FastInsecureHasher; use crate::emit::Emitter; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::http_util::HttpClientProvider; use crate::npm::CliNpmResolver; use crate::npm::InnerCliNpmResolverRef; @@ -390,7 +390,7 @@ pub struct DenoCompileBinaryWriter<'a> { cli_options: &'a CliOptions, deno_dir: &'a DenoDir, emitter: &'a Emitter, - file_fetcher: &'a FileFetcher, + file_fetcher: &'a CliFileFetcher, http_client_provider: &'a HttpClientProvider, npm_resolver: &'a dyn CliNpmResolver, workspace_resolver: &'a WorkspaceResolver, @@ -404,7 +404,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { cli_options: &'a CliOptions, deno_dir: &'a DenoDir, emitter: &'a Emitter, - file_fetcher: &'a FileFetcher, + file_fetcher: &'a CliFileFetcher, http_client_provider: &'a HttpClientProvider, npm_resolver: &'a dyn CliNpmResolver, workspace_resolver: &'a WorkspaceResolver, diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 22e0b6d115..08ee5ba11a 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -9,6 +9,7 @@ use binary::StandaloneData; use binary::StandaloneModules; use code_cache::DenoCompileCodeCache; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::npm::NpmCacheDir; use deno_config::workspace::MappedResolution; use deno_config::workspace::MappedResolutionError; @@ -64,7 +65,6 @@ use crate::args::create_default_npmrc; use crate::args::get_root_cert_store; use crate::args::npm_pkg_req_ref_to_binary_command; use crate::args::CaData; -use crate::args::CacheSetting; use crate::args::NpmInstallDepsProvider; use crate::args::StorageKeyResolver; use crate::cache::Caches; diff --git a/cli/tools/check.rs b/cli/tools/check.rs index ad5c7c3ab1..9af084806f 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -64,7 +64,7 @@ pub async fn check( let file = file_fetcher.fetch(&s, root_permissions).await?; let snippet_files = extract::extract_snippet_files(file)?; for snippet_file in snippet_files { - specifiers_for_typecheck.push(snippet_file.specifier.clone()); + specifiers_for_typecheck.push(snippet_file.url.clone()); file_fetcher.insert_memory_files(snippet_file); } } diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 2a554c1335..624fa76bf6 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -6,6 +6,7 @@ use crate::args::FileFlags; use crate::args::Flags; use crate::cdp; use crate::factory::CliFactory; +use crate::file_fetcher::TextDecodedFile; use crate::tools::fmt::format_json; use crate::tools::test::is_supported_test_path; use crate::util::text_encoding::source_map_from_code; @@ -559,6 +560,12 @@ pub fn cover_files( }, None => None, }; + let get_message = |specifier: &ModuleSpecifier| -> String { + format!( + "Failed to fetch \"{}\" from cache. Before generating coverage report, run `deno test --coverage` to ensure consistent state.", + specifier, + ) + }; for script_coverage in script_coverages { let module_specifier = deno_core::resolve_url_or_path( @@ -566,21 +573,14 @@ pub fn cover_files( cli_options.initial_cwd(), )?; - let maybe_file = if module_specifier.scheme() == "file" { - file_fetcher.get_source(&module_specifier) - } else { - file_fetcher - .fetch_cached(&module_specifier, 10) - .with_context(|| { - format!("Failed to fetch \"{module_specifier}\" from cache.") - })? + let maybe_file_result = file_fetcher + .get_cached_source_or_local(&module_specifier) + .map_err(AnyError::from); + let file = match maybe_file_result { + Ok(Some(file)) => TextDecodedFile::decode(file)?, + Ok(None) => return Err(anyhow!("{}", get_message(&module_specifier))), + Err(err) => return Err(err).context(get_message(&module_specifier)), }; - let file = maybe_file.ok_or_else(|| { - anyhow!("Failed to fetch \"{}\" from cache. - Before generating coverage report, run `deno test --coverage` to ensure consistent state.", - module_specifier - ) - })?.into_text_decoded()?; let original_source = file.source.clone(); // Check if file was transpiled diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index d7c484beba..dac7340d40 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -3,7 +3,6 @@ use crate::args::resolve_no_prompt; use crate::args::AddFlags; use crate::args::CaData; -use crate::args::CacheSetting; use crate::args::ConfigFlag; use crate::args::Flags; use crate::args::InstallFlags; @@ -13,13 +12,14 @@ use crate::args::TypeCheckMode; use crate::args::UninstallFlags; use crate::args::UninstallKind; use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::graph_container::ModuleGraphContainer; use crate::http_util::HttpClientProvider; use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; use crate::util::fs::canonicalize_path_maybe_not_exists; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::generic_error; @@ -361,18 +361,18 @@ async fn install_global( let cli_options = factory.cli_options()?; let http_client = factory.http_client_provider(); let deps_http_cache = factory.global_http_cache()?; - let mut deps_file_fetcher = FileFetcher::new( + let deps_file_fetcher = CliFileFetcher::new( deps_http_cache.clone(), - CacheSetting::ReloadAll, - true, http_client.clone(), Default::default(), None, + true, + CacheSetting::ReloadAll, + log::Level::Trace, ); let npmrc = factory.cli_options().unwrap().npmrc(); - deps_file_fetcher.set_download_log_level(log::Level::Trace); let deps_file_fetcher = Arc::new(deps_file_fetcher); let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone())); let npm_resolver = Arc::new(NpmFetchResolver::new( diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 6f89ec7aae..791e54c67c 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -4,6 +4,7 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::AnyError; @@ -23,12 +24,11 @@ use jsonc_parser::cst::CstRootNode; use jsonc_parser::json; use crate::args::AddFlags; -use crate::args::CacheSetting; use crate::args::CliOptions; use crate::args::Flags; use crate::args::RemoveFlags; use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; @@ -411,18 +411,18 @@ pub async fn add( let http_client = cli_factory.http_client_provider(); let deps_http_cache = cli_factory.global_http_cache()?; - let mut deps_file_fetcher = FileFetcher::new( + let deps_file_fetcher = CliFileFetcher::new( deps_http_cache.clone(), - CacheSetting::ReloadAll, - true, http_client.clone(), Default::default(), None, + true, + CacheSetting::ReloadAll, + log::Level::Trace, ); let npmrc = cli_factory.cli_options().unwrap().npmrc(); - deps_file_fetcher.set_download_log_level(log::Level::Trace); let deps_file_fetcher = Arc::new(deps_file_fetcher); let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone())); let npm_resolver = diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index aef65a5de0..f767eb1522 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -3,6 +3,7 @@ use std::collections::HashSet; use std::sync::Arc; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_core::anyhow::bail; use deno_core::error::AnyError; use deno_semver::package::PackageNv; @@ -10,12 +11,11 @@ use deno_semver::package::PackageReq; use deno_semver::VersionReq; use deno_terminal::colors; -use crate::args::CacheSetting; use crate::args::CliOptions; use crate::args::Flags; use crate::args::OutdatedFlags; use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; use crate::tools::registry::pm::deps::DepKind; @@ -181,15 +181,15 @@ pub async fn outdated( let workspace = cli_options.workspace(); let http_client = factory.http_client_provider(); let deps_http_cache = factory.global_http_cache()?; - let mut file_fetcher = FileFetcher::new( + let file_fetcher = CliFileFetcher::new( deps_http_cache.clone(), - CacheSetting::RespectHeaders, - true, http_client.clone(), Default::default(), None, + true, + CacheSetting::RespectHeaders, + log::Level::Trace, ); - file_fetcher.set_download_log_level(log::Level::Trace); let file_fetcher = Arc::new(file_fetcher); let npm_fetch_resolver = Arc::new(NpmFetchResolver::new( file_fetcher.clone(), diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index a303046879..9fb4624fa4 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -11,7 +11,8 @@ use crate::args::ReplFlags; use crate::cdp; use crate::colors; use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::TextDecodedFile; use deno_core::error::AnyError; use deno_core::futures::StreamExt; use deno_core::serde_json; @@ -143,7 +144,7 @@ async fn read_line_and_poll( async fn read_eval_file( cli_options: &CliOptions, - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, eval_file: &str, ) -> Result, AnyError> { let specifier = @@ -151,7 +152,7 @@ async fn read_eval_file( let file = file_fetcher.fetch_bypass_permissions(&specifier).await?; - Ok(file.into_text_decoded()?.source) + Ok(TextDecodedFile::decode(file)?.source) } #[allow(clippy::print_stdout)] diff --git a/cli/tools/run/mod.rs b/cli/tools/run/mod.rs index d3f7b093d4..cd7d1dd6c4 100644 --- a/cli/tools/run/mod.rs +++ b/cli/tools/run/mod.rs @@ -3,6 +3,7 @@ use std::io::Read; use std::sync::Arc; +use deno_cache_dir::file_fetcher::File; use deno_config::deno_json::NodeModulesDirMode; use deno_core::error::AnyError; use deno_runtime::WorkerExecutionMode; @@ -11,7 +12,6 @@ use crate::args::EvalFlags; use crate::args::Flags; use crate::args::WatchFlagsWithPaths; use crate::factory::CliFactory; -use crate::file_fetcher::File; use crate::util; use crate::util::file_watcher::WatcherRestartMode; @@ -97,7 +97,7 @@ pub async fn run_from_stdin(flags: Arc) -> Result { // Save a fake file into file fetcher cache // to allow module access by TS compiler file_fetcher.insert_memory_files(File { - specifier: main_module.clone(), + url: main_module.clone(), maybe_headers: None, source: source.into(), }); @@ -184,7 +184,7 @@ pub async fn eval_command( // Save a fake file into file fetcher cache // to allow module access by TS compiler. file_fetcher.insert_memory_files(File { - specifier: main_module.clone(), + url: main_module.clone(), maybe_headers: None, source: source_code.into_bytes().into(), }); diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 2e46bdd4da..48bf42c9c7 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -7,8 +7,7 @@ use crate::args::TestReporterConfig; use crate::colors; use crate::display; use crate::factory::CliFactory; -use crate::file_fetcher::File; -use crate::file_fetcher::FileFetcher; +use crate::file_fetcher::CliFileFetcher; use crate::graph_util::has_graph_root_local_dependent_changed; use crate::ops; use crate::util::extract::extract_doc_tests; @@ -21,6 +20,7 @@ use crate::worker::CliMainWorkerFactory; use crate::worker::CoverageCollector; use deno_ast::MediaType; +use deno_cache_dir::file_fetcher::File; use deno_config::glob::FilePatterns; use deno_config::glob::WalkEntry; use deno_core::anyhow; @@ -1514,7 +1514,7 @@ fn collect_specifiers_with_test_mode( /// as well. async fn fetch_specifiers_with_test_mode( cli_options: &CliOptions, - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, member_patterns: impl Iterator, doc: &bool, ) -> Result, AnyError> { @@ -1822,7 +1822,7 @@ pub async fn run_tests_with_watch( /// Extracts doc tests from files specified by the given specifiers. async fn get_doc_tests( specifiers_with_mode: &[(Url, TestMode)], - file_fetcher: &FileFetcher, + file_fetcher: &CliFileFetcher, ) -> Result, AnyError> { let specifiers_needing_extraction = specifiers_with_mode .iter() @@ -1847,7 +1847,7 @@ fn get_target_specifiers( specifiers_with_mode .into_iter() .filter_map(|(s, mode)| mode.needs_test_run().then_some(s)) - .chain(doc_tests.iter().map(|d| d.specifier.clone())) + .chain(doc_tests.iter().map(|d| d.url.clone())) .collect() } diff --git a/cli/util/extract.rs b/cli/util/extract.rs index be68202aa1..c4562060d8 100644 --- a/cli/util/extract.rs +++ b/cli/util/extract.rs @@ -13,6 +13,7 @@ use deno_ast::swc::visit::VisitMut; use deno_ast::swc::visit::VisitWith as _; use deno_ast::MediaType; use deno_ast::SourceRangedForSpanned as _; +use deno_cache_dir::file_fetcher::File; use deno_core::error::AnyError; use deno_core::ModuleSpecifier; use regex::Regex; @@ -20,7 +21,7 @@ use std::collections::BTreeSet; use std::fmt::Write as _; use std::sync::Arc; -use crate::file_fetcher::File; +use crate::file_fetcher::TextDecodedFile; use crate::util::path::mapped_specifier_for_tsc; /// Extracts doc tests from a given file, transforms them into pseudo test @@ -52,7 +53,7 @@ fn extract_inner( file: File, wrap_kind: WrapKind, ) -> Result, AnyError> { - let file = file.into_text_decoded()?; + let file = TextDecodedFile::decode(file)?; let exports = match deno_ast::parse_program(deno_ast::ParseParams { specifier: file.specifier.clone(), @@ -230,7 +231,7 @@ fn extract_files_from_regex_blocks( .unwrap_or(file_specifier); Some(File { - specifier: file_specifier, + url: file_specifier, maybe_headers: None, source: file_source.into_bytes().into(), }) @@ -558,7 +559,7 @@ fn generate_pseudo_file( exports: &ExportCollector, wrap_kind: WrapKind, ) -> Result { - let file = file.into_text_decoded()?; + let file = TextDecodedFile::decode(file)?; let parsed = deno_ast::parse_program(deno_ast::ParseParams { specifier: file.specifier.clone(), @@ -594,7 +595,7 @@ fn generate_pseudo_file( log::debug!("{}:\n{}", file.specifier, source); Ok(File { - specifier: file.specifier, + url: file.specifier, maybe_headers: None, source: source.into_bytes().into(), }) @@ -1199,14 +1200,14 @@ Deno.test("file:///main.ts$3-7.ts", async ()=>{ for test in tests { let file = File { - specifier: ModuleSpecifier::parse(test.input.specifier).unwrap(), + url: ModuleSpecifier::parse(test.input.specifier).unwrap(), maybe_headers: None, source: test.input.source.as_bytes().into(), }; let got_decoded = extract_doc_tests(file) .unwrap() .into_iter() - .map(|f| f.into_text_decoded().unwrap()) + .map(|f| TextDecodedFile::decode(f).unwrap()) .collect::>(); let expected = test .expected @@ -1435,14 +1436,14 @@ add('1', '2'); for test in tests { let file = File { - specifier: ModuleSpecifier::parse(test.input.specifier).unwrap(), + url: ModuleSpecifier::parse(test.input.specifier).unwrap(), maybe_headers: None, source: test.input.source.as_bytes().into(), }; let got_decoded = extract_snippet_files(file) .unwrap() .into_iter() - .map(|f| f.into_text_decoded().unwrap()) + .map(|f| TextDecodedFile::decode(f).unwrap()) .collect::>(); let expected = test .expected diff --git a/resolvers/npm_cache/lib.rs b/resolvers/npm_cache/lib.rs index 9f5424dc46..c16c29aaf2 100644 --- a/resolvers/npm_cache/lib.rs +++ b/resolvers/npm_cache/lib.rs @@ -9,6 +9,7 @@ use std::sync::Arc; use anyhow::bail; use anyhow::Context; use anyhow::Error as AnyError; +use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::npm::NpmCacheDir; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; @@ -90,6 +91,27 @@ pub enum NpmCacheSetting { } impl NpmCacheSetting { + pub fn from_cache_setting(cache_setting: &CacheSetting) -> NpmCacheSetting { + match cache_setting { + CacheSetting::Only => NpmCacheSetting::Only, + CacheSetting::ReloadAll => NpmCacheSetting::ReloadAll, + CacheSetting::ReloadSome(values) => { + if values.iter().any(|v| v == "npm:") { + NpmCacheSetting::ReloadAll + } else { + NpmCacheSetting::ReloadSome { + npm_package_names: values + .iter() + .filter_map(|v| v.strip_prefix("npm:")) + .map(|n| n.to_string()) + .collect(), + } + } + } + CacheSetting::RespectHeaders => panic!("not supported"), + CacheSetting::Use => NpmCacheSetting::Use, + } + } pub fn should_use_for_npm_package(&self, package_name: &str) -> bool { match self { NpmCacheSetting::ReloadAll => false, diff --git a/tests/integration/bench_tests.rs b/tests/integration/bench_tests.rs index d588f5b437..4ee029d648 100644 --- a/tests/integration/bench_tests.rs +++ b/tests/integration/bench_tests.rs @@ -43,7 +43,7 @@ fn conditionally_loads_type_graph() { .new_command() .args("bench --reload -L debug run/type_directives_js_main.js") .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow_with_options - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); + output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); let output = context .new_command() .args("bench --reload -L debug --no-check run/type_directives_js_main.js") diff --git a/tests/integration/cache_tests.rs b/tests/integration/cache_tests.rs index d9fb8e38e5..4cddae1af1 100644 --- a/tests/integration/cache_tests.rs +++ b/tests/integration/cache_tests.rs @@ -107,5 +107,5 @@ fn loads_type_graph() { .new_command() .args("cache --reload -L debug run/type_directives_js_main.js") .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow_with_options - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); + output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); } diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index f0b536aa22..77c0a46c5f 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -922,7 +922,7 @@ fn type_directives_js_main() { .new_command() .args("run --reload -L debug --check run/type_directives_js_main.js") .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow_with_options - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); + output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); let output = context .new_command() .args("run --reload -L debug run/type_directives_js_main.js") diff --git a/tests/integration/test_tests.rs b/tests/integration/test_tests.rs index 64857ae110..ca83682833 100644 --- a/tests/integration/test_tests.rs +++ b/tests/integration/test_tests.rs @@ -111,7 +111,7 @@ fn conditionally_loads_type_graph() { .new_command() .args("test --reload -L debug run/type_directives_js_main.js") .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow_with_options - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); + output.assert_matches_text("[WILDCARD] - FileFetcher::fetch_no_follow - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); let output = context .new_command() .args("test --reload -L debug --no-check run/type_directives_js_main.js") diff --git a/tests/specs/cert/localhost_unsafe_ssl/localhost_unsafe_ssl.ts.out b/tests/specs/cert/localhost_unsafe_ssl/localhost_unsafe_ssl.ts.out index c7bdfde0ed..ffb84ebfde 100644 --- a/tests/specs/cert/localhost_unsafe_ssl/localhost_unsafe_ssl.ts.out +++ b/tests/specs/cert/localhost_unsafe_ssl/localhost_unsafe_ssl.ts.out @@ -1,3 +1,6 @@ DANGER: TLS certificate validation is disabled for: deno.land -error: Import 'https://localhost:5545/subdir/mod2.ts' failed: error sending request for url (https://localhost:5545/subdir/mod2.ts): client error[WILDCARD] - at file:///[WILDCARD]/cafile_url_imports.ts:[WILDCARD] +error: Import 'https://localhost:5545/subdir/mod2.ts' failed. + 0: error sending request for url (https://localhost:5545/subdir/mod2.ts): client error (Connect): invalid peer certificate: UnknownIssuer + 1: client error (Connect) + 2: invalid peer certificate: UnknownIssuer + at file:///[WILDLINE]/cafile_url_imports.ts:[WILDLINE] diff --git a/tests/specs/run/jsx_import_source/__test__.jsonc b/tests/specs/run/jsx_import_source/__test__.jsonc index cbda2dd32e..0350df7f17 100644 --- a/tests/specs/run/jsx_import_source/__test__.jsonc +++ b/tests/specs/run/jsx_import_source/__test__.jsonc @@ -19,6 +19,7 @@ "output": "jsx_import_source_dev.out" }, "jsx_import_source_pragma_with_config_vendor_dir": { + "tempDir": true, "args": "run --allow-import --reload --config jsx/deno-jsx.jsonc --no-lock --vendor jsx_import_source_pragma.tsx", "output": "jsx_import_source.out" }, diff --git a/tests/util/server/src/servers/mod.rs b/tests/util/server/src/servers/mod.rs index 0b1d99aeb9..4345c27cde 100644 --- a/tests/util/server/src/servers/mod.rs +++ b/tests/util/server/src/servers/mod.rs @@ -577,11 +577,6 @@ async fn main_server( ); Ok(res) } - (_, "/bad_redirect") => { - let mut res = Response::new(empty_body()); - *res.status_mut() = StatusCode::FOUND; - Ok(res) - } (_, "/server_error") => { let mut res = Response::new(empty_body()); *res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; From 59f263409eb40f282350c2fc1ca50e09a1623190 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 16 Dec 2024 19:20:53 -0500 Subject: [PATCH 008/107] refactor: use capacity_builder for writing deno compile data section (#27393) --- Cargo.lock | 5 +- Cargo.toml | 2 +- cli/Cargo.toml | 1 + cli/standalone/serialization.rs | 106 +++++++++++++++----------------- 4 files changed, 56 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 850ab038f2..b892f97fd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -670,9 +670,9 @@ checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" [[package]] name = "capacity_builder" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c0f637033edd76ceb881faaee372868a383f0ed7a4a59e8fdf90db2502f3d3" +checksum = "58ec49028cb308564429cd8fac4ef21290067a0afe8f5955330a8d487d0d790c" dependencies = [ "itoa", ] @@ -1224,6 +1224,7 @@ dependencies = [ "boxed_error", "bytes", "cache_control", + "capacity_builder", "chrono", "clap", "clap_complete", diff --git a/Cargo.toml b/Cargo.toml index 0ab9c93376..37c3091fe8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,7 @@ boxed_error = "0.2.3" brotli = "6.0.0" bytes = "1.4.0" cache_control = "=0.2.0" -capacity_builder = "0.1.0" +capacity_builder = "0.1.3" 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() diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 84464bb01f..70708002c7 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -97,6 +97,7 @@ bincode = "=1.3.3" boxed_error.workspace = true bytes.workspace = true cache_control.workspace = true +capacity_builder.workspace = true chrono = { workspace = true, features = ["now"] } clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] } clap_complete = "=4.5.24" diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index 6062e21019..ac76172e37 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -1,6 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use std::borrow::Cow; +use std::cell::Cell; use std::collections::BTreeMap; use std::collections::HashMap; use std::io::Write; @@ -42,50 +43,46 @@ pub fn serialize_binary_data_section( remote_modules: &RemoteModulesStoreBuilder, vfs: &BuiltVfs, ) -> Result, AnyError> { - fn write_bytes_with_len(bytes: &mut Vec, data: &[u8]) { - bytes.extend_from_slice(&(data.len() as u64).to_le_bytes()); - bytes.extend_from_slice(data); - } + let metadata = serde_json::to_string(metadata)?; + let npm_snapshot = + npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default(); + let remote_modules_len = Cell::new(0_u64); + let serialized_vfs = serde_json::to_string(&vfs.root)?; - let mut bytes = Vec::new(); - bytes.extend_from_slice(MAGIC_BYTES); - - // 1. Metadata - { - let metadata = serde_json::to_string(metadata)?; - write_bytes_with_len(&mut bytes, metadata.as_bytes()); - } - // 2. Npm snapshot - { - let npm_snapshot = - npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default(); - write_bytes_with_len(&mut bytes, &npm_snapshot); - } - // 3. Remote modules - { - let update_index = bytes.len(); - bytes.extend_from_slice(&(0_u64).to_le_bytes()); - let start_index = bytes.len(); - remote_modules.write(&mut bytes)?; - let length = bytes.len() - start_index; - let length_bytes = (length as u64).to_le_bytes(); - bytes[update_index..update_index + length_bytes.len()] - .copy_from_slice(&length_bytes); - } - // 4. VFS - { - let serialized_vfs = serde_json::to_string(&vfs.root)?; - write_bytes_with_len(&mut bytes, serialized_vfs.as_bytes()); - let vfs_bytes_len = vfs.files.iter().map(|f| f.len() as u64).sum::(); - bytes.extend_from_slice(&vfs_bytes_len.to_le_bytes()); - for file in &vfs.files { - bytes.extend_from_slice(file); + let bytes = capacity_builder::BytesBuilder::build(|builder| { + builder.append(MAGIC_BYTES); + // 1. Metadata + { + builder.append_le(metadata.len() as u64); + builder.append(&metadata); + } + // 2. Npm snapshot + { + builder.append_le(npm_snapshot.len() as u64); + builder.append(&npm_snapshot); + } + // 3. Remote modules + { + builder.append_le(remote_modules_len.get()); // this will be properly initialized on the second pass + let start_index = builder.len(); + remote_modules.write(builder); + remote_modules_len.set((builder.len() - start_index) as u64); + } + // 4. VFS + { + builder.append_le(serialized_vfs.len() as u64); + builder.append(&serialized_vfs); + let vfs_bytes_len = vfs.files.iter().map(|f| f.len() as u64).sum::(); + builder.append_le(vfs_bytes_len); + for file in &vfs.files { + builder.append(file); + } } - } - // write the magic bytes at the end so we can use it - // to make sure we've deserialized correctly - bytes.extend_from_slice(MAGIC_BYTES); + // write the magic bytes at the end so we can use it + // to make sure we've deserialized correctly + builder.append(MAGIC_BYTES); + })?; Ok(bytes) } @@ -191,26 +188,25 @@ impl RemoteModulesStoreBuilder { } } - fn write(&self, writer: &mut dyn Write) -> Result<(), AnyError> { - writer.write_all(&(self.specifiers.len() as u32).to_le_bytes())?; - writer.write_all(&(self.redirects.len() as u32).to_le_bytes())?; + fn write<'a>(&'a self, builder: &mut capacity_builder::BytesBuilder<'a>) { + builder.append_le(self.specifiers.len() as u32); + builder.append_le(self.redirects.len() as u32); for (specifier, offset) in &self.specifiers { - writer.write_all(&(specifier.len() as u32).to_le_bytes())?; - writer.write_all(specifier.as_bytes())?; - writer.write_all(&offset.to_le_bytes())?; + builder.append_le(specifier.len() as u32); + builder.append(specifier.as_bytes()); + builder.append_le(*offset); } for (from, to) in &self.redirects { - writer.write_all(&(from.len() as u32).to_le_bytes())?; - writer.write_all(from.as_bytes())?; - writer.write_all(&(to.len() as u32).to_le_bytes())?; - writer.write_all(to.as_bytes())?; + builder.append_le(from.len() as u32); + builder.append(from); + builder.append_le(to.len() as u32); + builder.append(to); } for (media_type, data) in &self.data { - writer.write_all(&[serialize_media_type(*media_type)])?; - writer.write_all(&(data.len() as u64).to_le_bytes())?; - writer.write_all(data)?; + builder.append(serialize_media_type(*media_type)); + builder.append_le(data.len() as u64); + builder.append(data); } - Ok(()) } } From f9add94e17ea14af67c3b70ec8581e8563679743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 17 Dec 2024 01:35:26 +0000 Subject: [PATCH 009/107] refactor(lint): renames and code flattening (#27386) Working on loading plugin configuration for https://github.com/denoland/deno/pull/27203 I encountered a lot of complexity, so did some drive-by cleanups to make it easier to grok the code and have fewer duplicate names. --- cli/args/mod.rs | 7 +- cli/lsp/diagnostics.rs | 3 +- cli/tools/lint/mod.rs | 483 ++++++++++++++++++++++------------------- 3 files changed, 263 insertions(+), 230 deletions(-) diff --git a/cli/args/mod.rs b/cli/args/mod.rs index ddf990fcab..f820afe78d 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -24,6 +24,7 @@ use deno_config::workspace::WorkspaceLintConfig; use deno_config::workspace::WorkspaceResolver; use deno_core::resolve_url_or_path; use deno_graph::GraphKind; +use deno_lint::linter::LintConfig as DenoLintConfig; use deno_npm::npm_rc::NpmRc; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; @@ -1351,9 +1352,7 @@ impl CliOptions { Ok(result) } - pub fn resolve_deno_lint_config( - &self, - ) -> Result { + pub fn resolve_deno_lint_config(&self) -> Result { let ts_config_result = self.resolve_ts_config_for_emit(TsConfigType::Emit)?; @@ -1362,7 +1361,7 @@ impl CliOptions { ts_config_result.ts_config, )?; - Ok(deno_lint::linter::LintConfig { + Ok(DenoLintConfig { default_jsx_factory: (!transpile_options.jsx_automatic) .then(|| transpile_options.jsx_factory.clone()), default_jsx_fragment_factory: (!transpile_options.jsx_automatic) diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index e99e5a19b0..804cebfb9b 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -45,6 +45,7 @@ use deno_graph::source::ResolveError; use deno_graph::Resolution; use deno_graph::ResolutionError; use deno_graph::SpecifierError; +use deno_lint::linter::LintConfig as DenoLintConfig; use deno_resolver::sloppy_imports::SloppyImportsResolution; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; use deno_runtime::deno_fs; @@ -834,7 +835,7 @@ fn generate_lint_diagnostics( lint_rule_provider.resolve_lint_rules(Default::default(), None) }, fix: false, - deno_lint_config: deno_lint::linter::LintConfig { + deno_lint_config: DenoLintConfig { default_jsx_factory: None, default_jsx_fragment_factory: None, }, diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index 596359bdc0..e49197bbad 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -20,7 +20,7 @@ use deno_core::unsync::future::LocalFutureExt; use deno_core::unsync::future::SharedLocal; use deno_graph::ModuleGraph; use deno_lint::diagnostic::LintDiagnostic; -use deno_lint::linter::LintConfig; +use deno_lint::linter::LintConfig as DenoLintConfig; use log::debug; use reporters::create_reporter; use reporters::LintReporter; @@ -29,7 +29,6 @@ use std::collections::HashSet; use std::fs; use std::io::stdin; use std::io::Read; -use std::path::Path; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; @@ -47,6 +46,7 @@ use crate::graph_util::ModuleGraphCreator; use crate::tools::fmt::run_parallelized; use crate::util::display; use crate::util::file_watcher; +use crate::util::file_watcher::WatcherCommunicator; use crate::util::fs::canonicalize_path; use crate::util::path::is_script_ext; use crate::util::sync::AtomicFlag; @@ -69,136 +69,139 @@ pub async fn lint( flags: Arc, lint_flags: LintFlags, ) -> Result<(), AnyError> { - if let Some(watch_flags) = &lint_flags.watch { + if lint_flags.watch.is_some() { if lint_flags.is_stdin() { return Err(generic_error( "Lint watch on standard input is not supported.", )); } - file_watcher::watch_func( - flags, - file_watcher::PrintConfig::new("Lint", !watch_flags.no_clear_screen), - move |flags, watcher_communicator, changed_paths| { - let lint_flags = lint_flags.clone(); - watcher_communicator.show_path_changed(changed_paths.clone()); - Ok(async move { - let factory = CliFactory::from_flags(flags); - let cli_options = factory.cli_options()?; - let lint_config = cli_options.resolve_deno_lint_config()?; - let mut paths_with_options_batches = - resolve_paths_with_options_batches(cli_options, &lint_flags)?; - for paths_with_options in &mut paths_with_options_batches { - _ = watcher_communicator - .watch_paths(paths_with_options.paths.clone()); - let files = std::mem::take(&mut paths_with_options.paths); - paths_with_options.paths = if let Some(paths) = &changed_paths { - // lint all files on any changed (https://github.com/denoland/deno/issues/12446) - files - .iter() - .any(|path| { - canonicalize_path(path) - .map(|p| paths.contains(&p)) - .unwrap_or(false) - }) - .then_some(files) - .unwrap_or_else(|| [].to_vec()) - } else { - files - }; - } + return lint_with_watch(flags, lint_flags).await; + } - let mut linter = WorkspaceLinter::new( - factory.caches()?.clone(), - factory.lint_rule_provider().await?, - factory.module_graph_creator().await?.clone(), - cli_options.start_dir.clone(), - &cli_options.resolve_workspace_lint_options(&lint_flags)?, - ); - for paths_with_options in paths_with_options_batches { - linter - .lint_files( - cli_options, - paths_with_options.options, - lint_config.clone(), - paths_with_options.dir, - paths_with_options.paths, - ) - .await?; - } - - linter.finish(); - - Ok(()) - }) - }, - ) - .await?; + let factory = CliFactory::from_flags(flags); + let cli_options = factory.cli_options()?; + let lint_rule_provider = factory.lint_rule_provider().await?; + let is_stdin = lint_flags.is_stdin(); + let deno_lint_config = cli_options.resolve_deno_lint_config()?; + let workspace_lint_options = + cli_options.resolve_workspace_lint_options(&lint_flags)?; + let success = if is_stdin { + lint_stdin( + cli_options, + lint_rule_provider, + workspace_lint_options, + lint_flags, + deno_lint_config, + )? } else { - let factory = CliFactory::from_flags(flags); - let cli_options = factory.cli_options()?; - let is_stdin = lint_flags.is_stdin(); - let deno_lint_config = cli_options.resolve_deno_lint_config()?; - let workspace_lint_options = - cli_options.resolve_workspace_lint_options(&lint_flags)?; - let success = if is_stdin { - let start_dir = &cli_options.start_dir; - let reporter_lock = Arc::new(Mutex::new(create_reporter( - workspace_lint_options.reporter_kind, - ))); - let lint_config = start_dir - .to_lint_config(FilePatterns::new_with_base(start_dir.dir_path()))?; - let lint_options = LintOptions::resolve(lint_config, &lint_flags); - let lint_rules = factory - .lint_rule_provider() - .await? - .resolve_lint_rules_err_empty( - lint_options.rules, - start_dir.maybe_deno_json().map(|c| c.as_ref()), - )?; - let mut file_path = cli_options.initial_cwd().join(STDIN_FILE_NAME); - if let Some(ext) = cli_options.ext_flag() { - file_path.set_extension(ext); - } - let r = lint_stdin(&file_path, lint_rules, deno_lint_config); - let success = handle_lint_result( - &file_path.to_string_lossy(), - r, - reporter_lock.clone(), - ); - reporter_lock.lock().close(1); - success - } else { - let mut linter = WorkspaceLinter::new( - factory.caches()?.clone(), - factory.lint_rule_provider().await?, - factory.module_graph_creator().await?.clone(), - cli_options.start_dir.clone(), - &workspace_lint_options, - ); - let paths_with_options_batches = - resolve_paths_with_options_batches(cli_options, &lint_flags)?; - for paths_with_options in paths_with_options_batches { - linter - .lint_files( - cli_options, - paths_with_options.options, - deno_lint_config.clone(), - paths_with_options.dir, - paths_with_options.paths, - ) - .await?; - } - linter.finish() - }; - if !success { - deno_runtime::exit(1); + let mut linter = WorkspaceLinter::new( + factory.caches()?.clone(), + lint_rule_provider, + factory.module_graph_creator().await?.clone(), + cli_options.start_dir.clone(), + &workspace_lint_options, + ); + let paths_with_options_batches = + resolve_paths_with_options_batches(cli_options, &lint_flags)?; + for paths_with_options in paths_with_options_batches { + linter + .lint_files( + cli_options, + paths_with_options.options, + deno_lint_config.clone(), + paths_with_options.dir, + paths_with_options.paths, + ) + .await?; } + linter.finish() + }; + if !success { + deno_runtime::exit(1); } Ok(()) } +async fn lint_with_watch_inner( + flags: Arc, + lint_flags: LintFlags, + watcher_communicator: Arc, + changed_paths: Option>, +) -> Result<(), AnyError> { + let factory = CliFactory::from_flags(flags); + let cli_options = factory.cli_options()?; + let lint_config = cli_options.resolve_deno_lint_config()?; + let mut paths_with_options_batches = + resolve_paths_with_options_batches(cli_options, &lint_flags)?; + for paths_with_options in &mut paths_with_options_batches { + _ = watcher_communicator.watch_paths(paths_with_options.paths.clone()); + + let files = std::mem::take(&mut paths_with_options.paths); + paths_with_options.paths = if let Some(paths) = &changed_paths { + // lint all files on any changed (https://github.com/denoland/deno/issues/12446) + files + .iter() + .any(|path| { + canonicalize_path(path) + .map(|p| paths.contains(&p)) + .unwrap_or(false) + }) + .then_some(files) + .unwrap_or_else(|| [].to_vec()) + } else { + files + }; + } + + let mut linter = WorkspaceLinter::new( + factory.caches()?.clone(), + factory.lint_rule_provider().await?, + factory.module_graph_creator().await?.clone(), + cli_options.start_dir.clone(), + &cli_options.resolve_workspace_lint_options(&lint_flags)?, + ); + for paths_with_options in paths_with_options_batches { + linter + .lint_files( + cli_options, + paths_with_options.options, + lint_config.clone(), + paths_with_options.dir, + paths_with_options.paths, + ) + .await?; + } + + linter.finish(); + + Ok(()) +} + +async fn lint_with_watch( + flags: Arc, + lint_flags: LintFlags, +) -> Result<(), AnyError> { + let watch_flags = lint_flags.watch.as_ref().unwrap(); + + file_watcher::watch_func( + flags, + file_watcher::PrintConfig::new("Lint", !watch_flags.no_clear_screen), + move |flags, watcher_communicator, changed_paths| { + let lint_flags = lint_flags.clone(); + watcher_communicator.show_path_changed(changed_paths.clone()); + Ok(lint_with_watch_inner( + flags, + lint_flags, + watcher_communicator, + changed_paths, + )) + }, + ) + .await +} + struct PathsWithOptions { dir: WorkspaceDirectory, paths: Vec, @@ -269,7 +272,7 @@ impl WorkspaceLinter { &mut self, cli_options: &Arc, lint_options: LintOptions, - lint_config: LintConfig, + lint_config: DenoLintConfig, member_dir: WorkspaceDirectory, paths: Vec, ) -> Result<(), AnyError> { @@ -294,112 +297,63 @@ impl WorkspaceLinter { deno_lint_config: lint_config, })); + let has_error = self.has_error.clone(); + let reporter_lock = self.reporter_lock.clone(); + let mut futures = Vec::with_capacity(2); if linter.has_package_rules() { - if self.workspace_module_graph.is_none() { - let module_graph_creator = self.module_graph_creator.clone(); - let packages = self.workspace_dir.jsr_packages_for_publish(); - self.workspace_module_graph = Some( - async move { - module_graph_creator - .create_and_validate_publish_graph(&packages, true) - .await - .map(Rc::new) - .map_err(Rc::new) - } - .boxed_local() - .shared_local(), - ); - } - let workspace_module_graph_future = - self.workspace_module_graph.as_ref().unwrap().clone(); - let publish_config = member_dir.maybe_package_config(); - if let Some(publish_config) = publish_config { - let has_error = self.has_error.clone(); - let reporter_lock = self.reporter_lock.clone(); - let linter = linter.clone(); - let path_urls = paths - .iter() - .filter_map(|p| ModuleSpecifier::from_file_path(p).ok()) - .collect::>(); - futures.push( - async move { - let graph = workspace_module_graph_future - .await - .map_err(|err| anyhow!("{:#}", err))?; - let export_urls = - publish_config.config_file.resolve_export_value_urls()?; - if !export_urls.iter().any(|url| path_urls.contains(url)) { - return Ok(()); // entrypoint is not specified, so skip - } - let diagnostics = linter.lint_package(&graph, &export_urls); - if !diagnostics.is_empty() { - has_error.raise(); - let mut reporter = reporter_lock.lock(); - for diagnostic in &diagnostics { - reporter.visit_diagnostic(diagnostic); - } - } - Ok(()) - } - .boxed_local(), - ); + if let Some(fut) = self.run_package_rules(&linter, &member_dir, &paths) { + futures.push(fut); } } - futures.push({ - let has_error = self.has_error.clone(); - let reporter_lock = self.reporter_lock.clone(); - let maybe_incremental_cache = maybe_incremental_cache.clone(); - let linter = linter.clone(); - let cli_options = cli_options.clone(); - async move { - run_parallelized(paths, { - move |file_path| { - let file_text = - deno_ast::strip_bom(fs::read_to_string(&file_path)?); + let maybe_incremental_cache_ = maybe_incremental_cache.clone(); + let linter = linter.clone(); + let cli_options = cli_options.clone(); + let fut = async move { + let operation = move |file_path: PathBuf| { + let file_text = deno_ast::strip_bom(fs::read_to_string(&file_path)?); - // don't bother rechecking this file if it didn't have any diagnostics before - if let Some(incremental_cache) = &maybe_incremental_cache { - if incremental_cache.is_file_same(&file_path, &file_text) { - return Ok(()); - } - } - - let r = linter.lint_file( - &file_path, - file_text, - cli_options.ext_flag().as_deref(), - ); - if let Ok((file_source, file_diagnostics)) = &r { - if let Some(incremental_cache) = &maybe_incremental_cache { - if file_diagnostics.is_empty() { - // update the incremental cache if there were no diagnostics - incremental_cache.update_file( - &file_path, - // ensure the returned text is used here as it may have been modified via --fix - file_source.text(), - ) - } - } - } - - let success = handle_lint_result( - &file_path.to_string_lossy(), - r, - reporter_lock.clone(), - ); - if !success { - has_error.raise(); - } - - Ok(()) + // don't bother rechecking this file if it didn't have any diagnostics before + if let Some(incremental_cache) = &maybe_incremental_cache_ { + if incremental_cache.is_file_same(&file_path, &file_text) { + return Ok(()); } - }) - .await - } - .boxed_local() - }); + } + + let r = linter.lint_file( + &file_path, + file_text, + cli_options.ext_flag().as_deref(), + ); + if let Ok((file_source, file_diagnostics)) = &r { + if let Some(incremental_cache) = &maybe_incremental_cache_ { + if file_diagnostics.is_empty() { + // update the incremental cache if there were no diagnostics + incremental_cache.update_file( + &file_path, + // ensure the returned text is used here as it may have been modified via --fix + file_source.text(), + ) + } + } + } + + let success = handle_lint_result( + &file_path.to_string_lossy(), + r, + reporter_lock.clone(), + ); + if !success { + has_error.raise(); + } + + Ok(()) + }; + run_parallelized(paths, operation).await + } + .boxed_local(); + futures.push(fut); if lint_options.fix { // run sequentially when using `--fix` to lower the chances of weird @@ -419,6 +373,63 @@ impl WorkspaceLinter { Ok(()) } + fn run_package_rules( + &mut self, + linter: &Arc, + member_dir: &WorkspaceDirectory, + paths: &[PathBuf], + ) -> Option>> { + if self.workspace_module_graph.is_none() { + let module_graph_creator = self.module_graph_creator.clone(); + let packages = self.workspace_dir.jsr_packages_for_publish(); + self.workspace_module_graph = Some( + async move { + module_graph_creator + .create_and_validate_publish_graph(&packages, true) + .await + .map(Rc::new) + .map_err(Rc::new) + } + .boxed_local() + .shared_local(), + ); + } + + let workspace_module_graph_future = + self.workspace_module_graph.as_ref().unwrap().clone(); + let maybe_publish_config = member_dir.maybe_package_config(); + let publish_config = maybe_publish_config?; + + let has_error = self.has_error.clone(); + let reporter_lock = self.reporter_lock.clone(); + let linter = linter.clone(); + let path_urls = paths + .iter() + .filter_map(|p| ModuleSpecifier::from_file_path(p).ok()) + .collect::>(); + let fut = async move { + let graph = workspace_module_graph_future + .await + .map_err(|err| anyhow!("{:#}", err))?; + let export_urls = + publish_config.config_file.resolve_export_value_urls()?; + if !export_urls.iter().any(|url| path_urls.contains(url)) { + return Ok(()); // entrypoint is not specified, so skip + } + let diagnostics = linter.lint_package(&graph, &export_urls); + if !diagnostics.is_empty() { + has_error.raise(); + let mut reporter = reporter_lock.lock(); + for diagnostic in &diagnostics { + reporter.visit_diagnostic(diagnostic); + } + } + Ok(()) + } + .boxed_local(); + Some(fut) + } + pub fn finish(self) -> bool { debug!("Found {} files", self.file_count); self.reporter_lock.lock().close(self.file_count); @@ -494,10 +505,27 @@ pub fn print_rules_list(json: bool, maybe_rules_tags: Option>) { /// Treats input as TypeScript. /// Compatible with `--json` flag. fn lint_stdin( - file_path: &Path, - configured_rules: ConfiguredRules, - deno_lint_config: LintConfig, -) -> Result<(ParsedSource, Vec), AnyError> { + cli_options: &Arc, + lint_rule_provider: LintRuleProvider, + workspace_lint_options: WorkspaceLintOptions, + lint_flags: LintFlags, + deno_lint_config: DenoLintConfig, +) -> Result { + let start_dir = &cli_options.start_dir; + let reporter_lock = Arc::new(Mutex::new(create_reporter( + workspace_lint_options.reporter_kind, + ))); + let lint_config = start_dir + .to_lint_config(FilePatterns::new_with_base(start_dir.dir_path()))?; + let lint_options = LintOptions::resolve(lint_config, &lint_flags); + let configured_rules = lint_rule_provider.resolve_lint_rules_err_empty( + lint_options.rules, + start_dir.maybe_deno_json().map(|c| c.as_ref()), + )?; + let mut file_path = cli_options.initial_cwd().join(STDIN_FILE_NAME); + if let Some(ext) = cli_options.ext_flag() { + file_path.set_extension(ext); + } let mut source_code = String::new(); if stdin().read_to_string(&mut source_code).is_err() { return Err(generic_error("Failed to read from stdin")); @@ -509,9 +537,14 @@ fn lint_stdin( deno_lint_config, }); - linter - .lint_file(file_path, deno_ast::strip_bom(source_code), None) - .map_err(AnyError::from) + let r = linter + .lint_file(&file_path, deno_ast::strip_bom(source_code), None) + .map_err(AnyError::from); + + let success = + handle_lint_result(&file_path.to_string_lossy(), r, reporter_lock.clone()); + reporter_lock.lock().close(1); + Ok(success) } fn handle_lint_result( From c56274285da8f4b2ea4da9be4b5a104d405201f4 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Tue, 17 Dec 2024 11:45:18 +0900 Subject: [PATCH 010/107] feat(permission): separate PermissionDeniedError to Retryable and Fatal (#27282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit separates `PermissionDeniedError` into two kinds; `Retryable` and `Fatal`. The existing `PermissionDeniedError`s fall into `Retryable`, since permission errors can be resolved by retrying with proper permissions in Deno CLI. The motivation of adding `Fatal` is that in some environments some operations are just disabled; for instance, in Deno Deploy, any write operations to filesystem can never be granted, in which case `Fatal` kind becomes useful. Co-authored-by: Bartek Iwańczuk --- runtime/permissions/lib.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index a1a217738d..bbd0301db4 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -39,10 +39,11 @@ pub use prompter::PromptCallback; pub use prompter::PromptResponse; #[derive(Debug, thiserror::Error)] -#[error("Requires {access}, {}", format_permission_error(.name))] -pub struct PermissionDeniedError { - pub access: String, - pub name: &'static str, +pub enum PermissionDeniedError { + #[error("Requires {access}, {}", format_permission_error(.name))] + Retryable { access: String, name: &'static str }, + #[error("Requires {access}, which cannot be granted in this environment")] + Fatal { access: String }, } fn format_permission_error(name: &'static str) -> String { @@ -144,11 +145,11 @@ impl PermissionState { ) } - fn error( + fn retryable_error( name: &'static str, info: impl FnOnce() -> Option, ) -> PermissionDeniedError { - PermissionDeniedError { + PermissionDeniedError::Retryable { access: Self::fmt_access(name, info), name, } @@ -201,10 +202,12 @@ impl PermissionState { Self::log_perm_access(name, info); (Ok(()), true, true) } - PromptResponse::Deny => (Err(Self::error(name, info)), true, false), + PromptResponse::Deny => { + (Err(Self::retryable_error(name, info)), true, false) + } } } - _ => (Err(Self::error(name, info)), false, false), + _ => (Err(Self::retryable_error(name, info)), false, false), } } } From ee9f24cdcd65983c0e856622be115928d343da1e Mon Sep 17 00:00:00 2001 From: denobot <33910674+denobot@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:03:58 -0500 Subject: [PATCH 011/107] chore: release "deno_*" crates (#27402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: bartlomieju Co-authored-by: Bartek Iwańczuk --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 8 ++--- Cargo.lock | 60 ++++++++++++++++---------------- Cargo.toml | 60 ++++++++++++++++---------------- bench_util/Cargo.toml | 2 +- ext/broadcast_channel/Cargo.toml | 2 +- ext/cache/Cargo.toml | 2 +- ext/canvas/Cargo.toml | 2 +- ext/console/Cargo.toml | 2 +- ext/cron/Cargo.toml | 2 +- ext/crypto/Cargo.toml | 2 +- ext/fetch/Cargo.toml | 2 +- ext/ffi/Cargo.toml | 2 +- ext/fs/Cargo.toml | 2 +- ext/http/Cargo.toml | 2 +- ext/io/Cargo.toml | 2 +- ext/kv/Cargo.toml | 2 +- ext/napi/Cargo.toml | 2 +- ext/napi/sym/Cargo.toml | 2 +- ext/net/Cargo.toml | 2 +- ext/node/Cargo.toml | 2 +- ext/telemetry/Cargo.toml | 2 +- ext/tls/Cargo.toml | 2 +- ext/url/Cargo.toml | 2 +- ext/web/Cargo.toml | 2 +- ext/webgpu/Cargo.toml | 2 +- ext/webidl/Cargo.toml | 2 +- ext/websocket/Cargo.toml | 2 +- ext/webstorage/Cargo.toml | 2 +- resolvers/deno/Cargo.toml | 2 +- resolvers/node/Cargo.toml | 2 +- resolvers/npm_cache/Cargo.toml | 2 +- runtime/Cargo.toml | 2 +- runtime/permissions/Cargo.toml | 2 +- 34 files changed, 95 insertions(+), 95 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index c22f87a861..6fbcd8f242 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -5,7 +5,7 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify"; // Bump this number when you want to purge the cache. // Note: the tools/release/01_bump_crate_versions.ts script will update this version // automatically via regex, so ensure that this line maintains this format. -const cacheVersion = 30; +const cacheVersion = 31; const ubuntuX86Runner = "ubuntu-24.04"; const ubuntuX86XlRunner = "ubuntu-24.04-xl"; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa7500f7c6..2f5d8f5b6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -184,8 +184,8 @@ jobs: ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db - key: '30-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '30-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' + key: '31-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '31-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' if: '!(matrix.skip)' - uses: dsherret/rust-toolchain-file@v1 if: '!(matrix.skip)' @@ -379,7 +379,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '30-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '31-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 @@ -689,7 +689,7 @@ jobs: !./target/*/gn_root !./target/*/*.zip !./target/*/*.tar.gz - key: '30-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '31-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-24.04 diff --git a/Cargo.lock b/Cargo.lock index b892f97fd5..6cfb3779e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "deno_bench_util" -version = "0.176.0" +version = "0.177.0" dependencies = [ "bencher", "deno_core", @@ -1400,7 +1400,7 @@ dependencies = [ [[package]] name = "deno_broadcast_channel" -version = "0.176.0" +version = "0.177.0" dependencies = [ "async-trait", "deno_core", @@ -1411,7 +1411,7 @@ dependencies = [ [[package]] name = "deno_cache" -version = "0.114.0" +version = "0.115.0" dependencies = [ "async-trait", "deno_core", @@ -1452,7 +1452,7 @@ dependencies = [ [[package]] name = "deno_canvas" -version = "0.51.0" +version = "0.52.0" dependencies = [ "deno_core", "deno_webgpu", @@ -1487,7 +1487,7 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.182.0" +version = "0.183.0" dependencies = [ "deno_core", ] @@ -1536,7 +1536,7 @@ checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" [[package]] name = "deno_cron" -version = "0.62.0" +version = "0.63.0" dependencies = [ "anyhow", "async-trait", @@ -1549,7 +1549,7 @@ dependencies = [ [[package]] name = "deno_crypto" -version = "0.196.0" +version = "0.197.0" dependencies = [ "aes", "aes-gcm", @@ -1639,7 +1639,7 @@ dependencies = [ [[package]] name = "deno_fetch" -version = "0.206.0" +version = "0.207.0" dependencies = [ "base64 0.21.7", "bytes", @@ -1674,7 +1674,7 @@ dependencies = [ [[package]] name = "deno_ffi" -version = "0.169.0" +version = "0.170.0" dependencies = [ "deno_core", "deno_permissions", @@ -1694,7 +1694,7 @@ dependencies = [ [[package]] name = "deno_fs" -version = "0.92.0" +version = "0.93.0" dependencies = [ "async-trait", "base32", @@ -1747,7 +1747,7 @@ dependencies = [ [[package]] name = "deno_http" -version = "0.180.0" +version = "0.181.0" dependencies = [ "async-compression", "async-trait", @@ -1786,7 +1786,7 @@ dependencies = [ [[package]] name = "deno_io" -version = "0.92.0" +version = "0.93.0" dependencies = [ "async-trait", "deno_core", @@ -1807,7 +1807,7 @@ dependencies = [ [[package]] name = "deno_kv" -version = "0.90.0" +version = "0.91.0" dependencies = [ "anyhow", "async-trait", @@ -1880,7 +1880,7 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.113.0" +version = "0.114.0" dependencies = [ "deno_core", "deno_permissions", @@ -1908,7 +1908,7 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.174.0" +version = "0.175.0" dependencies = [ "deno_core", "deno_permissions", @@ -1925,7 +1925,7 @@ dependencies = [ [[package]] name = "deno_node" -version = "0.119.0" +version = "0.120.0" dependencies = [ "aead-gcm-stream", "aes", @@ -2036,7 +2036,7 @@ dependencies = [ [[package]] name = "deno_npm_cache" -version = "0.2.0" +version = "0.3.0" dependencies = [ "anyhow", "async-trait", @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "deno_permissions" -version = "0.42.0" +version = "0.43.0" dependencies = [ "capacity_builder", "deno_core", @@ -2128,7 +2128,7 @@ dependencies = [ [[package]] name = "deno_resolver" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "base32", @@ -2147,7 +2147,7 @@ dependencies = [ [[package]] name = "deno_runtime" -version = "0.191.0" +version = "0.192.0" dependencies = [ "color-print", "deno_ast", @@ -2248,7 +2248,7 @@ dependencies = [ [[package]] name = "deno_telemetry" -version = "0.4.0" +version = "0.5.0" dependencies = [ "async-trait", "deno_core", @@ -2289,7 +2289,7 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.169.0" +version = "0.170.0" dependencies = [ "deno_core", "deno_native_certs", @@ -2339,7 +2339,7 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.182.0" +version = "0.183.0" dependencies = [ "deno_bench_util", "deno_console", @@ -2351,7 +2351,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.213.0" +version = "0.214.0" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -2373,7 +2373,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.149.0" +version = "0.150.0" dependencies = [ "deno_core", "raw-window-handle", @@ -2386,7 +2386,7 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.182.0" +version = "0.183.0" dependencies = [ "deno_bench_util", "deno_core", @@ -2394,7 +2394,7 @@ dependencies = [ [[package]] name = "deno_websocket" -version = "0.187.0" +version = "0.188.0" dependencies = [ "bytes", "deno_core", @@ -2416,7 +2416,7 @@ dependencies = [ [[package]] name = "deno_webstorage" -version = "0.177.0" +version = "0.178.0" dependencies = [ "deno_core", "deno_web", @@ -4930,7 +4930,7 @@ dependencies = [ [[package]] name = "napi_sym" -version = "0.112.0" +version = "0.113.0" dependencies = [ "quote", "serde", @@ -4985,7 +4985,7 @@ dependencies = [ [[package]] name = "node_resolver" -version = "0.21.0" +version = "0.22.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 37c3091fe8..0f52c28de6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,17 +50,17 @@ repository = "https://github.com/denoland/deno" deno_ast = { version = "=0.44.0", features = ["transpiling"] } deno_core = { version = "0.326.0" } -deno_bench_util = { version = "0.176.0", path = "./bench_util" } +deno_bench_util = { version = "0.177.0", path = "./bench_util" } deno_config = { version = "=0.39.3", features = ["workspace", "sync"] } deno_lockfile = "=0.23.2" deno_media_type = { version = "0.2.0", features = ["module_specifier"] } deno_npm = "=0.26.0" deno_path_util = "=0.2.2" -deno_permissions = { version = "0.42.0", path = "./runtime/permissions" } -deno_runtime = { version = "0.191.0", path = "./runtime" } +deno_permissions = { version = "0.43.0", path = "./runtime/permissions" } +deno_runtime = { version = "0.192.0", path = "./runtime" } deno_semver = "=0.6.1" deno_terminal = "0.2.0" -napi_sym = { version = "0.112.0", path = "./ext/napi/sym" } +napi_sym = { version = "0.113.0", path = "./ext/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } denokv_proto = "0.8.4" @@ -69,34 +69,34 @@ denokv_remote = "0.8.4" denokv_sqlite = { default-features = false, version = "0.8.4" } # exts -deno_broadcast_channel = { version = "0.176.0", path = "./ext/broadcast_channel" } -deno_cache = { version = "0.114.0", path = "./ext/cache" } -deno_canvas = { version = "0.51.0", path = "./ext/canvas" } -deno_console = { version = "0.182.0", path = "./ext/console" } -deno_cron = { version = "0.62.0", path = "./ext/cron" } -deno_crypto = { version = "0.196.0", path = "./ext/crypto" } -deno_fetch = { version = "0.206.0", path = "./ext/fetch" } -deno_ffi = { version = "0.169.0", path = "./ext/ffi" } -deno_fs = { version = "0.92.0", path = "./ext/fs" } -deno_http = { version = "0.180.0", path = "./ext/http" } -deno_io = { version = "0.92.0", path = "./ext/io" } -deno_kv = { version = "0.90.0", path = "./ext/kv" } -deno_napi = { version = "0.113.0", path = "./ext/napi" } -deno_net = { version = "0.174.0", path = "./ext/net" } -deno_node = { version = "0.119.0", path = "./ext/node" } -deno_telemetry = { version = "0.4.0", path = "./ext/telemetry" } -deno_tls = { version = "0.169.0", path = "./ext/tls" } -deno_url = { version = "0.182.0", path = "./ext/url" } -deno_web = { version = "0.213.0", path = "./ext/web" } -deno_webgpu = { version = "0.149.0", path = "./ext/webgpu" } -deno_webidl = { version = "0.182.0", path = "./ext/webidl" } -deno_websocket = { version = "0.187.0", path = "./ext/websocket" } -deno_webstorage = { version = "0.177.0", path = "./ext/webstorage" } +deno_broadcast_channel = { version = "0.177.0", path = "./ext/broadcast_channel" } +deno_cache = { version = "0.115.0", path = "./ext/cache" } +deno_canvas = { version = "0.52.0", path = "./ext/canvas" } +deno_console = { version = "0.183.0", path = "./ext/console" } +deno_cron = { version = "0.63.0", path = "./ext/cron" } +deno_crypto = { version = "0.197.0", path = "./ext/crypto" } +deno_fetch = { version = "0.207.0", path = "./ext/fetch" } +deno_ffi = { version = "0.170.0", path = "./ext/ffi" } +deno_fs = { version = "0.93.0", path = "./ext/fs" } +deno_http = { version = "0.181.0", path = "./ext/http" } +deno_io = { version = "0.93.0", path = "./ext/io" } +deno_kv = { version = "0.91.0", path = "./ext/kv" } +deno_napi = { version = "0.114.0", path = "./ext/napi" } +deno_net = { version = "0.175.0", path = "./ext/net" } +deno_node = { version = "0.120.0", path = "./ext/node" } +deno_telemetry = { version = "0.5.0", path = "./ext/telemetry" } +deno_tls = { version = "0.170.0", path = "./ext/tls" } +deno_url = { version = "0.183.0", path = "./ext/url" } +deno_web = { version = "0.214.0", path = "./ext/web" } +deno_webgpu = { version = "0.150.0", path = "./ext/webgpu" } +deno_webidl = { version = "0.183.0", path = "./ext/webidl" } +deno_websocket = { version = "0.188.0", path = "./ext/websocket" } +deno_webstorage = { version = "0.178.0", path = "./ext/webstorage" } # resolvers -deno_npm_cache = { version = "0.2.0", path = "./resolvers/npm_cache" } -deno_resolver = { version = "0.14.0", path = "./resolvers/deno" } -node_resolver = { version = "0.21.0", path = "./resolvers/node" } +deno_npm_cache = { version = "0.3.0", path = "./resolvers/npm_cache" } +deno_resolver = { version = "0.15.0", path = "./resolvers/deno" } +node_resolver = { version = "0.22.0", path = "./resolvers/node" } aes = "=0.8.3" anyhow = "1.0.57" diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index 9833996fd3..8a20f07638 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_bench_util" -version = "0.176.0" +version = "0.177.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index 5b238aad25..714f230cd2 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_broadcast_channel" -version = "0.176.0" +version = "0.177.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index d03779d364..7c05996498 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cache" -version = "0.114.0" +version = "0.115.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index c851b7724a..ac9b236a95 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_canvas" -version = "0.51.0" +version = "0.52.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/console/Cargo.toml b/ext/console/Cargo.toml index 4a26917933..df67b14a86 100644 --- a/ext/console/Cargo.toml +++ b/ext/console/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_console" -version = "0.182.0" +version = "0.183.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index d8f2d949f7..c5408e450b 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cron" -version = "0.62.0" +version = "0.63.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 63656bf642..86d984a421 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_crypto" -version = "0.196.0" +version = "0.197.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index 716d268a04..98d2fdf5da 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fetch" -version = "0.206.0" +version = "0.207.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index d54249329d..afcbf7b4e6 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_ffi" -version = "0.169.0" +version = "0.170.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index d11520ad8c..608554607c 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fs" -version = "0.92.0" +version = "0.93.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index 27a91ca61b..dfb53559d6 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_http" -version = "0.180.0" +version = "0.181.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 1b73bad348..7a464ecde9 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_io" -version = "0.92.0" +version = "0.93.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index c69a962fa3..e65880942b 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_kv" -version = "0.90.0" +version = "0.91.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index 783b4b7cff..5a9eb7441f 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_napi" -version = "0.113.0" +version = "0.114.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/sym/Cargo.toml b/ext/napi/sym/Cargo.toml index a3dd56e2bf..b07dadd634 100644 --- a/ext/napi/sym/Cargo.toml +++ b/ext/napi/sym/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "napi_sym" -version = "0.112.0" +version = "0.113.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index f5aa32c8ce..546152bd4b 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_net" -version = "0.174.0" +version = "0.175.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 1bfe3a4d8d..8ba1141ec1 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_node" -version = "0.119.0" +version = "0.120.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/telemetry/Cargo.toml b/ext/telemetry/Cargo.toml index f3d4bbd336..d0bdc6be1b 100644 --- a/ext/telemetry/Cargo.toml +++ b/ext/telemetry/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_telemetry" -version = "0.4.0" +version = "0.5.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index fc75b0a05d..690267b7e0 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_tls" -version = "0.169.0" +version = "0.170.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index d30332d0ff..de4fc67df8 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_url" -version = "0.182.0" +version = "0.183.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index ac2b14fbed..b4cd69f970 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_web" -version = "0.213.0" +version = "0.214.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 9621c085e8..858cdb2dab 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.149.0" +version = "0.150.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" diff --git a/ext/webidl/Cargo.toml b/ext/webidl/Cargo.toml index 38eff7b66f..0ad7d8ac10 100644 --- a/ext/webidl/Cargo.toml +++ b/ext/webidl/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webidl" -version = "0.182.0" +version = "0.183.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index cb72618cad..2cd48a3816 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_websocket" -version = "0.187.0" +version = "0.188.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index 700a252016..ff76458f33 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webstorage" -version = "0.177.0" +version = "0.178.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 4dca044377..a7273c7e73 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_resolver" -version = "0.14.0" +version = "0.15.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/node/Cargo.toml b/resolvers/node/Cargo.toml index e175bcfafa..52aedbee9d 100644 --- a/resolvers/node/Cargo.toml +++ b/resolvers/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "node_resolver" -version = "0.21.0" +version = "0.22.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/npm_cache/Cargo.toml b/resolvers/npm_cache/Cargo.toml index 1cc7237025..a0a106c89b 100644 --- a/resolvers/npm_cache/Cargo.toml +++ b/resolvers/npm_cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_npm_cache" -version = "0.2.0" +version = "0.3.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index cb12abb141..4612e87887 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_runtime" -version = "0.191.0" +version = "0.192.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index dc46b03310..a7bd342a9c 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_permissions" -version = "0.42.0" +version = "0.43.0" authors.workspace = true edition.workspace = true license.workspace = true From d632ec9e707c8f8ebe4956828ff3f22e479861c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 17 Dec 2024 17:36:32 +0000 Subject: [PATCH 012/107] fix: deno_resolver crate without 'sync' feature (#27403) --- resolvers/deno/sync.rs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/resolvers/deno/sync.rs b/resolvers/deno/sync.rs index 6e62336901..ebcf8509d5 100644 --- a/resolvers/deno/sync.rs +++ b/resolvers/deno/sync.rs @@ -13,21 +13,35 @@ mod inner { #[cfg(not(feature = "sync"))] mod inner { + use std::cell::Ref; + use std::cell::RefCell; + use std::collections::HashMap; + use std::hash::BuildHasher; + use std::hash::Hash; use std::hash::RandomState; pub use std::rc::Rc as MaybeArc; // Wrapper struct that exposes a subset of `DashMap` API. - #[derive(Default)] - struct MaybeDashMap(RefCell>); + #[derive(Debug)] + pub struct MaybeDashMap(RefCell>); - impl MaybeDashMap { - pub fn get(&'a self, key: &K) -> Option<&'a V> { - let inner = self.0.borrow(); - inner.get(key) + impl Default for MaybeDashMap + where + K: Eq + Hash, + S: Default + BuildHasher + Clone, + { + fn default() -> Self { + Self(RefCell::new(Default::default())) + } + } + + impl MaybeDashMap { + pub fn get<'a>(&'a self, key: &K) -> Option> { + Ref::filter_map(self.0.borrow(), |map| map.get(key)).ok() } pub fn insert(&self, key: K, value: V) -> Option { - let inner = self.0.borrow_mut(); + let mut inner = self.0.borrow_mut(); inner.insert(key, value) } } From 2820ba1e22f6487b5fc2f60f434acd870f63b2f8 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Tue, 17 Dec 2024 18:55:17 +0000 Subject: [PATCH 013/107] fix(lsp): include "node:" prefix for node builtin auto-imports (#27404) --- cli/lsp/analysis.rs | 4 ++ cli/lsp/tsc.rs | 51 ++++++++++++++++++++------ tests/integration/lsp_tests.rs | 67 ++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 12 deletions(-) diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index c128372dcd..9f9cf14864 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -270,6 +270,10 @@ impl<'a> TsResponseImportMapper<'a> { } } + if specifier.scheme() == "node" { + return Some(specifier.to_string()); + } + if let Some(jsr_path) = specifier.as_str().strip_prefix(jsr_url().as_str()) { let mut segments = jsr_path.split('/'); diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 6ae6265688..f8b972511f 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -64,6 +64,7 @@ use deno_core::OpState; use deno_core::PollEventLoopOptions; use deno_core::RuntimeOptions; use deno_path_util::url_to_file_path; +use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES; use deno_runtime::inspector_server::InspectorServer; use deno_runtime::tokio_util::create_basic_runtime; use indexmap::IndexMap; @@ -3411,10 +3412,18 @@ fn parse_code_actions( additional_text_edits.extend(change.text_changes.iter().map(|tc| { let mut text_edit = tc.as_text_edit(asset_or_doc.line_index()); if let Some(specifier_rewrite) = &data.specifier_rewrite { - text_edit.new_text = text_edit.new_text.replace( - &specifier_rewrite.old_specifier, - &specifier_rewrite.new_specifier, - ); + let specifier_index = text_edit + .new_text + .char_indices() + .find_map(|(b, c)| (c == '\'' || c == '"').then_some(b)); + if let Some(i) = specifier_index { + let mut specifier_part = text_edit.new_text.split_off(i); + specifier_part = specifier_part.replace( + &specifier_rewrite.old_specifier, + &specifier_rewrite.new_specifier, + ); + text_edit.new_text.push_str(&specifier_part); + } if let Some(deno_types_specifier) = &specifier_rewrite.new_deno_types_specifier { @@ -3587,10 +3596,17 @@ impl CompletionEntryDetails { &mut insert_replace_edit.new_text } }; - *new_text = new_text.replace( - &specifier_rewrite.old_specifier, - &specifier_rewrite.new_specifier, - ); + let specifier_index = new_text + .char_indices() + .find_map(|(b, c)| (c == '\'' || c == '"').then_some(b)); + if let Some(i) = specifier_index { + let mut specifier_part = new_text.split_off(i); + specifier_part = specifier_part.replace( + &specifier_rewrite.old_specifier, + &specifier_rewrite.new_specifier, + ); + new_text.push_str(&specifier_part); + } if let Some(deno_types_specifier) = &specifier_rewrite.new_deno_types_specifier { @@ -3729,7 +3745,7 @@ pub struct CompletionItemData { #[serde(rename_all = "camelCase")] struct CompletionEntryDataAutoImport { module_specifier: String, - file_name: String, + file_name: Option, } #[derive(Debug)] @@ -3786,9 +3802,20 @@ impl CompletionEntry { else { return; }; - if let Ok(normalized) = specifier_map.normalize(&raw.file_name) { - self.auto_import_data = - Some(CompletionNormalizedAutoImportData { raw, normalized }); + if let Some(file_name) = &raw.file_name { + if let Ok(normalized) = specifier_map.normalize(file_name) { + self.auto_import_data = + Some(CompletionNormalizedAutoImportData { raw, normalized }); + } + } else if SUPPORTED_BUILTIN_NODE_MODULES + .contains(&raw.module_specifier.as_str()) + { + if let Ok(normalized) = + resolve_url(&format!("node:{}", &raw.module_specifier)) + { + self.auto_import_data = + Some(CompletionNormalizedAutoImportData { raw, normalized }); + } } } diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 92cefb98f0..568cad44b0 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -7960,6 +7960,73 @@ fn lsp_completions_auto_import() { client.shutdown(); } +#[test] +fn lsp_completions_auto_import_node_builtin() { + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); + let temp_dir = context.temp_dir(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": r#" + import "npm:@types/node"; + pathToFileURL + "#, + } + })); + client.write_request( + "workspace/executeCommand", + json!({ + "command": "deno.cache", + "arguments": [[], temp_dir.url().join("file.ts").unwrap()], + }), + ); + let list = client.get_completion_list( + temp_dir.url().join("file.ts").unwrap(), + (2, 21), + json!({ "triggerKind": 2 }), + ); + assert!(!list.is_incomplete); + let item = list + .items + .iter() + .find(|item| item.label == "pathToFileURL") + .unwrap(); + let res = client.write_request("completionItem/resolve", json!(item)); + assert_eq!( + res, + json!({ + "label": "pathToFileURL", + "labelDetails": { + "description": "node:url", + }, + "kind": 3, + "detail": "function pathToFileURL(path: string, options?: PathToFileUrlOptions): URL", + "documentation": { + "kind": "markdown", + "value": "This function ensures that `path` is resolved absolutely, and that the URL\ncontrol characters are correctly encoded when converting into a File URL.\n\n```js\nimport { pathToFileURL } from 'node:url';\n\nnew URL('/foo#1', 'file:'); // Incorrect: file:///foo#1\npathToFileURL('/foo#1'); // Correct: file:///foo#1 (POSIX)\n\nnew URL('/some/path%.c', 'file:'); // Incorrect: file:///some/path%.c\npathToFileURL('/some/path%.c'); // Correct: file:///some/path%.c (POSIX)\n```\n\n*@since* - v10.12.0 \n\n*@param* - path The path to convert to a File URL. \n\n*@return* - The file URL object.", + }, + "sortText": "￿16_1", + "additionalTextEdits": [ + { + "range": { + "start": { "line": 2, "character": 0 }, + "end": { "line": 2, "character": 0 }, + }, + "newText": " import { pathToFileURL } from \"node:url\";\n", + }, + ], + }), + ); + client.shutdown(); +} + #[test] fn lsp_npm_completions_auto_import_and_quick_fix_no_import_map() { let context = TestContextBuilder::new() From 9d7174e434187ab938df52d8c1d778a26b8e4e66 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:56:03 -0800 Subject: [PATCH 014/107] fix(outdated): ensure "Latest" version is greater than "Update" version (#27390) Fixes #27038. Previously, for NPM packages the latest version was the version with the "latest" tag. For JSR packages, the latest version was the greatest version that matched a `*` version requirement. Unfortunately, that doesn't work well with pre-release versions. This PR changes it so that the latest version is always > the currently requested version. For NPM: if "latest" tag > current then "latest" tag; otherwise the greatest version that is >= current For JSR: greatest version >= current This is the most reasonable behavior I could come up with. For example, ``` versions: 2.0.0-beta.2 2.0.0-beta.1 1.0.0 => "latest" tag with a version req `^2.0.0-beta.1` previously: "Update" column => 2.0.0-beta.2 "Latest" column => 1.0.0 now: "Update" column => 2.0.0-beta.2 "Latest" column => 2.0.0-beta.2 ``` --- cli/tools/registry/pm/deps.rs | 82 +++++++++++++------ .../has-only-pre-release/2.0.0-beta.1/mod.ts | 1 + .../2.0.0-beta.1_meta.json | 5 ++ .../has-only-pre-release/2.0.0-beta.2/mod.ts | 1 + .../2.0.0-beta.2_meta.json | 5 ++ .../@denotest/has-only-pre-release/meta.json | 6 ++ .../@denotest/has-pre-release/1.0.0/mod.ts | 1 + .../@denotest/has-pre-release/1.0.0_meta.json | 3 + .../has-pre-release/2.0.0-beta.1/mod.ts | 1 + .../has-pre-release/2.0.0-beta.1_meta.json | 5 ++ .../has-pre-release/2.0.0-beta.2/mod.ts | 1 + .../has-pre-release/2.0.0-beta.2_meta.json | 5 ++ .../jsr/@denotest/has-pre-release/meta.json | 7 ++ .../has-pre-release/1.0.0/package.json | 7 ++ .../has-pre-release/2.0.0-beta.1/package.json | 4 + .../has-pre-release/2.0.0-beta.2/package.json | 4 + tests/specs/update/pre_release/__test__.jsonc | 21 +++++ tests/specs/update/pre_release/deno.json | 7 ++ tests/specs/update/pre_release/deno.lock | 28 +++++++ tests/specs/update/pre_release/outdated.out | 11 +++ tests/specs/update/pre_release/update.out | 5 ++ tests/util/server/src/npm.rs | 14 +++- 22 files changed, 198 insertions(+), 26 deletions(-) create mode 100644 tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1/mod.ts create mode 100644 tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1_meta.json create mode 100644 tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2/mod.ts create mode 100644 tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2_meta.json create mode 100644 tests/registry/jsr/@denotest/has-only-pre-release/meta.json create mode 100644 tests/registry/jsr/@denotest/has-pre-release/1.0.0/mod.ts create mode 100644 tests/registry/jsr/@denotest/has-pre-release/1.0.0_meta.json create mode 100644 tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1/mod.ts create mode 100644 tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1_meta.json create mode 100644 tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2/mod.ts create mode 100644 tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2_meta.json create mode 100644 tests/registry/jsr/@denotest/has-pre-release/meta.json create mode 100644 tests/registry/npm/@denotest/has-pre-release/1.0.0/package.json create mode 100644 tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.1/package.json create mode 100644 tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.2/package.json create mode 100644 tests/specs/update/pre_release/__test__.jsonc create mode 100644 tests/specs/update/pre_release/deno.json create mode 100644 tests/specs/update/pre_release/deno.lock create mode 100644 tests/specs/update/pre_release/outdated.out create mode 100644 tests/specs/update/pre_release/update.out diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index e4c38276f7..bb03e97f2d 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -3,7 +3,6 @@ use std::borrow::Cow; use std::collections::HashMap; use std::path::PathBuf; -use std::sync::atomic::AtomicBool; use std::sync::Arc; use deno_ast::ModuleSpecifier; @@ -28,6 +27,7 @@ use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use deno_semver::package::PackageReqReference; +use deno_semver::Version; use deno_semver::VersionReq; use import_map::ImportMap; use import_map::ImportMapWithDiagnostics; @@ -42,6 +42,7 @@ use crate::jsr::JsrFetchResolver; use crate::module_loader::ModuleLoadPreparer; use crate::npm::CliNpmResolver; use crate::npm::NpmFetchResolver; +use crate::util::sync::AtomicFlag; use super::ConfigUpdater; @@ -447,7 +448,7 @@ pub struct DepManager { pending_changes: Vec, - dependencies_resolved: AtomicBool, + dependencies_resolved: AtomicFlag, module_load_preparer: Arc, // TODO(nathanwhit): probably shouldn't be pub pub(crate) jsr_fetch_resolver: Arc, @@ -489,7 +490,7 @@ impl DepManager { resolved_versions: Vec::new(), latest_versions: Vec::new(), jsr_fetch_resolver, - dependencies_resolved: AtomicBool::new(false), + dependencies_resolved: AtomicFlag::lowered(), module_load_preparer, npm_fetch_resolver, npm_resolver, @@ -530,10 +531,7 @@ impl DepManager { } async fn run_dependency_resolution(&self) -> Result<(), AnyError> { - if self - .dependencies_resolved - .load(std::sync::atomic::Ordering::Relaxed) - { + if self.dependencies_resolved.is_raised() { return Ok(()); } @@ -556,9 +554,7 @@ impl DepManager { } DepKind::Jsr => graph.packages.mappings().contains_key(&dep.req), }) { - self - .dependencies_resolved - .store(true, std::sync::atomic::Ordering::Relaxed); + self.dependencies_resolved.raise(); graph_permit.commit(); return Ok(()); } @@ -613,6 +609,7 @@ impl DepManager { ) .await?; + self.dependencies_resolved.raise(); graph_permit.commit(); Ok(()) @@ -655,10 +652,6 @@ impl DepManager { if self.latest_versions.len() == self.deps.len() { return Ok(self.latest_versions.clone()); } - let latest_tag_req = deno_semver::VersionReq::from_raw_text_and_inner( - "latest".into(), - deno_semver::RangeSetOrTag::Tag("latest".into()), - ); let mut latest_versions = Vec::with_capacity(self.deps.len()); let npm_sema = Semaphore::new(32); @@ -670,14 +663,25 @@ impl DepManager { DepKind::Npm => futs.push_back( async { let semver_req = &dep.req; - let latest_req = PackageReq { - name: dep.req.name.clone(), - version_req: latest_tag_req.clone(), - }; let _permit = npm_sema.acquire().await; let semver_compatible = self.npm_fetch_resolver.req_to_nv(semver_req).await; - let latest = self.npm_fetch_resolver.req_to_nv(&latest_req).await; + let info = + self.npm_fetch_resolver.package_info(&semver_req.name).await; + let latest = info + .and_then(|info| { + let latest_tag = info.dist_tags.get("latest")?; + let lower_bound = &semver_compatible.as_ref()?.version; + if latest_tag > lower_bound { + Some(latest_tag.clone()) + } else { + latest_version(Some(latest_tag), info.versions.keys()) + } + }) + .map(|version| PackageNv { + name: semver_req.name.clone(), + version, + }); PackageLatestVersion { latest, semver_compatible, @@ -688,14 +692,29 @@ impl DepManager { DepKind::Jsr => futs.push_back( async { let semver_req = &dep.req; - let latest_req = PackageReq { - name: dep.req.name.clone(), - version_req: deno_semver::WILDCARD_VERSION_REQ.clone(), - }; let _permit = jsr_sema.acquire().await; let semver_compatible = self.jsr_fetch_resolver.req_to_nv(semver_req).await; - let latest = self.jsr_fetch_resolver.req_to_nv(&latest_req).await; + let info = + self.jsr_fetch_resolver.package_info(&semver_req.name).await; + let latest = info + .and_then(|info| { + let lower_bound = &semver_compatible.as_ref()?.version; + latest_version( + Some(lower_bound), + info.versions.iter().filter_map(|(version, version_info)| { + if !version_info.yanked { + Some(version) + } else { + None + } + }), + ) + }) + .map(|version| PackageNv { + name: semver_req.name.clone(), + version, + }); PackageLatestVersion { latest, semver_compatible, @@ -893,3 +912,18 @@ fn parse_req_reference( DepKind::Jsr => JsrPackageReqReference::from_str(input)?.into_inner(), }) } + +fn latest_version<'a>( + start: Option<&Version>, + versions: impl IntoIterator, +) -> Option { + let mut best = start; + for version in versions { + match best { + Some(best_version) if version > best_version => best = Some(version), + None => best = Some(version), + _ => {} + } + } + best.cloned() +} diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1/mod.ts b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1_meta.json b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1_meta.json new file mode 100644 index 0000000000..6c213a9c05 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.1_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2/mod.ts b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2_meta.json b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2_meta.json new file mode 100644 index 0000000000..6c213a9c05 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/2.0.0-beta.2_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/has-only-pre-release/meta.json b/tests/registry/jsr/@denotest/has-only-pre-release/meta.json new file mode 100644 index 0000000000..cce8d0954f --- /dev/null +++ b/tests/registry/jsr/@denotest/has-only-pre-release/meta.json @@ -0,0 +1,6 @@ +{ + "versions": { + "2.0.0-beta.1": {}, + "2.0.0-beta.2": {} + } +} diff --git a/tests/registry/jsr/@denotest/has-pre-release/1.0.0/mod.ts b/tests/registry/jsr/@denotest/has-pre-release/1.0.0/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/1.0.0/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-pre-release/1.0.0_meta.json b/tests/registry/jsr/@denotest/has-pre-release/1.0.0_meta.json new file mode 100644 index 0000000000..c5807f588c --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/1.0.0_meta.json @@ -0,0 +1,3 @@ +{ + "exports": {} +} diff --git a/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1/mod.ts b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1_meta.json b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1_meta.json new file mode 100644 index 0000000000..6c213a9c05 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.1_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2/mod.ts b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2/mod.ts new file mode 100644 index 0000000000..6a8018af41 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2/mod.ts @@ -0,0 +1 @@ +export const foo = 1; \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2_meta.json b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2_meta.json new file mode 100644 index 0000000000..6c213a9c05 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/2.0.0-beta.2_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/has-pre-release/meta.json b/tests/registry/jsr/@denotest/has-pre-release/meta.json new file mode 100644 index 0000000000..ba7bc452d7 --- /dev/null +++ b/tests/registry/jsr/@denotest/has-pre-release/meta.json @@ -0,0 +1,7 @@ +{ + "versions": { + "1.0.0": {}, + "2.0.0-beta.1": {}, + "2.0.0-beta.2": {} + } +} diff --git a/tests/registry/npm/@denotest/has-pre-release/1.0.0/package.json b/tests/registry/npm/@denotest/has-pre-release/1.0.0/package.json new file mode 100644 index 0000000000..027b783d54 --- /dev/null +++ b/tests/registry/npm/@denotest/has-pre-release/1.0.0/package.json @@ -0,0 +1,7 @@ +{ + "name": "@denotest/has-pre-release", + "version": "1.0.0", + "publishConfig": { + "tag": "latest" + } +} \ No newline at end of file diff --git a/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.1/package.json b/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.1/package.json new file mode 100644 index 0000000000..b3d3b3e634 --- /dev/null +++ b/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.1/package.json @@ -0,0 +1,4 @@ +{ + "name": "@denotest/has-pre-release", + "version": "2.0.0-beta.1" +} \ No newline at end of file diff --git a/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.2/package.json b/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.2/package.json new file mode 100644 index 0000000000..b6f8ea4c76 --- /dev/null +++ b/tests/registry/npm/@denotest/has-pre-release/2.0.0-beta.2/package.json @@ -0,0 +1,4 @@ +{ + "name": "@denotest/has-pre-release", + "version": "2.0.0-beta.2" +} \ No newline at end of file diff --git a/tests/specs/update/pre_release/__test__.jsonc b/tests/specs/update/pre_release/__test__.jsonc new file mode 100644 index 0000000000..7e2ea39dab --- /dev/null +++ b/tests/specs/update/pre_release/__test__.jsonc @@ -0,0 +1,21 @@ +{ + "tempDir": true, + "steps": [ + { + "args": "i", + "output": "[WILDCARD]" + }, + { + "args": "outdated", + "output": "outdated.out" + }, + { + "args": "outdated --compatible", + "output": "outdated.out" + }, + { + "args": "outdated --update --latest", + "output": "update.out" + } + ] +} diff --git a/tests/specs/update/pre_release/deno.json b/tests/specs/update/pre_release/deno.json new file mode 100644 index 0000000000..07646b5059 --- /dev/null +++ b/tests/specs/update/pre_release/deno.json @@ -0,0 +1,7 @@ +{ + "imports": { + "@denotest/npm-has-pre-release": "npm:@denotest/has-pre-release@^2.0.0-beta.1", + "@denotest/jsr-has-pre-release": "jsr:@denotest/has-pre-release@^2.0.0-beta.1", + "@denotest/has-only-pre-release": "jsr:@denotest/has-only-pre-release@^2.0.0-beta.1" + } +} diff --git a/tests/specs/update/pre_release/deno.lock b/tests/specs/update/pre_release/deno.lock new file mode 100644 index 0000000000..33b136dd53 --- /dev/null +++ b/tests/specs/update/pre_release/deno.lock @@ -0,0 +1,28 @@ +{ + "version": "4", + "specifiers": { + "jsr:@denotest/has-only-pre-release@^2.0.0-beta.1": "2.0.0-beta.1", + "jsr:@denotest/has-pre-release@^2.0.0-beta.1": "2.0.0-beta.1", + "npm:@denotest/has-pre-release@^2.0.0-beta.1": "2.0.0-beta.1" + }, + "jsr": { + "@denotest/has-only-pre-release@2.0.0-beta.1": { + "integrity": "43fd680ea94bb5db5fe1a2d86101c47d0e2cc77323b881755cea9a0372e49537" + }, + "@denotest/has-pre-release@2.0.0-beta.1": { + "integrity": "43fd680ea94bb5db5fe1a2d86101c47d0e2cc77323b881755cea9a0372e49537" + } + }, + "npm": { + "@denotest/has-pre-release@2.0.0-beta.1": { + "integrity": "sha512-K1fHe1L2EUSLgijtzzALNpkkIO0SrX3z+IXvVjjOIE8HKd4T7lkpzDdoUp+WllwS3KXmuJh+9vIfY5lFp38pew==" + } + }, + "workspace": { + "dependencies": [ + "jsr:@denotest/has-only-pre-release@^2.0.0-beta.1", + "jsr:@denotest/has-pre-release@^2.0.0-beta.1", + "npm:@denotest/has-pre-release@^2.0.0-beta.1" + ] + } +} diff --git a/tests/specs/update/pre_release/outdated.out b/tests/specs/update/pre_release/outdated.out new file mode 100644 index 0000000000..b8369b25f7 --- /dev/null +++ b/tests/specs/update/pre_release/outdated.out @@ -0,0 +1,11 @@ +┌────────────────────────────────────┬──────────────┬──────────────┬──────────────┐ +│ Package │ Current │ Update │ Latest │ +├────────────────────────────────────┼──────────────┼──────────────┼──────────────┤ +│ jsr:@denotest/has-only-pre-release │ 2.0.0-beta.1 │ 2.0.0-beta.2 │ 2.0.0-beta.2 │ +├────────────────────────────────────┼──────────────┼──────────────┼──────────────┤ +│ jsr:@denotest/has-pre-release │ 2.0.0-beta.1 │ 2.0.0-beta.2 │ 2.0.0-beta.2 │ +├────────────────────────────────────┼──────────────┼──────────────┼──────────────┤ +│ npm:@denotest/has-pre-release │ 2.0.0-beta.1 │ 2.0.0-beta.2 │ 2.0.0-beta.2 │ +└────────────────────────────────────┴──────────────┴──────────────┴──────────────┘ + +[WILDCARD] diff --git a/tests/specs/update/pre_release/update.out b/tests/specs/update/pre_release/update.out new file mode 100644 index 0000000000..d019457f0a --- /dev/null +++ b/tests/specs/update/pre_release/update.out @@ -0,0 +1,5 @@ +[WILDCARD] +Updated 3 dependencies: + - jsr:@denotest/has-only-pre-release 2.0.0-beta.1 -> 2.0.0-beta.2 + - jsr:@denotest/has-pre-release 2.0.0-beta.1 -> 2.0.0-beta.2 + - npm:@denotest/has-pre-release 2.0.0-beta.1 -> 2.0.0-beta.2 diff --git a/tests/util/server/src/npm.rs b/tests/util/server/src/npm.rs index 081989ddb5..0261b2532c 100644 --- a/tests/util/server/src/npm.rs +++ b/tests/util/server/src/npm.rs @@ -267,6 +267,7 @@ fn get_npm_package( let mut tarballs = HashMap::new(); let mut versions = serde_json::Map::new(); let mut latest_version = semver::Version::parse("0.0.0").unwrap(); + let mut dist_tags = serde_json::Map::new(); for entry in fs::read_dir(&package_folder)? { let entry = entry?; let file_type = entry.file_type()?; @@ -345,6 +346,14 @@ fn get_npm_package( } } + if let Some(publish_config) = version_info.get("publishConfig") { + if let Some(tag) = publish_config.get("tag") { + if let Some(tag) = tag.as_str() { + dist_tags.insert(tag.to_string(), version.clone().into()); + } + } + } + versions.insert(version.clone(), version_info.into()); let version = semver::Version::parse(&version)?; if version.cmp(&latest_version).is_gt() { @@ -352,8 +361,9 @@ fn get_npm_package( } } - let mut dist_tags = serde_json::Map::new(); - dist_tags.insert("latest".to_string(), latest_version.to_string().into()); + if !dist_tags.contains_key("latest") { + dist_tags.insert("latest".to_string(), latest_version.to_string().into()); + } // create the registry file for this package let mut registry_file = serde_json::Map::new(); From 14e406498672e7b8fbbfbcb7a057f798358909b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 18 Dec 2024 02:32:37 +0000 Subject: [PATCH 015/107] fix(task): properly handle task name wildcards with --recursive (#27396) This commit fixes `deno task` by checking if the provided task name actually has a wildcard char ("*"). Previously, if the "--recursive" flag was passed, the task name was treated as a regex, which lead to a situation where exact task name resulted in a regex that matched all tasks with the specific prefix. This commit fixes it, by checking if the provided task name, is an exact name, or is it a wildcard match. Closes https://github.com/denoland/deno/issues/27370 Closes https://github.com/denoland/deno/issues/27401 Closes https://github.com/denoland/deno/issues/27408 --- cli/tools/task.rs | 50 ++++++++++++++++++- .../task/workspace_regex_match/__test__.jsonc | 11 ++++ .../task/workspace_regex_match/deno.json | 6 +++ .../specs/task/workspace_regex_match/root.out | 3 ++ .../workspace_regex_match/subdir/deno.json | 5 ++ 5 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/specs/task/workspace_regex_match/__test__.jsonc create mode 100644 tests/specs/task/workspace_regex_match/deno.json create mode 100644 tests/specs/task/workspace_regex_match/root.out create mode 100644 tests/specs/task/workspace_regex_match/subdir/deno.json diff --git a/cli/tools/task.rs b/cli/tools/task.rs index 25d1d66710..ec9b238847 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -94,7 +94,7 @@ pub async fn execute_script( return Ok(0); }; - let task_regex = arg_to_regex(task_name)?; + let task_name_filter = arg_to_task_name_filter(task_name)?; let mut packages_task_info: Vec = vec![]; for folder in workspace.config_folders() { @@ -137,12 +137,20 @@ pub async fn execute_script( // Match tasks in deno.json for name in tasks_config.task_names() { - if task_regex.is_match(name) && !visited.contains(name) { + let matches_filter = match &task_name_filter { + TaskNameFilter::Exact(n) => *n == name, + TaskNameFilter::Regex(re) => re.is_match(name), + }; + if matches_filter && !visited.contains(name) { matched.insert(name.to_string()); visit_task(&tasks_config, &mut visited, name); } } + if matched.is_empty() { + continue; + } + packages_task_info.push(PackageTaskInfo { matched_tasks: matched .iter() @@ -902,3 +910,41 @@ fn strip_ansi_codes_and_escape_control_chars(s: &str) -> String { }) .collect() } + +fn arg_to_task_name_filter(input: &str) -> Result { + if !input.contains("*") { + return Ok(TaskNameFilter::Exact(input)); + } + + let mut regex_str = regex::escape(input); + regex_str = regex_str.replace("\\*", ".*"); + let re = Regex::new(®ex_str)?; + Ok(TaskNameFilter::Regex(re)) +} + +#[derive(Debug)] +enum TaskNameFilter<'s> { + Exact(&'s str), + Regex(regex::Regex), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_arg_to_task_name_filter() { + assert!(matches!( + arg_to_task_name_filter("test").unwrap(), + TaskNameFilter::Exact("test") + )); + assert!(matches!( + arg_to_task_name_filter("test-").unwrap(), + TaskNameFilter::Exact("test-") + )); + assert!(matches!( + arg_to_task_name_filter("test*").unwrap(), + TaskNameFilter::Regex(_) + )); + } +} diff --git a/tests/specs/task/workspace_regex_match/__test__.jsonc b/tests/specs/task/workspace_regex_match/__test__.jsonc new file mode 100644 index 0000000000..258c288d44 --- /dev/null +++ b/tests/specs/task/workspace_regex_match/__test__.jsonc @@ -0,0 +1,11 @@ +{ + "tempDir": true, + "tests": { + // Regression test for https://github.com/denoland/deno/issues/27370 + "root": { + "args": "task test-all", + "output": "root.out", + "exitCode": 0 + } + } +} diff --git a/tests/specs/task/workspace_regex_match/deno.json b/tests/specs/task/workspace_regex_match/deno.json new file mode 100644 index 0000000000..ce040ba5ab --- /dev/null +++ b/tests/specs/task/workspace_regex_match/deno.json @@ -0,0 +1,6 @@ +{ + "workspace": ["./subdir"], + "tasks": { + "test-all": "deno task --recursive test" + } +} diff --git a/tests/specs/task/workspace_regex_match/root.out b/tests/specs/task/workspace_regex_match/root.out new file mode 100644 index 0000000000..9da724a5c0 --- /dev/null +++ b/tests/specs/task/workspace_regex_match/root.out @@ -0,0 +1,3 @@ +Task test-all deno task --recursive test +Task test echo 'ok' +ok diff --git a/tests/specs/task/workspace_regex_match/subdir/deno.json b/tests/specs/task/workspace_regex_match/subdir/deno.json new file mode 100644 index 0000000000..78d768e396 --- /dev/null +++ b/tests/specs/task/workspace_regex_match/subdir/deno.json @@ -0,0 +1,5 @@ +{ + "tasks": { + "test": "echo 'ok'" + } +} From 8590aa9cee9c63088ef8eac017d6325318aa9df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 18 Dec 2024 13:44:53 +0000 Subject: [PATCH 016/107] fix(ext/node): sort list of built-in modules alphabetically (#27410) --- Cargo.lock | 2 +- Cargo.toml | 2 +- ext/node/Cargo.toml | 2 +- ext/node/polyfill.rs | 9 ++++++++- tests/integration/lsp_tests.rs | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cfb3779e4..7203286773 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1925,7 +1925,7 @@ dependencies = [ [[package]] name = "deno_node" -version = "0.120.0" +version = "0.121.0" dependencies = [ "aead-gcm-stream", "aes", diff --git a/Cargo.toml b/Cargo.toml index 0f52c28de6..b404114054 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ deno_io = { version = "0.93.0", path = "./ext/io" } deno_kv = { version = "0.91.0", path = "./ext/kv" } deno_napi = { version = "0.114.0", path = "./ext/napi" } deno_net = { version = "0.175.0", path = "./ext/net" } -deno_node = { version = "0.120.0", path = "./ext/node" } +deno_node = { version = "0.121.0", path = "./ext/node" } deno_telemetry = { version = "0.5.0", path = "./ext/telemetry" } deno_tls = { version = "0.170.0", path = "./ext/tls" } deno_url = { version = "0.183.0", path = "./ext/url" } diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 8ba1141ec1..127633a09b 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_node" -version = "0.120.0" +version = "0.121.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node/polyfill.rs b/ext/node/polyfill.rs index 762e32f7ba..42cc794955 100644 --- a/ext/node/polyfill.rs +++ b/ext/node/polyfill.rs @@ -67,9 +67,9 @@ generate_builtin_node_module_lists! { "process", "punycode", "querystring", - "repl", "readline", "readline/promises", + "repl", "stream", "stream/consumers", "stream/promises", @@ -90,3 +90,10 @@ generate_builtin_node_module_lists! { "worker_threads", "zlib", } + +#[test] +fn test_builtins_are_sorted() { + let mut builtins_list = SUPPORTED_BUILTIN_NODE_MODULES.to_vec(); + builtins_list.sort(); + assert_eq!(SUPPORTED_BUILTIN_NODE_MODULES, builtins_list); +} diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 568cad44b0..56c060d958 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -8609,9 +8609,9 @@ fn lsp_completions_node_specifier() { "node:process", "node:punycode", "node:querystring", - "node:repl", "node:readline", "node:readline/promises", + "node:repl", "node:stream", "node:stream/consumers", "node:stream/promises", From ae744074128de129e6ceb3229cc69060968f0ebc Mon Sep 17 00:00:00 2001 From: snek Date: Wed, 18 Dec 2024 14:47:21 +0100 Subject: [PATCH 017/107] chore: upgrade libc (#27414) need to do this for quic and they deprecated this method in libc without actually providing an alternative so :/ --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- ext/node/ops/os/cpus.rs | 9 +++++++-- runtime/ops/os/mod.rs | 5 ++++- runtime/sys_info.rs | 6 +++++- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7203286773..a74724e92d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4552,9 +4552,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libffi" diff --git a/Cargo.toml b/Cargo.toml index b404114054..0d045c9059 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,7 +149,7 @@ indexmap = { version = "2", features = ["serde"] } ipnet = "2.3" jsonc-parser = { version = "=0.26.2", features = ["serde"] } lazy-regex = "3" -libc = "0.2.126" +libc = "0.2.168" libz-sys = { version = "1.1.20", default-features = false } log = { version = "0.4.20", features = ["kv"] } lsp-types = "=0.97.0" # used by tower-lsp and "proposed" feature is unstable in patch releases diff --git a/ext/node/ops/os/cpus.rs b/ext/node/ops/os/cpus.rs index 3f5f430f65..2b931884c3 100644 --- a/ext/node/ops/os/cpus.rs +++ b/ext/node/ops/os/cpus.rs @@ -73,12 +73,17 @@ pub fn cpu_info() -> Option> { cpu_speed = 2_400_000_000; } + extern "C" { + fn mach_host_self() -> std::ffi::c_uint; + static mut mach_task_self_: std::ffi::c_uint; + } + let mut num_cpus: libc::natural_t = 0; let mut info: *mut libc::processor_cpu_load_info_data_t = std::ptr::null_mut(); let mut msg_type: libc::mach_msg_type_number_t = 0; if libc::host_processor_info( - libc::mach_host_self(), + mach_host_self(), libc::PROCESSOR_CPU_LOAD_INFO, &mut num_cpus, &mut info as *mut _ as *mut libc::processor_info_array_t, @@ -111,7 +116,7 @@ pub fn cpu_info() -> Option> { } libc::vm_deallocate( - libc::mach_task_self(), + mach_task_self_, info.as_ptr() as libc::vm_address_t, msg_type as _, ); diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs index b8ebc88bed..71169217a7 100644 --- a/runtime/ops/os/mod.rs +++ b/runtime/ops/os/mod.rs @@ -424,8 +424,11 @@ fn rss() -> usize { let mut count = libc::MACH_TASK_BASIC_INFO_COUNT; // SAFETY: libc calls let r = unsafe { + extern "C" { + static mut mach_task_self_: std::ffi::c_uint; + } libc::task_info( - libc::mach_task_self(), + mach_task_self_, libc::MACH_TASK_BASIC_INFO, task_info.as_mut_ptr() as libc::task_info_t, &mut count as *mut libc::mach_msg_type_number_t, diff --git a/runtime/sys_info.rs b/runtime/sys_info.rs index cffc90e9da..f99cfc99f9 100644 --- a/runtime/sys_info.rs +++ b/runtime/sys_info.rs @@ -278,11 +278,15 @@ pub fn mem_info() -> Option { mem_info.swap_total = xs.xsu_total; mem_info.swap_free = xs.xsu_avail; + extern "C" { + fn mach_host_self() -> std::ffi::c_uint; + } + let mut count: u32 = libc::HOST_VM_INFO64_COUNT as _; let mut stat = std::mem::zeroed::(); if libc::host_statistics64( // TODO(@littledivy): Put this in a once_cell. - libc::mach_host_self(), + mach_host_self(), libc::HOST_VM_INFO64, &mut stat as *mut libc::vm_statistics64 as *mut _, &mut count, From b1c685f4b773abd1af8f543e86c73909cc41efd7 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 18 Dec 2024 17:04:29 -0500 Subject: [PATCH 018/107] fix(ext/fetch): retry some http/2 errors (#27417) This brings some of the HTTP/2 retry behavior from reqwest to `ext/fetch`. It will retry very specific HTTP/2 errors once, if the body is able to be used again. Closes #27332 --- Cargo.lock | 28 +++- Cargo.toml | 2 +- cli/http_util.rs | 12 +- cli/tools/registry/mod.rs | 5 +- ext/fetch/Cargo.toml | 1 + ext/fetch/lib.rs | 190 +++++++++++++++++++++++++-- ext/fetch/tests.rs | 6 +- ext/kv/remote.rs | 4 +- runtime/ops/web_worker/sync_fetch.rs | 6 +- 9 files changed, 209 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a74724e92d..bb0617ceee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -380,7 +380,7 @@ dependencies = [ "rustversion", "serde", "sync_wrapper", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", ] @@ -1651,6 +1651,7 @@ dependencies = [ "dyn-clone", "error_reporter", "fast-socks5", + "h2 0.4.4", "hickory-resolver", "http 1.1.0", "http-body-util", @@ -1667,7 +1668,7 @@ dependencies = [ "tokio-rustls", "tokio-socks", "tokio-util", - "tower", + "tower 0.5.2", "tower-http", "tower-service", ] @@ -2322,7 +2323,7 @@ dependencies = [ "serde_json", "tokio", "tokio-util", - "tower", + "tower 0.4.13", "tracing", ] @@ -7976,7 +7977,7 @@ dependencies = [ "socket2", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -8002,6 +8003,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-http" version = "0.6.1" @@ -8030,9 +8046,9 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" diff --git a/Cargo.toml b/Cargo.toml index 0d045c9059..55a94b75a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -202,7 +202,7 @@ tokio-metrics = { version = "0.3.0", features = ["rt"] } tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring", "tls12"] } tokio-socks = "0.5.1" tokio-util = "0.7.4" -tower = { version = "0.4.13", default-features = false, features = ["util"] } +tower = { version = "0.5.2", default-features = false, features = ["retry", "util"] } tower-http = { version = "0.6.1", features = ["decompression-br", "decompression-gzip"] } tower-lsp = { package = "deno_tower_lsp", version = "0.1.0", features = ["proposed"] } tower-service = "0.3.2" diff --git a/cli/http_util.rs b/cli/http_util.rs index ce05d66b78..b24dd7bc0c 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -145,9 +145,7 @@ impl HttpClient { } pub fn get(&self, url: Url) -> Result { - let body = http_body_util::Empty::new() - .map_err(|never| match never {}) - .boxed(); + let body = deno_fetch::ReqBody::empty(); let mut req = http::Request::new(body); *req.uri_mut() = url.as_str().parse()?; Ok(RequestBuilder { @@ -179,9 +177,7 @@ impl HttpClient { S: serde::Serialize, { let json = deno_core::serde_json::to_vec(ser)?; - let body = http_body_util::Full::new(json.into()) - .map_err(|never| match never {}) - .boxed(); + let body = deno_fetch::ReqBody::full(json.into()); let builder = self.post(url, body)?; Ok(builder.header( http::header::CONTENT_TYPE, @@ -194,9 +190,7 @@ impl HttpClient { url: &Url, headers: HeaderMap, ) -> Result, SendError> { - let body = http_body_util::Empty::new() - .map_err(|never| match never {}) - .boxed(); + let body = deno_fetch::ReqBody::empty(); let mut request = http::Request::new(body); *request.uri_mut() = http::Uri::try_from(url.as_str())?; *request.headers_mut() = headers; diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index 001e401459..45a040d236 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -26,6 +26,7 @@ use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::url::Url; +use deno_runtime::deno_fetch; use deno_terminal::colors; use http_body_util::BodyExt; use serde::Deserialize; @@ -911,9 +912,7 @@ async fn publish_package( package.config ); - let body = http_body_util::Full::new(package.tarball.bytes.clone()) - .map_err(|never| match never {}) - .boxed(); + let body = deno_fetch::ReqBody::full(package.tarball.bytes.clone()); let response = http_client .post(url.parse()?, body)? .header( diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index 98d2fdf5da..fee21808e7 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -23,6 +23,7 @@ deno_permissions.workspace = true deno_tls.workspace = true dyn-clone = "1" error_reporter = "1" +h2.workspace = true hickory-resolver.workspace = true http.workspace = true http-body-util.workspace = true diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index 919c6d3044..103698b3bf 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -10,6 +10,7 @@ use std::borrow::Cow; use std::cell::RefCell; use std::cmp::min; use std::convert::From; +use std::future; use std::path::Path; use std::path::PathBuf; use std::pin::Pin; @@ -66,6 +67,7 @@ use http::header::USER_AGENT; use http::Extensions; use http::Method; use http::Uri; +use http_body_util::combinators::BoxBody; use http_body_util::BodyExt; use hyper::body::Frame; use hyper_util::client::legacy::connect::HttpConnector; @@ -75,6 +77,7 @@ use hyper_util::rt::TokioExecutor; use hyper_util::rt::TokioTimer; use serde::Deserialize; use serde::Serialize; +use tower::retry; use tower::ServiceExt; use tower_http::decompression::Decompression; @@ -476,9 +479,7 @@ where // If a body is passed, we use it, and don't return a body for streaming. con_len = Some(data.len() as u64); - http_body_util::Full::new(data.to_vec().into()) - .map_err(|never| match never {}) - .boxed() + ReqBody::full(data.to_vec().into()) } (_, Some(resource)) => { let resource = state @@ -491,7 +492,7 @@ where } _ => {} } - ReqBody::new(ResourceToBodyAdapter::new(resource)) + ReqBody::streaming(ResourceToBodyAdapter::new(resource)) } (None, None) => unreachable!(), } @@ -501,9 +502,7 @@ where if matches!(method, Method::POST | Method::PUT) { con_len = Some(0); } - http_body_util::Empty::new() - .map_err(|never| match never {}) - .boxed() + ReqBody::empty() }; let mut request = http::Request::new(body); @@ -1066,7 +1065,8 @@ pub fn create_http_client( } let pooled_client = builder.build(connector); - let decompress = Decompression::new(pooled_client).gzip(true).br(true); + let retry_client = retry::Retry::new(FetchRetry, pooled_client); + let decompress = Decompression::new(retry_client).gzip(true).br(true); Ok(Client { inner: decompress, @@ -1083,7 +1083,12 @@ pub fn op_utf8_to_byte_string(#[string] input: String) -> ByteString { #[derive(Clone, Debug)] pub struct Client { - inner: Decompression>, + inner: Decompression< + retry::Retry< + FetchRetry, + hyper_util::client::legacy::Client, + >, + >, // Used to check whether to include a proxy-authorization header proxies: Arc, user_agent: HeaderValue, @@ -1174,10 +1179,70 @@ impl Client { } } -pub type ReqBody = - http_body_util::combinators::BoxBody; -pub type ResBody = - http_body_util::combinators::BoxBody; +// This is a custom enum to allow the retry policy to clone the variants that could be retried. +pub enum ReqBody { + Full(http_body_util::Full), + Empty(http_body_util::Empty), + Streaming(BoxBody), +} + +pub type ResBody = BoxBody; + +impl ReqBody { + pub fn full(bytes: Bytes) -> Self { + ReqBody::Full(http_body_util::Full::new(bytes)) + } + + pub fn empty() -> Self { + ReqBody::Empty(http_body_util::Empty::new()) + } + + pub fn streaming(body: B) -> Self + where + B: hyper::body::Body + + Send + + Sync + + 'static, + { + ReqBody::Streaming(BoxBody::new(body)) + } +} + +impl hyper::body::Body for ReqBody { + type Data = Bytes; + type Error = deno_core::error::AnyError; + + fn poll_frame( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + match &mut *self { + ReqBody::Full(ref mut b) => { + Pin::new(b).poll_frame(cx).map_err(|never| match never {}) + } + ReqBody::Empty(ref mut b) => { + Pin::new(b).poll_frame(cx).map_err(|never| match never {}) + } + ReqBody::Streaming(ref mut b) => Pin::new(b).poll_frame(cx), + } + } + + fn is_end_stream(&self) -> bool { + match self { + ReqBody::Full(ref b) => b.is_end_stream(), + ReqBody::Empty(ref b) => b.is_end_stream(), + ReqBody::Streaming(ref b) => b.is_end_stream(), + } + } + + fn size_hint(&self) -> hyper::body::SizeHint { + match self { + ReqBody::Full(ref b) => b.size_hint(), + ReqBody::Empty(ref b) => b.size_hint(), + ReqBody::Streaming(ref b) => b.size_hint(), + } + } +} /// Copied from https://github.com/seanmonstar/reqwest/blob/b9d62a0323d96f11672a61a17bf8849baec00275/src/async_impl/request.rs#L572 /// Check the request URL for a "username:password" type authority, and if @@ -1214,3 +1279,102 @@ pub fn extract_authority(url: &mut Url) -> Option<(String, Option)> { fn op_fetch_promise_is_settled(promise: v8::Local) -> bool { promise.state() != v8::PromiseState::Pending } + +/// Deno.fetch's retry policy. +#[derive(Clone, Debug)] +struct FetchRetry; + +/// Marker extension that a request has been retried once. +#[derive(Clone, Debug)] +struct Retried; + +impl + retry::Policy, http::Response, E> + for FetchRetry +where + E: std::error::Error + 'static, +{ + /// Don't delay retries. + type Future = future::Ready<()>; + + fn retry( + &mut self, + req: &mut http::Request, + result: &mut Result, E>, + ) -> Option { + if req.extensions().get::().is_some() { + // only retry once + return None; + } + + match result { + Ok(..) => { + // never retry a Response + None + } + Err(err) => { + if is_error_retryable(&*err) { + req.extensions_mut().insert(Retried); + Some(future::ready(())) + } else { + None + } + } + } + } + + fn clone_request( + &mut self, + req: &http::Request, + ) -> Option> { + let body = match req.body() { + ReqBody::Full(b) => ReqBody::Full(b.clone()), + ReqBody::Empty(b) => ReqBody::Empty(*b), + ReqBody::Streaming(..) => return None, + }; + + let mut clone = http::Request::new(body); + *clone.method_mut() = req.method().clone(); + *clone.uri_mut() = req.uri().clone(); + *clone.headers_mut() = req.headers().clone(); + *clone.extensions_mut() = req.extensions().clone(); + Some(clone) + } +} + +fn is_error_retryable(err: &(dyn std::error::Error + 'static)) -> bool { + // Note: hyper doesn't promise it will always be this h2 version. Keep up to date. + if let Some(err) = find_source::(err) { + // They sent us a graceful shutdown, try with a new connection! + if err.is_go_away() + && err.is_remote() + && err.reason() == Some(h2::Reason::NO_ERROR) + { + return true; + } + + // REFUSED_STREAM was sent from the server, which is safe to retry. + // https://www.rfc-editor.org/rfc/rfc9113.html#section-8.7-3.2 + if err.is_reset() + && err.is_remote() + && err.reason() == Some(h2::Reason::REFUSED_STREAM) + { + return true; + } + } + + false +} + +fn find_source<'a, E: std::error::Error + 'static>( + err: &'a (dyn std::error::Error + 'static), +) -> Option<&'a E> { + let mut err = Some(err); + while let Some(src) = err { + if let Some(found) = src.downcast_ref::() { + return Some(found); + } + err = src.source(); + } + None +} diff --git a/ext/fetch/tests.rs b/ext/fetch/tests.rs index 3da29f8aa7..243b80bd90 100644 --- a/ext/fetch/tests.rs +++ b/ext/fetch/tests.rs @@ -133,11 +133,7 @@ async fn rust_test_client_with_resolver( let req = http::Request::builder() .uri(format!("https://{}/foo", src_addr)) - .body( - http_body_util::Empty::new() - .map_err(|err| match err {}) - .boxed(), - ) + .body(crate::ReqBody::empty()) .unwrap(); let resp = client.send(req).await.unwrap(); assert_eq!(resp.status(), http::StatusCode::OK); diff --git a/ext/kv/remote.rs b/ext/kv/remote.rs index 1830aa67ee..891786e319 100644 --- a/ext/kv/remote.rs +++ b/ext/kv/remote.rs @@ -122,9 +122,7 @@ impl RemoteTransport for FetchClient { headers: http::HeaderMap, body: Bytes, ) -> Result<(Url, http::StatusCode, Self::Response), anyhow::Error> { - let body = http_body_util::Full::new(body) - .map_err(|never| match never {}) - .boxed(); + let body = deno_fetch::ReqBody::full(body); let mut req = http::Request::new(body); *req.method_mut() = http::Method::POST; *req.uri_mut() = url.as_str().parse()?; diff --git a/runtime/ops/web_worker/sync_fetch.rs b/runtime/ops/web_worker/sync_fetch.rs index d1f133d3d2..508bcb7bb0 100644 --- a/runtime/ops/web_worker/sync_fetch.rs +++ b/runtime/ops/web_worker/sync_fetch.rs @@ -104,11 +104,7 @@ pub fn op_worker_sync_fetch( let (body, mime_type, res_url) = match script_url.scheme() { "http" | "https" => { - let mut req = http::Request::new( - http_body_util::Empty::new() - .map_err(|never| match never {}) - .boxed(), - ); + let mut req = http::Request::new(deno_fetch::ReqBody::empty()); *req.uri_mut() = script_url.as_str().parse()?; let resp = From 8fc4796ed5f765a8b55cf08b1802af6774f2d1a1 Mon Sep 17 00:00:00 2001 From: Filip Stevanovic <62512535+filipstev@users.noreply.github.com> Date: Wed, 18 Dec 2024 23:52:37 +0100 Subject: [PATCH 019/107] fix(ext/node): Fix `fs.access`/`fs.promises.access` with `X_OK` mode parameter on Windows (#27407) - Fixes an issue on Windows where the `fs.constants.X_OK` flag caused `fs.access` and `fs.promises.access` to incorrectly throw a "permission denied" error - Introduced formatting changes due to the formatting tool - Fixed the issue by always removing the `X_OK` bit from the mode variable m (not sure if it's necessary to check for the presence of it in the `mode` param first?) - Updated unit tests to handle the mentioned constant - `X_OK` bit is ignored in the Node implementation and should behave like `F_OK` fs constants Node documentation: https://nodejs.org/api/fs.html#fsconstants fixes https://github.com/denoland/deno/issues/27405 --- ext/node/polyfills/_fs/_fs_access.ts | 102 ++++++++++++++----------- tests/unit_node/_fs/_fs_access_test.ts | 4 + 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/ext/node/polyfills/_fs/_fs_access.ts b/ext/node/polyfills/_fs/_fs_access.ts index b501bcbcae..824386e64b 100644 --- a/ext/node/polyfills/_fs/_fs_access.ts +++ b/ext/node/polyfills/_fs/_fs_access.ts @@ -30,50 +30,58 @@ export function access( mode = getValidMode(mode, "access"); const cb = makeCallback(callback); - Deno.lstat(path).then((info) => { - if (info.mode === null) { - // If the file mode is unavailable, we pretend it has - // the permission - cb(null); - return; - } - const m = +mode || 0; - let fileMode = +info.mode || 0; - if (Deno.build.os !== "windows" && info.uid === Deno.uid()) { - // If the user is the owner of the file, then use the owner bits of - // the file permission - fileMode >>= 6; - } - // TODO(kt3k): Also check the case when the user belong to the group - // of the file - if ((m & fileMode) === m) { - // all required flags exist - cb(null); - } else { - // some required flags don't - // deno-lint-ignore no-explicit-any - const e: any = new Error(`EACCES: permission denied, access '${path}'`); - e.path = path; - e.syscall = "access"; - e.errno = codeMap.get("EACCES"); - e.code = "EACCES"; - cb(e); - } - }, (err) => { - if (err instanceof Deno.errors.NotFound) { - // deno-lint-ignore no-explicit-any - const e: any = new Error( - `ENOENT: no such file or directory, access '${path}'`, - ); - e.path = path; - e.syscall = "access"; - e.errno = codeMap.get("ENOENT"); - e.code = "ENOENT"; - cb(e); - } else { - cb(err); - } - }); + Deno.lstat(path).then( + (info) => { + if (info.mode === null) { + // If the file mode is unavailable, we pretend it has + // the permission + cb(null); + return; + } + let m = +mode || 0; + let fileMode = +info.mode || 0; + + if (Deno.build.os === "windows") { + m &= ~fs.X_OK; // Ignore the X_OK bit on Windows + } else if (info.uid === Deno.uid()) { + // If the user is the owner of the file, then use the owner bits of + // the file permission + fileMode >>= 6; + } + + // TODO(kt3k): Also check the case when the user belong to the group + // of the file + + if ((m & fileMode) === m) { + // all required flags exist + cb(null); + } else { + // some required flags don't + // deno-lint-ignore no-explicit-any + const e: any = new Error(`EACCES: permission denied, access '${path}'`); + e.path = path; + e.syscall = "access"; + e.errno = codeMap.get("EACCES"); + e.code = "EACCES"; + cb(e); + } + }, + (err) => { + if (err instanceof Deno.errors.NotFound) { + // deno-lint-ignore no-explicit-any + const e: any = new Error( + `ENOENT: no such file or directory, access '${path}'`, + ); + e.path = path; + e.syscall = "access"; + e.errno = codeMap.get("ENOENT"); + e.code = "ENOENT"; + cb(e); + } else { + cb(err); + } + }, + ); } export const accessPromise = promisify(access) as ( @@ -91,9 +99,11 @@ export function accessSync(path: string | Buffer | URL, mode?: number) { // the permission return; } - const m = +mode! || 0; + let m = +mode! || 0; let fileMode = +info.mode! || 0; - if (Deno.build.os !== "windows" && info.uid === Deno.uid()) { + if (Deno.build.os === "windows") { + m &= ~fs.X_OK; // Ignore the X_OK bit on Windows + } else if (info.uid === Deno.uid()) { // If the user is the owner of the file, then use the owner bits of // the file permission fileMode >>= 6; diff --git a/tests/unit_node/_fs/_fs_access_test.ts b/tests/unit_node/_fs/_fs_access_test.ts index f8010b0b8b..0881769f2c 100644 --- a/tests/unit_node/_fs/_fs_access_test.ts +++ b/tests/unit_node/_fs/_fs_access_test.ts @@ -28,6 +28,8 @@ Deno.test( try { await fs.promises.access(file, fs.constants.R_OK); await fs.promises.access(file, fs.constants.W_OK); + await fs.promises.access(file, fs.constants.X_OK); + await fs.promises.access(file, fs.constants.F_OK); } finally { await Deno.remove(file); } @@ -60,6 +62,8 @@ Deno.test( try { fs.accessSync(file, fs.constants.R_OK); fs.accessSync(file, fs.constants.W_OK); + fs.accessSync(file, fs.constants.X_OK); + fs.accessSync(file, fs.constants.F_OK); } finally { Deno.removeSync(file); } From 55d345baed709920cdf17c5662e8f3cb5c28be05 Mon Sep 17 00:00:00 2001 From: denobot <33910674+denobot@users.noreply.github.com> Date: Wed, 18 Dec 2024 21:39:02 -0500 Subject: [PATCH 020/107] chore: release ext/ crates (#27419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: bartlomieju Co-authored-by: Bartek Iwańczuk --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 8 ++--- Cargo.lock | 50 ++++++++++++++++---------------- Cargo.toml | 50 ++++++++++++++++---------------- bench_util/Cargo.toml | 2 +- ext/broadcast_channel/Cargo.toml | 2 +- ext/cache/Cargo.toml | 2 +- ext/canvas/Cargo.toml | 2 +- ext/console/Cargo.toml | 2 +- ext/cron/Cargo.toml | 2 +- ext/crypto/Cargo.toml | 2 +- ext/fetch/Cargo.toml | 2 +- ext/ffi/Cargo.toml | 2 +- ext/fs/Cargo.toml | 2 +- ext/http/Cargo.toml | 2 +- ext/io/Cargo.toml | 2 +- ext/kv/Cargo.toml | 2 +- ext/napi/Cargo.toml | 2 +- ext/napi/sym/Cargo.toml | 2 +- ext/net/Cargo.toml | 2 +- ext/node/Cargo.toml | 2 +- ext/telemetry/Cargo.toml | 2 +- ext/tls/Cargo.toml | 2 +- ext/url/Cargo.toml | 2 +- ext/web/Cargo.toml | 2 +- ext/webgpu/Cargo.toml | 2 +- ext/webidl/Cargo.toml | 2 +- ext/websocket/Cargo.toml | 2 +- ext/webstorage/Cargo.toml | 2 +- 29 files changed, 80 insertions(+), 80 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index 6fbcd8f242..bc3f15380b 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -5,7 +5,7 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify"; // Bump this number when you want to purge the cache. // Note: the tools/release/01_bump_crate_versions.ts script will update this version // automatically via regex, so ensure that this line maintains this format. -const cacheVersion = 31; +const cacheVersion = 32; const ubuntuX86Runner = "ubuntu-24.04"; const ubuntuX86XlRunner = "ubuntu-24.04-xl"; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f5d8f5b6d..cc1aa89669 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -184,8 +184,8 @@ jobs: ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db - key: '31-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '31-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' + key: '32-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '32-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' if: '!(matrix.skip)' - uses: dsherret/rust-toolchain-file@v1 if: '!(matrix.skip)' @@ -379,7 +379,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '31-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '32-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 @@ -689,7 +689,7 @@ jobs: !./target/*/gn_root !./target/*/*.zip !./target/*/*.tar.gz - key: '31-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '32-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-24.04 diff --git a/Cargo.lock b/Cargo.lock index bb0617ceee..4dddd53d75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "deno_bench_util" -version = "0.177.0" +version = "0.178.0" dependencies = [ "bencher", "deno_core", @@ -1400,7 +1400,7 @@ dependencies = [ [[package]] name = "deno_broadcast_channel" -version = "0.177.0" +version = "0.178.0" dependencies = [ "async-trait", "deno_core", @@ -1411,7 +1411,7 @@ dependencies = [ [[package]] name = "deno_cache" -version = "0.115.0" +version = "0.116.0" dependencies = [ "async-trait", "deno_core", @@ -1452,7 +1452,7 @@ dependencies = [ [[package]] name = "deno_canvas" -version = "0.52.0" +version = "0.53.0" dependencies = [ "deno_core", "deno_webgpu", @@ -1487,7 +1487,7 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.183.0" +version = "0.184.0" dependencies = [ "deno_core", ] @@ -1536,7 +1536,7 @@ checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" [[package]] name = "deno_cron" -version = "0.63.0" +version = "0.64.0" dependencies = [ "anyhow", "async-trait", @@ -1549,7 +1549,7 @@ dependencies = [ [[package]] name = "deno_crypto" -version = "0.197.0" +version = "0.198.0" dependencies = [ "aes", "aes-gcm", @@ -1639,7 +1639,7 @@ dependencies = [ [[package]] name = "deno_fetch" -version = "0.207.0" +version = "0.208.0" dependencies = [ "base64 0.21.7", "bytes", @@ -1675,7 +1675,7 @@ dependencies = [ [[package]] name = "deno_ffi" -version = "0.170.0" +version = "0.171.0" dependencies = [ "deno_core", "deno_permissions", @@ -1695,7 +1695,7 @@ dependencies = [ [[package]] name = "deno_fs" -version = "0.93.0" +version = "0.94.0" dependencies = [ "async-trait", "base32", @@ -1748,7 +1748,7 @@ dependencies = [ [[package]] name = "deno_http" -version = "0.181.0" +version = "0.182.0" dependencies = [ "async-compression", "async-trait", @@ -1787,7 +1787,7 @@ dependencies = [ [[package]] name = "deno_io" -version = "0.93.0" +version = "0.94.0" dependencies = [ "async-trait", "deno_core", @@ -1808,7 +1808,7 @@ dependencies = [ [[package]] name = "deno_kv" -version = "0.91.0" +version = "0.92.0" dependencies = [ "anyhow", "async-trait", @@ -1881,7 +1881,7 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.114.0" +version = "0.115.0" dependencies = [ "deno_core", "deno_permissions", @@ -1909,7 +1909,7 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.175.0" +version = "0.176.0" dependencies = [ "deno_core", "deno_permissions", @@ -1926,7 +1926,7 @@ dependencies = [ [[package]] name = "deno_node" -version = "0.121.0" +version = "0.122.0" dependencies = [ "aead-gcm-stream", "aes", @@ -2249,7 +2249,7 @@ dependencies = [ [[package]] name = "deno_telemetry" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-trait", "deno_core", @@ -2290,7 +2290,7 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.170.0" +version = "0.171.0" dependencies = [ "deno_core", "deno_native_certs", @@ -2340,7 +2340,7 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.183.0" +version = "0.184.0" dependencies = [ "deno_bench_util", "deno_console", @@ -2352,7 +2352,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.214.0" +version = "0.215.0" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -2374,7 +2374,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.150.0" +version = "0.151.0" dependencies = [ "deno_core", "raw-window-handle", @@ -2387,7 +2387,7 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.183.0" +version = "0.184.0" dependencies = [ "deno_bench_util", "deno_core", @@ -2395,7 +2395,7 @@ dependencies = [ [[package]] name = "deno_websocket" -version = "0.188.0" +version = "0.189.0" dependencies = [ "bytes", "deno_core", @@ -2417,7 +2417,7 @@ dependencies = [ [[package]] name = "deno_webstorage" -version = "0.178.0" +version = "0.179.0" dependencies = [ "deno_core", "deno_web", @@ -4931,7 +4931,7 @@ dependencies = [ [[package]] name = "napi_sym" -version = "0.113.0" +version = "0.114.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 55a94b75a0..63a6824e88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ repository = "https://github.com/denoland/deno" deno_ast = { version = "=0.44.0", features = ["transpiling"] } deno_core = { version = "0.326.0" } -deno_bench_util = { version = "0.177.0", path = "./bench_util" } +deno_bench_util = { version = "0.178.0", path = "./bench_util" } deno_config = { version = "=0.39.3", features = ["workspace", "sync"] } deno_lockfile = "=0.23.2" deno_media_type = { version = "0.2.0", features = ["module_specifier"] } @@ -60,7 +60,7 @@ deno_permissions = { version = "0.43.0", path = "./runtime/permissions" } deno_runtime = { version = "0.192.0", path = "./runtime" } deno_semver = "=0.6.1" deno_terminal = "0.2.0" -napi_sym = { version = "0.113.0", path = "./ext/napi/sym" } +napi_sym = { version = "0.114.0", path = "./ext/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } denokv_proto = "0.8.4" @@ -69,29 +69,29 @@ denokv_remote = "0.8.4" denokv_sqlite = { default-features = false, version = "0.8.4" } # exts -deno_broadcast_channel = { version = "0.177.0", path = "./ext/broadcast_channel" } -deno_cache = { version = "0.115.0", path = "./ext/cache" } -deno_canvas = { version = "0.52.0", path = "./ext/canvas" } -deno_console = { version = "0.183.0", path = "./ext/console" } -deno_cron = { version = "0.63.0", path = "./ext/cron" } -deno_crypto = { version = "0.197.0", path = "./ext/crypto" } -deno_fetch = { version = "0.207.0", path = "./ext/fetch" } -deno_ffi = { version = "0.170.0", path = "./ext/ffi" } -deno_fs = { version = "0.93.0", path = "./ext/fs" } -deno_http = { version = "0.181.0", path = "./ext/http" } -deno_io = { version = "0.93.0", path = "./ext/io" } -deno_kv = { version = "0.91.0", path = "./ext/kv" } -deno_napi = { version = "0.114.0", path = "./ext/napi" } -deno_net = { version = "0.175.0", path = "./ext/net" } -deno_node = { version = "0.121.0", path = "./ext/node" } -deno_telemetry = { version = "0.5.0", path = "./ext/telemetry" } -deno_tls = { version = "0.170.0", path = "./ext/tls" } -deno_url = { version = "0.183.0", path = "./ext/url" } -deno_web = { version = "0.214.0", path = "./ext/web" } -deno_webgpu = { version = "0.150.0", path = "./ext/webgpu" } -deno_webidl = { version = "0.183.0", path = "./ext/webidl" } -deno_websocket = { version = "0.188.0", path = "./ext/websocket" } -deno_webstorage = { version = "0.178.0", path = "./ext/webstorage" } +deno_broadcast_channel = { version = "0.178.0", path = "./ext/broadcast_channel" } +deno_cache = { version = "0.116.0", path = "./ext/cache" } +deno_canvas = { version = "0.53.0", path = "./ext/canvas" } +deno_console = { version = "0.184.0", path = "./ext/console" } +deno_cron = { version = "0.64.0", path = "./ext/cron" } +deno_crypto = { version = "0.198.0", path = "./ext/crypto" } +deno_fetch = { version = "0.208.0", path = "./ext/fetch" } +deno_ffi = { version = "0.171.0", path = "./ext/ffi" } +deno_fs = { version = "0.94.0", path = "./ext/fs" } +deno_http = { version = "0.182.0", path = "./ext/http" } +deno_io = { version = "0.94.0", path = "./ext/io" } +deno_kv = { version = "0.92.0", path = "./ext/kv" } +deno_napi = { version = "0.115.0", path = "./ext/napi" } +deno_net = { version = "0.176.0", path = "./ext/net" } +deno_node = { version = "0.122.0", path = "./ext/node" } +deno_telemetry = { version = "0.6.0", path = "./ext/telemetry" } +deno_tls = { version = "0.171.0", path = "./ext/tls" } +deno_url = { version = "0.184.0", path = "./ext/url" } +deno_web = { version = "0.215.0", path = "./ext/web" } +deno_webgpu = { version = "0.151.0", path = "./ext/webgpu" } +deno_webidl = { version = "0.184.0", path = "./ext/webidl" } +deno_websocket = { version = "0.189.0", path = "./ext/websocket" } +deno_webstorage = { version = "0.179.0", path = "./ext/webstorage" } # resolvers deno_npm_cache = { version = "0.3.0", path = "./resolvers/npm_cache" } diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index 8a20f07638..014b74f264 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_bench_util" -version = "0.177.0" +version = "0.178.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index 714f230cd2..4dea8f21e1 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_broadcast_channel" -version = "0.177.0" +version = "0.178.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index 7c05996498..96aec27576 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cache" -version = "0.115.0" +version = "0.116.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index ac9b236a95..7c7cc49b7c 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_canvas" -version = "0.52.0" +version = "0.53.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/console/Cargo.toml b/ext/console/Cargo.toml index df67b14a86..f68dd7d198 100644 --- a/ext/console/Cargo.toml +++ b/ext/console/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_console" -version = "0.183.0" +version = "0.184.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index c5408e450b..022a8418cf 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cron" -version = "0.63.0" +version = "0.64.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 86d984a421..c283cc9277 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_crypto" -version = "0.197.0" +version = "0.198.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index fee21808e7..e6e4ded4af 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fetch" -version = "0.207.0" +version = "0.208.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index afcbf7b4e6..9cd5c77013 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_ffi" -version = "0.170.0" +version = "0.171.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index 608554607c..1d0b623718 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fs" -version = "0.93.0" +version = "0.94.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index dfb53559d6..e7aaad2fc0 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_http" -version = "0.181.0" +version = "0.182.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 7a464ecde9..9298c654c1 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_io" -version = "0.93.0" +version = "0.94.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index e65880942b..c97aa75552 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_kv" -version = "0.91.0" +version = "0.92.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index 5a9eb7441f..5d726b3e31 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_napi" -version = "0.114.0" +version = "0.115.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/sym/Cargo.toml b/ext/napi/sym/Cargo.toml index b07dadd634..22228bd2f6 100644 --- a/ext/napi/sym/Cargo.toml +++ b/ext/napi/sym/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "napi_sym" -version = "0.113.0" +version = "0.114.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index 546152bd4b..8dbb0be391 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_net" -version = "0.175.0" +version = "0.176.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 127633a09b..60e7c96a08 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_node" -version = "0.121.0" +version = "0.122.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/telemetry/Cargo.toml b/ext/telemetry/Cargo.toml index d0bdc6be1b..fedaed6656 100644 --- a/ext/telemetry/Cargo.toml +++ b/ext/telemetry/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_telemetry" -version = "0.5.0" +version = "0.6.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index 690267b7e0..6bf1b8ea03 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_tls" -version = "0.170.0" +version = "0.171.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index de4fc67df8..9ca3ce6752 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_url" -version = "0.183.0" +version = "0.184.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index b4cd69f970..44fb2e46bf 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_web" -version = "0.214.0" +version = "0.215.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 858cdb2dab..3a491afcf8 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.150.0" +version = "0.151.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" diff --git a/ext/webidl/Cargo.toml b/ext/webidl/Cargo.toml index 0ad7d8ac10..60cb9f29f8 100644 --- a/ext/webidl/Cargo.toml +++ b/ext/webidl/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webidl" -version = "0.183.0" +version = "0.184.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index 2cd48a3816..8b8359f074 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_websocket" -version = "0.188.0" +version = "0.189.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index ff76458f33..4f9795d098 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webstorage" -version = "0.178.0" +version = "0.179.0" authors.workspace = true edition.workspace = true license.workspace = true From 350d9dce41b78b124d23c70d78feef3c8ce39a1c Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 19 Dec 2024 17:39:20 +0900 Subject: [PATCH 021/107] fix(ext/node): do not exit worker thread when there is pending async op (#27378) This change fixes the premature exit of worker threads when there are still remaining pending ops. This change reuses the idea of #22647 (unref'ing `op_worker_recv_message` in worker threads if closeOnIdle specified) and uses `web_worker.has_message_event_listener` check in the opposite way as #22944. (Now we continue the worker when `has_message_event_listener` is true instead of stopping it when `has_message_event_listener` is false. closes #23061 closes #26154 --- cli/worker.rs | 2 ++ ext/node/polyfills/worker_threads.ts | 6 ++-- ext/web/13_message_port.js | 4 +-- runtime/js/99_main.js | 11 +++++-- runtime/web_worker.rs | 33 +++---------------- runtime/worker_bootstrap.rs | 5 +++ .../permission/allow_import_worker/denied.out | 3 +- tests/unit_node/worker_threads_test.ts | 23 +++++++++++++ 8 files changed, 49 insertions(+), 38 deletions(-) diff --git a/cli/worker.rs b/cli/worker.rs index 0bbc27b29f..c733f41321 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -612,6 +612,7 @@ impl CliMainWorkerFactory { serve_port: shared.options.serve_port, serve_host: shared.options.serve_host.clone(), otel_config: shared.otel_config.clone(), + close_on_idle: true, }, extensions: custom_extensions, startup_snapshot: crate::js::deno_isolate_init(), @@ -812,6 +813,7 @@ fn create_web_worker_callback( serve_port: shared.options.serve_port, serve_host: shared.options.serve_host.clone(), otel_config: shared.otel_config.clone(), + close_on_idle: args.close_on_idle, }, extensions: vec![], startup_snapshot: crate::js::deno_isolate_init(), diff --git a/ext/node/polyfills/worker_threads.ts b/ext/node/polyfills/worker_threads.ts index 1b175fb1dd..dc844169c5 100644 --- a/ext/node/polyfills/worker_threads.ts +++ b/ext/node/polyfills/worker_threads.ts @@ -21,7 +21,7 @@ import { nodeWorkerThreadCloseCb, refMessagePort, serializeJsMessageData, - unrefPollForMessages, + unrefParentPort, } from "ext:deno_web/13_message_port.js"; import * as webidl from "ext:deno_webidl/00_webidl.js"; import { notImplemented } from "ext:deno_node/_utils.ts"; @@ -451,10 +451,10 @@ internals.__initWorkerThreads = ( parentPort.emit("close"); }); parentPort.unref = () => { - parentPort[unrefPollForMessages] = true; + parentPort[unrefParentPort] = true; }; parentPort.ref = () => { - parentPort[unrefPollForMessages] = false; + parentPort[unrefParentPort] = false; }; if (isWorkerThread) { diff --git a/ext/web/13_message_port.js b/ext/web/13_message_port.js index cf72c43e6f..79fec9de2f 100644 --- a/ext/web/13_message_port.js +++ b/ext/web/13_message_port.js @@ -102,8 +102,8 @@ const nodeWorkerThreadCloseCb = Symbol("nodeWorkerThreadCloseCb"); const nodeWorkerThreadCloseCbInvoked = Symbol("nodeWorkerThreadCloseCbInvoked"); export const refMessagePort = Symbol("refMessagePort"); /** It is used by 99_main.js and worker_threads to - * unref/ref on the global pollForMessages promise. */ -export const unrefPollForMessages = Symbol("unrefPollForMessages"); + * unref/ref on the global message event handler count. */ +export const unrefParentPort = Symbol("unrefParentPort"); /** * @param {number} id diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 19432745d4..bceb1f7ddb 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -170,12 +170,14 @@ function postMessage(message, transferOrOptions = { __proto__: null }) { let isClosing = false; let globalDispatchEvent; +let closeOnIdle; function hasMessageEventListener() { // the function name is kind of a misnomer, but we want to behave // as if we have message event listeners if a node message port is explicitly // refed (and the inverse as well) - return event.listenerCount(globalThis, "message") > 0 || + return (event.listenerCount(globalThis, "message") > 0 && + !globalThis[messagePort.unrefParentPort]) || messagePort.refedMessagePortsCount > 0; } @@ -188,7 +190,10 @@ async function pollForMessages() { } while (!isClosing) { const recvMessage = op_worker_recv_message(); - if (globalThis[messagePort.unrefPollForMessages] === true) { + // In a Node.js worker, unref() the op promise to prevent it from + // keeping the event loop alive. This avoids the need to explicitly + // call self.close() or worker.terminate(). + if (closeOnIdle) { core.unrefOpPromise(recvMessage); } const data = await recvMessage; @@ -915,6 +920,7 @@ function bootstrapWorkerRuntime( 6: argv0, 7: nodeDebug, 13: otelConfig, + 14: closeOnIdle_, } = runtimeOptions; performance.setTimeOrigin(); @@ -967,6 +973,7 @@ function bootstrapWorkerRuntime( globalThis.pollForMessages = pollForMessages; globalThis.hasMessageEventListener = hasMessageEventListener; + closeOnIdle = closeOnIdle_; for (let i = 0; i <= unstableFeatures.length; i++) { const id = unstableFeatures[i]; diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index e3a69b39c0..faf4f3fc52 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -58,7 +58,6 @@ use std::task::Poll; use crate::inspector_server::InspectorServer; use crate::ops; use crate::ops::process::NpmProcessStateProviderRc; -use crate::ops::worker_host::WorkersTable; use crate::shared::maybe_transpile_source; use crate::shared::runtime; use crate::tokio_util::create_and_run_current_thread; @@ -385,7 +384,6 @@ pub struct WebWorker { pub js_runtime: JsRuntime, pub name: String, close_on_idle: bool, - has_executed_main_module: bool, internal_handle: WebWorkerInternalHandle, pub worker_type: WebWorkerType, pub main_module: ModuleSpecifier, @@ -658,7 +656,6 @@ impl WebWorker { has_message_event_listener_fn: None, bootstrap_fn_global: Some(bootstrap_fn_global), close_on_idle: options.close_on_idle, - has_executed_main_module: false, maybe_worker_metadata: options.maybe_worker_metadata, }, external_handle, @@ -799,7 +796,6 @@ impl WebWorker { maybe_result = &mut receiver => { debug!("received worker module evaluate {:#?}", maybe_result); - self.has_executed_main_module = true; maybe_result } @@ -837,6 +833,9 @@ impl WebWorker { } if self.close_on_idle { + if self.has_message_event_listener() { + return Poll::Pending; + } return Poll::Ready(Ok(())); } @@ -851,22 +850,7 @@ impl WebWorker { Poll::Ready(Ok(())) } } - Poll::Pending => { - // This is special code path for workers created from `node:worker_threads` - // module that have different semantics than Web workers. - // We want the worker thread to terminate automatically if we've done executing - // Top-Level await, there are no child workers spawned by that workers - // and there's no "message" event listener. - if self.close_on_idle - && self.has_executed_main_module - && !self.has_child_workers() - && !self.has_message_event_listener() - { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } + Poll::Pending => Poll::Pending, } } @@ -904,15 +888,6 @@ impl WebWorker { None => false, } } - - fn has_child_workers(&mut self) -> bool { - !self - .js_runtime - .op_state() - .borrow() - .borrow::() - .is_empty() - } } fn print_worker_error( diff --git a/runtime/worker_bootstrap.rs b/runtime/worker_bootstrap.rs index 2020c2bc8d..8364fe0d2b 100644 --- a/runtime/worker_bootstrap.rs +++ b/runtime/worker_bootstrap.rs @@ -120,6 +120,7 @@ pub struct BootstrapOptions { pub serve_port: Option, pub serve_host: Option, pub otel_config: OtelConfig, + pub close_on_idle: bool, } impl Default for BootstrapOptions { @@ -155,6 +156,7 @@ impl Default for BootstrapOptions { serve_port: Default::default(), serve_host: Default::default(), otel_config: Default::default(), + close_on_idle: false, } } } @@ -198,6 +200,8 @@ struct BootstrapV8<'a>( Option, // OTEL config Box<[u8]>, + // close on idle + bool, ); impl BootstrapOptions { @@ -225,6 +229,7 @@ impl BootstrapOptions { serve_is_main, serve_worker_count, self.otel_config.as_v8(), + self.close_on_idle, ); bootstrap.serialize(ser).unwrap() diff --git a/tests/specs/permission/allow_import_worker/denied.out b/tests/specs/permission/allow_import_worker/denied.out index 6e4dcaee09..af44ae21ee 100644 --- a/tests/specs/permission/allow_import_worker/denied.out +++ b/tests/specs/permission/allow_import_worker/denied.out @@ -3,5 +3,4 @@ await import(specifier); ^ at async file:///[WILDLINE] error: Uncaught (in promise) Error: Unhandled error in child worker. - at [WILDLINE] - at [WILDLINE] \ No newline at end of file + at [WILDCARD] \ No newline at end of file diff --git a/tests/unit_node/worker_threads_test.ts b/tests/unit_node/worker_threads_test.ts index 808fd6116e..5f38d51d4d 100644 --- a/tests/unit_node/worker_threads_test.ts +++ b/tests/unit_node/worker_threads_test.ts @@ -841,3 +841,26 @@ Deno.test({ assertEquals(result, true); }, }); + +Deno.test("[node/worker_threads] Worker runs async ops correctly", async () => { + const recvMessage = Promise.withResolvers(); + const timer = setTimeout(() => recvMessage.reject(), 1000); + const worker = new workerThreads.Worker( + ` + import { parentPort } from "node:worker_threads"; + setTimeout(() => { + parentPort.postMessage("Hello from worker"); + }, 10); + `, + { eval: true }, + ); + + worker.on("message", (msg) => { + assertEquals(msg, "Hello from worker"); + worker.terminate(); + recvMessage.resolve(); + clearTimeout(timer); + }); + + await recvMessage.promise; +}); From 074444ab6c0d431f9e61a3371db4bf426f973869 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 19 Dec 2024 12:53:52 -0500 Subject: [PATCH 022/107] fix(compile): be more deterministic when compiling the same code in different directories (#27395) Additionaly, this no longer unnecessarily stores the source twice for file specifiers and fixes some sourcemap issues. Closes https://github.com/denoland/deno/issues/27284 --- cli/args/mod.rs | 4 +- cli/emit.rs | 35 ++- cli/standalone/binary.rs | 193 ++++++++++------ cli/standalone/mod.rs | 50 ++++- cli/standalone/serialization.rs | 210 ++++++++++++++---- cli/standalone/virtual_fs.rs | 159 +++++++------ tests/integration/compile_tests.rs | 73 ------ tests/specs/compile/code_cache/__test__.jsonc | 3 + tests/specs/compile/code_cache/cleanup.ts | 11 + .../specs/compile/determinism/__test__.jsonc | 17 +- tests/specs/compile/determinism/setup.ts | 10 + .../specs/compile/error/local/__test__.jsonc | 24 ++ tests/specs/compile/error/local/output.out | 6 + .../compile/error/local}/standalone_error.ts | 0 .../specs/compile/error/remote/__test__.jsonc | 24 ++ tests/specs/compile/error/remote/main.ts | 1 + tests/specs/compile/error/remote/output.out | 5 + .../standalone_error_module_with_imports_2.ts | 7 +- 18 files changed, 563 insertions(+), 269 deletions(-) create mode 100644 tests/specs/compile/code_cache/cleanup.ts create mode 100644 tests/specs/compile/determinism/setup.ts create mode 100644 tests/specs/compile/error/local/__test__.jsonc create mode 100644 tests/specs/compile/error/local/output.out rename tests/{testdata/compile => specs/compile/error/local}/standalone_error.ts (100%) create mode 100644 tests/specs/compile/error/remote/__test__.jsonc create mode 100644 tests/specs/compile/error/remote/main.ts create mode 100644 tests/specs/compile/error/remote/output.out diff --git a/cli/args/mod.rs b/cli/args/mod.rs index f820afe78d..fd34b53c8c 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1363,9 +1363,9 @@ impl CliOptions { Ok(DenoLintConfig { default_jsx_factory: (!transpile_options.jsx_automatic) - .then(|| transpile_options.jsx_factory.clone()), + .then_some(transpile_options.jsx_factory), default_jsx_fragment_factory: (!transpile_options.jsx_automatic) - .then(|| transpile_options.jsx_fragment_factory.clone()), + .then_some(transpile_options.jsx_fragment_factory), }) } diff --git a/cli/emit.rs b/cli/emit.rs index 3cd23b7abb..733a89d832 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -5,6 +5,7 @@ use crate::cache::FastInsecureHasher; use crate::cache::ParsedSourceCache; use crate::resolver::CjsTracker; +use deno_ast::EmittedSourceText; use deno_ast::ModuleKind; use deno_ast::SourceMapOption; use deno_ast::SourceRange; @@ -132,6 +133,7 @@ impl Emitter { &transpile_and_emit_options.0, &transpile_and_emit_options.1, ) + .map(|r| r.text) } }) .await @@ -166,7 +168,8 @@ impl Emitter { source.clone(), &self.transpile_and_emit_options.0, &self.transpile_and_emit_options.1, - )?; + )? + .text; helper.post_emit_parsed_source( specifier, &transpiled_source, @@ -177,6 +180,31 @@ impl Emitter { } } + pub fn emit_parsed_source_for_deno_compile( + &self, + specifier: &ModuleSpecifier, + media_type: MediaType, + module_kind: deno_ast::ModuleKind, + source: &Arc, + ) -> Result<(String, String), AnyError> { + let mut emit_options = self.transpile_and_emit_options.1.clone(); + emit_options.inline_sources = false; + emit_options.source_map = SourceMapOption::Separate; + // strip off the path to have more deterministic builds as we don't care + // about the source name because we manually provide the source map to v8 + emit_options.source_map_base = Some(deno_path_util::url_parent(specifier)); + let source = EmitParsedSourceHelper::transpile( + &self.parsed_source_cache, + specifier, + media_type, + module_kind, + source.clone(), + &self.transpile_and_emit_options.0, + &emit_options, + )?; + Ok((source.text, source.source_map.unwrap())) + } + /// Expects a file URL, panics otherwise. pub async fn load_and_emit_for_hmr( &self, @@ -282,7 +310,7 @@ impl<'a> EmitParsedSourceHelper<'a> { source: Arc, transpile_options: &deno_ast::TranspileOptions, emit_options: &deno_ast::EmitOptions, - ) -> Result { + ) -> Result { // nothing else needs the parsed source at this point, so remove from // the cache in order to not transpile owned let parsed_source = parsed_source_cache @@ -302,8 +330,7 @@ impl<'a> EmitParsedSourceHelper<'a> { source } }; - debug_assert!(transpiled_source.source_map.is_none()); - Ok(transpiled_source.text) + Ok(transpiled_source) } pub fn post_emit_parsed_source( diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 2ed52010fb..f7ffa46e4f 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -91,6 +91,7 @@ use super::serialization::DenoCompileModuleData; use super::serialization::DeserializedDataSection; use super::serialization::RemoteModulesStore; use super::serialization::RemoteModulesStoreBuilder; +use super::serialization::SourceMapStore; use super::virtual_fs::output_vfs; use super::virtual_fs::BuiltVfs; use super::virtual_fs::FileBackedVfs; @@ -98,6 +99,7 @@ use super::virtual_fs::VfsBuilder; use super::virtual_fs::VfsFileSubDataKind; use super::virtual_fs::VfsRoot; use super::virtual_fs::VirtualDirectory; +use super::virtual_fs::VirtualDirectoryEntries; use super::virtual_fs::WindowsSystemRootablePath; pub static DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME: &str = @@ -203,18 +205,25 @@ pub struct Metadata { pub otel_config: OtelConfig, } +#[allow(clippy::too_many_arguments)] fn write_binary_bytes( mut file_writer: File, original_bin: Vec, metadata: &Metadata, npm_snapshot: Option, remote_modules: &RemoteModulesStoreBuilder, + source_map_store: &SourceMapStore, vfs: &BuiltVfs, compile_flags: &CompileFlags, ) -> Result<(), AnyError> { - let data_section_bytes = - serialize_binary_data_section(metadata, npm_snapshot, remote_modules, vfs) - .context("Serializing binary data section.")?; + let data_section_bytes = serialize_binary_data_section( + metadata, + npm_snapshot, + remote_modules, + source_map_store, + vfs, + ) + .context("Serializing binary data section.")?; let target = compile_flags.resolve_target(); if target.contains("linux") { @@ -256,6 +265,7 @@ pub struct StandaloneData { pub modules: StandaloneModules, pub npm_snapshot: Option, pub root_path: PathBuf, + pub source_maps: SourceMapStore, pub vfs: Arc, } @@ -283,13 +293,12 @@ impl StandaloneModules { pub fn read<'a>( &'a self, specifier: &'a ModuleSpecifier, + kind: VfsFileSubDataKind, ) -> Result>, AnyError> { if specifier.scheme() == "file" { let path = deno_path_util::url_to_file_path(specifier)?; let bytes = match self.vfs.file_entry(&path) { - Ok(entry) => self - .vfs - .read_file_all(entry, VfsFileSubDataKind::ModuleGraph)?, + Ok(entry) => self.vfs.read_file_all(entry, kind)?, Err(err) if err.kind() == ErrorKind::NotFound => { match RealFs.read_file_sync(&path, None) { Ok(bytes) => bytes, @@ -307,7 +316,18 @@ impl StandaloneModules { data: bytes, })) } else { - self.remote_modules.read(specifier) + self.remote_modules.read(specifier).map(|maybe_entry| { + maybe_entry.map(|entry| DenoCompileModuleData { + media_type: entry.media_type, + specifier: entry.specifier, + data: match kind { + VfsFileSubDataKind::Raw => entry.data, + VfsFileSubDataKind::ModuleGraph => { + entry.transpiled_data.unwrap_or(entry.data) + } + }, + }) + }) } } } @@ -328,7 +348,8 @@ pub fn extract_standalone( mut metadata, npm_snapshot, remote_modules, - mut vfs_dir, + source_maps, + vfs_root_entries, vfs_files_data, } = match deserialize_binary_data_section(data)? { Some(data_section) => data_section, @@ -351,11 +372,12 @@ pub fn extract_standalone( metadata.argv.push(arg.into_string().unwrap()); } let vfs = { - // align the name of the directory with the root dir - vfs_dir.name = root_path.file_name().unwrap().to_string_lossy().to_string(); - let fs_root = VfsRoot { - dir: vfs_dir, + dir: VirtualDirectory { + // align the name of the directory with the root dir + name: root_path.file_name().unwrap().to_string_lossy().to_string(), + entries: vfs_root_entries, + }, root_path: root_path.clone(), start_file_offset: 0, }; @@ -372,6 +394,7 @@ pub fn extract_standalone( }, npm_snapshot, root_path, + source_maps, vfs, })) } @@ -451,7 +474,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { ) } } - self.write_standalone_binary(options, original_binary).await + self.write_standalone_binary(options, original_binary) } async fn get_base_binary( @@ -554,7 +577,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { /// This functions creates a standalone deno binary by appending a bundle /// and magic trailer to the currently executing binary. #[allow(clippy::too_many_arguments)] - async fn write_standalone_binary( + fn write_standalone_binary( &self, options: WriteBinOptions<'_>, original_bin: Vec, @@ -598,71 +621,81 @@ impl<'a> DenoCompileBinaryWriter<'a> { .with_context(|| format!("Including {}", path.display()))?; } let mut remote_modules_store = RemoteModulesStoreBuilder::default(); - let mut code_cache_key_hasher = if self.cli_options.code_cache_enabled() { - Some(FastInsecureHasher::new_deno_versioned()) - } else { - None - }; + let mut source_maps = Vec::with_capacity(graph.specifiers_count()); + // todo(dsherret): transpile in parallel for module in graph.modules() { if module.specifier().scheme() == "data" { continue; // don't store data urls as an entry as they're in the code } - if let Some(hasher) = &mut code_cache_key_hasher { - if let Some(source) = module.source() { - hasher.write(module.specifier().as_str().as_bytes()); - hasher.write(source.as_bytes()); - } - } - let (maybe_source, media_type) = match module { + let (maybe_original_source, maybe_transpiled, media_type) = match module { deno_graph::Module::Js(m) => { - let source = if m.media_type.is_emittable() { + let original_bytes = m.source.as_bytes().to_vec(); + let maybe_transpiled = if m.media_type.is_emittable() { let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script( &m.specifier, m.media_type, m.is_script, )?; let module_kind = ModuleKind::from_is_cjs(is_cjs); - let source = self - .emitter - .emit_parsed_source( + let (source, source_map) = + self.emitter.emit_parsed_source_for_deno_compile( &m.specifier, m.media_type, module_kind, &m.source, - ) - .await?; - source.into_bytes() + )?; + if source != m.source.as_ref() { + source_maps.push((&m.specifier, source_map)); + Some(source.into_bytes()) + } else { + None + } } else { - m.source.as_bytes().to_vec() + None }; - (Some(source), m.media_type) + (Some(original_bytes), maybe_transpiled, m.media_type) } deno_graph::Module::Json(m) => { - (Some(m.source.as_bytes().to_vec()), m.media_type) + (Some(m.source.as_bytes().to_vec()), None, m.media_type) } deno_graph::Module::Wasm(m) => { - (Some(m.source.to_vec()), MediaType::Wasm) + (Some(m.source.to_vec()), None, MediaType::Wasm) } deno_graph::Module::Npm(_) | deno_graph::Module::Node(_) - | deno_graph::Module::External(_) => (None, MediaType::Unknown), + | deno_graph::Module::External(_) => (None, None, MediaType::Unknown), }; - if module.specifier().scheme() == "file" { - let file_path = deno_path_util::url_to_file_path(module.specifier())?; - vfs - .add_file_with_data( - &file_path, - match maybe_source { - Some(source) => source, - None => RealFs.read_file_sync(&file_path, None)?.into_owned(), - }, - VfsFileSubDataKind::ModuleGraph, - ) - .with_context(|| { - format!("Failed adding '{}'", file_path.display()) - })?; - } else if let Some(source) = maybe_source { - remote_modules_store.add(module.specifier(), media_type, source); + if let Some(original_source) = maybe_original_source { + if module.specifier().scheme() == "file" { + let file_path = deno_path_util::url_to_file_path(module.specifier())?; + vfs + .add_file_with_data( + &file_path, + original_source, + VfsFileSubDataKind::Raw, + ) + .with_context(|| { + format!("Failed adding '{}'", file_path.display()) + })?; + if let Some(transpiled_source) = maybe_transpiled { + vfs + .add_file_with_data( + &file_path, + transpiled_source, + VfsFileSubDataKind::ModuleGraph, + ) + .with_context(|| { + format!("Failed adding '{}'", file_path.display()) + })?; + } + } else { + remote_modules_store.add( + module.specifier(), + media_type, + original_source, + maybe_transpiled, + ); + } } } remote_modules_store.add_redirects(&graph.redirects); @@ -695,6 +728,28 @@ impl<'a> DenoCompileBinaryWriter<'a> { None => StandaloneRelativeFileBaseUrl::WindowsSystemRoot, }; + let code_cache_key = if self.cli_options.code_cache_enabled() { + let mut hasher = FastInsecureHasher::new_deno_versioned(); + for module in graph.modules() { + if let Some(source) = module.source() { + hasher + .write(root_dir_url.specifier_key(module.specifier()).as_bytes()); + hasher.write(source.as_bytes()); + } + } + Some(hasher.finish()) + } else { + None + }; + + let mut source_map_store = SourceMapStore::with_capacity(source_maps.len()); + for (specifier, source_map) in source_maps { + source_map_store.add( + Cow::Owned(root_dir_url.specifier_key(specifier).into_owned()), + Cow::Owned(source_map), + ); + } + let node_modules = match self.npm_resolver.as_inner() { InnerCliNpmResolverRef::Managed(_) => { npm_snapshot.as_ref().map(|_| NodeModules::Managed { @@ -742,7 +797,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { let metadata = Metadata { argv: compile_flags.args.clone(), seed: self.cli_options.seed(), - code_cache_key: code_cache_key_hasher.map(|h| h.finish()), + code_cache_key, location: self.cli_options.location_flag().clone(), permissions: self.cli_options.permission_flags().clone(), v8_flags: self.cli_options.v8_flags().clone(), @@ -809,6 +864,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { &metadata, npm_snapshot.map(|s| s.into_serialized()), &remote_modules_store, + &source_map_store, &vfs, compile_flags, ) @@ -903,10 +959,10 @@ impl<'a> DenoCompileBinaryWriter<'a> { root_dir.name = DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME.to_string(); let mut new_entries = Vec::with_capacity(root_dir.entries.len()); let mut localhost_entries = IndexMap::new(); - for entry in std::mem::take(&mut root_dir.entries) { + for entry in root_dir.entries.take_inner() { match entry { - VfsEntry::Dir(dir) => { - for entry in dir.entries { + VfsEntry::Dir(mut dir) => { + for entry in dir.entries.take_inner() { log::debug!("Flattening {} into node_modules", entry.name()); if let Some(existing) = localhost_entries.insert(entry.name().to_string(), entry) @@ -925,11 +981,11 @@ impl<'a> DenoCompileBinaryWriter<'a> { } new_entries.push(VfsEntry::Dir(VirtualDirectory { name: "localhost".to_string(), - entries: localhost_entries.into_iter().map(|(_, v)| v).collect(), + entries: VirtualDirectoryEntries::new( + localhost_entries.into_iter().map(|(_, v)| v).collect(), + ), })); - // needs to be sorted by name - new_entries.sort_by(|a, b| a.name().cmp(b.name())); - root_dir.entries = new_entries; + root_dir.entries = VirtualDirectoryEntries::new(new_entries); // it's better to not expose the user's cache directory, so take it out // of there @@ -937,10 +993,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { let parent_dir = vfs.get_dir_mut(parent).unwrap(); let index = parent_dir .entries - .iter() - .position(|entry| { - entry.name() == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME - }) + .binary_search(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME) .unwrap(); let npm_global_cache_dir_entry = parent_dir.entries.remove(index); @@ -950,11 +1003,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME); for ancestor in parent.ancestors() { let dir = vfs.get_dir_mut(ancestor).unwrap(); - if let Some(index) = dir - .entries - .iter() - .position(|entry| entry.name() == last_name) - { + if let Ok(index) = dir.entries.binary_search(&last_name) { dir.entries.remove(index); } last_name = Cow::Owned(dir.name.clone()); @@ -965,7 +1014,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { // now build the vfs and add the global cache dir entry there let mut built_vfs = vfs.build(); - built_vfs.root.insert_entry(npm_global_cache_dir_entry); + built_vfs.entries.insert(npm_global_cache_dir_entry); built_vfs } InnerCliNpmResolverRef::Byonm(_) => vfs.build(), diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 08ee5ba11a..d7227f2bd5 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -55,6 +55,7 @@ use node_resolver::errors::ClosestPkgJsonError; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use serialization::DenoCompileModuleSource; +use serialization::SourceMapStore; use std::borrow::Cow; use std::rc::Rc; use std::sync::Arc; @@ -122,6 +123,7 @@ struct SharedModuleLoaderState { npm_module_loader: Arc, npm_req_resolver: Arc, npm_resolver: Arc, + source_maps: SourceMapStore, vfs: Arc, workspace_resolver: WorkspaceResolver, } @@ -396,7 +398,11 @@ impl ModuleLoader for EmbeddedModuleLoader { ); } - match self.shared.modules.read(original_specifier) { + match self + .shared + .modules + .read(original_specifier, VfsFileSubDataKind::ModuleGraph) + { Ok(Some(module)) => { let media_type = module.media_type; let (module_specifier, module_type, module_source) = @@ -495,6 +501,46 @@ impl ModuleLoader for EmbeddedModuleLoader { } std::future::ready(()).boxed_local() } + + fn get_source_map(&self, file_name: &str) -> Option> { + if file_name.starts_with("file:///") { + let url = + deno_path_util::url_from_directory_path(self.shared.vfs.root()).ok()?; + let file_url = ModuleSpecifier::parse(file_name).ok()?; + let relative_path = url.make_relative(&file_url)?; + self.shared.source_maps.get(&relative_path) + } else { + self.shared.source_maps.get(file_name) + } + // todo(https://github.com/denoland/deno_core/pull/1007): don't clone + .map(|s| s.as_bytes().to_vec()) + } + + fn get_source_mapped_source_line( + &self, + file_name: &str, + line_number: usize, + ) -> Option { + let specifier = ModuleSpecifier::parse(file_name).ok()?; + let data = self + .shared + .modules + .read(&specifier, VfsFileSubDataKind::Raw) + .ok()??; + + let source = String::from_utf8_lossy(&data.data); + // Do NOT use .lines(): it skips the terminating empty line. + // (due to internally using_terminator() instead of .split()) + let lines: Vec<&str> = source.split('\n').collect(); + if line_number >= lines.len() { + Some(format!( + "{} Couldn't format source line: Line {} is out of bounds (source may have changed at runtime)", + crate::colors::yellow("Warning"), line_number + 1, + )) + } else { + Some(lines[line_number].to_string()) + } + } } impl NodeRequireLoader for EmbeddedModuleLoader { @@ -590,6 +636,7 @@ pub async fn run(data: StandaloneData) -> Result { modules, npm_snapshot, root_path, + source_maps, vfs, } = data; let deno_dir_provider = Arc::new(DenoDirProvider::new(None)); @@ -841,6 +888,7 @@ pub async fn run(data: StandaloneData) -> Result { )), npm_resolver: npm_resolver.clone(), npm_req_resolver, + source_maps, vfs, workspace_resolver, }), diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index ac76172e37..8fe593c005 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -6,6 +6,7 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::io::Write; +use deno_ast::swc::common::source_map; use deno_ast::MediaType; use deno_core::anyhow::bail; use deno_core::anyhow::Context; @@ -20,12 +21,14 @@ use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmPackageId; use deno_semver::package::PackageReq; +use indexmap::IndexMap; use crate::standalone::virtual_fs::VirtualDirectory; use super::binary::Metadata; use super::virtual_fs::BuiltVfs; use super::virtual_fs::VfsBuilder; +use super::virtual_fs::VirtualDirectoryEntries; const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd"; @@ -33,21 +36,22 @@ const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd"; /// * d3n0l4nd /// * /// * -/// * +/// * /// * /// * +/// * /// * d3n0l4nd pub fn serialize_binary_data_section( metadata: &Metadata, npm_snapshot: Option, remote_modules: &RemoteModulesStoreBuilder, + source_map_store: &SourceMapStore, vfs: &BuiltVfs, ) -> Result, AnyError> { let metadata = serde_json::to_string(metadata)?; let npm_snapshot = npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default(); - let remote_modules_len = Cell::new(0_u64); - let serialized_vfs = serde_json::to_string(&vfs.root)?; + let serialized_vfs = serde_json::to_string(&vfs.entries)?; let bytes = capacity_builder::BytesBuilder::build(|builder| { builder.append(MAGIC_BYTES); @@ -63,10 +67,7 @@ pub fn serialize_binary_data_section( } // 3. Remote modules { - builder.append_le(remote_modules_len.get()); // this will be properly initialized on the second pass - let start_index = builder.len(); remote_modules.write(builder); - remote_modules_len.set((builder.len() - start_index) as u64); } // 4. VFS { @@ -78,6 +79,16 @@ pub fn serialize_binary_data_section( builder.append(file); } } + // 5. Source maps + { + builder.append_le(source_map_store.data.len() as u32); + for (specifier, source_map) in &source_map_store.data { + builder.append_le(specifier.len() as u32); + builder.append(specifier); + builder.append_le(source_map.len() as u32); + builder.append(source_map); + } + } // write the magic bytes at the end so we can use it // to make sure we've deserialized correctly @@ -91,19 +102,14 @@ pub struct DeserializedDataSection { pub metadata: Metadata, pub npm_snapshot: Option, pub remote_modules: RemoteModulesStore, - pub vfs_dir: VirtualDirectory, + pub source_maps: SourceMapStore, + pub vfs_root_entries: VirtualDirectoryEntries, pub vfs_files_data: &'static [u8], } pub fn deserialize_binary_data_section( data: &'static [u8], ) -> Result, AnyError> { - fn read_bytes_with_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> { - let (input, len) = read_u64(input)?; - let (input, data) = read_bytes(input, len as usize)?; - Ok((input, data)) - } - fn read_magic_bytes(input: &[u8]) -> Result<(&[u8], bool), AnyError> { if input.len() < MAGIC_BYTES.len() { bail!("Unexpected end of data. Could not find magic bytes."); @@ -115,34 +121,51 @@ pub fn deserialize_binary_data_section( Ok((input, true)) } + #[allow(clippy::type_complexity)] + fn read_source_map_entry( + input: &[u8], + ) -> Result<(&[u8], (Cow, Cow)), AnyError> { + let (input, specifier) = read_string_lossy(input)?; + let (input, source_map) = read_string_lossy(input)?; + Ok((input, (specifier, source_map))) + } + let (input, found) = read_magic_bytes(data)?; if !found { return Ok(None); } // 1. Metadata - let (input, data) = read_bytes_with_len(input).context("reading metadata")?; + let (input, data) = + read_bytes_with_u64_len(input).context("reading metadata")?; let metadata: Metadata = serde_json::from_slice(data).context("deserializing metadata")?; // 2. Npm snapshot let (input, data) = - read_bytes_with_len(input).context("reading npm snapshot")?; + read_bytes_with_u64_len(input).context("reading npm snapshot")?; let npm_snapshot = if data.is_empty() { None } else { Some(deserialize_npm_snapshot(data).context("deserializing npm snapshot")?) }; // 3. Remote modules - let (input, data) = - read_bytes_with_len(input).context("reading remote modules data")?; - let remote_modules = - RemoteModulesStore::build(data).context("deserializing remote modules")?; + let (input, remote_modules) = + RemoteModulesStore::build(input).context("deserializing remote modules")?; // 4. VFS - let (input, data) = read_bytes_with_len(input).context("vfs")?; - let vfs_dir: VirtualDirectory = + let (input, data) = read_bytes_with_u64_len(input).context("vfs")?; + let vfs_root_entries: VirtualDirectoryEntries = serde_json::from_slice(data).context("deserializing vfs data")?; let (input, vfs_files_data) = - read_bytes_with_len(input).context("reading vfs files data")?; + read_bytes_with_u64_len(input).context("reading vfs files data")?; + // 5. Source maps + let (mut input, source_map_data_len) = read_u32_as_usize(input)?; + let mut source_maps = SourceMapStore::with_capacity(source_map_data_len); + for _ in 0..source_map_data_len { + let (current_input, (specifier, source_map)) = + read_source_map_entry(input)?; + input = current_input; + source_maps.add(specifier, source_map); + } // finally ensure we read the magic bytes at the end let (_input, found) = read_magic_bytes(input)?; @@ -154,7 +177,8 @@ pub fn deserialize_binary_data_section( metadata, npm_snapshot, remote_modules, - vfs_dir, + source_maps, + vfs_root_entries, vfs_files_data, })) } @@ -162,19 +186,31 @@ pub fn deserialize_binary_data_section( #[derive(Default)] pub struct RemoteModulesStoreBuilder { specifiers: Vec<(String, u64)>, - data: Vec<(MediaType, Vec)>, + data: Vec<(MediaType, Vec, Option>)>, data_byte_len: u64, redirects: Vec<(String, String)>, redirects_len: u64, } impl RemoteModulesStoreBuilder { - pub fn add(&mut self, specifier: &Url, media_type: MediaType, data: Vec) { + pub fn add( + &mut self, + specifier: &Url, + media_type: MediaType, + data: Vec, + maybe_transpiled: Option>, + ) { log::debug!("Adding '{}' ({})", specifier, media_type); let specifier = specifier.to_string(); self.specifiers.push((specifier, self.data_byte_len)); - self.data_byte_len += 1 + 8 + data.len() as u64; // media type (1 byte), data length (8 bytes), data - self.data.push((media_type, data)); + let maybe_transpiled_len = match &maybe_transpiled { + // data length (4 bytes), data + Some(data) => 4 + data.len() as u64, + None => 0, + }; + // media type (1 byte), data length (4 bytes), data, has transpiled (1 byte), transpiled length + self.data_byte_len += 1 + 4 + data.len() as u64 + 1 + maybe_transpiled_len; + self.data.push((media_type, data, maybe_transpiled)); } pub fn add_redirects(&mut self, redirects: &BTreeMap) { @@ -193,7 +229,7 @@ impl RemoteModulesStoreBuilder { builder.append_le(self.redirects.len() as u32); for (specifier, offset) in &self.specifiers { builder.append_le(specifier.len() as u32); - builder.append(specifier.as_bytes()); + builder.append(specifier); builder.append_le(*offset); } for (from, to) in &self.redirects { @@ -202,10 +238,32 @@ impl RemoteModulesStoreBuilder { builder.append_le(to.len() as u32); builder.append(to); } - for (media_type, data) in &self.data { + builder.append_le( + self + .data + .iter() + .map(|(_, data, maybe_transpiled)| { + 1 + 4 + + (data.len() as u64) + + 1 + + match maybe_transpiled { + Some(transpiled) => 4 + (transpiled.len() as u64), + None => 0, + } + }) + .sum::(), + ); + for (media_type, data, maybe_transpiled) in &self.data { builder.append(serialize_media_type(*media_type)); - builder.append_le(data.len() as u64); + builder.append_le(data.len() as u32); builder.append(data); + if let Some(transpiled) = maybe_transpiled { + builder.append(1); + builder.append_le(transpiled.len() as u32); + builder.append(transpiled); + } else { + builder.append(0); + } } } } @@ -234,6 +292,30 @@ impl DenoCompileModuleSource { } } +pub struct SourceMapStore { + data: IndexMap, Cow<'static, str>>, +} + +impl SourceMapStore { + pub fn with_capacity(capacity: usize) -> Self { + Self { + data: IndexMap::with_capacity(capacity), + } + } + + pub fn add( + &mut self, + specifier: Cow<'static, str>, + source_map: Cow<'static, str>, + ) { + self.data.insert(specifier, source_map); + } + + pub fn get(&self, specifier: &str) -> Option<&Cow<'static, str>> { + self.data.get(specifier) + } +} + pub struct DenoCompileModuleData<'a> { pub specifier: &'a Url, pub media_type: MediaType, @@ -280,6 +362,13 @@ impl<'a> DenoCompileModuleData<'a> { } } +pub struct RemoteModuleEntry<'a> { + pub specifier: &'a Url, + pub media_type: MediaType, + pub data: Cow<'static, [u8]>, + pub transpiled_data: Option>, +} + enum RemoteModulesStoreSpecifierValue { Data(usize), Redirect(Url), @@ -291,7 +380,7 @@ pub struct RemoteModulesStore { } impl RemoteModulesStore { - fn build(data: &'static [u8]) -> Result { + fn build(input: &'static [u8]) -> Result<(&'static [u8], Self), AnyError> { fn read_specifier(input: &[u8]) -> Result<(&[u8], (Url, u64)), AnyError> { let (input, specifier) = read_string_lossy(input)?; let specifier = Url::parse(&specifier)?; @@ -334,12 +423,16 @@ impl RemoteModulesStore { Ok((input, specifiers)) } - let (files_data, specifiers) = read_headers(data)?; + let (input, specifiers) = read_headers(input)?; + let (input, files_data) = read_bytes_with_u64_len(input)?; - Ok(Self { - specifiers, - files_data, - }) + Ok(( + input, + Self { + specifiers, + files_data, + }, + )) } pub fn resolve_specifier<'a>( @@ -370,7 +463,7 @@ impl RemoteModulesStore { pub fn read<'a>( &'a self, original_specifier: &'a Url, - ) -> Result>, AnyError> { + ) -> Result>, AnyError> { let mut count = 0; let mut specifier = original_specifier; loop { @@ -386,12 +479,25 @@ impl RemoteModulesStore { let input = &self.files_data[*offset..]; let (input, media_type_byte) = read_bytes(input, 1)?; let media_type = deserialize_media_type(media_type_byte[0])?; - let (input, len) = read_u64(input)?; - let (_input, data) = read_bytes(input, len as usize)?; - return Ok(Some(DenoCompileModuleData { + let (input, data) = read_bytes_with_u32_len(input)?; + check_has_len(input, 1)?; + let (input, has_transpiled) = (&input[1..], input[0]); + let (_, transpiled_data) = match has_transpiled { + 0 => (input, None), + 1 => { + let (input, data) = read_bytes_with_u32_len(input)?; + (input, Some(data)) + } + value => bail!( + "Invalid transpiled data flag: {}. Compiled data is corrupt.", + value + ), + }; + return Ok(Some(RemoteModuleEntry { specifier, media_type, data: Cow::Borrowed(data), + transpiled_data: transpiled_data.map(Cow::Borrowed), })); } None => { @@ -630,14 +736,32 @@ fn parse_vec_n_times_with_index( Ok((input, results)) } +fn read_bytes_with_u64_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> { + let (input, len) = read_u64(input)?; + let (input, data) = read_bytes(input, len as usize)?; + Ok((input, data)) +} + +fn read_bytes_with_u32_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> { + let (input, len) = read_u32_as_usize(input)?; + let (input, data) = read_bytes(input, len)?; + Ok((input, data)) +} + fn read_bytes(input: &[u8], len: usize) -> Result<(&[u8], &[u8]), AnyError> { - if input.len() < len { - bail!("Unexpected end of data.",); - } + check_has_len(input, len)?; let (len_bytes, input) = input.split_at(len); Ok((input, len_bytes)) } +#[inline(always)] +fn check_has_len(input: &[u8], len: usize) -> Result<(), AnyError> { + if input.len() < len { + bail!("Unexpected end of data."); + } + Ok(()) +} + fn read_string_lossy(input: &[u8]) -> Result<(&[u8], Cow), AnyError> { let (input, str_len) = read_u32_as_usize(input)?; let (input, data_bytes) = read_bytes(input, str_len)?; diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index 04e66d680e..522fe47dd9 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -67,7 +67,7 @@ impl WindowsSystemRootablePath { #[derive(Debug)] pub struct BuiltVfs { pub root_path: WindowsSystemRootablePath, - pub root: VirtualDirectory, + pub entries: VirtualDirectoryEntries, pub files: Vec>, } @@ -95,7 +95,7 @@ impl VfsBuilder { Self { executable_root: VirtualDirectory { name: "/".to_string(), - entries: Vec::new(), + entries: Default::default(), }, files: Vec::new(), current_offset: 0, @@ -208,23 +208,20 @@ impl VfsBuilder { continue; } let name = component.as_os_str().to_string_lossy(); - let index = match current_dir - .entries - .binary_search_by(|e| e.name().cmp(&name)) - { + let index = match current_dir.entries.binary_search(&name) { Ok(index) => index, Err(insert_index) => { - current_dir.entries.insert( + current_dir.entries.0.insert( insert_index, VfsEntry::Dir(VirtualDirectory { name: name.to_string(), - entries: Vec::new(), + entries: Default::default(), }), ); insert_index } }; - match &mut current_dir.entries[index] { + match &mut current_dir.entries.0[index] { VfsEntry::Dir(dir) => { current_dir = dir; } @@ -248,14 +245,8 @@ impl VfsBuilder { continue; } let name = component.as_os_str().to_string_lossy(); - let index = match current_dir - .entries - .binary_search_by(|e| e.name().cmp(&name)) - { - Ok(index) => index, - Err(_) => return None, - }; - match &mut current_dir.entries[index] { + let entry = current_dir.entries.get_mut_by_name(&name)?; + match entry { VfsEntry::Dir(dir) => { current_dir = dir; } @@ -320,9 +311,9 @@ impl VfsBuilder { offset, len: data.len() as u64, }; - match dir.entries.binary_search_by(|e| e.name().cmp(&name)) { + match dir.entries.binary_search(&name) { Ok(index) => { - let entry = &mut dir.entries[index]; + let entry = &mut dir.entries.0[index]; match entry { VfsEntry::File(virtual_file) => match sub_data_kind { VfsFileSubDataKind::Raw => { @@ -336,7 +327,7 @@ impl VfsBuilder { } } Err(insert_index) => { - dir.entries.insert( + dir.entries.0.insert( insert_index, VfsEntry::File(VirtualFile { name: name.to_string(), @@ -384,10 +375,10 @@ impl VfsBuilder { let target = normalize_path(path.parent().unwrap().join(&target)); let dir = self.add_dir_raw(path.parent().unwrap()); let name = path.file_name().unwrap().to_string_lossy(); - match dir.entries.binary_search_by(|e| e.name().cmp(&name)) { + match dir.entries.binary_search(&name) { Ok(_) => {} // previously inserted Err(insert_index) => { - dir.entries.insert( + dir.entries.0.insert( insert_index, VfsEntry::Symlink(VirtualSymlink { name: name.to_string(), @@ -426,7 +417,7 @@ impl VfsBuilder { dir: &mut VirtualDirectory, parts: &[String], ) { - for entry in &mut dir.entries { + for entry in &mut dir.entries.0 { match entry { VfsEntry::Dir(dir) => { strip_prefix_from_symlinks(dir, parts); @@ -454,13 +445,13 @@ impl VfsBuilder { if self.min_root_dir.as_ref() == Some(¤t_path) { break; } - match ¤t_dir.entries[0] { + match ¤t_dir.entries.0[0] { VfsEntry::Dir(dir) => { if dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME { // special directory we want to maintain break; } - match current_dir.entries.remove(0) { + match current_dir.entries.0.remove(0) { VfsEntry::Dir(dir) => { current_path = WindowsSystemRootablePath::Path(current_path.join(&dir.name)); @@ -480,7 +471,7 @@ impl VfsBuilder { } BuiltVfs { root_path: current_path, - root: current_dir, + entries: current_dir.entries, files: self.files, } } @@ -506,7 +497,7 @@ pub fn output_vfs(vfs: &BuiltVfs, executable_name: &str) { return; // no need to compute if won't output } - if vfs.root.entries.is_empty() { + if vfs.entries.is_empty() { return; // nothing to output } @@ -696,7 +687,7 @@ fn vfs_as_display_tree( fn dir_size(dir: &VirtualDirectory, seen_offsets: &mut HashSet) -> Size { let mut size = Size::default(); - for entry in &dir.entries { + for entry in dir.entries.iter() { match entry { VfsEntry::Dir(virtual_directory) => { size = size + dir_size(virtual_directory, seen_offsets); @@ -760,15 +751,10 @@ fn vfs_as_display_tree( fn include_all_entries<'a>( dir_path: &WindowsSystemRootablePath, - vfs_dir: &'a VirtualDirectory, + entries: &'a VirtualDirectoryEntries, seen_offsets: &mut HashSet, ) -> Vec> { - if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME { - return show_global_node_modules_dir(vfs_dir, seen_offsets); - } - - vfs_dir - .entries + entries .iter() .map(|entry| DirEntryOutput { name: Cow::Borrowed(entry.name()), @@ -826,10 +812,12 @@ fn vfs_as_display_tree( } else { EntryOutput::Subset(children) } + } else if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME { + EntryOutput::Subset(show_global_node_modules_dir(vfs_dir, seen_offsets)) } else { EntryOutput::Subset(include_all_entries( &WindowsSystemRootablePath::Path(dir), - vfs_dir, + &vfs_dir.entries, seen_offsets, )) } @@ -839,7 +827,7 @@ fn vfs_as_display_tree( // user might not have context about what's being shown let mut seen_offsets = HashSet::with_capacity(vfs.files.len()); let mut child_entries = - include_all_entries(&vfs.root_path, &vfs.root, &mut seen_offsets); + include_all_entries(&vfs.root_path, &vfs.entries, &mut seen_offsets); for child_entry in &mut child_entries { child_entry.collapse_leaf_nodes(); } @@ -961,27 +949,70 @@ impl VfsEntry { } } +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct VirtualDirectoryEntries(Vec); + +impl VirtualDirectoryEntries { + pub fn new(mut entries: Vec) -> Self { + // needs to be sorted by name + entries.sort_by(|a, b| a.name().cmp(b.name())); + Self(entries) + } + + pub fn take_inner(&mut self) -> Vec { + std::mem::take(&mut self.0) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get_by_name(&self, name: &str) -> Option<&VfsEntry> { + self.binary_search(name).ok().map(|index| &self.0[index]) + } + + pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut VfsEntry> { + self + .binary_search(name) + .ok() + .map(|index| &mut self.0[index]) + } + + pub fn binary_search(&self, name: &str) -> Result { + self.0.binary_search_by(|e| e.name().cmp(name)) + } + + pub fn insert(&mut self, entry: VfsEntry) { + match self.binary_search(entry.name()) { + Ok(index) => { + self.0[index] = entry; + } + Err(insert_index) => { + self.0.insert(insert_index, entry); + } + } + } + + pub fn remove(&mut self, index: usize) -> VfsEntry { + self.0.remove(index) + } + + pub fn iter(&self) -> std::slice::Iter<'_, VfsEntry> { + self.0.iter() + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct VirtualDirectory { #[serde(rename = "n")] pub name: String, // should be sorted by name #[serde(rename = "e")] - pub entries: Vec, -} - -impl VirtualDirectory { - pub fn insert_entry(&mut self, entry: VfsEntry) { - let name = entry.name(); - match self.entries.binary_search_by(|e| e.name().cmp(name)) { - Ok(index) => { - self.entries[index] = entry; - } - Err(insert_index) => { - self.entries.insert(insert_index, entry); - } - } - } + pub entries: VirtualDirectoryEntries, } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] @@ -1136,20 +1167,13 @@ impl VfsRoot { } }; let component = component.to_string_lossy(); - match current_dir + current_entry = current_dir .entries - .binary_search_by(|e| e.name().cmp(&component)) - { - Ok(index) => { - current_entry = current_dir.entries[index].as_ref(); - } - Err(_) => { - return Err(std::io::Error::new( - std::io::ErrorKind::NotFound, - "path not found", - )); - } - } + .get_by_name(&component) + .ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::NotFound, "path not found") + })? + .as_ref(); } Ok((final_path, current_entry)) @@ -1706,7 +1730,10 @@ mod test { FileBackedVfs::new( Cow::Owned(data), VfsRoot { - dir: vfs.root, + dir: VirtualDirectory { + name: "".to_string(), + entries: vfs.entries, + }, root_path: dest_path.to_path_buf(), start_file_offset: 0, }, diff --git a/tests/integration/compile_tests.rs b/tests/integration/compile_tests.rs index a69e873ab4..a715233933 100644 --- a/tests/integration/compile_tests.rs +++ b/tests/integration/compile_tests.rs @@ -2,7 +2,6 @@ use deno_core::serde_json; use test_util as util; -use util::assert_contains; use util::assert_not_contains; use util::testdata_path; use util::TestContext; @@ -90,78 +89,6 @@ fn standalone_args() { .assert_exit_code(0); } -#[test] -fn standalone_error() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("error.exe") - } else { - dir.path().join("error") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_error.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - let output = context.new_command().name(&exe).split_output().run(); - output.assert_exit_code(1); - output.assert_stdout_matches_text(""); - let stderr = output.stderr(); - // On Windows, we cannot assert the file path (because '\'). - // Instead we just check for relevant output. - assert_contains!(stderr, "error: Uncaught (in promise) Error: boom!"); - assert_contains!(stderr, "\n at boom (file://"); - assert_contains!(stderr, "standalone_error.ts:2:9"); - assert_contains!(stderr, "at foo (file://"); - assert_contains!(stderr, "standalone_error.ts:5:3"); - assert_contains!(stderr, "standalone_error.ts:7:1"); -} - -#[test] -fn standalone_error_module_with_imports() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("error.exe") - } else { - dir.path().join("error") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_error_module_with_imports_1.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - let output = context - .new_command() - .name(&exe) - .env("NO_COLOR", "1") - .split_output() - .run(); - output.assert_stdout_matches_text("hello\n"); - let stderr = output.stderr(); - // On Windows, we cannot assert the file path (because '\'). - // Instead we just check for relevant output. - assert_contains!(stderr, "error: Uncaught (in promise) Error: boom!"); - assert_contains!(stderr, "\n at file://"); - assert_contains!(stderr, "standalone_error_module_with_imports_2.ts:2:7"); - output.assert_exit_code(1); -} - #[test] fn standalone_load_datauri() { let context = TestContextBuilder::new().build(); diff --git a/tests/specs/compile/code_cache/__test__.jsonc b/tests/specs/compile/code_cache/__test__.jsonc index 72353e27da..f1c3461adc 100644 --- a/tests/specs/compile/code_cache/__test__.jsonc +++ b/tests/specs/compile/code_cache/__test__.jsonc @@ -1,6 +1,9 @@ { "tempDir": true, "steps": [{ + "args": "run -A cleanup.ts", + "output": "[WILDCARD]" + }, { "if": "unix", "args": "compile --output using_code_cache --log-level=debug main.ts", "output": "[WILDCARD]" diff --git a/tests/specs/compile/code_cache/cleanup.ts b/tests/specs/compile/code_cache/cleanup.ts new file mode 100644 index 0000000000..d9e7c805f8 --- /dev/null +++ b/tests/specs/compile/code_cache/cleanup.ts @@ -0,0 +1,11 @@ +import { tmpdir } from "node:os"; + +// cleanup the code cache file from a previous run +try { + if (Deno.build.os === "windows") { + Deno.removeSync(tmpdir() + "\\deno-compile-using_code_cache.exe.cache"); + } else { + Deno.removeSync(tmpdir() + "\\deno-compile-using_code_cache.cache"); + } +} catch { +} diff --git a/tests/specs/compile/determinism/__test__.jsonc b/tests/specs/compile/determinism/__test__.jsonc index 97045744f1..b84a1fdf18 100644 --- a/tests/specs/compile/determinism/__test__.jsonc +++ b/tests/specs/compile/determinism/__test__.jsonc @@ -1,28 +1,31 @@ { "tempDir": true, "steps": [{ - "if": "unix", - "args": "compile --output main1 main.ts", + "args": "run -A setup.ts", "output": "[WILDCARD]" }, { "if": "unix", - "args": "compile --output main2 main.ts", + "args": "compile --no-config --output a/main a/main.ts", "output": "[WILDCARD]" }, { "if": "unix", - "args": "run --allow-read=. assert_equal.ts main1 main2", + "args": "compile --no-config --output b/main b/main.ts", + "output": "[WILDCARD]" + }, { + "if": "unix", + "args": "run --allow-read=. assert_equal.ts a/main b/main", "output": "Same\n" }, { "if": "windows", - "args": "compile --output main1.exe main.ts", + "args": "compile --no-config --output a/main.exe a/main.ts", "output": "[WILDCARD]" }, { "if": "windows", - "args": "compile --output main2.exe main.ts", + "args": "compile --no-config --output b/main.exe b/main.ts", "output": "[WILDCARD]" }, { "if": "windows", - "args": "run --allow-read=. assert_equal.ts main1.exe main2.exe", + "args": "run --allow-read=. assert_equal.ts a/main.exe b/main.exe", "output": "Same\n" }] } diff --git a/tests/specs/compile/determinism/setup.ts b/tests/specs/compile/determinism/setup.ts new file mode 100644 index 0000000000..8bb5753079 --- /dev/null +++ b/tests/specs/compile/determinism/setup.ts @@ -0,0 +1,10 @@ +// for setup, we create two directories with the same file in each +// and then when compiling we ensure this directory name has no +// effect on the output +makeCopyDir("a"); +makeCopyDir("b"); + +function makeCopyDir(dirName) { + Deno.mkdirSync(dirName); + Deno.copyFileSync("main.ts", `${dirName}/main.ts`); +} diff --git a/tests/specs/compile/error/local/__test__.jsonc b/tests/specs/compile/error/local/__test__.jsonc new file mode 100644 index 0000000000..8d6a015a51 --- /dev/null +++ b/tests/specs/compile/error/local/__test__.jsonc @@ -0,0 +1,24 @@ +{ + "tempDir": true, + "steps": [{ + "if": "unix", + "args": "compile --output main standalone_error.ts", + "output": "[WILDCARD]" + }, { + "if": "unix", + "commandName": "./main", + "args": [], + "output": "output.out", + "exitCode": 1 + }, { + "if": "windows", + "args": "compile --output main.exe standalone_error.ts", + "output": "[WILDCARD]" + }, { + "if": "windows", + "commandName": "./main.exe", + "args": [], + "output": "output.out", + "exitCode": 1 + }] +} diff --git a/tests/specs/compile/error/local/output.out b/tests/specs/compile/error/local/output.out new file mode 100644 index 0000000000..b346734ae6 --- /dev/null +++ b/tests/specs/compile/error/local/output.out @@ -0,0 +1,6 @@ +error: Uncaught (in promise) Error: boom! + throw new Error("boom!"); + ^ + at boom (file:///[WILDLINE]standalone_error.ts:2:9) + at foo (file:///[WILDLINE]standalone_error.ts:6:3) + at file:///[WILDLINE]standalone_error.ts:9:1 diff --git a/tests/testdata/compile/standalone_error.ts b/tests/specs/compile/error/local/standalone_error.ts similarity index 100% rename from tests/testdata/compile/standalone_error.ts rename to tests/specs/compile/error/local/standalone_error.ts diff --git a/tests/specs/compile/error/remote/__test__.jsonc b/tests/specs/compile/error/remote/__test__.jsonc new file mode 100644 index 0000000000..9ad9091ec6 --- /dev/null +++ b/tests/specs/compile/error/remote/__test__.jsonc @@ -0,0 +1,24 @@ +{ + "tempDir": true, + "steps": [{ + "if": "unix", + "args": "compile -A --output main main.ts", + "output": "[WILDCARD]" + }, { + "if": "unix", + "commandName": "./main", + "args": [], + "output": "output.out", + "exitCode": 1 + }, { + "if": "windows", + "args": "compile -A --output main.exe main.ts", + "output": "[WILDCARD]" + }, { + "if": "windows", + "commandName": "./main.exe", + "args": [], + "output": "output.out", + "exitCode": 1 + }] +} diff --git a/tests/specs/compile/error/remote/main.ts b/tests/specs/compile/error/remote/main.ts new file mode 100644 index 0000000000..7a27276dd8 --- /dev/null +++ b/tests/specs/compile/error/remote/main.ts @@ -0,0 +1 @@ +import "http://localhost:4545/compile/standalone_error_module_with_imports_1.ts"; diff --git a/tests/specs/compile/error/remote/output.out b/tests/specs/compile/error/remote/output.out new file mode 100644 index 0000000000..3e23694c16 --- /dev/null +++ b/tests/specs/compile/error/remote/output.out @@ -0,0 +1,5 @@ +hello +error: Uncaught (in promise) Error: boom! +throw new Error(value); + ^ + at http://localhost:4545/compile/standalone_error_module_with_imports_2.ts:7:7 diff --git a/tests/testdata/compile/standalone_error_module_with_imports_2.ts b/tests/testdata/compile/standalone_error_module_with_imports_2.ts index ef052b512e..c83d7ceea6 100644 --- a/tests/testdata/compile/standalone_error_module_with_imports_2.ts +++ b/tests/testdata/compile/standalone_error_module_with_imports_2.ts @@ -1,2 +1,7 @@ +// file has blank lines to make the input line +// different than the output console.log("hello"); -throw new Error("boom!"); + +const value: string = "boom!"; + +throw new Error(value); From 351e79642a7cbceec5f5e97f41529207fc08796a Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Thu, 19 Dec 2024 19:10:58 +0100 Subject: [PATCH 023/107] fix(task): support tasks without commands (#27191) Support running tasks that have no command and only dependencies. This is useful for when you want to group tasks only. --- Cargo.lock | 4 +-- Cargo.toml | 2 +- cli/lsp/language_server.rs | 2 +- cli/lsp/lsp_custom.rs | 2 +- cli/schemas/config-file.v1.json | 1 - cli/tools/task.rs | 28 +++++++++++++------ tests/specs/task/dependencies/__test__.jsonc | 12 ++++++++ tests/specs/task/dependencies/no_command.out | 5 ++++ .../task/dependencies/no_command/deno.json | 13 +++++++++ .../task/dependencies/no_command_list.out | 7 +++++ 10 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 tests/specs/task/dependencies/no_command.out create mode 100644 tests/specs/task/dependencies/no_command/deno.json create mode 100644 tests/specs/task/dependencies/no_command_list.out diff --git a/Cargo.lock b/Cargo.lock index 4dddd53d75..05d3ef429b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1463,9 +1463,9 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.39.3" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce717af3fe6788dae63965d58d5637fd62be8fe4f345f189137ffc06c51837d2" +checksum = "459b0bf193d2f9a177d18064a4888062ba0716312f56dbda8f1319444a8b2544" dependencies = [ "anyhow", "deno_package_json", diff --git a/Cargo.toml b/Cargo.toml index 63a6824e88..ccdf64986e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ deno_ast = { version = "=0.44.0", features = ["transpiling"] } deno_core = { version = "0.326.0" } deno_bench_util = { version = "0.178.0", path = "./bench_util" } -deno_config = { version = "=0.39.3", features = ["workspace", "sync"] } +deno_config = { version = "=0.40.0", features = ["workspace", "sync"] } deno_lockfile = "=0.23.2" deno_media_type = { version = "0.2.0", features = ["module_specifier"] } deno_npm = "=0.26.0" diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 3ffe4491e0..bd461833c2 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -3793,7 +3793,7 @@ impl Inner { for (name, command) in scripts { result.push(TaskDefinition { name: name.clone(), - command: command.clone(), + command: Some(command.clone()), source_uri: url_to_uri(&package_json.specifier()) .map_err(|_| LspError::internal_error())?, }); diff --git a/cli/lsp/lsp_custom.rs b/cli/lsp/lsp_custom.rs index 74c6aca88b..8df4ba1d07 100644 --- a/cli/lsp/lsp_custom.rs +++ b/cli/lsp/lsp_custom.rs @@ -14,7 +14,7 @@ pub const LATEST_DIAGNOSTIC_BATCH_INDEX: &str = #[serde(rename_all = "camelCase")] pub struct TaskDefinition { pub name: String, - pub command: String, + pub command: Option, pub source_uri: lsp::Uri, } diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json index 1e3abb2c0d..d644072f4c 100644 --- a/cli/schemas/config-file.v1.json +++ b/cli/schemas/config-file.v1.json @@ -446,7 +446,6 @@ }, "command": { "type": "string", - "required": true, "description": "The task to execute" }, "dependencies": { diff --git a/cli/tools/task.rs b/cli/tools/task.rs index ec9b238847..740b0ce843 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -231,7 +231,7 @@ pub async fn execute_script( &Url::from_directory_path(cli_options.initial_cwd()).unwrap(), "", &TaskDefinition { - command: task_flags.task.as_ref().unwrap().to_string(), + command: Some(task_flags.task.as_ref().unwrap().to_string()), dependencies: vec![], description: None, }, @@ -448,6 +448,16 @@ impl<'a> TaskRunner<'a> { kill_signal: KillSignal, argv: &'a [String], ) -> Result { + let Some(command) = &definition.command else { + log::info!( + "{} {} {}", + colors::green("Task"), + colors::cyan(task_name), + colors::gray("(no command)") + ); + return Ok(0); + }; + if let Some(npm_resolver) = self.npm_resolver.as_managed() { npm_resolver.ensure_top_level_package_json_install().await?; npm_resolver @@ -469,7 +479,7 @@ impl<'a> TaskRunner<'a> { self .run_single(RunSingleOptions { task_name, - script: &definition.command, + script: command, cwd: &cwd, custom_commands, kill_signal, @@ -837,7 +847,7 @@ fn print_available_tasks( is_deno: false, name: name.to_string(), task: deno_config::deno_json::TaskDefinition { - command: script.to_string(), + command: Some(script.to_string()), dependencies: vec![], description: None, }, @@ -873,11 +883,13 @@ fn print_available_tasks( )?; } } - writeln!( - writer, - " {}", - strip_ansi_codes_and_escape_control_chars(&desc.task.command) - )?; + if let Some(command) = &desc.task.command { + writeln!( + writer, + " {}", + strip_ansi_codes_and_escape_control_chars(command) + )?; + }; if !desc.task.dependencies.is_empty() { let dependencies = desc .task diff --git a/tests/specs/task/dependencies/__test__.jsonc b/tests/specs/task/dependencies/__test__.jsonc index 84c98f11a4..c9032153b3 100644 --- a/tests/specs/task/dependencies/__test__.jsonc +++ b/tests/specs/task/dependencies/__test__.jsonc @@ -61,6 +61,18 @@ "cwd": "arg_task_with_deps", "args": "task a a", "output": "./arg_task_with_deps.out" + }, + "no_command": { + "cwd": "no_command", + "args": "task a", + "output": "./no_command.out", + "exitCode": 0 + }, + "no_command_list": { + "cwd": "no_command", + "args": "task", + "output": "./no_command_list.out", + "exitCode": 0 } } } diff --git a/tests/specs/task/dependencies/no_command.out b/tests/specs/task/dependencies/no_command.out new file mode 100644 index 0000000000..521b3541df --- /dev/null +++ b/tests/specs/task/dependencies/no_command.out @@ -0,0 +1,5 @@ +Task b echo 'b' +b +Task c echo 'c' +c +Task a (no command) diff --git a/tests/specs/task/dependencies/no_command/deno.json b/tests/specs/task/dependencies/no_command/deno.json new file mode 100644 index 0000000000..5588365a92 --- /dev/null +++ b/tests/specs/task/dependencies/no_command/deno.json @@ -0,0 +1,13 @@ +{ + "tasks": { + "a": { + "dependencies": ["b", "c"] + }, + "b": { + "command": "echo 'b'" + }, + "c": { + "command": "echo 'c'" + } + } +} diff --git a/tests/specs/task/dependencies/no_command_list.out b/tests/specs/task/dependencies/no_command_list.out new file mode 100644 index 0000000000..3d58c1cb06 --- /dev/null +++ b/tests/specs/task/dependencies/no_command_list.out @@ -0,0 +1,7 @@ +Available tasks: +- a + depends on: b, c +- b + echo 'b' +- c + echo 'c' From 3c147d6be1845743ef7b83386b3ef4f9c40938b1 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 19 Dec 2024 13:53:33 -0500 Subject: [PATCH 024/107] fix(publish): infer literal types in const contexts (#27425) * https://github.com/denoland/deno_graph/pull/555 --- Cargo.lock | 5 +++-- cli/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05d3ef429b..965b7a4150 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1718,12 +1718,13 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.86.3" +version = "0.86.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc78ed0b4bbcb4197300f0d6e7d1edc2d2c5019cdb9dedba7ff229158441885b" +checksum = "f8502f5dd37f522c76e92961ec6e855f40bc351d50d62bc0752cc19517eed8ec" dependencies = [ "anyhow", "async-trait", + "capacity_builder", "data-url", "deno_ast", "deno_semver", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 70708002c7..4b0380717b 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -74,7 +74,7 @@ deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] } deno_error.workspace = true -deno_graph = { version = "=0.86.3" } +deno_graph = { version = "=0.86.4" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true deno_npm.workspace = true From feb94d09e714b345a41d77b72db61afc0f2d68d3 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Fri, 20 Dec 2024 02:33:35 +0000 Subject: [PATCH 025/107] fix(lsp): rewrite imports for 'Move to a new file' action (#27427) --- cli/lsp/analysis.rs | 16 +++-- cli/lsp/documents.rs | 7 ++ cli/lsp/language_server.rs | 44 +++++-------- tests/integration/lsp_tests.rs | 113 +++++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 33 deletions(-) diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 9f9cf14864..4c6a20927b 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -603,18 +603,24 @@ fn try_reverse_map_package_json_exports( /// For a set of tsc changes, can them for any that contain something that looks /// like an import and rewrite the import specifier to include the extension pub fn fix_ts_import_changes( - referrer: &ModuleSpecifier, - resolution_mode: ResolutionMode, changes: &[tsc::FileTextChanges], language_server: &language_server::Inner, ) -> Result, AnyError> { - let import_mapper = language_server.get_ts_response_import_mapper(referrer); let mut r = Vec::new(); for change in changes { + let Ok(referrer) = ModuleSpecifier::parse(&change.file_name) else { + continue; + }; + let referrer_doc = language_server.get_asset_or_document(&referrer).ok(); + let resolution_mode = referrer_doc + .as_ref() + .map(|d| d.resolution_mode()) + .unwrap_or(ResolutionMode::Import); + let import_mapper = + language_server.get_ts_response_import_mapper(&referrer); let mut text_changes = Vec::new(); for text_change in &change.text_changes { let lines = text_change.new_text.split('\n'); - let new_lines: Vec = lines .map(|line| { // This assumes that there's only one import per line. @@ -622,7 +628,7 @@ pub fn fix_ts_import_changes( let specifier = captures.iter().skip(1).find_map(|s| s).unwrap().as_str(); if let Some(new_specifier) = import_mapper - .check_unresolved_specifier(specifier, referrer, resolution_mode) + .check_unresolved_specifier(specifier, &referrer, resolution_mode) { line.replace(specifier, &new_specifier) } else { diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index bdb64c9da3..d15cfe5a6c 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -251,6 +251,13 @@ impl AssetOrDocument { pub fn document_lsp_version(&self) -> Option { self.document().and_then(|d| d.maybe_lsp_version()) } + + pub fn resolution_mode(&self) -> ResolutionMode { + match self { + AssetOrDocument::Asset(_) => ResolutionMode::Import, + AssetOrDocument::Document(d) => d.resolution_mode(), + } + } } type ModuleResult = Result; diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index bd461833c2..a7a0a59743 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1855,20 +1855,12 @@ impl Inner { } let changes = if code_action_data.fix_id == "fixMissingImport" { - fix_ts_import_changes( - &code_action_data.specifier, - maybe_asset_or_doc - .as_ref() - .and_then(|d| d.document()) - .map(|d| d.resolution_mode()) - .unwrap_or(ResolutionMode::Import), - &combined_code_actions.changes, - self, - ) - .map_err(|err| { - error!("Unable to remap changes: {:#}", err); - LspError::internal_error() - })? + fix_ts_import_changes(&combined_code_actions.changes, self).map_err( + |err| { + error!("Unable to remap changes: {:#}", err); + LspError::internal_error() + }, + )? } else { combined_code_actions.changes }; @@ -1912,20 +1904,16 @@ impl Inner { asset_or_doc.scope().cloned(), ) .await?; - if kind_suffix == ".rewrite.function.returnType" { - refactor_edit_info.edits = fix_ts_import_changes( - &action_data.specifier, - asset_or_doc - .document() - .map(|d| d.resolution_mode()) - .unwrap_or(ResolutionMode::Import), - &refactor_edit_info.edits, - self, - ) - .map_err(|err| { - error!("Unable to remap changes: {:#}", err); - LspError::internal_error() - })? + if kind_suffix == ".rewrite.function.returnType" + || kind_suffix == ".move.newFile" + { + refactor_edit_info.edits = + fix_ts_import_changes(&refactor_edit_info.edits, self).map_err( + |err| { + error!("Unable to remap changes: {:#}", err); + LspError::internal_error() + }, + )? } code_action.edit = refactor_edit_info.to_workspace_edit(self)?; code_action diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 56c060d958..825cef6247 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -6066,6 +6066,119 @@ fn lsp_jsr_code_action_missing_declaration() { ); } +#[test] +fn lsp_jsr_code_action_move_to_new_file() { + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); + let temp_dir = context.temp_dir(); + let file = source_file( + temp_dir.path().join("file.ts"), + r#" + import { someFunction } from "jsr:@denotest/types-file"; + export const someValue = someFunction(); + "#, + ); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.write_request( + "workspace/executeCommand", + json!({ + "command": "deno.cache", + "arguments": [[], file.url()], + }), + ); + client.did_open_file(&file); + let list = client + .write_request_with_res_as::>( + "textDocument/codeAction", + json!({ + "textDocument": { "uri": file.url() }, + "range": { + "start": { "line": 2, "character": 19 }, + "end": { "line": 2, "character": 28 }, + }, + "context": { "diagnostics": [] }, + }), + ) + .unwrap(); + let action = list + .iter() + .find_map(|c| match c { + lsp::CodeActionOrCommand::CodeAction(a) + if &a.title == "Move to a new file" => + { + Some(a) + } + _ => None, + }) + .unwrap(); + let res = client.write_request("codeAction/resolve", json!(action)); + assert_eq!( + res, + json!({ + "title": "Move to a new file", + "kind": "refactor.move.newFile", + "edit": { + "documentChanges": [ + { + "textDocument": { "uri": file.url(), "version": 1 }, + "edits": [ + { + "range": { + "start": { "line": 1, "character": 6 }, + "end": { "line": 2, "character": 0 }, + }, + "newText": "", + }, + { + "range": { + "start": { "line": 2, "character": 0 }, + "end": { "line": 3, "character": 4 }, + }, + "newText": "", + }, + ], + }, + { + "kind": "create", + "uri": file.url().join("someValue.ts").unwrap(), + "options": { + "ignoreIfExists": true, + }, + }, + { + "textDocument": { + "uri": file.url().join("someValue.ts").unwrap(), + "version": null, + }, + "edits": [ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 0 }, + }, + "newText": "import { someFunction } from \"jsr:@denotest/types-file\";\n\nexport const someValue = someFunction();\n", + }, + ], + }, + ], + }, + "isPreferred": false, + "data": { + "specifier": file.url(), + "range": { + "start": { "line": 2, "character": 19 }, + "end": { "line": 2, "character": 28 }, + }, + "refactorName": "Move to a new file", + "actionName": "Move to a new file", + }, + }), + ); +} + #[test] fn lsp_code_actions_deno_cache_npm() { let context = TestContextBuilder::new().use_temp_cwd().build(); From 23f7032d56630c3fcd8da9e226992fa61d3d9db6 Mon Sep 17 00:00:00 2001 From: Filip Stevanovic <62512535+filipstev@users.noreply.github.com> Date: Fri, 20 Dec 2024 05:23:51 +0100 Subject: [PATCH 026/107] fix(ext/node): add `truncate` method to the `FileHandle` class (#27389) --- ext/node/polyfills/_fs/_fs_ftruncate.ts | 18 ++++-- ext/node/polyfills/internal/fs/handle.ts | 25 +++----- tests/unit_node/_fs/_fs_handle_test.ts | 82 ++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 20 deletions(-) diff --git a/ext/node/polyfills/_fs/_fs_ftruncate.ts b/ext/node/polyfills/_fs/_fs_ftruncate.ts index 92af46f521..79320137f9 100644 --- a/ext/node/polyfills/_fs/_fs_ftruncate.ts +++ b/ext/node/polyfills/_fs/_fs_ftruncate.ts @@ -16,16 +16,24 @@ export function ftruncate( : undefined; const callback: CallbackWithError = typeof lenOrCallback === "function" ? lenOrCallback - : maybeCallback as CallbackWithError; + : (maybeCallback as CallbackWithError); if (!callback) throw new Error("No callback function supplied"); - new FsFile(fd, Symbol.for("Deno.internal.FsFile")).truncate(len).then( - () => callback(null), - callback, - ); + new FsFile(fd, Symbol.for("Deno.internal.FsFile")) + .truncate(len) + .then(() => callback(null), callback); } export function ftruncateSync(fd: number, len?: number) { new FsFile(fd, Symbol.for("Deno.internal.FsFile")).truncateSync(len); } + +export function ftruncatePromise(fd: number, len?: number): Promise { + return new Promise((resolve, reject) => { + ftruncate(fd, len, (err) => { + if (err) reject(err); + else resolve(); + }); + }); +} diff --git a/ext/node/polyfills/internal/fs/handle.ts b/ext/node/polyfills/internal/fs/handle.ts index 9ec0fc97e2..ee035f2f5c 100644 --- a/ext/node/polyfills/internal/fs/handle.ts +++ b/ext/node/polyfills/internal/fs/handle.ts @@ -13,6 +13,7 @@ import { ReadOptions, TextOptionsArgument, } from "ext:deno_node/_fs/_fs_common.ts"; +import { ftruncatePromise } from "ext:deno_node/_fs/_fs_ftruncate.ts"; import { core } from "ext:core/mod.js"; interface WriteResult { @@ -73,6 +74,10 @@ export class FileHandle extends EventEmitter { } } + truncate(len?: number): Promise { + return fsCall(ftruncatePromise, this, len); + } + readFile( opt?: TextOptionsArgument | BinaryOptionsArgument | FileOptionsArgument, ): Promise { @@ -85,11 +90,7 @@ export class FileHandle extends EventEmitter { length: number, position: number, ): Promise; - write( - str: string, - position: number, - encoding: string, - ): Promise; + write(str: string, position: number, encoding: string): Promise; write( bufferOrStr: Uint8Array | string, offsetOrPosition: number, @@ -120,16 +121,10 @@ export class FileHandle extends EventEmitter { const encoding = lengthOrEncoding; return new Promise((resolve, reject) => { - write( - this.fd, - str, - position, - encoding, - (err, bytesWritten, buffer) => { - if (err) reject(err); - else resolve({ buffer, bytesWritten }); - }, - ); + write(this.fd, str, position, encoding, (err, bytesWritten, buffer) => { + if (err) reject(err); + else resolve({ buffer, bytesWritten }); + }); }); } } diff --git a/tests/unit_node/_fs/_fs_handle_test.ts b/tests/unit_node/_fs/_fs_handle_test.ts index e26b82aa06..84d72c0745 100644 --- a/tests/unit_node/_fs/_fs_handle_test.ts +++ b/tests/unit_node/_fs/_fs_handle_test.ts @@ -117,3 +117,85 @@ Deno.test("[node/fs filehandle.writeFile] Write to file", async function () { assertEquals(decoder.decode(data), "hello world"); }); + +Deno.test( + "[node/fs filehandle.truncate] Truncate file with length", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w+"); + + await fileHandle.writeFile("hello world"); + + await fileHandle.truncate(5); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(decoder.decode(data), "hello"); + }, +); + +Deno.test( + "[node/fs filehandle.truncate] Truncate file without length", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w+"); + + await fileHandle.writeFile("hello world"); + + await fileHandle.truncate(); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(decoder.decode(data), ""); + }, +); + +Deno.test( + "[node/fs filehandle.truncate] Truncate file with extension", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w+"); + + await fileHandle.writeFile("hi"); + + await fileHandle.truncate(5); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + const expected = new Uint8Array(5); + expected.set(new TextEncoder().encode("hi")); + + assertEquals(data, expected); + assertEquals(data.length, 5); + assertEquals(decoder.decode(data.subarray(0, 2)), "hi"); + // Verify null bytes + assertEquals(data[2], 0); + assertEquals(data[3], 0); + assertEquals(data[4], 0); + }, +); + +Deno.test( + "[node/fs filehandle.truncate] Truncate file with negative length", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w+"); + + await fileHandle.writeFile("hello world"); + + await fileHandle.truncate(-1); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(decoder.decode(data), ""); + assertEquals(data.length, 0); + }, +); From 65b647909d75a586c087af3e10f54338e689e81c Mon Sep 17 00:00:00 2001 From: snek Date: Fri, 20 Dec 2024 13:48:48 +0100 Subject: [PATCH 027/107] feat(unstable): Implement QUIC (#21942) Implements a QUIC interface, loosely based on the WebTransport API (a future change could add the WebTransport API, built on top of this one). [quinn](https://docs.rs/quinn/latest/quinn/) is used for the underlying QUIC implementation, for a few reasons: - A cloneable "handle" api which fits quite nicely into deno resources. - Good collaboration with the rust ecosystem, especially rustls. - I like it. --- Cargo.lock | 53 ++- cli/tsc/99_main_compiler.js | 7 + ext/net/03_quic.js | 367 ++++++++++++++++ ext/net/Cargo.toml | 1 + ext/net/lib.deno_net.d.ts | 288 +++++++++++++ ext/net/lib.rs | 29 +- ext/net/quic.rs | 660 +++++++++++++++++++++++++++++ ext/web/06_streams.js | 8 +- runtime/fmt_errors.rs | 14 + runtime/js/90_deno_ns.js | 10 + tests/integration/js_unit_tests.rs | 1 + tests/unit/quic_test.ts | 172 ++++++++ 12 files changed, 1591 insertions(+), 19 deletions(-) create mode 100644 ext/net/03_quic.js create mode 100644 ext/net/quic.rs create mode 100644 tests/unit/quic_test.ts diff --git a/Cargo.lock b/Cargo.lock index 965b7a4150..6cd68e5ce1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -728,6 +728,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.37" @@ -1918,6 +1924,7 @@ dependencies = [ "hickory-proto", "hickory-resolver", "pin-project", + "quinn", "rustls-tokio-stream", "serde", "socket2", @@ -4025,9 +4032,9 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper 1.4.1", "hyper-util", @@ -5908,49 +5915,54 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.2" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "rustls", - "thiserror 1.0.64", + "socket2", + "thiserror 2.0.3", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom", "rand", "ring", "rustc-hash 2.0.0", "rustls", + "rustls-pki-types", "slab", - "thiserror 1.0.64", + "thiserror 2.0.3", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.2" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" +checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" dependencies = [ + "cfg_aliases 0.2.1", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6427,6 +6439,9 @@ name = "rustls-pki-types" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +dependencies = [ + "web-time", +] [[package]] name = "rustls-tokio-stream" @@ -8523,6 +8538,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-root-certs" version = "0.26.6" @@ -8550,7 +8575,7 @@ dependencies = [ "arrayvec", "bit-vec", "bitflags 2.6.0", - "cfg_aliases", + "cfg_aliases 0.1.1", "codespan-reporting", "document-features", "indexmap 2.3.0", @@ -8582,7 +8607,7 @@ dependencies = [ "bit-set", "bitflags 2.6.0", "block", - "cfg_aliases", + "cfg_aliases 0.1.1", "core-graphics-types", "d3d12", "glow", diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 7e8a407cf9..f7862c95e4 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -41,6 +41,13 @@ delete Object.prototype.__proto__; "listen", "listenDatagram", "openKv", + "connectQuic", + "listenQuic", + "QuicBidirectionalStream", + "QuicConn", + "QuicListener", + "QuicReceiveStream", + "QuicSendStream", ]); const unstableMsgSuggestion = "If not, try changing the 'lib' compiler option to include 'deno.unstable' " + diff --git a/ext/net/03_quic.js b/ext/net/03_quic.js new file mode 100644 index 0000000000..e100e7bd64 --- /dev/null +++ b/ext/net/03_quic.js @@ -0,0 +1,367 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +import { core, primordials } from "ext:core/mod.js"; +import { + op_quic_accept, + op_quic_accept_bi, + op_quic_accept_incoming, + op_quic_accept_uni, + op_quic_close_connection, + op_quic_close_endpoint, + op_quic_connect, + op_quic_connection_closed, + op_quic_connection_get_protocol, + op_quic_connection_get_remote_addr, + op_quic_endpoint_get_addr, + op_quic_get_send_stream_priority, + op_quic_incoming_accept, + op_quic_incoming_ignore, + op_quic_incoming_local_ip, + op_quic_incoming_refuse, + op_quic_incoming_remote_addr, + op_quic_incoming_remote_addr_validated, + op_quic_listen, + op_quic_max_datagram_size, + op_quic_open_bi, + op_quic_open_uni, + op_quic_read_datagram, + op_quic_send_datagram, + op_quic_set_send_stream_priority, +} from "ext:core/ops"; +import { + getWritableStreamResourceBacking, + ReadableStream, + readableStreamForRid, + WritableStream, + writableStreamForRid, +} from "ext:deno_web/06_streams.js"; +import { loadTlsKeyPair } from "ext:deno_net/02_tls.js"; +const { + BadResourcePrototype, +} = core; +const { + Uint8Array, + TypedArrayPrototypeSubarray, + SymbolAsyncIterator, + SafePromisePrototypeFinally, + ObjectPrototypeIsPrototypeOf, +} = primordials; + +class QuicSendStream extends WritableStream { + get sendOrder() { + return op_quic_get_send_stream_priority( + getWritableStreamResourceBacking(this).rid, + ); + } + + set sendOrder(p) { + op_quic_set_send_stream_priority( + getWritableStreamResourceBacking(this).rid, + p, + ); + } +} + +class QuicReceiveStream extends ReadableStream {} + +function readableStream(rid, closed) { + // stream can be indirectly closed by closing connection. + SafePromisePrototypeFinally(closed, () => { + core.tryClose(rid); + }); + return readableStreamForRid(rid, true, QuicReceiveStream); +} + +function writableStream(rid, closed) { + // stream can be indirectly closed by closing connection. + SafePromisePrototypeFinally(closed, () => { + core.tryClose(rid); + }); + return writableStreamForRid(rid, true, QuicSendStream); +} + +class QuicBidirectionalStream { + #readable; + #writable; + + constructor(txRid, rxRid, closed) { + this.#readable = readableStream(rxRid, closed); + this.#writable = writableStream(txRid, closed); + } + + get readable() { + return this.#readable; + } + + get writable() { + return this.#writable; + } +} + +async function* bidiStream(conn, closed) { + try { + while (true) { + const r = await op_quic_accept_bi(conn); + yield new QuicBidirectionalStream(r[0], r[1], closed); + } + } catch (error) { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + return; + } + throw error; + } +} + +async function* uniStream(conn, closed) { + try { + while (true) { + const uniRid = await op_quic_accept_uni(conn); + yield readableStream(uniRid, closed); + } + } catch (error) { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + return; + } + throw error; + } +} + +class QuicConn { + #resource; + #bidiStream = null; + #uniStream = null; + #closed; + + constructor(resource) { + this.#resource = resource; + + this.#closed = op_quic_connection_closed(this.#resource); + core.unrefOpPromise(this.#closed); + } + + get protocol() { + return op_quic_connection_get_protocol(this.#resource); + } + + get remoteAddr() { + return op_quic_connection_get_remote_addr(this.#resource); + } + + async createBidirectionalStream( + { sendOrder, waitUntilAvailable } = { __proto__: null }, + ) { + const { 0: txRid, 1: rxRid } = await op_quic_open_bi( + this.#resource, + waitUntilAvailable ?? false, + ); + if (sendOrder !== null && sendOrder !== undefined) { + op_quic_set_send_stream_priority(txRid, sendOrder); + } + return new QuicBidirectionalStream(txRid, rxRid, this.#closed); + } + + async createUnidirectionalStream( + { sendOrder, waitUntilAvailable } = { __proto__: null }, + ) { + const rid = await op_quic_open_uni( + this.#resource, + waitUntilAvailable ?? false, + ); + if (sendOrder !== null && sendOrder !== undefined) { + op_quic_set_send_stream_priority(rid, sendOrder); + } + return writableStream(rid, this.#closed); + } + + get incomingBidirectionalStreams() { + if (this.#bidiStream === null) { + this.#bidiStream = ReadableStream.from( + bidiStream(this.#resource, this.#closed), + ); + } + return this.#bidiStream; + } + + get incomingUnidirectionalStreams() { + if (this.#uniStream === null) { + this.#uniStream = ReadableStream.from( + uniStream(this.#resource, this.#closed), + ); + } + return this.#uniStream; + } + + get maxDatagramSize() { + return op_quic_max_datagram_size(this.#resource); + } + + async readDatagram(p) { + const view = p || new Uint8Array(this.maxDatagramSize); + const nread = await op_quic_read_datagram(this.#resource, view); + return TypedArrayPrototypeSubarray(view, 0, nread); + } + + async sendDatagram(data) { + await op_quic_send_datagram(this.#resource, data); + } + + get closed() { + core.refOpPromise(this.#closed); + return this.#closed; + } + + close({ closeCode, reason }) { + op_quic_close_connection(this.#resource, closeCode, reason); + } +} + +class QuicIncoming { + #incoming; + + constructor(incoming) { + this.#incoming = incoming; + } + + get localIp() { + return op_quic_incoming_local_ip(this.#incoming); + } + + get remoteAddr() { + return op_quic_incoming_remote_addr(this.#incoming); + } + + get remoteAddressValidated() { + return op_quic_incoming_remote_addr_validated(this.#incoming); + } + + async accept() { + const conn = await op_quic_incoming_accept(this.#incoming); + return new QuicConn(conn); + } + + refuse() { + op_quic_incoming_refuse(this.#incoming); + } + + ignore() { + op_quic_incoming_ignore(this.#incoming); + } +} + +class QuicListener { + #endpoint; + + constructor(endpoint) { + this.#endpoint = endpoint; + } + + get addr() { + return op_quic_endpoint_get_addr(this.#endpoint); + } + + async accept() { + const conn = await op_quic_accept(this.#endpoint); + return new QuicConn(conn); + } + + async incoming() { + const incoming = await op_quic_accept_incoming(this.#endpoint); + return new QuicIncoming(incoming); + } + + async next() { + let conn; + try { + conn = await this.accept(); + } catch (error) { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + return { value: undefined, done: true }; + } + throw error; + } + return { value: conn, done: false }; + } + + [SymbolAsyncIterator]() { + return this; + } + + close({ closeCode, reason }) { + op_quic_close_endpoint(this.#endpoint, closeCode, reason); + } +} + +async function listenQuic( + { + hostname, + port, + cert, + key, + alpnProtocols, + keepAliveInterval, + maxIdleTimeout, + maxConcurrentBidirectionalStreams, + maxConcurrentUnidirectionalStreams, + }, +) { + hostname = hostname || "0.0.0.0"; + const keyPair = loadTlsKeyPair("Deno.listenQuic", { cert, key }); + const endpoint = await op_quic_listen( + { hostname, port }, + { alpnProtocols }, + { + keepAliveInterval, + maxIdleTimeout, + maxConcurrentBidirectionalStreams, + maxConcurrentUnidirectionalStreams, + }, + keyPair, + ); + return new QuicListener(endpoint); +} + +async function connectQuic( + { + hostname, + port, + serverName, + caCerts, + cert, + key, + alpnProtocols, + keepAliveInterval, + maxIdleTimeout, + maxConcurrentBidirectionalStreams, + maxConcurrentUnidirectionalStreams, + congestionControl, + }, +) { + const keyPair = loadTlsKeyPair("Deno.connectQuic", { cert, key }); + const conn = await op_quic_connect( + { hostname, port }, + { + caCerts, + alpnProtocols, + serverName, + }, + { + keepAliveInterval, + maxIdleTimeout, + maxConcurrentBidirectionalStreams, + maxConcurrentUnidirectionalStreams, + congestionControl, + }, + keyPair, + ); + return new QuicConn(conn); +} + +export { + connectQuic, + listenQuic, + QuicBidirectionalStream, + QuicConn, + QuicIncoming, + QuicListener, + QuicReceiveStream, + QuicSendStream, +}; diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index 8dbb0be391..eaee7bfb4b 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -20,6 +20,7 @@ deno_tls.workspace = true hickory-proto = "0.25.0-alpha.4" hickory-resolver.workspace = true pin-project.workspace = true +quinn = { version = "0.11.6", default-features = false, features = ["runtime-tokio", "rustls", "ring"] } rustls-tokio-stream.workspace = true serde.workspace = true socket2.workspace = true diff --git a/ext/net/lib.deno_net.d.ts b/ext/net/lib.deno_net.d.ts index 827081f2a4..958474cbbd 100644 --- a/ext/net/lib.deno_net.d.ts +++ b/ext/net/lib.deno_net.d.ts @@ -450,5 +450,293 @@ declare namespace Deno { options?: StartTlsOptions, ): Promise; + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicTransportOptions { + /** Period of inactivity before sending a keep-alive packet. Keep-alive + * packets prevent an inactive but otherwise healthy connection from timing + * out. Only one side of any given connection needs keep-alive enabled for + * the connection to be preserved. + * @default {undefined} + */ + keepAliveInterval?: number; + /** Maximum duration of inactivity to accept before timing out the + * connection. The true idle timeout is the minimum of this and the peer’s + * own max idle timeout. + * @default {undefined} + */ + maxIdleTimeout?: number; + /** Maximum number of incoming bidirectional streams that may be open + * concurrently. + * @default {100} + */ + maxConcurrentBidirectionalStreams?: number; + /** Maximum number of incoming unidirectional streams that may be open + * concurrently. + * @default {100} + */ + maxConcurrentUnidirectionalStreams?: number; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface ListenQuicOptions extends QuicTransportOptions { + /** The port to connect to. */ + port: number; + /** + * A literal IP address or host name that can be resolved to an IP address. + * @default {"0.0.0.0"} + */ + hostname?: string; + /** Server private key in PEM format */ + key: string; + /** Cert chain in PEM format */ + cert: string; + /** Application-Layer Protocol Negotiation (ALPN) protocols to announce to + * the client. QUIC requires the use of ALPN. + */ + alpnProtocols: string[]; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * Listen announces on the local transport address over QUIC. + * + * ```ts + * const lstnr = await Deno.listenQuic({ port: 443, cert: "...", key: "...", alpnProtocols: ["h3"] }); + * ``` + * + * Requires `allow-net` permission. + * + * @experimental + * @tags allow-net + * @category Network + */ + export function listenQuic(options: ListenQuicOptions): Promise; + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface ConnectQuicOptions extends QuicTransportOptions { + /** The port to connect to. */ + port: number; + /** A literal IP address or host name that can be resolved to an IP address. */ + hostname: string; + /** The name used for validating the certificate provided by the server. If + * not provided, defaults to `hostname`. */ + serverName?: string | undefined; + /** Application-Layer Protocol Negotiation (ALPN) protocols supported by + * the client. QUIC requires the use of ALPN. + */ + alpnProtocols: string[]; + /** A list of root certificates that will be used in addition to the + * default root certificates to verify the peer's certificate. + * + * Must be in PEM format. */ + caCerts?: string[]; + /** + * The congestion control algorithm used when sending data over this connection. + */ + congestionControl?: "throughput" | "low-latency"; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * Establishes a secure connection over QUIC using a hostname and port. The + * cert file is optional and if not included Mozilla's root certificates will + * be used. See also https://github.com/ctz/webpki-roots for specifics. + * + * ```ts + * const caCert = await Deno.readTextFile("./certs/my_custom_root_CA.pem"); + * const conn1 = await Deno.connectQuic({ hostname: "example.com", port: 443, alpnProtocols: ["h3"] }); + * const conn2 = await Deno.connectQuic({ caCerts: [caCert], hostname: "example.com", port: 443, alpnProtocols: ["h3"] }); + * ``` + * + * Requires `allow-net` permission. + * + * @experimental + * @tags allow-net + * @category Network + */ + export function connectQuic(options: ConnectQuicOptions): Promise; + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicCloseInfo { + /** A number representing the error code for the error. */ + closeCode: number; + /** A string representing the reason for closing the connection. */ + reason: string; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * An incoming connection for which the server has not yet begun its part of the handshake. + * + * @experimental + * @category Network + */ + export interface QuicIncoming { + /** + * The local IP address which was used when the peer established the connection. + */ + readonly localIp: string; + + /** + * The peer’s UDP address. + */ + readonly remoteAddr: NetAddr; + + /** + * Whether the socket address that is initiating this connection has proven that they can receive traffic. + */ + readonly remoteAddressValidated: boolean; + + /** + * Accept this incoming connection. + */ + accept(): Promise; + + /** + * Refuse this incoming connection. + */ + refuse(): void; + + /** + * Ignore this incoming connection attempt, not sending any packet in response. + */ + ignore(): void; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * Specialized listener that accepts QUIC connections. + * + * @experimental + * @category Network + */ + export interface QuicListener extends AsyncIterable { + /** Return the address of the `QuicListener`. */ + readonly addr: NetAddr; + + /** Waits for and resolves to the next connection to the `QuicListener`. */ + accept(): Promise; + + /** Waits for and resolves to the next incoming request to the `QuicListener`. */ + incoming(): Promise; + + /** Close closes the listener. Any pending accept promises will be rejected + * with errors. */ + close(info: QuicCloseInfo): void; + + [Symbol.asyncIterator](): AsyncIterableIterator; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicSendStreamOptions { + /** Indicates the send priority of this stream relative to other streams for + * which the value has been set. + * @default {undefined} + */ + sendOrder?: number; + /** Wait until there is sufficient flow credit to create the stream. + * @default {false} + */ + waitUntilAvailable?: boolean; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicConn { + /** Close closes the listener. Any pending accept promises will be rejected + * with errors. */ + close(info: QuicCloseInfo): void; + /** Opens and returns a bidirectional stream. */ + createBidirectionalStream( + options?: QuicSendStreamOptions, + ): Promise; + /** Opens and returns a unidirectional stream. */ + createUnidirectionalStream( + options?: QuicSendStreamOptions, + ): Promise; + /** Send a datagram. The provided data cannot be larger than + * `maxDatagramSize`. */ + sendDatagram(data: Uint8Array): Promise; + /** Receive a datagram. If no buffer is provider, one will be allocated. + * The size of the provided buffer should be at least `maxDatagramSize`. */ + readDatagram(buffer?: Uint8Array): Promise; + + /** Return the remote address for the connection. Clients may change + * addresses at will, for example when switching to a cellular internet + * connection. + */ + readonly remoteAddr: NetAddr; + /** The negotiated ALPN protocol, if provided. */ + readonly protocol: string | undefined; + /** Returns a promise that resolves when the connection is closed. */ + readonly closed: Promise; + /** A stream of bidirectional streams opened by the peer. */ + readonly incomingBidirectionalStreams: ReadableStream< + QuicBidirectionalStream + >; + /** A stream of unidirectional streams opened by the peer. */ + readonly incomingUnidirectionalStreams: ReadableStream; + /** Returns the datagram stream for sending and receiving datagrams. */ + readonly maxDatagramSize: number; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicBidirectionalStream { + /** Returns a QuicReceiveStream instance that can be used to read incoming data. */ + readonly readable: QuicReceiveStream; + /** Returns a QuicSendStream instance that can be used to write outgoing data. */ + readonly writable: QuicSendStream; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicSendStream extends WritableStream { + /** Indicates the send priority of this stream relative to other streams for + * which the value has been set. */ + sendOrder: number; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * @experimental + * @category Network + */ + export interface QuicReceiveStream extends ReadableStream {} + export {}; // only export exports } diff --git a/ext/net/lib.rs b/ext/net/lib.rs index f482750b38..04b3f80010 100644 --- a/ext/net/lib.rs +++ b/ext/net/lib.rs @@ -5,6 +5,7 @@ pub mod ops; pub mod ops_tls; #[cfg(unix)] pub mod ops_unix; +mod quic; pub mod raw; pub mod resolve_addr; pub mod tcp; @@ -158,8 +159,34 @@ deno_core::extension!(deno_net, ops_unix::op_node_unstable_net_listen_unixpacket

, ops_unix::op_net_recv_unixpacket, ops_unix::op_net_send_unixpacket

, + + quic::op_quic_accept, + quic::op_quic_accept_bi, + quic::op_quic_accept_incoming, + quic::op_quic_accept_uni, + quic::op_quic_close_connection, + quic::op_quic_close_endpoint, + quic::op_quic_connection_closed, + quic::op_quic_connection_get_protocol, + quic::op_quic_connection_get_remote_addr, + quic::op_quic_connect

, + quic::op_quic_endpoint_get_addr, + quic::op_quic_get_send_stream_priority, + quic::op_quic_incoming_accept, + quic::op_quic_incoming_refuse, + quic::op_quic_incoming_ignore, + quic::op_quic_incoming_local_ip, + quic::op_quic_incoming_remote_addr, + quic::op_quic_incoming_remote_addr_validated, + quic::op_quic_listen

, + quic::op_quic_max_datagram_size, + quic::op_quic_open_bi, + quic::op_quic_open_uni, + quic::op_quic_read_datagram, + quic::op_quic_send_datagram, + quic::op_quic_set_send_stream_priority, ], - esm = [ "01_net.js", "02_tls.js" ], + esm = [ "01_net.js", "02_tls.js", "03_quic.js" ], options = { root_cert_store_provider: Option>, unsafely_ignore_certificate_errors: Option>, diff --git a/ext/net/quic.rs b/ext/net/quic.rs new file mode 100644 index 0000000000..16f68364be --- /dev/null +++ b/ext/net/quic.rs @@ -0,0 +1,660 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use crate::resolve_addr::resolve_addr; +use crate::DefaultTlsOptions; +use crate::NetPermissions; +use crate::UnsafelyIgnoreCertificateErrors; +use deno_core::error::bad_resource; +use deno_core::error::generic_error; +use deno_core::error::AnyError; +use deno_core::futures::task::noop_waker_ref; +use deno_core::op2; +use deno_core::AsyncRefCell; +use deno_core::AsyncResult; +use deno_core::BufView; +use deno_core::GarbageCollected; +use deno_core::JsBuffer; +use deno_core::OpState; +use deno_core::RcRef; +use deno_core::Resource; +use deno_core::ResourceId; +use deno_core::WriteOutcome; +use deno_tls::create_client_config; +use deno_tls::SocketUse; +use deno_tls::TlsKeys; +use deno_tls::TlsKeysHolder; +use quinn::crypto::rustls::QuicClientConfig; +use quinn::crypto::rustls::QuicServerConfig; +use serde::Deserialize; +use serde::Serialize; +use std::borrow::Cow; +use std::cell::RefCell; +use std::future::Future; +use std::net::IpAddr; +use std::net::Ipv4Addr; +use std::net::Ipv6Addr; +use std::net::SocketAddrV4; +use std::net::SocketAddrV6; +use std::pin::pin; +use std::rc::Rc; +use std::sync::Arc; +use std::task::Context; +use std::task::Poll; +use std::time::Duration; + +#[derive(Debug, Deserialize, Serialize)] +struct Addr { + hostname: String, + port: u16, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct ListenArgs { + alpn_protocols: Option>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct TransportConfig { + keep_alive_interval: Option, + max_idle_timeout: Option, + max_concurrent_bidirectional_streams: Option, + max_concurrent_unidirectional_streams: Option, + preferred_address_v4: Option, + preferred_address_v6: Option, + congestion_control: Option, +} + +impl TryInto for TransportConfig { + type Error = AnyError; + + fn try_into(self) -> Result { + let mut cfg = quinn::TransportConfig::default(); + + if let Some(interval) = self.keep_alive_interval { + cfg.keep_alive_interval(Some(Duration::from_millis(interval))); + } + + if let Some(timeout) = self.max_idle_timeout { + cfg.max_idle_timeout(Some(Duration::from_millis(timeout).try_into()?)); + } + + if let Some(max) = self.max_concurrent_bidirectional_streams { + cfg.max_concurrent_bidi_streams(max.into()); + } + + if let Some(max) = self.max_concurrent_unidirectional_streams { + cfg.max_concurrent_uni_streams(max.into()); + } + + if let Some(v) = self.congestion_control { + let controller: Option< + Arc, + > = match v.as_str() { + "low-latency" => { + Some(Arc::new(quinn::congestion::BbrConfig::default())) + } + "throughput" => { + Some(Arc::new(quinn::congestion::CubicConfig::default())) + } + _ => None, + }; + if let Some(controller) = controller { + cfg.congestion_controller_factory(controller); + } + } + + Ok(cfg) + } +} + +struct EndpointResource(quinn::Endpoint, Arc); + +impl GarbageCollected for EndpointResource {} + +#[op2(async)] +#[cppgc] +pub(crate) async fn op_quic_listen( + state: Rc>, + #[serde] addr: Addr, + #[serde] args: ListenArgs, + #[serde] transport_config: TransportConfig, + #[cppgc] keys: &TlsKeysHolder, +) -> Result +where + NP: NetPermissions + 'static, +{ + state + .borrow_mut() + .borrow_mut::() + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenQuic()")?; + + let addr = resolve_addr(&addr.hostname, addr.port) + .await? + .next() + .ok_or_else(|| generic_error("No resolved address found"))?; + + let TlsKeys::Static(deno_tls::TlsKey(cert, key)) = keys.take() else { + unreachable!() + }; + + let mut crypto = + quinn::rustls::ServerConfig::builder_with_protocol_versions(&[ + &quinn::rustls::version::TLS13, + ]) + .with_no_client_auth() + .with_single_cert(cert.clone(), key.clone_key())?; + + if let Some(alpn_protocols) = args.alpn_protocols { + crypto.alpn_protocols = alpn_protocols + .into_iter() + .map(|alpn| alpn.into_bytes()) + .collect(); + } + + let server_config = Arc::new(QuicServerConfig::try_from(crypto)?); + let mut config = quinn::ServerConfig::with_crypto(server_config.clone()); + config.preferred_address_v4(transport_config.preferred_address_v4); + config.preferred_address_v6(transport_config.preferred_address_v6); + config.transport_config(Arc::new(transport_config.try_into()?)); + let endpoint = quinn::Endpoint::server(config, addr)?; + + Ok(EndpointResource(endpoint, server_config)) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_endpoint_get_addr( + #[cppgc] endpoint: &EndpointResource, +) -> Result { + let addr = endpoint.0.local_addr()?; + let addr = Addr { + hostname: format!("{}", addr.ip()), + port: addr.port(), + }; + Ok(addr) +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct CloseInfo { + close_code: u64, + reason: String, +} + +#[op2(fast)] +pub(crate) fn op_quic_close_endpoint( + #[cppgc] endpoint: &EndpointResource, + #[bigint] close_code: u64, + #[string] reason: String, +) -> Result<(), AnyError> { + endpoint + .0 + .close(quinn::VarInt::from_u64(close_code)?, reason.as_bytes()); + Ok(()) +} + +struct ConnectionResource(quinn::Connection); + +impl GarbageCollected for ConnectionResource {} + +#[op2(async)] +#[cppgc] +pub(crate) async fn op_quic_accept( + #[cppgc] endpoint: &EndpointResource, +) -> Result { + match endpoint.0.accept().await { + Some(incoming) => { + let conn = incoming.accept()?.await?; + Ok(ConnectionResource(conn)) + } + None => Err(bad_resource("QuicListener is closed")), + } +} + +struct IncomingResource( + RefCell>, + Arc, +); + +impl GarbageCollected for IncomingResource {} + +#[op2(async)] +#[cppgc] +pub(crate) async fn op_quic_accept_incoming( + #[cppgc] endpoint: &EndpointResource, +) -> Result { + match endpoint.0.accept().await { + Some(incoming) => Ok(IncomingResource( + RefCell::new(Some(incoming)), + endpoint.1.clone(), + )), + None => Err(bad_resource("QuicListener is closed")), + } +} + +#[op2] +#[string] +pub(crate) fn op_quic_incoming_local_ip( + #[cppgc] incoming_resource: &IncomingResource, +) -> Result, AnyError> { + let Some(incoming) = incoming_resource.0.borrow_mut().take() else { + return Err(bad_resource("QuicIncoming already used")); + }; + Ok(incoming.local_ip().map(|ip| ip.to_string())) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_incoming_remote_addr( + #[cppgc] incoming_resource: &IncomingResource, +) -> Result { + let Some(incoming) = incoming_resource.0.borrow_mut().take() else { + return Err(bad_resource("QuicIncoming already used")); + }; + let addr = incoming.remote_address(); + Ok(Addr { + hostname: format!("{}", addr.ip()), + port: addr.port(), + }) +} + +#[op2(fast)] +pub(crate) fn op_quic_incoming_remote_addr_validated( + #[cppgc] incoming_resource: &IncomingResource, +) -> Result { + let Some(incoming) = incoming_resource.0.borrow_mut().take() else { + return Err(bad_resource("QuicIncoming already used")); + }; + Ok(incoming.remote_address_validated()) +} + +#[op2(async)] +#[cppgc] +pub(crate) async fn op_quic_incoming_accept( + #[cppgc] incoming_resource: &IncomingResource, + #[serde] transport_config: Option, +) -> Result { + let Some(incoming) = incoming_resource.0.borrow_mut().take() else { + return Err(bad_resource("QuicIncoming already used")); + }; + let conn = match transport_config { + Some(transport_config) => { + let mut config = + quinn::ServerConfig::with_crypto(incoming_resource.1.clone()); + config.preferred_address_v4(transport_config.preferred_address_v4); + config.preferred_address_v6(transport_config.preferred_address_v6); + config.transport_config(Arc::new(transport_config.try_into()?)); + incoming.accept_with(Arc::new(config))?.await? + } + None => incoming.accept()?.await?, + }; + Ok(ConnectionResource(conn)) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_incoming_refuse( + #[cppgc] incoming: &IncomingResource, +) -> Result<(), AnyError> { + let Some(incoming) = incoming.0.borrow_mut().take() else { + return Err(bad_resource("QuicIncoming already used")); + }; + incoming.refuse(); + Ok(()) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_incoming_ignore( + #[cppgc] incoming: &IncomingResource, +) -> Result<(), AnyError> { + let Some(incoming) = incoming.0.borrow_mut().take() else { + return Err(bad_resource("QuicIncoming already used")); + }; + incoming.ignore(); + Ok(()) +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct ConnectArgs { + ca_certs: Option>, + alpn_protocols: Option>, + server_name: Option, +} + +#[op2(async)] +#[cppgc] +pub(crate) async fn op_quic_connect( + state: Rc>, + #[serde] addr: Addr, + #[serde] args: ConnectArgs, + #[serde] transport_config: TransportConfig, + #[cppgc] key_pair: &TlsKeysHolder, +) -> Result +where + NP: NetPermissions + 'static, +{ + state + .borrow_mut() + .borrow_mut::() + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectQuic()")?; + + let sock_addr = resolve_addr(&addr.hostname, addr.port) + .await? + .next() + .ok_or_else(|| generic_error("No resolved address found"))?; + + let root_cert_store = state + .borrow() + .borrow::() + .root_cert_store()?; + + let unsafely_ignore_certificate_errors = state + .borrow() + .try_borrow::() + .and_then(|it| it.0.clone()); + + let ca_certs = args + .ca_certs + .unwrap_or_default() + .into_iter() + .map(|s| s.into_bytes()) + .collect::>(); + + let mut tls_config = create_client_config( + root_cert_store, + ca_certs, + unsafely_ignore_certificate_errors, + key_pair.take(), + SocketUse::GeneralSsl, + )?; + + if let Some(alpn_protocols) = args.alpn_protocols { + tls_config.alpn_protocols = + alpn_protocols.into_iter().map(|s| s.into_bytes()).collect(); + } + + let client_config = QuicClientConfig::try_from(tls_config)?; + let mut client_config = quinn::ClientConfig::new(Arc::new(client_config)); + client_config.transport_config(Arc::new(transport_config.try_into()?)); + + let local_addr = match sock_addr.ip() { + IpAddr::V4(_) => IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), + IpAddr::V6(_) => IpAddr::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), + }; + + let conn = quinn::Endpoint::client((local_addr, 0).into())? + .connect_with( + client_config, + sock_addr, + &args.server_name.unwrap_or(addr.hostname), + )? + .await?; + + Ok(ConnectionResource(conn)) +} + +#[op2] +#[string] +pub(crate) fn op_quic_connection_get_protocol( + #[cppgc] connection: &ConnectionResource, +) -> Option { + connection + .0 + .handshake_data() + .and_then(|h| h.downcast::().ok()) + .and_then(|h| h.protocol) + .map(|p| String::from_utf8_lossy(&p).into_owned()) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_connection_get_remote_addr( + #[cppgc] connection: &ConnectionResource, +) -> Result { + let addr = connection.0.remote_address(); + Ok(Addr { + hostname: format!("{}", addr.ip()), + port: addr.port(), + }) +} + +#[op2(fast)] +pub(crate) fn op_quic_close_connection( + #[cppgc] connection: &ConnectionResource, + #[bigint] close_code: u64, + #[string] reason: String, +) -> Result<(), AnyError> { + connection + .0 + .close(quinn::VarInt::from_u64(close_code)?, reason.as_bytes()); + Ok(()) +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_connection_closed( + #[cppgc] connection: &ConnectionResource, +) -> Result { + let e = connection.0.closed().await; + match e { + quinn::ConnectionError::LocallyClosed => Ok(CloseInfo { + close_code: 0, + reason: "".into(), + }), + quinn::ConnectionError::ApplicationClosed(i) => Ok(CloseInfo { + close_code: i.error_code.into(), + reason: String::from_utf8_lossy(&i.reason).into_owned(), + }), + e => Err(e.into()), + } +} + +struct SendStreamResource(AsyncRefCell); + +impl SendStreamResource { + fn new(stream: quinn::SendStream) -> Self { + Self(AsyncRefCell::new(stream)) + } +} + +impl Resource for SendStreamResource { + fn name(&self) -> Cow { + "quicSendStream".into() + } + + fn write(self: Rc, view: BufView) -> AsyncResult { + Box::pin(async move { + let mut r = RcRef::map(self, |r| &r.0).borrow_mut().await; + let nwritten = r.write(&view).await?; + Ok(WriteOutcome::Partial { nwritten, view }) + }) + } +} + +struct RecvStreamResource(AsyncRefCell); + +impl RecvStreamResource { + fn new(stream: quinn::RecvStream) -> Self { + Self(AsyncRefCell::new(stream)) + } +} + +impl Resource for RecvStreamResource { + fn name(&self) -> Cow { + "quicReceiveStream".into() + } + + fn read(self: Rc, limit: usize) -> AsyncResult { + Box::pin(async move { + let mut r = RcRef::map(self, |r| &r.0).borrow_mut().await; + let mut data = vec![0; limit]; + let nread = r.read(&mut data).await?.unwrap_or(0); + data.truncate(nread); + Ok(BufView::from(data)) + }) + } +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_accept_bi( + #[cppgc] connection: &ConnectionResource, + state: Rc>, +) -> Result<(ResourceId, ResourceId), AnyError> { + match connection.0.accept_bi().await { + Ok((tx, rx)) => { + let mut state = state.borrow_mut(); + let tx_rid = state.resource_table.add(SendStreamResource::new(tx)); + let rx_rid = state.resource_table.add(RecvStreamResource::new(rx)); + Ok((tx_rid, rx_rid)) + } + Err(e) => match e { + quinn::ConnectionError::LocallyClosed + | quinn::ConnectionError::ApplicationClosed(..) => { + Err(bad_resource("QuicConn is closed")) + } + _ => Err(e.into()), + }, + } +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_open_bi( + #[cppgc] connection: &ConnectionResource, + state: Rc>, + wait_for_available: bool, +) -> Result<(ResourceId, ResourceId), AnyError> { + let (tx, rx) = if wait_for_available { + connection.0.open_bi().await? + } else { + let waker = noop_waker_ref(); + let mut cx = Context::from_waker(waker); + match pin!(connection.0.open_bi()).poll(&mut cx) { + Poll::Ready(r) => r?, + Poll::Pending => { + return Err(generic_error("Connection has reached the maximum number of outgoing concurrent bidirectional streams")); + } + } + }; + let mut state = state.borrow_mut(); + let tx_rid = state.resource_table.add(SendStreamResource::new(tx)); + let rx_rid = state.resource_table.add(RecvStreamResource::new(rx)); + Ok((tx_rid, rx_rid)) +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_accept_uni( + #[cppgc] connection: &ConnectionResource, + state: Rc>, +) -> Result { + match connection.0.accept_uni().await { + Ok(rx) => { + let rid = state + .borrow_mut() + .resource_table + .add(RecvStreamResource::new(rx)); + Ok(rid) + } + Err(e) => match e { + quinn::ConnectionError::LocallyClosed + | quinn::ConnectionError::ApplicationClosed(..) => { + Err(bad_resource("QuicConn is closed")) + } + _ => Err(e.into()), + }, + } +} + +#[op2(async)] +#[serde] +pub(crate) async fn op_quic_open_uni( + #[cppgc] connection: &ConnectionResource, + state: Rc>, + wait_for_available: bool, +) -> Result { + let tx = if wait_for_available { + connection.0.open_uni().await? + } else { + let waker = noop_waker_ref(); + let mut cx = Context::from_waker(waker); + match pin!(connection.0.open_uni()).poll(&mut cx) { + Poll::Ready(r) => r?, + Poll::Pending => { + return Err(generic_error("Connection has reached the maximum number of outgoing concurrent unidirectional streams")); + } + } + }; + let rid = state + .borrow_mut() + .resource_table + .add(SendStreamResource::new(tx)); + Ok(rid) +} + +#[op2(async)] +pub(crate) async fn op_quic_send_datagram( + #[cppgc] connection: &ConnectionResource, + #[buffer] buf: JsBuffer, +) -> Result<(), AnyError> { + connection.0.send_datagram_wait(buf.to_vec().into()).await?; + Ok(()) +} + +#[op2(async)] +pub(crate) async fn op_quic_read_datagram( + #[cppgc] connection: &ConnectionResource, + #[buffer] mut buf: JsBuffer, +) -> Result { + let data = connection.0.read_datagram().await?; + buf[0..data.len()].copy_from_slice(&data); + Ok(data.len() as _) +} + +#[op2(fast)] +pub(crate) fn op_quic_max_datagram_size( + #[cppgc] connection: &ConnectionResource, +) -> Result { + Ok(connection.0.max_datagram_size().unwrap_or(0) as _) +} + +#[op2(fast)] +pub(crate) fn op_quic_get_send_stream_priority( + state: Rc>, + #[smi] rid: ResourceId, +) -> Result { + let resource = state + .borrow() + .resource_table + .get::(rid)?; + let r = RcRef::map(resource, |r| &r.0).try_borrow(); + match r { + Some(s) => Ok(s.priority()?), + None => Err(generic_error("Unable to get priority")), + } +} + +#[op2(fast)] +pub(crate) fn op_quic_set_send_stream_priority( + state: Rc>, + #[smi] rid: ResourceId, + priority: i32, +) -> Result<(), AnyError> { + let resource = state + .borrow() + .resource_table + .get::(rid)?; + let r = RcRef::map(resource, |r| &r.0).try_borrow(); + match r { + Some(s) => { + s.set_priority(priority)?; + Ok(()) + } + None => Err(generic_error("Unable to set priority")), + } +} diff --git a/ext/web/06_streams.js b/ext/web/06_streams.js index e673ee2bb4..f3ac711fc7 100644 --- a/ext/web/06_streams.js +++ b/ext/web/06_streams.js @@ -908,8 +908,8 @@ const _original = Symbol("[[original]]"); * @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true. * @returns {ReadableStream} */ -function readableStreamForRid(rid, autoClose = true) { - const stream = new ReadableStream(_brand); +function readableStreamForRid(rid, autoClose = true, Super) { + const stream = new (Super ?? ReadableStream)(_brand); stream[_resourceBacking] = { rid, autoClose }; const tryClose = () => { @@ -1130,8 +1130,8 @@ async function readableStreamCollectIntoUint8Array(stream) { * @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true. * @returns {ReadableStream} */ -function writableStreamForRid(rid, autoClose = true) { - const stream = new WritableStream(_brand); +function writableStreamForRid(rid, autoClose = true, Super) { + const stream = new (Super ?? WritableStream)(_brand); stream[_resourceBacking] = { rid, autoClose }; const tryClose = () => { diff --git a/runtime/fmt_errors.rs b/runtime/fmt_errors.rs index 6f120b5d46..3c60c3a3d7 100644 --- a/runtime/fmt_errors.rs +++ b/runtime/fmt_errors.rs @@ -422,6 +422,20 @@ fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec { "Run again with `--unstable-webgpu` flag to enable this API.", ), ]; + } else if msg.contains("listenQuic is not a function") { + return vec![ + FixSuggestion::info("listenQuic is an unstable API."), + FixSuggestion::hint( + "Run again with `--unstable-net` flag to enable this API.", + ), + ]; + } else if msg.contains("connectQuic is not a function") { + return vec![ + FixSuggestion::info("connectQuic is an unstable API."), + FixSuggestion::hint( + "Run again with `--unstable-net` flag to enable this API.", + ), + ]; // Try to capture errors like: // ``` // Uncaught Error: Cannot find module '../build/Release/canvas.node' diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index a510ee33c4..5511649279 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -13,6 +13,7 @@ import * as console from "ext:deno_console/01_console.js"; import * as ffi from "ext:deno_ffi/00_ffi.js"; import * as net from "ext:deno_net/01_net.js"; import * as tls from "ext:deno_net/02_tls.js"; +import * as quic from "ext:deno_net/03_quic.js"; import * as serve from "ext:deno_http/00_serve.ts"; import * as http from "ext:deno_http/01_http.js"; import * as websocket from "ext:deno_http/02_websocket.ts"; @@ -174,6 +175,15 @@ denoNsUnstableById[unstableIds.net] = { op_net_listen_udp, op_net_listen_unixpacket, ), + + connectQuic: quic.connectQuic, + listenQuic: quic.listenQuic, + QuicBidirectionalStream: quic.QuicBidirectionalStream, + QuicConn: quic.QuicConn, + QuicListener: quic.QuicListener, + QuicReceiveStream: quic.QuicReceiveStream, + QuicSendStream: quic.QuicSendStream, + QuicIncoming: quic.QuicIncoming, }; // denoNsUnstableById[unstableIds.unsafeProto] = { __proto__: null } diff --git a/tests/integration/js_unit_tests.rs b/tests/integration/js_unit_tests.rs index 577ca043ca..717a8d8e7c 100644 --- a/tests/integration/js_unit_tests.rs +++ b/tests/integration/js_unit_tests.rs @@ -66,6 +66,7 @@ util::unit_test_factory!( process_test, progressevent_test, promise_hooks_test, + quic_test, read_dir_test, read_file_test, read_link_test, diff --git a/tests/unit/quic_test.ts b/tests/unit/quic_test.ts new file mode 100644 index 0000000000..f5423327de --- /dev/null +++ b/tests/unit/quic_test.ts @@ -0,0 +1,172 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals } from "./test_util.ts"; + +const cert = Deno.readTextFileSync("tests/testdata/tls/localhost.crt"); +const key = Deno.readTextFileSync("tests/testdata/tls/localhost.key"); +const caCerts = [Deno.readTextFileSync("tests/testdata/tls/RootCA.pem")]; + +async function pair(opt?: Deno.QuicTransportOptions): Promise< + [Deno.QuicConn, Deno.QuicConn, Deno.QuicListener] +> { + const listener = await Deno.listenQuic({ + hostname: "localhost", + port: 0, + cert, + key, + alpnProtocols: ["deno-test"], + ...opt, + }); + + const [server, client] = await Promise.all([ + listener.accept(), + Deno.connectQuic({ + hostname: "localhost", + port: listener.addr.port, + caCerts, + alpnProtocols: ["deno-test"], + ...opt, + }), + ]); + + assertEquals(server.protocol, "deno-test"); + assertEquals(client.protocol, "deno-test"); + assertEquals(client.remoteAddr, listener.addr); + + return [server, client, listener]; +} + +Deno.test("bidirectional stream", async () => { + const [server, client, listener] = await pair(); + + const encoded = (new TextEncoder()).encode("hi!"); + + { + const bi = await server.createBidirectionalStream({ sendOrder: 42 }); + assertEquals(bi.writable.sendOrder, 42); + bi.writable.sendOrder = 0; + assertEquals(bi.writable.sendOrder, 0); + await bi.writable.getWriter().write(encoded); + } + + { + const { value: bi } = await client.incomingBidirectionalStreams + .getReader() + .read(); + const { value: data } = await bi!.readable.getReader().read(); + assertEquals(data, encoded); + } + + listener.close({ closeCode: 0, reason: "" }); + client.close({ closeCode: 0, reason: "" }); +}); + +Deno.test("unidirectional stream", async () => { + const [server, client, listener] = await pair(); + + const encoded = (new TextEncoder()).encode("hi!"); + + { + const uni = await server.createUnidirectionalStream({ sendOrder: 42 }); + assertEquals(uni.sendOrder, 42); + uni.sendOrder = 0; + assertEquals(uni.sendOrder, 0); + await uni.getWriter().write(encoded); + } + + { + const { value: uni } = await client.incomingUnidirectionalStreams + .getReader() + .read(); + const { value: data } = await uni!.getReader().read(); + assertEquals(data, encoded); + } + + listener.close({ closeCode: 0, reason: "" }); + client.close({ closeCode: 0, reason: "" }); +}); + +Deno.test("datagrams", async () => { + const [server, client, listener] = await pair(); + + const encoded = (new TextEncoder()).encode("hi!"); + + await server.sendDatagram(encoded); + + const data = await client.readDatagram(); + assertEquals(data, encoded); + + listener.close({ closeCode: 0, reason: "" }); + client.close({ closeCode: 0, reason: "" }); +}); + +Deno.test("closing", async () => { + const [server, client, listener] = await pair(); + + server.close({ closeCode: 42, reason: "hi!" }); + + assertEquals(await client.closed, { closeCode: 42, reason: "hi!" }); + + listener.close({ closeCode: 0, reason: "" }); +}); + +Deno.test("max concurrent streams", async () => { + const [server, client, listener] = await pair({ + maxConcurrentBidirectionalStreams: 1, + maxConcurrentUnidirectionalStreams: 1, + }); + + { + await server.createBidirectionalStream(); + await server.createBidirectionalStream() + .then(() => { + throw new Error("expected failure"); + }, () => { + // success! + }); + } + + { + await server.createUnidirectionalStream(); + await server.createUnidirectionalStream() + .then(() => { + throw new Error("expected failure"); + }, () => { + // success! + }); + } + + listener.close({ closeCode: 0, reason: "" }); + server.close({ closeCode: 0, reason: "" }); + client.close({ closeCode: 0, reason: "" }); +}); + +Deno.test("incoming", async () => { + const listener = await Deno.listenQuic({ + hostname: "localhost", + port: 0, + cert, + key, + alpnProtocols: ["deno-test"], + }); + + const connect = () => + Deno.connectQuic({ + hostname: "localhost", + port: listener.addr.port, + caCerts, + alpnProtocols: ["deno-test"], + }); + + const c1p = connect(); + const i1 = await listener.incoming(); + const server = await i1.accept(); + const client = await c1p; + + assertEquals(server.protocol, "deno-test"); + assertEquals(client.protocol, "deno-test"); + assertEquals(client.remoteAddr, listener.addr); + + listener.close({ closeCode: 0, reason: "" }); + client.close({ closeCode: 0, reason: "" }); +}); From c30f3450c64d6db07b2f8ec40a29f50835fa2108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 20 Dec 2024 17:43:03 +0000 Subject: [PATCH 028/107] perf: don't store duplicate info for ops in the snapshot (#27430) Mostly for changes from https://github.com/denoland/deno_core/pull/1010 --------- Co-authored-by: David Sherret --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- cli/module_loader.rs | 4 ++-- cli/standalone/binary.rs | 2 +- cli/standalone/mod.rs | 5 ++--- cli/standalone/serialization.rs | 19 +++++++++---------- cli/tools/coverage/mod.rs | 4 ++-- cli/util/text_encoding.rs | 12 ++++++------ 8 files changed, 29 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cd68e5ce1..90e857dc57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1500,9 +1500,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.326.0" +version = "0.327.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed157162dc5320a2b46ffeeaec24788339df0f2437cfaea78a8d82696715ad7f" +checksum = "eaf8dff204b9c2415deb47b9f30d4d38b0925d0d88f1f9074e8e76f59e6d7ded" dependencies = [ "anyhow", "az", @@ -2074,9 +2074,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.202.0" +version = "0.203.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd8ac1af251e292388e516dd339b9a3b982a6d1e7f8644c08e34671ca39003c" +checksum = "b146ca74cac431843486ade58e2accc16c11315fb2c6934590a52a73c56b7ec3" dependencies = [ "proc-macro-rules", "proc-macro2", @@ -6725,9 +6725,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.235.0" +version = "0.236.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07afd8b67b4a442ecc2823038473ac0e9e5682de93c213323b60661afdd7eb4" +checksum = "e23b3abce64010612f88f4ff689a959736f99eb3dc0dbf1c7903434b8bd8cda5" dependencies = [ "num-bigint", "serde", diff --git a/Cargo.toml b/Cargo.toml index ccdf64986e..f290d1480b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ repository = "https://github.com/denoland/deno" [workspace.dependencies] deno_ast = { version = "=0.44.0", features = ["transpiling"] } -deno_core = { version = "0.326.0" } +deno_core = { version = "0.327.0" } deno_bench_util = { version = "0.178.0", path = "./bench_util" } deno_config = { version = "=0.40.0", features = ["workspace", "sync"] } diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 5e4ff875dc..3ba8753335 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -996,7 +996,7 @@ impl ModuleLoader std::future::ready(()).boxed_local() } - fn get_source_map(&self, file_name: &str) -> Option> { + fn get_source_map(&self, file_name: &str) -> Option> { let specifier = resolve_url(file_name).ok()?; match specifier.scheme() { // we should only be looking for emits for schemes that denote external @@ -1008,7 +1008,7 @@ impl ModuleLoader .0 .load_prepared_module_for_source_map_sync(&specifier) .ok()??; - source_map_from_code(source.code.as_bytes()) + source_map_from_code(source.code.as_bytes()).map(Cow::Owned) } fn get_source_mapped_source_line( diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index f7ffa46e4f..3707543eb0 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -746,7 +746,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { for (specifier, source_map) in source_maps { source_map_store.add( Cow::Owned(root_dir_url.specifier_key(specifier).into_owned()), - Cow::Owned(source_map), + Cow::Owned(source_map.into_bytes()), ); } diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index d7227f2bd5..75247a98c0 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -502,7 +502,7 @@ impl ModuleLoader for EmbeddedModuleLoader { std::future::ready(()).boxed_local() } - fn get_source_map(&self, file_name: &str) -> Option> { + fn get_source_map(&self, file_name: &str) -> Option> { if file_name.starts_with("file:///") { let url = deno_path_util::url_from_directory_path(self.shared.vfs.root()).ok()?; @@ -512,8 +512,7 @@ impl ModuleLoader for EmbeddedModuleLoader { } else { self.shared.source_maps.get(file_name) } - // todo(https://github.com/denoland/deno_core/pull/1007): don't clone - .map(|s| s.as_bytes().to_vec()) + .map(Cow::Borrowed) } fn get_source_mapped_source_line( diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index 8fe593c005..4c18e0eb53 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -86,7 +86,7 @@ pub fn serialize_binary_data_section( builder.append_le(specifier.len() as u32); builder.append(specifier); builder.append_le(source_map.len() as u32); - builder.append(source_map); + builder.append(source_map.as_ref()); } } @@ -124,9 +124,9 @@ pub fn deserialize_binary_data_section( #[allow(clippy::type_complexity)] fn read_source_map_entry( input: &[u8], - ) -> Result<(&[u8], (Cow, Cow)), AnyError> { + ) -> Result<(&[u8], (Cow, &[u8])), AnyError> { let (input, specifier) = read_string_lossy(input)?; - let (input, source_map) = read_string_lossy(input)?; + let (input, source_map) = read_bytes_with_u32_len(input)?; Ok((input, (specifier, source_map))) } @@ -164,7 +164,7 @@ pub fn deserialize_binary_data_section( let (current_input, (specifier, source_map)) = read_source_map_entry(input)?; input = current_input; - source_maps.add(specifier, source_map); + source_maps.add(specifier, Cow::Borrowed(source_map)); } // finally ensure we read the magic bytes at the end @@ -293,7 +293,7 @@ impl DenoCompileModuleSource { } pub struct SourceMapStore { - data: IndexMap, Cow<'static, str>>, + data: IndexMap, Cow<'static, [u8]>>, } impl SourceMapStore { @@ -306,13 +306,13 @@ impl SourceMapStore { pub fn add( &mut self, specifier: Cow<'static, str>, - source_map: Cow<'static, str>, + source_map: Cow<'static, [u8]>, ) { self.data.insert(specifier, source_map); } - pub fn get(&self, specifier: &str) -> Option<&Cow<'static, str>> { - self.data.get(specifier) + pub fn get(&self, specifier: &str) -> Option<&[u8]> { + self.data.get(specifier).map(|v| v.as_ref()) } } @@ -763,8 +763,7 @@ fn check_has_len(input: &[u8], len: usize) -> Result<(), AnyError> { } fn read_string_lossy(input: &[u8]) -> Result<(&[u8], Cow), AnyError> { - let (input, str_len) = read_u32_as_usize(input)?; - let (input, data_bytes) = read_bytes(input, str_len)?; + let (input, data_bytes) = read_bytes_with_u32_len(input)?; Ok((input, String::from_utf8_lossy(data_bytes))) } diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 624fa76bf6..2b36a8ddd8 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -198,7 +198,7 @@ pub struct CoverageReport { fn generate_coverage_report( script_coverage: &cdp::ScriptCoverage, script_source: String, - maybe_source_map: &Option>, + maybe_source_map: Option<&[u8]>, output: &Option, ) -> CoverageReport { let maybe_source_map = maybe_source_map @@ -625,7 +625,7 @@ pub fn cover_files( let coverage_report = generate_coverage_report( &script_coverage, runtime_code.as_str().to_owned(), - &source_map, + source_map.as_deref(), &out_mode, ); diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs index 06b311e150..107b78a213 100644 --- a/cli/util/text_encoding.rs +++ b/cli/util/text_encoding.rs @@ -140,23 +140,23 @@ mod tests { #[test] fn test_source_map_from_code() { let to_string = - |bytes: Vec| -> String { String::from_utf8(bytes).unwrap() }; + |bytes: Vec| -> String { String::from_utf8(bytes.to_vec()).unwrap() }; assert_eq!( source_map_from_code( - b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=", + b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=" ).map(to_string), Some("testingtesting".to_string()) ); assert_eq!( source_map_from_code( - b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n \n", + b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n \n" ).map(to_string), Some("testingtesting".to_string()) ); assert_eq!( source_map_from_code( - b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n test\n", - ), + b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n test\n" + ).map(to_string), None ); assert_eq!( @@ -164,7 +164,7 @@ mod tests { b"\"use strict\"; throw new Error(\"Hello world!\"); -//# sourceMappingURL=data:application/json;base64,{", +//# sourceMappingURL=data:application/json;base64,{" ), None ); From ece718eb3e77917b67a63c56c63271a4a1fb9d8e Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 20 Dec 2024 16:14:37 -0500 Subject: [PATCH 029/107] perf: upgrade to deno_semver 0.7 (#27426) --- Cargo.lock | 88 +++++++++++++++---- Cargo.toml | 12 +-- cli/Cargo.toml | 2 +- cli/args/mod.rs | 31 ++++--- cli/args/package_json.rs | 31 ++++--- cli/graph_util.rs | 3 +- cli/lsp/analysis.rs | 17 +++- cli/lsp/jsr.rs | 23 +++-- cli/lsp/search.rs | 4 +- cli/npm/managed/mod.rs | 6 +- cli/npm/managed/resolution.rs | 10 ++- .../managed/resolvers/common/bin_entries.rs | 4 +- cli/npm/managed/resolvers/local.rs | 15 ++-- cli/standalone/serialization.rs | 12 ++- cli/tools/doc.rs | 4 +- cli/tools/info.rs | 19 ++-- cli/tools/installer.rs | 4 +- cli/tools/registry/pm.rs | 33 ++++--- cli/tools/registry/pm/deps.rs | 30 +++---- cli/tools/registry/pm/outdated.rs | 3 +- cli/tools/upgrade.rs | 5 +- resolvers/deno/npm/byonm.rs | 23 ++--- resolvers/node/resolution.rs | 2 + resolvers/npm_cache/Cargo.toml | 1 + resolvers/npm_cache/lib.rs | 3 +- resolvers/npm_cache/registry_info.rs | 32 ++++++- resolvers/npm_cache/tarball_extract.rs | 2 +- runtime/permissions/lib.rs | 4 +- tests/integration/check_tests.rs | 4 +- tests/integration/jsr_tests.rs | 2 +- 30 files changed, 282 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90e857dc57..4bb0cfb18e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -677,6 +677,28 @@ dependencies = [ "itoa", ] +[[package]] +name = "capacity_builder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6" +dependencies = [ + "capacity_builder_macros", + "ecow", + "hipstr", + "itoa", +] + +[[package]] +name = "capacity_builder_macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" +dependencies = [ + "quote", + "syn 2.0.87", +] + [[package]] name = "caseless" version = "0.2.1" @@ -1230,7 +1252,7 @@ dependencies = [ "boxed_error", "bytes", "cache_control", - "capacity_builder", + "capacity_builder 0.5.0", "chrono", "clap", "clap_complete", @@ -1469,9 +1491,9 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.40.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459b0bf193d2f9a177d18064a4888062ba0716312f56dbda8f1319444a8b2544" +checksum = "8afa3beb6b9e0604cfe0380d30f88c5b758d44e228d5a5fc42ae637ccfb7d089" dependencies = [ "anyhow", "deno_package_json", @@ -1510,7 +1532,7 @@ dependencies = [ "bit-set", "bit-vec", "bytes", - "capacity_builder", + "capacity_builder 0.1.3", "cooked-waker", "deno_core_icudata", "deno_ops", @@ -1724,13 +1746,13 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.86.4" +version = "0.86.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8502f5dd37f522c76e92961ec6e855f40bc351d50d62bc0752cc19517eed8ec" +checksum = "f669d96d63841d9ba10f86b161d898678ce05bc1e3c9ee1c1f7449a68eed2b64" dependencies = [ "anyhow", "async-trait", - "capacity_builder", + "capacity_builder 0.5.0", "data-url", "deno_ast", "deno_semver", @@ -1865,9 +1887,9 @@ dependencies = [ [[package]] name = "deno_lockfile" -version = "0.23.2" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c19feb00af0c34f0bd4a20e56e12463fafd5c5069d6005f3ce33008027eea" +checksum = "632e835a53ed667d62fdd766c5780fe8361c831d3e3fbf1a760a0b7896657587" dependencies = [ "deno_semver", "serde", @@ -2026,12 +2048,13 @@ dependencies = [ [[package]] name = "deno_npm" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f125a5dba7839c46394a0a9c835da9fe60f5f412587ab4956a76492a1cc6a8" +checksum = "5f818ad5dc4c206b50b5cfa6f10b4b94b127e15c8342c152768eba40c225ca23" dependencies = [ - "anyhow", "async-trait", + "capacity_builder 0.5.0", + "deno_error", "deno_lockfile", "deno_semver", "futures", @@ -2053,6 +2076,7 @@ dependencies = [ "boxed_error", "deno_cache_dir", "deno_core", + "deno_error", "deno_npm", "deno_semver", "deno_unsync", @@ -2090,10 +2114,11 @@ dependencies = [ [[package]] name = "deno_package_json" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b0a3d81c592624a1ae15332a04b4dc2b7c163ef1dfc7c60171f736d1babdf5" +checksum = "81d72db99fdebfc371d7be16972c18a47daa7a29cb5fbb3900ab2114b1f42d96" dependencies = [ + "boxed_error", "deno_error", "deno_path_util", "deno_semver", @@ -2120,7 +2145,7 @@ dependencies = [ name = "deno_permissions" version = "0.43.0" dependencies = [ - "capacity_builder", + "capacity_builder 0.5.0", "deno_core", "deno_path_util", "deno_terminal 0.2.0", @@ -2225,11 +2250,14 @@ dependencies = [ [[package]] name = "deno_semver" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d1259270d66a5e6d29bb75c9289656541874f79ae9ff6c9f1c790846d5c07ba" +checksum = "4775271f9b5602482698f76d24ea9ed8ba27af7f587a7e9a876916300c542435" dependencies = [ + "capacity_builder 0.5.0", "deno_error", + "ecow", + "hipstr", "monch", "once_cell", "serde", @@ -2896,6 +2924,15 @@ dependencies = [ "spki", ] +[[package]] +name = "ecow" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42fc0a93992b20c58b99e59d61eaf1635a25bfbe49e4275c34ba0aee98119ba" +dependencies = [ + "serde", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -3832,6 +3869,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "hipstr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97971ffc85d4c98de12e2608e992a43f5294ebb625fdb045b27c731b64c4c6d6" +dependencies = [ + "serde", + "serde_bytes", + "sptr", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -7003,6 +7051,12 @@ dependencies = [ "der", ] +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "sqlformat" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index f290d1480b..81c750f0af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,14 +51,14 @@ deno_ast = { version = "=0.44.0", features = ["transpiling"] } deno_core = { version = "0.327.0" } deno_bench_util = { version = "0.178.0", path = "./bench_util" } -deno_config = { version = "=0.40.0", features = ["workspace", "sync"] } -deno_lockfile = "=0.23.2" +deno_config = { version = "=0.41.0", features = ["workspace", "sync"] } +deno_lockfile = "=0.24.0" deno_media_type = { version = "0.2.0", features = ["module_specifier"] } -deno_npm = "=0.26.0" +deno_npm = "=0.27.0" deno_path_util = "=0.2.2" deno_permissions = { version = "0.43.0", path = "./runtime/permissions" } deno_runtime = { version = "0.192.0", path = "./runtime" } -deno_semver = "=0.6.1" +deno_semver = "=0.7.1" deno_terminal = "0.2.0" napi_sym = { version = "0.114.0", path = "./ext/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } @@ -108,7 +108,7 @@ boxed_error = "0.2.3" brotli = "6.0.0" bytes = "1.4.0" cache_control = "=0.2.0" -capacity_builder = "0.1.3" +capacity_builder = "0.5.0" cbc = { version = "=0.1.2", features = ["alloc"] } # Note: Do not use the "clock" feature of chrono, as it links us to CoreFoundation on macOS. # Instead use util::time::utc_now() @@ -120,7 +120,7 @@ data-encoding = "2.3.3" data-url = "=0.3.1" deno_cache_dir = "=0.15.0" deno_error = "=0.5.2" -deno_package_json = { version = "0.2.1", default-features = false } +deno_package_json = { version = "0.3.0", default-features = false } deno_unsync = "0.4.2" dlopen2 = "0.6.1" ecb = "=0.1.2" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 4b0380717b..ad0e4fa95d 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -74,7 +74,7 @@ deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] } deno_error.workspace = true -deno_graph = { version = "=0.86.4" } +deno_graph = { version = "=0.86.5" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true deno_npm.workspace = true diff --git a/cli/args/mod.rs b/cli/args/mod.rs index fd34b53c8c..3f42a6b9d4 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -31,6 +31,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmSystemInfo; use deno_path_util::normalize_path; use deno_semver::npm::NpmPackageReqReference; +use deno_semver::StackString; use deno_telemetry::OtelConfig; use deno_telemetry::OtelRuntimeConfig; use import_map::resolve_import_map_value_from_specifier; @@ -992,24 +993,24 @@ impl CliOptions { // https://nodejs.org/api/process.html match target.as_str() { "aarch64-apple-darwin" => NpmSystemInfo { - os: "darwin".to_string(), - cpu: "arm64".to_string(), + os: "darwin".into(), + cpu: "arm64".into(), }, "aarch64-unknown-linux-gnu" => NpmSystemInfo { - os: "linux".to_string(), - cpu: "arm64".to_string(), + os: "linux".into(), + cpu: "arm64".into(), }, "x86_64-apple-darwin" => NpmSystemInfo { - os: "darwin".to_string(), - cpu: "x64".to_string(), + os: "darwin".into(), + cpu: "x64".into(), }, "x86_64-unknown-linux-gnu" => NpmSystemInfo { - os: "linux".to_string(), - cpu: "x64".to_string(), + os: "linux".into(), + cpu: "x64".into(), }, "x86_64-pc-windows-msvc" => NpmSystemInfo { - os: "win32".to_string(), - cpu: "x64".to_string(), + os: "win32".into(), + cpu: "x64".into(), }, value => { log::warn!( @@ -1946,15 +1947,17 @@ pub fn has_flag_env_var(name: &str) -> bool { pub fn npm_pkg_req_ref_to_binary_command( req_ref: &NpmPackageReqReference, ) -> String { - let binary_name = req_ref.sub_path().unwrap_or(req_ref.req().name.as_str()); - binary_name.to_string() + req_ref + .sub_path() + .map(|s| s.to_string()) + .unwrap_or_else(|| req_ref.req().name.to_string()) } pub fn config_to_deno_graph_workspace_member( config: &ConfigFile, ) -> Result { - let name = match &config.json.name { - Some(name) => name.clone(), + let name: StackString = match &config.json.name { + Some(name) => name.as_str().into(), None => bail!("Missing 'name' field in config file."), }; let version = match &config.json.version { diff --git a/cli/args/package_json.rs b/cli/args/package_json.rs index b0f0a2f9ba..50d1c04799 100644 --- a/cli/args/package_json.rs +++ b/cli/args/package_json.rs @@ -11,19 +11,20 @@ use deno_package_json::PackageJsonDepValueParseError; use deno_package_json::PackageJsonDepWorkspaceReq; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::VersionReq; use thiserror::Error; #[derive(Debug)] pub struct InstallNpmRemotePkg { - pub alias: Option, + pub alias: Option, pub base_dir: PathBuf, pub req: PackageReq, } #[derive(Debug)] pub struct InstallNpmWorkspacePkg { - pub alias: Option, + pub alias: Option, pub target_dir: PathBuf, } @@ -31,7 +32,7 @@ pub struct InstallNpmWorkspacePkg { #[error("Failed to install '{}'\n at {}", alias, location)] pub struct PackageJsonDepValueParseWithLocationError { pub location: Url, - pub alias: String, + pub alias: StackString, #[source] pub source: PackageJsonDepValueParseError, } @@ -100,10 +101,8 @@ impl NpmInstallDepsProvider { let mut pkg_pkgs = Vec::with_capacity( deps.dependencies.len() + deps.dev_dependencies.len(), ); - for (alias, dep) in deps - .dependencies - .into_iter() - .chain(deps.dev_dependencies.into_iter()) + for (alias, dep) in + deps.dependencies.iter().chain(deps.dev_dependencies.iter()) { let dep = match dep { Ok(dep) => dep, @@ -111,8 +110,8 @@ impl NpmInstallDepsProvider { pkg_json_dep_errors.push( PackageJsonDepValueParseWithLocationError { location: pkg_json.specifier(), - alias, - source: err, + alias: alias.clone(), + source: err.clone(), }, ); continue; @@ -121,28 +120,28 @@ impl NpmInstallDepsProvider { match dep { PackageJsonDepValue::Req(pkg_req) => { let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| { - pkg.matches_req(&pkg_req) + pkg.matches_req(pkg_req) // do not resolve to the current package && pkg.pkg_json.path != pkg_json.path }); if let Some(pkg) = workspace_pkg { workspace_pkgs.push(InstallNpmWorkspacePkg { - alias: Some(alias), + alias: Some(alias.clone()), target_dir: pkg.pkg_json.dir_path().to_path_buf(), }); } else { pkg_pkgs.push(InstallNpmRemotePkg { - alias: Some(alias), + alias: Some(alias.clone()), base_dir: pkg_json.dir_path().to_path_buf(), - req: pkg_req, + req: pkg_req.clone(), }); } } PackageJsonDepValue::Workspace(workspace_version_req) => { let version_req = match workspace_version_req { PackageJsonDepWorkspaceReq::VersionReq(version_req) => { - version_req + version_req.clone() } PackageJsonDepWorkspaceReq::Tilde | PackageJsonDepWorkspaceReq::Caret => { @@ -150,10 +149,10 @@ impl NpmInstallDepsProvider { } }; if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| { - pkg.matches_name_and_version_req(&alias, &version_req) + pkg.matches_name_and_version_req(alias, &version_req) }) { workspace_pkgs.push(InstallNpmWorkspacePkg { - alias: Some(alias), + alias: Some(alias.clone()), target_dir: pkg.pkg_json.dir_path().to_path_buf(), }); } diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 6abdbe247a..88c412b65a 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -52,6 +52,7 @@ use deno_runtime::deno_node; use deno_runtime::deno_permissions::PermissionsContainer; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; +use deno_semver::SmallStackString; use import_map::ImportMapError; use node_resolver::InNpmPackageChecker; use std::collections::HashSet; @@ -680,7 +681,7 @@ impl ModuleGraphBuilder { for (from, to) in graph.packages.mappings() { lockfile.insert_package_specifier( JsrDepPackageReq::jsr(from.clone()), - to.version.to_string(), + to.version.to_custom_string::(), ); } } diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 4c6a20927b..8fb3454bc8 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -36,6 +36,8 @@ use deno_semver::package::PackageNv; use deno_semver::package::PackageNvReference; use deno_semver::package::PackageReq; use deno_semver::package::PackageReqReference; +use deno_semver::SmallStackString; +use deno_semver::StackString; use deno_semver::Version; use import_map::ImportMap; use node_resolver::NodeResolutionKind; @@ -278,9 +280,16 @@ impl<'a> TsResponseImportMapper<'a> { { let mut segments = jsr_path.split('/'); let name = if jsr_path.starts_with('@') { - format!("{}/{}", segments.next()?, segments.next()?) + let scope = segments.next()?; + let name = segments.next()?; + capacity_builder::StringBuilder::::build(|builder| { + builder.append(scope); + builder.append("/"); + builder.append(name); + }) + .unwrap() } else { - segments.next()?.to_string() + StackString::from(segments.next()?) }; let version = Version::parse_standard(segments.next()?).ok()?; let nv = PackageNv { name, version }; @@ -290,7 +299,9 @@ impl<'a> TsResponseImportMapper<'a> { &path, Some(&self.file_referrer), )?; - let sub_path = (export != ".").then_some(export); + let sub_path = (export != ".") + .then_some(export) + .map(SmallStackString::from_string); let mut req = None; req = req.or_else(|| { let import_map = self.maybe_import_map?; diff --git a/cli/lsp/jsr.rs b/cli/lsp/jsr.rs index 1d012b42f0..fc30de2ae0 100644 --- a/cli/lsp/jsr.rs +++ b/cli/lsp/jsr.rs @@ -18,6 +18,7 @@ use deno_graph::ModuleSpecifier; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::Version; use serde::Deserialize; use std::collections::HashMap; @@ -33,8 +34,8 @@ pub struct JsrCacheResolver { /// The `module_graph` fields of the version infos should be forcibly absent. /// It can be large and we don't want to store it. info_by_nv: DashMap>>, - info_by_name: DashMap>>, - workspace_scope_by_name: HashMap, + info_by_name: DashMap>>, + workspace_scope_by_name: HashMap, cache: Arc, } @@ -59,7 +60,7 @@ impl JsrCacheResolver { continue; }; let nv = PackageNv { - name: jsr_pkg_config.name.clone(), + name: jsr_pkg_config.name.as_str().into(), version: version.clone(), }; info_by_name.insert( @@ -125,8 +126,8 @@ impl JsrCacheResolver { return nv.value().clone(); } let maybe_get_nv = || { - let name = req.name.clone(); - let package_info = self.package_info(&name)?; + let name = &req.name; + let package_info = self.package_info(name)?; // Find the first matching version of the package which is cached. let mut versions = package_info.versions.keys().collect::>(); versions.sort(); @@ -144,7 +145,10 @@ impl JsrCacheResolver { self.package_version_info(&nv).is_some() }) .cloned()?; - Some(PackageNv { name, version }) + Some(PackageNv { + name: name.clone(), + version, + }) }; let nv = maybe_get_nv(); self.nv_by_req.insert(req.clone(), nv.clone()); @@ -216,7 +220,10 @@ impl JsrCacheResolver { None } - pub fn package_info(&self, name: &str) -> Option> { + pub fn package_info( + &self, + name: &StackString, + ) -> Option> { if let Some(info) = self.info_by_name.get(name) { return info.value().clone(); } @@ -226,7 +233,7 @@ impl JsrCacheResolver { serde_json::from_slice::(&meta_bytes).ok() }; let info = read_cached_package_info().map(Arc::new); - self.info_by_name.insert(name.to_string(), info.clone()); + self.info_by_name.insert(name.clone(), info.clone()); info } diff --git a/cli/lsp/search.rs b/cli/lsp/search.rs index 8933eeb186..c98acde6f1 100644 --- a/cli/lsp/search.rs +++ b/cli/lsp/search.rs @@ -67,7 +67,9 @@ pub mod tests { &self, nv: &PackageNv, ) -> Result>, AnyError> { - let Some(exports_by_version) = self.package_versions.get(&nv.name) else { + let Some(exports_by_version) = + self.package_versions.get(nv.name.as_str()) + else { return Err(anyhow!("Package not found.")); }; let Some(exports) = exports_by_version.get(&nv.version) else { diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 4545800e99..5006902aa1 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -560,11 +560,11 @@ impl ManagedCliNpmResolver { &self, ) -> Result<(), Box> { for err in self.npm_install_deps_provider.pkg_json_dep_errors() { - match &err.source { - deno_package_json::PackageJsonDepValueParseError::VersionReq(_) => { + match err.source.as_kind() { + deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => { return Err(Box::new(err.clone())); } - deno_package_json::PackageJsonDepValueParseError::Unsupported { + deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported { .. } => { // only warn for this one diff --git a/cli/npm/managed/resolution.rs b/cli/npm/managed/resolution.rs index 73c5c31caf..5d9fcf4646 100644 --- a/cli/npm/managed/resolution.rs +++ b/cli/npm/managed/resolution.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::collections::HashSet; use std::sync::Arc; +use capacity_builder::StringBuilder; use deno_core::error::AnyError; use deno_lockfile::NpmPackageDependencyLockfileInfo; use deno_lockfile::NpmPackageLockfileInfo; @@ -24,6 +25,7 @@ use deno_npm::NpmSystemInfo; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use deno_semver::SmallStackString; use deno_semver::VersionReq; use crate::args::CliLockfile; @@ -336,7 +338,13 @@ fn populate_lockfile_from_snapshot( let id = &snapshot.resolve_package_from_deno_module(nv).unwrap().id; lockfile.insert_package_specifier( JsrDepPackageReq::npm(package_req.clone()), - format!("{}{}", id.nv.version, id.peer_deps_serialized()), + { + StringBuilder::::build(|builder| { + builder.append(&id.nv.version); + builder.append(&id.peer_dependencies); + }) + .unwrap() + }, ); } for package in snapshot.all_packages_for_every_system() { diff --git a/cli/npm/managed/resolvers/common/bin_entries.rs b/cli/npm/managed/resolvers/common/bin_entries.rs index e4a1845689..ca47b9a086 100644 --- a/cli/npm/managed/resolvers/common/bin_entries.rs +++ b/cli/npm/managed/resolvers/common/bin_entries.rs @@ -28,8 +28,10 @@ fn default_bin_name(package: &NpmResolutionPackage) -> &str { .id .nv .name + .as_str() .rsplit_once('/') - .map_or(package.id.nv.name.as_str(), |(_, name)| name) + .map(|(_, name)| name) + .unwrap_or(package.id.nv.name.as_str()) } pub fn warn_missing_entrypoint( diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 1e83717f15..788d6569ae 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -38,6 +38,7 @@ use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder; use deno_runtime::deno_fs; use deno_runtime::deno_node::NodePermissions; use deno_semver::package::PackageNv; +use deno_semver::StackString; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageNotFoundError; @@ -355,8 +356,10 @@ async fn sync_resolution_with_fs( let package_partitions = snapshot.all_system_packages_partitioned(system_info); let mut cache_futures = FuturesUnordered::new(); - let mut newest_packages_by_name: HashMap<&String, &NpmResolutionPackage> = - HashMap::with_capacity(package_partitions.packages.len()); + let mut newest_packages_by_name: HashMap< + &StackString, + &NpmResolutionPackage, + > = HashMap::with_capacity(package_partitions.packages.len()); let bin_entries = Rc::new(RefCell::new(bin_entries::BinEntries::new())); let mut lifecycle_scripts = super::common::lifecycle_scripts::LifecycleScripts::new( @@ -536,7 +539,7 @@ async fn sync_resolution_with_fs( } } - let mut found_names: HashMap<&String, &PackageNv> = HashMap::new(); + let mut found_names: HashMap<&StackString, &PackageNv> = HashMap::new(); // set of node_modules in workspace packages that we've already ensured exist let mut existing_child_node_modules_dirs: HashSet = HashSet::new(); @@ -1012,10 +1015,10 @@ fn get_package_folder_id_from_folder_name( ) -> Option { let folder_name = folder_name.replace('+', "/"); let (name, ending) = folder_name.rsplit_once('@')?; - let name = if let Some(encoded_name) = name.strip_prefix('_') { - mixed_case_package_name_decode(encoded_name)? + let name: StackString = if let Some(encoded_name) = name.strip_prefix('_') { + StackString::from_string(mixed_case_package_name_decode(encoded_name)?) } else { - name.to_string() + name.into() }; let (raw_version, copy_index) = match ending.split_once('_') { Some((raw_version, copy_index)) => { diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index 4c18e0eb53..30802aa081 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -6,6 +6,7 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::io::Write; +use capacity_builder::BytesAppendable; use deno_ast::swc::common::source_map; use deno_ast::MediaType; use deno_core::anyhow::bail; @@ -21,6 +22,7 @@ use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmPackageId; use deno_semver::package::PackageReq; +use deno_semver::StackString; use indexmap::IndexMap; use crate::standalone::virtual_fs::VirtualDirectory; @@ -224,7 +226,10 @@ impl RemoteModulesStoreBuilder { } } - fn write<'a>(&'a self, builder: &mut capacity_builder::BytesBuilder<'a>) { + fn write<'a, TBytes: capacity_builder::BytesType>( + &'a self, + builder: &mut capacity_builder::BytesBuilder<'a, TBytes>, + ) { builder.append_le(self.specifiers.len() as u32); builder.append_le(self.redirects.len() as u32); for (specifier, offset) in &self.specifiers { @@ -581,12 +586,13 @@ fn deserialize_npm_snapshot( #[allow(clippy::needless_lifetimes)] // clippy bug fn parse_package_dep<'a>( id_to_npm_id: &'a impl Fn(usize) -> Result, - ) -> impl Fn(&[u8]) -> Result<(&[u8], (String, NpmPackageId)), AnyError> + 'a + ) -> impl Fn(&[u8]) -> Result<(&[u8], (StackString, NpmPackageId)), AnyError> + 'a { |input| { let (input, req) = read_string_lossy(input)?; let (input, id) = read_u32_as_usize(input)?; - Ok((input, (req.into_owned(), id_to_npm_id(id)?))) + let req = StackString::from_cow(req); + Ok((input, (req, id_to_npm_id(id)?))) } } diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 647a36dc48..90d54f6cd9 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -343,14 +343,14 @@ impl deno_doc::html::HrefResolver for DocResolver { let name = &res.req().name; Some(( format!("https://www.npmjs.com/package/{name}"), - name.to_owned(), + name.to_string(), )) } "jsr" => { let res = deno_semver::jsr::JsrPackageReqReference::from_str(module).ok()?; let name = &res.req().name; - Some((format!("https://jsr.io/{name}"), name.to_owned())) + Some((format!("https://jsr.io/{name}"), name.to_string())) } _ => None, } diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 7a35f597c3..39a7a912bf 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -278,8 +278,10 @@ fn add_npm_packages_to_json( }); if let Some(pkg) = maybe_package { if let Some(module) = module.as_object_mut() { - module - .insert("npmPackage".to_string(), pkg.id.as_serialized().into()); + module.insert( + "npmPackage".to_string(), + pkg.id.as_serialized().into_string().into(), + ); } } } @@ -296,7 +298,7 @@ fn add_npm_packages_to_json( { dep.insert( "npmPackage".to_string(), - pkg.id.as_serialized().into(), + pkg.id.as_serialized().into_string().into(), ); } } @@ -324,19 +326,19 @@ 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.clone().into()); + kv.insert("name".to_string(), pkg.id.nv.name.to_string().into()); kv.insert("version".to_string(), pkg.id.nv.version.to_string().into()); let mut deps = pkg.dependencies.values().collect::>(); deps.sort(); let deps = deps .into_iter() - .map(|id| serde_json::Value::String(id.as_serialized())) + .map(|id| serde_json::Value::String(id.as_serialized().into_string())) .collect::>(); kv.insert("dependencies".to_string(), deps.into()); let registry_url = npmrc.get_registry_url(&pkg.id.nv.name); kv.insert("registryUrl".to_string(), registry_url.to_string().into()); - json_packages.insert(pkg.id.as_serialized(), kv.into()); + json_packages.insert(pkg.id.as_serialized().into_string(), kv.into()); } json.insert("npmPackages".to_string(), json_packages.into()); @@ -549,7 +551,7 @@ impl<'a> GraphDisplayContext<'a> { None => Specifier(module.specifier().clone()), }; let was_seen = !self.seen.insert(match &package_or_specifier { - Package(package) => package.id.as_serialized(), + Package(package) => package.id.as_serialized().into_string(), Specifier(specifier) => specifier.to_string(), }); let header_text = if was_seen { @@ -631,7 +633,8 @@ impl<'a> GraphDisplayContext<'a> { )); if let Some(package) = self.npm_info.packages.get(dep_id) { if !package.dependencies.is_empty() { - let was_seen = !self.seen.insert(package.id.as_serialized()); + let was_seen = + !self.seen.insert(package.id.as_serialized().into_string()); if was_seen { child.text = format!("{} {}", child.text, colors::gray("*")); } else { diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index dac7340d40..ec538ecb0a 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -161,11 +161,11 @@ pub async fn infer_name_from_url( let npm_ref = npm_ref.into_inner(); if let Some(sub_path) = npm_ref.sub_path { if !sub_path.contains('/') { - return Some(sub_path); + return Some(sub_path.to_string()); } } if !npm_ref.req.name.contains('/') { - return Some(npm_ref.req.name); + return Some(npm_ref.req.name.into_string()); } return None; } diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 791e54c67c..afa9b0222c 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -15,6 +15,7 @@ use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::Version; use deno_semver::VersionReq; use deps::KeyPath; @@ -283,7 +284,7 @@ fn package_json_dependency_entry( (npm_package.into(), selected.version_req) } else { ( - selected.import_name, + selected.import_name.into_string(), format!("npm:{}@{}", npm_package, selected.version_req), ) } @@ -292,7 +293,7 @@ fn package_json_dependency_entry( let scope_replaced = jsr_package.replace('/', "__"); let version_req = format!("npm:@jsr/{scope_replaced}@{}", selected.version_req); - (selected.import_name, version_req) + (selected.import_name.into_string(), version_req) } else { (selected.package_name, selected.version_req) } @@ -549,10 +550,10 @@ pub async fn add( } struct SelectedPackage { - import_name: String, + import_name: StackString, package_name: String, version_req: String, - selected_version: String, + selected_version: StackString, } enum NotFoundHelp { @@ -683,7 +684,7 @@ async fn find_package_and_select_version_for_req( import_name: add_package_req.alias, package_name: prefixed_name, version_req: format!("{}{}", range_symbol, &nv.version), - selected_version: nv.version.to_string(), + selected_version: nv.version.to_custom_string::(), })) } @@ -705,7 +706,7 @@ enum AddRmPackageReqValue { #[derive(Debug, PartialEq, Eq)] pub struct AddRmPackageReq { - alias: String, + alias: StackString, value: AddRmPackageReqValue, } @@ -753,7 +754,11 @@ impl AddRmPackageReq { return Ok(Err(PackageReq::from_str(entry_text)?)); } - (maybe_prefix.unwrap(), Some(alias.to_string()), entry_text) + ( + maybe_prefix.unwrap(), + Some(StackString::from(alias)), + entry_text, + ) } None => return Ok(Err(PackageReq::from_str(entry_text)?)), }, @@ -765,7 +770,7 @@ impl AddRmPackageReq { JsrPackageReqReference::from_str(&format!("jsr:{}", entry_text))?; let package_req = req_ref.into_inner().req; Ok(Ok(AddRmPackageReq { - alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()), + alias: maybe_alias.unwrap_or_else(|| package_req.name.clone()), value: AddRmPackageReqValue::Jsr(package_req), })) } @@ -785,7 +790,7 @@ impl AddRmPackageReq { ); } Ok(Ok(AddRmPackageReq { - alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()), + alias: maybe_alias.unwrap_or_else(|| package_req.name.clone()), value: AddRmPackageReqValue::Npm(package_req), })) } @@ -878,14 +883,14 @@ mod test { assert_eq!( AddRmPackageReq::parse("jsr:foo").unwrap().unwrap(), AddRmPackageReq { - alias: "foo".to_string(), + alias: "foo".into(), value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap()) } ); assert_eq!( AddRmPackageReq::parse("alias@jsr:foo").unwrap().unwrap(), AddRmPackageReq { - alias: "alias".to_string(), + alias: "alias".into(), value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap()) } ); @@ -894,7 +899,7 @@ mod test { .unwrap() .unwrap(), AddRmPackageReq { - alias: "@alias/pkg".to_string(), + alias: "@alias/pkg".into(), value: AddRmPackageReqValue::Npm( PackageReq::from_str("foo@latest").unwrap() ) @@ -905,7 +910,7 @@ mod test { .unwrap() .unwrap(), AddRmPackageReq { - alias: "@alias/pkg".to_string(), + alias: "@alias/pkg".into(), value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap()) } ); @@ -914,7 +919,7 @@ mod test { .unwrap() .unwrap(), AddRmPackageReq { - alias: "alias".to_string(), + alias: "alias".into(), value: AddRmPackageReqValue::Jsr( PackageReq::from_str("foo@^1.5.0").unwrap() ) diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index bb03e97f2d..ffa53417e9 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -27,6 +27,7 @@ use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use deno_semver::package::PackageReqReference; +use deno_semver::StackString; use deno_semver::Version; use deno_semver::VersionReq; use import_map::ImportMap; @@ -139,13 +140,7 @@ pub enum KeyPart { Scopes, Dependencies, DevDependencies, - String(String), -} - -impl From for KeyPart { - fn from(value: String) -> Self { - KeyPart::String(value) - } + String(StackString), } impl From for KeyPart { @@ -164,7 +159,7 @@ impl KeyPart { KeyPart::Scopes => "scopes", KeyPart::Dependencies => "dependencies", KeyPart::DevDependencies => "devDependencies", - KeyPart::String(s) => s, + KeyPart::String(s) => s.as_str(), } } } @@ -217,12 +212,12 @@ fn import_map_entries( .chain(import_map.scopes().flat_map(|scope| { let path = KeyPath::from_parts([ KeyPart::Scopes, - scope.raw_key.to_string().into(), + KeyPart::String(scope.raw_key.into()), ]); scope.imports.entries().map(move |entry| { let mut full_path = path.clone(); - full_path.push(KeyPart::String(entry.raw_key.to_string())); + full_path.push(KeyPart::String(entry.raw_key.into())); (full_path, entry) }) })) @@ -338,7 +333,7 @@ fn add_deps_from_package_json( package_json: &PackageJsonRc, mut filter: impl DepFilter, package_dep_kind: PackageJsonDepKind, - package_json_deps: PackageJsonDepsMap, + package_json_deps: &PackageJsonDepsMap, deps: &mut Vec, ) { for (k, v) in package_json_deps { @@ -353,7 +348,7 @@ fn add_deps_from_package_json( deno_package_json::PackageJsonDepValue::Req(req) => { let alias = k.as_str(); let alias = (alias != req.name).then(|| alias.to_string()); - if !filter.should_include(alias.as_deref(), &req, DepKind::Npm) { + if !filter.should_include(alias.as_deref(), req, DepKind::Npm) { continue; } let id = DepId(deps.len()); @@ -362,9 +357,12 @@ fn add_deps_from_package_json( kind: DepKind::Npm, location: DepLocation::PackageJson( package_json.clone(), - KeyPath::from_parts([package_dep_kind.into(), k.into()]), + KeyPath::from_parts([ + package_dep_kind.into(), + KeyPart::String(k.clone()), + ]), ), - req, + req: req.clone(), alias, }) } @@ -377,14 +375,14 @@ fn add_deps_from_package_json( package_json, filter, PackageJsonDepKind::Normal, - package_json_deps.dependencies, + &package_json_deps.dependencies, deps, ); iterate( package_json, filter, PackageJsonDepKind::Dev, - package_json_deps.dev_dependencies, + &package_json_deps.dev_dependencies, deps, ); } diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index f767eb1522..20a6043f26 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -8,6 +8,7 @@ use deno_core::anyhow::bail; use deno_core::error::AnyError; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::VersionReq; use deno_terminal::colors; @@ -31,7 +32,7 @@ struct OutdatedPackage { latest: String, semver_compatible: String, current: String, - name: String, + name: StackString, } #[allow(clippy::print_stdout)] diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs index cb85859f7a..b3d7618be9 100644 --- a/cli/tools/upgrade.rs +++ b/cli/tools/upgrade.rs @@ -21,6 +21,7 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::unsync::spawn; use deno_core::url::Url; +use deno_semver::SmallStackString; use deno_semver::Version; use once_cell::sync::Lazy; use std::borrow::Cow; @@ -255,7 +256,7 @@ async fn print_release_notes( let is_deno_2_rc = new_semver.major == 2 && new_semver.minor == 0 && new_semver.patch == 0 - && new_semver.pre.first() == Some(&"rc".to_string()); + && new_semver.pre.first().map(|s| s.as_str()) == Some("rc"); if is_deno_2_rc || is_switching_from_deno1_to_deno2 { log::info!( @@ -674,7 +675,7 @@ impl RequestedVersion { ); }; - if semver.pre.contains(&"rc".to_string()) { + if semver.pre.contains(&SmallStackString::from_static("rc")) { (ReleaseChannel::Rc, passed_version) } else { (ReleaseChannel::Stable, passed_version) diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index 08d06f9cac..c6f5d1cd04 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -9,6 +9,7 @@ use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonRc; use deno_path_util::url_to_file_path; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::Version; use node_resolver::env::NodeResolverEnv; use node_resolver::errors::PackageFolderResolveError; @@ -30,7 +31,7 @@ use super::ResolvePkgFolderFromDenoReqError; #[derive(Debug, Error)] pub enum ByonmResolvePkgFolderFromDenoReqError { #[error("Could not find \"{}\" in a node_modules folder. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", .0)] - MissingAlias(String), + MissingAlias(StackString), #[error(transparent)] PackageJson(#[from] PackageJsonLoadError), #[error("Could not find a matching package for 'npm:{}' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `\"nodeModulesDir\": \"auto\"` in your deno.json file.", .0)] @@ -177,16 +178,14 @@ impl ByonmNpmResolver { &self, req: &PackageReq, referrer: &Url, - ) -> Result, PackageJsonLoadError> { + ) -> Result, PackageJsonLoadError> { fn resolve_alias_from_pkg_json( req: &PackageReq, pkg_json: &PackageJson, - ) -> Option { + ) -> Option { let deps = pkg_json.resolve_local_package_json_deps(); - for (key, value) in deps - .dependencies - .into_iter() - .chain(deps.dev_dependencies.into_iter()) + for (key, value) in + deps.dependencies.iter().chain(deps.dev_dependencies.iter()) { if let Ok(value) = value { match value { @@ -194,12 +193,14 @@ impl ByonmNpmResolver { if dep_req.name == req.name && dep_req.version_req.intersects(&req.version_req) { - return Some(key); + return Some(key.clone()); } } PackageJsonDepValue::Workspace(_workspace) => { - if key == req.name && req.version_req.tag() == Some("workspace") { - return Some(key); + if key.as_str() == req.name + && req.version_req.tag() == Some("workspace") + { + return Some(key.clone()); } } } @@ -246,7 +247,7 @@ impl ByonmNpmResolver { if let Ok(Some(dep_pkg_json)) = self.load_pkg_json(&pkg_folder.join("package.json")) { - if dep_pkg_json.name.as_ref() == Some(&req.name) { + if dep_pkg_json.name.as_deref() == Some(req.name.as_str()) { let matches_req = dep_pkg_json .version .as_ref() diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs index 5f87698cd6..6b0bda57d7 100644 --- a/resolvers/node/resolution.rs +++ b/resolvers/node/resolution.rs @@ -318,6 +318,8 @@ impl NodeResolver { resolution_mode: ResolutionMode, resolution_kind: NodeResolutionKind, ) -> Result { + // todo(dsherret): don't allocate a string here (maybe use an + // enum that says the subpath is not prefixed with a ./) let package_subpath = package_subpath .map(|s| format!("./{s}")) .unwrap_or_else(|| ".".to_string()); diff --git a/resolvers/npm_cache/Cargo.toml b/resolvers/npm_cache/Cargo.toml index a0a106c89b..010f8a436b 100644 --- a/resolvers/npm_cache/Cargo.toml +++ b/resolvers/npm_cache/Cargo.toml @@ -23,6 +23,7 @@ async-trait.workspace = true base64.workspace = true boxed_error.workspace = true deno_cache_dir.workspace = true +deno_error.workspace = true deno_npm.workspace = true deno_semver.workspace = true deno_unsync = { workspace = true, features = ["tokio"] } diff --git a/resolvers/npm_cache/lib.rs b/resolvers/npm_cache/lib.rs index c16c29aaf2..dbd33d29eb 100644 --- a/resolvers/npm_cache/lib.rs +++ b/resolvers/npm_cache/lib.rs @@ -15,6 +15,7 @@ use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_npm::NpmPackageCacheFolderId; use deno_semver::package::PackageNv; +use deno_semver::StackString; use deno_semver::Version; use http::HeaderName; use http::HeaderValue; @@ -260,7 +261,7 @@ impl NpmCache { .and_then(|cache_id| { Some(NpmPackageCacheFolderId { nv: PackageNv { - name: cache_id.name, + name: StackString::from_string(cache_id.name), version: Version::parse_from_npm(&cache_id.version).ok()?, }, copy_index: cache_id.copy_index, diff --git a/resolvers/npm_cache/registry_info.rs b/resolvers/npm_cache/registry_info.rs index 543ddadc5a..dd8a639591 100644 --- a/resolvers/npm_cache/registry_info.rs +++ b/resolvers/npm_cache/registry_info.rs @@ -18,6 +18,7 @@ use deno_unsync::sync::MultiRuntimeAsyncValueCreator; use futures::future::LocalBoxFuture; use futures::FutureExt; use parking_lot::Mutex; +use thiserror::Error; use url::Url; use crate::remote::maybe_auth_header_for_npm_registry; @@ -28,6 +29,31 @@ use crate::NpmCacheSetting; type LoadResult = Result>; type LoadFuture = LocalBoxFuture<'static, LoadResult>; +#[derive(Debug, Error)] +#[error(transparent)] +pub struct AnyhowJsError(pub AnyError); + +impl deno_error::JsErrorClass for AnyhowJsError { + fn get_class(&self) -> &'static str { + "generic" + } + + fn get_message(&self) -> std::borrow::Cow<'static, str> { + self.0.to_string().into() + } + + fn get_additional_properties( + &self, + ) -> Option< + Vec<( + std::borrow::Cow<'static, str>, + std::borrow::Cow<'static, str>, + )>, + > { + None + } +} + #[derive(Debug, Clone)] enum FutureResult { PackageNotExists, @@ -157,9 +183,9 @@ impl RegistryInfoProvider { Ok(None) => Err(NpmRegistryPackageInfoLoadError::PackageNotExists { package_name: name.to_string(), }), - Err(err) => { - Err(NpmRegistryPackageInfoLoadError::LoadError(Arc::new(err))) - } + Err(err) => Err(NpmRegistryPackageInfoLoadError::LoadError(Arc::new( + AnyhowJsError(err), + ))), } } diff --git a/resolvers/npm_cache/tarball_extract.rs b/resolvers/npm_cache/tarball_extract.rs index c4c614b35f..affe93eaa4 100644 --- a/resolvers/npm_cache/tarball_extract.rs +++ b/resolvers/npm_cache/tarball_extract.rs @@ -236,7 +236,7 @@ mod test { #[test] pub fn test_verify_tarball() { let package = PackageNv { - name: "package".to_string(), + name: "package".into(), version: Version::parse_from_npm("1.0.0").unwrap(), }; let actual_checksum = diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index bbd0301db4..1c5fb36f93 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -183,7 +183,7 @@ impl PermissionState { PermissionState::Prompt if prompt => { let msg = { let info = info(); - StringBuilder::build(|builder| { + StringBuilder::::build(|builder| { builder.append(name); builder.append(" access"); if let Some(info) = &info { @@ -498,7 +498,7 @@ impl UnaryPermission { } let maybe_formatted_display_name = desc.map(|d| format_display_name(d.display_name())); - let message = StringBuilder::build(|builder| { + let message = StringBuilder::::build(|builder| { builder.append(TQuery::flag_name()); builder.append(" access"); if let Some(display_name) = &maybe_formatted_display_name { diff --git a/tests/integration/check_tests.rs b/tests/integration/check_tests.rs index b98d719fca..a1fdf83403 100644 --- a/tests/integration/check_tests.rs +++ b/tests/integration/check_tests.rs @@ -218,7 +218,7 @@ fn npm_module_check_then_error() { "npm:@denotest/breaking-change-between-versions", ) .unwrap(), - "1.0.0".to_string(), + "1.0.0".into(), ); lockfile_path.write(lockfile.as_json_string()); temp_dir.write( @@ -236,7 +236,7 @@ fn npm_module_check_then_error() { "npm:@denotest/breaking-change-between-versions", ) .unwrap(), - "2.0.0".to_string(), + "2.0.0".into(), ); lockfile_path.write(lockfile.as_json_string()); diff --git a/tests/integration/jsr_tests.rs b/tests/integration/jsr_tests.rs index c4812e6bfb..f438ebfd50 100644 --- a/tests/integration/jsr_tests.rs +++ b/tests/integration/jsr_tests.rs @@ -159,7 +159,7 @@ console.log(version);"#, .get_mut( &JsrDepPackageReq::from_str("jsr:@denotest/no-module-graph@0.1").unwrap(), ) - .unwrap() = "0.1.0".to_string(); + .unwrap() = "0.1.0".into(); lockfile_path.write(lockfile.as_json_string()); test_context From 77e1af79bd47f4c134cfa08d33fce8b2753bb4fa Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 20 Dec 2024 17:35:02 -0500 Subject: [PATCH 030/107] perf: remove now needless canonicalization getting closest package.json (#27437) This is no longer required because we now store everything in the file system for deno compile instead of in an eszip. --- resolvers/node/errors.rs | 17 ----------------- resolvers/node/package_json.rs | 33 +-------------------------------- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/resolvers/node/errors.rs b/resolvers/node/errors.rs index 600a365a8f..26b1a1d84a 100644 --- a/resolvers/node/errors.rs +++ b/resolvers/node/errors.rs @@ -320,7 +320,6 @@ impl NodeJsErrorCoded for PackageJsonLoadError { impl NodeJsErrorCoded for ClosestPkgJsonError { fn code(&self) -> NodeJsErrorCode { match self.as_kind() { - ClosestPkgJsonErrorKind::CanonicalizingDir(e) => e.code(), ClosestPkgJsonErrorKind::Load(e) => e.code(), } } @@ -331,26 +330,10 @@ pub struct ClosestPkgJsonError(pub Box); #[derive(Debug, Error)] pub enum ClosestPkgJsonErrorKind { - #[error(transparent)] - CanonicalizingDir(#[from] CanonicalizingPkgJsonDirError), #[error(transparent)] Load(#[from] PackageJsonLoadError), } -#[derive(Debug, Error)] -#[error("[{}] Failed canonicalizing package.json directory '{}'.", self.code(), dir_path.display())] -pub struct CanonicalizingPkgJsonDirError { - pub dir_path: PathBuf, - #[source] - pub source: std::io::Error, -} - -impl NodeJsErrorCoded for CanonicalizingPkgJsonDirError { - fn code(&self) -> NodeJsErrorCode { - NodeJsErrorCode::ERR_MODULE_NOT_FOUND - } -} - // todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError #[derive(Debug, Error)] #[error( diff --git a/resolvers/node/package_json.rs b/resolvers/node/package_json.rs index e3793af84a..cb99e5a0aa 100644 --- a/resolvers/node/package_json.rs +++ b/resolvers/node/package_json.rs @@ -2,7 +2,6 @@ use deno_package_json::PackageJson; use deno_package_json::PackageJsonRc; -use deno_path_util::strip_unc_prefix; use std::cell::RefCell; use std::collections::HashMap; use std::io::ErrorKind; @@ -11,7 +10,6 @@ use std::path::PathBuf; use url::Url; use crate::env::NodeResolverEnv; -use crate::errors::CanonicalizingPkgJsonDirError; use crate::errors::ClosestPkgJsonError; use crate::errors::PackageJsonLoadError; @@ -67,37 +65,8 @@ impl PackageJsonResolver { &self, file_path: &Path, ) -> Result, ClosestPkgJsonError> { - // we use this for deno compile using byonm because the script paths - // won't be in virtual file system, but the package.json paths will be - fn canonicalize_first_ancestor_exists( - dir_path: &Path, - env: &TEnv, - ) -> Result, std::io::Error> { - for ancestor in dir_path.ancestors() { - match env.realpath_sync(ancestor) { - Ok(dir_path) => return Ok(Some(dir_path)), - Err(err) if err.kind() == std::io::ErrorKind::NotFound => { - // keep searching - } - Err(err) => return Err(err), - } - } - Ok(None) - } - let parent_dir = file_path.parent().unwrap(); - let Some(start_dir) = canonicalize_first_ancestor_exists( - parent_dir, &self.env, - ) - .map_err(|source| CanonicalizingPkgJsonDirError { - dir_path: parent_dir.to_path_buf(), - source, - })? - else { - return Ok(None); - }; - let start_dir = strip_unc_prefix(start_dir); - for current_dir in start_dir.ancestors() { + for current_dir in parent_dir.ancestors() { let package_json_path = current_dir.join("package.json"); if let Some(pkg_json) = self.load_package_json(&package_json_path)? { return Ok(Some(pkg_json)); From 26425a137b7489fe675d106c3943cdcea6fce0cb Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Sat, 21 Dec 2024 00:58:03 +0100 Subject: [PATCH 031/107] feat(unstable): add JS linting plugin infrastructure (#27416) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR extracts the core part of https://github.com/denoland/deno/pull/27203 to make it easier to review and land in parts. It contains: - The JS plugin code the deserializes and walks the buffer - The Rust portion to serialize SWC to the buffer format (a bunch of nodes are still todos, but imo these can land anytime later) - Basic lint plugin types, without the AST node types to make this PR easier to review - Added more code comments to explain the format etc. More fixes and changes will be done in follow-up PRs. --------- Co-authored-by: Bartek Iwańczuk --- cli/js/40_lint.js | 783 ++++++ cli/js/40_lint_types.d.ts | 50 + cli/ops/lint.rs | 34 + cli/ops/mod.rs | 1 + cli/tools/lint/ast_buffer/buffer.rs | 516 ++++ cli/tools/lint/ast_buffer/mod.rs | 13 + cli/tools/lint/ast_buffer/swc.rs | 3018 ++++++++++++++++++++++++ cli/tools/lint/ast_buffer/ts_estree.rs | 513 ++++ cli/tools/lint/mod.rs | 3 + cli/tools/test/mod.rs | 5 +- cli/worker.rs | 3 +- runtime/js/99_main.js | 3 + tests/integration/js_unit_tests.rs | 1 + tests/unit/lint_plugin_test.ts | 557 +++++ tests/unit/ops_test.ts | 2 +- 15 files changed, 5499 insertions(+), 3 deletions(-) create mode 100644 cli/js/40_lint.js create mode 100644 cli/js/40_lint_types.d.ts create mode 100644 cli/ops/lint.rs create mode 100644 cli/tools/lint/ast_buffer/buffer.rs create mode 100644 cli/tools/lint/ast_buffer/mod.rs create mode 100644 cli/tools/lint/ast_buffer/swc.rs create mode 100644 cli/tools/lint/ast_buffer/ts_estree.rs create mode 100644 tests/unit/lint_plugin_test.ts diff --git a/cli/js/40_lint.js b/cli/js/40_lint.js new file mode 100644 index 0000000000..9606f787b3 --- /dev/null +++ b/cli/js/40_lint.js @@ -0,0 +1,783 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// @ts-check + +import { core, internals } from "ext:core/mod.js"; +const { + op_lint_create_serialized_ast, +} = core.ops; + +// Keep in sync with Rust +// These types are expected to be present on every node. Note that this +// isn't set in stone. We could revise this at a future point. +const AST_PROP_TYPE = 0; +const AST_PROP_PARENT = 1; +const AST_PROP_RANGE = 2; + +// Keep in sync with Rust +// Each node property is tagged with this enum to denote +// what kind of value it holds. +/** @enum {number} */ +const PropFlags = { + /** This is an offset to another node */ + Ref: 0, + /** This is an array of offsets to other nodes (like children of a BlockStatement) */ + RefArr: 1, + /** + * This is a string id. The actual string needs to be looked up in + * the string table that was included in the message. + */ + String: 2, + /** This value is either 0 = false, or 1 = true */ + Bool: 3, + /** No value, it's null */ + Null: 4, + /** No value, it's undefined */ + Undefined: 5, +}; + +/** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */ +/** @typedef {import("./40_lint_types.d.ts").VisitorFn} VisitorFn */ +/** @typedef {import("./40_lint_types.d.ts").CompiledVisitor} CompiledVisitor */ +/** @typedef {import("./40_lint_types.d.ts").LintState} LintState */ +/** @typedef {import("./40_lint_types.d.ts").RuleContext} RuleContext */ +/** @typedef {import("./40_lint_types.d.ts").NodeFacade} NodeFacade */ +/** @typedef {import("./40_lint_types.d.ts").LintPlugin} LintPlugin */ +/** @typedef {import("./40_lint_types.d.ts").LintReportData} LintReportData */ +/** @typedef {import("./40_lint_types.d.ts").TestReportData} TestReportData */ + +/** @type {LintState} */ +const state = { + plugins: [], + installedPlugins: new Set(), +}; + +/** + * Every rule gets their own instance of this class. This is the main + * API lint rules interact with. + * @implements {RuleContext} + */ +export class Context { + id; + + fileName; + + /** + * @param {string} id + * @param {string} fileName + */ + constructor(id, fileName) { + this.id = id; + this.fileName = fileName; + } +} + +/** + * @param {LintPlugin} plugin + */ +export function installPlugin(plugin) { + if (typeof plugin !== "object") { + throw new Error("Linter plugin must be an object"); + } + if (typeof plugin.name !== "string") { + throw new Error("Linter plugin name must be a string"); + } + if (typeof plugin.rules !== "object") { + throw new Error("Linter plugin rules must be an object"); + } + if (state.installedPlugins.has(plugin.name)) { + throw new Error(`Linter plugin ${plugin.name} has already been registered`); + } + state.plugins.push(plugin); + state.installedPlugins.add(plugin.name); +} + +/** + * @param {AstContext} ctx + * @param {number} offset + * @returns + */ +function getNode(ctx, offset) { + if (offset === 0) return null; + + const cached = ctx.nodes.get(offset); + if (cached !== undefined) return cached; + + const node = new Node(ctx, offset); + ctx.nodes.set(offset, /** @type {*} */ (cached)); + return node; +} + +/** + * Find the offset of a specific property of a specific node. This will + * be used later a lot more for selectors. + * @param {Uint8Array} buf + * @param {number} search + * @param {number} offset + * @returns {number} + */ +function findPropOffset(buf, offset, search) { + // type + parentId + SpanLo + SpanHi + offset += 1 + 4 + 4 + 4; + + const propCount = buf[offset]; + offset += 1; + + for (let i = 0; i < propCount; i++) { + const maybe = offset; + const prop = buf[offset++]; + const kind = buf[offset++]; + if (prop === search) return maybe; + + if (kind === PropFlags.Ref) { + offset += 4; + } else if (kind === PropFlags.RefArr) { + const len = readU32(buf, offset); + offset += 4 + (len * 4); + } else if (kind === PropFlags.String) { + offset += 4; + } else if (kind === PropFlags.Bool) { + offset++; + } else if (kind === PropFlags.Null || kind === PropFlags.Undefined) { + // No value + } else { + offset++; + } + } + + return -1; +} + +const INTERNAL_CTX = Symbol("ctx"); +const INTERNAL_OFFSET = Symbol("offset"); + +// This class is a facade for all materialized nodes. Instead of creating a +// unique class per AST node, we have one class with getters for every +// possible node property. This allows us to lazily materialize child node +// only when they are needed. +class Node { + [INTERNAL_CTX]; + [INTERNAL_OFFSET]; + + /** + * @param {AstContext} ctx + * @param {number} offset + */ + constructor(ctx, offset) { + this[INTERNAL_CTX] = ctx; + this[INTERNAL_OFFSET] = offset; + } + + /** + * Logging a class with only getters prints just the class name. This + * makes debugging difficult because you don't see any of the properties. + * For that reason we'll intercept inspection and serialize the node to + * a plain JSON structure which can be logged and allows users to see all + * properties and their values. + * + * This is only expected to be used during development of a rule. + * @param {*} _ + * @param {Deno.InspectOptions} options + * @returns {string} + */ + [Symbol.for("Deno.customInspect")](_, options) { + const json = toJsValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET]); + return Deno.inspect(json, options); + } + + [Symbol.for("Deno.lint.toJsValue")]() { + return toJsValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET]); + } +} + +/** @type {Set} */ +const appliedGetters = new Set(); + +/** + * Add getters for all potential properties found in the message. + * @param {AstContext} ctx + */ +function setNodeGetters(ctx) { + if (appliedGetters.size === ctx.strByProp.length) return; + + for (let i = 0; i < ctx.strByProp.length; i++) { + const id = ctx.strByProp[i]; + if (id === 0 || appliedGetters.has(i)) continue; + appliedGetters.add(i); + + const name = getString(ctx.strTable, id); + + Object.defineProperty(Node.prototype, name, { + get() { + return readValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET], i); + }, + }); + } +} + +/** + * Serialize a node recursively to plain JSON + * @param {AstContext} ctx + * @param {number} offset + * @returns {*} + */ +function toJsValue(ctx, offset) { + const { buf } = ctx; + + /** @type {Record} */ + const node = { + type: readValue(ctx, offset, AST_PROP_TYPE), + range: readValue(ctx, offset, AST_PROP_RANGE), + }; + + // type + parentId + SpanLo + SpanHi + offset += 1 + 4 + 4 + 4; + + const count = buf[offset++]; + for (let i = 0; i < count; i++) { + const prop = buf[offset++]; + const kind = buf[offset++]; + const name = getString(ctx.strTable, ctx.strByProp[prop]); + + if (kind === PropFlags.Ref) { + const v = readU32(buf, offset); + offset += 4; + node[name] = v === 0 ? null : toJsValue(ctx, v); + } else if (kind === PropFlags.RefArr) { + const len = readU32(buf, offset); + offset += 4; + const nodes = new Array(len); + for (let i = 0; i < len; i++) { + const v = readU32(buf, offset); + if (v === 0) continue; + nodes[i] = toJsValue(ctx, v); + offset += 4; + } + node[name] = nodes; + } else if (kind === PropFlags.Bool) { + const v = buf[offset++]; + node[name] = v === 1; + } else if (kind === PropFlags.String) { + const v = readU32(buf, offset); + offset += 4; + node[name] = getString(ctx.strTable, v); + } else if (kind === PropFlags.Null) { + node[name] = null; + } else if (kind === PropFlags.Undefined) { + node[name] = undefined; + } + } + + return node; +} + +/** + * Read a specific property from a node + * @param {AstContext} ctx + * @param {number} offset + * @param {number} search + * @returns {*} + */ +function readValue(ctx, offset, search) { + const { buf } = ctx; + const type = buf[offset]; + + if (search === AST_PROP_TYPE) { + return getString(ctx.strTable, ctx.strByType[type]); + } else if (search === AST_PROP_RANGE) { + const start = readU32(buf, offset + 1 + 4); + const end = readU32(buf, offset + 1 + 4 + 4); + return [start, end]; + } else if (search === AST_PROP_PARENT) { + const pos = readU32(buf, offset + 1); + return getNode(ctx, pos); + } + + offset = findPropOffset(ctx.buf, offset, search); + if (offset === -1) return undefined; + + const kind = buf[offset + 1]; + + if (kind === PropFlags.Ref) { + const value = readU32(buf, offset + 2); + return getNode(ctx, value); + } else if (kind === PropFlags.RefArr) { + const len = readU32(buf, offset); + offset += 4; + + const nodes = new Array(len); + for (let i = 0; i < len; i++) { + nodes[i] = getNode(ctx, readU32(buf, offset)); + offset += 4; + } + return nodes; + } else if (kind === PropFlags.Bool) { + return buf[offset] === 1; + } else if (kind === PropFlags.String) { + const v = readU32(buf, offset); + return getString(ctx.strTable, v); + } else if (kind === PropFlags.Null) { + return null; + } else if (kind === PropFlags.Undefined) { + return undefined; + } + + throw new Error(`Unknown prop kind: ${kind}`); +} + +const DECODER = new TextDecoder(); + +/** + * TODO: Check if it's faster to use the `ArrayView` API instead. + * @param {Uint8Array} buf + * @param {number} i + * @returns {number} + */ +function readU32(buf, i) { + return (buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + + buf[i + 3]; +} + +/** + * Get a string by id and error if it wasn't found + * @param {AstContext["strTable"]} strTable + * @param {number} id + * @returns {string} + */ +function getString(strTable, id) { + const name = strTable.get(id); + if (name === undefined) { + throw new Error(`Missing string id: ${id}`); + } + + return name; +} + +/** + * @param {Uint8Array} buf + * @param {AstContext} buf + */ +function createAstContext(buf) { + /** @type {Map} */ + const strTable = new Map(); + + // The buffer has a few offsets at the end which allows us to easily + // jump to the relevant sections of the message. + const typeMapOffset = readU32(buf, buf.length - 16); + const propMapOffset = readU32(buf, buf.length - 12); + const strTableOffset = readU32(buf, buf.length - 8); + + // Offset of the topmost node in the AST Tree. + const rootOffset = readU32(buf, buf.length - 4); + + let offset = strTableOffset; + const stringCount = readU32(buf, offset); + offset += 4; + + // TODO(@marvinhagemeister): We could lazily decode the strings on an as needed basis. + // Not sure if this matters much in practice though. + let id = 0; + for (let i = 0; i < stringCount; i++) { + const len = readU32(buf, offset); + offset += 4; + + const strBytes = buf.slice(offset, offset + len); + offset += len; + const s = DECODER.decode(strBytes); + strTable.set(id, s); + id++; + } + + if (strTable.size !== stringCount) { + throw new Error( + `Could not deserialize string table. Expected ${stringCount} items, but got ${strTable.size}`, + ); + } + + offset = typeMapOffset; + const typeCount = readU32(buf, offset); + offset += 4; + + const typeByStr = new Map(); + const strByType = new Array(typeCount).fill(0); + for (let i = 0; i < typeCount; i++) { + const v = readU32(buf, offset); + offset += 4; + + strByType[i] = v; + typeByStr.set(strTable.get(v), i); + } + + offset = propMapOffset; + const propCount = readU32(buf, offset); + offset += 4; + + const propByStr = new Map(); + const strByProp = new Array(propCount).fill(0); + for (let i = 0; i < propCount; i++) { + const v = readU32(buf, offset); + offset += 4; + + strByProp[i] = v; + propByStr.set(strTable.get(v), i); + } + + /** @type {AstContext} */ + const ctx = { + buf, + strTable, + rootOffset, + nodes: new Map(), + strTableOffset, + strByProp, + strByType, + typeByStr, + propByStr, + }; + + setNodeGetters(ctx); + + // DEV ONLY: Enable this to inspect the buffer message + // _dump(ctx); + + return ctx; +} + +/** + * @param {*} _node + */ +const NOOP = (_node) => {}; + +/** + * Kick off the actual linting process of JS plugins. + * @param {string} fileName + * @param {Uint8Array} serializedAst + */ +export function runPluginsForFile(fileName, serializedAst) { + const ctx = createAstContext(serializedAst); + + /** @type {Map} */ + const bySelector = new Map(); + + const destroyFns = []; + + // Instantiate and merge visitors. This allows us to only traverse + // the AST once instead of per plugin. When ever we enter or exit a + // node we'll call all visitors that match. + for (let i = 0; i < state.plugins.length; i++) { + const plugin = state.plugins[i]; + + for (const name of Object.keys(plugin.rules)) { + const rule = plugin.rules[name]; + const id = `${plugin.name}/${name}`; + const ctx = new Context(id, fileName); + const visitor = rule.create(ctx); + + // deno-lint-ignore guard-for-in + for (let key in visitor) { + const fn = visitor[key]; + if (fn === undefined) continue; + + // Support enter and exit callbacks on a visitor. + // Exit callbacks are marked by having `:exit` at the end. + let isExit = false; + if (key.endsWith(":exit")) { + isExit = true; + key = key.slice(0, -":exit".length); + } + + let info = bySelector.get(key); + if (info === undefined) { + info = { enter: NOOP, exit: NOOP }; + bySelector.set(key, info); + } + const prevFn = isExit ? info.exit : info.enter; + + /** + * @param {*} node + */ + const wrapped = (node) => { + prevFn(node); + + try { + fn(node); + } catch (err) { + throw new Error(`Visitor "${name}" of plugin "${id}" errored`, { + cause: err, + }); + } + }; + + if (isExit) { + info.exit = wrapped; + } else { + info.enter = wrapped; + } + } + + if (typeof rule.destroy === "function") { + const destroyFn = rule.destroy.bind(rule); + destroyFns.push(() => { + try { + destroyFn(ctx); + } catch (err) { + throw new Error(`Destroy hook of "${id}" errored`, { cause: err }); + } + }); + } + } + } + + /** @type {CompiledVisitor[]} */ + const visitors = []; + for (const [sel, info] of bySelector.entries()) { + // This will make more sense once selectors land as it's faster + // to precompile them once upfront. + + // Convert the visiting element name to a number. This number + // is part of the serialized buffer and comparing a single number + // is quicker than strings. + const elemId = ctx.typeByStr.get(sel) ?? -1; + + visitors.push({ + info, + // Check if we should call this visitor + matcher: (offset) => { + const type = ctx.buf[offset]; + return type === elemId; + }, + }); + } + + // Traverse ast with all visitors at the same time to avoid traversing + // multiple times. + try { + traverse(ctx, visitors, ctx.rootOffset); + } finally { + ctx.nodes.clear(); + + // Optional: Destroy rules + for (let i = 0; i < destroyFns.length; i++) { + destroyFns[i](); + } + } +} + +/** + * @param {AstContext} ctx + * @param {CompiledVisitor[]} visitors + * @param {number} offset + */ +function traverse(ctx, visitors, offset) { + // The 0 offset is used to denote an empty/placeholder node + if (offset === 0) return; + + const { buf } = ctx; + + /** @type {VisitorFn[] | null} */ + let exits = null; + + for (let i = 0; i < visitors.length; i++) { + const v = visitors[i]; + + if (v.matcher(offset)) { + if (v.info.exit !== NOOP) { + if (exits === null) { + exits = [v.info.exit]; + } else { + exits.push(v.info.exit); + } + } + + if (v.info.enter !== NOOP) { + const node = /** @type {*} */ (getNode(ctx, offset)); + v.info.enter(node); + } + } + } + + // Search for node references in the properties of the current node. All + // other properties can be ignored. + try { + // type + parentId + SpanLo + SpanHi + offset += 1 + 4 + 4 + 4; + + const propCount = buf[offset]; + offset += 1; + + for (let i = 0; i < propCount; i++) { + const kind = buf[offset + 1]; + offset += 2; // propId + propFlags + + if (kind === PropFlags.Ref) { + const next = readU32(buf, offset); + offset += 4; + traverse(ctx, visitors, next); + } else if (kind === PropFlags.RefArr) { + const len = readU32(buf, offset); + offset += 4; + + for (let j = 0; j < len; j++) { + const child = readU32(buf, offset); + offset += 4; + traverse(ctx, visitors, child); + } + } else if (kind === PropFlags.String) { + offset += 4; + } else if (kind === PropFlags.Bool) { + offset += 1; + } else if (kind === PropFlags.Null || kind === PropFlags.Undefined) { + // No value + } + } + } finally { + if (exits !== null) { + for (let i = 0; i < exits.length; i++) { + const node = /** @type {*} */ (getNode(ctx, offset)); + exits[i](node); + } + } + } +} + +/** + * This is useful debugging helper to display the buffer's contents. + * @param {AstContext} ctx + */ +function _dump(ctx) { + const { buf, strTableOffset, strTable, strByType, strByProp } = ctx; + + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(strTable); + + for (let i = 0; i < strByType.length; i++) { + const v = strByType[i]; + // @ts-ignore dump fn + // deno-lint-ignore no-console + if (v > 0) console.log(" > type:", i, getString(ctx.strTable, v), v); + } + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(); + for (let i = 0; i < strByProp.length; i++) { + const v = strByProp[i]; + // @ts-ignore dump fn + // deno-lint-ignore no-console + if (v > 0) console.log(" > prop:", i, getString(ctx.strTable, v), v); + } + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(); + + let offset = 0; + + while (offset < strTableOffset) { + const type = buf[offset]; + const name = getString(ctx.strTable, ctx.strByType[type]); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(`${name}, offset: ${offset}, type: ${type}`); + offset += 1; + + const parent = readU32(buf, offset); + offset += 4; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` parent: ${parent}`); + + const start = readU32(buf, offset); + offset += 4; + const end = readU32(buf, offset); + offset += 4; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` range: ${start} -> ${end}`); + + const count = buf[offset++]; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` prop count: ${count}`); + + for (let i = 0; i < count; i++) { + const prop = buf[offset++]; + const kind = buf[offset++]; + const name = getString(ctx.strTable, ctx.strByProp[prop]); + + let kindName = "unknown"; + for (const k in PropFlags) { + // @ts-ignore dump fn + if (kind === PropFlags[k]) { + kindName = k; + } + } + + if (kind === PropFlags.Ref) { + const v = readU32(buf, offset); + offset += 4; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${v} (${kindName}, ${prop})`); + } else if (kind === PropFlags.RefArr) { + const len = readU32(buf, offset); + offset += 4; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: Array(${len}) (${kindName}, ${prop})`); + + for (let j = 0; j < len; j++) { + const v = readU32(buf, offset); + offset += 4; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` - ${v} (${prop})`); + } + } else if (kind === PropFlags.Bool) { + const v = buf[offset]; + offset += 1; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${v} (${kindName}, ${prop})`); + } else if (kind === PropFlags.String) { + const v = readU32(buf, offset); + offset += 4; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log( + ` ${name}: ${getString(ctx.strTable, v)} (${kindName}, ${prop})`, + ); + } else if (kind === PropFlags.Null) { + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: null (${kindName}, ${prop})`); + } else if (kind === PropFlags.Undefined) { + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: undefined (${kindName}, ${prop})`); + } + } + } +} + +// TODO(bartlomieju): this is temporary, until we get plugins plumbed through +// the CLI linter +/** + * @param {LintPlugin} plugin + * @param {string} fileName + * @param {string} sourceText + */ +function runLintPlugin(plugin, fileName, sourceText) { + installPlugin(plugin); + const serializedAst = op_lint_create_serialized_ast(fileName, sourceText); + + try { + runPluginsForFile(fileName, serializedAst); + } finally { + // During testing we don't want to keep plugins around + state.installedPlugins.clear(); + } +} + +// TODO(bartlomieju): this is temporary, until we get plugins plumbed through +// the CLI linter +internals.runLintPlugin = runLintPlugin; diff --git a/cli/js/40_lint_types.d.ts b/cli/js/40_lint_types.d.ts new file mode 100644 index 0000000000..8c252f10ad --- /dev/null +++ b/cli/js/40_lint_types.d.ts @@ -0,0 +1,50 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +export interface NodeFacade { + type: string; + range: [number, number]; + [key: string]: unknown; +} + +export interface AstContext { + buf: Uint8Array; + strTable: Map; + strTableOffset: number; + rootOffset: number; + nodes: Map; + strByType: number[]; + strByProp: number[]; + typeByStr: Map; + propByStr: Map; +} + +// TODO(@marvinhagemeister) Remove once we land "official" types +export interface RuleContext { + id: string; +} + +// TODO(@marvinhagemeister) Remove once we land "official" types +export interface LintRule { + create(ctx: RuleContext): Record void>; + destroy?(ctx: RuleContext): void; +} + +// TODO(@marvinhagemeister) Remove once we land "official" types +export interface LintPlugin { + name: string; + rules: Record; +} + +export interface LintState { + plugins: LintPlugin[]; + installedPlugins: Set; +} + +export type VisitorFn = (node: unknown) => void; + +export interface CompiledVisitor { + matcher: (offset: number) => boolean; + info: { enter: VisitorFn; exit: VisitorFn }; +} + +export {}; diff --git a/cli/ops/lint.rs b/cli/ops/lint.rs new file mode 100644 index 0000000000..c38ac0c8a2 --- /dev/null +++ b/cli/ops/lint.rs @@ -0,0 +1,34 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use deno_ast::MediaType; +use deno_ast::ModuleSpecifier; +use deno_core::error::generic_error; +use deno_core::error::AnyError; +use deno_core::op2; + +use crate::tools::lint; + +deno_core::extension!(deno_lint, ops = [op_lint_create_serialized_ast,],); + +#[op2] +#[buffer] +fn op_lint_create_serialized_ast( + #[string] file_name: &str, + #[string] source: String, +) -> Result, AnyError> { + let file_text = deno_ast::strip_bom(source); + let path = std::env::current_dir()?.join(file_name); + let specifier = ModuleSpecifier::from_file_path(&path).map_err(|_| { + generic_error(format!("Failed to parse path as URL: {}", path.display())) + })?; + let media_type = MediaType::from_specifier(&specifier); + let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { + specifier, + text: file_text.into(), + media_type, + capture_tokens: false, + scope_analysis: false, + maybe_syntax: None, + })?; + Ok(lint::serialize_ast_to_buffer(&parsed_source)) +} diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index 230d268ab4..4ac1618816 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -2,4 +2,5 @@ pub mod bench; pub mod jupyter; +pub mod lint; pub mod testing; diff --git a/cli/tools/lint/ast_buffer/buffer.rs b/cli/tools/lint/ast_buffer/buffer.rs new file mode 100644 index 0000000000..c440b73ccd --- /dev/null +++ b/cli/tools/lint/ast_buffer/buffer.rs @@ -0,0 +1,516 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use std::fmt::Display; + +use deno_ast::swc::common::Span; +use deno_ast::swc::common::DUMMY_SP; +use indexmap::IndexMap; + +/// Each property has this flag to mark what kind of value it holds- +/// Plain objects and arrays are not supported yet, but could be easily +/// added if needed. +#[derive(Debug, PartialEq)] +pub enum PropFlags { + Ref, + RefArr, + String, + Bool, + Null, + Undefined, +} + +impl From for u8 { + fn from(m: PropFlags) -> u8 { + m as u8 + } +} + +impl TryFrom for PropFlags { + type Error = &'static str; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(PropFlags::Ref), + 1 => Ok(PropFlags::RefArr), + 2 => Ok(PropFlags::String), + 3 => Ok(PropFlags::Bool), + 4 => Ok(PropFlags::Null), + 5 => Ok(PropFlags::Undefined), + _ => Err("Unknown Prop flag"), + } + } +} + +const MASK_U32_1: u32 = 0b11111111_00000000_00000000_00000000; +const MASK_U32_2: u32 = 0b00000000_11111111_00000000_00000000; +const MASK_U32_3: u32 = 0b00000000_00000000_11111111_00000000; +const MASK_U32_4: u32 = 0b00000000_00000000_00000000_11111111; + +// TODO: There is probably a native Rust function to do this. +pub fn append_u32(result: &mut Vec, value: u32) { + let v1: u8 = ((value & MASK_U32_1) >> 24) as u8; + let v2: u8 = ((value & MASK_U32_2) >> 16) as u8; + let v3: u8 = ((value & MASK_U32_3) >> 8) as u8; + let v4: u8 = (value & MASK_U32_4) as u8; + + result.push(v1); + result.push(v2); + result.push(v3); + result.push(v4); +} + +pub fn append_usize(result: &mut Vec, value: usize) { + let raw = u32::try_from(value).unwrap(); + append_u32(result, raw); +} + +pub fn write_usize(result: &mut [u8], value: usize, idx: usize) { + let raw = u32::try_from(value).unwrap(); + + let v1: u8 = ((raw & MASK_U32_1) >> 24) as u8; + let v2: u8 = ((raw & MASK_U32_2) >> 16) as u8; + let v3: u8 = ((raw & MASK_U32_3) >> 8) as u8; + let v4: u8 = (raw & MASK_U32_4) as u8; + + result[idx] = v1; + result[idx + 1] = v2; + result[idx + 2] = v3; + result[idx + 3] = v4; +} + +#[derive(Debug)] +pub struct StringTable { + id: usize, + table: IndexMap, +} + +impl StringTable { + pub fn new() -> Self { + Self { + id: 0, + table: IndexMap::new(), + } + } + + pub fn insert(&mut self, s: &str) -> usize { + if let Some(id) = self.table.get(s) { + return *id; + } + + let id = self.id; + self.id += 1; + self.table.insert(s.to_string(), id); + id + } + + pub fn serialize(&mut self) -> Vec { + let mut result: Vec = vec![]; + append_u32(&mut result, self.table.len() as u32); + + // Assume that it's sorted by id + for (s, _id) in &self.table { + let bytes = s.as_bytes(); + append_u32(&mut result, bytes.len() as u32); + result.append(&mut bytes.to_vec()); + } + + result + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct NodeRef(pub usize); + +#[derive(Debug)] +pub struct BoolPos(pub usize); +#[derive(Debug)] +pub struct FieldPos(pub usize); +#[derive(Debug)] +pub struct FieldArrPos(pub usize); +#[derive(Debug)] +pub struct StrPos(pub usize); +#[derive(Debug)] +pub struct UndefPos(pub usize); +#[derive(Debug)] +pub struct NullPos(pub usize); + +#[derive(Debug)] +pub enum NodePos { + Bool(BoolPos), + #[allow(dead_code)] + Field(FieldPos), + #[allow(dead_code)] + FieldArr(FieldArrPos), + Str(StrPos), + Undef(UndefPos), + #[allow(dead_code)] + Null(NullPos), +} + +pub trait AstBufSerializer +where + K: Into + Display, + P: Into + Display, +{ + fn header( + &mut self, + kind: K, + parent: NodeRef, + span: &Span, + prop_count: usize, + ) -> NodeRef; + fn ref_field(&mut self, prop: P) -> FieldPos; + fn ref_vec_field(&mut self, prop: P, len: usize) -> FieldArrPos; + fn str_field(&mut self, prop: P) -> StrPos; + fn bool_field(&mut self, prop: P) -> BoolPos; + fn undefined_field(&mut self, prop: P) -> UndefPos; + #[allow(dead_code)] + fn null_field(&mut self, prop: P) -> NullPos; + + fn write_ref(&mut self, pos: FieldPos, value: NodeRef); + fn write_maybe_ref(&mut self, pos: FieldPos, value: Option); + fn write_refs(&mut self, pos: FieldArrPos, value: Vec); + fn write_str(&mut self, pos: StrPos, value: &str); + fn write_bool(&mut self, pos: BoolPos, value: bool); + + fn serialize(&mut self) -> Vec; +} + +#[derive(Debug)] +pub struct SerializeCtx { + buf: Vec, + start_buf: NodeRef, + str_table: StringTable, + kind_map: Vec, + prop_map: Vec, +} + +/// This is the internal context used to allocate and fill the buffer. The point +/// is to be able to write absolute offsets directly in place. +/// +/// The typical workflow is to reserve all necessary space for the currrent +/// node with placeholders for the offsets of the child nodes. Once child +/// nodes have been traversed, we know their offsets and can replace the +/// placeholder values with the actual ones. +impl SerializeCtx { + pub fn new(kind_len: u8, prop_len: u8) -> Self { + let kind_size = kind_len as usize; + let prop_size = prop_len as usize; + let mut ctx = Self { + start_buf: NodeRef(0), + buf: vec![], + str_table: StringTable::new(), + kind_map: vec![0; kind_size + 1], + prop_map: vec![0; prop_size + 1], + }; + + ctx.str_table.insert(""); + + // Placeholder node is always 0 + ctx.append_node(0, NodeRef(0), &DUMMY_SP, 0); + ctx.kind_map[0] = 0; + ctx.start_buf = NodeRef(ctx.buf.len()); + + // Insert default props that are always present + let type_str = ctx.str_table.insert("type"); + let parent_str = ctx.str_table.insert("parent"); + let range_str = ctx.str_table.insert("range"); + + // These values are expected to be in this order on the JS side + ctx.prop_map[0] = type_str; + ctx.prop_map[1] = parent_str; + ctx.prop_map[2] = range_str; + + ctx + } + + /// Allocate a node's header + fn field_header

(&mut self, prop: P, prop_flags: PropFlags) -> usize + where + P: Into + Display + Clone, + { + let offset = self.buf.len(); + + let n: u8 = prop.clone().into(); + self.buf.push(n); + + if let Some(v) = self.prop_map.get::(n.into()) { + if *v == 0 { + let id = self.str_table.insert(&format!("{prop}")); + self.prop_map[n as usize] = id; + } + } + + let flags: u8 = prop_flags.into(); + self.buf.push(flags); + + offset + } + + /// Allocate a property pointing to another node. + fn field

(&mut self, prop: P, prop_flags: PropFlags) -> usize + where + P: Into + Display + Clone, + { + let offset = self.field_header(prop, prop_flags); + + append_usize(&mut self.buf, 0); + + offset + } + + fn append_node( + &mut self, + kind: u8, + parent: NodeRef, + span: &Span, + prop_count: usize, + ) -> NodeRef { + let offset = self.buf.len(); + + // Node type fits in a u8 + self.buf.push(kind); + + // Offset to the parent node. Will be 0 if none exists + append_usize(&mut self.buf, parent.0); + + // Span, the start and end location of this node + append_u32(&mut self.buf, span.lo.0); + append_u32(&mut self.buf, span.hi.0); + + // No node has more than <10 properties + debug_assert!(prop_count < 10); + self.buf.push(prop_count as u8); + + NodeRef(offset) + } + + /// Allocate the node header. It's always the same for every node. + /// + /// + /// + /// + /// (There is no node with more than 10 properties) + pub fn header( + &mut self, + kind: N, + parent: NodeRef, + span: &Span, + prop_count: usize, + ) -> NodeRef + where + N: Into + Display + Clone, + { + let n: u8 = kind.clone().into(); + + if let Some(v) = self.kind_map.get::(n.into()) { + if *v == 0 { + let id = self.str_table.insert(&format!("{kind}")); + self.kind_map[n as usize] = id; + } + } + + self.append_node(n, parent, span, prop_count) + } + + /// Allocate a reference property that will hold the offset of + /// another node. + pub fn ref_field

(&mut self, prop: P) -> usize + where + P: Into + Display + Clone, + { + self.field(prop, PropFlags::Ref) + } + + /// Allocate a property that is a vec of node offsets pointing to other + /// nodes. + pub fn ref_vec_field

(&mut self, prop: P, len: usize) -> usize + where + P: Into + Display + Clone, + { + let offset = self.field(prop, PropFlags::RefArr); + + for _ in 0..len { + append_u32(&mut self.buf, 0); + } + + offset + } + + // Allocate a property representing a string. Strings are deduplicated + // in the message and the property will only contain the string id. + pub fn str_field

(&mut self, prop: P) -> usize + where + P: Into + Display + Clone, + { + self.field(prop, PropFlags::String) + } + + /// Allocate a bool field + pub fn bool_field

(&mut self, prop: P) -> usize + where + P: Into + Display + Clone, + { + let offset = self.field_header(prop, PropFlags::Bool); + self.buf.push(0); + offset + } + + /// Allocate an undefined field + pub fn undefined_field

(&mut self, prop: P) -> usize + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Undefined) + } + + /// Allocate an undefined field + #[allow(dead_code)] + pub fn null_field

(&mut self, prop: P) -> usize + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Null) + } + + /// Replace the placeholder of a reference field with the actual offset + /// to the node we want to point to. + pub fn write_ref(&mut self, field_offset: usize, value: NodeRef) { + #[cfg(debug_assertions)] + { + let value_kind = self.buf[field_offset + 1]; + if PropFlags::try_from(value_kind).unwrap() != PropFlags::Ref { + panic!("Trying to write a ref into a non-ref field") + } + } + + write_usize(&mut self.buf, value.0, field_offset + 2); + } + + /// Helper for writing optional node offsets + pub fn write_maybe_ref( + &mut self, + field_offset: usize, + value: Option, + ) { + #[cfg(debug_assertions)] + { + let value_kind = self.buf[field_offset + 1]; + if PropFlags::try_from(value_kind).unwrap() != PropFlags::Ref { + panic!("Trying to write a ref into a non-ref field") + } + } + + let ref_value = if let Some(v) = value { v } else { NodeRef(0) }; + write_usize(&mut self.buf, ref_value.0, field_offset + 2); + } + + /// Write a vec of node offsets into the property. The necessary space + /// has been reserved earlier. + pub fn write_refs(&mut self, field_offset: usize, value: Vec) { + #[cfg(debug_assertions)] + { + let value_kind = self.buf[field_offset + 1]; + if PropFlags::try_from(value_kind).unwrap() != PropFlags::RefArr { + panic!("Trying to write a ref into a non-ref array field") + } + } + + let mut offset = field_offset + 2; + write_usize(&mut self.buf, value.len(), offset); + offset += 4; + + for item in value { + write_usize(&mut self.buf, item.0, offset); + offset += 4; + } + } + + /// Store the string in our string table and save the id of the string + /// in the current field. + pub fn write_str(&mut self, field_offset: usize, value: &str) { + #[cfg(debug_assertions)] + { + let value_kind = self.buf[field_offset + 1]; + if PropFlags::try_from(value_kind).unwrap() != PropFlags::String { + panic!("Trying to write a ref into a non-string field") + } + } + + let id = self.str_table.insert(value); + write_usize(&mut self.buf, id, field_offset + 2); + } + + /// Write a bool to a field. + pub fn write_bool(&mut self, field_offset: usize, value: bool) { + #[cfg(debug_assertions)] + { + let value_kind = self.buf[field_offset + 1]; + if PropFlags::try_from(value_kind).unwrap() != PropFlags::Bool { + panic!("Trying to write a ref into a non-bool field") + } + } + + self.buf[field_offset + 2] = if value { 1 } else { 0 }; + } + + /// Serialize all information we have into a buffer that can be sent to JS. + /// It has the following structure: + /// + /// <...ast> + /// + /// <- node kind id maps to string id + /// <- node property id maps to string id + /// + /// + /// + pub fn serialize(&mut self) -> Vec { + let mut buf: Vec = vec![]; + + // The buffer starts with the serialized AST first, because that + // contains absolute offsets. By butting this at the start of the + // message we don't have to waste time updating any offsets. + buf.append(&mut self.buf); + + // Next follows the string table. We'll keep track of the offset + // in the message of where the string table begins + let offset_str_table = buf.len(); + + // Serialize string table + buf.append(&mut self.str_table.serialize()); + + // Next, serialize the mappings of kind -> string of encountered + // nodes in the AST. We use this additional lookup table to compress + // the message so that we can save space by using a u8 . All nodes of + // JS, TS and JSX together are <200 + let offset_kind_map = buf.len(); + + // Write the total number of entries in the kind -> str mapping table + // TODO: make this a u8 + append_usize(&mut buf, self.kind_map.len()); + for v in &self.kind_map { + append_usize(&mut buf, *v); + } + + // Store offset to prop -> string map. It's the same as with node kind + // as the total number of properties is <120 which allows us to store it + // as u8. + let offset_prop_map = buf.len(); + // Write the total number of entries in the kind -> str mapping table + append_usize(&mut buf, self.prop_map.len()); + for v in &self.prop_map { + append_usize(&mut buf, *v); + } + + // Putting offsets of relevant parts of the buffer at the end. This + // allows us to hop to the relevant part by merely looking at the last + // for values in the message. Each value represents an offset into the + // buffer. + append_usize(&mut buf, offset_kind_map); + append_usize(&mut buf, offset_prop_map); + append_usize(&mut buf, offset_str_table); + append_usize(&mut buf, self.start_buf.0); + + buf + } +} diff --git a/cli/tools/lint/ast_buffer/mod.rs b/cli/tools/lint/ast_buffer/mod.rs new file mode 100644 index 0000000000..8838bcc5f2 --- /dev/null +++ b/cli/tools/lint/ast_buffer/mod.rs @@ -0,0 +1,13 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use deno_ast::ParsedSource; +use swc::serialize_swc_to_buffer; + +mod buffer; +mod swc; +mod ts_estree; + +pub fn serialize_ast_to_buffer(parsed_source: &ParsedSource) -> Vec { + // TODO: We could support multiple languages here + serialize_swc_to_buffer(parsed_source) +} diff --git a/cli/tools/lint/ast_buffer/swc.rs b/cli/tools/lint/ast_buffer/swc.rs new file mode 100644 index 0000000000..785a38a7d8 --- /dev/null +++ b/cli/tools/lint/ast_buffer/swc.rs @@ -0,0 +1,3018 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use deno_ast::swc::ast::AssignTarget; +use deno_ast::swc::ast::AssignTargetPat; +use deno_ast::swc::ast::BlockStmtOrExpr; +use deno_ast::swc::ast::Callee; +use deno_ast::swc::ast::ClassMember; +use deno_ast::swc::ast::Decl; +use deno_ast::swc::ast::ExportSpecifier; +use deno_ast::swc::ast::Expr; +use deno_ast::swc::ast::ExprOrSpread; +use deno_ast::swc::ast::FnExpr; +use deno_ast::swc::ast::ForHead; +use deno_ast::swc::ast::Function; +use deno_ast::swc::ast::Ident; +use deno_ast::swc::ast::IdentName; +use deno_ast::swc::ast::JSXAttrName; +use deno_ast::swc::ast::JSXAttrOrSpread; +use deno_ast::swc::ast::JSXAttrValue; +use deno_ast::swc::ast::JSXElement; +use deno_ast::swc::ast::JSXElementChild; +use deno_ast::swc::ast::JSXElementName; +use deno_ast::swc::ast::JSXEmptyExpr; +use deno_ast::swc::ast::JSXExpr; +use deno_ast::swc::ast::JSXExprContainer; +use deno_ast::swc::ast::JSXFragment; +use deno_ast::swc::ast::JSXMemberExpr; +use deno_ast::swc::ast::JSXNamespacedName; +use deno_ast::swc::ast::JSXObject; +use deno_ast::swc::ast::JSXOpeningElement; +use deno_ast::swc::ast::Lit; +use deno_ast::swc::ast::MemberExpr; +use deno_ast::swc::ast::MemberProp; +use deno_ast::swc::ast::ModuleDecl; +use deno_ast::swc::ast::ModuleExportName; +use deno_ast::swc::ast::ModuleItem; +use deno_ast::swc::ast::ObjectPatProp; +use deno_ast::swc::ast::OptChainBase; +use deno_ast::swc::ast::Param; +use deno_ast::swc::ast::ParamOrTsParamProp; +use deno_ast::swc::ast::Pat; +use deno_ast::swc::ast::PrivateName; +use deno_ast::swc::ast::Program; +use deno_ast::swc::ast::Prop; +use deno_ast::swc::ast::PropName; +use deno_ast::swc::ast::PropOrSpread; +use deno_ast::swc::ast::SimpleAssignTarget; +use deno_ast::swc::ast::Stmt; +use deno_ast::swc::ast::SuperProp; +use deno_ast::swc::ast::Tpl; +use deno_ast::swc::ast::TsEntityName; +use deno_ast::swc::ast::TsEnumMemberId; +use deno_ast::swc::ast::TsFnOrConstructorType; +use deno_ast::swc::ast::TsFnParam; +use deno_ast::swc::ast::TsIndexSignature; +use deno_ast::swc::ast::TsLit; +use deno_ast::swc::ast::TsLitType; +use deno_ast::swc::ast::TsThisTypeOrIdent; +use deno_ast::swc::ast::TsType; +use deno_ast::swc::ast::TsTypeAnn; +use deno_ast::swc::ast::TsTypeElement; +use deno_ast::swc::ast::TsTypeParam; +use deno_ast::swc::ast::TsTypeParamDecl; +use deno_ast::swc::ast::TsTypeParamInstantiation; +use deno_ast::swc::ast::TsTypeQueryExpr; +use deno_ast::swc::ast::TsUnionOrIntersectionType; +use deno_ast::swc::ast::VarDeclOrExpr; +use deno_ast::swc::common::Span; +use deno_ast::swc::common::Spanned; +use deno_ast::swc::common::SyntaxContext; +use deno_ast::view::Accessibility; +use deno_ast::view::AssignOp; +use deno_ast::view::BinaryOp; +use deno_ast::view::TruePlusMinus; +use deno_ast::view::TsKeywordTypeKind; +use deno_ast::view::TsTypeOperatorOp; +use deno_ast::view::UnaryOp; +use deno_ast::view::UpdateOp; +use deno_ast::view::VarDeclKind; +use deno_ast::ParsedSource; + +use super::buffer::AstBufSerializer; +use super::buffer::BoolPos; +use super::buffer::NodePos; +use super::buffer::NodeRef; +use super::buffer::StrPos; +use super::ts_estree::AstNode; +use super::ts_estree::AstProp; +use super::ts_estree::TsEsTreeBuilder; + +pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec { + let mut ctx = TsEsTreeBuilder::new(); + + let program = &parsed_source.program(); + + let pos = ctx.header(AstNode::Program, NodeRef(0), &program.span(), 2); + let source_type_pos = ctx.str_field(AstProp::SourceType); + + match program.as_ref() { + Program::Module(module) => { + let body_pos = ctx.ref_vec_field(AstProp::Body, module.body.len()); + + let children = module + .body + .iter() + .map(|item| match item { + ModuleItem::ModuleDecl(module_decl) => { + serialize_module_decl(&mut ctx, module_decl, pos) + } + ModuleItem::Stmt(stmt) => serialize_stmt(&mut ctx, stmt, pos), + }) + .collect::>(); + + ctx.write_str(source_type_pos, "module"); + ctx.write_refs(body_pos, children); + } + Program::Script(script) => { + let body_pos = ctx.ref_vec_field(AstProp::Body, script.body.len()); + let children = script + .body + .iter() + .map(|stmt| serialize_stmt(&mut ctx, stmt, pos)) + .collect::>(); + + ctx.write_str(source_type_pos, "script"); + ctx.write_refs(body_pos, children); + } + } + + ctx.serialize() +} + +fn serialize_module_decl( + ctx: &mut TsEsTreeBuilder, + module_decl: &ModuleDecl, + parent: NodeRef, +) -> NodeRef { + match module_decl { + ModuleDecl::Import(node) => { + ctx.header(AstNode::ImportExpression, parent, &node.span, 0) + } + ModuleDecl::ExportDecl(node) => { + let pos = + ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span, 1); + let decl_pos = ctx.ref_field(AstProp::Declarations); + + let decl = serialize_decl(ctx, &node.decl, pos); + + ctx.write_ref(decl_pos, decl); + + pos + } + ModuleDecl::ExportNamed(node) => { + let id = + ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span, 2); + let src_pos = ctx.ref_field(AstProp::Source); + let spec_pos = + ctx.ref_vec_field(AstProp::Specifiers, node.specifiers.len()); + + // FIXME: Flags + // let mut flags = FlagValue::new(); + // flags.set(Flag::ExportType); + + let src_id = node + .src + .as_ref() + .map(|src| serialize_lit(ctx, &Lit::Str(*src.clone()), id)); + + let spec_ids = node + .specifiers + .iter() + .map(|spec| { + match spec { + ExportSpecifier::Named(child) => { + let spec_pos = + ctx.header(AstNode::ExportSpecifier, id, &child.span, 2); + let local_pos = ctx.ref_field(AstProp::Local); + let exp_pos = ctx.ref_field(AstProp::Exported); + + // let mut flags = FlagValue::new(); + // flags.set(Flag::ExportType); + + let local = + serialize_module_exported_name(ctx, &child.orig, spec_pos); + + let exported = child.exported.as_ref().map(|exported| { + serialize_module_exported_name(ctx, exported, spec_pos) + }); + + // ctx.write_flags(&flags); + ctx.write_ref(local_pos, local); + ctx.write_maybe_ref(exp_pos, exported); + + spec_pos + } + + // These two aren't syntactically valid + ExportSpecifier::Namespace(_) => todo!(), + ExportSpecifier::Default(_) => todo!(), + } + }) + .collect::>(); + + // ctx.write_flags(&flags); + ctx.write_maybe_ref(src_pos, src_id); + ctx.write_refs(spec_pos, spec_ids); + + id + } + ModuleDecl::ExportDefaultDecl(node) => { + ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span, 0) + } + ModuleDecl::ExportDefaultExpr(node) => { + ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span, 0) + } + ModuleDecl::ExportAll(node) => { + ctx.header(AstNode::ExportAllDeclaration, parent, &node.span, 0) + } + ModuleDecl::TsImportEquals(node) => { + ctx.header(AstNode::TsImportEquals, parent, &node.span, 0) + } + ModuleDecl::TsExportAssignment(node) => { + ctx.header(AstNode::TsExportAssignment, parent, &node.span, 0) + } + ModuleDecl::TsNamespaceExport(node) => { + ctx.header(AstNode::TsNamespaceExport, parent, &node.span, 0) + } + } +} + +fn serialize_stmt( + ctx: &mut TsEsTreeBuilder, + stmt: &Stmt, + parent: NodeRef, +) -> NodeRef { + match stmt { + Stmt::Block(node) => { + let pos = ctx.header(AstNode::BlockStatement, parent, &node.span, 1); + let body_pos = ctx.ref_vec_field(AstProp::Body, node.stmts.len()); + + let children = node + .stmts + .iter() + .map(|stmt| serialize_stmt(ctx, stmt, pos)) + .collect::>(); + + ctx.write_refs(body_pos, children); + + pos + } + Stmt::Empty(_) => NodeRef(0), + Stmt::Debugger(node) => { + ctx.header(AstNode::DebuggerStatement, parent, &node.span, 0) + } + Stmt::With(_) => todo!(), + Stmt::Return(node) => { + let pos = ctx.header(AstNode::ReturnStatement, parent, &node.span, 1); + let arg_pos = ctx.ref_field(AstProp::Argument); + + let arg = node.arg.as_ref().map(|arg| serialize_expr(ctx, arg, pos)); + ctx.write_maybe_ref(arg_pos, arg); + + pos + } + Stmt::Labeled(node) => { + let pos = ctx.header(AstNode::LabeledStatement, parent, &node.span, 2); + let label_pos = ctx.ref_field(AstProp::Label); + let body_pos = ctx.ref_field(AstProp::Body); + + let ident = serialize_ident(ctx, &node.label, pos); + let stmt = serialize_stmt(ctx, &node.body, pos); + + ctx.write_ref(label_pos, ident); + ctx.write_ref(body_pos, stmt); + + pos + } + Stmt::Break(node) => { + let pos = ctx.header(AstNode::BreakStatement, parent, &node.span, 1); + let label_pos = ctx.ref_field(AstProp::Label); + + let arg = node + .label + .as_ref() + .map(|label| serialize_ident(ctx, label, pos)); + + ctx.write_maybe_ref(label_pos, arg); + + pos + } + Stmt::Continue(node) => { + let pos = ctx.header(AstNode::ContinueStatement, parent, &node.span, 1); + let label_pos = ctx.ref_field(AstProp::Label); + + let arg = node + .label + .as_ref() + .map(|label| serialize_ident(ctx, label, pos)); + + ctx.write_maybe_ref(label_pos, arg); + + pos + } + Stmt::If(node) => { + let pos = ctx.header(AstNode::IfStatement, parent, &node.span, 3); + let test_pos = ctx.ref_field(AstProp::Test); + let cons_pos = ctx.ref_field(AstProp::Consequent); + let alt_pos = ctx.ref_field(AstProp::Alternate); + + let test = serialize_expr(ctx, node.test.as_ref(), pos); + let cons = serialize_stmt(ctx, node.cons.as_ref(), pos); + let alt = node.alt.as_ref().map(|alt| serialize_stmt(ctx, alt, pos)); + + ctx.write_ref(test_pos, test); + ctx.write_ref(cons_pos, cons); + ctx.write_maybe_ref(alt_pos, alt); + + pos + } + Stmt::Switch(node) => { + let id = ctx.header(AstNode::SwitchStatement, parent, &node.span, 2); + let disc_pos = ctx.ref_field(AstProp::Discriminant); + let cases_pos = ctx.ref_vec_field(AstProp::Cases, node.cases.len()); + + let disc = serialize_expr(ctx, &node.discriminant, id); + + let cases = node + .cases + .iter() + .map(|case| { + let case_pos = ctx.header(AstNode::SwitchCase, id, &case.span, 2); + let test_pos = ctx.ref_field(AstProp::Test); + let cons_pos = + ctx.ref_vec_field(AstProp::Consequent, case.cons.len()); + + let test = case + .test + .as_ref() + .map(|test| serialize_expr(ctx, test, case_pos)); + + let cons = case + .cons + .iter() + .map(|cons| serialize_stmt(ctx, cons, case_pos)) + .collect::>(); + + ctx.write_maybe_ref(test_pos, test); + ctx.write_refs(cons_pos, cons); + + case_pos + }) + .collect::>(); + + ctx.write_ref(disc_pos, disc); + ctx.write_refs(cases_pos, cases); + + id + } + Stmt::Throw(node) => { + let pos = ctx.header(AstNode::ThrowStatement, parent, &node.span, 1); + let arg_pos = ctx.ref_field(AstProp::Argument); + + let arg = serialize_expr(ctx, &node.arg, pos); + ctx.write_ref(arg_pos, arg); + + pos + } + Stmt::Try(node) => { + let pos = ctx.header(AstNode::TryStatement, parent, &node.span, 3); + let block_pos = ctx.ref_field(AstProp::Block); + let handler_pos = ctx.ref_field(AstProp::Handler); + let finalizer_pos = ctx.ref_field(AstProp::Finalizer); + + let block = serialize_stmt(ctx, &Stmt::Block(node.block.clone()), pos); + + let handler = node.handler.as_ref().map(|catch| { + let clause_pos = ctx.header(AstNode::CatchClause, pos, &catch.span, 2); + let param_pos = ctx.ref_field(AstProp::Param); + let body_pos = ctx.ref_field(AstProp::Body); + + let param = catch + .param + .as_ref() + .map(|param| serialize_pat(ctx, param, clause_pos)); + + let body = + serialize_stmt(ctx, &Stmt::Block(catch.body.clone()), clause_pos); + + ctx.write_maybe_ref(param_pos, param); + ctx.write_ref(body_pos, body); + + clause_pos + }); + + let finalizer = node.finalizer.as_ref().map(|finalizer| { + serialize_stmt(ctx, &Stmt::Block(finalizer.clone()), pos) + }); + + ctx.write_ref(block_pos, block); + ctx.write_maybe_ref(handler_pos, handler); + ctx.write_maybe_ref(finalizer_pos, finalizer); + + pos + } + Stmt::While(node) => { + let pos = ctx.header(AstNode::WhileStatement, parent, &node.span, 2); + let test_pos = ctx.ref_field(AstProp::Test); + let body_pos = ctx.ref_field(AstProp::Body); + + let test = serialize_expr(ctx, node.test.as_ref(), pos); + let stmt = serialize_stmt(ctx, node.body.as_ref(), pos); + + ctx.write_ref(test_pos, test); + ctx.write_ref(body_pos, stmt); + + pos + } + Stmt::DoWhile(node) => { + let pos = ctx.header(AstNode::DoWhileStatement, parent, &node.span, 2); + let test_pos = ctx.ref_field(AstProp::Test); + let body_pos = ctx.ref_field(AstProp::Body); + + let expr = serialize_expr(ctx, node.test.as_ref(), pos); + let stmt = serialize_stmt(ctx, node.body.as_ref(), pos); + + ctx.write_ref(test_pos, expr); + ctx.write_ref(body_pos, stmt); + + pos + } + Stmt::For(node) => { + let pos = ctx.header(AstNode::ForStatement, parent, &node.span, 4); + let init_pos = ctx.ref_field(AstProp::Init); + let test_pos = ctx.ref_field(AstProp::Test); + let update_pos = ctx.ref_field(AstProp::Update); + let body_pos = ctx.ref_field(AstProp::Body); + + let init = node.init.as_ref().map(|init| match init { + VarDeclOrExpr::VarDecl(var_decl) => { + serialize_stmt(ctx, &Stmt::Decl(Decl::Var(var_decl.clone())), pos) + } + VarDeclOrExpr::Expr(expr) => serialize_expr(ctx, expr, pos), + }); + + let test = node + .test + .as_ref() + .map(|expr| serialize_expr(ctx, expr, pos)); + let update = node + .update + .as_ref() + .map(|expr| serialize_expr(ctx, expr, pos)); + let body = serialize_stmt(ctx, node.body.as_ref(), pos); + + ctx.write_maybe_ref(init_pos, init); + ctx.write_maybe_ref(test_pos, test); + ctx.write_maybe_ref(update_pos, update); + ctx.write_ref(body_pos, body); + + pos + } + Stmt::ForIn(node) => { + let pos = ctx.header(AstNode::ForInStatement, parent, &node.span, 3); + let left_pos = ctx.ref_field(AstProp::Left); + let right_pos = ctx.ref_field(AstProp::Right); + let body_pos = ctx.ref_field(AstProp::Body); + + let left = serialize_for_head(ctx, &node.left, pos); + let right = serialize_expr(ctx, node.right.as_ref(), pos); + let body = serialize_stmt(ctx, node.body.as_ref(), pos); + + ctx.write_ref(left_pos, left); + ctx.write_ref(right_pos, right); + ctx.write_ref(body_pos, body); + + pos + } + Stmt::ForOf(node) => { + let pos = ctx.header(AstNode::ForOfStatement, parent, &node.span, 4); + let await_pos = ctx.bool_field(AstProp::Await); + let left_pos = ctx.ref_field(AstProp::Left); + let right_pos = ctx.ref_field(AstProp::Right); + let body_pos = ctx.ref_field(AstProp::Body); + + let left = serialize_for_head(ctx, &node.left, pos); + let right = serialize_expr(ctx, node.right.as_ref(), pos); + let body = serialize_stmt(ctx, node.body.as_ref(), pos); + + ctx.write_bool(await_pos, node.is_await); + ctx.write_ref(left_pos, left); + ctx.write_ref(right_pos, right); + ctx.write_ref(body_pos, body); + + pos + } + Stmt::Decl(node) => serialize_decl(ctx, node, parent), + Stmt::Expr(node) => { + let pos = ctx.header(AstNode::ExpressionStatement, parent, &node.span, 1); + let expr_pos = ctx.ref_field(AstProp::Expression); + + let expr = serialize_expr(ctx, node.expr.as_ref(), pos); + ctx.write_ref(expr_pos, expr); + + pos + } + } +} + +fn serialize_expr( + ctx: &mut TsEsTreeBuilder, + expr: &Expr, + parent: NodeRef, +) -> NodeRef { + match expr { + Expr::This(node) => { + ctx.header(AstNode::ThisExpression, parent, &node.span, 0) + } + Expr::Array(node) => { + let pos = ctx.header(AstNode::ArrayExpression, parent, &node.span, 1); + let elems_pos = ctx.ref_vec_field(AstProp::Elements, node.elems.len()); + + let elems = node + .elems + .iter() + .map(|item| { + item + .as_ref() + .map_or(NodeRef(0), |item| serialize_expr_or_spread(ctx, item, pos)) + }) + .collect::>(); + + ctx.write_refs(elems_pos, elems); + + pos + } + Expr::Object(node) => { + let pos = ctx.header(AstNode::ObjectExpression, parent, &node.span, 1); + let props_pos = ctx.ref_vec_field(AstProp::Properties, node.props.len()); + + let prop_ids = node + .props + .iter() + .map(|prop| serialize_prop_or_spread(ctx, prop, pos)) + .collect::>(); + + ctx.write_refs(props_pos, prop_ids); + + pos + } + Expr::Fn(node) => { + let fn_obj = node.function.as_ref(); + + let pos = + ctx.header(AstNode::FunctionExpression, parent, &fn_obj.span, 7); + + let async_pos = ctx.bool_field(AstProp::Async); + let gen_pos = ctx.bool_field(AstProp::Generator); + let id_pos = ctx.ref_field(AstProp::Id); + let tparams_pos = ctx.ref_field(AstProp::TypeParameters); + let params_pos = ctx.ref_vec_field(AstProp::Params, fn_obj.params.len()); + let return_pos = ctx.ref_field(AstProp::ReturnType); + let body_pos = ctx.ref_field(AstProp::Body); + + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, pos)); + + let type_params = + maybe_serialize_ts_type_param(ctx, &fn_obj.type_params, pos); + + let params = fn_obj + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat, pos)) + .collect::>(); + + let return_id = + maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type, pos); + let body = fn_obj + .body + .as_ref() + .map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone()), pos)); + + ctx.write_bool(async_pos, fn_obj.is_async); + ctx.write_bool(gen_pos, fn_obj.is_generator); + ctx.write_maybe_ref(id_pos, ident); + ctx.write_maybe_ref(tparams_pos, type_params); + ctx.write_refs(params_pos, params); + ctx.write_maybe_ref(return_pos, return_id); + ctx.write_maybe_ref(body_pos, body); + + pos + } + Expr::Unary(node) => { + let pos = ctx.header(AstNode::UnaryExpression, parent, &node.span, 2); + let flag_pos = ctx.str_field(AstProp::Operator); + let arg_pos = ctx.ref_field(AstProp::Argument); + + let arg = serialize_expr(ctx, &node.arg, pos); + + ctx.write_str( + flag_pos, + match node.op { + UnaryOp::Minus => "-", + UnaryOp::Plus => "+", + UnaryOp::Bang => "!", + UnaryOp::Tilde => "~", + UnaryOp::TypeOf => "typeof", + UnaryOp::Void => "void", + UnaryOp::Delete => "delete", + }, + ); + ctx.write_ref(arg_pos, arg); + + pos + } + Expr::Update(node) => { + let pos = ctx.header(AstNode::UpdateExpression, parent, &node.span, 3); + let prefix_pos = ctx.bool_field(AstProp::Prefix); + let arg_pos = ctx.ref_field(AstProp::Argument); + let op_ops = ctx.str_field(AstProp::Operator); + + let arg = serialize_expr(ctx, node.arg.as_ref(), pos); + + ctx.write_bool(prefix_pos, node.prefix); + ctx.write_ref(arg_pos, arg); + ctx.write_str( + op_ops, + match node.op { + UpdateOp::PlusPlus => "++", + UpdateOp::MinusMinus => "--", + }, + ); + + pos + } + Expr::Bin(node) => { + let (node_type, flag_str) = match node.op { + BinaryOp::LogicalAnd => (AstNode::LogicalExpression, "&&"), + BinaryOp::LogicalOr => (AstNode::LogicalExpression, "||"), + BinaryOp::NullishCoalescing => (AstNode::LogicalExpression, "??"), + BinaryOp::EqEq => (AstNode::BinaryExpression, "=="), + BinaryOp::NotEq => (AstNode::BinaryExpression, "!="), + BinaryOp::EqEqEq => (AstNode::BinaryExpression, "==="), + BinaryOp::NotEqEq => (AstNode::BinaryExpression, "!="), + BinaryOp::Lt => (AstNode::BinaryExpression, "<"), + BinaryOp::LtEq => (AstNode::BinaryExpression, "<="), + BinaryOp::Gt => (AstNode::BinaryExpression, ">"), + BinaryOp::GtEq => (AstNode::BinaryExpression, ">="), + BinaryOp::LShift => (AstNode::BinaryExpression, "<<"), + BinaryOp::RShift => (AstNode::BinaryExpression, ">>"), + BinaryOp::ZeroFillRShift => (AstNode::BinaryExpression, ">>>"), + BinaryOp::Add => (AstNode::BinaryExpression, "+"), + BinaryOp::Sub => (AstNode::BinaryExpression, "-"), + BinaryOp::Mul => (AstNode::BinaryExpression, "*"), + BinaryOp::Div => (AstNode::BinaryExpression, "/"), + BinaryOp::Mod => (AstNode::BinaryExpression, "%"), + BinaryOp::BitOr => (AstNode::BinaryExpression, "|"), + BinaryOp::BitXor => (AstNode::BinaryExpression, "^"), + BinaryOp::BitAnd => (AstNode::BinaryExpression, "&"), + BinaryOp::In => (AstNode::BinaryExpression, "in"), + BinaryOp::InstanceOf => (AstNode::BinaryExpression, "instanceof"), + BinaryOp::Exp => (AstNode::BinaryExpression, "**"), + }; + + let pos = ctx.header(node_type, parent, &node.span, 3); + let op_pos = ctx.str_field(AstProp::Operator); + let left_pos = ctx.ref_field(AstProp::Left); + let right_pos = ctx.ref_field(AstProp::Right); + + let left_id = serialize_expr(ctx, node.left.as_ref(), pos); + let right_id = serialize_expr(ctx, node.right.as_ref(), pos); + + ctx.write_str(op_pos, flag_str); + ctx.write_ref(left_pos, left_id); + ctx.write_ref(right_pos, right_id); + + pos + } + Expr::Assign(node) => { + let pos = + ctx.header(AstNode::AssignmentExpression, parent, &node.span, 3); + let op_pos = ctx.str_field(AstProp::Operator); + let left_pos = ctx.ref_field(AstProp::Left); + let right_pos = ctx.ref_field(AstProp::Right); + + let left = match &node.left { + AssignTarget::Simple(simple_assign_target) => { + match simple_assign_target { + SimpleAssignTarget::Ident(target) => { + serialize_ident(ctx, &target.id, pos) + } + SimpleAssignTarget::Member(target) => { + serialize_expr(ctx, &Expr::Member(target.clone()), pos) + } + SimpleAssignTarget::SuperProp(target) => { + serialize_expr(ctx, &Expr::SuperProp(target.clone()), pos) + } + SimpleAssignTarget::Paren(target) => { + serialize_expr(ctx, &target.expr, pos) + } + SimpleAssignTarget::OptChain(target) => { + serialize_expr(ctx, &Expr::OptChain(target.clone()), pos) + } + SimpleAssignTarget::TsAs(target) => { + serialize_expr(ctx, &Expr::TsAs(target.clone()), pos) + } + SimpleAssignTarget::TsSatisfies(target) => { + serialize_expr(ctx, &Expr::TsSatisfies(target.clone()), pos) + } + SimpleAssignTarget::TsNonNull(target) => { + serialize_expr(ctx, &Expr::TsNonNull(target.clone()), pos) + } + SimpleAssignTarget::TsTypeAssertion(target) => { + serialize_expr(ctx, &Expr::TsTypeAssertion(target.clone()), pos) + } + SimpleAssignTarget::TsInstantiation(target) => { + serialize_expr(ctx, &Expr::TsInstantiation(target.clone()), pos) + } + SimpleAssignTarget::Invalid(_) => unreachable!(), + } + } + AssignTarget::Pat(target) => match target { + AssignTargetPat::Array(array_pat) => { + serialize_pat(ctx, &Pat::Array(array_pat.clone()), pos) + } + AssignTargetPat::Object(object_pat) => { + serialize_pat(ctx, &Pat::Object(object_pat.clone()), pos) + } + AssignTargetPat::Invalid(_) => unreachable!(), + }, + }; + + let right = serialize_expr(ctx, node.right.as_ref(), pos); + + ctx.write_str( + op_pos, + match node.op { + AssignOp::Assign => "=", + AssignOp::AddAssign => "+=", + AssignOp::SubAssign => "-=", + AssignOp::MulAssign => "*=", + AssignOp::DivAssign => "/=", + AssignOp::ModAssign => "%=", + AssignOp::LShiftAssign => "<<=", + AssignOp::RShiftAssign => ">>=", + AssignOp::ZeroFillRShiftAssign => ">>>=", + AssignOp::BitOrAssign => "|=", + AssignOp::BitXorAssign => "^=", + AssignOp::BitAndAssign => "&=", + AssignOp::ExpAssign => "**=", + AssignOp::AndAssign => "&&=", + AssignOp::OrAssign => "||=", + AssignOp::NullishAssign => "??=", + }, + ); + ctx.write_ref(left_pos, left); + ctx.write_ref(right_pos, right); + + pos + } + Expr::Member(node) => serialize_member_expr(ctx, node, parent, false), + Expr::SuperProp(node) => { + let pos = ctx.header(AstNode::MemberExpression, parent, &node.span, 3); + let computed_pos = ctx.bool_field(AstProp::Computed); + let obj_pos = ctx.ref_field(AstProp::Object); + let prop_pos = ctx.ref_field(AstProp::Property); + + let obj = ctx.header(AstNode::Super, pos, &node.obj.span, 0); + + let mut computed = false; + let prop = match &node.prop { + SuperProp::Ident(ident_name) => { + serialize_ident_name(ctx, ident_name, pos) + } + SuperProp::Computed(prop) => { + computed = true; + serialize_expr(ctx, &prop.expr, pos) + } + }; + + ctx.write_bool(computed_pos, computed); + ctx.write_ref(obj_pos, obj); + ctx.write_ref(prop_pos, prop); + + pos + } + Expr::Cond(node) => { + let pos = + ctx.header(AstNode::ConditionalExpression, parent, &node.span, 3); + let test_pos = ctx.ref_field(AstProp::Test); + let cons_pos = ctx.ref_field(AstProp::Consequent); + let alt_pos = ctx.ref_field(AstProp::Alternate); + + let test = serialize_expr(ctx, node.test.as_ref(), pos); + let cons = serialize_expr(ctx, node.cons.as_ref(), pos); + let alt = serialize_expr(ctx, node.alt.as_ref(), pos); + + ctx.write_ref(test_pos, test); + ctx.write_ref(cons_pos, cons); + ctx.write_ref(alt_pos, alt); + + pos + } + Expr::Call(node) => { + let pos = ctx.header(AstNode::CallExpression, parent, &node.span, 4); + let opt_pos = ctx.bool_field(AstProp::Optional); + let callee_pos = ctx.ref_field(AstProp::Callee); + let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + let args_pos = ctx.ref_vec_field(AstProp::Arguments, node.args.len()); + + let callee = match &node.callee { + Callee::Super(super_node) => { + ctx.header(AstNode::Super, pos, &super_node.span, 0) + } + Callee::Import(_) => todo!(), + Callee::Expr(expr) => serialize_expr(ctx, expr, pos), + }; + + let type_arg = node.type_args.clone().map(|param_node| { + serialize_ts_param_inst(ctx, param_node.as_ref(), pos) + }); + + let args = node + .args + .iter() + .map(|arg| serialize_expr_or_spread(ctx, arg, pos)) + .collect::>(); + + ctx.write_bool(opt_pos, false); + ctx.write_ref(callee_pos, callee); + ctx.write_maybe_ref(type_args_pos, type_arg); + ctx.write_refs(args_pos, args); + + pos + } + Expr::New(node) => { + let pos = ctx.header(AstNode::NewExpression, parent, &node.span, 3); + let callee_pos = ctx.ref_field(AstProp::Callee); + let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + let args_pos = ctx.ref_vec_field( + AstProp::Arguments, + node.args.as_ref().map_or(0, |v| v.len()), + ); + + let callee = serialize_expr(ctx, node.callee.as_ref(), pos); + + let args: Vec = node.args.as_ref().map_or(vec![], |args| { + args + .iter() + .map(|arg| serialize_expr_or_spread(ctx, arg, pos)) + .collect::>() + }); + + let type_args = node.type_args.clone().map(|param_node| { + serialize_ts_param_inst(ctx, param_node.as_ref(), pos) + }); + + ctx.write_ref(callee_pos, callee); + ctx.write_maybe_ref(type_args_pos, type_args); + ctx.write_refs(args_pos, args); + + pos + } + Expr::Seq(node) => { + let pos = ctx.header(AstNode::SequenceExpression, parent, &node.span, 1); + let exprs_pos = ctx.ref_vec_field(AstProp::Expressions, node.exprs.len()); + + let children = node + .exprs + .iter() + .map(|expr| serialize_expr(ctx, expr, pos)) + .collect::>(); + + ctx.write_refs(exprs_pos, children); + + pos + } + Expr::Ident(node) => serialize_ident(ctx, node, parent), + Expr::Lit(node) => serialize_lit(ctx, node, parent), + Expr::Tpl(node) => { + let pos = ctx.header(AstNode::TemplateLiteral, parent, &node.span, 2); + let quasis_pos = ctx.ref_vec_field(AstProp::Quasis, node.quasis.len()); + let exprs_pos = ctx.ref_vec_field(AstProp::Expressions, node.exprs.len()); + + let quasis = node + .quasis + .iter() + .map(|quasi| { + let tpl_pos = + ctx.header(AstNode::TemplateElement, pos, &quasi.span, 3); + let tail_pos = ctx.bool_field(AstProp::Tail); + let raw_pos = ctx.str_field(AstProp::Raw); + let cooked_pos = ctx.str_field(AstProp::Cooked); + + ctx.write_bool(tail_pos, quasi.tail); + ctx.write_str(raw_pos, &quasi.raw); + ctx.write_str( + cooked_pos, + &quasi + .cooked + .as_ref() + .map_or("".to_string(), |v| v.to_string()), + ); + + tpl_pos + }) + .collect::>(); + + let exprs = node + .exprs + .iter() + .map(|expr| serialize_expr(ctx, expr, pos)) + .collect::>(); + + ctx.write_refs(quasis_pos, quasis); + ctx.write_refs(exprs_pos, exprs); + + pos + } + Expr::TaggedTpl(node) => { + let pos = + ctx.header(AstNode::TaggedTemplateExpression, parent, &node.span, 3); + let tag_pos = ctx.ref_field(AstProp::Tag); + let type_arg_pos = ctx.ref_field(AstProp::TypeArguments); + let quasi_pos = ctx.ref_field(AstProp::Quasi); + + let tag = serialize_expr(ctx, &node.tag, pos); + + let type_param_id = node + .type_params + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref(), pos)); + let quasi = serialize_expr(ctx, &Expr::Tpl(*node.tpl.clone()), pos); + + ctx.write_ref(tag_pos, tag); + ctx.write_maybe_ref(type_arg_pos, type_param_id); + ctx.write_ref(quasi_pos, quasi); + + pos + } + Expr::Arrow(node) => { + let pos = + ctx.header(AstNode::ArrowFunctionExpression, parent, &node.span, 6); + let async_pos = ctx.bool_field(AstProp::Async); + let gen_pos = ctx.bool_field(AstProp::Generator); + let type_param_pos = ctx.ref_field(AstProp::TypeParameters); + let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); + let body_pos = ctx.ref_field(AstProp::Body); + let return_type_pos = ctx.ref_field(AstProp::ReturnType); + + let type_param = + maybe_serialize_ts_type_param(ctx, &node.type_params, pos); + + let params = node + .params + .iter() + .map(|param| serialize_pat(ctx, param, pos)) + .collect::>(); + + let body = match node.body.as_ref() { + BlockStmtOrExpr::BlockStmt(block_stmt) => { + serialize_stmt(ctx, &Stmt::Block(block_stmt.clone()), pos) + } + BlockStmtOrExpr::Expr(expr) => serialize_expr(ctx, expr.as_ref(), pos), + }; + + let return_type = + maybe_serialize_ts_type_ann(ctx, &node.return_type, pos); + + ctx.write_bool(async_pos, node.is_async); + ctx.write_bool(gen_pos, node.is_generator); + ctx.write_maybe_ref(type_param_pos, type_param); + ctx.write_refs(params_pos, params); + ctx.write_ref(body_pos, body); + ctx.write_maybe_ref(return_type_pos, return_type); + + pos + } + Expr::Class(node) => { + // FIXME + ctx.header(AstNode::ClassExpression, parent, &node.class.span, 0) + } + Expr::Yield(node) => { + let pos = ctx.header(AstNode::YieldExpression, parent, &node.span, 2); + let delegate_pos = ctx.bool_field(AstProp::Delegate); + let arg_pos = ctx.ref_field(AstProp::Argument); + + let arg = node + .arg + .as_ref() + .map(|arg| serialize_expr(ctx, arg.as_ref(), pos)); + + ctx.write_bool(delegate_pos, node.delegate); + ctx.write_maybe_ref(arg_pos, arg); + + pos + } + Expr::MetaProp(node) => { + ctx.header(AstNode::MetaProp, parent, &node.span, 0) + } + Expr::Await(node) => { + let pos = ctx.header(AstNode::AwaitExpression, parent, &node.span, 1); + let arg_pos = ctx.ref_field(AstProp::Argument); + + let arg = serialize_expr(ctx, node.arg.as_ref(), pos); + + ctx.write_ref(arg_pos, arg); + + pos + } + Expr::Paren(node) => { + // Paren nodes are treated as a syntax only thing in TSEStree + // and are never materialized to actual AST nodes. + serialize_expr(ctx, &node.expr, parent) + } + Expr::JSXMember(node) => serialize_jsx_member_expr(ctx, node, parent), + Expr::JSXNamespacedName(node) => { + serialize_jsx_namespaced_name(ctx, node, parent) + } + Expr::JSXEmpty(node) => serialize_jsx_empty_expr(ctx, node, parent), + Expr::JSXElement(node) => serialize_jsx_element(ctx, node, parent), + Expr::JSXFragment(node) => serialize_jsx_fragment(ctx, node, parent), + Expr::TsTypeAssertion(node) => { + let pos = ctx.header(AstNode::TSTypeAssertion, parent, &node.span, 2); + let expr_pos = ctx.ref_field(AstProp::Expression); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let expr = serialize_expr(ctx, &node.expr, parent); + let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); + + ctx.write_ref(expr_pos, expr); + ctx.write_ref(type_ann_pos, type_ann); + + pos + } + Expr::TsConstAssertion(node) => { + let pos = ctx.header(AstNode::TsConstAssertion, parent, &node.span, 1); + let arg_pos = ctx.ref_field(AstProp::Argument); + let arg = serialize_expr(ctx, node.expr.as_ref(), pos); + + // FIXME + ctx.write_ref(arg_pos, arg); + + pos + } + Expr::TsNonNull(node) => { + let pos = ctx.header(AstNode::TSNonNullExpression, parent, &node.span, 1); + let expr_pos = ctx.ref_field(AstProp::Expression); + + let expr_id = serialize_expr(ctx, node.expr.as_ref(), pos); + + ctx.write_ref(expr_pos, expr_id); + + pos + } + Expr::TsAs(node) => { + let id = ctx.header(AstNode::TSAsExpression, parent, &node.span, 2); + let expr_pos = ctx.ref_field(AstProp::Expression); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let expr = serialize_expr(ctx, node.expr.as_ref(), id); + let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref(), id); + + ctx.write_ref(expr_pos, expr); + ctx.write_ref(type_ann_pos, type_ann); + + id + } + Expr::TsInstantiation(node) => { + let pos = ctx.header(AstNode::TsInstantiation, parent, &node.span, 1); + let expr_pos = ctx.ref_field(AstProp::Expression); + let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + + let expr = serialize_expr(ctx, node.expr.as_ref(), pos); + + let type_arg = serialize_ts_param_inst(ctx, node.type_args.as_ref(), pos); + + ctx.write_ref(expr_pos, expr); + ctx.write_ref(type_args_pos, type_arg); + + pos + } + Expr::TsSatisfies(node) => { + let pos = + ctx.header(AstNode::TSSatisfiesExpression, parent, &node.span, 2); + let expr_pos = ctx.ref_field(AstProp::Expression); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let epxr = serialize_expr(ctx, node.expr.as_ref(), pos); + let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref(), pos); + + ctx.write_ref(expr_pos, epxr); + ctx.write_ref(type_ann_pos, type_ann); + + pos + } + Expr::PrivateName(node) => serialize_private_name(ctx, node, parent), + Expr::OptChain(node) => { + let pos = ctx.header(AstNode::ChainExpression, parent, &node.span, 1); + let arg_pos = ctx.ref_field(AstProp::Argument); + + let arg = match node.base.as_ref() { + OptChainBase::Member(member_expr) => { + serialize_member_expr(ctx, member_expr, pos, true) + } + OptChainBase::Call(opt_call) => { + let call_pos = + ctx.header(AstNode::CallExpression, pos, &opt_call.span, 4); + let opt_pos = ctx.bool_field(AstProp::Optional); + let callee_pos = ctx.ref_field(AstProp::Callee); + let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + let args_pos = + ctx.ref_vec_field(AstProp::Arguments, opt_call.args.len()); + + let callee = serialize_expr(ctx, &opt_call.callee, pos); + + let type_param_id = opt_call.type_args.clone().map(|params| { + serialize_ts_param_inst(ctx, params.as_ref(), call_pos) + }); + + let args = opt_call + .args + .iter() + .map(|arg| serialize_expr_or_spread(ctx, arg, pos)) + .collect::>(); + + ctx.write_bool(opt_pos, true); + ctx.write_ref(callee_pos, callee); + ctx.write_maybe_ref(type_args_pos, type_param_id); + ctx.write_refs(args_pos, args); + + call_pos + } + }; + + ctx.write_ref(arg_pos, arg); + + pos + } + Expr::Invalid(_) => { + unreachable!() + } + } +} + +fn serialize_prop_or_spread( + ctx: &mut TsEsTreeBuilder, + prop: &PropOrSpread, + parent: NodeRef, +) -> NodeRef { + match prop { + PropOrSpread::Spread(spread_element) => serialize_spread( + ctx, + spread_element.expr.as_ref(), + &spread_element.dot3_token, + parent, + ), + PropOrSpread::Prop(prop) => { + let pos = ctx.header(AstNode::Property, parent, &prop.span(), 6); + + let shorthand_pos = ctx.bool_field(AstProp::Shorthand); + let computed_pos = ctx.bool_field(AstProp::Computed); + let method_pos = ctx.bool_field(AstProp::Method); + let kind_pos = ctx.str_field(AstProp::Kind); + let key_pos = ctx.ref_field(AstProp::Key); + let value_pos = ctx.ref_field(AstProp::Value); + + let mut shorthand = false; + let mut computed = false; + let mut method = false; + let mut kind = "init"; + + // FIXME: optional + let (key_id, value_id) = match prop.as_ref() { + Prop::Shorthand(ident) => { + shorthand = true; + + let value = serialize_ident(ctx, ident, pos); + (value, value) + } + Prop::KeyValue(key_value_prop) => { + if let PropName::Computed(_) = key_value_prop.key { + computed = true; + } + + let key = serialize_prop_name(ctx, &key_value_prop.key, pos); + let value = serialize_expr(ctx, key_value_prop.value.as_ref(), pos); + + (key, value) + } + Prop::Assign(assign_prop) => { + let child_id = + ctx.header(AstNode::AssignmentPattern, pos, &assign_prop.span, 2); + let left_pos = ctx.ref_field(AstProp::Left); + let right_pos = ctx.ref_field(AstProp::Right); + + let left = serialize_ident(ctx, &assign_prop.key, child_id); + let right = serialize_expr(ctx, assign_prop.value.as_ref(), child_id); + + ctx.write_ref(left_pos, left); + ctx.write_ref(right_pos, right); + + (left, child_id) + } + Prop::Getter(getter_prop) => { + kind = "get"; + + let key = serialize_prop_name(ctx, &getter_prop.key, pos); + + let value = serialize_expr( + ctx, + &Expr::Fn(FnExpr { + ident: None, + function: Box::new(Function { + params: vec![], + decorators: vec![], + span: getter_prop.span, + ctxt: SyntaxContext::empty(), + body: getter_prop.body.clone(), + is_generator: false, + is_async: false, + type_params: None, // FIXME + return_type: None, + }), + }), + pos, + ); + + (key, value) + } + Prop::Setter(setter_prop) => { + kind = "set"; + + let key_id = serialize_prop_name(ctx, &setter_prop.key, pos); + + let param = Param::from(*setter_prop.param.clone()); + + let value_id = serialize_expr( + ctx, + &Expr::Fn(FnExpr { + ident: None, + function: Box::new(Function { + params: vec![param], + decorators: vec![], + span: setter_prop.span, + ctxt: SyntaxContext::empty(), + body: setter_prop.body.clone(), + is_generator: false, + is_async: false, + type_params: None, + return_type: None, + }), + }), + pos, + ); + + (key_id, value_id) + } + Prop::Method(method_prop) => { + method = true; + + let key_id = serialize_prop_name(ctx, &method_prop.key, pos); + + let value_id = serialize_expr( + ctx, + &Expr::Fn(FnExpr { + ident: None, + function: method_prop.function.clone(), + }), + pos, + ); + + (key_id, value_id) + } + }; + + ctx.write_bool(shorthand_pos, shorthand); + ctx.write_bool(computed_pos, computed); + ctx.write_bool(method_pos, method); + ctx.write_str(kind_pos, kind); + ctx.write_ref(key_pos, key_id); + ctx.write_ref(value_pos, value_id); + + pos + } + } +} + +fn serialize_member_expr( + ctx: &mut TsEsTreeBuilder, + node: &MemberExpr, + parent: NodeRef, + optional: bool, +) -> NodeRef { + let pos = ctx.header(AstNode::MemberExpression, parent, &node.span, 4); + let opt_pos = ctx.bool_field(AstProp::Optional); + let computed_pos = ctx.bool_field(AstProp::Computed); + let obj_pos = ctx.ref_field(AstProp::Object); + let prop_pos = ctx.ref_field(AstProp::Property); + + let obj = serialize_expr(ctx, node.obj.as_ref(), pos); + + let mut computed = false; + + let prop = match &node.prop { + MemberProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name, pos), + MemberProp::PrivateName(private_name) => { + serialize_private_name(ctx, private_name, pos) + } + MemberProp::Computed(computed_prop_name) => { + computed = true; + serialize_expr(ctx, computed_prop_name.expr.as_ref(), pos) + } + }; + + ctx.write_bool(opt_pos, optional); + ctx.write_bool(computed_pos, computed); + ctx.write_ref(obj_pos, obj); + ctx.write_ref(prop_pos, prop); + + pos +} + +fn serialize_class_member( + ctx: &mut TsEsTreeBuilder, + member: &ClassMember, + parent: NodeRef, +) -> NodeRef { + match member { + ClassMember::Constructor(constructor) => { + let member_id = + ctx.header(AstNode::MethodDefinition, parent, &constructor.span, 3); + let key_pos = ctx.ref_field(AstProp::Key); + let body_pos = ctx.ref_field(AstProp::Body); + let args_pos = + ctx.ref_vec_field(AstProp::Arguments, constructor.params.len()); + let acc_pos = if constructor.accessibility.is_some() { + NodePos::Str(ctx.str_field(AstProp::Accessibility)) + } else { + NodePos::Undef(ctx.undefined_field(AstProp::Accessibility)) + }; + + // FIXME flags + + let key = serialize_prop_name(ctx, &constructor.key, member_id); + let body = constructor + .body + .as_ref() + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()), member_id)); + + let params = constructor + .params + .iter() + .map(|param| match param { + ParamOrTsParamProp::TsParamProp(_) => { + todo!() + } + ParamOrTsParamProp::Param(param) => { + serialize_pat(ctx, ¶m.pat, member_id) + } + }) + .collect::>(); + + if let Some(acc) = constructor.accessibility { + if let NodePos::Str(str_pos) = acc_pos { + ctx.write_str(str_pos, &accessibility_to_str(acc)); + } + } + + ctx.write_ref(key_pos, key); + ctx.write_maybe_ref(body_pos, body); + // FIXME + ctx.write_refs(args_pos, params); + + member_id + } + ClassMember::Method(method) => { + let member_id = + ctx.header(AstNode::MethodDefinition, parent, &method.span, 0); + + // let mut flags = FlagValue::new(); + // flags.set(Flag::ClassMethod); + if method.function.is_async { + // FIXME + } + + // accessibility_to_flag(&mut flags, method.accessibility); + + let _key_id = serialize_prop_name(ctx, &method.key, member_id); + + let _body_id = + method.function.body.as_ref().map(|body| { + serialize_stmt(ctx, &Stmt::Block(body.clone()), member_id) + }); + + let _params = method + .function + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat, member_id)) + .collect::>(); + + // ctx.write_node(member_id, ); + // ctx.write_flags(&flags); + // ctx.write_id(key_id); + // ctx.write_id(body_id); + // ctx.write_ids(AstProp::Params, params); + + member_id + } + ClassMember::PrivateMethod(_) => todo!(), + ClassMember::ClassProp(_) => todo!(), + ClassMember::PrivateProp(_) => todo!(), + ClassMember::TsIndexSignature(member) => { + serialize_ts_index_sig(ctx, member, parent) + } + ClassMember::Empty(_) => unreachable!(), + ClassMember::StaticBlock(_) => todo!(), + ClassMember::AutoAccessor(_) => todo!(), + } +} + +fn serialize_expr_or_spread( + ctx: &mut TsEsTreeBuilder, + arg: &ExprOrSpread, + parent: NodeRef, +) -> NodeRef { + if let Some(spread) = &arg.spread { + serialize_spread(ctx, &arg.expr, spread, parent) + } else { + serialize_expr(ctx, arg.expr.as_ref(), parent) + } +} + +fn serialize_ident( + ctx: &mut TsEsTreeBuilder, + ident: &Ident, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::Identifier, parent, &ident.span, 1); + let name_pos = ctx.str_field(AstProp::Name); + ctx.write_str(name_pos, ident.sym.as_str()); + + pos +} + +fn serialize_module_exported_name( + ctx: &mut TsEsTreeBuilder, + name: &ModuleExportName, + parent: NodeRef, +) -> NodeRef { + match &name { + ModuleExportName::Ident(ident) => serialize_ident(ctx, ident, parent), + ModuleExportName::Str(lit) => { + serialize_lit(ctx, &Lit::Str(lit.clone()), parent) + } + } +} + +fn serialize_decl( + ctx: &mut TsEsTreeBuilder, + decl: &Decl, + parent: NodeRef, +) -> NodeRef { + match decl { + Decl::Class(node) => { + let id = + ctx.header(AstNode::ClassDeclaration, parent, &node.class.span, 8); + let declare_pos = ctx.bool_field(AstProp::Declare); + let abstract_pos = ctx.bool_field(AstProp::Abstract); + let id_pos = ctx.ref_field(AstProp::Id); + let body_pos = ctx.ref_field(AstProp::Body); + let type_params_pos = ctx.ref_field(AstProp::TypeParameters); + let super_pos = ctx.ref_field(AstProp::SuperClass); + let super_type_pos = ctx.ref_field(AstProp::SuperTypeArguments); + let impl_pos = + ctx.ref_vec_field(AstProp::Implements, node.class.implements.len()); + + let body_id = ctx.header(AstNode::ClassBody, id, &node.class.span, 1); + let body_body_pos = + ctx.ref_vec_field(AstProp::Body, node.class.body.len()); + + let ident = serialize_ident(ctx, &node.ident, id); + let type_params = + maybe_serialize_ts_type_param(ctx, &node.class.type_params, id); + + let super_class = node + .class + .super_class + .as_ref() + .map(|super_class| serialize_expr(ctx, super_class, id)); + + let super_type_params = node + .class + .super_type_params + .as_ref() + .map(|super_params| serialize_ts_param_inst(ctx, super_params, id)); + + let implement_ids = node + .class + .implements + .iter() + .map(|implements| { + let child_pos = + ctx.header(AstNode::TSClassImplements, id, &implements.span, 2); + + let expr_pos = ctx.ref_field(AstProp::Expression); + let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + + let type_args = implements + .type_args + .clone() + .map(|args| serialize_ts_param_inst(ctx, &args, child_pos)); + + let expr = serialize_expr(ctx, &implements.expr, child_pos); + + ctx.write_ref(expr_pos, expr); + ctx.write_maybe_ref(type_args_pos, type_args); + + child_pos + }) + .collect::>(); + + let member_ids = node + .class + .body + .iter() + .map(|member| serialize_class_member(ctx, member, parent)) + .collect::>(); + + ctx.write_ref(body_pos, body_id); + + ctx.write_bool(declare_pos, node.declare); + ctx.write_bool(abstract_pos, node.class.is_abstract); + ctx.write_ref(id_pos, ident); + ctx.write_maybe_ref(type_params_pos, type_params); + ctx.write_maybe_ref(super_pos, super_class); + ctx.write_maybe_ref(super_type_pos, super_type_params); + ctx.write_refs(impl_pos, implement_ids); + + // body + ctx.write_refs(body_body_pos, member_ids); + + id + } + Decl::Fn(node) => { + let pos = ctx.header( + AstNode::FunctionDeclaration, + parent, + &node.function.span, + 8, + ); + let declare_pos = ctx.bool_field(AstProp::Declare); + let async_pos = ctx.bool_field(AstProp::Async); + let gen_pos = ctx.bool_field(AstProp::Generator); + let id_pos = ctx.ref_field(AstProp::Id); + let type_params_pos = ctx.ref_field(AstProp::TypeParameters); + let return_pos = ctx.ref_field(AstProp::ReturnType); + let body_pos = ctx.ref_field(AstProp::Body); + let params_pos = + ctx.ref_vec_field(AstProp::Params, node.function.params.len()); + + let ident_id = serialize_ident(ctx, &node.ident, parent); + let type_param_id = + maybe_serialize_ts_type_param(ctx, &node.function.type_params, pos); + let return_type = + maybe_serialize_ts_type_ann(ctx, &node.function.return_type, pos); + + let body = node + .function + .body + .as_ref() + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()), pos)); + + let params = node + .function + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat, pos)) + .collect::>(); + + ctx.write_bool(declare_pos, node.declare); + ctx.write_bool(async_pos, node.function.is_async); + ctx.write_bool(gen_pos, node.function.is_generator); + ctx.write_ref(id_pos, ident_id); + ctx.write_maybe_ref(type_params_pos, type_param_id); + ctx.write_maybe_ref(return_pos, return_type); + ctx.write_maybe_ref(body_pos, body); + ctx.write_refs(params_pos, params); + + pos + } + Decl::Var(node) => { + let id = ctx.header(AstNode::VariableDeclaration, parent, &node.span, 3); + let declare_pos = ctx.bool_field(AstProp::Declare); + let kind_pos = ctx.str_field(AstProp::Kind); + let decls_pos = + ctx.ref_vec_field(AstProp::Declarations, node.decls.len()); + + let children = node + .decls + .iter() + .map(|decl| { + let child_id = + ctx.header(AstNode::VariableDeclarator, id, &decl.span, 2); + let id_pos = ctx.ref_field(AstProp::Id); + let init_pos = ctx.ref_field(AstProp::Init); + + // FIXME: Definite? + + let ident = serialize_pat(ctx, &decl.name, child_id); + + let init = decl + .init + .as_ref() + .map(|init| serialize_expr(ctx, init.as_ref(), child_id)); + + ctx.write_ref(id_pos, ident); + ctx.write_maybe_ref(init_pos, init); + + child_id + }) + .collect::>(); + + ctx.write_bool(declare_pos, node.declare); + ctx.write_str( + kind_pos, + match node.kind { + VarDeclKind::Var => "var", + VarDeclKind::Let => "let", + VarDeclKind::Const => "const", + }, + ); + ctx.write_refs(decls_pos, children); + + id + } + Decl::Using(_) => { + todo!(); + } + Decl::TsInterface(node) => { + let pos = ctx.header(AstNode::TSInterface, parent, &node.span, 0); + let declare_pos = ctx.bool_field(AstProp::Declare); + let id_pos = ctx.ref_field(AstProp::Id); + let extends_pos = ctx.ref_vec_field(AstProp::Extends, node.extends.len()); + let type_param_pos = ctx.ref_field(AstProp::TypeParameters); + let body_pos = ctx.ref_field(AstProp::Body); + + let body_id = + ctx.header(AstNode::TSInterfaceBody, pos, &node.body.span, 0); + let body_body_pos = + ctx.ref_vec_field(AstProp::Body, node.body.body.len()); + + let ident_id = serialize_ident(ctx, &node.id, pos); + let type_param = + maybe_serialize_ts_type_param(ctx, &node.type_params, pos); + + let extend_ids = node + .extends + .iter() + .map(|item| { + let child_pos = + ctx.header(AstNode::TSInterfaceHeritage, pos, &item.span, 1); + let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + let expr_pos = ctx.ref_field(AstProp::Expression); + + let expr = serialize_expr(ctx, &item.expr, child_pos); + let type_args = item.type_args.clone().map(|params| { + serialize_ts_param_inst(ctx, params.as_ref(), child_pos) + }); + + ctx.write_ref(expr_pos, expr); + ctx.write_maybe_ref(type_args_pos, type_args); + + child_pos + }) + .collect::>(); + + let body_elem_ids = node + .body + .body + .iter() + .map(|item| match item { + TsTypeElement::TsCallSignatureDecl(ts_call) => { + let item_id = ctx.header( + AstNode::TsCallSignatureDeclaration, + pos, + &ts_call.span, + 3, + ); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let params_pos = + ctx.ref_vec_field(AstProp::Params, ts_call.params.len()); + let return_pos = ctx.ref_field(AstProp::ReturnType); + + let type_param = + maybe_serialize_ts_type_param(ctx, &ts_call.type_params, pos); + let return_type = + maybe_serialize_ts_type_ann(ctx, &ts_call.type_ann, pos); + let params = ts_call + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param, pos)) + .collect::>(); + + ctx.write_maybe_ref(type_ann_pos, type_param); + ctx.write_refs(params_pos, params); + ctx.write_maybe_ref(return_pos, return_type); + + item_id + } + TsTypeElement::TsConstructSignatureDecl(_) => todo!(), + TsTypeElement::TsPropertySignature(sig) => { + let item_pos = + ctx.header(AstNode::TSPropertySignature, pos, &sig.span, 6); + + let computed_pos = ctx.bool_field(AstProp::Computed); + let optional_pos = ctx.bool_field(AstProp::Optional); + let readonly_pos = ctx.bool_field(AstProp::Readonly); + // TODO: where is this coming from? + let _static_bos = ctx.bool_field(AstProp::Static); + let key_pos = ctx.ref_field(AstProp::Key); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let key = serialize_expr(ctx, &sig.key, item_pos); + let type_ann = + maybe_serialize_ts_type_ann(ctx, &sig.type_ann, item_pos); + + ctx.write_bool(computed_pos, sig.computed); + ctx.write_bool(optional_pos, sig.optional); + ctx.write_bool(readonly_pos, sig.readonly); + ctx.write_ref(key_pos, key); + ctx.write_maybe_ref(type_ann_pos, type_ann); + + item_pos + } + TsTypeElement::TsGetterSignature(sig) => { + let item_pos = + ctx.header(AstNode::TSMethodSignature, pos, &sig.span, 6); + let computed_pos = ctx.bool_field(AstProp::Computed); + let optional_pos = ctx.bool_field(AstProp::Optional); + let readonly_pos = ctx.bool_field(AstProp::Readonly); + // TODO: where is this coming from? + let _static_bos = ctx.bool_field(AstProp::Static); + let kind_pos = ctx.str_field(AstProp::Kind); + let key_pos = ctx.ref_field(AstProp::Key); + let return_type_pos = ctx.ref_field(AstProp::ReturnType); + + let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); + let return_type = + maybe_serialize_ts_type_ann(ctx, &sig.type_ann, item_pos); + + ctx.write_bool(computed_pos, false); + ctx.write_bool(optional_pos, false); + ctx.write_bool(readonly_pos, false); + ctx.write_str(kind_pos, "getter"); + ctx.write_maybe_ref(return_type_pos, return_type); + ctx.write_ref(key_pos, key); + + item_pos + } + TsTypeElement::TsSetterSignature(sig) => { + let item_pos = + ctx.header(AstNode::TSMethodSignature, pos, &sig.span, 6); + let computed_pos = ctx.bool_field(AstProp::Computed); + let optional_pos = ctx.bool_field(AstProp::Optional); + let readonly_pos = ctx.bool_field(AstProp::Readonly); + // TODO: where is this coming from? + let _static_bos = ctx.bool_field(AstProp::Static); + let kind_pos = ctx.str_field(AstProp::Kind); + let key_pos = ctx.ref_field(AstProp::Key); + let params_pos = ctx.ref_vec_field(AstProp::Params, 1); + + let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); + let params = serialize_ts_fn_param(ctx, &sig.param, item_pos); + + ctx.write_bool(computed_pos, false); + ctx.write_bool(optional_pos, false); + ctx.write_bool(readonly_pos, false); + ctx.write_str(kind_pos, "setter"); + ctx.write_ref(key_pos, key); + ctx.write_refs(params_pos, vec![params]); + + item_pos + } + TsTypeElement::TsMethodSignature(sig) => { + let item_pos = + ctx.header(AstNode::TSMethodSignature, pos, &sig.span, 8); + let computed_pos = ctx.bool_field(AstProp::Computed); + let optional_pos = ctx.bool_field(AstProp::Optional); + let readonly_pos = ctx.bool_field(AstProp::Readonly); + // TODO: where is this coming from? + let _static_bos = ctx.bool_field(AstProp::Static); + let kind_pos = ctx.str_field(AstProp::Kind); + let key_pos = ctx.ref_field(AstProp::Key); + let params_pos = + ctx.ref_vec_field(AstProp::Params, sig.params.len()); + let return_type_pos = ctx.ref_field(AstProp::ReturnType); + + let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); + let params = sig + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param, item_pos)) + .collect::>(); + let return_type = + maybe_serialize_ts_type_ann(ctx, &sig.type_ann, item_pos); + + ctx.write_bool(computed_pos, false); + ctx.write_bool(optional_pos, false); + ctx.write_bool(readonly_pos, false); + ctx.write_str(kind_pos, "method"); + ctx.write_ref(key_pos, key); + ctx.write_refs(params_pos, params); + ctx.write_maybe_ref(return_type_pos, return_type); + + item_pos + } + TsTypeElement::TsIndexSignature(sig) => { + serialize_ts_index_sig(ctx, sig, pos) + } + }) + .collect::>(); + + ctx.write_bool(declare_pos, node.declare); + ctx.write_ref(id_pos, ident_id); + ctx.write_maybe_ref(type_param_pos, type_param); + ctx.write_refs(extends_pos, extend_ids); + ctx.write_ref(body_pos, body_id); + + // Body + ctx.write_refs(body_body_pos, body_elem_ids); + + pos + } + Decl::TsTypeAlias(node) => { + let pos = ctx.header(AstNode::TsTypeAlias, parent, &node.span, 4); + let declare_pos = ctx.bool_field(AstProp::Declare); + let id_pos = ctx.ref_field(AstProp::Id); + let type_params_pos = ctx.ref_field(AstProp::TypeParameters); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let ident = serialize_ident(ctx, &node.id, pos); + let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); + let type_param = + maybe_serialize_ts_type_param(ctx, &node.type_params, pos); + + ctx.write_bool(declare_pos, node.declare); + ctx.write_ref(id_pos, ident); + ctx.write_maybe_ref(type_params_pos, type_param); + ctx.write_ref(type_ann_pos, type_ann); + + pos + } + Decl::TsEnum(node) => { + let pos = ctx.header(AstNode::TSEnumDeclaration, parent, &node.span, 3); + let declare_pos = ctx.bool_field(AstProp::Declare); + let const_pos = ctx.bool_field(AstProp::Const); + let id_pos = ctx.ref_field(AstProp::Id); + let body_pos = ctx.ref_field(AstProp::Body); + + let body = ctx.header(AstNode::TSEnumBody, pos, &node.span, 1); + let members_pos = ctx.ref_vec_field(AstProp::Members, node.members.len()); + + let ident_id = serialize_ident(ctx, &node.id, parent); + + let members = node + .members + .iter() + .map(|member| { + let member_id = + ctx.header(AstNode::TSEnumMember, body, &member.span, 2); + let id_pos = ctx.ref_field(AstProp::Id); + let init_pos = ctx.ref_field(AstProp::Initializer); + + let ident = match &member.id { + TsEnumMemberId::Ident(ident) => { + serialize_ident(ctx, ident, member_id) + } + TsEnumMemberId::Str(lit_str) => { + serialize_lit(ctx, &Lit::Str(lit_str.clone()), member_id) + } + }; + + let init = member + .init + .as_ref() + .map(|init| serialize_expr(ctx, init, member_id)); + + ctx.write_ref(id_pos, ident); + ctx.write_maybe_ref(init_pos, init); + + member_id + }) + .collect::>(); + + ctx.write_refs(members_pos, members); + + ctx.write_bool(declare_pos, node.declare); + ctx.write_bool(const_pos, node.is_const); + ctx.write_ref(id_pos, ident_id); + ctx.write_ref(body_pos, body); + + pos + } + Decl::TsModule(ts_module_decl) => { + ctx.header(AstNode::TsModule, parent, &ts_module_decl.span, 0) + } + } +} + +fn serialize_ts_index_sig( + ctx: &mut TsEsTreeBuilder, + node: &TsIndexSignature, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::TSMethodSignature, parent, &node.span, 4); + let readonly_pos = ctx.bool_field(AstProp::Readonly); + // TODO: where is this coming from? + let static_pos = ctx.bool_field(AstProp::Static); + let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + + let params = node + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param, pos)) + .collect::>(); + + ctx.write_bool(readonly_pos, false); + ctx.write_bool(static_pos, node.is_static); + ctx.write_refs(params_pos, params); + ctx.write_maybe_ref(type_ann_pos, type_ann); + + pos +} + +fn accessibility_to_str(accessibility: Accessibility) -> String { + match accessibility { + Accessibility::Public => "public".to_string(), + Accessibility::Protected => "protected".to_string(), + Accessibility::Private => "private".to_string(), + } +} + +fn serialize_private_name( + ctx: &mut TsEsTreeBuilder, + node: &PrivateName, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::PrivateIdentifier, parent, &node.span, 1); + let name_pos = ctx.str_field(AstProp::Name); + + ctx.write_str(name_pos, node.name.as_str()); + + pos +} + +fn serialize_jsx_element( + ctx: &mut TsEsTreeBuilder, + node: &JSXElement, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::JSXElement, parent, &node.span, 3); + let open_pos = ctx.ref_field(AstProp::OpeningElement); + let close_pos = ctx.ref_field(AstProp::ClosingElement); + let children_pos = ctx.ref_vec_field(AstProp::Children, node.children.len()); + + let open = serialize_jsx_opening_element(ctx, &node.opening, pos); + + let close = node.closing.as_ref().map(|closing| { + let closing_pos = + ctx.header(AstNode::JSXClosingElement, pos, &closing.span, 1); + let name_pos = ctx.ref_field(AstProp::Name); + + let name = serialize_jsx_element_name(ctx, &closing.name, closing_pos); + ctx.write_ref(name_pos, name); + + closing_pos + }); + + let children = serialize_jsx_children(ctx, &node.children, pos); + + ctx.write_ref(open_pos, open); + ctx.write_maybe_ref(close_pos, close); + ctx.write_refs(children_pos, children); + + pos +} + +fn serialize_jsx_fragment( + ctx: &mut TsEsTreeBuilder, + node: &JSXFragment, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::JSXFragment, parent, &node.span, 3); + + let opening_pos = ctx.ref_field(AstProp::OpeningFragment); + let closing_pos = ctx.ref_field(AstProp::ClosingFragment); + let children_pos = ctx.ref_vec_field(AstProp::Children, node.children.len()); + + let opening_id = + ctx.header(AstNode::JSXOpeningFragment, pos, &node.opening.span, 0); + let closing_id = + ctx.header(AstNode::JSXClosingFragment, pos, &node.closing.span, 0); + + let children = serialize_jsx_children(ctx, &node.children, pos); + + ctx.write_ref(opening_pos, opening_id); + ctx.write_ref(closing_pos, closing_id); + ctx.write_refs(children_pos, children); + + pos +} + +fn serialize_jsx_children( + ctx: &mut TsEsTreeBuilder, + children: &[JSXElementChild], + parent: NodeRef, +) -> Vec { + children + .iter() + .map(|child| { + match child { + JSXElementChild::JSXText(text) => { + let pos = ctx.header(AstNode::JSXText, parent, &text.span, 2); + let raw_pos = ctx.str_field(AstProp::Raw); + let value_pos = ctx.str_field(AstProp::Value); + + ctx.write_str(raw_pos, &text.raw); + ctx.write_str(value_pos, &text.value); + + pos + } + JSXElementChild::JSXExprContainer(container) => { + serialize_jsx_container_expr(ctx, container, parent) + } + JSXElementChild::JSXElement(el) => { + serialize_jsx_element(ctx, el, parent) + } + JSXElementChild::JSXFragment(frag) => { + serialize_jsx_fragment(ctx, frag, parent) + } + // No parser supports this + JSXElementChild::JSXSpreadChild(_) => unreachable!(), + } + }) + .collect::>() +} + +fn serialize_jsx_member_expr( + ctx: &mut TsEsTreeBuilder, + node: &JSXMemberExpr, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::JSXMemberExpression, parent, &node.span, 2); + let obj_ref = ctx.ref_field(AstProp::Object); + let prop_ref = ctx.ref_field(AstProp::Property); + + let obj = match &node.obj { + JSXObject::JSXMemberExpr(member) => { + serialize_jsx_member_expr(ctx, member, pos) + } + JSXObject::Ident(ident) => serialize_jsx_identifier(ctx, ident, parent), + }; + + let prop = serialize_ident_name_as_jsx_identifier(ctx, &node.prop, pos); + + ctx.write_ref(obj_ref, obj); + ctx.write_ref(prop_ref, prop); + + pos +} + +fn serialize_jsx_element_name( + ctx: &mut TsEsTreeBuilder, + node: &JSXElementName, + parent: NodeRef, +) -> NodeRef { + match &node { + JSXElementName::Ident(ident) => { + serialize_jsx_identifier(ctx, ident, parent) + } + JSXElementName::JSXMemberExpr(member) => { + serialize_jsx_member_expr(ctx, member, parent) + } + JSXElementName::JSXNamespacedName(ns) => { + serialize_jsx_namespaced_name(ctx, ns, parent) + } + } +} + +fn serialize_jsx_opening_element( + ctx: &mut TsEsTreeBuilder, + node: &JSXOpeningElement, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::JSXOpeningElement, parent, &node.span, 3); + let sclose_pos = ctx.bool_field(AstProp::SelfClosing); + let name_pos = ctx.ref_field(AstProp::Name); + let attrs_pos = ctx.ref_vec_field(AstProp::Attributes, node.attrs.len()); + + let name = serialize_jsx_element_name(ctx, &node.name, pos); + + // FIXME: type args + + let attrs = node + .attrs + .iter() + .map(|attr| match attr { + JSXAttrOrSpread::JSXAttr(attr) => { + let attr_pos = ctx.header(AstNode::JSXAttribute, pos, &attr.span, 2); + let name_pos = ctx.ref_field(AstProp::Name); + let value_pos = ctx.ref_field(AstProp::Value); + + let name = match &attr.name { + JSXAttrName::Ident(name) => { + serialize_ident_name_as_jsx_identifier(ctx, name, attr_pos) + } + JSXAttrName::JSXNamespacedName(node) => { + serialize_jsx_namespaced_name(ctx, node, attr_pos) + } + }; + + let value = attr.value.as_ref().map(|value| match value { + JSXAttrValue::Lit(lit) => serialize_lit(ctx, lit, attr_pos), + JSXAttrValue::JSXExprContainer(container) => { + serialize_jsx_container_expr(ctx, container, attr_pos) + } + JSXAttrValue::JSXElement(el) => { + serialize_jsx_element(ctx, el, attr_pos) + } + JSXAttrValue::JSXFragment(frag) => { + serialize_jsx_fragment(ctx, frag, attr_pos) + } + }); + + ctx.write_ref(name_pos, name); + ctx.write_maybe_ref(value_pos, value); + + attr_pos + } + JSXAttrOrSpread::SpreadElement(spread) => { + let attr_pos = + ctx.header(AstNode::JSXAttribute, pos, &spread.dot3_token, 1); + let arg_pos = ctx.ref_field(AstProp::Argument); + + let arg = serialize_expr(ctx, &spread.expr, attr_pos); + + ctx.write_ref(arg_pos, arg); + + attr_pos + } + }) + .collect::>(); + + ctx.write_bool(sclose_pos, node.self_closing); + ctx.write_ref(name_pos, name); + ctx.write_refs(attrs_pos, attrs); + + pos +} + +fn serialize_jsx_container_expr( + ctx: &mut TsEsTreeBuilder, + node: &JSXExprContainer, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::JSXExpressionContainer, parent, &node.span, 1); + let expr_pos = ctx.ref_field(AstProp::Expression); + + let expr = match &node.expr { + JSXExpr::JSXEmptyExpr(expr) => serialize_jsx_empty_expr(ctx, expr, pos), + JSXExpr::Expr(expr) => serialize_expr(ctx, expr, pos), + }; + + ctx.write_ref(expr_pos, expr); + + pos +} + +fn serialize_jsx_empty_expr( + ctx: &mut TsEsTreeBuilder, + node: &JSXEmptyExpr, + parent: NodeRef, +) -> NodeRef { + ctx.header(AstNode::JSXEmptyExpression, parent, &node.span, 0) +} + +fn serialize_jsx_namespaced_name( + ctx: &mut TsEsTreeBuilder, + node: &JSXNamespacedName, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::JSXNamespacedName, parent, &node.span, 2); + let ns_pos = ctx.ref_field(AstProp::Namespace); + let name_pos = ctx.ref_field(AstProp::Name); + + let ns_id = serialize_ident_name_as_jsx_identifier(ctx, &node.ns, pos); + let name_id = serialize_ident_name_as_jsx_identifier(ctx, &node.name, pos); + + ctx.write_ref(ns_pos, ns_id); + ctx.write_ref(name_pos, name_id); + + pos +} + +fn serialize_ident_name_as_jsx_identifier( + ctx: &mut TsEsTreeBuilder, + node: &IdentName, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::JSXIdentifier, parent, &node.span, 1); + let name_pos = ctx.str_field(AstProp::Name); + + ctx.write_str(name_pos, &node.sym); + + pos +} + +fn serialize_jsx_identifier( + ctx: &mut TsEsTreeBuilder, + node: &Ident, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::JSXIdentifier, parent, &node.span, 1); + let name_pos = ctx.str_field(AstProp::Name); + + ctx.write_str(name_pos, &node.sym); + + pos +} + +fn serialize_pat( + ctx: &mut TsEsTreeBuilder, + pat: &Pat, + parent: NodeRef, +) -> NodeRef { + match pat { + Pat::Ident(node) => serialize_ident(ctx, &node.id, parent), + Pat::Array(node) => { + let pos = ctx.header(AstNode::ArrayPattern, parent, &node.span, 3); + let opt_pos = ctx.bool_field(AstProp::Optional); + let type_pos = ctx.ref_field(AstProp::TypeAnnotation); + let elems_pos = ctx.ref_vec_field(AstProp::Elements, node.elems.len()); + + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + + let children = node + .elems + .iter() + .map(|pat| { + pat + .as_ref() + .map_or(NodeRef(0), |v| serialize_pat(ctx, v, pos)) + }) + .collect::>(); + + ctx.write_bool(opt_pos, node.optional); + ctx.write_maybe_ref(type_pos, type_ann); + ctx.write_refs(elems_pos, children); + + pos + } + Pat::Rest(node) => { + let pos = ctx.header(AstNode::RestElement, parent, &node.span, 2); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let arg_pos = ctx.ref_field(AstProp::Argument); + + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + let arg = serialize_pat(ctx, &node.arg, parent); + + ctx.write_maybe_ref(type_ann_pos, type_ann); + ctx.write_ref(arg_pos, arg); + + pos + } + Pat::Object(node) => { + let pos = ctx.header(AstNode::ObjectPattern, parent, &node.span, 3); + let opt_pos = ctx.bool_field(AstProp::Optional); + let props_pos = ctx.ref_vec_field(AstProp::Properties, node.props.len()); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + + let children = node + .props + .iter() + .map(|prop| match prop { + ObjectPatProp::KeyValue(key_value_prop) => { + let child_pos = + ctx.header(AstNode::Property, pos, &key_value_prop.span(), 3); + let computed_pos = ctx.bool_field(AstProp::Computed); + let key_pos = ctx.ref_field(AstProp::Key); + let value_pos = ctx.ref_field(AstProp::Value); + + let computed = matches!(key_value_prop.key, PropName::Computed(_)); + + let key = serialize_prop_name(ctx, &key_value_prop.key, child_pos); + let value = + serialize_pat(ctx, key_value_prop.value.as_ref(), child_pos); + + ctx.write_bool(computed_pos, computed); + ctx.write_ref(key_pos, key); + ctx.write_ref(value_pos, value); + + child_pos + } + ObjectPatProp::Assign(assign_pat_prop) => { + let child_pos = + ctx.header(AstNode::Property, pos, &assign_pat_prop.span, 3); + // TOOD: Doesn't seem to be present in SWC ast + let _computed_pos = ctx.bool_field(AstProp::Computed); + let key_pos = ctx.ref_field(AstProp::Key); + let value_pos = ctx.ref_field(AstProp::Value); + + let ident = serialize_ident(ctx, &assign_pat_prop.key.id, parent); + + let value = assign_pat_prop + .value + .as_ref() + .map(|value| serialize_expr(ctx, value, child_pos)); + + ctx.write_ref(key_pos, ident); + ctx.write_maybe_ref(value_pos, value); + + child_pos + } + ObjectPatProp::Rest(rest_pat) => { + serialize_pat(ctx, &Pat::Rest(rest_pat.clone()), parent) + } + }) + .collect::>(); + + ctx.write_bool(opt_pos, node.optional); + ctx.write_maybe_ref(type_ann_pos, type_ann); + ctx.write_refs(props_pos, children); + + pos + } + Pat::Assign(node) => { + let pos = ctx.header(AstNode::AssignmentPattern, parent, &node.span, 2); + let left_pos = ctx.ref_field(AstProp::Left); + let right_pos = ctx.ref_field(AstProp::Right); + + let left = serialize_pat(ctx, &node.left, pos); + let right = serialize_expr(ctx, &node.right, pos); + + ctx.write_ref(left_pos, left); + ctx.write_ref(right_pos, right); + + pos + } + Pat::Invalid(_) => unreachable!(), + Pat::Expr(node) => serialize_expr(ctx, node, parent), + } +} + +fn serialize_for_head( + ctx: &mut TsEsTreeBuilder, + for_head: &ForHead, + parent: NodeRef, +) -> NodeRef { + match for_head { + ForHead::VarDecl(var_decl) => { + serialize_decl(ctx, &Decl::Var(var_decl.clone()), parent) + } + ForHead::UsingDecl(using_decl) => { + serialize_decl(ctx, &Decl::Using(using_decl.clone()), parent) + } + ForHead::Pat(pat) => serialize_pat(ctx, pat, parent), + } +} + +fn serialize_spread( + ctx: &mut TsEsTreeBuilder, + expr: &Expr, + span: &Span, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::SpreadElement, parent, span, 1); + let arg_pos = ctx.ref_field(AstProp::Argument); + + let expr_pos = serialize_expr(ctx, expr, parent); + ctx.write_ref(arg_pos, expr_pos); + + pos +} + +fn serialize_ident_name( + ctx: &mut TsEsTreeBuilder, + ident_name: &IdentName, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::Identifier, parent, &ident_name.span, 1); + let name_pos = ctx.str_field(AstProp::Name); + ctx.write_str(name_pos, ident_name.sym.as_str()); + + pos +} + +fn serialize_prop_name( + ctx: &mut TsEsTreeBuilder, + prop_name: &PropName, + parent: NodeRef, +) -> NodeRef { + match prop_name { + PropName::Ident(ident_name) => { + serialize_ident_name(ctx, ident_name, parent) + } + PropName::Str(str_prop) => { + let child_pos = + ctx.header(AstNode::StringLiteral, parent, &str_prop.span, 1); + let value_pos = ctx.str_field(AstProp::Value); + ctx.write_str(value_pos, &str_prop.value); + + child_pos + } + PropName::Num(number) => { + serialize_lit(ctx, &Lit::Num(number.clone()), parent) + } + PropName::Computed(node) => serialize_expr(ctx, &node.expr, parent), + PropName::BigInt(big_int) => { + serialize_lit(ctx, &Lit::BigInt(big_int.clone()), parent) + } + } +} + +fn serialize_lit( + ctx: &mut TsEsTreeBuilder, + lit: &Lit, + parent: NodeRef, +) -> NodeRef { + match lit { + Lit::Str(node) => { + let pos = ctx.header(AstNode::StringLiteral, parent, &node.span, 1); + let value_pos = ctx.str_field(AstProp::Value); + + ctx.write_str(value_pos, &node.value); + + pos + } + Lit::Bool(lit_bool) => { + let pos = ctx.header(AstNode::Bool, parent, &lit_bool.span, 1); + let value_pos = ctx.bool_field(AstProp::Value); + + ctx.write_bool(value_pos, lit_bool.value); + + pos + } + Lit::Null(node) => ctx.header(AstNode::Null, parent, &node.span, 0), + Lit::Num(node) => { + let pos = ctx.header(AstNode::NumericLiteral, parent, &node.span, 1); + let value_pos = ctx.str_field(AstProp::Value); + + let value = node.raw.as_ref().unwrap(); + ctx.write_str(value_pos, value); + + pos + } + Lit::BigInt(node) => { + let pos = ctx.header(AstNode::BigIntLiteral, parent, &node.span, 1); + let value_pos = ctx.str_field(AstProp::Value); + + ctx.write_str(value_pos, &node.value.to_string()); + + pos + } + Lit::Regex(node) => { + let pos = ctx.header(AstNode::RegExpLiteral, parent, &node.span, 2); + let pattern_pos = ctx.str_field(AstProp::Pattern); + let flags_pos = ctx.str_field(AstProp::Flags); + + ctx.write_str(pattern_pos, node.exp.as_str()); + ctx.write_str(flags_pos, node.flags.as_str()); + + pos + } + Lit::JSXText(jsxtext) => { + ctx.header(AstNode::JSXText, parent, &jsxtext.span, 0) + } + } +} + +fn serialize_ts_param_inst( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeParamInstantiation, + parent: NodeRef, +) -> NodeRef { + let pos = + ctx.header(AstNode::TSTypeParameterInstantiation, parent, &node.span, 1); + let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); + + let params = node + .params + .iter() + .map(|param| serialize_ts_type(ctx, param, pos)) + .collect::>(); + + ctx.write_refs(params_pos, params); + + pos +} + +fn serialize_ts_type( + ctx: &mut TsEsTreeBuilder, + node: &TsType, + parent: NodeRef, +) -> NodeRef { + match node { + TsType::TsKeywordType(node) => { + let kind = match node.kind { + TsKeywordTypeKind::TsAnyKeyword => AstNode::TSAnyKeyword, + TsKeywordTypeKind::TsUnknownKeyword => AstNode::TSUnknownKeyword, + TsKeywordTypeKind::TsNumberKeyword => AstNode::TSNumberKeyword, + TsKeywordTypeKind::TsObjectKeyword => AstNode::TSObjectKeyword, + TsKeywordTypeKind::TsBooleanKeyword => AstNode::TSBooleanKeyword, + TsKeywordTypeKind::TsBigIntKeyword => AstNode::TSBigIntKeyword, + TsKeywordTypeKind::TsStringKeyword => AstNode::TSStringKeyword, + TsKeywordTypeKind::TsSymbolKeyword => AstNode::TSSymbolKeyword, + TsKeywordTypeKind::TsVoidKeyword => AstNode::TSVoidKeyword, + TsKeywordTypeKind::TsUndefinedKeyword => AstNode::TSUndefinedKeyword, + TsKeywordTypeKind::TsNullKeyword => AstNode::TSNullKeyword, + TsKeywordTypeKind::TsNeverKeyword => AstNode::TSNeverKeyword, + TsKeywordTypeKind::TsIntrinsicKeyword => AstNode::TSIntrinsicKeyword, + }; + + ctx.header(kind, parent, &node.span, 0) + } + TsType::TsThisType(node) => { + ctx.header(AstNode::TSThisType, parent, &node.span, 0) + } + TsType::TsFnOrConstructorType(node) => match node { + TsFnOrConstructorType::TsFnType(node) => { + let pos = ctx.header(AstNode::TSFunctionType, parent, &node.span, 1); + let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); + + let param_ids = node + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param, pos)) + .collect::>(); + + ctx.write_refs(params_pos, param_ids); + + pos + } + TsFnOrConstructorType::TsConstructorType(_) => { + todo!() + } + }, + TsType::TsTypeRef(node) => { + let pos = ctx.header(AstNode::TSTypeReference, parent, &node.span, 2); + let name_pos = ctx.ref_field(AstProp::TypeName); + let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + + let name = serialize_ts_entity_name(ctx, &node.type_name, pos); + + let type_args = node + .type_params + .clone() + .map(|param| serialize_ts_param_inst(ctx, ¶m, pos)); + + ctx.write_ref(name_pos, name); + ctx.write_maybe_ref(type_args_pos, type_args); + + pos + } + TsType::TsTypeQuery(node) => { + let pos = ctx.header(AstNode::TSTypeQuery, parent, &node.span, 2); + let name_pos = ctx.ref_field(AstProp::ExprName); + let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + + let expr_name = match &node.expr_name { + TsTypeQueryExpr::TsEntityName(entity) => { + serialize_ts_entity_name(ctx, entity, pos) + } + TsTypeQueryExpr::Import(child) => { + serialize_ts_type(ctx, &TsType::TsImportType(child.clone()), pos) + } + }; + + let type_args = node + .type_args + .clone() + .map(|param| serialize_ts_param_inst(ctx, ¶m, pos)); + + ctx.write_ref(name_pos, expr_name); + ctx.write_maybe_ref(type_args_pos, type_args); + + pos + } + TsType::TsTypeLit(_) => { + // TODO: Not sure what this is + todo!() + } + TsType::TsArrayType(node) => { + let pos = ctx.header(AstNode::TSArrayType, parent, &node.span, 1); + let elem_pos = ctx.ref_field(AstProp::ElementType); + + let elem = serialize_ts_type(ctx, &node.elem_type, pos); + + ctx.write_ref(elem_pos, elem); + + pos + } + TsType::TsTupleType(node) => { + let pos = ctx.header(AstNode::TSTupleType, parent, &node.span, 1); + let children_pos = + ctx.ref_vec_field(AstProp::ElementTypes, node.elem_types.len()); + + let children = node + .elem_types + .iter() + .map(|elem| { + if let Some(label) = &elem.label { + let child_pos = + ctx.header(AstNode::TSNamedTupleMember, pos, &elem.span, 1); + let label_pos = ctx.ref_field(AstProp::Label); + let type_pos = ctx.ref_field(AstProp::ElementType); + + let label_id = serialize_pat(ctx, label, child_pos); + let type_id = serialize_ts_type(ctx, elem.ty.as_ref(), child_pos); + + ctx.write_ref(label_pos, label_id); + ctx.write_ref(type_pos, type_id); + + child_pos + } else { + serialize_ts_type(ctx, elem.ty.as_ref(), pos) + } + }) + .collect::>(); + + ctx.write_refs(children_pos, children); + + pos + } + TsType::TsOptionalType(_) => todo!(), + TsType::TsRestType(node) => { + let pos = ctx.header(AstNode::TSRestType, parent, &node.span, 1); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); + + ctx.write_ref(type_ann_pos, type_ann); + + pos + } + TsType::TsUnionOrIntersectionType(node) => match node { + TsUnionOrIntersectionType::TsUnionType(node) => { + let pos = ctx.header(AstNode::TSUnionType, parent, &node.span, 1); + let types_pos = ctx.ref_vec_field(AstProp::Types, node.types.len()); + + let children = node + .types + .iter() + .map(|item| serialize_ts_type(ctx, item, pos)) + .collect::>(); + + ctx.write_refs(types_pos, children); + + pos + } + TsUnionOrIntersectionType::TsIntersectionType(node) => { + let pos = + ctx.header(AstNode::TSIntersectionType, parent, &node.span, 1); + let types_pos = ctx.ref_vec_field(AstProp::Types, node.types.len()); + + let children = node + .types + .iter() + .map(|item| serialize_ts_type(ctx, item, pos)) + .collect::>(); + + ctx.write_refs(types_pos, children); + + pos + } + }, + TsType::TsConditionalType(node) => { + let pos = ctx.header(AstNode::TSConditionalType, parent, &node.span, 4); + let check_pos = ctx.ref_field(AstProp::CheckType); + let extends_pos = ctx.ref_field(AstProp::ExtendsType); + let true_pos = ctx.ref_field(AstProp::TrueType); + let false_pos = ctx.ref_field(AstProp::FalseType); + + let check = serialize_ts_type(ctx, &node.check_type, pos); + let extends = serialize_ts_type(ctx, &node.extends_type, pos); + let v_true = serialize_ts_type(ctx, &node.true_type, pos); + let v_false = serialize_ts_type(ctx, &node.false_type, pos); + + ctx.write_ref(check_pos, check); + ctx.write_ref(extends_pos, extends); + ctx.write_ref(true_pos, v_true); + ctx.write_ref(false_pos, v_false); + + pos + } + TsType::TsInferType(node) => { + let pos = ctx.header(AstNode::TSInferType, parent, &node.span, 1); + let param_pos = ctx.ref_field(AstProp::TypeParameter); + + let param = serialize_ts_type_param(ctx, &node.type_param, parent); + + ctx.write_ref(param_pos, param); + + pos + } + TsType::TsParenthesizedType(_) => todo!(), + TsType::TsTypeOperator(node) => { + let pos = ctx.header(AstNode::TSTypeOperator, parent, &node.span, 2); + + let operator_pos = ctx.str_field(AstProp::Operator); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); + + ctx.write_str( + operator_pos, + match node.op { + TsTypeOperatorOp::KeyOf => "keyof", + TsTypeOperatorOp::Unique => "unique", + TsTypeOperatorOp::ReadOnly => "readonly", + }, + ); + ctx.write_ref(type_ann_pos, type_ann); + + pos + } + TsType::TsIndexedAccessType(node) => { + let pos = ctx.header(AstNode::TSIndexedAccessType, parent, &node.span, 2); + + let index_type_pos = ctx.ref_field(AstProp::IndexType); + let obj_type_pos = ctx.ref_field(AstProp::ObjectType); + + let index = serialize_ts_type(ctx, &node.index_type, pos); + let obj = serialize_ts_type(ctx, &node.obj_type, pos); + + ctx.write_ref(index_type_pos, index); + ctx.write_ref(obj_type_pos, obj); + + pos + } + TsType::TsMappedType(node) => { + let pos = ctx.header(AstNode::TSMappedType, parent, &node.span, 5); + + let name_pos = ctx.ref_field(AstProp::NameType); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let type_param_pos = ctx.ref_field(AstProp::TypeParameter); + + let opt_pos = + create_true_plus_minus_field(ctx, AstProp::Optional, node.optional); + let readonly_pos = + create_true_plus_minus_field(ctx, AstProp::Readonly, node.readonly); + + let name_id = maybe_serialize_ts_type(ctx, &node.name_type, pos); + let type_ann = maybe_serialize_ts_type(ctx, &node.type_ann, pos); + let type_param = serialize_ts_type_param(ctx, &node.type_param, pos); + + write_true_plus_minus(ctx, opt_pos, node.optional); + write_true_plus_minus(ctx, readonly_pos, node.readonly); + ctx.write_maybe_ref(name_pos, name_id); + ctx.write_maybe_ref(type_ann_pos, type_ann); + ctx.write_ref(type_param_pos, type_param); + + pos + } + TsType::TsLitType(node) => serialize_ts_lit_type(ctx, node, parent), + TsType::TsTypePredicate(node) => { + let pos = ctx.header(AstNode::TSTypePredicate, parent, &node.span, 3); + + let asserts_pos = ctx.bool_field(AstProp::Asserts); + let param_name_pos = ctx.ref_field(AstProp::ParameterName); + let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let param_name = match &node.param_name { + TsThisTypeOrIdent::TsThisType(ts_this_type) => { + ctx.header(AstNode::TSThisType, pos, &ts_this_type.span, 0) + } + TsThisTypeOrIdent::Ident(ident) => serialize_ident(ctx, ident, pos), + }; + + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + + ctx.write_bool(asserts_pos, node.asserts); + ctx.write_ref(param_name_pos, param_name); + ctx.write_maybe_ref(type_ann_pos, type_ann); + + pos + } + TsType::TsImportType(node) => { + let pos = ctx.header(AstNode::TSTypePredicate, parent, &node.span, 3); + let arg_pos = ctx.ref_field(AstProp::Argument); + let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + let qualifier_pos = ctx.ref_field(AstProp::Qualifier); + + let arg = serialize_ts_lit_type( + ctx, + &TsLitType { + lit: TsLit::Str(node.arg.clone()), + span: node.arg.span, + }, + pos, + ); + + let type_arg = node.type_args.clone().map(|param_node| { + serialize_ts_param_inst(ctx, param_node.as_ref(), pos) + }); + + let qualifier = node.qualifier.clone().map_or(NodeRef(0), |quali| { + serialize_ts_entity_name(ctx, &quali, pos) + }); + + ctx.write_ref(arg_pos, arg); + ctx.write_ref(qualifier_pos, qualifier); + ctx.write_maybe_ref(type_args_pos, type_arg); + + pos + } + } +} + +fn serialize_ts_lit_type( + ctx: &mut TsEsTreeBuilder, + node: &TsLitType, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::TSLiteralType, parent, &node.span, 1); + let lit_pos = ctx.ref_field(AstProp::Literal); + + let lit = match &node.lit { + TsLit::Number(lit) => serialize_lit(ctx, &Lit::Num(lit.clone()), pos), + TsLit::Str(lit) => serialize_lit(ctx, &Lit::Str(lit.clone()), pos), + TsLit::Bool(lit) => serialize_lit(ctx, &Lit::Bool(*lit), pos), + TsLit::BigInt(lit) => serialize_lit(ctx, &Lit::BigInt(lit.clone()), pos), + TsLit::Tpl(lit) => serialize_expr( + ctx, + &Expr::Tpl(Tpl { + span: lit.span, + exprs: vec![], + quasis: lit.quasis.clone(), + }), + pos, + ), + }; + + ctx.write_ref(lit_pos, lit); + + pos +} + +fn create_true_plus_minus_field( + ctx: &mut TsEsTreeBuilder, + prop: AstProp, + value: Option, +) -> NodePos { + if let Some(v) = value { + match v { + TruePlusMinus::True => NodePos::Bool(ctx.bool_field(prop)), + TruePlusMinus::Plus | TruePlusMinus::Minus => { + NodePos::Str(ctx.str_field(prop)) + } + } + } else { + NodePos::Undef(ctx.undefined_field(prop)) + } +} + +fn extract_pos(pos: NodePos) -> usize { + match pos { + NodePos::Bool(bool_pos) => bool_pos.0, + NodePos::Field(field_pos) => field_pos.0, + NodePos::FieldArr(field_arr_pos) => field_arr_pos.0, + NodePos::Str(str_pos) => str_pos.0, + NodePos::Undef(undef_pos) => undef_pos.0, + NodePos::Null(null_pos) => null_pos.0, + } +} + +fn write_true_plus_minus( + ctx: &mut TsEsTreeBuilder, + pos: NodePos, + value: Option, +) { + if let Some(v) = value { + match v { + TruePlusMinus::True => { + let bool_pos = BoolPos(extract_pos(pos)); + ctx.write_bool(bool_pos, true); + } + TruePlusMinus::Plus => { + let str_pos = StrPos(extract_pos(pos)); + ctx.write_str(str_pos, "+") + } + TruePlusMinus::Minus => { + let str_pos = StrPos(extract_pos(pos)); + ctx.write_str(str_pos, "-") + } + } + } +} + +fn serialize_ts_entity_name( + ctx: &mut TsEsTreeBuilder, + node: &TsEntityName, + parent: NodeRef, +) -> NodeRef { + match &node { + TsEntityName::TsQualifiedName(_) => todo!(), + TsEntityName::Ident(ident) => serialize_ident(ctx, ident, parent), + } +} + +fn maybe_serialize_ts_type_ann( + ctx: &mut TsEsTreeBuilder, + node: &Option>, + parent: NodeRef, +) -> Option { + node + .as_ref() + .map(|type_ann| serialize_ts_type_ann(ctx, type_ann, parent)) +} + +fn serialize_ts_type_ann( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeAnn, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::TSTypeAnnotation, parent, &node.span, 1); + let type_pos = ctx.ref_field(AstProp::TypeAnnotation); + + let v_type = serialize_ts_type(ctx, &node.type_ann, pos); + + ctx.write_ref(type_pos, v_type); + + pos +} + +fn maybe_serialize_ts_type( + ctx: &mut TsEsTreeBuilder, + node: &Option>, + parent: NodeRef, +) -> Option { + node + .as_ref() + .map(|item| serialize_ts_type(ctx, item, parent)) +} + +fn serialize_ts_type_param( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeParam, + parent: NodeRef, +) -> NodeRef { + let pos = ctx.header(AstNode::TSTypeParameter, parent, &node.span, 6); + let name_pos = ctx.ref_field(AstProp::Name); + let constraint_pos = ctx.ref_field(AstProp::Constraint); + let default_pos = ctx.ref_field(AstProp::Default); + let const_pos = ctx.bool_field(AstProp::Const); + let in_pos = ctx.bool_field(AstProp::In); + let out_pos = ctx.bool_field(AstProp::Out); + + let name = serialize_ident(ctx, &node.name, pos); + let constraint = maybe_serialize_ts_type(ctx, &node.constraint, pos); + let default = maybe_serialize_ts_type(ctx, &node.default, pos); + + ctx.write_bool(const_pos, node.is_const); + ctx.write_bool(in_pos, node.is_in); + ctx.write_bool(out_pos, node.is_out); + ctx.write_ref(name_pos, name); + ctx.write_maybe_ref(constraint_pos, constraint); + ctx.write_maybe_ref(default_pos, default); + + pos +} + +fn maybe_serialize_ts_type_param( + ctx: &mut TsEsTreeBuilder, + node: &Option>, + parent: NodeRef, +) -> Option { + node.as_ref().map(|node| { + let pos = + ctx.header(AstNode::TSTypeParameterDeclaration, parent, &node.span, 1); + let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); + + let params = node + .params + .iter() + .map(|param| serialize_ts_type_param(ctx, param, pos)) + .collect::>(); + + ctx.write_refs(params_pos, params); + + pos + }) +} + +fn serialize_ts_fn_param( + ctx: &mut TsEsTreeBuilder, + node: &TsFnParam, + parent: NodeRef, +) -> NodeRef { + match node { + TsFnParam::Ident(ident) => serialize_ident(ctx, ident, parent), + TsFnParam::Array(pat) => { + serialize_pat(ctx, &Pat::Array(pat.clone()), parent) + } + TsFnParam::Rest(pat) => serialize_pat(ctx, &Pat::Rest(pat.clone()), parent), + TsFnParam::Object(pat) => { + serialize_pat(ctx, &Pat::Object(pat.clone()), parent) + } + } +} diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs new file mode 100644 index 0000000000..af5fea4b46 --- /dev/null +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -0,0 +1,513 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use std::fmt; +use std::fmt::Debug; +use std::fmt::Display; + +use deno_ast::swc::common::Span; + +use super::buffer::AstBufSerializer; +use super::buffer::BoolPos; +use super::buffer::FieldArrPos; +use super::buffer::FieldPos; +use super::buffer::NodeRef; +use super::buffer::NullPos; +use super::buffer::SerializeCtx; +use super::buffer::StrPos; +use super::buffer::UndefPos; + +#[derive(Debug, Clone, PartialEq)] +pub enum AstNode { + // First node must always be the empty/invalid node + Invalid, + // Typically the + Program, + + // Module declarations + ExportAllDeclaration, + ExportDefaultDeclaration, + ExportNamedDeclaration, + ImportDeclaration, + TsExportAssignment, + TsImportEquals, + TsNamespaceExport, + + // Decls + ClassDeclaration, + FunctionDeclaration, + TSEnumDeclaration, + TSInterface, + TsModule, + TsTypeAlias, + Using, + VariableDeclaration, + + // Statements + BlockStatement, + BreakStatement, + ContinueStatement, + DebuggerStatement, + DoWhileStatement, + EmptyStatement, + ExpressionStatement, + ForInStatement, + ForOfStatement, + ForStatement, + IfStatement, + LabeledStatement, + ReturnStatement, + SwitchCase, + SwitchStatement, + ThrowStatement, + TryStatement, + WhileStatement, + WithStatement, + + // Expressions + ArrayExpression, + ArrowFunctionExpression, + AssignmentExpression, + AwaitExpression, + BinaryExpression, + CallExpression, + ChainExpression, + ClassExpression, + ConditionalExpression, + FunctionExpression, + Identifier, + ImportExpression, + LogicalExpression, + MemberExpression, + MetaProp, + NewExpression, + ObjectExpression, + PrivateIdentifier, + SequenceExpression, + Super, + TaggedTemplateExpression, + TemplateLiteral, + ThisExpression, + TSAsExpression, + TsConstAssertion, + TsInstantiation, + TSNonNullExpression, + TSSatisfiesExpression, + TSTypeAssertion, + UnaryExpression, + UpdateExpression, + YieldExpression, + + // TODO: TSEsTree uses a single literal node + // Literals + StringLiteral, + Bool, + Null, + NumericLiteral, + BigIntLiteral, + RegExpLiteral, + + EmptyExpr, + SpreadElement, + Property, + VariableDeclarator, + CatchClause, + RestElement, + ExportSpecifier, + TemplateElement, + MethodDefinition, + ClassBody, + + // Patterns + ArrayPattern, + AssignmentPattern, + ObjectPattern, + + // JSX + JSXAttribute, + JSXClosingElement, + JSXClosingFragment, + JSXElement, + JSXEmptyExpression, + JSXExpressionContainer, + JSXFragment, + JSXIdentifier, + JSXMemberExpression, + JSXNamespacedName, + JSXOpeningElement, + JSXOpeningFragment, + JSXSpreadAttribute, + JSXSpreadChild, + JSXText, + + TSTypeAnnotation, + TSTypeParameterDeclaration, + TSTypeParameter, + TSTypeParameterInstantiation, + TSEnumMember, + TSInterfaceBody, + TSInterfaceHeritage, + TSTypeReference, + TSThisType, + TSLiteralType, + TSInferType, + TSConditionalType, + TSUnionType, + TSIntersectionType, + TSMappedType, + TSTypeQuery, + TSTupleType, + TSNamedTupleMember, + TSFunctionType, + TsCallSignatureDeclaration, + TSPropertySignature, + TSMethodSignature, + TSIndexSignature, + TSIndexedAccessType, + TSTypeOperator, + TSTypePredicate, + TSImportType, + TSRestType, + TSArrayType, + TSClassImplements, + + TSAnyKeyword, + TSBigIntKeyword, + TSBooleanKeyword, + TSIntrinsicKeyword, + TSNeverKeyword, + TSNullKeyword, + TSNumberKeyword, + TSObjectKeyword, + TSStringKeyword, + TSSymbolKeyword, + TSUndefinedKeyword, + TSUnknownKeyword, + TSVoidKeyword, + TSEnumBody, // Last value is used for max value +} + +impl Display for AstNode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(self, f) + } +} + +impl From for u8 { + fn from(m: AstNode) -> u8 { + m as u8 + } +} + +#[derive(Debug, Clone)] +pub enum AstProp { + // Base, these three must be in sync with JS. The + // order here for these 3 fields is important. + Type, + Parent, + Range, + + // Starting from here the order doesn't matter. + // Following are all possible AST node properties. + Abstract, + Accessibility, + Alternate, + Argument, + Arguments, + Asserts, + Async, + Attributes, + Await, + Block, + Body, + Callee, + Cases, + Children, + CheckType, + ClosingElement, + ClosingFragment, + Computed, + Consequent, + Const, + Constraint, + Cooked, + Declaration, + Declarations, + Declare, + Default, + Definite, + Delegate, + Discriminant, + Elements, + ElementType, + ElementTypes, + ExprName, + Expression, + Expressions, + Exported, + Extends, + ExtendsType, + FalseType, + Finalizer, + Flags, + Generator, + Handler, + Id, + In, + IndexType, + Init, + Initializer, + Implements, + Key, + Kind, + Label, + Left, + Literal, + Local, + Members, + Meta, + Method, + Name, + Namespace, + NameType, + Object, + ObjectType, + OpeningElement, + OpeningFragment, + Operator, + Optional, + Out, + Param, + ParameterName, + Params, + Pattern, + Prefix, + Properties, + Property, + Qualifier, + Quasi, + Quasis, + Raw, + Readonly, + ReturnType, + Right, + SelfClosing, + Shorthand, + Source, + SourceType, + Specifiers, + Static, + SuperClass, + SuperTypeArguments, + Tag, + Tail, + Test, + TrueType, + TypeAnnotation, + TypeArguments, + TypeName, + TypeParameter, + TypeParameters, + Types, + Update, + Value, // Last value is used for max value +} + +// TODO: Feels like there should be an easier way to iterater over an +// enum in Rust and lowercase the first letter. +impl Display for AstProp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + AstProp::Parent => "parent", + AstProp::Range => "range", + AstProp::Type => "type", + AstProp::Abstract => "abstract", + AstProp::Accessibility => "accessibility", + AstProp::Alternate => "alternate", + AstProp::Argument => "argument", + AstProp::Arguments => "arguments", + AstProp::Asserts => "asserts", + AstProp::Async => "async", + AstProp::Attributes => "attributes", + AstProp::Await => "await", + AstProp::Block => "block", + AstProp::Body => "body", + AstProp::Callee => "callee", + AstProp::Cases => "cases", + AstProp::Children => "children", + AstProp::CheckType => "checkType", + AstProp::ClosingElement => "closingElement", + AstProp::ClosingFragment => "closingFragment", + AstProp::Computed => "computed", + AstProp::Consequent => "consequent", + AstProp::Const => "const", + AstProp::Constraint => "constraint", + AstProp::Cooked => "cooked", + AstProp::Declaration => "declaration", + AstProp::Declarations => "declarations", + AstProp::Declare => "declare", + AstProp::Default => "default", + AstProp::Definite => "definite", + AstProp::Delegate => "delegate", + AstProp::Discriminant => "discriminant", + AstProp::Elements => "elements", + AstProp::ElementType => "elementType", + AstProp::ElementTypes => "elementTypes", + AstProp::ExprName => "exprName", + AstProp::Expression => "expression", + AstProp::Expressions => "expressions", + AstProp::Exported => "exported", + AstProp::Extends => "extends", + AstProp::ExtendsType => "extendsType", + AstProp::FalseType => "falseType", + AstProp::Finalizer => "finalizer", + AstProp::Flags => "flags", + AstProp::Generator => "generator", + AstProp::Handler => "handler", + AstProp::Id => "id", + AstProp::In => "in", + AstProp::IndexType => "indexType", + AstProp::Init => "init", + AstProp::Initializer => "initializer", + AstProp::Implements => "implements", + AstProp::Key => "key", + AstProp::Kind => "kind", + AstProp::Label => "label", + AstProp::Left => "left", + AstProp::Literal => "literal", + AstProp::Local => "local", + AstProp::Members => "members", + AstProp::Meta => "meta", + AstProp::Method => "method", + AstProp::Name => "name", + AstProp::Namespace => "namespace", + AstProp::NameType => "nameType", + AstProp::Object => "object", + AstProp::ObjectType => "objectType", + AstProp::OpeningElement => "openingElement", + AstProp::OpeningFragment => "openingFragment", + AstProp::Operator => "operator", + AstProp::Optional => "optional", + AstProp::Out => "out", + AstProp::Param => "param", + AstProp::ParameterName => "parameterName", + AstProp::Params => "params", + AstProp::Pattern => "pattern", + AstProp::Prefix => "prefix", + AstProp::Properties => "properties", + AstProp::Property => "property", + AstProp::Qualifier => "qualifier", + AstProp::Quasi => "quasi", + AstProp::Quasis => "quasis", + AstProp::Raw => "raw", + AstProp::Readonly => "readonly", + AstProp::ReturnType => "returnType", + AstProp::Right => "right", + AstProp::SelfClosing => "selfClosing", + AstProp::Shorthand => "shorthand", + AstProp::Source => "source", + AstProp::SourceType => "sourceType", + AstProp::Specifiers => "specifiers", + AstProp::Static => "static", + AstProp::SuperClass => "superClass", + AstProp::SuperTypeArguments => "superTypeArguments", + AstProp::Tag => "tag", + AstProp::Tail => "tail", + AstProp::Test => "test", + AstProp::TrueType => "trueType", + AstProp::TypeAnnotation => "typeAnnotation", + AstProp::TypeArguments => "typeArguments", + AstProp::TypeName => "typeName", + AstProp::TypeParameter => "typeParameter", + AstProp::TypeParameters => "typeParameters", + AstProp::Types => "types", + AstProp::Update => "update", + AstProp::Value => "value", + }; + + write!(f, "{}", s) + } +} + +impl From for u8 { + fn from(m: AstProp) -> u8 { + m as u8 + } +} + +pub struct TsEsTreeBuilder { + ctx: SerializeCtx, +} + +// TODO: Add a builder API to make it easier to convert from different source +// ast formats. +impl TsEsTreeBuilder { + pub fn new() -> Self { + // Max values + // TODO: Maybe there is a rust macro to grab the last enum value? + let kind_count: u8 = AstNode::TSEnumBody.into(); + let prop_count: u8 = AstProp::Value.into(); + Self { + ctx: SerializeCtx::new(kind_count, prop_count), + } + } +} + +impl AstBufSerializer for TsEsTreeBuilder { + fn header( + &mut self, + kind: AstNode, + parent: NodeRef, + span: &Span, + prop_count: usize, + ) -> NodeRef { + self.ctx.header(kind, parent, span, prop_count) + } + + fn ref_field(&mut self, prop: AstProp) -> FieldPos { + FieldPos(self.ctx.ref_field(prop)) + } + + fn ref_vec_field(&mut self, prop: AstProp, len: usize) -> FieldArrPos { + FieldArrPos(self.ctx.ref_vec_field(prop, len)) + } + + fn str_field(&mut self, prop: AstProp) -> StrPos { + StrPos(self.ctx.str_field(prop)) + } + + fn bool_field(&mut self, prop: AstProp) -> BoolPos { + BoolPos(self.ctx.bool_field(prop)) + } + + fn undefined_field(&mut self, prop: AstProp) -> UndefPos { + UndefPos(self.ctx.undefined_field(prop)) + } + + fn null_field(&mut self, prop: AstProp) -> NullPos { + NullPos(self.ctx.null_field(prop)) + } + + fn write_ref(&mut self, pos: FieldPos, value: NodeRef) { + self.ctx.write_ref(pos.0, value); + } + + fn write_maybe_ref(&mut self, pos: FieldPos, value: Option) { + self.ctx.write_maybe_ref(pos.0, value); + } + + fn write_refs(&mut self, pos: FieldArrPos, value: Vec) { + self.ctx.write_refs(pos.0, value); + } + + fn write_str(&mut self, pos: StrPos, value: &str) { + self.ctx.write_str(pos.0, value); + } + + fn write_bool(&mut self, pos: BoolPos, value: bool) { + self.ctx.write_bool(pos.0, value); + } + + fn serialize(&mut self) -> Vec { + self.ctx.serialize() + } +} diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index e49197bbad..50fc16799a 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -51,10 +51,13 @@ use crate::util::fs::canonicalize_path; use crate::util::path::is_script_ext; use crate::util::sync::AtomicFlag; +mod ast_buffer; mod linter; mod reporters; mod rules; +// TODO(bartlomieju): remove once we wire plugins through the CLI linter +pub use ast_buffer::serialize_ast_to_buffer; pub use linter::CliLinter; pub use linter::CliLinterOptions; pub use rules::collect_no_slow_type_diagnostics; diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 48bf42c9c7..3164b8ae59 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -616,7 +616,10 @@ async fn configure_main_worker( WorkerExecutionMode::Test, specifier.clone(), permissions_container, - vec![ops::testing::deno_test::init_ops(worker_sender.sender)], + vec![ + ops::testing::deno_test::init_ops(worker_sender.sender), + ops::lint::deno_lint::init_ops(), + ], Stdio { stdin: StdioPipe::inherit(), stdout: StdioPipe::file(worker_sender.stdout), diff --git a/cli/worker.rs b/cli/worker.rs index c733f41321..6b87b5966a 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -656,7 +656,8 @@ impl CliMainWorkerFactory { "40_test_common.js", "40_test.js", "40_bench.js", - "40_jupyter.js" + "40_jupyter.js", + "40_lint.js" ); } diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index bceb1f7ddb..a11444bc36 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -526,6 +526,9 @@ const NOT_IMPORTED_OPS = [ // Used in jupyter API "op_base64_encode", + // Used in the lint API + "op_lint_create_serialized_ast", + // Related to `Deno.test()` API "op_test_event_step_result_failed", "op_test_event_step_result_ignored", diff --git a/tests/integration/js_unit_tests.rs b/tests/integration/js_unit_tests.rs index 717a8d8e7c..899329b319 100644 --- a/tests/integration/js_unit_tests.rs +++ b/tests/integration/js_unit_tests.rs @@ -52,6 +52,7 @@ util::unit_test_factory!( kv_queue_test, kv_queue_undelivered_test, link_test, + lint_plugin_test, make_temp_test, message_channel_test, mkdir_test, diff --git a/tests/unit/lint_plugin_test.ts b/tests/unit/lint_plugin_test.ts new file mode 100644 index 0000000000..649c8bde9e --- /dev/null +++ b/tests/unit/lint_plugin_test.ts @@ -0,0 +1,557 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals } from "./test_util.ts"; + +// TODO(@marvinhagemeister) Remove once we land "official" types +export interface LintReportData { + // deno-lint-ignore no-explicit-any + node: any; + message: string; +} +// TODO(@marvinhagemeister) Remove once we land "official" types +interface LintContext { + id: string; +} +// TODO(@marvinhagemeister) Remove once we land "official" types +// deno-lint-ignore no-explicit-any +type LintVisitor = Record void>; + +// TODO(@marvinhagemeister) Remove once we land "official" types +interface LintRule { + create(ctx: LintContext): LintVisitor; + destroy?(): void; +} + +// TODO(@marvinhagemeister) Remove once we land "official" types +interface LintPlugin { + name: string; + rules: Record; +} + +function runLintPlugin(plugin: LintPlugin, fileName: string, source: string) { + // deno-lint-ignore no-explicit-any + return (Deno as any)[(Deno as any).internal].runLintPlugin( + plugin, + fileName, + source, + ); +} + +function testPlugin( + source: string, + rule: LintRule, +) { + const plugin = { + name: "test-plugin", + rules: { + testRule: rule, + }, + }; + + return runLintPlugin(plugin, "source.tsx", source); +} + +function testVisit(source: string, ...selectors: string[]): string[] { + const log: string[] = []; + + testPlugin(source, { + create() { + const visitor: LintVisitor = {}; + + for (const s of selectors) { + visitor[s] = () => log.push(s); + } + + return visitor; + }, + }); + + return log; +} + +function testLintNode(source: string, ...selectors: string[]) { + // deno-lint-ignore no-explicit-any + const log: any[] = []; + + testPlugin(source, { + create() { + const visitor: LintVisitor = {}; + + for (const s of selectors) { + visitor[s] = (node) => { + log.push(node[Symbol.for("Deno.lint.toJsValue")]()); + }; + } + + return visitor; + }, + }); + + return log; +} + +Deno.test("Plugin - visitor enter/exit", () => { + const enter = testVisit("foo", "Identifier"); + assertEquals(enter, ["Identifier"]); + + const exit = testVisit("foo", "Identifier:exit"); + assertEquals(exit, ["Identifier:exit"]); + + const both = testVisit("foo", "Identifier", "Identifier:exit"); + assertEquals(both, ["Identifier", "Identifier:exit"]); +}); + +Deno.test("Plugin - Program", () => { + const node = testLintNode("", "Program"); + assertEquals(node[0], { + type: "Program", + sourceType: "script", + range: [1, 1], + body: [], + }); +}); + +Deno.test("Plugin - BlockStatement", () => { + const node = testLintNode("{ foo; }", "BlockStatement"); + assertEquals(node[0], { + type: "BlockStatement", + range: [1, 9], + body: [{ + type: "ExpressionStatement", + range: [3, 7], + expression: { + type: "Identifier", + name: "foo", + range: [3, 6], + }, + }], + }); +}); + +Deno.test("Plugin - BreakStatement", () => { + let node = testLintNode("break;", "BreakStatement"); + assertEquals(node[0], { + type: "BreakStatement", + range: [1, 7], + label: null, + }); + + node = testLintNode("break foo;", "BreakStatement"); + assertEquals(node[0], { + type: "BreakStatement", + range: [1, 11], + label: { + type: "Identifier", + range: [7, 10], + name: "foo", + }, + }); +}); + +Deno.test("Plugin - ContinueStatement", () => { + let node = testLintNode("continue;", "ContinueStatement"); + assertEquals(node[0], { + type: "ContinueStatement", + range: [1, 10], + label: null, + }); + + node = testLintNode("continue foo;", "ContinueStatement"); + assertEquals(node[0], { + type: "ContinueStatement", + range: [1, 14], + label: { + type: "Identifier", + range: [10, 13], + name: "foo", + }, + }); +}); + +Deno.test("Plugin - DebuggerStatement", () => { + const node = testLintNode("debugger;", "DebuggerStatement"); + assertEquals(node[0], { + type: "DebuggerStatement", + range: [1, 10], + }); +}); + +Deno.test("Plugin - DoWhileStatement", () => { + const node = testLintNode("do {} while (foo);", "DoWhileStatement"); + assertEquals(node[0], { + type: "DoWhileStatement", + range: [1, 19], + test: { + type: "Identifier", + range: [14, 17], + name: "foo", + }, + body: { + type: "BlockStatement", + range: [4, 6], + body: [], + }, + }); +}); + +Deno.test("Plugin - ExpressionStatement", () => { + const node = testLintNode("foo;", "ExpressionStatement"); + assertEquals(node[0], { + type: "ExpressionStatement", + range: [1, 5], + expression: { + type: "Identifier", + range: [1, 4], + name: "foo", + }, + }); +}); + +Deno.test("Plugin - ForInStatement", () => { + const node = testLintNode("for (a in b) {}", "ForInStatement"); + assertEquals(node[0], { + type: "ForInStatement", + range: [1, 16], + left: { + type: "Identifier", + range: [6, 7], + name: "a", + }, + right: { + type: "Identifier", + range: [11, 12], + name: "b", + }, + body: { + type: "BlockStatement", + range: [14, 16], + body: [], + }, + }); +}); + +Deno.test("Plugin - ForOfStatement", () => { + let node = testLintNode("for (a of b) {}", "ForOfStatement"); + assertEquals(node[0], { + type: "ForOfStatement", + range: [1, 16], + await: false, + left: { + type: "Identifier", + range: [6, 7], + name: "a", + }, + right: { + type: "Identifier", + range: [11, 12], + name: "b", + }, + body: { + type: "BlockStatement", + range: [14, 16], + body: [], + }, + }); + + node = testLintNode("for await (a of b) {}", "ForOfStatement"); + assertEquals(node[0], { + type: "ForOfStatement", + range: [1, 22], + await: true, + left: { + type: "Identifier", + range: [12, 13], + name: "a", + }, + right: { + type: "Identifier", + range: [17, 18], + name: "b", + }, + body: { + type: "BlockStatement", + range: [20, 22], + body: [], + }, + }); +}); + +Deno.test("Plugin - ForStatement", () => { + let node = testLintNode("for (;;) {}", "ForStatement"); + assertEquals(node[0], { + type: "ForStatement", + range: [1, 12], + init: null, + test: null, + update: null, + body: { + type: "BlockStatement", + range: [10, 12], + body: [], + }, + }); + + node = testLintNode("for (a; b; c) {}", "ForStatement"); + assertEquals(node[0], { + type: "ForStatement", + range: [1, 17], + init: { + type: "Identifier", + range: [6, 7], + name: "a", + }, + test: { + type: "Identifier", + range: [9, 10], + name: "b", + }, + update: { + type: "Identifier", + range: [12, 13], + name: "c", + }, + body: { + type: "BlockStatement", + range: [15, 17], + body: [], + }, + }); +}); + +Deno.test("Plugin - IfStatement", () => { + let node = testLintNode("if (foo) {}", "IfStatement"); + assertEquals(node[0], { + type: "IfStatement", + range: [1, 12], + test: { + type: "Identifier", + name: "foo", + range: [5, 8], + }, + consequent: { + type: "BlockStatement", + range: [10, 12], + body: [], + }, + alternate: null, + }); + + node = testLintNode("if (foo) {} else {}", "IfStatement"); + assertEquals(node[0], { + type: "IfStatement", + range: [1, 20], + test: { + type: "Identifier", + name: "foo", + range: [5, 8], + }, + consequent: { + type: "BlockStatement", + range: [10, 12], + body: [], + }, + alternate: { + type: "BlockStatement", + range: [18, 20], + body: [], + }, + }); +}); + +Deno.test("Plugin - LabeledStatement", () => { + const node = testLintNode("foo: {};", "LabeledStatement"); + assertEquals(node[0], { + type: "LabeledStatement", + range: [1, 8], + label: { + type: "Identifier", + name: "foo", + range: [1, 4], + }, + body: { + type: "BlockStatement", + range: [6, 8], + body: [], + }, + }); +}); + +Deno.test("Plugin - ReturnStatement", () => { + let node = testLintNode("return", "ReturnStatement"); + assertEquals(node[0], { + type: "ReturnStatement", + range: [1, 7], + argument: null, + }); + + node = testLintNode("return foo;", "ReturnStatement"); + assertEquals(node[0], { + type: "ReturnStatement", + range: [1, 12], + argument: { + type: "Identifier", + name: "foo", + range: [8, 11], + }, + }); +}); + +Deno.test("Plugin - SwitchStatement", () => { + const node = testLintNode( + `switch (foo) { + case foo: + case bar: + break; + default: + {} + }`, + "SwitchStatement", + ); + assertEquals(node[0], { + type: "SwitchStatement", + range: [1, 94], + discriminant: { + type: "Identifier", + range: [9, 12], + name: "foo", + }, + cases: [ + { + type: "SwitchCase", + range: [22, 31], + test: { + type: "Identifier", + range: [27, 30], + name: "foo", + }, + consequent: [], + }, + { + type: "SwitchCase", + range: [38, 62], + test: { + type: "Identifier", + range: [43, 46], + name: "bar", + }, + consequent: [ + { + type: "BreakStatement", + label: null, + range: [56, 62], + }, + ], + }, + { + type: "SwitchCase", + range: [69, 88], + test: null, + consequent: [ + { + type: "BlockStatement", + range: [86, 88], + body: [], + }, + ], + }, + ], + }); +}); + +Deno.test("Plugin - ThrowStatement", () => { + const node = testLintNode("throw foo;", "ThrowStatement"); + assertEquals(node[0], { + type: "ThrowStatement", + range: [1, 11], + argument: { + type: "Identifier", + range: [7, 10], + name: "foo", + }, + }); +}); + +Deno.test("Plugin - TryStatement", () => { + let node = testLintNode("try {} catch {};", "TryStatement"); + assertEquals(node[0], { + type: "TryStatement", + range: [1, 16], + block: { + type: "BlockStatement", + range: [5, 7], + body: [], + }, + handler: { + type: "CatchClause", + range: [8, 16], + param: null, + body: { + type: "BlockStatement", + range: [14, 16], + body: [], + }, + }, + finalizer: null, + }); + + node = testLintNode("try {} catch (e) {};", "TryStatement"); + assertEquals(node[0], { + type: "TryStatement", + range: [1, 20], + block: { + type: "BlockStatement", + range: [5, 7], + body: [], + }, + handler: { + type: "CatchClause", + range: [8, 20], + param: { + type: "Identifier", + range: [15, 16], + name: "e", + }, + body: { + type: "BlockStatement", + range: [18, 20], + body: [], + }, + }, + finalizer: null, + }); + + node = testLintNode("try {} finally {};", "TryStatement"); + assertEquals(node[0], { + type: "TryStatement", + range: [1, 18], + block: { + type: "BlockStatement", + range: [5, 7], + body: [], + }, + handler: null, + finalizer: { + type: "BlockStatement", + range: [16, 18], + body: [], + }, + }); +}); + +Deno.test("Plugin - WhileStatement", () => { + const node = testLintNode("while (foo) {}", "WhileStatement"); + assertEquals(node[0], { + type: "WhileStatement", + range: [1, 15], + test: { + type: "Identifier", + range: [8, 11], + name: "foo", + }, + body: { + type: "BlockStatement", + range: [13, 15], + body: [], + }, + }); +}); diff --git a/tests/unit/ops_test.ts b/tests/unit/ops_test.ts index 6de55f8b66..631e5c5736 100644 --- a/tests/unit/ops_test.ts +++ b/tests/unit/ops_test.ts @@ -1,6 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -const EXPECTED_OP_COUNT = 12; +const EXPECTED_OP_COUNT = 13; Deno.test(function checkExposedOps() { // @ts-ignore TS doesn't allow to index with symbol From e6869d7fa668017bacf23ad80a52a4168f562e7b Mon Sep 17 00:00:00 2001 From: David Sherret Date: Sat, 21 Dec 2024 10:00:18 -0500 Subject: [PATCH 032/107] fix(node): handle cjs exports with escaped chars (#27438) Closes https://github.com/denoland/deno/issues/27422 --- resolvers/node/analyze.rs | 26 +++++++++++-------- .../cjs_key_escaped_whitespace/__test__.jsonc | 4 +++ .../node/cjs_key_escaped_whitespace/main.js | 2 ++ .../cjs_key_escaped_whitespace/module.cjs | 6 +++++ .../cjs_key_escaped_whitespace/output.out | 7 +++++ 5 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 tests/specs/node/cjs_key_escaped_whitespace/__test__.jsonc create mode 100644 tests/specs/node/cjs_key_escaped_whitespace/main.js create mode 100644 tests/specs/node/cjs_key_escaped_whitespace/module.cjs create mode 100644 tests/specs/node/cjs_key_escaped_whitespace/output.out diff --git a/resolvers/node/analyze.rs b/resolvers/node/analyze.rs index a444f4d923..9e6219b082 100644 --- a/resolvers/node/analyze.rs +++ b/resolvers/node/analyze.rs @@ -162,7 +162,7 @@ impl add_export( &mut source, export, - &format!("mod[\"{}\"]", escape_for_double_quote_string(export)), + &format!("mod[{}]", to_double_quote_string(export)), &mut temp_var_count, ); } @@ -561,8 +561,8 @@ fn add_export( "const __deno_export_{temp_var_count}__ = {initializer};" )); source.push(format!( - "export {{ __deno_export_{temp_var_count}__ as \"{}\" }};", - escape_for_double_quote_string(name) + "export {{ __deno_export_{temp_var_count}__ as {} }};", + to_double_quote_string(name) )); } else { source.push(format!("export const {name} = {initializer};")); @@ -620,14 +620,9 @@ fn not_found(path: &str, referrer: &Path) -> AnyError { std::io::Error::new(std::io::ErrorKind::NotFound, msg).into() } -fn escape_for_double_quote_string(text: &str) -> Cow { - // this should be rare, so doing a scan first before allocating is ok - if text.chars().any(|c| matches!(c, '"' | '\\')) { - // don't bother making this more complex for perf because it's rare - Cow::Owned(text.replace('\\', "\\\\").replace('"', "\\\"")) - } else { - Cow::Borrowed(text) - } +fn to_double_quote_string(text: &str) -> String { + // serde can handle this for us + serde_json::to_string(text).unwrap() } #[cfg(test)] @@ -665,4 +660,13 @@ mod tests { Some(("@some-package/core".to_string(), "./actions".to_string())) ); } + + #[test] + fn test_to_double_quote_string() { + assert_eq!(to_double_quote_string("test"), "\"test\""); + assert_eq!( + to_double_quote_string("\r\n\t\"test"), + "\"\\r\\n\\t\\\"test\"" + ); + } } diff --git a/tests/specs/node/cjs_key_escaped_whitespace/__test__.jsonc b/tests/specs/node/cjs_key_escaped_whitespace/__test__.jsonc new file mode 100644 index 0000000000..ebaae5bfd6 --- /dev/null +++ b/tests/specs/node/cjs_key_escaped_whitespace/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "run -A main.js", + "output": "output.out" +} diff --git a/tests/specs/node/cjs_key_escaped_whitespace/main.js b/tests/specs/node/cjs_key_escaped_whitespace/main.js new file mode 100644 index 0000000000..9d4f2ee26c --- /dev/null +++ b/tests/specs/node/cjs_key_escaped_whitespace/main.js @@ -0,0 +1,2 @@ +const bang = await import("./module.cjs"); +console.log("imported:", bang); diff --git a/tests/specs/node/cjs_key_escaped_whitespace/module.cjs b/tests/specs/node/cjs_key_escaped_whitespace/module.cjs new file mode 100644 index 0000000000..5accc6196a --- /dev/null +++ b/tests/specs/node/cjs_key_escaped_whitespace/module.cjs @@ -0,0 +1,6 @@ +module.exports = { + "\nx": "test", + "\ty": "test", + "\rz": "test", + '"a': "test", +}; diff --git a/tests/specs/node/cjs_key_escaped_whitespace/output.out b/tests/specs/node/cjs_key_escaped_whitespace/output.out new file mode 100644 index 0000000000..49e92abdec --- /dev/null +++ b/tests/specs/node/cjs_key_escaped_whitespace/output.out @@ -0,0 +1,7 @@ +imported: [Module: null prototype] { + "\ty": "test", + "\nx": "test", + "\rz": "test", + '"a': "test", + default: { "\nx": "test", "\ty": "test", "\rz": "test", '"a': "test" } +} From 2e58e088b09642acf9e4b7f08f89cfc2218985a8 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Mon, 23 Dec 2024 13:56:37 +0900 Subject: [PATCH 033/107] fix(ext/fs): do not throw for bigint ctime/mtime/atime (#27453) --- ext/fs/30_fs.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js index fc2b18be13..4e71acb1b2 100644 --- a/ext/fs/30_fs.js +++ b/ext/fs/30_fs.js @@ -77,6 +77,7 @@ const { Error, Function, MathTrunc, + Number, ObjectEntries, ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, @@ -373,12 +374,12 @@ function parseFileInfo(response) { isDirectory: response.isDirectory, isSymlink: response.isSymlink, size: response.size, - mtime: response.mtimeSet === true ? new Date(response.mtime) : null, - atime: response.atimeSet === true ? new Date(response.atime) : null, + mtime: response.mtimeSet === true ? new Date(Number(response.mtime)) : null, + atime: response.atimeSet === true ? new Date(Number(response.atime)) : null, birthtime: response.birthtimeSet === true ? new Date(response.birthtime) : null, - ctime: response.ctimeSet === true ? new Date(response.ctime) : null, + ctime: response.ctimeSet === true ? new Date(Number(response.ctime)) : null, dev: response.dev, mode: response.mode, ino: unix ? response.ino : null, From 3cc861cdca4fb204fbcf40c9542396965039f31f Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Mon, 23 Dec 2024 13:59:04 +0900 Subject: [PATCH 034/107] fix(ext/fetch): better error message when body resource is unavailable (#27429) fixes #27132 When the body resource is unavailable when start reading it, the error message is `Bad Resource ID` and that doesn't tell what's wrong very well. This PR changes that error message to `Cannot read body as underlying resource unavailable` --- ext/fetch/22_body.js | 15 ++++++++++++++- tests/unit/body_test.ts | 13 ++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ext/fetch/22_body.js b/ext/fetch/22_body.js index a34758d19a..bb2bee77e2 100644 --- a/ext/fetch/22_body.js +++ b/ext/fetch/22_body.js @@ -13,6 +13,7 @@ import { core, primordials } from "ext:core/mod.js"; const { + BadResourcePrototype, isAnyArrayBuffer, isArrayBuffer, isStringObject, @@ -26,6 +27,7 @@ const { JSONParse, ObjectDefineProperties, ObjectPrototypeIsPrototypeOf, + PromisePrototypeCatch, TypedArrayPrototypeGetBuffer, TypedArrayPrototypeGetByteLength, TypedArrayPrototypeGetByteOffset, @@ -160,7 +162,18 @@ class InnerBody { ) ) { readableStreamThrowIfErrored(this.stream); - return readableStreamCollectIntoUint8Array(this.stream); + return PromisePrototypeCatch( + readableStreamCollectIntoUint8Array(this.stream), + (e) => { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, e)) { + // TODO(kt3k): We probably like to pass e as `cause` if BadResource supports it. + throw new e.constructor( + "Cannot read body as underlying resource unavailable", + ); + } + throw e; + }, + ); } else { this.streamOrStatic.consumed = true; return this.streamOrStatic.body; diff --git a/tests/unit/body_test.ts b/tests/unit/body_test.ts index 18cdb22be0..fb51fd0076 100644 --- a/tests/unit/body_test.ts +++ b/tests/unit/body_test.ts @@ -1,5 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "./test_util.ts"; +import { assert, assertEquals, assertRejects } from "./test_util.ts"; // just a hack to get a body object // deno-lint-ignore no-explicit-any @@ -187,3 +187,14 @@ Deno.test( assertEquals(file.size, 1); }, ); + +Deno.test(async function bodyBadResourceError() { + const file = await Deno.open("README.md"); + file.close(); + const body = buildBody(file.readable); + await assertRejects( + () => body.arrayBuffer(), + Deno.errors.BadResource, + "Cannot read body as underlying resource unavailable", + ); +}); From 1a809b811559b5d77039eb7b270482816766ab74 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Mon, 23 Dec 2024 08:45:47 +0100 Subject: [PATCH 035/107] feat(unstable): support selectors in JS lint plugins (#27452) This PR adds support for using selectors in the JS linting plugin API. Supported at the moment are: - `Foo Bar` (descendant) - `Foo > Bar` (child combinator) - `Foo + Foo` (next sibling) - `Foo ~ Foo` (subsequent sibling) - `[attr]`, `[attr=value]` (attribute selectors, supported operators: `=`, `!=`, `<`, `>`, `<=`, `>=`) - `:first-child` - `:last-child` - `:nth-child(2)`, `:nth-child(2n + 1)` --- cli/js/40_lint.js | 402 +++++++++- cli/js/40_lint_selector.js | 1014 ++++++++++++++++++++++++ cli/js/40_lint_types.d.ts | 84 +- cli/tools/lint/ast_buffer/buffer.rs | 2 + cli/tools/lint/ast_buffer/ts_estree.rs | 2 + cli/worker.rs | 2 + tests/integration/js_unit_tests.rs | 1 + tests/unit/lint_plugin_test.ts | 208 ++++- tests/unit/lint_selectors_test.ts | 610 ++++++++++++++ tools/core_import_map.json | 1 + 10 files changed, 2272 insertions(+), 54 deletions(-) create mode 100644 cli/js/40_lint_selector.js create mode 100644 tests/unit/lint_selectors_test.ts diff --git a/cli/js/40_lint.js b/cli/js/40_lint.js index 9606f787b3..30b1f86884 100644 --- a/cli/js/40_lint.js +++ b/cli/js/40_lint.js @@ -2,6 +2,11 @@ // @ts-check +import { + compileSelector, + parseSelector, + splitSelectors, +} from "ext:cli/40_lint_selector.js"; import { core, internals } from "ext:core/mod.js"; const { op_lint_create_serialized_ast, @@ -13,6 +18,7 @@ const { const AST_PROP_TYPE = 0; const AST_PROP_PARENT = 1; const AST_PROP_RANGE = 2; +const AST_PROP_LENGTH = 3; // Keep in sync with Rust // Each node property is tagged with this enum to denote @@ -43,8 +49,8 @@ const PropFlags = { /** @typedef {import("./40_lint_types.d.ts").RuleContext} RuleContext */ /** @typedef {import("./40_lint_types.d.ts").NodeFacade} NodeFacade */ /** @typedef {import("./40_lint_types.d.ts").LintPlugin} LintPlugin */ -/** @typedef {import("./40_lint_types.d.ts").LintReportData} LintReportData */ -/** @typedef {import("./40_lint_types.d.ts").TestReportData} TestReportData */ +/** @typedef {import("./40_lint_types.d.ts").TransformFn} TransformFn */ +/** @typedef {import("./40_lint_types.d.ts").MatchContext} MatchContext */ /** @type {LintState} */ const state = { @@ -99,7 +105,6 @@ export function installPlugin(plugin) { */ function getNode(ctx, offset) { if (offset === 0) return null; - const cached = ctx.nodes.get(offset); if (cached !== undefined) return cached; @@ -297,9 +302,10 @@ function readValue(ctx, offset, search) { if (offset === -1) return undefined; const kind = buf[offset + 1]; + offset += 2; if (kind === PropFlags.Ref) { - const value = readU32(buf, offset + 2); + const value = readU32(buf, offset); return getNode(ctx, value); } else if (kind === PropFlags.RefArr) { const len = readU32(buf, offset); @@ -353,6 +359,303 @@ function getString(strTable, id) { return name; } +/** + * @param {AstContext["buf"]} buf + * @param {number} child + * @returns {null | [number, number]} + */ +function findChildOffset(buf, child) { + let offset = readU32(buf, child + 1); + + // type + parentId + SpanLo + SpanHi + offset += 1 + 4 + 4 + 4; + + const propCount = buf[offset++]; + for (let i = 0; i < propCount; i++) { + const _prop = buf[offset++]; + const kind = buf[offset++]; + + switch (kind) { + case PropFlags.Ref: { + const start = offset; + const value = readU32(buf, offset); + offset += 4; + if (value === child) { + return [start, -1]; + } + break; + } + case PropFlags.RefArr: { + const start = offset; + + const len = readU32(buf, offset); + offset += 4; + + for (let j = 0; j < len; j++) { + const value = readU32(buf, offset); + offset += 4; + if (value === child) { + return [start, j]; + } + } + + break; + } + case PropFlags.String: + offset += 4; + break; + case PropFlags.Bool: + offset++; + break; + case PropFlags.Null: + case PropFlags.Undefined: + break; + } + } + + return null; +} + +/** @implements {MatchContext} */ +class MatchCtx { + /** + * @param {AstContext["buf"]} buf + * @param {AstContext["strTable"]} strTable + */ + constructor(buf, strTable) { + this.buf = buf; + this.strTable = strTable; + } + + /** + * @param {number} offset + * @returns {number} + */ + getParent(offset) { + return readU32(this.buf, offset + 1); + } + + /** + * @param {number} offset + * @returns {number} + */ + getType(offset) { + return this.buf[offset]; + } + + /** + * @param {number} offset + * @param {number[]} propIds + * @param {number} idx + * @returns {unknown} + */ + getAttrPathValue(offset, propIds, idx) { + const { buf } = this; + + offset = findPropOffset(buf, offset, propIds[idx]); + if (offset === -1) return undefined; + const _prop = buf[offset++]; + const kind = buf[offset++]; + + if (kind === PropFlags.Ref) { + const value = readU32(buf, offset); + // Checks need to end with a value, not a node + if (idx === propIds.length - 1) return undefined; + return this.getAttrPathValue(value, propIds, idx + 1); + } else if (kind === PropFlags.RefArr) { + const count = readU32(buf, offset); + offset += 4; + + if (idx < propIds.length - 1 && propIds[idx + 1] === AST_PROP_LENGTH) { + return count; + } + + // TODO(@marvinhagemeister): Allow traversing into array children? + } + + // Cannot traverse into primitives further + if (idx < propIds.length - 1) return undefined; + + if (kind === PropFlags.String) { + const s = readU32(buf, offset); + return getString(this.strTable, s); + } else if (kind === PropFlags.Bool) { + return buf[offset] === 1; + } else if (kind === PropFlags.Null) { + return null; + } else if (kind === PropFlags.Undefined) { + return undefined; + } + + return undefined; + } + + /** + * @param {number} offset + * @param {number[]} propIds + * @param {number} idx + * @returns {boolean} + */ + hasAttrPath(offset, propIds, idx) { + const { buf } = this; + + offset = findPropOffset(buf, offset, propIds[idx]); + if (offset === -1) return false; + if (idx === propIds.length - 1) return true; + + const _prop = buf[offset++]; + const kind = buf[offset++]; + if (kind === PropFlags.Ref) { + const value = readU32(buf, offset); + return this.hasAttrPath(value, propIds, idx + 1); + } else if (kind === PropFlags.RefArr) { + const _count = readU32(buf, offset); + offset += 4; + + if (idx < propIds.length - 1 && propIds[idx + 1] === AST_PROP_LENGTH) { + return true; + } + + // TODO(@marvinhagemeister): Allow traversing into array children? + } + + // Primitives cannot be traversed further. This means we + // didn't found the attribute. + if (idx < propIds.length - 1) return false; + + return true; + } + + /** + * @param {number} offset + * @returns {number} + */ + getFirstChild(offset) { + const { buf } = this; + + // type + parentId + SpanLo + SpanHi + offset += 1 + 4 + 4 + 4; + + const count = buf[offset++]; + for (let i = 0; i < count; i++) { + const _prop = buf[offset++]; + const kind = buf[offset++]; + + switch (kind) { + case PropFlags.Ref: { + const v = readU32(buf, offset); + offset += 4; + return v; + } + case PropFlags.RefArr: { + const len = readU32(buf, offset); + offset += 4; + for (let j = 0; j < len; j++) { + const v = readU32(buf, offset); + offset += 4; + return v; + } + + return len; + } + + case PropFlags.String: + offset += 4; + break; + case PropFlags.Bool: + offset++; + break; + case PropFlags.Null: + case PropFlags.Undefined: + break; + } + } + + return -1; + } + + /** + * @param {number} offset + * @returns {number} + */ + getLastChild(offset) { + const { buf } = this; + + // type + parentId + SpanLo + SpanHi + offset += 1 + 4 + 4 + 4; + + let last = -1; + + const count = buf[offset++]; + for (let i = 0; i < count; i++) { + const _prop = buf[offset++]; + const kind = buf[offset++]; + + switch (kind) { + case PropFlags.Ref: { + const v = readU32(buf, offset); + offset += 4; + last = v; + break; + } + case PropFlags.RefArr: { + const len = readU32(buf, offset); + offset += 4; + for (let j = 0; j < len; j++) { + const v = readU32(buf, offset); + last = v; + offset += 4; + } + + break; + } + + case PropFlags.String: + offset += 4; + break; + case PropFlags.Bool: + offset++; + break; + case PropFlags.Null: + case PropFlags.Undefined: + break; + } + } + + return last; + } + + /** + * @param {number} id + * @returns {number[]} + */ + getSiblings(id) { + const { buf } = this; + + const result = findChildOffset(buf, id); + // Happens for program nodes + if (result === null) return []; + + if (result[1] === -1) { + return [id]; + } + + let offset = result[0]; + const count = readU32(buf, offset); + offset += 4; + + /** @type {number[]} */ + const out = []; + for (let i = 0; i < count; i++) { + const v = readU32(buf, offset); + offset += 4; + out.push(v); + } + + return out; + } +} + /** * @param {Uint8Array} buf * @param {AstContext} buf @@ -433,6 +736,7 @@ function createAstContext(buf) { strByType, typeByStr, propByStr, + matcher: new MatchCtx(buf, strTable), }; setNodeGetters(ctx); @@ -456,7 +760,7 @@ const NOOP = (_node) => {}; export function runPluginsForFile(fileName, serializedAst) { const ctx = createAstContext(serializedAst); - /** @type {Map} */ + /** @type {Map}>} */ const bySelector = new Map(); const destroyFns = []; @@ -486,32 +790,38 @@ export function runPluginsForFile(fileName, serializedAst) { key = key.slice(0, -":exit".length); } - let info = bySelector.get(key); - if (info === undefined) { - info = { enter: NOOP, exit: NOOP }; - bySelector.set(key, info); - } - const prevFn = isExit ? info.exit : info.enter; + const selectors = splitSelectors(key); - /** - * @param {*} node - */ - const wrapped = (node) => { - prevFn(node); + for (let j = 0; j < selectors.length; j++) { + const key = selectors[j]; - try { - fn(node); - } catch (err) { - throw new Error(`Visitor "${name}" of plugin "${id}" errored`, { - cause: err, - }); + let info = bySelector.get(key); + if (info === undefined) { + info = { enter: NOOP, exit: NOOP }; + bySelector.set(key, info); } - }; + const prevFn = isExit ? info.exit : info.enter; - if (isExit) { - info.exit = wrapped; - } else { - info.enter = wrapped; + /** + * @param {*} node + */ + const wrapped = (node) => { + prevFn(node); + + try { + fn(node); + } catch (err) { + throw new Error(`Visitor "${name}" of plugin "${id}" errored`, { + cause: err, + }); + } + }; + + if (isExit) { + info.exit = wrapped; + } else { + info.enter = wrapped; + } } } @@ -528,25 +838,27 @@ export function runPluginsForFile(fileName, serializedAst) { } } + // Create selectors + /** @type {TransformFn} */ + const toElem = (str) => { + const id = ctx.typeByStr.get(str); + return id === undefined ? 0 : id; + }; + /** @type {TransformFn} */ + const toAttr = (str) => { + const id = ctx.propByStr.get(str); + return id === undefined ? 0 : id; + }; + /** @type {CompiledVisitor[]} */ const visitors = []; for (const [sel, info] of bySelector.entries()) { - // This will make more sense once selectors land as it's faster - // to precompile them once upfront. + // Selectors are already split here. + // TODO(@marvinhagemeister): Avoid array allocation (not sure if that matters) + const parsed = parseSelector(sel, toElem, toAttr)[0]; + const matcher = compileSelector(parsed); - // Convert the visiting element name to a number. This number - // is part of the serialized buffer and comparing a single number - // is quicker than strings. - const elemId = ctx.typeByStr.get(sel) ?? -1; - - visitors.push({ - info, - // Check if we should call this visitor - matcher: (offset) => { - const type = ctx.buf[offset]; - return type === elemId; - }, - }); + visitors.push({ info, matcher }); } // Traverse ast with all visitors at the same time to avoid traversing @@ -572,6 +884,8 @@ function traverse(ctx, visitors, offset) { // The 0 offset is used to denote an empty/placeholder node if (offset === 0) return; + const originalOffset = offset; + const { buf } = ctx; /** @type {VisitorFn[] | null} */ @@ -580,7 +894,7 @@ function traverse(ctx, visitors, offset) { for (let i = 0; i < visitors.length; i++) { const v = visitors[i]; - if (v.matcher(offset)) { + if (v.matcher(ctx.matcher, offset)) { if (v.info.exit !== NOOP) { if (exits === null) { exits = [v.info.exit]; @@ -633,7 +947,7 @@ function traverse(ctx, visitors, offset) { } finally { if (exits !== null) { for (let i = 0; i < exits.length; i++) { - const node = /** @type {*} */ (getNode(ctx, offset)); + const node = /** @type {*} */ (getNode(ctx, originalOffset)); exits[i](node); } } diff --git a/cli/js/40_lint_selector.js b/cli/js/40_lint_selector.js new file mode 100644 index 0000000000..b78f7a5d0e --- /dev/null +++ b/cli/js/40_lint_selector.js @@ -0,0 +1,1014 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// @ts-check + +/** @typedef {import("./40_lint_types.d.ts").LintState} LintState */ +/** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */ +/** @typedef {import("./40_lint_types.d.ts").MatchContext} MatchCtx */ +/** @typedef {import("./40_lint_types.d.ts").AttrExists} AttrExists */ +/** @typedef {import("./40_lint_types.d.ts").AttrBin} AttrBin */ +/** @typedef {import("./40_lint_types.d.ts").AttrSelector} AttrSelector */ +/** @typedef {import("./40_lint_types.d.ts").ElemSelector} ElemSelector */ +/** @typedef {import("./40_lint_types.d.ts").PseudoNthChild} PseudoNthChild */ +/** @typedef {import("./40_lint_types.d.ts").PseudoHas} PseudoHas */ +/** @typedef {import("./40_lint_types.d.ts").PseudoNot} PseudoNot */ +/** @typedef {import("./40_lint_types.d.ts").Relation} SRelation */ +/** @typedef {import("./40_lint_types.d.ts").Selector} Selector */ +/** @typedef {import("./40_lint_types.d.ts").SelectorParseCtx} SelectorParseCtx */ +/** @typedef {import("./40_lint_types.d.ts").NextFn} NextFn */ +/** @typedef {import("./40_lint_types.d.ts").MatcherFn} MatcherFn */ +/** @typedef {import("./40_lint_types.d.ts").TransformFn} Transformer */ + +const Char = { + Tab: 9, + Space: 32, + Bang: 33, + DoubleQuote: 34, + Quote: 39, + BraceOpen: 40, + BraceClose: 41, + Plus: 43, + Comma: 44, + Minus: 45, + Dot: 46, + Slash: 47, + n0: 49, + n9: 57, + Colon: 58, + Less: 60, + Equal: 61, + Greater: 62, + A: 65, + Z: 90, + BracketOpen: 91, + BackSlash: 92, + BracketClose: 93, + Underscore: 95, + a: 97, + z: 122, + Tilde: 126, +}; + +export const Token = { + EOF: 0, + Word: 1, + Space: 2, + Op: 3, + Colon: 4, + Comma: 7, + BraceOpen: 8, + BraceClose: 9, + BracketOpen: 10, + BracketClose: 11, + String: 12, + Number: 13, + Bool: 14, + Null: 15, + Undefined: 16, + Dot: 17, + Minus: 17, +}; + +export const BinOp = { + /** [attr="value"] or [attr=value] */ + Equal: 1, + /** [attr!="value"] or [attr!=value] */ + NotEqual: 2, + /** [attr>1] */ + Greater: 3, + /** [attr>=1] */ + GreaterThan: 4, + /** [attr<1] */ + Less: 5, + /** [attr<=1] */ + LessThan: 6, + Tilde: 7, + Plus: 8, + Space: 9, +}; + +/** + * @param {string} s + * @returns {number} + */ +function getAttrOp(s) { + switch (s) { + case "=": + return BinOp.Equal; + case "!=": + return BinOp.NotEqual; + case ">": + return BinOp.Greater; + case ">=": + return BinOp.GreaterThan; + case "<": + return BinOp.Less; + case "<=": + return BinOp.LessThan; + case "~": + return BinOp.Tilde; + case "+": + return BinOp.Plus; + default: + throw new Error(`Unknown attribute operator: '${s}'`); + } +} + +export class Lexer { + token = Token.Word; + start = 0; + end = 0; + ch = 0; + i = -1; + + value = ""; + + /** + * @param {string} input + */ + constructor(input) { + this.input = input; + this.step(); + this.next(); + } + + /** + * @param {number} token + */ + expect(token) { + if (this.token !== token) { + throw new Error( + `Expected token '${token}', but got '${this.token}'.\n\n${this.input}\n${ + " ".repeat(this.i) + }^`, + ); + } + } + + /** + * @param {number} token + */ + readAsWordUntil(token) { + const s = this.i; + while (this.token !== Token.EOF && this.token !== token) { + this.next(); + } + + this.start = s; + this.end = this.i - 1; + this.value = this.getSlice(); + } + + getSlice() { + return this.input.slice(this.start, this.end); + } + + step() { + this.i++; + if (this.i >= this.input.length) { + this.ch = -1; + } else { + this.ch = this.input.charCodeAt(this.i); + } + } + + next() { + this.value = ""; + + if (this.i >= this.input.length) { + this.token = Token.EOF; + return; + } + + // console.log( + // "NEXT", + // this.input, + // this.i, + // JSON.stringify(String.fromCharCode(this.ch)), + // ); + + while (true) { + switch (this.ch) { + case Char.Space: + while (this.isWhiteSpace()) { + this.step(); + } + + // Check if space preceeded operator + if (this.isOpContinue()) { + continue; + } + + this.token = Token.Space; + return; + case Char.BracketOpen: + this.token = Token.BracketOpen; + this.step(); + return; + case Char.BracketClose: + this.token = Token.BracketClose; + this.step(); + return; + case Char.BraceOpen: + this.token = Token.BraceOpen; + this.step(); + return; + case Char.BraceClose: + this.token = Token.BraceClose; + this.step(); + return; + case Char.Colon: + this.token = Token.Colon; + this.step(); + return; + case Char.Comma: + this.token = Token.Comma; + this.step(); + return; + case Char.Dot: + this.token = Token.Dot; + this.step(); + return; + case Char.Minus: + this.token = Token.Minus; + this.step(); + return; + + case Char.Plus: + case Char.Tilde: + case Char.Greater: + case Char.Equal: + case Char.Less: + case Char.Bang: { + this.token = Token.Op; + this.start = this.i; + this.step(); + + while (this.isOpContinue()) { + this.step(); + } + + this.end = this.i; + this.value = this.getSlice(); + + // Consume remaining space + while (this.isWhiteSpace()) { + this.step(); + } + + return; + } + + case Char.Quote: + case Char.DoubleQuote: { + this.token = Token.String; + const ch = this.ch; + + this.step(); + this.start = this.i; + + while (this.ch > 0 && this.ch !== ch) { + this.step(); + } + + this.end = this.i; + this.value = this.getSlice(); + this.step(); + + return; + } + + default: + this.start = this.i; + this.step(); + + while (this.isWordContinue()) { + this.step(); + } + + this.end = this.i; + this.value = this.getSlice(); + this.token = Token.Word; + return; + } + } + } + + isWordContinue() { + const ch = this.ch; + switch (ch) { + case Char.Minus: + case Char.Underscore: + return true; + default: + return (ch >= Char.a && ch <= Char.z) || + (ch >= Char.A && ch <= Char.Z) || + (ch >= Char.n0 && ch <= Char.n9); + } + } + + isOpContinue() { + const ch = this.ch; + switch (ch) { + case Char.Equal: + case Char.Bang: + case Char.Greater: + case Char.Less: + case Char.Tilde: + case Char.Plus: + return true; + default: + return false; + } + } + + isWhiteSpace() { + return this.ch === Char.Space || this.ch === Char.Tab; + } +} + +const NUMBER_REG = /^(\d+\.)?\d+$/; +const BIGINT_REG = /^\d+n$/; + +/** + * @param {string} raw + * @returns {any} + */ +function getFromRawValue(raw) { + switch (raw) { + case "true": + return true; + case "false": + return false; + case "null": + return null; + case "undefined": + return undefined; + default: + if (raw.startsWith("'") && raw.endsWith("'")) { + if (raw.length === 2) return ""; + return raw.slice(1, -1); + } else if (raw.startsWith('"') && raw.endsWith('"')) { + if (raw.length === 2) return ""; + return raw.slice(1, -1); + } else if (raw.startsWith("/")) { + const end = raw.lastIndexOf("/"); + if (end === -1) throw new Error(`Invalid RegExp pattern: ${raw}`); + const pattern = raw.slice(1, end); + const flags = end < raw.length - 1 ? raw.slice(end + 1) : undefined; + return new RegExp(pattern, flags); + } else if (NUMBER_REG.test(raw)) { + return Number(raw); + } else if (BIGINT_REG.test(raw)) { + return BigInt(raw.slice(0, -1)); + } + + return raw; + } +} + +export const ELEM_NODE = 1; +export const RELATION_NODE = 2; +export const ATTR_EXISTS_NODE = 3; +export const ATTR_BIN_NODE = 4; +export const PSEUDO_NTH_CHILD = 5; +export const PSEUDO_HAS = 6; +export const PSEUDO_NOT = 7; +export const PSEUDO_FIRST_CHILD = 8; +export const PSEUDO_LAST_CHILD = 9; + +/** + * Parse out all unique selectors of a selector list. + * @param {string} input + * @returns {string[]} + */ +export function splitSelectors(input) { + /** @type {string[]} */ + const out = []; + + let last = 0; + let depth = 0; + for (let i = 0; i < input.length; i++) { + const ch = input.charCodeAt(i); + switch (ch) { + case Char.BraceOpen: + depth++; + break; + case Char.BraceClose: + depth--; + break; + case Char.Comma: + if (depth === 0) { + out.push(input.slice(last, i).trim()); + last = i + 1; + } + break; + } + } + + if (last < input.length - 1) { + out.push(input.slice(last).trim()); + } + + return out; +} + +/** + * @param {string} input + * @param {Transformer} toElem + * @param {Transformer} toAttr + * @returns {Selector[]} + */ +export function parseSelector(input, toElem, toAttr) { + /** @type {Selector[]} */ + const result = []; + + /** @type {Selector[]} */ + const stack = [[]]; + + const lex = new Lexer(input); + + // Some subselectors like `:nth-child(.. of )` must have + // a single selector instead of selector list. + let throwOnComma = false; + + while (lex.token !== Token.EOF) { + const current = /** @type {Selector} */ (stack.at(-1)); + + if (lex.token === Token.Word) { + const value = lex.value; + const wildcard = value === "*"; + + const elem = !wildcard ? toElem(value) : 0; + current.push({ + type: ELEM_NODE, + elem, + wildcard, + }); + lex.next(); + + continue; + } else if (lex.token === Token.Space) { + lex.next(); + + if (lex.token === Token.Word) { + current.push({ + type: RELATION_NODE, + op: BinOp.Space, + }); + } + + continue; + } else if (lex.token === Token.BracketOpen) { + lex.next(); + lex.expect(Token.Word); + + // Check for value comparison + const prop = [toAttr(lex.value)]; + lex.next(); + + while (lex.token === Token.Dot) { + lex.next(); + lex.expect(Token.Word); + + prop.push(toAttr(lex.value)); + lex.next(); + } + + if (lex.token === Token.Op) { + const op = getAttrOp(lex.value); + lex.readAsWordUntil(Token.BracketClose); + + const value = getFromRawValue(lex.value); + current.push({ type: ATTR_BIN_NODE, prop, op, value }); + } else { + current.push({ + type: ATTR_EXISTS_NODE, + prop, + }); + } + + lex.expect(Token.BracketClose); + lex.next(); + continue; + } else if (lex.token === Token.Colon) { + lex.next(); + lex.expect(Token.Word); + + switch (lex.value) { + case "first-child": + current.push({ + type: PSEUDO_FIRST_CHILD, + }); + break; + case "last-child": + current.push({ + type: PSEUDO_LAST_CHILD, + }); + break; + case "nth-child": { + lex.next(); + lex.expect(Token.BraceOpen); + lex.next(); + + let mul = 1; + let repeat = false; + let step = 0; + if (lex.token === Token.Minus) { + mul = -1; + lex.next(); + } + + lex.expect(Token.Word); + const value = lex.getSlice(); + + if (value.endsWith("n")) { + repeat = true; + step = +value.slice(0, -1) * mul; + } else { + step = +value * mul; + } + + lex.next(); + + /** @type {PseudoNthChild} */ + const node = { + type: PSEUDO_NTH_CHILD, + of: null, + op: null, + step, + stepOffset: 0, + repeat, + }; + current.push(node); + + if (lex.token === Token.Space) lex.next(); + + if (lex.token !== Token.BraceClose) { + if (lex.token === Token.Op) { + node.op = lex.value; + lex.next(); + + if (lex.token === Token.Space) lex.next(); + } else if (lex.token === Token.Minus) { + node.op = "-"; + lex.next(); + + if (lex.token === Token.Space) { + lex.next(); + } + } + + lex.expect(Token.Word); + node.stepOffset = +lex.value; + lex.next(); + + if (lex.token !== Token.BraceClose) { + lex.next(); // Space + + if (lex.token === Token.Word) { + if (/** @type {string} */ (lex.value) !== "of") { + throw new Error( + `Expected 'of' keyword in ':nth-child' but got: ${lex.value}`, + ); + } + + lex.next(); + lex.expect(Token.Space); + lex.next(); + throwOnComma = true; + stack.push([]); + } + + continue; + } + + lex.expect(Token.BraceClose); + } else if (!node.repeat) { + // :nth-child(2) -> step is actually stepOffset + node.stepOffset = node.step - 1; + node.step = 0; + } + + lex.next(); + + continue; + } + + case "has": + case "where": + case "is": { + lex.next(); + lex.expect(Token.BraceOpen); + lex.next(); + + current.push({ + type: PSEUDO_HAS, + selectors: [], + }); + stack.push([]); + + continue; + } + case "not": { + lex.next(); + lex.expect(Token.BraceOpen); + lex.next(); + + current.push({ + type: PSEUDO_NOT, + selectors: [], + }); + stack.push([]); + + continue; + } + default: + throw new Error(`Unknown pseudo selector: '${lex.value}'`); + } + } else if (lex.token === Token.Comma) { + if (throwOnComma) { + throw new Error(`Multiple selector arguments not supported here`); + } + + lex.next(); + if (lex.token === Token.Space) { + lex.next(); + } + + popSelector(result, stack); + stack.push([]); + continue; + } else if (lex.token === Token.BraceClose) { + throwOnComma = false; + popSelector(result, stack); + } else if (lex.token === Token.Op) { + current.push({ + type: RELATION_NODE, + op: getAttrOp(lex.value), + }); + } + + lex.next(); + } + + if (stack.length > 0) { + result.push(stack[0]); + } + + return result; +} + +/** + * @param {Selector[]} result + * @param {Selector[]} stack + */ +function popSelector(result, stack) { + const sel = /** @type {Selector} */ (stack.pop()); + + if (stack.length === 0) { + result.push(sel); + stack.push([]); + } else { + const prev = /** @type {Selector} */ (stack.at(-1)); + if (prev.length === 0) { + throw new Error(`Empty selector`); + } + + const node = prev.at(-1); + if (node === undefined) { + throw new Error(`Empty node`); + } + + if (node.type === PSEUDO_NTH_CHILD) { + node.of = sel; + } else if (node.type === PSEUDO_HAS || node.type === PSEUDO_NOT) { + node.selectors.push(sel); + } else { + throw new Error(`Multiple selectors not allowed here`); + } + } +} + +const TRUE_FN = () => { + return true; +}; + +/** + * @param {Selector} selector + * @returns {MatcherFn} + */ +export function compileSelector(selector) { + /** @type {MatcherFn} */ + let fn = TRUE_FN; + + for (let i = 0; i < selector.length; i++) { + const node = selector[i]; + + switch (node.type) { + case ELEM_NODE: + fn = matchElem(node, fn); + break; + case RELATION_NODE: + switch (node.op) { + case BinOp.Space: + fn = matchDescendant(fn); + break; + case BinOp.Greater: + fn = matchChild(fn); + break; + case BinOp.Plus: + fn = matchAdjacent(fn); + break; + case BinOp.Tilde: + fn = matchFollowing(fn); + break; + default: + throw new Error(`Unknown relation op ${node.op}`); + } + break; + case ATTR_EXISTS_NODE: + fn = matchAttrExists(node, fn); + break; + case ATTR_BIN_NODE: + fn = matchAttrBin(node, fn); + break; + case PSEUDO_FIRST_CHILD: + fn = matchFirstChild(fn); + break; + case PSEUDO_LAST_CHILD: + fn = matchLastChild(fn); + break; + case PSEUDO_NTH_CHILD: + fn = matchNthChild(node, fn); + break; + case PSEUDO_HAS: + // FIXME + // fn = matchIs(part, fn); + throw new Error("TODO: :has"); + case PSEUDO_NOT: + fn = matchNot(node.selectors, fn); + break; + default: + // @ts-ignore error handling + // deno-lint-ignore no-console + console.log(node); + throw new Error(`Unknown selector node`); + } + } + + return fn; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchFirstChild(next) { + return (ctx, id) => { + const parent = ctx.getParent(id); + const first = ctx.getFirstChild(parent); + return first === id && next(ctx, first); + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchLastChild(next) { + return (ctx, id) => { + const parent = ctx.getParent(id); + const last = ctx.getLastChild(parent); + return last === id && next(ctx, id); + }; +} + +/** + * @param {PseudoNthChild} node + * @param {number} i + * @returns {number} + */ +function getNthAnB(node, i) { + const n = node.step * i; + + if (node.op === null) return n; + + switch (node.op) { + case "+": + return n + node.stepOffset; + case "-": + return n - node.stepOffset; + default: + throw new Error("Not supported nth-child operator: " + node.op); + } +} + +/** + * @param {PseudoNthChild} node + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchNthChild(node, next) { + const ofSelector = node.of !== null ? compileSelector(node.of) : TRUE_FN; + + // TODO(@marvinhagemeister): we should probably cache results here + + return (ctx, id) => { + const siblings = ctx.getSiblings(id); + const idx = siblings.indexOf(id); + + if (!node.repeat) { + return idx === node.stepOffset && next(ctx, id); + } + + for (let i = 0; i < siblings.length; i++) { + const n = getNthAnB(node, i); + + if (n > siblings.length - 1) return false; + + const search = siblings[n]; + if (id === search) { + if (node.of !== null && !ofSelector(ctx, id)) { + continue; + } else if (next(ctx, id)) { + return true; + } + } else if (n > idx) { + return false; + } + } + + return false; + }; +} + +/** + * @param {Selector[]} selectors + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchNot(selectors, next) { + /** @type {MatcherFn[]} */ + const compiled = []; + + for (let i = 0; i < selectors.length; i++) { + const sel = selectors[i]; + compiled.push(compileSelector(sel)); + } + + return (ctx, id) => { + for (let i = 0; i < compiled.length; i++) { + const fn = compiled[i]; + if (fn(ctx, id)) { + return false; + } + } + + return next(ctx, id); + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchDescendant(next) { + // TODO(@marvinhagemeister): we should probably cache results here + return (ctx, id) => { + let current = ctx.getParent(id); + while (current > 0) { + if (next(ctx, current)) { + return true; + } + + current = ctx.getParent(current); + } + + return false; + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchChild(next) { + return (ctx, id) => { + const parent = ctx.getParent(id); + if (parent < 0) return false; + + return next(ctx, parent); + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchAdjacent(next) { + return (ctx, id) => { + const siblings = ctx.getSiblings(id); + const idx = siblings.indexOf(id) - 1; + + if (idx < 0) return false; + + const prev = siblings[idx]; + return next(ctx, prev); + }; +} + +/** + * @param {NextFn} next + * @returns {MatcherFn} + */ +function matchFollowing(next) { + return (ctx, id) => { + const siblings = ctx.getSiblings(id); + const idx = siblings.indexOf(id) - 1; + + if (idx < 0) return false; + + for (let i = idx; i >= 0; i--) { + const sib = siblings[i]; + if (next(ctx, sib)) return true; + } + + return false; + }; +} + +/** + * @param {ElemSelector} part + * @param {MatcherFn} next + * @returns {MatcherFn} + */ +function matchElem(part, next) { + return (ctx, id) => { + // Placeholder node cannot be matched + if (id === 0) return false; + // Wildcard always matches + else if (part.wildcard) return next(ctx, id); + // 0 means it's the placeholder node which + // can never be matched. + else if (part.elem === 0) return false; + + const type = ctx.getType(id); + if (type > 0 && type === part.elem) return next(ctx, id); + + return false; + }; +} + +/** + * @param {AttrExists} attr + * @param {MatcherFn} next + * @returns {MatcherFn} + */ +function matchAttrExists(attr, next) { + return (ctx, id) => { + return ctx.hasAttrPath(id, attr.prop, 0) ? next(ctx, id) : false; + }; +} + +/** + * @param {AttrBin} attr + * @param {MatcherFn} next + * @returns {MatcherFn} + */ +function matchAttrBin(attr, next) { + return (ctx, id) => { + if (!ctx.hasAttrPath(id, attr.prop, 0)) return false; + const value = ctx.getAttrPathValue(id, attr.prop, 0); + if (!matchAttrValue(attr, value)) return false; + return next(ctx, id); + }; +} + +/** + * @param {AttrBin} attr + * @param {*} value + * @returns {boolean} + */ +function matchAttrValue(attr, value) { + switch (attr.op) { + case BinOp.Equal: + return value === attr.value; + case BinOp.NotEqual: + return value !== attr.value; + case BinOp.Greater: + return typeof value === "number" && typeof attr.value === "number" && + value > attr.value; + case BinOp.GreaterThan: + return typeof value === "number" && typeof attr.value === "number" && + value >= attr.value; + case BinOp.Less: + return typeof value === "number" && typeof attr.value === "number" && + value < attr.value; + case BinOp.LessThan: + return typeof value === "number" && typeof attr.value === "number" && + value <= attr.value; + default: + return false; + } +} diff --git a/cli/js/40_lint_types.d.ts b/cli/js/40_lint_types.d.ts index 8c252f10ad..7b06e36098 100644 --- a/cli/js/40_lint_types.d.ts +++ b/cli/js/40_lint_types.d.ts @@ -16,6 +16,7 @@ export interface AstContext { strByProp: number[]; typeByStr: Map; propByStr: Map; + matcher: MatchContext; } // TODO(@marvinhagemeister) Remove once we land "official" types @@ -43,8 +44,89 @@ export interface LintState { export type VisitorFn = (node: unknown) => void; export interface CompiledVisitor { - matcher: (offset: number) => boolean; + matcher: (ctx: MatchContext, offset: number) => boolean; info: { enter: VisitorFn; exit: VisitorFn }; } +export interface AttrExists { + type: 3; + prop: number[]; +} + +export interface AttrBin { + type: 4; + prop: number[]; + op: number; + // deno-lint-ignore no-explicit-any + value: any; +} + +export type AttrSelector = AttrExists | AttrBin; + +export interface ElemSelector { + type: 1; + wildcard: boolean; + elem: number; +} + +export interface PseudoNthChild { + type: 5; + op: string | null; + step: number; + stepOffset: number; + of: Selector | null; + repeat: boolean; +} + +export interface PseudoHas { + type: 6; + selectors: Selector[]; +} +export interface PseudoNot { + type: 7; + selectors: Selector[]; +} +export interface PseudoFirstChild { + type: 8; +} +export interface PseudoLastChild { + type: 9; +} + +export interface Relation { + type: 2; + op: number; +} + +export type Selector = Array< + | ElemSelector + | Relation + | AttrExists + | AttrBin + | PseudoNthChild + | PseudoNot + | PseudoHas + | PseudoFirstChild + | PseudoLastChild +>; + +export interface SelectorParseCtx { + root: Selector; + current: Selector; +} + +export interface MatchContext { + getFirstChild(id: number): number; + getLastChild(id: number): number; + getSiblings(id: number): number[]; + getParent(id: number): number; + getType(id: number): number; + hasAttrPath(id: number, propIds: number[], idx: number): boolean; + getAttrPathValue(id: number, propIds: number[], idx: number): unknown; +} + +export type NextFn = (ctx: MatchContext, id: number) => boolean; +export type MatcherFn = (ctx: MatchContext, id: number) => boolean; +export type TransformFn = (value: string) => number; + export {}; diff --git a/cli/tools/lint/ast_buffer/buffer.rs b/cli/tools/lint/ast_buffer/buffer.rs index c440b73ccd..f37041eff2 100644 --- a/cli/tools/lint/ast_buffer/buffer.rs +++ b/cli/tools/lint/ast_buffer/buffer.rs @@ -215,11 +215,13 @@ impl SerializeCtx { let type_str = ctx.str_table.insert("type"); let parent_str = ctx.str_table.insert("parent"); let range_str = ctx.str_table.insert("range"); + let length_str = ctx.str_table.insert("length"); // These values are expected to be in this order on the JS side ctx.prop_map[0] = type_str; ctx.prop_map[1] = parent_str; ctx.prop_map[2] = range_str; + ctx.prop_map[3] = length_str; ctx } diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs index af5fea4b46..599499aa8d 100644 --- a/cli/tools/lint/ast_buffer/ts_estree.rs +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -205,6 +205,7 @@ pub enum AstProp { Type, Parent, Range, + Length, // Not used in AST, but can be used in attr selectors // Starting from here the order doesn't matter. // Following are all possible AST node properties. @@ -320,6 +321,7 @@ impl Display for AstProp { AstProp::Parent => "parent", AstProp::Range => "range", AstProp::Type => "type", + AstProp::Length => "length", AstProp::Abstract => "abstract", AstProp::Accessibility => "accessibility", AstProp::Alternate => "alternate", diff --git a/cli/worker.rs b/cli/worker.rs index 6b87b5966a..7653e72b75 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -657,6 +657,8 @@ impl CliMainWorkerFactory { "40_test.js", "40_bench.js", "40_jupyter.js", + // TODO(bartlomieju): probably shouldn't include these files here? + "40_lint_selector.js", "40_lint.js" ); } diff --git a/tests/integration/js_unit_tests.rs b/tests/integration/js_unit_tests.rs index 899329b319..afb97a3458 100644 --- a/tests/integration/js_unit_tests.rs +++ b/tests/integration/js_unit_tests.rs @@ -52,6 +52,7 @@ util::unit_test_factory!( kv_queue_test, kv_queue_undelivered_test, link_test, + lint_selectors_test, lint_plugin_test, make_temp_test, message_channel_test, diff --git a/tests/unit/lint_plugin_test.ts b/tests/unit/lint_plugin_test.ts index 649c8bde9e..9506c3e0a8 100644 --- a/tests/unit/lint_plugin_test.ts +++ b/tests/unit/lint_plugin_test.ts @@ -51,22 +51,38 @@ function testPlugin( return runLintPlugin(plugin, "source.tsx", source); } -function testVisit(source: string, ...selectors: string[]): string[] { - const log: string[] = []; +interface VisitResult { + selector: string; + kind: "enter" | "exit"; + // deno-lint-ignore no-explicit-any + node: any; +} + +function testVisit( + source: string, + ...selectors: string[] +): VisitResult[] { + const result: VisitResult[] = []; testPlugin(source, { create() { const visitor: LintVisitor = {}; for (const s of selectors) { - visitor[s] = () => log.push(s); + visitor[s] = (node) => { + result.push({ + kind: s.endsWith(":exit") ? "exit" : "enter", + selector: s, + node, + }); + }; } return visitor; }, }); - return log; + return result; } function testLintNode(source: string, ...selectors: string[]) { @@ -91,14 +107,188 @@ function testLintNode(source: string, ...selectors: string[]) { } Deno.test("Plugin - visitor enter/exit", () => { - const enter = testVisit("foo", "Identifier"); - assertEquals(enter, ["Identifier"]); + const enter = testVisit( + "foo", + "Identifier", + ); + assertEquals(enter[0].node.type, "Identifier"); - const exit = testVisit("foo", "Identifier:exit"); - assertEquals(exit, ["Identifier:exit"]); + const exit = testVisit( + "foo", + "Identifier:exit", + ); + assertEquals(exit[0].node.type, "Identifier"); const both = testVisit("foo", "Identifier", "Identifier:exit"); - assertEquals(both, ["Identifier", "Identifier:exit"]); + assertEquals(both.map((t) => t.selector), ["Identifier", "Identifier:exit"]); +}); + +Deno.test("Plugin - visitor descendant", () => { + let result = testVisit( + "if (false) foo; if (false) bar()", + "IfStatement CallExpression", + ); + assertEquals(result[0].node.type, "CallExpression"); + assertEquals(result[0].node.callee.name, "bar"); + + result = testVisit( + "if (false) foo; foo()", + "IfStatement IfStatement", + ); + assertEquals(result, []); + + result = testVisit( + "if (false) foo; foo()", + "* CallExpression", + ); + assertEquals(result[0].node.type, "CallExpression"); +}); + +Deno.test("Plugin - visitor child combinator", () => { + let result = testVisit( + "if (false) foo; if (false) { bar; }", + "IfStatement > ExpressionStatement > Identifier", + ); + assertEquals(result[0].node.name, "foo"); + + result = testVisit( + "if (false) foo; foo()", + "IfStatement IfStatement", + ); + assertEquals(result, []); +}); + +Deno.test("Plugin - visitor next sibling", () => { + const result = testVisit( + "if (false) foo; if (false) bar;", + "IfStatement + IfStatement Identifier", + ); + assertEquals(result[0].node.name, "bar"); +}); + +Deno.test("Plugin - visitor subsequent sibling", () => { + const result = testVisit( + "if (false) foo; if (false) bar; if (false) baz;", + "IfStatement ~ IfStatement Identifier", + ); + assertEquals(result.map((r) => r.node.name), ["bar", "baz"]); +}); + +Deno.test("Plugin - visitor attr", () => { + let result = testVisit( + "for (const a of b) {}", + "[await]", + ); + assertEquals(result[0].node.await, false); + + result = testVisit( + "for await (const a of b) {}", + "[await=true]", + ); + assertEquals(result[0].node.await, true); + + result = testVisit( + "for await (const a of b) {}", + "ForOfStatement[await=true]", + ); + assertEquals(result[0].node.await, true); + + result = testVisit( + "for (const a of b) {}", + "ForOfStatement[await != true]", + ); + assertEquals(result[0].node.await, false); + + result = testVisit( + "async function *foo() {}", + "FunctionDeclaration[async=true][generator=true]", + ); + assertEquals(result[0].node.type, "FunctionDeclaration"); + + result = testVisit( + "foo", + "[name='foo']", + ); + assertEquals(result[0].node.name, "foo"); +}); + +Deno.test("Plugin - visitor attr length special case", () => { + let result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length=2]", + ); + assertEquals(result[0].node.arguments.length, 2); + + result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length>1]", + ); + assertEquals(result[0].node.arguments.length, 2); + + result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length<2]", + ); + assertEquals(result[0].node.arguments.length, 1); + + result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length<=3]", + ); + assertEquals(result[0].node.arguments.length, 1); + assertEquals(result[1].node.arguments.length, 2); + + result = testVisit( + "foo(1); foo(1, 2);", + "CallExpression[arguments.length>=1]", + ); + assertEquals(result[0].node.arguments.length, 1); + assertEquals(result[1].node.arguments.length, 2); +}); + +Deno.test("Plugin - visitor :first-child", () => { + const result = testVisit( + "{ foo; bar }", + "BlockStatement ExpressionStatement:first-child Identifier", + ); + assertEquals(result[0].node.name, "foo"); +}); + +Deno.test("Plugin - visitor :last-child", () => { + const result = testVisit( + "{ foo; bar }", + "BlockStatement ExpressionStatement:last-child Identifier", + ); + assertEquals(result[0].node.name, "bar"); +}); + +Deno.test("Plugin - visitor :nth-child", () => { + let result = testVisit( + "{ foo; bar; baz; foobar; }", + "BlockStatement ExpressionStatement:nth-child(2) Identifier", + ); + assertEquals(result[0].node.name, "bar"); + + result = testVisit( + "{ foo; bar; baz; foobar; }", + "BlockStatement ExpressionStatement:nth-child(2n) Identifier", + ); + assertEquals(result[0].node.name, "foo"); + assertEquals(result[1].node.name, "baz"); + + result = testVisit( + "{ foo; bar; baz; foobar; }", + "BlockStatement ExpressionStatement:nth-child(2n + 1) Identifier", + ); + assertEquals(result[0].node.name, "bar"); + assertEquals(result[1].node.name, "foobar"); + + result = testVisit( + "{ foo; bar; baz; foobar; }", + "BlockStatement *:nth-child(2n + 1 of ExpressionStatement) Identifier", + ); + assertEquals(result[0].node.name, "bar"); + assertEquals(result[1].node.name, "foobar"); }); Deno.test("Plugin - Program", () => { diff --git a/tests/unit/lint_selectors_test.ts b/tests/unit/lint_selectors_test.ts new file mode 100644 index 0000000000..0909a4907a --- /dev/null +++ b/tests/unit/lint_selectors_test.ts @@ -0,0 +1,610 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals } from "@std/assert/equals"; +import { + ATTR_BIN_NODE, + ATTR_EXISTS_NODE, + BinOp, + ELEM_NODE, + Lexer, + parseSelector, + PSEUDO_FIRST_CHILD, + PSEUDO_HAS, + PSEUDO_LAST_CHILD, + PSEUDO_NOT, + PSEUDO_NTH_CHILD, + RELATION_NODE, + splitSelectors, + Token, +} from "../../cli/js/40_lint_selector.js"; +import { assertThrows } from "@std/assert"; + +Deno.test("splitSelectors", () => { + assertEquals(splitSelectors("foo"), ["foo"]); + assertEquals(splitSelectors("foo, bar"), ["foo", "bar"]); + assertEquals(splitSelectors("foo:f(bar, baz)"), ["foo:f(bar, baz)"]); + assertEquals(splitSelectors("foo:f(bar, baz), foobar"), [ + "foo:f(bar, baz)", + "foobar", + ]); +}); + +interface LexState { + token: number; + value: string; +} + +function testLexer(input: string): LexState[] { + const out: LexState[] = []; + const l = new Lexer(input); + + while (l.token !== Token.EOF) { + out.push({ token: l.token, value: l.value }); + l.next(); + } + + return out; +} + +const Tags: Record = { Foo: 1, Bar: 2, FooBar: 3 }; +const Attrs: Record = { foo: 1, bar: 2, foobar: 3, attr: 4 }; +const toTag = (name: string): number => Tags[name]; +const toAttr = (name: string): number => Attrs[name]; + +const testParse = (input: string) => parseSelector(input, toTag, toAttr); + +Deno.test("Lexer - Elem", () => { + assertEquals(testLexer("Foo"), [ + { token: Token.Word, value: "Foo" }, + ]); + assertEquals(testLexer("foo-bar"), [ + { token: Token.Word, value: "foo-bar" }, + ]); + assertEquals(testLexer("foo_bar"), [ + { token: Token.Word, value: "foo_bar" }, + ]); + assertEquals(testLexer("Foo Bar Baz"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Bar" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Baz" }, + ]); + assertEquals(testLexer("Foo Bar Baz"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Bar" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Baz" }, + ]); +}); + +Deno.test("Lexer - Relation >", () => { + assertEquals(testLexer("Foo > Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: ">" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("Foo>Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: ">" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer(">Bar"), [ + { token: Token.Op, value: ">" }, + { token: Token.Word, value: "Bar" }, + ]); +}); + +Deno.test("Lexer - Relation +", () => { + assertEquals(testLexer("Foo + Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: "+" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("Foo+Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: "+" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("+Bar"), [ + { token: Token.Op, value: "+" }, + { token: Token.Word, value: "Bar" }, + ]); +}); + +Deno.test("Lexer - Relation ~", () => { + assertEquals(testLexer("Foo ~ Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: "~" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("Foo~Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Op, value: "~" }, + { token: Token.Word, value: "Bar" }, + ]); + assertEquals(testLexer("~Bar"), [ + { token: Token.Op, value: "~" }, + { token: Token.Word, value: "Bar" }, + ]); + + assertEquals(testLexer("Foo Bar ~ Bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Space, value: "" }, + { token: Token.Word, value: "Bar" }, + { token: Token.Op, value: "~" }, + { token: Token.Word, value: "Bar" }, + ]); +}); + +Deno.test("Lexer - Attr", () => { + assertEquals(testLexer("[attr]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr=1]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "=" }, + { token: Token.Word, value: "1" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr='foo']"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "=" }, + { token: Token.String, value: "foo" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr>=2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: ">=" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr<=2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "<=" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr>2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: ">" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr<2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "<" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr!=2]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Op, value: "!=" }, + { token: Token.Word, value: "2" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr.foo=1]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.Dot, value: "" }, + { token: Token.Word, value: "foo" }, + { token: Token.Op, value: "=" }, + { token: Token.Word, value: "1" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("[attr] [attr]"), [ + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.BracketClose, value: "" }, + { token: Token.Space, value: "" }, + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.BracketClose, value: "" }, + ]); + assertEquals(testLexer("Foo[attr][attr2=1]"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr" }, + { token: Token.BracketClose, value: "" }, + { token: Token.BracketOpen, value: "" }, + { token: Token.Word, value: "attr2" }, + { token: Token.Op, value: "=" }, + { token: Token.Word, value: "1" }, + { token: Token.BracketClose, value: "" }, + ]); +}); + +Deno.test("Lexer - Pseudo", () => { + assertEquals(testLexer(":foo-bar"), [ + { token: Token.Colon, value: "" }, + { token: Token.Word, value: "foo-bar" }, + ]); + assertEquals(testLexer("Foo:foo-bar"), [ + { token: Token.Word, value: "Foo" }, + { token: Token.Colon, value: "" }, + { token: Token.Word, value: "foo-bar" }, + ]); + assertEquals(testLexer(":foo-bar(baz)"), [ + { token: Token.Colon, value: "" }, + { token: Token.Word, value: "foo-bar" }, + { token: Token.BraceOpen, value: "" }, + { token: Token.Word, value: "baz" }, + { token: Token.BraceClose, value: "" }, + ]); + assertEquals(testLexer(":foo-bar(2n + 1)"), [ + { token: Token.Colon, value: "" }, + { token: Token.Word, value: "foo-bar" }, + { token: Token.BraceOpen, value: "" }, + { token: Token.Word, value: "2n" }, + { token: Token.Op, value: "+" }, + { token: Token.Word, value: "1" }, + { token: Token.BraceClose, value: "" }, + ]); +}); + +Deno.test("Parser - Elem", () => { + assertEquals(testParse("Foo"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + ]]); +}); + +Deno.test("Parser - Relation (descendant)", () => { + assertEquals(testParse("Foo Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { + type: RELATION_NODE, + op: BinOp.Space, + }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); +}); + +Deno.test("Parser - Relation", () => { + assertEquals(testParse("Foo > Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { + type: RELATION_NODE, + op: BinOp.Greater, + }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); + + assertEquals(testParse("Foo ~ Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { + type: RELATION_NODE, + op: BinOp.Tilde, + }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); + + assertEquals(testParse("Foo + Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { + type: RELATION_NODE, + op: BinOp.Plus, + }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); +}); + +Deno.test("Parser - Attr", () => { + assertEquals(testParse("[foo]"), [[ + { + type: ATTR_EXISTS_NODE, + prop: [1], + }, + ]]); + + assertEquals(testParse("[foo][bar]"), [[ + { + type: ATTR_EXISTS_NODE, + prop: [1], + }, + { + type: ATTR_EXISTS_NODE, + prop: [2], + }, + ]]); + + assertEquals(testParse("[foo=1]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: 1, + }, + ]]); + assertEquals(testParse("[foo=true]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: true, + }, + ]]); + assertEquals(testParse("[foo=false]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: false, + }, + ]]); + assertEquals(testParse("[foo=null]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: null, + }, + ]]); + assertEquals(testParse("[foo='str']"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: "str", + }, + ]]); + assertEquals(testParse('[foo="str"]'), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: "str", + }, + ]]); + assertEquals(testParse("[foo=/str/]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: /str/, + }, + ]]); + assertEquals(testParse("[foo=/str/g]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1], + value: /str/g, + }, + ]]); +}); + +Deno.test("Parser - Attr nested", () => { + assertEquals(testParse("[foo.bar]"), [[ + { + type: ATTR_EXISTS_NODE, + prop: [1, 2], + }, + ]]); + + assertEquals(testParse("[foo.bar = 2]"), [[ + { + type: ATTR_BIN_NODE, + op: BinOp.Equal, + prop: [1, 2], + value: 2, + }, + ]]); +}); + +Deno.test("Parser - Pseudo no value", () => { + assertEquals(testParse(":first-child"), [[ + { + type: PSEUDO_FIRST_CHILD, + }, + ]]); + assertEquals(testParse(":last-child"), [[ + { + type: PSEUDO_LAST_CHILD, + }, + ]]); +}); + +Deno.test("Parser - Pseudo nth-child", () => { + assertEquals(testParse(":nth-child(2)"), [[ + { + type: PSEUDO_NTH_CHILD, + of: null, + op: null, + step: 0, + stepOffset: 1, + repeat: false, + }, + ]]); + assertEquals(testParse(":nth-child(2n)"), [[ + { + type: PSEUDO_NTH_CHILD, + of: null, + op: null, + step: 2, + stepOffset: 0, + repeat: true, + }, + ]]); + assertEquals(testParse(":nth-child(-2n)"), [[ + { + type: PSEUDO_NTH_CHILD, + of: null, + op: null, + step: -2, + stepOffset: 0, + repeat: true, + }, + ]]); + assertEquals(testParse(":nth-child(2n + 1)"), [[ + { + type: PSEUDO_NTH_CHILD, + of: null, + op: "+", + step: 2, + stepOffset: 1, + repeat: true, + }, + ]]); + assertEquals(testParse(":nth-child(2n + 1 of Foo[attr])"), [[ + { + type: PSEUDO_NTH_CHILD, + of: [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { type: ATTR_EXISTS_NODE, prop: [4] }, + ], + op: "+", + step: 2, + stepOffset: 1, + repeat: true, + }, + ]]); + + // Invalid selectors + assertThrows(() => testParse(":nth-child(2n + 1 of Foo[attr], Bar)")); + assertThrows(() => testParse(":nth-child(2n - 1 foo)")); +}); + +Deno.test("Parser - Pseudo has/is/where", () => { + assertEquals(testParse(":has(Foo:has(Foo), Bar)"), [[ + { + type: PSEUDO_HAS, + selectors: [ + [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { + type: PSEUDO_HAS, + selectors: [ + [{ type: ELEM_NODE, elem: 1, wildcard: false }], + ], + }, + ], + [ + { type: ELEM_NODE, elem: 2, wildcard: false }, + ], + ], + }, + ]]); + assertEquals(testParse(":where(Foo:where(Foo), Bar)"), [[ + { + type: PSEUDO_HAS, + selectors: [ + [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { + type: PSEUDO_HAS, + selectors: [ + [{ type: ELEM_NODE, elem: 1, wildcard: false }], + ], + }, + ], + [ + { type: ELEM_NODE, elem: 2, wildcard: false }, + ], + ], + }, + ]]); + assertEquals(testParse(":is(Foo:is(Foo), Bar)"), [[ + { + type: PSEUDO_HAS, + selectors: [ + [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { + type: PSEUDO_HAS, + selectors: [ + [{ type: ELEM_NODE, elem: 1, wildcard: false }], + ], + }, + ], + [ + { type: ELEM_NODE, elem: 2, wildcard: false }, + ], + ], + }, + ]]); +}); + +Deno.test("Parser - Pseudo not", () => { + assertEquals(testParse(":not(Foo:not(Foo), Bar)"), [[ + { + type: PSEUDO_NOT, + selectors: [ + [ + { type: ELEM_NODE, elem: 1, wildcard: false }, + { + type: PSEUDO_NOT, + selectors: [ + [{ type: ELEM_NODE, elem: 1, wildcard: false }], + ], + }, + ], + [ + { type: ELEM_NODE, elem: 2, wildcard: false }, + ], + ], + }, + ]]); +}); + +Deno.test("Parser - mixed", () => { + assertEquals(testParse("Foo[foo=true] Bar"), [[ + { + type: ELEM_NODE, + elem: 1, + wildcard: false, + }, + { type: ATTR_BIN_NODE, op: BinOp.Equal, prop: [1], value: true }, + { type: RELATION_NODE, op: BinOp.Space }, + { + type: ELEM_NODE, + elem: 2, + wildcard: false, + }, + ]]); +}); diff --git a/tools/core_import_map.json b/tools/core_import_map.json index bc0674277e..d38221eb4c 100644 --- a/tools/core_import_map.json +++ b/tools/core_import_map.json @@ -250,6 +250,7 @@ "ext:deno_node/_util/std_fmt_colors.ts": "../ext/node/polyfills/_util/std_fmt_colors.ts", "ext:deno_telemetry/telemetry.ts": "../ext/deno_telemetry/telemetry.ts", "ext:deno_telemetry/util.ts": "../ext/deno_telemetry/util.ts", + "ext:cli/40_lint_selector.js": "../cli/js/40_lint_selector.js", "@std/archive": "../tests/util/std/archive/mod.ts", "@std/archive/tar": "../tests/util/std/archive/tar.ts", "@std/archive/untar": "../tests/util/std/archive/untar.ts", From a9ab7a80da2395823dfd3bc4c4f60c46b126734d Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Tue, 24 Dec 2024 00:48:00 -0500 Subject: [PATCH 036/107] fix: incorrect memory info free/available bytes on mac (#27460) Fixes https://github.com/denoland/deno/issues/27435 For some reason this was dividing by 1024 (as if the unit was KB, and we wanted bytes) but the page size is already in bytes. --- runtime/sys_info.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/runtime/sys_info.rs b/runtime/sys_info.rs index f99cfc99f9..99bfcfe103 100644 --- a/runtime/sys_info.rs +++ b/runtime/sys_info.rs @@ -295,11 +295,9 @@ pub fn mem_info() -> Option { // TODO(@littledivy): Put this in a once_cell let page_size = libc::sysconf(libc::_SC_PAGESIZE) as u64; mem_info.available = - (stat.free_count as u64 + stat.inactive_count as u64) * page_size - / 1024; + (stat.free_count as u64 + stat.inactive_count as u64) * page_size; mem_info.free = - (stat.free_count as u64 - stat.speculative_count as u64) * page_size - / 1024; + (stat.free_count as u64 - stat.speculative_count as u64) * page_size; } } } From 91150706d82b427820f96ccd38f718abdea291f4 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 26 Dec 2024 13:24:28 +0900 Subject: [PATCH 037/107] fix(ext/node): make getCiphers return supported ciphers (#27466) Currently we only supports 7 ciphers (`aes-(128|192|256)-ecb` and `aes-(128|256)-(cbc|gcm)`) in `node:crypto`, but `crypto.getCiphers` returns other supported cipher names. That confuses `npm:openpgp` package and causes https://github.com/denoland/deno/issues/26875. This PR makes `getCiphers` return actually supported cipher names. With this change, the example given in #26875 can create private and public key files. closes #26875 --- ext/node/ops/crypto/cipher.rs | 2 +- ext/node/polyfills/internal/crypto/util.ts | 24 ++++++++------------ tests/unit_node/crypto/crypto_cipher_test.ts | 13 +++++++++++ 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/ext/node/ops/crypto/cipher.rs b/ext/node/ops/crypto/cipher.rs index ec45146b49..16e32a34af 100644 --- a/ext/node/ops/crypto/cipher.rs +++ b/ext/node/ops/crypto/cipher.rs @@ -172,7 +172,7 @@ impl Cipher { ) -> Result { use Cipher::*; Ok(match algorithm_name { - "aes-128-cbc" => { + "aes128" | "aes-128-cbc" => { Aes128Cbc(Box::new(cbc::Encryptor::new(key.into(), iv.into()))) } "aes-128-ecb" => Aes128Ecb(Box::new(ecb::Encryptor::new(key.into()))), diff --git a/ext/node/polyfills/internal/crypto/util.ts b/ext/node/polyfills/internal/crypto/util.ts index a39b031ee3..6c925f6577 100644 --- a/ext/node/polyfills/internal/crypto/util.ts +++ b/ext/node/polyfills/internal/crypto/util.ts @@ -67,22 +67,16 @@ export const ellipticCurves: Array = [ }, // NIST P-224 EC ]; -// deno-fmt-ignore const supportedCiphers = [ - "aes-128-ecb", "aes-192-ecb", - "aes-256-ecb", "aes-128-cbc", - "aes-192-cbc", "aes-256-cbc", - "aes128", "aes192", - "aes256", "aes-128-cfb", - "aes-192-cfb", "aes-256-cfb", - "aes-128-cfb8", "aes-192-cfb8", - "aes-256-cfb8", "aes-128-cfb1", - "aes-192-cfb1", "aes-256-cfb1", - "aes-128-ofb", "aes-192-ofb", - "aes-256-ofb", "aes-128-ctr", - "aes-192-ctr", "aes-256-ctr", - "aes-128-gcm", "aes-192-gcm", - "aes-256-gcm" + "aes-128-ecb", + "aes-192-ecb", + "aes-256-ecb", + "aes-128-cbc", + "aes-256-cbc", + "aes128", + "aes256", + "aes-128-gcm", + "aes-256-gcm", ]; export function getCiphers(): string[] { diff --git a/tests/unit_node/crypto/crypto_cipher_test.ts b/tests/unit_node/crypto/crypto_cipher_test.ts index 65a5b29eeb..e40625c5a4 100644 --- a/tests/unit_node/crypto/crypto_cipher_test.ts +++ b/tests/unit_node/crypto/crypto_cipher_test.ts @@ -361,6 +361,19 @@ Deno.test({ name: "getCiphers", fn() { assertEquals(crypto.getCiphers().includes("aes-128-cbc"), true); + + const getZeroKey = (cipher: string) => zeros(+cipher.match(/\d+/)![0] / 8); + const getZeroIv = (cipher: string) => { + if (cipher.includes("gcm") || cipher.includes("ecb")) { + return zeros(12); + } + return zeros(16); + }; + + for (const cipher of crypto.getCiphers()) { + crypto.createCipheriv(cipher, getZeroKey(cipher), getZeroIv(cipher)) + .final(); + } }, }); From f4e321342f0b0e574beeff9f779db185afeab101 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 26 Dec 2024 09:01:39 +0100 Subject: [PATCH 038/107] feat(unstable): add OTEL MeterProvider (#27240) This commit replaces `Deno.telemetry.MetricsExporter` with `Deno.telemetry.MeterProvider`. Signed-off-by: Luca Casonato Co-authored-by: snek --- cli/args/flags.rs | 2 + ext/telemetry/lib.rs | 1451 ++++++++++++--------- ext/telemetry/telemetry.ts | 852 +++++++----- tests/specs/cli/otel_basic/__test__.jsonc | 21 +- tests/specs/cli/otel_basic/metric.out | 284 ++++ tests/specs/cli/otel_basic/metric.ts | 87 +- 6 files changed, 1764 insertions(+), 933 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 418edcf34b..2b0b9a2908 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1006,6 +1006,8 @@ impl Flags { OtelConfig { tracing_enabled: !disabled && otel_var("OTEL_DENO_TRACING").unwrap_or(default), + metrics_enabled: !disabled + && otel_var("OTEL_DENO_METRICS").unwrap_or(default), console: match std::env::var("OTEL_DENO_CONSOLE").as_deref() { Ok(_) if disabled => OtelConsoleConfig::Ignore, Ok("ignore") => OtelConsoleConfig::Ignore, diff --git a/ext/telemetry/lib.rs b/ext/telemetry/lib.rs index 816e838743..8018843dc4 100644 --- a/ext/telemetry/lib.rs +++ b/ext/telemetry/lib.rs @@ -6,16 +6,22 @@ use deno_core::futures::channel::mpsc; use deno_core::futures::channel::mpsc::UnboundedSender; use deno_core::futures::future::BoxFuture; use deno_core::futures::stream; +use deno_core::futures::FutureExt; use deno_core::futures::Stream; use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::v8; +use deno_core::GarbageCollected; use deno_core::OpState; use once_cell::sync::Lazy; use once_cell::sync::OnceCell; use opentelemetry::logs::AnyValue; use opentelemetry::logs::LogRecord as LogRecordTrait; use opentelemetry::logs::Severity; +use opentelemetry::metrics::AsyncInstrumentBuilder; +use opentelemetry::metrics::InstrumentBuilder; +use opentelemetry::metrics::MeterProvider; +use opentelemetry::otel_debug; use opentelemetry::otel_error; use opentelemetry::trace::SpanContext; use opentelemetry::trace::SpanId; @@ -28,7 +34,6 @@ use opentelemetry::KeyValue; use opentelemetry::StringValue; use opentelemetry::Value; use opentelemetry_otlp::HttpExporterBuilder; -use opentelemetry_otlp::MetricExporter; use opentelemetry_otlp::Protocol; use opentelemetry_otlp::WithExportConfig; use opentelemetry_otlp::WithHttpConfig; @@ -36,10 +41,11 @@ use opentelemetry_sdk::export::trace::SpanData; use opentelemetry_sdk::logs::BatchLogProcessor; use opentelemetry_sdk::logs::LogProcessor; use opentelemetry_sdk::logs::LogRecord; -use opentelemetry_sdk::metrics::data::Metric; -use opentelemetry_sdk::metrics::data::ResourceMetrics; -use opentelemetry_sdk::metrics::data::ScopeMetrics; use opentelemetry_sdk::metrics::exporter::PushMetricExporter; +use opentelemetry_sdk::metrics::reader::MetricReader; +use opentelemetry_sdk::metrics::ManualReader; +use opentelemetry_sdk::metrics::MetricResult; +use opentelemetry_sdk::metrics::SdkMeterProvider; use opentelemetry_sdk::metrics::Temporality; use opentelemetry_sdk::trace::BatchSpanProcessor; use opentelemetry_sdk::trace::SpanProcessor; @@ -52,14 +58,21 @@ use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_VERSION; use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; use std::env; use std::fmt::Debug; use std::pin::Pin; +use std::rc::Rc; +use std::sync::Arc; +use std::sync::Mutex; use std::task::Context; use std::task::Poll; use std::thread; use std::time::Duration; use std::time::SystemTime; +use tokio::sync::oneshot; +use tokio::task::JoinSet; deno_core::extension!( deno_telemetry, @@ -75,23 +88,24 @@ deno_core::extension!( op_otel_span_attribute3, op_otel_span_set_dropped, op_otel_span_flush, - op_otel_metrics_resource_attribute, - op_otel_metrics_resource_attribute2, - op_otel_metrics_resource_attribute3, - op_otel_metrics_scope, - op_otel_metrics_sum, - op_otel_metrics_gauge, - op_otel_metrics_sum_or_gauge_data_point, - op_otel_metrics_histogram, - op_otel_metrics_histogram_data_point, - op_otel_metrics_histogram_data_point_entry_final, - op_otel_metrics_histogram_data_point_entry1, - op_otel_metrics_histogram_data_point_entry2, - op_otel_metrics_histogram_data_point_entry3, - op_otel_metrics_data_point_attribute, - op_otel_metrics_data_point_attribute2, - op_otel_metrics_data_point_attribute3, - op_otel_metrics_submit, + op_otel_metric_create_counter, + op_otel_metric_create_up_down_counter, + op_otel_metric_create_gauge, + op_otel_metric_create_histogram, + op_otel_metric_create_observable_counter, + op_otel_metric_create_observable_gauge, + op_otel_metric_create_observable_up_down_counter, + op_otel_metric_attribute3, + op_otel_metric_record0, + op_otel_metric_record1, + op_otel_metric_record2, + op_otel_metric_record3, + op_otel_metric_observable_record0, + op_otel_metric_observable_record1, + op_otel_metric_observable_record2, + op_otel_metric_observable_record3, + op_otel_metric_wait_to_observe, + op_otel_metric_observation_done, ], esm = ["telemetry.ts", "util.ts"], ); @@ -105,6 +119,7 @@ pub struct OtelRuntimeConfig { #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct OtelConfig { pub tracing_enabled: bool, + pub metrics_enabled: bool, pub console: OtelConsoleConfig, pub deterministic: bool, } @@ -113,6 +128,7 @@ impl OtelConfig { pub fn as_v8(&self) -> Box<[u8]> { Box::new([ self.tracing_enabled as u8, + self.metrics_enabled as u8, self.console as u8, self.deterministic as u8, ]) @@ -137,6 +153,10 @@ static OTEL_SHARED_RUNTIME_SPAWN_TASK_TX: Lazy< UnboundedSender>, > = Lazy::new(otel_create_shared_runtime); +static OTEL_PRE_COLLECT_CALLBACKS: Lazy< + Mutex>>>, +> = Lazy::new(Default::default); + fn otel_create_shared_runtime() -> UnboundedSender> { let (spawn_task_tx, mut spawn_task_rx) = mpsc::unbounded::>(); @@ -273,6 +293,181 @@ impl Stream for BatchMessageChannelReceiver { } } +enum DenoPeriodicReaderMessage { + Register(std::sync::Weak), + Export, + ForceFlush(oneshot::Sender>), + Shutdown(oneshot::Sender>), +} + +#[derive(Debug)] +struct DenoPeriodicReader { + tx: tokio::sync::mpsc::Sender, + temporality: Temporality, +} + +impl MetricReader for DenoPeriodicReader { + fn register_pipeline( + &self, + pipeline: std::sync::Weak, + ) { + let _ = self + .tx + .try_send(DenoPeriodicReaderMessage::Register(pipeline)); + } + + fn collect( + &self, + _rm: &mut opentelemetry_sdk::metrics::data::ResourceMetrics, + ) -> opentelemetry_sdk::metrics::MetricResult<()> { + unreachable!("collect should not be called on DenoPeriodicReader"); + } + + fn force_flush(&self) -> opentelemetry_sdk::metrics::MetricResult<()> { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.try_send(DenoPeriodicReaderMessage::ForceFlush(tx)); + deno_core::futures::executor::block_on(rx).unwrap()?; + Ok(()) + } + + fn shutdown(&self) -> opentelemetry_sdk::metrics::MetricResult<()> { + let (tx, rx) = oneshot::channel(); + let _ = self.tx.try_send(DenoPeriodicReaderMessage::Shutdown(tx)); + deno_core::futures::executor::block_on(rx).unwrap()?; + Ok(()) + } + + fn temporality( + &self, + _kind: opentelemetry_sdk::metrics::InstrumentKind, + ) -> Temporality { + self.temporality + } +} + +const METRIC_EXPORT_INTERVAL_NAME: &str = "OTEL_METRIC_EXPORT_INTERVAL"; +const DEFAULT_INTERVAL: Duration = Duration::from_secs(60); + +impl DenoPeriodicReader { + fn new(exporter: opentelemetry_otlp::MetricExporter) -> Self { + let interval = env::var(METRIC_EXPORT_INTERVAL_NAME) + .ok() + .and_then(|v| v.parse().map(Duration::from_millis).ok()) + .unwrap_or(DEFAULT_INTERVAL); + + let (tx, mut rx) = tokio::sync::mpsc::channel(256); + + let temporality = PushMetricExporter::temporality(&exporter); + + let worker = async move { + let inner = ManualReader::builder() + .with_temporality(PushMetricExporter::temporality(&exporter)) + .build(); + + let collect_and_export = |collect_observed: bool| { + let inner = &inner; + let exporter = &exporter; + async move { + let mut resource_metrics = + opentelemetry_sdk::metrics::data::ResourceMetrics { + resource: Default::default(), + scope_metrics: Default::default(), + }; + if collect_observed { + let callbacks = { + let mut callbacks = OTEL_PRE_COLLECT_CALLBACKS.lock().unwrap(); + std::mem::take(&mut *callbacks) + }; + let mut futures = JoinSet::new(); + for callback in callbacks { + let (tx, rx) = oneshot::channel(); + if let Ok(()) = callback.send(tx) { + futures.spawn(rx); + } + } + while futures.join_next().await.is_some() {} + } + inner.collect(&mut resource_metrics)?; + if resource_metrics.scope_metrics.is_empty() { + return Ok(()); + } + exporter.export(&mut resource_metrics).await?; + Ok(()) + } + }; + + let mut ticker = tokio::time::interval(interval); + ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); + ticker.tick().await; + + loop { + let message = tokio::select! { + _ = ticker.tick() => DenoPeriodicReaderMessage::Export, + message = rx.recv() => if let Some(message) = message { + message + } else { + break; + }, + }; + + match message { + DenoPeriodicReaderMessage::Register(new_pipeline) => { + inner.register_pipeline(new_pipeline); + } + DenoPeriodicReaderMessage::Export => { + otel_debug!( + name: "DenoPeriodicReader.ExportTriggered", + message = "Export message received.", + ); + if let Err(err) = collect_and_export(true).await { + otel_error!( + name: "DenoPeriodicReader.ExportFailed", + message = "Failed to export metrics", + reason = format!("{}", err)); + } + } + DenoPeriodicReaderMessage::ForceFlush(sender) => { + otel_debug!( + name: "DenoPeriodicReader.ForceFlushCalled", + message = "Flush message received.", + ); + let res = collect_and_export(false).await; + if let Err(send_error) = sender.send(res) { + otel_debug!( + name: "DenoPeriodicReader.Flush.SendResultError", + message = "Failed to send flush result.", + reason = format!("{:?}", send_error), + ); + } + } + DenoPeriodicReaderMessage::Shutdown(sender) => { + otel_debug!( + name: "DenoPeriodicReader.ShutdownCalled", + message = "Shutdown message received", + ); + let res = collect_and_export(false).await; + let _ = exporter.shutdown(); + if let Err(send_error) = sender.send(res) { + otel_debug!( + name: "DenoPeriodicReader.Shutdown.SendResultError", + message = "Failed to send shutdown result", + reason = format!("{:?}", send_error), + ); + } + break; + } + } + } + }; + + (*OTEL_SHARED_RUNTIME_SPAWN_TASK_TX) + .unbounded_send(worker.boxed()) + .expect("failed to send task to shared OpenTelemetry runtime"); + + DenoPeriodicReader { tx, temporality } + } +} + mod hyper_client { use http_body_util::BodyExt; use http_body_util::Full; @@ -353,66 +548,10 @@ mod hyper_client { } } -enum MetricProcessorMessage { - ResourceMetrics(ResourceMetrics), - Flush(tokio::sync::oneshot::Sender<()>), -} - -struct MetricProcessor { - tx: tokio::sync::mpsc::Sender, -} - -impl MetricProcessor { - fn new(exporter: MetricExporter) -> Self { - let (tx, mut rx) = tokio::sync::mpsc::channel(2048); - let future = async move { - while let Some(message) = rx.recv().await { - match message { - MetricProcessorMessage::ResourceMetrics(mut rm) => { - if let Err(err) = exporter.export(&mut rm).await { - otel_error!( - name: "MetricProcessor.Export.Error", - error = format!("{}", err) - ); - } - } - MetricProcessorMessage::Flush(tx) => { - if let Err(()) = tx.send(()) { - otel_error!( - name: "MetricProcessor.Flush.SendResultError", - error = "()", - ); - } - } - } - } - }; - - (*OTEL_SHARED_RUNTIME_SPAWN_TASK_TX) - .unbounded_send(Box::pin(future)) - .expect("failed to send task to shared OpenTelemetry runtime"); - - Self { tx } - } - - fn submit(&self, rm: ResourceMetrics) { - let _ = self - .tx - .try_send(MetricProcessorMessage::ResourceMetrics(rm)); - } - - fn force_flush(&self) -> Result<(), anyhow::Error> { - let (tx, rx) = tokio::sync::oneshot::channel(); - self.tx.try_send(MetricProcessorMessage::Flush(tx))?; - deno_core::futures::executor::block_on(rx)?; - Ok(()) - } -} - struct Processors { spans: BatchSpanProcessor, logs: BatchLogProcessor, - metrics: MetricProcessor, + meter_provider: SdkMeterProvider, } static OTEL_PROCESSORS: OnceCell = OnceCell::new(); @@ -421,7 +560,7 @@ static BUILT_IN_INSTRUMENTATION_SCOPE: OnceCell< opentelemetry::InstrumentationScope, > = OnceCell::new(); -pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { +pub fn init(rt_config: OtelRuntimeConfig) -> anyhow::Result<()> { // Parse the `OTEL_EXPORTER_OTLP_PROTOCOL` variable. The opentelemetry_* // crates don't do this automatically. // TODO(piscisaureus): enable GRPC support. @@ -454,8 +593,8 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { // Add the runtime name and version to the resource attributes. Also override // the `telemetry.sdk` attributes to include the Deno runtime. resource = resource.merge(&Resource::new(vec![ - KeyValue::new(PROCESS_RUNTIME_NAME, config.runtime_name), - KeyValue::new(PROCESS_RUNTIME_VERSION, config.runtime_version.clone()), + KeyValue::new(PROCESS_RUNTIME_NAME, rt_config.runtime_name), + KeyValue::new(PROCESS_RUNTIME_VERSION, rt_config.runtime_version.clone()), KeyValue::new( TELEMETRY_SDK_LANGUAGE, format!( @@ -474,7 +613,7 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { TELEMETRY_SDK_VERSION, format!( "{}-{}", - config.runtime_version, + rt_config.runtime_version, resource.get(Key::new(TELEMETRY_SDK_VERSION)).unwrap() ), ), @@ -494,11 +633,30 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { BatchSpanProcessor::builder(span_exporter, OtelSharedRuntime).build(); span_processor.set_resource(&resource); + let temporality_preference = + env::var("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE") + .ok() + .map(|s| s.to_lowercase()); + let temporality = match temporality_preference.as_deref() { + None | Some("cumulative") => Temporality::Cumulative, + Some("delta") => Temporality::Delta, + Some("lowmemory") => Temporality::LowMemory, + Some(other) => { + return Err(anyhow!( + "Invalid value for OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: {}", + other + )); + } + }; let metric_exporter = HttpExporterBuilder::default() .with_http_client(client.clone()) .with_protocol(protocol) - .build_metrics_exporter(Temporality::Cumulative)?; - let metric_processor = MetricProcessor::new(metric_exporter); + .build_metrics_exporter(temporality)?; + let metric_reader = DenoPeriodicReader::new(metric_exporter); + let meter_provider = SdkMeterProvider::builder() + .with_reader(metric_reader) + .with_resource(resource.clone()) + .build(); let log_exporter = HttpExporterBuilder::default() .with_http_client(client) @@ -512,13 +670,13 @@ pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> { .set(Processors { spans: span_processor, logs: log_processor, - metrics: metric_processor, + meter_provider, }) .map_err(|_| anyhow!("failed to init otel"))?; let builtin_instrumentation_scope = opentelemetry::InstrumentationScope::builder("deno") - .with_version(config.runtime_version.clone()) + .with_version(rt_config.runtime_version.clone()) .build(); BUILT_IN_INSTRUMENTATION_SCOPE .set(builtin_instrumentation_scope) @@ -534,12 +692,12 @@ pub fn flush() { if let Some(Processors { spans, logs, - metrics, + meter_provider, }) = OTEL_PROCESSORS.get() { let _ = spans.force_flush(); let _ = logs.force_flush(); - let _ = metrics.force_flush(); + let _ = meter_provider.force_flush(); } } @@ -659,8 +817,8 @@ fn parse_span_id( } } -macro_rules! attr { - ($scope:ident, $attributes:expr $(=> $dropped_attributes_count:expr)?, $name:expr, $value:expr) => { +macro_rules! attr_raw { + ($scope:ident, $name:expr, $value:expr) => {{ let name = if let Ok(name) = $name.try_cast() { let view = v8::ValueView::new($scope, name); match view.data() { @@ -695,7 +853,18 @@ macro_rules! attr { None }; if let (Some(name), Some(value)) = (name, value) { - $attributes.push(KeyValue::new(name, value)); + Some(KeyValue::new(name, value)) + } else { + None + } + }}; +} + +macro_rules! attr { + ($scope:ident, $attributes:expr $(=> $dropped_attributes_count:expr)?, $name:expr, $value:expr) => { + let attr = attr_raw!($scope, $name, $value); + if let Some(kv) = attr { + $attributes.push(kv); } $( else { @@ -909,7 +1078,8 @@ fn op_otel_span_attribute<'s>( ) { if let Some(temporary_span) = state.try_borrow_mut::() { temporary_span.0.attributes.reserve_exact( - (capacity as usize) - temporary_span.0.attributes.capacity(), + (capacity as usize) + .saturating_sub(temporary_span.0.attributes.capacity()), ); attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key, value); } @@ -927,7 +1097,8 @@ fn op_otel_span_attribute2<'s>( ) { if let Some(temporary_span) = state.try_borrow_mut::() { temporary_span.0.attributes.reserve_exact( - (capacity as usize) - temporary_span.0.attributes.capacity(), + (capacity as usize) + .saturating_sub(temporary_span.0.attributes.capacity()), ); attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1); attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2); @@ -949,7 +1120,8 @@ fn op_otel_span_attribute3<'s>( ) { if let Some(temporary_span) = state.try_borrow_mut::() { temporary_span.0.attributes.reserve_exact( - (capacity as usize) - temporary_span.0.attributes.capacity(), + (capacity as usize) + .saturating_sub(temporary_span.0.attributes.capacity()), ); attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1); attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2); @@ -984,538 +1156,572 @@ fn op_otel_span_flush(state: &mut OpState) { spans.on_end(temporary_span.0); } -// Holds data being built from JS before -// it is submitted to the rust processor. -struct TemporaryMetricsExport { - resource_attributes: Vec, - scope_metrics: Vec, - metric: Option, +enum Instrument { + Counter(opentelemetry::metrics::Counter), + UpDownCounter(opentelemetry::metrics::UpDownCounter), + Gauge(opentelemetry::metrics::Gauge), + Histogram(opentelemetry::metrics::Histogram), + Observable(Arc, f64>>>), } -struct TemporaryMetric { - name: String, - description: String, - unit: String, - data: TemporaryMetricData, -} +impl GarbageCollected for Instrument {} -enum TemporaryMetricData { - Sum(opentelemetry_sdk::metrics::data::Sum), - Gauge(opentelemetry_sdk::metrics::data::Gauge), - Histogram(opentelemetry_sdk::metrics::data::Histogram), -} - -impl From for Metric { - fn from(value: TemporaryMetric) -> Self { - Metric { - name: Cow::Owned(value.name), - description: Cow::Owned(value.description), - unit: Cow::Owned(value.unit), - data: match value.data { - TemporaryMetricData::Sum(sum) => Box::new(sum), - TemporaryMetricData::Gauge(gauge) => Box::new(gauge), - TemporaryMetricData::Histogram(histogram) => Box::new(histogram), - }, - } - } -} - -#[op2(fast)] -fn op_otel_metrics_resource_attribute<'s>( - scope: &mut v8::HandleScope<'s>, +fn create_instrument<'a, T>( + cb: impl FnOnce( + &'_ opentelemetry::metrics::Meter, + String, + ) -> InstrumentBuilder<'_, T>, + cb2: impl FnOnce(InstrumentBuilder<'_, T>) -> Instrument, state: &mut OpState, - #[smi] capacity: u32, - key: v8::Local<'s, v8::Value>, - value: v8::Local<'s, v8::Value>, -) { - let metrics_export = if let Some(metrics_export) = - state.try_borrow_mut::() - { - metrics_export.resource_attributes.reserve_exact( - (capacity as usize) - metrics_export.resource_attributes.capacity(), - ); - metrics_export - } else { - state.put(TemporaryMetricsExport { - resource_attributes: Vec::with_capacity(capacity as usize), - scope_metrics: vec![], - metric: None, - }); - state.borrow_mut() + scope: &mut v8::HandleScope<'a>, + name: v8::Local<'a, v8::Value>, + description: v8::Local<'a, v8::Value>, + unit: v8::Local<'a, v8::Value>, +) -> Result { + let Some(InstrumentationScope(instrumentation_scope)) = + state.try_borrow::() + else { + return Err(anyhow!("instrumentation scope not available")); }; - attr!(scope, metrics_export.resource_attributes, key, value); -} -#[op2(fast)] -fn op_otel_metrics_resource_attribute2<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, - key1: v8::Local<'s, v8::Value>, - value1: v8::Local<'s, v8::Value>, - key2: v8::Local<'s, v8::Value>, - value2: v8::Local<'s, v8::Value>, -) { - let metrics_export = if let Some(metrics_export) = - state.try_borrow_mut::() - { - metrics_export.resource_attributes.reserve_exact( - (capacity as usize) - metrics_export.resource_attributes.capacity(), - ); - metrics_export - } else { - state.put(TemporaryMetricsExport { - resource_attributes: Vec::with_capacity(capacity as usize), - scope_metrics: vec![], - metric: None, - }); - state.borrow_mut() + let meter = OTEL_PROCESSORS + .get() + .unwrap() + .meter_provider + .meter_with_scope(instrumentation_scope.clone()); + + let name = owned_string(scope, name.try_cast()?); + let mut builder = cb(&meter, name); + if !description.is_null_or_undefined() { + let description = owned_string(scope, description.try_cast()?); + builder = builder.with_description(description); }; - attr!(scope, metrics_export.resource_attributes, key1, value1); - attr!(scope, metrics_export.resource_attributes, key2, value2); -} - -#[allow(clippy::too_many_arguments)] -#[op2(fast)] -fn op_otel_metrics_resource_attribute3<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, - key1: v8::Local<'s, v8::Value>, - value1: v8::Local<'s, v8::Value>, - key2: v8::Local<'s, v8::Value>, - value2: v8::Local<'s, v8::Value>, - key3: v8::Local<'s, v8::Value>, - value3: v8::Local<'s, v8::Value>, -) { - let metrics_export = if let Some(metrics_export) = - state.try_borrow_mut::() - { - metrics_export.resource_attributes.reserve_exact( - (capacity as usize) - metrics_export.resource_attributes.capacity(), - ); - metrics_export - } else { - state.put(TemporaryMetricsExport { - resource_attributes: Vec::with_capacity(capacity as usize), - scope_metrics: vec![], - metric: None, - }); - state.borrow_mut() + if !unit.is_null_or_undefined() { + let unit = owned_string(scope, unit.try_cast()?); + builder = builder.with_unit(unit); }; - attr!(scope, metrics_export.resource_attributes, key1, value1); - attr!(scope, metrics_export.resource_attributes, key2, value2); - attr!(scope, metrics_export.resource_attributes, key3, value3); + + Ok(cb2(builder)) } -#[op2(fast)] -fn op_otel_metrics_scope<'s>( - scope: &mut v8::HandleScope<'s>, +#[op2] +#[cppgc] +fn op_otel_metric_create_counter<'s>( state: &mut OpState, + scope: &mut v8::HandleScope<'s>, name: v8::Local<'s, v8::Value>, - schema_url: v8::Local<'s, v8::Value>, - version: v8::Local<'s, v8::Value>, -) { - let name = owned_string(scope, name.cast()); - - let scope_builder = opentelemetry::InstrumentationScope::builder(name); - let scope_builder = if schema_url.is_null_or_undefined() { - scope_builder - } else { - scope_builder.with_schema_url(owned_string(scope, schema_url.cast())) - }; - let scope_builder = if version.is_null_or_undefined() { - scope_builder - } else { - scope_builder.with_version(owned_string(scope, version.cast())) - }; - let scope = scope_builder.build(); - let scope_metric = ScopeMetrics { + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, +) -> Result { + create_instrument( + |meter, name| meter.f64_counter(name), + |i| Instrument::Counter(i.build()), + state, scope, - metrics: vec![], - }; - - match state.try_borrow_mut::() { - Some(temp) => { - if let Some(current_metric) = temp.metric.take() { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); - } - temp.scope_metrics.push(scope_metric); - } - None => { - state.put(TemporaryMetricsExport { - resource_attributes: vec![], - scope_metrics: vec![scope_metric], - metric: None, - }); - } - } -} - -#[op2(fast)] -fn op_otel_metrics_sum<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, - #[smi] temporality: u8, - is_monotonic: bool, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(current_metric) = temp.metric.take() { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); - } - - let name = owned_string(scope, name.cast()); - let description = owned_string(scope, description.cast()); - let unit = owned_string(scope, unit.cast()); - let temporality = match temporality { - 0 => Temporality::Delta, - 1 => Temporality::Cumulative, - _ => return, - }; - let sum = opentelemetry_sdk::metrics::data::Sum { - data_points: vec![], - temporality, - is_monotonic, - }; - - temp.metric = Some(TemporaryMetric { name, description, unit, - data: TemporaryMetricData::Sum(sum), - }); + ) } -#[op2(fast)] -fn op_otel_metrics_gauge<'s>( - scope: &mut v8::HandleScope<'s>, +#[op2] +#[cppgc] +fn op_otel_metric_create_up_down_counter<'s>( state: &mut OpState, + scope: &mut v8::HandleScope<'s>, name: v8::Local<'s, v8::Value>, description: v8::Local<'s, v8::Value>, unit: v8::Local<'s, v8::Value>, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(current_metric) = temp.metric.take() { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); - } - - let name = owned_string(scope, name.cast()); - let description = owned_string(scope, description.cast()); - let unit = owned_string(scope, unit.cast()); - - let gauge = opentelemetry_sdk::metrics::data::Gauge { - data_points: vec![], - }; - - temp.metric = Some(TemporaryMetric { +) -> Result { + create_instrument( + |meter, name| meter.f64_up_down_counter(name), + |i| Instrument::UpDownCounter(i.build()), + state, + scope, name, description, unit, - data: TemporaryMetricData::Gauge(gauge), + ) +} + +#[op2] +#[cppgc] +fn op_otel_metric_create_gauge<'s>( + state: &mut OpState, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, +) -> Result { + create_instrument( + |meter, name| meter.f64_gauge(name), + |i| Instrument::Gauge(i.build()), + state, + scope, + name, + description, + unit, + ) +} + +#[op2] +#[cppgc] +fn op_otel_metric_create_histogram<'s>( + state: &mut OpState, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + #[serde] boundaries: Option>, +) -> Result { + let Some(InstrumentationScope(instrumentation_scope)) = + state.try_borrow::() + else { + return Err(anyhow!("instrumentation scope not available")); + }; + + let meter = OTEL_PROCESSORS + .get() + .unwrap() + .meter_provider + .meter_with_scope(instrumentation_scope.clone()); + + let name = owned_string(scope, name.try_cast()?); + let mut builder = meter.f64_histogram(name); + if !description.is_null_or_undefined() { + let description = owned_string(scope, description.try_cast()?); + builder = builder.with_description(description); + }; + if !unit.is_null_or_undefined() { + let unit = owned_string(scope, unit.try_cast()?); + builder = builder.with_unit(unit); + }; + if let Some(boundaries) = boundaries { + builder = builder.with_boundaries(boundaries); + } + + Ok(Instrument::Histogram(builder.build())) +} + +fn create_async_instrument<'a, T>( + cb: impl FnOnce( + &'_ opentelemetry::metrics::Meter, + String, + ) -> AsyncInstrumentBuilder<'_, T, f64>, + cb2: impl FnOnce(AsyncInstrumentBuilder<'_, T, f64>), + state: &mut OpState, + scope: &mut v8::HandleScope<'a>, + name: v8::Local<'a, v8::Value>, + description: v8::Local<'a, v8::Value>, + unit: v8::Local<'a, v8::Value>, +) -> Result { + let Some(InstrumentationScope(instrumentation_scope)) = + state.try_borrow::() + else { + return Err(anyhow!("instrumentation scope not available")); + }; + + let meter = OTEL_PROCESSORS + .get() + .unwrap() + .meter_provider + .meter_with_scope(instrumentation_scope.clone()); + + let name = owned_string(scope, name.try_cast()?); + let mut builder = cb(&meter, name); + if !description.is_null_or_undefined() { + let description = owned_string(scope, description.try_cast()?); + builder = builder.with_description(description); + }; + if !unit.is_null_or_undefined() { + let unit = owned_string(scope, unit.try_cast()?); + builder = builder.with_unit(unit); + }; + + let data_share = Arc::new(Mutex::new(HashMap::new())); + let data_share_: Arc, f64>>> = data_share.clone(); + builder = builder.with_callback(move |i| { + let data = { + let mut data = data_share_.lock().unwrap(); + std::mem::take(&mut *data) + }; + for (attributes, value) in data { + i.observe(value, &attributes); + } }); + cb2(builder); + + Ok(Instrument::Observable(data_share)) +} + +#[op2] +#[cppgc] +fn op_otel_metric_create_observable_counter<'s>( + state: &mut OpState, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, +) -> Result { + create_async_instrument( + |meter, name| meter.f64_observable_counter(name), + |i| { + i.build(); + }, + state, + scope, + name, + description, + unit, + ) +} + +#[op2] +#[cppgc] +fn op_otel_metric_create_observable_up_down_counter<'s>( + state: &mut OpState, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, +) -> Result { + create_async_instrument( + |meter, name| meter.f64_observable_up_down_counter(name), + |i| { + i.build(); + }, + state, + scope, + name, + description, + unit, + ) +} + +#[op2] +#[cppgc] +fn op_otel_metric_create_observable_gauge<'s>( + state: &mut OpState, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, +) -> Result { + create_async_instrument( + |meter, name| meter.f64_observable_gauge(name), + |i| { + i.build(); + }, + state, + scope, + name, + description, + unit, + ) +} + +struct MetricAttributes { + attributes: Vec, } #[op2(fast)] -fn op_otel_metrics_sum_or_gauge_data_point( +fn op_otel_metric_record0( state: &mut OpState, + #[cppgc] instrument: &Instrument, value: f64, - start_time: f64, - time: f64, ) { - let Some(temp) = state.try_borrow_mut::() else { - return; + let values = state.try_take::(); + let attributes = match &values { + Some(values) => &*values.attributes, + None => &[], }; - - let start_time = SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(start_time)) - .unwrap(); - let time = SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(time)) - .unwrap(); - - let data_point = opentelemetry_sdk::metrics::data::DataPoint { - value, - start_time: Some(start_time), - time: Some(time), - attributes: vec![], - exemplars: vec![], - }; - - match &mut temp.metric { - Some(TemporaryMetric { - data: TemporaryMetricData::Sum(sum), - .. - }) => sum.data_points.push(data_point), - Some(TemporaryMetric { - data: TemporaryMetricData::Gauge(gauge), - .. - }) => gauge.data_points.push(data_point), + match instrument { + Instrument::Counter(counter) => counter.add(value, attributes), + Instrument::UpDownCounter(counter) => counter.add(value, attributes), + Instrument::Gauge(gauge) => gauge.record(value, attributes), + Instrument::Histogram(histogram) => histogram.record(value, attributes), _ => {} } } #[op2(fast)] -fn op_otel_metrics_histogram<'s>( - scope: &mut v8::HandleScope<'s>, +fn op_otel_metric_record1( state: &mut OpState, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, - #[smi] temporality: u8, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, ) { - let Some(temp) = state.try_borrow_mut::() else { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { return; }; - - if let Some(current_metric) = temp.metric.take() { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); - } - - let name = owned_string(scope, name.cast()); - let description = owned_string(scope, description.cast()); - let unit = owned_string(scope, unit.cast()); - - let temporality = match temporality { - 0 => Temporality::Delta, - 1 => Temporality::Cumulative, - _ => return, - }; - let histogram = opentelemetry_sdk::metrics::data::Histogram { - data_points: vec![], - temporality, - }; - - temp.metric = Some(TemporaryMetric { - name, - description, - unit, - data: TemporaryMetricData::Histogram(histogram), - }); -} - -#[allow(clippy::too_many_arguments)] -#[op2(fast)] -fn op_otel_metrics_histogram_data_point( - state: &mut OpState, - #[number] count: u64, - min: f64, - max: f64, - sum: f64, - start_time: f64, - time: f64, - #[smi] buckets: u32, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - let min = if min.is_nan() { None } else { Some(min) }; - let max = if max.is_nan() { None } else { Some(max) }; - - let start_time = SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(start_time)) - .unwrap(); - let time = SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(time)) - .unwrap(); - - let data_point = opentelemetry_sdk::metrics::data::HistogramDataPoint { - bounds: Vec::with_capacity(buckets as usize), - bucket_counts: Vec::with_capacity((buckets as usize) + 1), - count, - sum, - min, - max, - start_time, - time, - attributes: vec![], - exemplars: vec![], - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - histogram.data_points.push(data_point); - } -} - -#[op2(fast)] -fn op_otel_metrics_histogram_data_point_entry_final( - state: &mut OpState, - #[number] count1: u64, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - histogram - .data_points - .last_mut() - .unwrap() - .bucket_counts - .push(count1) - } -} - -#[op2(fast)] -fn op_otel_metrics_histogram_data_point_entry1( - state: &mut OpState, - #[number] count1: u64, - bound1: f64, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - let data_point = histogram.data_points.last_mut().unwrap(); - data_point.bucket_counts.push(count1); - data_point.bounds.push(bound1); - } -} - -#[op2(fast)] -fn op_otel_metrics_histogram_data_point_entry2( - state: &mut OpState, - #[number] count1: u64, - bound1: f64, - #[number] count2: u64, - bound2: f64, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - let data_point = histogram.data_points.last_mut().unwrap(); - data_point.bucket_counts.push(count1); - data_point.bounds.push(bound1); - data_point.bucket_counts.push(count2); - data_point.bounds.push(bound2); - } -} - -#[op2(fast)] -fn op_otel_metrics_histogram_data_point_entry3( - state: &mut OpState, - #[number] count1: u64, - bound1: f64, - #[number] count2: u64, - bound2: f64, - #[number] count3: u64, - bound3: f64, -) { - let Some(temp) = state.try_borrow_mut::() else { - return; - }; - - if let Some(TemporaryMetric { - data: TemporaryMetricData::Histogram(histogram), - .. - }) = &mut temp.metric - { - let data_point = histogram.data_points.last_mut().unwrap(); - data_point.bucket_counts.push(count1); - data_point.bounds.push(bound1); - data_point.bucket_counts.push(count2); - data_point.bounds.push(bound2); - data_point.bucket_counts.push(count3); - data_point.bounds.push(bound3); - } -} - -#[op2(fast)] -fn op_otel_metrics_data_point_attribute<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, - key: v8::Local<'s, v8::Value>, - value: v8::Local<'s, v8::Value>, -) { - if let Some(TemporaryMetricsExport { - metric: Some(metric), - .. - }) = state.try_borrow_mut::() - { - let attributes = match &mut metric.data { - TemporaryMetricData::Sum(sum) => { - &mut sum.data_points.last_mut().unwrap().attributes + let mut values = state.try_take::(); + let attr1 = attr_raw!(scope, key1, value1); + let attributes = match &mut values { + Some(values) => { + if let Some(kv) = attr1 { + values.attributes.reserve_exact(1); + values.attributes.push(kv); } - TemporaryMetricData::Gauge(gauge) => { - &mut gauge.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Histogram(histogram) => { - &mut histogram.data_points.last_mut().unwrap().attributes - } - }; - attributes.reserve_exact((capacity as usize) - attributes.capacity()); - attr!(scope, attributes, key, value); - } -} - -#[op2(fast)] -fn op_otel_metrics_data_point_attribute2<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, - key1: v8::Local<'s, v8::Value>, - value1: v8::Local<'s, v8::Value>, - key2: v8::Local<'s, v8::Value>, - value2: v8::Local<'s, v8::Value>, -) { - if let Some(TemporaryMetricsExport { - metric: Some(metric), - .. - }) = state.try_borrow_mut::() - { - let attributes = match &mut metric.data { - TemporaryMetricData::Sum(sum) => { - &mut sum.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Gauge(gauge) => { - &mut gauge.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Histogram(histogram) => { - &mut histogram.data_points.last_mut().unwrap().attributes - } - }; - attributes.reserve_exact((capacity as usize) - attributes.capacity()); - attr!(scope, attributes, key1, value1); - attr!(scope, attributes, key2, value2); + &*values.attributes + } + None => match attr1 { + Some(kv1) => &[kv1] as &[KeyValue], + None => &[], + }, + }; + match &*instrument { + Instrument::Counter(counter) => counter.add(value, attributes), + Instrument::UpDownCounter(counter) => counter.add(value, attributes), + Instrument::Gauge(gauge) => gauge.record(value, attributes), + Instrument::Histogram(histogram) => histogram.record(value, attributes), + _ => {} } } #[allow(clippy::too_many_arguments)] #[op2(fast)] -fn op_otel_metrics_data_point_attribute3<'s>( +fn op_otel_metric_record2( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, + key2: v8::Local<'_, v8::Value>, + value2: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let mut values = state.try_take::(); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + let attributes = match &mut values { + Some(values) => { + values.attributes.reserve_exact(2); + if let Some(kv1) = attr1 { + values.attributes.push(kv1); + } + if let Some(kv2) = attr2 { + values.attributes.push(kv2); + } + &*values.attributes + } + None => match (attr1, attr2) { + (Some(kv1), Some(kv2)) => &[kv1, kv2] as &[KeyValue], + (Some(kv1), None) => &[kv1], + (None, Some(kv2)) => &[kv2], + (None, None) => &[], + }, + }; + match &*instrument { + Instrument::Counter(counter) => counter.add(value, attributes), + Instrument::UpDownCounter(counter) => counter.add(value, attributes), + Instrument::Gauge(gauge) => gauge.record(value, attributes), + Instrument::Histogram(histogram) => histogram.record(value, attributes), + _ => {} + } +} + +#[allow(clippy::too_many_arguments)] +#[op2(fast)] +fn op_otel_metric_record3( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, + key2: v8::Local<'_, v8::Value>, + value2: v8::Local<'_, v8::Value>, + key3: v8::Local<'_, v8::Value>, + value3: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let mut values = state.try_take::(); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + let attr3 = attr_raw!(scope, key3, value3); + let attributes = match &mut values { + Some(values) => { + values.attributes.reserve_exact(3); + if let Some(kv1) = attr1 { + values.attributes.push(kv1); + } + if let Some(kv2) = attr2 { + values.attributes.push(kv2); + } + if let Some(kv3) = attr3 { + values.attributes.push(kv3); + } + &*values.attributes + } + None => match (attr1, attr2, attr3) { + (Some(kv1), Some(kv2), Some(kv3)) => &[kv1, kv2, kv3] as &[KeyValue], + (Some(kv1), Some(kv2), None) => &[kv1, kv2], + (Some(kv1), None, Some(kv3)) => &[kv1, kv3], + (None, Some(kv2), Some(kv3)) => &[kv2, kv3], + (Some(kv1), None, None) => &[kv1], + (None, Some(kv2), None) => &[kv2], + (None, None, Some(kv3)) => &[kv3], + (None, None, None) => &[], + }, + }; + match &*instrument { + Instrument::Counter(counter) => counter.add(value, attributes), + Instrument::UpDownCounter(counter) => counter.add(value, attributes), + Instrument::Gauge(gauge) => gauge.record(value, attributes), + Instrument::Histogram(histogram) => histogram.record(value, attributes), + _ => {} + } +} + +#[op2(fast)] +fn op_otel_metric_observable_record0( + state: &mut OpState, + #[cppgc] instrument: &Instrument, + value: f64, +) { + let values = state.try_take::(); + let attributes = values.map(|attr| attr.attributes).unwrap_or_default(); + if let Instrument::Observable(data_share) = instrument { + let mut data = data_share.lock().unwrap(); + data.insert(attributes, value); + } +} + +#[op2(fast)] +fn op_otel_metric_observable_record1( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let values = state.try_take::(); + let attr1 = attr_raw!(scope, key1, value1); + let mut attributes = values + .map(|mut attr| { + attr.attributes.reserve_exact(1); + attr.attributes + }) + .unwrap_or_else(|| Vec::with_capacity(1)); + if let Some(kv1) = attr1 { + attributes.push(kv1); + } + if let Instrument::Observable(data_share) = &*instrument { + let mut data = data_share.lock().unwrap(); + data.insert(attributes, value); + } +} + +#[allow(clippy::too_many_arguments)] +#[op2(fast)] +fn op_otel_metric_observable_record2( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, + key2: v8::Local<'_, v8::Value>, + value2: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let values = state.try_take::(); + let mut attributes = values + .map(|mut attr| { + attr.attributes.reserve_exact(2); + attr.attributes + }) + .unwrap_or_else(|| Vec::with_capacity(2)); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + if let Some(kv1) = attr1 { + attributes.push(kv1); + } + if let Some(kv2) = attr2 { + attributes.push(kv2); + } + if let Instrument::Observable(data_share) = &*instrument { + let mut data = data_share.lock().unwrap(); + data.insert(attributes, value); + } +} + +#[allow(clippy::too_many_arguments)] +#[op2(fast)] +fn op_otel_metric_observable_record3( + state: &mut OpState, + scope: &mut v8::HandleScope<'_>, + instrument: v8::Local<'_, v8::Value>, + value: f64, + key1: v8::Local<'_, v8::Value>, + value1: v8::Local<'_, v8::Value>, + key2: v8::Local<'_, v8::Value>, + value2: v8::Local<'_, v8::Value>, + key3: v8::Local<'_, v8::Value>, + value3: v8::Local<'_, v8::Value>, +) { + let Some(instrument) = deno_core::_ops::try_unwrap_cppgc_object::( + &mut *scope, + instrument, + ) else { + return; + }; + let values = state.try_take::(); + let mut attributes = values + .map(|mut attr| { + attr.attributes.reserve_exact(3); + attr.attributes + }) + .unwrap_or_else(|| Vec::with_capacity(3)); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + let attr3 = attr_raw!(scope, key3, value3); + if let Some(kv1) = attr1 { + attributes.push(kv1); + } + if let Some(kv2) = attr2 { + attributes.push(kv2); + } + if let Some(kv3) = attr3 { + attributes.push(kv3); + } + if let Instrument::Observable(data_share) = &*instrument { + let mut data = data_share.lock().unwrap(); + data.insert(attributes, value); + } +} + +#[allow(clippy::too_many_arguments)] +#[op2(fast)] +fn op_otel_metric_attribute3<'s>( scope: &mut v8::HandleScope<'s>, state: &mut OpState, #[smi] capacity: u32, @@ -1526,49 +1732,60 @@ fn op_otel_metrics_data_point_attribute3<'s>( key3: v8::Local<'s, v8::Value>, value3: v8::Local<'s, v8::Value>, ) { - if let Some(TemporaryMetricsExport { - metric: Some(metric), - .. - }) = state.try_borrow_mut::() + let mut values = state.try_borrow_mut::(); + let attr1 = attr_raw!(scope, key1, value1); + let attr2 = attr_raw!(scope, key2, value2); + let attr3 = attr_raw!(scope, key3, value3); + if let Some(values) = &mut values { + values.attributes.reserve_exact( + (capacity as usize).saturating_sub(values.attributes.capacity()), + ); + if let Some(kv1) = attr1 { + values.attributes.push(kv1); + } + if let Some(kv2) = attr2 { + values.attributes.push(kv2); + } + if let Some(kv3) = attr3 { + values.attributes.push(kv3); + } + } else { + let mut attributes = Vec::with_capacity(capacity as usize); + if let Some(kv1) = attr1 { + attributes.push(kv1); + } + if let Some(kv2) = attr2 { + attributes.push(kv2); + } + if let Some(kv3) = attr3 { + attributes.push(kv3); + } + state.put(MetricAttributes { attributes }); + } +} + +struct ObservationDone(oneshot::Sender<()>); + +#[op2(async)] +async fn op_otel_metric_wait_to_observe(state: Rc>) -> bool { + let (tx, rx) = oneshot::channel(); { - let attributes = match &mut metric.data { - TemporaryMetricData::Sum(sum) => { - &mut sum.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Gauge(gauge) => { - &mut gauge.data_points.last_mut().unwrap().attributes - } - TemporaryMetricData::Histogram(histogram) => { - &mut histogram.data_points.last_mut().unwrap().attributes - } - }; - attributes.reserve_exact((capacity as usize) - attributes.capacity()); - attr!(scope, attributes, key1, value1); - attr!(scope, attributes, key2, value2); - attr!(scope, attributes, key3, value3); + OTEL_PRE_COLLECT_CALLBACKS + .lock() + .expect("mutex poisoned") + .push(tx); + } + if let Ok(done) = rx.await { + state.borrow_mut().put(ObservationDone(done)); + true + } else { + false } } #[op2(fast)] -fn op_otel_metrics_submit(state: &mut OpState) { - let Some(mut temp) = state.try_take::() else { - return; - }; - - let Some(Processors { metrics, .. }) = OTEL_PROCESSORS.get() else { - return; - }; - - if let Some(current_metric) = temp.metric { - let metric = Metric::from(current_metric); - temp.scope_metrics.last_mut().unwrap().metrics.push(metric); +fn op_otel_metric_observation_done(state: &mut OpState) { + if let Some(ObservationDone(done)) = state.try_take::() { + let _ = done.send(()); } - - let resource = Resource::new(temp.resource_attributes); - let scope_metrics = temp.scope_metrics; - - metrics.submit(ResourceMetrics { - resource, - scope_metrics, - }); } diff --git a/ext/telemetry/telemetry.ts b/ext/telemetry/telemetry.ts index d1335f65b5..86b4fe059d 100644 --- a/ext/telemetry/telemetry.ts +++ b/ext/telemetry/telemetry.ts @@ -7,23 +7,24 @@ import { op_otel_instrumentation_scope_enter, op_otel_instrumentation_scope_enter_builtin, op_otel_log, - op_otel_metrics_data_point_attribute, - op_otel_metrics_data_point_attribute2, - op_otel_metrics_data_point_attribute3, - op_otel_metrics_gauge, - op_otel_metrics_histogram, - op_otel_metrics_histogram_data_point, - op_otel_metrics_histogram_data_point_entry1, - op_otel_metrics_histogram_data_point_entry2, - op_otel_metrics_histogram_data_point_entry3, - op_otel_metrics_histogram_data_point_entry_final, - op_otel_metrics_resource_attribute, - op_otel_metrics_resource_attribute2, - op_otel_metrics_resource_attribute3, - op_otel_metrics_scope, - op_otel_metrics_submit, - op_otel_metrics_sum, - op_otel_metrics_sum_or_gauge_data_point, + op_otel_metric_attribute3, + op_otel_metric_create_counter, + op_otel_metric_create_gauge, + op_otel_metric_create_histogram, + op_otel_metric_create_observable_counter, + op_otel_metric_create_observable_gauge, + op_otel_metric_create_observable_up_down_counter, + op_otel_metric_create_up_down_counter, + op_otel_metric_observable_record0, + op_otel_metric_observable_record1, + op_otel_metric_observable_record2, + op_otel_metric_observable_record3, + op_otel_metric_observation_done, + op_otel_metric_record0, + op_otel_metric_record1, + op_otel_metric_record2, + op_otel_metric_record3, + op_otel_metric_wait_to_observe, op_otel_span_attribute, op_otel_span_attribute2, op_otel_span_attribute3, @@ -36,25 +37,32 @@ import { Console } from "ext:deno_console/01_console.js"; import { performance } from "ext:deno_web/15_performance.js"; const { - SafeWeakMap, Array, - ObjectEntries, - ReflectApply, - SymbolFor, + ArrayPrototypePush, Error, - Uint8Array, - TypedArrayPrototypeSubarray, ObjectAssign, ObjectDefineProperty, - WeakRefPrototypeDeref, + ObjectEntries, + ObjectPrototypeIsPrototypeOf, + ReflectApply, + SafeIterator, + SafeMap, + SafePromiseAll, + SafeSet, + SafeWeakMap, + SafeWeakRef, + SafeWeakSet, String, StringPrototypePadStart, - ObjectPrototypeIsPrototypeOf, - SafeWeakRef, + SymbolFor, + TypedArrayPrototypeSubarray, + Uint8Array, + WeakRefPrototypeDeref, } = primordials; const { AsyncVariable, setAsyncContext } = core; export let TRACING_ENABLED = false; +export let METRICS_ENABLED = false; let DETERMINISTIC = false; // Note: These start at 0 in the JS library, @@ -202,30 +210,9 @@ const instrumentationScopes = new SafeWeakMap< >(); let activeInstrumentationLibrary: WeakRef | null = null; -function submitSpan( - spanId: string | Uint8Array, - traceId: string | Uint8Array, - traceFlags: number, - parentSpanId: string | Uint8Array | null, - span: Omit< - ReadableSpan, - | "spanContext" - | "startTime" - | "endTime" - | "parentSpanId" - | "duration" - | "ended" - | "resource" - >, - startTime: number, - endTime: number, +function activateInstrumentationLibrary( + instrumentationLibrary: InstrumentationLibrary, ) { - if (!TRACING_ENABLED) return; - if (!(traceFlags & TRACE_FLAG_SAMPLED)) return; - - // TODO(@lucacasonato): `resource` is ignored for now, should we implement it? - - const instrumentationLibrary = span.instrumentationLibrary; if ( !activeInstrumentationLibrary || WeakRefPrototypeDeref(activeInstrumentationLibrary) !== @@ -255,6 +242,32 @@ function submitSpan( } } } +} + +function submitSpan( + spanId: string | Uint8Array, + traceId: string | Uint8Array, + traceFlags: number, + parentSpanId: string | Uint8Array | null, + span: Omit< + ReadableSpan, + | "spanContext" + | "startTime" + | "endTime" + | "parentSpanId" + | "duration" + | "ended" + | "resource" + >, + startTime: number, + endTime: number, +) { + if (!TRACING_ENABLED) return; + if (!(traceFlags & TRACE_FLAG_SAMPLED)) return; + + // TODO(@lucacasonato): `resource` is ignored for now, should we implement it? + + activateInstrumentationLibrary(span.instrumentationLibrary); op_otel_span_start( traceId, @@ -368,7 +381,7 @@ export let endSpan: (span: Span) => void; export class Span { #traceId: string | Uint8Array; - #spanId: Uint8Array; + #spanId: string | Uint8Array; #traceFlags = TRACE_FLAG_SAMPLED; #spanContext: SpanContext | null = null; @@ -687,260 +700,510 @@ class ContextManager { } } -function attributeValue(value: IAnyValue) { - return value.boolValue ?? value.stringValue ?? value.doubleValue ?? - value.intValue; +// metrics + +interface MeterOptions { + schemaUrl?: string; } -function submitMetrics(resource, scopeMetrics) { - let i = 0; - while (i < resource.attributes.length) { - if (i + 2 < resource.attributes.length) { - op_otel_metrics_resource_attribute3( - resource.attributes.length, - resource.attributes[i].key, - attributeValue(resource.attributes[i].value), - resource.attributes[i + 1].key, - attributeValue(resource.attributes[i + 1].value), - resource.attributes[i + 2].key, - attributeValue(resource.attributes[i + 2].value), - ); - i += 3; - } else if (i + 1 < resource.attributes.length) { - op_otel_metrics_resource_attribute2( - resource.attributes.length, - resource.attributes[i].key, - attributeValue(resource.attributes[i].value), - resource.attributes[i + 1].key, - attributeValue(resource.attributes[i + 1].value), - ); - i += 2; - } else { - op_otel_metrics_resource_attribute( - resource.attributes.length, - resource.attributes[i].key, - attributeValue(resource.attributes[i].value), - ); - i += 1; - } +interface MetricOptions { + description?: string; + + unit?: string; + + valueType?: ValueType; + + advice?: MetricAdvice; +} + +enum ValueType { + INT = 0, + DOUBLE = 1, +} + +interface MetricAdvice { + /** + * Hint the explicit bucket boundaries for SDK if the metric is been + * aggregated with a HistogramAggregator. + */ + explicitBucketBoundaries?: number[]; +} + +export class MeterProvider { + getMeter(name: string, version?: string, options?: MeterOptions): Meter { + return new Meter({ name, version, schemaUrl: options?.schemaUrl }); + } +} + +type MetricAttributes = Attributes; + +type Instrument = { __key: "instrument" }; + +let batchResultHasObservables: ( + res: BatchObservableResult, + observables: Observable[], +) => boolean; + +class BatchObservableResult { + #observables: WeakSet; + + constructor(observables: WeakSet) { + this.#observables = observables; } - for (let smi = 0; smi < scopeMetrics.length; smi += 1) { - const { scope, metrics } = scopeMetrics[smi]; + static { + batchResultHasObservables = (cb, observables) => { + for (const observable of new SafeIterator(observables)) { + if (!cb.#observables.has(observable)) return false; + } + return true; + }; + } - op_otel_metrics_scope(scope.name, scope.schemaUrl, scope.version); + observe( + metric: Observable, + value: number, + attributes?: MetricAttributes, + ): void { + if (!this.#observables.has(metric)) return; + getObservableResult(metric).observe(value, attributes); + } +} - for (let mi = 0; mi < metrics.length; mi += 1) { - const metric = metrics[mi]; - switch (metric.dataPointType) { - case 3: - op_otel_metrics_sum( - metric.descriptor.name, - // deno-lint-ignore prefer-primordials - metric.descriptor.description, - metric.descriptor.unit, - metric.aggregationTemporality, - metric.isMonotonic, - ); - for (let di = 0; di < metric.dataPoints.length; di += 1) { - const dataPoint = metric.dataPoints[di]; - op_otel_metrics_sum_or_gauge_data_point( - dataPoint.value, - hrToSecs(dataPoint.startTime), - hrToSecs(dataPoint.endTime), - ); - const attributes = ObjectEntries(dataPoint.attributes); - let i = 0; - while (i < attributes.length) { - if (i + 2 < attributes.length) { - op_otel_metrics_data_point_attribute3( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - attributes[i + 2][0], - attributes[i + 2][1], - ); - i += 3; - } else if (i + 1 < attributes.length) { - op_otel_metrics_data_point_attribute2( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - ); - i += 2; - } else { - op_otel_metrics_data_point_attribute( - attributes.length, - attributes[i][0], - attributes[i][1], - ); - i += 1; - } - } - } - break; - case 2: - op_otel_metrics_gauge( - metric.descriptor.name, - // deno-lint-ignore prefer-primordials - metric.descriptor.description, - metric.descriptor.unit, - ); - for (let di = 0; di < metric.dataPoints.length; di += 1) { - const dataPoint = metric.dataPoints[di]; - op_otel_metrics_sum_or_gauge_data_point( - dataPoint.value, - hrToSecs(dataPoint.startTime), - hrToSecs(dataPoint.endTime), - ); - const attributes = ObjectEntries(dataPoint.attributes); - let i = 0; - while (i < attributes.length) { - if (i + 2 < attributes.length) { - op_otel_metrics_data_point_attribute3( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - attributes[i + 2][0], - attributes[i + 2][1], - ); - i += 3; - } else if (i + 1 < attributes.length) { - op_otel_metrics_data_point_attribute2( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - ); - i += 2; - } else { - op_otel_metrics_data_point_attribute( - attributes.length, - attributes[i][0], - attributes[i][1], - ); - i += 1; - } - } - } - break; - case 0: - op_otel_metrics_histogram( - metric.descriptor.name, - // deno-lint-ignore prefer-primordials - metric.descriptor.description, - metric.descriptor.unit, - metric.aggregationTemporality, - ); - for (let di = 0; di < metric.dataPoints.length; di += 1) { - const dataPoint = metric.dataPoints[di]; - const { boundaries, counts } = dataPoint.value.buckets; - op_otel_metrics_histogram_data_point( - dataPoint.value.count, - dataPoint.value.min ?? NaN, - dataPoint.value.max ?? NaN, - dataPoint.value.sum, - hrToSecs(dataPoint.startTime), - hrToSecs(dataPoint.endTime), - boundaries.length, - ); - let j = 0; - while (j < boundaries.length) { - if (j + 3 < boundaries.length) { - op_otel_metrics_histogram_data_point_entry3( - counts[j], - boundaries[j], - counts[j + 1], - boundaries[j + 1], - counts[j + 2], - boundaries[j + 2], - ); - j += 3; - } else if (j + 2 < boundaries.length) { - op_otel_metrics_histogram_data_point_entry2( - counts[j], - boundaries[j], - counts[j + 1], - boundaries[j + 1], - ); - j += 2; - } else { - op_otel_metrics_histogram_data_point_entry1( - counts[j], - boundaries[j], - ); - j += 1; - } - } - op_otel_metrics_histogram_data_point_entry_final(counts[j]); - const attributes = ObjectEntries(dataPoint.attributes); - let i = 0; - while (i < attributes.length) { - if (i + 2 < attributes.length) { - op_otel_metrics_data_point_attribute3( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - attributes[i + 2][0], - attributes[i + 2][1], - ); - i += 3; - } else if (i + 1 < attributes.length) { - op_otel_metrics_data_point_attribute2( - attributes.length, - attributes[i][0], - attributes[i][1], - attributes[i + 1][0], - attributes[i + 1][1], - ); - i += 2; - } else { - op_otel_metrics_data_point_attribute( - attributes.length, - attributes[i][0], - attributes[i][1], - ); - i += 1; - } - } - } - break; - default: - continue; +const BATCH_CALLBACKS = new SafeMap< + BatchObservableCallback, + BatchObservableResult +>(); +const INDIVIDUAL_CALLBACKS = new SafeMap>(); + +class Meter { + #instrumentationLibrary: InstrumentationLibrary; + + constructor(instrumentationLibrary: InstrumentationLibrary) { + this.#instrumentationLibrary = instrumentationLibrary; + } + + createCounter( + name: string, + options?: MetricOptions, + ): Counter { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) return new Counter(null, false); + activateInstrumentationLibrary(this.#instrumentationLibrary); + const instrument = op_otel_metric_create_counter( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Counter(instrument, false); + } + + createUpDownCounter( + name: string, + options?: MetricOptions, + ): Counter { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) return new Counter(null, true); + activateInstrumentationLibrary(this.#instrumentationLibrary); + const instrument = op_otel_metric_create_up_down_counter( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Counter(instrument, true); + } + + createGauge( + name: string, + options?: MetricOptions, + ): Gauge { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) return new Gauge(null); + activateInstrumentationLibrary(this.#instrumentationLibrary); + const instrument = op_otel_metric_create_gauge( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Gauge(instrument); + } + + createHistogram( + name: string, + options?: MetricOptions, + ): Histogram { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) return new Histogram(null); + activateInstrumentationLibrary(this.#instrumentationLibrary); + const instrument = op_otel_metric_create_histogram( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + options?.advice?.explicitBucketBoundaries, + ) as Instrument; + return new Histogram(instrument); + } + + createObservableCounter( + name: string, + options?: MetricOptions, + ): Observable { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) new Observable(new ObservableResult(null, true)); + activateInstrumentationLibrary(this.#instrumentationLibrary); + const instrument = op_otel_metric_create_observable_counter( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Observable(new ObservableResult(instrument, true)); + } + + createObservableGauge( + name: string, + options?: MetricOptions, + ): Observable { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) new Observable(new ObservableResult(null, false)); + activateInstrumentationLibrary(this.#instrumentationLibrary); + const instrument = op_otel_metric_create_observable_gauge( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Observable(new ObservableResult(instrument, false)); + } + + createObservableUpDownCounter( + name: string, + options?: MetricOptions, + ): Observable { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) new Observable(new ObservableResult(null, false)); + activateInstrumentationLibrary(this.#instrumentationLibrary); + const instrument = op_otel_metric_create_observable_up_down_counter( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Observable(new ObservableResult(instrument, false)); + } + + addBatchObservableCallback( + callback: BatchObservableCallback, + observables: Observable[], + ): void { + if (!METRICS_ENABLED) return; + const result = new BatchObservableResult(new SafeWeakSet(observables)); + startObserving(); + BATCH_CALLBACKS.set(callback, result); + } + + removeBatchObservableCallback( + callback: BatchObservableCallback, + observables: Observable[], + ): void { + if (!METRICS_ENABLED) return; + const result = BATCH_CALLBACKS.get(callback); + if (result && batchResultHasObservables(result, observables)) { + BATCH_CALLBACKS.delete(callback); + } + } +} + +type BatchObservableCallback = ( + observableResult: BatchObservableResult, +) => void | Promise; + +function record( + instrument: Instrument | null, + value: number, + attributes?: MetricAttributes, +) { + if (instrument === null) return; + if (attributes === undefined) { + op_otel_metric_record0(instrument, value); + } else { + const attrs = ObjectEntries(attributes); + if (attrs.length === 0) { + op_otel_metric_record0(instrument, value); + } + let i = 0; + while (i < attrs.length) { + const remaining = attrs.length - i; + if (remaining > 3) { + op_otel_metric_attribute3( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + attrs[i + 2][0], + attrs[i + 2][1], + ); + i += 3; + } else if (remaining === 3) { + op_otel_metric_record3( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + attrs[i + 2][0], + attrs[i + 2][1], + ); + i += 3; + } else if (remaining === 2) { + op_otel_metric_record2( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + ); + i += 2; + } else if (remaining === 1) { + op_otel_metric_record1( + instrument, + value, + attrs[i][0], + attrs[i][1], + ); + i += 1; } } } - - op_otel_metrics_submit(); } -class MetricExporter { - export(metrics, resultCallback: (result: ExportResult) => void) { - try { - submitMetrics(metrics.resource, metrics.scopeMetrics); - resultCallback({ code: 0 }); - } catch (error) { - resultCallback({ - code: 1, - error: ObjectPrototypeIsPrototypeOf(error, Error) - ? error as Error - : new Error(String(error)), - }); +function recordObservable( + instrument: Instrument | null, + value: number, + attributes?: MetricAttributes, +) { + if (instrument === null) return; + if (attributes === undefined) { + op_otel_metric_observable_record0(instrument, value); + } else { + const attrs = ObjectEntries(attributes); + if (attrs.length === 0) { + op_otel_metric_observable_record0(instrument, value); + } + let i = 0; + while (i < attrs.length) { + const remaining = attrs.length - i; + if (remaining > 3) { + op_otel_metric_attribute3( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + attrs[i + 2][0], + attrs[i + 2][1], + ); + i += 3; + } else if (remaining === 3) { + op_otel_metric_observable_record3( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + attrs[i + 2][0], + attrs[i + 2][1], + ); + i += 3; + } else if (remaining === 2) { + op_otel_metric_observable_record2( + instrument, + value, + attrs[i][0], + attrs[i][1], + attrs[i + 1][0], + attrs[i + 1][1], + ); + i += 2; + } else if (remaining === 1) { + op_otel_metric_observable_record1( + instrument, + value, + attrs[i][0], + attrs[i][1], + ); + i += 1; + } } } +} - async forceFlush() {} +class Counter { + #instrument: Instrument | null; + #upDown: boolean; - async shutdown() {} + constructor(instrument: Instrument | null, upDown: boolean) { + this.#instrument = instrument; + this.#upDown = upDown; + } + + add(value: number, attributes?: MetricAttributes, _context?: Context): void { + if (value < 0 && !this.#upDown) { + throw new Error("Counter can only be incremented"); + } + record(this.#instrument, value, attributes); + } +} + +class Gauge { + #instrument: Instrument | null; + + constructor(instrument: Instrument | null) { + this.#instrument = instrument; + } + + record( + value: number, + attributes?: MetricAttributes, + _context?: Context, + ): void { + record(this.#instrument, value, attributes); + } +} + +class Histogram { + #instrument: Instrument | null; + + constructor(instrument: Instrument | null) { + this.#instrument = instrument; + } + + record( + value: number, + attributes?: MetricAttributes, + _context?: Context, + ): void { + record(this.#instrument, value, attributes); + } +} + +type ObservableCallback = ( + observableResult: ObservableResult, +) => void | Promise; + +let getObservableResult: (observable: Observable) => ObservableResult; + +class Observable { + #result: ObservableResult; + + constructor(result: ObservableResult) { + this.#result = result; + } + + static { + getObservableResult = (observable) => observable.#result; + } + + addCallback(callback: ObservableCallback): void { + const res = INDIVIDUAL_CALLBACKS.get(this); + if (res) res.add(callback); + else INDIVIDUAL_CALLBACKS.set(this, new SafeSet([callback])); + startObserving(); + } + + removeCallback(callback: ObservableCallback): void { + const res = INDIVIDUAL_CALLBACKS.get(this); + if (res) res.delete(callback); + if (res?.size === 0) INDIVIDUAL_CALLBACKS.delete(this); + } +} + +class ObservableResult { + #instrument: Instrument | null; + #isRegularCounter: boolean; + + constructor(instrument: Instrument | null, isRegularCounter: boolean) { + this.#instrument = instrument; + this.#isRegularCounter = isRegularCounter; + } + + observe( + this: ObservableResult, + value: number, + attributes?: MetricAttributes, + ): void { + if (this.#isRegularCounter) { + if (value < 0) { + throw new Error("Observable counters can only be incremented"); + } + } + recordObservable(this.#instrument, value, attributes); + } +} + +async function observe(): Promise { + const promises: Promise[] = []; + // Primordials are not needed, because this is a SafeMap. + // deno-lint-ignore prefer-primordials + for (const { 0: observable, 1: callbacks } of INDIVIDUAL_CALLBACKS) { + const result = getObservableResult(observable); + // Primordials are not needed, because this is a SafeSet. + // deno-lint-ignore prefer-primordials + for (const callback of callbacks) { + // PromiseTry is not in primordials? + // deno-lint-ignore prefer-primordials + ArrayPrototypePush(promises, Promise.try(callback, result)); + } + } + // Primordials are not needed, because this is a SafeMap. + // deno-lint-ignore prefer-primordials + for (const { 0: callback, 1: result } of BATCH_CALLBACKS) { + // PromiseTry is not in primordials? + // deno-lint-ignore prefer-primordials + ArrayPrototypePush(promises, Promise.try(callback, result)); + } + await SafePromiseAll(promises); +} + +let isObserving = false; +function startObserving() { + if (!isObserving) { + isObserving = true; + (async () => { + while (true) { + const promise = op_otel_metric_wait_to_observe(); + core.unrefOpPromise(promise); + const ok = await promise; + if (!ok) break; + await observe(); + op_otel_metric_observation_done(); + } + })(); + } } const otelConsoleConfig = { @@ -951,14 +1214,21 @@ const otelConsoleConfig = { export function bootstrap( config: [ + 0 | 1, 0 | 1, typeof otelConsoleConfig[keyof typeof otelConsoleConfig], 0 | 1, ], ): void { - const { 0: tracingEnabled, 1: consoleConfig, 2: deterministic } = config; + const { + 0: tracingEnabled, + 1: metricsEnabled, + 2: consoleConfig, + 3: deterministic, + } = config; TRACING_ENABLED = tracingEnabled === 1; + METRICS_ENABLED = metricsEnabled === 1; DETERMINISTIC = deterministic === 1; switch (consoleConfig) { @@ -980,5 +1250,5 @@ export function bootstrap( export const telemetry = { SpanExporter, ContextManager, - MetricExporter, + MeterProvider, }; diff --git a/tests/specs/cli/otel_basic/__test__.jsonc b/tests/specs/cli/otel_basic/__test__.jsonc index e7f8d17c7a..f9826671e8 100644 --- a/tests/specs/cli/otel_basic/__test__.jsonc +++ b/tests/specs/cli/otel_basic/__test__.jsonc @@ -1,28 +1,27 @@ { - "steps": [ - { + "tests": { + "basic": { "args": "run -A main.ts basic.ts", "output": "basic.out" }, - { + "natural_exit": { "args": "run -A main.ts natural_exit.ts", "output": "natural_exit.out" }, - { + "deno_dot_exit": { "args": "run -A main.ts deno_dot_exit.ts", "output": "deno_dot_exit.out" }, - { + "uncaught": { "args": "run -A main.ts uncaught.ts", "output": "uncaught.out" }, - { + "metric": { + "envs": { + "OTEL_METRIC_EXPORT_INTERVAL": "1000" + }, "args": "run -A main.ts metric.ts", "output": "metric.out" - }, - { - "args": "run -A --unstable-otel context.ts", - "output": "" } - ] + } } diff --git a/tests/specs/cli/otel_basic/metric.out b/tests/specs/cli/otel_basic/metric.out index 26ed4a23c6..dd53734230 100644 --- a/tests/specs/cli/otel_basic/metric.out +++ b/tests/specs/cli/otel_basic/metric.out @@ -56,6 +56,31 @@ "isMonotonic": false } }, + { + "name": "gauge", + "description": "Example of a Gauge", + "unit": "", + "metadata": [], + "gauge": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ] + } + }, { "name": "histogram", "description": "Example of a Histogram", @@ -119,6 +144,265 @@ ], "aggregationTemporality": 2 } + }, + { + "name": "observable_counter", + "description": "Example of a ObservableCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": true + } + }, + { + "name": "observable_up_down_counter", + "description": "Example of a ObservableUpDownCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": false + } + }, + { + "name": "observable_gauge", + "description": "Example of a ObservableGauge", + "unit": "", + "metadata": [], + "gauge": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ] + } + }, + { + "name": "counter", + "description": "Example of a Counter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": true + } + }, + { + "name": "up_down_counter", + "description": "Example of a UpDownCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": -1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": false + } + }, + { + "name": "gauge", + "description": "Example of a Gauge", + "unit": "", + "metadata": [], + "gauge": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ] + } + }, + { + "name": "histogram", + "description": "Example of a Histogram", + "unit": "", + "metadata": [], + "histogram": { + "dataPoints": [ + { + "attributes": [ + { + "key": "attribute", + "value": { + "doubleValue": 1 + } + } + ], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "count": 1, + "sum": 1, + "bucketCounts": [ + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "explicitBounds": [ + 0, + 5, + 10, + 25, + 50, + 75, + 100, + 250, + 500, + 750, + 1000, + 2500, + 5000, + 7500, + 10000 + ], + "exemplars": [], + "flags": 0, + "min": 1, + "max": 1 + } + ], + "aggregationTemporality": 2 + } + }, + { + "name": "observable_counter", + "description": "Example of a ObservableCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": true + } + }, + { + "name": "observable_up_down_counter", + "description": "Example of a ObservableUpDownCounter", + "unit": "", + "metadata": [], + "sum": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ], + "aggregationTemporality": 2, + "isMonotonic": false + } + }, + { + "name": "observable_gauge", + "description": "Example of a ObservableGauge", + "unit": "", + "metadata": [], + "gauge": { + "dataPoints": [ + { + "attributes": [], + "startTimeUnixNano": "[WILDCARD]", + "timeUnixNano": "[WILDCARD]", + "exemplars": [], + "flags": 0, + "asDouble": 1 + } + ] + } } ] } diff --git a/tests/specs/cli/otel_basic/metric.ts b/tests/specs/cli/otel_basic/metric.ts index 7d332f0432..2b472a6fb8 100644 --- a/tests/specs/cli/otel_basic/metric.ts +++ b/tests/specs/cli/otel_basic/metric.ts @@ -1,18 +1,8 @@ -import { - MeterProvider, - PeriodicExportingMetricReader, -} from "npm:@opentelemetry/sdk-metrics@1.28.0"; +import { metrics } from "npm:@opentelemetry/api@1"; -const meterProvider = new MeterProvider(); +metrics.setGlobalMeterProvider(new Deno.telemetry.MeterProvider()); -meterProvider.addMetricReader( - new PeriodicExportingMetricReader({ - exporter: new Deno.telemetry.MetricExporter(), - exportIntervalMillis: 100, - }), -); - -const meter = meterProvider.getMeter("m"); +const meter = metrics.getMeter("m"); const counter = meter.createCounter("counter", { description: "Example of a Counter", @@ -22,13 +12,82 @@ const upDownCounter = meter.createUpDownCounter("up_down_counter", { description: "Example of a UpDownCounter", }); +const gauge = meter.createGauge("gauge", { + description: "Example of a Gauge", +}); + const histogram = meter.createHistogram("histogram", { description: "Example of a Histogram", }); +const observableCounterPromise = Promise.withResolvers(); +const observableCounter = meter.createObservableCounter("observable_counter", { + description: "Example of a ObservableCounter", +}); +observableCounter.addCallback((res) => { + res.observe(1); + observableCounterPromise.resolve(); +}); + +const observableUpDownCounterPromise = Promise.withResolvers(); +const observableUpDownCounter = meter + .createObservableUpDownCounter("observable_up_down_counter", { + description: "Example of a ObservableUpDownCounter", + }); +observableUpDownCounter.addCallback((res) => { + res.observe(1); + observableUpDownCounterPromise.resolve(); +}); + +const observableGaugePromise = Promise.withResolvers(); +const observableGauge = meter.createObservableGauge("observable_gauge", { + description: "Example of a ObservableGauge", +}); +observableGauge.addCallback((res) => { + res.observe(1); + observableGaugePromise.resolve(); +}); + +const observableCounterBatch = meter.createObservableCounter( + "observable_counter_batch", + { description: "Example of a ObservableCounter, written in batch" }, +); +const observableUpDownCounterBatch = meter.createObservableUpDownCounter( + "observable_up_down_counter_batch", + { description: "Example of a ObservableUpDownCounter, written in batch" }, +); +const observableGaugeBatch = meter.createObservableGauge( + "observable_gauge_batch", + { + description: "Example of a ObservableGauge, written in batch", + }, +); + +const observableBatchPromise = Promise.withResolvers(); +meter.addBatchObservableCallback((observer) => { + observer.observe(observableCounter, 2); + observer.observe(observableUpDownCounter, 2); + observer.observe(observableGauge, 2); + observableBatchPromise.resolve(); +}, [ + observableCounterBatch, + observableUpDownCounterBatch, + observableGaugeBatch, +]); + const attributes = { attribute: 1 }; counter.add(1, attributes); upDownCounter.add(-1, attributes); +gauge.record(1, attributes); histogram.record(1, attributes); -await meterProvider.forceFlush(); +const timer = setTimeout(() => {}, 100000); + +await Promise.all([ + observableCounterPromise.promise, + observableUpDownCounterPromise.promise, + observableGaugePromise.promise, + observableBatchPromise.promise, +]); + +clearTimeout(timer); From 6061f22abd7ea55cc5846c3c4f12a6aba4bd1ea0 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 27 Dec 2024 11:20:49 +0530 Subject: [PATCH 039/107] fix(ext/node): RangeError timingSafeEqual with different byteLength (#27470) Fixes https://github.com/denoland/deno/issues/27214 --- ext/node/polyfills/internal/errors.ts | 10 ++++++++++ .../polyfills/internal_binding/_timingSafeEqual.ts | 3 ++- tests/unit_node/crypto/crypto_misc_test.ts | 9 ++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ext/node/polyfills/internal/errors.ts b/ext/node/polyfills/internal/errors.ts index 61b53fa968..d79232aed7 100644 --- a/ext/node/polyfills/internal/errors.ts +++ b/ext/node/polyfills/internal/errors.ts @@ -624,6 +624,15 @@ function createInvalidArgType( return msg; } +export class ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH extends NodeRangeError { + constructor() { + super( + "ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH", + "Input buffers must have the same length", + ); + } +} + export class ERR_INVALID_ARG_TYPE_RANGE extends NodeRangeError { constructor(name: string, expected: string | string[], actual: unknown) { const msg = createInvalidArgType(name, expected); @@ -2842,6 +2851,7 @@ export default { ERR_INVALID_ADDRESS_FAMILY, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_TYPE_RANGE, + ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH, ERR_INVALID_ARG_VALUE, ERR_INVALID_ARG_VALUE_RANGE, ERR_INVALID_ASYNC_ID, diff --git a/ext/node/polyfills/internal_binding/_timingSafeEqual.ts b/ext/node/polyfills/internal_binding/_timingSafeEqual.ts index 559b7685b8..d9811c5505 100644 --- a/ext/node/polyfills/internal_binding/_timingSafeEqual.ts +++ b/ext/node/polyfills/internal_binding/_timingSafeEqual.ts @@ -4,6 +4,7 @@ // deno-lint-ignore-file prefer-primordials import { Buffer } from "node:buffer"; +import { ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH } from "ext:deno_node/internal/errors.ts"; function toDataView(ab: ArrayBufferLike | ArrayBufferView): DataView { if (ArrayBuffer.isView(ab)) { @@ -19,7 +20,7 @@ function stdTimingSafeEqual( b: ArrayBufferView | ArrayBufferLike | DataView, ): boolean { if (a.byteLength !== b.byteLength) { - return false; + throw new ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH(); } if (!(a instanceof DataView)) { a = toDataView(a); diff --git a/tests/unit_node/crypto/crypto_misc_test.ts b/tests/unit_node/crypto/crypto_misc_test.ts index 007009339d..9f72683398 100644 --- a/tests/unit_node/crypto/crypto_misc_test.ts +++ b/tests/unit_node/crypto/crypto_misc_test.ts @@ -1,7 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. import { randomFillSync, randomUUID, timingSafeEqual } from "node:crypto"; import { Buffer } from "node:buffer"; -import { assert, assertEquals } from "../../unit/test_util.ts"; +import { assert, assertEquals, assertThrows } from "../../unit/test_util.ts"; import { assertNotEquals } from "@std/assert"; Deno.test("[node/crypto.getRandomUUID] works the same way as Web Crypto API", () => { @@ -36,3 +36,10 @@ Deno.test("[node/crypto.timingSafeEqual] compares equal Buffer with different by assert(timingSafeEqual(a, b)); }); + +Deno.test("[node/crypto.timingSafeEqual] RangeError on Buffer with different byteLength", () => { + const a = Buffer.from([212, 213]); + const b = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 212, 213, 0]); + + assertThrows(() => timingSafeEqual(a, b), RangeError); +}); From c45d0dadb32144201dc181211e56b6bddecccded Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 27 Dec 2024 17:46:01 +0900 Subject: [PATCH 040/107] fix(ext/node): add support of any length IV for aes-(128|256)-gcm ciphers (#27476) --- Cargo.lock | 4 +- ext/node/Cargo.toml | 2 +- ext/node/ops/crypto/cipher.rs | 16 - .../crypto/crypto_cipher_gcm_test.ts | 20 + .../unit_node/crypto/gcmEncryptExtIV128.json | 317 ++++++++++++++++ .../unit_node/crypto/gcmEncryptExtIV256.json | 349 ++++++++++++++++++ 6 files changed, 689 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4bb0cfb18e..5e86a7647c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "aead-gcm-stream" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4947a169074c7e038fa43051d1c4e073f4488b0e4b0a30658f1e1a1b06449ce8" +checksum = "e70c8dec860340effb00f6945c49c0daaa6dac963602750db862eabb74bf7886" dependencies = [ "aead", "aes", diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 60e7c96a08..50e72dfcbe 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -17,7 +17,7 @@ path = "lib.rs" sync_fs = ["deno_package_json/sync", "node_resolver/sync"] [dependencies] -aead-gcm-stream = "0.3" +aead-gcm-stream = "0.4" aes.workspace = true async-trait.workspace = true base64.workspace = true diff --git a/ext/node/ops/crypto/cipher.rs b/ext/node/ops/crypto/cipher.rs index 16e32a34af..7f5b108a04 100644 --- a/ext/node/ops/crypto/cipher.rs +++ b/ext/node/ops/crypto/cipher.rs @@ -179,20 +179,12 @@ impl Cipher { "aes-192-ecb" => Aes192Ecb(Box::new(ecb::Encryptor::new(key.into()))), "aes-256-ecb" => Aes256Ecb(Box::new(ecb::Encryptor::new(key.into()))), "aes-128-gcm" => { - if iv.len() != 12 { - return Err(CipherError::InvalidIvLength); - } - let cipher = aead_gcm_stream::AesGcm::::new(key.into(), iv); Aes128Gcm(Box::new(cipher)) } "aes-256-gcm" => { - if iv.len() != 12 { - return Err(CipherError::InvalidIvLength); - } - let cipher = aead_gcm_stream::AesGcm::::new(key.into(), iv); @@ -395,20 +387,12 @@ impl Decipher { "aes-192-ecb" => Aes192Ecb(Box::new(ecb::Decryptor::new(key.into()))), "aes-256-ecb" => Aes256Ecb(Box::new(ecb::Decryptor::new(key.into()))), "aes-128-gcm" => { - if iv.len() != 12 { - return Err(DecipherError::InvalidIvLength); - } - let decipher = aead_gcm_stream::AesGcm::::new(key.into(), iv); Aes128Gcm(Box::new(decipher)) } "aes-256-gcm" => { - if iv.len() != 12 { - return Err(DecipherError::InvalidIvLength); - } - let decipher = aead_gcm_stream::AesGcm::::new(key.into(), iv); diff --git a/tests/unit_node/crypto/crypto_cipher_gcm_test.ts b/tests/unit_node/crypto/crypto_cipher_gcm_test.ts index b379a43696..16f6f56a9c 100644 --- a/tests/unit_node/crypto/crypto_cipher_gcm_test.ts +++ b/tests/unit_node/crypto/crypto_cipher_gcm_test.ts @@ -119,3 +119,23 @@ Deno.test({ ); }, }); + +// Issue #27441 +// https://github.com/denoland/deno/issues/27441 +Deno.test({ + name: "aes-256-gcm supports IV of non standard length", + fn() { + const decipher = crypto.createDecipheriv( + "aes-256-gcm", + Buffer.from("eYLEiLFQnpjYksWTiKpwv2sKhw+WJb5Fo/aY2YqXswc=", "base64"), + Buffer.from("k5oP3kb8tTbZaL3PxbFWN8ToOb8vfv2b1EuPz1LbmYU=", "base64"), // 256 bits IV + ); + const decrypted = decipher.update( + "s0/KBsFec29XLrGbAnLiNA==", + "base64", + "utf-8", + ); + assertEquals(decrypted, "this is a secret"); + decipher.final(); + }, +}); diff --git a/tests/unit_node/crypto/gcmEncryptExtIV128.json b/tests/unit_node/crypto/gcmEncryptExtIV128.json index 64896642d4..f0b4bca1f1 100644 --- a/tests/unit_node/crypto/gcmEncryptExtIV128.json +++ b/tests/unit_node/crypto/gcmEncryptExtIV128.json @@ -51373,5 +51373,322 @@ 102, 238 ] + }, + { + "key": [ + 131, + 249, + 217, + 125, + 74, + 183, + 89, + 253, + 220, + 195, + 239, + 84, + 160, + 226, + 168, + 236 + ], + "nonce": [ + 207 + ], + "aad": [ + 109, + 212, + 158, + 174, + 180, + 16, + 61, + 172, + 143, + 151, + 227, + 35, + 73, + 70, + 221, + 45 + ], + "plaintext": [ + 119, + 230, + 50, + 156, + 249, + 66, + 79, + 113, + 200, + 8, + 223, + 145, + 112, + 191, + 210, + 152 + ], + "ciphertext": [ + 80, + 222, + 134, + 167, + 169, + 42, + 138, + 94, + 163, + 61, + 181, + 105, + 107, + 150, + 205, + 119 + ], + "tag": [ + 170, + 24, + 30, + 132, + 188, + 139, + 75, + 245, + 166, + 137, + 39, + 196, + 9, + 212, + 34, + 203 + ] + }, + { + "key": [ + 202, + 145, + 226, + 65, + 68, + 9, + 164, + 57, + 176, + 101, + 115, + 215, + 114, + 249, + 10, + 251 + ], + "nonce": [ + 23, + 112, + 8, + 249, + 32, + 160, + 97, + 105, + 204, + 223, + 117, + 58, + 51, + 133, + 83, + 254, + 253, + 70, + 132, + 88, + 105, + 201, + 36, + 77, + 164, + 73, + 151, + 248, + 61, + 76, + 232, + 5, + 161, + 135, + 7, + 200, + 77, + 17, + 79, + 156, + 104, + 66, + 123, + 34, + 132, + 21, + 145, + 230, + 202, + 236, + 245, + 195, + 231, + 42, + 37, + 22, + 122, + 168, + 96, + 197, + 27, + 220, + 26, + 165, + 109, + 205, + 105, + 242, + 154, + 47, + 53, + 231, + 10, + 50, + 43, + 158, + 186, + 9, + 42, + 152, + 214, + 106, + 149, + 107, + 77, + 41, + 67, + 131, + 160, + 235, + 171, + 38, + 247, + 196, + 223, + 26, + 93, + 64, + 96, + 223, + 196, + 90, + 20, + 21, + 81, + 0, + 234, + 125, + 158, + 50, + 222, + 187, + 101, + 55, + 64, + 107, + 117, + 114, + 145, + 113, + 5, + 5, + 20, + 46, + 118, + 89, + 252, + 119 + ], + "aad": [ + 191, + 235, + 21, + 252, + 247, + 177, + 95, + 14, + 20, + 192, + 68, + 57, + 182, + 121, + 80, + 189 + ], + "plaintext": [ + 40, + 0, + 62, + 48, + 196, + 164, + 202, + 158, + 65, + 170, + 254, + 250, + 193, + 225, + 195, + 222 + ], + "ciphertext": [ + 0, + 228, + 114, + 151, + 31, + 58, + 119, + 112, + 170, + 113, + 88, + 253, + 146, + 241, + 123, + 183 + ], + "tag": [ + 22, + 102, + 27, + 133, + 235, + 81, + 100, + 108, + 148, + 207, + 43, + 228, + 228, + 45, + 122, + 142 + ] } ] diff --git a/tests/unit_node/crypto/gcmEncryptExtIV256.json b/tests/unit_node/crypto/gcmEncryptExtIV256.json index cb8ba30869..808c47ec6e 100644 --- a/tests/unit_node/crypto/gcmEncryptExtIV256.json +++ b/tests/unit_node/crypto/gcmEncryptExtIV256.json @@ -57373,5 +57373,354 @@ 246, 57 ] + }, + { + "key": [ + 187, + 70, + 53, + 215, + 102, + 221, + 14, + 74, + 112, + 25, + 209, + 114, + 76, + 115, + 110, + 31, + 44, + 1, + 106, + 249, + 226, + 158, + 125, + 58, + 162, + 192, + 222, + 35, + 231, + 128, + 175, + 38 + ], + "nonce": [ + 171 + ], + "aad": [ + 15, + 133, + 199, + 219, + 235, + 103, + 75, + 122, + 112, + 195, + 81, + 37, + 211, + 97, + 147, + 80 + ], + "plaintext": [ + 208, + 92, + 232, + 120, + 217, + 70, + 98, + 209, + 82, + 11, + 24, + 75, + 75, + 239, + 60, + 69 + ], + "ciphertext": [ + 81, + 186, + 162, + 106, + 106, + 113, + 156, + 22, + 0, + 100, + 95, + 243, + 191, + 223, + 165, + 59 + ], + "tag": [ + 107, + 213, + 78, + 81, + 132, + 235, + 48, + 9, + 52, + 179, + 146, + 195, + 43, + 124, + 26, + 110 + ] + }, + { + "key": [ + 252, + 188, + 126, + 182, + 39, + 22, + 220, + 127, + 121, + 43, + 97, + 148, + 210, + 109, + 109, + 86, + 158, + 174, + 224, + 122, + 157, + 60, + 55, + 202, + 66, + 133, + 64, + 144, + 102, + 30, + 24, + 69 + ], + "nonce": [ + 76, + 140, + 70, + 36, + 39, + 155, + 35, + 180, + 149, + 199, + 136, + 132, + 76, + 118, + 210, + 37, + 235, + 242, + 56, + 38, + 89, + 156, + 62, + 28, + 244, + 219, + 29, + 162, + 214, + 90, + 127, + 117, + 68, + 216, + 232, + 111, + 204, + 51, + 251, + 17, + 61, + 49, + 116, + 184, + 199, + 144, + 49, + 34, + 203, + 89, + 103, + 246, + 16, + 115, + 130, + 204, + 90, + 198, + 231, + 160, + 228, + 202, + 79, + 8, + 222, + 62, + 145, + 29, + 72, + 62, + 104, + 37, + 61, + 63, + 136, + 108, + 254, + 52, + 155, + 249, + 50, + 153, + 162, + 142, + 102, + 91, + 192, + 150, + 165, + 28, + 232, + 76, + 230, + 148, + 11, + 52, + 160, + 55, + 114, + 36, + 131, + 185, + 106, + 123, + 37, + 80, + 127, + 90, + 4, + 100, + 60, + 103, + 48, + 250, + 170, + 182, + 24, + 230, + 35, + 26, + 114, + 119, + 20, + 214, + 243, + 102, + 250, + 155 + ], + "aad": [ + 60, + 24, + 42, + 241, + 156, + 70, + 255, + 74, + 203, + 218, + 206, + 207, + 112, + 180, + 47, + 181 + ], + "plaintext": [ + 34, + 20, + 79, + 193, + 47, + 123, + 197, + 82, + 43, + 136, + 183, + 108, + 141, + 237, + 28, + 118 + ], + "ciphertext": [ + 200, + 217, + 129, + 7, + 192, + 203, + 60, + 15, + 210, + 24, + 154, + 233, + 114, + 128, + 213, + 98 + ], + "tag": [ + 41, + 6, + 119, + 35, + 48, + 236, + 217, + 163, + 184, + 168, + 40, + 118, + 164, + 235, + 222, + 234 + ] } ] From fdd0edf23cd1edc5fbfab8e699dca39d54c18e22 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Fri, 27 Dec 2024 22:46:29 +0100 Subject: [PATCH 041/107] fix(unstable): don't error on non-existing attrs or type attr (#27456) When running selectors for JS linting plugins we would error when encountering an unknown attribute name: ```js // selector Foo[non-existant] // error Error: Missing string id: ``` This was caused by using `0` as the invalid marker, but also overloading `0` with an actual node type. So the fix is to reserve `0` as the invalid marker and move the property type to the next index. --- cli/js/40_lint.js | 41 +++++++++++++++++++++----- cli/tools/lint/ast_buffer/buffer.rs | 13 ++++---- cli/tools/lint/ast_buffer/ts_estree.rs | 5 ++-- tests/unit/lint_plugin_test.ts | 22 ++++++++++++++ 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/cli/js/40_lint.js b/cli/js/40_lint.js index 30b1f86884..d29dc3e850 100644 --- a/cli/js/40_lint.js +++ b/cli/js/40_lint.js @@ -15,10 +15,10 @@ const { // Keep in sync with Rust // These types are expected to be present on every node. Note that this // isn't set in stone. We could revise this at a future point. -const AST_PROP_TYPE = 0; -const AST_PROP_PARENT = 1; -const AST_PROP_RANGE = 2; -const AST_PROP_LENGTH = 3; +const AST_PROP_TYPE = 1; +const AST_PROP_PARENT = 2; +const AST_PROP_RANGE = 3; +const AST_PROP_LENGTH = 4; // Keep in sync with Rust // Each node property is tagged with this enum to denote @@ -421,10 +421,12 @@ class MatchCtx { /** * @param {AstContext["buf"]} buf * @param {AstContext["strTable"]} strTable + * @param {AstContext["strByType"]} strByType */ - constructor(buf, strTable) { + constructor(buf, strTable, strByType) { this.buf = buf; this.strTable = strTable; + this.strByType = strByType; } /** @@ -452,7 +454,19 @@ class MatchCtx { getAttrPathValue(offset, propIds, idx) { const { buf } = this; - offset = findPropOffset(buf, offset, propIds[idx]); + const propId = propIds[idx]; + + switch (propId) { + case AST_PROP_TYPE: { + const type = this.getType(offset); + return getString(this.strTable, this.strByType[type]); + } + case AST_PROP_PARENT: + case AST_PROP_RANGE: + throw new Error(`Not supported`); + } + + offset = findPropOffset(buf, offset, propId); if (offset === -1) return undefined; const _prop = buf[offset++]; const kind = buf[offset++]; @@ -499,7 +513,18 @@ class MatchCtx { hasAttrPath(offset, propIds, idx) { const { buf } = this; - offset = findPropOffset(buf, offset, propIds[idx]); + const propId = propIds[idx]; + // If propId is 0 then the property doesn't exist in the AST + if (propId === 0) return false; + + switch (propId) { + case AST_PROP_TYPE: + case AST_PROP_PARENT: + case AST_PROP_RANGE: + return true; + } + + offset = findPropOffset(buf, offset, propId); if (offset === -1) return false; if (idx === propIds.length - 1) return true; @@ -736,7 +761,7 @@ function createAstContext(buf) { strByType, typeByStr, propByStr, - matcher: new MatchCtx(buf, strTable), + matcher: new MatchCtx(buf, strTable, strByType), }; setNodeGetters(ctx); diff --git a/cli/tools/lint/ast_buffer/buffer.rs b/cli/tools/lint/ast_buffer/buffer.rs index f37041eff2..d162ee3de1 100644 --- a/cli/tools/lint/ast_buffer/buffer.rs +++ b/cli/tools/lint/ast_buffer/buffer.rs @@ -204,11 +204,11 @@ impl SerializeCtx { prop_map: vec![0; prop_size + 1], }; - ctx.str_table.insert(""); + let empty_str = ctx.str_table.insert(""); // Placeholder node is always 0 ctx.append_node(0, NodeRef(0), &DUMMY_SP, 0); - ctx.kind_map[0] = 0; + ctx.kind_map[0] = empty_str; ctx.start_buf = NodeRef(ctx.buf.len()); // Insert default props that are always present @@ -218,10 +218,11 @@ impl SerializeCtx { let length_str = ctx.str_table.insert("length"); // These values are expected to be in this order on the JS side - ctx.prop_map[0] = type_str; - ctx.prop_map[1] = parent_str; - ctx.prop_map[2] = range_str; - ctx.prop_map[3] = length_str; + ctx.prop_map[0] = empty_str; + ctx.prop_map[1] = type_str; + ctx.prop_map[2] = parent_str; + ctx.prop_map[3] = range_str; + ctx.prop_map[4] = length_str; ctx } diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs index 599499aa8d..64dbd82cde 100644 --- a/cli/tools/lint/ast_buffer/ts_estree.rs +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -200,8 +200,8 @@ impl From for u8 { #[derive(Debug, Clone)] pub enum AstProp { - // Base, these three must be in sync with JS. The - // order here for these 3 fields is important. + // Base, these must be in sync with JS in the same order. + Invalid, Type, Parent, Range, @@ -318,6 +318,7 @@ pub enum AstProp { impl Display for AstProp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match self { + AstProp::Invalid => "__invalid__", // unused AstProp::Parent => "parent", AstProp::Range => "range", AstProp::Type => "type", diff --git a/tests/unit/lint_plugin_test.ts b/tests/unit/lint_plugin_test.ts index 9506c3e0a8..38a7e1b091 100644 --- a/tests/unit/lint_plugin_test.ts +++ b/tests/unit/lint_plugin_test.ts @@ -212,6 +212,28 @@ Deno.test("Plugin - visitor attr", () => { assertEquals(result[0].node.name, "foo"); }); +Deno.test("Plugin - visitor attr to check type", () => { + let result = testVisit( + "foo", + "Identifier[type]", + ); + assertEquals(result[0].node.type, "Identifier"); + + result = testVisit( + "foo", + "Identifier[type='Identifier']", + ); + assertEquals(result[0].node.type, "Identifier"); +}); + +Deno.test("Plugin - visitor attr non-existing", () => { + const result = testVisit( + "foo", + "[non-existing]", + ); + assertEquals(result, []); +}); + Deno.test("Plugin - visitor attr length special case", () => { let result = testVisit( "foo(1); foo(1, 2);", From 5194222e02d54158c47240ef78f7d3379a274eeb Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 28 Dec 2024 12:44:37 +0530 Subject: [PATCH 042/107] fix(ext/node): convert brotli chunks with proper byte offset (#27455) Fixes https://github.com/denoland/deno/issues/27029 Fixes https://github.com/denoland/deno/issues/26086 --- ext/node/polyfills/_brotli.js | 19 +++++++++--- tests/unit_node/http_test.ts | 58 +++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/ext/node/polyfills/_brotli.js b/ext/node/polyfills/_brotli.js index ebd0351561..108e5319a9 100644 --- a/ext/node/polyfills/_brotli.js +++ b/ext/node/polyfills/_brotli.js @@ -10,9 +10,12 @@ const { ArrayPrototypeMap, TypedArrayPrototypeSlice, TypedArrayPrototypeSubarray, - TypedArrayPrototypeGetByteLength, - DataViewPrototypeGetBuffer, TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetByteOffset, + DataViewPrototypeGetBuffer, + DataViewPrototypeGetByteLength, + DataViewPrototypeGetByteOffset, } = primordials; const { isTypedArray, isDataView, close } = core; import { @@ -40,9 +43,17 @@ const toU8 = (input) => { } if (isTypedArray(input)) { - return new Uint8Array(TypedArrayPrototypeGetBuffer(input)); + return new Uint8Array( + TypedArrayPrototypeGetBuffer(input), + TypedArrayPrototypeGetByteOffset(input), + TypedArrayPrototypeGetByteLength(input), + ); } else if (isDataView(input)) { - return new Uint8Array(DataViewPrototypeGetBuffer(input)); + return new Uint8Array( + DataViewPrototypeGetBuffer(input), + DataViewPrototypeGetByteOffset(input), + DataViewPrototypeGetByteLength(input), + ); } return input; diff --git a/tests/unit_node/http_test.ts b/tests/unit_node/http_test.ts index e6c36eea19..f30a4a20a3 100644 --- a/tests/unit_node/http_test.ts +++ b/tests/unit_node/http_test.ts @@ -10,6 +10,7 @@ import http, { } from "node:http"; import url from "node:url"; import https from "node:https"; +import zlib from "node:zlib"; import net, { Socket } from "node:net"; import fs from "node:fs"; import { text } from "node:stream/consumers"; @@ -1823,3 +1824,60 @@ Deno.test("[node/http] ServerResponse socket", async () => { await promise; }); + +Deno.test("[node/http] decompress brotli response", { + permissions: { net: true }, +}, async () => { + let received = false; + const ac = new AbortController(); + const server = Deno.serve({ port: 5928, signal: ac.signal }, (_req) => { + received = true; + return Response.json([ + ["accept-language", "*"], + ["host", "localhost:3000"], + ["user-agent", "Deno/2.1.1"], + ], {}); + }); + const { promise, resolve, reject } = Promise.withResolvers(); + let body = ""; + + const request = http.get( + "http://localhost:5928/", + { + headers: { + "accept-encoding": "gzip, deflate, br, zstd", + }, + }, + (resp) => { + const decompress = zlib.createBrotliDecompress(); + resp.on("data", (chunk) => { + decompress.write(chunk); + }); + + resp.on("end", () => { + decompress.end(); + }); + + decompress.on("data", (chunk) => { + body += chunk; + }); + + decompress.on("end", () => { + resolve(); + }); + }, + ); + request.on("error", reject); + request.end(() => { + assert(received); + }); + + await promise; + ac.abort(); + await server.finished; + + assertEquals(JSON.parse(body), [["accept-language", "*"], [ + "host", + "localhost:3000", + ], ["user-agent", "Deno/2.1.1"]]); +}); From fd8400eaec2404120bd6f254f4ca13da3c68f99f Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 30 Dec 2024 00:36:29 -0500 Subject: [PATCH 043/107] perf(node/fs/copy): reduce metadata lookups copying directory (#27495) --- ext/fs/std_fs.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index 86ad213160..76d37e430c 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -723,30 +723,34 @@ fn cp(from: &Path, to: &Path) -> FsResult<()> { } } - match (fs::metadata(to), fs::symlink_metadata(to)) { - (Ok(m), _) if m.is_dir() => cp_( - source_meta, - from, - &to.join(from.file_name().ok_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidInput, - "the source path is not a valid file", - ) - })?), - )?, - (_, Ok(m)) if is_identical(&source_meta, &m) => { + if let Ok(m) = fs::metadata(to) { + if m.is_dir() { + return cp_( + source_meta, + from, + &to.join(from.file_name().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "the source path is not a valid file", + ) + })?), + ); + } + } + + if let Ok(m) = fs::symlink_metadata(to) { + if is_identical(&source_meta, &m) { return Err( io::Error::new( io::ErrorKind::InvalidInput, "the source and destination are the same file", ) .into(), - ) + ); } - _ => cp_(source_meta, from, to)?, } - Ok(()) + cp_(source_meta, from, to) } #[cfg(not(windows))] From c391ad315ef75fb32298e87f9b80781fb11043ac Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 30 Dec 2024 12:38:20 -0500 Subject: [PATCH 044/107] refactor: use sys_traits (#27480) --- Cargo.lock | 79 +++++-- Cargo.toml | 9 +- cli/Cargo.toml | 3 +- cli/args/deno_json.rs | 56 ----- cli/args/lockfile.rs | 6 +- cli/args/mod.rs | 8 +- cli/cache/deno_dir.rs | 153 ++---------- cli/cache/disk_cache.rs | 10 +- cli/cache/mod.rs | 137 +---------- cli/factory.rs | 40 ++-- cli/file_fetcher.rs | 55 +++-- cli/graph_util.rs | 164 ++++--------- cli/lsp/cache.rs | 3 +- cli/lsp/config.rs | 93 +------- cli/lsp/diagnostics.rs | 4 +- cli/lsp/language_server.rs | 5 +- cli/lsp/registries.rs | 3 +- cli/lsp/resolver.rs | 37 ++- cli/module_loader.rs | 79 ++++--- cli/node.rs | 25 +- cli/npm/byonm.rs | 8 +- cli/npm/managed/mod.rs | 100 ++++---- cli/npm/managed/resolvers/common.rs | 13 +- cli/npm/managed/resolvers/global.rs | 6 +- cli/npm/managed/resolvers/local.rs | 38 +-- cli/npm/managed/resolvers/mod.rs | 8 +- cli/npm/mod.rs | 53 +---- cli/resolver.rs | 96 +++----- cli/standalone/binary.rs | 1 - cli/standalone/code_cache.rs | 6 +- cli/standalone/file_system.rs | 1 + cli/standalone/mod.rs | 47 ++-- cli/tools/coverage/mod.rs | 3 +- cli/tools/doc.rs | 5 +- cli/tools/fmt.rs | 3 +- cli/tools/lint/linter.rs | 6 +- cli/tools/lint/mod.rs | 3 +- cli/tools/registry/paths.rs | 7 +- cli/tools/registry/unfurl.rs | 6 +- cli/tsc/mod.rs | 12 +- cli/util/fs.rs | 318 +------------------------ cli/util/path.rs | 14 -- ext/fs/Cargo.toml | 2 + ext/fs/interface.rs | 342 ++++++++++++++++++++++++++- ext/fs/lib.rs | 2 + ext/fs/sync.rs | 6 + ext/node/lib.rs | 96 ++------ resolvers/deno/Cargo.toml | 1 + resolvers/deno/cjs.rs | 21 +- resolvers/deno/fs.rs | 22 -- resolvers/deno/lib.rs | 41 ++-- resolvers/deno/npm/byonm.rs | 117 +++++---- resolvers/deno/npm/mod.rs | 48 ++-- resolvers/node/Cargo.toml | 1 + resolvers/node/analyze.rs | 41 ++-- resolvers/node/env.rs | 39 --- resolvers/node/lib.rs | 2 +- resolvers/node/package_json.rs | 25 +- resolvers/node/resolution.rs | 111 +++++---- resolvers/npm_cache/Cargo.toml | 2 + resolvers/npm_cache/fs_util.rs | 99 ++++++++ resolvers/npm_cache/lib.rs | 76 ++++-- resolvers/npm_cache/registry_info.rs | 95 ++++++-- resolvers/npm_cache/tarball.rs | 61 ++++- tests/Cargo.toml | 1 + tests/integration/jsr_tests.rs | 2 +- 66 files changed, 1378 insertions(+), 1598 deletions(-) delete mode 100644 resolvers/deno/fs.rs delete mode 100644 resolvers/node/env.rs create mode 100644 resolvers/npm_cache/fs_util.rs diff --git a/Cargo.lock b/Cargo.lock index 5e86a7647c..0fb9493629 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -867,6 +867,7 @@ dependencies = [ "regex", "reqwest", "serde", + "sys_traits", "test_server", "tokio", "url", @@ -1274,7 +1275,7 @@ dependencies = [ "deno_npm", "deno_npm_cache", "deno_package_json", - "deno_path_util", + "deno_path_util 0.3.0", "deno_resolver", "deno_runtime", "deno_semver", @@ -1340,6 +1341,7 @@ dependencies = [ "spki", "sqlformat", "strsim", + "sys_traits", "tar", "tempfile", "test_server", @@ -1452,9 +1454,9 @@ dependencies = [ [[package]] name = "deno_cache_dir" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54df1c5177ace01d92b872584ab9af8290681bb150fd9b423c37a494ad5ddbdc" +checksum = "e73ed17f285731a23df9779ca1e0e721de866db6776ed919ebd9235e0a107c4c" dependencies = [ "async-trait", "base32", @@ -1465,7 +1467,7 @@ dependencies = [ "data-url", "deno_error", "deno_media_type", - "deno_path_util", + "deno_path_util 0.3.0", "http 1.1.0", "indexmap 2.3.0", "log", @@ -1474,6 +1476,7 @@ dependencies = [ "serde", "serde_json", "sha2", + "sys_traits", "thiserror 1.0.64", "url", ] @@ -1491,13 +1494,13 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.41.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afa3beb6b9e0604cfe0380d30f88c5b758d44e228d5a5fc42ae637ccfb7d089" +checksum = "b45aaf31e58ca915d5c0746bf8e2d07b94635154ad9e5afe5ff265cae6187b19" dependencies = [ "anyhow", "deno_package_json", - "deno_path_util", + "deno_path_util 0.3.0", "deno_semver", "glob", "ignore", @@ -1509,6 +1512,7 @@ dependencies = [ "phf", "serde", "serde_json", + "sys_traits", "thiserror 1.0.64", "url", ] @@ -1623,7 +1627,7 @@ dependencies = [ "comrak", "deno_ast", "deno_graph", - "deno_path_util", + "deno_path_util 0.2.2", "handlebars", "html-escape", "import_map", @@ -1673,7 +1677,7 @@ dependencies = [ "bytes", "data-url", "deno_core", - "deno_path_util", + "deno_path_util 0.3.0", "deno_permissions", "deno_tls", "dyn-clone", @@ -1730,15 +1734,17 @@ dependencies = [ "boxed_error", "deno_core", "deno_io", - "deno_path_util", + "deno_path_util 0.3.0", "deno_permissions", "filetime", + "getrandom", "junction", "libc", "nix", "rand", "rayon", "serde", + "sys_traits", "thiserror 2.0.3", "winapi", "windows-sys 0.59.0", @@ -1746,15 +1752,16 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.86.5" +version = "0.86.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f669d96d63841d9ba10f86b161d898678ce05bc1e3c9ee1c1f7449a68eed2b64" +checksum = "83af194ca492ea7b624d21055f933676d3f3d27586de93be31c8f1babcc73510" dependencies = [ "anyhow", "async-trait", "capacity_builder 0.5.0", "data-url", "deno_ast", + "deno_path_util 0.3.0", "deno_semver", "deno_unsync", "encoding_rs", @@ -1769,6 +1776,7 @@ dependencies = [ "serde", "serde_json", "sha2", + "sys_traits", "thiserror 2.0.3", "twox-hash", "url", @@ -1847,7 +1855,7 @@ dependencies = [ "chrono", "deno_core", "deno_fetch", - "deno_path_util", + "deno_path_util 0.3.0", "deno_permissions", "deno_tls", "denokv_proto", @@ -1976,7 +1984,7 @@ dependencies = [ "deno_media_type", "deno_net", "deno_package_json", - "deno_path_util", + "deno_path_util 0.3.0", "deno_permissions", "deno_whoami", "der", @@ -2078,6 +2086,7 @@ dependencies = [ "deno_core", "deno_error", "deno_npm", + "deno_path_util 0.3.0", "deno_semver", "deno_unsync", "faster-hex", @@ -2090,6 +2099,7 @@ dependencies = [ "rand", "ring", "serde_json", + "sys_traits", "tar", "tempfile", "thiserror 2.0.3", @@ -2114,17 +2124,18 @@ dependencies = [ [[package]] name = "deno_package_json" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d72db99fdebfc371d7be16972c18a47daa7a29cb5fbb3900ab2114b1f42d96" +checksum = "e1d3c0f699ba2040669204ce24ab73720499fc290af843e4ce0fc8a9b3d67735" dependencies = [ "boxed_error", "deno_error", - "deno_path_util", + "deno_path_util 0.3.0", "deno_semver", "indexmap 2.3.0", "serde", "serde_json", + "sys_traits", "thiserror 2.0.3", "url", ] @@ -2141,13 +2152,26 @@ dependencies = [ "url", ] +[[package]] +name = "deno_path_util" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420e8211aaba7fde83ccaa9a5dad855c3b940ed988d70c95159acd600a70dc87" +dependencies = [ + "deno_error", + "percent-encoding", + "sys_traits", + "thiserror 2.0.3", + "url", +] + [[package]] name = "deno_permissions" version = "0.43.0" dependencies = [ "capacity_builder 0.5.0", "deno_core", - "deno_path_util", + "deno_path_util 0.3.0", "deno_terminal 0.2.0", "fqdn", "libc", @@ -2171,9 +2195,10 @@ dependencies = [ "deno_config", "deno_media_type", "deno_package_json", - "deno_path_util", + "deno_path_util 0.3.0", "deno_semver", "node_resolver", + "sys_traits", "test_server", "thiserror 2.0.3", "url", @@ -2201,7 +2226,7 @@ dependencies = [ "deno_napi", "deno_net", "deno_node", - "deno_path_util", + "deno_path_util 0.3.0", "deno_permissions", "deno_telemetry", "deno_terminal 0.2.0", @@ -5049,13 +5074,14 @@ dependencies = [ "boxed_error", "deno_media_type", "deno_package_json", - "deno_path_util", + "deno_path_util 0.3.0", "futures", "lazy-regex", "once_cell", "path-clean", "regex", "serde_json", + "sys_traits", "thiserror 2.0.3", "tokio", "url", @@ -7652,6 +7678,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "sys_traits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a12729b699487bb50163466e87be7197871d83d04cc6815d430cf7c893bbd7" +dependencies = [ + "getrandom", + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "tagptr" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 81c750f0af..442680c332 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,11 +51,11 @@ deno_ast = { version = "=0.44.0", features = ["transpiling"] } deno_core = { version = "0.327.0" } deno_bench_util = { version = "0.178.0", path = "./bench_util" } -deno_config = { version = "=0.41.0", features = ["workspace", "sync"] } +deno_config = { version = "=0.42.0", features = ["workspace", "sync"] } deno_lockfile = "=0.24.0" deno_media_type = { version = "0.2.0", features = ["module_specifier"] } deno_npm = "=0.27.0" -deno_path_util = "=0.2.2" +deno_path_util = "=0.3.0" deno_permissions = { version = "0.43.0", path = "./runtime/permissions" } deno_runtime = { version = "0.192.0", path = "./runtime" } deno_semver = "=0.7.1" @@ -118,9 +118,9 @@ console_static_text = "=0.8.1" dashmap = "5.5.3" data-encoding = "2.3.3" data-url = "=0.3.1" -deno_cache_dir = "=0.15.0" +deno_cache_dir = "=0.16.0" deno_error = "=0.5.2" -deno_package_json = { version = "0.3.0", default-features = false } +deno_package_json = { version = "0.4.0", default-features = false } deno_unsync = "0.4.2" dlopen2 = "0.6.1" ecb = "=0.1.2" @@ -193,6 +193,7 @@ slab = "0.4" smallvec = "1.8" socket2 = { version = "0.5.3", features = ["all"] } spki = "0.7.2" +sys_traits = "=0.1.1" tar = "=0.4.40" tempfile = "3.4.0" termcolor = "1.1.3" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ad0e4fa95d..2cf12f14d4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -74,7 +74,7 @@ deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] } deno_error.workspace = true -deno_graph = { version = "=0.86.5" } +deno_graph = { version = "=0.86.6" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true deno_npm.workspace = true @@ -158,6 +158,7 @@ shell-escape = "=0.1.5" spki = { version = "0.7", features = ["pem"] } sqlformat = "=0.3.2" strsim = "0.11.1" +sys_traits = { workspace = true, features = ["libc", "real", "winapi"] } tar.workspace = true tempfile.workspace = true text-size = "=1.1.0" diff --git a/cli/args/deno_json.rs b/cli/args/deno_json.rs index 8853107eef..47dcbb91ea 100644 --- a/cli/args/deno_json.rs +++ b/cli/args/deno_json.rs @@ -8,62 +8,6 @@ use deno_semver::jsr::JsrDepPackageReq; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; -#[cfg(test)] // happens to only be used by the tests at the moment -pub struct DenoConfigFsAdapter<'a>( - pub &'a dyn deno_runtime::deno_fs::FileSystem, -); - -#[cfg(test)] -impl<'a> deno_config::fs::DenoConfigFs for DenoConfigFsAdapter<'a> { - fn read_to_string_lossy( - &self, - path: &std::path::Path, - ) -> Result, std::io::Error> { - self - .0 - .read_text_file_lossy_sync(path, None) - .map_err(|err| err.into_io_error()) - } - - fn stat_sync( - &self, - path: &std::path::Path, - ) -> Result { - self - .0 - .stat_sync(path) - .map(|stat| deno_config::fs::FsMetadata { - is_file: stat.is_file, - is_directory: stat.is_directory, - is_symlink: stat.is_symlink, - }) - .map_err(|err| err.into_io_error()) - } - - fn read_dir( - &self, - path: &std::path::Path, - ) -> Result, std::io::Error> { - self - .0 - .read_dir_sync(path) - .map_err(|err| err.into_io_error()) - .map(|entries| { - entries - .into_iter() - .map(|e| deno_config::fs::FsDirEntry { - path: path.join(e.name), - metadata: deno_config::fs::FsMetadata { - is_file: e.is_file, - is_directory: e.is_directory, - is_symlink: e.is_symlink, - }, - }) - .collect() - }) - } -} - pub fn import_map_deps( import_map: &serde_json::Value, ) -> HashSet { diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index 1075f93a6f..0648b6e5ed 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -12,12 +12,13 @@ use deno_core::parking_lot::MutexGuard; use deno_core::serde_json; use deno_lockfile::WorkspaceMemberConfig; use deno_package_json::PackageJsonDepValue; +use deno_path_util::fs::atomic_write_file_with_retries; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::PackageJson; use deno_semver::jsr::JsrDepPackageReq; use crate::args::deno_json::import_map_deps; use crate::cache; -use crate::util::fs::atomic_write_file_with_retries; use crate::Flags; use crate::args::DenoSubcommand; @@ -91,8 +92,9 @@ impl CliLockfile { // do an atomic write to reduce the chance of multiple deno // processes corrupting the file atomic_write_file_with_retries( + &FsSysTraitsAdapter::new_real(), &lockfile.filename, - bytes, + &bytes, cache::CACHE_PERM, ) .context("Failed writing lockfile.")?; diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 3f42a6b9d4..aa3a251105 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -30,6 +30,7 @@ use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmSystemInfo; use deno_path_util::normalize_path; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_semver::npm::NpmPackageReqReference; use deno_semver::StackString; use deno_telemetry::OtelConfig; @@ -83,9 +84,9 @@ use std::num::NonZeroUsize; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use sys_traits::EnvHomeDir; use thiserror::Error; -use crate::cache; use crate::cache::DenoDirProvider; use crate::file_fetcher::CliFileFetcher; use crate::util::fs::canonicalize_path_maybe_not_exists; @@ -572,7 +573,7 @@ fn discover_npmrc( // TODO(bartlomieju): update to read both files - one in the project root and one and // home dir and then merge them. // 3. Try `.npmrc` in the user's home directory - if let Some(home_dir) = cache::home_dir() { + if let Some(home_dir) = sys_traits::impls::RealSys.env_home_dir() { match try_to_read_npmrc(&home_dir) { Ok(Some((source, path))) => { return try_to_parse_npmrc(source, &path).map(|r| (r, Some(path))); @@ -845,7 +846,6 @@ impl CliOptions { log::debug!("package.json auto-discovery is disabled"); } WorkspaceDiscoverOptions { - fs: Default::default(), // use real fs deno_json_cache: None, pkg_json_cache: Some(&node_resolver::PackageJsonThreadLocalCache), workspace_cache: None, @@ -867,6 +867,7 @@ impl CliOptions { ConfigFlag::Discover => { if let Some(start_paths) = flags.config_path_args(&initial_cwd) { WorkspaceDirectory::discover( + &FsSysTraitsAdapter::new_real(), WorkspaceDiscoverStart::Paths(&start_paths), &resolve_workspace_discover_options(), )? @@ -877,6 +878,7 @@ impl CliOptions { ConfigFlag::Path(path) => { let config_path = normalize_path(initial_cwd.join(path)); WorkspaceDirectory::discover( + &FsSysTraitsAdapter::new_real(), WorkspaceDiscoverStart::ConfigFile(&config_path), &resolve_workspace_discover_options(), )? diff --git a/cli/cache/deno_dir.rs b/cli/cache/deno_dir.rs index 7b7059c224..d83ea8ebd5 100644 --- a/cli/cache/deno_dir.rs +++ b/cli/cache/deno_dir.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_cache_dir::DenoDirResolutionError; use once_cell::sync::OnceCell; use super::DiskCache; @@ -11,7 +12,7 @@ use std::path::PathBuf; /// where functionality wants to continue if the DENO_DIR can't be created. pub struct DenoDirProvider { maybe_custom_root: Option, - deno_dir: OnceCell>, + deno_dir: OnceCell>, } impl DenoDirProvider { @@ -22,12 +23,21 @@ impl DenoDirProvider { } } - pub fn get_or_create(&self) -> Result<&DenoDir, std::io::Error> { + pub fn get_or_create(&self) -> Result<&DenoDir, DenoDirResolutionError> { self .deno_dir .get_or_init(|| DenoDir::new(self.maybe_custom_root.clone())) .as_ref() - .map_err(|err| std::io::Error::new(err.kind(), err.to_string())) + .map_err(|err| match err { + DenoDirResolutionError::NoCacheOrHomeDir => { + DenoDirResolutionError::NoCacheOrHomeDir + } + DenoDirResolutionError::FailedCwd { source } => { + DenoDirResolutionError::FailedCwd { + source: std::io::Error::new(source.kind(), source.to_string()), + } + } + }) } } @@ -42,27 +52,13 @@ pub struct DenoDir { } impl DenoDir { - pub fn new(maybe_custom_root: Option) -> std::io::Result { - let maybe_custom_root = - maybe_custom_root.or_else(|| env::var("DENO_DIR").map(String::into).ok()); - let root: PathBuf = if let Some(root) = maybe_custom_root { - root - } else if let Some(cache_dir) = dirs::cache_dir() { - // We use the OS cache dir because all files deno writes are cache files - // Once that changes we need to start using different roots if DENO_DIR - // is not set, and keep a single one if it is. - cache_dir.join("deno") - } else if let Some(home_dir) = dirs::home_dir() { - // fallback path - home_dir.join(".deno") - } else { - panic!("Could not set the Deno root directory") - }; - let root = if root.is_absolute() { - root - } else { - std::env::current_dir()?.join(root) - }; + pub fn new( + maybe_custom_root: Option, + ) -> Result { + let root = deno_cache_dir::resolve_deno_dir( + &sys_traits::impls::RealSys, + maybe_custom_root, + )?; assert!(root.is_absolute()); let gen_path = root.join("gen"); @@ -166,112 +162,3 @@ impl DenoDir { self.root.join("dl") } } - -/// To avoid the poorly managed dirs crate -#[cfg(not(windows))] -pub mod dirs { - use std::path::PathBuf; - - pub fn cache_dir() -> Option { - if cfg!(target_os = "macos") { - home_dir().map(|h| h.join("Library/Caches")) - } else { - std::env::var_os("XDG_CACHE_HOME") - .map(PathBuf::from) - .or_else(|| home_dir().map(|h| h.join(".cache"))) - } - } - - pub fn home_dir() -> Option { - std::env::var_os("HOME") - .and_then(|h| if h.is_empty() { None } else { Some(h) }) - .or_else(|| { - // TODO(bartlomieju): - #[allow(clippy::undocumented_unsafe_blocks)] - unsafe { - fallback() - } - }) - .map(PathBuf::from) - } - - // This piece of code is taken from the deprecated home_dir() function in Rust's standard library: https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/os.rs#L579 - // The same code is used by the dirs crate - unsafe fn fallback() -> Option { - let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { - n if n < 0 => 512_usize, - n => n as usize, - }; - let mut buf = Vec::with_capacity(amt); - let mut passwd: libc::passwd = std::mem::zeroed(); - let mut result = std::ptr::null_mut(); - match libc::getpwuid_r( - libc::getuid(), - &mut passwd, - buf.as_mut_ptr(), - buf.capacity(), - &mut result, - ) { - 0 if !result.is_null() => { - let ptr = passwd.pw_dir as *const _; - let bytes = std::ffi::CStr::from_ptr(ptr).to_bytes().to_vec(); - Some(std::os::unix::ffi::OsStringExt::from_vec(bytes)) - } - _ => None, - } - } -} - -/// To avoid the poorly managed dirs crate -// Copied from -// https://github.com/dirs-dev/dirs-sys-rs/blob/ec7cee0b3e8685573d847f0a0f60aae3d9e07fa2/src/lib.rs#L140-L164 -// MIT license. Copyright (c) 2018-2019 dirs-rs contributors -#[cfg(windows)] -pub mod dirs { - use std::ffi::OsString; - use std::os::windows::ffi::OsStringExt; - use std::path::PathBuf; - use winapi::shared::winerror; - use winapi::um::combaseapi; - use winapi::um::knownfolders; - use winapi::um::shlobj; - use winapi::um::shtypes; - use winapi::um::winbase; - use winapi::um::winnt; - - fn known_folder(folder_id: shtypes::REFKNOWNFOLDERID) -> Option { - // SAFETY: winapi calls - unsafe { - let mut path_ptr: winnt::PWSTR = std::ptr::null_mut(); - let result = shlobj::SHGetKnownFolderPath( - folder_id, - 0, - std::ptr::null_mut(), - &mut path_ptr, - ); - if result == winerror::S_OK { - let len = winbase::lstrlenW(path_ptr) as usize; - let path = std::slice::from_raw_parts(path_ptr, len); - let ostr: OsString = OsStringExt::from_wide(path); - combaseapi::CoTaskMemFree(path_ptr as *mut winapi::ctypes::c_void); - Some(PathBuf::from(ostr)) - } else { - None - } - } - } - - pub fn cache_dir() -> Option { - known_folder(&knownfolders::FOLDERID_LocalAppData) - } - - pub fn home_dir() -> Option { - if let Some(userprofile) = std::env::var_os("USERPROFILE") { - if !userprofile.is_empty() { - return Some(PathBuf::from(userprofile)); - } - } - - known_folder(&knownfolders::FOLDERID_Profile) - } -} diff --git a/cli/cache/disk_cache.rs b/cli/cache/disk_cache.rs index 2fee1efe09..b22b2e3cc7 100644 --- a/cli/cache/disk_cache.rs +++ b/cli/cache/disk_cache.rs @@ -1,11 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use super::CACHE_PERM; -use crate::util::fs::atomic_write_file_with_retries; use deno_cache_dir::url_to_filename; use deno_core::url::Host; use deno_core::url::Url; +use deno_path_util::fs::atomic_write_file_with_retries; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use std::ffi::OsStr; use std::fs; use std::path::Component; @@ -120,7 +121,12 @@ impl DiskCache { pub fn set(&self, filename: &Path, data: &[u8]) -> std::io::Result<()> { let path = self.location.join(filename); - atomic_write_file_with_retries(&path, data, CACHE_PERM) + atomic_write_file_with_retries( + &FsSysTraitsAdapter::new_real(), + &path, + data, + CACHE_PERM, + ) } } diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index 31968be0c2..c8b0eacaa4 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -5,9 +5,6 @@ use crate::file_fetcher::CliFetchNoFollowErrorKind; use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::FetchNoFollowOptions; use crate::file_fetcher::FetchPermissionsOptionRef; -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 deno_ast::MediaType; use deno_cache_dir::file_fetcher::CacheSetting; @@ -21,15 +18,12 @@ use deno_graph::source::CacheInfo; use deno_graph::source::LoadFuture; use deno_graph::source::LoadResponse; use deno_graph::source::Loader; -use deno_runtime::deno_fs; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_permissions::PermissionsContainer; use node_resolver::InNpmPackageChecker; -use std::borrow::Cow; use std::collections::HashMap; -use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use std::time::SystemTime; mod cache_db; mod caches; @@ -50,7 +44,6 @@ pub use caches::Caches; pub use check::TypeCheckCache; pub use code_cache::CodeCache; pub use common::FastInsecureHasher; -pub use deno_dir::dirs::home_dir; pub use deno_dir::DenoDir; pub use deno_dir::DenoDirProvider; pub use disk_cache::DiskCache; @@ -63,121 +56,12 @@ pub use parsed_source::LazyGraphSourceParser; pub use parsed_source::ParsedSourceCache; /// Permissions used to save a file in the disk caches. -pub const CACHE_PERM: u32 = 0o644; +pub use deno_cache_dir::CACHE_PERM; -#[derive(Debug, Clone)] -pub struct RealDenoCacheEnv; - -impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv { - fn read_file_bytes( - &self, - path: &Path, - ) -> std::io::Result> { - std::fs::read(path).map(Cow::Owned) - } - - fn atomic_write_file( - &self, - path: &Path, - bytes: &[u8], - ) -> std::io::Result<()> { - atomic_write_file_with_retries(path, bytes, CACHE_PERM) - } - - fn canonicalize_path(&self, path: &Path) -> std::io::Result { - crate::util::fs::canonicalize_path(path) - } - - fn create_dir_all(&self, path: &Path) -> std::io::Result<()> { - std::fs::create_dir_all(path) - } - - fn modified(&self, path: &Path) -> std::io::Result> { - match std::fs::metadata(path) { - Ok(metadata) => Ok(Some( - metadata.modified().unwrap_or_else(|_| SystemTime::now()), - )), - Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None), - Err(err) => Err(err), - } - } - - fn is_file(&self, path: &Path) -> bool { - path.is_file() - } - - fn time_now(&self) -> SystemTime { - SystemTime::now() - } -} - -#[derive(Debug, Clone)] -pub struct DenoCacheEnvFsAdapter<'a>( - pub &'a dyn deno_runtime::deno_fs::FileSystem, -); - -impl<'a> deno_cache_dir::DenoCacheEnv for DenoCacheEnvFsAdapter<'a> { - fn read_file_bytes( - &self, - path: &Path, - ) -> std::io::Result> { - self - .0 - .read_file_sync(path, None) - .map_err(|err| err.into_io_error()) - } - - fn atomic_write_file( - &self, - path: &Path, - bytes: &[u8], - ) -> std::io::Result<()> { - atomic_write_file_with_retries_and_fs( - &AtomicWriteFileFsAdapter { - fs: self.0, - write_mode: CACHE_PERM, - }, - path, - bytes, - ) - } - - fn canonicalize_path(&self, path: &Path) -> std::io::Result { - self.0.realpath_sync(path).map_err(|e| e.into_io_error()) - } - - fn create_dir_all(&self, path: &Path) -> std::io::Result<()> { - self - .0 - .mkdir_sync(path, true, None) - .map_err(|e| e.into_io_error()) - } - - fn modified(&self, path: &Path) -> std::io::Result> { - self - .0 - .stat_sync(path) - .map(|stat| { - stat - .mtime - .map(|ts| SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(ts)) - }) - .map_err(|e| e.into_io_error()) - } - - fn is_file(&self, path: &Path) -> bool { - self.0.is_file_sync(path) - } - - fn time_now(&self) -> SystemTime { - SystemTime::now() - } -} - -pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache; -pub type LocalHttpCache = deno_cache_dir::LocalHttpCache; +pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache; +pub type LocalHttpCache = deno_cache_dir::LocalHttpCache; pub type LocalLspHttpCache = - deno_cache_dir::LocalLspHttpCache; + deno_cache_dir::LocalLspHttpCache; pub use deno_cache_dir::HttpCache; pub struct FetchCacherOptions { @@ -192,11 +76,11 @@ pub struct FetchCacherOptions { pub struct FetchCacher { pub file_header_overrides: HashMap>, file_fetcher: Arc, - fs: Arc, global_http_cache: Arc, in_npm_pkg_checker: Arc, module_info_cache: Arc, permissions: PermissionsContainer, + sys: FsSysTraitsAdapter, is_deno_publish: bool, cache_info_enabled: bool, } @@ -204,18 +88,18 @@ pub struct FetchCacher { impl FetchCacher { pub fn new( file_fetcher: Arc, - fs: Arc, global_http_cache: Arc, in_npm_pkg_checker: Arc, module_info_cache: Arc, + sys: FsSysTraitsAdapter, options: FetchCacherOptions, ) -> Self { Self { file_fetcher, - fs, global_http_cache, in_npm_pkg_checker, module_info_cache, + sys, file_header_overrides: options.file_header_overrides, permissions: options.permissions, is_deno_publish: options.is_deno_publish, @@ -277,9 +161,8 @@ impl Loader for FetchCacher { // symlinked to `/my-project-2/node_modules`), so first we checked if the path // is in a node_modules dir to avoid needlessly canonicalizing, then now compare // against the canonicalized specifier. - let specifier = crate::node::resolve_specifier_into_node_modules( - specifier, - self.fs.as_ref(), + let specifier = node_resolver::resolve_specifier_into_node_modules( + &self.sys, specifier, ); if self.in_npm_pkg_checker.in_npm_package(&specifier) { return Box::pin(futures::future::ready(Ok(Some( diff --git a/cli/factory.rs b/cli/factory.rs index d6940d6df1..5c73537743 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -11,7 +11,6 @@ use crate::args::StorageKeyResolver; use crate::args::TsConfigType; use crate::cache::Caches; use crate::cache::CodeCache; -use crate::cache::DenoCacheEnvFsAdapter; use crate::cache::DenoDir; use crate::cache::DenoDirProvider; use crate::cache::EmitCache; @@ -43,7 +42,6 @@ use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CreateInNpmPkgCheckerOptions; use crate::resolver::CjsTracker; use crate::resolver::CliDenoResolver; -use crate::resolver::CliDenoResolverFs; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; use crate::resolver::CliResolverOptions; @@ -76,9 +74,10 @@ use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; use deno_resolver::NodeAndNpmReqResolver; use deno_runtime::deno_fs; -use deno_runtime::deno_node::DenoFsNodeResolverEnv; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::PackageJsonResolver; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_tls::rustls::RootCertStore; @@ -318,8 +317,8 @@ impl CliFactory { pub fn global_http_cache(&self) -> Result<&Arc, AnyError> { self.services.global_http_cache.get_or_try_init(|| { Ok(Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter(self.fs().clone()), self.deno_dir()?.remote_folder_path(), - crate::cache::RealDenoCacheEnv, ))) }) } @@ -396,7 +395,7 @@ impl CliFactory { let global_path = self.deno_dir()?.npm_folder_path(); let cli_options = self.cli_options()?; Ok(Arc::new(NpmCacheDir::new( - &DenoCacheEnvFsAdapter(fs.as_ref()), + &FsSysTraitsAdapter(fs.clone()), global_path, cli_options.npmrc().get_all_known_registries_urls(), ))) @@ -416,7 +415,7 @@ impl CliFactory { create_cli_npm_resolver(if cli_options.use_byonm() { CliNpmResolverCreateOptions::Byonm( CliByonmNpmResolverCreateOptions { - fs: CliDenoResolverFs(fs.clone()), + sys: FsSysTraitsAdapter(fs.clone()), pkg_json_resolver: self.pkg_json_resolver().clone(), root_node_modules_dir: Some( match cli_options.node_modules_dir_path() { @@ -434,6 +433,13 @@ impl CliFactory { } else { CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { + http_client_provider: self.http_client_provider().clone(), + npm_install_deps_provider: Arc::new( + NpmInstallDepsProvider::from_workspace( + cli_options.workspace(), + ), + ), + sys: FsSysTraitsAdapter(self.fs().clone()), snapshot: match cli_options.resolve_npm_resolution_snapshot()? { Some(snapshot) => { CliNpmResolverManagedSnapshotOption::Specified(Some( @@ -452,19 +458,12 @@ impl CliFactory { }, }, maybe_lockfile: cli_options.maybe_lockfile().cloned(), - fs: fs.clone(), - http_client_provider: self.http_client_provider().clone(), npm_cache_dir: self.npm_cache_dir()?.clone(), cache_setting: cli_options.cache_setting(), text_only_progress_bar: self.text_only_progress_bar().clone(), maybe_node_modules_path: cli_options .node_modules_dir_path() .cloned(), - npm_install_deps_provider: Arc::new( - NpmInstallDepsProvider::from_workspace( - cli_options.workspace(), - ), - ), npm_system_info: cli_options.npm_system_info(), npmrc: cli_options.npmrc().clone(), lifecycle_scripts: cli_options.lifecycle_scripts_config(), @@ -487,7 +486,7 @@ impl CliFactory { .get_or_try_init(|| { Ok(self.cli_options()?.unstable_sloppy_imports().then(|| { Arc::new(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new( - self.fs().clone(), + FsSysTraitsAdapter(self.fs().clone()), ))) })) }) @@ -655,14 +654,15 @@ impl CliFactory { .get_or_try_init_async( async { Ok(Arc::new(NodeResolver::new( - DenoFsNodeResolverEnv::new(self.fs().clone()), self.in_npm_pkg_checker()?.clone(), + RealIsBuiltInNodeModuleChecker, self .npm_resolver() .await? .clone() .into_npm_pkg_folder_resolver(), self.pkg_json_resolver().clone(), + FsSysTraitsAdapter(self.fs().clone()), ))) } .boxed_local(), @@ -690,7 +690,6 @@ impl CliFactory { Ok(Arc::new(NodeCodeTranslator::new( cjs_esm_analyzer, - DenoFsNodeResolverEnv::new(self.fs().clone()), self.in_npm_pkg_checker()?.clone(), node_resolver, self @@ -699,6 +698,7 @@ impl CliFactory { .clone() .into_npm_pkg_folder_resolver(), self.pkg_json_resolver().clone(), + FsSysTraitsAdapter(self.fs().clone()), ))) }) .await @@ -714,7 +714,7 @@ impl CliFactory { let npm_resolver = self.npm_resolver().await?; Ok(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), - fs: CliDenoResolverFs(self.fs().clone()), + sys: FsSysTraitsAdapter(self.fs().clone()), in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(), node_resolver: self.node_resolver().await?.clone(), npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), @@ -725,7 +725,7 @@ impl CliFactory { pub fn pkg_json_resolver(&self) -> &Arc { self.services.pkg_json_resolver.get_or_init(|| { - Arc::new(PackageJsonResolver::new(DenoFsNodeResolverEnv::new( + Arc::new(PackageJsonResolver::new(FsSysTraitsAdapter( self.fs().clone(), ))) }) @@ -765,7 +765,6 @@ impl CliFactory { self.cjs_tracker()?.clone(), cli_options.clone(), self.file_fetcher()?.clone(), - self.fs().clone(), self.global_http_cache()?.clone(), self.in_npm_pkg_checker()?.clone(), cli_options.maybe_lockfile().cloned(), @@ -775,6 +774,7 @@ impl CliFactory { self.parsed_source_cache().clone(), self.resolver().await?.clone(), self.root_permissions_container()?.clone(), + FsSysTraitsAdapter(self.fs().clone()), ))) }) .await @@ -960,7 +960,6 @@ impl CliFactory { None }, self.emitter()?.clone(), - fs.clone(), in_npm_pkg_checker.clone(), self.main_module_graph_container().await?.clone(), self.module_load_preparer().await?.clone(), @@ -975,6 +974,7 @@ impl CliFactory { ), self.parsed_source_cache().clone(), self.resolver().await?.clone(), + FsSysTraitsAdapter(self.fs().clone()), )), node_resolver.clone(), npm_resolver.clone(), diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 1b286c76b7..38f3dd1847 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -1,11 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::cache::HttpCache; -use crate::cache::RealDenoCacheEnv; -use crate::colors; -use crate::http_util::get_response_body_with_progress; -use crate::http_util::HttpClientProvider; -use crate::util::progress_bar::ProgressBar; +use std::borrow::Cow; +use std::collections::HashMap; +use std::sync::Arc; use boxed_error::Boxed; use deno_ast::MediaType; @@ -27,7 +24,7 @@ use deno_core::url::Url; use deno_core::ModuleSpecifier; use deno_error::JsError; use deno_graph::source::LoaderChecksum; - +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_permissions::CheckSpecifierKind; use deno_runtime::deno_permissions::PermissionCheckError; use deno_runtime::deno_permissions::PermissionsContainer; @@ -35,12 +32,14 @@ use deno_runtime::deno_web::BlobStore; use http::header; use http::HeaderMap; use http::StatusCode; -use std::borrow::Cow; -use std::collections::HashMap; -use std::env; -use std::sync::Arc; use thiserror::Error; +use crate::cache::HttpCache; +use crate::colors; +use crate::http_util::get_response_body_with_progress; +use crate::http_util::HttpClientProvider; +use crate::util::progress_bar::ProgressBar; + #[derive(Debug, Clone, Eq, PartialEq)] pub struct TextDecodedFile { pub media_type: MediaType, @@ -268,7 +267,7 @@ pub struct FetchNoFollowOptions<'a> { type DenoCacheDirFileFetcher = deno_cache_dir::file_fetcher::FileFetcher< BlobStoreAdapter, - RealDenoCacheEnv, + FsSysTraitsAdapter, HttpClientAdapter, >; @@ -290,9 +289,11 @@ impl CliFileFetcher { download_log_level: log::Level, ) -> Self { let memory_files = Arc::new(MemoryFiles::default()); + let sys = FsSysTraitsAdapter::new_real(); + let auth_tokens = AuthTokens::new_from_sys(&sys); let file_fetcher = DenoCacheDirFileFetcher::new( BlobStoreAdapter(blob_store), - RealDenoCacheEnv, + sys, http_cache, HttpClientAdapter { http_client_provider: http_client_provider.clone(), @@ -303,7 +304,7 @@ impl CliFileFetcher { FileFetcherOptions { allow_remote, cache_setting, - auth_tokens: AuthTokens::new(env::var("DENO_AUTH_TOKENS").ok()), + auth_tokens, }, ); Self { @@ -497,7 +498,6 @@ fn validate_scheme(specifier: &Url) -> Result<(), UnsupportedSchemeError> { #[cfg(test)] mod tests { use crate::cache::GlobalHttpCache; - use crate::cache::RealDenoCacheEnv; use crate::http_util::HttpClientProvider; use super::*; @@ -538,7 +538,10 @@ mod tests { let temp_dir = maybe_temp_dir.unwrap_or_default(); let location = temp_dir.path().join("remote").to_path_buf(); let blob_store: Arc = Default::default(); - let cache = Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)); + let cache = Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), + location, + )); let file_fetcher = CliFileFetcher::new( cache.clone(), Arc::new(HttpClientProvider::new(None, None)), @@ -752,8 +755,8 @@ mod tests { let location = temp_dir.path().join("remote").to_path_buf(); let file_fetcher = CliFileFetcher::new( Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), location, - crate::cache::RealDenoCacheEnv, )), Arc::new(HttpClientProvider::new(None, None)), Default::default(), @@ -781,8 +784,8 @@ mod tests { resolve_url("http://localhost:4545/subdir/mismatch_ext.ts").unwrap(); let http_cache = Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), location.clone(), - crate::cache::RealDenoCacheEnv, )); let file_modified_01 = { let file_fetcher = CliFileFetcher::new( @@ -808,8 +811,8 @@ mod tests { let file_modified_02 = { let file_fetcher = CliFileFetcher::new( Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), location, - crate::cache::RealDenoCacheEnv, )), Arc::new(HttpClientProvider::new(None, None)), Default::default(), @@ -938,8 +941,8 @@ mod tests { let redirected_specifier = resolve_url("http://localhost:4546/subdir/mismatch_ext.ts").unwrap(); let http_cache = Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), location.clone(), - crate::cache::RealDenoCacheEnv, )); let metadata_file_modified_01 = { @@ -1073,8 +1076,8 @@ mod tests { let location = temp_dir.path().join("remote").to_path_buf(); let file_fetcher = CliFileFetcher::new( Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), location, - crate::cache::RealDenoCacheEnv, )), Arc::new(HttpClientProvider::new(None, None)), Default::default(), @@ -1110,7 +1113,10 @@ mod tests { let temp_dir = TempDir::new(); let location = temp_dir.path().join("remote").to_path_buf(); let file_fetcher_01 = CliFileFetcher::new( - Arc::new(GlobalHttpCache::new(location.clone(), RealDenoCacheEnv)), + Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), + location.clone(), + )), Arc::new(HttpClientProvider::new(None, None)), Default::default(), None, @@ -1119,7 +1125,10 @@ mod tests { log::Level::Info, ); let file_fetcher_02 = CliFileFetcher::new( - Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)), + Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), + location, + )), Arc::new(HttpClientProvider::new(None, None)), Default::default(), None, diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 88c412b65a..a21d055adc 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -1,5 +1,43 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::HashSet; +use std::error::Error; +use std::ops::Deref; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_config::deno_json::JsxImportSourceConfig; +use deno_config::workspace::JsrPackageConfig; +use deno_core::anyhow::bail; +use deno_core::error::custom_error; +use deno_core::error::AnyError; +use deno_core::parking_lot::Mutex; +use deno_core::ModuleSpecifier; +use deno_graph::source::Loader; +use deno_graph::source::LoaderChecksum; +use deno_graph::source::ResolutionKind; +use deno_graph::source::ResolveError; +use deno_graph::FillFromLockfileOptions; +use deno_graph::GraphKind; +use deno_graph::JsrLoadError; +use deno_graph::ModuleError; +use deno_graph::ModuleGraph; +use deno_graph::ModuleGraphError; +use deno_graph::ModuleLoadError; +use deno_graph::ResolutionError; +use deno_graph::SpecifierError; +use deno_graph::WorkspaceFastCheckOption; +use deno_path_util::url_to_file_path; +use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; +use deno_runtime::deno_fs::FsSysTraitsAdapter; +use deno_runtime::deno_node; +use deno_runtime::deno_permissions::PermissionsContainer; +use deno_semver::jsr::JsrDepPackageReq; +use deno_semver::package::PackageNv; +use deno_semver::SmallStackString; +use import_map::ImportMapError; +use node_resolver::InNpmPackageChecker; + use crate::args::config_to_deno_graph_workspace_member; use crate::args::jsr_url; use crate::args::CliLockfile; @@ -23,43 +61,6 @@ use crate::tools::check; use crate::tools::check::TypeChecker; use crate::util::file_watcher::WatcherCommunicator; use crate::util::fs::canonicalize_path; -use deno_config::deno_json::JsxImportSourceConfig; -use deno_config::workspace::JsrPackageConfig; -use deno_core::anyhow::bail; -use deno_graph::source::LoaderChecksum; -use deno_graph::source::ResolutionKind; -use deno_graph::FillFromLockfileOptions; -use deno_graph::JsrLoadError; -use deno_graph::ModuleLoadError; -use deno_graph::WorkspaceFastCheckOption; - -use deno_core::error::custom_error; -use deno_core::error::AnyError; -use deno_core::parking_lot::Mutex; -use deno_core::ModuleSpecifier; -use deno_graph::source::Loader; -use deno_graph::source::ResolveError; -use deno_graph::GraphKind; -use deno_graph::ModuleError; -use deno_graph::ModuleGraph; -use deno_graph::ModuleGraphError; -use deno_graph::ResolutionError; -use deno_graph::SpecifierError; -use deno_path_util::url_to_file_path; -use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; -use deno_runtime::deno_fs::FileSystem; -use deno_runtime::deno_node; -use deno_runtime::deno_permissions::PermissionsContainer; -use deno_semver::jsr::JsrDepPackageReq; -use deno_semver::package::PackageNv; -use deno_semver::SmallStackString; -use import_map::ImportMapError; -use node_resolver::InNpmPackageChecker; -use std::collections::HashSet; -use std::error::Error; -use std::ops::Deref; -use std::path::PathBuf; -use std::sync::Arc; #[derive(Clone)] pub struct GraphValidOptions { @@ -80,7 +81,7 @@ pub struct GraphValidOptions { /// for the CLI. pub fn graph_valid( graph: &ModuleGraph, - fs: &Arc, + sys: &FsSysTraitsAdapter, roots: &[ModuleSpecifier], options: GraphValidOptions, ) -> Result<(), AnyError> { @@ -90,7 +91,7 @@ pub fn graph_valid( let mut errors = graph_walk_errors( graph, - fs, + sys, roots, GraphWalkErrorsOptions { check_js: options.check_js, @@ -140,7 +141,7 @@ pub struct GraphWalkErrorsOptions { /// and enhances them with CLI information. pub fn graph_walk_errors<'a>( graph: &'a ModuleGraph, - fs: &'a Arc, + sys: &'a FsSysTraitsAdapter, roots: &'a [ModuleSpecifier], options: GraphWalkErrorsOptions, ) -> impl Iterator + 'a { @@ -175,7 +176,7 @@ pub fn graph_walk_errors<'a>( } ModuleGraphError::ModuleError(error) => { enhanced_integrity_error_message(error) - .or_else(|| enhanced_sloppy_imports_error_message(fs, error)) + .or_else(|| enhanced_sloppy_imports_error_message(sys, error)) .unwrap_or_else(|| format_deno_graph_error(error)) } }; @@ -433,7 +434,6 @@ pub struct ModuleGraphBuilder { cjs_tracker: Arc, cli_options: Arc, file_fetcher: Arc, - fs: Arc, global_http_cache: Arc, in_npm_pkg_checker: Arc, lockfile: Option>, @@ -443,6 +443,7 @@ pub struct ModuleGraphBuilder { parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, + sys: FsSysTraitsAdapter, } impl ModuleGraphBuilder { @@ -452,7 +453,6 @@ impl ModuleGraphBuilder { cjs_tracker: Arc, cli_options: Arc, file_fetcher: Arc, - fs: Arc, global_http_cache: Arc, in_npm_pkg_checker: Arc, lockfile: Option>, @@ -462,13 +462,13 @@ impl ModuleGraphBuilder { parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, + sys: FsSysTraitsAdapter, ) -> Self { Self { caches, cjs_tracker, cli_options, file_fetcher, - fs, global_http_cache, in_npm_pkg_checker, lockfile, @@ -478,6 +478,7 @@ impl ModuleGraphBuilder { parsed_source_cache, resolver, root_permissions_container, + sys, } } @@ -593,7 +594,7 @@ impl ModuleGraphBuilder { is_dynamic: options.is_dynamic, passthrough_jsr_specifiers: false, executor: Default::default(), - file_system: &DenoGraphFsAdapter(self.fs.as_ref()), + file_system: &self.sys, jsr_url_provider: &CliJsrUrlProvider, npm_resolver: Some(&graph_npm_resolver), module_analyzer: &analyzer, @@ -747,10 +748,10 @@ impl ModuleGraphBuilder { ) -> cache::FetchCacher { cache::FetchCacher::new( self.file_fetcher.clone(), - self.fs.clone(), self.global_http_cache.clone(), self.in_npm_pkg_checker.clone(), self.module_info_cache.clone(), + self.sys.clone(), cache::FetchCacherOptions { file_header_overrides: self.cli_options.resolve_file_header_overrides(), permissions, @@ -779,7 +780,7 @@ impl ModuleGraphBuilder { ) -> Result<(), AnyError> { graph_valid( graph, - &self.fs, + &self.sys, roots, GraphValidOptions { kind: if self.cli_options.type_check_mode().is_true() { @@ -835,13 +836,13 @@ pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String { } fn enhanced_sloppy_imports_error_message( - fs: &Arc, + sys: &FsSysTraitsAdapter, error: &ModuleError, ) -> Option { match error { ModuleError::LoadingErr(specifier, _, ModuleLoadError::Loader(_)) // ex. "Is a directory" error | ModuleError::Missing(specifier, _) => { - let additional_message = CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(fs.clone())) + let additional_message = CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(sys.clone())) .resolve(specifier, SloppyImportsResolutionKind::Execution)? .as_suggestion_message(); Some(format!( @@ -1082,71 +1083,6 @@ impl deno_graph::source::Reporter for FileWatcherReporter { } } -pub struct DenoGraphFsAdapter<'a>( - pub &'a dyn deno_runtime::deno_fs::FileSystem, -); - -impl<'a> deno_graph::source::FileSystem for DenoGraphFsAdapter<'a> { - fn read_dir( - &self, - dir_url: &deno_graph::ModuleSpecifier, - ) -> Vec { - use deno_core::anyhow; - use deno_graph::source::DirEntry; - use deno_graph::source::DirEntryKind; - - let dir_path = match dir_url.to_file_path() { - Ok(path) => path, - // ignore, treat as non-analyzable - Err(()) => return vec![], - }; - let entries = match self.0.read_dir_sync(&dir_path) { - Ok(dir) => dir, - Err(err) - if matches!( - err.kind(), - std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::NotFound - ) => - { - return vec![]; - } - Err(err) => { - return vec![DirEntry { - kind: DirEntryKind::Error( - anyhow::Error::from(err) - .context("Failed to read directory.".to_string()), - ), - url: dir_url.clone(), - }]; - } - }; - let mut dir_entries = Vec::with_capacity(entries.len()); - for entry in entries { - let entry_path = dir_path.join(&entry.name); - dir_entries.push(if entry.is_directory { - DirEntry { - kind: DirEntryKind::Dir, - url: ModuleSpecifier::from_directory_path(&entry_path).unwrap(), - } - } else if entry.is_file { - DirEntry { - kind: DirEntryKind::File, - url: ModuleSpecifier::from_file_path(&entry_path).unwrap(), - } - } else if entry.is_symlink { - DirEntry { - kind: DirEntryKind::Symlink, - url: ModuleSpecifier::from_file_path(&entry_path).unwrap(), - } - } else { - continue; - }); - } - - dir_entries - } -} - pub fn format_range_with_colors(referrer: &deno_graph::Range) -> String { format!( "{}:{}:{}", diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs index fbf9ea6f1b..c6d1b39ef4 100644 --- a/cli/lsp/cache.rs +++ b/cli/lsp/cache.rs @@ -11,6 +11,7 @@ use crate::lsp::logging::lsp_warn; use deno_core::url::Url; use deno_core::ModuleSpecifier; use deno_path_util::url_to_file_path; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use std::collections::BTreeMap; use std::fs; use std::path::Path; @@ -94,8 +95,8 @@ impl LspCache { let deno_dir = DenoDir::new(global_cache_path) .expect("should be infallible with absolute custom root"); let global = Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), deno_dir.remote_folder_path(), - crate::cache::RealDenoCacheEnv, )); Self { deno_dir, diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 3efebe63b1..a43e774934 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -9,8 +9,6 @@ use deno_config::deno_json::LintConfig; use deno_config::deno_json::NodeModulesDirMode; use deno_config::deno_json::TestConfig; use deno_config::deno_json::TsConfig; -use deno_config::fs::DenoConfigFs; -use deno_config::fs::RealDenoConfigFs; use deno_config::glob::FilePatterns; use deno_config::glob::PathOrPatternSet; use deno_config::workspace::CreateResolverOptions; @@ -38,10 +36,10 @@ use deno_lint::linter::LintConfig as DenoLintConfig; use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonCache; use deno_path_util::url_to_file_path; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::PackageJson; use indexmap::IndexSet; use lsp_types::ClientCapabilities; -use std::borrow::Cow; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::HashMap; @@ -1220,7 +1218,6 @@ impl ConfigData { settings: &Settings, file_fetcher: &Arc, // sync requirement is because the lsp requires sync - cached_deno_config_fs: &(dyn DenoConfigFs + Sync), deno_json_cache: &(dyn DenoJsonCache + Sync), pkg_json_cache: &(dyn PackageJsonCache + Sync), workspace_cache: &(dyn WorkspaceCache + Sync), @@ -1230,6 +1227,7 @@ impl ConfigData { Ok(scope_dir_path) => { let paths = [scope_dir_path]; WorkspaceDirectory::discover( + &FsSysTraitsAdapter::new_real(), match specified_config { Some(config_path) => { deno_config::workspace::WorkspaceDiscoverStart::ConfigFile( @@ -1241,7 +1239,6 @@ impl ConfigData { } }, &WorkspaceDiscoverOptions { - fs: cached_deno_config_fs, additional_config_file_names: &[], deno_json_cache: Some(deno_json_cache), pkg_json_cache: Some(pkg_json_cache), @@ -1618,9 +1615,9 @@ impl ConfigData { || unstable.contains("sloppy-imports"); let sloppy_imports_resolver = unstable_sloppy_imports.then(|| { Arc::new(CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new_without_stat_cache(Arc::new( - deno_runtime::deno_fs::RealFs, - )), + SloppyImportsCachedFs::new_without_stat_cache( + FsSysTraitsAdapter::new_real(), + ), )) }); let resolver = Arc::new(resolver); @@ -1840,7 +1837,6 @@ impl ConfigTree { // since we're resolving a workspace multiple times in different // folders, we want to cache all the lookups and config files across // ConfigData::load calls - let cached_fs = CachedDenoConfigFs::default(); let deno_json_cache = DenoJsonMemCache::default(); let pkg_json_cache = PackageJsonMemCache::default(); let workspace_cache = WorkspaceMemCache::default(); @@ -1865,7 +1861,6 @@ impl ConfigTree { folder_uri, settings, file_fetcher, - &cached_fs, &deno_json_cache, &pkg_json_cache, &workspace_cache, @@ -1896,7 +1891,6 @@ impl ConfigTree { &scope, settings, file_fetcher, - &cached_fs, &deno_json_cache, &pkg_json_cache, &workspace_cache, @@ -1913,7 +1907,6 @@ impl ConfigTree { member_scope, settings, file_fetcher, - &cached_fs, &deno_json_cache, &pkg_json_cache, &workspace_cache, @@ -1930,7 +1923,7 @@ impl ConfigTree { pub async fn inject_config_file(&mut self, config_file: ConfigFile) { let scope = config_file.specifier.join(".").unwrap(); let json_text = serde_json::to_string(&config_file.json).unwrap(); - let test_fs = deno_runtime::deno_fs::InMemoryFs::default(); + let test_fs = Arc::new(deno_runtime::deno_fs::InMemoryFs::default()); let config_path = url_to_file_path(&config_file.specifier).unwrap(); test_fs.setup_text_files(vec![( config_path.to_string_lossy().to_string(), @@ -1938,11 +1931,11 @@ impl ConfigTree { )]); let workspace_dir = Arc::new( WorkspaceDirectory::discover( + &FsSysTraitsAdapter(test_fs.clone()), deno_config::workspace::WorkspaceDiscoverStart::ConfigFile( &config_path, ), &deno_config::workspace::WorkspaceDiscoverOptions { - fs: &crate::args::deno_json::DenoConfigFsAdapter(&test_fs), ..Default::default() }, ) @@ -2076,78 +2069,6 @@ impl deno_config::workspace::WorkspaceCache for WorkspaceMemCache { } } -#[derive(Default)] -struct CachedFsItems { - items: HashMap>, -} - -impl CachedFsItems { - pub fn get( - &mut self, - path: &Path, - action: impl FnOnce(&Path) -> Result, - ) -> Result { - let value = if let Some(value) = self.items.get(path) { - value - } else { - let value = action(path); - // just in case this gets really large for some reason - if self.items.len() == 16_384 { - return value; - } - self.items.insert(path.to_owned(), value); - self.items.get(path).unwrap() - }; - value - .as_ref() - .map(|v| (*v).clone()) - .map_err(|e| std::io::Error::new(e.kind(), e.to_string())) - } -} - -#[derive(Default)] -struct InnerData { - stat_calls: CachedFsItems, - read_to_string_calls: CachedFsItems>, -} - -#[derive(Default)] -struct CachedDenoConfigFs(Mutex); - -impl DenoConfigFs for CachedDenoConfigFs { - fn stat_sync( - &self, - path: &Path, - ) -> Result { - self - .0 - .lock() - .stat_calls - .get(path, |path| RealDenoConfigFs.stat_sync(path)) - } - - fn read_to_string_lossy( - &self, - path: &Path, - ) -> Result, std::io::Error> { - self - .0 - .lock() - .read_to_string_calls - .get(path, |path| RealDenoConfigFs.read_to_string_lossy(path)) - } - - fn read_dir( - &self, - path: &Path, - ) -> Result, std::io::Error> { - // no need to cache these because the workspace cache will ensure - // we only do read_dir calls once (read_dirs are only used for - // npm workspace resolution) - RealDenoConfigFs.read_dir(path) - } -} - #[cfg(test)] mod tests { use deno_config::deno_json::ConfigParseOptions; diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 804cebfb9b..33fd4897c4 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -48,7 +48,7 @@ use deno_graph::SpecifierError; use deno_lint::linter::LintConfig as DenoLintConfig; use deno_resolver::sloppy_imports::SloppyImportsResolution; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; -use deno_runtime::deno_fs; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node; use deno_runtime::tokio_util::create_basic_runtime; use deno_semver::jsr::JsrPackageReqReference; @@ -1281,7 +1281,7 @@ impl DenoDiagnostic { Self::NotInstalledNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("npm package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))), Self::NoLocal(specifier) => { let maybe_sloppy_resolution = CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new(Arc::new(deno_fs::RealFs)) + SloppyImportsCachedFs::new(FsSysTraitsAdapter::new_real()) ).resolve(specifier, SloppyImportsResolutionKind::Execution); let data = maybe_sloppy_resolution.as_ref().map(|res| { json!({ diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index a7a0a59743..1cc26aff58 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -17,6 +17,7 @@ use deno_core::ModuleSpecifier; use deno_graph::GraphKind; use deno_graph::Resolution; use deno_path_util::url_to_file_path; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::RootCertStoreProvider; use deno_semver::jsr::JsrPackageReqReference; @@ -279,7 +280,7 @@ impl LanguageServer { .await?; graph_util::graph_valid( &graph, - factory.fs(), + &FsSysTraitsAdapter(factory.fs().clone()), &roots, graph_util::GraphValidOptions { kind: GraphKind::All, @@ -3612,11 +3613,11 @@ impl Inner { let workspace = match config_data { Some(d) => d.member_dir.clone(), None => Arc::new(WorkspaceDirectory::discover( + &FsSysTraitsAdapter::new_real(), deno_config::workspace::WorkspaceDiscoverStart::Paths(&[ initial_cwd.clone() ]), &WorkspaceDiscoverOptions { - fs: Default::default(), // use real fs, deno_json_cache: None, pkg_json_cache: None, workspace_cache: None, diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index 067f201829..488e333e9d 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -32,6 +32,7 @@ use deno_core::url::Position; use deno_core::url::Url; use deno_core::ModuleSpecifier; use deno_graph::Dependency; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use log::error; use once_cell::sync::Lazy; use std::borrow::Cow; @@ -430,8 +431,8 @@ impl ModuleRegistry { ) -> Self { // the http cache should always be the global one for registry completions let http_cache = Arc::new(GlobalHttpCache::new( + FsSysTraitsAdapter::new_real(), location.clone(), - crate::cache::RealDenoCacheEnv, )); let file_fetcher = CliFileFetcher::new( http_cache.clone(), diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 482f2ddb40..addecc9a61 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -19,9 +19,10 @@ use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; use deno_resolver::NodeAndNpmReqResolver; -use deno_runtime::deno_fs; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::PackageJsonResolver; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; @@ -42,7 +43,6 @@ use super::jsr::JsrCacheResolver; use crate::args::create_default_npmrc; use crate::args::CliLockfile; use crate::args::NpmInstallDepsProvider; -use crate::cache::DenoCacheEnvFsAdapter; use crate::factory::Deferred; use crate::graph_util::to_node_resolution_kind; use crate::graph_util::to_node_resolution_mode; @@ -61,7 +61,6 @@ use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CreateInNpmPkgCheckerOptions; use crate::npm::ManagedCliNpmResolver; use crate::resolver::CliDenoResolver; -use crate::resolver::CliDenoResolverFs; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; use crate::resolver::CliResolverOptions; @@ -599,21 +598,19 @@ struct ResolverFactoryServices { struct ResolverFactory<'a> { config_data: Option<&'a Arc>, - fs: Arc, pkg_json_resolver: Arc, + sys: FsSysTraitsAdapter, services: ResolverFactoryServices, } impl<'a> ResolverFactory<'a> { pub fn new(config_data: Option<&'a Arc>) -> Self { - let fs = Arc::new(deno_fs::RealFs); - let pkg_json_resolver = Arc::new(PackageJsonResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), - )); + let sys = FsSysTraitsAdapter::new_real(); + let pkg_json_resolver = Arc::new(PackageJsonResolver::new(sys.clone())); Self { config_data, - fs, pkg_json_resolver, + sys, services: Default::default(), } } @@ -624,9 +621,10 @@ impl<'a> ResolverFactory<'a> { cache: &LspCache, ) { let enable_byonm = self.config_data.map(|d| d.byonm).unwrap_or(false); + let sys = FsSysTraitsAdapter::new_real(); let options = if enable_byonm { CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { - fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)), + sys, pkg_json_resolver: self.pkg_json_resolver.clone(), root_node_modules_dir: self.config_data.and_then(|config_data| { config_data.node_modules_dir.clone().or_else(|| { @@ -642,12 +640,14 @@ impl<'a> ResolverFactory<'a> { .and_then(|d| d.npmrc.clone()) .unwrap_or_else(create_default_npmrc); let npm_cache_dir = Arc::new(NpmCacheDir::new( - &DenoCacheEnvFsAdapter(self.fs.as_ref()), + &sys, cache.deno_dir().npm_folder_path(), npmrc.get_all_known_registries_urls(), )); CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { http_client_provider: http_client_provider.clone(), + // only used for top level install, so we can ignore this + npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) { Some(lockfile) => { CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( @@ -656,10 +656,7 @@ impl<'a> ResolverFactory<'a> { } None => CliNpmResolverManagedSnapshotOption::Specified(None), }, - // Don't provide the lockfile. We don't want these resolvers - // updating it. Only the cache request should update the lockfile. - maybe_lockfile: None, - fs: Arc::new(deno_fs::RealFs), + sys: FsSysTraitsAdapter::new_real(), npm_cache_dir, // Use an "only" cache setting in order to make the // user do an explicit "cache" command and prevent @@ -667,11 +664,12 @@ impl<'a> ResolverFactory<'a> { // the user is typing. cache_setting: CacheSetting::Only, text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly), + // Don't provide the lockfile. We don't want these resolvers + // updating it. Only the cache request should update the lockfile. + maybe_lockfile: None, maybe_node_modules_path: self .config_data .and_then(|d| d.node_modules_dir.clone()), - // only used for top level install, so we can ignore this - npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), npmrc, npm_system_info: NpmSystemInfo::default(), lifecycle_scripts: Default::default(), @@ -779,10 +777,11 @@ impl<'a> ResolverFactory<'a> { .get_or_init(|| { let npm_resolver = self.services.npm_resolver.as_ref()?; Some(Arc::new(NodeResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(self.fs.clone()), self.in_npm_pkg_checker().clone(), + RealIsBuiltInNodeModuleChecker, npm_resolver.clone().into_npm_pkg_folder_resolver(), self.pkg_json_resolver.clone(), + self.sys.clone(), ))) }) .as_ref() @@ -797,10 +796,10 @@ impl<'a> ResolverFactory<'a> { let npm_resolver = self.npm_resolver()?; Some(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), - fs: CliDenoResolverFs(self.fs.clone()), in_npm_pkg_checker: self.in_npm_pkg_checker().clone(), node_resolver: node_resolver.clone(), npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), + sys: self.sys.clone(), }))) }) .as_ref() diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 3ba8753335..3e81dbc881 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -11,36 +11,6 @@ use std::sync::atomic::AtomicU16; use std::sync::atomic::Ordering; use std::sync::Arc; -use crate::args::jsr_url; -use crate::args::CliLockfile; -use crate::args::CliOptions; -use crate::args::DenoSubcommand; -use crate::args::TsTypeLib; -use crate::cache::CodeCache; -use crate::cache::FastInsecureHasher; -use crate::cache::ParsedSourceCache; -use crate::emit::Emitter; -use crate::graph_container::MainModuleGraphContainer; -use crate::graph_container::ModuleGraphContainer; -use crate::graph_container::ModuleGraphUpdatePermit; -use crate::graph_util::CreateGraphOptions; -use crate::graph_util::ModuleGraphBuilder; -use crate::node; -use crate::node::CliNodeCodeTranslator; -use crate::npm::CliNpmResolver; -use crate::resolver::CjsTracker; -use crate::resolver::CliNpmReqResolver; -use crate::resolver::CliResolver; -use crate::resolver::ModuleCodeStringSource; -use crate::resolver::NotSupportedKindInNpmError; -use crate::resolver::NpmModuleLoader; -use crate::tools::check; -use crate::tools::check::TypeChecker; -use crate::util::progress_bar::ProgressBar; -use crate::util::text_encoding::code_without_source_map; -use crate::util::text_encoding::source_map_from_code; -use crate::worker::CreateModuleLoaderResult; -use crate::worker::ModuleLoaderFactory; use deno_ast::MediaType; use deno_ast::ModuleKind; use deno_core::anyhow::anyhow; @@ -69,7 +39,7 @@ use deno_graph::ModuleGraph; use deno_graph::Resolution; use deno_graph::WasmModule; use deno_runtime::code_cache; -use deno_runtime::deno_fs::FileSystem; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::NodeRequireLoader; use deno_runtime::deno_node::NodeResolver; @@ -79,6 +49,37 @@ use node_resolver::errors::ClosestPkgJsonError; use node_resolver::InNpmPackageChecker; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; +use sys_traits::FsRead; + +use crate::args::jsr_url; +use crate::args::CliLockfile; +use crate::args::CliOptions; +use crate::args::DenoSubcommand; +use crate::args::TsTypeLib; +use crate::cache::CodeCache; +use crate::cache::FastInsecureHasher; +use crate::cache::ParsedSourceCache; +use crate::emit::Emitter; +use crate::graph_container::MainModuleGraphContainer; +use crate::graph_container::ModuleGraphContainer; +use crate::graph_container::ModuleGraphUpdatePermit; +use crate::graph_util::CreateGraphOptions; +use crate::graph_util::ModuleGraphBuilder; +use crate::node::CliNodeCodeTranslator; +use crate::npm::CliNpmResolver; +use crate::resolver::CjsTracker; +use crate::resolver::CliNpmReqResolver; +use crate::resolver::CliResolver; +use crate::resolver::ModuleCodeStringSource; +use crate::resolver::NotSupportedKindInNpmError; +use crate::resolver::NpmModuleLoader; +use crate::tools::check; +use crate::tools::check::TypeChecker; +use crate::util::progress_bar::ProgressBar; +use crate::util::text_encoding::code_without_source_map; +use crate::util::text_encoding::source_map_from_code; +use crate::worker::CreateModuleLoaderResult; +use crate::worker::ModuleLoaderFactory; pub struct ModuleLoadPreparer { options: Arc, @@ -215,7 +216,6 @@ struct SharedCliModuleLoaderState { cjs_tracker: Arc, code_cache: Option>, emitter: Arc, - fs: Arc, in_npm_pkg_checker: Arc, main_module_graph_container: Arc, module_load_preparer: Arc, @@ -226,6 +226,7 @@ struct SharedCliModuleLoaderState { npm_module_loader: NpmModuleLoader, parsed_source_cache: Arc, resolver: Arc, + sys: FsSysTraitsAdapter, in_flight_loads_tracker: InFlightModuleLoadsTracker, } @@ -275,7 +276,6 @@ impl CliModuleLoaderFactory { cjs_tracker: Arc, code_cache: Option>, emitter: Arc, - fs: Arc, in_npm_pkg_checker: Arc, main_module_graph_container: Arc, module_load_preparer: Arc, @@ -286,6 +286,7 @@ impl CliModuleLoaderFactory { npm_module_loader: NpmModuleLoader, parsed_source_cache: Arc, resolver: Arc, + sys: FsSysTraitsAdapter, ) -> Self { Self { shared: Arc::new(SharedCliModuleLoaderState { @@ -301,7 +302,6 @@ impl CliModuleLoaderFactory { cjs_tracker, code_cache, emitter, - fs, in_npm_pkg_checker, main_module_graph_container, module_load_preparer, @@ -312,6 +312,7 @@ impl CliModuleLoaderFactory { npm_module_loader, parsed_source_cache, resolver, + sys, in_flight_loads_tracker: InFlightModuleLoadsTracker { loads_number: Arc::new(AtomicU16::new(0)), cleanup_task_timeout: 10_000, @@ -344,7 +345,7 @@ impl CliModuleLoaderFactory { let node_require_loader = Rc::new(CliNodeRequireLoader { cjs_tracker: self.shared.cjs_tracker.clone(), emitter: self.shared.emitter.clone(), - fs: self.shared.fs.clone(), + sys: self.shared.sys.clone(), graph_container, in_npm_pkg_checker: self.shared.in_npm_pkg_checker.clone(), npm_resolver: self.shared.npm_resolver.clone(), @@ -593,9 +594,9 @@ impl Some(Module::Json(module)) => module.specifier.clone(), Some(Module::Wasm(module)) => module.specifier.clone(), Some(Module::External(module)) => { - node::resolve_specifier_into_node_modules( + node_resolver::resolve_specifier_into_node_modules( + &self.shared.sys, &module.specifier, - self.shared.fs.as_ref(), ) } None => specifier.into_owned(), @@ -1091,7 +1092,7 @@ impl ModuleGraphUpdatePermit for WorkerModuleGraphUpdatePermit { struct CliNodeRequireLoader { cjs_tracker: Arc, emitter: Arc, - fs: Arc, + sys: FsSysTraitsAdapter, graph_container: TGraphContainer, in_npm_pkg_checker: Arc, npm_resolver: Arc, @@ -1120,7 +1121,7 @@ impl NodeRequireLoader ) -> Result, AnyError> { // todo(dsherret): use the preloaded module from the graph if available? let media_type = MediaType::from_path(path); - let text = self.fs.read_text_file_lossy_sync(path, None)?; + let text = self.sys.fs_read_to_string_lossy(path)?; if media_type.is_emittable() { let specifier = deno_path_util::url_from_file_path(path)?; if self.in_npm_pkg_checker.in_npm_package(&specifier) { diff --git a/cli/node.rs b/cli/node.rs index 11959df6b9..480da506c8 100644 --- a/cli/node.rs +++ b/cli/node.rs @@ -8,7 +8,8 @@ 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 deno_runtime::deno_fs::FsSysTraitsAdapter; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis; use node_resolver::analyze::CjsAnalysisExports; use node_resolver::analyze::CjsCodeAnalyzer; @@ -21,23 +22,11 @@ use crate::cache::NodeAnalysisCache; use crate::cache::ParsedSourceCache; use crate::resolver::CjsTracker; -pub type CliNodeCodeTranslator = - NodeCodeTranslator; - -/// Resolves a specifier that is pointing into a node_modules folder. -/// -/// Note: This should be called whenever getting the specifier from -/// a Module::External(module) reference because that module might -/// not be fully resolved at the time deno_graph is analyzing it -/// because the node_modules folder might not exist at that time. -pub fn resolve_specifier_into_node_modules( - specifier: &ModuleSpecifier, - fs: &dyn deno_fs::FileSystem, -) -> ModuleSpecifier { - node_resolver::resolve_specifier_into_node_modules(specifier, &|path| { - fs.realpath_sync(path).map_err(|err| err.into_io_error()) - }) -} +pub type CliNodeCodeTranslator = NodeCodeTranslator< + CliCjsCodeAnalyzer, + RealIsBuiltInNodeModuleChecker, + FsSysTraitsAdapter, +>; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum CliCjsAnalysis { diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index eca399251b..218f33989d 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -9,22 +9,20 @@ use deno_core::serde_json; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmNpmResolverCreateOptions; use deno_resolver::npm::CliNpmReqResolver; -use deno_runtime::deno_node::DenoFsNodeResolverEnv; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use node_resolver::NpmPackageFolderResolver; use crate::args::NpmProcessState; use crate::args::NpmProcessStateKind; -use crate::resolver::CliDenoResolverFs; use super::CliNpmResolver; use super::InnerCliNpmResolverRef; pub type CliByonmNpmResolverCreateOptions = - ByonmNpmResolverCreateOptions; -pub type CliByonmNpmResolver = - ByonmNpmResolver; + ByonmNpmResolverCreateOptions; +pub type CliByonmNpmResolver = ByonmNpmResolver; // todo(dsherret): the services hanging off `CliNpmResolver` doesn't seem ideal. We should probably decouple. #[derive(Debug)] diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 5006902aa1..5b0a304de8 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -21,9 +21,10 @@ use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; use deno_npm_cache::NpmCacheSetting; +use deno_path_util::fs::canonicalize_path_maybe_not_exists; use deno_resolver::npm::CliNpmReqResolver; use deno_runtime::colors; -use deno_runtime::deno_fs::FileSystem; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; @@ -41,7 +42,6 @@ use crate::args::NpmProcessState; use crate::args::NpmProcessStateKind; use crate::args::PackageJsonDepValueParseWithLocationError; use crate::cache::FastInsecureHasher; -use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use crate::util::progress_bar::ProgressBar; use crate::util::sync::AtomicFlag; @@ -50,7 +50,7 @@ use self::resolvers::create_npm_fs_resolver; use self::resolvers::NpmPackageFsResolver; use super::CliNpmCache; -use super::CliNpmCacheEnv; +use super::CliNpmCacheHttpClient; use super::CliNpmRegistryInfoProvider; use super::CliNpmResolver; use super::CliNpmTarballCache; @@ -68,9 +68,9 @@ pub enum CliNpmResolverManagedSnapshotOption { pub struct CliManagedNpmResolverCreateOptions { pub snapshot: CliNpmResolverManagedSnapshotOption, pub maybe_lockfile: Option>, - pub fs: Arc, pub http_client_provider: Arc, pub npm_cache_dir: Arc, + pub sys: FsSysTraitsAdapter, pub cache_setting: deno_cache_dir::file_fetcher::CacheSetting, pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, pub maybe_node_modules_path: Option, @@ -83,9 +83,12 @@ pub struct CliManagedNpmResolverCreateOptions { pub async fn create_managed_npm_resolver_for_lsp( options: CliManagedNpmResolverCreateOptions, ) -> Arc { - let cache_env = create_cache_env(&options); - let npm_cache = create_cache(cache_env.clone(), &options); - let npm_api = create_api(npm_cache.clone(), cache_env.clone(), &options); + let npm_cache = create_cache(&options); + let http_client = Arc::new(CliNpmCacheHttpClient::new( + options.http_client_provider.clone(), + options.text_only_progress_bar.clone(), + )); + let npm_api = create_api(npm_cache.clone(), http_client.clone(), &options); // spawn due to the lsp's `Send` requirement deno_core::unsync::spawn(async move { let snapshot = match resolve_snapshot(&npm_api, options.snapshot).await { @@ -96,14 +99,14 @@ pub async fn create_managed_npm_resolver_for_lsp( } }; create_inner( - cache_env, - options.fs, - options.maybe_lockfile, - npm_api, + http_client, npm_cache, - options.npmrc, options.npm_install_deps_provider, + npm_api, + options.sys, options.text_only_progress_bar, + options.maybe_lockfile, + options.npmrc, options.maybe_node_modules_path, options.npm_system_info, snapshot, @@ -117,19 +120,22 @@ pub async fn create_managed_npm_resolver_for_lsp( pub async fn create_managed_npm_resolver( options: CliManagedNpmResolverCreateOptions, ) -> Result, AnyError> { - let npm_cache_env = create_cache_env(&options); - let npm_cache = create_cache(npm_cache_env.clone(), &options); - let api = create_api(npm_cache.clone(), npm_cache_env.clone(), &options); + let npm_cache = create_cache(&options); + let http_client = Arc::new(CliNpmCacheHttpClient::new( + options.http_client_provider.clone(), + options.text_only_progress_bar.clone(), + )); + let api = create_api(npm_cache.clone(), http_client.clone(), &options); let snapshot = resolve_snapshot(&api, options.snapshot).await?; Ok(create_inner( - npm_cache_env, - options.fs, - options.maybe_lockfile, - api, + http_client, npm_cache, - options.npmrc, options.npm_install_deps_provider, + api, + options.sys, options.text_only_progress_bar, + options.maybe_lockfile, + options.npmrc, options.maybe_node_modules_path, options.npm_system_info, snapshot, @@ -139,14 +145,14 @@ pub async fn create_managed_npm_resolver( #[allow(clippy::too_many_arguments)] fn create_inner( - env: Arc, - fs: Arc, - maybe_lockfile: Option>, - registry_info_provider: Arc, + http_client: Arc, npm_cache: Arc, - npm_rc: Arc, npm_install_deps_provider: Arc, + registry_info_provider: Arc, + sys: FsSysTraitsAdapter, text_only_progress_bar: crate::util::progress_bar::ProgressBar, + maybe_lockfile: Option>, + npm_rc: Arc, node_modules_dir_path: Option, npm_system_info: NpmSystemInfo, snapshot: Option, @@ -159,28 +165,29 @@ fn create_inner( )); let tarball_cache = Arc::new(CliNpmTarballCache::new( npm_cache.clone(), - env, + http_client, + sys.clone(), npm_rc.clone(), )); let fs_resolver = create_npm_fs_resolver( - fs.clone(), npm_cache.clone(), &npm_install_deps_provider, &text_only_progress_bar, resolution.clone(), + sys.clone(), tarball_cache.clone(), node_modules_dir_path, npm_system_info.clone(), lifecycle_scripts.clone(), ); Arc::new(ManagedCliNpmResolver::new( - fs, fs_resolver, maybe_lockfile, registry_info_provider, npm_cache, npm_install_deps_provider, resolution, + sys, tarball_cache, text_only_progress_bar, npm_system_info, @@ -188,36 +195,25 @@ fn create_inner( )) } -fn create_cache_env( - options: &CliManagedNpmResolverCreateOptions, -) -> Arc { - Arc::new(CliNpmCacheEnv::new( - options.fs.clone(), - options.http_client_provider.clone(), - options.text_only_progress_bar.clone(), - )) -} - fn create_cache( - env: Arc, options: &CliManagedNpmResolverCreateOptions, ) -> Arc { Arc::new(CliNpmCache::new( options.npm_cache_dir.clone(), + options.sys.clone(), NpmCacheSetting::from_cache_setting(&options.cache_setting), - env, options.npmrc.clone(), )) } fn create_api( cache: Arc, - env: Arc, + http_client: Arc, options: &CliManagedNpmResolverCreateOptions, ) -> Arc { Arc::new(CliNpmRegistryInfoProvider::new( cache, - env, + http_client, options.npmrc.clone(), )) } @@ -306,12 +302,12 @@ pub enum PackageCaching<'a> { /// An npm resolver where the resolution is managed by Deno rather than /// the user bringing their own node_modules (BYONM) on the file system. pub struct ManagedCliNpmResolver { - fs: Arc, fs_resolver: Arc, maybe_lockfile: Option>, registry_info_provider: Arc, npm_cache: Arc, npm_install_deps_provider: Arc, + sys: FsSysTraitsAdapter, resolution: Arc, tarball_cache: Arc, text_only_progress_bar: ProgressBar, @@ -331,20 +327,19 @@ impl std::fmt::Debug for ManagedCliNpmResolver { impl ManagedCliNpmResolver { #[allow(clippy::too_many_arguments)] pub fn new( - fs: Arc, fs_resolver: Arc, maybe_lockfile: Option>, registry_info_provider: Arc, npm_cache: Arc, npm_install_deps_provider: Arc, resolution: Arc, + sys: FsSysTraitsAdapter, tarball_cache: Arc, text_only_progress_bar: ProgressBar, npm_system_info: NpmSystemInfo, lifecycle_scripts: LifecycleScriptsConfig, ) -> Self { Self { - fs, fs_resolver, maybe_lockfile, registry_info_provider, @@ -352,6 +347,7 @@ impl ManagedCliNpmResolver { npm_install_deps_provider, text_only_progress_bar, resolution, + sys, tarball_cache, npm_system_info, top_level_install_flag: Default::default(), @@ -364,8 +360,7 @@ impl ManagedCliNpmResolver { pkg_id: &NpmPackageId, ) -> Result { let path = self.fs_resolver.package_folder(pkg_id)?; - let path = - canonicalize_path_maybe_not_exists_with_fs(&path, self.fs.as_ref())?; + let path = canonicalize_path_maybe_not_exists(&self.sys, &path)?; log::debug!( "Resolved package folder of {} to {}", pkg_id.as_serialized(), @@ -667,12 +662,13 @@ impl NpmPackageFolderResolver for ManagedCliNpmResolver { .fs_resolver .resolve_package_folder_from_package(name, referrer)?; let path = - canonicalize_path_maybe_not_exists_with_fs(&path, self.fs.as_ref()) - .map_err(|err| PackageFolderResolveIoError { + canonicalize_path_maybe_not_exists(&self.sys, &path).map_err(|err| { + PackageFolderResolveIoError { package_name: name.to_string(), referrer: referrer.clone(), source: err, - })?; + } + })?; log::debug!("Resolved {} from {} to {}", name, referrer, path.display()); Ok(path) } @@ -728,13 +724,12 @@ impl CliNpmResolver for ManagedCliNpmResolver { )); Arc::new(ManagedCliNpmResolver::new( - self.fs.clone(), create_npm_fs_resolver( - self.fs.clone(), self.npm_cache.clone(), &self.npm_install_deps_provider, &self.text_only_progress_bar, npm_resolution.clone(), + self.sys.clone(), self.tarball_cache.clone(), self.root_node_modules_path().map(ToOwned::to_owned), self.npm_system_info.clone(), @@ -745,6 +740,7 @@ impl CliNpmResolver for ManagedCliNpmResolver { self.npm_cache.clone(), self.npm_install_deps_provider.clone(), npm_resolution, + self.sys.clone(), self.tarball_cache.clone(), self.text_only_progress_bar.clone(), self.npm_system_info.clone(), diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs index 68e95fb39a..83081d3b8e 100644 --- a/cli/npm/managed/resolvers/common.rs +++ b/cli/npm/managed/resolvers/common.rs @@ -11,7 +11,6 @@ use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; -use super::super::PackageCaching; use async_trait::async_trait; use deno_ast::ModuleSpecifier; use deno_core::anyhow::Context; @@ -21,10 +20,12 @@ use deno_core::futures::StreamExt; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; -use deno_runtime::deno_fs::FileSystem; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use node_resolver::errors::PackageFolderResolveError; +use sys_traits::FsCanonicalize; +use super::super::PackageCaching; use crate::npm::CliNpmTarballCache; /// Part of the resolution that interacts with the file system. @@ -73,15 +74,15 @@ pub trait NpmPackageFsResolver: Send + Sync { #[derive(Debug)] pub struct RegistryReadPermissionChecker { - fs: Arc, + sys: FsSysTraitsAdapter, cache: Mutex>, registry_path: PathBuf, } impl RegistryReadPermissionChecker { - pub fn new(fs: Arc, registry_path: PathBuf) -> Self { + pub fn new(fs: FsSysTraitsAdapter, registry_path: PathBuf) -> Self { Self { - fs, + sys: fs, registry_path, cache: Default::default(), } @@ -108,7 +109,7 @@ impl RegistryReadPermissionChecker { |path: &Path| -> Result, AnyError> { match cache.get(path) { Some(canon) => Ok(Some(canon.clone())), - None => match self.fs.realpath_sync(path) { + None => match self.sys.fs_canonicalize(path) { Ok(canon) => { cache.insert(path.to_path_buf(), canon.clone()); Ok(Some(canon)) diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs index 4e79941af6..f56f012407 100644 --- a/cli/npm/managed/resolvers/global.rs +++ b/cli/npm/managed/resolvers/global.rs @@ -18,7 +18,7 @@ use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; -use deno_runtime::deno_fs::FileSystem; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageNotFoundError; @@ -47,15 +47,15 @@ pub struct GlobalNpmPackageResolver { impl GlobalNpmPackageResolver { pub fn new( cache: Arc, - fs: Arc, tarball_cache: Arc, resolution: Arc, + sys: FsSysTraitsAdapter, system_info: NpmSystemInfo, lifecycle_scripts: LifecycleScriptsConfig, ) -> Self { Self { registry_read_permission_checker: RegistryReadPermissionChecker::new( - fs, + sys, cache.root_dir_path().to_path_buf(), ), cache, diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 788d6569ae..8bbaf6c51c 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -15,11 +15,6 @@ use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; -use crate::args::LifecycleScriptsConfig; -use crate::colors; -use crate::npm::managed::PackageCaching; -use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; use async_trait::async_trait; use deno_ast::ModuleSpecifier; use deno_cache_dir::npm::mixed_case_package_name_decode; @@ -34,8 +29,10 @@ use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; +use deno_path_util::fs::atomic_write_file_with_retries; +use deno_path_util::fs::canonicalize_path_maybe_not_exists; use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder; -use deno_runtime::deno_fs; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use deno_semver::package::PackageNv; use deno_semver::StackString; @@ -45,11 +42,15 @@ use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::ReferrerNotFoundError; use serde::Deserialize; use serde::Serialize; +use sys_traits::FsMetadata; +use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; use crate::cache::CACHE_PERM; -use crate::util::fs::atomic_write_file_with_retries; -use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; +use crate::colors; +use crate::npm::managed::PackageCaching; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; use crate::util::fs::clone_dir_recursive; use crate::util::fs::symlink_dir; use crate::util::fs::LaxSingleProcessFsFlag; @@ -66,10 +67,10 @@ use super::common::RegistryReadPermissionChecker; #[derive(Debug)] pub struct LocalNpmPackageResolver { cache: Arc, - fs: Arc, npm_install_deps_provider: Arc, progress_bar: ProgressBar, resolution: Arc, + sys: FsSysTraitsAdapter, tarball_cache: Arc, root_node_modules_path: PathBuf, root_node_modules_url: Url, @@ -82,10 +83,10 @@ impl LocalNpmPackageResolver { #[allow(clippy::too_many_arguments)] pub fn new( cache: Arc, - fs: Arc, npm_install_deps_provider: Arc, progress_bar: ProgressBar, resolution: Arc, + sys: FsSysTraitsAdapter, tarball_cache: Arc, node_modules_folder: PathBuf, system_info: NpmSystemInfo, @@ -93,15 +94,15 @@ impl LocalNpmPackageResolver { ) -> Self { Self { cache, - fs: fs.clone(), npm_install_deps_provider, progress_bar, resolution, tarball_cache, registry_read_permission_checker: RegistryReadPermissionChecker::new( - fs, + sys.clone(), node_modules_folder.clone(), ), + sys, root_node_modules_url: Url::from_directory_path(&node_modules_folder) .unwrap(), root_node_modules_path: node_modules_folder, @@ -140,8 +141,7 @@ impl LocalNpmPackageResolver { }; // Canonicalize the path so it's not pointing to the symlinked directory // in `node_modules` directory of the referrer. - canonicalize_path_maybe_not_exists_with_fs(&path, self.fs.as_ref()) - .map(Some) + canonicalize_path_maybe_not_exists(&self.sys, &path).map(Some) } fn resolve_package_folder_from_specifier( @@ -210,7 +210,7 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { }; let sub_dir = join_package_name(&node_modules_folder, name); - if self.fs.is_dir_sync(&sub_dir) { + if self.sys.fs_is_dir_no_err(&sub_dir) { return Ok(sub_dir); } @@ -925,7 +925,13 @@ impl SetupCache { } bincode::serialize(&self.current).ok().and_then(|data| { - atomic_write_file_with_retries(&self.file_path, data, CACHE_PERM).ok() + atomic_write_file_with_retries( + &FsSysTraitsAdapter::new_real(), + &self.file_path, + &data, + CACHE_PERM, + ) + .ok() }); true } diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs index 736270749f..2d6d37798f 100644 --- a/cli/npm/managed/resolvers/mod.rs +++ b/cli/npm/managed/resolvers/mod.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; use std::sync::Arc; use deno_npm::NpmSystemInfo; -use deno_runtime::deno_fs::FileSystem; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; @@ -25,11 +25,11 @@ use super::resolution::NpmResolution; #[allow(clippy::too_many_arguments)] pub fn create_npm_fs_resolver( - fs: Arc, npm_cache: Arc, npm_install_deps_provider: &Arc, progress_bar: &ProgressBar, resolution: Arc, + sys: FsSysTraitsAdapter, tarball_cache: Arc, maybe_node_modules_path: Option, system_info: NpmSystemInfo, @@ -38,10 +38,10 @@ pub fn create_npm_fs_resolver( match maybe_node_modules_path { Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new( npm_cache, - fs, npm_install_deps_provider.clone(), progress_bar.clone(), resolution, + sys, tarball_cache, node_modules_folder, system_info, @@ -49,9 +49,9 @@ pub fn create_npm_fs_resolver( )), None => Arc::new(GlobalNpmPackageResolver::new( npm_cache, - fs, tarball_cache, resolution, + sys, system_info, lifecycle_scripts, )), diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 312ea2055b..6f686c3553 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -17,7 +17,7 @@ use deno_resolver::npm::ByonmInNpmPackageChecker; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::CliNpmReqResolver; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::deno_fs::FileSystem; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; @@ -30,9 +30,6 @@ use node_resolver::NpmPackageFolderResolver; use crate::file_fetcher::CliFileFetcher; use crate::http_util::HttpClientProvider; -use crate::util::fs::atomic_write_file_with_retries_and_fs; -use crate::util::fs::hard_link_dir_recursive; -use crate::util::fs::AtomicWriteFileFsAdapter; use crate::util::progress_bar::ProgressBar; pub use self::byonm::CliByonmNpmResolver; @@ -43,26 +40,26 @@ pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::ManagedCliNpmResolver; pub use self::managed::PackageCaching; -pub type CliNpmTarballCache = deno_npm_cache::TarballCache; -pub type CliNpmCache = deno_npm_cache::NpmCache; -pub type CliNpmRegistryInfoProvider = - deno_npm_cache::RegistryInfoProvider; +pub type CliNpmTarballCache = + deno_npm_cache::TarballCache; +pub type CliNpmCache = deno_npm_cache::NpmCache; +pub type CliNpmRegistryInfoProvider = deno_npm_cache::RegistryInfoProvider< + CliNpmCacheHttpClient, + FsSysTraitsAdapter, +>; #[derive(Debug)] -pub struct CliNpmCacheEnv { - fs: Arc, +pub struct CliNpmCacheHttpClient { http_client_provider: Arc, progress_bar: ProgressBar, } -impl CliNpmCacheEnv { +impl CliNpmCacheHttpClient { pub fn new( - fs: Arc, http_client_provider: Arc, progress_bar: ProgressBar, ) -> Self { Self { - fs, http_client_provider, progress_bar, } @@ -70,35 +67,7 @@ impl CliNpmCacheEnv { } #[async_trait::async_trait(?Send)] -impl deno_npm_cache::NpmCacheEnv for CliNpmCacheEnv { - fn exists(&self, path: &Path) -> bool { - self.fs.exists_sync(path) - } - - fn hard_link_dir_recursive( - &self, - from: &Path, - to: &Path, - ) -> Result<(), AnyError> { - // todo(dsherret): use self.fs here instead - hard_link_dir_recursive(from, to) - } - - fn atomic_write_file_with_retries( - &self, - file_path: &Path, - data: &[u8], - ) -> std::io::Result<()> { - atomic_write_file_with_retries_and_fs( - &AtomicWriteFileFsAdapter { - fs: self.fs.as_ref(), - write_mode: crate::cache::CACHE_PERM, - }, - file_path, - data, - ) - } - +impl deno_npm_cache::NpmCacheHttpClient for CliNpmCacheHttpClient { async fn download_with_retries_on_any_tokio_runtime( &self, url: Url, diff --git a/cli/resolver.rs b/cli/resolver.rs index f5c3f68f36..0a4fd78686 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -1,5 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + use async_trait::async_trait; use dashmap::DashMap; use dashmap::DashSet; @@ -20,16 +25,14 @@ use deno_npm::resolution::NpmResolutionError; use deno_resolver::sloppy_imports::SloppyImportsResolver; use deno_runtime::colors; use deno_runtime::deno_fs; -use deno_runtime::deno_fs::FileSystem; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::is_builtin_node_module; -use deno_runtime::deno_node::DenoFsNodeResolverEnv; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_semver::package::PackageReq; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; +use sys_traits::FsMetadata; +use sys_traits::FsMetadataValue; use thiserror::Error; use crate::args::NpmCachingStrategy; @@ -40,18 +43,19 @@ use crate::npm::InnerCliNpmResolverRef; use crate::util::sync::AtomicFlag; use crate::util::text_encoding::from_utf8_lossy_cow; -pub type CjsTracker = deno_resolver::cjs::CjsTracker; -pub type IsCjsResolver = - deno_resolver::cjs::IsCjsResolver; +pub type CjsTracker = deno_resolver::cjs::CjsTracker; +pub type IsCjsResolver = deno_resolver::cjs::IsCjsResolver; pub type CliSloppyImportsResolver = SloppyImportsResolver; pub type CliDenoResolver = deno_resolver::DenoResolver< - CliDenoResolverFs, - DenoFsNodeResolverEnv, + RealIsBuiltInNodeModuleChecker, SloppyImportsCachedFs, + FsSysTraitsAdapter, +>; +pub type CliNpmReqResolver = deno_resolver::npm::NpmReqResolver< + RealIsBuiltInNodeModuleChecker, + FsSysTraitsAdapter, >; -pub type CliNpmReqResolver = - deno_resolver::npm::NpmReqResolver; pub struct ModuleCodeStringSource { pub code: ModuleSourceCode, @@ -59,53 +63,6 @@ pub struct ModuleCodeStringSource { pub media_type: MediaType, } -#[derive(Debug, Clone)] -pub struct CliDenoResolverFs(pub Arc); - -impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs { - fn read_to_string_lossy( - &self, - path: &Path, - ) -> std::io::Result> { - self - .0 - .read_text_file_lossy_sync(path, None) - .map_err(|e| e.into_io_error()) - } - - fn realpath_sync(&self, path: &Path) -> std::io::Result { - self.0.realpath_sync(path).map_err(|e| e.into_io_error()) - } - - fn exists_sync(&self, path: &Path) -> bool { - self.0.exists_sync(path) - } - - fn is_dir_sync(&self, path: &Path) -> bool { - self.0.is_dir_sync(path) - } - - fn read_dir_sync( - &self, - dir_path: &Path, - ) -> std::io::Result> { - self - .0 - .read_dir_sync(dir_path) - .map(|entries| { - entries - .into_iter() - .map(|e| deno_resolver::fs::DirEntry { - name: e.name, - is_file: e.is_file, - is_directory: e.is_directory, - }) - .collect::>() - }) - .map_err(|err| err.into_io_error()) - } -} - #[derive(Debug, Error)] #[error("{media_type} files are not supported in npm packages: {specifier}")] pub struct NotSupportedKindInNpmError { @@ -440,7 +397,7 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { #[derive(Debug)] pub struct SloppyImportsCachedFs { - fs: Arc, + sys: FsSysTraitsAdapter, cache: Option< DashMap< PathBuf, @@ -450,15 +407,18 @@ pub struct SloppyImportsCachedFs { } impl SloppyImportsCachedFs { - pub fn new(fs: Arc) -> Self { + pub fn new(sys: FsSysTraitsAdapter) -> Self { Self { - fs, + sys, cache: Some(Default::default()), } } - pub fn new_without_stat_cache(fs: Arc) -> Self { - Self { fs, cache: None } + pub fn new_without_stat_cache(fs: FsSysTraitsAdapter) -> Self { + Self { + sys: fs, + cache: None, + } } } @@ -475,10 +435,10 @@ impl deno_resolver::sloppy_imports::SloppyImportResolverFs } } - let entry = self.fs.stat_sync(path).ok().and_then(|stat| { - if stat.is_file { + let entry = self.sys.fs_metadata(path).ok().and_then(|stat| { + if stat.file_type().is_file() { Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::File) - } else if stat.is_directory { + } else if stat.file_type().is_dir() { Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::Dir) } else { None diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 3707543eb0..91187c48d1 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -37,7 +37,6 @@ use deno_core::futures::AsyncReadExt; use deno_core::futures::AsyncSeekExt; use deno_core::serde_json; use deno_core::url::Url; -use deno_graph::source::RealFileSystem; use deno_graph::ModuleGraph; use deno_npm::resolution::SerializedNpmResolutionSnapshot; use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage; diff --git a/cli/standalone/code_cache.rs b/cli/standalone/code_cache.rs index 9580b9b44e..a44c920328 100644 --- a/cli/standalone/code_cache.rs +++ b/cli/standalone/code_cache.rs @@ -15,11 +15,12 @@ use deno_core::anyhow::bail; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::unsync::sync::AtomicFlag; +use deno_path_util::get_atomic_path; use deno_runtime::code_cache::CodeCache; use deno_runtime::code_cache::CodeCacheType; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use crate::cache::FastInsecureHasher; -use crate::util::path::get_atomic_file_path; use crate::worker::CliCodeCache; enum CodeCacheStrategy { @@ -189,7 +190,8 @@ impl FirstRunCodeCacheStrategy { cache_data: &HashMap, ) { let count = cache_data.len(); - let temp_file = get_atomic_file_path(&self.file_path); + let temp_file = + get_atomic_path(&FsSysTraitsAdapter::new_real(), &self.file_path); match serialize(&temp_file, self.cache_key, cache_data) { Ok(()) => { if let Err(err) = std::fs::rename(&temp_file, &self.file_path) { diff --git a/cli/standalone/file_system.rs b/cli/standalone/file_system.rs index 48dc907570..4b1024db6a 100644 --- a/cli/standalone/file_system.rs +++ b/cli/standalone/file_system.rs @@ -9,6 +9,7 @@ use deno_runtime::deno_fs::AccessCheckCb; use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FsDirEntry; use deno_runtime::deno_fs::FsFileType; +use deno_runtime::deno_fs::FsStatSlim; use deno_runtime::deno_fs::OpenOptions; use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_io::fs::File; diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 75247a98c0..0ce25ff62f 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -36,10 +36,12 @@ use deno_package_json::PackageJsonDepValue; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::NpmReqResolverOptions; use deno_runtime::deno_fs; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::NodeRequireLoader; use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::PackageJsonResolver; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_tls::rustls::RootCertStore; @@ -69,11 +71,9 @@ use crate::args::CaData; use crate::args::NpmInstallDepsProvider; use crate::args::StorageKeyResolver; use crate::cache::Caches; -use crate::cache::DenoCacheEnvFsAdapter; use crate::cache::DenoDirProvider; use crate::cache::FastInsecureHasher; use crate::cache::NodeAnalysisCache; -use crate::cache::RealDenoCacheEnv; use crate::http_util::HttpClientProvider; use crate::node::CliCjsCodeAnalyzer; use crate::node::CliNodeCodeTranslator; @@ -87,7 +87,6 @@ use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CreateInNpmPkgCheckerOptions; use crate::resolver::CjsTracker; -use crate::resolver::CliDenoResolverFs; use crate::resolver::CliNpmReqResolver; use crate::resolver::NpmModuleLoader; use crate::util::progress_bar::ProgressBar; @@ -656,9 +655,8 @@ pub async fn run(data: StandaloneData) -> Result { let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap(); let npm_global_cache_dir = root_path.join(".deno_compile_node_modules"); let cache_setting = CacheSetting::Only; - let pkg_json_resolver = Arc::new(PackageJsonResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), - )); + let sys = FsSysTraitsAdapter(fs.clone()); + let pkg_json_resolver = Arc::new(PackageJsonResolver::new(sys.clone())); let (in_npm_pkg_checker, npm_resolver) = match metadata.node_modules { Some(binary::NodeModules::Managed { node_modules_dir }) => { // create an npmrc that uses the fake npm_registry_url to resolve packages @@ -671,7 +669,7 @@ pub async fn run(data: StandaloneData) -> Result { registry_configs: Default::default(), }); let npm_cache_dir = Arc::new(NpmCacheDir::new( - &DenoCacheEnvFsAdapter(fs.as_ref()), + &sys, npm_global_cache_dir, npmrc.get_all_known_registries_urls(), )); @@ -692,17 +690,17 @@ pub async fn run(data: StandaloneData) -> Result { snapshot, )), maybe_lockfile: None, - fs: fs.clone(), http_client_provider: http_client_provider.clone(), npm_cache_dir, - cache_setting, - text_only_progress_bar: progress_bar, - maybe_node_modules_path, - npm_system_info: Default::default(), npm_install_deps_provider: Arc::new( // this is only used for installing packages, which isn't necessary with deno compile NpmInstallDepsProvider::empty(), ), + sys: sys.clone(), + text_only_progress_bar: progress_bar, + cache_setting, + maybe_node_modules_path, + npm_system_info: Default::default(), npmrc, lifecycle_scripts: Default::default(), }, @@ -719,7 +717,7 @@ pub async fn run(data: StandaloneData) -> Result { create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Byonm); let npm_resolver = create_cli_npm_resolver( CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { - fs: CliDenoResolverFs(fs.clone()), + sys: sys.clone(), pkg_json_resolver: pkg_json_resolver.clone(), root_node_modules_dir, }), @@ -732,7 +730,7 @@ pub async fn run(data: StandaloneData) -> Result { // so no need to create actual `.npmrc` configuration. let npmrc = create_default_npmrc(); let npm_cache_dir = Arc::new(NpmCacheDir::new( - &DenoCacheEnvFsAdapter(fs.as_ref()), + &sys, npm_global_cache_dir, npmrc.get_all_known_registries_urls(), )); @@ -747,18 +745,18 @@ pub async fn run(data: StandaloneData) -> Result { create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { snapshot: CliNpmResolverManagedSnapshotOption::Specified(None), - maybe_lockfile: None, - fs: fs.clone(), http_client_provider: http_client_provider.clone(), - npm_cache_dir, - cache_setting, - text_only_progress_bar: progress_bar, - maybe_node_modules_path: None, - npm_system_info: Default::default(), npm_install_deps_provider: Arc::new( // this is only used for installing packages, which isn't necessary with deno compile NpmInstallDepsProvider::empty(), ), + sys: sys.clone(), + cache_setting, + text_only_progress_bar: progress_bar, + npm_cache_dir, + maybe_lockfile: None, + maybe_node_modules_path: None, + npm_system_info: Default::default(), npmrc: create_default_npmrc(), lifecycle_scripts: Default::default(), }, @@ -770,10 +768,11 @@ pub async fn run(data: StandaloneData) -> Result { let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some(); let node_resolver = Arc::new(NodeResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), in_npm_pkg_checker.clone(), + RealIsBuiltInNodeModuleChecker, npm_resolver.clone().into_npm_pkg_folder_resolver(), pkg_json_resolver.clone(), + sys.clone(), )); let cjs_tracker = Arc::new(CjsTracker::new( in_npm_pkg_checker.clone(), @@ -791,7 +790,7 @@ pub async fn run(data: StandaloneData) -> Result { let npm_req_resolver = Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), - fs: CliDenoResolverFs(fs.clone()), + sys: sys.clone(), in_npm_pkg_checker: in_npm_pkg_checker.clone(), node_resolver: node_resolver.clone(), npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), @@ -804,11 +803,11 @@ pub async fn run(data: StandaloneData) -> Result { ); let node_code_translator = Arc::new(NodeCodeTranslator::new( cjs_esm_code_analyzer, - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), in_npm_pkg_checker, node_resolver.clone(), npm_resolver.clone().into_npm_pkg_folder_resolver(), pkg_json_resolver.clone(), + sys, )); let workspace_resolver = { let import_map = match metadata.workspace_resolver.import_map { diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 2b36a8ddd8..4736bdab41 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -26,6 +26,7 @@ use deno_core::serde_json; use deno_core::sourcemap::SourceMap; use deno_core::url::Url; use deno_core::LocalInspectorSession; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use node_resolver::InNpmPackageChecker; use regex::Regex; use std::fs; @@ -428,7 +429,7 @@ fn collect_coverages( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, file_patterns)?; + .collect_file_patterns(&FsSysTraitsAdapter::new_real(), file_patterns)?; let coverage_patterns = FilePatterns { base: initial_cwd.to_path_buf(), diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 90d54f6cd9..c33b988de0 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -28,6 +28,7 @@ use deno_graph::EsParser; use deno_graph::GraphKind; use deno_graph::ModuleAnalyzer; use deno_graph::ModuleSpecifier; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use doc::html::ShortPath; use doc::DocDiagnostic; use indexmap::IndexMap; @@ -114,7 +115,7 @@ pub async fn doc( } DocSourceFileFlag::Paths(ref source_files) => { let module_graph_creator = factory.module_graph_creator().await?; - let fs = factory.fs(); + let fs = FsSysTraitsAdapter(factory.fs().clone()); let module_specifiers = collect_specifiers( FilePatterns { @@ -141,7 +142,7 @@ pub async fn doc( graph_exit_integrity_errors(&graph); let errors = graph_walk_errors( &graph, - fs, + &fs, &module_specifiers, GraphWalkErrorsOptions { check_js: false, diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index e29627345c..55046155c0 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -34,6 +34,7 @@ use deno_core::futures; use deno_core::parking_lot::Mutex; use deno_core::unsync::spawn_blocking; use deno_core::url::Url; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use log::debug; use log::info; use log::warn; @@ -230,7 +231,7 @@ fn collect_fmt_files( .ignore_node_modules() .use_gitignore() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files) + .collect_file_patterns(&FsSysTraitsAdapter::new_real(), files) } /// Formats markdown (using ) and its code blocks diff --git a/cli/tools/lint/linter.rs b/cli/tools/lint/linter.rs index 2c2bc43acb..a10ad6479e 100644 --- a/cli/tools/lint/linter.rs +++ b/cli/tools/lint/linter.rs @@ -15,8 +15,9 @@ use deno_lint::linter::LintConfig as DenoLintConfig; use deno_lint::linter::LintFileOptions; use deno_lint::linter::Linter as DenoLintLinter; use deno_lint::linter::LinterOptions; +use deno_path_util::fs::atomic_write_file_with_retries; +use deno_runtime::deno_fs::FsSysTraitsAdapter; -use crate::util::fs::atomic_write_file_with_retries; use crate::util::fs::specifier_from_file_path; use super::rules::FileOrPackageLintRule; @@ -176,8 +177,9 @@ impl CliLinter { if fix_iterations > 0 { // everything looks good and the file still parses, so write it out atomic_write_file_with_retries( + &FsSysTraitsAdapter::new_real(), file_path, - source.text().as_ref(), + source.text().as_bytes(), crate::cache::CACHE_PERM, ) .context("Failed writing fix to file.")?; diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index 50fc16799a..4071f30e7e 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -21,6 +21,7 @@ use deno_core::unsync::future::SharedLocal; use deno_graph::ModuleGraph; use deno_lint::diagnostic::LintDiagnostic; use deno_lint::linter::LintConfig as DenoLintConfig; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use log::debug; use reporters::create_reporter; use reporters::LintReporter; @@ -452,7 +453,7 @@ fn collect_lint_files( .ignore_node_modules() .use_gitignore() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files) + .collect_file_patterns(&FsSysTraitsAdapter::new_real(), files) } #[allow(clippy::print_stdout)] diff --git a/cli/tools/registry/paths.rs b/cli/tools/registry/paths.rs index 8b6c05fc01..b607c0924c 100644 --- a/cli/tools/registry/paths.rs +++ b/cli/tools/registry/paths.rs @@ -11,6 +11,7 @@ use deno_ast::ModuleSpecifier; use deno_config::glob::FileCollector; use deno_config::glob::FilePatterns; use deno_core::error::AnyError; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use thiserror::Error; use crate::args::CliOptions; @@ -323,11 +324,11 @@ fn collect_paths( file_patterns: FilePatterns, ) -> Result, AnyError> { FileCollector::new(|e| { - if !e.metadata.is_file { + if !e.metadata.file_type().is_file() { if let Ok(specifier) = ModuleSpecifier::from_file_path(e.path) { diagnostics_collector.push(PublishDiagnostic::UnsupportedFileType { specifier, - kind: if e.metadata.is_symlink { + kind: if e.metadata.file_type().is_symlink() { "symlink".to_string() } else { "Unknown".to_string() @@ -345,5 +346,5 @@ fn collect_paths( .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) .use_gitignore() - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, file_patterns) + .collect_file_patterns(&FsSysTraitsAdapter::new_real(), file_patterns) } diff --git a/cli/tools/registry/unfurl.rs b/cli/tools/registry/unfurl.rs index bf6aaaf50d..ca50775717 100644 --- a/cli/tools/registry/unfurl.rs +++ b/cli/tools/registry/unfurl.rs @@ -663,6 +663,7 @@ mod tests { use deno_config::workspace::ResolverWorkspaceJsrPackage; use deno_core::serde_json::json; use deno_core::url::Url; + use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_node::PackageJson; use deno_semver::Version; @@ -722,10 +723,9 @@ mod tests { vec![Arc::new(package_json)], deno_config::workspace::PackageJsonDepResolution::Enabled, ); - let fs = Arc::new(RealFs); let unfurler = SpecifierUnfurler::new( Some(Arc::new(CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new(fs), + SloppyImportsCachedFs::new(FsSysTraitsAdapter::new_real()), ))), Arc::new(workspace_resolver), true, @@ -863,7 +863,7 @@ const warn2 = await import(`${expr}`); ], deno_config::workspace::PackageJsonDepResolution::Enabled, ); - let fs = Arc::new(RealFs); + let fs = FsSysTraitsAdapter(Arc::new(RealFs)); let unfurler = SpecifierUnfurler::new( Some(Arc::new(CliSloppyImportsResolver::new( SloppyImportsCachedFs::new(fs), diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 4c18d1a2b0..26cf385734 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -4,7 +4,6 @@ use crate::args::TsConfig; use crate::args::TypeCheckMode; use crate::cache::FastInsecureHasher; use crate::cache::ModuleInfoCache; -use crate::node; use crate::npm::CliNpmResolver; use crate::resolver::CjsTracker; use crate::util::checksum; @@ -35,12 +34,13 @@ use deno_graph::Module; use deno_graph::ModuleGraph; use deno_graph::ResolutionResolved; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::deno_fs; +use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodeResolver; use deno_semver::npm::NpmPackageReqReference; use node_resolver::errors::NodeJsErrorCode; use node_resolver::errors::NodeJsErrorCoded; use node_resolver::errors::PackageSubpathResolveError; +use node_resolver::resolve_specifier_into_node_modules; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use once_cell::sync::Lazy; @@ -660,9 +660,9 @@ fn op_load_inner( None } else { // means it's Deno code importing an npm module - let specifier = node::resolve_specifier_into_node_modules( + let specifier = resolve_specifier_into_node_modules( + &FsSysTraitsAdapter::new_real(), &module.specifier, - &deno_fs::RealFs, ); Some(Cow::Owned(load_from_node_modules( &specifier, @@ -924,9 +924,9 @@ fn resolve_graph_specifier_types( Some(Module::External(module)) => { // we currently only use "External" for when the module is in an npm package Ok(state.maybe_npm.as_ref().map(|_| { - let specifier = node::resolve_specifier_into_node_modules( + let specifier = resolve_specifier_into_node_modules( + &FsSysTraitsAdapter::new_real(), &module.specifier, - &deno_fs::RealFs, ); into_specifier_and_media_type(Some(specifier)) })) diff --git a/cli/util/fs.rs b/cli/util/fs.rs index ba84a0e8f3..58b5fc72c6 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -1,9 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use std::fs::OpenOptions; use std::io::Error; use std::io::ErrorKind; -use std::io::Write; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -19,185 +17,12 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::unsync::spawn_blocking; use deno_core::ModuleSpecifier; -use deno_runtime::deno_fs::FileSystem; +use deno_runtime::deno_fs::FsSysTraitsAdapter; -use crate::util::path::get_atomic_file_path; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressMessagePrompt; -/// Writes the file to the file system at a temporary path, then -/// renames it to the destination in a single sys call in order -/// to never leave the file system in a corrupted state. -/// -/// This also handles creating the directory if a NotFound error -/// occurs. -pub fn atomic_write_file_with_retries>( - file_path: &Path, - data: T, - mode: u32, -) -> std::io::Result<()> { - struct RealAtomicWriteFileFs { - mode: u32, - } - - impl AtomicWriteFileFs for RealAtomicWriteFileFs { - fn write_file(&self, path: &Path, bytes: &[u8]) -> std::io::Result<()> { - write_file(path, bytes, self.mode) - } - fn rename_file(&self, from: &Path, to: &Path) -> std::io::Result<()> { - std::fs::rename(from, to) - } - fn remove_file(&self, path: &Path) -> std::io::Result<()> { - std::fs::remove_file(path) - } - fn create_dir_all(&self, dir_path: &Path) -> std::io::Result<()> { - std::fs::create_dir_all(dir_path) - } - fn path_exists(&self, path: &Path) -> bool { - path.exists() - } - } - - atomic_write_file_with_retries_and_fs( - &RealAtomicWriteFileFs { mode }, - file_path, - data.as_ref(), - ) -} - -pub trait AtomicWriteFileFs { - fn write_file(&self, path: &Path, bytes: &[u8]) -> std::io::Result<()>; - fn rename_file(&self, from: &Path, to: &Path) -> std::io::Result<()>; - fn remove_file(&self, path: &Path) -> std::io::Result<()>; - fn create_dir_all(&self, dir_path: &Path) -> std::io::Result<()>; - fn path_exists(&self, path: &Path) -> bool; -} - -pub struct AtomicWriteFileFsAdapter<'a> { - pub fs: &'a dyn FileSystem, - pub write_mode: u32, -} - -impl<'a> AtomicWriteFileFs for AtomicWriteFileFsAdapter<'a> { - fn write_file(&self, path: &Path, bytes: &[u8]) -> std::io::Result<()> { - self - .fs - .write_file_sync( - path, - deno_runtime::deno_fs::OpenOptions::write( - true, - false, - false, - Some(self.write_mode), - ), - None, - bytes, - ) - .map_err(|e| e.into_io_error()) - } - - fn rename_file(&self, from: &Path, to: &Path) -> std::io::Result<()> { - self.fs.rename_sync(from, to).map_err(|e| e.into_io_error()) - } - - fn remove_file(&self, path: &Path) -> std::io::Result<()> { - self - .fs - .remove_sync(path, false) - .map_err(|e| e.into_io_error()) - } - - fn create_dir_all(&self, dir_path: &Path) -> std::io::Result<()> { - self - .fs - .mkdir_sync(dir_path, /* recursive */ true, None) - .map_err(|e| e.into_io_error()) - } - - fn path_exists(&self, path: &Path) -> bool { - self.fs.exists_sync(path) - } -} - -pub fn atomic_write_file_with_retries_and_fs>( - fs: &impl AtomicWriteFileFs, - file_path: &Path, - data: T, -) -> std::io::Result<()> { - let mut count = 0; - loop { - match atomic_write_file(fs, file_path, data.as_ref()) { - Ok(()) => return Ok(()), - Err(err) => { - if count >= 5 { - // too many retries, return the error - return Err(err); - } - count += 1; - let sleep_ms = std::cmp::min(50, 10 * count); - std::thread::sleep(std::time::Duration::from_millis(sleep_ms)); - } - } - } -} - -/// Writes the file to the file system at a temporary path, then -/// renames it to the destination in a single sys call in order -/// to never leave the file system in a corrupted state. -/// -/// This also handles creating the directory if a NotFound error -/// occurs. -fn atomic_write_file( - fs: &impl AtomicWriteFileFs, - file_path: &Path, - data: &[u8], -) -> std::io::Result<()> { - fn atomic_write_file_raw( - fs: &impl AtomicWriteFileFs, - temp_file_path: &Path, - file_path: &Path, - data: &[u8], - ) -> std::io::Result<()> { - fs.write_file(temp_file_path, data)?; - fs.rename_file(temp_file_path, file_path) - .inspect_err(|_err| { - // clean up the created temp file on error - let _ = fs.remove_file(temp_file_path); - }) - } - - let temp_file_path = get_atomic_file_path(file_path); - - if let Err(write_err) = - atomic_write_file_raw(fs, &temp_file_path, file_path, data) - { - if write_err.kind() == ErrorKind::NotFound { - let parent_dir_path = file_path.parent().unwrap(); - match fs.create_dir_all(parent_dir_path) { - Ok(()) => { - return atomic_write_file_raw(fs, &temp_file_path, file_path, data) - .map_err(|err| add_file_context_to_err(file_path, err)); - } - Err(create_err) => { - if !fs.path_exists(parent_dir_path) { - return Err(Error::new( - create_err.kind(), - format!( - "{:#} (for '{}')\nCheck the permission of the directory.", - create_err, - parent_dir_path.display() - ), - )); - } - } - } - } - return Err(add_file_context_to_err(file_path, write_err)); - } - Ok(()) -} - /// Creates a std::fs::File handling if the parent does not exist. pub fn create_file(file_path: &Path) -> std::io::Result { match std::fs::File::create(file_path) { @@ -236,45 +61,6 @@ fn add_file_context_to_err(file_path: &Path, err: Error) -> Error { ) } -pub fn write_file>( - filename: &Path, - data: T, - mode: u32, -) -> std::io::Result<()> { - write_file_2(filename, data, true, mode, true, false) -} - -pub fn write_file_2>( - filename: &Path, - data: T, - update_mode: bool, - mode: u32, - is_create: bool, - is_append: bool, -) -> std::io::Result<()> { - let mut file = OpenOptions::new() - .read(false) - .write(true) - .append(is_append) - .truncate(!is_append) - .create(is_create) - .open(filename)?; - - if update_mode { - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let mode = mode & 0o777; - let permissions = PermissionsExt::from_mode(mode); - file.set_permissions(permissions)?; - } - #[cfg(not(unix))] - let _ = mode; - } - - file.write_all(data.as_ref()) -} - /// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows. pub fn canonicalize_path(path: &Path) -> Result { Ok(deno_path_util::strip_unc_prefix(path.canonicalize()?)) @@ -289,16 +75,10 @@ pub fn canonicalize_path(path: &Path) -> Result { pub fn canonicalize_path_maybe_not_exists( path: &Path, ) -> Result { - deno_path_util::canonicalize_path_maybe_not_exists(path, &canonicalize_path) -} - -pub fn canonicalize_path_maybe_not_exists_with_fs( - path: &Path, - fs: &dyn FileSystem, -) -> Result { - deno_path_util::canonicalize_path_maybe_not_exists(path, &|path| { - fs.realpath_sync(path).map_err(|err| err.into_io_error()) - }) + deno_path_util::fs::canonicalize_path_maybe_not_exists( + &FsSysTraitsAdapter::new_real(), + path, + ) } /// Collects module specifiers that satisfy the given predicate as a file path, by recursively walking `include`. @@ -346,7 +126,7 @@ pub fn collect_specifiers( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(vendor_folder) - .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files)?; + .collect_file_patterns(&FsSysTraitsAdapter::new_real(), files)?; let mut collected_files_as_urls = collected_files .iter() .map(|f| specifier_from_file_path(f).unwrap()) @@ -418,7 +198,13 @@ mod clone_dir_imp { from: &std::path::Path, to: &std::path::Path, ) -> Result<(), deno_core::error::AnyError> { - if let Err(e) = super::hard_link_dir_recursive(from, to) { + use deno_runtime::deno_fs::FsSysTraitsAdapter; + + if let Err(e) = deno_npm_cache::hard_link_dir_recursive( + &FsSysTraitsAdapter::new_real(), + from, + to, + ) { log::debug!("Failed to hard link dir {:?} to {:?}: {}", from, to, e); super::copy_dir_recursive(from, to)?; } @@ -465,84 +251,6 @@ pub fn copy_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { Ok(()) } -/// Hardlinks the files in one directory to another directory. -/// -/// Note: Does not handle symlinks. -pub fn hard_link_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { - std::fs::create_dir_all(to) - .with_context(|| format!("Creating {}", to.display()))?; - let read_dir = std::fs::read_dir(from) - .with_context(|| format!("Reading {}", from.display()))?; - - for entry in read_dir { - let entry = entry?; - let file_type = entry.file_type()?; - let new_from = from.join(entry.file_name()); - let new_to = to.join(entry.file_name()); - - if file_type.is_dir() { - hard_link_dir_recursive(&new_from, &new_to).with_context(|| { - format!("Dir {} to {}", new_from.display(), new_to.display()) - })?; - } else if file_type.is_file() { - // note: chance for race conditions here between attempting to create, - // then removing, then attempting to create. There doesn't seem to be - // a way to hard link with overwriting in Rust, but maybe there is some - // way with platform specific code. The workaround here is to handle - // scenarios where something else might create or remove files. - if let Err(err) = std::fs::hard_link(&new_from, &new_to) { - if err.kind() == ErrorKind::AlreadyExists { - if let Err(err) = std::fs::remove_file(&new_to) { - if err.kind() == ErrorKind::NotFound { - // Assume another process/thread created this hard link to the file we are wanting - // to remove then sleep a little bit to let the other process/thread move ahead - // faster to reduce contention. - std::thread::sleep(Duration::from_millis(10)); - } else { - return Err(err).with_context(|| { - format!( - "Removing file to hard link {} to {}", - new_from.display(), - new_to.display() - ) - }); - } - } - - // Always attempt to recreate the hardlink. In contention scenarios, the other process - // might have been killed or exited after removing the file, but before creating the hardlink - if let Err(err) = std::fs::hard_link(&new_from, &new_to) { - // Assume another process/thread created this hard link to the file we are wanting - // to now create then sleep a little bit to let the other process/thread move ahead - // faster to reduce contention. - if err.kind() == ErrorKind::AlreadyExists { - std::thread::sleep(Duration::from_millis(10)); - } else { - return Err(err).with_context(|| { - format!( - "Hard linking {} to {}", - new_from.display(), - new_to.display() - ) - }); - } - } - } else { - return Err(err).with_context(|| { - format!( - "Hard linking {} to {}", - new_from.display(), - new_to.display() - ) - }); - } - } - } - } - - Ok(()) -} - pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> { let err_mapper = |err: Error, kind: Option| { Error::new( diff --git a/cli/util/path.rs b/cli/util/path.rs index de72843406..539e1235a8 100644 --- a/cli/util/path.rs +++ b/cli/util/path.rs @@ -1,7 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use std::borrow::Cow; -use std::fmt::Write; use std::path::Path; use std::path::PathBuf; @@ -52,19 +51,6 @@ pub fn get_extension(file_path: &Path) -> Option { .map(|e| e.to_lowercase()); } -pub fn get_atomic_file_path(file_path: &Path) -> PathBuf { - let rand = gen_rand_path_component(); - let extension = format!("{rand}.tmp"); - file_path.with_extension(extension) -} - -fn gen_rand_path_component() -> String { - (0..4).fold(String::with_capacity(8), |mut output, _| { - write!(&mut output, "{:02x}", rand::random::()).unwrap(); - output - }) -} - /// TypeScript figures out the type of file based on the extension, but we take /// other factors into account like the file headers. The hack here is to map the /// specifier passed to TypeScript to a new specifier with the file extension. diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index 1d0b623718..8692f04a73 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -25,10 +25,12 @@ deno_io.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true filetime.workspace = true +getrandom = "0.2" libc.workspace = true rand.workspace = true rayon = "1.8.0" serde.workspace = true +sys_traits.workspace = true thiserror.workspace = true [target.'cfg(unix)'.dependencies] diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs index 28a49c5d9b..304c263614 100644 --- a/ext/fs/interface.rs +++ b/ext/fs/interface.rs @@ -5,6 +5,8 @@ use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; +use std::time::Duration; +use std::time::SystemTime; use serde::Deserialize; use serde::Serialize; @@ -12,6 +14,8 @@ use serde::Serialize; use deno_io::fs::File; use deno_io::fs::FsResult; use deno_io::fs::FsStat; +use sys_traits::FsFile; +use sys_traits::FsFileSetPermissions; use crate::sync::MaybeSend; use crate::sync::MaybeSync; @@ -71,7 +75,7 @@ pub enum FsFileType { } /// WARNING: This is part of the public JS Deno API. -#[derive(Serialize)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct FsDirEntry { pub name: String, @@ -100,6 +104,56 @@ impl AccessCheckFn for T where { } +#[derive(Debug)] +pub struct FsStatSlim { + file_type: sys_traits::FileType, + modified: Result, +} + +impl FsStatSlim { + pub fn from_std(metadata: &std::fs::Metadata) -> Self { + Self { + file_type: metadata.file_type().into(), + modified: metadata.modified(), + } + } + + pub fn from_deno_fs_stat(data: &FsStat) -> Self { + FsStatSlim { + file_type: if data.is_file { + sys_traits::FileType::File + } else if data.is_directory { + sys_traits::FileType::Dir + } else if data.is_symlink { + sys_traits::FileType::Symlink + } else { + sys_traits::FileType::Unknown + }, + modified: data + .mtime + .map(|ms| SystemTime::UNIX_EPOCH + Duration::from_millis(ms)) + .ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidData, "No mtime") + }), + } + } +} + +impl sys_traits::FsMetadataValue for FsStatSlim { + #[inline] + fn file_type(&self) -> sys_traits::FileType { + self.file_type + } + + fn modified(&self) -> Result { + self + .modified + .as_ref() + .copied() + .map_err(|err| std::io::Error::new(err.kind(), err.to_string())) + } +} + pub type AccessCheckCb<'a> = &'a mut (dyn AccessCheckFn + 'a); #[async_trait::async_trait(?Send)] @@ -361,3 +415,289 @@ fn string_from_utf8_lossy(buf: Vec) -> String { Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(buf) }, } } + +// todo(dsherret): this is temporary. Instead of using the `FileSystem` trait implementation +// in the CLI, the CLI should instead create it's own file system using `sys_traits` traits +// then that can implement the `FileSystem` trait. Then this `FileSystem` trait can stay here +// for use only for `ext/fs` and not the entire CLI. +#[derive(Debug, Clone)] +pub struct FsSysTraitsAdapter(pub FileSystemRc); + +impl FsSysTraitsAdapter { + pub fn new_real() -> Self { + Self(crate::sync::new_rc(crate::RealFs)) + } +} + +impl sys_traits::BaseFsHardLink for FsSysTraitsAdapter { + #[inline] + fn base_fs_hard_link(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + self + .0 + .link_sync(src, dst) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsRead for FsSysTraitsAdapter { + #[inline] + fn base_fs_read(&self, path: &Path) -> std::io::Result> { + self + .0 + .read_file_sync(path, None) + .map_err(|err| err.into_io_error()) + } +} + +#[derive(Debug)] +pub struct FsSysTraitsAdapterReadDirEntry { + path: PathBuf, + entry: FsDirEntry, +} + +impl sys_traits::FsDirEntry for FsSysTraitsAdapterReadDirEntry { + type Metadata = FsStatSlim; + + fn file_name(&self) -> Cow { + Cow::Borrowed(self.entry.name.as_ref()) + } + + fn file_type(&self) -> std::io::Result { + if self.entry.is_file { + Ok(sys_traits::FileType::File) + } else if self.entry.is_directory { + Ok(sys_traits::FileType::Dir) + } else if self.entry.is_symlink { + Ok(sys_traits::FileType::Symlink) + } else { + Ok(sys_traits::FileType::Unknown) + } + } + + fn metadata(&self) -> std::io::Result { + Ok(FsStatSlim { + file_type: self.file_type().unwrap(), + modified: Err(std::io::Error::new( + std::io::ErrorKind::Other, + "not supported", + )), + }) + } + + fn path(&self) -> Cow { + Cow::Borrowed(&self.path) + } +} + +impl sys_traits::BaseFsReadDir for FsSysTraitsAdapter { + type ReadDirEntry = FsSysTraitsAdapterReadDirEntry; + + fn base_fs_read_dir( + &self, + path: &Path, + ) -> std::io::Result< + Box>>, + > { + // todo(dsherret): needs to actually be iterable and not allocate a vector + let entries = self + .0 + .read_dir_sync(path) + .map_err(|err| err.into_io_error())?; + let parent_dir = path.to_path_buf(); + Ok(Box::new(entries.into_iter().map(move |entry| { + Ok(FsSysTraitsAdapterReadDirEntry { + path: parent_dir.join(&entry.name), + entry, + }) + }))) + } +} + +impl sys_traits::BaseFsCanonicalize for FsSysTraitsAdapter { + #[inline] + fn base_fs_canonicalize(&self, path: &Path) -> std::io::Result { + self + .0 + .realpath_sync(path) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsMetadata for FsSysTraitsAdapter { + type Metadata = FsStatSlim; + + #[inline] + fn base_fs_metadata(&self, path: &Path) -> std::io::Result { + self + .0 + .stat_sync(path) + .map(|data| FsStatSlim::from_deno_fs_stat(&data)) + .map_err(|err| err.into_io_error()) + } + + #[inline] + fn base_fs_symlink_metadata( + &self, + path: &Path, + ) -> std::io::Result { + self + .0 + .lstat_sync(path) + .map(|data| FsStatSlim::from_deno_fs_stat(&data)) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsCreateDir for FsSysTraitsAdapter { + #[inline] + fn base_fs_create_dir( + &self, + path: &Path, + options: &sys_traits::CreateDirOptions, + ) -> std::io::Result<()> { + self + .0 + .mkdir_sync(path, options.recursive, options.mode) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsRemoveFile for FsSysTraitsAdapter { + #[inline] + fn base_fs_remove_file(&self, path: &Path) -> std::io::Result<()> { + self + .0 + .remove_sync(path, false) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsRename for FsSysTraitsAdapter { + #[inline] + fn base_fs_rename(&self, from: &Path, to: &Path) -> std::io::Result<()> { + self + .0 + .rename_sync(from, to) + .map_err(|err| err.into_io_error()) + } +} + +pub struct FsFileAdapter(pub Rc); + +impl FsFile for FsFileAdapter {} + +impl FsFileSetPermissions for FsFileAdapter { + #[inline] + fn fs_file_set_permissions(&mut self, mode: u32) -> std::io::Result<()> { + if cfg!(windows) { + Ok(()) // ignore + } else { + self + .0 + .clone() + .chmod_sync(mode) + .map_err(|err| err.into_io_error()) + } + } +} + +impl std::io::Read for FsFileAdapter { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self + .0 + .clone() + .read_sync(buf) + .map_err(|err| err.into_io_error()) + } +} + +impl std::io::Seek for FsFileAdapter { + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + self + .0 + .clone() + .seek_sync(pos) + .map_err(|err| err.into_io_error()) + } +} + +impl std::io::Write for FsFileAdapter { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self + .0 + .clone() + .write_sync(buf) + .map_err(|err| err.into_io_error()) + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + self + .0 + .clone() + .sync_sync() + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsOpen for FsSysTraitsAdapter { + type File = FsFileAdapter; + + fn base_fs_open( + &self, + path: &Path, + options: &sys_traits::OpenOptions, + ) -> std::io::Result { + self + .0 + .open_sync( + path, + OpenOptions { + read: options.read, + write: options.write, + create: options.create, + truncate: options.truncate, + append: options.append, + create_new: options.create_new, + mode: options.mode, + }, + None, + ) + .map(FsFileAdapter) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::SystemRandom for FsSysTraitsAdapter { + #[inline] + fn sys_random(&self, buf: &mut [u8]) -> std::io::Result<()> { + getrandom::getrandom(buf).map_err(|err| { + std::io::Error::new(std::io::ErrorKind::Other, err.to_string()) + }) + } +} + +impl sys_traits::SystemTimeNow for FsSysTraitsAdapter { + #[inline] + fn sys_time_now(&self) -> SystemTime { + SystemTime::now() + } +} + +impl sys_traits::ThreadSleep for FsSysTraitsAdapter { + #[inline] + fn thread_sleep(&self, dur: Duration) { + std::thread::sleep(dur); + } +} + +impl sys_traits::BaseEnvVar for FsSysTraitsAdapter { + fn base_env_var_os( + &self, + key: &std::ffi::OsStr, + ) -> Option { + std::env::var_os(key) + } +} diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index 26fac1e79f..cfcf249783 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -13,6 +13,8 @@ pub use crate::interface::FileSystem; pub use crate::interface::FileSystemRc; pub use crate::interface::FsDirEntry; pub use crate::interface::FsFileType; +pub use crate::interface::FsStatSlim; +pub use crate::interface::FsSysTraitsAdapter; pub use crate::interface::OpenOptions; pub use crate::ops::FsOpsError; pub use crate::ops::FsOpsErrorKind; diff --git a/ext/fs/sync.rs b/ext/fs/sync.rs index 6a913f658a..06694f1dc4 100644 --- a/ext/fs/sync.rs +++ b/ext/fs/sync.rs @@ -21,3 +21,9 @@ mod inner { pub trait MaybeSend {} impl MaybeSend for T where T: ?Sized {} } + +#[allow(clippy::disallowed_types)] +#[inline] +pub fn new_rc(value: T) -> MaybeArc { + MaybeArc::new(value) +} diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 1e6c920c9e..b9b459efc1 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -14,7 +14,9 @@ use deno_core::url::Url; #[allow(unused_imports)] use deno_core::v8; use deno_core::v8::ExternalReference; +use deno_fs::FsSysTraitsAdapter; use node_resolver::errors::ClosestPkgJsonError; +use node_resolver::IsBuiltInNodeModuleChecker; use node_resolver::NpmPackageFolderResolverRc; use once_cell::sync::Lazy; @@ -807,92 +809,28 @@ deno_core::extension!(deno_node, }, ); -pub type NodeResolver = node_resolver::NodeResolver; -#[allow(clippy::disallowed_types)] -pub type NodeResolverRc = - deno_fs::sync::MaybeArc>; -pub type PackageJsonResolver = - node_resolver::PackageJsonResolver; -#[allow(clippy::disallowed_types)] -pub type PackageJsonResolverRc = deno_fs::sync::MaybeArc< - node_resolver::PackageJsonResolver, ->; - #[derive(Debug)] -pub struct DenoFsNodeResolverEnv { - fs: deno_fs::FileSystemRc, -} +pub struct RealIsBuiltInNodeModuleChecker; -impl DenoFsNodeResolverEnv { - pub fn new(fs: deno_fs::FileSystemRc) -> Self { - Self { fs } - } -} - -impl node_resolver::env::NodeResolverEnv for DenoFsNodeResolverEnv { +impl IsBuiltInNodeModuleChecker for RealIsBuiltInNodeModuleChecker { + #[inline] fn is_builtin_node_module(&self, specifier: &str) -> bool { is_builtin_node_module(specifier) } - - fn realpath_sync( - &self, - path: &std::path::Path, - ) -> std::io::Result { - self - .fs - .realpath_sync(path) - .map_err(|err| err.into_io_error()) - } - - fn stat_sync( - &self, - path: &std::path::Path, - ) -> std::io::Result { - self - .fs - .stat_sync(path) - .map(|stat| node_resolver::env::NodeResolverFsStat { - is_file: stat.is_file, - is_dir: stat.is_directory, - is_symlink: stat.is_symlink, - }) - .map_err(|err| err.into_io_error()) - } - - fn exists_sync(&self, path: &std::path::Path) -> bool { - self.fs.exists_sync(path) - } - - fn pkg_json_fs(&self) -> &dyn deno_package_json::fs::DenoPkgJsonFs { - self - } } -impl deno_package_json::fs::DenoPkgJsonFs for DenoFsNodeResolverEnv { - fn read_to_string_lossy( - &self, - path: &std::path::Path, - ) -> Result, std::io::Error> { - self - .fs - .read_text_file_lossy_sync(path, None) - .map_err(|err| err.into_io_error()) - } -} - -pub struct DenoPkgJsonFsAdapter<'a>(pub &'a dyn deno_fs::FileSystem); - -impl<'a> deno_package_json::fs::DenoPkgJsonFs for DenoPkgJsonFsAdapter<'a> { - fn read_to_string_lossy( - &self, - path: &Path, - ) -> Result, std::io::Error> { - self - .0 - .read_text_file_lossy_sync(path, None) - .map_err(|err| err.into_io_error()) - } -} +pub type NodeResolver = node_resolver::NodeResolver< + RealIsBuiltInNodeModuleChecker, + FsSysTraitsAdapter, +>; +#[allow(clippy::disallowed_types)] +pub type NodeResolverRc = deno_fs::sync::MaybeArc; +pub type PackageJsonResolver = + node_resolver::PackageJsonResolver; +#[allow(clippy::disallowed_types)] +pub type PackageJsonResolverRc = deno_fs::sync::MaybeArc< + node_resolver::PackageJsonResolver, +>; pub fn create_host_defined_options<'s>( scope: &mut v8::HandleScope<'s>, diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index a7273c7e73..12c18d4452 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -29,6 +29,7 @@ deno_path_util.workspace = true deno_semver.workspace = true node_resolver.workspace = true node_resolver.features = ["sync"] +sys_traits.workspace = true thiserror.workspace = true url.workspace = true diff --git a/resolvers/deno/cjs.rs b/resolvers/deno/cjs.rs index 6ae648deab..2ec253d41a 100644 --- a/resolvers/deno/cjs.rs +++ b/resolvers/deno/cjs.rs @@ -1,29 +1,30 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::sync::MaybeDashMap; use deno_media_type::MediaType; -use node_resolver::env::NodeResolverEnv; use node_resolver::errors::ClosestPkgJsonError; use node_resolver::InNpmPackageCheckerRc; use node_resolver::PackageJsonResolverRc; use node_resolver::ResolutionMode; +use sys_traits::FsRead; use url::Url; +use crate::sync::MaybeDashMap; + /// Keeps track of what module specifiers were resolved as CJS. /// /// Modules that are `.js`, `.ts`, `.jsx`, and `tsx` are only known to /// be CJS or ESM after they're loaded based on their contents. So these /// files will be "maybe CJS" until they're loaded. #[derive(Debug)] -pub struct CjsTracker { - is_cjs_resolver: IsCjsResolver, +pub struct CjsTracker { + is_cjs_resolver: IsCjsResolver, known: MaybeDashMap, } -impl CjsTracker { +impl CjsTracker { pub fn new( in_npm_pkg_checker: InNpmPackageCheckerRc, - pkg_json_resolver: PackageJsonResolverRc, + pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, ) -> Self { Self { @@ -124,16 +125,16 @@ pub enum IsCjsResolutionMode { /// Resolves whether a module is CJS or ESM. #[derive(Debug)] -pub struct IsCjsResolver { +pub struct IsCjsResolver { in_npm_pkg_checker: InNpmPackageCheckerRc, - pkg_json_resolver: PackageJsonResolverRc, + pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, } -impl IsCjsResolver { +impl IsCjsResolver { pub fn new( in_npm_pkg_checker: InNpmPackageCheckerRc, - pkg_json_resolver: PackageJsonResolverRc, + pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, ) -> Self { Self { diff --git a/resolvers/deno/fs.rs b/resolvers/deno/fs.rs deleted file mode 100644 index f2021a73a9..0000000000 --- a/resolvers/deno/fs.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; - -pub struct DirEntry { - pub name: String, - pub is_file: bool, - pub is_directory: bool, -} - -pub trait DenoResolverFs { - fn read_to_string_lossy( - &self, - path: &Path, - ) -> std::io::Result>; - fn realpath_sync(&self, path: &Path) -> std::io::Result; - fn exists_sync(&self, path: &Path) -> bool; - fn is_dir_sync(&self, path: &Path) -> bool; - fn read_dir_sync(&self, dir_path: &Path) -> std::io::Result>; -} diff --git a/resolvers/deno/lib.rs b/resolvers/deno/lib.rs index 05fa416da1..c943aacdae 100644 --- a/resolvers/deno/lib.rs +++ b/resolvers/deno/lib.rs @@ -14,11 +14,10 @@ use deno_config::workspace::WorkspaceResolver; use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValueParseError; use deno_semver::npm::NpmPackageReqReference; -use fs::DenoResolverFs; -use node_resolver::env::NodeResolverEnv; use node_resolver::errors::NodeResolveError; use node_resolver::errors::PackageSubpathResolveError; use node_resolver::InNpmPackageCheckerRc; +use node_resolver::IsBuiltInNodeModuleChecker; use node_resolver::NodeResolution; use node_resolver::NodeResolutionKind; use node_resolver::NodeResolverRc; @@ -32,11 +31,14 @@ use npm::ResolveReqWithSubPathErrorKind; use sloppy_imports::SloppyImportResolverFs; use sloppy_imports::SloppyImportsResolutionKind; use sloppy_imports::SloppyImportsResolverRc; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsRead; +use sys_traits::FsReadDir; use thiserror::Error; use url::Url; pub mod cjs; -pub mod fs; pub mod npm; pub mod sloppy_imports; mod sync; @@ -80,22 +82,22 @@ pub enum DenoResolveErrorKind { #[derive(Debug)] pub struct NodeAndNpmReqResolver< - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - pub node_resolver: NodeResolverRc, - pub npm_req_resolver: NpmReqResolverRc, + pub node_resolver: NodeResolverRc, + pub npm_req_resolver: NpmReqResolverRc, } pub struct DenoResolverOptions< 'a, - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, TSloppyImportResolverFs: SloppyImportResolverFs, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { pub in_npm_pkg_checker: InNpmPackageCheckerRc, pub node_and_req_resolver: - Option>, + Option>, pub sloppy_imports_resolver: Option>, pub workspace_resolver: WorkspaceResolverRc, @@ -110,12 +112,13 @@ pub struct DenoResolverOptions< /// import map, JSX settings. #[derive(Debug)] pub struct DenoResolver< - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, TSloppyImportResolverFs: SloppyImportResolverFs, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { in_npm_pkg_checker: InNpmPackageCheckerRc, - node_and_npm_resolver: Option>, + node_and_npm_resolver: + Option>, sloppy_imports_resolver: Option>, workspace_resolver: WorkspaceResolverRc, @@ -124,13 +127,17 @@ pub struct DenoResolver< } impl< - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, TSloppyImportResolverFs: SloppyImportResolverFs, - > DenoResolver + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, + > DenoResolver { pub fn new( - options: DenoResolverOptions, + options: DenoResolverOptions< + TIsBuiltInNodeModuleChecker, + TSloppyImportResolverFs, + TSys, + >, ) -> Self { Self { in_npm_pkg_checker: options.in_npm_pkg_checker, diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index c6f5d1cd04..3056a70f61 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -11,7 +11,6 @@ use deno_path_util::url_to_file_path; use deno_semver::package::PackageReq; use deno_semver::StackString; use deno_semver::Version; -use node_resolver::env::NodeResolverEnv; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageJsonLoadError; @@ -19,11 +18,14 @@ use node_resolver::errors::PackageNotFoundError; use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; use node_resolver::PackageJsonResolverRc; +use sys_traits::FsCanonicalize; +use sys_traits::FsDirEntry; +use sys_traits::FsMetadata; +use sys_traits::FsRead; +use sys_traits::FsReadDir; use thiserror::Error; use url::Url; -use crate::fs::DenoResolverFs; - use super::local::normalize_pkg_name_for_node_modules_deno_folder; use super::CliNpmReqResolver; use super::ResolvePkgFolderFromDenoReqError; @@ -40,44 +42,45 @@ pub enum ByonmResolvePkgFolderFromDenoReqError { Io(#[from] std::io::Error), } -pub struct ByonmNpmResolverCreateOptions< - Fs: DenoResolverFs, - TEnv: NodeResolverEnv, -> { +pub struct ByonmNpmResolverCreateOptions { // todo(dsherret): investigate removing this pub root_node_modules_dir: Option, - pub fs: Fs, - pub pkg_json_resolver: PackageJsonResolverRc, + pub sys: TSys, + pub pkg_json_resolver: PackageJsonResolverRc, } #[allow(clippy::disallowed_types)] -pub type ByonmNpmResolverRc = - crate::sync::MaybeArc>; +pub type ByonmNpmResolverRc = + crate::sync::MaybeArc>; #[derive(Debug)] -pub struct ByonmNpmResolver { - fs: Fs, - pkg_json_resolver: PackageJsonResolverRc, +pub struct ByonmNpmResolver< + TSys: FsCanonicalize + FsRead + FsMetadata + FsReadDir, +> { + sys: TSys, + pkg_json_resolver: PackageJsonResolverRc, root_node_modules_dir: Option, } -impl Clone - for ByonmNpmResolver +impl Clone + for ByonmNpmResolver { fn clone(&self) -> Self { Self { - fs: self.fs.clone(), + sys: self.sys.clone(), pkg_json_resolver: self.pkg_json_resolver.clone(), root_node_modules_dir: self.root_node_modules_dir.clone(), } } } -impl ByonmNpmResolver { - pub fn new(options: ByonmNpmResolverCreateOptions) -> Self { +impl + ByonmNpmResolver +{ + pub fn new(options: ByonmNpmResolverCreateOptions) -> Self { Self { root_node_modules_dir: options.root_node_modules_dir, - fs: options.fs, + sys: options.sys, pkg_json_resolver: options.pkg_json_resolver, } } @@ -129,19 +132,20 @@ impl ByonmNpmResolver { req: &PackageReq, referrer: &Url, ) -> Result { - fn node_resolve_dir( - fs: &Fs, + fn node_resolve_dir( + sys: &TSys, alias: &str, start_dir: &Path, ) -> std::io::Result> { for ancestor in start_dir.ancestors() { let node_modules_folder = ancestor.join("node_modules"); let sub_dir = join_package_name(&node_modules_folder, alias); - if fs.is_dir_sync(&sub_dir) { - return Ok(Some(deno_path_util::canonicalize_path_maybe_not_exists( - &sub_dir, - &|path| fs.realpath_sync(path), - )?)); + if sys.fs_is_dir_no_err(&sub_dir) { + return Ok(Some( + deno_path_util::fs::canonicalize_path_maybe_not_exists( + sys, &sub_dir, + )?, + )); } } Ok(None) @@ -154,7 +158,7 @@ impl ByonmNpmResolver { Some((pkg_json, alias)) => { // now try node resolution if let Some(resolved) = - node_resolve_dir(&self.fs, &alias, pkg_json.dir_path())? + node_resolve_dir(&self.sys, &alias, pkg_json.dir_path())? { return Ok(resolved); } @@ -298,7 +302,7 @@ impl ByonmNpmResolver { // now check if node_modules/.deno/ matches this constraint let root_node_modules_dir = self.root_node_modules_dir.as_ref()?; let node_modules_deno_dir = root_node_modules_dir.join(".deno"); - let Ok(entries) = self.fs.read_dir_sync(&node_modules_deno_dir) else { + let Ok(entries) = self.sys.fs_read_dir(&node_modules_deno_dir) else { return None; }; let search_prefix = format!( @@ -311,10 +315,17 @@ impl ByonmNpmResolver { // - @denotest+add@1.0.0 // - @denotest+add@1.0.0_1 for entry in entries { - if !entry.is_directory { + let Ok(entry) = entry else { + continue; + }; + let Ok(file_type) = entry.file_type() else { + continue; + }; + if !file_type.is_dir() { continue; } - let Some(version_and_copy_idx) = entry.name.strip_prefix(&search_prefix) + let entry_name = entry.file_name().to_string_lossy().into_owned(); + let Some(version_and_copy_idx) = entry_name.strip_prefix(&search_prefix) else { continue; }; @@ -327,8 +338,8 @@ impl ByonmNpmResolver { }; if let Some(tag) = req.version_req.tag() { let initialized_file = - node_modules_deno_dir.join(&entry.name).join(".initialized"); - let Ok(contents) = self.fs.read_to_string_lossy(&initialized_file) + node_modules_deno_dir.join(&entry_name).join(".initialized"); + let Ok(contents) = self.sys.fs_read_to_string_lossy(&initialized_file) else { continue; }; @@ -336,19 +347,19 @@ impl ByonmNpmResolver { if tags.any(|t| t == tag) { if let Some((best_version_version, _)) = &best_version { if version > *best_version_version { - best_version = Some((version, entry.name)); + best_version = Some((version, entry_name)); } } else { - best_version = Some((version, entry.name)); + best_version = Some((version, entry_name)); } } } else if req.version_req.matches(&version) { if let Some((best_version_version, _)) = &best_version { if version > *best_version_version { - best_version = Some((version, entry.name)); + best_version = Some((version, entry_name)); } } else { - best_version = Some((version, entry.name)); + best_version = Some((version, entry_name)); } } } @@ -363,9 +374,14 @@ impl ByonmNpmResolver { } impl< - Fs: DenoResolverFs + Send + Sync + std::fmt::Debug, - TEnv: NodeResolverEnv, - > CliNpmReqResolver for ByonmNpmResolver + Sys: FsCanonicalize + + FsMetadata + + FsRead + + FsReadDir + + Send + + Sync + + std::fmt::Debug, + > CliNpmReqResolver for ByonmNpmResolver { fn resolve_pkg_folder_from_deno_module_req( &self, @@ -380,17 +396,22 @@ impl< } impl< - Fs: DenoResolverFs + Send + Sync + std::fmt::Debug, - TEnv: NodeResolverEnv, - > NpmPackageFolderResolver for ByonmNpmResolver + Sys: FsCanonicalize + + FsMetadata + + FsRead + + FsReadDir + + Send + + Sync + + std::fmt::Debug, + > NpmPackageFolderResolver for ByonmNpmResolver { fn resolve_package_folder_from_package( &self, name: &str, referrer: &Url, ) -> Result { - fn inner( - fs: &Fs, + fn inner( + sys: &TSys, name: &str, referrer: &Url, ) -> Result { @@ -407,7 +428,7 @@ impl< }; let sub_dir = join_package_name(&node_modules_folder, name); - if fs.is_dir_sync(&sub_dir) { + if sys.fs_is_dir_no_err(&sub_dir) { return Ok(sub_dir); } } @@ -423,8 +444,8 @@ impl< ) } - let path = inner(&self.fs, name, referrer)?; - self.fs.realpath_sync(&path).map_err(|err| { + let path = inner(&self.sys, name, referrer)?; + self.sys.fs_canonicalize(&path).map_err(|err| { PackageFolderResolveIoError { package_name: name.to_string(), referrer: referrer.clone(), diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 64ec86fe3f..082940eb34 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -6,7 +6,6 @@ use std::path::PathBuf; use boxed_error::Boxed; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; -use node_resolver::env::NodeResolverEnv; use node_resolver::errors::NodeResolveError; use node_resolver::errors::NodeResolveErrorKind; use node_resolver::errors::PackageFolderResolveErrorKind; @@ -15,15 +14,18 @@ use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::PackageResolveErrorKind; use node_resolver::errors::PackageSubpathResolveError; use node_resolver::InNpmPackageCheckerRc; +use node_resolver::IsBuiltInNodeModuleChecker; use node_resolver::NodeResolution; use node_resolver::NodeResolutionKind; use node_resolver::NodeResolverRc; use node_resolver::ResolutionMode; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsRead; +use sys_traits::FsReadDir; use thiserror::Error; use url::Url; -use crate::fs::DenoResolverFs; - pub use byonm::ByonmInNpmPackageChecker; pub use byonm::ByonmNpmResolver; pub use byonm::ByonmNpmResolverCreateOptions; @@ -95,40 +97,46 @@ pub trait CliNpmReqResolver: Debug + Send + Sync { } pub struct NpmReqResolverOptions< - Fs: DenoResolverFs, - TNodeResolverEnv: NodeResolverEnv, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { /// The resolver when "bring your own node_modules" is enabled where Deno /// does not setup the node_modules directories automatically, but instead /// uses what already exists on the file system. - pub byonm_resolver: Option>, - pub fs: Fs, + pub byonm_resolver: Option>, pub in_npm_pkg_checker: InNpmPackageCheckerRc, - pub node_resolver: NodeResolverRc, + pub node_resolver: NodeResolverRc, pub npm_req_resolver: CliNpmReqResolverRc, + pub sys: TSys, } #[allow(clippy::disallowed_types)] -pub type NpmReqResolverRc = - crate::sync::MaybeArc>; +pub type NpmReqResolverRc = + crate::sync::MaybeArc>; #[derive(Debug)] -pub struct NpmReqResolver -{ - byonm_resolver: Option>, - fs: Fs, +pub struct NpmReqResolver< + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, +> { + byonm_resolver: Option>, + sys: TSys, in_npm_pkg_checker: InNpmPackageCheckerRc, - node_resolver: NodeResolverRc, + node_resolver: NodeResolverRc, npm_resolver: CliNpmReqResolverRc, } -impl - NpmReqResolver +impl< + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, + > NpmReqResolver { - pub fn new(options: NpmReqResolverOptions) -> Self { + pub fn new( + options: NpmReqResolverOptions, + ) -> Self { Self { byonm_resolver: options.byonm_resolver, - fs: options.fs, + sys: options.sys, in_npm_pkg_checker: options.in_npm_pkg_checker, node_resolver: options.node_resolver, npm_resolver: options.npm_req_resolver, @@ -175,7 +183,7 @@ impl Err(err) => { if self.byonm_resolver.is_some() { let package_json_path = package_folder.join("package.json"); - if !self.fs.exists_sync(&package_json_path) { + if !self.sys.fs_exists_no_err(&package_json_path) { return Err( MissingPackageNodeModulesFolderError { package_json_path }.into(), ); diff --git a/resolvers/node/Cargo.toml b/resolvers/node/Cargo.toml index 52aedbee9d..1e35c0a355 100644 --- a/resolvers/node/Cargo.toml +++ b/resolvers/node/Cargo.toml @@ -29,6 +29,7 @@ once_cell.workspace = true path-clean = "=0.1.0" regex.workspace = true serde_json.workspace = true +sys_traits.workspace = true thiserror.workspace = true tokio.workspace = true url.workspace = true diff --git a/resolvers/node/analyze.rs b/resolvers/node/analyze.rs index 9e6219b082..2024e6a1e8 100644 --- a/resolvers/node/analyze.rs +++ b/resolvers/node/analyze.rs @@ -16,11 +16,14 @@ use once_cell::sync::Lazy; use anyhow::Context; use anyhow::Error as AnyError; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsRead; use url::Url; -use crate::env::NodeResolverEnv; use crate::npm::InNpmPackageCheckerRc; use crate::resolution::NodeResolverRc; +use crate::IsBuiltInNodeModuleChecker; use crate::NodeResolutionKind; use crate::NpmPackageFolderResolverRc; use crate::PackageJsonResolverRc; @@ -60,34 +63,38 @@ pub trait CjsCodeAnalyzer { pub struct NodeCodeTranslator< TCjsCodeAnalyzer: CjsCodeAnalyzer, - TNodeResolverEnv: NodeResolverEnv, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TSys: FsCanonicalize + FsMetadata + FsRead, > { cjs_code_analyzer: TCjsCodeAnalyzer, - env: TNodeResolverEnv, in_npm_pkg_checker: InNpmPackageCheckerRc, - node_resolver: NodeResolverRc, + node_resolver: NodeResolverRc, npm_resolver: NpmPackageFolderResolverRc, - pkg_json_resolver: PackageJsonResolverRc, + pkg_json_resolver: PackageJsonResolverRc, + sys: TSys, } -impl - NodeCodeTranslator +impl< + TCjsCodeAnalyzer: CjsCodeAnalyzer, + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TSys: FsCanonicalize + FsMetadata + FsRead, + > NodeCodeTranslator { pub fn new( cjs_code_analyzer: TCjsCodeAnalyzer, - env: TNodeResolverEnv, in_npm_pkg_checker: InNpmPackageCheckerRc, - node_resolver: NodeResolverRc, + node_resolver: NodeResolverRc, npm_resolver: NpmPackageFolderResolverRc, - pkg_json_resolver: PackageJsonResolverRc, + pkg_json_resolver: PackageJsonResolverRc, + sys: TSys, ) -> Self { Self { cjs_code_analyzer, - env, in_npm_pkg_checker, node_resolver, npm_resolver, pkg_json_resolver, + sys, } } @@ -366,7 +373,7 @@ impl // old school if package_subpath != "." { let d = module_dir.join(package_subpath); - if self.env.is_dir_sync(&d) { + if self.sys.fs_is_dir_no_err(&d) { // subdir might have a package.json that specifies the entrypoint let package_json_path = d.join("package.json"); let maybe_package_json = self @@ -423,13 +430,13 @@ impl referrer: &Path, ) -> Result { let p = p.clean(); - if self.env.exists_sync(&p) { + if self.sys.fs_exists_no_err(&p) { let file_name = p.file_name().unwrap(); let p_js = p.with_file_name(format!("{}.js", file_name.to_str().unwrap())); - if self.env.is_file_sync(&p_js) { + if self.sys.fs_is_file_no_err(&p_js) { return Ok(p_js); - } else if self.env.is_dir_sync(&p) { + } else if self.sys.fs_is_dir_no_err(&p) { return Ok(p.join("index.js")); } else { return Ok(p); @@ -438,14 +445,14 @@ impl { let p_js = p.with_file_name(format!("{}.js", file_name.to_str().unwrap())); - if self.env.is_file_sync(&p_js) { + if self.sys.fs_is_file_no_err(&p_js) { return Ok(p_js); } } { let p_json = p.with_file_name(format!("{}.json", file_name.to_str().unwrap())); - if self.env.is_file_sync(&p_json) { + if self.sys.fs_is_file_no_err(&p_json) { return Ok(p_json); } } diff --git a/resolvers/node/env.rs b/resolvers/node/env.rs deleted file mode 100644 index b520ece0f8..0000000000 --- a/resolvers/node/env.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use std::path::Path; -use std::path::PathBuf; - -use crate::sync::MaybeSend; -use crate::sync::MaybeSync; - -pub struct NodeResolverFsStat { - pub is_file: bool, - pub is_dir: bool, - pub is_symlink: bool, -} - -pub trait NodeResolverEnv: std::fmt::Debug + MaybeSend + MaybeSync { - fn is_builtin_node_module(&self, specifier: &str) -> bool; - - fn realpath_sync(&self, path: &Path) -> std::io::Result; - - fn stat_sync(&self, path: &Path) -> std::io::Result; - - fn exists_sync(&self, path: &Path) -> bool; - - fn is_file_sync(&self, path: &Path) -> bool { - self - .stat_sync(path) - .map(|stat| stat.is_file) - .unwrap_or(false) - } - - fn is_dir_sync(&self, path: &Path) -> bool { - self - .stat_sync(path) - .map(|stat| stat.is_dir) - .unwrap_or(false) - } - - fn pkg_json_fs(&self) -> &dyn deno_package_json::fs::DenoPkgJsonFs; -} diff --git a/resolvers/node/lib.rs b/resolvers/node/lib.rs index c73c395dfc..075f819ebb 100644 --- a/resolvers/node/lib.rs +++ b/resolvers/node/lib.rs @@ -4,7 +4,6 @@ #![deny(clippy::print_stdout)] pub mod analyze; -pub mod env; pub mod errors; mod npm; mod package_json; @@ -23,6 +22,7 @@ pub use package_json::PackageJsonThreadLocalCache; pub use path::PathClean; pub use resolution::parse_npm_pkg_name; pub use resolution::resolve_specifier_into_node_modules; +pub use resolution::IsBuiltInNodeModuleChecker; pub use resolution::NodeResolution; pub use resolution::NodeResolutionKind; pub use resolution::NodeResolver; diff --git a/resolvers/node/package_json.rs b/resolvers/node/package_json.rs index cb99e5a0aa..ebbe099014 100644 --- a/resolvers/node/package_json.rs +++ b/resolvers/node/package_json.rs @@ -1,15 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_package_json::PackageJson; -use deno_package_json::PackageJsonRc; use std::cell::RefCell; use std::collections::HashMap; use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; + +use deno_package_json::PackageJson; +use deno_package_json::PackageJsonRc; +use sys_traits::FsRead; use url::Url; -use crate::env::NodeResolverEnv; use crate::errors::ClosestPkgJsonError; use crate::errors::PackageJsonLoadError; @@ -38,17 +39,17 @@ impl deno_package_json::PackageJsonCache for PackageJsonThreadLocalCache { } #[allow(clippy::disallowed_types)] -pub type PackageJsonResolverRc = - crate::sync::MaybeArc>; +pub type PackageJsonResolverRc = + crate::sync::MaybeArc>; #[derive(Debug)] -pub struct PackageJsonResolver { - env: TEnv, +pub struct PackageJsonResolver { + sys: TSys, } -impl PackageJsonResolver { - pub fn new(env: TEnv) -> Self { - Self { env } +impl PackageJsonResolver { + pub fn new(sys: TSys) -> Self { + Self { sys } } pub fn get_closest_package_json( @@ -81,9 +82,9 @@ impl PackageJsonResolver { path: &Path, ) -> Result, PackageJsonLoadError> { let result = PackageJson::load_from_path( - path, - self.env.pkg_json_fs(), + &self.sys, Some(&PackageJsonThreadLocalCache), + path, ); match result { Ok(pkg_json) => Ok(Some(pkg_json)), diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs index 6b0bda57d7..95631daf39 100644 --- a/resolvers/node/resolution.rs +++ b/resolvers/node/resolution.rs @@ -9,9 +9,13 @@ use anyhow::Error as AnyError; use deno_path_util::url_from_file_path; use serde_json::Map; use serde_json::Value; +use sys_traits::FileType; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsMetadataValue; +use sys_traits::FsRead; use url::Url; -use crate::env::NodeResolverEnv; use crate::errors; use crate::errors::DataUrlReferrerError; use crate::errors::FinalizeResolutionError; @@ -98,29 +102,44 @@ impl NodeResolution { } } -#[allow(clippy::disallowed_types)] -pub type NodeResolverRc = crate::sync::MaybeArc>; - -#[derive(Debug)] -pub struct NodeResolver { - env: TEnv, - in_npm_pkg_checker: InNpmPackageCheckerRc, - npm_pkg_folder_resolver: NpmPackageFolderResolverRc, - pkg_json_resolver: PackageJsonResolverRc, +pub trait IsBuiltInNodeModuleChecker: std::fmt::Debug { + fn is_builtin_node_module(&self, specifier: &str) -> bool; } -impl NodeResolver { +#[allow(clippy::disallowed_types)] +pub type NodeResolverRc = + crate::sync::MaybeArc>; + +#[derive(Debug)] +pub struct NodeResolver< + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TSys: FsCanonicalize + FsMetadata + FsRead, +> { + in_npm_pkg_checker: InNpmPackageCheckerRc, + is_built_in_node_module_checker: TIsBuiltInNodeModuleChecker, + npm_pkg_folder_resolver: NpmPackageFolderResolverRc, + pkg_json_resolver: PackageJsonResolverRc, + sys: TSys, +} + +impl< + TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TSys: FsCanonicalize + FsMetadata + FsRead, + > NodeResolver +{ pub fn new( - env: TEnv, in_npm_pkg_checker: InNpmPackageCheckerRc, + is_built_in_node_module_checker: TIsBuiltInNodeModuleChecker, npm_pkg_folder_resolver: NpmPackageFolderResolverRc, - pkg_json_resolver: PackageJsonResolverRc, + pkg_json_resolver: PackageJsonResolverRc, + sys: TSys, ) -> Self { Self { - env, in_npm_pkg_checker, + is_built_in_node_module_checker, npm_pkg_folder_resolver, pkg_json_resolver, + sys, } } @@ -140,7 +159,10 @@ impl NodeResolver { // Note: if we are here, then the referrer is an esm module // TODO(bartlomieju): skipped "policy" part as we don't plan to support it - if self.env.is_builtin_node_module(specifier) { + if self + .is_built_in_node_module_checker + .is_builtin_node_module(specifier) + { return Ok(NodeResolution::BuiltIn(specifier.to_string())); } @@ -282,32 +304,25 @@ impl NodeResolver { p_str.to_string() }; - let (is_dir, is_file) = if let Ok(stats) = self.env.stat_sync(Path::new(&p)) - { - (stats.is_dir, stats.is_file) - } else { - (false, false) - }; - if is_dir { - return Err( + let maybe_file_type = self.sys.fs_metadata(p).map(|m| m.file_type()); + match maybe_file_type { + Ok(FileType::Dir) => Err( UnsupportedDirImportError { dir_url: resolved.clone(), maybe_referrer: maybe_referrer.map(ToOwned::to_owned), } .into(), - ); - } else if !is_file { - return Err( + ), + Ok(FileType::File) => Ok(resolved), + _ => Err( ModuleNotFoundError { specifier: resolved, maybe_referrer: maybe_referrer.map(ToOwned::to_owned), typ: "module", } .into(), - ); + ), } - - Ok(resolved) } pub fn resolve_package_subpath_from_deno_module( @@ -397,8 +412,8 @@ impl NodeResolver { maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, ) -> Result { - fn probe_extensions( - fs: &TEnv, + fn probe_extensions( + sys: &TSys, path: &Path, lowercase_path: &str, resolution_mode: ResolutionMode, @@ -407,20 +422,20 @@ impl NodeResolver { let mut searched_for_d_cts = false; if lowercase_path.ends_with(".mjs") { let d_mts_path = with_known_extension(path, "d.mts"); - if fs.exists_sync(&d_mts_path) { + if sys.fs_exists_no_err(&d_mts_path) { return Some(d_mts_path); } searched_for_d_mts = true; } else if lowercase_path.ends_with(".cjs") { let d_cts_path = with_known_extension(path, "d.cts"); - if fs.exists_sync(&d_cts_path) { + if sys.fs_exists_no_err(&d_cts_path) { return Some(d_cts_path); } searched_for_d_cts = true; } let dts_path = with_known_extension(path, "d.ts"); - if fs.exists_sync(&dts_path) { + if sys.fs_exists_no_err(&dts_path) { return Some(dts_path); } @@ -434,7 +449,7 @@ impl NodeResolver { _ => None, // already searched above }; if let Some(specific_dts_path) = specific_dts_path { - if fs.exists_sync(&specific_dts_path) { + if sys.fs_exists_no_err(&specific_dts_path) { return Some(specific_dts_path); } } @@ -449,11 +464,11 @@ impl NodeResolver { return Ok(url_from_file_path(path).unwrap()); } if let Some(path) = - probe_extensions(&self.env, path, &lowercase_path, resolution_mode) + probe_extensions(&self.sys, path, &lowercase_path, resolution_mode) { return Ok(url_from_file_path(&path).unwrap()); } - if self.env.is_dir_sync(path) { + if self.sys.fs_is_dir_no_err(path) { let resolution_result = self.resolve_package_dir_subpath( path, /* sub path */ ".", @@ -467,7 +482,7 @@ impl NodeResolver { } let index_path = path.join("index.js"); if let Some(path) = probe_extensions( - &self.env, + &self.sys, &index_path, &index_path.to_string_lossy().to_lowercase(), resolution_mode, @@ -671,7 +686,10 @@ impl NodeResolver { return match result { Ok(url) => Ok(url), Err(err) => { - if self.env.is_builtin_node_module(target) { + if self + .is_built_in_node_module_checker + .is_builtin_node_module(target) + { Ok(Url::parse(&format!("node:{}", target)).unwrap()) } else { Err(err) @@ -1353,7 +1371,7 @@ impl NodeResolver { if let Some(main) = maybe_main { let guess = package_json.path.parent().unwrap().join(main).clean(); - if self.env.is_file_sync(&guess) { + if self.sys.fs_is_file_no_err(&guess) { return Ok(url_from_file_path(&guess).unwrap()); } @@ -1382,7 +1400,7 @@ impl NodeResolver { .unwrap() .join(format!("{main}{ending}")) .clean(); - if self.env.is_file_sync(&guess) { + if self.sys.fs_is_file_no_err(&guess) { // TODO(bartlomieju): emitLegacyIndexDeprecation() return Ok(url_from_file_path(&guess).unwrap()); } @@ -1417,7 +1435,7 @@ impl NodeResolver { }; for index_file_name in index_file_names { let guess = directory.join(index_file_name).clean(); - if self.env.is_file_sync(&guess) { + if self.sys.fs_is_file_no_err(&guess) { // TODO(bartlomieju): emitLegacyIndexDeprecation() return Ok(url_from_file_path(&guess).unwrap()); } @@ -1454,9 +1472,7 @@ impl NodeResolver { { // Specifiers in the node_modules directory are canonicalized // so canoncalize then check if it's in the node_modules directory. - let specifier = resolve_specifier_into_node_modules(specifier, &|path| { - self.env.realpath_sync(path) - }); + let specifier = resolve_specifier_into_node_modules(&self.sys, specifier); return Some(specifier); } @@ -1717,16 +1733,15 @@ pub fn parse_npm_pkg_name( /// not be fully resolved at the time deno_graph is analyzing it /// because the node_modules folder might not exist at that time. pub fn resolve_specifier_into_node_modules( + sys: &impl FsCanonicalize, specifier: &Url, - canonicalize: &impl Fn(&Path) -> std::io::Result, ) -> Url { deno_path_util::url_to_file_path(specifier) .ok() // this path might not exist at the time the graph is being created // because the node_modules folder might not yet exist .and_then(|path| { - deno_path_util::canonicalize_path_maybe_not_exists(&path, canonicalize) - .ok() + deno_path_util::fs::canonicalize_path_maybe_not_exists(sys, &path).ok() }) .and_then(|path| deno_path_util::url_from_file_path(&path).ok()) .unwrap_or_else(|| specifier.clone()) diff --git a/resolvers/npm_cache/Cargo.toml b/resolvers/npm_cache/Cargo.toml index 010f8a436b..48d0a32437 100644 --- a/resolvers/npm_cache/Cargo.toml +++ b/resolvers/npm_cache/Cargo.toml @@ -25,6 +25,7 @@ boxed_error.workspace = true deno_cache_dir.workspace = true deno_error.workspace = true deno_npm.workspace = true +deno_path_util.workspace = true deno_semver.workspace = true deno_unsync = { workspace = true, features = ["tokio"] } faster-hex.workspace = true @@ -37,6 +38,7 @@ percent-encoding.workspace = true rand.workspace = true ring.workspace = true serde_json.workspace = true +sys_traits.workspace = true tar.workspace = true tempfile = "3.4.0" thiserror.workspace = true diff --git a/resolvers/npm_cache/fs_util.rs b/resolvers/npm_cache/fs_util.rs new file mode 100644 index 0000000000..ed123f085c --- /dev/null +++ b/resolvers/npm_cache/fs_util.rs @@ -0,0 +1,99 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use anyhow::Context; +use anyhow::Error as AnyError; +use std::io::ErrorKind; +use std::path::Path; +use std::time::Duration; +use sys_traits::FsCreateDirAll; +use sys_traits::FsDirEntry; +use sys_traits::FsHardLink; +use sys_traits::FsReadDir; +use sys_traits::FsRemoveFile; +use sys_traits::ThreadSleep; + +/// Hardlinks the files in one directory to another directory. +/// +/// Note: Does not handle symlinks. +pub fn hard_link_dir_recursive< + TSys: FsCreateDirAll + FsHardLink + FsReadDir + FsRemoveFile + ThreadSleep, +>( + sys: &TSys, + from: &Path, + to: &Path, +) -> Result<(), AnyError> { + sys + .fs_create_dir_all(to) + .with_context(|| format!("Creating {}", to.display()))?; + let read_dir = sys + .fs_read_dir(from) + .with_context(|| format!("Reading {}", from.display()))?; + + for entry in read_dir { + let entry = entry?; + let file_type = entry.file_type()?; + let new_from = from.join(entry.file_name()); + let new_to = to.join(entry.file_name()); + + if file_type.is_dir() { + hard_link_dir_recursive(sys, &new_from, &new_to).with_context(|| { + format!("Dir {} to {}", new_from.display(), new_to.display()) + })?; + } else if file_type.is_file() { + // note: chance for race conditions here between attempting to create, + // then removing, then attempting to create. There doesn't seem to be + // a way to hard link with overwriting in Rust, but maybe there is some + // way with platform specific code. The workaround here is to handle + // scenarios where something else might create or remove files. + if let Err(err) = sys.fs_hard_link(&new_from, &new_to) { + if err.kind() == ErrorKind::AlreadyExists { + if let Err(err) = sys.fs_remove_file(&new_to) { + if err.kind() == ErrorKind::NotFound { + // Assume another process/thread created this hard link to the file we are wanting + // to remove then sleep a little bit to let the other process/thread move ahead + // faster to reduce contention. + sys.thread_sleep(Duration::from_millis(10)); + } else { + return Err(err).with_context(|| { + format!( + "Removing file to hard link {} to {}", + new_from.display(), + new_to.display() + ) + }); + } + } + + // Always attempt to recreate the hardlink. In contention scenarios, the other process + // might have been killed or exited after removing the file, but before creating the hardlink + if let Err(err) = sys.fs_hard_link(&new_from, &new_to) { + // Assume another process/thread created this hard link to the file we are wanting + // to now create then sleep a little bit to let the other process/thread move ahead + // faster to reduce contention. + if err.kind() == ErrorKind::AlreadyExists { + sys.thread_sleep(Duration::from_millis(10)); + } else { + return Err(err).with_context(|| { + format!( + "Hard linking {} to {}", + new_from.display(), + new_to.display() + ) + }); + } + } + } else { + return Err(err).with_context(|| { + format!( + "Hard linking {} to {}", + new_from.display(), + new_to.display() + ) + }); + } + } + } + } + + Ok(()) +} diff --git a/resolvers/npm_cache/lib.rs b/resolvers/npm_cache/lib.rs index dbd33d29eb..e681fa71ac 100644 --- a/resolvers/npm_cache/lib.rs +++ b/resolvers/npm_cache/lib.rs @@ -14,6 +14,7 @@ use deno_cache_dir::npm::NpmCacheDir; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_npm::NpmPackageCacheFolderId; +use deno_path_util::fs::atomic_write_file_with_retries; use deno_semver::package::PackageNv; use deno_semver::StackString; use deno_semver::Version; @@ -21,13 +22,24 @@ use http::HeaderName; use http::HeaderValue; use http::StatusCode; use parking_lot::Mutex; +use sys_traits::FsCreateDirAll; +use sys_traits::FsHardLink; +use sys_traits::FsMetadata; +use sys_traits::FsOpen; +use sys_traits::FsReadDir; +use sys_traits::FsRemoveFile; +use sys_traits::FsRename; +use sys_traits::SystemRandom; +use sys_traits::ThreadSleep; use url::Url; +mod fs_util; mod registry_info; mod remote; mod tarball; mod tarball_extract; +pub use fs_util::hard_link_dir_recursive; pub use registry_info::RegistryInfoProvider; pub use tarball::TarballCache; @@ -55,18 +67,7 @@ impl std::fmt::Display for DownloadError { } #[async_trait::async_trait(?Send)] -pub trait NpmCacheEnv: Send + Sync + 'static { - fn exists(&self, path: &Path) -> bool; - fn hard_link_dir_recursive( - &self, - from: &Path, - to: &Path, - ) -> Result<(), AnyError>; - fn atomic_write_file_with_retries( - &self, - file_path: &Path, - data: &[u8], - ) -> std::io::Result<()>; +pub trait NpmCacheHttpClient: Send + Sync + 'static { async fn download_with_retries_on_any_tokio_runtime( &self, url: Url, @@ -126,27 +127,48 @@ impl NpmCacheSetting { /// Stores a single copy of npm packages in a cache. #[derive(Debug)] -pub struct NpmCache { - env: Arc, +pub struct NpmCache< + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom, +> { cache_dir: Arc, + sys: TSys, cache_setting: NpmCacheSetting, npmrc: Arc, previously_reloaded_packages: Mutex>, } -impl NpmCache { +impl< + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom, + > NpmCache +{ pub fn new( cache_dir: Arc, + sys: TSys, cache_setting: NpmCacheSetting, - env: Arc, npmrc: Arc, ) -> Self { Self { cache_dir, + sys, cache_setting, - env, - previously_reloaded_packages: Default::default(), npmrc, + previously_reloaded_packages: Default::default(), } } @@ -211,9 +233,11 @@ impl NpmCache { // it seems Windows does an "AccessDenied" error when moving a // directory with hard links, so that's why this solution is done with_folder_sync_lock(&folder_id.nv, &package_folder, || { - self - .env - .hard_link_dir_recursive(&original_package_folder, &package_folder) + hard_link_dir_recursive( + &self.sys, + &original_package_folder, + &package_folder, + ) })?; Ok(()) } @@ -290,9 +314,12 @@ impl NpmCache { ) -> Result<(), AnyError> { let file_cache_path = self.get_registry_package_info_file_cache_path(name); let file_text = serde_json::to_string(&package_info)?; - self - .env - .atomic_write_file_with_retries(&file_cache_path, file_text.as_bytes())?; + atomic_write_file_with_retries( + &self.sys, + &file_cache_path, + file_text.as_bytes(), + 0o644, + )?; Ok(()) } @@ -304,6 +331,7 @@ impl NpmCache { const NPM_PACKAGE_SYNC_LOCK_FILENAME: &str = ".deno_sync_lock"; +// todo(dsherret): use `sys` here instead of `std::fs`. fn with_folder_sync_lock( package: &PackageNv, output_folder: &Path, diff --git a/resolvers/npm_cache/registry_info.rs b/resolvers/npm_cache/registry_info.rs index dd8a639591..57e188200d 100644 --- a/resolvers/npm_cache/registry_info.rs +++ b/resolvers/npm_cache/registry_info.rs @@ -18,12 +18,21 @@ use deno_unsync::sync::MultiRuntimeAsyncValueCreator; use futures::future::LocalBoxFuture; use futures::FutureExt; use parking_lot::Mutex; +use sys_traits::FsCreateDirAll; +use sys_traits::FsHardLink; +use sys_traits::FsMetadata; +use sys_traits::FsOpen; +use sys_traits::FsReadDir; +use sys_traits::FsRemoveFile; +use sys_traits::FsRename; +use sys_traits::SystemRandom; +use sys_traits::ThreadSleep; use thiserror::Error; use url::Url; use crate::remote::maybe_auth_header_for_npm_registry; use crate::NpmCache; -use crate::NpmCacheEnv; +use crate::NpmCacheHttpClient; use crate::NpmCacheSetting; type LoadResult = Result>; @@ -122,25 +131,54 @@ impl MemoryCache { /// /// This is shared amongst all the workers. #[derive(Debug)] -pub struct RegistryInfoProvider { +pub struct RegistryInfoProvider< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, +> { // todo(#27198): remove this - cache: Arc>, - env: Arc, + cache: Arc>, + http_client: Arc, npmrc: Arc, force_reload_flag: AtomicFlag, memory_cache: Mutex, previously_loaded_packages: Mutex>, } -impl RegistryInfoProvider { +impl< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, + > RegistryInfoProvider +{ pub fn new( - cache: Arc>, - env: Arc, + cache: Arc>, + http_client: Arc, npmrc: Arc, ) -> Self { Self { cache, - env, + http_client, npmrc, force_reload_flag: AtomicFlag::lowered(), memory_cache: Default::default(), @@ -170,7 +208,9 @@ impl RegistryInfoProvider { } } - pub fn as_npm_registry_api(self: &Arc) -> NpmRegistryApiAdapter { + pub fn as_npm_registry_api( + self: &Arc, + ) -> NpmRegistryApiAdapter { NpmRegistryApiAdapter(self.clone()) } @@ -341,7 +381,7 @@ impl RegistryInfoProvider { downloader.previously_loaded_packages.lock().insert(name.to_string()); let maybe_bytes = downloader - .env + .http_client .download_with_retries_on_any_tokio_runtime( package_url, maybe_auth_header, @@ -378,12 +418,39 @@ impl RegistryInfoProvider { } } -pub struct NpmRegistryApiAdapter( - Arc>, -); +pub struct NpmRegistryApiAdapter< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, +>(Arc>); #[async_trait(?Send)] -impl NpmRegistryApi for NpmRegistryApiAdapter { +impl< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsReadDir + + FsRemoveFile + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, + > NpmRegistryApi for NpmRegistryApiAdapter +{ async fn package_info( &self, name: &str, diff --git a/resolvers/npm_cache/tarball.rs b/resolvers/npm_cache/tarball.rs index 5c8e460fd6..3a7e9df8a9 100644 --- a/resolvers/npm_cache/tarball.rs +++ b/resolvers/npm_cache/tarball.rs @@ -15,13 +15,22 @@ use futures::future::LocalBoxFuture; use futures::FutureExt; use http::StatusCode; use parking_lot::Mutex; +use sys_traits::FsCreateDirAll; +use sys_traits::FsHardLink; +use sys_traits::FsMetadata; +use sys_traits::FsOpen; +use sys_traits::FsReadDir; +use sys_traits::FsRemoveFile; +use sys_traits::FsRename; +use sys_traits::SystemRandom; +use sys_traits::ThreadSleep; use url::Url; use crate::remote::maybe_auth_header_for_npm_registry; use crate::tarball_extract::verify_and_extract_tarball; use crate::tarball_extract::TarballExtractionMode; use crate::NpmCache; -use crate::NpmCacheEnv; +use crate::NpmCacheHttpClient; use crate::NpmCacheSetting; type LoadResult = Result<(), Arc>; @@ -42,22 +51,54 @@ enum MemoryCacheItem { /// /// This is shared amongst all the workers. #[derive(Debug)] -pub struct TarballCache { - cache: Arc>, - env: Arc, +pub struct TarballCache< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsRemoveFile + + FsReadDir + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, +> { + cache: Arc>, + http_client: Arc, + sys: TSys, npmrc: Arc, memory_cache: Mutex>, } -impl TarballCache { +impl< + THttpClient: NpmCacheHttpClient, + TSys: FsCreateDirAll + + FsHardLink + + FsMetadata + + FsOpen + + FsRemoveFile + + FsReadDir + + FsRename + + ThreadSleep + + SystemRandom + + Send + + Sync + + 'static, + > TarballCache +{ pub fn new( - cache: Arc>, - env: Arc, + cache: Arc>, + http_client: Arc, + sys: TSys, npmrc: Arc, ) -> Self { Self { cache, - env, + http_client, + sys, npmrc, memory_cache: Default::default(), } @@ -131,7 +172,7 @@ impl TarballCache { let package_folder = tarball_cache.cache.package_folder_for_nv_and_url(&package_nv, registry_url); let should_use_cache = tarball_cache.cache.should_use_cache_for_package(&package_nv); - let package_folder_exists = tarball_cache.env.exists(&package_folder); + let package_folder_exists = tarball_cache.sys.fs_exists_no_err(&package_folder); if should_use_cache && package_folder_exists { return Ok(()); } else if tarball_cache.cache.cache_setting() == &NpmCacheSetting::Only { @@ -156,7 +197,7 @@ impl TarballCache { tarball_cache.npmrc.tarball_config(&tarball_uri); let maybe_auth_header = maybe_registry_config.and_then(|c| maybe_auth_header_for_npm_registry(c).ok()?); - let result = tarball_cache.env + let result = tarball_cache.http_client .download_with_retries_on_any_tokio_runtime(tarball_uri, maybe_auth_header) .await; let maybe_bytes = match result { diff --git a/tests/Cargo.toml b/tests/Cargo.toml index fa51d7b77b..1300066c64 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -60,6 +60,7 @@ pretty_assertions.workspace = true regex.workspace = true reqwest.workspace = true serde.workspace = true +sys_traits = { workspace = true, features = ["real", "getrandom", "libc", "winapi"] } test_util.workspace = true tokio.workspace = true tower-lsp.workspace = true diff --git a/tests/integration/jsr_tests.rs b/tests/integration/jsr_tests.rs index f438ebfd50..d3fa5cd98f 100644 --- a/tests/integration/jsr_tests.rs +++ b/tests/integration/jsr_tests.rs @@ -191,8 +191,8 @@ fn reload_info_not_found_cache_but_exists_remote() { Url::parse(&format!("http://127.0.0.1:4250/{}/meta.json", package)) .unwrap(); let cache = deno_cache_dir::GlobalHttpCache::new( + sys_traits::impls::RealSys, deno_dir.path().join("remote").to_path_buf(), - deno_cache_dir::TestRealDenoCacheEnv, ); let entry = cache .get(&cache.cache_item_key(&specifier).unwrap(), None) From a844d96ee914950780b428e992a772a62ff2fd81 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Tue, 31 Dec 2024 08:59:41 +0100 Subject: [PATCH 045/107] chore(lint): remove manual AST field counter (#27449) Addresses the review feedback in https://github.com/denoland/deno/pull/27416 . - Hoist the buffer max size variable to make it less confusing - Remove manual AST field counter in favour of an explicit "commit schema" step which writes the actual field count. --- cli/tools/lint/ast_buffer/buffer.rs | 45 ++- cli/tools/lint/ast_buffer/swc.rs | 510 +++++++++++++++---------- cli/tools/lint/ast_buffer/ts_estree.rs | 16 +- 3 files changed, 354 insertions(+), 217 deletions(-) diff --git a/cli/tools/lint/ast_buffer/buffer.rs b/cli/tools/lint/ast_buffer/buffer.rs index d162ee3de1..b6387a0ef9 100644 --- a/cli/tools/lint/ast_buffer/buffer.rs +++ b/cli/tools/lint/ast_buffer/buffer.rs @@ -121,6 +121,10 @@ impl StringTable { #[derive(Debug, Clone, Copy, PartialEq)] pub struct NodeRef(pub usize); +/// Represents an offset to a node whose schema hasn't been committed yet +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct PendingNodeRef(pub NodeRef); + #[derive(Debug)] pub struct BoolPos(pub usize); #[derive(Debug)] @@ -152,13 +156,8 @@ where K: Into + Display, P: Into + Display, { - fn header( - &mut self, - kind: K, - parent: NodeRef, - span: &Span, - prop_count: usize, - ) -> NodeRef; + fn header(&mut self, kind: K, parent: NodeRef, span: &Span) + -> PendingNodeRef; fn ref_field(&mut self, prop: P) -> FieldPos; fn ref_vec_field(&mut self, prop: P, len: usize) -> FieldArrPos; fn str_field(&mut self, prop: P) -> StrPos; @@ -166,6 +165,7 @@ where fn undefined_field(&mut self, prop: P) -> UndefPos; #[allow(dead_code)] fn null_field(&mut self, prop: P) -> NullPos; + fn commit_schema(&mut self, offset: PendingNodeRef) -> NodeRef; fn write_ref(&mut self, pos: FieldPos, value: NodeRef); fn write_maybe_ref(&mut self, pos: FieldPos, value: Option); @@ -183,6 +183,7 @@ pub struct SerializeCtx { str_table: StringTable, kind_map: Vec, prop_map: Vec, + field_count: u8, } /// This is the internal context used to allocate and fill the buffer. The point @@ -200,8 +201,9 @@ impl SerializeCtx { start_buf: NodeRef(0), buf: vec![], str_table: StringTable::new(), - kind_map: vec![0; kind_size + 1], - prop_map: vec![0; prop_size + 1], + kind_map: vec![0; kind_size], + prop_map: vec![0; prop_size], + field_count: 0, }; let empty_str = ctx.str_table.insert(""); @@ -232,6 +234,8 @@ impl SerializeCtx { where P: Into + Display + Clone, { + self.field_count += 1; + let offset = self.buf.len(); let n: u8 = prop.clone().into(); @@ -268,7 +272,7 @@ impl SerializeCtx { parent: NodeRef, span: &Span, prop_count: usize, - ) -> NodeRef { + ) -> PendingNodeRef { let offset = self.buf.len(); // Node type fits in a u8 @@ -285,7 +289,19 @@ impl SerializeCtx { debug_assert!(prop_count < 10); self.buf.push(prop_count as u8); - NodeRef(offset) + PendingNodeRef(NodeRef(offset)) + } + + pub fn commit_schema(&mut self, node_ref: PendingNodeRef) -> NodeRef { + let mut offset = node_ref.0 .0; + + // type + parentId + span lo + span hi + offset += 1 + 4 + 4 + 4; + + self.buf[offset] = self.field_count; + self.field_count = 0; + + node_ref.0 } /// Allocate the node header. It's always the same for every node. @@ -299,8 +315,7 @@ impl SerializeCtx { kind: N, parent: NodeRef, span: &Span, - prop_count: usize, - ) -> NodeRef + ) -> PendingNodeRef where N: Into + Display + Clone, { @@ -313,7 +328,9 @@ impl SerializeCtx { } } - self.append_node(n, parent, span, prop_count) + // Prop count will be filled with the actual value when the + // schema is committed. + self.append_node(n, parent, span, 0) } /// Allocate a reference property that will hold the offset of diff --git a/cli/tools/lint/ast_buffer/swc.rs b/cli/tools/lint/ast_buffer/swc.rs index 785a38a7d8..b26c213105 100644 --- a/cli/tools/lint/ast_buffer/swc.rs +++ b/cli/tools/lint/ast_buffer/swc.rs @@ -93,12 +93,13 @@ pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec { let program = &parsed_source.program(); - let pos = ctx.header(AstNode::Program, NodeRef(0), &program.span(), 2); + let raw = ctx.header(AstNode::Program, NodeRef(0), &program.span()); let source_type_pos = ctx.str_field(AstProp::SourceType); match program.as_ref() { Program::Module(module) => { let body_pos = ctx.ref_vec_field(AstProp::Body, module.body.len()); + let pos = ctx.commit_schema(raw); let children = module .body @@ -116,6 +117,8 @@ pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec { } Program::Script(script) => { let body_pos = ctx.ref_vec_field(AstProp::Body, script.body.len()); + let pos = ctx.commit_schema(raw); + let children = script .body .iter() @@ -137,12 +140,13 @@ fn serialize_module_decl( ) -> NodeRef { match module_decl { ModuleDecl::Import(node) => { - ctx.header(AstNode::ImportExpression, parent, &node.span, 0) + let raw = ctx.header(AstNode::ImportExpression, parent, &node.span); + ctx.commit_schema(raw) } ModuleDecl::ExportDecl(node) => { - let pos = - ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span, 1); + let raw = ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span); let decl_pos = ctx.ref_field(AstProp::Declarations); + let pos = ctx.commit_schema(raw); let decl = serialize_decl(ctx, &node.decl, pos); @@ -151,11 +155,11 @@ fn serialize_module_decl( pos } ModuleDecl::ExportNamed(node) => { - let id = - ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span, 2); + let raw = ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span); let src_pos = ctx.ref_field(AstProp::Source); let spec_pos = ctx.ref_vec_field(AstProp::Specifiers, node.specifiers.len()); + let id = ctx.commit_schema(raw); // FIXME: Flags // let mut flags = FlagValue::new(); @@ -172,10 +176,10 @@ fn serialize_module_decl( .map(|spec| { match spec { ExportSpecifier::Named(child) => { - let spec_pos = - ctx.header(AstNode::ExportSpecifier, id, &child.span, 2); + let raw = ctx.header(AstNode::ExportSpecifier, id, &child.span); let local_pos = ctx.ref_field(AstProp::Local); let exp_pos = ctx.ref_field(AstProp::Exported); + let spec_pos = ctx.commit_schema(raw); // let mut flags = FlagValue::new(); // flags.set(Flag::ExportType); @@ -208,22 +212,30 @@ fn serialize_module_decl( id } ModuleDecl::ExportDefaultDecl(node) => { - ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span, 0) + let raw = + ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span); + ctx.commit_schema(raw) } ModuleDecl::ExportDefaultExpr(node) => { - ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span, 0) + let raw = + ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span); + ctx.commit_schema(raw) } ModuleDecl::ExportAll(node) => { - ctx.header(AstNode::ExportAllDeclaration, parent, &node.span, 0) + let raw = ctx.header(AstNode::ExportAllDeclaration, parent, &node.span); + ctx.commit_schema(raw) } ModuleDecl::TsImportEquals(node) => { - ctx.header(AstNode::TsImportEquals, parent, &node.span, 0) + let raw = ctx.header(AstNode::TsImportEquals, parent, &node.span); + ctx.commit_schema(raw) } ModuleDecl::TsExportAssignment(node) => { - ctx.header(AstNode::TsExportAssignment, parent, &node.span, 0) + let raw = ctx.header(AstNode::TsExportAssignment, parent, &node.span); + ctx.commit_schema(raw) } ModuleDecl::TsNamespaceExport(node) => { - ctx.header(AstNode::TsNamespaceExport, parent, &node.span, 0) + let raw = ctx.header(AstNode::TsNamespaceExport, parent, &node.span); + ctx.commit_schema(raw) } } } @@ -235,8 +247,9 @@ fn serialize_stmt( ) -> NodeRef { match stmt { Stmt::Block(node) => { - let pos = ctx.header(AstNode::BlockStatement, parent, &node.span, 1); + let raw = ctx.header(AstNode::BlockStatement, parent, &node.span); let body_pos = ctx.ref_vec_field(AstProp::Body, node.stmts.len()); + let pos = ctx.commit_schema(raw); let children = node .stmts @@ -250,12 +263,14 @@ fn serialize_stmt( } Stmt::Empty(_) => NodeRef(0), Stmt::Debugger(node) => { - ctx.header(AstNode::DebuggerStatement, parent, &node.span, 0) + let raw = ctx.header(AstNode::DebuggerStatement, parent, &node.span); + ctx.commit_schema(raw) } Stmt::With(_) => todo!(), Stmt::Return(node) => { - let pos = ctx.header(AstNode::ReturnStatement, parent, &node.span, 1); + let raw = ctx.header(AstNode::ReturnStatement, parent, &node.span); let arg_pos = ctx.ref_field(AstProp::Argument); + let pos = ctx.commit_schema(raw); let arg = node.arg.as_ref().map(|arg| serialize_expr(ctx, arg, pos)); ctx.write_maybe_ref(arg_pos, arg); @@ -263,9 +278,10 @@ fn serialize_stmt( pos } Stmt::Labeled(node) => { - let pos = ctx.header(AstNode::LabeledStatement, parent, &node.span, 2); + let raw = ctx.header(AstNode::LabeledStatement, parent, &node.span); let label_pos = ctx.ref_field(AstProp::Label); let body_pos = ctx.ref_field(AstProp::Body); + let pos = ctx.commit_schema(raw); let ident = serialize_ident(ctx, &node.label, pos); let stmt = serialize_stmt(ctx, &node.body, pos); @@ -276,8 +292,9 @@ fn serialize_stmt( pos } Stmt::Break(node) => { - let pos = ctx.header(AstNode::BreakStatement, parent, &node.span, 1); + let raw = ctx.header(AstNode::BreakStatement, parent, &node.span); let label_pos = ctx.ref_field(AstProp::Label); + let pos = ctx.commit_schema(raw); let arg = node .label @@ -289,8 +306,9 @@ fn serialize_stmt( pos } Stmt::Continue(node) => { - let pos = ctx.header(AstNode::ContinueStatement, parent, &node.span, 1); + let raw = ctx.header(AstNode::ContinueStatement, parent, &node.span); let label_pos = ctx.ref_field(AstProp::Label); + let pos = ctx.commit_schema(raw); let arg = node .label @@ -302,10 +320,11 @@ fn serialize_stmt( pos } Stmt::If(node) => { - let pos = ctx.header(AstNode::IfStatement, parent, &node.span, 3); + let raw = ctx.header(AstNode::IfStatement, parent, &node.span); let test_pos = ctx.ref_field(AstProp::Test); let cons_pos = ctx.ref_field(AstProp::Consequent); let alt_pos = ctx.ref_field(AstProp::Alternate); + let pos = ctx.commit_schema(raw); let test = serialize_expr(ctx, node.test.as_ref(), pos); let cons = serialize_stmt(ctx, node.cons.as_ref(), pos); @@ -318,20 +337,22 @@ fn serialize_stmt( pos } Stmt::Switch(node) => { - let id = ctx.header(AstNode::SwitchStatement, parent, &node.span, 2); + let raw = ctx.header(AstNode::SwitchStatement, parent, &node.span); let disc_pos = ctx.ref_field(AstProp::Discriminant); let cases_pos = ctx.ref_vec_field(AstProp::Cases, node.cases.len()); + let pos = ctx.commit_schema(raw); - let disc = serialize_expr(ctx, &node.discriminant, id); + let disc = serialize_expr(ctx, &node.discriminant, pos); let cases = node .cases .iter() .map(|case| { - let case_pos = ctx.header(AstNode::SwitchCase, id, &case.span, 2); + let raw = ctx.header(AstNode::SwitchCase, pos, &case.span); let test_pos = ctx.ref_field(AstProp::Test); let cons_pos = ctx.ref_vec_field(AstProp::Consequent, case.cons.len()); + let case_pos = ctx.commit_schema(raw); let test = case .test @@ -354,11 +375,12 @@ fn serialize_stmt( ctx.write_ref(disc_pos, disc); ctx.write_refs(cases_pos, cases); - id + pos } Stmt::Throw(node) => { - let pos = ctx.header(AstNode::ThrowStatement, parent, &node.span, 1); + let raw = ctx.header(AstNode::ThrowStatement, parent, &node.span); let arg_pos = ctx.ref_field(AstProp::Argument); + let pos = ctx.commit_schema(raw); let arg = serialize_expr(ctx, &node.arg, pos); ctx.write_ref(arg_pos, arg); @@ -366,17 +388,19 @@ fn serialize_stmt( pos } Stmt::Try(node) => { - let pos = ctx.header(AstNode::TryStatement, parent, &node.span, 3); + let raw = ctx.header(AstNode::TryStatement, parent, &node.span); let block_pos = ctx.ref_field(AstProp::Block); let handler_pos = ctx.ref_field(AstProp::Handler); let finalizer_pos = ctx.ref_field(AstProp::Finalizer); + let pos = ctx.commit_schema(raw); let block = serialize_stmt(ctx, &Stmt::Block(node.block.clone()), pos); let handler = node.handler.as_ref().map(|catch| { - let clause_pos = ctx.header(AstNode::CatchClause, pos, &catch.span, 2); + let raw = ctx.header(AstNode::CatchClause, pos, &catch.span); let param_pos = ctx.ref_field(AstProp::Param); let body_pos = ctx.ref_field(AstProp::Body); + let clause_pos = ctx.commit_schema(raw); let param = catch .param @@ -403,9 +427,10 @@ fn serialize_stmt( pos } Stmt::While(node) => { - let pos = ctx.header(AstNode::WhileStatement, parent, &node.span, 2); + let raw = ctx.header(AstNode::WhileStatement, parent, &node.span); let test_pos = ctx.ref_field(AstProp::Test); let body_pos = ctx.ref_field(AstProp::Body); + let pos = ctx.commit_schema(raw); let test = serialize_expr(ctx, node.test.as_ref(), pos); let stmt = serialize_stmt(ctx, node.body.as_ref(), pos); @@ -416,9 +441,10 @@ fn serialize_stmt( pos } Stmt::DoWhile(node) => { - let pos = ctx.header(AstNode::DoWhileStatement, parent, &node.span, 2); + let raw = ctx.header(AstNode::DoWhileStatement, parent, &node.span); let test_pos = ctx.ref_field(AstProp::Test); let body_pos = ctx.ref_field(AstProp::Body); + let pos = ctx.commit_schema(raw); let expr = serialize_expr(ctx, node.test.as_ref(), pos); let stmt = serialize_stmt(ctx, node.body.as_ref(), pos); @@ -429,11 +455,12 @@ fn serialize_stmt( pos } Stmt::For(node) => { - let pos = ctx.header(AstNode::ForStatement, parent, &node.span, 4); + let raw = ctx.header(AstNode::ForStatement, parent, &node.span); let init_pos = ctx.ref_field(AstProp::Init); let test_pos = ctx.ref_field(AstProp::Test); let update_pos = ctx.ref_field(AstProp::Update); let body_pos = ctx.ref_field(AstProp::Body); + let pos = ctx.commit_schema(raw); let init = node.init.as_ref().map(|init| match init { VarDeclOrExpr::VarDecl(var_decl) => { @@ -460,10 +487,11 @@ fn serialize_stmt( pos } Stmt::ForIn(node) => { - let pos = ctx.header(AstNode::ForInStatement, parent, &node.span, 3); + let raw = ctx.header(AstNode::ForInStatement, parent, &node.span); let left_pos = ctx.ref_field(AstProp::Left); let right_pos = ctx.ref_field(AstProp::Right); let body_pos = ctx.ref_field(AstProp::Body); + let pos = ctx.commit_schema(raw); let left = serialize_for_head(ctx, &node.left, pos); let right = serialize_expr(ctx, node.right.as_ref(), pos); @@ -476,11 +504,12 @@ fn serialize_stmt( pos } Stmt::ForOf(node) => { - let pos = ctx.header(AstNode::ForOfStatement, parent, &node.span, 4); + let raw = ctx.header(AstNode::ForOfStatement, parent, &node.span); let await_pos = ctx.bool_field(AstProp::Await); let left_pos = ctx.ref_field(AstProp::Left); let right_pos = ctx.ref_field(AstProp::Right); let body_pos = ctx.ref_field(AstProp::Body); + let pos = ctx.commit_schema(raw); let left = serialize_for_head(ctx, &node.left, pos); let right = serialize_expr(ctx, node.right.as_ref(), pos); @@ -495,8 +524,9 @@ fn serialize_stmt( } Stmt::Decl(node) => serialize_decl(ctx, node, parent), Stmt::Expr(node) => { - let pos = ctx.header(AstNode::ExpressionStatement, parent, &node.span, 1); + let raw = ctx.header(AstNode::ExpressionStatement, parent, &node.span); let expr_pos = ctx.ref_field(AstProp::Expression); + let pos = ctx.commit_schema(raw); let expr = serialize_expr(ctx, node.expr.as_ref(), pos); ctx.write_ref(expr_pos, expr); @@ -513,11 +543,13 @@ fn serialize_expr( ) -> NodeRef { match expr { Expr::This(node) => { - ctx.header(AstNode::ThisExpression, parent, &node.span, 0) + let raw = ctx.header(AstNode::ThisExpression, parent, &node.span); + ctx.commit_schema(raw) } Expr::Array(node) => { - let pos = ctx.header(AstNode::ArrayExpression, parent, &node.span, 1); + let raw = ctx.header(AstNode::ArrayExpression, parent, &node.span); let elems_pos = ctx.ref_vec_field(AstProp::Elements, node.elems.len()); + let pos = ctx.commit_schema(raw); let elems = node .elems @@ -534,8 +566,9 @@ fn serialize_expr( pos } Expr::Object(node) => { - let pos = ctx.header(AstNode::ObjectExpression, parent, &node.span, 1); + let raw = ctx.header(AstNode::ObjectExpression, parent, &node.span); let props_pos = ctx.ref_vec_field(AstProp::Properties, node.props.len()); + let pos = ctx.commit_schema(raw); let prop_ids = node .props @@ -550,8 +583,7 @@ fn serialize_expr( Expr::Fn(node) => { let fn_obj = node.function.as_ref(); - let pos = - ctx.header(AstNode::FunctionExpression, parent, &fn_obj.span, 7); + let raw = ctx.header(AstNode::FunctionExpression, parent, &fn_obj.span); let async_pos = ctx.bool_field(AstProp::Async); let gen_pos = ctx.bool_field(AstProp::Generator); @@ -560,6 +592,7 @@ fn serialize_expr( let params_pos = ctx.ref_vec_field(AstProp::Params, fn_obj.params.len()); let return_pos = ctx.ref_field(AstProp::ReturnType); let body_pos = ctx.ref_field(AstProp::Body); + let pos = ctx.commit_schema(raw); let ident = node .ident @@ -593,9 +626,10 @@ fn serialize_expr( pos } Expr::Unary(node) => { - let pos = ctx.header(AstNode::UnaryExpression, parent, &node.span, 2); + let raw = ctx.header(AstNode::UnaryExpression, parent, &node.span); let flag_pos = ctx.str_field(AstProp::Operator); let arg_pos = ctx.ref_field(AstProp::Argument); + let pos = ctx.commit_schema(raw); let arg = serialize_expr(ctx, &node.arg, pos); @@ -616,10 +650,11 @@ fn serialize_expr( pos } Expr::Update(node) => { - let pos = ctx.header(AstNode::UpdateExpression, parent, &node.span, 3); + let raw = ctx.header(AstNode::UpdateExpression, parent, &node.span); let prefix_pos = ctx.bool_field(AstProp::Prefix); let arg_pos = ctx.ref_field(AstProp::Argument); let op_ops = ctx.str_field(AstProp::Operator); + let pos = ctx.commit_schema(raw); let arg = serialize_expr(ctx, node.arg.as_ref(), pos); @@ -664,10 +699,11 @@ fn serialize_expr( BinaryOp::Exp => (AstNode::BinaryExpression, "**"), }; - let pos = ctx.header(node_type, parent, &node.span, 3); + let raw = ctx.header(node_type, parent, &node.span); let op_pos = ctx.str_field(AstProp::Operator); let left_pos = ctx.ref_field(AstProp::Left); let right_pos = ctx.ref_field(AstProp::Right); + let pos = ctx.commit_schema(raw); let left_id = serialize_expr(ctx, node.left.as_ref(), pos); let right_id = serialize_expr(ctx, node.right.as_ref(), pos); @@ -679,11 +715,11 @@ fn serialize_expr( pos } Expr::Assign(node) => { - let pos = - ctx.header(AstNode::AssignmentExpression, parent, &node.span, 3); + let raw = ctx.header(AstNode::AssignmentExpression, parent, &node.span); let op_pos = ctx.str_field(AstProp::Operator); let left_pos = ctx.ref_field(AstProp::Left); let right_pos = ctx.ref_field(AstProp::Right); + let pos = ctx.commit_schema(raw); let left = match &node.left { AssignTarget::Simple(simple_assign_target) => { @@ -762,12 +798,14 @@ fn serialize_expr( } Expr::Member(node) => serialize_member_expr(ctx, node, parent, false), Expr::SuperProp(node) => { - let pos = ctx.header(AstNode::MemberExpression, parent, &node.span, 3); + let raw = ctx.header(AstNode::MemberExpression, parent, &node.span); let computed_pos = ctx.bool_field(AstProp::Computed); let obj_pos = ctx.ref_field(AstProp::Object); let prop_pos = ctx.ref_field(AstProp::Property); + let pos = ctx.commit_schema(raw); - let obj = ctx.header(AstNode::Super, pos, &node.obj.span, 0); + let raw = ctx.header(AstNode::Super, pos, &node.obj.span); + let obj = ctx.commit_schema(raw); let mut computed = false; let prop = match &node.prop { @@ -787,11 +825,11 @@ fn serialize_expr( pos } Expr::Cond(node) => { - let pos = - ctx.header(AstNode::ConditionalExpression, parent, &node.span, 3); + let raw = ctx.header(AstNode::ConditionalExpression, parent, &node.span); let test_pos = ctx.ref_field(AstProp::Test); let cons_pos = ctx.ref_field(AstProp::Consequent); let alt_pos = ctx.ref_field(AstProp::Alternate); + let pos = ctx.commit_schema(raw); let test = serialize_expr(ctx, node.test.as_ref(), pos); let cons = serialize_expr(ctx, node.cons.as_ref(), pos); @@ -804,15 +842,17 @@ fn serialize_expr( pos } Expr::Call(node) => { - let pos = ctx.header(AstNode::CallExpression, parent, &node.span, 4); + let raw = ctx.header(AstNode::CallExpression, parent, &node.span); let opt_pos = ctx.bool_field(AstProp::Optional); let callee_pos = ctx.ref_field(AstProp::Callee); let type_args_pos = ctx.ref_field(AstProp::TypeArguments); let args_pos = ctx.ref_vec_field(AstProp::Arguments, node.args.len()); + let pos = ctx.commit_schema(raw); let callee = match &node.callee { Callee::Super(super_node) => { - ctx.header(AstNode::Super, pos, &super_node.span, 0) + let raw = ctx.header(AstNode::Super, pos, &super_node.span); + ctx.commit_schema(raw) } Callee::Import(_) => todo!(), Callee::Expr(expr) => serialize_expr(ctx, expr, pos), @@ -836,13 +876,14 @@ fn serialize_expr( pos } Expr::New(node) => { - let pos = ctx.header(AstNode::NewExpression, parent, &node.span, 3); + let raw = ctx.header(AstNode::NewExpression, parent, &node.span); let callee_pos = ctx.ref_field(AstProp::Callee); let type_args_pos = ctx.ref_field(AstProp::TypeArguments); let args_pos = ctx.ref_vec_field( AstProp::Arguments, node.args.as_ref().map_or(0, |v| v.len()), ); + let pos = ctx.commit_schema(raw); let callee = serialize_expr(ctx, node.callee.as_ref(), pos); @@ -864,8 +905,9 @@ fn serialize_expr( pos } Expr::Seq(node) => { - let pos = ctx.header(AstNode::SequenceExpression, parent, &node.span, 1); + let raw = ctx.header(AstNode::SequenceExpression, parent, &node.span); let exprs_pos = ctx.ref_vec_field(AstProp::Expressions, node.exprs.len()); + let pos = ctx.commit_schema(raw); let children = node .exprs @@ -880,19 +922,20 @@ fn serialize_expr( Expr::Ident(node) => serialize_ident(ctx, node, parent), Expr::Lit(node) => serialize_lit(ctx, node, parent), Expr::Tpl(node) => { - let pos = ctx.header(AstNode::TemplateLiteral, parent, &node.span, 2); + let raw = ctx.header(AstNode::TemplateLiteral, parent, &node.span); let quasis_pos = ctx.ref_vec_field(AstProp::Quasis, node.quasis.len()); let exprs_pos = ctx.ref_vec_field(AstProp::Expressions, node.exprs.len()); + let pos = ctx.commit_schema(raw); let quasis = node .quasis .iter() .map(|quasi| { - let tpl_pos = - ctx.header(AstNode::TemplateElement, pos, &quasi.span, 3); + let raw = ctx.header(AstNode::TemplateElement, pos, &quasi.span); let tail_pos = ctx.bool_field(AstProp::Tail); let raw_pos = ctx.str_field(AstProp::Raw); let cooked_pos = ctx.str_field(AstProp::Cooked); + let tpl_pos = ctx.commit_schema(raw); ctx.write_bool(tail_pos, quasi.tail); ctx.write_str(raw_pos, &quasi.raw); @@ -920,11 +963,12 @@ fn serialize_expr( pos } Expr::TaggedTpl(node) => { - let pos = - ctx.header(AstNode::TaggedTemplateExpression, parent, &node.span, 3); + let raw = + ctx.header(AstNode::TaggedTemplateExpression, parent, &node.span); let tag_pos = ctx.ref_field(AstProp::Tag); let type_arg_pos = ctx.ref_field(AstProp::TypeArguments); let quasi_pos = ctx.ref_field(AstProp::Quasi); + let pos = ctx.commit_schema(raw); let tag = serialize_expr(ctx, &node.tag, pos); @@ -941,14 +985,15 @@ fn serialize_expr( pos } Expr::Arrow(node) => { - let pos = - ctx.header(AstNode::ArrowFunctionExpression, parent, &node.span, 6); + let raw = + ctx.header(AstNode::ArrowFunctionExpression, parent, &node.span); let async_pos = ctx.bool_field(AstProp::Async); let gen_pos = ctx.bool_field(AstProp::Generator); let type_param_pos = ctx.ref_field(AstProp::TypeParameters); let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); let body_pos = ctx.ref_field(AstProp::Body); let return_type_pos = ctx.ref_field(AstProp::ReturnType); + let pos = ctx.commit_schema(raw); let type_param = maybe_serialize_ts_type_param(ctx, &node.type_params, pos); @@ -980,12 +1025,14 @@ fn serialize_expr( } Expr::Class(node) => { // FIXME - ctx.header(AstNode::ClassExpression, parent, &node.class.span, 0) + let raw = ctx.header(AstNode::ClassExpression, parent, &node.class.span); + ctx.commit_schema(raw) } Expr::Yield(node) => { - let pos = ctx.header(AstNode::YieldExpression, parent, &node.span, 2); + let raw = ctx.header(AstNode::YieldExpression, parent, &node.span); let delegate_pos = ctx.bool_field(AstProp::Delegate); let arg_pos = ctx.ref_field(AstProp::Argument); + let pos = ctx.commit_schema(raw); let arg = node .arg @@ -998,11 +1045,13 @@ fn serialize_expr( pos } Expr::MetaProp(node) => { - ctx.header(AstNode::MetaProp, parent, &node.span, 0) + let raw = ctx.header(AstNode::MetaProp, parent, &node.span); + ctx.commit_schema(raw) } Expr::Await(node) => { - let pos = ctx.header(AstNode::AwaitExpression, parent, &node.span, 1); + let raw = ctx.header(AstNode::AwaitExpression, parent, &node.span); let arg_pos = ctx.ref_field(AstProp::Argument); + let pos = ctx.commit_schema(raw); let arg = serialize_expr(ctx, node.arg.as_ref(), pos); @@ -1023,9 +1072,10 @@ fn serialize_expr( Expr::JSXElement(node) => serialize_jsx_element(ctx, node, parent), Expr::JSXFragment(node) => serialize_jsx_fragment(ctx, node, parent), Expr::TsTypeAssertion(node) => { - let pos = ctx.header(AstNode::TSTypeAssertion, parent, &node.span, 2); + let raw = ctx.header(AstNode::TSTypeAssertion, parent, &node.span); let expr_pos = ctx.ref_field(AstProp::Expression); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); let expr = serialize_expr(ctx, &node.expr, parent); let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); @@ -1036,8 +1086,10 @@ fn serialize_expr( pos } Expr::TsConstAssertion(node) => { - let pos = ctx.header(AstNode::TsConstAssertion, parent, &node.span, 1); + let raw = ctx.header(AstNode::TsConstAssertion, parent, &node.span); let arg_pos = ctx.ref_field(AstProp::Argument); + let pos = ctx.commit_schema(raw); + let arg = serialize_expr(ctx, node.expr.as_ref(), pos); // FIXME @@ -1046,8 +1098,9 @@ fn serialize_expr( pos } Expr::TsNonNull(node) => { - let pos = ctx.header(AstNode::TSNonNullExpression, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSNonNullExpression, parent, &node.span); let expr_pos = ctx.ref_field(AstProp::Expression); + let pos = ctx.commit_schema(raw); let expr_id = serialize_expr(ctx, node.expr.as_ref(), pos); @@ -1056,22 +1109,24 @@ fn serialize_expr( pos } Expr::TsAs(node) => { - let id = ctx.header(AstNode::TSAsExpression, parent, &node.span, 2); + let raw = ctx.header(AstNode::TSAsExpression, parent, &node.span); let expr_pos = ctx.ref_field(AstProp::Expression); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); - let expr = serialize_expr(ctx, node.expr.as_ref(), id); - let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref(), id); + let expr = serialize_expr(ctx, node.expr.as_ref(), pos); + let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref(), pos); ctx.write_ref(expr_pos, expr); ctx.write_ref(type_ann_pos, type_ann); - id + pos } Expr::TsInstantiation(node) => { - let pos = ctx.header(AstNode::TsInstantiation, parent, &node.span, 1); + let raw = ctx.header(AstNode::TsInstantiation, parent, &node.span); let expr_pos = ctx.ref_field(AstProp::Expression); let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + let pos = ctx.commit_schema(raw); let expr = serialize_expr(ctx, node.expr.as_ref(), pos); @@ -1083,10 +1138,10 @@ fn serialize_expr( pos } Expr::TsSatisfies(node) => { - let pos = - ctx.header(AstNode::TSSatisfiesExpression, parent, &node.span, 2); + let raw = ctx.header(AstNode::TSSatisfiesExpression, parent, &node.span); let expr_pos = ctx.ref_field(AstProp::Expression); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); let epxr = serialize_expr(ctx, node.expr.as_ref(), pos); let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref(), pos); @@ -1098,21 +1153,22 @@ fn serialize_expr( } Expr::PrivateName(node) => serialize_private_name(ctx, node, parent), Expr::OptChain(node) => { - let pos = ctx.header(AstNode::ChainExpression, parent, &node.span, 1); + let raw = ctx.header(AstNode::ChainExpression, parent, &node.span); let arg_pos = ctx.ref_field(AstProp::Argument); + let pos = ctx.commit_schema(raw); let arg = match node.base.as_ref() { OptChainBase::Member(member_expr) => { serialize_member_expr(ctx, member_expr, pos, true) } OptChainBase::Call(opt_call) => { - let call_pos = - ctx.header(AstNode::CallExpression, pos, &opt_call.span, 4); + let raw = ctx.header(AstNode::CallExpression, pos, &opt_call.span); let opt_pos = ctx.bool_field(AstProp::Optional); let callee_pos = ctx.ref_field(AstProp::Callee); let type_args_pos = ctx.ref_field(AstProp::TypeArguments); let args_pos = ctx.ref_vec_field(AstProp::Arguments, opt_call.args.len()); + let call_pos = ctx.commit_schema(raw); let callee = serialize_expr(ctx, &opt_call.callee, pos); @@ -1158,7 +1214,7 @@ fn serialize_prop_or_spread( parent, ), PropOrSpread::Prop(prop) => { - let pos = ctx.header(AstNode::Property, parent, &prop.span(), 6); + let raw = ctx.header(AstNode::Property, parent, &prop.span()); let shorthand_pos = ctx.bool_field(AstProp::Shorthand); let computed_pos = ctx.bool_field(AstProp::Computed); @@ -1166,6 +1222,7 @@ fn serialize_prop_or_spread( let kind_pos = ctx.str_field(AstProp::Kind); let key_pos = ctx.ref_field(AstProp::Key); let value_pos = ctx.ref_field(AstProp::Value); + let pos = ctx.commit_schema(raw); let mut shorthand = false; let mut computed = false; @@ -1191,18 +1248,20 @@ fn serialize_prop_or_spread( (key, value) } Prop::Assign(assign_prop) => { - let child_id = - ctx.header(AstNode::AssignmentPattern, pos, &assign_prop.span, 2); + let raw = + ctx.header(AstNode::AssignmentPattern, pos, &assign_prop.span); let left_pos = ctx.ref_field(AstProp::Left); let right_pos = ctx.ref_field(AstProp::Right); + let child_pos = ctx.commit_schema(raw); - let left = serialize_ident(ctx, &assign_prop.key, child_id); - let right = serialize_expr(ctx, assign_prop.value.as_ref(), child_id); + let left = serialize_ident(ctx, &assign_prop.key, child_pos); + let right = + serialize_expr(ctx, assign_prop.value.as_ref(), child_pos); ctx.write_ref(left_pos, left); ctx.write_ref(right_pos, right); - (left, child_id) + (left, child_pos) } Prop::Getter(getter_prop) => { kind = "get"; @@ -1294,11 +1353,12 @@ fn serialize_member_expr( parent: NodeRef, optional: bool, ) -> NodeRef { - let pos = ctx.header(AstNode::MemberExpression, parent, &node.span, 4); + let raw = ctx.header(AstNode::MemberExpression, parent, &node.span); let opt_pos = ctx.bool_field(AstProp::Optional); let computed_pos = ctx.bool_field(AstProp::Computed); let obj_pos = ctx.ref_field(AstProp::Object); let prop_pos = ctx.ref_field(AstProp::Property); + let pos = ctx.commit_schema(raw); let obj = serialize_expr(ctx, node.obj.as_ref(), pos); @@ -1330,8 +1390,8 @@ fn serialize_class_member( ) -> NodeRef { match member { ClassMember::Constructor(constructor) => { - let member_id = - ctx.header(AstNode::MethodDefinition, parent, &constructor.span, 3); + let raw = + ctx.header(AstNode::MethodDefinition, parent, &constructor.span); let key_pos = ctx.ref_field(AstProp::Key); let body_pos = ctx.ref_field(AstProp::Body); let args_pos = @@ -1341,6 +1401,7 @@ fn serialize_class_member( } else { NodePos::Undef(ctx.undefined_field(AstProp::Accessibility)) }; + let member_id = ctx.commit_schema(raw); // FIXME flags @@ -1377,8 +1438,9 @@ fn serialize_class_member( member_id } ClassMember::Method(method) => { - let member_id = - ctx.header(AstNode::MethodDefinition, parent, &method.span, 0); + let raw = ctx.header(AstNode::MethodDefinition, parent, &method.span); + + let member_id = ctx.commit_schema(raw); // let mut flags = FlagValue::new(); // flags.set(Flag::ClassMethod); @@ -1439,8 +1501,10 @@ fn serialize_ident( ident: &Ident, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::Identifier, parent, &ident.span, 1); + let raw = ctx.header(AstNode::Identifier, parent, &ident.span); let name_pos = ctx.str_field(AstProp::Name); + let pos = ctx.commit_schema(raw); + ctx.write_str(name_pos, ident.sym.as_str()); pos @@ -1466,8 +1530,7 @@ fn serialize_decl( ) -> NodeRef { match decl { Decl::Class(node) => { - let id = - ctx.header(AstNode::ClassDeclaration, parent, &node.class.span, 8); + let raw = ctx.header(AstNode::ClassDeclaration, parent, &node.class.span); let declare_pos = ctx.bool_field(AstProp::Declare); let abstract_pos = ctx.bool_field(AstProp::Abstract); let id_pos = ctx.ref_field(AstProp::Id); @@ -1477,10 +1540,12 @@ fn serialize_decl( let super_type_pos = ctx.ref_field(AstProp::SuperTypeArguments); let impl_pos = ctx.ref_vec_field(AstProp::Implements, node.class.implements.len()); + let id = ctx.commit_schema(raw); - let body_id = ctx.header(AstNode::ClassBody, id, &node.class.span, 1); + let body_raw = ctx.header(AstNode::ClassBody, id, &node.class.span); let body_body_pos = ctx.ref_vec_field(AstProp::Body, node.class.body.len()); + let body_id = ctx.commit_schema(body_raw); let ident = serialize_ident(ctx, &node.ident, id); let type_params = @@ -1503,11 +1568,12 @@ fn serialize_decl( .implements .iter() .map(|implements| { - let child_pos = - ctx.header(AstNode::TSClassImplements, id, &implements.span, 2); + let raw = + ctx.header(AstNode::TSClassImplements, id, &implements.span); let expr_pos = ctx.ref_field(AstProp::Expression); let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + let child_pos = ctx.commit_schema(raw); let type_args = implements .type_args @@ -1546,12 +1612,8 @@ fn serialize_decl( id } Decl::Fn(node) => { - let pos = ctx.header( - AstNode::FunctionDeclaration, - parent, - &node.function.span, - 8, - ); + let raw = + ctx.header(AstNode::FunctionDeclaration, parent, &node.function.span); let declare_pos = ctx.bool_field(AstProp::Declare); let async_pos = ctx.bool_field(AstProp::Async); let gen_pos = ctx.bool_field(AstProp::Generator); @@ -1561,6 +1623,7 @@ fn serialize_decl( let body_pos = ctx.ref_field(AstProp::Body); let params_pos = ctx.ref_vec_field(AstProp::Params, node.function.params.len()); + let pos = ctx.commit_schema(raw); let ident_id = serialize_ident(ctx, &node.ident, parent); let type_param_id = @@ -1593,20 +1656,21 @@ fn serialize_decl( pos } Decl::Var(node) => { - let id = ctx.header(AstNode::VariableDeclaration, parent, &node.span, 3); + let raw = ctx.header(AstNode::VariableDeclaration, parent, &node.span); let declare_pos = ctx.bool_field(AstProp::Declare); let kind_pos = ctx.str_field(AstProp::Kind); let decls_pos = ctx.ref_vec_field(AstProp::Declarations, node.decls.len()); + let id = ctx.commit_schema(raw); let children = node .decls .iter() .map(|decl| { - let child_id = - ctx.header(AstNode::VariableDeclarator, id, &decl.span, 2); + let raw = ctx.header(AstNode::VariableDeclarator, id, &decl.span); let id_pos = ctx.ref_field(AstProp::Id); let init_pos = ctx.ref_field(AstProp::Init); + let child_id = ctx.commit_schema(raw); // FIXME: Definite? @@ -1641,17 +1705,18 @@ fn serialize_decl( todo!(); } Decl::TsInterface(node) => { - let pos = ctx.header(AstNode::TSInterface, parent, &node.span, 0); + let raw = ctx.header(AstNode::TSInterface, parent, &node.span); let declare_pos = ctx.bool_field(AstProp::Declare); let id_pos = ctx.ref_field(AstProp::Id); let extends_pos = ctx.ref_vec_field(AstProp::Extends, node.extends.len()); let type_param_pos = ctx.ref_field(AstProp::TypeParameters); let body_pos = ctx.ref_field(AstProp::Body); + let pos = ctx.commit_schema(raw); - let body_id = - ctx.header(AstNode::TSInterfaceBody, pos, &node.body.span, 0); + let body_raw = ctx.header(AstNode::TSInterfaceBody, pos, &node.body.span); let body_body_pos = ctx.ref_vec_field(AstProp::Body, node.body.body.len()); + let body_id = ctx.commit_schema(body_raw); let ident_id = serialize_ident(ctx, &node.id, pos); let type_param = @@ -1661,10 +1726,10 @@ fn serialize_decl( .extends .iter() .map(|item| { - let child_pos = - ctx.header(AstNode::TSInterfaceHeritage, pos, &item.span, 1); + let raw = ctx.header(AstNode::TSInterfaceHeritage, pos, &item.span); let type_args_pos = ctx.ref_field(AstProp::TypeArguments); let expr_pos = ctx.ref_field(AstProp::Expression); + let child_pos = ctx.commit_schema(raw); let expr = serialize_expr(ctx, &item.expr, child_pos); let type_args = item.type_args.clone().map(|params| { @@ -1684,16 +1749,16 @@ fn serialize_decl( .iter() .map(|item| match item { TsTypeElement::TsCallSignatureDecl(ts_call) => { - let item_id = ctx.header( + let raw = ctx.header( AstNode::TsCallSignatureDeclaration, pos, &ts_call.span, - 3, ); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); let params_pos = ctx.ref_vec_field(AstProp::Params, ts_call.params.len()); let return_pos = ctx.ref_field(AstProp::ReturnType); + let item_id = ctx.commit_schema(raw); let type_param = maybe_serialize_ts_type_param(ctx, &ts_call.type_params, pos); @@ -1713,8 +1778,7 @@ fn serialize_decl( } TsTypeElement::TsConstructSignatureDecl(_) => todo!(), TsTypeElement::TsPropertySignature(sig) => { - let item_pos = - ctx.header(AstNode::TSPropertySignature, pos, &sig.span, 6); + let raw = ctx.header(AstNode::TSPropertySignature, pos, &sig.span); let computed_pos = ctx.bool_field(AstProp::Computed); let optional_pos = ctx.bool_field(AstProp::Optional); @@ -1723,6 +1787,7 @@ fn serialize_decl( let _static_bos = ctx.bool_field(AstProp::Static); let key_pos = ctx.ref_field(AstProp::Key); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let item_pos = ctx.commit_schema(raw); let key = serialize_expr(ctx, &sig.key, item_pos); let type_ann = @@ -1737,8 +1802,7 @@ fn serialize_decl( item_pos } TsTypeElement::TsGetterSignature(sig) => { - let item_pos = - ctx.header(AstNode::TSMethodSignature, pos, &sig.span, 6); + let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); let computed_pos = ctx.bool_field(AstProp::Computed); let optional_pos = ctx.bool_field(AstProp::Optional); let readonly_pos = ctx.bool_field(AstProp::Readonly); @@ -1747,6 +1811,7 @@ fn serialize_decl( let kind_pos = ctx.str_field(AstProp::Kind); let key_pos = ctx.ref_field(AstProp::Key); let return_type_pos = ctx.ref_field(AstProp::ReturnType); + let item_pos = ctx.commit_schema(raw); let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); let return_type = @@ -1762,8 +1827,7 @@ fn serialize_decl( item_pos } TsTypeElement::TsSetterSignature(sig) => { - let item_pos = - ctx.header(AstNode::TSMethodSignature, pos, &sig.span, 6); + let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); let computed_pos = ctx.bool_field(AstProp::Computed); let optional_pos = ctx.bool_field(AstProp::Optional); let readonly_pos = ctx.bool_field(AstProp::Readonly); @@ -1772,6 +1836,7 @@ fn serialize_decl( let kind_pos = ctx.str_field(AstProp::Kind); let key_pos = ctx.ref_field(AstProp::Key); let params_pos = ctx.ref_vec_field(AstProp::Params, 1); + let item_pos = ctx.commit_schema(raw); let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); let params = serialize_ts_fn_param(ctx, &sig.param, item_pos); @@ -1786,8 +1851,7 @@ fn serialize_decl( item_pos } TsTypeElement::TsMethodSignature(sig) => { - let item_pos = - ctx.header(AstNode::TSMethodSignature, pos, &sig.span, 8); + let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); let computed_pos = ctx.bool_field(AstProp::Computed); let optional_pos = ctx.bool_field(AstProp::Optional); let readonly_pos = ctx.bool_field(AstProp::Readonly); @@ -1798,6 +1862,7 @@ fn serialize_decl( let params_pos = ctx.ref_vec_field(AstProp::Params, sig.params.len()); let return_type_pos = ctx.ref_field(AstProp::ReturnType); + let item_pos = ctx.commit_schema(raw); let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); let params = sig @@ -1836,11 +1901,12 @@ fn serialize_decl( pos } Decl::TsTypeAlias(node) => { - let pos = ctx.header(AstNode::TsTypeAlias, parent, &node.span, 4); + let raw = ctx.header(AstNode::TsTypeAlias, parent, &node.span); let declare_pos = ctx.bool_field(AstProp::Declare); let id_pos = ctx.ref_field(AstProp::Id); let type_params_pos = ctx.ref_field(AstProp::TypeParameters); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); let ident = serialize_ident(ctx, &node.id, pos); let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); @@ -1855,14 +1921,16 @@ fn serialize_decl( pos } Decl::TsEnum(node) => { - let pos = ctx.header(AstNode::TSEnumDeclaration, parent, &node.span, 3); + let raw = ctx.header(AstNode::TSEnumDeclaration, parent, &node.span); let declare_pos = ctx.bool_field(AstProp::Declare); let const_pos = ctx.bool_field(AstProp::Const); let id_pos = ctx.ref_field(AstProp::Id); let body_pos = ctx.ref_field(AstProp::Body); + let pos = ctx.commit_schema(raw); - let body = ctx.header(AstNode::TSEnumBody, pos, &node.span, 1); + let body_raw = ctx.header(AstNode::TSEnumBody, pos, &node.span); let members_pos = ctx.ref_vec_field(AstProp::Members, node.members.len()); + let body = ctx.commit_schema(body_raw); let ident_id = serialize_ident(ctx, &node.id, parent); @@ -1870,10 +1938,10 @@ fn serialize_decl( .members .iter() .map(|member| { - let member_id = - ctx.header(AstNode::TSEnumMember, body, &member.span, 2); + let raw = ctx.header(AstNode::TSEnumMember, body, &member.span); let id_pos = ctx.ref_field(AstProp::Id); let init_pos = ctx.ref_field(AstProp::Initializer); + let member_id = ctx.commit_schema(raw); let ident = match &member.id { TsEnumMemberId::Ident(ident) => { @@ -1906,7 +1974,8 @@ fn serialize_decl( pos } Decl::TsModule(ts_module_decl) => { - ctx.header(AstNode::TsModule, parent, &ts_module_decl.span, 0) + let raw = ctx.header(AstNode::TsModule, parent, &ts_module_decl.span); + ctx.commit_schema(raw) } } } @@ -1916,12 +1985,13 @@ fn serialize_ts_index_sig( node: &TsIndexSignature, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::TSMethodSignature, parent, &node.span, 4); + let raw = ctx.header(AstNode::TSMethodSignature, parent, &node.span); let readonly_pos = ctx.bool_field(AstProp::Readonly); // TODO: where is this coming from? let static_pos = ctx.bool_field(AstProp::Static); let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); @@ -1952,8 +2022,9 @@ fn serialize_private_name( node: &PrivateName, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::PrivateIdentifier, parent, &node.span, 1); + let raw = ctx.header(AstNode::PrivateIdentifier, parent, &node.span); let name_pos = ctx.str_field(AstProp::Name); + let pos = ctx.commit_schema(raw); ctx.write_str(name_pos, node.name.as_str()); @@ -1965,17 +2036,18 @@ fn serialize_jsx_element( node: &JSXElement, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::JSXElement, parent, &node.span, 3); + let raw = ctx.header(AstNode::JSXElement, parent, &node.span); let open_pos = ctx.ref_field(AstProp::OpeningElement); let close_pos = ctx.ref_field(AstProp::ClosingElement); let children_pos = ctx.ref_vec_field(AstProp::Children, node.children.len()); + let pos = ctx.commit_schema(raw); let open = serialize_jsx_opening_element(ctx, &node.opening, pos); let close = node.closing.as_ref().map(|closing| { - let closing_pos = - ctx.header(AstNode::JSXClosingElement, pos, &closing.span, 1); + let raw = ctx.header(AstNode::JSXClosingElement, pos, &closing.span); let name_pos = ctx.ref_field(AstProp::Name); + let closing_pos = ctx.commit_schema(raw); let name = serialize_jsx_element_name(ctx, &closing.name, closing_pos); ctx.write_ref(name_pos, name); @@ -1997,16 +2069,18 @@ fn serialize_jsx_fragment( node: &JSXFragment, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::JSXFragment, parent, &node.span, 3); + let raw = ctx.header(AstNode::JSXFragment, parent, &node.span); let opening_pos = ctx.ref_field(AstProp::OpeningFragment); let closing_pos = ctx.ref_field(AstProp::ClosingFragment); let children_pos = ctx.ref_vec_field(AstProp::Children, node.children.len()); + let pos = ctx.commit_schema(raw); - let opening_id = - ctx.header(AstNode::JSXOpeningFragment, pos, &node.opening.span, 0); - let closing_id = - ctx.header(AstNode::JSXClosingFragment, pos, &node.closing.span, 0); + let raw = ctx.header(AstNode::JSXOpeningFragment, pos, &node.opening.span); + let opening_id = ctx.commit_schema(raw); + + let raw = ctx.header(AstNode::JSXClosingFragment, pos, &node.closing.span); + let closing_id = ctx.commit_schema(raw); let children = serialize_jsx_children(ctx, &node.children, pos); @@ -2027,9 +2101,10 @@ fn serialize_jsx_children( .map(|child| { match child { JSXElementChild::JSXText(text) => { - let pos = ctx.header(AstNode::JSXText, parent, &text.span, 2); + let raw = ctx.header(AstNode::JSXText, parent, &text.span); let raw_pos = ctx.str_field(AstProp::Raw); let value_pos = ctx.str_field(AstProp::Value); + let pos = ctx.commit_schema(raw); ctx.write_str(raw_pos, &text.raw); ctx.write_str(value_pos, &text.value); @@ -2057,9 +2132,10 @@ fn serialize_jsx_member_expr( node: &JSXMemberExpr, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::JSXMemberExpression, parent, &node.span, 2); + let raw = ctx.header(AstNode::JSXMemberExpression, parent, &node.span); let obj_ref = ctx.ref_field(AstProp::Object); let prop_ref = ctx.ref_field(AstProp::Property); + let pos = ctx.commit_schema(raw); let obj = match &node.obj { JSXObject::JSXMemberExpr(member) => { @@ -2099,10 +2175,11 @@ fn serialize_jsx_opening_element( node: &JSXOpeningElement, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::JSXOpeningElement, parent, &node.span, 3); + let raw = ctx.header(AstNode::JSXOpeningElement, parent, &node.span); let sclose_pos = ctx.bool_field(AstProp::SelfClosing); let name_pos = ctx.ref_field(AstProp::Name); let attrs_pos = ctx.ref_vec_field(AstProp::Attributes, node.attrs.len()); + let pos = ctx.commit_schema(raw); let name = serialize_jsx_element_name(ctx, &node.name, pos); @@ -2113,9 +2190,10 @@ fn serialize_jsx_opening_element( .iter() .map(|attr| match attr { JSXAttrOrSpread::JSXAttr(attr) => { - let attr_pos = ctx.header(AstNode::JSXAttribute, pos, &attr.span, 2); + let raw = ctx.header(AstNode::JSXAttribute, pos, &attr.span); let name_pos = ctx.ref_field(AstProp::Name); let value_pos = ctx.ref_field(AstProp::Value); + let attr_pos = ctx.commit_schema(raw); let name = match &attr.name { JSXAttrName::Ident(name) => { @@ -2145,9 +2223,9 @@ fn serialize_jsx_opening_element( attr_pos } JSXAttrOrSpread::SpreadElement(spread) => { - let attr_pos = - ctx.header(AstNode::JSXAttribute, pos, &spread.dot3_token, 1); + let raw = ctx.header(AstNode::JSXAttribute, pos, &spread.dot3_token); let arg_pos = ctx.ref_field(AstProp::Argument); + let attr_pos = ctx.commit_schema(raw); let arg = serialize_expr(ctx, &spread.expr, attr_pos); @@ -2170,8 +2248,9 @@ fn serialize_jsx_container_expr( node: &JSXExprContainer, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::JSXExpressionContainer, parent, &node.span, 1); + let raw = ctx.header(AstNode::JSXExpressionContainer, parent, &node.span); let expr_pos = ctx.ref_field(AstProp::Expression); + let pos = ctx.commit_schema(raw); let expr = match &node.expr { JSXExpr::JSXEmptyExpr(expr) => serialize_jsx_empty_expr(ctx, expr, pos), @@ -2188,7 +2267,8 @@ fn serialize_jsx_empty_expr( node: &JSXEmptyExpr, parent: NodeRef, ) -> NodeRef { - ctx.header(AstNode::JSXEmptyExpression, parent, &node.span, 0) + let raw = ctx.header(AstNode::JSXEmptyExpression, parent, &node.span); + ctx.commit_schema(raw) } fn serialize_jsx_namespaced_name( @@ -2196,9 +2276,10 @@ fn serialize_jsx_namespaced_name( node: &JSXNamespacedName, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::JSXNamespacedName, parent, &node.span, 2); + let raw = ctx.header(AstNode::JSXNamespacedName, parent, &node.span); let ns_pos = ctx.ref_field(AstProp::Namespace); let name_pos = ctx.ref_field(AstProp::Name); + let pos = ctx.commit_schema(raw); let ns_id = serialize_ident_name_as_jsx_identifier(ctx, &node.ns, pos); let name_id = serialize_ident_name_as_jsx_identifier(ctx, &node.name, pos); @@ -2214,8 +2295,9 @@ fn serialize_ident_name_as_jsx_identifier( node: &IdentName, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::JSXIdentifier, parent, &node.span, 1); + let raw = ctx.header(AstNode::JSXIdentifier, parent, &node.span); let name_pos = ctx.str_field(AstProp::Name); + let pos = ctx.commit_schema(raw); ctx.write_str(name_pos, &node.sym); @@ -2227,8 +2309,9 @@ fn serialize_jsx_identifier( node: &Ident, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::JSXIdentifier, parent, &node.span, 1); + let raw = ctx.header(AstNode::JSXIdentifier, parent, &node.span); let name_pos = ctx.str_field(AstProp::Name); + let pos = ctx.commit_schema(raw); ctx.write_str(name_pos, &node.sym); @@ -2243,10 +2326,11 @@ fn serialize_pat( match pat { Pat::Ident(node) => serialize_ident(ctx, &node.id, parent), Pat::Array(node) => { - let pos = ctx.header(AstNode::ArrayPattern, parent, &node.span, 3); + let raw = ctx.header(AstNode::ArrayPattern, parent, &node.span); let opt_pos = ctx.bool_field(AstProp::Optional); let type_pos = ctx.ref_field(AstProp::TypeAnnotation); let elems_pos = ctx.ref_vec_field(AstProp::Elements, node.elems.len()); + let pos = ctx.commit_schema(raw); let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); @@ -2267,9 +2351,10 @@ fn serialize_pat( pos } Pat::Rest(node) => { - let pos = ctx.header(AstNode::RestElement, parent, &node.span, 2); + let raw = ctx.header(AstNode::RestElement, parent, &node.span); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); let arg_pos = ctx.ref_field(AstProp::Argument); + let pos = ctx.commit_schema(raw); let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); let arg = serialize_pat(ctx, &node.arg, parent); @@ -2280,10 +2365,11 @@ fn serialize_pat( pos } Pat::Object(node) => { - let pos = ctx.header(AstNode::ObjectPattern, parent, &node.span, 3); + let raw = ctx.header(AstNode::ObjectPattern, parent, &node.span); let opt_pos = ctx.bool_field(AstProp::Optional); let props_pos = ctx.ref_vec_field(AstProp::Properties, node.props.len()); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); @@ -2292,11 +2378,12 @@ fn serialize_pat( .iter() .map(|prop| match prop { ObjectPatProp::KeyValue(key_value_prop) => { - let child_pos = - ctx.header(AstNode::Property, pos, &key_value_prop.span(), 3); + let raw = + ctx.header(AstNode::Property, pos, &key_value_prop.span()); let computed_pos = ctx.bool_field(AstProp::Computed); let key_pos = ctx.ref_field(AstProp::Key); let value_pos = ctx.ref_field(AstProp::Value); + let child_pos = ctx.commit_schema(raw); let computed = matches!(key_value_prop.key, PropName::Computed(_)); @@ -2311,12 +2398,12 @@ fn serialize_pat( child_pos } ObjectPatProp::Assign(assign_pat_prop) => { - let child_pos = - ctx.header(AstNode::Property, pos, &assign_pat_prop.span, 3); + let raw = ctx.header(AstNode::Property, pos, &assign_pat_prop.span); // TOOD: Doesn't seem to be present in SWC ast let _computed_pos = ctx.bool_field(AstProp::Computed); let key_pos = ctx.ref_field(AstProp::Key); let value_pos = ctx.ref_field(AstProp::Value); + let child_pos = ctx.commit_schema(raw); let ident = serialize_ident(ctx, &assign_pat_prop.key.id, parent); @@ -2343,9 +2430,10 @@ fn serialize_pat( pos } Pat::Assign(node) => { - let pos = ctx.header(AstNode::AssignmentPattern, parent, &node.span, 2); + let raw = ctx.header(AstNode::AssignmentPattern, parent, &node.span); let left_pos = ctx.ref_field(AstProp::Left); let right_pos = ctx.ref_field(AstProp::Right); + let pos = ctx.commit_schema(raw); let left = serialize_pat(ctx, &node.left, pos); let right = serialize_expr(ctx, &node.right, pos); @@ -2382,8 +2470,9 @@ fn serialize_spread( span: &Span, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::SpreadElement, parent, span, 1); + let raw = ctx.header(AstNode::SpreadElement, parent, span); let arg_pos = ctx.ref_field(AstProp::Argument); + let pos = ctx.commit_schema(raw); let expr_pos = serialize_expr(ctx, expr, parent); ctx.write_ref(arg_pos, expr_pos); @@ -2396,8 +2485,10 @@ fn serialize_ident_name( ident_name: &IdentName, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::Identifier, parent, &ident_name.span, 1); + let raw = ctx.header(AstNode::Identifier, parent, &ident_name.span); let name_pos = ctx.str_field(AstProp::Name); + let pos = ctx.commit_schema(raw); + ctx.write_str(name_pos, ident_name.sym.as_str()); pos @@ -2413,12 +2504,10 @@ fn serialize_prop_name( serialize_ident_name(ctx, ident_name, parent) } PropName::Str(str_prop) => { - let child_pos = - ctx.header(AstNode::StringLiteral, parent, &str_prop.span, 1); + let raw = ctx.header(AstNode::StringLiteral, parent, &str_prop.span); let value_pos = ctx.str_field(AstProp::Value); ctx.write_str(value_pos, &str_prop.value); - - child_pos + ctx.commit_schema(raw) } PropName::Num(number) => { serialize_lit(ctx, &Lit::Num(number.clone()), parent) @@ -2437,25 +2526,31 @@ fn serialize_lit( ) -> NodeRef { match lit { Lit::Str(node) => { - let pos = ctx.header(AstNode::StringLiteral, parent, &node.span, 1); + let raw = ctx.header(AstNode::StringLiteral, parent, &node.span); let value_pos = ctx.str_field(AstProp::Value); + let pos = ctx.commit_schema(raw); ctx.write_str(value_pos, &node.value); pos } Lit::Bool(lit_bool) => { - let pos = ctx.header(AstNode::Bool, parent, &lit_bool.span, 1); + let raw = ctx.header(AstNode::Bool, parent, &lit_bool.span); let value_pos = ctx.bool_field(AstProp::Value); + let pos = ctx.commit_schema(raw); ctx.write_bool(value_pos, lit_bool.value); pos } - Lit::Null(node) => ctx.header(AstNode::Null, parent, &node.span, 0), + Lit::Null(node) => { + let raw = ctx.header(AstNode::Null, parent, &node.span); + ctx.commit_schema(raw) + } Lit::Num(node) => { - let pos = ctx.header(AstNode::NumericLiteral, parent, &node.span, 1); + let raw = ctx.header(AstNode::NumericLiteral, parent, &node.span); let value_pos = ctx.str_field(AstProp::Value); + let pos = ctx.commit_schema(raw); let value = node.raw.as_ref().unwrap(); ctx.write_str(value_pos, value); @@ -2463,17 +2558,19 @@ fn serialize_lit( pos } Lit::BigInt(node) => { - let pos = ctx.header(AstNode::BigIntLiteral, parent, &node.span, 1); + let raw = ctx.header(AstNode::BigIntLiteral, parent, &node.span); let value_pos = ctx.str_field(AstProp::Value); + let pos = ctx.commit_schema(raw); ctx.write_str(value_pos, &node.value.to_string()); pos } Lit::Regex(node) => { - let pos = ctx.header(AstNode::RegExpLiteral, parent, &node.span, 2); + let raw = ctx.header(AstNode::RegExpLiteral, parent, &node.span); let pattern_pos = ctx.str_field(AstProp::Pattern); let flags_pos = ctx.str_field(AstProp::Flags); + let pos = ctx.commit_schema(raw); ctx.write_str(pattern_pos, node.exp.as_str()); ctx.write_str(flags_pos, node.flags.as_str()); @@ -2481,7 +2578,8 @@ fn serialize_lit( pos } Lit::JSXText(jsxtext) => { - ctx.header(AstNode::JSXText, parent, &jsxtext.span, 0) + let raw = ctx.header(AstNode::JSXText, parent, &jsxtext.span); + ctx.commit_schema(raw) } } } @@ -2491,9 +2589,10 @@ fn serialize_ts_param_inst( node: &TsTypeParamInstantiation, parent: NodeRef, ) -> NodeRef { - let pos = - ctx.header(AstNode::TSTypeParameterInstantiation, parent, &node.span, 1); + let raw = + ctx.header(AstNode::TSTypeParameterInstantiation, parent, &node.span); let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); + let pos = ctx.commit_schema(raw); let params = node .params @@ -2529,15 +2628,18 @@ fn serialize_ts_type( TsKeywordTypeKind::TsIntrinsicKeyword => AstNode::TSIntrinsicKeyword, }; - ctx.header(kind, parent, &node.span, 0) + let raw = ctx.header(kind, parent, &node.span); + ctx.commit_schema(raw) } TsType::TsThisType(node) => { - ctx.header(AstNode::TSThisType, parent, &node.span, 0) + let raw = ctx.header(AstNode::TSThisType, parent, &node.span); + ctx.commit_schema(raw) } TsType::TsFnOrConstructorType(node) => match node { TsFnOrConstructorType::TsFnType(node) => { - let pos = ctx.header(AstNode::TSFunctionType, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSFunctionType, parent, &node.span); let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); + let pos = ctx.commit_schema(raw); let param_ids = node .params @@ -2554,9 +2656,10 @@ fn serialize_ts_type( } }, TsType::TsTypeRef(node) => { - let pos = ctx.header(AstNode::TSTypeReference, parent, &node.span, 2); + let raw = ctx.header(AstNode::TSTypeReference, parent, &node.span); let name_pos = ctx.ref_field(AstProp::TypeName); let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + let pos = ctx.commit_schema(raw); let name = serialize_ts_entity_name(ctx, &node.type_name, pos); @@ -2571,9 +2674,10 @@ fn serialize_ts_type( pos } TsType::TsTypeQuery(node) => { - let pos = ctx.header(AstNode::TSTypeQuery, parent, &node.span, 2); + let raw = ctx.header(AstNode::TSTypeQuery, parent, &node.span); let name_pos = ctx.ref_field(AstProp::ExprName); let type_args_pos = ctx.ref_field(AstProp::TypeArguments); + let pos = ctx.commit_schema(raw); let expr_name = match &node.expr_name { TsTypeQueryExpr::TsEntityName(entity) => { @@ -2599,8 +2703,9 @@ fn serialize_ts_type( todo!() } TsType::TsArrayType(node) => { - let pos = ctx.header(AstNode::TSArrayType, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSArrayType, parent, &node.span); let elem_pos = ctx.ref_field(AstProp::ElementType); + let pos = ctx.commit_schema(raw); let elem = serialize_ts_type(ctx, &node.elem_type, pos); @@ -2609,19 +2714,20 @@ fn serialize_ts_type( pos } TsType::TsTupleType(node) => { - let pos = ctx.header(AstNode::TSTupleType, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSTupleType, parent, &node.span); let children_pos = ctx.ref_vec_field(AstProp::ElementTypes, node.elem_types.len()); + let pos = ctx.commit_schema(raw); let children = node .elem_types .iter() .map(|elem| { if let Some(label) = &elem.label { - let child_pos = - ctx.header(AstNode::TSNamedTupleMember, pos, &elem.span, 1); + let raw = ctx.header(AstNode::TSNamedTupleMember, pos, &elem.span); let label_pos = ctx.ref_field(AstProp::Label); let type_pos = ctx.ref_field(AstProp::ElementType); + let child_pos = ctx.commit_schema(raw); let label_id = serialize_pat(ctx, label, child_pos); let type_id = serialize_ts_type(ctx, elem.ty.as_ref(), child_pos); @@ -2642,8 +2748,9 @@ fn serialize_ts_type( } TsType::TsOptionalType(_) => todo!(), TsType::TsRestType(node) => { - let pos = ctx.header(AstNode::TSRestType, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSRestType, parent, &node.span); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); @@ -2653,8 +2760,9 @@ fn serialize_ts_type( } TsType::TsUnionOrIntersectionType(node) => match node { TsUnionOrIntersectionType::TsUnionType(node) => { - let pos = ctx.header(AstNode::TSUnionType, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSUnionType, parent, &node.span); let types_pos = ctx.ref_vec_field(AstProp::Types, node.types.len()); + let pos = ctx.commit_schema(raw); let children = node .types @@ -2667,9 +2775,9 @@ fn serialize_ts_type( pos } TsUnionOrIntersectionType::TsIntersectionType(node) => { - let pos = - ctx.header(AstNode::TSIntersectionType, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSIntersectionType, parent, &node.span); let types_pos = ctx.ref_vec_field(AstProp::Types, node.types.len()); + let pos = ctx.commit_schema(raw); let children = node .types @@ -2683,11 +2791,12 @@ fn serialize_ts_type( } }, TsType::TsConditionalType(node) => { - let pos = ctx.header(AstNode::TSConditionalType, parent, &node.span, 4); + let raw = ctx.header(AstNode::TSConditionalType, parent, &node.span); let check_pos = ctx.ref_field(AstProp::CheckType); let extends_pos = ctx.ref_field(AstProp::ExtendsType); let true_pos = ctx.ref_field(AstProp::TrueType); let false_pos = ctx.ref_field(AstProp::FalseType); + let pos = ctx.commit_schema(raw); let check = serialize_ts_type(ctx, &node.check_type, pos); let extends = serialize_ts_type(ctx, &node.extends_type, pos); @@ -2702,8 +2811,9 @@ fn serialize_ts_type( pos } TsType::TsInferType(node) => { - let pos = ctx.header(AstNode::TSInferType, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSInferType, parent, &node.span); let param_pos = ctx.ref_field(AstProp::TypeParameter); + let pos = ctx.commit_schema(raw); let param = serialize_ts_type_param(ctx, &node.type_param, parent); @@ -2713,10 +2823,10 @@ fn serialize_ts_type( } TsType::TsParenthesizedType(_) => todo!(), TsType::TsTypeOperator(node) => { - let pos = ctx.header(AstNode::TSTypeOperator, parent, &node.span, 2); - + let raw = ctx.header(AstNode::TSTypeOperator, parent, &node.span); let operator_pos = ctx.str_field(AstProp::Operator); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); @@ -2733,10 +2843,10 @@ fn serialize_ts_type( pos } TsType::TsIndexedAccessType(node) => { - let pos = ctx.header(AstNode::TSIndexedAccessType, parent, &node.span, 2); - + let raw = ctx.header(AstNode::TSIndexedAccessType, parent, &node.span); let index_type_pos = ctx.ref_field(AstProp::IndexType); let obj_type_pos = ctx.ref_field(AstProp::ObjectType); + let pos = ctx.commit_schema(raw); let index = serialize_ts_type(ctx, &node.index_type, pos); let obj = serialize_ts_type(ctx, &node.obj_type, pos); @@ -2747,11 +2857,11 @@ fn serialize_ts_type( pos } TsType::TsMappedType(node) => { - let pos = ctx.header(AstNode::TSMappedType, parent, &node.span, 5); - + let raw = ctx.header(AstNode::TSMappedType, parent, &node.span); let name_pos = ctx.ref_field(AstProp::NameType); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); let type_param_pos = ctx.ref_field(AstProp::TypeParameter); + let pos = ctx.commit_schema(raw); let opt_pos = create_true_plus_minus_field(ctx, AstProp::Optional, node.optional); @@ -2772,15 +2882,16 @@ fn serialize_ts_type( } TsType::TsLitType(node) => serialize_ts_lit_type(ctx, node, parent), TsType::TsTypePredicate(node) => { - let pos = ctx.header(AstNode::TSTypePredicate, parent, &node.span, 3); - + let raw = ctx.header(AstNode::TSTypePredicate, parent, &node.span); let asserts_pos = ctx.bool_field(AstProp::Asserts); let param_name_pos = ctx.ref_field(AstProp::ParameterName); let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); let param_name = match &node.param_name { TsThisTypeOrIdent::TsThisType(ts_this_type) => { - ctx.header(AstNode::TSThisType, pos, &ts_this_type.span, 0) + let raw = ctx.header(AstNode::TSThisType, pos, &ts_this_type.span); + ctx.commit_schema(raw) } TsThisTypeOrIdent::Ident(ident) => serialize_ident(ctx, ident, pos), }; @@ -2794,10 +2905,11 @@ fn serialize_ts_type( pos } TsType::TsImportType(node) => { - let pos = ctx.header(AstNode::TSTypePredicate, parent, &node.span, 3); + let raw = ctx.header(AstNode::TSTypePredicate, parent, &node.span); let arg_pos = ctx.ref_field(AstProp::Argument); let type_args_pos = ctx.ref_field(AstProp::TypeArguments); let qualifier_pos = ctx.ref_field(AstProp::Qualifier); + let pos = ctx.commit_schema(raw); let arg = serialize_ts_lit_type( ctx, @@ -2830,8 +2942,9 @@ fn serialize_ts_lit_type( node: &TsLitType, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::TSLiteralType, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSLiteralType, parent, &node.span); let lit_pos = ctx.ref_field(AstProp::Literal); + let pos = ctx.commit_schema(raw); let lit = match &node.lit { TsLit::Number(lit) => serialize_lit(ctx, &Lit::Num(lit.clone()), pos), @@ -2931,8 +3044,9 @@ fn serialize_ts_type_ann( node: &TsTypeAnn, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::TSTypeAnnotation, parent, &node.span, 1); + let raw = ctx.header(AstNode::TSTypeAnnotation, parent, &node.span); let type_pos = ctx.ref_field(AstProp::TypeAnnotation); + let pos = ctx.commit_schema(raw); let v_type = serialize_ts_type(ctx, &node.type_ann, pos); @@ -2956,13 +3070,14 @@ fn serialize_ts_type_param( node: &TsTypeParam, parent: NodeRef, ) -> NodeRef { - let pos = ctx.header(AstNode::TSTypeParameter, parent, &node.span, 6); + let raw = ctx.header(AstNode::TSTypeParameter, parent, &node.span); let name_pos = ctx.ref_field(AstProp::Name); let constraint_pos = ctx.ref_field(AstProp::Constraint); let default_pos = ctx.ref_field(AstProp::Default); let const_pos = ctx.bool_field(AstProp::Const); let in_pos = ctx.bool_field(AstProp::In); let out_pos = ctx.bool_field(AstProp::Out); + let pos = ctx.commit_schema(raw); let name = serialize_ident(ctx, &node.name, pos); let constraint = maybe_serialize_ts_type(ctx, &node.constraint, pos); @@ -2984,9 +3099,10 @@ fn maybe_serialize_ts_type_param( parent: NodeRef, ) -> Option { node.as_ref().map(|node| { - let pos = - ctx.header(AstNode::TSTypeParameterDeclaration, parent, &node.span, 1); + let raw = + ctx.header(AstNode::TSTypeParameterDeclaration, parent, &node.span); let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); + let pos = ctx.commit_schema(raw); let params = node .params diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs index 64dbd82cde..29bdb0d378 100644 --- a/cli/tools/lint/ast_buffer/ts_estree.rs +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -12,6 +12,7 @@ use super::buffer::FieldArrPos; use super::buffer::FieldPos; use super::buffer::NodeRef; use super::buffer::NullPos; +use super::buffer::PendingNodeRef; use super::buffer::SerializeCtx; use super::buffer::StrPos; use super::buffer::UndefPos; @@ -447,10 +448,10 @@ impl TsEsTreeBuilder { pub fn new() -> Self { // Max values // TODO: Maybe there is a rust macro to grab the last enum value? - let kind_count: u8 = AstNode::TSEnumBody.into(); - let prop_count: u8 = AstProp::Value.into(); + let kind_max_count: u8 = u8::from(AstNode::TSEnumBody) + 1; + let prop_max_count: u8 = u8::from(AstProp::Value) + 1; Self { - ctx: SerializeCtx::new(kind_count, prop_count), + ctx: SerializeCtx::new(kind_max_count, prop_max_count), } } } @@ -461,9 +462,12 @@ impl AstBufSerializer for TsEsTreeBuilder { kind: AstNode, parent: NodeRef, span: &Span, - prop_count: usize, - ) -> NodeRef { - self.ctx.header(kind, parent, span, prop_count) + ) -> PendingNodeRef { + self.ctx.header(kind, parent, span) + } + + fn commit_schema(&mut self, offset: PendingNodeRef) -> NodeRef { + self.ctx.commit_schema(offset) } fn ref_field(&mut self, prop: AstProp) -> FieldPos { From 7b491a28df20ddc8fe0a9944d3c45e7872bff8ec Mon Sep 17 00:00:00 2001 From: Je Xia Date: Tue, 31 Dec 2024 18:06:21 +0800 Subject: [PATCH 046/107] fix(node): Add missing `inspector/promises` (#27491) Add missing `inspector/promises` in node builtin module list, that causes types checking error. --- cli/tsc/00_typescript.js | 1 + ext/node/polyfill.rs | 1 + tests/integration/lsp_tests.rs | 1 + tests/unit_node/inspector_test.ts | 11 +++++++++++ 4 files changed, 14 insertions(+) diff --git a/cli/tsc/00_typescript.js b/cli/tsc/00_typescript.js index 7d20f92367..b7626fe082 100644 --- a/cli/tsc/00_typescript.js +++ b/cli/tsc/00_typescript.js @@ -136063,6 +136063,7 @@ var unprefixedNodeCoreModuleList = [ "https", "http2", "inspector", + "inspector/promises", "module", "net", "os", diff --git a/ext/node/polyfill.rs b/ext/node/polyfill.rs index 42cc794955..556cb48a42 100644 --- a/ext/node/polyfill.rs +++ b/ext/node/polyfill.rs @@ -57,6 +57,7 @@ generate_builtin_node_module_lists! { "http2", "https", "inspector", + "inspector/promises", "module", "net", "os", diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 825cef6247..ee31b61751 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -8712,6 +8712,7 @@ fn lsp_completions_node_specifier() { "node:http2", "node:https", "node:inspector", + "node:inspector/promises", "node:module", "node:net", "node:os", diff --git a/tests/unit_node/inspector_test.ts b/tests/unit_node/inspector_test.ts index a53e977bb6..0eb3f5a07b 100644 --- a/tests/unit_node/inspector_test.ts +++ b/tests/unit_node/inspector_test.ts @@ -1,5 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. import inspector, { Session } from "node:inspector"; +import inspectorPromises, { + Session as SessionPromise, +} from "node:inspector/promises"; import { assertEquals } from "@std/assert/equals"; Deno.test("[node/inspector] - importing inspector works", () => { @@ -9,3 +12,11 @@ Deno.test("[node/inspector] - importing inspector works", () => { Deno.test("[node/inspector] - Session constructor should not throw", () => { new Session(); }); + +Deno.test("[node/inspector/promises] - importing inspector works", () => { + assertEquals(typeof inspectorPromises.open, "function"); +}); + +Deno.test("[node/inspector/promises] - Session constructor should not throw", () => { + new SessionPromise(); +}); From 1cd36009b0f5eeae0eb337415b35f7a27702bd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 31 Dec 2024 12:49:02 +0000 Subject: [PATCH 047/107] fix(ext/node): support private key export in JWK format (#27325) Closes https://github.com/denoland/deno/issues/26643 --------- Co-authored-by: Divy Srivastava --- ext/node/lib.rs | 1 + ext/node/ops/crypto/keys.rs | 109 +++++++++++++++++++++ ext/node/polyfills/internal/crypto/keys.ts | 3 +- tests/unit_node/crypto/crypto_key_test.ts | 16 +++ 4 files changed, 128 insertions(+), 1 deletion(-) diff --git a/ext/node/lib.rs b/ext/node/lib.rs index b9b459efc1..0c4791eb72 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -262,6 +262,7 @@ deno_core::extension!(deno_node, ops::crypto::keys::op_node_derive_public_key_from_private_key, ops::crypto::keys::op_node_dh_keys_generate_and_export, ops::crypto::keys::op_node_export_private_key_der, + ops::crypto::keys::op_node_export_private_key_jwk, ops::crypto::keys::op_node_export_private_key_pem, ops::crypto::keys::op_node_export_public_key_der, ops::crypto::keys::op_node_export_public_key_pem, diff --git a/ext/node/ops/crypto/keys.rs b/ext/node/ops/crypto/keys.rs index f164972d48..dfcd3d11bf 100644 --- a/ext/node/ops/crypto/keys.rs +++ b/ext/node/ops/crypto/keys.rs @@ -26,6 +26,7 @@ use rsa::pkcs1::DecodeRsaPrivateKey as _; use rsa::pkcs1::DecodeRsaPublicKey; use rsa::pkcs1::EncodeRsaPrivateKey as _; use rsa::pkcs1::EncodeRsaPublicKey; +use rsa::traits::PrivateKeyParts; use rsa::traits::PublicKeyParts; use rsa::RsaPrivateKey; use rsa::RsaPublicKey; @@ -255,6 +256,16 @@ impl EcPrivateKey { EcPrivateKey::P384(key) => EcPublicKey::P384(key.public_key()), } } + + pub fn to_jwk(&self) -> Result { + match self { + EcPrivateKey::P224(_) => { + Err(AsymmetricPrivateKeyJwkError::UnsupportedJwkEcCurveP224) + } + EcPrivateKey::P256(key) => Ok(key.to_jwk()), + EcPrivateKey::P384(key) => Ok(key.to_jwk()), + } + } } // https://oidref.com/ @@ -1107,6 +1118,16 @@ fn bytes_to_b64(bytes: &[u8]) -> String { BASE64_URL_SAFE_NO_PAD.encode(bytes) } +#[derive(Debug, thiserror::Error)] +pub enum AsymmetricPrivateKeyJwkError { + #[error("key is not an asymmetric private key")] + KeyIsNotAsymmetricPrivateKey, + #[error("Unsupported JWK EC curve: P224")] + UnsupportedJwkEcCurveP224, + #[error("jwk export not implemented for this key type")] + JwkExportNotImplementedForKeyType, +} + #[derive(Debug, thiserror::Error)] pub enum AsymmetricPublicKeyJwkError { #[error("key is not an asymmetric public key")] @@ -1328,7 +1349,73 @@ pub enum AsymmetricPrivateKeyDerError { UnsupportedKeyType(String), } +// https://datatracker.ietf.org/doc/html/rfc7518#section-6.3.2 +fn rsa_private_to_jwk(key: &RsaPrivateKey) -> deno_core::serde_json::Value { + let n = key.n(); + let e = key.e(); + let d = key.d(); + let p = &key.primes()[0]; + let q = &key.primes()[1]; + let dp = key.dp(); + let dq = key.dq(); + let qi = key.crt_coefficient(); + let oth = &key.primes()[2..]; + + let mut obj = deno_core::serde_json::json!({ + "kty": "RSA", + "n": bytes_to_b64(&n.to_bytes_be()), + "e": bytes_to_b64(&e.to_bytes_be()), + "d": bytes_to_b64(&d.to_bytes_be()), + "p": bytes_to_b64(&p.to_bytes_be()), + "q": bytes_to_b64(&q.to_bytes_be()), + "dp": dp.map(|dp| bytes_to_b64(&dp.to_bytes_be())), + "dq": dq.map(|dq| bytes_to_b64(&dq.to_bytes_be())), + "qi": qi.map(|qi| bytes_to_b64(&qi.to_bytes_be())), + }); + + if !oth.is_empty() { + obj["oth"] = deno_core::serde_json::json!(oth + .iter() + .map(|o| o.to_bytes_be()) + .collect::>()); + } + + obj +} + impl AsymmetricPrivateKey { + fn export_jwk( + &self, + ) -> Result { + match self { + AsymmetricPrivateKey::Rsa(key) => Ok(rsa_private_to_jwk(key)), + AsymmetricPrivateKey::RsaPss(key) => Ok(rsa_private_to_jwk(&key.key)), + AsymmetricPrivateKey::Ec(key) => { + let jwk = key.to_jwk()?; + Ok(deno_core::serde_json::json!(jwk)) + } + AsymmetricPrivateKey::X25519(static_secret) => { + let bytes = static_secret.to_bytes(); + + Ok(deno_core::serde_json::json!({ + "kty": "OKP", + "crv": "X25519", + "d": bytes_to_b64(&bytes), + })) + } + AsymmetricPrivateKey::Ed25519(key) => { + let bytes = key.to_bytes(); + + Ok(deno_core::serde_json::json!({ + "kty": "OKP", + "crv": "Ed25519", + "d": bytes_to_b64(&bytes), + })) + } + _ => Err(AsymmetricPrivateKeyJwkError::JwkExportNotImplementedForKeyType), + } + } + fn export_der( &self, typ: &str, @@ -2329,6 +2416,28 @@ pub fn op_node_export_private_key_pem( Ok(String::from_utf8(out).expect("invalid pem is not possible")) } +#[derive(Debug, thiserror::Error)] +pub enum ExportPrivateKeyJwkError { + #[error(transparent)] + AsymmetricPublicKeyJwk(#[from] AsymmetricPrivateKeyJwkError), + #[error("very large data")] + VeryLargeData, + #[error(transparent)] + Der(#[from] der::Error), +} + +#[op2] +#[serde] +pub fn op_node_export_private_key_jwk( + #[cppgc] handle: &KeyObjectHandle, +) -> Result { + let private_key = handle + .as_private_key() + .ok_or(AsymmetricPrivateKeyJwkError::KeyIsNotAsymmetricPrivateKey)?; + + Ok(private_key.export_jwk()?) +} + #[op2] #[buffer] pub fn op_node_export_private_key_der( diff --git a/ext/node/polyfills/internal/crypto/keys.ts b/ext/node/polyfills/internal/crypto/keys.ts index c91c23cc3d..932856df0e 100644 --- a/ext/node/polyfills/internal/crypto/keys.ts +++ b/ext/node/polyfills/internal/crypto/keys.ts @@ -20,6 +20,7 @@ import { op_node_create_secret_key, op_node_derive_public_key_from_private_key, op_node_export_private_key_der, + op_node_export_private_key_jwk, op_node_export_private_key_pem, op_node_export_public_key_der, op_node_export_public_key_jwk, @@ -791,7 +792,7 @@ export class PrivateKeyObject extends AsymmetricKeyObject { export(options: JwkKeyExportOptions | KeyExportOptions) { if (options && options.format === "jwk") { - notImplemented("jwk private key export not implemented"); + return op_node_export_private_key_jwk(this[kHandle]); } const { format, diff --git a/tests/unit_node/crypto/crypto_key_test.ts b/tests/unit_node/crypto/crypto_key_test.ts index 5d206acc72..82306d02fe 100644 --- a/tests/unit_node/crypto/crypto_key_test.ts +++ b/tests/unit_node/crypto/crypto_key_test.ts @@ -700,3 +700,19 @@ Deno.test("generateKeyPair promisify", async () => { assert(publicKey.startsWith("-----BEGIN PUBLIC KEY-----")); assert(privateKey.startsWith("-----BEGIN PRIVATE KEY-----")); }); + +Deno.test("RSA export private JWK", function () { + // @ts-ignore @types/node broken + const { privateKey, publicKey } = generateKeyPairSync("rsa", { + modulusLength: 4096, + publicKeyEncoding: { + format: "jwk", + }, + privateKeyEncoding: { + format: "jwk", + }, + }); + + assertEquals((privateKey as any).kty, "RSA"); + assertEquals((privateKey as any).n, (publicKey as any).n); +}); From 4638caa74044cf5100f515b49c3a86522f013fc9 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 31 Dec 2024 11:29:07 -0500 Subject: [PATCH 048/107] refactor: do not use `deno_fs::FileSystem` everywhere (#27508) This changes the cli to mostly use `std::fs` via `sys_traits` instead of the implemention of `deno_fs::FileSystem`. --- Cargo.lock | 10 +- Cargo.toml | 2 +- cli/Cargo.toml | 3 +- cli/args/lockfile.rs | 21 +- cli/args/mod.rs | 20 +- cli/cache/deno_dir.rs | 13 +- cli/cache/disk_cache.rs | 22 +- cli/cache/emit.rs | 5 +- cli/cache/mod.rs | 13 +- cli/clippy.toml | 1 + cli/factory.rs | 68 ++-- cli/file_fetcher.rs | 58 ++-- cli/graph_util.rs | 12 +- cli/lsp/cache.rs | 11 +- cli/lsp/config.rs | 36 ++- cli/lsp/diagnostics.rs | 4 +- cli/lsp/language_server.rs | 8 +- cli/lsp/registries.rs | 9 +- cli/lsp/resolver.rs | 32 +- cli/main.rs | 1 + cli/mainrt.rs | 7 +- cli/module_loader.rs | 14 +- cli/node.rs | 6 +- cli/npm/byonm.rs | 6 +- cli/npm/managed/mod.rs | 10 +- cli/npm/managed/resolvers/common.rs | 8 +- cli/npm/managed/resolvers/global.rs | 4 +- cli/npm/managed/resolvers/local.rs | 8 +- cli/npm/managed/resolvers/mod.rs | 4 +- cli/npm/mod.rs | 12 +- cli/resolver.rs | 20 +- cli/standalone/binary.rs | 4 - cli/standalone/code_cache.rs | 3 +- cli/standalone/file_system.rs | 450 +++++++++++++++++++++++++- cli/standalone/mod.rs | 27 +- cli/standalone/virtual_fs.rs | 264 +++++++-------- cli/sys.rs | 218 +++++++++++++ cli/task_runner.rs | 6 +- cli/tools/bench/mod.rs | 3 +- cli/tools/check.rs | 6 +- cli/tools/clean.rs | 3 +- cli/tools/compile.rs | 2 +- cli/tools/coverage/mod.rs | 4 +- cli/tools/doc.rs | 6 +- cli/tools/fmt.rs | 6 +- cli/tools/installer.rs | 1 + cli/tools/lint/linter.rs | 4 +- cli/tools/lint/mod.rs | 4 +- cli/tools/registry/paths.rs | 4 +- cli/tools/registry/pm.rs | 1 + cli/tools/registry/pm/outdated.rs | 1 + cli/tools/registry/unfurl.rs | 9 +- cli/tools/task.rs | 4 +- cli/tools/test/mod.rs | 3 +- cli/tsc/mod.rs | 10 +- cli/util/fs.rs | 16 +- cli/worker.rs | 26 +- ext/fs/Cargo.toml | 2 - ext/fs/in_memory_fs.rs | 481 ---------------------------- ext/fs/interface.rs | 342 +------------------- ext/fs/lib.rs | 5 - ext/fs/ops.rs | 55 +--- ext/fs/std_fs.rs | 2 +- ext/node/Cargo.toml | 3 +- ext/node/lib.rs | 58 ++-- ext/node/ops/require.rs | 151 +++++---- ext/node/ops/worker_threads.rs | 31 +- runtime/Cargo.toml | 1 + runtime/errors.rs | 4 +- runtime/examples/extension/main.rs | 7 +- runtime/permissions.rs | 31 +- runtime/snapshot.rs | 5 +- runtime/web_worker.rs | 15 +- runtime/worker.rs | 15 +- 74 files changed, 1304 insertions(+), 1437 deletions(-) create mode 100644 cli/sys.rs delete mode 100644 ext/fs/in_memory_fs.rs diff --git a/Cargo.lock b/Cargo.lock index 0fb9493629..bad4a8ea2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1737,14 +1737,12 @@ dependencies = [ "deno_path_util 0.3.0", "deno_permissions", "filetime", - "getrandom", "junction", "libc", "nix", "rand", "rayon", "serde", - "sys_traits", "thiserror 2.0.3", "winapi", "windows-sys 0.59.0", @@ -2042,6 +2040,7 @@ dependencies = [ "sm3", "spki", "stable_deref_trait", + "sys_traits", "thiserror 2.0.3", "tokio", "tokio-eld", @@ -2261,6 +2260,7 @@ dependencies = [ "serde", "signal-hook", "signal-hook-registry", + "sys_traits", "tempfile", "test_server", "thiserror 2.0.3", @@ -7680,12 +7680,14 @@ dependencies = [ [[package]] name = "sys_traits" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5a12729b699487bb50163466e87be7197871d83d04cc6815d430cf7c893bbd7" +checksum = "6683465f4e1d8fd75069cbc36c646258c05b7d8d6676bcb5d71968b99b7d5ae2" dependencies = [ + "filetime", "getrandom", "libc", + "parking_lot", "windows-sys 0.59.0", ] diff --git a/Cargo.toml b/Cargo.toml index 442680c332..bfd7437441 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -193,7 +193,7 @@ slab = "0.4" smallvec = "1.8" socket2 = { version = "0.5.3", features = ["all"] } spki = "0.7.2" -sys_traits = "=0.1.1" +sys_traits = "=0.1.4" tar = "=0.4.40" tempfile = "3.4.0" termcolor = "1.1.3" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2cf12f14d4..d05c3fb3e3 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -158,7 +158,7 @@ shell-escape = "=0.1.5" spki = { version = "0.7", features = ["pem"] } sqlformat = "=0.3.2" strsim = "0.11.1" -sys_traits = { workspace = true, features = ["libc", "real", "winapi"] } +sys_traits = { workspace = true, features = ["getrandom", "filetime", "libc", "real", "strip_unc", "winapi"] } tar.workspace = true tempfile.workspace = true text-size = "=1.1.0" @@ -187,6 +187,7 @@ nix.workspace = true [dev-dependencies] deno_bench_util.workspace = true pretty_assertions.workspace = true +sys_traits = { workspace = true, features = ["memory"] } test_util.workspace = true [package.metadata.winres] diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index 0648b6e5ed..7d5fe57bc3 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -13,12 +13,12 @@ use deno_core::serde_json; use deno_lockfile::WorkspaceMemberConfig; use deno_package_json::PackageJsonDepValue; use deno_path_util::fs::atomic_write_file_with_retries; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::PackageJson; use deno_semver::jsr::JsrDepPackageReq; use crate::args::deno_json::import_map_deps; use crate::cache; +use crate::sys::CliSys; use crate::Flags; use crate::args::DenoSubcommand; @@ -36,6 +36,7 @@ pub struct CliLockfileReadFromPathOptions { #[derive(Debug)] pub struct CliLockfile { + sys: CliSys, lockfile: Mutex, pub filename: PathBuf, frozen: bool, @@ -92,7 +93,7 @@ impl CliLockfile { // do an atomic write to reduce the chance of multiple deno // processes corrupting the file atomic_write_file_with_retries( - &FsSysTraitsAdapter::new_real(), + &self.sys, &lockfile.filename, &bytes, cache::CACHE_PERM, @@ -103,6 +104,7 @@ impl CliLockfile { } pub fn discover( + sys: &CliSys, flags: &Flags, workspace: &Workspace, maybe_external_import_map: Option<&serde_json::Value>, @@ -165,11 +167,14 @@ impl CliLockfile { .unwrap_or(false) }); - let lockfile = Self::read_from_path(CliLockfileReadFromPathOptions { - file_path, - frozen, - skip_write: flags.internal.lockfile_skip_write, - })?; + let lockfile = Self::read_from_path( + sys, + CliLockfileReadFromPathOptions { + file_path, + frozen, + skip_write: flags.internal.lockfile_skip_write, + }, + )?; // initialize the lockfile with the workspace's configuration let root_url = workspace.root_dir(); @@ -225,6 +230,7 @@ impl CliLockfile { } pub fn read_from_path( + sys: &CliSys, opts: CliLockfileReadFromPathOptions, ) -> Result { let lockfile = match std::fs::read_to_string(&opts.file_path) { @@ -243,6 +249,7 @@ impl CliLockfile { } }; Ok(CliLockfile { + sys: sys.clone(), filename: lockfile.filename.clone(), lockfile: Mutex::new(lockfile), frozen: opts.frozen, diff --git a/cli/args/mod.rs b/cli/args/mod.rs index aa3a251105..a059b07757 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -30,7 +30,6 @@ use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmSystemInfo; use deno_path_util::normalize_path; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_semver::npm::NpmPackageReqReference; use deno_semver::StackString; use deno_telemetry::OtelConfig; @@ -89,6 +88,7 @@ use thiserror::Error; use crate::cache::DenoDirProvider; use crate::file_fetcher::CliFileFetcher; +use crate::sys::CliSys; use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::version; @@ -573,7 +573,7 @@ fn discover_npmrc( // TODO(bartlomieju): update to read both files - one in the project root and one and // home dir and then merge them. // 3. Try `.npmrc` in the user's home directory - if let Some(home_dir) = sys_traits::impls::RealSys.env_home_dir() { + if let Some(home_dir) = crate::sys::CliSys::default().env_home_dir() { match try_to_read_npmrc(&home_dir) { Ok(Some((source, path))) => { return try_to_parse_npmrc(source, &path).map(|r| (r, Some(path))); @@ -772,7 +772,9 @@ pub struct CliOptions { } impl CliOptions { + #[allow(clippy::too_many_arguments)] pub fn new( + sys: &CliSys, flags: Arc, initial_cwd: PathBuf, maybe_lockfile: Option>, @@ -797,8 +799,10 @@ impl CliOptions { } let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache); - let deno_dir_provider = - Arc::new(DenoDirProvider::new(flags.internal.cache_path.clone())); + let deno_dir_provider = Arc::new(DenoDirProvider::new( + sys.clone(), + flags.internal.cache_path.clone(), + )); let maybe_node_modules_folder = resolve_node_modules_folder( &initial_cwd, &flags, @@ -823,7 +827,7 @@ impl CliOptions { }) } - pub fn from_flags(flags: Arc) -> Result { + pub fn from_flags(sys: &CliSys, flags: Arc) -> Result { let initial_cwd = std::env::current_dir().with_context(|| "Failed getting cwd.")?; let maybe_vendor_override = flags.vendor.map(|v| match v { @@ -867,7 +871,7 @@ impl CliOptions { ConfigFlag::Discover => { if let Some(start_paths) = flags.config_path_args(&initial_cwd) { WorkspaceDirectory::discover( - &FsSysTraitsAdapter::new_real(), + sys, WorkspaceDiscoverStart::Paths(&start_paths), &resolve_workspace_discover_options(), )? @@ -878,7 +882,7 @@ impl CliOptions { ConfigFlag::Path(path) => { let config_path = normalize_path(initial_cwd.join(path)); WorkspaceDirectory::discover( - &FsSysTraitsAdapter::new_real(), + sys, WorkspaceDiscoverStart::ConfigFile(&config_path), &resolve_workspace_discover_options(), )? @@ -917,6 +921,7 @@ impl CliOptions { }; let maybe_lock_file = CliLockfile::discover( + sys, &flags, &start_dir.workspace, external_import_map.as_ref().map(|(_, v)| v), @@ -925,6 +930,7 @@ impl CliOptions { log::debug!("Finished config loading."); Self::new( + sys, flags, initial_cwd, maybe_lock_file.map(Arc::new), diff --git a/cli/cache/deno_dir.rs b/cli/cache/deno_dir.rs index d83ea8ebd5..90a3add54e 100644 --- a/cli/cache/deno_dir.rs +++ b/cli/cache/deno_dir.rs @@ -3,6 +3,8 @@ use deno_cache_dir::DenoDirResolutionError; use once_cell::sync::OnceCell; +use crate::sys::CliSys; + use super::DiskCache; use std::env; @@ -11,13 +13,15 @@ use std::path::PathBuf; /// Lazily creates the deno dir which might be useful in scenarios /// where functionality wants to continue if the DENO_DIR can't be created. pub struct DenoDirProvider { + sys: CliSys, maybe_custom_root: Option, deno_dir: OnceCell>, } impl DenoDirProvider { - pub fn new(maybe_custom_root: Option) -> Self { + pub fn new(sys: CliSys, maybe_custom_root: Option) -> Self { Self { + sys, maybe_custom_root, deno_dir: Default::default(), } @@ -26,7 +30,9 @@ impl DenoDirProvider { pub fn get_or_create(&self) -> Result<&DenoDir, DenoDirResolutionError> { self .deno_dir - .get_or_init(|| DenoDir::new(self.maybe_custom_root.clone())) + .get_or_init(|| { + DenoDir::new(self.sys.clone(), self.maybe_custom_root.clone()) + }) .as_ref() .map_err(|err| match err { DenoDirResolutionError::NoCacheOrHomeDir => { @@ -53,6 +59,7 @@ pub struct DenoDir { impl DenoDir { pub fn new( + sys: CliSys, maybe_custom_root: Option, ) -> Result { let root = deno_cache_dir::resolve_deno_dir( @@ -64,7 +71,7 @@ impl DenoDir { let deno_dir = Self { root, - gen_cache: DiskCache::new(&gen_path), + gen_cache: DiskCache::new(sys, &gen_path), }; Ok(deno_dir) diff --git a/cli/cache/disk_cache.rs b/cli/cache/disk_cache.rs index b22b2e3cc7..c96a3943c0 100644 --- a/cli/cache/disk_cache.rs +++ b/cli/cache/disk_cache.rs @@ -1,12 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use crate::sys::CliSys; + use super::CACHE_PERM; use deno_cache_dir::url_to_filename; use deno_core::url::Host; use deno_core::url::Url; use deno_path_util::fs::atomic_write_file_with_retries; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use std::ffi::OsStr; use std::fs; use std::path::Component; @@ -17,14 +18,16 @@ use std::str; #[derive(Debug, Clone)] pub struct DiskCache { + sys: CliSys, pub location: PathBuf, } impl DiskCache { /// `location` must be an absolute path. - pub fn new(location: &Path) -> Self { + pub fn new(sys: CliSys, location: &Path) -> Self { assert!(location.is_absolute()); Self { + sys, location: location.to_owned(), } } @@ -121,12 +124,7 @@ impl DiskCache { pub fn set(&self, filename: &Path, data: &[u8]) -> std::io::Result<()> { let path = self.location.join(filename); - atomic_write_file_with_retries( - &FsSysTraitsAdapter::new_real(), - &path, - data, - CACHE_PERM, - ) + atomic_write_file_with_retries(&self.sys, &path, data, CACHE_PERM) } } @@ -139,7 +137,7 @@ mod tests { fn test_set_get_cache_file() { let temp_dir = TempDir::new(); let sub_dir = temp_dir.path().join("sub_dir"); - let cache = DiskCache::new(&sub_dir.to_path_buf()); + let cache = DiskCache::new(CliSys::default(), &sub_dir.to_path_buf()); let path = PathBuf::from("foo/bar.txt"); cache.set(&path, b"hello").unwrap(); assert_eq!(cache.get(&path).unwrap(), b"hello"); @@ -153,7 +151,7 @@ mod tests { PathBuf::from("/deno_dir/") }; - let cache = DiskCache::new(&cache_location); + let cache = DiskCache::new(CliSys::default(), &cache_location); let mut test_cases = vec![ ( @@ -209,7 +207,7 @@ mod tests { } else { "/foo" }; - let cache = DiskCache::new(&PathBuf::from(p)); + let cache = DiskCache::new(CliSys::default(), &PathBuf::from(p)); let mut test_cases = vec![ ( @@ -257,7 +255,7 @@ mod tests { PathBuf::from("/deno_dir/") }; - let cache = DiskCache::new(&cache_location); + let cache = DiskCache::new(CliSys::default(), &cache_location); let mut test_cases = vec!["unknown://localhost/test.ts"]; diff --git a/cli/cache/emit.rs b/cli/cache/emit.rs index 3c9eecfcbd..b239cc93ba 100644 --- a/cli/cache/emit.rs +++ b/cli/cache/emit.rs @@ -159,12 +159,15 @@ impl EmitFileSerializer { mod test { use test_util::TempDir; + use crate::sys::CliSys; + use super::*; #[test] pub fn emit_cache_general_use() { let temp_dir = TempDir::new(); - let disk_cache = DiskCache::new(temp_dir.path().as_path()); + let disk_cache = + DiskCache::new(CliSys::default(), temp_dir.path().as_path()); let cache = EmitCache { disk_cache: disk_cache.clone(), file_serializer: EmitFileSerializer { diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index c8b0eacaa4..bc6f792667 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -5,6 +5,7 @@ use crate::file_fetcher::CliFetchNoFollowErrorKind; use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::FetchNoFollowOptions; use crate::file_fetcher::FetchPermissionsOptionRef; +use crate::sys::CliSys; use deno_ast::MediaType; use deno_cache_dir::file_fetcher::CacheSetting; @@ -18,7 +19,6 @@ use deno_graph::source::CacheInfo; use deno_graph::source::LoadFuture; use deno_graph::source::LoadResponse; use deno_graph::source::Loader; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_permissions::PermissionsContainer; use node_resolver::InNpmPackageChecker; use std::collections::HashMap; @@ -58,10 +58,9 @@ pub use parsed_source::ParsedSourceCache; /// Permissions used to save a file in the disk caches. pub use deno_cache_dir::CACHE_PERM; -pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache; -pub type LocalHttpCache = deno_cache_dir::LocalHttpCache; -pub type LocalLspHttpCache = - deno_cache_dir::LocalLspHttpCache; +pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache; +pub type LocalHttpCache = deno_cache_dir::LocalHttpCache; +pub type LocalLspHttpCache = deno_cache_dir::LocalLspHttpCache; pub use deno_cache_dir::HttpCache; pub struct FetchCacherOptions { @@ -80,7 +79,7 @@ pub struct FetchCacher { in_npm_pkg_checker: Arc, module_info_cache: Arc, permissions: PermissionsContainer, - sys: FsSysTraitsAdapter, + sys: CliSys, is_deno_publish: bool, cache_info_enabled: bool, } @@ -91,7 +90,7 @@ impl FetchCacher { global_http_cache: Arc, in_npm_pkg_checker: Arc, module_info_cache: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, options: FetchCacherOptions, ) -> Self { Self { diff --git a/cli/clippy.toml b/cli/clippy.toml index f1c25acfb8..21a544aebd 100644 --- a/cli/clippy.toml +++ b/cli/clippy.toml @@ -4,6 +4,7 @@ disallowed-methods = [ ] disallowed-types = [ { path = "reqwest::Client", reason = "use crate::http_util::HttpClient instead" }, + { path = "sys_traits::impls::RealSys", reason = "use crate::sys::CliSys instead" }, ] ignore-interior-mutability = [ "lsp_types::Uri", diff --git a/cli/factory.rs b/cli/factory.rs index 5c73537743..e33b95d235 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -31,6 +31,8 @@ use crate::module_loader::CliModuleLoaderFactory; use crate::module_loader::ModuleLoadPreparer; use crate::node::CliCjsCodeAnalyzer; use crate::node::CliNodeCodeTranslator; +use crate::node::CliNodeResolver; +use crate::node::CliPackageJsonResolver; use crate::npm::create_cli_npm_resolver; use crate::npm::create_in_npm_pkg_checker; use crate::npm::CliByonmNpmResolverCreateOptions; @@ -48,7 +50,8 @@ use crate::resolver::CliResolverOptions; use crate::resolver::CliSloppyImportsResolver; use crate::resolver::NpmModuleLoader; use crate::resolver::SloppyImportsCachedFs; -use crate::standalone::DenoCompileBinaryWriter; +use crate::standalone::binary::DenoCompileBinaryWriter; +use crate::sys::CliSys; use crate::tools::check::TypeChecker; use crate::tools::coverage::CoverageCollector; use crate::tools::lint::LintRuleProvider; @@ -74,9 +77,7 @@ use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; use deno_resolver::NodeAndNpmReqResolver; use deno_runtime::deno_fs; -use deno_runtime::deno_fs::FsSysTraitsAdapter; -use deno_runtime::deno_node::NodeResolver; -use deno_runtime::deno_node::PackageJsonResolver; +use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; @@ -198,13 +199,14 @@ struct CliFactoryServices { module_info_cache: Deferred>, module_load_preparer: Deferred>, node_code_translator: Deferred>, - node_resolver: Deferred>, + node_resolver: Deferred>, npm_cache_dir: Deferred>, npm_req_resolver: Deferred>, npm_resolver: Deferred>, parsed_source_cache: Deferred>, - permission_desc_parser: Deferred>, - pkg_json_resolver: Deferred>, + permission_desc_parser: + Deferred>>, + pkg_json_resolver: Deferred>, resolver: Deferred>, root_cert_store_provider: Deferred>, root_permissions_container: Deferred, @@ -254,7 +256,7 @@ impl CliFactory { pub fn cli_options(&self) -> Result<&Arc, AnyError> { self.services.cli_options.get_or_try_init(|| { - CliOptions::from_flags(self.flags.clone()).map(Arc::new) + CliOptions::from_flags(&self.sys(), self.flags.clone()).map(Arc::new) }) } @@ -317,7 +319,7 @@ impl CliFactory { pub fn global_http_cache(&self) -> Result<&Arc, AnyError> { self.services.global_http_cache.get_or_try_init(|| { Ok(Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter(self.fs().clone()), + self.sys(), self.deno_dir()?.remote_folder_path(), ))) }) @@ -355,6 +357,7 @@ impl CliFactory { Ok(Arc::new(CliFileFetcher::new( self.http_cache()?.clone(), self.http_client_provider().clone(), + self.sys(), self.blob_store().clone(), Some(self.text_only_progress_bar().clone()), !cli_options.no_remote(), @@ -365,7 +368,11 @@ impl CliFactory { } pub fn fs(&self) -> &Arc { - self.services.fs.get_or_init(|| Arc::new(deno_fs::RealFs)) + self.services.fs.get_or_init(|| Arc::new(RealFs)) + } + + pub fn sys(&self) -> CliSys { + CliSys::default() // very cheap to make } pub fn in_npm_pkg_checker( @@ -391,11 +398,10 @@ impl CliFactory { pub fn npm_cache_dir(&self) -> Result<&Arc, AnyError> { self.services.npm_cache_dir.get_or_try_init(|| { - let fs = self.fs(); let global_path = self.deno_dir()?.npm_folder_path(); let cli_options = self.cli_options()?; Ok(Arc::new(NpmCacheDir::new( - &FsSysTraitsAdapter(fs.clone()), + &self.sys(), global_path, cli_options.npmrc().get_all_known_registries_urls(), ))) @@ -410,12 +416,11 @@ impl CliFactory { .npm_resolver .get_or_try_init_async( async { - let fs = self.fs(); let cli_options = self.cli_options()?; create_cli_npm_resolver(if cli_options.use_byonm() { CliNpmResolverCreateOptions::Byonm( CliByonmNpmResolverCreateOptions { - sys: FsSysTraitsAdapter(fs.clone()), + sys: self.sys(), pkg_json_resolver: self.pkg_json_resolver().clone(), root_node_modules_dir: Some( match cli_options.node_modules_dir_path() { @@ -439,7 +444,7 @@ impl CliFactory { cli_options.workspace(), ), ), - sys: FsSysTraitsAdapter(self.fs().clone()), + sys: self.sys(), snapshot: match cli_options.resolve_npm_resolution_snapshot()? { Some(snapshot) => { CliNpmResolverManagedSnapshotOption::Specified(Some( @@ -486,7 +491,7 @@ impl CliFactory { .get_or_try_init(|| { Ok(self.cli_options()?.unstable_sloppy_imports().then(|| { Arc::new(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new( - FsSysTraitsAdapter(self.fs().clone()), + self.sys(), ))) })) }) @@ -647,13 +652,13 @@ impl CliFactory { )) } - pub async fn node_resolver(&self) -> Result<&Arc, AnyError> { + pub async fn node_resolver(&self) -> Result<&Arc, AnyError> { self .services .node_resolver .get_or_try_init_async( async { - Ok(Arc::new(NodeResolver::new( + Ok(Arc::new(CliNodeResolver::new( self.in_npm_pkg_checker()?.clone(), RealIsBuiltInNodeModuleChecker, self @@ -662,7 +667,7 @@ impl CliFactory { .clone() .into_npm_pkg_folder_resolver(), self.pkg_json_resolver().clone(), - FsSysTraitsAdapter(self.fs().clone()), + self.sys(), ))) } .boxed_local(), @@ -698,7 +703,7 @@ impl CliFactory { .clone() .into_npm_pkg_folder_resolver(), self.pkg_json_resolver().clone(), - FsSysTraitsAdapter(self.fs().clone()), + self.sys(), ))) }) .await @@ -714,7 +719,7 @@ impl CliFactory { let npm_resolver = self.npm_resolver().await?; Ok(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), - sys: FsSysTraitsAdapter(self.fs().clone()), + sys: self.sys(), in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(), node_resolver: self.node_resolver().await?.clone(), npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), @@ -723,12 +728,11 @@ impl CliFactory { .await } - pub fn pkg_json_resolver(&self) -> &Arc { - self.services.pkg_json_resolver.get_or_init(|| { - Arc::new(PackageJsonResolver::new(FsSysTraitsAdapter( - self.fs().clone(), - ))) - }) + pub fn pkg_json_resolver(&self) -> &Arc { + self + .services + .pkg_json_resolver + .get_or_init(|| Arc::new(CliPackageJsonResolver::new(self.sys()))) } pub async fn type_checker(&self) -> Result<&Arc, AnyError> { @@ -774,7 +778,7 @@ impl CliFactory { self.parsed_source_cache().clone(), self.resolver().await?.clone(), self.root_permissions_container()?.clone(), - FsSysTraitsAdapter(self.fs().clone()), + self.sys(), ))) }) .await @@ -864,10 +868,9 @@ impl CliFactory { pub fn permission_desc_parser( &self, - ) -> Result<&Arc, AnyError> { + ) -> Result<&Arc>, AnyError> { self.services.permission_desc_parser.get_or_try_init(|| { - let fs = self.fs().clone(); - Ok(Arc::new(RuntimePermissionDescriptorParser::new(fs))) + Ok(Arc::new(RuntimePermissionDescriptorParser::new(self.sys()))) }) } @@ -974,7 +977,7 @@ impl CliFactory { ), self.parsed_source_cache().clone(), self.resolver().await?.clone(), - FsSysTraitsAdapter(self.fs().clone()), + self.sys(), )), node_resolver.clone(), npm_resolver.clone(), @@ -982,6 +985,7 @@ impl CliFactory { self.root_cert_store_provider().clone(), self.root_permissions_container()?.clone(), StorageKeyResolver::from_options(cli_options), + self.sys(), cli_options.sub_command().clone(), self.create_cli_main_worker_options()?, self.cli_options()?.otel_config(), diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 38f3dd1847..7e8438d639 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -24,7 +24,6 @@ use deno_core::url::Url; use deno_core::ModuleSpecifier; use deno_error::JsError; use deno_graph::source::LoaderChecksum; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_permissions::CheckSpecifierKind; use deno_runtime::deno_permissions::PermissionCheckError; use deno_runtime::deno_permissions::PermissionsContainer; @@ -38,6 +37,7 @@ use crate::cache::HttpCache; use crate::colors; use crate::http_util::get_response_body_with_progress; use crate::http_util::HttpClientProvider; +use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; #[derive(Debug, Clone, Eq, PartialEq)] @@ -267,7 +267,7 @@ pub struct FetchNoFollowOptions<'a> { type DenoCacheDirFileFetcher = deno_cache_dir::file_fetcher::FileFetcher< BlobStoreAdapter, - FsSysTraitsAdapter, + CliSys, HttpClientAdapter, >; @@ -279,9 +279,11 @@ pub struct CliFileFetcher { } impl CliFileFetcher { + #[allow(clippy::too_many_arguments)] pub fn new( http_cache: Arc, http_client_provider: Arc, + sys: CliSys, blob_store: Arc, progress_bar: Option, allow_remote: bool, @@ -289,7 +291,6 @@ impl CliFileFetcher { download_log_level: log::Level, ) -> Self { let memory_files = Arc::new(MemoryFiles::default()); - let sys = FsSysTraitsAdapter::new_real(); let auth_tokens = AuthTokens::new_from_sys(&sys); let file_fetcher = DenoCacheDirFileFetcher::new( BlobStoreAdapter(blob_store), @@ -538,13 +539,11 @@ mod tests { let temp_dir = maybe_temp_dir.unwrap_or_default(); let location = temp_dir.path().join("remote").to_path_buf(); let blob_store: Arc = Default::default(); - let cache = Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - location, - )); + let cache = Arc::new(GlobalHttpCache::new(CliSys::default(), location)); let file_fetcher = CliFileFetcher::new( cache.clone(), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), blob_store.clone(), None, true, @@ -754,11 +753,9 @@ mod tests { // invocation and indicates to "cache bust". let location = temp_dir.path().join("remote").to_path_buf(); let file_fetcher = CliFileFetcher::new( - Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - location, - )), + Arc::new(GlobalHttpCache::new(CliSys::default(), location)), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, true, @@ -783,14 +780,13 @@ mod tests { let specifier = resolve_url("http://localhost:4545/subdir/mismatch_ext.ts").unwrap(); - let http_cache = Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - location.clone(), - )); + let http_cache = + Arc::new(GlobalHttpCache::new(CliSys::default(), location.clone())); let file_modified_01 = { let file_fetcher = CliFileFetcher::new( http_cache.clone(), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, true, @@ -810,11 +806,9 @@ mod tests { let file_modified_02 = { let file_fetcher = CliFileFetcher::new( - Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - location, - )), + Arc::new(GlobalHttpCache::new(CliSys::default(), location)), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, true, @@ -940,15 +934,14 @@ mod tests { resolve_url("http://localhost:4548/subdir/mismatch_ext.ts").unwrap(); let redirected_specifier = resolve_url("http://localhost:4546/subdir/mismatch_ext.ts").unwrap(); - let http_cache = Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - location.clone(), - )); + let http_cache = + Arc::new(GlobalHttpCache::new(CliSys::default(), location.clone())); let metadata_file_modified_01 = { let file_fetcher = CliFileFetcher::new( http_cache.clone(), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, true, @@ -971,6 +964,7 @@ mod tests { let file_fetcher = CliFileFetcher::new( http_cache.clone(), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, true, @@ -1075,11 +1069,9 @@ mod tests { let temp_dir = TempDir::new(); let location = temp_dir.path().join("remote").to_path_buf(); let file_fetcher = CliFileFetcher::new( - Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - location, - )), + Arc::new(GlobalHttpCache::new(CliSys::default(), location)), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, false, @@ -1113,11 +1105,9 @@ mod tests { let temp_dir = TempDir::new(); let location = temp_dir.path().join("remote").to_path_buf(); let file_fetcher_01 = CliFileFetcher::new( - Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - location.clone(), - )), + Arc::new(GlobalHttpCache::new(CliSys::default(), location.clone())), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, true, @@ -1125,11 +1115,9 @@ mod tests { log::Level::Info, ); let file_fetcher_02 = CliFileFetcher::new( - Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - location, - )), + Arc::new(GlobalHttpCache::new(CliSys::default(), location)), Arc::new(HttpClientProvider::new(None, None)), + CliSys::default(), Default::default(), None, true, diff --git a/cli/graph_util.rs b/cli/graph_util.rs index a21d055adc..68d48d9bbc 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -29,7 +29,6 @@ use deno_graph::SpecifierError; use deno_graph::WorkspaceFastCheckOption; use deno_path_util::url_to_file_path; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node; use deno_runtime::deno_permissions::PermissionsContainer; use deno_semver::jsr::JsrDepPackageReq; @@ -57,6 +56,7 @@ use crate::resolver::CjsTracker; use crate::resolver::CliResolver; use crate::resolver::CliSloppyImportsResolver; use crate::resolver::SloppyImportsCachedFs; +use crate::sys::CliSys; use crate::tools::check; use crate::tools::check::TypeChecker; use crate::util::file_watcher::WatcherCommunicator; @@ -81,7 +81,7 @@ pub struct GraphValidOptions { /// for the CLI. pub fn graph_valid( graph: &ModuleGraph, - sys: &FsSysTraitsAdapter, + sys: &CliSys, roots: &[ModuleSpecifier], options: GraphValidOptions, ) -> Result<(), AnyError> { @@ -141,7 +141,7 @@ pub struct GraphWalkErrorsOptions { /// and enhances them with CLI information. pub fn graph_walk_errors<'a>( graph: &'a ModuleGraph, - sys: &'a FsSysTraitsAdapter, + sys: &'a CliSys, roots: &'a [ModuleSpecifier], options: GraphWalkErrorsOptions, ) -> impl Iterator + 'a { @@ -443,7 +443,7 @@ pub struct ModuleGraphBuilder { parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, - sys: FsSysTraitsAdapter, + sys: CliSys, } impl ModuleGraphBuilder { @@ -462,7 +462,7 @@ impl ModuleGraphBuilder { parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, - sys: FsSysTraitsAdapter, + sys: CliSys, ) -> Self { Self { caches, @@ -836,7 +836,7 @@ pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String { } fn enhanced_sloppy_imports_error_message( - sys: &FsSysTraitsAdapter, + sys: &CliSys, error: &ModuleError, ) -> Option { match error { diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs index c6d1b39ef4..24a55d495c 100644 --- a/cli/lsp/cache.rs +++ b/cli/lsp/cache.rs @@ -7,11 +7,11 @@ use crate::cache::LocalLspHttpCache; use crate::lsp::config::Config; use crate::lsp::logging::lsp_log; use crate::lsp::logging::lsp_warn; +use crate::sys::CliSys; use deno_core::url::Url; use deno_core::ModuleSpecifier; use deno_path_util::url_to_file_path; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use std::collections::BTreeMap; use std::fs; use std::path::Path; @@ -92,12 +92,11 @@ impl LspCache { }) .ok() }); - let deno_dir = DenoDir::new(global_cache_path) + let sys = CliSys::default(); + let deno_dir = DenoDir::new(sys.clone(), global_cache_path) .expect("should be infallible with absolute custom root"); - let global = Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - deno_dir.remote_folder_path(), - )); + let global = + Arc::new(GlobalHttpCache::new(sys, deno_dir.remote_folder_path())); Self { deno_dir, global, diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index a43e774934..ff4c2978d5 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -36,7 +36,6 @@ use deno_lint::linter::LintConfig as DenoLintConfig; use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonCache; use deno_path_util::url_to_file_path; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::PackageJson; use indexmap::IndexSet; use lsp_types::ClientCapabilities; @@ -65,6 +64,7 @@ use crate::file_fetcher::CliFileFetcher; use crate::lsp::logging::lsp_warn; use crate::resolver::CliSloppyImportsResolver; use crate::resolver::SloppyImportsCachedFs; +use crate::sys::CliSys; use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinterOptions; use crate::tools::lint::LintRuleProvider; @@ -1227,7 +1227,7 @@ impl ConfigData { Ok(scope_dir_path) => { let paths = [scope_dir_path]; WorkspaceDirectory::discover( - &FsSysTraitsAdapter::new_real(), + &CliSys::default(), match specified_config { Some(config_path) => { deno_config::workspace::WorkspaceDiscoverStart::ConfigFile( @@ -1615,9 +1615,7 @@ impl ConfigData { || unstable.contains("sloppy-imports"); let sloppy_imports_resolver = unstable_sloppy_imports.then(|| { Arc::new(CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new_without_stat_cache( - FsSysTraitsAdapter::new_real(), - ), + SloppyImportsCachedFs::new_without_stat_cache(CliSys::default()), )) }); let resolver = Arc::new(resolver); @@ -1921,17 +1919,20 @@ impl ConfigTree { #[cfg(test)] pub async fn inject_config_file(&mut self, config_file: ConfigFile) { + use sys_traits::FsCreateDirAll; + use sys_traits::FsWrite; + let scope = config_file.specifier.join(".").unwrap(); let json_text = serde_json::to_string(&config_file.json).unwrap(); - let test_fs = Arc::new(deno_runtime::deno_fs::InMemoryFs::default()); + let memory_sys = sys_traits::impls::InMemorySys::default(); let config_path = url_to_file_path(&config_file.specifier).unwrap(); - test_fs.setup_text_files(vec![( - config_path.to_string_lossy().to_string(), - json_text, - )]); + memory_sys + .fs_create_dir_all(config_path.parent().unwrap()) + .unwrap(); + memory_sys.fs_write(&config_path, json_text).unwrap(); let workspace_dir = Arc::new( WorkspaceDirectory::discover( - &FsSysTraitsAdapter(test_fs.clone()), + &memory_sys, deno_config::workspace::WorkspaceDiscoverStart::ConfigFile( &config_path, ), @@ -2008,11 +2009,14 @@ fn resolve_lockfile_from_path( lockfile_path: PathBuf, frozen: bool, ) -> Option { - match CliLockfile::read_from_path(CliLockfileReadFromPathOptions { - file_path: lockfile_path, - frozen, - skip_write: false, - }) { + match CliLockfile::read_from_path( + &CliSys::default(), + CliLockfileReadFromPathOptions { + file_path: lockfile_path, + frozen, + skip_write: false, + }, + ) { Ok(value) => { if value.filename.exists() { if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename) diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 33fd4897c4..af6fdf53a4 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -21,6 +21,7 @@ use crate::graph_util::enhanced_resolution_error_message; use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams; use crate::resolver::CliSloppyImportsResolver; use crate::resolver::SloppyImportsCachedFs; +use crate::sys::CliSys; use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinterOptions; use crate::tools::lint::LintRuleProvider; @@ -48,7 +49,6 @@ use deno_graph::SpecifierError; use deno_lint::linter::LintConfig as DenoLintConfig; use deno_resolver::sloppy_imports::SloppyImportsResolution; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node; use deno_runtime::tokio_util::create_basic_runtime; use deno_semver::jsr::JsrPackageReqReference; @@ -1281,7 +1281,7 @@ impl DenoDiagnostic { Self::NotInstalledNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("npm package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))), Self::NoLocal(specifier) => { let maybe_sloppy_resolution = CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new(FsSysTraitsAdapter::new_real()) + SloppyImportsCachedFs::new(CliSys::default()) ).resolve(specifier, SloppyImportsResolutionKind::Execution); let data = maybe_sloppy_resolution.as_ref().map(|res| { json!({ diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 1cc26aff58..9ab1d9786c 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -17,7 +17,6 @@ use deno_core::ModuleSpecifier; use deno_graph::GraphKind; use deno_graph::Resolution; use deno_path_util::url_to_file_path; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::RootCertStoreProvider; use deno_semver::jsr::JsrPackageReqReference; @@ -109,6 +108,7 @@ use crate::lsp::config::ConfigWatchedFileType; use crate::lsp::logging::init_log_file; use crate::lsp::tsc::file_text_changes_to_workspace_edit; use crate::lsp::urls::LspUrlKind; +use crate::sys::CliSys; use crate::tools::fmt::format_file; use crate::tools::fmt::format_parsed_source; use crate::tools::upgrade::check_for_upgrades_for_lsp; @@ -280,7 +280,7 @@ impl LanguageServer { .await?; graph_util::graph_valid( &graph, - &FsSysTraitsAdapter(factory.fs().clone()), + &CliSys::default(), &roots, graph_util::GraphValidOptions { kind: GraphKind::All, @@ -962,6 +962,7 @@ impl Inner { let file_fetcher = CliFileFetcher::new( self.cache.global().clone(), self.http_client_provider.clone(), + CliSys::default(), Default::default(), None, true, @@ -3613,7 +3614,7 @@ impl Inner { let workspace = match config_data { Some(d) => d.member_dir.clone(), None => Arc::new(WorkspaceDirectory::discover( - &FsSysTraitsAdapter::new_real(), + &CliSys::default(), deno_config::workspace::WorkspaceDiscoverStart::Paths(&[ initial_cwd.clone() ]), @@ -3634,6 +3635,7 @@ impl Inner { )?), }; let cli_options = CliOptions::new( + &CliSys::default(), Arc::new(Flags { internal: InternalFlags { cache_path: Some(self.cache.deno_dir().root.clone()), diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index 488e333e9d..c8dd7fa1a7 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -19,6 +19,7 @@ use crate::file_fetcher::FetchOptions; use crate::file_fetcher::FetchPermissionsOptionRef; use crate::file_fetcher::TextDecodedFile; use crate::http_util::HttpClientProvider; +use crate::sys::CliSys; use deno_cache_dir::file_fetcher::CacheSetting; use deno_core::anyhow::anyhow; @@ -32,7 +33,6 @@ use deno_core::url::Position; use deno_core::url::Url; use deno_core::ModuleSpecifier; use deno_graph::Dependency; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use log::error; use once_cell::sync::Lazy; use std::borrow::Cow; @@ -430,13 +430,12 @@ impl ModuleRegistry { http_client_provider: Arc, ) -> Self { // the http cache should always be the global one for registry completions - let http_cache = Arc::new(GlobalHttpCache::new( - FsSysTraitsAdapter::new_real(), - location.clone(), - )); + let http_cache = + Arc::new(GlobalHttpCache::new(CliSys::default(), location.clone())); let file_fetcher = CliFileFetcher::new( http_cache.clone(), http_client_provider, + CliSys::default(), Default::default(), None, true, diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index addecc9a61..2dec5266f4 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -19,9 +19,6 @@ use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; use deno_resolver::NodeAndNpmReqResolver; -use deno_runtime::deno_fs::FsSysTraitsAdapter; -use deno_runtime::deno_node::NodeResolver; -use deno_runtime::deno_node::PackageJsonResolver; use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; @@ -51,6 +48,8 @@ use crate::http_util::HttpClientProvider; use crate::lsp::config::Config; use crate::lsp::config::ConfigData; use crate::lsp::logging::lsp_warn; +use crate::node::CliNodeResolver; +use crate::node::CliPackageJsonResolver; use crate::npm::create_cli_npm_resolver_for_lsp; use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliManagedInNpmPkgCheckerCreateOptions; @@ -66,6 +65,7 @@ use crate::resolver::CliResolver; use crate::resolver::CliResolverOptions; use crate::resolver::IsCjsResolver; use crate::resolver::WorkerCliNpmGraphResolver; +use crate::sys::CliSys; use crate::tsc::into_specifier_and_media_type; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; @@ -77,9 +77,9 @@ struct LspScopeResolver { is_cjs_resolver: Arc, jsr_resolver: Option>, npm_resolver: Option>, - node_resolver: Option>, + node_resolver: Option>, npm_pkg_req_resolver: Option>, - pkg_json_resolver: Arc, + pkg_json_resolver: Arc, redirect_resolver: Option>, graph_imports: Arc>, dep_info: Arc>>, @@ -383,7 +383,7 @@ impl LspResolver { pub fn pkg_json_resolver( &self, referrer: &ModuleSpecifier, - ) -> &Arc { + ) -> &Arc { let resolver = self.get_scope_resolver(Some(referrer)); &resolver.pkg_json_resolver } @@ -591,22 +591,22 @@ struct ResolverFactoryServices { cli_resolver: Deferred>, in_npm_pkg_checker: Deferred>, is_cjs_resolver: Deferred>, - node_resolver: Deferred>>, + node_resolver: Deferred>>, npm_pkg_req_resolver: Deferred>>, npm_resolver: Option>, } struct ResolverFactory<'a> { config_data: Option<&'a Arc>, - pkg_json_resolver: Arc, - sys: FsSysTraitsAdapter, + pkg_json_resolver: Arc, + sys: CliSys, services: ResolverFactoryServices, } impl<'a> ResolverFactory<'a> { pub fn new(config_data: Option<&'a Arc>) -> Self { - let sys = FsSysTraitsAdapter::new_real(); - let pkg_json_resolver = Arc::new(PackageJsonResolver::new(sys.clone())); + let sys = CliSys::default(); + let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone())); Self { config_data, pkg_json_resolver, @@ -621,7 +621,7 @@ impl<'a> ResolverFactory<'a> { cache: &LspCache, ) { let enable_byonm = self.config_data.map(|d| d.byonm).unwrap_or(false); - let sys = FsSysTraitsAdapter::new_real(); + let sys = CliSys::default(); let options = if enable_byonm { CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { sys, @@ -656,7 +656,7 @@ impl<'a> ResolverFactory<'a> { } None => CliNpmResolverManagedSnapshotOption::Specified(None), }, - sys: FsSysTraitsAdapter::new_real(), + sys: CliSys::default(), npm_cache_dir, // Use an "only" cache setting in order to make the // user do an explicit "cache" command and prevent @@ -729,7 +729,7 @@ impl<'a> ResolverFactory<'a> { }) } - pub fn pkg_json_resolver(&self) -> &Arc { + pub fn pkg_json_resolver(&self) -> &Arc { &self.pkg_json_resolver } @@ -770,13 +770,13 @@ impl<'a> ResolverFactory<'a> { }) } - pub fn node_resolver(&self) -> Option<&Arc> { + pub fn node_resolver(&self) -> Option<&Arc> { self .services .node_resolver .get_or_init(|| { let npm_resolver = self.services.npm_resolver.as_ref()?; - Some(Arc::new(NodeResolver::new( + Some(Arc::new(CliNodeResolver::new( self.in_npm_pkg_checker().clone(), RealIsBuiltInNodeModuleChecker, npm_resolver.clone().into_npm_pkg_folder_resolver(), diff --git a/cli/main.rs b/cli/main.rs index d68f27146b..c3c7286e71 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -20,6 +20,7 @@ mod ops; mod resolver; mod shared; mod standalone; +mod sys; mod task_runner; mod tools; mod tsc; diff --git a/cli/mainrt.rs b/cli/mainrt.rs index cba54b044c..2b767ea89c 100644 --- a/cli/mainrt.rs +++ b/cli/mainrt.rs @@ -18,6 +18,7 @@ mod node; mod npm; mod resolver; mod shared; +mod sys; mod task_runner; mod util; mod version; @@ -31,11 +32,13 @@ use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; use deno_terminal::colors; use indexmap::IndexMap; +use standalone::DenoCompileFileSystem; use std::borrow::Cow; use std::collections::HashMap; use std::env; use std::env::current_exe; +use std::sync::Arc; use crate::args::Flags; @@ -92,7 +95,9 @@ fn main() { Some(data.metadata.otel_config.clone()), ); load_env_vars(&data.metadata.env_vars_from_env_file); - let exit_code = standalone::run(data).await?; + let fs = DenoCompileFileSystem::new(data.vfs.clone()); + let sys = crate::sys::CliSys::DenoCompile(fs.clone()); + let exit_code = standalone::run(Arc::new(fs), sys, data).await?; deno_runtime::exit(exit_code); } Ok(None) => Ok(()), diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 3e81dbc881..ea40dbe609 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -11,6 +11,8 @@ use std::sync::atomic::AtomicU16; use std::sync::atomic::Ordering; use std::sync::Arc; +use crate::node::CliNodeResolver; +use crate::sys::CliSys; use deno_ast::MediaType; use deno_ast::ModuleKind; use deno_core::anyhow::anyhow; @@ -39,10 +41,8 @@ use deno_graph::ModuleGraph; use deno_graph::Resolution; use deno_graph::WasmModule; use deno_runtime::code_cache; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::NodeRequireLoader; -use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_permissions::PermissionsContainer; use deno_semver::npm::NpmPackageReqReference; use node_resolver::errors::ClosestPkgJsonError; @@ -220,13 +220,13 @@ struct SharedCliModuleLoaderState { main_module_graph_container: Arc, module_load_preparer: Arc, node_code_translator: Arc, - node_resolver: Arc, + node_resolver: Arc, npm_req_resolver: Arc, npm_resolver: Arc, npm_module_loader: NpmModuleLoader, parsed_source_cache: Arc, resolver: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, in_flight_loads_tracker: InFlightModuleLoadsTracker, } @@ -280,13 +280,13 @@ impl CliModuleLoaderFactory { main_module_graph_container: Arc, module_load_preparer: Arc, node_code_translator: Arc, - node_resolver: Arc, + node_resolver: Arc, npm_req_resolver: Arc, npm_resolver: Arc, npm_module_loader: NpmModuleLoader, parsed_source_cache: Arc, resolver: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, ) -> Self { Self { shared: Arc::new(SharedCliModuleLoaderState { @@ -1092,7 +1092,7 @@ impl ModuleGraphUpdatePermit for WorkerModuleGraphUpdatePermit { struct CliNodeRequireLoader { cjs_tracker: Arc, emitter: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, graph_container: TGraphContainer, in_npm_pkg_checker: Arc, npm_resolver: Arc, diff --git a/cli/node.rs b/cli/node.rs index 480da506c8..4a87d26ee0 100644 --- a/cli/node.rs +++ b/cli/node.rs @@ -8,7 +8,6 @@ use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; use deno_graph::ParsedSourceStore; use deno_runtime::deno_fs; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis; use node_resolver::analyze::CjsAnalysisExports; @@ -21,12 +20,15 @@ use crate::cache::CacheDBHash; use crate::cache::NodeAnalysisCache; use crate::cache::ParsedSourceCache; use crate::resolver::CjsTracker; +use crate::sys::CliSys; pub type CliNodeCodeTranslator = NodeCodeTranslator< CliCjsCodeAnalyzer, RealIsBuiltInNodeModuleChecker, - FsSysTraitsAdapter, + CliSys, >; +pub type CliNodeResolver = deno_runtime::deno_node::NodeResolver; +pub type CliPackageJsonResolver = node_resolver::PackageJsonResolver; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum CliCjsAnalysis { diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index 218f33989d..ca89a7399e 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -4,12 +4,12 @@ use std::borrow::Cow; use std::path::Path; use std::sync::Arc; +use crate::sys::CliSys; use deno_core::error::AnyError; use deno_core::serde_json; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmNpmResolverCreateOptions; use deno_resolver::npm::CliNpmReqResolver; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use node_resolver::NpmPackageFolderResolver; @@ -21,8 +21,8 @@ use super::CliNpmResolver; use super::InnerCliNpmResolverRef; pub type CliByonmNpmResolverCreateOptions = - ByonmNpmResolverCreateOptions; -pub type CliByonmNpmResolver = ByonmNpmResolver; + ByonmNpmResolverCreateOptions; +pub type CliByonmNpmResolver = ByonmNpmResolver; // todo(dsherret): the services hanging off `CliNpmResolver` doesn't seem ideal. We should probably decouple. #[derive(Debug)] diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 5b0a304de8..97a87dd9b8 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -5,6 +5,7 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use crate::sys::CliSys; use deno_ast::ModuleSpecifier; use deno_cache_dir::npm::NpmCacheDir; use deno_core::anyhow::Context; @@ -24,7 +25,6 @@ use deno_npm_cache::NpmCacheSetting; use deno_path_util::fs::canonicalize_path_maybe_not_exists; use deno_resolver::npm::CliNpmReqResolver; use deno_runtime::colors; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; @@ -70,7 +70,7 @@ pub struct CliManagedNpmResolverCreateOptions { pub maybe_lockfile: Option>, pub http_client_provider: Arc, pub npm_cache_dir: Arc, - pub sys: FsSysTraitsAdapter, + pub sys: CliSys, pub cache_setting: deno_cache_dir::file_fetcher::CacheSetting, pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, pub maybe_node_modules_path: Option, @@ -149,7 +149,7 @@ fn create_inner( npm_cache: Arc, npm_install_deps_provider: Arc, registry_info_provider: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, text_only_progress_bar: crate::util::progress_bar::ProgressBar, maybe_lockfile: Option>, npm_rc: Arc, @@ -307,7 +307,7 @@ pub struct ManagedCliNpmResolver { registry_info_provider: Arc, npm_cache: Arc, npm_install_deps_provider: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, resolution: Arc, tarball_cache: Arc, text_only_progress_bar: ProgressBar, @@ -333,7 +333,7 @@ impl ManagedCliNpmResolver { npm_cache: Arc, npm_install_deps_provider: Arc, resolution: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, tarball_cache: Arc, text_only_progress_bar: ProgressBar, npm_system_info: NpmSystemInfo, diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs index 83081d3b8e..26f6d8516d 100644 --- a/cli/npm/managed/resolvers/common.rs +++ b/cli/npm/managed/resolvers/common.rs @@ -20,13 +20,13 @@ use deno_core::futures::StreamExt; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use node_resolver::errors::PackageFolderResolveError; use sys_traits::FsCanonicalize; use super::super::PackageCaching; use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; /// Part of the resolution that interacts with the file system. #[async_trait(?Send)] @@ -74,15 +74,15 @@ pub trait NpmPackageFsResolver: Send + Sync { #[derive(Debug)] pub struct RegistryReadPermissionChecker { - sys: FsSysTraitsAdapter, + sys: CliSys, cache: Mutex>, registry_path: PathBuf, } impl RegistryReadPermissionChecker { - pub fn new(fs: FsSysTraitsAdapter, registry_path: PathBuf) -> Self { + pub fn new(sys: CliSys, registry_path: PathBuf) -> Self { Self { - sys: fs, + sys, registry_path, cache: Default::default(), } diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs index f56f012407..77e0d0ea3e 100644 --- a/cli/npm/managed/resolvers/global.rs +++ b/cli/npm/managed/resolvers/global.rs @@ -11,6 +11,7 @@ use crate::colors; use crate::npm::managed::PackageCaching; use crate::npm::CliNpmCache; use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; use async_trait::async_trait; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; @@ -18,7 +19,6 @@ use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageNotFoundError; @@ -49,7 +49,7 @@ impl GlobalNpmPackageResolver { cache: Arc, tarball_cache: Arc, resolution: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, system_info: NpmSystemInfo, lifecycle_scripts: LifecycleScriptsConfig, ) -> Self { diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 8bbaf6c51c..5c93c228e8 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -32,7 +32,6 @@ use deno_npm::NpmSystemInfo; use deno_path_util::fs::atomic_write_file_with_retries; use deno_path_util::fs::canonicalize_path_maybe_not_exists; use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use deno_semver::package::PackageNv; use deno_semver::StackString; @@ -51,6 +50,7 @@ use crate::colors; use crate::npm::managed::PackageCaching; use crate::npm::CliNpmCache; use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; use crate::util::fs::clone_dir_recursive; use crate::util::fs::symlink_dir; use crate::util::fs::LaxSingleProcessFsFlag; @@ -70,7 +70,7 @@ pub struct LocalNpmPackageResolver { npm_install_deps_provider: Arc, progress_bar: ProgressBar, resolution: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, tarball_cache: Arc, root_node_modules_path: PathBuf, root_node_modules_url: Url, @@ -86,7 +86,7 @@ impl LocalNpmPackageResolver { npm_install_deps_provider: Arc, progress_bar: ProgressBar, resolution: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, tarball_cache: Arc, node_modules_folder: PathBuf, system_info: NpmSystemInfo, @@ -926,7 +926,7 @@ impl SetupCache { bincode::serialize(&self.current).ok().and_then(|data| { atomic_write_file_with_retries( - &FsSysTraitsAdapter::new_real(), + &CliSys::default(), &self.file_path, &data, CACHE_PERM, diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs index 2d6d37798f..c2fc8d2d92 100644 --- a/cli/npm/managed/resolvers/mod.rs +++ b/cli/npm/managed/resolvers/mod.rs @@ -7,8 +7,8 @@ mod local; use std::path::PathBuf; use std::sync::Arc; +use crate::sys::CliSys; use deno_npm::NpmSystemInfo; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; @@ -29,7 +29,7 @@ pub fn create_npm_fs_resolver( npm_install_deps_provider: &Arc, progress_bar: &ProgressBar, resolution: Arc, - sys: FsSysTraitsAdapter, + sys: CliSys, tarball_cache: Arc, maybe_node_modules_path: Option, system_info: NpmSystemInfo, diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 6f686c3553..34eaf21419 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -7,6 +7,7 @@ use std::borrow::Cow; use std::path::Path; use std::sync::Arc; +use crate::sys::CliSys; use dashmap::DashMap; use deno_core::error::AnyError; use deno_core::serde_json; @@ -17,7 +18,6 @@ use deno_resolver::npm::ByonmInNpmPackageChecker; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::CliNpmReqResolver; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; @@ -41,12 +41,10 @@ pub use self::managed::ManagedCliNpmResolver; pub use self::managed::PackageCaching; pub type CliNpmTarballCache = - deno_npm_cache::TarballCache; -pub type CliNpmCache = deno_npm_cache::NpmCache; -pub type CliNpmRegistryInfoProvider = deno_npm_cache::RegistryInfoProvider< - CliNpmCacheHttpClient, - FsSysTraitsAdapter, ->; + deno_npm_cache::TarballCache; +pub type CliNpmCache = deno_npm_cache::NpmCache; +pub type CliNpmRegistryInfoProvider = + deno_npm_cache::RegistryInfoProvider; #[derive(Debug)] pub struct CliNpmCacheHttpClient { diff --git a/cli/resolver.rs b/cli/resolver.rs index 0a4fd78686..c4c8ef8b36 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -5,6 +5,7 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use crate::sys::CliSys; use async_trait::async_trait; use dashmap::DashMap; use dashmap::DashSet; @@ -25,7 +26,6 @@ use deno_npm::resolution::NpmResolutionError; use deno_resolver::sloppy_imports::SloppyImportsResolver; use deno_runtime::colors; use deno_runtime::deno_fs; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use deno_runtime::deno_node::is_builtin_node_module; use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_semver::package::PackageReq; @@ -43,19 +43,17 @@ use crate::npm::InnerCliNpmResolverRef; use crate::util::sync::AtomicFlag; use crate::util::text_encoding::from_utf8_lossy_cow; -pub type CjsTracker = deno_resolver::cjs::CjsTracker; -pub type IsCjsResolver = deno_resolver::cjs::IsCjsResolver; +pub type CjsTracker = deno_resolver::cjs::CjsTracker; +pub type IsCjsResolver = deno_resolver::cjs::IsCjsResolver; pub type CliSloppyImportsResolver = SloppyImportsResolver; pub type CliDenoResolver = deno_resolver::DenoResolver< RealIsBuiltInNodeModuleChecker, SloppyImportsCachedFs, - FsSysTraitsAdapter, ->; -pub type CliNpmReqResolver = deno_resolver::npm::NpmReqResolver< - RealIsBuiltInNodeModuleChecker, - FsSysTraitsAdapter, + CliSys, >; +pub type CliNpmReqResolver = + deno_resolver::npm::NpmReqResolver; pub struct ModuleCodeStringSource { pub code: ModuleSourceCode, @@ -397,7 +395,7 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { #[derive(Debug)] pub struct SloppyImportsCachedFs { - sys: FsSysTraitsAdapter, + sys: CliSys, cache: Option< DashMap< PathBuf, @@ -407,14 +405,14 @@ pub struct SloppyImportsCachedFs { } impl SloppyImportsCachedFs { - pub fn new(sys: FsSysTraitsAdapter) -> Self { + pub fn new(sys: CliSys) -> Self { Self { sys, cache: Some(Default::default()), } } - pub fn new_without_stat_cache(fs: FsSysTraitsAdapter) -> Self { + pub fn new_without_stat_cache(fs: CliSys) -> Self { Self { sys: fs, cache: None, diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 91187c48d1..48af787f18 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -259,7 +259,6 @@ pub fn is_standalone_binary(exe_path: &Path) -> bool { } pub struct StandaloneData { - pub fs: Arc, pub metadata: Metadata, pub modules: StandaloneModules, pub npm_snapshot: Option, @@ -382,10 +381,7 @@ pub fn extract_standalone( }; Arc::new(FileBackedVfs::new(Cow::Borrowed(vfs_files_data), fs_root)) }; - let fs: Arc = - Arc::new(DenoCompileFileSystem::new(vfs.clone())); Ok(Some(StandaloneData { - fs, metadata, modules: StandaloneModules { remote_modules, diff --git a/cli/standalone/code_cache.rs b/cli/standalone/code_cache.rs index a44c920328..ec89c3ab1b 100644 --- a/cli/standalone/code_cache.rs +++ b/cli/standalone/code_cache.rs @@ -18,7 +18,6 @@ use deno_core::unsync::sync::AtomicFlag; use deno_path_util::get_atomic_path; use deno_runtime::code_cache::CodeCache; use deno_runtime::code_cache::CodeCacheType; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use crate::cache::FastInsecureHasher; use crate::worker::CliCodeCache; @@ -191,7 +190,7 @@ impl FirstRunCodeCacheStrategy { ) { let count = cache_data.len(); let temp_file = - get_atomic_path(&FsSysTraitsAdapter::new_real(), &self.file_path); + get_atomic_path(&sys_traits::impls::RealSys, &self.file_path); match serialize(&temp_file, self.cache_key, cache_data) { Ok(()) => { if let Err(err) = std::fs::rename(&temp_file, &self.file_path) { diff --git a/cli/standalone/file_system.rs b/cli/standalone/file_system.rs index 4b1024db6a..0a11d4550f 100644 --- a/cli/standalone/file_system.rs +++ b/cli/standalone/file_system.rs @@ -1,23 +1,34 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; +use std::time::Duration; +use std::time::SystemTime; use deno_runtime::deno_fs::AccessCheckCb; use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FsDirEntry; use deno_runtime::deno_fs::FsFileType; -use deno_runtime::deno_fs::FsStatSlim; use deno_runtime::deno_fs::OpenOptions; use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_io::fs::File; use deno_runtime::deno_io::fs::FsError; use deno_runtime::deno_io::fs::FsResult; use deno_runtime::deno_io::fs::FsStat; +use sys_traits::boxed::BoxedFsDirEntry; +use sys_traits::boxed::BoxedFsMetadataValue; +use sys_traits::boxed::FsMetadataBoxed; +use sys_traits::boxed::FsReadDirBoxed; +use sys_traits::FsMetadata; use super::virtual_fs::FileBackedVfs; +use super::virtual_fs::FileBackedVfsDirEntry; +use super::virtual_fs::FileBackedVfsFile; +use super::virtual_fs::FileBackedVfsMetadata; use super::virtual_fs::VfsFileSubDataKind; #[derive(Debug, Clone)] @@ -83,7 +94,7 @@ impl FileSystem for DenoCompileFileSystem { access_check: Option, ) -> FsResult> { if self.0.is_path_within(path) { - Ok(self.0.open_file(path)?) + Ok(Rc::new(self.0.open_file(path)?)) } else { RealFs.open_sync(path, options, access_check) } @@ -95,7 +106,7 @@ impl FileSystem for DenoCompileFileSystem { access_check: Option>, ) -> FsResult> { if self.0.is_path_within(&path) { - Ok(self.0.open_file(&path)?) + Ok(Rc::new(self.0.open_file(&path)?)) } else { RealFs.open_async(path, options, access_check).await } @@ -215,14 +226,14 @@ impl FileSystem for DenoCompileFileSystem { fn stat_sync(&self, path: &Path) -> FsResult { if self.0.is_path_within(path) { - Ok(self.0.stat(path)?) + Ok(self.0.stat(path)?.as_fs_stat()) } else { RealFs.stat_sync(path) } } async fn stat_async(&self, path: PathBuf) -> FsResult { if self.0.is_path_within(&path) { - Ok(self.0.stat(&path)?) + Ok(self.0.stat(&path)?.as_fs_stat()) } else { RealFs.stat_async(path).await } @@ -230,14 +241,14 @@ impl FileSystem for DenoCompileFileSystem { fn lstat_sync(&self, path: &Path) -> FsResult { if self.0.is_path_within(path) { - Ok(self.0.lstat(path)?) + Ok(self.0.lstat(path)?.as_fs_stat()) } else { RealFs.lstat_sync(path) } } async fn lstat_async(&self, path: PathBuf) -> FsResult { if self.0.is_path_within(&path) { - Ok(self.0.lstat(&path)?) + Ok(self.0.lstat(&path)?.as_fs_stat()) } else { RealFs.lstat_async(path).await } @@ -398,3 +409,428 @@ impl FileSystem for DenoCompileFileSystem { .await } } + +impl sys_traits::BaseFsHardLink for DenoCompileFileSystem { + #[inline] + fn base_fs_hard_link(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + self.link_sync(src, dst).map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsRead for DenoCompileFileSystem { + #[inline] + fn base_fs_read(&self, path: &Path) -> std::io::Result> { + self + .read_file_sync(path, None) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::FsMetadataValue for FileBackedVfsMetadata { + fn file_type(&self) -> sys_traits::FileType { + self.file_type + } + + fn len(&self) -> u64 { + self.len + } + + fn accessed(&self) -> std::io::Result { + Err(not_supported("accessed time")) + } + + fn created(&self) -> std::io::Result { + Err(not_supported("created time")) + } + + fn changed(&self) -> std::io::Result { + Err(not_supported("changed time")) + } + + fn modified(&self) -> std::io::Result { + Err(not_supported("modified time")) + } + + fn dev(&self) -> std::io::Result { + Ok(0) + } + + fn ino(&self) -> std::io::Result { + Ok(0) + } + + fn mode(&self) -> std::io::Result { + Ok(0) + } + + fn nlink(&self) -> std::io::Result { + Ok(0) + } + + fn uid(&self) -> std::io::Result { + Ok(0) + } + + fn gid(&self) -> std::io::Result { + Ok(0) + } + + fn rdev(&self) -> std::io::Result { + Ok(0) + } + + fn blksize(&self) -> std::io::Result { + Ok(0) + } + + fn blocks(&self) -> std::io::Result { + Ok(0) + } + + fn is_block_device(&self) -> std::io::Result { + Ok(false) + } + + fn is_char_device(&self) -> std::io::Result { + Ok(false) + } + + fn is_fifo(&self) -> std::io::Result { + Ok(false) + } + + fn is_socket(&self) -> std::io::Result { + Ok(false) + } + + fn file_attributes(&self) -> std::io::Result { + Ok(0) + } +} + +fn not_supported(name: &str) -> std::io::Error { + std::io::Error::new( + ErrorKind::Unsupported, + format!( + "{} is not supported for an embedded deno compile file", + name + ), + ) +} + +impl sys_traits::FsDirEntry for FileBackedVfsDirEntry { + type Metadata = BoxedFsMetadataValue; + + fn file_name(&self) -> Cow { + Cow::Borrowed(self.metadata.name.as_ref()) + } + + fn file_type(&self) -> std::io::Result { + Ok(self.metadata.file_type) + } + + fn metadata(&self) -> std::io::Result { + Ok(BoxedFsMetadataValue(Box::new(self.metadata.clone()))) + } + + fn path(&self) -> Cow { + Cow::Owned(self.parent_path.join(&self.metadata.name)) + } +} + +impl sys_traits::BaseFsReadDir for DenoCompileFileSystem { + type ReadDirEntry = BoxedFsDirEntry; + + fn base_fs_read_dir( + &self, + path: &Path, + ) -> std::io::Result< + Box> + '_>, + > { + if self.0.is_path_within(path) { + let entries = self.0.read_dir_with_metadata(path)?; + Ok(Box::new( + entries.map(|entry| Ok(BoxedFsDirEntry::new(entry))), + )) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.fs_read_dir_boxed(path) + } + } +} + +impl sys_traits::BaseFsCanonicalize for DenoCompileFileSystem { + #[inline] + fn base_fs_canonicalize(&self, path: &Path) -> std::io::Result { + self.realpath_sync(path).map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsMetadata for DenoCompileFileSystem { + type Metadata = BoxedFsMetadataValue; + + #[inline] + fn base_fs_metadata(&self, path: &Path) -> std::io::Result { + if self.0.is_path_within(path) { + Ok(BoxedFsMetadataValue::new(self.0.stat(path)?)) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.fs_metadata_boxed(path) + } + } + + #[inline] + fn base_fs_symlink_metadata( + &self, + path: &Path, + ) -> std::io::Result { + if self.0.is_path_within(path) { + Ok(BoxedFsMetadataValue::new(self.0.lstat(path)?)) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.fs_symlink_metadata_boxed(path) + } + } +} + +impl sys_traits::BaseFsCreateDir for DenoCompileFileSystem { + #[inline] + fn base_fs_create_dir( + &self, + path: &Path, + options: &sys_traits::CreateDirOptions, + ) -> std::io::Result<()> { + self + .mkdir_sync(path, options.recursive, options.mode) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsRemoveFile for DenoCompileFileSystem { + #[inline] + fn base_fs_remove_file(&self, path: &Path) -> std::io::Result<()> { + self + .remove_sync(path, false) + .map_err(|err| err.into_io_error()) + } +} + +impl sys_traits::BaseFsRename for DenoCompileFileSystem { + #[inline] + fn base_fs_rename(&self, from: &Path, to: &Path) -> std::io::Result<()> { + self + .rename_sync(from, to) + .map_err(|err| err.into_io_error()) + } +} + +pub enum FsFileAdapter { + Real(sys_traits::impls::RealFsFile), + Vfs(FileBackedVfsFile), +} + +impl sys_traits::FsFile for FsFileAdapter {} + +impl sys_traits::FsFileAsRaw for FsFileAdapter { + #[cfg(windows)] + fn fs_file_as_raw_handle(&self) -> Option { + match self { + Self::Real(file) => file.fs_file_as_raw_handle(), + Self::Vfs(_) => None, + } + } + + #[cfg(unix)] + fn fs_file_as_raw_fd(&self) -> Option { + match self { + Self::Real(file) => file.fs_file_as_raw_fd(), + Self::Vfs(_) => None, + } + } +} + +impl sys_traits::FsFileSyncData for FsFileAdapter { + fn fs_file_sync_data(&mut self) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_sync_data(), + Self::Vfs(_) => Ok(()), + } + } +} + +impl sys_traits::FsFileSyncAll for FsFileAdapter { + fn fs_file_sync_all(&mut self) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_sync_all(), + Self::Vfs(_) => Ok(()), + } + } +} + +impl sys_traits::FsFileSetPermissions for FsFileAdapter { + #[inline] + fn fs_file_set_permissions(&mut self, mode: u32) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_set_permissions(mode), + Self::Vfs(_) => Ok(()), + } + } +} + +impl std::io::Read for FsFileAdapter { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + match self { + Self::Real(file) => file.read(buf), + Self::Vfs(file) => file.read_to_buf(buf), + } + } +} + +impl std::io::Seek for FsFileAdapter { + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + match self { + Self::Real(file) => file.seek(pos), + Self::Vfs(file) => file.seek(pos), + } + } +} + +impl std::io::Write for FsFileAdapter { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + match self { + Self::Real(file) => file.write(buf), + Self::Vfs(_) => Err(not_supported("writing files")), + } + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + match self { + Self::Real(file) => file.flush(), + Self::Vfs(_) => Err(not_supported("writing files")), + } + } +} + +impl sys_traits::FsFileSetLen for FsFileAdapter { + #[inline] + fn fs_file_set_len(&mut self, len: u64) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_set_len(len), + Self::Vfs(_) => Err(not_supported("setting file length")), + } + } +} + +impl sys_traits::FsFileSetTimes for FsFileAdapter { + fn fs_file_set_times( + &mut self, + times: sys_traits::FsFileTimes, + ) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_set_times(times), + Self::Vfs(_) => Err(not_supported("setting file times")), + } + } +} + +impl sys_traits::FsFileLock for FsFileAdapter { + fn fs_file_lock( + &mut self, + mode: sys_traits::FsFileLockMode, + ) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_lock(mode), + Self::Vfs(_) => Err(not_supported("locking files")), + } + } + + fn fs_file_try_lock( + &mut self, + mode: sys_traits::FsFileLockMode, + ) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_try_lock(mode), + Self::Vfs(_) => Err(not_supported("locking files")), + } + } + + fn fs_file_unlock(&mut self) -> std::io::Result<()> { + match self { + Self::Real(file) => file.fs_file_unlock(), + Self::Vfs(_) => Err(not_supported("unlocking files")), + } + } +} + +impl sys_traits::FsFileIsTerminal for FsFileAdapter { + #[inline] + fn fs_file_is_terminal(&self) -> bool { + match self { + Self::Real(file) => file.fs_file_is_terminal(), + Self::Vfs(_) => false, + } + } +} + +impl sys_traits::BaseFsOpen for DenoCompileFileSystem { + type File = FsFileAdapter; + + fn base_fs_open( + &self, + path: &Path, + options: &sys_traits::OpenOptions, + ) -> std::io::Result { + if self.0.is_path_within(path) { + Ok(FsFileAdapter::Vfs(self.0.open_file(path)?)) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + Ok(FsFileAdapter::Real( + sys_traits::impls::RealSys.base_fs_open(path, options)?, + )) + } + } +} + +impl sys_traits::SystemRandom for DenoCompileFileSystem { + #[inline] + fn sys_random(&self, buf: &mut [u8]) -> std::io::Result<()> { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.sys_random(buf) + } +} + +impl sys_traits::SystemTimeNow for DenoCompileFileSystem { + #[inline] + fn sys_time_now(&self) -> SystemTime { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.sys_time_now() + } +} + +impl sys_traits::ThreadSleep for DenoCompileFileSystem { + #[inline] + fn thread_sleep(&self, dur: Duration) { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.thread_sleep(dur) + } +} + +impl sys_traits::EnvCurrentDir for DenoCompileFileSystem { + fn env_current_dir(&self) -> std::io::Result { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.env_current_dir() + } +} + +impl sys_traits::BaseEnvVar for DenoCompileFileSystem { + fn base_env_var_os( + &self, + key: &std::ffi::OsStr, + ) -> Option { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.base_env_var_os(key) + } +} diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 0ce25ff62f..0fe6de0b9a 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -36,11 +36,10 @@ use deno_package_json::PackageJsonDepValue; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::NpmReqResolverOptions; use deno_runtime::deno_fs; -use deno_runtime::deno_fs::FsSysTraitsAdapter; +use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::NodeRequireLoader; use deno_runtime::deno_node::NodeResolver; -use deno_runtime::deno_node::PackageJsonResolver; use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; @@ -77,6 +76,8 @@ use crate::cache::NodeAnalysisCache; use crate::http_util::HttpClientProvider; use crate::node::CliCjsCodeAnalyzer; use crate::node::CliNodeCodeTranslator; +use crate::node::CliNodeResolver; +use crate::node::CliPackageJsonResolver; use crate::npm::create_cli_npm_resolver; use crate::npm::create_in_npm_pkg_checker; use crate::npm::CliByonmNpmResolverCreateOptions; @@ -89,6 +90,7 @@ use crate::npm::CreateInNpmPkgCheckerOptions; use crate::resolver::CjsTracker; use crate::resolver::CliNpmReqResolver; use crate::resolver::NpmModuleLoader; +use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::util::text_encoding::from_utf8_lossy_cow; @@ -105,12 +107,12 @@ mod file_system; mod serialization; mod virtual_fs; +pub use self::file_system::DenoCompileFileSystem; pub use binary::extract_standalone; pub use binary::is_standalone_binary; pub use binary::DenoCompileBinaryWriter; use self::binary::Metadata; -use self::file_system::DenoCompileFileSystem; struct SharedModuleLoaderState { cjs_tracker: Arc, @@ -118,7 +120,7 @@ struct SharedModuleLoaderState { fs: Arc, modules: StandaloneModules, node_code_translator: Arc, - node_resolver: Arc, + node_resolver: Arc, npm_module_loader: Arc, npm_req_resolver: Arc, npm_resolver: Arc, @@ -627,9 +629,12 @@ impl RootCertStoreProvider for StandaloneRootCertStoreProvider { } } -pub async fn run(data: StandaloneData) -> Result { +pub async fn run( + fs: Arc, + sys: CliSys, + data: StandaloneData, +) -> Result { let StandaloneData { - fs, metadata, modules, npm_snapshot, @@ -637,7 +642,7 @@ pub async fn run(data: StandaloneData) -> Result { source_maps, vfs, } = data; - let deno_dir_provider = Arc::new(DenoDirProvider::new(None)); + let deno_dir_provider = Arc::new(DenoDirProvider::new(sys.clone(), None)); let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider { ca_stores: metadata.ca_stores, ca_data: metadata.ca_data.map(CaData::Bytes), @@ -655,8 +660,7 @@ pub async fn run(data: StandaloneData) -> Result { let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap(); let npm_global_cache_dir = root_path.join(".deno_compile_node_modules"); let cache_setting = CacheSetting::Only; - let sys = FsSysTraitsAdapter(fs.clone()); - let pkg_json_resolver = Arc::new(PackageJsonResolver::new(sys.clone())); + let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone())); let (in_npm_pkg_checker, npm_resolver) = match metadata.node_modules { Some(binary::NodeModules::Managed { node_modules_dir }) => { // create an npmrc that uses the fake npm_registry_url to resolve packages @@ -807,7 +811,7 @@ pub async fn run(data: StandaloneData) -> Result { node_resolver.clone(), npm_resolver.clone().into_npm_pkg_folder_resolver(), pkg_json_resolver.clone(), - sys, + sys.clone(), )); let workspace_resolver = { let import_map = match metadata.workspace_resolver.import_map { @@ -910,7 +914,7 @@ pub async fn run(data: StandaloneData) -> Result { } let desc_parser = - Arc::new(RuntimePermissionDescriptorParser::new(fs.clone())); + Arc::new(RuntimePermissionDescriptorParser::new(sys.clone())); let permissions = Permissions::from_options(desc_parser.as_ref(), &permissions)?; PermissionsContainer::new(desc_parser, permissions) @@ -940,6 +944,7 @@ pub async fn run(data: StandaloneData) -> Result { root_cert_store_provider, permissions, StorageKeyResolver::empty(), + sys, crate::args::DenoSubcommand::Run(Default::default()), CliMainWorkerOptions { argv: metadata.argv, diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index 522fe47dd9..370d07a488 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -51,8 +51,16 @@ pub enum WindowsSystemRootablePath { impl WindowsSystemRootablePath { pub fn join(&self, name_component: &str) -> PathBuf { // this method doesn't handle multiple components - debug_assert!(!name_component.contains('\\')); - debug_assert!(!name_component.contains('/')); + debug_assert!( + !name_component.contains('\\'), + "Invalid component: {}", + name_component + ); + debug_assert!( + !name_component.contains('/'), + "Invalid component: {}", + name_component + ); match self { WindowsSystemRootablePath::WindowSystemRoot => { @@ -847,78 +855,28 @@ enum VfsEntryRef<'a> { Symlink(&'a VirtualSymlink), } -impl<'a> VfsEntryRef<'a> { - pub fn as_fs_stat(&self) -> FsStat { +impl VfsEntryRef<'_> { + pub fn as_metadata(&self) -> FileBackedVfsMetadata { + FileBackedVfsMetadata { + file_type: match self { + Self::Dir(_) => sys_traits::FileType::Dir, + Self::File(_) => sys_traits::FileType::File, + Self::Symlink(_) => sys_traits::FileType::Symlink, + }, + name: self.name().to_string(), + len: match self { + Self::Dir(_) => 0, + Self::File(file) => file.offset.len, + Self::Symlink(_) => 0, + }, + } + } + + pub fn name(&self) -> &str { match self { - VfsEntryRef::Dir(_) => FsStat { - is_directory: true, - is_file: false, - is_symlink: false, - atime: None, - birthtime: None, - mtime: None, - ctime: None, - blksize: 0, - size: 0, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }, - VfsEntryRef::File(file) => FsStat { - is_directory: false, - is_file: true, - is_symlink: false, - atime: None, - birthtime: None, - mtime: None, - ctime: None, - blksize: 0, - size: file.offset.len, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }, - VfsEntryRef::Symlink(_) => FsStat { - is_directory: false, - is_file: false, - is_symlink: true, - atime: None, - birthtime: None, - mtime: None, - ctime: None, - blksize: 0, - size: 0, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }, + Self::Dir(dir) => &dir.name, + Self::File(file) => &file.name, + Self::Symlink(symlink) => &symlink.name, } } } @@ -934,9 +892,9 @@ pub enum VfsEntry { impl VfsEntry { pub fn name(&self) -> &str { match self { - VfsEntry::Dir(dir) => &dir.name, - VfsEntry::File(file) => &file.name, - VfsEntry::Symlink(symlink) => &symlink.name, + Self::Dir(dir) => &dir.name, + Self::File(file) => &file.name, + Self::Symlink(symlink) => &symlink.name, } } @@ -1180,14 +1138,14 @@ impl VfsRoot { } } -struct FileBackedVfsFile { +pub struct FileBackedVfsFile { file: VirtualFile, pos: RefCell, vfs: Arc, } impl FileBackedVfsFile { - fn seek(&self, pos: SeekFrom) -> FsResult { + pub fn seek(&self, pos: SeekFrom) -> std::io::Result { match pos { SeekFrom::Start(pos) => { *self.pos.borrow_mut() = pos; @@ -1196,10 +1154,10 @@ impl FileBackedVfsFile { SeekFrom::End(offset) => { if offset < 0 && -offset as u64 > self.file.offset.len { let msg = "An attempt was made to move the file pointer before the beginning of the file."; - Err( - std::io::Error::new(std::io::ErrorKind::PermissionDenied, msg) - .into(), - ) + Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + msg, + )) } else { let mut current_pos = self.pos.borrow_mut(); *current_pos = if offset >= 0 { @@ -1215,7 +1173,7 @@ impl FileBackedVfsFile { if offset >= 0 { *current_pos += offset as u64; } else if -offset as u64 > *current_pos { - return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "An attempt was made to move the file pointer before the beginning of the file.").into()); + return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "An attempt was made to move the file pointer before the beginning of the file.")); } else { *current_pos -= -offset as u64; } @@ -1224,7 +1182,7 @@ impl FileBackedVfsFile { } } - fn read_to_buf(&self, buf: &mut [u8]) -> FsResult { + pub fn read_to_buf(&self, buf: &mut [u8]) -> std::io::Result { let read_pos = { let mut pos = self.pos.borrow_mut(); let read_pos = *pos; @@ -1232,10 +1190,7 @@ impl FileBackedVfsFile { *pos = std::cmp::min(self.file.offset.len, *pos + buf.len() as u64); read_pos }; - self - .vfs - .read_file(&self.file, read_pos, buf) - .map_err(|err| err.into()) + self.vfs.read_file(&self.file, read_pos, buf) } fn read_to_end(&self) -> FsResult> { @@ -1270,7 +1225,7 @@ impl FileBackedVfsFile { #[async_trait::async_trait(?Send)] impl deno_io::fs::File for FileBackedVfsFile { fn read_sync(self: Rc, buf: &mut [u8]) -> FsResult { - self.read_to_buf(buf) + self.read_to_buf(buf).map_err(Into::into) } async fn read_byob( self: Rc, @@ -1314,10 +1269,10 @@ impl deno_io::fs::File for FileBackedVfsFile { } fn seek_sync(self: Rc, pos: SeekFrom) -> FsResult { - self.seek(pos) + self.seek(pos).map_err(|err| err.into()) } async fn seek_async(self: Rc, pos: SeekFrom) -> FsResult { - self.seek(pos) + self.seek(pos).map_err(|err| err.into()) } fn datasync_sync(self: Rc) -> FsResult<()> { @@ -1393,6 +1348,47 @@ impl deno_io::fs::File for FileBackedVfsFile { } } +#[derive(Debug, Clone)] +pub struct FileBackedVfsDirEntry { + pub parent_path: PathBuf, + pub metadata: FileBackedVfsMetadata, +} + +#[derive(Debug, Clone)] +pub struct FileBackedVfsMetadata { + pub name: String, + pub file_type: sys_traits::FileType, + pub len: u64, +} + +impl FileBackedVfsMetadata { + pub fn as_fs_stat(&self) -> FsStat { + FsStat { + is_directory: self.file_type == sys_traits::FileType::Dir, + is_file: self.file_type == sys_traits::FileType::File, + is_symlink: self.file_type == sys_traits::FileType::Symlink, + atime: None, + birthtime: None, + mtime: None, + ctime: None, + blksize: 0, + size: self.len, + dev: 0, + ino: 0, + mode: 0, + nlink: 0, + uid: 0, + gid: 0, + rdev: 0, + blocks: 0, + is_block_device: false, + is_char_device: false, + is_fifo: false, + is_socket: false, + } + } +} + #[derive(Debug)] pub struct FileBackedVfs { vfs_data: Cow<'static, [u8]>, @@ -1418,13 +1414,13 @@ impl FileBackedVfs { pub fn open_file( self: &Arc, path: &Path, - ) -> std::io::Result> { + ) -> std::io::Result { let file = self.file_entry(path)?; - Ok(Rc::new(FileBackedVfsFile { + Ok(FileBackedVfsFile { file: file.clone(), vfs: self.clone(), pos: Default::default(), - })) + }) } pub fn read_dir(&self, path: &Path) -> std::io::Result> { @@ -1443,6 +1439,18 @@ impl FileBackedVfs { ) } + pub fn read_dir_with_metadata<'a>( + &'a self, + path: &Path, + ) -> std::io::Result + 'a> { + let dir = self.dir_entry(path)?; + let path = path.to_path_buf(); + Ok(dir.entries.iter().map(move |entry| FileBackedVfsDirEntry { + parent_path: path.to_path_buf(), + metadata: entry.as_ref().as_metadata(), + })) + } + pub fn read_link(&self, path: &Path) -> std::io::Result { let (_, entry) = self.fs_root.find_entry_no_follow(path)?; match entry { @@ -1456,14 +1464,14 @@ impl FileBackedVfs { } } - pub fn lstat(&self, path: &Path) -> std::io::Result { + pub fn lstat(&self, path: &Path) -> std::io::Result { let (_, entry) = self.fs_root.find_entry_no_follow(path)?; - Ok(entry.as_fs_stat()) + Ok(entry.as_metadata()) } - pub fn stat(&self, path: &Path) -> std::io::Result { + pub fn stat(&self, path: &Path) -> std::io::Result { let (_, entry) = self.fs_root.find_entry(path)?; - Ok(entry.as_fs_stat()) + Ok(entry.as_metadata()) } pub fn canonicalize(&self, path: &Path) -> std::io::Result { @@ -1556,6 +1564,7 @@ impl FileBackedVfs { #[cfg(test)] mod test { use console_static_text::ansi::strip_ansi_codes; + use deno_io::fs::File; use std::io::Write; use test_util::assert_contains; use test_util::TempDir; @@ -1641,25 +1650,31 @@ mod test { ); // metadata - assert!( + assert_eq!( virtual_fs .lstat(&dest_path.join("sub_dir").join("e.txt")) .unwrap() - .is_symlink + .file_type, + sys_traits::FileType::Symlink, ); - assert!( + assert_eq!( virtual_fs .stat(&dest_path.join("sub_dir").join("e.txt")) .unwrap() - .is_file + .file_type, + sys_traits::FileType::File, ); - assert!( + assert_eq!( virtual_fs .stat(&dest_path.join("sub_dir")) .unwrap() - .is_directory, + .file_type, + sys_traits::FileType::Dir, + ); + assert_eq!( + virtual_fs.stat(&dest_path.join("e.txt")).unwrap().file_type, + sys_traits::FileType::File ); - assert!(virtual_fs.stat(&dest_path.join("e.txt")).unwrap().is_file,); } #[test] @@ -1696,11 +1711,12 @@ mod test { read_file(&virtual_fs, &dest_path.join("sub_dir_link").join("c.txt")), "c", ); - assert!( + assert_eq!( virtual_fs .lstat(&dest_path.join("sub_dir_link")) .unwrap() - .is_symlink + .file_type, + sys_traits::FileType::Symlink, ); assert_eq!( @@ -1772,37 +1788,35 @@ mod test { let (dest_path, virtual_fs) = into_virtual_fs(builder, &temp_dir); let virtual_fs = Arc::new(virtual_fs); let file = virtual_fs.open_file(&dest_path.join("a.txt")).unwrap(); - file.clone().seek_sync(SeekFrom::Current(2)).unwrap(); + file.seek(SeekFrom::Current(2)).unwrap(); let mut buf = vec![0; 2]; - file.clone().read_sync(&mut buf).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"23"); - file.clone().read_sync(&mut buf).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"45"); - file.clone().seek_sync(SeekFrom::Current(-4)).unwrap(); - file.clone().read_sync(&mut buf).unwrap(); + file.seek(SeekFrom::Current(-4)).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"23"); - file.clone().seek_sync(SeekFrom::Start(2)).unwrap(); - file.clone().read_sync(&mut buf).unwrap(); + file.seek(SeekFrom::Start(2)).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"23"); - file.clone().seek_sync(SeekFrom::End(2)).unwrap(); - file.clone().read_sync(&mut buf).unwrap(); + file.seek(SeekFrom::End(2)).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"89"); - file.clone().seek_sync(SeekFrom::Current(-8)).unwrap(); - file.clone().read_sync(&mut buf).unwrap(); + file.seek(SeekFrom::Current(-8)).unwrap(); + file.read_to_buf(&mut buf).unwrap(); assert_eq!(buf, b"23"); assert_eq!( file - .clone() - .seek_sync(SeekFrom::Current(-5)) - .err() - .unwrap() - .into_io_error() + .seek(SeekFrom::Current(-5)) + .unwrap_err() .to_string(), "An attempt was made to move the file pointer before the beginning of the file." ); // go beyond the file length, then back - file.clone().seek_sync(SeekFrom::Current(40)).unwrap(); - file.clone().seek_sync(SeekFrom::Current(-38)).unwrap(); + file.seek(SeekFrom::Current(40)).unwrap(); + file.seek(SeekFrom::Current(-38)).unwrap(); + let file = Rc::new(file); let read_buf = file.clone().read(2).await.unwrap(); assert_eq!(read_buf.to_vec(), b"67"); file.clone().seek_sync(SeekFrom::Current(-2)).unwrap(); diff --git a/cli/sys.rs b/cli/sys.rs new file mode 100644 index 0000000000..55b50a199d --- /dev/null +++ b/cli/sys.rs @@ -0,0 +1,218 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// todo(dsherret): this should instead use conditional compilation and directly +// surface the underlying implementation. +// +// The problem atm is that there's no way to have conditional compilation for +// denort or the deno binary. We should extract out denort to a separate binary. + +use std::borrow::Cow; + +use sys_traits::boxed::BoxedFsDirEntry; +use sys_traits::boxed::BoxedFsFile; +use sys_traits::boxed::BoxedFsMetadataValue; +use sys_traits::boxed::FsMetadataBoxed; +use sys_traits::boxed::FsOpenBoxed; +use sys_traits::boxed::FsReadDirBoxed; +use sys_traits::CreateDirOptions; + +use crate::standalone::DenoCompileFileSystem; + +#[derive(Debug, Clone)] +pub enum CliSys { + #[allow(dead_code)] // will be dead code for denort + #[allow(clippy::disallowed_types)] // ok because sys impl + Real(sys_traits::impls::RealSys), + #[allow(dead_code)] // will be dead code for deno + DenoCompile(DenoCompileFileSystem), +} + +impl Default for CliSys { + fn default() -> Self { + Self::Real(sys_traits::impls::RealSys) + } +} + +impl deno_runtime::deno_node::ExtNodeSys for CliSys {} + +impl sys_traits::BaseFsHardLink for CliSys { + fn base_fs_hard_link( + &self, + src: &std::path::Path, + dst: &std::path::Path, + ) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_hard_link(src, dst), + Self::DenoCompile(sys) => sys.base_fs_hard_link(src, dst), + } + } +} + +impl sys_traits::BaseFsRead for CliSys { + fn base_fs_read( + &self, + p: &std::path::Path, + ) -> std::io::Result> { + match self { + Self::Real(sys) => sys.base_fs_read(p), + Self::DenoCompile(sys) => sys.base_fs_read(p), + } + } +} + +impl sys_traits::BaseFsReadDir for CliSys { + type ReadDirEntry = BoxedFsDirEntry; + + fn base_fs_read_dir( + &self, + p: &std::path::Path, + ) -> std::io::Result< + Box> + '_>, + > { + match self { + Self::Real(sys) => sys.fs_read_dir_boxed(p), + Self::DenoCompile(sys) => sys.fs_read_dir_boxed(p), + } + } +} + +impl sys_traits::BaseFsCanonicalize for CliSys { + fn base_fs_canonicalize( + &self, + p: &std::path::Path, + ) -> std::io::Result { + match self { + Self::Real(sys) => sys.base_fs_canonicalize(p), + Self::DenoCompile(sys) => sys.base_fs_canonicalize(p), + } + } +} + +impl sys_traits::BaseFsMetadata for CliSys { + type Metadata = BoxedFsMetadataValue; + + fn base_fs_metadata( + &self, + path: &std::path::Path, + ) -> std::io::Result { + match self { + Self::Real(sys) => sys.fs_metadata_boxed(path), + Self::DenoCompile(sys) => sys.fs_metadata_boxed(path), + } + } + + fn base_fs_symlink_metadata( + &self, + path: &std::path::Path, + ) -> std::io::Result { + match self { + Self::Real(sys) => sys.fs_symlink_metadata_boxed(path), + Self::DenoCompile(sys) => sys.fs_symlink_metadata_boxed(path), + } + } +} + +impl sys_traits::BaseFsCreateDir for CliSys { + fn base_fs_create_dir( + &self, + p: &std::path::Path, + options: &CreateDirOptions, + ) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_create_dir(p, options), + Self::DenoCompile(sys) => sys.base_fs_create_dir(p, options), + } + } +} + +impl sys_traits::BaseFsOpen for CliSys { + type File = BoxedFsFile; + + fn base_fs_open( + &self, + path: &std::path::Path, + options: &sys_traits::OpenOptions, + ) -> std::io::Result { + match self { + Self::Real(sys) => sys.fs_open_boxed(path, options), + Self::DenoCompile(sys) => sys.fs_open_boxed(path, options), + } + } +} + +impl sys_traits::BaseFsRemoveFile for CliSys { + fn base_fs_remove_file(&self, p: &std::path::Path) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_remove_file(p), + Self::DenoCompile(sys) => sys.base_fs_remove_file(p), + } + } +} + +impl sys_traits::BaseFsRename for CliSys { + fn base_fs_rename( + &self, + old: &std::path::Path, + new: &std::path::Path, + ) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_rename(old, new), + Self::DenoCompile(sys) => sys.base_fs_rename(old, new), + } + } +} + +impl sys_traits::SystemRandom for CliSys { + fn sys_random(&self, buf: &mut [u8]) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.sys_random(buf), + Self::DenoCompile(sys) => sys.sys_random(buf), + } + } +} + +impl sys_traits::SystemTimeNow for CliSys { + fn sys_time_now(&self) -> std::time::SystemTime { + match self { + Self::Real(sys) => sys.sys_time_now(), + Self::DenoCompile(sys) => sys.sys_time_now(), + } + } +} + +impl sys_traits::ThreadSleep for CliSys { + fn thread_sleep(&self, dur: std::time::Duration) { + match self { + Self::Real(sys) => sys.thread_sleep(dur), + Self::DenoCompile(sys) => sys.thread_sleep(dur), + } + } +} + +impl sys_traits::EnvCurrentDir for CliSys { + fn env_current_dir(&self) -> std::io::Result { + match self { + Self::Real(sys) => sys.env_current_dir(), + Self::DenoCompile(sys) => sys.env_current_dir(), + } + } +} + +impl sys_traits::BaseEnvVar for CliSys { + fn base_env_var_os( + &self, + key: &std::ffi::OsStr, + ) -> Option { + match self { + Self::Real(sys) => sys.base_env_var_os(key), + Self::DenoCompile(sys) => sys.base_env_var_os(key), + } + } +} + +impl sys_traits::EnvHomeDir for CliSys { + fn env_home_dir(&self) -> Option { + #[allow(clippy::disallowed_types)] // ok because sys impl + sys_traits::impls::RealSys.env_home_dir() + } +} diff --git a/cli/task_runner.rs b/cli/task_runner.rs index d6589a1832..c7232387ca 100644 --- a/cli/task_runner.rs +++ b/cli/task_runner.rs @@ -10,7 +10,6 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::futures; use deno_core::futures::future::LocalBoxFuture; -use deno_runtime::deno_node::NodeResolver; use deno_semver::package::PackageNv; use deno_task_shell::ExecutableCommand; use deno_task_shell::ExecuteResult; @@ -25,6 +24,7 @@ use tokio::task::JoinHandle; use tokio::task::LocalSet; use tokio_util::sync::CancellationToken; +use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; use crate::npm::InnerCliNpmResolverRef; use crate::npm::ManagedCliNpmResolver; @@ -415,7 +415,7 @@ impl ShellCommand for NodeModulesFileRunCommand { pub fn resolve_custom_commands( npm_resolver: &dyn CliNpmResolver, - node_resolver: &NodeResolver, + node_resolver: &CliNodeResolver, ) -> Result>, AnyError> { let mut commands = match npm_resolver.as_inner() { InnerCliNpmResolverRef::Byonm(npm_resolver) => { @@ -522,7 +522,7 @@ fn resolve_execution_path_from_npx_shim( fn resolve_managed_npm_commands( npm_resolver: &ManagedCliNpmResolver, - node_resolver: &NodeResolver, + node_resolver: &CliNodeResolver, ) -> Result>, AnyError> { let mut result = HashMap::new(); let snapshot = npm_resolver.snapshot(); diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index 5983590531..1b47c9bfb0 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -7,6 +7,7 @@ use crate::display::write_json_to_stdout; use crate::factory::CliFactory; use crate::graph_util::has_graph_root_local_dependent_changed; use crate::ops; +use crate::sys::CliSys; use crate::tools::test::format_test_error; use crate::tools::test::TestFilter; use crate::util::file_watcher; @@ -265,7 +266,7 @@ async fn bench_specifier_inner( async fn bench_specifiers( worker_factory: Arc, permissions: &Permissions, - permissions_desc_parser: &Arc, + permissions_desc_parser: &Arc>, specifiers: Vec, options: BenchSpecifierOptions, ) -> Result<(), AnyError> { diff --git a/cli/tools/check.rs b/cli/tools/check.rs index 9af084806f..acfff70401 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -9,7 +9,6 @@ use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; use deno_graph::Module; use deno_graph::ModuleGraph; -use deno_runtime::deno_node::NodeResolver; use deno_terminal::colors; use once_cell::sync::Lazy; use regex::Regex; @@ -29,6 +28,7 @@ use crate::cache::TypeCheckCache; use crate::factory::CliFactory; use crate::graph_util::BuildFastCheckGraphOptions; use crate::graph_util::ModuleGraphBuilder; +use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; use crate::tsc; use crate::tsc::Diagnostics; @@ -103,7 +103,7 @@ pub struct TypeChecker { cjs_tracker: Arc, cli_options: Arc, module_graph_builder: Arc, - node_resolver: Arc, + node_resolver: Arc, npm_resolver: Arc, } @@ -113,7 +113,7 @@ impl TypeChecker { cjs_tracker: Arc, cli_options: Arc, module_graph_builder: Arc, - node_resolver: Arc, + node_resolver: Arc, npm_resolver: Arc, ) -> Self { Self { diff --git a/cli/tools/clean.rs b/cli/tools/clean.rs index 2a77434f88..cdc1c51dcd 100644 --- a/cli/tools/clean.rs +++ b/cli/tools/clean.rs @@ -7,6 +7,7 @@ use std::path::Path; use crate::cache::DenoDir; use crate::colors; use crate::display; +use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressMessagePrompt; @@ -28,7 +29,7 @@ impl CleanState { } pub fn clean() -> Result<(), AnyError> { - let deno_dir = DenoDir::new(None)?; + let deno_dir = DenoDir::new(CliSys::default(), None)?; if deno_dir.root.exists() { let no_of_files = walkdir::WalkDir::new(&deno_dir.root).into_iter().count(); let progress_bar = ProgressBar::new(ProgressBarStyle::ProgressBars); diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index 7a463a7b09..cbd376bae2 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -5,8 +5,8 @@ use crate::args::CompileFlags; use crate::args::Flags; use crate::factory::CliFactory; use crate::http_util::HttpClientProvider; +use crate::standalone::binary::is_standalone_binary; use crate::standalone::binary::WriteBinOptions; -use crate::standalone::is_standalone_binary; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_core::anyhow::bail; diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 4736bdab41..a9054207b0 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -7,6 +7,7 @@ use crate::args::Flags; use crate::cdp; use crate::factory::CliFactory; use crate::file_fetcher::TextDecodedFile; +use crate::sys::CliSys; use crate::tools::fmt::format_json; use crate::tools::test::is_supported_test_path; use crate::util::text_encoding::source_map_from_code; @@ -26,7 +27,6 @@ use deno_core::serde_json; use deno_core::sourcemap::SourceMap; use deno_core::url::Url; use deno_core::LocalInspectorSession; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use node_resolver::InNpmPackageChecker; use regex::Regex; use std::fs; @@ -429,7 +429,7 @@ fn collect_coverages( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&FsSysTraitsAdapter::new_real(), file_patterns)?; + .collect_file_patterns(&CliSys::default(), file_patterns)?; let coverage_patterns = FilePatterns { base: initial_cwd.to_path_buf(), diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index c33b988de0..0ff1806a9e 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -10,6 +10,7 @@ use crate::factory::CliFactory; use crate::graph_util::graph_exit_integrity_errors; use crate::graph_util::graph_walk_errors; use crate::graph_util::GraphWalkErrorsOptions; +use crate::sys::CliSys; use crate::tsc::get_types_declaration_file_text; use crate::util::fs::collect_specifiers; use deno_ast::diagnostics::Diagnostic; @@ -28,7 +29,6 @@ use deno_graph::EsParser; use deno_graph::GraphKind; use deno_graph::ModuleAnalyzer; use deno_graph::ModuleSpecifier; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use doc::html::ShortPath; use doc::DocDiagnostic; use indexmap::IndexMap; @@ -115,7 +115,7 @@ pub async fn doc( } DocSourceFileFlag::Paths(ref source_files) => { let module_graph_creator = factory.module_graph_creator().await?; - let fs = FsSysTraitsAdapter(factory.fs().clone()); + let sys = CliSys::default(); let module_specifiers = collect_specifiers( FilePatterns { @@ -142,7 +142,7 @@ pub async fn doc( graph_exit_integrity_errors(&graph); let errors = graph_walk_errors( &graph, - &fs, + &sys, &module_specifiers, GraphWalkErrorsOptions { check_js: false, diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 55046155c0..7f9a15f4b2 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -17,6 +17,7 @@ use crate::args::UnstableFmtOptions; use crate::cache::Caches; use crate::colors; use crate::factory::CliFactory; +use crate::sys::CliSys; use crate::util::diff::diff; use crate::util::file_watcher; use crate::util::fs::canonicalize_path; @@ -34,7 +35,6 @@ use deno_core::futures; use deno_core::parking_lot::Mutex; use deno_core::unsync::spawn_blocking; use deno_core::url::Url; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use log::debug; use log::info; use log::warn; @@ -58,7 +58,7 @@ pub async fn format( fmt_flags: FmtFlags, ) -> Result<(), AnyError> { if fmt_flags.is_stdin() { - let cli_options = CliOptions::from_flags(flags)?; + let cli_options = CliOptions::from_flags(&CliSys::default(), flags)?; let start_dir = &cli_options.start_dir; let fmt_config = start_dir .to_fmt_config(FilePatterns::new_with_base(start_dir.dir_path()))?; @@ -231,7 +231,7 @@ fn collect_fmt_files( .ignore_node_modules() .use_gitignore() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&FsSysTraitsAdapter::new_real(), files) + .collect_file_patterns(&CliSys::default(), files) } /// Formats markdown (using ) and its code blocks diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index ec538ecb0a..1bfd17f30d 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -364,6 +364,7 @@ async fn install_global( let deps_file_fetcher = CliFileFetcher::new( deps_http_cache.clone(), http_client.clone(), + factory.sys(), Default::default(), None, true, diff --git a/cli/tools/lint/linter.rs b/cli/tools/lint/linter.rs index a10ad6479e..6bb3c628fa 100644 --- a/cli/tools/lint/linter.rs +++ b/cli/tools/lint/linter.rs @@ -16,8 +16,8 @@ use deno_lint::linter::LintFileOptions; use deno_lint::linter::Linter as DenoLintLinter; use deno_lint::linter::LinterOptions; use deno_path_util::fs::atomic_write_file_with_retries; -use deno_runtime::deno_fs::FsSysTraitsAdapter; +use crate::sys::CliSys; use crate::util::fs::specifier_from_file_path; use super::rules::FileOrPackageLintRule; @@ -177,7 +177,7 @@ impl CliLinter { if fix_iterations > 0 { // everything looks good and the file still parses, so write it out atomic_write_file_with_retries( - &FsSysTraitsAdapter::new_real(), + &CliSys::default(), file_path, source.text().as_bytes(), crate::cache::CACHE_PERM, diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index 4071f30e7e..6d3997ac3b 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -21,7 +21,6 @@ use deno_core::unsync::future::SharedLocal; use deno_graph::ModuleGraph; use deno_lint::diagnostic::LintDiagnostic; use deno_lint::linter::LintConfig as DenoLintConfig; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use log::debug; use reporters::create_reporter; use reporters::LintReporter; @@ -44,6 +43,7 @@ use crate::cache::IncrementalCache; use crate::colors; use crate::factory::CliFactory; use crate::graph_util::ModuleGraphCreator; +use crate::sys::CliSys; use crate::tools::fmt::run_parallelized; use crate::util::display; use crate::util::file_watcher; @@ -453,7 +453,7 @@ fn collect_lint_files( .ignore_node_modules() .use_gitignore() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&FsSysTraitsAdapter::new_real(), files) + .collect_file_patterns(&CliSys::default(), files) } #[allow(clippy::print_stdout)] diff --git a/cli/tools/registry/paths.rs b/cli/tools/registry/paths.rs index b607c0924c..1c675982df 100644 --- a/cli/tools/registry/paths.rs +++ b/cli/tools/registry/paths.rs @@ -6,12 +6,12 @@ use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; +use crate::sys::CliSys; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_config::glob::FileCollector; use deno_config::glob::FilePatterns; use deno_core::error::AnyError; -use deno_runtime::deno_fs::FsSysTraitsAdapter; use thiserror::Error; use crate::args::CliOptions; @@ -346,5 +346,5 @@ fn collect_paths( .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) .use_gitignore() - .collect_file_patterns(&FsSysTraitsAdapter::new_real(), file_patterns) + .collect_file_patterns(&CliSys::default(), file_patterns) } diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index afa9b0222c..ab4d92762f 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -415,6 +415,7 @@ pub async fn add( let deps_file_fetcher = CliFileFetcher::new( deps_http_cache.clone(), http_client.clone(), + cli_factory.sys(), Default::default(), None, true, diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index 20a6043f26..bb4c60fde8 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -185,6 +185,7 @@ pub async fn outdated( let file_fetcher = CliFileFetcher::new( deps_http_cache.clone(), http_client.clone(), + factory.sys(), Default::default(), None, true, diff --git a/cli/tools/registry/unfurl.rs b/cli/tools/registry/unfurl.rs index ca50775717..989a6e1ed4 100644 --- a/cli/tools/registry/unfurl.rs +++ b/cli/tools/registry/unfurl.rs @@ -658,13 +658,12 @@ mod tests { use crate::resolver::SloppyImportsCachedFs; use super::*; + use crate::sys::CliSys; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_config::workspace::ResolverWorkspaceJsrPackage; use deno_core::serde_json::json; use deno_core::url::Url; - use deno_runtime::deno_fs::FsSysTraitsAdapter; - use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_node::PackageJson; use deno_semver::Version; use import_map::ImportMapWithDiagnostics; @@ -725,7 +724,7 @@ mod tests { ); let unfurler = SpecifierUnfurler::new( Some(Arc::new(CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new(FsSysTraitsAdapter::new_real()), + SloppyImportsCachedFs::new(CliSys::default()), ))), Arc::new(workspace_resolver), true, @@ -863,10 +862,10 @@ const warn2 = await import(`${expr}`); ], deno_config::workspace::PackageJsonDepResolution::Enabled, ); - let fs = FsSysTraitsAdapter(Arc::new(RealFs)); + let sys = CliSys::default(); let unfurler = SpecifierUnfurler::new( Some(Arc::new(CliSloppyImportsResolver::new( - SloppyImportsCachedFs::new(fs), + SloppyImportsCachedFs::new(sys), ))), Arc::new(workspace_resolver), true, diff --git a/cli/tools/task.rs b/cli/tools/task.rs index 740b0ce843..4d83cc98fd 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -25,7 +25,6 @@ use deno_core::futures::FutureExt; use deno_core::futures::StreamExt; use deno_core::url::Url; use deno_path_util::normalize_path; -use deno_runtime::deno_node::NodeResolver; use deno_task_shell::KillSignal; use deno_task_shell::ShellCommand; use indexmap::IndexMap; @@ -36,6 +35,7 @@ use crate::args::Flags; use crate::args::TaskFlags; use crate::colors; use crate::factory::CliFactory; +use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; use crate::task_runner; use crate::task_runner::run_future_forwarding_signals; @@ -267,7 +267,7 @@ struct RunSingleOptions<'a> { struct TaskRunner<'a> { task_flags: &'a TaskFlags, npm_resolver: &'a dyn CliNpmResolver, - node_resolver: &'a NodeResolver, + node_resolver: &'a CliNodeResolver, env_vars: HashMap, cli_options: &'a CliOptions, concurrency: usize, diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 3164b8ae59..3745d7c7ec 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -10,6 +10,7 @@ use crate::factory::CliFactory; use crate::file_fetcher::CliFileFetcher; use crate::graph_util::has_graph_root_local_dependent_changed; use crate::ops; +use crate::sys::CliSys; use crate::util::extract::extract_doc_tests; use crate::util::file_watcher; use crate::util::fs::collect_specifiers; @@ -1194,7 +1195,7 @@ static HAS_TEST_RUN_SIGINT_HANDLER: AtomicBool = AtomicBool::new(false); async fn test_specifiers( worker_factory: Arc, permissions: &Permissions, - permission_desc_parser: &Arc, + permission_desc_parser: &Arc>, specifiers: Vec, options: TestSpecifiersOptions, ) -> Result<(), AnyError> { diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 26cf385734..4c84050b5e 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -4,8 +4,10 @@ use crate::args::TsConfig; use crate::args::TypeCheckMode; use crate::cache::FastInsecureHasher; use crate::cache::ModuleInfoCache; +use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; use crate::resolver::CjsTracker; +use crate::sys::CliSys; use crate::util::checksum; use crate::util::path::mapped_specifier_for_tsc; use crate::worker::create_isolate_create_params; @@ -34,8 +36,6 @@ use deno_graph::Module; use deno_graph::ModuleGraph; use deno_graph::ResolutionResolved; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::deno_fs::FsSysTraitsAdapter; -use deno_runtime::deno_node::NodeResolver; use deno_semver::npm::NpmPackageReqReference; use node_resolver::errors::NodeJsErrorCode; use node_resolver::errors::NodeJsErrorCoded; @@ -380,7 +380,7 @@ impl TypeCheckingCjsTracker { #[derive(Debug)] pub struct RequestNpmState { pub cjs_tracker: Arc, - pub node_resolver: Arc, + pub node_resolver: Arc, pub npm_resolver: Arc, } @@ -661,7 +661,7 @@ fn op_load_inner( } else { // means it's Deno code importing an npm module let specifier = resolve_specifier_into_node_modules( - &FsSysTraitsAdapter::new_real(), + &CliSys::default(), &module.specifier, ); Some(Cow::Owned(load_from_node_modules( @@ -925,7 +925,7 @@ fn resolve_graph_specifier_types( // we currently only use "External" for when the module is in an npm package Ok(state.maybe_npm.as_ref().map(|_| { let specifier = resolve_specifier_into_node_modules( - &FsSysTraitsAdapter::new_real(), + &CliSys::default(), &module.specifier, ); into_specifier_and_media_type(Some(specifier)) diff --git a/cli/util/fs.rs b/cli/util/fs.rs index 58b5fc72c6..e0b9a6f4ee 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -17,8 +17,8 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::unsync::spawn_blocking; use deno_core::ModuleSpecifier; -use deno_runtime::deno_fs::FsSysTraitsAdapter; +use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressMessagePrompt; @@ -76,7 +76,7 @@ pub fn canonicalize_path_maybe_not_exists( path: &Path, ) -> Result { deno_path_util::fs::canonicalize_path_maybe_not_exists( - &FsSysTraitsAdapter::new_real(), + &CliSys::default(), path, ) } @@ -126,7 +126,7 @@ pub fn collect_specifiers( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(vendor_folder) - .collect_file_patterns(&FsSysTraitsAdapter::new_real(), files)?; + .collect_file_patterns(&CliSys::default(), files)?; let mut collected_files_as_urls = collected_files .iter() .map(|f| specifier_from_file_path(f).unwrap()) @@ -198,13 +198,11 @@ mod clone_dir_imp { from: &std::path::Path, to: &std::path::Path, ) -> Result<(), deno_core::error::AnyError> { - use deno_runtime::deno_fs::FsSysTraitsAdapter; + use crate::sys::CliSys; - if let Err(e) = deno_npm_cache::hard_link_dir_recursive( - &FsSysTraitsAdapter::new_real(), - from, - to, - ) { + if let Err(e) = + deno_npm_cache::hard_link_dir_recursive(&CliSys::default(), from, to) + { log::debug!("Failed to hard link dir {:?} to {:?}: {}", from, to, e); super::copy_dir_recursive(from, to)?; } diff --git a/cli/worker.rs b/cli/worker.rs index 7653e72b75..ef519c7278 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -23,8 +23,6 @@ use deno_runtime::deno_fs; use deno_runtime::deno_node::NodeExtInitServices; use deno_runtime::deno_node::NodeRequireLoader; use deno_runtime::deno_node::NodeRequireLoaderRc; -use deno_runtime::deno_node::NodeResolver; -use deno_runtime::deno_node::PackageJsonResolver; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_web::BlobStore; @@ -53,7 +51,10 @@ use crate::args::DenoSubcommand; use crate::args::NpmCachingStrategy; use crate::args::StorageKeyResolver; use crate::errors; +use crate::node::CliNodeResolver; +use crate::node::CliPackageJsonResolver; use crate::npm::CliNpmResolver; +use crate::sys::CliSys; use crate::util::checksum; use crate::util::file_watcher::WatcherCommunicator; use crate::util::file_watcher::WatcherRestartMode; @@ -145,13 +146,14 @@ struct SharedWorkerState { maybe_inspector_server: Option>, maybe_lockfile: Option>, module_loader_factory: Box, - node_resolver: Arc, + node_resolver: Arc, npm_resolver: Arc, - pkg_json_resolver: Arc, + pkg_json_resolver: Arc, root_cert_store_provider: Arc, root_permissions: PermissionsContainer, shared_array_buffer_store: SharedArrayBufferStore, storage_key_resolver: StorageKeyResolver, + sys: CliSys, options: CliMainWorkerOptions, subcommand: DenoSubcommand, otel_config: OtelConfig, @@ -162,12 +164,13 @@ impl SharedWorkerState { pub fn create_node_init_services( &self, node_require_loader: NodeRequireLoaderRc, - ) -> NodeExtInitServices { + ) -> NodeExtInitServices { NodeExtInitServices { node_require_loader, node_resolver: self.node_resolver.clone(), npm_resolver: self.npm_resolver.clone().into_npm_pkg_folder_resolver(), pkg_json_resolver: self.pkg_json_resolver.clone(), + sys: self.sys.clone(), } } @@ -418,12 +421,13 @@ impl CliMainWorkerFactory { maybe_inspector_server: Option>, maybe_lockfile: Option>, module_loader_factory: Box, - node_resolver: Arc, + node_resolver: Arc, npm_resolver: Arc, - pkg_json_resolver: Arc, + pkg_json_resolver: Arc, root_cert_store_provider: Arc, root_permissions: PermissionsContainer, storage_key_resolver: StorageKeyResolver, + sys: CliSys, subcommand: DenoSubcommand, options: CliMainWorkerOptions, otel_config: OtelConfig, @@ -448,6 +452,7 @@ impl CliMainWorkerFactory { root_permissions, shared_array_buffer_store: Default::default(), storage_key_resolver, + sys, options, subcommand, otel_config, @@ -869,14 +874,15 @@ mod tests { let main_module = resolve_path("./hello.js", &std::env::current_dir().unwrap()).unwrap(); let fs = Arc::new(RealFs); - let permission_desc_parser = - Arc::new(RuntimePermissionDescriptorParser::new(fs.clone())); + let permission_desc_parser = Arc::new( + RuntimePermissionDescriptorParser::new(crate::sys::CliSys::default()), + ); let options = WorkerOptions { startup_snapshot: crate::js::deno_isolate_init(), ..Default::default() }; - MainWorker::bootstrap_from_options( + MainWorker::bootstrap_from_options::( main_module, WorkerServiceOptions { module_loader: Rc::new(FsModuleLoader), diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index 8692f04a73..1d0b623718 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -25,12 +25,10 @@ deno_io.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true filetime.workspace = true -getrandom = "0.2" libc.workspace = true rand.workspace = true rayon = "1.8.0" serde.workspace = true -sys_traits.workspace = true thiserror.workspace = true [target.'cfg(unix)'.dependencies] diff --git a/ext/fs/in_memory_fs.rs b/ext/fs/in_memory_fs.rs deleted file mode 100644 index b79b0ae984..0000000000 --- a/ext/fs/in_memory_fs.rs +++ /dev/null @@ -1,481 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// Allow using Arc for this module. -#![allow(clippy::disallowed_types)] - -use std::borrow::Cow; -use std::collections::hash_map::Entry; -use std::collections::HashMap; -use std::io::Error; -use std::io::ErrorKind; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; - -use deno_core::parking_lot::Mutex; -use deno_io::fs::File; -use deno_io::fs::FsError; -use deno_io::fs::FsResult; -use deno_io::fs::FsStat; -use deno_path_util::normalize_path; - -use crate::interface::AccessCheckCb; -use crate::interface::FsDirEntry; -use crate::interface::FsFileType; -use crate::FileSystem; -use crate::OpenOptions; - -#[derive(Debug)] -enum PathEntry { - Dir, - File(Vec), -} - -/// A very basic in-memory file system useful for swapping out in -/// the place of a RealFs for testing purposes. -/// -/// Please develop this out as you need functionality. -#[derive(Debug, Default)] -pub struct InMemoryFs { - entries: Mutex>>, -} - -impl InMemoryFs { - pub fn setup_text_files(&self, files: Vec<(String, String)>) { - for (path, text) in files { - let path = PathBuf::from(path); - self.mkdir_sync(path.parent().unwrap(), true, None).unwrap(); - self - .write_file_sync( - &path, - OpenOptions::write(true, false, false, None), - None, - &text.into_bytes(), - ) - .unwrap(); - } - } - - fn get_entry(&self, path: &Path) -> Option> { - let path = normalize_path(path); - self.entries.lock().get(&path).cloned() - } -} - -#[async_trait::async_trait(?Send)] -impl FileSystem for InMemoryFs { - fn cwd(&self) -> FsResult { - Err(FsError::NotSupported) - } - - fn tmp_dir(&self) -> FsResult { - Err(FsError::NotSupported) - } - - fn chdir(&self, _path: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - - fn umask(&self, _mask: Option) -> FsResult { - Err(FsError::NotSupported) - } - - fn open_sync( - &self, - _path: &Path, - _options: OpenOptions, - _access_check: Option, - ) -> FsResult> { - Err(FsError::NotSupported) - } - async fn open_async<'a>( - &'a self, - path: PathBuf, - options: OpenOptions, - access_check: Option>, - ) -> FsResult> { - self.open_sync(&path, options, access_check) - } - - fn mkdir_sync( - &self, - path: &Path, - recursive: bool, - _mode: Option, - ) -> FsResult<()> { - let path = normalize_path(path); - - if let Some(parent) = path.parent() { - let entry = self.entries.lock().get(parent).cloned(); - match entry { - Some(entry) => match &*entry { - PathEntry::File(_) => { - return Err(FsError::Io(Error::new( - ErrorKind::InvalidInput, - "Parent is a file", - ))) - } - PathEntry::Dir => {} - }, - None => { - if recursive { - self.mkdir_sync(parent, true, None)?; - } else { - return Err(FsError::Io(Error::new( - ErrorKind::NotFound, - "Not found", - ))); - } - } - } - } - - let entry = self.entries.lock().get(&path).cloned(); - match entry { - Some(entry) => match &*entry { - PathEntry::File(_) => Err(FsError::Io(Error::new( - ErrorKind::InvalidInput, - "Is a file", - ))), - PathEntry::Dir => Ok(()), - }, - None => { - self.entries.lock().insert(path, Arc::new(PathEntry::Dir)); - Ok(()) - } - } - } - async fn mkdir_async( - &self, - path: PathBuf, - recursive: bool, - mode: Option, - ) -> FsResult<()> { - self.mkdir_sync(&path, recursive, mode) - } - - fn chmod_sync(&self, _path: &Path, _mode: u32) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn chmod_async(&self, path: PathBuf, mode: u32) -> FsResult<()> { - self.chmod_sync(&path, mode) - } - - fn chown_sync( - &self, - _path: &Path, - _uid: Option, - _gid: Option, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn chown_async( - &self, - path: PathBuf, - uid: Option, - gid: Option, - ) -> FsResult<()> { - self.chown_sync(&path, uid, gid) - } - - fn lchown_sync( - &self, - _path: &Path, - _uid: Option, - _gid: Option, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - - async fn lchown_async( - &self, - path: PathBuf, - uid: Option, - gid: Option, - ) -> FsResult<()> { - self.lchown_sync(&path, uid, gid) - } - - fn remove_sync(&self, _path: &Path, _recursive: bool) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn remove_async(&self, path: PathBuf, recursive: bool) -> FsResult<()> { - self.remove_sync(&path, recursive) - } - - fn copy_file_sync(&self, _from: &Path, _to: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn copy_file_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> { - self.copy_file_sync(&from, &to) - } - - fn cp_sync(&self, _from: &Path, _to: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn cp_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> { - self.cp_sync(&from, &to) - } - - fn stat_sync(&self, path: &Path) -> FsResult { - let entry = self.get_entry(path); - match entry { - Some(entry) => match &*entry { - PathEntry::Dir => Ok(FsStat { - is_file: false, - is_directory: true, - is_symlink: false, - size: 0, - mtime: None, - atime: None, - birthtime: None, - ctime: None, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blksize: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }), - PathEntry::File(data) => Ok(FsStat { - is_file: true, - is_directory: false, - is_symlink: false, - size: data.len() as u64, - mtime: None, - atime: None, - birthtime: None, - ctime: None, - dev: 0, - ino: 0, - mode: 0, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blksize: 0, - blocks: 0, - is_block_device: false, - is_char_device: false, - is_fifo: false, - is_socket: false, - }), - }, - None => Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))), - } - } - async fn stat_async(&self, path: PathBuf) -> FsResult { - self.stat_sync(&path) - } - - fn lstat_sync(&self, _path: &Path) -> FsResult { - Err(FsError::NotSupported) - } - async fn lstat_async(&self, path: PathBuf) -> FsResult { - self.lstat_sync(&path) - } - - fn realpath_sync(&self, _path: &Path) -> FsResult { - Err(FsError::NotSupported) - } - async fn realpath_async(&self, path: PathBuf) -> FsResult { - self.realpath_sync(&path) - } - - fn read_dir_sync(&self, _path: &Path) -> FsResult> { - Err(FsError::NotSupported) - } - async fn read_dir_async(&self, path: PathBuf) -> FsResult> { - self.read_dir_sync(&path) - } - - fn rename_sync(&self, _oldpath: &Path, _newpath: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn rename_async( - &self, - oldpath: PathBuf, - newpath: PathBuf, - ) -> FsResult<()> { - self.rename_sync(&oldpath, &newpath) - } - - fn link_sync(&self, _oldpath: &Path, _newpath: &Path) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn link_async( - &self, - oldpath: PathBuf, - newpath: PathBuf, - ) -> FsResult<()> { - self.link_sync(&oldpath, &newpath) - } - - fn symlink_sync( - &self, - _oldpath: &Path, - _newpath: &Path, - _file_type: Option, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn symlink_async( - &self, - oldpath: PathBuf, - newpath: PathBuf, - file_type: Option, - ) -> FsResult<()> { - self.symlink_sync(&oldpath, &newpath, file_type) - } - - fn read_link_sync(&self, _path: &Path) -> FsResult { - Err(FsError::NotSupported) - } - async fn read_link_async(&self, path: PathBuf) -> FsResult { - self.read_link_sync(&path) - } - - fn truncate_sync(&self, _path: &Path, _len: u64) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn truncate_async(&self, path: PathBuf, len: u64) -> FsResult<()> { - self.truncate_sync(&path, len) - } - - fn utime_sync( - &self, - _path: &Path, - _atime_secs: i64, - _atime_nanos: u32, - _mtime_secs: i64, - _mtime_nanos: u32, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn utime_async( - &self, - path: PathBuf, - atime_secs: i64, - atime_nanos: u32, - mtime_secs: i64, - mtime_nanos: u32, - ) -> FsResult<()> { - self.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) - } - - fn lutime_sync( - &self, - _path: &Path, - _atime_secs: i64, - _atime_nanos: u32, - _mtime_secs: i64, - _mtime_nanos: u32, - ) -> FsResult<()> { - Err(FsError::NotSupported) - } - async fn lutime_async( - &self, - path: PathBuf, - atime_secs: i64, - atime_nanos: u32, - mtime_secs: i64, - mtime_nanos: u32, - ) -> FsResult<()> { - self.lutime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) - } - - fn write_file_sync( - &self, - path: &Path, - options: OpenOptions, - _access_check: Option, - data: &[u8], - ) -> FsResult<()> { - let path = normalize_path(path); - let has_parent_dir = path - .parent() - .and_then(|parent| self.get_entry(parent)) - .map(|e| matches!(*e, PathEntry::Dir)) - .unwrap_or(false); - if !has_parent_dir { - return Err(FsError::Io(Error::new( - ErrorKind::NotFound, - "Parent directory does not exist", - ))); - } - let mut entries = self.entries.lock(); - let entry = entries.entry(path.clone()); - match entry { - Entry::Occupied(mut entry) => { - if let PathEntry::File(existing_data) = &**entry.get() { - if options.create_new { - return Err(FsError::Io(Error::new( - ErrorKind::AlreadyExists, - "File already exists", - ))); - } - if options.append { - let mut new_data = existing_data.clone(); - new_data.extend_from_slice(data); - entry.insert(Arc::new(PathEntry::File(new_data))); - } else { - entry.insert(Arc::new(PathEntry::File(data.to_vec()))); - } - Ok(()) - } else { - Err(FsError::Io(Error::new( - ErrorKind::InvalidInput, - "Not a file", - ))) - } - } - Entry::Vacant(entry) => { - entry.insert(Arc::new(PathEntry::File(data.to_vec()))); - Ok(()) - } - } - } - - async fn write_file_async<'a>( - &'a self, - path: PathBuf, - options: OpenOptions, - access_check: Option>, - data: Vec, - ) -> FsResult<()> { - self.write_file_sync(&path, options, access_check, &data) - } - - fn read_file_sync( - &self, - path: &Path, - _access_check: Option, - ) -> FsResult> { - let entry = self.get_entry(path); - match entry { - Some(entry) => match &*entry { - PathEntry::File(data) => Ok(Cow::Owned(data.clone())), - PathEntry::Dir => Err(FsError::Io(Error::new( - ErrorKind::InvalidInput, - "Is a directory", - ))), - }, - None => Err(FsError::Io(Error::new(ErrorKind::NotFound, "Not found"))), - } - } - async fn read_file_async<'a>( - &'a self, - path: PathBuf, - access_check: Option>, - ) -> FsResult> { - self.read_file_sync(&path, access_check) - } -} diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs index 304c263614..0e753d684c 100644 --- a/ext/fs/interface.rs +++ b/ext/fs/interface.rs @@ -5,8 +5,6 @@ use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; -use std::time::Duration; -use std::time::SystemTime; use serde::Deserialize; use serde::Serialize; @@ -14,8 +12,6 @@ use serde::Serialize; use deno_io::fs::File; use deno_io::fs::FsResult; use deno_io::fs::FsStat; -use sys_traits::FsFile; -use sys_traits::FsFileSetPermissions; use crate::sync::MaybeSend; use crate::sync::MaybeSync; @@ -75,7 +71,7 @@ pub enum FsFileType { } /// WARNING: This is part of the public JS Deno API. -#[derive(Debug, Serialize)] +#[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct FsDirEntry { pub name: String, @@ -104,56 +100,6 @@ impl AccessCheckFn for T where { } -#[derive(Debug)] -pub struct FsStatSlim { - file_type: sys_traits::FileType, - modified: Result, -} - -impl FsStatSlim { - pub fn from_std(metadata: &std::fs::Metadata) -> Self { - Self { - file_type: metadata.file_type().into(), - modified: metadata.modified(), - } - } - - pub fn from_deno_fs_stat(data: &FsStat) -> Self { - FsStatSlim { - file_type: if data.is_file { - sys_traits::FileType::File - } else if data.is_directory { - sys_traits::FileType::Dir - } else if data.is_symlink { - sys_traits::FileType::Symlink - } else { - sys_traits::FileType::Unknown - }, - modified: data - .mtime - .map(|ms| SystemTime::UNIX_EPOCH + Duration::from_millis(ms)) - .ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::InvalidData, "No mtime") - }), - } - } -} - -impl sys_traits::FsMetadataValue for FsStatSlim { - #[inline] - fn file_type(&self) -> sys_traits::FileType { - self.file_type - } - - fn modified(&self) -> Result { - self - .modified - .as_ref() - .copied() - .map_err(|err| std::io::Error::new(err.kind(), err.to_string())) - } -} - pub type AccessCheckCb<'a> = &'a mut (dyn AccessCheckFn + 'a); #[async_trait::async_trait(?Send)] @@ -415,289 +361,3 @@ fn string_from_utf8_lossy(buf: Vec) -> String { Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(buf) }, } } - -// todo(dsherret): this is temporary. Instead of using the `FileSystem` trait implementation -// in the CLI, the CLI should instead create it's own file system using `sys_traits` traits -// then that can implement the `FileSystem` trait. Then this `FileSystem` trait can stay here -// for use only for `ext/fs` and not the entire CLI. -#[derive(Debug, Clone)] -pub struct FsSysTraitsAdapter(pub FileSystemRc); - -impl FsSysTraitsAdapter { - pub fn new_real() -> Self { - Self(crate::sync::new_rc(crate::RealFs)) - } -} - -impl sys_traits::BaseFsHardLink for FsSysTraitsAdapter { - #[inline] - fn base_fs_hard_link(&self, src: &Path, dst: &Path) -> std::io::Result<()> { - self - .0 - .link_sync(src, dst) - .map_err(|err| err.into_io_error()) - } -} - -impl sys_traits::BaseFsRead for FsSysTraitsAdapter { - #[inline] - fn base_fs_read(&self, path: &Path) -> std::io::Result> { - self - .0 - .read_file_sync(path, None) - .map_err(|err| err.into_io_error()) - } -} - -#[derive(Debug)] -pub struct FsSysTraitsAdapterReadDirEntry { - path: PathBuf, - entry: FsDirEntry, -} - -impl sys_traits::FsDirEntry for FsSysTraitsAdapterReadDirEntry { - type Metadata = FsStatSlim; - - fn file_name(&self) -> Cow { - Cow::Borrowed(self.entry.name.as_ref()) - } - - fn file_type(&self) -> std::io::Result { - if self.entry.is_file { - Ok(sys_traits::FileType::File) - } else if self.entry.is_directory { - Ok(sys_traits::FileType::Dir) - } else if self.entry.is_symlink { - Ok(sys_traits::FileType::Symlink) - } else { - Ok(sys_traits::FileType::Unknown) - } - } - - fn metadata(&self) -> std::io::Result { - Ok(FsStatSlim { - file_type: self.file_type().unwrap(), - modified: Err(std::io::Error::new( - std::io::ErrorKind::Other, - "not supported", - )), - }) - } - - fn path(&self) -> Cow { - Cow::Borrowed(&self.path) - } -} - -impl sys_traits::BaseFsReadDir for FsSysTraitsAdapter { - type ReadDirEntry = FsSysTraitsAdapterReadDirEntry; - - fn base_fs_read_dir( - &self, - path: &Path, - ) -> std::io::Result< - Box>>, - > { - // todo(dsherret): needs to actually be iterable and not allocate a vector - let entries = self - .0 - .read_dir_sync(path) - .map_err(|err| err.into_io_error())?; - let parent_dir = path.to_path_buf(); - Ok(Box::new(entries.into_iter().map(move |entry| { - Ok(FsSysTraitsAdapterReadDirEntry { - path: parent_dir.join(&entry.name), - entry, - }) - }))) - } -} - -impl sys_traits::BaseFsCanonicalize for FsSysTraitsAdapter { - #[inline] - fn base_fs_canonicalize(&self, path: &Path) -> std::io::Result { - self - .0 - .realpath_sync(path) - .map_err(|err| err.into_io_error()) - } -} - -impl sys_traits::BaseFsMetadata for FsSysTraitsAdapter { - type Metadata = FsStatSlim; - - #[inline] - fn base_fs_metadata(&self, path: &Path) -> std::io::Result { - self - .0 - .stat_sync(path) - .map(|data| FsStatSlim::from_deno_fs_stat(&data)) - .map_err(|err| err.into_io_error()) - } - - #[inline] - fn base_fs_symlink_metadata( - &self, - path: &Path, - ) -> std::io::Result { - self - .0 - .lstat_sync(path) - .map(|data| FsStatSlim::from_deno_fs_stat(&data)) - .map_err(|err| err.into_io_error()) - } -} - -impl sys_traits::BaseFsCreateDir for FsSysTraitsAdapter { - #[inline] - fn base_fs_create_dir( - &self, - path: &Path, - options: &sys_traits::CreateDirOptions, - ) -> std::io::Result<()> { - self - .0 - .mkdir_sync(path, options.recursive, options.mode) - .map_err(|err| err.into_io_error()) - } -} - -impl sys_traits::BaseFsRemoveFile for FsSysTraitsAdapter { - #[inline] - fn base_fs_remove_file(&self, path: &Path) -> std::io::Result<()> { - self - .0 - .remove_sync(path, false) - .map_err(|err| err.into_io_error()) - } -} - -impl sys_traits::BaseFsRename for FsSysTraitsAdapter { - #[inline] - fn base_fs_rename(&self, from: &Path, to: &Path) -> std::io::Result<()> { - self - .0 - .rename_sync(from, to) - .map_err(|err| err.into_io_error()) - } -} - -pub struct FsFileAdapter(pub Rc); - -impl FsFile for FsFileAdapter {} - -impl FsFileSetPermissions for FsFileAdapter { - #[inline] - fn fs_file_set_permissions(&mut self, mode: u32) -> std::io::Result<()> { - if cfg!(windows) { - Ok(()) // ignore - } else { - self - .0 - .clone() - .chmod_sync(mode) - .map_err(|err| err.into_io_error()) - } - } -} - -impl std::io::Read for FsFileAdapter { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - self - .0 - .clone() - .read_sync(buf) - .map_err(|err| err.into_io_error()) - } -} - -impl std::io::Seek for FsFileAdapter { - fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { - self - .0 - .clone() - .seek_sync(pos) - .map_err(|err| err.into_io_error()) - } -} - -impl std::io::Write for FsFileAdapter { - #[inline] - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self - .0 - .clone() - .write_sync(buf) - .map_err(|err| err.into_io_error()) - } - - #[inline] - fn flush(&mut self) -> std::io::Result<()> { - self - .0 - .clone() - .sync_sync() - .map_err(|err| err.into_io_error()) - } -} - -impl sys_traits::BaseFsOpen for FsSysTraitsAdapter { - type File = FsFileAdapter; - - fn base_fs_open( - &self, - path: &Path, - options: &sys_traits::OpenOptions, - ) -> std::io::Result { - self - .0 - .open_sync( - path, - OpenOptions { - read: options.read, - write: options.write, - create: options.create, - truncate: options.truncate, - append: options.append, - create_new: options.create_new, - mode: options.mode, - }, - None, - ) - .map(FsFileAdapter) - .map_err(|err| err.into_io_error()) - } -} - -impl sys_traits::SystemRandom for FsSysTraitsAdapter { - #[inline] - fn sys_random(&self, buf: &mut [u8]) -> std::io::Result<()> { - getrandom::getrandom(buf).map_err(|err| { - std::io::Error::new(std::io::ErrorKind::Other, err.to_string()) - }) - } -} - -impl sys_traits::SystemTimeNow for FsSysTraitsAdapter { - #[inline] - fn sys_time_now(&self) -> SystemTime { - SystemTime::now() - } -} - -impl sys_traits::ThreadSleep for FsSysTraitsAdapter { - #[inline] - fn thread_sleep(&self, dur: Duration) { - std::thread::sleep(dur); - } -} - -impl sys_traits::BaseEnvVar for FsSysTraitsAdapter { - fn base_env_var_os( - &self, - key: &std::ffi::OsStr, - ) -> Option { - std::env::var_os(key) - } -} diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index cfcf249783..360400df0d 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -1,25 +1,20 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -mod in_memory_fs; mod interface; mod ops; mod std_fs; pub mod sync; -pub use crate::in_memory_fs::InMemoryFs; pub use crate::interface::AccessCheckCb; pub use crate::interface::AccessCheckFn; pub use crate::interface::FileSystem; pub use crate::interface::FileSystemRc; pub use crate::interface::FsDirEntry; pub use crate::interface::FsFileType; -pub use crate::interface::FsStatSlim; -pub use crate::interface::FsSysTraitsAdapter; pub use crate::interface::OpenOptions; pub use crate::ops::FsOpsError; pub use crate::ops::FsOpsErrorKind; pub use crate::ops::OperationError; -pub use crate::ops::V8MaybeStaticStr; pub use crate::std_fs::RealFs; pub use crate::sync::MaybeSend; pub use crate::sync::MaybeSync; diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index 521ff65471..ac0a8901d7 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -19,7 +19,6 @@ use crate::FsPermissions; use crate::OpenOptions; use boxed_error::Boxed; use deno_core::op2; -use deno_core::v8; use deno_core::CancelFuture; use deno_core::CancelHandle; use deno_core::FastString; @@ -27,7 +26,6 @@ use deno_core::JsBuffer; use deno_core::OpState; use deno_core::ResourceId; use deno_core::ToJsBuffer; -use deno_core::ToV8; use deno_io::fs::FileResource; use deno_io::fs::FsError; use deno_io::fs::FsStat; @@ -1384,51 +1382,12 @@ where Ok(buf.into_owned().into_boxed_slice().into()) } -// todo(https://github.com/denoland/deno_core/pull/986): remove -// when upgrading deno_core -#[derive(Debug)] -pub struct FastStringV8AllocationError; - -impl std::error::Error for FastStringV8AllocationError {} - -impl std::fmt::Display for FastStringV8AllocationError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "failed to allocate string; buffer exceeds maximum length" - ) - } -} - -/// Maintains a static reference to the string if possible. -pub struct V8MaybeStaticStr(pub Cow<'static, str>); - -impl<'s> ToV8<'s> for V8MaybeStaticStr { - type Error = FastStringV8AllocationError; - - #[inline] - fn to_v8( - self, - scope: &mut v8::HandleScope<'s>, - ) -> Result, Self::Error> { - Ok( - match self.0 { - Cow::Borrowed(text) => FastString::from_static(text), - Cow::Owned(value) => value.into(), - } - .v8_string(scope) - .map_err(|_| FastStringV8AllocationError)? - .into(), - ) - } -} - #[op2(stack_trace)] #[to_v8] pub fn op_fs_read_file_text_sync

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

( state: Rc>, #[string] path: String, #[smi] cancel_rid: Option, -) -> Result +) -> Result where P: FsPermissions + 'static, { @@ -1483,7 +1445,10 @@ where .map_err(|error| map_permission_error("readfile", error, &path))? }; - Ok(V8MaybeStaticStr(str)) + Ok(match str { + Cow::Borrowed(text) => FastString::from_static(text), + Cow::Owned(value) => value.into(), + }) } fn to_seek_from(offset: i64, whence: i32) -> Result { diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index 76d37e430c..a1549196dd 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -26,7 +26,7 @@ use crate::interface::FsFileType; use crate::FileSystem; use crate::OpenOptions; -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone)] pub struct RealFs; #[async_trait::async_trait(?Send)] diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 50e72dfcbe..82478e7f7c 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -14,7 +14,7 @@ description = "Node compatibility for Deno" path = "lib.rs" [features] -sync_fs = ["deno_package_json/sync", "node_resolver/sync"] +sync_fs = ["deno_fs/sync_fs", "deno_package_json/sync", "node_resolver/sync"] [dependencies] aead-gcm-stream = "0.4" @@ -93,6 +93,7 @@ simd-json = "0.14.0" sm3 = "0.4.2" spki.workspace = true stable_deref_trait = "1.2.0" +sys_traits = { workspace = true, features = ["real"] } thiserror.workspace = true tokio.workspace = true tokio-eld = "0.2" diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 0c4791eb72..740cc3c136 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -14,10 +14,10 @@ use deno_core::url::Url; #[allow(unused_imports)] use deno_core::v8; use deno_core::v8::ExternalReference; -use deno_fs::FsSysTraitsAdapter; use node_resolver::errors::ClosestPkgJsonError; use node_resolver::IsBuiltInNodeModuleChecker; use node_resolver::NpmPackageFolderResolverRc; +use node_resolver::PackageJsonResolverRc; use once_cell::sync::Lazy; extern crate libz_sys as zlib; @@ -185,16 +185,17 @@ fn op_node_build_os() -> String { } #[derive(Clone)] -pub struct NodeExtInitServices { +pub struct NodeExtInitServices { pub node_require_loader: NodeRequireLoaderRc, - pub node_resolver: NodeResolverRc, + pub node_resolver: NodeResolverRc, pub npm_resolver: NpmPackageFolderResolverRc, - pub pkg_json_resolver: PackageJsonResolverRc, + pub pkg_json_resolver: PackageJsonResolverRc, + pub sys: TSys, } deno_core::extension!(deno_node, deps = [ deno_io, deno_fs ], - parameters = [P: NodePermissions], + parameters = [P: NodePermissions, TSys: ExtNodeSys], ops = [ ops::blocklist::op_socket_address_parse, ops::blocklist::op_socket_address_get_serialization, @@ -392,29 +393,29 @@ deno_core::extension!(deno_node, op_node_build_os, ops::require::op_require_can_parse_as_esm, ops::require::op_require_init_paths, - ops::require::op_require_node_module_paths

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

, - ops::require::op_require_try_self

, - ops::require::op_require_real_path

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

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

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

, - ops::require::op_require_read_package_scope

, - ops::require::op_require_package_imports_resolve

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

, + ops::worker_threads::op_worker_threads_filename, ops::ipc::op_node_child_ipc_pipe, ops::ipc::op_node_ipc_write, ops::ipc::op_node_ipc_read, @@ -680,13 +681,14 @@ deno_core::extension!(deno_node, "node:zlib" = "zlib.ts", ], options = { - maybe_init: Option, + maybe_init: Option>, fs: deno_fs::FileSystemRc, }, state = |state, options| { state.put(options.fs.clone()); if let Some(init) = &options.maybe_init { + state.put(init.sys.clone()); state.put(init.node_require_loader.clone()); state.put(init.node_resolver.clone()); state.put(init.npm_resolver.clone()); @@ -820,18 +822,22 @@ impl IsBuiltInNodeModuleChecker for RealIsBuiltInNodeModuleChecker { } } -pub type NodeResolver = node_resolver::NodeResolver< - RealIsBuiltInNodeModuleChecker, - FsSysTraitsAdapter, ->; +pub trait ExtNodeSys: + sys_traits::BaseFsCanonicalize + + sys_traits::BaseFsMetadata + + sys_traits::BaseFsRead + + sys_traits::EnvCurrentDir + + Clone +{ +} + +impl ExtNodeSys for sys_traits::impls::RealSys {} + +pub type NodeResolver = + node_resolver::NodeResolver; #[allow(clippy::disallowed_types)] -pub type NodeResolverRc = deno_fs::sync::MaybeArc; -pub type PackageJsonResolver = - node_resolver::PackageJsonResolver; +pub type NodeResolverRc = deno_fs::sync::MaybeArc>; #[allow(clippy::disallowed_types)] -pub type PackageJsonResolverRc = deno_fs::sync::MaybeArc< - node_resolver::PackageJsonResolver, ->; pub fn create_host_defined_options<'s>( scope: &mut v8::HandleScope<'s>, diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs index ddcdec0bbd..c5e3afa87d 100644 --- a/ext/node/ops/require.rs +++ b/ext/node/ops/require.rs @@ -1,14 +1,19 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; + use boxed_error::Boxed; use deno_core::error::AnyError; use deno_core::op2; use deno_core::url::Url; use deno_core::v8; +use deno_core::FastString; use deno_core::JsRuntimeInspector; use deno_core::OpState; -use deno_fs::FileSystemRc; -use deno_fs::V8MaybeStaticStr; use deno_package_json::PackageJsonRc; use deno_path_util::normalize_path; use deno_path_util::url_from_file_path; @@ -17,12 +22,11 @@ use node_resolver::errors::ClosestPkgJsonError; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use node_resolver::REQUIRE_CONDITIONS; -use std::borrow::Cow; -use std::cell::RefCell; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; +use sys_traits::FsMetadataValue; +use crate::ExtNodeSys; use crate::NodePermissions; use crate::NodeRequireLoaderRc; use crate::NodeResolverRc; @@ -68,11 +72,11 @@ pub enum RequireErrorKind { #[error(transparent)] UrlConversion(#[from] deno_path_util::PathToUrlError), #[error(transparent)] - Fs(#[from] deno_io::fs::FsError), + Fs(#[from] std::io::Error), #[error(transparent)] ReadModule(deno_core::error::AnyError), #[error("Unable to get CWD: {0}")] - UnableToGetCwd(deno_io::fs::FsError), + UnableToGetCwd(std::io::Error), } #[op2] @@ -128,19 +132,21 @@ pub fn op_require_init_paths() -> Vec { #[op2(stack_trace)] #[serde] -pub fn op_require_node_module_paths

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

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

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

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

(state, &path) .map_err(RequireErrorKind::Permission)?; - let fs = state.borrow::(); + let sys = state.borrow::(); let canonicalized_path = - deno_path_util::strip_unc_prefix(fs.realpath_sync(&path)?); + deno_path_util::strip_unc_prefix(sys.fs_canonicalize(&path)?); Ok(canonicalized_path.to_string_lossy().into_owned()) } @@ -384,15 +390,15 @@ pub fn op_require_path_basename( #[op2(stack_trace)] #[string] -pub fn op_require_try_self_parent_path

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

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

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

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

( +pub fn op_require_resolve_exports< + P: NodePermissions + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, uses_local_node_modules_dir: bool, #[string] modules_path_str: String, @@ -521,13 +533,10 @@ pub fn op_require_resolve_exports

( #[string] name: String, #[string] expansion: String, #[string] parent_path: String, -) -> Result, RequireError> -where - P: NodePermissions + 'static, -{ - let fs = state.borrow::(); - let node_resolver = state.borrow::(); - let pkg_json_resolver = state.borrow::(); +) -> Result, RequireError> { + let sys = state.borrow::(); + let node_resolver = state.borrow::>(); + let pkg_json_resolver = state.borrow::>(); let modules_path = PathBuf::from(&modules_path_str); let modules_specifier = deno_path_util::url_from_file_path(&modules_path)?; @@ -538,7 +547,7 @@ where } else { let mod_dir = path_resolve([modules_path_str.as_str(), name.as_str()].into_iter()); - if fs.is_dir_sync(&mod_dir) { + if sys.fs_is_dir_no_err(&mod_dir) { mod_dir } else { modules_path @@ -589,14 +598,14 @@ pub fn op_require_is_maybe_cjs( #[op2(stack_trace)] #[serde] -pub fn op_require_read_package_scope

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

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

(state, &referrer_path) .map_err(RequireErrorKind::Permission)?; - let pkg_json_resolver = state.borrow::(); + let pkg_json_resolver = state.borrow::>(); let Some(pkg) = pkg_json_resolver .get_closest_package_json_from_file_path(&referrer_path)? else { @@ -629,7 +638,7 @@ where }; if pkg.imports.is_some() { - let node_resolver = state.borrow::(); + let node_resolver = state.borrow::>(); let referrer_url = Url::from_file_path(&referrer_filename).unwrap(); let url = node_resolver.package_imports_resolve( &request, diff --git a/ext/node/ops/worker_threads.rs b/ext/node/ops/worker_threads.rs index 37a7b477d0..48683be1e7 100644 --- a/ext/node/ops/worker_threads.rs +++ b/ext/node/ops/worker_threads.rs @@ -1,13 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::op2; -use deno_core::url::Url; -use deno_core::OpState; -use deno_fs::FileSystemRc; use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; +use deno_core::op2; +use deno_core::url::Url; +use deno_core::OpState; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; + +use crate::ExtNodeSys; use crate::NodePermissions; use crate::NodeRequireLoaderRc; @@ -41,19 +44,19 @@ pub enum WorkerThreadsFilenameError { #[error("File not found [{0:?}]")] FileNotFound(PathBuf), #[error(transparent)] - Fs(#[from] deno_io::fs::FsError), + Fs(#[from] std::io::Error), } // todo(dsherret): we should remove this and do all this work inside op_create_worker #[op2(stack_trace)] #[string] -pub fn op_worker_threads_filename

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

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

(state, &url_path) .map_err(WorkerThreadsFilenameError::Permission)?; - let fs = state.borrow::(); - if !fs.exists_sync(&url_path) { + let sys = state.borrow::(); + if !sys.fs_exists_no_err(&url_path) { return Err(WorkerThreadsFilenameError::FileNotFound( url_path.to_path_buf(), )); diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 4612e87887..ca21547efc 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -122,6 +122,7 @@ same-file = "1.0.6" serde.workspace = true signal-hook = "0.3.17" signal-hook-registry = "1.4.0" +sys_traits.workspace = true tempfile.workspace = true thiserror.workspace = true tokio.workspace = true diff --git a/runtime/errors.rs b/runtime/errors.rs index 3f8e900851..01588c593b 100644 --- a/runtime/errors.rs +++ b/runtime/errors.rs @@ -1157,7 +1157,7 @@ mod node { WorkerThreadsFilenameError::UrlToPathString => "Error", WorkerThreadsFilenameError::UrlToPath => "Error", WorkerThreadsFilenameError::FileNotFound(_) => "Error", - WorkerThreadsFilenameError::Fs(e) => super::get_fs_error(e), + WorkerThreadsFilenameError::Fs(e) => super::get_io_error_class(e), } } @@ -1173,7 +1173,7 @@ mod node { | UrlConversion(_) | ReadModule(_) | PackageImportsResolve(_) => "Error", - Fs(e) | UnableToGetCwd(e) => super::get_fs_error(e), + Fs(e) | UnableToGetCwd(e) => super::get_io_error_class(e), } } diff --git a/runtime/examples/extension/main.rs b/runtime/examples/extension/main.rs index 1ff16ec83f..a4ac85bf5e 100644 --- a/runtime/examples/extension/main.rs +++ b/runtime/examples/extension/main.rs @@ -37,11 +37,12 @@ async fn main() -> Result<(), AnyError> { let main_module = ModuleSpecifier::from_file_path(js_path).unwrap(); eprintln!("Running {main_module}..."); let fs = Arc::new(RealFs); - let permission_desc_parser = - Arc::new(RuntimePermissionDescriptorParser::new(fs.clone())); + let permission_desc_parser = Arc::new( + RuntimePermissionDescriptorParser::new(sys_traits::impls::RealSys), + ); let mut worker = MainWorker::bootstrap_from_options( main_module.clone(), - WorkerServiceOptions { + WorkerServiceOptions:: { module_loader: Rc::new(FsModuleLoader), permissions: PermissionsContainer::allow_all(permission_desc_parser), blob_store: Default::default(), diff --git a/runtime/permissions.rs b/runtime/permissions.rs index e8460e03f8..968c41560c 100644 --- a/runtime/permissions.rs +++ b/runtime/permissions.rs @@ -21,13 +21,17 @@ use deno_permissions::SysDescriptorParseError; use deno_permissions::WriteDescriptor; #[derive(Debug)] -pub struct RuntimePermissionDescriptorParser { - fs: deno_fs::FileSystemRc, +pub struct RuntimePermissionDescriptorParser< + TSys: sys_traits::EnvCurrentDir + Send + Sync, +> { + sys: TSys, } -impl RuntimePermissionDescriptorParser { - pub fn new(fs: deno_fs::FileSystemRc) -> Self { - Self { fs } +impl + RuntimePermissionDescriptorParser +{ + pub fn new(sys: TSys) -> Self { + Self { sys } } fn resolve_from_cwd(&self, path: &str) -> Result { @@ -45,14 +49,15 @@ impl RuntimePermissionDescriptorParser { fn resolve_cwd(&self) -> Result { self - .fs - .cwd() - .map_err(|e| PathResolveError::CwdResolve(e.into_io_error())) + .sys + .env_current_dir() + .map_err(PathResolveError::CwdResolve) } } -impl deno_permissions::PermissionDescriptorParser - for RuntimePermissionDescriptorParser +impl + deno_permissions::PermissionDescriptorParser + for RuntimePermissionDescriptorParser { fn parse_read_descriptor( &self, @@ -151,16 +156,14 @@ impl deno_permissions::PermissionDescriptorParser #[cfg(test)] mod test { - use std::sync::Arc; - - use deno_fs::RealFs; use deno_permissions::PermissionDescriptorParser; use super::*; #[test] fn test_handle_empty_value() { - let parser = RuntimePermissionDescriptorParser::new(Arc::new(RealFs)); + let parser = + RuntimePermissionDescriptorParser::new(sys_traits::impls::RealSys); assert!(parser.parse_read_descriptor("").is_err()); assert!(parser.parse_write_descriptor("").is_err()); assert!(parser.parse_env_descriptor("").is_err()); diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index 48c500ef74..ad73f485ad 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -306,7 +306,10 @@ pub fn create_runtime_snapshot( ), deno_io::deno_io::init_ops_and_esm(Default::default()), deno_fs::deno_fs::init_ops_and_esm::(fs.clone()), - deno_node::deno_node::init_ops_and_esm::(None, fs.clone()), + deno_node::deno_node::init_ops_and_esm::< + Permissions, + sys_traits::impls::RealSys, + >(None, fs.clone()), runtime::init_ops_and_esm(), ops::runtime::deno_runtime::init_ops("deno:runtime".parse().unwrap()), ops::worker_host::deno_worker_host::init_ops( diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index faf4f3fc52..2b46d9a2ff 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -33,6 +33,7 @@ use deno_fs::FileSystem; use deno_http::DefaultHttpPropertyExtractor; use deno_io::Stdio; use deno_kv::dynamic::MultiBackendDbHandler; +use deno_node::ExtNodeSys; use deno_node::NodeExtInitServices; use deno_permissions::PermissionsContainer; use deno_terminal::colors; @@ -337,7 +338,7 @@ fn create_handles( (internal_handle, external_handle) } -pub struct WebWorkerServiceOptions { +pub struct WebWorkerServiceOptions { pub blob_store: Arc, pub broadcast_channel: InMemoryBroadcastChannel, pub compiled_wasm_module_store: Option, @@ -345,7 +346,7 @@ pub struct WebWorkerServiceOptions { pub fs: Arc, pub maybe_inspector_server: Option>, pub module_loader: Rc, - pub node_services: Option, + pub node_services: Option>, pub npm_process_state_provider: Option, pub permissions: PermissionsContainer, pub root_cert_store_provider: Option>, @@ -402,8 +403,8 @@ impl Drop for WebWorker { } impl WebWorker { - pub fn bootstrap_from_options( - services: WebWorkerServiceOptions, + pub fn bootstrap_from_options( + services: WebWorkerServiceOptions, options: WebWorkerOptions, ) -> (Self, SendableWebWorkerHandle) { let (mut worker, handle, bootstrap_options) = @@ -412,8 +413,8 @@ impl WebWorker { (worker, handle) } - fn from_options( - services: WebWorkerServiceOptions, + fn from_options( + services: WebWorkerServiceOptions, mut options: WebWorkerOptions, ) -> (Self, SendableWebWorkerHandle, BootstrapOptions) { deno_core::extension!(deno_permissions_web_worker, @@ -504,7 +505,7 @@ impl WebWorker { deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(), ), - deno_node::deno_node::init_ops_and_esm::( + deno_node::deno_node::init_ops_and_esm::( services.node_services, services.fs, ), diff --git a/runtime/worker.rs b/runtime/worker.rs index 46fbd7b43f..a9a4440410 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -39,6 +39,7 @@ use deno_fs::FileSystem; use deno_http::DefaultHttpPropertyExtractor; use deno_io::Stdio; use deno_kv::dynamic::MultiBackendDbHandler; +use deno_node::ExtNodeSys; use deno_node::NodeExtInitServices; use deno_permissions::PermissionsContainer; use deno_tls::RootCertStoreProvider; @@ -128,7 +129,7 @@ pub struct MainWorker { dispatch_process_exit_event_fn_global: v8::Global, } -pub struct WorkerServiceOptions { +pub struct WorkerServiceOptions { pub blob_store: Arc, pub broadcast_channel: InMemoryBroadcastChannel, pub feature_checker: Arc, @@ -139,7 +140,7 @@ pub struct WorkerServiceOptions { /// If not provided runtime will error if code being /// executed tries to load modules. pub module_loader: Rc, - pub node_services: Option, + pub node_services: Option>, pub npm_process_state_provider: Option, pub permissions: PermissionsContainer, pub root_cert_store_provider: Option>, @@ -304,9 +305,9 @@ pub fn create_op_metrics( } impl MainWorker { - pub fn bootstrap_from_options( + pub fn bootstrap_from_options( main_module: ModuleSpecifier, - services: WorkerServiceOptions, + services: WorkerServiceOptions, options: WorkerOptions, ) -> Self { let (mut worker, bootstrap_options) = @@ -315,9 +316,9 @@ impl MainWorker { worker } - fn from_options( + fn from_options( main_module: ModuleSpecifier, - services: WorkerServiceOptions, + services: WorkerServiceOptions, mut options: WorkerOptions, ) -> (Self, BootstrapOptions) { deno_core::extension!(deno_permissions_worker, @@ -417,7 +418,7 @@ impl MainWorker { deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(), ), - deno_node::deno_node::init_ops_and_esm::( + deno_node::deno_node::init_ops_and_esm::( services.node_services, services.fs, ), From 88bd5f09f7d4065fc539f1116da1bac6266a5a5b Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 31 Dec 2024 11:29:51 -0500 Subject: [PATCH 049/107] perf(fs/windows): stat - only open file once (#27487) --- ext/fs/std_fs.rs | 64 +++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index a1549196dd..c28fe9f915 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -61,7 +61,7 @@ impl FileSystem for RealFs { umask(Mode::from_bits_truncate(mask as mode_t)) } else { // If no mask provided, we query the current. Requires two syscalls. - let prev = umask(Mode::from_bits_truncate(0o777)); + let prev = umask(Mode::from_bits_truncate(0)); let _ = umask(prev); prev }; @@ -761,11 +761,16 @@ fn stat(path: &Path) -> FsResult { #[cfg(windows)] fn stat(path: &Path) -> FsResult { - let metadata = fs::metadata(path)?; - let mut fsstat = FsStat::from_std(metadata); + use std::os::windows::fs::OpenOptionsExt; use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; - let path = path.canonicalize()?; - stat_extra(&mut fsstat, &path, FILE_FLAG_BACKUP_SEMANTICS)?; + + let mut opts = fs::OpenOptions::new(); + opts.access_mode(0); // no read or write + opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS); + let file = opts.open(path)?; + let metadata = file.metadata()?; + let mut fsstat = FsStat::from_std(metadata); + stat_extra(&file, &mut fsstat)?; Ok(fsstat) } @@ -777,34 +782,24 @@ fn lstat(path: &Path) -> FsResult { #[cfg(windows)] fn lstat(path: &Path) -> FsResult { + use std::os::windows::fs::OpenOptionsExt; + use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; use winapi::um::winbase::FILE_FLAG_OPEN_REPARSE_POINT; - let metadata = fs::symlink_metadata(path)?; + let mut opts = fs::OpenOptions::new(); + opts.access_mode(0); // no read or write + opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); + let file = opts.open(path)?; + let metadata = file.metadata()?; let mut fsstat = FsStat::from_std(metadata); - stat_extra( - &mut fsstat, - path, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, - )?; + stat_extra(&file, &mut fsstat)?; Ok(fsstat) } #[cfg(windows)] -fn stat_extra( - fsstat: &mut FsStat, - path: &Path, - file_flags: winapi::shared::minwindef::DWORD, -) -> FsResult<()> { - use std::os::windows::prelude::OsStrExt; - - use winapi::um::fileapi::CreateFileW; - use winapi::um::fileapi::OPEN_EXISTING; - use winapi::um::handleapi::CloseHandle; - use winapi::um::handleapi::INVALID_HANDLE_VALUE; - use winapi::um::winnt::FILE_SHARE_DELETE; - use winapi::um::winnt::FILE_SHARE_READ; - use winapi::um::winnt::FILE_SHARE_WRITE; +fn stat_extra(file: &std::fs::File, fsstat: &mut FsStat) -> FsResult<()> { + use std::os::windows::io::AsRawHandle; unsafe fn get_dev( handle: winapi::shared::ntdef::HANDLE, @@ -873,23 +868,9 @@ fn stat_extra( // SAFETY: winapi calls unsafe { - let mut path: Vec<_> = path.as_os_str().encode_wide().collect(); - path.push(0); - let file_handle = CreateFileW( - path.as_ptr(), - 0, - FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, - std::ptr::null_mut(), - OPEN_EXISTING, - file_flags, - std::ptr::null_mut(), - ); - if file_handle == INVALID_HANDLE_VALUE { - return Err(std::io::Error::last_os_error().into()); - } + let file_handle = file.as_raw_handle(); - let result = get_dev(file_handle); - fsstat.dev = result?; + fsstat.dev = get_dev(file_handle)?; if let Ok(file_info) = query_file_information(file_handle) { fsstat.ctime = Some(windows_time_to_unix_time_msec( @@ -928,7 +909,6 @@ fn stat_extra( } } - CloseHandle(file_handle); Ok(()) } } From ac7b33a340f47dd3a97c48c9c6a086f1fb858464 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 31 Dec 2024 12:13:39 -0500 Subject: [PATCH 050/107] chore: cargo fmt - turn on group_imports=StdExternalCrate (#26646) --- .dprint.json | 2 +- bench_util/README.md | 1 - cli/args/flags.rs | 8 +- cli/args/flags_net.rs | 5 +- cli/args/lockfile.rs | 8 +- cli/args/mod.rs | 134 +++++++++--------- cli/bench/lsp.rs | 9 +- cli/bench/main.rs | 7 +- cli/build.rs | 12 +- cli/cache/cache_db.rs | 9 +- cli/cache/check.rs | 7 +- cli/cache/code_cache.rs | 3 +- cli/cache/deno_dir.rs | 9 +- cli/cache/disk_cache.rs | 19 +-- cli/cache/emit.rs | 3 +- cli/cache/mod.rs | 24 ++-- cli/cache/node.rs | 3 +- cli/emit.rs | 11 +- cli/factory.rs | 58 ++++---- cli/file_fetcher.rs | 8 +- cli/http_util.rs | 12 +- cli/jsr.rs | 8 +- cli/lsp/analysis.rs | 42 +++--- cli/lsp/cache.rs | 19 +-- cli/lsp/client.rs | 3 +- cli/lsp/code_lens.rs | 24 ++-- cli/lsp/completions.rs | 55 +++---- cli/lsp/config.rs | 17 +-- cli/lsp/diagnostics.rs | 77 +++++----- cli/lsp/documents.rs | 56 ++++---- cli/lsp/jsr.rs | 17 +-- cli/lsp/language_server.rs | 24 ++-- cli/lsp/logging.rs | 7 +- cli/lsp/mod.rs | 7 +- cli/lsp/npm.rs | 6 +- cli/lsp/parent_process_checker.rs | 4 +- cli/lsp/path_to_regex.rs | 9 +- cli/lsp/performance.rs | 9 +- cli/lsp/registries.rs | 51 +++---- cli/lsp/resolver.rs | 13 +- cli/lsp/search.rs | 9 +- cli/lsp/semantic_tokens.rs | 1 + cli/lsp/testing/collectors.rs | 16 +-- cli/lsp/testing/definitions.rs | 16 +-- cli/lsp/testing/execution.rs | 55 +++---- cli/lsp/testing/server.rs | 30 ++-- cli/lsp/text.rs | 3 +- cli/lsp/tsc.rs | 109 +++++++------- cli/lsp/urls.rs | 10 +- cli/main.rs | 50 +++---- cli/mainrt.rs | 12 +- cli/module_loader.rs | 7 +- cli/npm/byonm.rs | 7 +- cli/npm/managed/mod.rs | 22 ++- .../managed/resolvers/common/bin_entries.rs | 12 +- .../resolvers/common/lifecycle_scripts.rs | 30 ++-- cli/npm/managed/resolvers/global.rs | 15 +- cli/npm/managed/resolvers/local.rs | 9 +- cli/npm/managed/resolvers/mod.rs | 13 +- cli/npm/mod.rs | 9 +- cli/ops/jupyter.rs | 11 +- cli/ops/testing.rs | 21 +-- cli/resolver.rs | 2 +- cli/standalone/binary.rs | 35 +++-- cli/standalone/code_cache.rs | 3 +- cli/standalone/mod.rs | 9 +- cli/standalone/serialization.rs | 3 +- cli/standalone/virtual_fs.rs | 6 +- cli/tools/bench/mod.rs | 39 ++--- cli/tools/bench/reporters.rs | 3 +- cli/tools/clean.rs | 3 +- cli/tools/compile.rs | 25 ++-- cli/tools/coverage/merge.rs | 7 +- cli/tools/coverage/mod.rs | 37 ++--- cli/tools/coverage/range_tree.rs | 4 +- cli/tools/coverage/reporter.rs | 14 +- cli/tools/doc.rs | 34 ++--- cli/tools/fmt.rs | 53 +++---- cli/tools/init/mod.rs | 22 +-- cli/tools/installer.rs | 57 ++++---- cli/tools/jupyter/install.rs | 6 +- cli/tools/jupyter/mod.rs | 23 +-- cli/tools/jupyter/server.rs | 11 +- cli/tools/lint/linter.rs | 5 +- cli/tools/lint/mod.rs | 18 +-- cli/tools/lint/reporters.rs | 3 +- cli/tools/lint/rules/no_sloppy_imports.rs | 3 +- cli/tools/registry/api.rs | 2 +- cli/tools/registry/graph.rs | 3 +- cli/tools/registry/mod.rs | 9 +- cli/tools/registry/paths.rs | 5 +- cli/tools/registry/pm/cache_deps.rs | 9 +- cli/tools/registry/pm/deps.rs | 3 +- cli/tools/registry/pm/outdated.rs | 9 +- cli/tools/registry/provenance.rs | 18 +-- cli/tools/registry/tar.rs | 10 +- cli/tools/registry/unfurl.rs | 8 +- cli/tools/repl/channel.rs | 3 +- cli/tools/repl/editor.rs | 15 +- cli/tools/repl/mod.rs | 14 +- cli/tools/repl/session.rs | 32 ++--- cli/tools/test/channel.rs | 27 ++-- cli/tools/test/fmt.rs | 8 +- cli/tools/test/mod.rs | 77 +++++----- cli/tools/upgrade.rs | 41 +++--- cli/tsc/diagnostics.rs | 13 +- cli/tsc/mod.rs | 44 +++--- cli/util/diff.rs | 6 +- cli/util/display.rs | 3 +- cli/util/draw_thread.rs | 7 +- cli/util/extract.rs | 12 +- cli/util/file_watcher.rs | 21 +-- cli/util/fs.rs | 9 +- cli/util/logger.rs | 6 +- cli/util/progress_bar/mod.rs | 4 +- cli/util/progress_bar/renderer.rs | 9 +- cli/util/sync/task_queue.rs | 3 +- cli/util/windows.rs | 1 + cli/worker.rs | 3 +- ext/broadcast_channel/lib.rs | 5 +- ext/canvas/lib.rs | 3 +- ext/console/lib.rs | 3 +- ext/cron/interface.rs | 3 +- ext/cron/lib.rs | 3 +- ext/crypto/lib.rs | 17 +-- ext/fetch/fs_fetch_handler.rs | 9 +- ext/fetch/lib.rs | 15 +- ext/fetch/proxy.rs | 4 +- ext/fetch/tests.rs | 3 +- ext/ffi/call.rs | 24 ++-- ext/ffi/callback.rs | 30 ++-- ext/ffi/dlfcn.rs | 28 ++-- ext/ffi/ir.rs | 8 +- ext/ffi/lib.rs | 15 +- ext/ffi/repr.rs | 10 +- ext/ffi/static.rs | 8 +- ext/fs/interface.rs | 5 +- ext/fs/lib.rs | 16 +-- ext/fs/ops.rs | 13 +- ext/fs/std_fs.rs | 15 +- ext/fs/sync.rs | 3 +- ext/http/compressible.rs | 3 +- ext/http/fly_accept_encoding.rs | 3 +- ext/http/http_next.rs | 64 ++++----- ext/http/lib.rs | 31 ++-- ext/http/network_buffered_stream.rs | 10 +- ext/http/reader_stream.rs | 3 +- ext/http/request_body.rs | 11 +- ext/http/request_properties.rs | 11 +- ext/http/response_body.rs | 6 +- ext/http/service.rs | 45 +++--- ext/http/websocket_upgrade.rs | 3 +- ext/io/bi_pipe.rs | 10 +- ext/io/lib.rs | 59 ++++---- ext/io/pipe.rs | 5 +- ext/io/winpipe.rs | 8 +- ext/kv/dynamic.rs | 15 +- ext/kv/remote.rs | 3 +- ext/kv/sqlite.rs | 3 +- ext/napi/js_native_api.rs | 7 +- ext/napi/lib.rs | 28 ++-- ext/napi/node_api.rs | 20 +-- ext/napi/util.rs | 3 +- ext/napi/uv.rs | 9 +- ext/napi/value.rs | 3 +- ext/net/io.rs | 6 +- ext/net/lib.rs | 12 +- ext/net/ops.rs | 49 ++++--- ext/net/ops_tls.rs | 47 +++--- ext/net/ops_unix.rs | 18 +-- ext/net/quic.rs | 38 ++--- ext/net/raw.rs | 10 +- ext/net/resolve_addr.rs | 4 +- ext/node/ops/blocklist.rs | 1 - ext/node/ops/crypto/cipher.rs | 8 +- ext/node/ops/crypto/dh.rs | 3 +- ext/node/ops/crypto/digest.rs | 5 +- ext/node/ops/crypto/md5_sha1.rs | 1 + ext/node/ops/crypto/mod.rs | 12 +- ext/node/ops/crypto/primes.rs | 6 +- ext/node/ops/crypto/sign.rs | 16 +-- ext/node/ops/crypto/x509.rs | 10 +- ext/node/ops/fs.rs | 1 + ext/node/ops/http.rs | 2 +- ext/node/ops/idna.rs | 4 +- ext/node/ops/inspector.rs | 8 +- ext/node/ops/ipc.rs | 13 +- ext/node/ops/os/cpus.rs | 6 +- ext/node/ops/os/mod.rs | 3 +- ext/node/ops/perf_hooks.rs | 4 +- ext/node/ops/v8.rs | 3 +- ext/node/ops/vm.rs | 10 +- ext/node/ops/vm_internal.rs | 3 +- ext/node/ops/zlib/brotli.rs | 5 +- ext/node/ops/zlib/mod.rs | 5 +- ext/node/ops/zlib/stream.rs | 5 +- ext/telemetry/lib.rs | 38 ++--- ext/tls/lib.rs | 44 +++--- ext/tls/tls_key.rs | 16 ++- ext/url/benches/url_ops.rs | 1 - ext/url/lib.rs | 6 +- ext/url/urlpattern.rs | 1 - ext/web/compression.rs | 5 +- ext/web/lib.rs | 23 ++- ext/web/message_port.rs | 1 - ext/web/stream_resource.rs | 31 ++-- ext/web/timers.rs | 5 +- ext/webgpu/binding.rs | 5 +- ext/webgpu/buffer.rs | 9 +- ext/webgpu/bundle.rs | 7 +- ext/webgpu/byow.rs | 7 +- ext/webgpu/command_encoder.rs | 9 +- ext/webgpu/compute_pass.rs | 5 +- ext/webgpu/error.rs | 5 +- ext/webgpu/lib.rs | 12 +- ext/webgpu/pipeline.rs | 7 +- ext/webgpu/queue.rs | 9 +- ext/webgpu/render_pass.rs | 5 +- ext/webgpu/sampler.rs | 5 +- ext/webgpu/shader.rs | 5 +- ext/webgpu/surface.rs | 8 +- ext/webgpu/texture.rs | 5 +- ext/webidl/benches/dict.rs | 1 - ext/websocket/lib.rs | 35 ++--- ext/websocket/stream.rs | 9 +- ext/webstorage/lib.rs | 3 +- resolvers/deno/npm/mod.rs | 13 +- resolvers/node/analyze.rs | 5 +- resolvers/node/resolution.rs | 2 +- resolvers/node/sync.rs | 3 +- resolvers/npm_cache/fs_util.rs | 5 +- resolvers/npm_cache/lib.rs | 5 +- runtime/errors.rs | 41 +++--- runtime/fmt_errors.rs | 6 +- runtime/fs_util.rs | 5 +- runtime/inspector_server.rs | 15 +- runtime/ops/fs_events.rs | 19 ++- runtime/ops/os/mod.rs | 10 +- runtime/ops/process.rs | 33 ++--- runtime/ops/runtime.rs | 1 + runtime/ops/signal.rs | 17 ++- runtime/ops/tty.rs | 31 ++-- runtime/ops/web_worker.rs | 11 +- runtime/ops/web_worker/sync_fetch.rs | 5 +- runtime/ops/worker_host.rs | 30 ++-- runtime/permissions/lib.rs | 34 ++--- runtime/permissions/prompter.rs | 15 +- runtime/shared.rs | 3 +- runtime/snapshot.rs | 22 +-- runtime/sys_info.rs | 2 + runtime/web_worker.rs | 19 +-- runtime/worker_bootstrap.rs | 8 +- tests/ffi/tests/integration_tests.rs | 3 +- tests/integration/inspector_tests.rs | 8 +- tests/integration/js_unit_tests.rs | 1 + tests/integration/jupyter_tests.rs | 9 +- tests/integration/lsp_tests.rs | 5 +- tests/integration/node_unit_tests.rs | 1 + tests/integration/npm_tests.rs | 1 - tests/integration/run_tests.rs | 10 +- tests/integration/upgrade_tests.rs | 1 + tests/integration/watcher_tests.rs | 3 +- tests/napi/src/array.rs | 10 +- tests/napi/src/arraybuffer.rs | 3 +- tests/napi/src/async.rs | 14 +- tests/napi/src/bigint.rs | 10 +- tests/napi/src/callback.rs | 10 +- tests/napi/src/coerce.rs | 6 +- tests/napi/src/date.rs | 8 +- tests/napi/src/env.rs | 3 +- tests/napi/src/error.rs | 6 +- tests/napi/src/finalizer.rs | 8 +- tests/napi/src/make_callback.rs | 8 +- tests/napi/src/mem.rs | 6 +- tests/napi/src/numbers.rs | 8 +- tests/napi/src/object.rs | 6 +- tests/napi/src/object_wrap.rs | 12 +- tests/napi/src/primitives.rs | 6 +- tests/napi/src/promise.rs | 8 +- tests/napi/src/properties.rs | 8 +- tests/napi/src/strings.rs | 5 +- tests/napi/src/symbol.rs | 5 +- tests/napi/src/tsfn.rs | 3 +- tests/napi/src/typedarray.rs | 12 +- tests/napi/src/uv.rs | 18 +-- tests/napi/tests/napi_tests.rs | 1 + tests/util/server/src/factory.rs | 3 +- tests/util/server/src/fs.rs | 4 +- tests/util/server/src/https.rs | 9 +- tests/util/server/src/lib.rs | 3 +- tests/util/server/src/lsp.rs | 50 +++---- tests/util/server/src/pty.rs | 3 +- tests/util/server/src/servers/hyper_utils.rs | 11 +- tests/util/server/src/servers/jsr_registry.rs | 21 +-- tests/util/server/src/servers/mod.rs | 16 +-- tests/util/server/src/servers/npm_registry.rs | 27 ++-- tests/util/server/src/servers/ws.rs | 5 +- tests/util/server/src/spawn.rs | 4 +- 298 files changed, 2160 insertions(+), 1951 deletions(-) diff --git a/.dprint.json b/.dprint.json index b9c2d1ebc1..bd1279fd4a 100644 --- a/.dprint.json +++ b/.dprint.json @@ -13,7 +13,7 @@ }, "exec": { "commands": [{ - "command": "rustfmt --config imports_granularity=item", + "command": "rustfmt --config imports_granularity=item --config group_imports=StdExternalCrate", "exts": ["rs"] }] }, diff --git a/bench_util/README.md b/bench_util/README.md index 12474a86b6..30616a08fd 100644 --- a/bench_util/README.md +++ b/bench_util/README.md @@ -7,7 +7,6 @@ use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::Bencher; - use deno_core::Extension; #[op2] diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 2b0b9a2908..cd6db5eea2 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -43,11 +43,10 @@ use log::Level; use serde::Deserialize; use serde::Serialize; -use crate::args::resolve_no_prompt; -use crate::util::fs::canonicalize_path; - use super::flags_net; use super::jsr_url; +use crate::args::resolve_no_prompt; +use crate::util::fs::canonicalize_path; #[derive(Clone, Debug, Default, Eq, PartialEq)] pub enum ConfigFlag { @@ -6059,9 +6058,10 @@ pub fn resolve_urls(urls: Vec) -> Vec { #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; + use super::*; + /// Creates vector of strings, Vec macro_rules! svec { ($($x:expr),* $(,)?) => (vec![$($x.to_string().into()),*]); diff --git a/cli/args/flags_net.rs b/cli/args/flags_net.rs index abfcf28382..3d19a06183 100644 --- a/cli/args/flags_net.rs +++ b/cli/args/flags_net.rs @@ -1,9 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::net::IpAddr; +use std::str::FromStr; + use deno_core::url::Url; use deno_runtime::deno_permissions::NetDescriptor; -use std::net::IpAddr; -use std::str::FromStr; #[derive(Debug, PartialEq, Eq)] pub struct ParsePortError(String); diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index 7d5fe57bc3..5d93aa6a4a 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -10,6 +10,7 @@ use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::parking_lot::MutexGuard; use deno_core::serde_json; +use deno_lockfile::Lockfile; use deno_lockfile::WorkspaceMemberConfig; use deno_package_json::PackageJsonDepValue; use deno_path_util::fs::atomic_write_file_with_retries; @@ -17,15 +18,12 @@ use deno_runtime::deno_node::PackageJson; use deno_semver::jsr::JsrDepPackageReq; use crate::args::deno_json::import_map_deps; +use crate::args::DenoSubcommand; +use crate::args::InstallFlags; use crate::cache; use crate::sys::CliSys; use crate::Flags; -use crate::args::DenoSubcommand; -use crate::args::InstallFlags; - -use deno_lockfile::Lockfile; - #[derive(Debug)] pub struct CliLockfileReadFromPathOptions { pub file_path: PathBuf, diff --git a/cli/args/mod.rs b/cli/args/mod.rs index a059b07757..4516cfe06a 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -7,70 +7,6 @@ mod import_map; mod lockfile; mod package_json; -use deno_ast::MediaType; -use deno_ast::SourceMapOption; -use deno_cache_dir::file_fetcher::CacheSetting; -use deno_config::deno_json::NodeModulesDirMode; -use deno_config::workspace::CreateResolverOptions; -use deno_config::workspace::FolderConfigs; -use deno_config::workspace::PackageJsonDepResolution; -use deno_config::workspace::VendorEnablement; -use deno_config::workspace::Workspace; -use deno_config::workspace::WorkspaceDirectory; -use deno_config::workspace::WorkspaceDirectoryEmptyOptions; -use deno_config::workspace::WorkspaceDiscoverOptions; -use deno_config::workspace::WorkspaceDiscoverStart; -use deno_config::workspace::WorkspaceLintConfig; -use deno_config::workspace::WorkspaceResolver; -use deno_core::resolve_url_or_path; -use deno_graph::GraphKind; -use deno_lint::linter::LintConfig as DenoLintConfig; -use deno_npm::npm_rc::NpmRc; -use deno_npm::npm_rc::ResolvedNpmRc; -use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; -use deno_npm::NpmSystemInfo; -use deno_path_util::normalize_path; -use deno_semver::npm::NpmPackageReqReference; -use deno_semver::StackString; -use deno_telemetry::OtelConfig; -use deno_telemetry::OtelRuntimeConfig; -use import_map::resolve_import_map_value_from_specifier; - -pub use deno_config::deno_json::BenchConfig; -pub use deno_config::deno_json::ConfigFile; -pub use deno_config::deno_json::FmtOptionsConfig; -pub use deno_config::deno_json::LintRulesConfig; -pub use deno_config::deno_json::ProseWrap; -pub use deno_config::deno_json::TsConfig; -pub use deno_config::deno_json::TsConfigForEmit; -pub use deno_config::deno_json::TsConfigType; -pub use deno_config::deno_json::TsTypeLib; -pub use deno_config::glob::FilePatterns; -pub use deno_json::check_warn_tsconfig; -pub use flags::*; -pub use lockfile::CliLockfile; -pub use lockfile::CliLockfileReadFromPathOptions; -pub use package_json::NpmInstallDepsProvider; -pub use package_json::PackageJsonDepValueParseWithLocationError; - -use deno_ast::ModuleSpecifier; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::url::Url; -use deno_runtime::deno_permissions::PermissionsOptions; -use deno_runtime::deno_tls::deno_native_certs::load_native_certs; -use deno_runtime::deno_tls::rustls; -use deno_runtime::deno_tls::rustls::RootCertStore; -use deno_runtime::deno_tls::rustls_pemfile; -use deno_runtime::deno_tls::webpki_roots; -use deno_runtime::inspector_server::InspectorServer; -use deno_terminal::colors; -use dotenvy::from_filename; -use once_cell::sync::Lazy; -use serde::Deserialize; -use serde::Serialize; use std::borrow::Cow; use std::collections::HashMap; use std::env; @@ -83,6 +19,72 @@ use std::num::NonZeroUsize; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; + +use deno_ast::MediaType; +use deno_ast::ModuleSpecifier; +use deno_ast::SourceMapOption; +use deno_cache_dir::file_fetcher::CacheSetting; +pub use deno_config::deno_json::BenchConfig; +pub use deno_config::deno_json::ConfigFile; +use deno_config::deno_json::FmtConfig; +pub use deno_config::deno_json::FmtOptionsConfig; +use deno_config::deno_json::LintConfig; +pub use deno_config::deno_json::LintRulesConfig; +use deno_config::deno_json::NodeModulesDirMode; +pub use deno_config::deno_json::ProseWrap; +use deno_config::deno_json::TestConfig; +pub use deno_config::deno_json::TsConfig; +pub use deno_config::deno_json::TsConfigForEmit; +pub use deno_config::deno_json::TsConfigType; +pub use deno_config::deno_json::TsTypeLib; +pub use deno_config::glob::FilePatterns; +use deno_config::workspace::CreateResolverOptions; +use deno_config::workspace::FolderConfigs; +use deno_config::workspace::PackageJsonDepResolution; +use deno_config::workspace::VendorEnablement; +use deno_config::workspace::Workspace; +use deno_config::workspace::WorkspaceDirectory; +use deno_config::workspace::WorkspaceDirectoryEmptyOptions; +use deno_config::workspace::WorkspaceDiscoverOptions; +use deno_config::workspace::WorkspaceDiscoverStart; +use deno_config::workspace::WorkspaceLintConfig; +use deno_config::workspace::WorkspaceResolver; +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::resolve_url_or_path; +use deno_core::serde_json; +use deno_core::url::Url; +use deno_graph::GraphKind; +pub use deno_json::check_warn_tsconfig; +use deno_lint::linter::LintConfig as DenoLintConfig; +use deno_npm::npm_rc::NpmRc; +use deno_npm::npm_rc::ResolvedNpmRc; +use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; +use deno_npm::NpmSystemInfo; +use deno_path_util::normalize_path; +use deno_runtime::deno_permissions::PermissionsOptions; +use deno_runtime::deno_tls::deno_native_certs::load_native_certs; +use deno_runtime::deno_tls::rustls; +use deno_runtime::deno_tls::rustls::RootCertStore; +use deno_runtime::deno_tls::rustls_pemfile; +use deno_runtime::deno_tls::webpki_roots; +use deno_runtime::inspector_server::InspectorServer; +use deno_semver::npm::NpmPackageReqReference; +use deno_semver::StackString; +use deno_telemetry::OtelConfig; +use deno_telemetry::OtelRuntimeConfig; +use deno_terminal::colors; +use dotenvy::from_filename; +pub use flags::*; +use import_map::resolve_import_map_value_from_specifier; +pub use lockfile::CliLockfile; +pub use lockfile::CliLockfileReadFromPathOptions; +use once_cell::sync::Lazy; +pub use package_json::NpmInstallDepsProvider; +pub use package_json::PackageJsonDepValueParseWithLocationError; +use serde::Deserialize; +use serde::Serialize; use sys_traits::EnvHomeDir; use thiserror::Error; @@ -92,10 +94,6 @@ use crate::sys::CliSys; use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::version; -use deno_config::deno_json::FmtConfig; -use deno_config::deno_json::LintConfig; -use deno_config::deno_json::TestConfig; - pub fn npm_registry_url() -> &'static Url { static NPM_REGISTRY_DEFAULT_URL: Lazy = Lazy::new(|| { let env_var_name = "NPM_CONFIG_REGISTRY"; diff --git a/cli/bench/lsp.rs b/cli/bench/lsp.rs index 7baaffca7e..1e02291e10 100644 --- a/cli/bench/lsp.rs +++ b/cli/bench/lsp.rs @@ -1,14 +1,15 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::HashMap; +use std::path::Path; +use std::str::FromStr; +use std::time::Duration; + use deno_core::serde::Deserialize; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use lsp_types::Uri; -use std::collections::HashMap; -use std::path::Path; -use std::str::FromStr; -use std::time::Duration; use test_util::lsp::LspClientBuilder; use test_util::PathRef; use tower_lsp::lsp_types as lsp; diff --git a/cli/bench/main.rs b/cli/bench/main.rs index c3c42d2488..f38aa79e9e 100644 --- a/cli/bench/main.rs +++ b/cli/bench/main.rs @@ -3,9 +3,6 @@ #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::serde_json::Value; use std::collections::HashMap; use std::convert::From; use std::env; @@ -15,6 +12,10 @@ use std::path::PathBuf; use std::process::Command; use std::process::Stdio; use std::time::SystemTime; + +use deno_core::error::AnyError; +use deno_core::serde_json; +use deno_core::serde_json::Value; use test_util::PathRef; mod lsp; diff --git a/cli/build.rs b/cli/build.rs index 3d98661284..8367910678 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -8,16 +8,18 @@ use deno_runtime::*; mod shared; mod ts { - use super::*; + use std::collections::HashMap; + use std::io::Write; + use std::path::Path; + use std::path::PathBuf; + use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; use serde::Serialize; - use std::collections::HashMap; - use std::io::Write; - use std::path::Path; - use std::path::PathBuf; + + use super::*; #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] diff --git a/cli/cache/cache_db.rs b/cli/cache/cache_db.rs index 329ed2d970..c25c1955b2 100644 --- a/cli/cache/cache_db.rs +++ b/cli/cache/cache_db.rs @@ -1,5 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::io::IsTerminal; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::parking_lot::MutexGuard; @@ -9,10 +14,6 @@ use deno_runtime::deno_webstorage::rusqlite::Connection; use deno_runtime::deno_webstorage::rusqlite::OptionalExtension; use deno_runtime::deno_webstorage::rusqlite::Params; use once_cell::sync::OnceCell; -use std::io::IsTerminal; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use super::FastInsecureHasher; diff --git a/cli/cache/check.rs b/cli/cache/check.rs index ca4e938533..192d338a09 100644 --- a/cli/cache/check.rs +++ b/cli/cache/check.rs @@ -1,12 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_ast::ModuleSpecifier; +use deno_core::error::AnyError; +use deno_runtime::deno_webstorage::rusqlite::params; + use super::cache_db::CacheDB; use super::cache_db::CacheDBConfiguration; use super::cache_db::CacheDBHash; use super::cache_db::CacheFailure; -use deno_ast::ModuleSpecifier; -use deno_core::error::AnyError; -use deno_runtime::deno_webstorage::rusqlite::params; pub static TYPE_CHECK_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration { table_initializer: concat!( diff --git a/cli/cache/code_cache.rs b/cli/cache/code_cache.rs index b1d9ae757b..b6c9060ea0 100644 --- a/cli/cache/code_cache.rs +++ b/cli/cache/code_cache.rs @@ -7,12 +7,11 @@ use deno_core::error::AnyError; use deno_runtime::code_cache; use deno_runtime::deno_webstorage::rusqlite::params; -use crate::worker::CliCodeCache; - use super::cache_db::CacheDB; use super::cache_db::CacheDBConfiguration; use super::cache_db::CacheDBHash; use super::cache_db::CacheFailure; +use crate::worker::CliCodeCache; pub static CODE_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration { table_initializer: concat!( diff --git a/cli/cache/deno_dir.rs b/cli/cache/deno_dir.rs index 90a3add54e..2c0aa30bc2 100644 --- a/cli/cache/deno_dir.rs +++ b/cli/cache/deno_dir.rs @@ -1,14 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::env; +use std::path::PathBuf; + use deno_cache_dir::DenoDirResolutionError; use once_cell::sync::OnceCell; -use crate::sys::CliSys; - use super::DiskCache; - -use std::env; -use std::path::PathBuf; +use crate::sys::CliSys; /// Lazily creates the deno dir which might be useful in scenarios /// where functionality wants to continue if the DENO_DIR can't be created. diff --git a/cli/cache/disk_cache.rs b/cli/cache/disk_cache.rs index c96a3943c0..b23d7b8f78 100644 --- a/cli/cache/disk_cache.rs +++ b/cli/cache/disk_cache.rs @@ -1,13 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::sys::CliSys; - -use super::CACHE_PERM; - -use deno_cache_dir::url_to_filename; -use deno_core::url::Host; -use deno_core::url::Url; -use deno_path_util::fs::atomic_write_file_with_retries; use std::ffi::OsStr; use std::fs; use std::path::Component; @@ -16,6 +8,14 @@ use std::path::PathBuf; use std::path::Prefix; use std::str; +use deno_cache_dir::url_to_filename; +use deno_core::url::Host; +use deno_core::url::Url; +use deno_path_util::fs::atomic_write_file_with_retries; + +use super::CACHE_PERM; +use crate::sys::CliSys; + #[derive(Debug, Clone)] pub struct DiskCache { sys: CliSys, @@ -130,9 +130,10 @@ impl DiskCache { #[cfg(test)] mod tests { - use super::*; use test_util::TempDir; + use super::*; + #[test] fn test_set_get_cache_file() { let temp_dir = TempDir::new(); diff --git a/cli/cache/emit.rs b/cli/cache/emit.rs index b239cc93ba..100cbfe744 100644 --- a/cli/cache/emit.rs +++ b/cli/cache/emit.rs @@ -159,9 +159,8 @@ impl EmitFileSerializer { mod test { use test_util::TempDir; - use crate::sys::CliSys; - use super::*; + use crate::sys::CliSys; #[test] pub fn emit_cache_general_use() { diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index bc6f792667..ef93939c10 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -1,11 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::jsr_url; -use crate::file_fetcher::CliFetchNoFollowErrorKind; -use crate::file_fetcher::CliFileFetcher; -use crate::file_fetcher::FetchNoFollowOptions; -use crate::file_fetcher::FetchPermissionsOptionRef; -use crate::sys::CliSys; +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::Arc; use deno_ast::MediaType; use deno_cache_dir::file_fetcher::CacheSetting; @@ -21,9 +18,13 @@ use deno_graph::source::LoadResponse; use deno_graph::source::Loader; use deno_runtime::deno_permissions::PermissionsContainer; use node_resolver::InNpmPackageChecker; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::Arc; + +use crate::args::jsr_url; +use crate::file_fetcher::CliFetchNoFollowErrorKind; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::FetchNoFollowOptions; +use crate::file_fetcher::FetchPermissionsOptionRef; +use crate::sys::CliSys; mod cache_db; mod caches; @@ -44,6 +45,8 @@ pub use caches::Caches; pub use check::TypeCheckCache; pub use code_cache::CodeCache; pub use common::FastInsecureHasher; +/// Permissions used to save a file in the disk caches. +pub use deno_cache_dir::CACHE_PERM; pub use deno_dir::DenoDir; pub use deno_dir::DenoDirProvider; pub use disk_cache::DiskCache; @@ -55,9 +58,6 @@ pub use node::NodeAnalysisCache; pub use parsed_source::LazyGraphSourceParser; pub use parsed_source::ParsedSourceCache; -/// Permissions used to save a file in the disk caches. -pub use deno_cache_dir::CACHE_PERM; - pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache; pub type LocalHttpCache = deno_cache_dir::LocalHttpCache; pub type LocalLspHttpCache = deno_cache_dir::LocalLspHttpCache; diff --git a/cli/cache/node.rs b/cli/cache/node.rs index e80342e5c0..92f5a19d71 100644 --- a/cli/cache/node.rs +++ b/cli/cache/node.rs @@ -4,12 +4,11 @@ use deno_core::error::AnyError; use deno_core::serde_json; use deno_runtime::deno_webstorage::rusqlite::params; -use crate::node::CliCjsAnalysis; - use super::cache_db::CacheDB; use super::cache_db::CacheDBConfiguration; use super::cache_db::CacheFailure; use super::CacheDBHash; +use crate::node::CliCjsAnalysis; pub static NODE_ANALYSIS_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration { diff --git a/cli/emit.rs b/cli/emit.rs index 733a89d832..04547adf93 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -1,9 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::cache::EmitCache; -use crate::cache::FastInsecureHasher; -use crate::cache::ParsedSourceCache; -use crate::resolver::CjsTracker; +use std::sync::Arc; use deno_ast::EmittedSourceText; use deno_ast::ModuleKind; @@ -21,7 +18,11 @@ use deno_core::ModuleSpecifier; use deno_graph::MediaType; use deno_graph::Module; use deno_graph::ModuleGraph; -use std::sync::Arc; + +use crate::cache::EmitCache; +use crate::cache::FastInsecureHasher; +use crate::cache::ParsedSourceCache; +use crate::resolver::CjsTracker; #[derive(Debug)] pub struct Emitter { diff --git a/cli/factory.rs b/cli/factory.rs index e33b95d235..a57f7822a8 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -1,5 +1,34 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::future::Future; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_cache_dir::npm::NpmCacheDir; +use deno_config::workspace::PackageJsonDepResolution; +use deno_config::workspace::WorkspaceResolver; +use deno_core::error::AnyError; +use deno_core::futures::FutureExt; +use deno_core::FeatureChecker; +use deno_resolver::cjs::IsCjsResolutionMode; +use deno_resolver::npm::NpmReqResolverOptions; +use deno_resolver::DenoResolverOptions; +use deno_resolver::NodeAndNpmReqResolver; +use deno_runtime::deno_fs; +use deno_runtime::deno_fs::RealFs; +use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; +use deno_runtime::deno_permissions::Permissions; +use deno_runtime::deno_permissions::PermissionsContainer; +use deno_runtime::deno_tls::rustls::RootCertStore; +use deno_runtime::deno_tls::RootCertStoreProvider; +use deno_runtime::deno_web::BlobStore; +use deno_runtime::inspector_server::InspectorServer; +use deno_runtime::permissions::RuntimePermissionDescriptorParser; +use log::warn; +use node_resolver::analyze::NodeCodeTranslator; +use node_resolver::InNpmPackageChecker; +use once_cell::sync::OnceCell; + use crate::args::check_warn_tsconfig; use crate::args::get_root_cert_store; use crate::args::CaData; @@ -63,35 +92,6 @@ use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::worker::CliMainWorkerFactory; use crate::worker::CliMainWorkerOptions; -use std::path::PathBuf; - -use deno_cache_dir::npm::NpmCacheDir; -use deno_config::workspace::PackageJsonDepResolution; -use deno_config::workspace::WorkspaceResolver; -use deno_core::error::AnyError; -use deno_core::futures::FutureExt; -use deno_core::FeatureChecker; - -use deno_resolver::cjs::IsCjsResolutionMode; -use deno_resolver::npm::NpmReqResolverOptions; -use deno_resolver::DenoResolverOptions; -use deno_resolver::NodeAndNpmReqResolver; -use deno_runtime::deno_fs; -use deno_runtime::deno_fs::RealFs; -use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; -use deno_runtime::deno_permissions::Permissions; -use deno_runtime::deno_permissions::PermissionsContainer; -use deno_runtime::deno_tls::rustls::RootCertStore; -use deno_runtime::deno_tls::RootCertStoreProvider; -use deno_runtime::deno_web::BlobStore; -use deno_runtime::inspector_server::InspectorServer; -use deno_runtime::permissions::RuntimePermissionDescriptorParser; -use log::warn; -use node_resolver::analyze::NodeCodeTranslator; -use node_resolver::InNpmPackageChecker; -use once_cell::sync::OnceCell; -use std::future::Future; -use std::sync::Arc; struct CliRootCertStoreProvider { cell: OnceCell, diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 7e8438d639..3d73a7e4ea 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -498,10 +498,6 @@ fn validate_scheme(specifier: &Url) -> Result<(), UnsupportedSchemeError> { #[cfg(test)] mod tests { - use crate::cache::GlobalHttpCache; - use crate::http_util::HttpClientProvider; - - use super::*; use deno_cache_dir::file_fetcher::FetchNoFollowErrorKind; use deno_cache_dir::file_fetcher::HttpClient; use deno_core::resolve_url; @@ -509,6 +505,10 @@ mod tests { use deno_runtime::deno_web::InMemoryBlobPart; use test_util::TempDir; + use super::*; + use crate::cache::GlobalHttpCache; + use crate::http_util::HttpClientProvider; + fn setup( cache_setting: CacheSetting, maybe_temp_dir: Option, diff --git a/cli/http_util.rs b/cli/http_util.rs index b24dd7bc0c..618ba35346 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -1,7 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::util::progress_bar::UpdateGuard; -use crate::version; +use std::collections::HashMap; +use std::sync::Arc; +use std::thread::ThreadId; use boxed_error::Boxed; use deno_cache_dir::file_fetcher::RedirectHeaderParseError; @@ -23,12 +24,11 @@ use http::header::CONTENT_LENGTH; use http::HeaderMap; use http::StatusCode; use http_body_util::BodyExt; - -use std::collections::HashMap; -use std::sync::Arc; -use std::thread::ThreadId; use thiserror::Error; +use crate::util::progress_bar::UpdateGuard; +use crate::version; + #[derive(Debug, Error)] pub enum SendError { #[error(transparent)] diff --git a/cli/jsr.rs b/cli/jsr.rs index acfbb1c8e2..ef57e8fd64 100644 --- a/cli/jsr.rs +++ b/cli/jsr.rs @@ -1,14 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::jsr_url; -use crate::file_fetcher::CliFileFetcher; +use std::sync::Arc; + use dashmap::DashMap; use deno_core::serde_json; use deno_graph::packages::JsrPackageInfo; use deno_graph::packages::JsrPackageVersionInfo; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; -use std::sync::Arc; + +use crate::args::jsr_url; +use crate::file_fetcher::CliFileFetcher; /// This is similar to a subset of `JsrCacheResolver` which fetches rather than /// just reads the cache. Keep in sync! diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 8fb3454bc8..116780eb73 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -1,25 +1,15 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::diagnostics::DenoDiagnostic; -use super::diagnostics::DiagnosticSource; -use super::documents::Document; -use super::documents::Documents; -use super::language_server; -use super::resolver::LspResolver; -use super::tsc; -use super::urls::url_to_uri; - -use crate::args::jsr_url; -use crate::lsp::logging::lsp_warn; -use crate::lsp::search::PackageSearchApi; -use crate::tools::lint::CliLinter; -use crate::util::path::relative_specifier; -use deno_config::workspace::MappedResolution; -use deno_lint::diagnostic::LintDiagnosticRange; +use std::borrow::Cow; +use std::cmp::Ordering; +use std::collections::HashMap; +use std::collections::HashSet; +use std::path::Path; use deno_ast::SourceRange; use deno_ast::SourceRangedForSpanned; use deno_ast::SourceTextInfo; +use deno_config::workspace::MappedResolution; use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::serde::Deserialize; @@ -27,6 +17,7 @@ use deno_core::serde::Serialize; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::ModuleSpecifier; +use deno_lint::diagnostic::LintDiagnosticRange; use deno_path_util::url_to_file_path; use deno_runtime::deno_node::PathClean; use deno_semver::jsr::JsrPackageNvReference; @@ -44,16 +35,25 @@ use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use once_cell::sync::Lazy; use regex::Regex; -use std::borrow::Cow; -use std::cmp::Ordering; -use std::collections::HashMap; -use std::collections::HashSet; -use std::path::Path; use text_lines::LineAndColumnIndex; use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types::Position; use tower_lsp::lsp_types::Range; +use super::diagnostics::DenoDiagnostic; +use super::diagnostics::DiagnosticSource; +use super::documents::Document; +use super::documents::Documents; +use super::language_server; +use super::resolver::LspResolver; +use super::tsc; +use super::urls::url_to_uri; +use crate::args::jsr_url; +use crate::lsp::logging::lsp_warn; +use crate::lsp::search::PackageSearchApi; +use crate::tools::lint::CliLinter; +use crate::util::path::relative_specifier; + /// Diagnostic error codes which actually are the same, and so when grouping /// fixes we treat them the same. static FIX_ALL_ERROR_CODES: Lazy> = diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs index 24a55d495c..c69f66b77f 100644 --- a/cli/lsp/cache.rs +++ b/cli/lsp/cache.rs @@ -1,5 +1,15 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::BTreeMap; +use std::fs; +use std::path::Path; +use std::sync::Arc; +use std::time::SystemTime; + +use deno_core::url::Url; +use deno_core::ModuleSpecifier; +use deno_path_util::url_to_file_path; + use crate::cache::DenoDir; use crate::cache::GlobalHttpCache; use crate::cache::HttpCache; @@ -9,15 +19,6 @@ use crate::lsp::logging::lsp_log; use crate::lsp::logging::lsp_warn; use crate::sys::CliSys; -use deno_core::url::Url; -use deno_core::ModuleSpecifier; -use deno_path_util::url_to_file_path; -use std::collections::BTreeMap; -use std::fs; -use std::path::Path; -use std::sync::Arc; -use std::time::SystemTime; - pub fn calculate_fs_version( cache: &LspCache, specifier: &ModuleSpecifier, diff --git a/cli/lsp/client.rs b/cli/lsp/client.rs index 65865d5b32..2bd1ddc2ff 100644 --- a/cli/lsp/client.rs +++ b/cli/lsp/client.rs @@ -12,12 +12,11 @@ use lsp_types::Uri; use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types::ConfigurationItem; -use crate::lsp::repl::get_repl_workspace_settings; - use super::config::WorkspaceSettings; use super::config::SETTINGS_SECTION; use super::lsp_custom; use super::testing::lsp_custom as testing_lsp_custom; +use crate::lsp::repl::get_repl_workspace_settings; #[derive(Debug)] pub enum TestingNotification { diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index a57ca3ac9f..c5ef5f7223 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -1,13 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::lsp::logging::lsp_warn; - -use super::analysis::source_range_to_lsp_range; -use super::config::CodeLensSettings; -use super::language_server; -use super::text::LineIndex; -use super::tsc; -use super::tsc::NavigationTree; +use std::cell::RefCell; +use std::collections::HashSet; +use std::rc::Rc; +use std::sync::Arc; use deno_ast::swc::ast; use deno_ast::swc::visit::Visit; @@ -25,13 +21,17 @@ use deno_core::ModuleSpecifier; use lazy_regex::lazy_regex; use once_cell::sync::Lazy; use regex::Regex; -use std::cell::RefCell; -use std::collections::HashSet; -use std::rc::Rc; -use std::sync::Arc; use tower_lsp::jsonrpc::Error as LspError; use tower_lsp::lsp_types as lsp; +use super::analysis::source_range_to_lsp_range; +use super::config::CodeLensSettings; +use super::language_server; +use super::text::LineIndex; +use super::tsc; +use super::tsc::NavigationTree; +use crate::lsp::logging::lsp_warn; + static ABSTRACT_MODIFIER: Lazy = lazy_regex!(r"\babstract\b"); static EXPORT_MODIFIER: Lazy = lazy_regex!(r"\bexport\b"); diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs index 31f0b066ed..412ca6fd32 100644 --- a/cli/lsp/completions.rs +++ b/cli/lsp/completions.rs @@ -1,5 +1,27 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_ast::LineAndColumnIndex; +use deno_ast::SourceTextInfo; +use deno_core::resolve_path; +use deno_core::resolve_url; +use deno_core::serde::Deserialize; +use deno_core::serde::Serialize; +use deno_core::serde_json::json; +use deno_core::url::Position; +use deno_core::ModuleSpecifier; +use deno_path_util::url_to_file_path; +use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES; +use deno_semver::jsr::JsrPackageReqReference; +use deno_semver::package::PackageNv; +use import_map::ImportMap; +use indexmap::IndexSet; +use lsp_types::CompletionList; +use node_resolver::NodeResolutionKind; +use node_resolver::ResolutionMode; +use once_cell::sync::Lazy; +use regex::Regex; +use tower_lsp::lsp_types as lsp; + use super::client::Client; use super::config::Config; use super::config::WorkspaceSettings; @@ -12,33 +34,10 @@ use super::registries::ModuleRegistry; use super::resolver::LspResolver; use super::search::PackageSearchApi; use super::tsc; - use crate::graph_util::to_node_resolution_mode; use crate::jsr::JsrFetchResolver; use crate::util::path::is_importable_ext; use crate::util::path::relative_specifier; -use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES; - -use deno_ast::LineAndColumnIndex; -use deno_ast::SourceTextInfo; -use deno_core::resolve_path; -use deno_core::resolve_url; -use deno_core::serde::Deserialize; -use deno_core::serde::Serialize; -use deno_core::serde_json::json; -use deno_core::url::Position; -use deno_core::ModuleSpecifier; -use deno_path_util::url_to_file_path; -use deno_semver::jsr::JsrPackageReqReference; -use deno_semver::package::PackageNv; -use import_map::ImportMap; -use indexmap::IndexSet; -use lsp_types::CompletionList; -use node_resolver::NodeResolutionKind; -use node_resolver::ResolutionMode; -use once_cell::sync::Lazy; -use regex::Regex; -use tower_lsp::lsp_types as lsp; static FILE_PROTO_RE: Lazy = lazy_regex::lazy_regex!(r#"^file:/{2}(?:/[A-Za-z]:)?"#); @@ -822,16 +821,18 @@ fn get_workspace_completions( #[cfg(test)] mod tests { + use std::collections::HashMap; + + use deno_core::resolve_url; + use pretty_assertions::assert_eq; + use test_util::TempDir; + use super::*; use crate::cache::HttpCache; use crate::lsp::cache::LspCache; use crate::lsp::documents::Documents; use crate::lsp::documents::LanguageId; use crate::lsp::search::tests::TestPackageSearchApi; - use deno_core::resolve_url; - use pretty_assertions::assert_eq; - use std::collections::HashMap; - use test_util::TempDir; fn setup( open_sources: &[(&str, &str, i32, LanguageId)], diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index ff4c2978d5..8e61b523f7 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1,5 +1,14 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::ops::Deref; +use std::ops::DerefMut; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + use deno_ast::MediaType; use deno_config::deno_json::DenoJsonCache; use deno_config::deno_json::FmtConfig; @@ -39,14 +48,6 @@ use deno_path_util::url_to_file_path; use deno_runtime::deno_node::PackageJson; use indexmap::IndexSet; use lsp_types::ClientCapabilities; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::ops::Deref; -use std::ops::DerefMut; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use tower_lsp::lsp_types as lsp; use super::logging::lsp_log; diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index af6fdf53a4..625c777a84 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -1,32 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::analysis; -use super::client::Client; -use super::config::Config; -use super::documents; -use super::documents::Document; -use super::documents::Documents; -use super::documents::DocumentsFilter; -use super::language_server; -use super::language_server::StateSnapshot; -use super::performance::Performance; -use super::tsc; -use super::tsc::TsServer; -use super::urls::uri_parse_unencoded; -use super::urls::url_to_uri; -use super::urls::LspUrlMap; - -use crate::graph_util; -use crate::graph_util::enhanced_resolution_error_message; -use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams; -use crate::resolver::CliSloppyImportsResolver; -use crate::resolver::SloppyImportsCachedFs; -use crate::sys::CliSys; -use crate::tools::lint::CliLinter; -use crate::tools::lint::CliLinterOptions; -use crate::tools::lint::LintRuleProvider; -use crate::tsc::DiagnosticCategory; -use crate::util::path::to_percent_decoded_str; +use std::collections::HashMap; +use std::collections::HashSet; +use std::path::PathBuf; +use std::sync::atomic::AtomicUsize; +use std::sync::Arc; +use std::thread; use deno_ast::MediaType; use deno_config::deno_json::LintConfig; @@ -57,18 +36,39 @@ use deno_semver::package::PackageReq; use import_map::ImportMap; use import_map::ImportMapError; use log::error; -use std::collections::HashMap; -use std::collections::HashSet; -use std::path::PathBuf; -use std::sync::atomic::AtomicUsize; -use std::sync::Arc; -use std::thread; use tokio::sync::mpsc; use tokio::sync::Mutex; use tokio::time::Duration; use tokio_util::sync::CancellationToken; use tower_lsp::lsp_types as lsp; +use super::analysis; +use super::client::Client; +use super::config::Config; +use super::documents; +use super::documents::Document; +use super::documents::Documents; +use super::documents::DocumentsFilter; +use super::language_server; +use super::language_server::StateSnapshot; +use super::performance::Performance; +use super::tsc; +use super::tsc::TsServer; +use super::urls::uri_parse_unencoded; +use super::urls::url_to_uri; +use super::urls::LspUrlMap; +use crate::graph_util; +use crate::graph_util::enhanced_resolution_error_message; +use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams; +use crate::resolver::CliSloppyImportsResolver; +use crate::resolver::SloppyImportsCachedFs; +use crate::sys::CliSys; +use crate::tools::lint::CliLinter; +use crate::tools::lint::CliLinterOptions; +use crate::tools::lint::LintRuleProvider; +use crate::tsc::DiagnosticCategory; +use crate::util::path::to_percent_decoded_str; + #[derive(Debug)] pub struct DiagnosticServerUpdateMessage { pub snapshot: Arc, @@ -1646,6 +1646,12 @@ fn generate_deno_diagnostics( #[cfg(test)] mod tests { + use std::sync::Arc; + + use deno_config::deno_json::ConfigFile; + use pretty_assertions::assert_eq; + use test_util::TempDir; + use super::*; use crate::lsp::cache::LspCache; use crate::lsp::config::Config; @@ -1656,11 +1662,6 @@ mod tests { use crate::lsp::language_server::StateSnapshot; use crate::lsp::resolver::LspResolver; - use deno_config::deno_json::ConfigFile; - use pretty_assertions::assert_eq; - use std::sync::Arc; - use test_util::TempDir; - fn mock_config() -> Config { let root_url = resolve_url("file:///").unwrap(); let root_uri = url_to_uri(&root_url).unwrap(); diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index d15cfe5a6c..34c2deee46 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -1,18 +1,17 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::cache::calculate_fs_version; -use super::cache::LspCache; -use super::config::Config; -use super::resolver::LspResolver; -use super::resolver::ScopeDepInfo; -use super::resolver::SingleReferrerGraphResolver; -use super::testing::TestCollector; -use super::testing::TestModule; -use super::text::LineIndex; -use super::tsc; -use super::tsc::AssetDocument; - -use crate::graph_util::CliJsrUrlProvider; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::HashSet; +use std::fs; +use std::future::Future; +use std::ops::Range; +use std::pin::Pin; +use std::str::FromStr; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; +use std::sync::Arc; use dashmap::DashMap; use deno_ast::swc::visit::VisitWith; @@ -36,20 +35,21 @@ use indexmap::IndexMap; use indexmap::IndexSet; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::HashSet; -use std::fs; -use std::future::Future; -use std::ops::Range; -use std::pin::Pin; -use std::str::FromStr; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; -use std::sync::Arc; use tower_lsp::lsp_types as lsp; +use super::cache::calculate_fs_version; +use super::cache::LspCache; +use super::config::Config; +use super::resolver::LspResolver; +use super::resolver::ScopeDepInfo; +use super::resolver::SingleReferrerGraphResolver; +use super::testing::TestCollector; +use super::testing::TestModule; +use super::text::LineIndex; +use super::tsc; +use super::tsc::AssetDocument; +use crate::graph_util::CliJsrUrlProvider; + pub const DOCUMENT_SCHEMES: [&str; 5] = ["data", "blob", "file", "http", "https"]; @@ -1754,9 +1754,6 @@ fn bytes_to_content( #[cfg(test)] mod tests { - use super::*; - use crate::lsp::cache::LspCache; - use deno_config::deno_json::ConfigFile; use deno_config::deno_json::ConfigParseOptions; use deno_core::serde_json; @@ -1764,6 +1761,9 @@ mod tests { use pretty_assertions::assert_eq; use test_util::TempDir; + use super::*; + use crate::lsp::cache::LspCache; + async fn setup() -> (Documents, LspCache, TempDir) { let temp_dir = TempDir::new(); temp_dir.create_dir_all(".deno_dir"); diff --git a/cli/lsp/jsr.rs b/cli/lsp/jsr.rs index fc30de2ae0..48bac7ac47 100644 --- a/cli/lsp/jsr.rs +++ b/cli/lsp/jsr.rs @@ -1,11 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::jsr_api_url; -use crate::args::jsr_url; -use crate::file_fetcher::CliFileFetcher; -use crate::file_fetcher::TextDecodedFile; -use crate::jsr::partial_jsr_package_version_info_from_slice; -use crate::jsr::JsrFetchResolver; +use std::collections::HashMap; +use std::sync::Arc; + use dashmap::DashMap; use deno_cache_dir::HttpCache; use deno_core::anyhow::anyhow; @@ -21,11 +18,15 @@ use deno_semver::package::PackageReq; use deno_semver::StackString; use deno_semver::Version; use serde::Deserialize; -use std::collections::HashMap; -use std::sync::Arc; use super::config::ConfigData; use super::search::PackageSearchApi; +use crate::args::jsr_api_url; +use crate::args::jsr_url; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::TextDecodedFile; +use crate::jsr::partial_jsr_package_version_info_from_slice; +use crate::jsr::JsrFetchResolver; /// Keep in sync with `JsrFetchResolver`! #[derive(Debug)] diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 9ab1d9786c..98b2fb1462 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1,5 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::collections::HashSet; +use std::collections::VecDeque; +use std::env; +use std::fmt::Write as _; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; + use deno_ast::MediaType; use deno_cache_dir::file_fetcher::CacheSetting; use deno_config::workspace::WorkspaceDirectory; @@ -27,16 +38,6 @@ use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use serde::Deserialize; use serde_json::from_value; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::collections::HashSet; -use std::collections::VecDeque; -use std::env; -use std::fmt::Write as _; -use std::path::PathBuf; -use std::str::FromStr; -use std::sync::Arc; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; @@ -3963,10 +3964,11 @@ impl Inner { #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; use test_util::TempDir; + use super::*; + #[test] fn test_walk_workspace() { let temp_dir = TempDir::new(); diff --git a/cli/lsp/logging.rs b/cli/lsp/logging.rs index 2b85d77ec1..cb803b9478 100644 --- a/cli/lsp/logging.rs +++ b/cli/lsp/logging.rs @@ -1,8 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use chrono::DateTime; -use chrono::Utc; -use deno_core::parking_lot::Mutex; use std::fs; use std::io::prelude::*; use std::path::Path; @@ -12,6 +9,10 @@ use std::sync::atomic::Ordering; use std::thread; use std::time::SystemTime; +use chrono::DateTime; +use chrono::Utc; +use deno_core::parking_lot::Mutex; + static LSP_DEBUG_FLAG: AtomicBool = AtomicBool::new(false); static LSP_LOG_LEVEL: AtomicUsize = AtomicUsize::new(log::Level::Info as usize); static LSP_WARN_LEVEL: AtomicUsize = diff --git a/cli/lsp/mod.rs b/cli/lsp/mod.rs index afb949f68d..0b00f8ef68 100644 --- a/cli/lsp/mod.rs +++ b/cli/lsp/mod.rs @@ -2,15 +2,14 @@ use deno_core::error::AnyError; use deno_core::unsync::spawn; +pub use repl::ReplCompletionItem; +pub use repl::ReplLanguageServer; use tower_lsp::LspService; use tower_lsp::Server; +use self::diagnostics::should_send_diagnostic_batch_index_notifications; use crate::lsp::language_server::LanguageServer; use crate::util::sync::AsyncFlag; -pub use repl::ReplCompletionItem; -pub use repl::ReplLanguageServer; - -use self::diagnostics::should_send_diagnostic_batch_index_notifications; mod analysis; mod cache; diff --git a/cli/lsp/npm.rs b/cli/lsp/npm.rs index 18c7e2fccf..8b96eed66f 100644 --- a/cli/lsp/npm.rs +++ b/cli/lsp/npm.rs @@ -1,5 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::sync::Arc; + use dashmap::DashMap; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; @@ -8,15 +10,13 @@ use deno_npm::npm_rc::NpmRc; use deno_semver::package::PackageNv; use deno_semver::Version; use serde::Deserialize; -use std::sync::Arc; +use super::search::PackageSearchApi; use crate::args::npm_registry_url; use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::TextDecodedFile; use crate::npm::NpmFetchResolver; -use super::search::PackageSearchApi; - #[derive(Debug)] pub struct CliNpmSearchApi { file_fetcher: Arc, diff --git a/cli/lsp/parent_process_checker.rs b/cli/lsp/parent_process_checker.rs index b8a42cd1a4..287880ae32 100644 --- a/cli/lsp/parent_process_checker.rs +++ b/cli/lsp/parent_process_checker.rs @@ -52,10 +52,12 @@ fn is_process_active(process_id: u32) -> bool { #[cfg(test)] mod test { - use super::is_process_active; use std::process::Command; + use test_util::deno_exe_path; + use super::is_process_active; + #[test] fn process_active() { // launch a long running process diff --git a/cli/lsp/path_to_regex.rs b/cli/lsp/path_to_regex.rs index 88d8a2ec68..10b0cfbdc8 100644 --- a/cli/lsp/path_to_regex.rs +++ b/cli/lsp/path_to_regex.rs @@ -26,15 +26,16 @@ // THE SOFTWARE. // +use std::collections::HashMap; +use std::fmt; +use std::fmt::Write as _; +use std::iter::Peekable; + use deno_core::anyhow::anyhow; use deno_core::error::AnyError; use fancy_regex::Regex as FancyRegex; use once_cell::sync::Lazy; use regex::Regex; -use std::collections::HashMap; -use std::fmt; -use std::fmt::Write as _; -use std::iter::Peekable; static ESCAPE_STRING_RE: Lazy = lazy_regex::lazy_regex!(r"([.+*?=^!:${}()\[\]|/\\])"); diff --git a/cli/lsp/performance.rs b/cli/lsp/performance.rs index b3dc53b283..360bdf103e 100644 --- a/cli/lsp/performance.rs +++ b/cli/lsp/performance.rs @@ -1,9 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::parking_lot::Mutex; -use deno_core::serde::Deserialize; -use deno_core::serde::Serialize; -use deno_core::serde_json::json; use std::cmp; use std::collections::HashMap; use std::collections::VecDeque; @@ -12,6 +8,11 @@ use std::sync::Arc; use std::time::Duration; use std::time::Instant; +use deno_core::parking_lot::Mutex; +use deno_core::serde::Deserialize; +use deno_core::serde::Serialize; +use deno_core::serde_json::json; + use super::logging::lsp_debug; #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index c8dd7fa1a7..be36eeb73d 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -1,25 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::completions::IMPORT_COMMIT_CHARS; -use super::logging::lsp_log; -use super::path_to_regex::parse; -use super::path_to_regex::string_to_regex; -use super::path_to_regex::Compiler; -use super::path_to_regex::Key; -use super::path_to_regex::MatchResult; -use super::path_to_regex::Matcher; -use super::path_to_regex::StringOrNumber; -use super::path_to_regex::StringOrVec; -use super::path_to_regex::Token; - -use crate::cache::GlobalHttpCache; -use crate::cache::HttpCache; -use crate::file_fetcher::CliFileFetcher; -use crate::file_fetcher::FetchOptions; -use crate::file_fetcher::FetchPermissionsOptionRef; -use crate::file_fetcher::TextDecodedFile; -use crate::http_util::HttpClientProvider; -use crate::sys::CliSys; +use std::borrow::Cow; +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::Arc; use deno_cache_dir::file_fetcher::CacheSetting; use deno_core::anyhow::anyhow; @@ -35,12 +19,28 @@ use deno_core::ModuleSpecifier; use deno_graph::Dependency; use log::error; use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::Arc; use tower_lsp::lsp_types as lsp; +use super::completions::IMPORT_COMMIT_CHARS; +use super::logging::lsp_log; +use super::path_to_regex::parse; +use super::path_to_regex::string_to_regex; +use super::path_to_regex::Compiler; +use super::path_to_regex::Key; +use super::path_to_regex::MatchResult; +use super::path_to_regex::Matcher; +use super::path_to_regex::StringOrNumber; +use super::path_to_regex::StringOrVec; +use super::path_to_regex::Token; +use crate::cache::GlobalHttpCache; +use crate::cache::HttpCache; +use crate::file_fetcher::CliFileFetcher; +use crate::file_fetcher::FetchOptions; +use crate::file_fetcher::FetchPermissionsOptionRef; +use crate::file_fetcher::TextDecodedFile; +use crate::http_util::HttpClientProvider; +use crate::sys::CliSys; + const CONFIG_PATH: &str = "/.well-known/deno-import-intellisense.json"; const COMPONENT: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS .add(b' ') @@ -1128,9 +1128,10 @@ FetchPermissionsOptionRef::AllowAll, #[cfg(test)] mod tests { - use super::*; use test_util::TempDir; + use super::*; + #[test] fn test_validate_registry_configuration() { assert!(validate_config(&RegistryConfigurationJson { diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 2dec5266f4..7fb4e85529 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -1,5 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::collections::HashSet; +use std::sync::Arc; + use dashmap::DashMap; use deno_ast::MediaType; use deno_cache_dir::file_fetcher::CacheSetting; @@ -28,12 +35,6 @@ use indexmap::IndexMap; use node_resolver::InNpmPackageChecker; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::collections::HashSet; -use std::sync::Arc; use super::cache::LspCache; use super::jsr::JsrCacheResolver; diff --git a/cli/lsp/search.rs b/cli/lsp/search.rs index c98acde6f1..7d2760c1de 100644 --- a/cli/lsp/search.rs +++ b/cli/lsp/search.rs @@ -1,9 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::sync::Arc; + use deno_core::error::AnyError; use deno_semver::package::PackageNv; use deno_semver::Version; -use std::sync::Arc; #[async_trait::async_trait] pub trait PackageSearchApi { @@ -15,10 +16,12 @@ pub trait PackageSearchApi { #[cfg(test)] pub mod tests { - use super::*; - use deno_core::anyhow::anyhow; use std::collections::BTreeMap; + use deno_core::anyhow::anyhow; + + use super::*; + #[derive(Debug, Default)] pub struct TestPackageSearchApi { /// [(name -> [(version -> [export])])] diff --git a/cli/lsp/semantic_tokens.rs b/cli/lsp/semantic_tokens.rs index 0cf154d0ff..cc880de8b6 100644 --- a/cli/lsp/semantic_tokens.rs +++ b/cli/lsp/semantic_tokens.rs @@ -7,6 +7,7 @@ use std::ops::Index; use std::ops::IndexMut; + use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types::SemanticToken; use tower_lsp::lsp_types::SemanticTokenModifier; diff --git a/cli/lsp/testing/collectors.rs b/cli/lsp/testing/collectors.rs index 2dd7ec0d96..141d34d79d 100644 --- a/cli/lsp/testing/collectors.rs +++ b/cli/lsp/testing/collectors.rs @@ -1,8 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::lsp::analysis::source_range_to_lsp_range; - -use super::definitions::TestModule; +use std::collections::HashMap; +use std::collections::HashSet; use deno_ast::swc::ast; use deno_ast::swc::visit::Visit; @@ -11,10 +10,11 @@ use deno_ast::SourceRangedForSpanned; use deno_ast::SourceTextInfo; use deno_core::ModuleSpecifier; use lsp::Range; -use std::collections::HashMap; -use std::collections::HashSet; use tower_lsp::lsp_types as lsp; +use super::definitions::TestModule; +use crate::lsp::analysis::source_range_to_lsp_range; + /// Parse an arrow expression for any test steps and return them. fn visit_arrow( arrow_expr: &ast::ArrowExpr, @@ -626,12 +626,12 @@ impl Visit for TestCollector { #[cfg(test)] pub mod tests { - use crate::lsp::testing::definitions::TestDefinition; - - use super::*; use deno_core::resolve_url; use lsp::Position; + use super::*; + use crate::lsp::testing::definitions::TestDefinition; + pub fn new_range(l1: u32, c1: u32, l2: u32, c2: u32) -> Range { Range::new(Position::new(l1, c1), Position::new(l2, c2)) } diff --git a/cli/lsp/testing/definitions.rs b/cli/lsp/testing/definitions.rs index f23411852f..44ea5ce6fe 100644 --- a/cli/lsp/testing/definitions.rs +++ b/cli/lsp/testing/definitions.rs @@ -1,8 +1,15 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::HashMap; +use std::collections::HashSet; + +use deno_core::error::AnyError; +use deno_core::ModuleSpecifier; +use lsp::Range; +use tower_lsp::lsp_types as lsp; + use super::lsp_custom; use super::lsp_custom::TestData; - use crate::lsp::client::TestingNotification; use crate::lsp::logging::lsp_warn; use crate::lsp::urls::url_to_uri; @@ -10,13 +17,6 @@ use crate::tools::test::TestDescription; use crate::tools::test::TestStepDescription; use crate::util::checksum; -use deno_core::error::AnyError; -use deno_core::ModuleSpecifier; -use lsp::Range; -use std::collections::HashMap; -use std::collections::HashSet; -use tower_lsp::lsp_types as lsp; - #[derive(Debug, Clone, PartialEq)] pub struct TestDefinition { pub id: String, diff --git a/cli/lsp/testing/execution.rs b/cli/lsp/testing/execution.rs index 88fb496e4e..3378891316 100644 --- a/cli/lsp/testing/execution.rs +++ b/cli/lsp/testing/execution.rs @@ -1,24 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::definitions::TestDefinition; -use super::definitions::TestModule; -use super::lsp_custom; -use super::server::TestServerTests; - -use crate::args::flags_from_vec; -use crate::args::DenoSubcommand; -use crate::factory::CliFactory; -use crate::lsp::client::Client; -use crate::lsp::client::TestingNotification; -use crate::lsp::config; -use crate::lsp::logging::lsp_log; -use crate::lsp::urls::uri_parse_unencoded; -use crate::lsp::urls::uri_to_url; -use crate::lsp::urls::url_to_uri; -use crate::tools::test; -use crate::tools::test::create_test_event_channel; -use crate::tools::test::FailFastTracker; -use crate::tools::test::TestFailureFormatOptions; +use std::borrow::Cow; +use std::collections::HashMap; +use std::collections::HashSet; +use std::num::NonZeroUsize; +use std::sync::Arc; +use std::time::Duration; +use std::time::Instant; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; @@ -34,16 +22,28 @@ use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::tokio_util::create_and_run_current_thread; use indexmap::IndexMap; -use std::borrow::Cow; -use std::collections::HashMap; -use std::collections::HashSet; -use std::num::NonZeroUsize; -use std::sync::Arc; -use std::time::Duration; -use std::time::Instant; use tokio_util::sync::CancellationToken; use tower_lsp::lsp_types as lsp; +use super::definitions::TestDefinition; +use super::definitions::TestModule; +use super::lsp_custom; +use super::server::TestServerTests; +use crate::args::flags_from_vec; +use crate::args::DenoSubcommand; +use crate::factory::CliFactory; +use crate::lsp::client::Client; +use crate::lsp::client::TestingNotification; +use crate::lsp::config; +use crate::lsp::logging::lsp_log; +use crate::lsp::urls::uri_parse_unencoded; +use crate::lsp::urls::uri_to_url; +use crate::lsp::urls::url_to_uri; +use crate::tools::test; +use crate::tools::test::create_test_event_channel; +use crate::tools::test::FailFastTracker; +use crate::tools::test::TestFailureFormatOptions; + /// Logic to convert a test request into a set of test modules to be tested and /// any filters to be applied to those tests fn as_queue_and_filters( @@ -794,9 +794,10 @@ impl LspTestReporter { #[cfg(test)] mod tests { + use deno_core::serde_json::json; + use super::*; use crate::lsp::testing::collectors::tests::new_range; - use deno_core::serde_json::json; #[test] fn test_as_queue_and_filters() { diff --git a/cli/lsp/testing/server.rs b/cli/lsp/testing/server.rs index c9c39d9ffc..a4b286671e 100644 --- a/cli/lsp/testing/server.rs +++ b/cli/lsp/testing/server.rs @@ -1,16 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::definitions::TestModule; -use super::execution::TestRun; -use super::lsp_custom; - -use crate::lsp::client::Client; -use crate::lsp::client::TestingNotification; -use crate::lsp::config; -use crate::lsp::documents::DocumentsFilter; -use crate::lsp::language_server::StateSnapshot; -use crate::lsp::performance::Performance; -use crate::lsp::urls::url_to_uri; +use std::collections::HashMap; +use std::collections::HashSet; +use std::sync::Arc; +use std::thread; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; @@ -18,15 +11,22 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::ModuleSpecifier; use deno_runtime::tokio_util::create_basic_runtime; -use std::collections::HashMap; -use std::collections::HashSet; -use std::sync::Arc; -use std::thread; use tokio::sync::mpsc; use tower_lsp::jsonrpc::Error as LspError; use tower_lsp::jsonrpc::Result as LspResult; use tower_lsp::lsp_types as lsp; +use super::definitions::TestModule; +use super::execution::TestRun; +use super::lsp_custom; +use crate::lsp::client::Client; +use crate::lsp::client::TestingNotification; +use crate::lsp::config; +use crate::lsp::documents::DocumentsFilter; +use crate::lsp::language_server::StateSnapshot; +use crate::lsp::performance::Performance; +use crate::lsp::urls::url_to_uri; + fn as_delete_notification( url: &ModuleSpecifier, ) -> Result { diff --git a/cli/lsp/text.rs b/cli/lsp/text.rs index 88f27915bb..3c9721bdc9 100644 --- a/cli/lsp/text.rs +++ b/cli/lsp/text.rs @@ -1,10 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::HashMap; + use deno_core::error::custom_error; use deno_core::error::AnyError; use dissimilar::diff; use dissimilar::Chunk; -use std::collections::HashMap; use text_size::TextRange; use text_size::TextSize; use tower_lsp::jsonrpc; diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index f8b972511f..cdd46c2ded 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -1,52 +1,29 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::analysis::CodeActionData; -use super::code_lens; -use super::config; -use super::config::LspTsConfig; -use super::documents::AssetOrDocument; -use super::documents::Document; -use super::documents::DocumentsFilter; -use super::language_server; -use super::language_server::StateSnapshot; -use super::performance::Performance; -use super::performance::PerformanceMark; -use super::refactor::RefactorCodeActionData; -use super::refactor::ALL_KNOWN_REFACTOR_ACTION_KINDS; -use super::refactor::EXTRACT_CONSTANT; -use super::refactor::EXTRACT_INTERFACE; -use super::refactor::EXTRACT_TYPE; -use super::semantic_tokens; -use super::semantic_tokens::SemanticTokensBuilder; -use super::text::LineIndex; -use super::urls::uri_to_url; -use super::urls::url_to_uri; -use super::urls::INVALID_SPECIFIER; -use super::urls::INVALID_URI; - -use crate::args::jsr_url; -use crate::args::FmtOptionsConfig; -use crate::lsp::logging::lsp_warn; -use crate::tsc; -use crate::tsc::ResolveArgs; -use crate::tsc::MISSING_DEPENDENCY_SPECIFIER; -use crate::util::path::relative_specifier; -use crate::util::path::to_percent_decoded_str; -use crate::util::result::InfallibleResultExt; -use crate::util::v8::convert; -use crate::worker::create_isolate_create_params; -use deno_core::convert::Smi; -use deno_core::convert::ToV8; -use deno_core::error::StdAnyError; -use deno_core::futures::stream::FuturesOrdered; -use deno_core::futures::StreamExt; +use std::cell::RefCell; +use std::cmp; +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::HashSet; +use std::convert::Infallible; +use std::net::SocketAddr; +use std::ops::Range; +use std::path::Path; +use std::rc::Rc; +use std::sync::Arc; +use std::thread; use dashmap::DashMap; use deno_ast::MediaType; use deno_core::anyhow::anyhow; use deno_core::anyhow::Context as _; +use deno_core::convert::Smi; +use deno_core::convert::ToV8; use deno_core::error::AnyError; +use deno_core::error::StdAnyError; +use deno_core::futures::stream::FuturesOrdered; use deno_core::futures::FutureExt; +use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::parking_lot::Mutex; use deno_core::resolve_url; @@ -77,18 +54,6 @@ use regex::Captures; use regex::Regex; use serde_repr::Deserialize_repr; use serde_repr::Serialize_repr; -use std::cell::RefCell; -use std::cmp; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::HashSet; -use std::convert::Infallible; -use std::net::SocketAddr; -use std::ops::Range; -use std::path::Path; -use std::rc::Rc; -use std::sync::Arc; -use std::thread; use text_size::TextRange; use text_size::TextSize; use tokio::sync::mpsc; @@ -99,6 +64,41 @@ use tower_lsp::jsonrpc::Error as LspError; use tower_lsp::jsonrpc::Result as LspResult; use tower_lsp::lsp_types as lsp; +use super::analysis::CodeActionData; +use super::code_lens; +use super::config; +use super::config::LspTsConfig; +use super::documents::AssetOrDocument; +use super::documents::Document; +use super::documents::DocumentsFilter; +use super::language_server; +use super::language_server::StateSnapshot; +use super::performance::Performance; +use super::performance::PerformanceMark; +use super::refactor::RefactorCodeActionData; +use super::refactor::ALL_KNOWN_REFACTOR_ACTION_KINDS; +use super::refactor::EXTRACT_CONSTANT; +use super::refactor::EXTRACT_INTERFACE; +use super::refactor::EXTRACT_TYPE; +use super::semantic_tokens; +use super::semantic_tokens::SemanticTokensBuilder; +use super::text::LineIndex; +use super::urls::uri_to_url; +use super::urls::url_to_uri; +use super::urls::INVALID_SPECIFIER; +use super::urls::INVALID_URI; +use crate::args::jsr_url; +use crate::args::FmtOptionsConfig; +use crate::lsp::logging::lsp_warn; +use crate::tsc; +use crate::tsc::ResolveArgs; +use crate::tsc::MISSING_DEPENDENCY_SPECIFIER; +use crate::util::path::relative_specifier; +use crate::util::path::to_percent_decoded_str; +use crate::util::result::InfallibleResultExt; +use crate::util::v8::convert; +use crate::worker::create_isolate_create_params; + static BRACKET_ACCESSOR_RE: Lazy = lazy_regex!(r#"^\[['"](.+)[\['"]\]$"#); static CAPTION_RE: Lazy = @@ -5541,6 +5541,9 @@ impl TscRequest { #[cfg(test)] mod tests { + use pretty_assertions::assert_eq; + use test_util::TempDir; + use super::*; use crate::cache::HttpCache; use crate::lsp::cache::LspCache; @@ -5550,8 +5553,6 @@ mod tests { use crate::lsp::documents::LanguageId; use crate::lsp::resolver::LspResolver; use crate::lsp::text::LineIndex; - use pretty_assertions::assert_eq; - use test_util::TempDir; async fn setup( ts_config: Value, diff --git a/cli/lsp/urls.rs b/cli/lsp/urls.rs index 6c7da4f134..84dd3581d7 100644 --- a/cli/lsp/urls.rs +++ b/cli/lsp/urls.rs @@ -1,5 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::HashMap; +use std::str::FromStr; +use std::sync::Arc; + use deno_ast::MediaType; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; @@ -8,9 +12,6 @@ use deno_core::url::Url; use deno_core::ModuleSpecifier; use lsp_types::Uri; use once_cell::sync::Lazy; -use std::collections::HashMap; -use std::str::FromStr; -use std::sync::Arc; use super::cache::LspCache; use super::logging::lsp_warn; @@ -307,9 +308,10 @@ fn file_like_to_file_specifier(specifier: &Url) -> Option { #[cfg(test)] mod tests { - use super::*; use deno_core::resolve_url; + use super::*; + #[test] fn test_hash_data_specifier() { let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap(); diff --git a/cli/main.rs b/cli/main.rs index c3c7286e71..f3f3254b14 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -28,31 +28,6 @@ mod util; mod version; mod worker; -use crate::args::flags_from_vec; -use crate::args::DenoSubcommand; -use crate::args::Flags; -use crate::util::display; -use crate::util::v8::get_v8_flags_from_env; -use crate::util::v8::init_v8_flags; - -use args::TaskFlags; -use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; -use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::WorkerExecutionMode; -pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; - -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::error::JsError; -use deno_core::futures::FutureExt; -use deno_core::unsync::JoinHandle; -use deno_npm::resolution::SnapshotFromLockfileError; -use deno_runtime::fmt_errors::format_js_error; -use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; -use deno_terminal::colors; -use factory::CliFactory; -use standalone::MODULE_NOT_FOUND; -use standalone::UNSUPPORTED_SCHEME; use std::env; use std::future::Future; use std::io::IsTerminal; @@ -60,6 +35,31 @@ use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; +use args::TaskFlags; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::error::JsError; +use deno_core::futures::FutureExt; +use deno_core::unsync::JoinHandle; +use deno_npm::resolution::SnapshotFromLockfileError; +use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; +use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; +use deno_runtime::fmt_errors::format_js_error; +use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; +use deno_runtime::WorkerExecutionMode; +pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; +use deno_terminal::colors; +use factory::CliFactory; +use standalone::MODULE_NOT_FOUND; +use standalone::UNSUPPORTED_SCHEME; + +use crate::args::flags_from_vec; +use crate::args::DenoSubcommand; +use crate::args::Flags; +use crate::util::display; +use crate::util::v8::get_v8_flags_from_env; +use crate::util::v8::init_v8_flags; + #[cfg(feature = "dhat-heap")] #[global_allocator] static ALLOC: dhat::Alloc = dhat::Alloc; diff --git a/cli/mainrt.rs b/cli/mainrt.rs index 2b767ea89c..19e010fb53 100644 --- a/cli/mainrt.rs +++ b/cli/mainrt.rs @@ -24,6 +24,12 @@ mod util; mod version; mod worker; +use std::borrow::Cow; +use std::collections::HashMap; +use std::env; +use std::env::current_exe; +use std::sync::Arc; + use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::error::JsError; @@ -34,12 +40,6 @@ use deno_terminal::colors; use indexmap::IndexMap; use standalone::DenoCompileFileSystem; -use std::borrow::Cow; -use std::collections::HashMap; -use std::env; -use std::env::current_exe; -use std::sync::Arc; - use crate::args::Flags; pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) { diff --git a/cli/module_loader.rs b/cli/module_loader.rs index ea40dbe609..c2720ac5ad 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -11,8 +11,6 @@ use std::sync::atomic::AtomicU16; use std::sync::atomic::Ordering; use std::sync::Arc; -use crate::node::CliNodeResolver; -use crate::sys::CliSys; use deno_ast::MediaType; use deno_ast::ModuleKind; use deno_core::anyhow::anyhow; @@ -66,6 +64,7 @@ use crate::graph_container::ModuleGraphUpdatePermit; use crate::graph_util::CreateGraphOptions; use crate::graph_util::ModuleGraphBuilder; use crate::node::CliNodeCodeTranslator; +use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; use crate::resolver::CjsTracker; use crate::resolver::CliNpmReqResolver; @@ -73,6 +72,7 @@ use crate::resolver::CliResolver; use crate::resolver::ModuleCodeStringSource; use crate::resolver::NotSupportedKindInNpmError; use crate::resolver::NpmModuleLoader; +use crate::sys::CliSys; use crate::tools::check; use crate::tools::check::TypeChecker; use crate::util::progress_bar::ProgressBar; @@ -1161,9 +1161,10 @@ impl NodeRequireLoader #[cfg(test)] mod tests { - use super::*; use deno_graph::ParsedSourceStore; + use super::*; + #[tokio::test] async fn test_inflight_module_loads_tracker() { let tracker = InFlightModuleLoadsTracker { diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index ca89a7399e..b8feb52f39 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -4,7 +4,6 @@ use std::borrow::Cow; use std::path::Path; use std::sync::Arc; -use crate::sys::CliSys; use deno_core::error::AnyError; use deno_core::serde_json; use deno_resolver::npm::ByonmNpmResolver; @@ -14,11 +13,11 @@ use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use node_resolver::NpmPackageFolderResolver; -use crate::args::NpmProcessState; -use crate::args::NpmProcessStateKind; - use super::CliNpmResolver; use super::InnerCliNpmResolverRef; +use crate::args::NpmProcessState; +use crate::args::NpmProcessStateKind; +use crate::sys::CliSys; pub type CliByonmNpmResolverCreateOptions = ByonmNpmResolverCreateOptions; diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 97a87dd9b8..0422445e62 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -5,7 +5,6 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use crate::sys::CliSys; use deno_ast::ModuleSpecifier; use deno_cache_dir::npm::NpmCacheDir; use deno_core::anyhow::Context; @@ -35,20 +34,9 @@ use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; use resolution::AddPkgReqsResult; -use crate::args::CliLockfile; -use crate::args::LifecycleScriptsConfig; -use crate::args::NpmInstallDepsProvider; -use crate::args::NpmProcessState; -use crate::args::NpmProcessStateKind; -use crate::args::PackageJsonDepValueParseWithLocationError; -use crate::cache::FastInsecureHasher; -use crate::util::progress_bar::ProgressBar; -use crate::util::sync::AtomicFlag; - use self::resolution::NpmResolution; use self::resolvers::create_npm_fs_resolver; use self::resolvers::NpmPackageFsResolver; - use super::CliNpmCache; use super::CliNpmCacheHttpClient; use super::CliNpmRegistryInfoProvider; @@ -56,6 +44,16 @@ use super::CliNpmResolver; use super::CliNpmTarballCache; use super::InnerCliNpmResolverRef; use super::ResolvePkgFolderFromDenoReqError; +use crate::args::CliLockfile; +use crate::args::LifecycleScriptsConfig; +use crate::args::NpmInstallDepsProvider; +use crate::args::NpmProcessState; +use crate::args::NpmProcessStateKind; +use crate::args::PackageJsonDepValueParseWithLocationError; +use crate::cache::FastInsecureHasher; +use crate::sys::CliSys; +use crate::util::progress_bar::ProgressBar; +use crate::util::sync::AtomicFlag; mod resolution; mod resolvers; diff --git a/cli/npm/managed/resolvers/common/bin_entries.rs b/cli/npm/managed/resolvers/common/bin_entries.rs index ca47b9a086..5f203a2ba0 100644 --- a/cli/npm/managed/resolvers/common/bin_entries.rs +++ b/cli/npm/managed/resolvers/common/bin_entries.rs @@ -1,16 +1,18 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::npm::managed::NpmResolutionPackage; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_npm::resolution::NpmResolutionSnapshot; -use deno_npm::NpmPackageId; use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; use std::path::Path; use std::path::PathBuf; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::NpmPackageId; + +use crate::npm::managed::NpmResolutionPackage; + #[derive(Default)] pub struct BinEntries<'a> { /// Packages that have colliding bin names diff --git a/cli/npm/managed/resolvers/common/lifecycle_scripts.rs b/cli/npm/managed/resolvers/common/lifecycle_scripts.rs index 958c4bcd19..c6527b1458 100644 --- a/cli/npm/managed/resolvers/common/lifecycle_scripts.rs +++ b/cli/npm/managed/resolvers/common/lifecycle_scripts.rs @@ -1,24 +1,24 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::collections::HashSet; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; + +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::NpmResolutionPackage; +use deno_runtime::deno_io::FromRawIoHandle; +use deno_semver::package::PackageNv; +use deno_semver::Version; +use deno_task_shell::KillSignal; + use super::bin_entries::BinEntries; use crate::args::LifecycleScriptsConfig; use crate::task_runner::TaskStdio; use crate::util::progress_bar::ProgressBar; -use deno_core::anyhow::Context; -use deno_npm::resolution::NpmResolutionSnapshot; -use deno_runtime::deno_io::FromRawIoHandle; -use deno_semver::package::PackageNv; -use deno_semver::Version; -use deno_task_shell::KillSignal; -use std::borrow::Cow; -use std::collections::HashSet; -use std::rc::Rc; - -use std::path::Path; -use std::path::PathBuf; - -use deno_core::error::AnyError; -use deno_npm::NpmResolutionPackage; pub trait LifecycleScriptsStrategy { fn can_run_scripts(&self) -> bool { diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs index 77e0d0ea3e..44157464b8 100644 --- a/cli/npm/managed/resolvers/global.rs +++ b/cli/npm/managed/resolvers/global.rs @@ -7,11 +7,6 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use crate::colors; -use crate::npm::managed::PackageCaching; -use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; -use crate::sys::CliSys; use async_trait::async_trait; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; @@ -24,14 +19,18 @@ use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::ReferrerNotFoundError; -use crate::args::LifecycleScriptsConfig; -use crate::cache::FastInsecureHasher; - use super::super::resolution::NpmResolution; use super::common::cache_packages; use super::common::lifecycle_scripts::LifecycleScriptsStrategy; use super::common::NpmPackageFsResolver; use super::common::RegistryReadPermissionChecker; +use crate::args::LifecycleScriptsConfig; +use crate::cache::FastInsecureHasher; +use crate::colors; +use crate::npm::managed::PackageCaching; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; /// Resolves packages from the global npm cache. #[derive(Debug)] diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 5c93c228e8..bff881d5f9 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -43,6 +43,10 @@ use serde::Deserialize; use serde::Serialize; use sys_traits::FsMetadata; +use super::super::resolution::NpmResolution; +use super::common::bin_entries; +use super::common::NpmPackageFsResolver; +use super::common::RegistryReadPermissionChecker; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; use crate::cache::CACHE_PERM; @@ -57,11 +61,6 @@ use crate::util::fs::LaxSingleProcessFsFlag; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressMessagePrompt; -use super::super::resolution::NpmResolution; -use super::common::bin_entries; -use super::common::NpmPackageFsResolver; -use super::common::RegistryReadPermissionChecker; - /// Resolver that creates a local node_modules directory /// and resolves packages from it. #[derive(Debug)] diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs index c2fc8d2d92..3d3fc2e3f2 100644 --- a/cli/npm/managed/resolvers/mod.rs +++ b/cli/npm/managed/resolvers/mod.rs @@ -7,22 +7,19 @@ mod local; use std::path::PathBuf; use std::sync::Arc; -use crate::sys::CliSys; use deno_npm::NpmSystemInfo; +pub use self::common::NpmPackageFsResolver; +use self::global::GlobalNpmPackageResolver; +use self::local::LocalNpmPackageResolver; +use super::resolution::NpmResolution; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; use crate::npm::CliNpmCache; use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; -pub use self::common::NpmPackageFsResolver; - -use self::global::GlobalNpmPackageResolver; -use self::local::LocalNpmPackageResolver; - -use super::resolution::NpmResolution; - #[allow(clippy::too_many_arguments)] pub fn create_npm_fs_resolver( npm_cache: Arc, diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 34eaf21419..c8a6002c4f 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -7,7 +7,6 @@ use std::borrow::Cow; use std::path::Path; use std::sync::Arc; -use crate::sys::CliSys; use dashmap::DashMap; use deno_core::error::AnyError; use deno_core::serde_json; @@ -28,10 +27,6 @@ use managed::create_managed_in_npm_pkg_checker; use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; -use crate::file_fetcher::CliFileFetcher; -use crate::http_util::HttpClientProvider; -use crate::util::progress_bar::ProgressBar; - pub use self::byonm::CliByonmNpmResolver; pub use self::byonm::CliByonmNpmResolverCreateOptions; pub use self::managed::CliManagedInNpmPkgCheckerCreateOptions; @@ -39,6 +34,10 @@ pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::ManagedCliNpmResolver; pub use self::managed::PackageCaching; +use crate::file_fetcher::CliFileFetcher; +use crate::http_util::HttpClientProvider; +use crate::sys::CliSys; +use crate::util::progress_bar::ProgressBar; pub type CliNpmTarballCache = deno_npm_cache::TarballCache; diff --git a/cli/ops/jupyter.rs b/cli/ops/jupyter.rs index 5bdf97e60f..da6fa657d2 100644 --- a/cli/ops/jupyter.rs +++ b/cli/ops/jupyter.rs @@ -8,17 +8,16 @@ use std::cell::RefCell; use std::rc::Rc; use std::sync::Arc; -use jupyter_runtime::InputRequest; -use jupyter_runtime::JupyterMessage; -use jupyter_runtime::JupyterMessageContent; -use jupyter_runtime::KernelIoPubConnection; -use jupyter_runtime::StreamContent; - use deno_core::error::AnyError; use deno_core::op2; use deno_core::parking_lot::Mutex; use deno_core::serde_json; use deno_core::OpState; +use jupyter_runtime::InputRequest; +use jupyter_runtime::JupyterMessage; +use jupyter_runtime::JupyterMessageContent; +use jupyter_runtime::KernelIoPubConnection; +use jupyter_runtime::StreamContent; use tokio::sync::mpsc; use crate::tools::jupyter::server::StdinConnectionProxy; diff --git a/cli/ops/testing.rs b/cli/ops/testing.rs index 3c6936971a..bc96e41650 100644 --- a/cli/ops/testing.rs +++ b/cli/ops/testing.rs @@ -1,13 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::tools::test::TestContainer; -use crate::tools::test::TestDescription; -use crate::tools::test::TestEvent; -use crate::tools::test::TestEventSender; -use crate::tools::test::TestFailure; -use crate::tools::test::TestLocation; -use crate::tools::test::TestStepDescription; -use crate::tools::test::TestStepResult; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; use deno_core::error::generic_error; use deno_core::error::type_error; @@ -18,10 +12,17 @@ use deno_core::ModuleSpecifier; use deno_core::OpState; use deno_runtime::deno_permissions::ChildPermissionsArg; use deno_runtime::deno_permissions::PermissionsContainer; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; use uuid::Uuid; +use crate::tools::test::TestContainer; +use crate::tools::test::TestDescription; +use crate::tools::test::TestEvent; +use crate::tools::test::TestEventSender; +use crate::tools::test::TestFailure; +use crate::tools::test::TestLocation; +use crate::tools::test::TestStepDescription; +use crate::tools::test::TestStepResult; + deno_core::extension!(deno_test, ops = [ op_pledge_test_permissions, diff --git a/cli/resolver.rs b/cli/resolver.rs index c4c8ef8b36..93c07bdbc3 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -5,7 +5,6 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use crate::sys::CliSys; use async_trait::async_trait; use dashmap::DashMap; use dashmap::DashSet; @@ -40,6 +39,7 @@ use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::node::CliNodeCodeTranslator; use crate::npm::CliNpmResolver; use crate::npm::InnerCliNpmResolverRef; +use crate::sys::CliSys; use crate::util::sync::AtomicFlag; use crate::util::text_encoding::from_utf8_lossy_cow; diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 48af787f18..5277b3d523 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -61,6 +61,23 @@ use log::Level; use serde::Deserialize; use serde::Serialize; +use super::file_system::DenoCompileFileSystem; +use super::serialization::deserialize_binary_data_section; +use super::serialization::serialize_binary_data_section; +use super::serialization::DenoCompileModuleData; +use super::serialization::DeserializedDataSection; +use super::serialization::RemoteModulesStore; +use super::serialization::RemoteModulesStoreBuilder; +use super::serialization::SourceMapStore; +use super::virtual_fs::output_vfs; +use super::virtual_fs::BuiltVfs; +use super::virtual_fs::FileBackedVfs; +use super::virtual_fs::VfsBuilder; +use super::virtual_fs::VfsFileSubDataKind; +use super::virtual_fs::VfsRoot; +use super::virtual_fs::VirtualDirectory; +use super::virtual_fs::VirtualDirectoryEntries; +use super::virtual_fs::WindowsSystemRootablePath; use crate::args::CaData; use crate::args::CliOptions; use crate::args::CompileFlags; @@ -83,24 +100,6 @@ use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; -use super::file_system::DenoCompileFileSystem; -use super::serialization::deserialize_binary_data_section; -use super::serialization::serialize_binary_data_section; -use super::serialization::DenoCompileModuleData; -use super::serialization::DeserializedDataSection; -use super::serialization::RemoteModulesStore; -use super::serialization::RemoteModulesStoreBuilder; -use super::serialization::SourceMapStore; -use super::virtual_fs::output_vfs; -use super::virtual_fs::BuiltVfs; -use super::virtual_fs::FileBackedVfs; -use super::virtual_fs::VfsBuilder; -use super::virtual_fs::VfsFileSubDataKind; -use super::virtual_fs::VfsRoot; -use super::virtual_fs::VirtualDirectory; -use super::virtual_fs::VirtualDirectoryEntries; -use super::virtual_fs::WindowsSystemRootablePath; - pub static DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME: &str = ".deno_compile_node_modules"; diff --git a/cli/standalone/code_cache.rs b/cli/standalone/code_cache.rs index ec89c3ab1b..0abec9ba9e 100644 --- a/cli/standalone/code_cache.rs +++ b/cli/standalone/code_cache.rs @@ -395,10 +395,11 @@ fn deserialize_with_reader( #[cfg(test)] mod test { + use std::fs::File; + use test_util::TempDir; use super::*; - use std::fs::File; #[test] fn serialize_deserialize() { diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 0fe6de0b9a..7ce06b309c 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -5,6 +5,10 @@ #![allow(dead_code)] #![allow(unused_imports)] +use std::borrow::Cow; +use std::rc::Rc; +use std::sync::Arc; + use binary::StandaloneData; use binary::StandaloneModules; use code_cache::DenoCompileCodeCache; @@ -57,9 +61,6 @@ use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use serialization::DenoCompileModuleSource; use serialization::SourceMapStore; -use std::borrow::Cow; -use std::rc::Rc; -use std::sync::Arc; use virtual_fs::FileBackedVfs; use virtual_fs::VfsFileSubDataKind; @@ -107,12 +108,12 @@ mod file_system; mod serialization; mod virtual_fs; -pub use self::file_system::DenoCompileFileSystem; pub use binary::extract_standalone; pub use binary::is_standalone_binary; pub use binary::DenoCompileBinaryWriter; use self::binary::Metadata; +pub use self::file_system::DenoCompileFileSystem; struct SharedModuleLoaderState { cjs_tracker: Arc, diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index 30802aa081..0f4d2dd9f4 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -25,12 +25,11 @@ use deno_semver::package::PackageReq; use deno_semver::StackString; use indexmap::IndexMap; -use crate::standalone::virtual_fs::VirtualDirectory; - use super::binary::Metadata; use super::virtual_fs::BuiltVfs; use super::virtual_fs::VfsBuilder; use super::virtual_fs::VirtualDirectoryEntries; +use crate::standalone::virtual_fs::VirtualDirectory; const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd"; diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index 370d07a488..61e2e33347 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -34,13 +34,12 @@ use serde::Deserialize; use serde::Serialize; use thiserror::Error; +use super::binary::DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME; use crate::util; use crate::util::display::human_size; use crate::util::display::DisplayTreeNode; use crate::util::fs::canonicalize_path; -use super::binary::DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME; - #[derive(Debug, PartialEq, Eq)] pub enum WindowsSystemRootablePath { /// The root of the system above any drive letters. @@ -1563,9 +1562,10 @@ impl FileBackedVfs { #[cfg(test)] mod test { + use std::io::Write; + use console_static_text::ansi::strip_ansi_codes; use deno_io::fs::File; - use std::io::Write; use test_util::assert_contains; use test_util::TempDir; diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index 1b47c9bfb0..e1441e95cd 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -1,20 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::BenchFlags; -use crate::args::Flags; -use crate::colors; -use crate::display::write_json_to_stdout; -use crate::factory::CliFactory; -use crate::graph_util::has_graph_root_local_dependent_changed; -use crate::ops; -use crate::sys::CliSys; -use crate::tools::test::format_test_error; -use crate::tools::test::TestFilter; -use crate::util::file_watcher; -use crate::util::fs::collect_specifiers; -use crate::util::path::is_script_ext; -use crate::util::path::matches_pattern_or_exact_path; -use crate::worker::CliMainWorkerFactory; +use std::collections::HashSet; +use std::path::Path; +use std::sync::Arc; +use std::time::Duration; use deno_config::glob::WalkEntry; use deno_core::error::generic_error; @@ -39,13 +28,25 @@ use indexmap::IndexSet; use log::Level; use serde::Deserialize; use serde::Serialize; -use std::collections::HashSet; -use std::path::Path; -use std::sync::Arc; -use std::time::Duration; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedSender; +use crate::args::BenchFlags; +use crate::args::Flags; +use crate::colors; +use crate::display::write_json_to_stdout; +use crate::factory::CliFactory; +use crate::graph_util::has_graph_root_local_dependent_changed; +use crate::ops; +use crate::sys::CliSys; +use crate::tools::test::format_test_error; +use crate::tools::test::TestFilter; +use crate::util::file_watcher; +use crate::util::fs::collect_specifiers; +use crate::util::path::is_script_ext; +use crate::util::path::matches_pattern_or_exact_path; +use crate::worker::CliMainWorkerFactory; + mod mitata; mod reporters; diff --git a/cli/tools/bench/reporters.rs b/cli/tools/bench/reporters.rs index 9aabd760b3..0c13d14961 100644 --- a/cli/tools/bench/reporters.rs +++ b/cli/tools/bench/reporters.rs @@ -2,11 +2,10 @@ use serde::Serialize; +use super::*; use crate::tools::test::TestFailureFormatOptions; use crate::version; -use super::*; - pub trait BenchReporter { fn report_group_summary(&mut self); fn report_plan(&mut self, plan: &BenchPlan); diff --git a/cli/tools/clean.rs b/cli/tools/clean.rs index cdc1c51dcd..763aa11540 100644 --- a/cli/tools/clean.rs +++ b/cli/tools/clean.rs @@ -1,8 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::path::Path; + use deno_core::anyhow::Context; use deno_core::error::AnyError; -use std::path::Path; use crate::cache::DenoDir; use crate::colors; diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index cbd376bae2..243e322f55 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -1,12 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::check_warn_tsconfig; -use crate::args::CompileFlags; -use crate::args::Flags; -use crate::factory::CliFactory; -use crate::http_util::HttpClientProvider; -use crate::standalone::binary::is_standalone_binary; -use crate::standalone::binary::WriteBinOptions; +use std::collections::HashSet; +use std::collections::VecDeque; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_core::anyhow::bail; @@ -19,13 +18,15 @@ use deno_path_util::url_from_file_path; use deno_path_util::url_to_file_path; use deno_terminal::colors; use rand::Rng; -use std::collections::HashSet; -use std::collections::VecDeque; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use super::installer::infer_name_from_url; +use crate::args::check_warn_tsconfig; +use crate::args::CompileFlags; +use crate::args::Flags; +use crate::factory::CliFactory; +use crate::http_util::HttpClientProvider; +use crate::standalone::binary::is_standalone_binary; +use crate::standalone::binary::WriteBinOptions; pub async fn compile( flags: Arc, diff --git a/cli/tools/coverage/merge.rs b/cli/tools/coverage/merge.rs index 81317df559..2c69981ad1 100644 --- a/cli/tools/coverage/merge.rs +++ b/cli/tools/coverage/merge.rs @@ -3,14 +3,15 @@ // Forked from https://github.com/demurgos/v8-coverage/tree/d0ca18da8740198681e0bc68971b0a6cdb11db3e/rust // Copyright 2021 Charles Samborski. All rights reserved. MIT license. -use super::range_tree::RangeTree; -use super::range_tree::RangeTreeArena; -use crate::cdp; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::HashMap; use std::iter::Peekable; +use super::range_tree::RangeTree; +use super::range_tree::RangeTreeArena; +use crate::cdp; + #[derive(Eq, PartialEq, Clone, Debug)] pub struct ProcessCoverage { pub result: Vec, diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index a9054207b0..1a03f38c53 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -1,16 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::CliOptions; -use crate::args::CoverageFlags; -use crate::args::FileFlags; -use crate::args::Flags; -use crate::cdp; -use crate::factory::CliFactory; -use crate::file_fetcher::TextDecodedFile; -use crate::sys::CliSys; -use crate::tools::fmt::format_json; -use crate::tools::test::is_supported_test_path; -use crate::util::text_encoding::source_map_from_code; +use std::fs; +use std::fs::File; +use std::io::BufWriter; +use std::io::Write; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleKind; @@ -29,16 +25,21 @@ use deno_core::url::Url; use deno_core::LocalInspectorSession; use node_resolver::InNpmPackageChecker; use regex::Regex; -use std::fs; -use std::fs::File; -use std::io::BufWriter; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use text_lines::TextLines; use uuid::Uuid; +use crate::args::CliOptions; +use crate::args::CoverageFlags; +use crate::args::FileFlags; +use crate::args::Flags; +use crate::cdp; +use crate::factory::CliFactory; +use crate::file_fetcher::TextDecodedFile; +use crate::sys::CliSys; +use crate::tools::fmt::format_json; +use crate::tools::test::is_supported_test_path; +use crate::util::text_encoding::source_map_from_code; + mod merge; mod range_tree; mod reporter; diff --git a/cli/tools/coverage/range_tree.rs b/cli/tools/coverage/range_tree.rs index bca52844c0..8e3cd95a51 100644 --- a/cli/tools/coverage/range_tree.rs +++ b/cli/tools/coverage/range_tree.rs @@ -3,10 +3,12 @@ // Forked from https://github.com/demurgos/v8-coverage/tree/d0ca18da8740198681e0bc68971b0a6cdb11db3e/rust // Copyright 2021 Charles Samborski. All rights reserved. MIT license. -use crate::cdp; use std::iter::Peekable; + use typed_arena::Arena; +use crate::cdp; + pub struct RangeTreeArena<'a>(Arena>); impl<'a> RangeTreeArena<'a> { diff --git a/cli/tools/coverage/reporter.rs b/cli/tools/coverage/reporter.rs index 6b0e5c885e..3bdb6b25f6 100644 --- a/cli/tools/coverage/reporter.rs +++ b/cli/tools/coverage/reporter.rs @@ -1,11 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::util; -use super::CoverageReport; -use crate::args::CoverageType; -use crate::colors; -use deno_core::error::AnyError; -use deno_core::url::Url; use std::collections::HashMap; use std::fs; use std::fs::File; @@ -15,6 +9,14 @@ use std::io::{self}; use std::path::Path; use std::path::PathBuf; +use deno_core::error::AnyError; +use deno_core::url::Url; + +use super::util; +use super::CoverageReport; +use crate::args::CoverageType; +use crate::colors; + #[derive(Default)] pub struct CoverageStats<'a> { pub line_hit: usize, diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 0ff1806a9e..a22de0c9a9 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -1,18 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::DocFlags; -use crate::args::DocHtmlFlag; -use crate::args::DocSourceFileFlag; -use crate::args::Flags; -use crate::colors; -use crate::display; -use crate::factory::CliFactory; -use crate::graph_util::graph_exit_integrity_errors; -use crate::graph_util::graph_walk_errors; -use crate::graph_util::GraphWalkErrorsOptions; -use crate::sys::CliSys; -use crate::tsc::get_types_declaration_file_text; -use crate::util::fs::collect_specifiers; +use std::collections::BTreeMap; +use std::rc::Rc; +use std::sync::Arc; + use deno_ast::diagnostics::Diagnostic; use deno_config::glob::FilePatterns; use deno_config::glob::PathOrPatternSet; @@ -32,9 +23,20 @@ use deno_graph::ModuleSpecifier; use doc::html::ShortPath; use doc::DocDiagnostic; use indexmap::IndexMap; -use std::collections::BTreeMap; -use std::rc::Rc; -use std::sync::Arc; + +use crate::args::DocFlags; +use crate::args::DocHtmlFlag; +use crate::args::DocSourceFileFlag; +use crate::args::Flags; +use crate::colors; +use crate::display; +use crate::factory::CliFactory; +use crate::graph_util::graph_exit_integrity_errors; +use crate::graph_util::graph_walk_errors; +use crate::graph_util::GraphWalkErrorsOptions; +use crate::sys::CliSys; +use crate::tsc::get_types_declaration_file_text; +use crate::util::fs::collect_specifiers; const JSON_SCHEMA_VERSION: u8 = 1; diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 7f9a15f4b2..74a85aaa09 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -7,21 +7,18 @@ //! the future it can be easily extended to provide //! the same functions as ops available in JS runtime. -use crate::args::CliOptions; -use crate::args::Flags; -use crate::args::FmtFlags; -use crate::args::FmtOptions; -use crate::args::FmtOptionsConfig; -use crate::args::ProseWrap; -use crate::args::UnstableFmtOptions; -use crate::cache::Caches; -use crate::colors; -use crate::factory::CliFactory; -use crate::sys::CliSys; -use crate::util::diff::diff; -use crate::util::file_watcher; -use crate::util::fs::canonicalize_path; -use crate::util::path::get_extension; +use std::borrow::Cow; +use std::fs; +use std::io::stdin; +use std::io::stdout; +use std::io::Read; +use std::io::Write; +use std::path::Path; +use std::path::PathBuf; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +use std::sync::Arc; + use async_trait::async_trait; use deno_ast::ParsedSource; use deno_config::glob::FileCollector; @@ -38,19 +35,23 @@ use deno_core::url::Url; use log::debug; use log::info; use log::warn; -use std::borrow::Cow; -use std::fs; -use std::io::stdin; -use std::io::stdout; -use std::io::Read; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; +use crate::args::CliOptions; +use crate::args::Flags; +use crate::args::FmtFlags; +use crate::args::FmtOptions; +use crate::args::FmtOptionsConfig; +use crate::args::ProseWrap; +use crate::args::UnstableFmtOptions; +use crate::cache::Caches; use crate::cache::IncrementalCache; +use crate::colors; +use crate::factory::CliFactory; +use crate::sys::CliSys; +use crate::util::diff::diff; +use crate::util::file_watcher; +use crate::util::fs::canonicalize_path; +use crate::util::path::get_extension; /// Format JavaScript/TypeScript files. pub async fn format( diff --git a/cli/tools/init/mod.rs b/cli/tools/init/mod.rs index 36bdbac2bc..ec25a051bb 100644 --- a/cli/tools/init/mod.rs +++ b/cli/tools/init/mod.rs @@ -1,12 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::DenoSubcommand; -use crate::args::Flags; -use crate::args::InitFlags; -use crate::args::PackagesAllowedScripts; -use crate::args::PermissionFlags; -use crate::args::RunFlags; -use crate::colors; +use std::io::IsTerminal; +use std::io::Write; +use std::path::Path; + use color_print::cformat; use color_print::cstr; use deno_config::deno_json::NodeModulesDirMode; @@ -15,9 +12,14 @@ use deno_core::error::AnyError; use deno_core::serde_json::json; use deno_runtime::WorkerExecutionMode; use log::info; -use std::io::IsTerminal; -use std::io::Write; -use std::path::Path; + +use crate::args::DenoSubcommand; +use crate::args::Flags; +use crate::args::InitFlags; +use crate::args::PackagesAllowedScripts; +use crate::args::PermissionFlags; +use crate::args::RunFlags; +use crate::colors; pub async fn init_project(init_flags: InitFlags) -> Result { if let Some(package) = &init_flags.package { diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 1bfd17f30d..12d650d98e 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -1,5 +1,29 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::env; +use std::fs; +use std::fs::File; +use std::io; +use std::io::Write; +#[cfg(not(windows))] +use std::os::unix::fs::PermissionsExt; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_cache_dir::file_fetcher::CacheSetting; +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::generic_error; +use deno_core::error::AnyError; +use deno_core::resolve_url_or_path; +use deno_core::url::Url; +use deno_semver::npm::NpmPackageReqReference; +use log::Level; +use once_cell::sync::Lazy; +use regex::Regex; +use regex::RegexBuilder; + use crate::args::resolve_no_prompt; use crate::args::AddFlags; use crate::args::CaData; @@ -19,30 +43,6 @@ use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; use crate::util::fs::canonicalize_path_maybe_not_exists; -use deno_cache_dir::file_fetcher::CacheSetting; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::generic_error; -use deno_core::error::AnyError; -use deno_core::resolve_url_or_path; -use deno_core::url::Url; -use deno_semver::npm::NpmPackageReqReference; -use log::Level; -use once_cell::sync::Lazy; -use regex::Regex; -use regex::RegexBuilder; -use std::env; -use std::fs; -use std::fs::File; -use std::io; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; - -#[cfg(not(windows))] -use std::os::unix::fs::PermissionsExt; -use std::sync::Arc; - static EXEC_NAME_RE: Lazy = Lazy::new(|| { RegexBuilder::new(r"^[a-z0-9][\w-]*$") .case_insensitive(true) @@ -659,16 +659,17 @@ fn is_in_path(dir: &Path) -> bool { #[cfg(test)] mod tests { - use super::*; + use std::process::Command; + use test_util::testdata_path; + use test_util::TempDir; + + use super::*; use crate::args::ConfigFlag; use crate::args::PermissionFlags; use crate::args::UninstallFlagsGlobal; use crate::args::UnstableConfig; use crate::util::fs::canonicalize_path; - use std::process::Command; - use test_util::testdata_path; - use test_util::TempDir; #[tokio::test] async fn install_infer_name_from_url() { diff --git a/cli/tools/jupyter/install.rs b/cli/tools/jupyter/install.rs index aeff89ccf4..40159a35e0 100644 --- a/cli/tools/jupyter/install.rs +++ b/cli/tools/jupyter/install.rs @@ -1,12 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::serde_json::json; use std::env::current_exe; use std::io::Write; use std::path::Path; +use deno_core::error::AnyError; +use deno_core::serde_json; +use deno_core::serde_json::json; use jupyter_runtime::dirs::user_data_dir; const DENO_ICON_32: &[u8] = include_bytes!("./resources/deno-logo-32x32.png"); diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs index 732f95c49f..7f11be36d4 100644 --- a/cli/tools/jupyter/mod.rs +++ b/cli/tools/jupyter/mod.rs @@ -2,17 +2,6 @@ use std::sync::Arc; -use crate::args::Flags; -use crate::args::JupyterFlags; -use crate::cdp; -use crate::lsp::ReplCompletionItem; -use crate::ops; -use crate::tools::repl; -use crate::tools::test::create_single_test_event_channel; -use crate::tools::test::reporters::PrettyTestReporter; -use crate::tools::test::TestEventWorkerSender; -use crate::tools::test::TestFailureFormatOptions; -use crate::CliFactory; use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::generic_error; @@ -34,6 +23,18 @@ use tokio::sync::mpsc; use tokio::sync::mpsc::UnboundedSender; use tokio::sync::oneshot; +use crate::args::Flags; +use crate::args::JupyterFlags; +use crate::cdp; +use crate::lsp::ReplCompletionItem; +use crate::ops; +use crate::tools::repl; +use crate::tools::test::create_single_test_event_channel; +use crate::tools::test::reporters::PrettyTestReporter; +use crate::tools::test::TestEventWorkerSender; +use crate::tools::test::TestFailureFormatOptions; +use crate::CliFactory; + mod install; pub mod server; diff --git a/cli/tools/jupyter/server.rs b/cli/tools/jupyter/server.rs index 5680ed4c13..6c6d076d01 100644 --- a/cli/tools/jupyter/server.rs +++ b/cli/tools/jupyter/server.rs @@ -11,8 +11,6 @@ use std::collections::HashMap; use std::rc::Rc; use std::sync::Arc; -use crate::cdp; -use crate::tools::repl; use deno_core::anyhow::bail; use deno_core::error::AnyError; use deno_core::futures; @@ -20,12 +18,9 @@ use deno_core::parking_lot::Mutex; use deno_core::serde_json; use deno_core::CancelFuture; use deno_core::CancelHandle; -use jupyter_runtime::ExecutionCount; -use tokio::sync::mpsc; -use tokio::sync::oneshot; - use jupyter_runtime::messaging; use jupyter_runtime::ConnectionInfo; +use jupyter_runtime::ExecutionCount; use jupyter_runtime::JupyterMessage; use jupyter_runtime::JupyterMessageContent; use jupyter_runtime::KernelControlConnection; @@ -34,9 +29,13 @@ use jupyter_runtime::KernelShellConnection; use jupyter_runtime::ReplyError; use jupyter_runtime::ReplyStatus; use jupyter_runtime::StreamContent; +use tokio::sync::mpsc; +use tokio::sync::oneshot; use uuid::Uuid; use super::JupyterReplProxy; +use crate::cdp; +use crate::tools::repl; pub struct JupyterServer { execution_count: ExecutionCount, diff --git a/cli/tools/lint/linter.rs b/cli/tools/lint/linter.rs index 6bb3c628fa..0537765a0a 100644 --- a/cli/tools/lint/linter.rs +++ b/cli/tools/lint/linter.rs @@ -17,12 +17,11 @@ use deno_lint::linter::Linter as DenoLintLinter; use deno_lint::linter::LinterOptions; use deno_path_util::fs::atomic_write_file_with_retries; -use crate::sys::CliSys; -use crate::util::fs::specifier_from_file_path; - use super::rules::FileOrPackageLintRule; use super::rules::PackageLintRule; use super::ConfiguredRules; +use crate::sys::CliSys; +use crate::util::fs::specifier_from_file_path; pub struct CliLinterOptions { pub configured_rules: ConfiguredRules, diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index 6d3997ac3b..cefc1d6e66 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -3,6 +3,14 @@ //! This module provides file linting utilities using //! [`deno_lint`](https://github.com/denoland/deno_lint). +use std::collections::HashSet; +use std::fs; +use std::io::stdin; +use std::io::Read; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + use deno_ast::ModuleSpecifier; use deno_ast::ParsedSource; use deno_config::deno_json::LintRulesConfig; @@ -25,13 +33,6 @@ use log::debug; use reporters::create_reporter; use reporters::LintReporter; use serde::Serialize; -use std::collections::HashSet; -use std::fs; -use std::io::stdin; -use std::io::Read; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; use crate::args::CliOptions; use crate::args::Flags; @@ -596,11 +597,12 @@ struct LintError { #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; use serde::Deserialize; use test_util as util; + use super::*; + #[derive(Serialize, Deserialize)] struct RulesSchema { #[serde(rename = "$schema")] diff --git a/cli/tools/lint/reporters.rs b/cli/tools/lint/reporters.rs index 18bc1216a6..0e93f4570f 100644 --- a/cli/tools/lint/reporters.rs +++ b/cli/tools/lint/reporters.rs @@ -8,9 +8,8 @@ use deno_runtime::colors; use log::info; use serde::Serialize; -use crate::args::LintReporterKind; - use super::LintError; +use crate::args::LintReporterKind; const JSON_SCHEMA_VERSION: u8 = 1; diff --git a/cli/tools/lint/rules/no_sloppy_imports.rs b/cli/tools/lint/rules/no_sloppy_imports.rs index 1bf7eddf6e..fc4641df12 100644 --- a/cli/tools/lint/rules/no_sloppy_imports.rs +++ b/cli/tools/lint/rules/no_sloppy_imports.rs @@ -20,11 +20,10 @@ use deno_resolver::sloppy_imports::SloppyImportsResolution; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; use text_lines::LineAndColumnIndex; +use super::ExtendedLintRule; use crate::graph_util::CliJsrUrlProvider; use crate::resolver::CliSloppyImportsResolver; -use super::ExtendedLintRule; - #[derive(Debug)] pub struct NoSloppyImportsRule { sloppy_imports_resolver: Option>, diff --git a/cli/tools/registry/api.rs b/cli/tools/registry/api.rs index 2f27cb2fea..623c9bee3c 100644 --- a/cli/tools/registry/api.rs +++ b/cli/tools/registry/api.rs @@ -1,12 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::http_util; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; use deno_runtime::deno_fetch; use serde::de::DeserializeOwned; +use crate::http_util; use crate::http_util::HttpClient; #[derive(serde::Deserialize)] diff --git a/cli/tools/registry/graph.rs b/cli/tools/registry/graph.rs index 21962d009e..e9cf01d710 100644 --- a/cli/tools/registry/graph.rs +++ b/cli/tools/registry/graph.rs @@ -16,10 +16,9 @@ use deno_graph::WalkOptions; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; -use crate::cache::ParsedSourceCache; - use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; +use crate::cache::ParsedSourceCache; pub struct GraphDiagnosticsCollector { parsed_source_cache: Arc, diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index 45a040d236..e3edc51d39 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -73,11 +73,10 @@ pub use pm::AddRmPackageReq; use publish_order::PublishOrderGraph; use unfurl::SpecifierUnfurler; -use super::check::TypeChecker; - use self::graph::GraphDiagnosticsCollector; use self::paths::CollectedPublishPath; use self::tar::PublishableTarball; +use super::check::TypeChecker; pub async fn publish( flags: Arc, @@ -1281,14 +1280,14 @@ fn ring_bell() { #[cfg(test)] mod tests { - use deno_ast::ModuleSpecifier; + use std::collections::HashMap; - use crate::tools::registry::has_license_file; + use deno_ast::ModuleSpecifier; use super::tar::PublishableTarball; use super::tar::PublishableTarballFile; use super::verify_version_manifest; - use std::collections::HashMap; + use crate::tools::registry::has_license_file; #[test] fn test_verify_version_manifest() { diff --git a/cli/tools/registry/paths.rs b/cli/tools/registry/paths.rs index 1c675982df..02b143b620 100644 --- a/cli/tools/registry/paths.rs +++ b/cli/tools/registry/paths.rs @@ -6,7 +6,6 @@ use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; -use crate::sys::CliSys; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_config::glob::FileCollector; @@ -14,10 +13,10 @@ use deno_config::glob::FilePatterns; use deno_core::error::AnyError; use thiserror::Error; -use crate::args::CliOptions; - use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; +use crate::args::CliOptions; +use crate::sys::CliSys; /// A package path, like '/foo' or '/foo/bar'. The path is prefixed with a slash /// and does not end with a slash. diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index 814c76cb27..d41c5c6b8d 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -3,15 +3,16 @@ use std::borrow::Cow; use std::sync::Arc; -use crate::factory::CliFactory; -use crate::graph_container::ModuleGraphContainer; -use crate::graph_container::ModuleGraphUpdatePermit; -use crate::graph_util::CreateGraphOptions; use deno_core::error::AnyError; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::StreamExt; use deno_semver::jsr::JsrPackageReqReference; +use crate::factory::CliFactory; +use crate::graph_container::ModuleGraphContainer; +use crate::graph_container::ModuleGraphUpdatePermit; +use crate::graph_util::CreateGraphOptions; + pub async fn cache_top_level_deps( // todo(dsherret): don't pass the factory into this function. Instead use ctor deps factory: &CliFactory, diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index ffa53417e9..6f92a9a47f 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -35,6 +35,7 @@ use import_map::ImportMapWithDiagnostics; use import_map::SpecifierMapEntry; use tokio::sync::Semaphore; +use super::ConfigUpdater; use crate::args::CliLockfile; use crate::graph_container::MainModuleGraphContainer; use crate::graph_container::ModuleGraphContainer; @@ -45,8 +46,6 @@ use crate::npm::CliNpmResolver; use crate::npm::NpmFetchResolver; use crate::util::sync::AtomicFlag; -use super::ConfigUpdater; - #[derive(Clone, Debug, PartialEq, Eq)] pub enum ImportMapKind { Inline, diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index bb4c60fde8..2885a58e1b 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -12,6 +12,10 @@ use deno_semver::StackString; use deno_semver::VersionReq; use deno_terminal::colors; +use super::deps::Dep; +use super::deps::DepManager; +use super::deps::DepManagerArgs; +use super::deps::PackageLatestVersion; use crate::args::CliOptions; use crate::args::Flags; use crate::args::OutdatedFlags; @@ -21,11 +25,6 @@ use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; use crate::tools::registry::pm::deps::DepKind; -use super::deps::Dep; -use super::deps::DepManager; -use super::deps::DepManagerArgs; -use super::deps::PackageLatestVersion; - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] struct OutdatedPackage { kind: DepKind, diff --git a/cli/tools/registry/provenance.rs b/cli/tools/registry/provenance.rs index 47169f2132..72e1dff80e 100644 --- a/cli/tools/registry/provenance.rs +++ b/cli/tools/registry/provenance.rs @@ -1,11 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::http_util; -use crate::http_util::HttpClient; +use std::collections::HashMap; +use std::env; -use super::api::OidcTokenResponse; -use super::auth::gha_oidc_token; -use super::auth::is_gha; use base64::engine::general_purpose::STANDARD_NO_PAD; use base64::prelude::BASE64_STANDARD; use base64::Engine as _; @@ -27,8 +24,12 @@ use sha2::Digest; use spki::der::asn1; use spki::der::pem::LineEnding; use spki::der::EncodePem; -use std::collections::HashMap; -use std::env; + +use super::api::OidcTokenResponse; +use super::auth::gha_oidc_token; +use super::auth::is_gha; +use crate::http_util; +use crate::http_util::HttpClient; const PAE_PREFIX: &str = "DSSEv1"; @@ -706,10 +707,11 @@ async fn testify( #[cfg(test)] mod tests { + use std::env; + use super::ProvenanceAttestation; use super::Subject; use super::SubjectDigest; - use std::env; #[test] fn slsa_github_actions() { diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs index 6d1801ce69..34d0ed0832 100644 --- a/cli/tools/registry/tar.rs +++ b/cli/tools/registry/tar.rs @@ -1,22 +1,22 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::fmt::Write as FmtWrite; +use std::io::Write; +use std::path::Path; + use bytes::Bytes; use deno_ast::MediaType; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::url::Url; use sha2::Digest; -use std::fmt::Write as FmtWrite; -use std::io::Write; -use std::path::Path; use tar::Header; -use crate::cache::LazyGraphSourceParser; - use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; use super::paths::CollectedPublishPath; use super::unfurl::SpecifierUnfurler; +use crate::cache::LazyGraphSourceParser; #[derive(Debug, Clone, PartialEq)] pub struct PublishableTarballFile { diff --git a/cli/tools/registry/unfurl.rs b/cli/tools/registry/unfurl.rs index 989a6e1ed4..e02dcd62d9 100644 --- a/cli/tools/registry/unfurl.rs +++ b/cli/tools/registry/unfurl.rs @@ -655,10 +655,6 @@ fn to_range( mod tests { use std::sync::Arc; - use crate::resolver::SloppyImportsCachedFs; - - use super::*; - use crate::sys::CliSys; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_config::workspace::ResolverWorkspaceJsrPackage; @@ -671,6 +667,10 @@ mod tests { use pretty_assertions::assert_eq; use test_util::testdata_path; + use super::*; + use crate::resolver::SloppyImportsCachedFs; + use crate::sys::CliSys; + fn parse_ast(specifier: &Url, source_code: &str) -> ParsedSource { let media_type = MediaType::from_specifier(specifier); deno_ast::parse_module(deno_ast::ParseParams { diff --git a/cli/tools/repl/channel.rs b/cli/tools/repl/channel.rs index 823a13d288..a32b48f614 100644 --- a/cli/tools/repl/channel.rs +++ b/cli/tools/repl/channel.rs @@ -1,10 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::cell::RefCell; + use deno_core::anyhow::anyhow; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::Value; -use std::cell::RefCell; use tokio::sync::mpsc::channel; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::Receiver; diff --git a/cli/tools/repl/editor.rs b/cli/tools/repl/editor.rs index dbc9bce703..95df2c02a0 100644 --- a/cli/tools/repl/editor.rs +++ b/cli/tools/repl/editor.rs @@ -1,7 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::cdp; -use crate::colors; +use std::borrow::Cow; +use std::path::PathBuf; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::Arc; + use deno_ast::swc::parser::error::SyntaxError; use deno_ast::swc::parser::token::BinOpToken; use deno_ast::swc::parser::token::Token; @@ -32,14 +36,11 @@ use rustyline::Modifiers; use rustyline::RepeatCount; use rustyline_derive::Helper; use rustyline_derive::Hinter; -use std::borrow::Cow; -use std::path::PathBuf; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering::Relaxed; -use std::sync::Arc; use super::channel::RustylineSyncMessageSender; use super::session::REPL_INTERNALS_NAME; +use crate::cdp; +use crate::colors; // Provides helpers to the editor like validation for multi-line edits, completion candidates for // tab completion. diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index 9fb4624fa4..eacafa2160 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -2,9 +2,15 @@ use std::io; use std::io::Write; - use std::sync::Arc; +use deno_core::error::AnyError; +use deno_core::futures::StreamExt; +use deno_core::serde_json; +use deno_core::unsync::spawn_blocking; +use deno_runtime::WorkerExecutionMode; +use rustyline::error::ReadlineError; + use crate::args::CliOptions; use crate::args::Flags; use crate::args::ReplFlags; @@ -13,12 +19,6 @@ use crate::colors; use crate::factory::CliFactory; use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::TextDecodedFile; -use deno_core::error::AnyError; -use deno_core::futures::StreamExt; -use deno_core::serde_json; -use deno_core::unsync::spawn_blocking; -use deno_runtime::WorkerExecutionMode; -use rustyline::error::ReadlineError; mod channel; mod editor; diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs index 02594f1519..b09fcafc91 100644 --- a/cli/tools/repl/session.rs +++ b/cli/tools/repl/session.rs @@ -2,22 +2,6 @@ use std::sync::Arc; -use crate::args::CliOptions; -use crate::cdp; -use crate::colors; -use crate::lsp::ReplLanguageServer; -use crate::npm::CliNpmResolver; -use crate::resolver::CliResolver; -use crate::tools::test::report_tests; -use crate::tools::test::reporters::PrettyTestReporter; -use crate::tools::test::reporters::TestReporter; -use crate::tools::test::run_tests_for_worker; -use crate::tools::test::send_test_event; -use crate::tools::test::worker_has_tests; -use crate::tools::test::TestEvent; -use crate::tools::test::TestEventReceiver; -use crate::tools::test::TestFailureFormatOptions; - use deno_ast::diagnostics::Diagnostic; use deno_ast::swc::ast as swc_ast; use deno_ast::swc::common::comments::CommentKind; @@ -55,6 +39,22 @@ use regex::Match; use regex::Regex; use tokio::sync::Mutex; +use crate::args::CliOptions; +use crate::cdp; +use crate::colors; +use crate::lsp::ReplLanguageServer; +use crate::npm::CliNpmResolver; +use crate::resolver::CliResolver; +use crate::tools::test::report_tests; +use crate::tools::test::reporters::PrettyTestReporter; +use crate::tools::test::reporters::TestReporter; +use crate::tools::test::run_tests_for_worker; +use crate::tools::test::send_test_event; +use crate::tools::test::worker_has_tests; +use crate::tools::test::TestEvent; +use crate::tools::test::TestEventReceiver; +use crate::tools::test::TestFailureFormatOptions; + fn comment_source_to_position_range( comment_start: SourcePos, m: &Match, diff --git a/cli/tools/test/channel.rs b/cli/tools/test/channel.rs index 9a003f2d5d..d3c1ff9564 100644 --- a/cli/tools/test/channel.rs +++ b/cli/tools/test/channel.rs @@ -1,15 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::TestEvent; -use deno_core::futures::future::poll_fn; -use deno_core::parking_lot; -use deno_core::parking_lot::lock_api::RawMutex; -use deno_core::parking_lot::lock_api::RawMutexTimed; -use deno_runtime::deno_io::pipe; -use deno_runtime::deno_io::AsyncPipeRead; -use deno_runtime::deno_io::PipeRead; -use deno_runtime::deno_io::PipeWrite; -use memmem::Searcher; use std::fmt::Display; use std::future::Future; use std::io::Write; @@ -19,6 +9,16 @@ use std::sync::atomic::Ordering; use std::task::ready; use std::task::Poll; use std::time::Duration; + +use deno_core::futures::future::poll_fn; +use deno_core::parking_lot; +use deno_core::parking_lot::lock_api::RawMutex; +use deno_core::parking_lot::lock_api::RawMutexTimed; +use deno_runtime::deno_io::pipe; +use deno_runtime::deno_io::AsyncPipeRead; +use deno_runtime::deno_io::PipeRead; +use deno_runtime::deno_io::PipeWrite; +use memmem::Searcher; use tokio::io::AsyncRead; use tokio::io::AsyncReadExt; use tokio::io::ReadBuf; @@ -27,6 +27,8 @@ use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::WeakUnboundedSender; +use super::TestEvent; + /// 8-byte sync marker that is unlikely to appear in normal output. Equivalent /// to the string `"\u{200B}\0\u{200B}\0"`. const SYNC_MARKER: &[u8; 8] = &[226, 128, 139, 0, 226, 128, 139, 0]; @@ -437,11 +439,12 @@ impl TestEventSender { #[allow(clippy::print_stderr)] #[cfg(test)] mod tests { - use super::*; - use crate::tools::test::TestResult; use deno_core::unsync::spawn; use deno_core::unsync::spawn_blocking; + use super::*; + use crate::tools::test::TestResult; + /// Test that output is correctly interleaved with messages. #[tokio::test] async fn spawn_worker() { diff --git a/cli/tools/test/fmt.rs b/cli/tools/test/fmt.rs index 0f6a9ed2b4..a1a5174845 100644 --- a/cli/tools/test/fmt.rs +++ b/cli/tools/test/fmt.rs @@ -1,16 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::ops::AddAssign; + use deno_core::stats::RuntimeActivity; use deno_core::stats::RuntimeActivityDiff; use deno_core::stats::RuntimeActivityTrace; use deno_core::stats::RuntimeActivityType; use phf::phf_map; -use std::borrow::Cow; -use std::ops::AddAssign; - -use crate::util::path::to_percent_decoded_str; use super::*; +use crate::util::path::to_percent_decoded_str; pub fn to_relative_path_or_remote_url(cwd: &Url, path_or_url: &str) -> String { let Ok(url) = Url::parse(path_or_url) else { diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 3745d7c7ec..09bdf6e3aa 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -1,24 +1,24 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::CliOptions; -use crate::args::Flags; -use crate::args::TestFlags; -use crate::args::TestReporterConfig; -use crate::colors; -use crate::display; -use crate::factory::CliFactory; -use crate::file_fetcher::CliFileFetcher; -use crate::graph_util::has_graph_root_local_dependent_changed; -use crate::ops; -use crate::sys::CliSys; -use crate::util::extract::extract_doc_tests; -use crate::util::file_watcher; -use crate::util::fs::collect_specifiers; -use crate::util::path::get_extension; -use crate::util::path::is_script_ext; -use crate::util::path::matches_pattern_or_exact_path; -use crate::worker::CliMainWorkerFactory; -use crate::worker::CoverageCollector; +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::collections::HashSet; +use std::env; +use std::fmt::Write as _; +use std::future::poll_fn; +use std::io::Write; +use std::num::NonZeroUsize; +use std::path::Path; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::task::Poll; +use std::time::Duration; +use std::time::Instant; use deno_ast::MediaType; use deno_cache_dir::file_fetcher::File; @@ -66,27 +66,28 @@ use rand::seq::SliceRandom; use rand::SeedableRng; use regex::Regex; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::collections::HashSet; -use std::env; -use std::fmt::Write as _; -use std::future::poll_fn; -use std::io::Write; -use std::num::NonZeroUsize; -use std::path::Path; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; -use std::task::Poll; -use std::time::Duration; -use std::time::Instant; use tokio::signal; +use crate::args::CliOptions; +use crate::args::Flags; +use crate::args::TestFlags; +use crate::args::TestReporterConfig; +use crate::colors; +use crate::display; +use crate::factory::CliFactory; +use crate::file_fetcher::CliFileFetcher; +use crate::graph_util::has_graph_root_local_dependent_changed; +use crate::ops; +use crate::sys::CliSys; +use crate::util::extract::extract_doc_tests; +use crate::util::file_watcher; +use crate::util::fs::collect_specifiers; +use crate::util::path::get_extension; +use crate::util::path::is_script_ext; +use crate::util::path::matches_pattern_or_exact_path; +use crate::worker::CliMainWorkerFactory; +use crate::worker::CoverageCollector; + mod channel; pub mod fmt; pub mod reporters; diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs index b3d7618be9..3d9e92df87 100644 --- a/cli/tools/upgrade.rs +++ b/cli/tools/upgrade.rs @@ -2,6 +2,27 @@ //! This module provides feature to upgrade deno executable +use std::borrow::Cow; +use std::env; +use std::fs; +use std::io::IsTerminal; +use std::ops::Sub; +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; +use std::sync::Arc; +use std::time::Duration; + +use async_trait::async_trait; +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::unsync::spawn; +use deno_core::url::Url; +use deno_semver::SmallStackString; +use deno_semver::Version; +use once_cell::sync::Lazy; + use crate::args::Flags; use crate::args::UpgradeFlags; use crate::args::UPGRADE_USAGE; @@ -15,26 +36,6 @@ use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::version; -use async_trait::async_trait; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::unsync::spawn; -use deno_core::url::Url; -use deno_semver::SmallStackString; -use deno_semver::Version; -use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::env; -use std::fs; -use std::io::IsTerminal; -use std::ops::Sub; -use std::path::Path; -use std::path::PathBuf; -use std::process::Command; -use std::sync::Arc; -use std::time::Duration; - const RELEASE_URL: &str = "https://github.com/denoland/deno/releases"; const CANARY_URL: &str = "https://dl.deno.land/canary"; const DL_RELEASE_URL: &str = "https://dl.deno.land/release"; diff --git a/cli/tsc/diagnostics.rs b/cli/tsc/diagnostics.rs index e4cc80723f..fd0a027ae3 100644 --- a/cli/tsc/diagnostics.rs +++ b/cli/tsc/diagnostics.rs @@ -1,16 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_ast::ModuleSpecifier; -use deno_graph::ModuleGraph; -use deno_terminal::colors; +use std::error::Error; +use std::fmt; +use deno_ast::ModuleSpecifier; use deno_core::serde::Deserialize; use deno_core::serde::Deserializer; use deno_core::serde::Serialize; use deno_core::serde::Serializer; use deno_core::sourcemap::SourceMap; -use std::error::Error; -use std::fmt; +use deno_graph::ModuleGraph; +use deno_terminal::colors; const MAX_SOURCE_LINE_LENGTH: usize = 150; @@ -401,11 +401,12 @@ impl Error for Diagnostics {} #[cfg(test)] mod tests { - use super::*; use deno_core::serde_json; use deno_core::serde_json::json; use test_util::strip_ansi_codes; + use super::*; + #[test] fn test_de_diagnostics() { let value = json!([ diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 4c84050b5e..734b5c7289 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -1,16 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::TsConfig; -use crate::args::TypeCheckMode; -use crate::cache::FastInsecureHasher; -use crate::cache::ModuleInfoCache; -use crate::node::CliNodeResolver; -use crate::npm::CliNpmResolver; -use crate::resolver::CjsTracker; -use crate::sys::CliSys; -use crate::util::checksum; -use crate::util::path::mapped_specifier_for_tsc; -use crate::worker::create_isolate_create_params; +use std::borrow::Cow; +use std::collections::HashMap; +use std::fmt; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; use deno_ast::MediaType; use deno_core::anyhow::anyhow; @@ -44,14 +39,20 @@ use node_resolver::resolve_specifier_into_node_modules; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::collections::HashMap; -use std::fmt; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; use thiserror::Error; +use crate::args::TsConfig; +use crate::args::TypeCheckMode; +use crate::cache::FastInsecureHasher; +use crate::cache::ModuleInfoCache; +use crate::node::CliNodeResolver; +use crate::npm::CliNpmResolver; +use crate::resolver::CjsTracker; +use crate::sys::CliSys; +use crate::util::checksum; +use crate::util::path::mapped_specifier_for_tsc; +use crate::worker::create_isolate_create_params; + mod diagnostics; pub use self::diagnostics::Diagnostic; @@ -1136,10 +1137,6 @@ pub fn exec(request: Request) -> Result { #[cfg(test)] mod tests { - use super::Diagnostic; - use super::DiagnosticCategory; - use super::*; - use crate::args::TsConfig; use deno_core::futures::future; use deno_core::serde_json; use deno_core::OpState; @@ -1147,6 +1144,11 @@ mod tests { use deno_graph::ModuleGraph; use test_util::PathRef; + use super::Diagnostic; + use super::DiagnosticCategory; + use super::*; + use crate::args::TsConfig; + #[derive(Debug, Default)] pub struct MockLoader { pub fixtures: PathRef, diff --git a/cli/util/diff.rs b/cli/util/diff.rs index 14ece0c44c..0852abdbaf 100644 --- a/cli/util/diff.rs +++ b/cli/util/diff.rs @@ -1,9 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::colors; +use std::fmt::Write as _; + use dissimilar::diff as difference; use dissimilar::Chunk; -use std::fmt::Write as _; + +use crate::colors; /// Print diff of the same file_path, before and after formatting. /// diff --git a/cli/util/display.rs b/cli/util/display.rs index 8795d3db68..244bd0740d 100644 --- a/cli/util/display.rs +++ b/cli/util/display.rs @@ -1,9 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::io::Write; + use deno_core::error::AnyError; use deno_core::serde_json; use deno_runtime::colors; -use std::io::Write; /// A function that converts a float to a string the represents a human /// readable version of that number. diff --git a/cli/util/draw_thread.rs b/cli/util/draw_thread.rs index 164a8fc713..80aa3959c2 100644 --- a/cli/util/draw_thread.rs +++ b/cli/util/draw_thread.rs @@ -1,13 +1,14 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::io::IsTerminal; +use std::sync::Arc; +use std::time::Duration; + use console_static_text::ConsoleStaticText; use deno_core::parking_lot::Mutex; use deno_core::unsync::spawn_blocking; use deno_runtime::ops::tty::ConsoleSize; use once_cell::sync::Lazy; -use std::io::IsTerminal; -use std::sync::Arc; -use std::time::Duration; use crate::util::console::console_size; diff --git a/cli/util/extract.rs b/cli/util/extract.rs index c4562060d8..ff778248ea 100644 --- a/cli/util/extract.rs +++ b/cli/util/extract.rs @@ -1,5 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::BTreeSet; +use std::fmt::Write as _; +use std::sync::Arc; + use deno_ast::swc::ast; use deno_ast::swc::atoms::Atom; use deno_ast::swc::common::collections::AHashSet; @@ -17,9 +21,6 @@ use deno_cache_dir::file_fetcher::File; use deno_core::error::AnyError; use deno_core::ModuleSpecifier; use regex::Regex; -use std::collections::BTreeSet; -use std::fmt::Write as _; -use std::sync::Arc; use crate::file_fetcher::TextDecodedFile; use crate::util::path::mapped_specifier_for_tsc; @@ -808,11 +809,12 @@ fn wrap_in_deno_test(stmts: Vec, test_name: Atom) -> ast::Stmt { #[cfg(test)] mod tests { - use super::*; - use crate::file_fetcher::TextDecodedFile; use deno_ast::swc::atoms::Atom; use pretty_assertions::assert_eq; + use super::*; + use crate::file_fetcher::TextDecodedFile; + #[test] fn test_extract_doc_tests() { struct Input { diff --git a/cli/util/file_watcher.rs b/cli/util/file_watcher.rs index b9318a6e4b..6596afa42c 100644 --- a/cli/util/file_watcher.rs +++ b/cli/util/file_watcher.rs @@ -1,8 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::Flags; -use crate::colors; -use crate::util::fs::canonicalize_path; +use std::cell::RefCell; +use std::collections::HashSet; +use std::io::IsTerminal; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; +use std::time::Duration; use deno_config::glob::PathOrPatternSet; use deno_core::error::AnyError; @@ -18,18 +22,15 @@ use notify::Error as NotifyError; use notify::RecommendedWatcher; use notify::RecursiveMode; use notify::Watcher; -use std::cell::RefCell; -use std::collections::HashSet; -use std::io::IsTerminal; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; -use std::time::Duration; use tokio::select; use tokio::sync::mpsc; use tokio::sync::mpsc::UnboundedReceiver; use tokio::time::sleep; +use crate::args::Flags; +use crate::colors; +use crate::util::fs::canonicalize_path; + const CLEAR_SCREEN: &str = "\x1B[H\x1B[2J\x1B[3J"; const DEBOUNCE_INTERVAL: Duration = Duration::from_millis(200); diff --git a/cli/util/fs.rs b/cli/util/fs.rs index e0b9a6f4ee..6c25879d6e 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -152,10 +152,12 @@ mod clone_dir_imp { #[cfg(target_vendor = "apple")] mod apple { - use super::super::copy_dir_recursive; - use deno_core::error::AnyError; use std::os::unix::ffi::OsStrExt; use std::path::Path; + + use deno_core::error::AnyError; + + use super::super::copy_dir_recursive; fn clonefile(from: &Path, to: &Path) -> std::io::Result<()> { let from = std::ffi::CString::new(from.as_os_str().as_bytes())?; let to = std::ffi::CString::new(to.as_os_str().as_bytes())?; @@ -462,7 +464,6 @@ pub fn specifier_from_file_path( #[cfg(test)] mod tests { - use super::*; use deno_core::futures; use deno_core::parking_lot::Mutex; use deno_path_util::normalize_path; @@ -471,6 +472,8 @@ mod tests { use test_util::TempDir; use tokio::sync::Notify; + use super::*; + #[test] fn test_normalize_path() { assert_eq!(normalize_path(Path::new("a/../b")), PathBuf::from("b")); diff --git a/cli/util/logger.rs b/cli/util/logger.rs index 783f8a5f68..a6f025790b 100644 --- a/cli/util/logger.rs +++ b/cli/util/logger.rs @@ -1,9 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::draw_thread::DrawThread; +use std::io::Write; + use deno_telemetry::OtelConfig; use deno_telemetry::OtelConsoleConfig; -use std::io::Write; + +use super::draw_thread::DrawThread; struct CliLogger { otel_console_config: OtelConsoleConfig, diff --git a/cli/util/progress_bar/mod.rs b/cli/util/progress_bar/mod.rs index 85be056d84..eb88492657 100644 --- a/cli/util/progress_bar/mod.rs +++ b/cli/util/progress_bar/mod.rs @@ -8,15 +8,13 @@ use std::time::Instant; use deno_core::parking_lot::Mutex; use deno_runtime::ops::tty::ConsoleSize; -use crate::colors; - use self::renderer::ProgressBarRenderer; use self::renderer::ProgressData; use self::renderer::ProgressDataDisplayEntry; - use super::draw_thread::DrawThread; use super::draw_thread::DrawThreadGuard; use super::draw_thread::DrawThreadRenderer; +use crate::colors; mod renderer; diff --git a/cli/util/progress_bar/renderer.rs b/cli/util/progress_bar/renderer.rs index db3d37140f..aca745f817 100644 --- a/cli/util/progress_bar/renderer.rs +++ b/cli/util/progress_bar/renderer.rs @@ -7,9 +7,8 @@ use std::time::Duration; use deno_terminal::colors; -use crate::util::display::human_download_size; - use super::ProgressMessagePrompt; +use crate::util::display::human_download_size; #[derive(Clone)] pub struct ProgressDataDisplayEntry { @@ -224,11 +223,13 @@ fn get_elapsed_text(elapsed: Duration) -> String { #[cfg(test)] mod test { - use super::*; - use pretty_assertions::assert_eq; use std::time::Duration; + + use pretty_assertions::assert_eq; use test_util::assert_contains; + use super::*; + #[test] fn should_get_elapsed_text() { assert_eq!(get_elapsed_text(Duration::from_secs(1)), "[00:01]"); diff --git a/cli/util/sync/task_queue.rs b/cli/util/sync/task_queue.rs index 6ef747e1ae..b06fc7b4e4 100644 --- a/cli/util/sync/task_queue.rs +++ b/cli/util/sync/task_queue.rs @@ -146,9 +146,10 @@ impl<'a> Future for TaskQueuePermitAcquireFuture<'a> { #[cfg(test)] mod test { + use std::sync::Arc; + use deno_core::futures; use deno_core::parking_lot::Mutex; - use std::sync::Arc; use super::*; diff --git a/cli/util/windows.rs b/cli/util/windows.rs index 37e78a5d08..c23bec868f 100644 --- a/cli/util/windows.rs +++ b/cli/util/windows.rs @@ -8,6 +8,7 @@ pub fn ensure_stdio_open() { // SAFETY: winapi calls unsafe { use std::mem::size_of; + use winapi::shared::minwindef::DWORD; use winapi::shared::minwindef::FALSE; use winapi::shared::minwindef::TRUE; diff --git a/cli/worker.rs b/cli/worker.rs index ef519c7278..ed166e6678 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -863,13 +863,14 @@ pub fn create_isolate_create_params() -> Option { #[allow(clippy::print_stderr)] #[cfg(test)] mod tests { - use super::*; use deno_core::resolve_path; use deno_core::FsModuleLoader; use deno_fs::RealFs; use deno_runtime::deno_permissions::Permissions; use deno_runtime::permissions::RuntimePermissionDescriptorParser; + use super::*; + fn create_test_worker() -> MainWorker { let main_module = resolve_path("./hello.js", &std::env::current_dir().unwrap()).unwrap(); diff --git a/ext/broadcast_channel/lib.rs b/ext/broadcast_channel/lib.rs index c1de118a36..e4dbe9fcb4 100644 --- a/ext/broadcast_channel/lib.rs +++ b/ext/broadcast_channel/lib.rs @@ -2,9 +2,6 @@ mod in_memory_broadcast_channel; -pub use in_memory_broadcast_channel::InMemoryBroadcastChannel; -pub use in_memory_broadcast_channel::InMemoryBroadcastChannelResource; - use std::cell::RefCell; use std::path::PathBuf; use std::rc::Rc; @@ -15,6 +12,8 @@ use deno_core::JsBuffer; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +pub use in_memory_broadcast_channel::InMemoryBroadcastChannel; +pub use in_memory_broadcast_channel::InMemoryBroadcastChannelResource; use tokio::sync::broadcast::error::SendError as BroadcastSendError; use tokio::sync::mpsc::error::SendError as MpscSendError; diff --git a/ext/canvas/lib.rs b/ext/canvas/lib.rs index defb288ac9..9ce5a5d257 100644 --- a/ext/canvas/lib.rs +++ b/ext/canvas/lib.rs @@ -1,5 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::path::PathBuf; + use deno_core::op2; use deno_core::ToJsBuffer; use image::imageops::FilterType; @@ -9,7 +11,6 @@ use image::Pixel; use image::RgbaImage; use serde::Deserialize; use serde::Serialize; -use std::path::PathBuf; #[derive(Debug, thiserror::Error)] pub enum CanvasError { diff --git a/ext/console/lib.rs b/ext/console/lib.rs index 87fc8327da..d44e26857e 100644 --- a/ext/console/lib.rs +++ b/ext/console/lib.rs @@ -1,7 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::path::PathBuf; + use deno_core::op2; use deno_core::v8; -use std::path::PathBuf; deno_core::extension!( deno_console, diff --git a/ext/cron/interface.rs b/ext/cron/interface.rs index a19525cc4e..50459b4afd 100644 --- a/ext/cron/interface.rs +++ b/ext/cron/interface.rs @@ -1,8 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::CronError; use async_trait::async_trait; +use crate::CronError; + pub trait CronHandler { type EH: CronHandle + 'static; diff --git a/ext/cron/lib.rs b/ext/cron/lib.rs index feffb5e511..09c4e2d3f6 100644 --- a/ext/cron/lib.rs +++ b/ext/cron/lib.rs @@ -7,13 +7,14 @@ use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; -pub use crate::interface::*; use deno_core::error::get_custom_error_class; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +pub use crate::interface::*; + pub const UNSTABLE_FEATURE_NAME: &str = "cron"; deno_core::extension!(deno_cron, diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 69dcd1413a..396069e776 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -1,22 +1,22 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::num::NonZeroU32; +use std::path::PathBuf; + use aes_kw::KekAes128; use aes_kw::KekAes192; use aes_kw::KekAes256; - use base64::prelude::BASE64_URL_SAFE_NO_PAD; use base64::Engine; use deno_core::error::not_supported; use deno_core::op2; -use deno_core::ToJsBuffer; - use deno_core::unsync::spawn_blocking; use deno_core::JsBuffer; use deno_core::OpState; -use serde::Deserialize; - +use deno_core::ToJsBuffer; use p256::elliptic_curve::sec1::FromEncodedPoint; use p256::pkcs8::DecodePrivateKey; +pub use rand; use rand::rngs::OsRng; use rand::rngs::StdRng; use rand::thread_rng; @@ -41,15 +41,12 @@ use rsa::traits::SignatureScheme; use rsa::Pss; use rsa::RsaPrivateKey; use rsa::RsaPublicKey; +use serde::Deserialize; use sha1::Sha1; use sha2::Digest; use sha2::Sha256; use sha2::Sha384; -use sha2::Sha512; -use std::num::NonZeroU32; -use std::path::PathBuf; - -pub use rand; // Re-export rand +use sha2::Sha512; // Re-export rand mod decrypt; mod ed25519; diff --git a/ext/fetch/fs_fetch_handler.rs b/ext/fetch/fs_fetch_handler.rs index c236dd9c67..c763d4e424 100644 --- a/ext/fetch/fs_fetch_handler.rs +++ b/ext/fetch/fs_fetch_handler.rs @@ -1,8 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::CancelHandle; -use crate::CancelableResponseFuture; -use crate::FetchHandler; +use std::rc::Rc; use deno_core::futures::FutureExt; use deno_core::futures::TryFutureExt; @@ -12,9 +10,12 @@ use deno_core::CancelFuture; use deno_core::OpState; use http::StatusCode; use http_body_util::BodyExt; -use std::rc::Rc; use tokio_util::io::ReaderStream; +use crate::CancelHandle; +use crate::CancelableResponseFuture; +use crate::FetchHandler; + /// An implementation which tries to read file URLs from the file system via /// tokio::fs. #[derive(Clone)] diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index 103698b3bf..5abdfc3339 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -19,6 +19,10 @@ use std::sync::Arc; use std::task::Context; use std::task::Poll; +use bytes::Bytes; +// Re-export data_url +pub use data_url; +use data_url::DataUrl; use deno_core::futures::stream::Peekable; use deno_core::futures::Future; use deno_core::futures::FutureExt; @@ -51,9 +55,7 @@ use deno_tls::RootCertStoreProvider; use deno_tls::TlsKey; use deno_tls::TlsKeys; use deno_tls::TlsKeysHolder; - -use bytes::Bytes; -use data_url::DataUrl; +pub use fs_fetch_handler::FsFetchHandler; use http::header::HeaderName; use http::header::HeaderValue; use http::header::ACCEPT; @@ -75,18 +77,13 @@ use hyper_util::client::legacy::connect::HttpInfo; use hyper_util::client::legacy::Builder as HyperClientBuilder; use hyper_util::rt::TokioExecutor; use hyper_util::rt::TokioTimer; +pub use proxy::basic_auth; use serde::Deserialize; use serde::Serialize; use tower::retry; use tower::ServiceExt; use tower_http::decompression::Decompression; -// Re-export data_url -pub use data_url; -pub use proxy::basic_auth; - -pub use fs_fetch_handler::FsFetchHandler; - #[derive(Clone)] pub struct Options { pub user_agent: String, diff --git a/ext/fetch/proxy.rs b/ext/fetch/proxy.rs index 88fc211ecc..ec1b1b13b7 100644 --- a/ext/fetch/proxy.rs +++ b/ext/fetch/proxy.rs @@ -13,7 +13,6 @@ use std::task::Poll; use deno_core::futures::TryFutureExt; use deno_tls::rustls::ClientConfig as TlsConfig; - use http::header::HeaderValue; use http::uri::Scheme; use http::Uri; @@ -108,9 +107,10 @@ pub(crate) fn from_env() -> Proxies { } pub fn basic_auth(user: &str, pass: Option<&str>) -> HeaderValue { + use std::io::Write; + use base64::prelude::BASE64_STANDARD; use base64::write::EncoderWriter; - use std::io::Write; let mut buf = b"Basic ".to_vec(); { diff --git a/ext/fetch/tests.rs b/ext/fetch/tests.rs index 243b80bd90..8d7436157e 100644 --- a/ext/fetch/tests.rs +++ b/ext/fetch/tests.rs @@ -12,10 +12,9 @@ use http_body_util::BodyExt; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; -use crate::dns; - use super::create_http_client; use super::CreateHttpClientOptions; +use crate::dns; static EXAMPLE_CRT: &[u8] = include_bytes!("../tls/testdata/example1_cert.der"); static EXAMPLE_KEY: &[u8] = diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs index c964071a09..dc1dc99ad9 100644 --- a/ext/ffi/call.rs +++ b/ext/ffi/call.rs @@ -1,12 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::callback::PtrSymbol; -use crate::dlfcn::DynamicLibraryResource; -use crate::ir::*; -use crate::symbol::NativeType; -use crate::symbol::Symbol; -use crate::FfiPermissions; -use crate::ForeignFunction; +use std::cell::RefCell; +use std::ffi::c_void; +use std::future::Future; +use std::rc::Rc; + use deno_core::op2; use deno_core::serde_json::Value; use deno_core::serde_v8::BigInt as V8BigInt; @@ -18,10 +16,14 @@ use deno_core::ResourceId; use libffi::middle::Arg; use num_bigint::BigInt; use serde::Serialize; -use std::cell::RefCell; -use std::ffi::c_void; -use std::future::Future; -use std::rc::Rc; + +use crate::callback::PtrSymbol; +use crate::dlfcn::DynamicLibraryResource; +use crate::ir::*; +use crate::symbol::NativeType; +use crate::symbol::Symbol; +use crate::FfiPermissions; +use crate::ForeignFunction; #[derive(Debug, thiserror::Error)] pub enum CallError { diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs index eff14503d1..81b8f7dda2 100644 --- a/ext/ffi/callback.rs +++ b/ext/ffi/callback.rs @@ -1,19 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::symbol::NativeType; -use crate::FfiPermissions; -use crate::ForeignFunction; -use deno_core::op2; -use deno_core::v8; -use deno_core::v8::TryCatch; -use deno_core::CancelFuture; -use deno_core::CancelHandle; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use deno_core::V8CrossThreadTaskSpawner; -use libffi::middle::Cif; -use serde::Deserialize; use std::borrow::Cow; use std::cell::RefCell; use std::ffi::c_void; @@ -27,6 +13,22 @@ use std::sync::atomic; use std::sync::atomic::AtomicU32; use std::task::Poll; +use deno_core::op2; +use deno_core::v8; +use deno_core::v8::TryCatch; +use deno_core::CancelFuture; +use deno_core::CancelHandle; +use deno_core::OpState; +use deno_core::Resource; +use deno_core::ResourceId; +use deno_core::V8CrossThreadTaskSpawner; +use libffi::middle::Cif; +use serde::Deserialize; + +use crate::symbol::NativeType; +use crate::FfiPermissions; +use crate::ForeignFunction; + static THREAD_ID_COUNTER: AtomicU32 = AtomicU32::new(1); thread_local! { diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs index e1bb121d8c..8e6294e790 100644 --- a/ext/ffi/dlfcn.rs +++ b/ext/ffi/dlfcn.rs @@ -1,11 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::ir::out_buffer_as_ptr; -use crate::symbol::NativeType; -use crate::symbol::Symbol; -use crate::turbocall; -use crate::turbocall::Turbocall; -use crate::FfiPermissions; +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; +use std::ffi::c_void; +use std::rc::Rc; + use deno_core::op2; use deno_core::v8; use deno_core::GarbageCollected; @@ -14,11 +14,13 @@ use deno_core::Resource; use dlopen2::raw::Library; use serde::Deserialize; use serde_value::ValueDeserializer; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::HashMap; -use std::ffi::c_void; -use std::rc::Rc; + +use crate::ir::out_buffer_as_ptr; +use crate::symbol::NativeType; +use crate::symbol::Symbol; +use crate::turbocall; +use crate::turbocall::Turbocall; +use crate::FfiPermissions; #[derive(Debug, thiserror::Error)] pub enum DlfcnError { @@ -324,6 +326,7 @@ pub(crate) fn format_error( // https://github.com/denoland/deno/issues/11632 dlopen2::Error::OpeningLibraryError(e) => { use std::os::windows::ffi::OsStrExt; + use winapi::shared::minwindef::DWORD; use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER; use winapi::um::errhandlingapi::GetLastError; @@ -392,10 +395,11 @@ pub(crate) fn format_error( #[cfg(test)] mod tests { + use serde_json::json; + use super::ForeignFunction; use super::ForeignSymbol; use crate::symbol::NativeType; - use serde_json::json; #[cfg(target_os = "windows")] #[test] diff --git a/ext/ffi/ir.rs b/ext/ffi/ir.rs index 2e80842166..4e6be13caf 100644 --- a/ext/ffi/ir.rs +++ b/ext/ffi/ir.rs @@ -1,11 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::symbol::NativeType; -use deno_core::v8; -use libffi::middle::Arg; use std::ffi::c_void; use std::ptr; +use deno_core::v8; +use libffi::middle::Arg; + +use crate::symbol::NativeType; + #[derive(Debug, thiserror::Error)] pub enum IRError { #[error("Invalid FFI u8 type, expected boolean")] diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 73ec7757ab..9079b7ef9f 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -17,24 +17,23 @@ mod turbocall; use call::op_ffi_call_nonblocking; use call::op_ffi_call_ptr; use call::op_ffi_call_ptr_nonblocking; +pub use call::CallError; use callback::op_ffi_unsafe_callback_close; use callback::op_ffi_unsafe_callback_create; use callback::op_ffi_unsafe_callback_ref; +pub use callback::CallbackError; +use deno_permissions::PermissionCheckError; use dlfcn::op_ffi_load; +pub use dlfcn::DlfcnError; use dlfcn::ForeignFunction; +pub use ir::IRError; use r#static::op_ffi_get_static; +pub use r#static::StaticError; +pub use repr::ReprError; use repr::*; use symbol::NativeType; use symbol::Symbol; -pub use call::CallError; -pub use callback::CallbackError; -use deno_permissions::PermissionCheckError; -pub use dlfcn::DlfcnError; -pub use ir::IRError; -pub use r#static::StaticError; -pub use repr::ReprError; - #[cfg(not(target_pointer_width = "64"))] compile_error!("platform not supported"); diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs index eea15f3e97..d7952a12b4 100644 --- a/ext/ffi/repr.rs +++ b/ext/ffi/repr.rs @@ -1,14 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::FfiPermissions; -use deno_core::op2; -use deno_core::v8; -use deno_core::OpState; use std::ffi::c_char; use std::ffi::c_void; use std::ffi::CStr; use std::ptr; +use deno_core::op2; +use deno_core::v8; +use deno_core::OpState; + +use crate::FfiPermissions; + #[derive(Debug, thiserror::Error)] pub enum ReprError { #[error("Invalid pointer to offset, pointer is null")] diff --git a/ext/ffi/static.rs b/ext/ffi/static.rs index 61b4059336..472faf77c5 100644 --- a/ext/ffi/static.rs +++ b/ext/ffi/static.rs @@ -1,12 +1,14 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::dlfcn::DynamicLibraryResource; -use crate::symbol::NativeType; +use std::ptr; + use deno_core::op2; use deno_core::v8; use deno_core::OpState; use deno_core::ResourceId; -use std::ptr; + +use crate::dlfcn::DynamicLibraryResource; +use crate::symbol::NativeType; #[derive(Debug, thiserror::Error)] pub enum StaticError { diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs index 0e753d684c..80818887e3 100644 --- a/ext/fs/interface.rs +++ b/ext/fs/interface.rs @@ -6,12 +6,11 @@ use std::path::Path; use std::path::PathBuf; use std::rc::Rc; -use serde::Deserialize; -use serde::Serialize; - use deno_io::fs::File; use deno_io::fs::FsResult; use deno_io::fs::FsStat; +use serde::Deserialize; +use serde::Serialize; use crate::sync::MaybeSend; use crate::sync::MaybeSync; diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index 360400df0d..088d71c1f6 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -5,6 +5,13 @@ mod ops; mod std_fs; pub mod sync; +use std::borrow::Cow; +use std::path::Path; +use std::path::PathBuf; + +use deno_io::fs::FsError; +use deno_permissions::PermissionCheckError; + pub use crate::interface::AccessCheckCb; pub use crate::interface::AccessCheckFn; pub use crate::interface::FileSystem; @@ -15,18 +22,11 @@ pub use crate::interface::OpenOptions; pub use crate::ops::FsOpsError; pub use crate::ops::FsOpsErrorKind; pub use crate::ops::OperationError; +use crate::ops::*; pub use crate::std_fs::RealFs; pub use crate::sync::MaybeSend; pub use crate::sync::MaybeSync; -use crate::ops::*; - -use deno_io::fs::FsError; -use deno_permissions::PermissionCheckError; -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; - pub trait FsPermissions { fn check_open<'a>( &mut self, diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index ac0a8901d7..655c2ca6b0 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -11,12 +11,6 @@ use std::path::PathBuf; use std::path::StripPrefixError; use std::rc::Rc; -use crate::interface::AccessCheckFn; -use crate::interface::FileSystemRc; -use crate::interface::FsDirEntry; -use crate::interface::FsFileType; -use crate::FsPermissions; -use crate::OpenOptions; use boxed_error::Boxed; use deno_core::op2; use deno_core::CancelFuture; @@ -35,6 +29,13 @@ use rand::thread_rng; use rand::Rng; use serde::Serialize; +use crate::interface::AccessCheckFn; +use crate::interface::FileSystemRc; +use crate::interface::FsDirEntry; +use crate::interface::FsFileType; +use crate::FsPermissions; +use crate::OpenOptions; + #[derive(Debug, Boxed)] pub struct FsOpsError(pub Box); diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index c28fe9f915..535859fb46 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -503,6 +503,7 @@ fn remove(path: &Path, recursive: bool) -> FsResult<()> { #[cfg(not(unix))] { use std::os::windows::prelude::MetadataExt; + use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY; if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 { fs::remove_dir(path) @@ -520,13 +521,14 @@ fn remove(path: &Path, recursive: bool) -> FsResult<()> { fn copy_file(from: &Path, to: &Path) -> FsResult<()> { #[cfg(target_os = "macos")] { - use libc::clonefile; - use libc::stat; - use libc::unlink; use std::ffi::CString; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::fs::PermissionsExt; + use libc::clonefile; + use libc::stat; + use libc::unlink; + let from_str = CString::new(from.as_os_str().as_encoded_bytes()) .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; let to_str = CString::new(to.as_os_str().as_encoded_bytes()) @@ -670,11 +672,12 @@ fn cp(from: &Path, to: &Path) -> FsResult<()> { #[cfg(target_os = "macos")] { // Just clonefile() - use libc::clonefile; - use libc::unlink; use std::ffi::CString; use std::os::unix::ffi::OsStrExt; + use libc::clonefile; + use libc::unlink; + let from_str = CString::new(from.as_os_str().as_bytes()) .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; let to_str = CString::new(to.as_os_str().as_bytes()) @@ -762,6 +765,7 @@ fn stat(path: &Path) -> FsResult { #[cfg(windows)] fn stat(path: &Path) -> FsResult { use std::os::windows::fs::OpenOptionsExt; + use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; let mut opts = fs::OpenOptions::new(); @@ -924,6 +928,7 @@ fn exists(path: &Path) -> bool { #[cfg(windows)] { use std::os::windows::ffi::OsStrExt; + use winapi::um::fileapi::GetFileAttributesW; use winapi::um::fileapi::INVALID_FILE_ATTRIBUTES; diff --git a/ext/fs/sync.rs b/ext/fs/sync.rs index 06694f1dc4..cf87236558 100644 --- a/ext/fs/sync.rs +++ b/ext/fs/sync.rs @@ -6,10 +6,9 @@ pub use inner::*; mod inner { #![allow(clippy::disallowed_types)] - pub use std::sync::Arc as MaybeArc; - pub use core::marker::Send as MaybeSend; pub use core::marker::Sync as MaybeSync; + pub use std::sync::Arc as MaybeArc; } #[cfg(not(feature = "sync_fs"))] diff --git a/ext/http/compressible.rs b/ext/http/compressible.rs index 6e96582e7e..ef5d830122 100644 --- a/ext/http/compressible.rs +++ b/ext/http/compressible.rs @@ -1,8 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use phf::phf_set; use std::str::FromStr; +use phf::phf_set; + // Data obtained from https://github.com/jshttp/mime-db/blob/fa5e4ef3cc8907ec3c5ec5b85af0c63d7059a5cd/db.json // Important! Keep this list sorted alphabetically. static CONTENT_TYPES: phf::Set<&'static [u8]> = phf_set! { diff --git a/ext/http/fly_accept_encoding.rs b/ext/http/fly_accept_encoding.rs index 4d6fd2231e..a946481bd2 100644 --- a/ext/http/fly_accept_encoding.rs +++ b/ext/http/fly_accept_encoding.rs @@ -124,11 +124,12 @@ fn encodings_iter_inner<'s>( #[cfg(test)] mod tests { - use super::*; use http_v02::header::ACCEPT_ENCODING; use http_v02::HeaderMap; use http_v02::HeaderValue; + use super::*; + fn encodings( headers: &HeaderMap, ) -> Result, f32)>, EncodingError> { diff --git a/ext/http/http_next.rs b/ext/http/http_next.rs index 7dbac6021a..003190379d 100644 --- a/ext/http/http_next.rs +++ b/ext/http/http_next.rs @@ -1,24 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::compressible::is_content_compressible; -use crate::extract_network_stream; -use crate::network_buffered_stream::NetworkStreamPrefixCheck; -use crate::request_body::HttpRequestBody; -use crate::request_properties::HttpConnectionProperties; -use crate::request_properties::HttpListenProperties; -use crate::request_properties::HttpPropertyExtractor; -use crate::response_body::Compression; -use crate::response_body::ResponseBytesInner; -use crate::service::handle_request; -use crate::service::http_general_trace; -use crate::service::http_trace; -use crate::service::HttpRecord; -use crate::service::HttpRecordResponse; -use crate::service::HttpRequestBodyAutocloser; -use crate::service::HttpServerState; -use crate::service::SignallingRc; -use crate::websocket_upgrade::WebSocketUpgrade; -use crate::LocalExecutor; -use crate::Options; +use std::borrow::Cow; +use std::cell::RefCell; +use std::ffi::c_void; +use std::future::Future; +use std::io; +use std::pin::Pin; +use std::ptr::null; +use std::rc::Rc; + use cache_control::CacheControl; use deno_core::external; use deno_core::futures::future::poll_fn; @@ -44,6 +33,7 @@ use deno_core::ResourceId; use deno_net::ops_tls::TlsStream; use deno_net::raw::NetworkStream; use deno_websocket::ws_create_server_stream; +use fly_accept_encoding::Encoding; use hyper::body::Incoming; use hyper::header::HeaderMap; use hyper::header::ACCEPT_ENCODING; @@ -63,21 +53,31 @@ use hyper::StatusCode; use hyper_util::rt::TokioIo; use once_cell::sync::Lazy; use smallvec::SmallVec; -use std::borrow::Cow; -use std::cell::RefCell; -use std::ffi::c_void; -use std::future::Future; -use std::io; -use std::pin::Pin; -use std::ptr::null; -use std::rc::Rc; - -use super::fly_accept_encoding; -use fly_accept_encoding::Encoding; - use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; +use super::fly_accept_encoding; +use crate::compressible::is_content_compressible; +use crate::extract_network_stream; +use crate::network_buffered_stream::NetworkStreamPrefixCheck; +use crate::request_body::HttpRequestBody; +use crate::request_properties::HttpConnectionProperties; +use crate::request_properties::HttpListenProperties; +use crate::request_properties::HttpPropertyExtractor; +use crate::response_body::Compression; +use crate::response_body::ResponseBytesInner; +use crate::service::handle_request; +use crate::service::http_general_trace; +use crate::service::http_trace; +use crate::service::HttpRecord; +use crate::service::HttpRecordResponse; +use crate::service::HttpRequestBodyAutocloser; +use crate::service::HttpServerState; +use crate::service::SignallingRc; +use crate::websocket_upgrade::WebSocketUpgrade; +use crate::LocalExecutor; +use crate::Options; + type Request = hyper::Request; static USE_WRITEV: Lazy = Lazy::new(|| { diff --git a/ext/http/lib.rs b/ext/http/lib.rs index 39b0bbc2af..d582303167 100644 --- a/ext/http/lib.rs +++ b/ext/http/lib.rs @@ -1,5 +1,21 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::cmp::min; +use std::error::Error; +use std::future::Future; +use std::io; +use std::io::Write; +use std::mem::replace; +use std::mem::take; +use std::pin::pin; +use std::pin::Pin; +use std::rc::Rc; +use std::sync::Arc; +use std::task::Context; +use std::task::Poll; + use async_compression::tokio::write::BrotliEncoder; use async_compression::tokio::write::GzipEncoder; use async_compression::Level; @@ -54,21 +70,6 @@ use hyper_v014::HeaderMap; use hyper_v014::Request; use hyper_v014::Response; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::cmp::min; -use std::error::Error; -use std::future::Future; -use std::io; -use std::io::Write; -use std::mem::replace; -use std::mem::take; -use std::pin::pin; -use std::pin::Pin; -use std::rc::Rc; -use std::sync::Arc; -use std::task::Context; -use std::task::Poll; use tokio::io::AsyncRead; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; diff --git a/ext/http/network_buffered_stream.rs b/ext/http/network_buffered_stream.rs index 73df2dbd9f..a195712d03 100644 --- a/ext/http/network_buffered_stream.rs +++ b/ext/http/network_buffered_stream.rs @@ -1,12 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use bytes::Bytes; -use deno_core::futures::future::poll_fn; -use deno_core::futures::ready; use std::io; use std::mem::MaybeUninit; use std::pin::Pin; use std::task::Poll; + +use bytes::Bytes; +use deno_core::futures::future::poll_fn; +use deno_core::futures::ready; use tokio::io::AsyncRead; use tokio::io::AsyncWrite; use tokio::io::ReadBuf; @@ -227,9 +228,10 @@ impl AsyncWrite #[cfg(test)] mod tests { - use super::*; use tokio::io::AsyncReadExt; + use super::*; + struct YieldsOneByteAtATime(&'static [u8]); impl AsyncRead for YieldsOneByteAtATime { diff --git a/ext/http/reader_stream.rs b/ext/http/reader_stream.rs index be6d571b1a..8dc6c90ae7 100644 --- a/ext/http/reader_stream.rs +++ b/ext/http/reader_stream.rs @@ -66,11 +66,12 @@ impl Stream for ExternallyAbortableReaderStream { #[cfg(test)] mod tests { - use super::*; use bytes::Bytes; use deno_core::futures::StreamExt; use tokio::io::AsyncWriteExt; + use super::*; + #[tokio::test] async fn success() { let (a, b) = tokio::io::duplex(64 * 1024); diff --git a/ext/http/request_body.rs b/ext/http/request_body.rs index f1c3f358ea..b06b591c38 100644 --- a/ext/http/request_body.rs +++ b/ext/http/request_body.rs @@ -1,4 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::pin::Pin; +use std::rc::Rc; +use std::task::ready; +use std::task::Poll; + use bytes::Bytes; use deno_core::futures::stream::Peekable; use deno_core::futures::Stream; @@ -12,11 +18,6 @@ use deno_core::Resource; use hyper::body::Body; use hyper::body::Incoming; use hyper::body::SizeHint; -use std::borrow::Cow; -use std::pin::Pin; -use std::rc::Rc; -use std::task::ready; -use std::task::Poll; /// Converts a hyper incoming body stream into a stream of [`Bytes`] that we can use to read in V8. struct ReadFuture(Incoming); diff --git a/ext/http/request_properties.rs b/ext/http/request_properties.rs index 39d35a79f1..6b89fa5a6a 100644 --- a/ext/http/request_properties.rs +++ b/ext/http/request_properties.rs @@ -1,4 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::net::Ipv4Addr; +use std::net::SocketAddr; +use std::net::SocketAddrV4; +use std::rc::Rc; + use deno_core::error::AnyError; use deno_core::OpState; use deno_core::ResourceId; @@ -11,11 +17,6 @@ use deno_net::raw::NetworkStreamType; use hyper::header::HOST; use hyper::HeaderMap; use hyper::Uri; -use std::borrow::Cow; -use std::net::Ipv4Addr; -use std::net::SocketAddr; -use std::net::SocketAddrV4; -use std::rc::Rc; // TODO(mmastrac): I don't like that we have to clone this, but it's one-time setup #[derive(Clone)] diff --git a/ext/http/response_body.rs b/ext/http/response_body.rs index bac43bf3c8..62430a3dea 100644 --- a/ext/http/response_body.rs +++ b/ext/http/response_body.rs @@ -573,12 +573,14 @@ impl PollFrame for BrotliResponseStream { #[allow(clippy::print_stderr)] #[cfg(test)] mod tests { - use super::*; - use deno_core::futures::future::poll_fn; use std::hash::Hasher; use std::io::Read; use std::io::Write; + use deno_core::futures::future::poll_fn; + + use super::*; + fn zeros() -> Vec { vec![0; 1024 * 1024] } diff --git a/ext/http/service.rs b/ext/http/service.rs index ce24dea43f..52bd290390 100644 --- a/ext/http/service.rs +++ b/ext/http/service.rs @@ -1,21 +1,4 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::request_properties::HttpConnectionProperties; -use crate::response_body::ResponseBytesInner; -use crate::response_body::ResponseStreamResult; -use deno_core::futures::ready; -use deno_core::BufView; -use deno_core::OpState; -use deno_core::ResourceId; -use http::request::Parts; -use hyper::body::Body; -use hyper::body::Frame; -use hyper::body::Incoming; -use hyper::body::SizeHint; -use hyper::header::HeaderMap; -use hyper::upgrade::OnUpgrade; - -use scopeguard::guard; -use scopeguard::ScopeGuard; use std::cell::Cell; use std::cell::Ref; use std::cell::RefCell; @@ -27,8 +10,26 @@ use std::rc::Rc; use std::task::Context; use std::task::Poll; use std::task::Waker; + +use deno_core::futures::ready; +use deno_core::BufView; +use deno_core::OpState; +use deno_core::ResourceId; +use http::request::Parts; +use hyper::body::Body; +use hyper::body::Frame; +use hyper::body::Incoming; +use hyper::body::SizeHint; +use hyper::header::HeaderMap; +use hyper::upgrade::OnUpgrade; +use scopeguard::guard; +use scopeguard::ScopeGuard; use tokio::sync::oneshot; +use crate::request_properties::HttpConnectionProperties; +use crate::response_body::ResponseBytesInner; +use crate::response_body::ResponseStreamResult; + pub type Request = hyper::Request; pub type Response = hyper::Response; @@ -606,16 +607,18 @@ impl Drop for HttpRecordResponse { #[cfg(test)] mod tests { - use super::*; - use crate::response_body::Compression; - use crate::response_body::ResponseBytesInner; + use std::error::Error as StdError; + use bytes::Buf; use deno_net::raw::NetworkStreamType; use hyper::body::Body; use hyper::service::service_fn; use hyper::service::HttpService; use hyper_util::rt::TokioIo; - use std::error::Error as StdError; + + use super::*; + use crate::response_body::Compression; + use crate::response_body::ResponseBytesInner; /// Execute client request on service and concurrently map the response. async fn serve_request( diff --git a/ext/http/websocket_upgrade.rs b/ext/http/websocket_upgrade.rs index af9504717e..fdee19cc05 100644 --- a/ext/http/websocket_upgrade.rs +++ b/ext/http/websocket_upgrade.rs @@ -169,9 +169,10 @@ impl WebSocketUpgrade { #[cfg(test)] mod tests { - use super::*; use hyper_v014::Body; + use super::*; + type ExpectedResponseAndHead = Option<(Response, &'static [u8])>; fn assert_response( diff --git a/ext/io/bi_pipe.rs b/ext/io/bi_pipe.rs index bf65d3037f..d8666e14a7 100644 --- a/ext/io/bi_pipe.rs +++ b/ext/io/bi_pipe.rs @@ -338,6 +338,11 @@ pub fn bi_pipe_pair_raw( // TODO(nathanwhit): more granular unsafe blocks // SAFETY: win32 calls unsafe { + use std::io; + use std::os::windows::ffi::OsStrExt; + use std::path::Path; + use std::ptr; + use windows_sys::Win32::Foundation::CloseHandle; use windows_sys::Win32::Foundation::ERROR_ACCESS_DENIED; use windows_sys::Win32::Foundation::ERROR_PIPE_CONNECTED; @@ -355,11 +360,6 @@ pub fn bi_pipe_pair_raw( use windows_sys::Win32::System::Pipes::PIPE_READMODE_BYTE; use windows_sys::Win32::System::Pipes::PIPE_TYPE_BYTE; - use std::io; - use std::os::windows::ffi::OsStrExt; - use std::path::Path; - use std::ptr; - let (path, hd1) = loop { let name = format!("\\\\.\\pipe\\{}", uuid::Uuid::new_v4()); let mut path = Path::new(&name) diff --git a/ext/io/lib.rs b/ext/io/lib.rs index 873fccd7b8..536a0346f1 100644 --- a/ext/io/lib.rs +++ b/ext/io/lib.rs @@ -1,5 +1,23 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::fs::File as StdFile; +use std::future::Future; +use std::io; +use std::io::ErrorKind; +use std::io::Read; +use std::io::Seek; +use std::io::Write; +#[cfg(unix)] +use std::os::unix::io::FromRawFd; +#[cfg(windows)] +use std::os::windows::io::FromRawHandle; +use std::rc::Rc; +#[cfg(windows)] +use std::sync::Arc; + +use deno_core::futures::TryFutureExt; use deno_core::op2; use deno_core::unsync::spawn_blocking; use deno_core::unsync::TaskQueue; @@ -21,40 +39,20 @@ use fs::FsResult; use fs::FsStat; use fs3::FileExt; use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::cell::RefCell; -use std::fs::File as StdFile; -use std::future::Future; -use std::io; -use std::io::ErrorKind; -use std::io::Read; -use std::io::Seek; -use std::io::Write; -use std::rc::Rc; +#[cfg(windows)] +use parking_lot::Condvar; +#[cfg(windows)] +use parking_lot::Mutex; use tokio::io::AsyncRead; use tokio::io::AsyncReadExt; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; use tokio::process; - -#[cfg(unix)] -use std::os::unix::io::FromRawFd; - -#[cfg(windows)] -use std::os::windows::io::FromRawHandle; #[cfg(windows)] use winapi::um::processenv::GetStdHandle; #[cfg(windows)] use winapi::um::winbase; -use deno_core::futures::TryFutureExt; -#[cfg(windows)] -use parking_lot::Condvar; -#[cfg(windows)] -use parking_lot::Mutex; -#[cfg(windows)] -use std::sync::Arc; - pub mod fs; mod pipe; #[cfg(windows)] @@ -62,19 +60,18 @@ mod winpipe; mod bi_pipe; -pub use pipe::pipe; -pub use pipe::AsyncPipeRead; -pub use pipe::AsyncPipeWrite; -pub use pipe::PipeRead; -pub use pipe::PipeWrite; -pub use pipe::RawPipeHandle; - pub use bi_pipe::bi_pipe_pair_raw; pub use bi_pipe::BiPipe; pub use bi_pipe::BiPipeRead; pub use bi_pipe::BiPipeResource; pub use bi_pipe::BiPipeWrite; pub use bi_pipe::RawBiPipeHandle; +pub use pipe::pipe; +pub use pipe::AsyncPipeRead; +pub use pipe::AsyncPipeWrite; +pub use pipe::PipeRead; +pub use pipe::PipeWrite; +pub use pipe::RawPipeHandle; /// Abstraction over `AsRawFd` (unix) and `AsRawHandle` (windows) pub trait AsRawIoHandle { diff --git a/ext/io/pipe.rs b/ext/io/pipe.rs index e0e019e277..6ff449b944 100644 --- a/ext/io/pipe.rs +++ b/ext/io/pipe.rs @@ -299,13 +299,14 @@ pub fn pipe_impl() -> io::Result<(PipeRead, PipeWrite)> { #[cfg(test)] mod tests { - use super::*; - use std::io::Read; use std::io::Write; + use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; + use super::*; + #[test] fn test_pipe() { let (mut read, mut write) = pipe().unwrap(); diff --git a/ext/io/winpipe.rs b/ext/io/winpipe.rs index 01d018008d..b3e659ea67 100644 --- a/ext/io/winpipe.rs +++ b/ext/io/winpipe.rs @@ -1,10 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use rand::thread_rng; -use rand::RngCore; use std::io; use std::os::windows::io::RawHandle; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; + +use rand::thread_rng; +use rand::RngCore; use winapi::shared::minwindef::DWORD; use winapi::um::errhandlingapi::GetLastError; use winapi::um::fileapi::CreateFileA; @@ -116,7 +117,6 @@ fn create_named_pipe_inner() -> io::Result<(RawHandle, RawHandle)> { #[cfg(test)] mod tests { - use super::*; use std::fs::File; use std::io::Read; use std::io::Write; @@ -124,6 +124,8 @@ mod tests { use std::sync::Arc; use std::sync::Barrier; + use super::*; + #[test] fn make_named_pipe() { let (server, client) = create_named_pipe().unwrap(); diff --git a/ext/kv/dynamic.rs b/ext/kv/dynamic.rs index 6d545d79f6..8db2b92926 100644 --- a/ext/kv/dynamic.rs +++ b/ext/kv/dynamic.rs @@ -3,6 +3,14 @@ use std::cell::RefCell; use std::rc::Rc; +use async_trait::async_trait; +use deno_core::error::type_error; +use deno_core::error::AnyError; +use deno_core::OpState; +use denokv_proto::CommitResult; +use denokv_proto::ReadRangeOutput; +use denokv_proto::WatchStream; + use crate::remote::RemoteDbHandlerPermissions; use crate::sqlite::SqliteDbHandler; use crate::sqlite::SqliteDbHandlerPermissions; @@ -12,13 +20,6 @@ use crate::DatabaseHandler; use crate::QueueMessageHandle; use crate::ReadRange; use crate::SnapshotReadOptions; -use async_trait::async_trait; -use deno_core::error::type_error; -use deno_core::error::AnyError; -use deno_core::OpState; -use denokv_proto::CommitResult; -use denokv_proto::ReadRangeOutput; -use denokv_proto::WatchStream; pub struct MultiBackendDbHandler { backends: Vec<(&'static [&'static str], Box)>, diff --git a/ext/kv/remote.rs b/ext/kv/remote.rs index 891786e319..b638ecbd4b 100644 --- a/ext/kv/remote.rs +++ b/ext/kv/remote.rs @@ -5,7 +5,6 @@ use std::marker::PhantomData; use std::rc::Rc; use std::sync::Arc; -use crate::DatabaseHandler; use anyhow::Context; use async_trait::async_trait; use bytes::Bytes; @@ -27,6 +26,8 @@ use denokv_remote::RemoteTransport; use http_body_util::BodyExt; use url::Url; +use crate::DatabaseHandler; + #[derive(Clone)] pub struct HttpOptions { pub user_agent: String, diff --git a/ext/kv/sqlite.rs b/ext/kv/sqlite.rs index 9de5209275..c28cfdd932 100644 --- a/ext/kv/sqlite.rs +++ b/ext/kv/sqlite.rs @@ -13,7 +13,6 @@ use std::sync::Arc; use std::sync::Mutex; use std::sync::OnceLock; -use crate::DatabaseHandler; use async_trait::async_trait; use deno_core::error::type_error; use deno_core::error::AnyError; @@ -27,6 +26,8 @@ use denokv_sqlite::SqliteNotifier; use rand::SeedableRng; use rusqlite::OpenFlags; +use crate::DatabaseHandler; + static SQLITE_NOTIFIERS_MAP: OnceLock>> = OnceLock::new(); diff --git a/ext/napi/js_native_api.rs b/ext/napi/js_native_api.rs index 53a12d6eba..a5b0e0d324 100644 --- a/ext/napi/js_native_api.rs +++ b/ext/napi/js_native_api.rs @@ -5,8 +5,10 @@ const NAPI_VERSION: u32 = 9; -use crate::*; +use std::ptr::NonNull; + use libc::INT_MAX; +use napi_sym::napi_sym; use super::util::check_new_from_utf8; use super::util::check_new_from_utf8_len; @@ -20,8 +22,7 @@ use crate::check_env; use crate::function::create_function; use crate::function::create_function_template; use crate::function::CallbackInfo; -use napi_sym::napi_sym; -use std::ptr::NonNull; +use crate::*; #[derive(Debug, Clone, Copy, PartialEq)] enum ReferenceOwnership { diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs index 6db6af48a2..dd7e5929c0 100644 --- a/ext/napi/lib.rs +++ b/ext/napi/lib.rs @@ -22,17 +22,18 @@ pub mod util; pub mod uv; use core::ptr::NonNull; +use std::cell::RefCell; +use std::collections::HashMap; +use std::path::PathBuf; +use std::rc::Rc; +use std::thread_local; + use deno_core::op2; use deno_core::parking_lot::RwLock; use deno_core::url::Url; use deno_core::ExternalOpsTracker; use deno_core::OpState; use deno_core::V8CrossThreadTaskSpawner; -use std::cell::RefCell; -use std::collections::HashMap; -use std::path::PathBuf; -use std::rc::Rc; -use std::thread_local; #[derive(Debug, thiserror::Error)] pub enum NApiError { @@ -46,20 +47,19 @@ pub enum NApiError { Permission(#[from] PermissionCheckError), } -#[cfg(unix)] -use libloading::os::unix::*; - -#[cfg(windows)] -use libloading::os::windows::*; +pub use std::ffi::CStr; +pub use std::os::raw::c_char; +pub use std::os::raw::c_void; +pub use std::ptr; // Expose common stuff for ease of use. // `use deno_napi::*` pub use deno_core::v8; use deno_permissions::PermissionCheckError; -pub use std::ffi::CStr; -pub use std::os::raw::c_char; -pub use std::os::raw::c_void; -pub use std::ptr; +#[cfg(unix)] +use libloading::os::unix::*; +#[cfg(windows)] +use libloading::os::windows::*; pub use value::napi_value; pub mod function; diff --git a/ext/napi/node_api.rs b/ext/napi/node_api.rs index 2ca5c8d0b4..abc3d32bd0 100644 --- a/ext/napi/node_api.rs +++ b/ext/napi/node_api.rs @@ -2,6 +2,17 @@ #![deny(unsafe_op_in_unsafe_fn)] +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicU8; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +use deno_core::parking_lot::Condvar; +use deno_core::parking_lot::Mutex; +use deno_core::V8CrossThreadTaskSpawner; +use napi_sym::napi_sym; + use super::util::get_array_buffer_ptr; use super::util::make_external_backing_store; use super::util::napi_clear_last_error; @@ -10,15 +21,6 @@ use super::util::SendPtr; use crate::check_arg; use crate::check_env; use crate::*; -use deno_core::parking_lot::Condvar; -use deno_core::parking_lot::Mutex; -use deno_core::V8CrossThreadTaskSpawner; -use napi_sym::napi_sym; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicU8; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; #[napi_sym] fn napi_module_register(module: *const NapiModule) -> napi_status { diff --git a/ext/napi/util.rs b/ext/napi/util.rs index 21e9d433aa..592536b502 100644 --- a/ext/napi/util.rs +++ b/ext/napi/util.rs @@ -1,7 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::*; use libc::INT_MAX; +use crate::*; + #[repr(transparent)] pub(crate) struct SendPtr(pub *const T); diff --git a/ext/napi/uv.rs b/ext/napi/uv.rs index ea6b539665..3dda65e627 100644 --- a/ext/napi/uv.rs +++ b/ext/napi/uv.rs @@ -1,10 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::*; -use deno_core::parking_lot::Mutex; use std::mem::MaybeUninit; use std::ptr::addr_of_mut; +use deno_core::parking_lot::Mutex; + +use crate::*; + fn assert_ok(res: c_int) -> c_int { if res != 0 { log::error!("bad result in uv polyfill: {res}"); @@ -15,11 +17,12 @@ fn assert_ok(res: c_int) -> c_int { res } +use std::ffi::c_int; + use js_native_api::napi_create_string_utf8; use node_api::napi_create_async_work; use node_api::napi_delete_async_work; use node_api::napi_queue_async_work; -use std::ffi::c_int; const UV_MUTEX_SIZE: usize = { #[cfg(unix)] diff --git a/ext/napi/value.rs b/ext/napi/value.rs index 71beac07e2..5bdfef0a00 100644 --- a/ext/napi/value.rs +++ b/ext/napi/value.rs @@ -1,11 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::v8; use std::mem::transmute; use std::ops::Deref; use std::os::raw::c_void; use std::ptr::NonNull; +use deno_core::v8; + /// An FFI-opaque, nullable wrapper around v8::Local. /// rusty_v8 Local handle cannot be empty but napi_value can be. #[repr(transparent)] diff --git a/ext/net/io.rs b/ext/net/io.rs index 2907fa398b..84f98668a9 100644 --- a/ext/net/io.rs +++ b/ext/net/io.rs @@ -1,5 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::rc::Rc; + use deno_core::futures::TryFutureExt; use deno_core::AsyncMutFuture; use deno_core::AsyncRefCell; @@ -9,14 +12,11 @@ use deno_core::CancelTryFuture; use deno_core::RcRef; use deno_core::Resource; use socket2::SockRef; -use std::borrow::Cow; -use std::rc::Rc; use tokio::io::AsyncRead; use tokio::io::AsyncReadExt; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; use tokio::net::tcp; - #[cfg(unix)] use tokio::net::unix; diff --git a/ext/net/lib.rs b/ext/net/lib.rs index 04b3f80010..18e00c5ac3 100644 --- a/ext/net/lib.rs +++ b/ext/net/lib.rs @@ -10,15 +10,16 @@ pub mod raw; pub mod resolve_addr; pub mod tcp; +use std::borrow::Cow; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + use deno_core::error::AnyError; use deno_core::OpState; use deno_permissions::PermissionCheckError; use deno_tls::rustls::RootCertStore; use deno_tls::RootCertStoreProvider; -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; pub const UNSTABLE_FEATURE_NAME: &str = "net"; @@ -204,9 +205,10 @@ deno_core::extension!(deno_net, /// Stub ops for non-unix platforms. #[cfg(not(unix))] mod ops_unix { - use crate::NetPermissions; use deno_core::op2; + use crate::NetPermissions; + macro_rules! stub_op { ($name:ident) => { #[op2(fast)] diff --git a/ext/net/ops.rs b/ext/net/ops.rs index 16148ac90d..f8f22cb41b 100644 --- a/ext/net/ops.rs +++ b/ext/net/ops.rs @@ -1,16 +1,17 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::io::TcpStreamResource; -use crate::raw::NetworkListenerResource; -use crate::resolve_addr::resolve_addr; -use crate::resolve_addr::resolve_addr_sync; -use crate::tcp::TcpListener; -use crate::NetPermissions; -use deno_core::op2; -use deno_core::CancelFuture; +use std::borrow::Cow; +use std::cell::RefCell; +use std::net::Ipv4Addr; +use std::net::Ipv6Addr; +use std::net::SocketAddr; +use std::rc::Rc; +use std::str::FromStr; +use deno_core::op2; use deno_core::AsyncRefCell; use deno_core::ByteString; +use deno_core::CancelFuture; use deno_core::CancelHandle; use deno_core::CancelTryFuture; use deno_core::JsBuffer; @@ -35,16 +36,16 @@ use socket2::Domain; use socket2::Protocol; use socket2::Socket; use socket2::Type; -use std::borrow::Cow; -use std::cell::RefCell; -use std::net::Ipv4Addr; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::rc::Rc; -use std::str::FromStr; use tokio::net::TcpStream; use tokio::net::UdpSocket; +use crate::io::TcpStreamResource; +use crate::raw::NetworkListenerResource; +use crate::resolve_addr::resolve_addr; +use crate::resolve_addr::resolve_addr_sync; +use crate::tcp::TcpListener; +use crate::NetPermissions; + #[derive(Serialize, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct TlsHandshakeInfo { @@ -834,7 +835,14 @@ fn rdata_to_return_record( #[cfg(test)] mod tests { - use super::*; + use std::net::Ipv4Addr; + use std::net::Ipv6Addr; + use std::net::ToSocketAddrs; + use std::path::Path; + use std::path::PathBuf; + use std::sync::Arc; + use std::sync::Mutex; + use deno_core::futures::FutureExt; use deno_core::JsRuntime; use deno_core::RuntimeOptions; @@ -855,13 +863,8 @@ mod tests { use hickory_proto::rr::record_data::RData; use hickory_proto::rr::Name; use socket2::SockRef; - use std::net::Ipv4Addr; - use std::net::Ipv6Addr; - use std::net::ToSocketAddrs; - use std::path::Path; - use std::path::PathBuf; - use std::sync::Arc; - use std::sync::Mutex; + + use super::*; #[test] fn rdata_to_return_record_a() { diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs index 4d2073fd09..4d715a8647 100644 --- a/ext/net/ops_tls.rs +++ b/ext/net/ops_tls.rs @@ -1,16 +1,17 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::io::TcpStreamResource; -use crate::ops::IpAddr; -use crate::ops::NetError; -use crate::ops::TlsHandshakeInfo; -use crate::raw::NetworkListenerResource; -use crate::resolve_addr::resolve_addr; -use crate::resolve_addr::resolve_addr_sync; -use crate::tcp::TcpListener; -use crate::DefaultTlsOptions; -use crate::NetPermissions; -use crate::UnsafelyIgnoreCertificateErrors; +use std::borrow::Cow; +use std::cell::RefCell; +use std::convert::From; +use std::fs::File; +use std::io::BufReader; +use std::io::ErrorKind; +use std::io::Read; +use std::net::SocketAddr; +use std::num::NonZeroUsize; +use std::rc::Rc; +use std::sync::Arc; + use deno_core::futures::TryFutureExt; use deno_core::op2; use deno_core::v8; @@ -35,25 +36,25 @@ use deno_tls::TlsKey; use deno_tls::TlsKeyLookup; use deno_tls::TlsKeys; use deno_tls::TlsKeysHolder; +pub use rustls_tokio_stream::TlsStream; use rustls_tokio_stream::TlsStreamRead; use rustls_tokio_stream::TlsStreamWrite; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::convert::From; -use std::fs::File; -use std::io::BufReader; -use std::io::ErrorKind; -use std::io::Read; -use std::net::SocketAddr; -use std::num::NonZeroUsize; -use std::rc::Rc; -use std::sync::Arc; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; use tokio::net::TcpStream; -pub use rustls_tokio_stream::TlsStream; +use crate::io::TcpStreamResource; +use crate::ops::IpAddr; +use crate::ops::NetError; +use crate::ops::TlsHandshakeInfo; +use crate::raw::NetworkListenerResource; +use crate::resolve_addr::resolve_addr; +use crate::resolve_addr::resolve_addr_sync; +use crate::tcp::TcpListener; +use crate::DefaultTlsOptions; +use crate::NetPermissions; +use crate::UnsafelyIgnoreCertificateErrors; pub(crate) const TLS_BUFFER_SIZE: Option = NonZeroUsize::new(65536); diff --git a/ext/net/ops_unix.rs b/ext/net/ops_unix.rs index 483dc99b40..4d10d1b622 100644 --- a/ext/net/ops_unix.rs +++ b/ext/net/ops_unix.rs @@ -1,9 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::io::UnixStreamResource; -use crate::ops::NetError; -use crate::raw::NetworkListenerResource; -use crate::NetPermissions; +use std::borrow::Cow; +use std::cell::RefCell; +use std::path::Path; +use std::rc::Rc; + use deno_core::op2; use deno_core::AsyncRefCell; use deno_core::CancelHandle; @@ -15,14 +16,15 @@ use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::path::Path; -use std::rc::Rc; use tokio::net::UnixDatagram; use tokio::net::UnixListener; pub use tokio::net::UnixStream; +use crate::io::UnixStreamResource; +use crate::ops::NetError; +use crate::raw::NetworkListenerResource; +use crate::NetPermissions; + /// A utility function to map OsStrings to Strings pub fn into_string(s: std::ffi::OsString) -> Result { s.into_string().map_err(NetError::InvalidUtf8) diff --git a/ext/net/quic.rs b/ext/net/quic.rs index 16f68364be..be9be2a817 100644 --- a/ext/net/quic.rs +++ b/ext/net/quic.rs @@ -1,9 +1,20 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::resolve_addr::resolve_addr; -use crate::DefaultTlsOptions; -use crate::NetPermissions; -use crate::UnsafelyIgnoreCertificateErrors; +use std::borrow::Cow; +use std::cell::RefCell; +use std::future::Future; +use std::net::IpAddr; +use std::net::Ipv4Addr; +use std::net::Ipv6Addr; +use std::net::SocketAddrV4; +use std::net::SocketAddrV6; +use std::pin::pin; +use std::rc::Rc; +use std::sync::Arc; +use std::task::Context; +use std::task::Poll; +use std::time::Duration; + use deno_core::error::bad_resource; use deno_core::error::generic_error; use deno_core::error::AnyError; @@ -27,20 +38,11 @@ use quinn::crypto::rustls::QuicClientConfig; use quinn::crypto::rustls::QuicServerConfig; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::future::Future; -use std::net::IpAddr; -use std::net::Ipv4Addr; -use std::net::Ipv6Addr; -use std::net::SocketAddrV4; -use std::net::SocketAddrV6; -use std::pin::pin; -use std::rc::Rc; -use std::sync::Arc; -use std::task::Context; -use std::task::Poll; -use std::time::Duration; + +use crate::resolve_addr::resolve_addr; +use crate::DefaultTlsOptions; +use crate::NetPermissions; +use crate::UnsafelyIgnoreCertificateErrors; #[derive(Debug, Deserialize, Serialize)] struct Addr { diff --git a/ext/net/raw.rs b/ext/net/raw.rs index a2ebfb5acb..a7134017c5 100644 --- a/ext/net/raw.rs +++ b/ext/net/raw.rs @@ -1,6 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::io::TcpStreamResource; -use crate::ops_tls::TlsStreamResource; +use std::borrow::Cow; +use std::rc::Rc; + use deno_core::error::bad_resource_id; use deno_core::error::custom_error; use deno_core::error::AnyError; @@ -9,8 +10,9 @@ use deno_core::CancelHandle; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ResourceTable; -use std::borrow::Cow; -use std::rc::Rc; + +use crate::io::TcpStreamResource; +use crate::ops_tls::TlsStreamResource; pub trait NetworkStreamTrait: Into { type Resource; diff --git a/ext/net/resolve_addr.rs b/ext/net/resolve_addr.rs index 3a97081eac..137af6c19f 100644 --- a/ext/net/resolve_addr.rs +++ b/ext/net/resolve_addr.rs @@ -2,6 +2,7 @@ use std::net::SocketAddr; use std::net::ToSocketAddrs; + use tokio::net::lookup_host; /// Resolve network address *asynchronously*. @@ -38,12 +39,13 @@ fn make_addr_port_pair(hostname: &str, port: u16) -> (&str, u16) { #[cfg(test)] mod tests { - use super::*; use std::net::Ipv4Addr; use std::net::Ipv6Addr; use std::net::SocketAddrV4; use std::net::SocketAddrV6; + use super::*; + #[tokio::test] async fn resolve_addr1() { let expected = vec![SocketAddr::V4(SocketAddrV4::new( diff --git a/ext/node/ops/blocklist.rs b/ext/node/ops/blocklist.rs index 6c64d68eca..5fce0d017e 100644 --- a/ext/node/ops/blocklist.rs +++ b/ext/node/ops/blocklist.rs @@ -9,7 +9,6 @@ use std::net::SocketAddr; use deno_core::op2; use deno_core::OpState; - use ipnetwork::IpNetwork; use ipnetwork::Ipv4Network; use ipnetwork::Ipv6Network; diff --git a/ext/node/ops/crypto/cipher.rs b/ext/node/ops/crypto/cipher.rs index 7f5b108a04..cedd092e81 100644 --- a/ext/node/ops/crypto/cipher.rs +++ b/ext/node/ops/crypto/cipher.rs @@ -1,5 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; + use aes::cipher::block_padding::Pkcs7; use aes::cipher::BlockDecryptMut; use aes::cipher::BlockEncryptMut; @@ -8,10 +12,6 @@ use deno_core::Resource; use digest::generic_array::GenericArray; use digest::KeyInit; -use std::borrow::Cow; -use std::cell::RefCell; -use std::rc::Rc; - type Tag = Option>; type Aes128Gcm = aead_gcm_stream::AesGcm; diff --git a/ext/node/ops/crypto/dh.rs b/ext/node/ops/crypto/dh.rs index ff2bd030eb..3da2d15fec 100644 --- a/ext/node/ops/crypto/dh.rs +++ b/ext/node/ops/crypto/dh.rs @@ -1,10 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::primes::Prime; use num_bigint_dig::BigUint; use num_bigint_dig::RandBigInt; use num_traits::FromPrimitive; +use super::primes::Prime; + #[derive(Clone)] pub struct PublicKey(BigUint); diff --git a/ext/node/ops/crypto/digest.rs b/ext/node/ops/crypto/digest.rs index a7d8fb51f1..dda62053bf 100644 --- a/ext/node/ops/crypto/digest.rs +++ b/ext/node/ops/crypto/digest.rs @@ -1,11 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::cell::RefCell; +use std::rc::Rc; + use deno_core::GarbageCollected; use digest::Digest; use digest::DynDigest; use digest::ExtendableOutput; use digest::Update; -use std::cell::RefCell; -use std::rc::Rc; pub struct Hasher { pub hash: Rc>>, diff --git a/ext/node/ops/crypto/md5_sha1.rs b/ext/node/ops/crypto/md5_sha1.rs index 9164b0a1cb..e2e4844913 100644 --- a/ext/node/ops/crypto/md5_sha1.rs +++ b/ext/node/ops/crypto/md5_sha1.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use core::fmt; + use digest::core_api::AlgorithmName; use digest::core_api::BlockSizeUser; use digest::core_api::Buffer; diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index e90e820909..2f650ba164 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -1,4 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::future::Future; +use std::rc::Rc; + use deno_core::error::generic_error; use deno_core::error::type_error; use deno_core::op2; @@ -16,16 +19,13 @@ use keys::EcPublicKey; use keys::KeyObjectHandle; use num_bigint::BigInt; use num_bigint_dig::BigUint; +use p224::NistP224; +use p256::NistP256; +use p384::NistP384; use rand::distributions::Distribution; use rand::distributions::Uniform; use rand::Rng; use ring::signature::Ed25519KeyPair; -use std::future::Future; -use std::rc::Rc; - -use p224::NistP224; -use p256::NistP256; -use p384::NistP384; use rsa::pkcs8::DecodePrivateKey; use rsa::pkcs8::DecodePublicKey; use rsa::Oaep; diff --git a/ext/node/ops/crypto/primes.rs b/ext/node/ops/crypto/primes.rs index 2e5d1ff63c..91297eb56c 100644 --- a/ext/node/ops/crypto/primes.rs +++ b/ext/node/ops/crypto/primes.rs @@ -1,12 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ops::Deref; + use num_bigint::BigInt; use num_bigint_dig::RandPrime; use num_integer::Integer; use num_traits::One; use num_traits::Zero; use rand::Rng; -use std::ops::Deref; #[derive(Clone)] pub struct Prime(pub num_bigint_dig::BigUint); @@ -283,9 +284,10 @@ static SMALL_PRIMES: [u32; 2047] = [ #[cfg(test)] mod tests { - use super::*; use num_bigint::BigInt; + use super::*; + #[test] fn test_prime() { for &p in SMALL_PRIMES.iter() { diff --git a/ext/node/ops/crypto/sign.rs b/ext/node/ops/crypto/sign.rs index 30094c0765..ccfd1a1165 100644 --- a/ext/node/ops/crypto/sign.rs +++ b/ext/node/ops/crypto/sign.rs @@ -1,24 +1,24 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use core::ops::Add; + +use ecdsa::der::MaxOverhead; +use ecdsa::der::MaxSize; +use elliptic_curve::generic_array::ArrayLength; +use elliptic_curve::FieldBytesSize; use rand::rngs::OsRng; use rsa::signature::hazmat::PrehashSigner as _; use rsa::signature::hazmat::PrehashVerifier as _; use rsa::traits::SignatureScheme as _; use spki::der::Decode; -use crate::ops::crypto::digest::match_fixed_digest; -use crate::ops::crypto::digest::match_fixed_digest_with_oid; - use super::keys::AsymmetricPrivateKey; use super::keys::AsymmetricPublicKey; use super::keys::EcPrivateKey; use super::keys::EcPublicKey; use super::keys::KeyObjectHandle; use super::keys::RsaPssHashAlgorithm; -use core::ops::Add; -use ecdsa::der::MaxOverhead; -use ecdsa::der::MaxSize; -use elliptic_curve::generic_array::ArrayLength; -use elliptic_curve::FieldBytesSize; +use crate::ops::crypto::digest::match_fixed_digest; +use crate::ops::crypto::digest::match_fixed_digest_with_oid; fn dsa_signature( encoding: u32, diff --git a/ext/node/ops/crypto/x509.rs b/ext/node/ops/crypto/x509.rs index ab8e52f703..ac80afb938 100644 --- a/ext/node/ops/crypto/x509.rs +++ b/ext/node/ops/crypto/x509.rs @@ -1,7 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::op2; +use std::ops::Deref; +use deno_core::op2; +use digest::Digest; use x509_parser::der_parser::asn1_rs::Any; use x509_parser::der_parser::asn1_rs::Tag; use x509_parser::der_parser::oid::Oid; @@ -9,14 +11,10 @@ pub use x509_parser::error::X509Error; use x509_parser::extensions; use x509_parser::pem; use x509_parser::prelude::*; - -use super::KeyObjectHandle; - -use std::ops::Deref; use yoke::Yoke; use yoke::Yokeable; -use digest::Digest; +use super::KeyObjectHandle; enum CertificateSources { Der(Box<[u8]>), diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs index 58a688a1fe..0f6b7afb11 100644 --- a/ext/node/ops/fs.rs +++ b/ext/node/ops/fs.rs @@ -207,6 +207,7 @@ where { use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; + use windows_sys::Win32::Storage::FileSystem::GetDiskFreeSpaceW; let _ = bigint; diff --git a/ext/node/ops/http.rs b/ext/node/ops/http.rs index eb28e68aee..ce5fdc163e 100644 --- a/ext/node/ops/http.rs +++ b/ext/node/ops/http.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::cell::RefCell; +use std::cmp::min; use std::fmt::Debug; use std::pin::Pin; use std::rc::Rc; @@ -48,7 +49,6 @@ use http_body_util::BodyExt; use hyper::body::Frame; use hyper::body::Incoming; use hyper_util::rt::TokioIo; -use std::cmp::min; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; diff --git a/ext/node/ops/idna.rs b/ext/node/ops/idna.rs index a3d85e77c2..eb037e6079 100644 --- a/ext/node/ops/idna.rs +++ b/ext/node/ops/idna.rs @@ -1,9 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::op2; - use std::borrow::Cow; +use deno_core::op2; + // map_domain, to_ascii and to_unicode are based on the punycode implementation in node.js // https://github.com/nodejs/node/blob/73025c4dec042e344eeea7912ed39f7b7c4a3991/lib/punycode.js diff --git a/ext/node/ops/inspector.rs b/ext/node/ops/inspector.rs index 9986aeb197..ecd1150e0c 100644 --- a/ext/node/ops/inspector.rs +++ b/ext/node/ops/inspector.rs @@ -1,6 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::NodePermissions; +use std::cell::RefCell; +use std::rc::Rc; + use deno_core::anyhow::Error; use deno_core::error::generic_error; use deno_core::futures::channel::mpsc; @@ -11,8 +13,8 @@ use deno_core::InspectorSessionKind; use deno_core::InspectorSessionOptions; use deno_core::JsRuntimeInspector; use deno_core::OpState; -use std::cell::RefCell; -use std::rc::Rc; + +use crate::NodePermissions; #[op2(fast)] pub fn op_inspector_enabled() -> bool { diff --git a/ext/node/ops/ipc.rs b/ext/node/ops/ipc.rs index 672cf0d707..8229ceadce 100644 --- a/ext/node/ops/ipc.rs +++ b/ext/node/ops/ipc.rs @@ -30,6 +30,9 @@ mod impl_ { use deno_core::RcRef; use deno_core::ResourceId; use deno_core::ToV8; + use deno_io::BiPipe; + use deno_io::BiPipeRead; + use deno_io::BiPipeWrite; use memchr::memchr; use pin_project_lite::pin_project; use serde::Serialize; @@ -37,10 +40,6 @@ mod impl_ { use tokio::io::AsyncWriteExt; use tokio::io::ReadBuf; - use deno_io::BiPipe; - use deno_io::BiPipeRead; - use deno_io::BiPipeWrite; - /// Wrapper around v8 value that implements Serialize. struct SerializeWrapper<'a, 'b>( RefCell<&'b mut v8::HandleScope<'a>>, @@ -624,13 +623,15 @@ mod impl_ { #[cfg(test)] mod tests { - use super::IpcJsonStreamResource; + use std::rc::Rc; + use deno_core::serde_json::json; use deno_core::v8; use deno_core::JsRuntime; use deno_core::RcRef; use deno_core::RuntimeOptions; - use std::rc::Rc; + + use super::IpcJsonStreamResource; #[allow(clippy::unused_async)] #[cfg(unix)] diff --git a/ext/node/ops/os/cpus.rs b/ext/node/ops/os/cpus.rs index 2b931884c3..c96b383822 100644 --- a/ext/node/ops/os/cpus.rs +++ b/ext/node/ops/os/cpus.rs @@ -127,13 +127,13 @@ pub fn cpu_info() -> Option> { #[cfg(target_os = "windows")] pub fn cpu_info() -> Option> { + use std::os::windows::ffi::OsStrExt; + use std::os::windows::ffi::OsStringExt; + use windows_sys::Wdk::System::SystemInformation::NtQuerySystemInformation; use windows_sys::Wdk::System::SystemInformation::SystemProcessorPerformanceInformation; use windows_sys::Win32::System::WindowsProgramming::SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; - use std::os::windows::ffi::OsStrExt; - use std::os::windows::ffi::OsStringExt; - fn encode_wide(s: &str) -> Vec { std::ffi::OsString::from(s) .encode_wide() diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs index ddb2a70c64..1cdf737f43 100644 --- a/ext/node/ops/os/mod.rs +++ b/ext/node/ops/os/mod.rs @@ -2,10 +2,11 @@ use std::mem::MaybeUninit; -use crate::NodePermissions; use deno_core::op2; use deno_core::OpState; +use crate::NodePermissions; + mod cpus; pub mod priority; diff --git a/ext/node/ops/perf_hooks.rs b/ext/node/ops/perf_hooks.rs index 636d0b2adb..256f879813 100644 --- a/ext/node/ops/perf_hooks.rs +++ b/ext/node/ops/perf_hooks.rs @@ -1,10 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::cell::Cell; + use deno_core::op2; use deno_core::GarbageCollected; -use std::cell::Cell; - #[derive(Debug, thiserror::Error)] pub enum PerfHooksError { #[error(transparent)] diff --git a/ext/node/ops/v8.rs b/ext/node/ops/v8.rs index 8f09314d1d..9c2dfa451a 100644 --- a/ext/node/ops/v8.rs +++ b/ext/node/ops/v8.rs @@ -1,11 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr::NonNull; + use deno_core::op2; use deno_core::v8; use deno_core::FastString; use deno_core::GarbageCollected; use deno_core::ToJsBuffer; -use std::ptr::NonNull; use v8::ValueDeserializerHelper; use v8::ValueSerializerHelper; diff --git a/ext/node/ops/vm.rs b/ext/node/ops/vm.rs index 25881cbaed..7720298e80 100644 --- a/ext/node/ops/vm.rs +++ b/ext/node/ops/vm.rs @@ -1,14 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::create_host_defined_options; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; +use std::time::Duration; + use deno_core::op2; use deno_core::serde_v8; use deno_core::v8; use deno_core::v8::MapFnTo; use deno_core::JsBuffer; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; -use std::time::Duration; + +use crate::create_host_defined_options; pub const PRIVATE_SYMBOL_NAME: v8::OneByteConst = v8::String::create_external_onebyte_const(b"node:contextify:context"); diff --git a/ext/node/ops/vm_internal.rs b/ext/node/ops/vm_internal.rs index 815f570ead..4c500786c5 100644 --- a/ext/node/ops/vm_internal.rs +++ b/ext/node/ops/vm_internal.rs @@ -1,11 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::create_host_defined_options; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::v8; use deno_core::v8::MapFnTo; +use crate::create_host_defined_options; + pub const PRIVATE_SYMBOL_NAME: v8::OneByteConst = v8::String::create_external_onebyte_const(b"node:contextify:context"); diff --git a/ext/node/ops/zlib/brotli.rs b/ext/node/ops/zlib/brotli.rs index 1a681ff7f7..7c798aa754 100644 --- a/ext/node/ops/zlib/brotli.rs +++ b/ext/node/ops/zlib/brotli.rs @@ -1,4 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::cell::RefCell; +use std::io::Read; + use brotli::enc::backward_references::BrotliEncoderMode; use brotli::enc::encode::BrotliEncoderCompress; use brotli::enc::encode::BrotliEncoderOperation; @@ -14,8 +17,6 @@ use deno_core::JsBuffer; use deno_core::OpState; use deno_core::Resource; use deno_core::ToJsBuffer; -use std::cell::RefCell; -use std::io::Read; #[derive(Debug, thiserror::Error)] pub enum BrotliError { diff --git a/ext/node/ops/zlib/mod.rs b/ext/node/ops/zlib/mod.rs index 991c0925d2..777faf6f86 100644 --- a/ext/node/ops/zlib/mod.rs +++ b/ext/node/ops/zlib/mod.rs @@ -1,9 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; + use deno_core::op2; use libc::c_ulong; -use std::borrow::Cow; -use std::cell::RefCell; use zlib::*; mod alloc; diff --git a/ext/node/ops/zlib/stream.rs b/ext/node/ops/zlib/stream.rs index afcdcc4d70..949e1230d8 100644 --- a/ext/node/ops/zlib/stream.rs +++ b/ext/node/ops/zlib/stream.rs @@ -1,11 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::mode::Flush; -use super::mode::Mode; use std::ffi::c_int; use std::ops::Deref; use std::ops::DerefMut; +use super::mode::Flush; +use super::mode::Mode; + pub struct StreamWrapper { pub strm: zlib::z_stream, } diff --git a/ext/telemetry/lib.rs b/ext/telemetry/lib.rs index 8018843dc4..ddf364a366 100644 --- a/ext/telemetry/lib.rs +++ b/ext/telemetry/lib.rs @@ -1,5 +1,20 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; +use std::env; +use std::fmt::Debug; +use std::pin::Pin; +use std::rc::Rc; +use std::sync::Arc; +use std::sync::Mutex; +use std::task::Context; +use std::task::Poll; +use std::thread; +use std::time::Duration; +use std::time::SystemTime; + use deno_core::anyhow; use deno_core::anyhow::anyhow; use deno_core::futures::channel::mpsc; @@ -57,20 +72,6 @@ use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_NAME; use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_VERSION; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::HashMap; -use std::env; -use std::fmt::Debug; -use std::pin::Pin; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::Mutex; -use std::task::Context; -use std::task::Poll; -use std::thread; -use std::time::Duration; -use std::time::SystemTime; use tokio::sync::oneshot; use tokio::task::JoinSet; @@ -469,6 +470,11 @@ impl DenoPeriodicReader { } mod hyper_client { + use std::fmt::Debug; + use std::pin::Pin; + use std::task::Poll; + use std::task::{self}; + use http_body_util::BodyExt; use http_body_util::Full; use hyper::body::Body as HttpBody; @@ -480,10 +486,6 @@ mod hyper_client { use opentelemetry_http::Request; use opentelemetry_http::Response; use opentelemetry_http::ResponseExt; - use std::fmt::Debug; - use std::pin::Pin; - use std::task::Poll; - use std::task::{self}; use super::OtelSharedRuntime; diff --git a/ext/tls/lib.rs b/ext/tls/lib.rs index 883d2995e4..a16e66ff92 100644 --- a/ext/tls/lib.rs +++ b/ext/tls/lib.rs @@ -1,32 +1,32 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -pub use deno_native_certs; -pub use rustls; -use rustls::pki_types::CertificateDer; -use rustls::pki_types::PrivateKeyDer; -use rustls::pki_types::ServerName; -pub use rustls_pemfile; -pub use rustls_tokio_stream::*; -pub use webpki; -pub use webpki_roots; - -use rustls::client::danger::HandshakeSignatureValid; -use rustls::client::danger::ServerCertVerified; -use rustls::client::danger::ServerCertVerifier; -use rustls::client::WebPkiServerVerifier; -use rustls::ClientConfig; -use rustls::DigitallySignedStruct; -use rustls::RootCertStore; -use rustls_pemfile::certs; -use rustls_pemfile::ec_private_keys; -use rustls_pemfile::pkcs8_private_keys; -use rustls_pemfile::rsa_private_keys; -use serde::Deserialize; use std::io::BufRead; use std::io::BufReader; use std::io::Cursor; use std::net::IpAddr; use std::sync::Arc; +pub use deno_native_certs; +pub use rustls; +use rustls::client::danger::HandshakeSignatureValid; +use rustls::client::danger::ServerCertVerified; +use rustls::client::danger::ServerCertVerifier; +use rustls::client::WebPkiServerVerifier; +use rustls::pki_types::CertificateDer; +use rustls::pki_types::PrivateKeyDer; +use rustls::pki_types::ServerName; +use rustls::ClientConfig; +use rustls::DigitallySignedStruct; +use rustls::RootCertStore; +pub use rustls_pemfile; +use rustls_pemfile::certs; +use rustls_pemfile::ec_private_keys; +use rustls_pemfile::pkcs8_private_keys; +use rustls_pemfile::rsa_private_keys; +pub use rustls_tokio_stream::*; +use serde::Deserialize; +pub use webpki; +pub use webpki_roots; + mod tls_key; pub use tls_key::*; diff --git a/ext/tls/tls_key.rs b/ext/tls/tls_key.rs index b7baa604b9..a11a2d8106 100644 --- a/ext/tls/tls_key.rs +++ b/ext/tls/tls_key.rs @@ -11,12 +11,6 @@ //! key lookup can handle closing one end of the pair, in which case they will just //! attempt to clean up the associated resources. -use deno_core::futures::future::poll_fn; -use deno_core::futures::future::Either; -use deno_core::futures::FutureExt; -use deno_core::unsync::spawn; -use rustls::ServerConfig; -use rustls_tokio_stream::ServerConfigProvider; use std::cell::RefCell; use std::collections::HashMap; use std::fmt::Debug; @@ -25,6 +19,13 @@ use std::future::Future; use std::io::ErrorKind; use std::rc::Rc; use std::sync::Arc; + +use deno_core::futures::future::poll_fn; +use deno_core::futures::future::Either; +use deno_core::futures::FutureExt; +use deno_core::unsync::spawn; +use rustls::ServerConfig; +use rustls_tokio_stream::ServerConfigProvider; use tokio::sync::broadcast; use tokio::sync::mpsc; use tokio::sync::oneshot; @@ -269,9 +270,10 @@ impl TlsKeyLookup { #[cfg(test)] pub mod tests { - use super::*; use deno_core::unsync::spawn; + use super::*; + fn tls_key_for_test(sni: &str) -> TlsKey { let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); diff --git a/ext/url/benches/url_ops.rs b/ext/url/benches/url_ops.rs index 70afb96db2..acde8be6bb 100644 --- a/ext/url/benches/url_ops.rs +++ b/ext/url/benches/url_ops.rs @@ -4,7 +4,6 @@ use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::Bencher; - use deno_core::Extension; fn setup() -> Vec { diff --git a/ext/url/lib.rs b/ext/url/lib.rs index f8946532ae..d012d35998 100644 --- a/ext/url/lib.rs +++ b/ext/url/lib.rs @@ -2,6 +2,8 @@ mod urlpattern; +use std::path::PathBuf; + use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::op2; @@ -10,13 +12,11 @@ use deno_core::url::quirks; use deno_core::url::Url; use deno_core::JsBuffer; use deno_core::OpState; -use std::path::PathBuf; +pub use urlpattern::UrlPatternError; use crate::urlpattern::op_urlpattern_parse; use crate::urlpattern::op_urlpattern_process_match_input; -pub use urlpattern::UrlPatternError; - deno_core::extension!( deno_url, deps = [deno_webidl], diff --git a/ext/url/urlpattern.rs b/ext/url/urlpattern.rs index 7d4e8ee71b..2ba159b896 100644 --- a/ext/url/urlpattern.rs +++ b/ext/url/urlpattern.rs @@ -1,7 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::op2; - use urlpattern::quirks; use urlpattern::quirks::MatchInput; use urlpattern::quirks::StringOrInit; diff --git a/ext/web/compression.rs b/ext/web/compression.rs index 6967009915..b6cc357839 100644 --- a/ext/web/compression.rs +++ b/ext/web/compression.rs @@ -1,5 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::cell::RefCell; +use std::io::Write; + use deno_core::op2; use flate2::write::DeflateDecoder; use flate2::write::DeflateEncoder; @@ -8,8 +11,6 @@ use flate2::write::GzEncoder; use flate2::write::ZlibDecoder; use flate2::write::ZlibEncoder; use flate2::Compression; -use std::cell::RefCell; -use std::io::Write; #[derive(Debug, thiserror::Error)] pub enum CompressionError { diff --git a/ext/web/lib.rs b/ext/web/lib.rs index af0fc2c276..a031a81ecb 100644 --- a/ext/web/lib.rs +++ b/ext/web/lib.rs @@ -6,17 +6,6 @@ mod message_port; mod stream_resource; mod timers; -use deno_core::op2; -use deno_core::url::Url; -use deno_core::v8; -use deno_core::ByteString; -use deno_core::ToJsBuffer; -use deno_core::U16String; - -use encoding_rs::CoderResult; -use encoding_rs::Decoder; -use encoding_rs::DecoderResult; -use encoding_rs::Encoding; use std::borrow::Cow; use std::cell::RefCell; use std::path::PathBuf; @@ -24,6 +13,16 @@ use std::sync::Arc; pub use blob::BlobError; pub use compression::CompressionError; +use deno_core::op2; +use deno_core::url::Url; +use deno_core::v8; +use deno_core::ByteString; +use deno_core::ToJsBuffer; +use deno_core::U16String; +use encoding_rs::CoderResult; +use encoding_rs::Decoder; +use encoding_rs::DecoderResult; +use encoding_rs::Encoding; pub use message_port::MessagePortError; pub use stream_resource::StreamResourceError; @@ -38,7 +37,6 @@ pub use crate::blob::Blob; pub use crate::blob::BlobPart; pub use crate::blob::BlobStore; pub use crate::blob::InMemoryBlobPart; - pub use crate::message_port::create_entangled_message_port; pub use crate::message_port::deserialize_js_transferables; use crate::message_port::op_message_port_create_entangled; @@ -49,7 +47,6 @@ pub use crate::message_port::serialize_transferables; pub use crate::message_port::JsMessageData; pub use crate::message_port::MessagePort; pub use crate::message_port::Transferable; - use crate::timers::op_defer; use crate::timers::op_now; use crate::timers::op_time_origin; diff --git a/ext/web/message_port.rs b/ext/web/message_port.rs index 1a4a09073d..c55b963e09 100644 --- a/ext/web/message_port.rs +++ b/ext/web/message_port.rs @@ -5,7 +5,6 @@ use std::cell::RefCell; use std::rc::Rc; use deno_core::op2; - use deno_core::CancelFuture; use deno_core::CancelHandle; use deno_core::DetachedBuffer; diff --git a/ext/web/stream_resource.rs b/ext/web/stream_resource.rs index c44a385ea9..cb0ce44073 100644 --- a/ext/web/stream_resource.rs +++ b/ext/web/stream_resource.rs @@ -1,4 +1,17 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::cell::RefMut; +use std::ffi::c_void; +use std::future::Future; +use std::marker::PhantomData; +use std::mem::MaybeUninit; +use std::pin::Pin; +use std::rc::Rc; +use std::task::Context; +use std::task::Poll; +use std::task::Waker; + use bytes::BytesMut; use deno_core::external; use deno_core::op2; @@ -17,18 +30,6 @@ use deno_core::Resource; use deno_core::ResourceId; use futures::future::poll_fn; use futures::TryFutureExt; -use std::borrow::Cow; -use std::cell::RefCell; -use std::cell::RefMut; -use std::ffi::c_void; -use std::future::Future; -use std::marker::PhantomData; -use std::mem::MaybeUninit; -use std::pin::Pin; -use std::rc::Rc; -use std::task::Context; -use std::task::Poll; -use std::task::Waker; #[derive(Debug, thiserror::Error)] pub enum StreamResourceError { @@ -607,13 +608,15 @@ impl Drop for ReadableStreamResourceData { #[cfg(test)] mod tests { - use super::*; - use deno_core::v8; use std::cell::OnceCell; use std::sync::atomic::AtomicUsize; use std::sync::OnceLock; use std::time::Duration; + use deno_core::v8; + + use super::*; + static V8_GLOBAL: OnceLock<()> = OnceLock::new(); thread_local! { diff --git a/ext/web/timers.rs b/ext/web/timers.rs index 06444ed34f..696f352de0 100644 --- a/ext/web/timers.rs +++ b/ext/web/timers.rs @@ -2,13 +2,14 @@ //! This module helps deno implement timers and performance APIs. -use deno_core::op2; -use deno_core::OpState; use std::time::Duration; use std::time::Instant; use std::time::SystemTime; use std::time::UNIX_EPOCH; +use deno_core::op2; +use deno_core::OpState; + pub trait TimersPermission { fn allow_hrtime(&mut self) -> bool; } diff --git a/ext/webgpu/binding.rs b/ext/webgpu/binding.rs index 41708acc7f..6d0d11db3f 100644 --- a/ext/webgpu/binding.rs +++ b/ext/webgpu/binding.rs @@ -1,13 +1,14 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::rc::Rc; + use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; diff --git a/ext/webgpu/buffer.rs b/ext/webgpu/buffer.rs index c2b53890e0..d3c15d978a 100644 --- a/ext/webgpu/buffer.rs +++ b/ext/webgpu/buffer.rs @@ -1,9 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; @@ -11,6 +7,11 @@ use std::sync::Arc; use std::sync::Mutex; use std::time::Duration; +use deno_core::op2; +use deno_core::OpState; +use deno_core::Resource; +use deno_core::ResourceId; + use super::error::WebGpuResult; #[derive(Debug, thiserror::Error)] diff --git a/ext/webgpu/bundle.rs b/ext/webgpu/bundle.rs index d9a5b29539..70bd64a77f 100644 --- a/ext/webgpu/bundle.rs +++ b/ext/webgpu/bundle.rs @@ -1,13 +1,14 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; + use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::rc::Rc; use super::error::WebGpuResult; diff --git a/ext/webgpu/byow.rs b/ext/webgpu/byow.rs index c9e1177b1e..dfc1678aef 100644 --- a/ext/webgpu/byow.rs +++ b/ext/webgpu/byow.rs @@ -1,8 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::op2; -use deno_core::OpState; -use deno_core::ResourceId; use std::ffi::c_void; #[cfg(any( target_os = "linux", @@ -12,6 +9,10 @@ use std::ffi::c_void; ))] use std::ptr::NonNull; +use deno_core::op2; +use deno_core::OpState; +use deno_core::ResourceId; + use crate::surface::WebGpuSurface; #[derive(Debug, thiserror::Error)] diff --git a/ext/webgpu/command_encoder.rs b/ext/webgpu/command_encoder.rs index 4bee7aac30..de1e6102c6 100644 --- a/ext/webgpu/command_encoder.rs +++ b/ext/webgpu/command_encoder.rs @@ -1,17 +1,18 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::WebGpuQuerySet; +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; + use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::rc::Rc; use super::error::WebGpuResult; +use crate::WebGpuQuerySet; pub(crate) struct WebGpuCommandEncoder( pub(crate) super::Instance, diff --git a/ext/webgpu/compute_pass.rs b/ext/webgpu/compute_pass.rs index 17043c7671..e0e11c19a5 100644 --- a/ext/webgpu/compute_pass.rs +++ b/ext/webgpu/compute_pass.rs @@ -1,12 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; + use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; -use std::borrow::Cow; -use std::cell::RefCell; use super::error::WebGpuResult; diff --git a/ext/webgpu/error.rs b/ext/webgpu/error.rs index f08f765386..516c3c8c35 100644 --- a/ext/webgpu/error.rs +++ b/ext/webgpu/error.rs @@ -1,9 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::convert::From; +use std::error::Error; + use deno_core::ResourceId; use serde::Serialize; -use std::convert::From; -use std::error::Error; use wgpu_core::binding_model::CreateBindGroupError; use wgpu_core::binding_model::CreateBindGroupLayoutError; use wgpu_core::binding_model::CreatePipelineLayoutError; diff --git a/ext/webgpu/lib.rs b/ext/webgpu/lib.rs index 5dc8278e41..a5a8ea5b95 100644 --- a/ext/webgpu/lib.rs +++ b/ext/webgpu/lib.rs @@ -2,21 +2,21 @@ #![cfg(not(target_arch = "wasm32"))] #![warn(unsafe_op_in_unsafe_fn)] +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashSet; +use std::rc::Rc; + use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +use error::WebGpuResult; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::HashSet; -use std::rc::Rc; pub use wgpu_core; pub use wgpu_types; -use error::WebGpuResult; - pub const UNSTABLE_FEATURE_NAME: &str = "webgpu"; #[macro_use] diff --git a/ext/webgpu/pipeline.rs b/ext/webgpu/pipeline.rs index a6b0cb8cec..62c1e340c8 100644 --- a/ext/webgpu/pipeline.rs +++ b/ext/webgpu/pipeline.rs @@ -1,5 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::collections::HashMap; +use std::rc::Rc; + use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; @@ -7,9 +11,6 @@ use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::collections::HashMap; -use std::rc::Rc; use super::error::WebGpuError; use super::error::WebGpuResult; diff --git a/ext/webgpu/queue.rs b/ext/webgpu/queue.rs index 8c8bbec95e..2d73f4fcc5 100644 --- a/ext/webgpu/queue.rs +++ b/ext/webgpu/queue.rs @@ -1,17 +1,18 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::command_encoder::WebGpuCommandBuffer; -use crate::Instance; +use std::borrow::Cow; +use std::rc::Rc; + use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; +use crate::command_encoder::WebGpuCommandBuffer; +use crate::Instance; pub struct WebGpuQueue(pub Instance, pub wgpu_core::id::QueueId); impl Resource for WebGpuQueue { diff --git a/ext/webgpu/render_pass.rs b/ext/webgpu/render_pass.rs index 9b9d87d9fc..e1bf2a5681 100644 --- a/ext/webgpu/render_pass.rs +++ b/ext/webgpu/render_pass.rs @@ -1,12 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; + use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::cell::RefCell; use super::error::WebGpuResult; diff --git a/ext/webgpu/sampler.rs b/ext/webgpu/sampler.rs index 9fc1269ea7..caeccdd5f3 100644 --- a/ext/webgpu/sampler.rs +++ b/ext/webgpu/sampler.rs @@ -1,12 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::rc::Rc; + use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; diff --git a/ext/webgpu/shader.rs b/ext/webgpu/shader.rs index 4653bd85bf..285a37f560 100644 --- a/ext/webgpu/shader.rs +++ b/ext/webgpu/shader.rs @@ -1,11 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::rc::Rc; + use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; diff --git a/ext/webgpu/surface.rs b/ext/webgpu/surface.rs index 297eaeb008..7471d567a1 100644 --- a/ext/webgpu/surface.rs +++ b/ext/webgpu/surface.rs @@ -1,15 +1,17 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use super::WebGpuResult; +use std::borrow::Cow; +use std::rc::Rc; + use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use wgpu_types::SurfaceStatus; +use super::WebGpuResult; + #[derive(Debug, thiserror::Error)] pub enum SurfaceError { #[error(transparent)] diff --git a/ext/webgpu/texture.rs b/ext/webgpu/texture.rs index f8a5e05a3e..4c95a4cb69 100644 --- a/ext/webgpu/texture.rs +++ b/ext/webgpu/texture.rs @@ -1,12 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::rc::Rc; + use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; -use std::borrow::Cow; -use std::rc::Rc; use super::error::WebGpuResult; pub(crate) struct WebGpuTexture { diff --git a/ext/webidl/benches/dict.rs b/ext/webidl/benches/dict.rs index cfb658fa84..1c0efc921c 100644 --- a/ext/webidl/benches/dict.rs +++ b/ext/webidl/benches/dict.rs @@ -4,7 +4,6 @@ use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::Bencher; - use deno_core::Extension; fn setup() -> Vec { diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index 5aef1a7a55..4dcead2a77 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -1,5 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::stream::WebSocketStream; +use std::borrow::Cow; +use std::cell::Cell; +use std::cell::RefCell; +use std::future::Future; +use std::num::NonZeroUsize; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + use bytes::Bytes; use deno_core::futures::TryFutureExt; use deno_core::op2; @@ -17,12 +25,20 @@ use deno_core::Resource; use deno_core::ResourceId; use deno_core::ToJsBuffer; use deno_net::raw::NetworkStream; +use deno_permissions::PermissionCheckError; use deno_tls::create_client_config; use deno_tls::rustls::ClientConfig; use deno_tls::rustls::ClientConnection; use deno_tls::RootCertStoreProvider; use deno_tls::SocketUse; use deno_tls::TlsKeys; +use fastwebsockets::CloseCode; +use fastwebsockets::FragmentCollectorRead; +use fastwebsockets::Frame; +use fastwebsockets::OpCode; +use fastwebsockets::Role; +use fastwebsockets::WebSocket; +use fastwebsockets::WebSocketWrite; use http::header::CONNECTION; use http::header::UPGRADE; use http::HeaderName; @@ -36,28 +52,13 @@ use rustls_tokio_stream::rustls::pki_types::ServerName; use rustls_tokio_stream::rustls::RootCertStore; use rustls_tokio_stream::TlsStream; use serde::Serialize; -use std::borrow::Cow; -use std::cell::Cell; -use std::cell::RefCell; -use std::future::Future; -use std::num::NonZeroUsize; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; use tokio::io::AsyncRead; use tokio::io::AsyncWrite; use tokio::io::ReadHalf; use tokio::io::WriteHalf; use tokio::net::TcpStream; -use deno_permissions::PermissionCheckError; -use fastwebsockets::CloseCode; -use fastwebsockets::FragmentCollectorRead; -use fastwebsockets::Frame; -use fastwebsockets::OpCode; -use fastwebsockets::Role; -use fastwebsockets::WebSocket; -use fastwebsockets::WebSocketWrite; +use crate::stream::WebSocketStream; mod stream; diff --git a/ext/websocket/stream.rs b/ext/websocket/stream.rs index 2cd2622175..6900922bb7 100644 --- a/ext/websocket/stream.rs +++ b/ext/websocket/stream.rs @@ -1,4 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::io::ErrorKind; +use std::pin::Pin; +use std::task::ready; +use std::task::Poll; + use bytes::Buf; use bytes::Bytes; use deno_net::raw::NetworkStream; @@ -6,10 +11,6 @@ use h2::RecvStream; use h2::SendStream; use hyper::upgrade::Upgraded; use hyper_util::rt::TokioIo; -use std::io::ErrorKind; -use std::pin::Pin; -use std::task::ready; -use std::task::Poll; use tokio::io::AsyncRead; use tokio::io::AsyncWrite; use tokio::io::ReadBuf; diff --git a/ext/webstorage/lib.rs b/ext/webstorage/lib.rs index c3e4c46596..6b14cbb08c 100644 --- a/ext/webstorage/lib.rs +++ b/ext/webstorage/lib.rs @@ -7,12 +7,11 @@ use std::path::PathBuf; use deno_core::op2; use deno_core::GarbageCollected; use deno_core::OpState; +pub use rusqlite; use rusqlite::params; use rusqlite::Connection; use rusqlite::OptionalExtension; -pub use rusqlite; - #[derive(Debug, thiserror::Error)] pub enum WebStorageError { #[error("LocalStorage is not supported in this context.")] diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 082940eb34..7b4b09a842 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -4,8 +4,14 @@ use std::fmt::Debug; use std::path::PathBuf; use boxed_error::Boxed; +pub use byonm::ByonmInNpmPackageChecker; +pub use byonm::ByonmNpmResolver; +pub use byonm::ByonmNpmResolverCreateOptions; +pub use byonm::ByonmNpmResolverRc; +pub use byonm::ByonmResolvePkgFolderFromDenoReqError; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; +pub use local::normalize_pkg_name_for_node_modules_deno_folder; use node_resolver::errors::NodeResolveError; use node_resolver::errors::NodeResolveErrorKind; use node_resolver::errors::PackageFolderResolveErrorKind; @@ -26,13 +32,6 @@ use sys_traits::FsReadDir; use thiserror::Error; use url::Url; -pub use byonm::ByonmInNpmPackageChecker; -pub use byonm::ByonmNpmResolver; -pub use byonm::ByonmNpmResolverCreateOptions; -pub use byonm::ByonmNpmResolverRc; -pub use byonm::ByonmResolvePkgFolderFromDenoReqError; -pub use local::normalize_pkg_name_for_node_modules_deno_folder; - mod byonm; mod local; diff --git a/resolvers/node/analyze.rs b/resolvers/node/analyze.rs index 2024e6a1e8..7f21473c10 100644 --- a/resolvers/node/analyze.rs +++ b/resolvers/node/analyze.rs @@ -6,6 +6,8 @@ use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; +use anyhow::Context; +use anyhow::Error as AnyError; use deno_path_util::url_from_file_path; use deno_path_util::url_to_file_path; use futures::future::LocalBoxFuture; @@ -13,9 +15,6 @@ use futures::stream::FuturesUnordered; use futures::FutureExt; use futures::StreamExt; use once_cell::sync::Lazy; - -use anyhow::Context; -use anyhow::Error as AnyError; use sys_traits::FsCanonicalize; use sys_traits::FsMetadata; use sys_traits::FsRead; diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs index 95631daf39..e2ee2799e6 100644 --- a/resolvers/node/resolution.rs +++ b/resolvers/node/resolution.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; use anyhow::bail; use anyhow::Error as AnyError; +use deno_package_json::PackageJson; use deno_path_util::url_from_file_path; use serde_json::Map; use serde_json::Value; @@ -48,7 +49,6 @@ use crate::npm::InNpmPackageCheckerRc; use crate::NpmPackageFolderResolverRc; use crate::PackageJsonResolverRc; use crate::PathClean; -use deno_package_json::PackageJson; pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"]; pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"]; diff --git a/resolvers/node/sync.rs b/resolvers/node/sync.rs index 3c4729aa2c..e8add4e0fc 100644 --- a/resolvers/node/sync.rs +++ b/resolvers/node/sync.rs @@ -6,10 +6,9 @@ pub use inner::*; mod inner { #![allow(clippy::disallowed_types)] - pub use std::sync::Arc as MaybeArc; - pub use core::marker::Send as MaybeSend; pub use core::marker::Sync as MaybeSync; + pub use std::sync::Arc as MaybeArc; } #[cfg(not(feature = "sync"))] diff --git a/resolvers/npm_cache/fs_util.rs b/resolvers/npm_cache/fs_util.rs index ed123f085c..d5a634e36f 100644 --- a/resolvers/npm_cache/fs_util.rs +++ b/resolvers/npm_cache/fs_util.rs @@ -1,10 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use anyhow::Context; -use anyhow::Error as AnyError; use std::io::ErrorKind; use std::path::Path; use std::time::Duration; + +use anyhow::Context; +use anyhow::Error as AnyError; use sys_traits::FsCreateDirAll; use sys_traits::FsDirEntry; use sys_traits::FsHardLink; diff --git a/resolvers/npm_cache/lib.rs b/resolvers/npm_cache/lib.rs index e681fa71ac..50614a239a 100644 --- a/resolvers/npm_cache/lib.rs +++ b/resolvers/npm_cache/lib.rs @@ -40,13 +40,12 @@ mod tarball; mod tarball_extract; pub use fs_util::hard_link_dir_recursive; -pub use registry_info::RegistryInfoProvider; -pub use tarball::TarballCache; - // todo(#27198): make both of these private and get the rest of the code // using RegistryInfoProvider. pub use registry_info::get_package_url; +pub use registry_info::RegistryInfoProvider; pub use remote::maybe_auth_header_for_npm_registry; +pub use tarball::TarballCache; #[derive(Debug)] pub struct DownloadError { diff --git a/runtime/errors.rs b/runtime/errors.rs index 01588c593b..e0f807d3ae 100644 --- a/runtime/errors.rs +++ b/runtime/errors.rs @@ -9,16 +9,11 @@ //! Diagnostics are compile-time type errors, whereas JsErrors are runtime //! exceptions. -use crate::ops::fs_events::FsEventsError; -use crate::ops::http::HttpStartError; -use crate::ops::os::OsError; -use crate::ops::permissions::PermissionError; -use crate::ops::process::CheckRunPermissionError; -use crate::ops::process::ProcessError; -use crate::ops::signal::SignalError; -use crate::ops::tty::TtyError; -use crate::ops::web_worker::SyncFetchError; -use crate::ops::worker_host::CreateWorkerError; +use std::env; +use std::error::Error; +use std::io; +use std::sync::Arc; + use deno_broadcast_channel::BroadcastChannelError; use deno_cache::CacheError; use deno_canvas::CanvasError; @@ -68,10 +63,17 @@ use deno_websocket::HandshakeError; use deno_websocket::WebsocketError; use deno_webstorage::WebStorageError; use rustyline::error::ReadlineError; -use std::env; -use std::error::Error; -use std::io; -use std::sync::Arc; + +use crate::ops::fs_events::FsEventsError; +use crate::ops::http::HttpStartError; +use crate::ops::os::OsError; +use crate::ops::permissions::PermissionError; +use crate::ops::process::CheckRunPermissionError; +use crate::ops::process::ProcessError; +use crate::ops::signal::SignalError; +use crate::ops::tty::TtyError; +use crate::ops::web_worker::SyncFetchError; +use crate::ops::worker_host::CreateWorkerError; fn get_run_descriptor_parse_error(e: &RunDescriptorParseError) -> &'static str { match e { @@ -1047,11 +1049,6 @@ fn get_fs_error(e: &FsError) -> &'static str { } mod node { - use super::get_error_class_name; - use super::get_io_error_class; - use super::get_permission_check_error_class; - use super::get_serde_json_error_class; - use super::get_url_parse_error_class; pub use deno_node::ops::blocklist::BlocklistError; pub use deno_node::ops::crypto::cipher::CipherContextError; pub use deno_node::ops::crypto::cipher::CipherError; @@ -1096,6 +1093,12 @@ mod node { pub use deno_node::ops::zlib::mode::ModeError; pub use deno_node::ops::zlib::ZlibError; + use super::get_error_class_name; + use super::get_io_error_class; + use super::get_permission_check_error_class; + use super::get_serde_json_error_class; + use super::get_url_parse_error_class; + pub fn get_blocklist_error(error: &BlocklistError) -> &'static str { match error { BlocklistError::AddrParse(_) => "Error", diff --git a/runtime/fmt_errors.rs b/runtime/fmt_errors.rs index 3c60c3a3d7..5af84c67bc 100644 --- a/runtime/fmt_errors.rs +++ b/runtime/fmt_errors.rs @@ -1,11 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. //! This mod provides DenoError to unify errors across Deno. +use std::fmt::Write as _; + use color_print::cformat; use color_print::cstr; use deno_core::error::format_frame; use deno_core::error::JsError; use deno_terminal::colors; -use std::fmt::Write as _; #[derive(Debug, Clone)] struct ErrorReference<'a> { @@ -490,9 +491,10 @@ pub fn format_js_error(js_error: &JsError) -> String { #[cfg(test)] mod tests { - use super::*; use test_util::strip_ansi_codes; + use super::*; + #[test] fn test_format_none_source_line() { let actual = format_maybe_source_line(None, None, false, 0); diff --git a/runtime/fs_util.rs b/runtime/fs_util.rs index a858e9770d..2fc34dd311 100644 --- a/runtime/fs_util.rs +++ b/runtime/fs_util.rs @@ -1,10 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::path::Path; +use std::path::PathBuf; + use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_path_util::normalize_path; -use std::path::Path; -use std::path::PathBuf; #[inline] pub fn resolve_from_cwd(path: &Path) -> Result { diff --git a/runtime/inspector_server.rs b/runtime/inspector_server.rs index a789dd3dca..cbe97738c7 100644 --- a/runtime/inspector_server.rs +++ b/runtime/inspector_server.rs @@ -2,6 +2,14 @@ // Alias for the future `!` type. use core::convert::Infallible as Never; +use std::cell::RefCell; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::pin::pin; +use std::process; +use std::rc::Rc; +use std::thread; + use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::futures::channel::mpsc; @@ -28,13 +36,6 @@ use fastwebsockets::OpCode; use fastwebsockets::WebSocket; use hyper::body::Bytes; use hyper_util::rt::TokioIo; -use std::cell::RefCell; -use std::collections::HashMap; -use std::net::SocketAddr; -use std::pin::pin; -use std::process; -use std::rc::Rc; -use std::thread; use tokio::net::TcpListener; use tokio::sync::broadcast; use uuid::Uuid; diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs index f6e5ceff5c..1b89199e37 100644 --- a/runtime/ops/fs_events.rs +++ b/runtime/ops/fs_events.rs @@ -1,5 +1,14 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::convert::From; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + +use deno_core::op2; use deno_core::parking_lot::Mutex; use deno_core::AsyncRefCell; use deno_core::CancelFuture; @@ -8,9 +17,6 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; - -use deno_core::op2; - use deno_permissions::PermissionsContainer; use notify::event::Event as NotifyEvent; use notify::event::ModifyKind; @@ -20,13 +26,6 @@ use notify::RecommendedWatcher; use notify::RecursiveMode; use notify::Watcher; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::convert::From; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; use tokio::sync::mpsc; deno_core::extension!( diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs index 71169217a7..3b767fd94f 100644 --- a/runtime/ops/os/mod.rs +++ b/runtime/ops/os/mod.rs @@ -1,7 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::sys_info; -use crate::worker::ExitCode; +use std::collections::HashMap; +use std::env; + use deno_core::op2; use deno_core::v8; use deno_core::OpState; @@ -9,8 +10,9 @@ use deno_node::NODE_ENV_VAR_ALLOWLIST; use deno_path_util::normalize_path; use deno_permissions::PermissionsContainer; use serde::Serialize; -use std::collections::HashMap; -use std::env; + +use crate::sys_info; +use crate::worker::ExitCode; deno_core::extension!( deno_os, diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index 422f229632..f4064367b9 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -1,5 +1,21 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; +use std::ffi::OsString; +use std::io::Write; +#[cfg(unix)] +use std::os::unix::prelude::ExitStatusExt; +#[cfg(unix)] +use std::os::unix::process::CommandExt; +#[cfg(windows)] +use std::os::windows::process::CommandExt; +use std::path::Path; +use std::path::PathBuf; +use std::process::ExitStatus; +use std::rc::Rc; + use deno_core::op2; use deno_core::serde_json; use deno_core::AsyncMutFuture; @@ -18,25 +34,9 @@ use deno_permissions::PermissionsContainer; use deno_permissions::RunQueryDescriptor; use serde::Deserialize; use serde::Serialize; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::HashMap; -use std::ffi::OsString; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; -use std::process::ExitStatus; -use std::rc::Rc; use tokio::process::Command; -#[cfg(windows)] -use std::os::windows::process::CommandExt; - use crate::ops::signal::SignalError; -#[cfg(unix)] -use std::os::unix::prelude::ExitStatusExt; -#[cfg(unix)] -use std::os::unix::process::CommandExt; pub const UNSTABLE_FEATURE_NAME: &str = "process"; @@ -1087,6 +1087,7 @@ mod deprecated { pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> { use std::io::Error; use std::io::ErrorKind::NotFound; + use winapi::shared::minwindef::DWORD; use winapi::shared::minwindef::FALSE; use winapi::shared::minwindef::TRUE; diff --git a/runtime/ops/runtime.rs b/runtime/ops/runtime.rs index 8d54783fc9..4b30ab8d8d 100644 --- a/runtime/ops/runtime.rs +++ b/runtime/ops/runtime.rs @@ -34,6 +34,7 @@ pub fn op_ppid() -> i64 { // - Apache License, Version 2.0 // - MIT license use std::mem; + use winapi::shared::minwindef::DWORD; use winapi::um::handleapi::CloseHandle; use winapi::um::handleapi::INVALID_HANDLE_VALUE; diff --git a/runtime/ops/signal.rs b/runtime/ops/signal.rs index ef87c37297..85c883021c 100644 --- a/runtime/ops/signal.rs +++ b/runtime/ops/signal.rs @@ -1,13 +1,4 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::op2; -use deno_core::AsyncRefCell; -use deno_core::CancelFuture; -use deno_core::CancelHandle; -use deno_core::OpState; -use deno_core::RcRef; -use deno_core::Resource; -use deno_core::ResourceId; - use std::borrow::Cow; use std::cell::RefCell; #[cfg(unix)] @@ -18,6 +9,14 @@ use std::sync::atomic::AtomicBool; #[cfg(unix)] use std::sync::Arc; +use deno_core::op2; +use deno_core::AsyncRefCell; +use deno_core::CancelFuture; +use deno_core::CancelHandle; +use deno_core::OpState; +use deno_core::RcRef; +use deno_core::Resource; +use deno_core::ResourceId; #[cfg(unix)] use tokio::signal::unix::signal; #[cfg(unix)] diff --git a/runtime/ops/tty.rs b/runtime/ops/tty.rs index 7849185faa..0b3c2e0064 100644 --- a/runtime/ops/tty.rs +++ b/runtime/ops/tty.rs @@ -1,9 +1,23 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +#[cfg(unix)] +use std::cell::RefCell; +#[cfg(unix)] +use std::collections::HashMap; use std::io::Error; +#[cfg(windows)] +use std::sync::Arc; use deno_core::op2; +#[cfg(windows)] +use deno_core::parking_lot::Mutex; use deno_core::OpState; +#[cfg(unix)] +use deno_core::ResourceId; +#[cfg(windows)] +use deno_io::WinTtyState; +#[cfg(unix)] +use nix::sys::termios; use rustyline::config::Configurer; use rustyline::error::ReadlineError; use rustyline::Cmd; @@ -12,22 +26,6 @@ use rustyline::KeyCode; use rustyline::KeyEvent; use rustyline::Modifiers; -#[cfg(windows)] -use deno_core::parking_lot::Mutex; -#[cfg(windows)] -use deno_io::WinTtyState; -#[cfg(windows)] -use std::sync::Arc; - -#[cfg(unix)] -use deno_core::ResourceId; -#[cfg(unix)] -use nix::sys::termios; -#[cfg(unix)] -use std::cell::RefCell; -#[cfg(unix)] -use std::collections::HashMap; - #[cfg(unix)] #[derive(Default, Clone)] struct TtyModeStore( @@ -116,7 +114,6 @@ fn op_set_raw( #[cfg(windows)] { use winapi::shared::minwindef::FALSE; - use winapi::um::consoleapi; let handle = handle_or_fd; diff --git a/runtime/ops/web_worker.rs b/runtime/ops/web_worker.rs index d0c3eea668..34466f8db9 100644 --- a/runtime/ops/web_worker.rs +++ b/runtime/ops/web_worker.rs @@ -2,18 +2,19 @@ mod sync_fetch; -use crate::web_worker::WebWorkerInternalHandle; -use crate::web_worker::WebWorkerType; +use std::cell::RefCell; +use std::rc::Rc; + use deno_core::op2; use deno_core::CancelFuture; use deno_core::OpState; use deno_web::JsMessageData; use deno_web::MessagePortError; -use std::cell::RefCell; -use std::rc::Rc; +pub use sync_fetch::SyncFetchError; use self::sync_fetch::op_worker_sync_fetch; -pub use sync_fetch::SyncFetchError; +use crate::web_worker::WebWorkerInternalHandle; +use crate::web_worker::WebWorkerType; deno_core::extension!( deno_web_worker, diff --git a/runtime/ops/web_worker/sync_fetch.rs b/runtime/ops/web_worker/sync_fetch.rs index 508bcb7bb0..a774e6db34 100644 --- a/runtime/ops/web_worker/sync_fetch.rs +++ b/runtime/ops/web_worker/sync_fetch.rs @@ -2,8 +2,6 @@ use std::sync::Arc; -use crate::web_worker::WebWorkerInternalHandle; -use crate::web_worker::WebWorkerType; use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::url::Url; @@ -16,6 +14,9 @@ use hyper::body::Bytes; use serde::Deserialize; use serde::Serialize; +use crate::web_worker::WebWorkerInternalHandle; +use crate::web_worker::WebWorkerType; + // TODO(andreubotella) Properly parse the MIME type fn mime_type_essence(mime_type: &str) -> String { let essence = match mime_type.split_once(';') { diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs index 131bad1962..fafce5dccd 100644 --- a/runtime/ops/worker_host.rs +++ b/runtime/ops/worker_host.rs @@ -1,15 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::ops::TestingFeaturesEnabled; -use crate::web_worker::run_web_worker; -use crate::web_worker::SendableWebWorkerHandle; -use crate::web_worker::WebWorker; -use crate::web_worker::WebWorkerHandle; -use crate::web_worker::WebWorkerType; -use crate::web_worker::WorkerControlEvent; -use crate::web_worker::WorkerId; -use crate::web_worker::WorkerMetadata; -use crate::worker::FormatJsErrorFn; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; +use std::sync::Arc; + use deno_core::op2; use deno_core::serde::Deserialize; use deno_core::CancelFuture; @@ -22,10 +17,17 @@ use deno_web::deserialize_js_transferables; use deno_web::JsMessageData; use deno_web::MessagePortError; use log::debug; -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; -use std::sync::Arc; + +use crate::ops::TestingFeaturesEnabled; +use crate::web_worker::run_web_worker; +use crate::web_worker::SendableWebWorkerHandle; +use crate::web_worker::WebWorker; +use crate::web_worker::WebWorkerHandle; +use crate::web_worker::WebWorkerType; +use crate::web_worker::WorkerControlEvent; +use crate::web_worker::WorkerId; +use crate::web_worker::WorkerMetadata; +use crate::worker::FormatJsErrorFn; pub const UNSTABLE_FEATURE_NAME: &str = "worker-options"; diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 1c5fb36f93..8730ba20d1 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -1,5 +1,18 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; +use std::collections::HashSet; +use std::ffi::OsStr; +use std::fmt; +use std::fmt::Debug; +use std::hash::Hash; +use std::net::IpAddr; +use std::net::Ipv6Addr; +use std::path::Path; +use std::path::PathBuf; +use std::string::ToString; +use std::sync::Arc; + use capacity_builder::StringBuilder; use deno_core::parking_lot::Mutex; use deno_core::serde::de; @@ -15,28 +28,15 @@ use deno_path_util::url_to_file_path; use deno_terminal::colors; use fqdn::FQDN; use once_cell::sync::Lazy; -use std::borrow::Cow; -use std::collections::HashSet; -use std::ffi::OsStr; -use std::fmt; -use std::fmt::Debug; -use std::hash::Hash; -use std::net::IpAddr; -use std::net::Ipv6Addr; -use std::path::Path; -use std::path::PathBuf; -use std::string::ToString; -use std::sync::Arc; pub mod prompter; use prompter::permission_prompt; -use prompter::PERMISSION_EMOJI; - pub use prompter::set_prompt_callbacks; pub use prompter::set_prompter; pub use prompter::PermissionPrompter; pub use prompter::PromptCallback; pub use prompter::PromptResponse; +use prompter::PERMISSION_EMOJI; #[derive(Debug, thiserror::Error)] pub enum PermissionDeniedError { @@ -3691,11 +3691,13 @@ pub fn is_standalone() -> bool { #[cfg(test)] mod tests { - use super::*; + use std::net::Ipv4Addr; + use deno_core::serde_json::json; use fqdn::fqdn; use prompter::tests::*; - use std::net::Ipv4Addr; + + use super::*; // Creates vector of strings, Vec macro_rules! svec { diff --git a/runtime/permissions/prompter.rs b/runtime/permissions/prompter.rs index 0272744cc2..19f94434ca 100644 --- a/runtime/permissions/prompter.rs +++ b/runtime/permissions/prompter.rs @@ -1,10 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::is_standalone; -use deno_core::error::JsStackFrame; -use deno_core::parking_lot::Mutex; -use deno_terminal::colors; -use once_cell::sync::Lazy; use std::fmt::Write; use std::io::BufRead; use std::io::IsTerminal; @@ -12,6 +7,13 @@ use std::io::StderrLock; use std::io::StdinLock; use std::io::Write as IoWrite; +use deno_core::error::JsStackFrame; +use deno_core::parking_lot::Mutex; +use deno_terminal::colors; +use once_cell::sync::Lazy; + +use crate::is_standalone; + /// Helper function to make control characters visible so users can see the underlying filename. fn escape_control_characters(s: &str) -> std::borrow::Cow { if !s.contains(|c: char| c.is_ascii_control() || c.is_control()) { @@ -489,10 +491,11 @@ impl PermissionPrompter for TtyPrompter { #[cfg(test)] pub mod tests { - use super::*; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; + use super::*; + pub struct TestPrompter; impl PermissionPrompter for TestPrompter { diff --git a/runtime/shared.rs b/runtime/shared.rs index f7d76f67a7..ce4350237f 100644 --- a/runtime/shared.rs +++ b/runtime/shared.rs @@ -1,6 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Utilities shared between `build.rs` and the rest of the crate. +use std::path::Path; + use deno_ast::MediaType; use deno_ast::ParseParams; use deno_ast::SourceMapOption; @@ -10,7 +12,6 @@ use deno_core::Extension; use deno_core::ModuleCodeString; use deno_core::ModuleName; use deno_core::SourceMapData; -use std::path::Path; extension!(runtime, deps = [ diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index ad73f485ad..751cc43f2d 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -1,9 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::ops; -use crate::ops::bootstrap::SnapshotOptions; -use crate::shared::maybe_transpile_source; -use crate::shared::runtime; +use std::borrow::Cow; +use std::io::Write; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + use deno_cache::SqliteBackedCache; use deno_core::snapshot::*; use deno_core::v8; @@ -11,12 +14,11 @@ use deno_core::Extension; use deno_http::DefaultHttpPropertyExtractor; use deno_io::fs::FsError; use deno_permissions::PermissionCheckError; -use std::borrow::Cow; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; + +use crate::ops; +use crate::ops::bootstrap::SnapshotOptions; +use crate::shared::maybe_transpile_source; +use crate::shared::runtime; #[derive(Clone)] struct Permissions; diff --git a/runtime/sys_info.rs b/runtime/sys_info.rs index 99bfcfe103..d711e80ff0 100644 --- a/runtime/sys_info.rs +++ b/runtime/sys_info.rs @@ -159,6 +159,7 @@ pub fn hostname() -> String { use std::ffi::OsString; use std::mem; use std::os::windows::ffi::OsStringExt; + use winapi::shared::minwindef::MAKEWORD; use winapi::um::winsock2::GetHostNameW; use winapi::um::winsock2::WSAStartup; @@ -307,6 +308,7 @@ pub fn mem_info() -> Option { // - `dwLength` is set to the size of the struct. unsafe { use std::mem; + use winapi::shared::minwindef; use winapi::um::psapi::GetPerformanceInfo; use winapi::um::psapi::PERFORMANCE_INFORMATION; diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 2b46d9a2ff..8902e0fe7e 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -1,5 +1,15 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::cell::RefCell; +use std::fmt; +use std::rc::Rc; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::task::Context; +use std::task::Poll; + use deno_broadcast_channel::InMemoryBroadcastChannel; use deno_cache::CreateCache; use deno_cache::SqliteBackedCache; @@ -46,15 +56,6 @@ use deno_web::JsMessageData; use deno_web::MessagePort; use deno_web::Transferable; use log::debug; -use std::cell::RefCell; -use std::fmt; -use std::rc::Rc; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicU32; -use std::sync::atomic::Ordering; -use std::sync::Arc; -use std::task::Context; -use std::task::Poll; use crate::inspector_server::InspectorServer; use crate::ops; diff --git a/runtime/worker_bootstrap.rs b/runtime/worker_bootstrap.rs index 8364fe0d2b..f1e7dc05d0 100644 --- a/runtime/worker_bootstrap.rs +++ b/runtime/worker_bootstrap.rs @@ -1,13 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::cell::RefCell; +use std::thread; + use deno_core::v8; use deno_core::ModuleSpecifier; use deno_telemetry::OtelConfig; -use serde::Serialize; -use std::cell::RefCell; -use std::thread; - use deno_terminal::colors; +use serde::Serialize; /// The execution mode for this worker. Some modes may have implicit behaviour. #[derive(Copy, Clone)] diff --git a/tests/ffi/tests/integration_tests.rs b/tests/ffi/tests/integration_tests.rs index dbc0036bc2..34fcc16355 100644 --- a/tests/ffi/tests/integration_tests.rs +++ b/tests/ffi/tests/integration_tests.rs @@ -3,8 +3,9 @@ #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] -use pretty_assertions::assert_eq; use std::process::Command; + +use pretty_assertions::assert_eq; use test_util::deno_cmd; use test_util::deno_config_path; use test_util::ffi_tests_path; diff --git a/tests/integration/inspector_tests.rs b/tests/integration/inspector_tests.rs index fa1b3a9d83..c3586beb96 100644 --- a/tests/integration/inspector_tests.rs +++ b/tests/integration/inspector_tests.rs @@ -1,12 +1,15 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::io::BufRead; +use std::process::ChildStderr; +use std::time::Duration; + use bytes::Bytes; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::url; - use fastwebsockets::FragmentCollector; use fastwebsockets::Frame; use fastwebsockets::WebSocket; @@ -15,9 +18,6 @@ use hyper::upgrade::Upgraded; use hyper::Request; use hyper::Response; use hyper_util::rt::TokioIo; -use std::io::BufRead; -use std::process::ChildStderr; -use std::time::Duration; use test_util as util; use tokio::net::TcpStream; use tokio::time::timeout; diff --git a/tests/integration/js_unit_tests.rs b/tests/integration/js_unit_tests.rs index afb97a3458..685c85e2fe 100644 --- a/tests/integration/js_unit_tests.rs +++ b/tests/integration/js_unit_tests.rs @@ -4,6 +4,7 @@ use std::io::BufRead; use std::io::BufReader; use std::time::Duration; use std::time::Instant; + use test_util as util; util::unit_test_factory!( diff --git a/tests/integration/jupyter_tests.rs b/tests/integration/jupyter_tests.rs index e99780a276..dc8c1a5aec 100644 --- a/tests/integration/jupyter_tests.rs +++ b/tests/integration/jupyter_tests.rs @@ -5,11 +5,6 @@ use std::sync::Arc; use std::time::Duration; use bytes::Bytes; -use test_util::assertions::assert_json_subset; -use test_util::DenoChild; -use test_util::TestContext; -use test_util::TestContextBuilder; - use chrono::DateTime; use chrono::Utc; use deno_core::anyhow::Result; @@ -18,6 +13,10 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use serde::Deserialize; use serde::Serialize; +use test_util::assertions::assert_json_subset; +use test_util::DenoChild; +use test_util::TestContext; +use test_util::TestContextBuilder; use tokio::sync::Mutex; use tokio::time::timeout; use uuid::Uuid; diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index ee31b61751..3641dd9230 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -1,5 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::fs; +use std::str::FromStr; + use deno_ast::ModuleSpecifier; use deno_core::serde::Deserialize; use deno_core::serde_json; @@ -7,8 +10,6 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::url::Url; use pretty_assertions::assert_eq; -use std::fs; -use std::str::FromStr; use test_util::assert_starts_with; use test_util::assertions::assert_json_subset; use test_util::deno_cmd_with_deno_dir; diff --git a/tests/integration/node_unit_tests.rs b/tests/integration/node_unit_tests.rs index 9cb1af9496..c49699be6a 100644 --- a/tests/integration/node_unit_tests.rs +++ b/tests/integration/node_unit_tests.rs @@ -4,6 +4,7 @@ use std::io::BufRead; use std::io::BufReader; use std::time::Duration; use std::time::Instant; + use test_util as util; use test_util::itest; use util::deno_config_path; diff --git a/tests/integration/npm_tests.rs b/tests/integration/npm_tests.rs index ffd3b817d4..16e956d947 100644 --- a/tests/integration/npm_tests.rs +++ b/tests/integration/npm_tests.rs @@ -3,7 +3,6 @@ use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; - use pretty_assertions::assert_eq; use test_util as util; use test_util::itest; diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index 77c0a46c5f..9edb29adae 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -11,7 +11,6 @@ use std::sync::Arc; use bytes::Bytes; use deno_core::serde_json::json; use deno_core::url; - use deno_tls::rustls; use deno_tls::rustls::ClientConnection; use deno_tls::rustls_pemfile; @@ -2215,15 +2214,16 @@ fn basic_auth_tokens() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_resolve_dns() { + use std::net::SocketAddr; + use std::str::FromStr; + use std::sync::Arc; + use std::time::Duration; + use hickory_server::authority::Catalog; use hickory_server::authority::ZoneType; use hickory_server::proto::rr::Name; use hickory_server::store::in_memory::InMemoryAuthority; use hickory_server::ServerFuture; - use std::net::SocketAddr; - use std::str::FromStr; - use std::sync::Arc; - use std::time::Duration; use tokio::net::TcpListener; use tokio::net::UdpSocket; use tokio::sync::oneshot; diff --git a/tests/integration/upgrade_tests.rs b/tests/integration/upgrade_tests.rs index 5132b4ca5b..59dea8bf74 100644 --- a/tests/integration/upgrade_tests.rs +++ b/tests/integration/upgrade_tests.rs @@ -3,6 +3,7 @@ use std::process::Command; use std::process::Stdio; use std::time::Instant; + use test_util as util; use test_util::assert_starts_with; use test_util::TestContext; diff --git a/tests/integration/watcher_tests.rs b/tests/integration/watcher_tests.rs index 055e46af9c..cd27062885 100644 --- a/tests/integration/watcher_tests.rs +++ b/tests/integration/watcher_tests.rs @@ -6,9 +6,8 @@ use test_util::assert_contains; use test_util::env_vars_for_npm_tests; use test_util::TempDir; use tokio::io::AsyncBufReadExt; -use util::DenoChild; - use util::assert_not_contains; +use util::DenoChild; /// Logs to stderr every time next_line() is called struct LoggingLines diff --git a/tests/napi/src/array.rs b/tests/napi/src/array.rs index 6df420eb57..3769264ad0 100644 --- a/tests/napi/src/array.rs +++ b/tests/napi/src/array.rs @@ -1,12 +1,14 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::ValueType::napi_number; +use napi_sys::ValueType::napi_object; +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_number; -use napi_sys::ValueType::napi_object; -use napi_sys::*; -use std::ptr; extern "C" fn test_array_new( env: napi_env, diff --git a/tests/napi/src/arraybuffer.rs b/tests/napi/src/arraybuffer.rs index 8402f5d861..cf165f2f91 100644 --- a/tests/napi/src/arraybuffer.rs +++ b/tests/napi/src/arraybuffer.rs @@ -1,9 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; extern "C" fn test_detached( env: napi_env, diff --git a/tests/napi/src/async.rs b/tests/napi/src/async.rs index 367d2e9ef0..dd79da7f18 100644 --- a/tests/napi/src/async.rs +++ b/tests/napi/src/async.rs @@ -1,14 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::os::raw::c_char; +use std::os::raw::c_void; +use std::ptr; + +use napi_sys::Status::napi_ok; +use napi_sys::ValueType::napi_function; +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::Status::napi_ok; -use napi_sys::ValueType::napi_function; -use napi_sys::*; -use std::os::raw::c_char; -use std::os::raw::c_void; -use std::ptr; pub struct Baton { called: bool, diff --git a/tests/napi/src/bigint.rs b/tests/napi/src/bigint.rs index d867823313..bea72c43c2 100644 --- a/tests/napi/src/bigint.rs +++ b/tests/napi/src/bigint.rs @@ -1,13 +1,15 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::Status::napi_pending_exception; +use napi_sys::ValueType::napi_bigint; +use napi_sys::*; + use crate::assert_napi_ok; use crate::cstr; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::Status::napi_pending_exception; -use napi_sys::ValueType::napi_bigint; -use napi_sys::*; -use std::ptr; extern "C" fn is_lossless( env: napi_env, diff --git a/tests/napi/src/callback.rs b/tests/napi/src/callback.rs index 2512f6a38f..9f12730478 100644 --- a/tests/napi/src/callback.rs +++ b/tests/napi/src/callback.rs @@ -1,15 +1,17 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::assert_napi_ok; -use crate::napi_get_callback_info; -use crate::napi_new_property; +use std::ptr; + use napi_sys::ValueType::napi_function; use napi_sys::ValueType::napi_object; use napi_sys::ValueType::napi_undefined; use napi_sys::*; -use std::ptr; use Status::napi_pending_exception; +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; + /// `test_callback_run((a, b) => a + b, [1, 2])` => 3 extern "C" fn test_callback_run( env: napi_env, diff --git a/tests/napi/src/coerce.rs b/tests/napi/src/coerce.rs index a405783843..a022481d2a 100644 --- a/tests/napi/src/coerce.rs +++ b/tests/napi/src/coerce.rs @@ -1,10 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn test_coerce_bool( env: napi_env, diff --git a/tests/napi/src/date.rs b/tests/napi/src/date.rs index 4d3c155c32..3a5a62b6f4 100644 --- a/tests/napi/src/date.rs +++ b/tests/napi/src/date.rs @@ -1,11 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::ValueType::napi_number; +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_number; -use napi_sys::*; -use std::ptr; extern "C" fn create_date( env: napi_env, diff --git a/tests/napi/src/env.rs b/tests/napi/src/env.rs index ebc6532a3c..57f7469d69 100644 --- a/tests/napi/src/env.rs +++ b/tests/napi/src/env.rs @@ -1,9 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; extern "C" fn get_node_global( env: napi_env, diff --git a/tests/napi/src/error.rs b/tests/napi/src/error.rs index e0d79c836a..9c421984c0 100644 --- a/tests/napi/src/error.rs +++ b/tests/napi/src/error.rs @@ -1,11 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::*; + use crate::assert_napi_ok; use crate::cstr; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn check_error( env: napi_env, diff --git a/tests/napi/src/finalizer.rs b/tests/napi/src/finalizer.rs index 9769e775e2..6b72dcfca7 100644 --- a/tests/napi/src/finalizer.rs +++ b/tests/napi/src/finalizer.rs @@ -1,11 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::ValueType::napi_object; +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_object; -use napi_sys::*; -use std::ptr; unsafe extern "C" fn finalize_cb( _env: napi_env, diff --git a/tests/napi/src/make_callback.rs b/tests/napi/src/make_callback.rs index 945df34523..19948dce3d 100644 --- a/tests/napi/src/make_callback.rs +++ b/tests/napi/src/make_callback.rs @@ -1,10 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::ValueType::napi_function; +use napi_sys::*; + use crate::assert_napi_ok; use crate::cstr; -use napi_sys::ValueType::napi_function; -use napi_sys::*; -use std::ptr; extern "C" fn make_callback( env: napi_env, diff --git a/tests/napi/src/mem.rs b/tests/napi/src/mem.rs index ebb6a5c7ac..fd488da974 100644 --- a/tests/napi/src/mem.rs +++ b/tests/napi/src/mem.rs @@ -1,9 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn adjust_external_memory( env: napi_env, diff --git a/tests/napi/src/numbers.rs b/tests/napi/src/numbers.rs index 777ccbfac7..00d68c9bf3 100644 --- a/tests/napi/src/numbers.rs +++ b/tests/napi/src/numbers.rs @@ -1,11 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::ValueType::napi_number; +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_number; -use napi_sys::*; -use std::ptr; extern "C" fn test_int32( env: napi_env, diff --git a/tests/napi/src/object.rs b/tests/napi/src/object.rs index 9876f4dae0..93424b4b26 100644 --- a/tests/napi/src/object.rs +++ b/tests/napi/src/object.rs @@ -1,10 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn test_object_new( env: napi_env, diff --git a/tests/napi/src/object_wrap.rs b/tests/napi/src/object_wrap.rs index 63e9e2e232..66b1a9f926 100644 --- a/tests/napi/src/object_wrap.rs +++ b/tests/napi/src/object_wrap.rs @@ -1,16 +1,18 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::assert_napi_ok; -use crate::napi_get_callback_info; -use crate::napi_new_property; -use napi_sys::ValueType::napi_number; -use napi_sys::*; use std::cell::RefCell; use std::collections::HashMap; use std::os::raw::c_char; use std::os::raw::c_void; use std::ptr; +use napi_sys::ValueType::napi_number; +use napi_sys::*; + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; + pub struct NapiObject { counter: i32, } diff --git a/tests/napi/src/primitives.rs b/tests/napi/src/primitives.rs index 28fb8ec3db..7afa05157b 100644 --- a/tests/napi/src/primitives.rs +++ b/tests/napi/src/primitives.rs @@ -1,9 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; extern "C" fn test_get_undefined( env: napi_env, diff --git a/tests/napi/src/promise.rs b/tests/napi/src/promise.rs index 1f1c31f1eb..26bfbbd1fa 100644 --- a/tests/napi/src/promise.rs +++ b/tests/napi/src/promise.rs @@ -1,11 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; +use std::ptr::addr_of_mut; + +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::*; -use std::ptr; -use std::ptr::addr_of_mut; static mut CURRENT_DEFERRED: napi_deferred = ptr::null_mut(); diff --git a/tests/napi/src/properties.rs b/tests/napi/src/properties.rs index 43bef1949a..3ef2290e67 100644 --- a/tests/napi/src/properties.rs +++ b/tests/napi/src/properties.rs @@ -1,10 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::ptr; + +use napi_sys::PropertyAttributes::*; +use napi_sys::*; + use crate::assert_napi_ok; use crate::cstr; -use napi_sys::PropertyAttributes::*; -use napi_sys::*; -use std::ptr; static NICE: i64 = 69; diff --git a/tests/napi/src/strings.rs b/tests/napi/src/strings.rs index 301ab23df2..dd7ae35b38 100644 --- a/tests/napi/src/strings.rs +++ b/tests/napi/src/strings.rs @@ -1,10 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use napi_sys::ValueType::napi_string; +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_string; -use napi_sys::*; extern "C" fn test_utf8(env: napi_env, info: napi_callback_info) -> napi_value { let (args, argc, _) = napi_get_callback_info!(env, info, 1); diff --git a/tests/napi/src/symbol.rs b/tests/napi/src/symbol.rs index 6387d449f1..780a6dad0f 100644 --- a/tests/napi/src/symbol.rs +++ b/tests/napi/src/symbol.rs @@ -1,10 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use napi_sys::ValueType::napi_string; +use napi_sys::*; + use crate::assert_napi_ok; use crate::napi_get_callback_info; use crate::napi_new_property; -use napi_sys::ValueType::napi_string; -use napi_sys::*; extern "C" fn symbol_new( env: napi_env, diff --git a/tests/napi/src/tsfn.rs b/tests/napi/src/tsfn.rs index a3a231cec1..c8e885c6c3 100644 --- a/tests/napi/src/tsfn.rs +++ b/tests/napi/src/tsfn.rs @@ -3,11 +3,12 @@ // This test performs initialization similar to napi-rs. // https://github.com/napi-rs/napi-rs/commit/a5a04a4e545f268769cc78e2bd6c45af4336aac3 -use napi_sys as sys; use std::ffi::c_char; use std::ffi::c_void; use std::ptr; +use napi_sys as sys; + macro_rules! check_status_or_panic { ($code:expr, $msg:expr) => {{ let c = $code; diff --git a/tests/napi/src/typedarray.rs b/tests/napi/src/typedarray.rs index b512bd32fe..2482c1dac6 100644 --- a/tests/napi/src/typedarray.rs +++ b/tests/napi/src/typedarray.rs @@ -1,16 +1,18 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::assert_napi_ok; -use crate::napi_get_callback_info; -use crate::napi_new_property; use core::ffi::c_void; +use std::os::raw::c_char; +use std::ptr; + use napi_sys::Status::napi_ok; use napi_sys::TypedarrayType; use napi_sys::ValueType::napi_number; use napi_sys::ValueType::napi_object; use napi_sys::*; -use std::os::raw::c_char; -use std::ptr; + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; extern "C" fn test_multiply( env: napi_env, diff --git a/tests/napi/src/uv.rs b/tests/napi/src/uv.rs index 555470c008..685b4040bd 100644 --- a/tests/napi/src/uv.rs +++ b/tests/napi/src/uv.rs @@ -1,8 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::assert_napi_ok; -use crate::napi_get_callback_info; -use crate::napi_new_property; +use std::mem::MaybeUninit; +use std::ptr; +use std::ptr::addr_of_mut; +use std::ptr::null_mut; +use std::time::Duration; + use libuv_sys_lite::uv_async_init; use libuv_sys_lite::uv_async_t; use libuv_sys_lite::uv_close; @@ -12,11 +15,10 @@ use libuv_sys_lite::uv_mutex_lock; use libuv_sys_lite::uv_mutex_t; use libuv_sys_lite::uv_mutex_unlock; use napi_sys::*; -use std::mem::MaybeUninit; -use std::ptr; -use std::ptr::addr_of_mut; -use std::ptr::null_mut; -use std::time::Duration; + +use crate::assert_napi_ok; +use crate::napi_get_callback_info; +use crate::napi_new_property; struct KeepAlive { tsfn: napi_threadsafe_function, diff --git a/tests/napi/tests/napi_tests.rs b/tests/napi/tests/napi_tests.rs index 53d4258f93..ff09565ff3 100644 --- a/tests/napi/tests/napi_tests.rs +++ b/tests/napi/tests/napi_tests.rs @@ -4,6 +4,7 @@ #![allow(clippy::print_stderr)] use std::process::Command; + use test_util::deno_cmd; use test_util::deno_config_path; use test_util::env_vars_for_npm_tests; diff --git a/tests/util/server/src/factory.rs b/tests/util/server/src/factory.rs index 5b796fbc1d..4b972d9b0e 100644 --- a/tests/util/server/src/factory.rs +++ b/tests/util/server/src/factory.rs @@ -1,8 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use glob::glob; use std::collections::HashSet; use std::path::PathBuf; +use glob::glob; + /// Generate a unit test factory verified and backed by a glob. #[macro_export] macro_rules! unit_test_factory { diff --git a/tests/util/server/src/fs.rs b/tests/util/server/src/fs.rs index 7feb0799ae..4bb3a7dd8c 100644 --- a/tests/util/server/src/fs.rs +++ b/tests/util/server/src/fs.rs @@ -1,7 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use lsp_types::Uri; -use pretty_assertions::assert_eq; use std::borrow::Cow; use std::collections::HashSet; use std::ffi::OsStr; @@ -15,6 +13,8 @@ use std::str::FromStr; use std::sync::Arc; use anyhow::Context; +use lsp_types::Uri; +use pretty_assertions::assert_eq; use serde::de::DeserializeOwned; use serde::Serialize; use url::Url; diff --git a/tests/util/server/src/https.rs b/tests/util/server/src/https.rs index 617fd5cae2..ab90800a45 100644 --- a/tests/util/server/src/https.rs +++ b/tests/util/server/src/https.rs @@ -1,4 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::io; +use std::num::NonZeroUsize; +use std::result::Result; +use std::sync::Arc; + use anyhow::anyhow; use futures::Stream; use futures::StreamExt; @@ -6,10 +11,6 @@ use rustls_tokio_stream::rustls; use rustls_tokio_stream::rustls::pki_types::CertificateDer; use rustls_tokio_stream::rustls::pki_types::PrivateKeyDer; use rustls_tokio_stream::TlsStream; -use std::io; -use std::num::NonZeroUsize; -use std::result::Result; -use std::sync::Arc; use tokio::net::TcpStream; use crate::get_tcp_listener_stream; diff --git a/tests/util/server/src/lib.rs b/tests/util/server/src/lib.rs index 531944bf6a..6361dac16d 100644 --- a/tests/util/server/src/lib.rs +++ b/tests/util/server/src/lib.rs @@ -1296,9 +1296,10 @@ pub(crate) mod colors { #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; + use super::*; + #[test] fn parse_wrk_output_1() { const TEXT: &str = include_str!("./testdata/wrk1.txt"); diff --git a/tests/util/server/src/lsp.rs b/tests/util/server/src/lsp.rs index 92169ee644..9da7042e20 100644 --- a/tests/util/server/src/lsp.rs +++ b/tests/util/server/src/lsp.rs @@ -1,11 +1,24 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::deno_exe_path; -use crate::jsr_registry_url; -use crate::npm_registry_url; -use crate::PathRef; - -use super::TempDir; +use std::collections::HashMap; +use std::collections::HashSet; +use std::ffi::OsStr; +use std::ffi::OsString; +use std::io; +use std::io::BufRead; +use std::io::BufReader; +use std::io::Write; +use std::path::Path; +use std::process::Child; +use std::process::ChildStdin; +use std::process::ChildStdout; +use std::process::Command; +use std::process::Stdio; +use std::str::FromStr; +use std::sync::mpsc; +use std::sync::Arc; +use std::time::Duration; +use std::time::Instant; use anyhow::Result; use lsp_types as lsp; @@ -32,27 +45,14 @@ use serde::Serialize; use serde_json::json; use serde_json::to_value; use serde_json::Value; -use std::collections::HashMap; -use std::collections::HashSet; -use std::ffi::OsStr; -use std::ffi::OsString; -use std::io; -use std::io::BufRead; -use std::io::BufReader; -use std::io::Write; -use std::path::Path; -use std::process::Child; -use std::process::ChildStdin; -use std::process::ChildStdout; -use std::process::Command; -use std::process::Stdio; -use std::str::FromStr; -use std::sync::mpsc; -use std::sync::Arc; -use std::time::Duration; -use std::time::Instant; use url::Url; +use super::TempDir; +use crate::deno_exe_path; +use crate::jsr_registry_url; +use crate::npm_registry_url; +use crate::PathRef; + static CONTENT_TYPE_REG: Lazy = lazy_regex::lazy_regex!(r"(?i)^content-length:\s+(\d+)"); diff --git a/tests/util/server/src/pty.rs b/tests/util/server/src/pty.rs index 07659262cf..fa22c4ecc8 100644 --- a/tests/util/server/src/pty.rs +++ b/tests/util/server/src/pty.rs @@ -323,9 +323,10 @@ fn create_pty( cwd: &Path, env_vars: Option>, ) -> Box { - use crate::pty::unix::UnixPty; use std::os::unix::process::CommandExt; + use crate::pty::unix::UnixPty; + // Manually open pty main/secondary sides in the test process. Since we're not actually // changing uid/gid here, this is the easiest way to do it. diff --git a/tests/util/server/src/servers/hyper_utils.rs b/tests/util/server/src/servers/hyper_utils.rs index 8e01151ed4..dfb9464abe 100644 --- a/tests/util/server/src/servers/hyper_utils.rs +++ b/tests/util/server/src/servers/hyper_utils.rs @@ -1,5 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::convert::Infallible; +use std::io; +use std::net::SocketAddr; +use std::pin::Pin; +use std::result::Result; + use bytes::Bytes; use futures::Future; use futures::FutureExt; @@ -10,11 +16,6 @@ use http::Request; use http::Response; use http_body_util::combinators::UnsyncBoxBody; use hyper_util::rt::TokioIo; -use std::convert::Infallible; -use std::io; -use std::net::SocketAddr; -use std::pin::Pin; -use std::result::Result; use tokio::net::TcpListener; #[derive(Debug, Clone, Copy)] diff --git a/tests/util/server/src/servers/jsr_registry.rs b/tests/util/server/src/servers/jsr_registry.rs index 8970750a28..418a0ebe29 100644 --- a/tests/util/server/src/servers/jsr_registry.rs +++ b/tests/util/server/src/servers/jsr_registry.rs @@ -1,10 +1,12 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::tests_path; +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::convert::Infallible; +use std::net::SocketAddr; +use std::path::Path; +use std::sync::Mutex; -use super::run_server; -use super::ServerKind; -use super::ServerOptions; use base64::engine::general_purpose::STANDARD_NO_PAD; use base64::Engine as _; use bytes::Bytes; @@ -17,12 +19,11 @@ use hyper::Response; use hyper::StatusCode; use once_cell::sync::Lazy; use serde_json::json; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::convert::Infallible; -use std::net::SocketAddr; -use std::path::Path; -use std::sync::Mutex; + +use super::run_server; +use super::ServerKind; +use super::ServerOptions; +use crate::tests_path; pub async fn registry_server(port: u16) { let registry_server_addr = SocketAddr::from(([127, 0, 0, 1], port)); diff --git a/tests/util/server/src/servers/mod.rs b/tests/util/server/src/servers/mod.rs index 4345c27cde..4c8901b7cc 100644 --- a/tests/util/server/src/servers/mod.rs +++ b/tests/util/server/src/servers/mod.rs @@ -2,6 +2,13 @@ // Usage: provide a port as argument to run hyper_hello benchmark server // otherwise this starts multiple servers on many ports for test endpoints. +use std::collections::HashMap; +use std::convert::Infallible; +use std::env; +use std::net::SocketAddr; +use std::result::Result; +use std::time::Duration; + use base64::prelude::BASE64_STANDARD; use base64::Engine; use bytes::Bytes; @@ -27,12 +34,6 @@ use http_body_util::Empty; use http_body_util::Full; use pretty_assertions::assert_eq; use prost::Message; -use std::collections::HashMap; -use std::convert::Infallible; -use std::env; -use std::net::SocketAddr; -use std::result::Result; -use std::time::Duration; use tokio::io::AsyncWriteExt; use tokio::net::TcpStream; @@ -48,12 +49,11 @@ use hyper_utils::run_server_with_acceptor; use hyper_utils::ServerKind; use hyper_utils::ServerOptions; -use crate::TEST_SERVERS_COUNT; - use super::https::get_tls_listener_stream; use super::https::SupportedHttpVersions; use super::std_path; use super::testdata_path; +use crate::TEST_SERVERS_COUNT; pub(crate) const PORT: u16 = 4545; const TEST_AUTH_TOKEN: &str = "abcdef123456789"; diff --git a/tests/util/server/src/servers/npm_registry.rs b/tests/util/server/src/servers/npm_registry.rs index 4ada468fac..fa8fa6a2f5 100644 --- a/tests/util/server/src/servers/npm_registry.rs +++ b/tests/util/server/src/servers/npm_registry.rs @@ -1,14 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::npm; +use std::convert::Infallible; +use std::net::Ipv6Addr; +use std::net::SocketAddr; +use std::net::SocketAddrV6; +use std::path::PathBuf; -use super::custom_headers; -use super::empty_body; -use super::hyper_utils::HandlerOutput; -use super::run_server; -use super::string_body; -use super::ServerKind; -use super::ServerOptions; use bytes::Bytes; use futures::future::LocalBoxFuture; use futures::Future; @@ -18,11 +15,15 @@ use hyper::body::Incoming; use hyper::Request; use hyper::Response; use hyper::StatusCode; -use std::convert::Infallible; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::net::SocketAddrV6; -use std::path::PathBuf; + +use super::custom_headers; +use super::empty_body; +use super::hyper_utils::HandlerOutput; +use super::run_server; +use super::string_body; +use super::ServerKind; +use super::ServerOptions; +use crate::npm; pub fn public_npm_registry(port: u16) -> Vec> { run_npm_server(port, "npm registry server error", { diff --git a/tests/util/server/src/servers/ws.rs b/tests/util/server/src/servers/ws.rs index dd4efbf659..f2637402fa 100644 --- a/tests/util/server/src/servers/ws.rs +++ b/tests/util/server/src/servers/ws.rs @@ -1,5 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::pin::Pin; +use std::result::Result; + use anyhow::anyhow; use bytes::Bytes; use fastwebsockets::FragmentCollector; @@ -22,8 +25,6 @@ use hyper::Response; use hyper::StatusCode; use hyper_util::rt::TokioIo; use pretty_assertions::assert_eq; -use std::pin::Pin; -use std::result::Result; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; diff --git a/tests/util/server/src/spawn.rs b/tests/util/server/src/spawn.rs index bfd83e9b26..c76168db53 100644 --- a/tests/util/server/src/spawn.rs +++ b/tests/util/server/src/spawn.rs @@ -1,7 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use anyhow::Error; use std::convert::Infallible; +use anyhow::Error; + /// For unix targets, we just replace our current process with the desired cargo process. #[cfg(unix)] pub fn exec_replace_inner( @@ -30,6 +31,7 @@ pub fn exec_replace_inner( ) -> Result { use std::os::windows::io::AsRawHandle; use std::process::Command; + use win32job::ExtendedLimitInfo; use win32job::Job; From 8fb073d7b4ea83b57fd1bf9875681347b414bbe6 Mon Sep 17 00:00:00 2001 From: Kenta Moriuchi Date: Wed, 1 Jan 2025 04:12:39 +0900 Subject: [PATCH 051/107] chore: Happy New Year 2025 (#27509) --- .github/mtime_cache/action.js | 2 +- .github/workflows/ci.generate.ts | 2 +- Cargo.toml | 2 +- LICENSE.md | 2 +- bench_util/Cargo.toml | 2 +- bench_util/benches/utf8.rs | 2 +- bench_util/js_runtime.rs | 2 +- bench_util/lib.rs | 2 +- bench_util/profiling.rs | 2 +- cli/Cargo.toml | 2 +- cli/args/deno_json.rs | 2 +- cli/args/flags.rs | 2 +- cli/args/flags_net.rs | 2 +- cli/args/import_map.rs | 2 +- cli/args/lockfile.rs | 2 +- cli/args/mod.rs | 2 +- cli/args/package_json.rs | 2 +- cli/bench/cache_api.js | 2 +- cli/bench/command.js | 2 +- cli/bench/console.js | 2 +- cli/bench/deno_common.js | 2 +- cli/bench/encode_into.js | 2 +- cli/bench/fs/run.mjs | 2 +- cli/bench/fs/serve.jsx | 2 +- cli/bench/getrandom.js | 2 +- cli/bench/lsp.rs | 2 +- cli/bench/lsp_bench_standalone.rs | 2 +- cli/bench/main.rs | 2 +- cli/bench/napi/bench.js | 2 +- cli/bench/napi/bench_node.mjs | 2 +- cli/bench/op_now.js | 2 +- cli/bench/secure_curves.js | 2 +- cli/bench/stdio/stdio.c | 2 +- cli/bench/stdio/stdio.js | 2 +- cli/bench/tcp.js | 2 +- cli/bench/tty.js | 2 +- cli/bench/url_parse.js | 2 +- cli/bench/webstorage.js | 2 +- cli/bench/write_file.js | 2 +- cli/build.rs | 2 +- cli/cache/cache_db.rs | 2 +- cli/cache/caches.rs | 2 +- cli/cache/check.rs | 2 +- cli/cache/code_cache.rs | 2 +- cli/cache/common.rs | 2 +- cli/cache/deno_dir.rs | 2 +- cli/cache/disk_cache.rs | 2 +- cli/cache/emit.rs | 2 +- cli/cache/fast_check.rs | 2 +- cli/cache/incremental.rs | 2 +- cli/cache/mod.rs | 2 +- cli/cache/module_info.rs | 2 +- cli/cache/node.rs | 2 +- cli/cache/parsed_source.rs | 2 +- cli/cdp.rs | 2 +- cli/emit.rs | 2 +- cli/errors.rs | 2 +- cli/factory.rs | 2 +- cli/file_fetcher.rs | 2 +- cli/graph_container.rs | 2 +- cli/graph_util.rs | 2 +- cli/http_util.rs | 2 +- cli/integration_tests_runner.rs | 2 +- cli/js.rs | 2 +- cli/js/40_bench.js | 2 +- cli/js/40_jupyter.js | 2 +- cli/js/40_lint.js | 2 +- cli/js/40_lint_selector.js | 2 +- cli/js/40_lint_types.d.ts | 2 +- cli/js/40_test.js | 2 +- cli/js/40_test_common.js | 2 +- cli/jsr.rs | 2 +- cli/lsp/analysis.rs | 2 +- cli/lsp/cache.rs | 2 +- cli/lsp/capabilities.rs | 2 +- cli/lsp/client.rs | 2 +- cli/lsp/code_lens.rs | 2 +- cli/lsp/completions.rs | 2 +- cli/lsp/config.rs | 2 +- cli/lsp/diagnostics.rs | 2 +- cli/lsp/documents.rs | 2 +- cli/lsp/jsr.rs | 2 +- cli/lsp/language_server.rs | 2 +- cli/lsp/logging.rs | 2 +- cli/lsp/lsp_custom.rs | 2 +- cli/lsp/mod.rs | 2 +- cli/lsp/npm.rs | 2 +- cli/lsp/parent_process_checker.rs | 2 +- cli/lsp/path_to_regex.rs | 2 +- cli/lsp/performance.rs | 2 +- cli/lsp/refactor.rs | 2 +- cli/lsp/registries.rs | 2 +- cli/lsp/repl.rs | 2 +- cli/lsp/resolver.rs | 2 +- cli/lsp/search.rs | 2 +- cli/lsp/semantic_tokens.rs | 2 +- cli/lsp/testing/collectors.rs | 2 +- cli/lsp/testing/definitions.rs | 2 +- cli/lsp/testing/execution.rs | 2 +- cli/lsp/testing/lsp_custom.rs | 2 +- cli/lsp/testing/mod.rs | 2 +- cli/lsp/testing/server.rs | 2 +- cli/lsp/text.rs | 2 +- cli/lsp/tsc.rs | 2 +- cli/lsp/urls.rs | 2 +- cli/main.rs | 2 +- cli/mainrt.rs | 2 +- cli/module_loader.rs | 2 +- cli/node.rs | 2 +- cli/npm/byonm.rs | 2 +- cli/npm/managed/mod.rs | 2 +- cli/npm/managed/resolution.rs | 2 +- cli/npm/managed/resolvers/common.rs | 2 +- cli/npm/managed/resolvers/common/bin_entries.rs | 2 +- cli/npm/managed/resolvers/common/lifecycle_scripts.rs | 2 +- cli/npm/managed/resolvers/global.rs | 2 +- cli/npm/managed/resolvers/local.rs | 2 +- cli/npm/managed/resolvers/mod.rs | 2 +- cli/npm/mod.rs | 2 +- cli/ops/bench.rs | 2 +- cli/ops/jupyter.rs | 2 +- cli/ops/lint.rs | 2 +- cli/ops/mod.rs | 2 +- cli/ops/testing.rs | 2 +- cli/resolver.rs | 2 +- cli/shared.rs | 2 +- cli/standalone/binary.rs | 2 +- cli/standalone/code_cache.rs | 2 +- cli/standalone/file_system.rs | 2 +- cli/standalone/mod.rs | 2 +- cli/standalone/serialization.rs | 2 +- cli/standalone/virtual_fs.rs | 2 +- cli/sys.rs | 2 +- cli/task_runner.rs | 2 +- cli/tools/bench/mod.rs | 2 +- cli/tools/bench/reporters.rs | 2 +- cli/tools/check.rs | 2 +- cli/tools/clean.rs | 2 +- cli/tools/compile.rs | 2 +- cli/tools/coverage/merge.rs | 2 +- cli/tools/coverage/mod.rs | 2 +- cli/tools/coverage/range_tree.rs | 2 +- cli/tools/coverage/reporter.rs | 2 +- cli/tools/coverage/util.rs | 2 +- cli/tools/doc.rs | 2 +- cli/tools/fmt.rs | 2 +- cli/tools/info.rs | 2 +- cli/tools/init/mod.rs | 2 +- cli/tools/installer.rs | 2 +- cli/tools/jupyter/install.rs | 2 +- cli/tools/jupyter/mod.rs | 2 +- cli/tools/jupyter/server.rs | 2 +- cli/tools/lint/ast_buffer/buffer.rs | 2 +- cli/tools/lint/ast_buffer/mod.rs | 2 +- cli/tools/lint/ast_buffer/swc.rs | 2 +- cli/tools/lint/ast_buffer/ts_estree.rs | 2 +- cli/tools/lint/linter.rs | 2 +- cli/tools/lint/mod.rs | 2 +- cli/tools/lint/reporters.rs | 2 +- cli/tools/lint/rules/mod.rs | 2 +- cli/tools/lint/rules/no_sloppy_imports.rs | 2 +- cli/tools/lint/rules/no_slow_types.rs | 2 +- cli/tools/mod.rs | 2 +- cli/tools/registry/api.rs | 2 +- cli/tools/registry/auth.rs | 2 +- cli/tools/registry/diagnostics.rs | 2 +- cli/tools/registry/graph.rs | 2 +- cli/tools/registry/mod.rs | 2 +- cli/tools/registry/paths.rs | 2 +- cli/tools/registry/pm.rs | 2 +- cli/tools/registry/pm/cache_deps.rs | 2 +- cli/tools/registry/pm/deps.rs | 2 +- cli/tools/registry/pm/outdated.rs | 2 +- cli/tools/registry/provenance.rs | 2 +- cli/tools/registry/publish_order.rs | 2 +- cli/tools/registry/tar.rs | 2 +- cli/tools/registry/unfurl.rs | 2 +- cli/tools/repl/channel.rs | 2 +- cli/tools/repl/editor.rs | 2 +- cli/tools/repl/mod.rs | 2 +- cli/tools/repl/session.rs | 2 +- cli/tools/run/hmr.rs | 2 +- cli/tools/run/mod.rs | 2 +- cli/tools/serve.rs | 2 +- cli/tools/task.rs | 2 +- cli/tools/test/channel.rs | 2 +- cli/tools/test/fmt.rs | 2 +- cli/tools/test/mod.rs | 2 +- cli/tools/test/reporters/common.rs | 2 +- cli/tools/test/reporters/compound.rs | 2 +- cli/tools/test/reporters/dot.rs | 2 +- cli/tools/test/reporters/junit.rs | 2 +- cli/tools/test/reporters/mod.rs | 2 +- cli/tools/test/reporters/pretty.rs | 2 +- cli/tools/test/reporters/tap.rs | 2 +- cli/tools/upgrade.rs | 2 +- cli/tsc/99_main_compiler.js | 2 +- cli/tsc/_analyze_types_node.ts | 2 +- cli/tsc/compiler.d.ts | 2 +- cli/tsc/diagnostics.rs | 2 +- cli/tsc/dts/lib.deno.ns.d.ts | 2 +- cli/tsc/dts/lib.deno.shared_globals.d.ts | 2 +- cli/tsc/dts/lib.deno.unstable.d.ts | 2 +- cli/tsc/dts/lib.deno.window.d.ts | 2 +- cli/tsc/dts/lib.deno.worker.d.ts | 2 +- cli/tsc/dts/lib.deno_webgpu.d.ts | 2 +- cli/tsc/dts/lib.dom.extras.d.ts | 2 +- cli/tsc/mod.rs | 2 +- cli/util/archive.rs | 2 +- cli/util/checksum.rs | 2 +- cli/util/console.rs | 2 +- cli/util/diff.rs | 2 +- cli/util/display.rs | 2 +- cli/util/draw_thread.rs | 2 +- cli/util/extract.rs | 2 +- cli/util/file_watcher.rs | 2 +- cli/util/fs.rs | 2 +- cli/util/logger.rs | 2 +- cli/util/mod.rs | 2 +- cli/util/path.rs | 2 +- cli/util/progress_bar/mod.rs | 2 +- cli/util/progress_bar/renderer.rs | 2 +- cli/util/result.rs | 2 +- cli/util/retry.rs | 2 +- cli/util/sync/async_flag.rs | 2 +- cli/util/sync/mod.rs | 2 +- cli/util/sync/sync_read_async_write_lock.rs | 2 +- cli/util/sync/task_queue.rs | 2 +- cli/util/text_encoding.rs | 2 +- cli/util/unix.rs | 2 +- cli/util/v8.rs | 2 +- cli/util/v8/convert.rs | 2 +- cli/util/windows.rs | 2 +- cli/version.rs | 2 +- cli/worker.rs | 2 +- ext/broadcast_channel/01_broadcast_channel.js | 2 +- ext/broadcast_channel/Cargo.toml | 2 +- ext/broadcast_channel/in_memory_broadcast_channel.rs | 2 +- ext/broadcast_channel/lib.deno_broadcast_channel.d.ts | 2 +- ext/broadcast_channel/lib.rs | 2 +- ext/cache/01_cache.js | 2 +- ext/cache/Cargo.toml | 2 +- ext/cache/lib.deno_cache.d.ts | 2 +- ext/cache/lib.rs | 2 +- ext/cache/sqlite.rs | 2 +- ext/canvas/01_image.js | 2 +- ext/canvas/Cargo.toml | 2 +- ext/canvas/lib.deno_canvas.d.ts | 2 +- ext/canvas/lib.rs | 2 +- ext/console/01_console.js | 2 +- ext/console/Cargo.toml | 2 +- ext/console/internal.d.ts | 2 +- ext/console/lib.deno_console.d.ts | 2 +- ext/console/lib.rs | 2 +- ext/cron/01_cron.ts | 2 +- ext/cron/Cargo.toml | 2 +- ext/cron/interface.rs | 2 +- ext/cron/lib.rs | 2 +- ext/cron/local.rs | 2 +- ext/crypto/00_crypto.js | 2 +- ext/crypto/Cargo.toml | 2 +- ext/crypto/decrypt.rs | 2 +- ext/crypto/ed25519.rs | 2 +- ext/crypto/encrypt.rs | 2 +- ext/crypto/export_key.rs | 2 +- ext/crypto/generate_key.rs | 2 +- ext/crypto/import_key.rs | 2 +- ext/crypto/key.rs | 2 +- ext/crypto/lib.deno_crypto.d.ts | 2 +- ext/crypto/lib.rs | 2 +- ext/crypto/shared.rs | 2 +- ext/crypto/x25519.rs | 2 +- ext/crypto/x448.rs | 2 +- ext/fetch/20_headers.js | 2 +- ext/fetch/21_formdata.js | 2 +- ext/fetch/22_body.js | 2 +- ext/fetch/22_http_client.js | 2 +- ext/fetch/23_request.js | 2 +- ext/fetch/23_response.js | 2 +- ext/fetch/26_fetch.js | 2 +- ext/fetch/27_eventsource.js | 2 +- ext/fetch/Cargo.toml | 2 +- ext/fetch/dns.rs | 2 +- ext/fetch/fs_fetch_handler.rs | 2 +- ext/fetch/internal.d.ts | 2 +- ext/fetch/lib.deno_fetch.d.ts | 2 +- ext/fetch/lib.rs | 2 +- ext/fetch/proxy.rs | 2 +- ext/fetch/tests.rs | 2 +- ext/ffi/00_ffi.js | 2 +- ext/ffi/Cargo.toml | 2 +- ext/ffi/call.rs | 2 +- ext/ffi/callback.rs | 2 +- ext/ffi/dlfcn.rs | 2 +- ext/ffi/ir.rs | 2 +- ext/ffi/lib.rs | 2 +- ext/ffi/repr.rs | 2 +- ext/ffi/static.rs | 2 +- ext/ffi/symbol.rs | 2 +- ext/ffi/turbocall.rs | 2 +- ext/fs/30_fs.js | 2 +- ext/fs/Cargo.toml | 2 +- ext/fs/interface.rs | 2 +- ext/fs/lib.rs | 2 +- ext/fs/ops.rs | 2 +- ext/fs/std_fs.rs | 2 +- ext/fs/sync.rs | 2 +- ext/http/00_serve.ts | 2 +- ext/http/01_http.js | 2 +- ext/http/02_websocket.ts | 2 +- ext/http/Cargo.toml | 2 +- ext/http/benches/compressible.rs | 2 +- ext/http/compressible.rs | 2 +- ext/http/fly_accept_encoding.rs | 2 +- ext/http/http_next.rs | 2 +- ext/http/lib.rs | 2 +- ext/http/network_buffered_stream.rs | 2 +- ext/http/reader_stream.rs | 2 +- ext/http/request_body.rs | 2 +- ext/http/request_properties.rs | 2 +- ext/http/response_body.rs | 2 +- ext/http/service.rs | 2 +- ext/http/websocket_upgrade.rs | 2 +- ext/io/12_io.js | 2 +- ext/io/Cargo.toml | 2 +- ext/io/bi_pipe.rs | 2 +- ext/io/fs.rs | 2 +- ext/io/lib.rs | 2 +- ext/io/pipe.rs | 2 +- ext/io/winpipe.rs | 2 +- ext/kv/01_db.ts | 2 +- ext/kv/Cargo.toml | 2 +- ext/kv/config.rs | 2 +- ext/kv/dynamic.rs | 2 +- ext/kv/interface.rs | 2 +- ext/kv/lib.rs | 2 +- ext/kv/remote.rs | 2 +- ext/kv/sqlite.rs | 2 +- ext/napi/Cargo.toml | 2 +- ext/napi/build.rs | 2 +- ext/napi/function.rs | 2 +- ext/napi/js_native_api.rs | 2 +- ext/napi/lib.rs | 2 +- ext/napi/node_api.rs | 2 +- ext/napi/sym/Cargo.toml | 2 +- ext/napi/sym/lib.rs | 2 +- ext/napi/util.rs | 2 +- ext/napi/uv.rs | 2 +- ext/napi/value.rs | 2 +- ext/net/01_net.js | 2 +- ext/net/02_tls.js | 2 +- ext/net/03_quic.js | 2 +- ext/net/Cargo.toml | 2 +- ext/net/io.rs | 2 +- ext/net/lib.deno_net.d.ts | 2 +- ext/net/lib.rs | 2 +- ext/net/ops.rs | 2 +- ext/net/ops_tls.rs | 2 +- ext/net/ops_unix.rs | 2 +- ext/net/quic.rs | 2 +- ext/net/raw.rs | 2 +- ext/net/resolve_addr.rs | 2 +- ext/net/tcp.rs | 2 +- ext/node/Cargo.toml | 2 +- ext/node/benchmarks/child_process_ipc.mjs | 2 +- ext/node/build.rs | 2 +- ext/node/global.rs | 2 +- ext/node/lib.rs | 2 +- ext/node/ops/blocklist.rs | 2 +- ext/node/ops/buffer.rs | 2 +- ext/node/ops/crypto/cipher.rs | 2 +- ext/node/ops/crypto/dh.rs | 2 +- ext/node/ops/crypto/digest.rs | 2 +- ext/node/ops/crypto/keys.rs | 2 +- ext/node/ops/crypto/md5_sha1.rs | 2 +- ext/node/ops/crypto/mod.rs | 2 +- ext/node/ops/crypto/pkcs3.rs | 2 +- ext/node/ops/crypto/primes.rs | 2 +- ext/node/ops/crypto/sign.rs | 2 +- ext/node/ops/crypto/x509.rs | 2 +- ext/node/ops/fs.rs | 2 +- ext/node/ops/http.rs | 2 +- ext/node/ops/http2.rs | 2 +- ext/node/ops/idna.rs | 2 +- ext/node/ops/inspector.rs | 2 +- ext/node/ops/ipc.rs | 2 +- ext/node/ops/mod.rs | 2 +- ext/node/ops/os/cpus.rs | 2 +- ext/node/ops/os/mod.rs | 2 +- ext/node/ops/os/priority.rs | 2 +- ext/node/ops/perf_hooks.rs | 2 +- ext/node/ops/process.rs | 2 +- ext/node/ops/require.rs | 2 +- ext/node/ops/tls.rs | 2 +- ext/node/ops/util.rs | 2 +- ext/node/ops/v8.rs | 2 +- ext/node/ops/vm.rs | 2 +- ext/node/ops/vm_internal.rs | 2 +- ext/node/ops/winerror.rs | 2 +- ext/node/ops/worker_threads.rs | 2 +- ext/node/ops/zlib/alloc.rs | 2 +- ext/node/ops/zlib/brotli.rs | 2 +- ext/node/ops/zlib/mod.rs | 2 +- ext/node/ops/zlib/mode.rs | 2 +- ext/node/ops/zlib/stream.rs | 2 +- ext/node/polyfill.rs | 2 +- ext/node/polyfills/00_globals.js | 2 +- ext/node/polyfills/01_require.js | 2 +- ext/node/polyfills/02_init.js | 2 +- ext/node/polyfills/_brotli.js | 2 +- ext/node/polyfills/_events.d.ts | 2 +- ext/node/polyfills/_events.mjs | 2 +- ext/node/polyfills/_fs/_fs_access.ts | 2 +- ext/node/polyfills/_fs/_fs_appendFile.ts | 2 +- ext/node/polyfills/_fs/_fs_chmod.ts | 2 +- ext/node/polyfills/_fs/_fs_chown.ts | 2 +- ext/node/polyfills/_fs/_fs_close.ts | 2 +- ext/node/polyfills/_fs/_fs_common.ts | 2 +- ext/node/polyfills/_fs/_fs_constants.ts | 2 +- ext/node/polyfills/_fs/_fs_copy.ts | 2 +- ext/node/polyfills/_fs/_fs_cp.js | 2 +- ext/node/polyfills/_fs/_fs_dir.ts | 2 +- ext/node/polyfills/_fs/_fs_dirent.ts | 2 +- ext/node/polyfills/_fs/_fs_exists.ts | 2 +- ext/node/polyfills/_fs/_fs_fdatasync.ts | 2 +- ext/node/polyfills/_fs/_fs_fstat.ts | 2 +- ext/node/polyfills/_fs/_fs_fsync.ts | 2 +- ext/node/polyfills/_fs/_fs_ftruncate.ts | 2 +- ext/node/polyfills/_fs/_fs_futimes.ts | 2 +- ext/node/polyfills/_fs/_fs_lchown.ts | 2 +- ext/node/polyfills/_fs/_fs_link.ts | 2 +- ext/node/polyfills/_fs/_fs_lstat.ts | 2 +- ext/node/polyfills/_fs/_fs_lutimes.ts | 2 +- ext/node/polyfills/_fs/_fs_mkdir.ts | 2 +- ext/node/polyfills/_fs/_fs_mkdtemp.ts | 2 +- ext/node/polyfills/_fs/_fs_open.ts | 2 +- ext/node/polyfills/_fs/_fs_opendir.ts | 2 +- ext/node/polyfills/_fs/_fs_read.ts | 2 +- ext/node/polyfills/_fs/_fs_readFile.ts | 2 +- ext/node/polyfills/_fs/_fs_readdir.ts | 2 +- ext/node/polyfills/_fs/_fs_readlink.ts | 2 +- ext/node/polyfills/_fs/_fs_readv.ts | 2 +- ext/node/polyfills/_fs/_fs_realpath.ts | 2 +- ext/node/polyfills/_fs/_fs_rename.ts | 2 +- ext/node/polyfills/_fs/_fs_rm.ts | 2 +- ext/node/polyfills/_fs/_fs_rmdir.ts | 2 +- ext/node/polyfills/_fs/_fs_stat.ts | 2 +- ext/node/polyfills/_fs/_fs_statfs.js | 2 +- ext/node/polyfills/_fs/_fs_symlink.ts | 2 +- ext/node/polyfills/_fs/_fs_truncate.ts | 2 +- ext/node/polyfills/_fs/_fs_unlink.ts | 2 +- ext/node/polyfills/_fs/_fs_utimes.ts | 2 +- ext/node/polyfills/_fs/_fs_watch.ts | 2 +- ext/node/polyfills/_fs/_fs_write.d.ts | 2 +- ext/node/polyfills/_fs/_fs_write.mjs | 2 +- ext/node/polyfills/_fs/_fs_writeFile.ts | 2 +- ext/node/polyfills/_fs/_fs_writev.d.ts | 2 +- ext/node/polyfills/_fs/_fs_writev.mjs | 2 +- ext/node/polyfills/_global.d.ts | 2 +- ext/node/polyfills/_http_agent.mjs | 2 +- ext/node/polyfills/_http_common.ts | 2 +- ext/node/polyfills/_http_outgoing.ts | 2 +- ext/node/polyfills/_http_server.ts | 2 +- ext/node/polyfills/_next_tick.ts | 2 +- ext/node/polyfills/_process/exiting.ts | 2 +- ext/node/polyfills/_process/process.ts | 2 +- ext/node/polyfills/_process/streams.mjs | 2 +- ext/node/polyfills/_readline.d.ts | 2 +- ext/node/polyfills/_readline.mjs | 2 +- ext/node/polyfills/_readline_shared_types.d.ts | 2 +- ext/node/polyfills/_stream.d.ts | 2 +- ext/node/polyfills/_stream.mjs | 2 +- ext/node/polyfills/_tls_common.ts | 2 +- ext/node/polyfills/_tls_wrap.ts | 2 +- ext/node/polyfills/_util/_util_callbackify.js | 2 +- ext/node/polyfills/_util/asserts.ts | 2 +- ext/node/polyfills/_util/async.ts | 2 +- ext/node/polyfills/_util/os.ts | 2 +- ext/node/polyfills/_util/std_asserts.ts | 2 +- ext/node/polyfills/_util/std_fmt_colors.ts | 2 +- ext/node/polyfills/_util/std_testing_diff.ts | 2 +- ext/node/polyfills/_utils.ts | 2 +- ext/node/polyfills/_zlib.mjs | 2 +- ext/node/polyfills/_zlib_binding.mjs | 2 +- ext/node/polyfills/assert.ts | 2 +- ext/node/polyfills/assert/strict.ts | 2 +- ext/node/polyfills/assertion_error.ts | 2 +- ext/node/polyfills/async_hooks.ts | 2 +- ext/node/polyfills/buffer.ts | 2 +- ext/node/polyfills/child_process.ts | 2 +- ext/node/polyfills/cluster.ts | 2 +- ext/node/polyfills/console.ts | 2 +- ext/node/polyfills/constants.ts | 2 +- ext/node/polyfills/crypto.ts | 2 +- ext/node/polyfills/dgram.ts | 2 +- ext/node/polyfills/diagnostics_channel.js | 2 +- ext/node/polyfills/dns.ts | 2 +- ext/node/polyfills/dns/promises.ts | 2 +- ext/node/polyfills/domain.ts | 2 +- ext/node/polyfills/events.ts | 2 +- ext/node/polyfills/fs.ts | 2 +- ext/node/polyfills/fs/promises.ts | 2 +- ext/node/polyfills/http.ts | 2 +- ext/node/polyfills/http2.ts | 2 +- ext/node/polyfills/https.ts | 2 +- ext/node/polyfills/inspector.js | 2 +- ext/node/polyfills/inspector/promises.js | 2 +- ext/node/polyfills/internal/assert.mjs | 2 +- ext/node/polyfills/internal/async_hooks.ts | 2 +- ext/node/polyfills/internal/blocklist.mjs | 2 +- ext/node/polyfills/internal/buffer.d.ts | 2 +- ext/node/polyfills/internal/buffer.mjs | 2 +- ext/node/polyfills/internal/child_process.ts | 2 +- ext/node/polyfills/internal/cli_table.ts | 2 +- ext/node/polyfills/internal/console/constructor.mjs | 2 +- ext/node/polyfills/internal/constants.ts | 2 +- ext/node/polyfills/internal/crypto/_keys.ts | 2 +- ext/node/polyfills/internal/crypto/_randomBytes.ts | 2 +- ext/node/polyfills/internal/crypto/_randomFill.mjs | 2 +- ext/node/polyfills/internal/crypto/_randomInt.ts | 2 +- ext/node/polyfills/internal/crypto/certificate.ts | 2 +- ext/node/polyfills/internal/crypto/cipher.ts | 2 +- ext/node/polyfills/internal/crypto/constants.ts | 2 +- ext/node/polyfills/internal/crypto/diffiehellman.ts | 2 +- ext/node/polyfills/internal/crypto/hash.ts | 2 +- ext/node/polyfills/internal/crypto/hkdf.ts | 2 +- ext/node/polyfills/internal/crypto/keygen.ts | 2 +- ext/node/polyfills/internal/crypto/keys.ts | 2 +- ext/node/polyfills/internal/crypto/pbkdf2.ts | 2 +- ext/node/polyfills/internal/crypto/random.ts | 2 +- ext/node/polyfills/internal/crypto/scrypt.ts | 2 +- ext/node/polyfills/internal/crypto/sig.ts | 2 +- ext/node/polyfills/internal/crypto/types.ts | 2 +- ext/node/polyfills/internal/crypto/util.ts | 2 +- ext/node/polyfills/internal/crypto/x509.ts | 2 +- ext/node/polyfills/internal/dgram.ts | 2 +- ext/node/polyfills/internal/dns/promises.ts | 2 +- ext/node/polyfills/internal/dns/utils.ts | 2 +- ext/node/polyfills/internal/dtrace.ts | 2 +- ext/node/polyfills/internal/error_codes.ts | 2 +- ext/node/polyfills/internal/errors.ts | 2 +- ext/node/polyfills/internal/event_target.mjs | 2 +- ext/node/polyfills/internal/events/abort_listener.mjs | 2 +- ext/node/polyfills/internal/fixed_queue.ts | 2 +- ext/node/polyfills/internal/fs/handle.ts | 2 +- ext/node/polyfills/internal/fs/streams.d.ts | 2 +- ext/node/polyfills/internal/fs/streams.mjs | 2 +- ext/node/polyfills/internal/fs/utils.mjs | 2 +- ext/node/polyfills/internal/hide_stack_frames.ts | 2 +- ext/node/polyfills/internal/http.ts | 2 +- ext/node/polyfills/internal/idna.ts | 2 +- ext/node/polyfills/internal/net.ts | 2 +- ext/node/polyfills/internal/normalize_encoding.mjs | 2 +- ext/node/polyfills/internal/options.ts | 2 +- ext/node/polyfills/internal/primordials.mjs | 2 +- ext/node/polyfills/internal/process/per_thread.mjs | 2 +- ext/node/polyfills/internal/process/report.ts | 2 +- ext/node/polyfills/internal/querystring.ts | 2 +- ext/node/polyfills/internal/readline/callbacks.mjs | 2 +- ext/node/polyfills/internal/readline/emitKeypressEvents.mjs | 2 +- ext/node/polyfills/internal/readline/interface.mjs | 2 +- ext/node/polyfills/internal/readline/promises.mjs | 2 +- ext/node/polyfills/internal/readline/symbols.mjs | 2 +- ext/node/polyfills/internal/readline/utils.mjs | 2 +- ext/node/polyfills/internal/stream_base_commons.ts | 2 +- ext/node/polyfills/internal/streams/add-abort-signal.mjs | 2 +- ext/node/polyfills/internal/streams/buffer_list.mjs | 2 +- ext/node/polyfills/internal/streams/destroy.mjs | 2 +- ext/node/polyfills/internal/streams/duplex.mjs | 2 +- ext/node/polyfills/internal/streams/end-of-stream.mjs | 2 +- ext/node/polyfills/internal/streams/lazy_transform.mjs | 2 +- ext/node/polyfills/internal/streams/passthrough.mjs | 2 +- ext/node/polyfills/internal/streams/readable.mjs | 2 +- ext/node/polyfills/internal/streams/state.mjs | 2 +- ext/node/polyfills/internal/streams/transform.mjs | 2 +- ext/node/polyfills/internal/streams/utils.mjs | 2 +- ext/node/polyfills/internal/streams/writable.mjs | 2 +- ext/node/polyfills/internal/test/binding.ts | 2 +- ext/node/polyfills/internal/timers.mjs | 2 +- ext/node/polyfills/internal/url.ts | 2 +- ext/node/polyfills/internal/util.mjs | 2 +- ext/node/polyfills/internal/util/comparisons.ts | 2 +- ext/node/polyfills/internal/util/debuglog.ts | 2 +- ext/node/polyfills/internal/util/inspect.mjs | 2 +- ext/node/polyfills/internal/util/parse_args/parse_args.js | 2 +- ext/node/polyfills/internal/util/parse_args/utils.js | 2 +- ext/node/polyfills/internal/util/types.ts | 2 +- ext/node/polyfills/internal/validators.mjs | 2 +- ext/node/polyfills/internal_binding/_libuv_winerror.ts | 2 +- ext/node/polyfills/internal_binding/_listen.ts | 2 +- ext/node/polyfills/internal_binding/_node.ts | 2 +- ext/node/polyfills/internal_binding/_timingSafeEqual.ts | 2 +- ext/node/polyfills/internal_binding/_utils.ts | 2 +- ext/node/polyfills/internal_binding/ares.ts | 2 +- ext/node/polyfills/internal_binding/async_wrap.ts | 2 +- ext/node/polyfills/internal_binding/buffer.ts | 2 +- ext/node/polyfills/internal_binding/cares_wrap.ts | 2 +- ext/node/polyfills/internal_binding/connection_wrap.ts | 2 +- ext/node/polyfills/internal_binding/constants.ts | 2 +- ext/node/polyfills/internal_binding/crypto.ts | 2 +- ext/node/polyfills/internal_binding/handle_wrap.ts | 2 +- ext/node/polyfills/internal_binding/http_parser.ts | 2 +- ext/node/polyfills/internal_binding/mod.ts | 2 +- ext/node/polyfills/internal_binding/node_file.ts | 2 +- ext/node/polyfills/internal_binding/node_options.ts | 2 +- ext/node/polyfills/internal_binding/pipe_wrap.ts | 2 +- ext/node/polyfills/internal_binding/stream_wrap.ts | 2 +- ext/node/polyfills/internal_binding/string_decoder.ts | 2 +- ext/node/polyfills/internal_binding/symbols.ts | 2 +- ext/node/polyfills/internal_binding/tcp_wrap.ts | 2 +- ext/node/polyfills/internal_binding/types.ts | 2 +- ext/node/polyfills/internal_binding/udp_wrap.ts | 2 +- ext/node/polyfills/internal_binding/util.ts | 2 +- ext/node/polyfills/internal_binding/uv.ts | 2 +- ext/node/polyfills/net.ts | 2 +- ext/node/polyfills/os.ts | 2 +- ext/node/polyfills/path.ts | 2 +- ext/node/polyfills/path/_constants.ts | 2 +- ext/node/polyfills/path/_interface.ts | 2 +- ext/node/polyfills/path/_posix.ts | 2 +- ext/node/polyfills/path/_util.ts | 2 +- ext/node/polyfills/path/_win32.ts | 2 +- ext/node/polyfills/path/common.ts | 2 +- ext/node/polyfills/path/mod.ts | 2 +- ext/node/polyfills/path/posix.ts | 2 +- ext/node/polyfills/path/separator.ts | 2 +- ext/node/polyfills/path/win32.ts | 2 +- ext/node/polyfills/perf_hooks.ts | 2 +- ext/node/polyfills/process.ts | 2 +- ext/node/polyfills/punycode.ts | 2 +- ext/node/polyfills/querystring.js | 2 +- ext/node/polyfills/readline.ts | 2 +- ext/node/polyfills/readline/promises.ts | 2 +- ext/node/polyfills/repl.ts | 2 +- ext/node/polyfills/stream.ts | 2 +- ext/node/polyfills/stream/consumers.mjs | 2 +- ext/node/polyfills/stream/promises.mjs | 2 +- ext/node/polyfills/stream/web.ts | 2 +- ext/node/polyfills/string_decoder.ts | 2 +- ext/node/polyfills/sys.ts | 2 +- ext/node/polyfills/testing.ts | 2 +- ext/node/polyfills/timers.ts | 2 +- ext/node/polyfills/timers/promises.ts | 2 +- ext/node/polyfills/tls.ts | 2 +- ext/node/polyfills/trace_events.ts | 2 +- ext/node/polyfills/tty.js | 2 +- ext/node/polyfills/url.ts | 2 +- ext/node/polyfills/util.ts | 2 +- ext/node/polyfills/util/types.ts | 2 +- ext/node/polyfills/v8.ts | 2 +- ext/node/polyfills/vm.js | 2 +- ext/node/polyfills/wasi.ts | 2 +- ext/node/polyfills/worker_threads.ts | 2 +- ext/node/polyfills/zlib.ts | 2 +- ext/telemetry/Cargo.toml | 2 +- ext/telemetry/lib.rs | 2 +- ext/telemetry/telemetry.ts | 2 +- ext/telemetry/util.ts | 2 +- ext/tls/Cargo.toml | 2 +- ext/tls/lib.rs | 2 +- ext/tls/tls_key.rs | 2 +- ext/url/00_url.js | 2 +- ext/url/01_urlpattern.js | 2 +- ext/url/Cargo.toml | 2 +- ext/url/benches/url_ops.rs | 2 +- ext/url/internal.d.ts | 2 +- ext/url/lib.deno_url.d.ts | 2 +- ext/url/lib.rs | 2 +- ext/url/urlpattern.rs | 2 +- ext/web/00_infra.js | 2 +- ext/web/01_dom_exception.js | 2 +- ext/web/01_mimesniff.js | 2 +- ext/web/02_event.js | 2 +- ext/web/02_structured_clone.js | 2 +- ext/web/02_timers.js | 2 +- ext/web/03_abort_signal.js | 2 +- ext/web/04_global_interfaces.js | 2 +- ext/web/05_base64.js | 2 +- ext/web/06_streams.js | 2 +- ext/web/06_streams_types.d.ts | 2 +- ext/web/08_text_encoding.js | 2 +- ext/web/09_file.js | 2 +- ext/web/10_filereader.js | 2 +- ext/web/12_location.js | 2 +- ext/web/13_message_port.js | 2 +- ext/web/14_compression.js | 2 +- ext/web/15_performance.js | 2 +- ext/web/16_image_data.js | 2 +- ext/web/Cargo.toml | 2 +- ext/web/benches/encoding.rs | 2 +- ext/web/benches/timers_ops.rs | 2 +- ext/web/blob.rs | 2 +- ext/web/compression.rs | 2 +- ext/web/internal.d.ts | 2 +- ext/web/lib.deno_web.d.ts | 2 +- ext/web/lib.rs | 2 +- ext/web/message_port.rs | 2 +- ext/web/stream_resource.rs | 2 +- ext/web/timers.rs | 2 +- ext/webgpu/00_init.js | 2 +- ext/webgpu/01_webgpu.js | 2 +- ext/webgpu/02_surface.js | 2 +- ext/webgpu/Cargo.toml | 2 +- ext/webgpu/binding.rs | 2 +- ext/webgpu/buffer.rs | 2 +- ext/webgpu/bundle.rs | 2 +- ext/webgpu/byow.rs | 2 +- ext/webgpu/command_encoder.rs | 2 +- ext/webgpu/compute_pass.rs | 2 +- ext/webgpu/error.rs | 2 +- ext/webgpu/lib.rs | 2 +- ext/webgpu/pipeline.rs | 2 +- ext/webgpu/queue.rs | 2 +- ext/webgpu/render_pass.rs | 2 +- ext/webgpu/sampler.rs | 2 +- ext/webgpu/shader.rs | 2 +- ext/webgpu/surface.rs | 2 +- ext/webgpu/texture.rs | 2 +- ext/webidl/00_webidl.js | 2 +- ext/webidl/Cargo.toml | 2 +- ext/webidl/benches/dict.js | 2 +- ext/webidl/benches/dict.rs | 2 +- ext/webidl/internal.d.ts | 2 +- ext/webidl/lib.rs | 2 +- ext/websocket/01_websocket.js | 2 +- ext/websocket/02_websocketstream.js | 2 +- ext/websocket/Cargo.toml | 2 +- ext/websocket/autobahn/autobahn_server.js | 2 +- ext/websocket/autobahn/fuzzingclient.js | 2 +- ext/websocket/lib.deno_websocket.d.ts | 2 +- ext/websocket/lib.rs | 2 +- ext/websocket/stream.rs | 2 +- ext/webstorage/01_webstorage.js | 2 +- ext/webstorage/Cargo.toml | 2 +- ext/webstorage/lib.deno_webstorage.d.ts | 2 +- ext/webstorage/lib.rs | 2 +- resolvers/deno/Cargo.toml | 2 +- resolvers/deno/cjs.rs | 2 +- resolvers/deno/lib.rs | 2 +- resolvers/deno/npm/byonm.rs | 2 +- resolvers/deno/npm/local.rs | 2 +- resolvers/deno/npm/mod.rs | 2 +- resolvers/deno/sloppy_imports.rs | 2 +- resolvers/deno/sync.rs | 2 +- resolvers/node/Cargo.toml | 2 +- resolvers/node/analyze.rs | 2 +- resolvers/node/errors.rs | 2 +- resolvers/node/lib.rs | 2 +- resolvers/node/npm.rs | 2 +- resolvers/node/package_json.rs | 2 +- resolvers/node/path.rs | 2 +- resolvers/node/resolution.rs | 2 +- resolvers/node/sync.rs | 2 +- resolvers/npm_cache/Cargo.toml | 2 +- resolvers/npm_cache/fs_util.rs | 2 +- resolvers/npm_cache/lib.rs | 2 +- resolvers/npm_cache/registry_info.rs | 2 +- resolvers/npm_cache/remote.rs | 2 +- resolvers/npm_cache/tarball.rs | 2 +- resolvers/npm_cache/tarball_extract.rs | 2 +- runtime/Cargo.toml | 2 +- runtime/code_cache.rs | 2 +- runtime/errors.rs | 2 +- runtime/examples/extension/bootstrap.js | 2 +- runtime/examples/extension/main.js | 2 +- runtime/examples/extension/main.rs | 2 +- runtime/fmt_errors.rs | 2 +- runtime/fs_util.rs | 2 +- runtime/inspector_server.rs | 2 +- runtime/js.rs | 2 +- runtime/js/01_errors.js | 2 +- runtime/js/01_version.ts | 2 +- runtime/js/06_util.js | 2 +- runtime/js/10_permissions.js | 2 +- runtime/js/11_workers.js | 2 +- runtime/js/30_os.js | 2 +- runtime/js/40_fs_events.js | 2 +- runtime/js/40_process.js | 2 +- runtime/js/40_signals.js | 2 +- runtime/js/40_tty.js | 2 +- runtime/js/41_prompt.js | 2 +- runtime/js/90_deno_ns.js | 2 +- runtime/js/98_global_scope_shared.js | 2 +- runtime/js/98_global_scope_window.js | 2 +- runtime/js/98_global_scope_worker.js | 2 +- runtime/js/99_main.js | 2 +- runtime/lib.rs | 2 +- runtime/ops/bootstrap.rs | 2 +- runtime/ops/fs_events.rs | 2 +- runtime/ops/http.rs | 2 +- runtime/ops/mod.rs | 2 +- runtime/ops/os/mod.rs | 2 +- runtime/ops/permissions.rs | 2 +- runtime/ops/process.rs | 2 +- runtime/ops/runtime.rs | 2 +- runtime/ops/signal.rs | 2 +- runtime/ops/tty.rs | 2 +- runtime/ops/web_worker.rs | 2 +- runtime/ops/web_worker/sync_fetch.rs | 2 +- runtime/ops/worker_host.rs | 2 +- runtime/permissions.rs | 2 +- runtime/permissions/Cargo.toml | 2 +- runtime/permissions/lib.rs | 2 +- runtime/permissions/prompter.rs | 2 +- runtime/shared.rs | 2 +- runtime/signal.rs | 2 +- runtime/snapshot.rs | 2 +- runtime/sys_info.rs | 2 +- runtime/tokio_util.rs | 2 +- runtime/web_worker.rs | 2 +- runtime/worker.rs | 2 +- runtime/worker_bootstrap.rs | 2 +- tests/Cargo.toml | 2 +- tests/ffi/Cargo.toml | 2 +- tests/ffi/src/lib.rs | 2 +- tests/ffi/tests/bench.js | 2 +- tests/ffi/tests/event_loop_integration.ts | 2 +- tests/ffi/tests/ffi_callback_errors.ts | 2 +- tests/ffi/tests/ffi_types.ts | 2 +- tests/ffi/tests/integration_tests.rs | 2 +- tests/ffi/tests/test.js | 2 +- tests/ffi/tests/thread_safe_test.js | 2 +- tests/ffi/tests/thread_safe_test_worker.js | 2 +- tests/integration/bench_tests.rs | 2 +- tests/integration/cache_tests.rs | 2 +- tests/integration/check_tests.rs | 2 +- tests/integration/compile_tests.rs | 2 +- tests/integration/coverage_tests.rs | 2 +- tests/integration/eval_tests.rs | 2 +- tests/integration/flags_tests.rs | 2 +- tests/integration/fmt_tests.rs | 2 +- tests/integration/init_tests.rs | 2 +- tests/integration/inspector_tests.rs | 2 +- tests/integration/install_tests.rs | 2 +- tests/integration/js_unit_tests.rs | 2 +- tests/integration/jsr_tests.rs | 2 +- tests/integration/jupyter_tests.rs | 2 +- tests/integration/lsp_tests.rs | 2 +- tests/integration/mod.rs | 2 +- tests/integration/node_unit_tests.rs | 2 +- tests/integration/npm_tests.rs | 2 +- tests/integration/pm_tests.rs | 2 +- tests/integration/publish_tests.rs | 2 +- tests/integration/repl_tests.rs | 2 +- tests/integration/run_tests.rs | 2 +- tests/integration/serve_tests.rs | 2 +- tests/integration/shared_library_tests.rs | 2 +- tests/integration/task_tests.rs | 2 +- tests/integration/test_tests.rs | 2 +- tests/integration/upgrade_tests.rs | 2 +- tests/integration/watcher_tests.rs | 2 +- tests/lib.rs | 2 +- tests/napi/Cargo.toml | 2 +- tests/napi/array_test.js | 2 +- tests/napi/arraybuffer_test.js | 2 +- tests/napi/async_test.js | 2 +- tests/napi/bigint_test.js | 2 +- tests/napi/build.rs | 2 +- tests/napi/callback_test.js | 2 +- tests/napi/cleanup_hook_test.js | 2 +- tests/napi/coerce_test.js | 2 +- tests/napi/common.js | 2 +- tests/napi/date_test.js | 2 +- tests/napi/env_test.js | 2 +- tests/napi/error_test.js | 2 +- tests/napi/init_test.js | 2 +- tests/napi/make_callback_test.js | 2 +- tests/napi/mem_test.js | 2 +- tests/napi/module.c | 2 +- tests/napi/numbers_test.js | 2 +- tests/napi/object_test.js | 2 +- tests/napi/object_wrap_test.js | 2 +- tests/napi/promise_test.js | 2 +- tests/napi/properties_test.js | 2 +- tests/napi/src/array.rs | 2 +- tests/napi/src/arraybuffer.rs | 2 +- tests/napi/src/async.rs | 2 +- tests/napi/src/bigint.rs | 2 +- tests/napi/src/callback.rs | 2 +- tests/napi/src/coerce.rs | 2 +- tests/napi/src/date.rs | 2 +- tests/napi/src/env.rs | 2 +- tests/napi/src/error.rs | 2 +- tests/napi/src/finalizer.rs | 2 +- tests/napi/src/lib.rs | 2 +- tests/napi/src/make_callback.rs | 2 +- tests/napi/src/mem.rs | 2 +- tests/napi/src/numbers.rs | 2 +- tests/napi/src/object.rs | 2 +- tests/napi/src/object_wrap.rs | 2 +- tests/napi/src/primitives.rs | 2 +- tests/napi/src/promise.rs | 2 +- tests/napi/src/properties.rs | 2 +- tests/napi/src/strings.rs | 2 +- tests/napi/src/symbol.rs | 2 +- tests/napi/src/tsfn.rs | 2 +- tests/napi/src/typedarray.rs | 2 +- tests/napi/src/uv.rs | 2 +- tests/napi/strings_test.js | 2 +- tests/napi/symbol_test.js | 2 +- tests/napi/tests/napi_tests.rs | 2 +- tests/napi/typedarray_test.js | 2 +- tests/napi/uv_test.js | 2 +- tests/node_compat/common.ts | 2 +- tests/node_compat/polyfill_globals.js | 2 +- tests/node_compat/runner.ts | 2 +- tests/node_compat/runner/challenge_new_test.ts | 2 +- tests/node_compat/runner/setup.ts | 2 +- tests/node_compat/test.ts | 2 +- tests/node_compat/test_runner.rs | 2 +- tests/registry/jsr/@std/assert/0.220.1/mod.ts | 2 +- tests/registry/jsr/@std/assert/1.0.0/mod.ts | 2 +- tests/registry/jsr/@std/http/1.0.0/mod.ts | 2 +- tests/registry/jsr/@std/path/0.220.1/_common/assert_path.ts | 2 +- tests/registry/jsr/@std/path/0.220.1/_common/constants.ts | 2 +- tests/registry/jsr/@std/path/0.220.1/_common/normalize.ts | 2 +- .../jsr/@std/path/0.220.1/_common/normalize_string.ts | 2 +- tests/registry/jsr/@std/path/0.220.1/posix/_util.ts | 2 +- tests/registry/jsr/@std/path/0.220.1/posix/join.ts | 2 +- tests/registry/jsr/@std/path/0.220.1/posix/normalize.ts | 2 +- tests/registry/jsr/@std/url/0.220.1/join.ts | 2 +- tests/registry/jsr/@std/url/0.220.1/normalize.ts | 2 +- tests/registry/npm/trim_registry_files.js | 2 +- tests/specs/cli/otel_basic/basic.ts | 2 +- tests/specs/cli/otel_basic/main.ts | 2 +- tests/specs/mod.rs | 2 +- tests/specs/repl/console_log/093_console_log_format.js | 2 +- tests/specs/run/045_proxy/programmatic_proxy_client.ts | 2 +- tests/specs/run/045_proxy/proxy_client.ts | 2 +- tests/specs/run/045_proxy/proxy_test.ts | 2 +- .../run/finalization_registry/finalization_registry.js | 2 +- tests/specs/run/heapstats/heapstats.js | 2 +- tests/specs/run/tls_connecttls/textproto.ts | 2 +- tests/specs/run/tls_starttls/textproto.ts | 2 +- .../worker_close_in_wasm_reactions.js | 2 +- tests/specs/run/worker_close_nested/close_nested_child.js | 2 +- tests/specs/run/worker_close_nested/close_nested_parent.js | 2 +- tests/specs/run/worker_close_nested/worker_close_nested.js | 2 +- tests/specs/run/worker_close_race/close_race_worker.js | 2 +- tests/specs/run/worker_close_race/worker_close_race.js | 2 +- .../run/worker_drop_handle_race/worker_drop_handle_race.js | 2 +- .../worker_drop_handle_race_terminate.js | 2 +- tests/testdata/commonjs/example.js | 2 +- tests/testdata/run/textproto.ts | 2 +- tests/testdata/workers/close_nested_child.js | 2 +- tests/testdata/workers/close_nested_parent.js | 2 +- tests/testdata/workers/close_race_worker.js | 2 +- tests/testdata/workers/http_worker.js | 2 +- tests/unit/abort_controller_test.ts | 2 +- tests/unit/blob_test.ts | 2 +- tests/unit/body_test.ts | 2 +- tests/unit/broadcast_channel_test.ts | 2 +- tests/unit/build_test.ts | 2 +- tests/unit/cache_api_test.ts | 2 +- tests/unit/chmod_test.ts | 2 +- tests/unit/chown_test.ts | 2 +- tests/unit/command_test.ts | 2 +- tests/unit/console_test.ts | 2 +- tests/unit/copy_file_test.ts | 2 +- tests/unit/cron_test.ts | 2 +- tests/unit/custom_event_test.ts | 2 +- tests/unit/dir_test.ts | 2 +- tests/unit/dom_exception_test.ts | 2 +- tests/unit/error_stack_test.ts | 2 +- tests/unit/error_test.ts | 2 +- tests/unit/esnext_test.ts | 2 +- tests/unit/event_source_test.ts | 2 +- tests/unit/event_target_test.ts | 2 +- tests/unit/event_test.ts | 2 +- tests/unit/fetch_test.ts | 2 +- tests/unit/ffi_test.ts | 2 +- tests/unit/file_test.ts | 2 +- tests/unit/filereader_test.ts | 2 +- tests/unit/files_test.ts | 2 +- tests/unit/fs_events_test.ts | 2 +- tests/unit/get_random_values_test.ts | 2 +- tests/unit/globals_test.ts | 2 +- tests/unit/headers_test.ts | 2 +- tests/unit/http_test.ts | 2 +- tests/unit/image_bitmap_test.ts | 2 +- tests/unit/image_data_test.ts | 2 +- tests/unit/internals_test.ts | 2 +- tests/unit/intl_test.ts | 2 +- tests/unit/jupyter_test.ts | 2 +- tests/unit/kv_queue_test.ts | 2 +- tests/unit/kv_queue_test_no_db_close.ts | 2 +- tests/unit/kv_queue_undelivered_test.ts | 2 +- tests/unit/kv_test.ts | 2 +- tests/unit/link_test.ts | 2 +- tests/unit/lint_plugin_test.ts | 2 +- tests/unit/lint_selectors_test.ts | 2 +- tests/unit/make_temp_test.ts | 2 +- tests/unit/message_channel_test.ts | 2 +- tests/unit/mkdir_test.ts | 2 +- tests/unit/navigator_test.ts | 2 +- tests/unit/net_test.ts | 2 +- tests/unit/network_interfaces_test.ts | 2 +- tests/unit/ops_test.ts | 2 +- tests/unit/os_test.ts | 2 +- tests/unit/path_from_url_test.ts | 2 +- tests/unit/performance_test.ts | 2 +- tests/unit/permissions_test.ts | 2 +- tests/unit/process_test.ts | 2 +- tests/unit/progressevent_test.ts | 2 +- tests/unit/promise_hooks_test.ts | 2 +- tests/unit/quic_test.ts | 2 +- tests/unit/read_dir_test.ts | 2 +- tests/unit/read_file_test.ts | 2 +- tests/unit/read_link_test.ts | 2 +- tests/unit/read_text_file_test.ts | 2 +- tests/unit/real_path_test.ts | 2 +- tests/unit/ref_unref_test.ts | 2 +- tests/unit/remove_test.ts | 2 +- tests/unit/rename_test.ts | 2 +- tests/unit/request_test.ts | 2 +- tests/unit/response_test.ts | 2 +- tests/unit/serve_test.ts | 2 +- tests/unit/signal_test.ts | 2 +- tests/unit/stat_test.ts | 2 +- tests/unit/stdio_test.ts | 2 +- tests/unit/streams_test.ts | 2 +- tests/unit/structured_clone_test.ts | 2 +- tests/unit/symbol_test.ts | 2 +- tests/unit/symlink_test.ts | 2 +- tests/unit/test_util.ts | 2 +- tests/unit/testing_test.ts | 2 +- tests/unit/text_encoding_test.ts | 2 +- tests/unit/timers_test.ts | 2 +- tests/unit/tls_sni_test.ts | 2 +- tests/unit/tls_test.ts | 2 +- tests/unit/truncate_test.ts | 2 +- tests/unit/tty_color_test.ts | 2 +- tests/unit/tty_test.ts | 2 +- tests/unit/umask_test.ts | 2 +- tests/unit/url_search_params_test.ts | 2 +- tests/unit/url_test.ts | 2 +- tests/unit/urlpattern_test.ts | 2 +- tests/unit/utime_test.ts | 2 +- tests/unit/version_test.ts | 2 +- tests/unit/wasm_test.ts | 2 +- tests/unit/webcrypto_test.ts | 2 +- tests/unit/webgpu_test.ts | 2 +- tests/unit/websocket_test.ts | 2 +- tests/unit/websocketstream_test.ts.disabled | 2 +- tests/unit/webstorage_test.ts | 2 +- tests/unit/worker_permissions_test.ts | 2 +- tests/unit/worker_test.ts | 2 +- tests/unit/write_file_test.ts | 2 +- tests/unit/write_text_file_test.ts | 2 +- tests/unit_node/_fs/_fs_access_test.ts | 2 +- tests/unit_node/_fs/_fs_appendFile_test.ts | 2 +- tests/unit_node/_fs/_fs_chmod_test.ts | 2 +- tests/unit_node/_fs/_fs_chown_test.ts | 2 +- tests/unit_node/_fs/_fs_close_test.ts | 2 +- tests/unit_node/_fs/_fs_copy_test.ts | 2 +- tests/unit_node/_fs/_fs_dir_test.ts | 2 +- tests/unit_node/_fs/_fs_dirent_test.ts | 2 +- tests/unit_node/_fs/_fs_exists_test.ts | 2 +- tests/unit_node/_fs/_fs_fdatasync_test.ts | 2 +- tests/unit_node/_fs/_fs_fstat_test.ts | 2 +- tests/unit_node/_fs/_fs_fsync_test.ts | 2 +- tests/unit_node/_fs/_fs_ftruncate_test.ts | 2 +- tests/unit_node/_fs/_fs_futimes_test.ts | 2 +- tests/unit_node/_fs/_fs_handle_test.ts | 2 +- tests/unit_node/_fs/_fs_link_test.ts | 2 +- tests/unit_node/_fs/_fs_lstat_test.ts | 2 +- tests/unit_node/_fs/_fs_mkdir_test.ts | 2 +- tests/unit_node/_fs/_fs_mkdtemp_test.ts | 2 +- tests/unit_node/_fs/_fs_open_test.ts | 2 +- tests/unit_node/_fs/_fs_opendir_test.ts | 2 +- tests/unit_node/_fs/_fs_readFile_test.ts | 2 +- tests/unit_node/_fs/_fs_read_test.ts | 2 +- tests/unit_node/_fs/_fs_readdir_test.ts | 2 +- tests/unit_node/_fs/_fs_readlink_test.ts | 2 +- tests/unit_node/_fs/_fs_realpath_test.ts | 2 +- tests/unit_node/_fs/_fs_rename_test.ts | 2 +- tests/unit_node/_fs/_fs_rm_test.ts | 2 +- tests/unit_node/_fs/_fs_rmdir_test.ts | 2 +- tests/unit_node/_fs/_fs_stat_test.ts | 2 +- tests/unit_node/_fs/_fs_statfs_test.ts | 2 +- tests/unit_node/_fs/_fs_symlink_test.ts | 2 +- tests/unit_node/_fs/_fs_truncate_test.ts | 2 +- tests/unit_node/_fs/_fs_unlink_test.ts | 2 +- tests/unit_node/_fs/_fs_utimes_test.ts | 2 +- tests/unit_node/_fs/_fs_watch_test.ts | 2 +- tests/unit_node/_fs/_fs_writeFile_test.ts | 2 +- tests/unit_node/_fs/_fs_write_test.ts | 2 +- tests/unit_node/_test_utils.ts | 2 +- tests/unit_node/assert_test.ts | 2 +- tests/unit_node/assertion_error_test.ts | 2 +- tests/unit_node/async_hooks_test.ts | 2 +- tests/unit_node/buffer_test.ts | 2 +- tests/unit_node/child_process_test.ts | 2 +- tests/unit_node/cluster_test.ts | 2 +- tests/unit_node/console_test.ts | 2 +- tests/unit_node/crypto/crypto_cipher_gcm_test.ts | 2 +- tests/unit_node/crypto/crypto_cipher_test.ts | 2 +- tests/unit_node/crypto/crypto_hash_test.ts | 2 +- tests/unit_node/crypto/crypto_hkdf_test.ts | 2 +- tests/unit_node/crypto/crypto_import_export.ts | 2 +- tests/unit_node/crypto/crypto_key_test.ts | 2 +- tests/unit_node/crypto/crypto_misc_test.ts | 2 +- tests/unit_node/crypto/crypto_pbkdf2_test.ts | 2 +- tests/unit_node/crypto/crypto_scrypt_test.ts | 2 +- tests/unit_node/crypto/crypto_sign_test.ts | 2 +- tests/unit_node/crypto/generate_fixture.mjs | 2 +- tests/unit_node/crypto/generate_keys.mjs | 2 +- tests/unit_node/dgram_test.ts | 2 +- tests/unit_node/domain_test.ts | 2 +- tests/unit_node/events_test.ts | 2 +- tests/unit_node/fetch_test.ts | 2 +- tests/unit_node/fs_test.ts | 2 +- tests/unit_node/http2_test.ts | 2 +- tests/unit_node/http_test.ts | 2 +- tests/unit_node/inspector_test.ts | 2 +- tests/unit_node/internal/_randomBytes_test.ts | 2 +- tests/unit_node/internal/_randomFill_test.ts | 2 +- tests/unit_node/internal/_randomInt_test.ts | 2 +- tests/unit_node/module_test.ts | 2 +- tests/unit_node/net_test.ts | 2 +- tests/unit_node/os_test.ts | 2 +- tests/unit_node/path_test.ts | 2 +- tests/unit_node/perf_hooks_test.ts | 2 +- tests/unit_node/process_test.ts | 2 +- tests/unit_node/punycode_test.ts | 2 +- tests/unit_node/querystring_test.ts | 2 +- tests/unit_node/readline_test.ts | 2 +- tests/unit_node/repl_test.ts | 2 +- tests/unit_node/stream_test.ts | 2 +- tests/unit_node/string_decoder_test.ts | 2 +- tests/unit_node/timers_test.ts | 2 +- tests/unit_node/tls_test.ts | 2 +- tests/unit_node/tty_test.ts | 2 +- tests/unit_node/util_test.ts | 2 +- tests/unit_node/v8_test.ts | 2 +- tests/unit_node/vm_test.ts | 2 +- tests/unit_node/wasi_test.ts | 2 +- tests/unit_node/worker_threads_test.ts | 2 +- tests/unit_node/zlib_test.ts | 2 +- tests/util/server/Cargo.toml | 2 +- tests/util/server/src/assertions.rs | 2 +- tests/util/server/src/builders.rs | 2 +- tests/util/server/src/factory.rs | 2 +- tests/util/server/src/fs.rs | 2 +- tests/util/server/src/https.rs | 2 +- tests/util/server/src/lib.rs | 2 +- tests/util/server/src/lsp.rs | 2 +- tests/util/server/src/macros.rs | 2 +- tests/util/server/src/npm.rs | 2 +- tests/util/server/src/pty.rs | 2 +- tests/util/server/src/servers/grpc.rs | 2 +- tests/util/server/src/servers/hyper_utils.rs | 2 +- tests/util/server/src/servers/jsr_registry.rs | 2 +- tests/util/server/src/servers/mod.rs | 2 +- tests/util/server/src/servers/nodejs_org_mirror.rs | 2 +- tests/util/server/src/servers/npm_registry.rs | 2 +- tests/util/server/src/servers/ws.rs | 2 +- tests/util/server/src/spawn.rs | 2 +- tests/util/server/src/test_server.rs | 2 +- tests/wpt/runner/runner.ts | 2 +- tests/wpt/runner/testharnessreport.js | 2 +- tests/wpt/runner/utils.ts | 2 +- tests/wpt/wpt.ts | 2 +- tools/build_bench.ts | 2 +- tools/build_benchmark_jsons.js | 2 +- tools/copyright_checker.js | 6 +++--- tools/format.js | 2 +- tools/generate_types_deno.ts | 2 +- tools/install_prebuilt.js | 2 +- tools/jsdoc_checker.js | 2 +- tools/lint.js | 2 +- tools/napi/generate_symbols_lists.js | 2 +- tools/ops.d.ts | 2 +- tools/release/00_start_release.ts | 2 +- tools/release/01_bump_crate_versions.ts | 2 +- tools/release/02_create_pr.ts | 2 +- tools/release/03_publish_crates.ts | 2 +- tools/release/04_post_publish.ts | 2 +- tools/release/05_create_release_notes.ts | 2 +- tools/release/deno_workspace.ts | 2 +- tools/release/deps.ts | 2 +- tools/release/npm/bin.cjs | 2 +- tools/release/npm/build.ts | 2 +- tools/release/npm/install.cjs | 2 +- tools/release/npm/install_api.cjs | 2 +- tools/release/promote_to_release.ts | 2 +- tools/upload_wptfyi.js | 2 +- tools/util.js | 2 +- tools/verify_pr_title.js | 2 +- tools/wgpu_sync.js | 2 +- 1190 files changed, 1192 insertions(+), 1192 deletions(-) diff --git a/.github/mtime_cache/action.js b/.github/mtime_cache/action.js index 72821749e3..1bf5b492fc 100644 --- a/.github/mtime_cache/action.js +++ b/.github/mtime_cache/action.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file contains the implementation of a Github Action. Github uses // Node.js v20.x to run actions, so this is Node code and not Deno code. diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index bc3f15380b..d43d80d8bc 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-write=. --lock=./tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { stringify } from "jsr:@std/yaml@^0.221/stringify"; // Bump this number when you want to purge the cache. diff --git a/Cargo.toml b/Cargo.toml index bfd7437441..1a7c04dbef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [workspace] resolver = "2" diff --git a/LICENSE.md b/LICENSE.md index 56753af367..406ae09364 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright 2018-2024 the Deno authors +Copyright 2018-2025 the Deno authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index 014b74f264..3172426c27 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_bench_util" diff --git a/bench_util/benches/utf8.rs b/bench_util/benches/utf8.rs index 48af4dba7e..88afce86c3 100644 --- a/bench_util/benches/utf8.rs +++ b/bench_util/benches/utf8.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_sync_with; use deno_bench_util::bench_or_profile; diff --git a/bench_util/js_runtime.rs b/bench_util/js_runtime.rs index a97d8ae501..402c9a4b00 100644 --- a/bench_util/js_runtime.rs +++ b/bench_util/js_runtime.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use bencher::Bencher; use deno_core::v8; use deno_core::Extension; diff --git a/bench_util/lib.rs b/bench_util/lib.rs index 39183be7fc..22587a5f7e 100644 --- a/bench_util/lib.rs +++ b/bench_util/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod js_runtime; mod profiling; diff --git a/bench_util/profiling.rs b/bench_util/profiling.rs index 151a29e599..1d2bfb51d2 100644 --- a/bench_util/profiling.rs +++ b/bench_util/profiling.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use bencher::DynBenchFn; use bencher::StaticBenchFn; use bencher::TestDescAndFn; diff --git a/cli/Cargo.toml b/cli/Cargo.toml index d05c3fb3e3..3ca1afc872 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno" diff --git a/cli/args/deno_json.rs b/cli/args/deno_json.rs index 47dcbb91ea..c27b1d3924 100644 --- a/cli/args/deno_json.rs +++ b/cli/args/deno_json.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; diff --git a/cli/args/flags.rs b/cli/args/flags.rs index cd6db5eea2..c57b81ae6d 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashSet; diff --git a/cli/args/flags_net.rs b/cli/args/flags_net.rs index 3d19a06183..c39e377e10 100644 --- a/cli/args/flags_net.rs +++ b/cli/args/flags_net.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::net::IpAddr; use std::str::FromStr; diff --git a/cli/args/import_map.rs b/cli/args/import_map.rs index d6434ed46a..ff7e42ef20 100644 --- a/cli/args/import_map.rs +++ b/cli/args/import_map.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::AnyError; use deno_core::serde_json; diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index 5d93aa6a4a..bc4c92638a 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::path::PathBuf; diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 4516cfe06a..1ad61a0a78 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod deno_json; mod flags; diff --git a/cli/args/package_json.rs b/cli/args/package_json.rs index 50d1c04799..efa5d46966 100644 --- a/cli/args/package_json.rs +++ b/cli/args/package_json.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::PathBuf; use std::sync::Arc; diff --git a/cli/bench/cache_api.js b/cli/bench/cache_api.js index af55fc132e..4b092ab627 100644 --- a/cli/bench/cache_api.js +++ b/cli/bench/cache_api.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const cacheName = "cache-v1"; const cache = await caches.open(cacheName); diff --git a/cli/bench/command.js b/cli/bench/command.js index 5b7c300d26..5916dcfee2 100644 --- a/cli/bench/command.js +++ b/cli/bench/command.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. Deno.bench("echo deno", async () => { await new Deno.Command("echo", { args: ["deno"] }).output(); diff --git a/cli/bench/console.js b/cli/bench/console.js index 1d336fbbde..c1704549c6 100644 --- a/cli/bench/console.js +++ b/cli/bench/console.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/cli/bench/deno_common.js b/cli/bench/deno_common.js index 3693333915..011a1244aa 100644 --- a/cli/bench/deno_common.js +++ b/cli/bench/deno_common.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // v8 builtin that's close to the upper bound non-NOPs Deno.bench("date_now", { n: 5e5 }, () => { diff --git a/cli/bench/encode_into.js b/cli/bench/encode_into.js index ab5e11b04d..57313ca04a 100644 --- a/cli/bench/encode_into.js +++ b/cli/bench/encode_into.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals let [total, count] = typeof Deno !== "undefined" diff --git a/cli/bench/fs/run.mjs b/cli/bench/fs/run.mjs index 94240f20d4..7f080daf6a 100644 --- a/cli/bench/fs/run.mjs +++ b/cli/bench/fs/run.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. let total = 5; let current = ""; diff --git a/cli/bench/fs/serve.jsx b/cli/bench/fs/serve.jsx index 51125235fe..8b3328617a 100644 --- a/cli/bench/fs/serve.jsx +++ b/cli/bench/fs/serve.jsx @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** @jsx h */ import results from "./deno.json" assert { type: "json" }; diff --git a/cli/bench/getrandom.js b/cli/bench/getrandom.js index fe99bbcbdf..775d02fc75 100644 --- a/cli/bench/getrandom.js +++ b/cli/bench/getrandom.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals let [total, count] = typeof Deno !== "undefined" diff --git a/cli/bench/lsp.rs b/cli/bench/lsp.rs index 1e02291e10..7a93dcae1e 100644 --- a/cli/bench/lsp.rs +++ b/cli/bench/lsp.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::path::Path; diff --git a/cli/bench/lsp_bench_standalone.rs b/cli/bench/lsp_bench_standalone.rs index 3c946cfbe3..45d8788256 100644 --- a/cli/bench/lsp_bench_standalone.rs +++ b/cli/bench/lsp_bench_standalone.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bencher::benchmark_group; use deno_bench_util::bencher::benchmark_main; diff --git a/cli/bench/main.rs b/cli/bench/main.rs index f38aa79e9e..e7f71f8cfa 100644 --- a/cli/bench/main.rs +++ b/cli/bench/main.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/cli/bench/napi/bench.js b/cli/bench/napi/bench.js index c12c7aacbc..f40611e7a8 100644 --- a/cli/bench/napi/bench.js +++ b/cli/bench/napi/bench.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { loadTestLibrary } from "../../../tests/napi/common.js"; diff --git a/cli/bench/napi/bench_node.mjs b/cli/bench/napi/bench_node.mjs index a772eeafa1..557c4daefd 100644 --- a/cli/bench/napi/bench_node.mjs +++ b/cli/bench/napi/bench_node.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { bench, run } from "mitata"; import { createRequire } from "module"; diff --git a/cli/bench/op_now.js b/cli/bench/op_now.js index 7c1427c809..26c4958fe0 100644 --- a/cli/bench/op_now.js +++ b/cli/bench/op_now.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; diff --git a/cli/bench/secure_curves.js b/cli/bench/secure_curves.js index 912b75cccd..a3fb4ebc41 100644 --- a/cli/bench/secure_curves.js +++ b/cli/bench/secure_curves.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals let [total, count] = typeof Deno !== "undefined" diff --git a/cli/bench/stdio/stdio.c b/cli/bench/stdio/stdio.c index acce207995..15df422405 100644 --- a/cli/bench/stdio/stdio.c +++ b/cli/bench/stdio/stdio.c @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // From https://github.com/just-js/benchmarks/tree/main/01-stdio #include diff --git a/cli/bench/stdio/stdio.js b/cli/bench/stdio/stdio.js index 81bea835a6..1ca947809a 100644 --- a/cli/bench/stdio/stdio.js +++ b/cli/bench/stdio/stdio.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // From https://github.com/just-js/benchmarks/tree/main/01-stdio diff --git a/cli/bench/tcp.js b/cli/bench/tcp.js index b9f05e3a7e..6681eeeb52 100644 --- a/cli/bench/tcp.js +++ b/cli/bench/tcp.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const listener = Deno.listen({ port: 4500 }); const response = new TextEncoder().encode( diff --git a/cli/bench/tty.js b/cli/bench/tty.js index e494e76af7..c61541ffa6 100644 --- a/cli/bench/tty.js +++ b/cli/bench/tty.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; diff --git a/cli/bench/url_parse.js b/cli/bench/url_parse.js index 9cb0045f64..80579d6f6b 100644 --- a/cli/bench/url_parse.js +++ b/cli/bench/url_parse.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; diff --git a/cli/bench/webstorage.js b/cli/bench/webstorage.js index d19f024c63..d284378d60 100644 --- a/cli/bench/webstorage.js +++ b/cli/bench/webstorage.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/cli/bench/write_file.js b/cli/bench/write_file.js index 747503ce2a..c7200a6f5b 100644 --- a/cli/bench/write_file.js +++ b/cli/bench/write_file.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; diff --git a/cli/build.rs b/cli/build.rs index 8367910678..83290599e6 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::env; use std::path::PathBuf; diff --git a/cli/cache/cache_db.rs b/cli/cache/cache_db.rs index c25c1955b2..02394d4cfd 100644 --- a/cli/cache/cache_db.rs +++ b/cli/cache/cache_db.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::IsTerminal; use std::path::Path; diff --git a/cli/cache/caches.rs b/cli/cache/caches.rs index 54371cee48..b83364c61b 100644 --- a/cli/cache/caches.rs +++ b/cli/cache/caches.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::PathBuf; use std::sync::Arc; diff --git a/cli/cache/check.rs b/cli/cache/check.rs index 192d338a09..a886f9fe0f 100644 --- a/cli/cache/check.rs +++ b/cli/cache/check.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; diff --git a/cli/cache/code_cache.rs b/cli/cache/code_cache.rs index b6c9060ea0..27ec544b5f 100644 --- a/cli/cache/code_cache.rs +++ b/cli/cache/code_cache.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/cache/common.rs b/cli/cache/common.rs index 0a68e95159..da607a27f2 100644 --- a/cli/cache/common.rs +++ b/cli/cache/common.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::hash::Hasher; diff --git a/cli/cache/deno_dir.rs b/cli/cache/deno_dir.rs index 2c0aa30bc2..1b35f53071 100644 --- a/cli/cache/deno_dir.rs +++ b/cli/cache/deno_dir.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::env; use std::path::PathBuf; diff --git a/cli/cache/disk_cache.rs b/cli/cache/disk_cache.rs index b23d7b8f78..f03b60854f 100644 --- a/cli/cache/disk_cache.rs +++ b/cli/cache/disk_cache.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ffi::OsStr; use std::fs; diff --git a/cli/cache/emit.rs b/cli/cache/emit.rs index 100cbfe744..2ba43d58b9 100644 --- a/cli/cache/emit.rs +++ b/cli/cache/emit.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::PathBuf; diff --git a/cli/cache/fast_check.rs b/cli/cache/fast_check.rs index 43be1b7186..323312d057 100644 --- a/cli/cache/fast_check.rs +++ b/cli/cache/fast_check.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::AnyError; use deno_graph::FastCheckCacheItem; diff --git a/cli/cache/incremental.rs b/cli/cache/incremental.rs index 2d31b4125e..9ba343f273 100644 --- a/cli/cache/incremental.rs +++ b/cli/cache/incremental.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::path::Path; diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index ef93939c10..868811c587 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::path::PathBuf; diff --git a/cli/cache/module_info.rs b/cli/cache/module_info.rs index 469e2fafac..671e7e3dc8 100644 --- a/cli/cache/module_info.rs +++ b/cli/cache/module_info.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/cache/node.rs b/cli/cache/node.rs index 92f5a19d71..89e372de43 100644 --- a/cli/cache/node.rs +++ b/cli/cache/node.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::AnyError; use deno_core::serde_json; diff --git a/cli/cache/parsed_source.rs b/cli/cache/parsed_source.rs index 4d031f8bf2..15207f4ba7 100644 --- a/cli/cache/parsed_source.rs +++ b/cli/cache/parsed_source.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::sync::Arc; diff --git a/cli/cdp.rs b/cli/cdp.rs index c5ff587dde..df82d58d9f 100644 --- a/cli/cdp.rs +++ b/cli/cdp.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// use deno_core::serde_json::Value; diff --git a/cli/emit.rs b/cli/emit.rs index 04547adf93..32a636de36 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/errors.rs b/cli/errors.rs index 38dc8259e3..ead1ccf127 100644 --- a/cli/errors.rs +++ b/cli/errors.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! There are many types of errors in Deno: //! - AnyError: a generic wrapper that can encapsulate any type of error. diff --git a/cli/factory.rs b/cli/factory.rs index a57f7822a8..fc6bca33fd 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::future::Future; use std::path::PathBuf; diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 3d73a7e4ea..cfc26d7e69 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; diff --git a/cli/graph_container.rs b/cli/graph_container.rs index c463d71a6a..1fe30b47ab 100644 --- a/cli/graph_container.rs +++ b/cli/graph_container.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 68d48d9bbc..fb933bdac8 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::error::Error; diff --git a/cli/http_util.rs b/cli/http_util.rs index 618ba35346..af6709c5d0 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::sync::Arc; diff --git a/cli/integration_tests_runner.rs b/cli/integration_tests_runner.rs index 12e83a0194..7342e62fa0 100644 --- a/cli/integration_tests_runner.rs +++ b/cli/integration_tests_runner.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub fn main() { let mut args = vec!["cargo", "test", "-p", "cli_tests", "--features", "run"]; diff --git a/cli/js.rs b/cli/js.rs index 2c93f004ca..5337c53f76 100644 --- a/cli/js.rs +++ b/cli/js.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use log::debug; diff --git a/cli/js/40_bench.js b/cli/js/40_bench.js index b07df3993c..0ad05c5197 100644 --- a/cli/js/40_bench.js +++ b/cli/js/40_bench.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file import { core, primordials } from "ext:core/mod.js"; diff --git a/cli/js/40_jupyter.js b/cli/js/40_jupyter.js index 198b6a3502..f392af1d43 100644 --- a/cli/js/40_jupyter.js +++ b/cli/js/40_jupyter.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file /* diff --git a/cli/js/40_lint.js b/cli/js/40_lint.js index d29dc3e850..4adbc1baa1 100644 --- a/cli/js/40_lint.js +++ b/cli/js/40_lint.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check diff --git a/cli/js/40_lint_selector.js b/cli/js/40_lint_selector.js index b78f7a5d0e..7b94c4f960 100644 --- a/cli/js/40_lint_selector.js +++ b/cli/js/40_lint_selector.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check diff --git a/cli/js/40_lint_types.d.ts b/cli/js/40_lint_types.d.ts index 7b06e36098..db2202981a 100644 --- a/cli/js/40_lint_types.d.ts +++ b/cli/js/40_lint_types.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export interface NodeFacade { type: string; diff --git a/cli/js/40_test.js b/cli/js/40_test.js index ea526a17d4..34d9ec6550 100644 --- a/cli/js/40_test.js +++ b/cli/js/40_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { escapeName, withPermissions } from "ext:cli/40_test_common.js"; diff --git a/cli/js/40_test_common.js b/cli/js/40_test_common.js index 7711148f1e..6b7828cd2d 100644 --- a/cli/js/40_test_common.js +++ b/cli/js/40_test_common.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { serializePermissions } from "ext:runtime/10_permissions.js"; const ops = core.ops; diff --git a/cli/jsr.rs b/cli/jsr.rs index ef57e8fd64..34a2ec04e4 100644 --- a/cli/jsr.rs +++ b/cli/jsr.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 116780eb73..968875e48a 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cmp::Ordering; diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs index c69f66b77f..97fbbaff14 100644 --- a/cli/lsp/cache.rs +++ b/cli/lsp/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::BTreeMap; use std::fs; diff --git a/cli/lsp/capabilities.rs b/cli/lsp/capabilities.rs index 5cdb1224d8..4c81edeea2 100644 --- a/cli/lsp/capabilities.rs +++ b/cli/lsp/capabilities.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! //! Provides information about what capabilities that are supported by the diff --git a/cli/lsp/client.rs b/cli/lsp/client.rs index 2bd1ddc2ff..85ea1bb4e5 100644 --- a/cli/lsp/client.rs +++ b/cli/lsp/client.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index c5ef5f7223..bb72d0db77 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashSet; diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs index 412ca6fd32..7309d984b4 100644 --- a/cli/lsp/completions.rs +++ b/cli/lsp/completions.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_ast::LineAndColumnIndex; use deno_ast::SourceTextInfo; diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 8e61b523f7..afc976b8fe 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::BTreeMap; use std::collections::BTreeSet; diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 625c777a84..cc9cca7f23 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 34c2deee46..a8398b4f2f 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::BTreeMap; diff --git a/cli/lsp/jsr.rs b/cli/lsp/jsr.rs index 48bac7ac47..1bcd6f3930 100644 --- a/cli/lsp/jsr.rs +++ b/cli/lsp/jsr.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::sync::Arc; diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 98b2fb1462..958c3a4606 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::BTreeMap; use std::collections::BTreeSet; diff --git a/cli/lsp/logging.rs b/cli/lsp/logging.rs index cb803b9478..efb49b2d48 100644 --- a/cli/lsp/logging.rs +++ b/cli/lsp/logging.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fs; use std::io::prelude::*; diff --git a/cli/lsp/lsp_custom.rs b/cli/lsp/lsp_custom.rs index 8df4ba1d07..050fcf3184 100644 --- a/cli/lsp/lsp_custom.rs +++ b/cli/lsp/lsp_custom.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde::Deserialize; use deno_core::serde::Serialize; diff --git a/cli/lsp/mod.rs b/cli/lsp/mod.rs index 0b00f8ef68..6b5d17798b 100644 --- a/cli/lsp/mod.rs +++ b/cli/lsp/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::AnyError; use deno_core::unsync::spawn; diff --git a/cli/lsp/npm.rs b/cli/lsp/npm.rs index 8b96eed66f..d53c8cb2ab 100644 --- a/cli/lsp/npm.rs +++ b/cli/lsp/npm.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/lsp/parent_process_checker.rs b/cli/lsp/parent_process_checker.rs index 287880ae32..b8ab60922b 100644 --- a/cli/lsp/parent_process_checker.rs +++ b/cli/lsp/parent_process_checker.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::time::Duration; diff --git a/cli/lsp/path_to_regex.rs b/cli/lsp/path_to_regex.rs index 10b0cfbdc8..65322da6d0 100644 --- a/cli/lsp/path_to_regex.rs +++ b/cli/lsp/path_to_regex.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // The logic of this module is heavily influenced by path-to-regexp at: // https://github.com/pillarjs/path-to-regexp/ which is licensed as follows: diff --git a/cli/lsp/performance.rs b/cli/lsp/performance.rs index 360bdf103e..8d836c7adf 100644 --- a/cli/lsp/performance.rs +++ b/cli/lsp/performance.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cmp; use std::collections::HashMap; diff --git a/cli/lsp/refactor.rs b/cli/lsp/refactor.rs index 82751d3b12..e1abd963a2 100644 --- a/cli/lsp/refactor.rs +++ b/cli/lsp/refactor.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // The logic of this module is heavily influenced by // https://github.com/microsoft/vscode/blob/main/extensions/typescript-language-features/src/languageFeatures/refactor.ts diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index be36eeb73d..f8c38d97ea 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; diff --git a/cli/lsp/repl.rs b/cli/lsp/repl.rs index b4aaa8cd0d..a30427312d 100644 --- a/cli/lsp/repl.rs +++ b/cli/lsp/repl.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 7fb4e85529..51820432f7 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::BTreeMap; diff --git a/cli/lsp/search.rs b/cli/lsp/search.rs index 7d2760c1de..281542742d 100644 --- a/cli/lsp/search.rs +++ b/cli/lsp/search.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/lsp/semantic_tokens.rs b/cli/lsp/semantic_tokens.rs index cc880de8b6..d2466f4c14 100644 --- a/cli/lsp/semantic_tokens.rs +++ b/cli/lsp/semantic_tokens.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // The logic of this module is heavily influenced by // https://github.com/microsoft/vscode/blob/main/extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts diff --git a/cli/lsp/testing/collectors.rs b/cli/lsp/testing/collectors.rs index 141d34d79d..8a36ca1f25 100644 --- a/cli/lsp/testing/collectors.rs +++ b/cli/lsp/testing/collectors.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/lsp/testing/definitions.rs b/cli/lsp/testing/definitions.rs index 44ea5ce6fe..8277dcbf00 100644 --- a/cli/lsp/testing/definitions.rs +++ b/cli/lsp/testing/definitions.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/lsp/testing/execution.rs b/cli/lsp/testing/execution.rs index 3378891316..3d6d6f6b73 100644 --- a/cli/lsp/testing/execution.rs +++ b/cli/lsp/testing/execution.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; diff --git a/cli/lsp/testing/lsp_custom.rs b/cli/lsp/testing/lsp_custom.rs index 84ac30de57..5ee6e84f52 100644 --- a/cli/lsp/testing/lsp_custom.rs +++ b/cli/lsp/testing/lsp_custom.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde::Deserialize; use deno_core::serde::Serialize; diff --git a/cli/lsp/testing/mod.rs b/cli/lsp/testing/mod.rs index d285481f06..1398ba1eca 100644 --- a/cli/lsp/testing/mod.rs +++ b/cli/lsp/testing/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod collectors; mod definitions; diff --git a/cli/lsp/testing/server.rs b/cli/lsp/testing/server.rs index a4b286671e..e0570fcada 100644 --- a/cli/lsp/testing/server.rs +++ b/cli/lsp/testing/server.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/lsp/text.rs b/cli/lsp/text.rs index 3c9721bdc9..a9a5f0753a 100644 --- a/cli/lsp/text.rs +++ b/cli/lsp/text.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index cdd46c2ded..d2b501a539 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::cmp; diff --git a/cli/lsp/urls.rs b/cli/lsp/urls.rs index 84dd3581d7..91c04f11c0 100644 --- a/cli/lsp/urls.rs +++ b/cli/lsp/urls.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::str::FromStr; diff --git a/cli/main.rs b/cli/main.rs index f3f3254b14..2e55d9d286 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod args; mod cache; diff --git a/cli/mainrt.rs b/cli/mainrt.rs index 19e010fb53..ce6fddaee9 100644 --- a/cli/mainrt.rs +++ b/cli/mainrt.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Allow unused code warnings because we share // code between the two bin targets. diff --git a/cli/module_loader.rs b/cli/module_loader.rs index c2720ac5ad..174e06e266 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/cli/node.rs b/cli/node.rs index 4a87d26ee0..e0feb557a7 100644 --- a/cli/node.rs +++ b/cli/node.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::sync::Arc; diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index b8feb52f39..7a0a450c11 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 0422445e62..4a4d593bbf 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; diff --git a/cli/npm/managed/resolution.rs b/cli/npm/managed/resolution.rs index 5d9fcf4646..12f7b17565 100644 --- a/cli/npm/managed/resolution.rs +++ b/cli/npm/managed/resolution.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs index 26f6d8516d..6a859ea9fd 100644 --- a/cli/npm/managed/resolvers/common.rs +++ b/cli/npm/managed/resolvers/common.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod bin_entries; pub mod lifecycle_scripts; diff --git a/cli/npm/managed/resolvers/common/bin_entries.rs b/cli/npm/managed/resolvers/common/bin_entries.rs index 5f203a2ba0..32ebd68746 100644 --- a/cli/npm/managed/resolvers/common/bin_entries.rs +++ b/cli/npm/managed/resolvers/common/bin_entries.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/npm/managed/resolvers/common/lifecycle_scripts.rs b/cli/npm/managed/resolvers/common/lifecycle_scripts.rs index c6527b1458..738326ad22 100644 --- a/cli/npm/managed/resolvers/common/lifecycle_scripts.rs +++ b/cli/npm/managed/resolvers/common/lifecycle_scripts.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashSet; diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs index 44157464b8..18b7911c2f 100644 --- a/cli/npm/managed/resolvers/global.rs +++ b/cli/npm/managed/resolvers/global.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! Code for global npm cache resolution. diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index bff881d5f9..1e4e33ff3f 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! Code for local node_modules resolution. diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs index 3d3fc2e3f2..b52a7e0e39 100644 --- a/cli/npm/managed/resolvers/mod.rs +++ b/cli/npm/managed/resolvers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod common; mod global; diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index c8a6002c4f..e0c10fa7a7 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod byonm; mod managed; diff --git a/cli/ops/bench.rs b/cli/ops/bench.rs index a7c788a189..c6eca9216f 100644 --- a/cli/ops/bench.rs +++ b/cli/ops/bench.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; diff --git a/cli/ops/jupyter.rs b/cli/ops/jupyter.rs index da6fa657d2..6a9252c35a 100644 --- a/cli/ops/jupyter.rs +++ b/cli/ops/jupyter.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // NOTE(bartlomieju): unfortunately it appears that clippy is broken // and can't allow a single line ignore for `await_holding_lock`. diff --git a/cli/ops/lint.rs b/cli/ops/lint.rs index c38ac0c8a2..0a444e942c 100644 --- a/cli/ops/lint.rs +++ b/cli/ops/lint.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_ast::MediaType; use deno_ast::ModuleSpecifier; diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index 4ac1618816..7cee5bcfa1 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod bench; pub mod jupyter; diff --git a/cli/ops/testing.rs b/cli/ops/testing.rs index bc96e41650..84e9aff83b 100644 --- a/cli/ops/testing.rs +++ b/cli/ops/testing.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; diff --git a/cli/resolver.rs b/cli/resolver.rs index 93c07bdbc3..661685c081 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; diff --git a/cli/shared.rs b/cli/shared.rs index 808aff707b..6a28473edd 100644 --- a/cli/shared.rs +++ b/cli/shared.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// This module is shared between build script and the binaries. Use it sparsely. use deno_core::anyhow::bail; diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 5277b3d523..7ca25fca51 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::BTreeMap; diff --git a/cli/standalone/code_cache.rs b/cli/standalone/code_cache.rs index 0abec9ba9e..de9ff2a141 100644 --- a/cli/standalone/code_cache.rs +++ b/cli/standalone/code_cache.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/cli/standalone/file_system.rs b/cli/standalone/file_system.rs index 0a11d4550f..f218277bef 100644 --- a/cli/standalone/file_system.rs +++ b/cli/standalone/file_system.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::io::ErrorKind; diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 7ce06b309c..4768c742f1 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Allow unused code warnings because we share // code between the two bin targets. diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index 0f4d2dd9f4..238ef44fd0 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::Cell; diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index 61e2e33347..fab4fad83c 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/cli/sys.rs b/cli/sys.rs index 55b50a199d..f23bbc9dc8 100644 --- a/cli/sys.rs +++ b/cli/sys.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // todo(dsherret): this should instead use conditional compilation and directly // surface the underlying implementation. diff --git a/cli/task_runner.rs b/cli/task_runner.rs index c7232387ca..8510a650e7 100644 --- a/cli/task_runner.rs +++ b/cli/task_runner.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::path::Path; diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index e1441e95cd..42895089ef 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::path::Path; diff --git a/cli/tools/bench/reporters.rs b/cli/tools/bench/reporters.rs index 0c13d14961..68a0c56bce 100644 --- a/cli/tools/bench/reporters.rs +++ b/cli/tools/bench/reporters.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use serde::Serialize; diff --git a/cli/tools/check.rs b/cli/tools/check.rs index acfff70401..f3df54626a 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::collections::VecDeque; diff --git a/cli/tools/clean.rs b/cli/tools/clean.rs index 763aa11540..e6f8c1e52b 100644 --- a/cli/tools/clean.rs +++ b/cli/tools/clean.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index 243e322f55..b148c9a0b1 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::collections::VecDeque; diff --git a/cli/tools/coverage/merge.rs b/cli/tools/coverage/merge.rs index 2c69981ad1..9c898e78d3 100644 --- a/cli/tools/coverage/merge.rs +++ b/cli/tools/coverage/merge.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Forked from https://github.com/demurgos/v8-coverage/tree/d0ca18da8740198681e0bc68971b0a6cdb11db3e/rust // Copyright 2021 Charles Samborski. All rights reserved. MIT license. diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 1a03f38c53..522d7d75be 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fs; use std::fs::File; diff --git a/cli/tools/coverage/range_tree.rs b/cli/tools/coverage/range_tree.rs index 8e3cd95a51..08ac914cd2 100644 --- a/cli/tools/coverage/range_tree.rs +++ b/cli/tools/coverage/range_tree.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Forked from https://github.com/demurgos/v8-coverage/tree/d0ca18da8740198681e0bc68971b0a6cdb11db3e/rust // Copyright 2021 Charles Samborski. All rights reserved. MIT license. diff --git a/cli/tools/coverage/reporter.rs b/cli/tools/coverage/reporter.rs index 3bdb6b25f6..bc6e85b47e 100644 --- a/cli/tools/coverage/reporter.rs +++ b/cli/tools/coverage/reporter.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::fs; diff --git a/cli/tools/coverage/util.rs b/cli/tools/coverage/util.rs index e9518e1f78..e61830b7fe 100644 --- a/cli/tools/coverage/util.rs +++ b/cli/tools/coverage/util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::url::Url; diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index a22de0c9a9..2fa944b362 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::BTreeMap; use std::rc::Rc; diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 74a85aaa09..29db06e97f 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This module provides file formatting utilities using //! [`dprint-plugin-typescript`](https://github.com/dprint/dprint-plugin-typescript). diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 39a7a912bf..74d6591124 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/tools/init/mod.rs b/cli/tools/init/mod.rs index ec25a051bb..d077de44ce 100644 --- a/cli/tools/init/mod.rs +++ b/cli/tools/init/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::IsTerminal; use std::io::Write; diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 12d650d98e..6dc065171a 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::env; use std::fs; diff --git a/cli/tools/jupyter/install.rs b/cli/tools/jupyter/install.rs index 40159a35e0..d76c59343b 100644 --- a/cli/tools/jupyter/install.rs +++ b/cli/tools/jupyter/install.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::env::current_exe; use std::io::Write; diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs index 7f11be36d4..bb39528f3e 100644 --- a/cli/tools/jupyter/mod.rs +++ b/cli/tools/jupyter/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/tools/jupyter/server.rs b/cli/tools/jupyter/server.rs index 6c6d076d01..bc045d9d9b 100644 --- a/cli/tools/jupyter/server.rs +++ b/cli/tools/jupyter/server.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file is forked/ported from // Copyright 2020 The Evcxr Authors. MIT license. diff --git a/cli/tools/lint/ast_buffer/buffer.rs b/cli/tools/lint/ast_buffer/buffer.rs index b6387a0ef9..517c3b14dc 100644 --- a/cli/tools/lint/ast_buffer/buffer.rs +++ b/cli/tools/lint/ast_buffer/buffer.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Display; diff --git a/cli/tools/lint/ast_buffer/mod.rs b/cli/tools/lint/ast_buffer/mod.rs index 8838bcc5f2..fc4045fb60 100644 --- a/cli/tools/lint/ast_buffer/mod.rs +++ b/cli/tools/lint/ast_buffer/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_ast::ParsedSource; use swc::serialize_swc_to_buffer; diff --git a/cli/tools/lint/ast_buffer/swc.rs b/cli/tools/lint/ast_buffer/swc.rs index b26c213105..7652756c9b 100644 --- a/cli/tools/lint/ast_buffer/swc.rs +++ b/cli/tools/lint/ast_buffer/swc.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_ast::swc::ast::AssignTarget; use deno_ast::swc::ast::AssignTargetPat; diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs index 29bdb0d378..967bbef32a 100644 --- a/cli/tools/lint/ast_buffer/ts_estree.rs +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt; use std::fmt::Debug; diff --git a/cli/tools/lint/linter.rs b/cli/tools/lint/linter.rs index 0537765a0a..5d6f845274 100644 --- a/cli/tools/lint/linter.rs +++ b/cli/tools/lint/linter.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::path::Path; diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index cefc1d6e66..8b8702c0f8 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This module provides file linting utilities using //! [`deno_lint`](https://github.com/denoland/deno_lint). diff --git a/cli/tools/lint/reporters.rs b/cli/tools/lint/reporters.rs index 0e93f4570f..24e04e840f 100644 --- a/cli/tools/lint/reporters.rs +++ b/cli/tools/lint/reporters.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_ast::diagnostics::Diagnostic; use deno_core::error::AnyError; diff --git a/cli/tools/lint/rules/mod.rs b/cli/tools/lint/rules/mod.rs index dd723ad159..e7be12d56d 100644 --- a/cli/tools/lint/rules/mod.rs +++ b/cli/tools/lint/rules/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashSet; diff --git a/cli/tools/lint/rules/no_sloppy_imports.rs b/cli/tools/lint/rules/no_sloppy_imports.rs index fc4641df12..33d3090fe3 100644 --- a/cli/tools/lint/rules/no_sloppy_imports.rs +++ b/cli/tools/lint/rules/no_sloppy_imports.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/cli/tools/lint/rules/no_slow_types.rs b/cli/tools/lint/rules/no_slow_types.rs index bc3f835b17..a792c38612 100644 --- a/cli/tools/lint/rules/no_slow_types.rs +++ b/cli/tools/lint/rules/no_slow_types.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; diff --git a/cli/tools/mod.rs b/cli/tools/mod.rs index a458da9f1b..35de8ab9fa 100644 --- a/cli/tools/mod.rs +++ b/cli/tools/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod bench; pub mod check; diff --git a/cli/tools/registry/api.rs b/cli/tools/registry/api.rs index 623c9bee3c..c2d34442a1 100644 --- a/cli/tools/registry/api.rs +++ b/cli/tools/registry/api.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::AnyError; use deno_core::serde_json; diff --git a/cli/tools/registry/auth.rs b/cli/tools/registry/auth.rs index 820d3b6b60..3665990905 100644 --- a/cli/tools/registry/auth.rs +++ b/cli/tools/registry/auth.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::IsTerminal; diff --git a/cli/tools/registry/diagnostics.rs b/cli/tools/registry/diagnostics.rs index 9c32b8e36d..27753167f3 100644 --- a/cli/tools/registry/diagnostics.rs +++ b/cli/tools/registry/diagnostics.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::PathBuf; diff --git a/cli/tools/registry/graph.rs b/cli/tools/registry/graph.rs index e9cf01d710..7152675ff8 100644 --- a/cli/tools/registry/graph.rs +++ b/cli/tools/registry/graph.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::sync::Arc; diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index e3edc51d39..ea457f8a71 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/tools/registry/paths.rs b/cli/tools/registry/paths.rs index 02b143b620..ef98838913 100644 --- a/cli/tools/registry/paths.rs +++ b/cli/tools/registry/paths.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Validation logic in this file is shared with registry/api/src/ids.rs diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index ab4d92762f..2b1266bafb 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; use std::path::PathBuf; diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index d41c5c6b8d..3137939cea 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::sync::Arc; diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index 6f92a9a47f..708f95f987 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index 2885a58e1b..e7fc88c31f 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::sync::Arc; diff --git a/cli/tools/registry/provenance.rs b/cli/tools/registry/provenance.rs index 72e1dff80e..bd5249b5cd 100644 --- a/cli/tools/registry/provenance.rs +++ b/cli/tools/registry/provenance.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::env; diff --git a/cli/tools/registry/publish_order.rs b/cli/tools/registry/publish_order.rs index ad77a56bb1..577627f348 100644 --- a/cli/tools/registry/publish_order.rs +++ b/cli/tools/registry/publish_order.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs index 34d0ed0832..2d6f53b5af 100644 --- a/cli/tools/registry/tar.rs +++ b/cli/tools/registry/tar.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Write as FmtWrite; use std::io::Write; diff --git a/cli/tools/registry/unfurl.rs b/cli/tools/registry/unfurl.rs index e02dcd62d9..e3501c348b 100644 --- a/cli/tools/registry/unfurl.rs +++ b/cli/tools/registry/unfurl.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::sync::Arc; diff --git a/cli/tools/repl/channel.rs b/cli/tools/repl/channel.rs index a32b48f614..4ac28b9d52 100644 --- a/cli/tools/repl/channel.rs +++ b/cli/tools/repl/channel.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; diff --git a/cli/tools/repl/editor.rs b/cli/tools/repl/editor.rs index 95df2c02a0..27d726255c 100644 --- a/cli/tools/repl/editor.rs +++ b/cli/tools/repl/editor.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::PathBuf; diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index eacafa2160..1bc6a2f9ea 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io; use std::io::Write; diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs index b09fcafc91..7b20717649 100644 --- a/cli/tools/repl/session.rs +++ b/cli/tools/repl/session.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/tools/run/hmr.rs b/cli/tools/run/hmr.rs index 373c207d69..9f19aea015 100644 --- a/cli/tools/run/hmr.rs +++ b/cli/tools/run/hmr.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::path::PathBuf; diff --git a/cli/tools/run/mod.rs b/cli/tools/run/mod.rs index cd7d1dd6c4..a10ce32947 100644 --- a/cli/tools/run/mod.rs +++ b/cli/tools/run/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::Read; use std::sync::Arc; diff --git a/cli/tools/serve.rs b/cli/tools/serve.rs index d7989140ae..18eac1b3e1 100644 --- a/cli/tools/serve.rs +++ b/cli/tools/serve.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/cli/tools/task.rs b/cli/tools/task.rs index 4d83cc98fd..85834a0bfc 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/cli/tools/test/channel.rs b/cli/tools/test/channel.rs index d3c1ff9564..29f24d65f1 100644 --- a/cli/tools/test/channel.rs +++ b/cli/tools/test/channel.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Display; use std::future::Future; diff --git a/cli/tools/test/fmt.rs b/cli/tools/test/fmt.rs index a1a5174845..e5b40b874b 100644 --- a/cli/tools/test/fmt.rs +++ b/cli/tools/test/fmt.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::ops::AddAssign; diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 09bdf6e3aa..2bd9cd3d7c 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/cli/tools/test/reporters/common.rs b/cli/tools/test/reporters/common.rs index 7ca83db809..2db6a36464 100644 --- a/cli/tools/test/reporters/common.rs +++ b/cli/tools/test/reporters/common.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::fmt::format_test_error; use super::fmt::to_relative_path_or_remote_url; diff --git a/cli/tools/test/reporters/compound.rs b/cli/tools/test/reporters/compound.rs index 3ab7297db5..e381dd0977 100644 --- a/cli/tools/test/reporters/compound.rs +++ b/cli/tools/test/reporters/compound.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::*; diff --git a/cli/tools/test/reporters/dot.rs b/cli/tools/test/reporters/dot.rs index 169c4b0e72..0912858431 100644 --- a/cli/tools/test/reporters/dot.rs +++ b/cli/tools/test/reporters/dot.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::common; use super::fmt::to_relative_path_or_remote_url; diff --git a/cli/tools/test/reporters/junit.rs b/cli/tools/test/reporters/junit.rs index 3998bee40d..42ced07600 100644 --- a/cli/tools/test/reporters/junit.rs +++ b/cli/tools/test/reporters/junit.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::VecDeque; use std::path::PathBuf; diff --git a/cli/tools/test/reporters/mod.rs b/cli/tools/test/reporters/mod.rs index 07351e9c3b..ef2b00b776 100644 --- a/cli/tools/test/reporters/mod.rs +++ b/cli/tools/test/reporters/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::*; diff --git a/cli/tools/test/reporters/pretty.rs b/cli/tools/test/reporters/pretty.rs index 4120bbfb54..3419fdea59 100644 --- a/cli/tools/test/reporters/pretty.rs +++ b/cli/tools/test/reporters/pretty.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use super::common; use super::fmt::to_relative_path_or_remote_url; diff --git a/cli/tools/test/reporters/tap.rs b/cli/tools/test/reporters/tap.rs index ea68ddd43a..c36df25756 100644 --- a/cli/tools/test/reporters/tap.rs +++ b/cli/tools/test/reporters/tap.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json::json; use deno_core::serde_json::{self}; diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs index 3d9e92df87..521c3217fe 100644 --- a/cli/tools/upgrade.rs +++ b/cli/tools/upgrade.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This module provides feature to upgrade deno executable diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index f7862c95e4..d532de265f 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/cli/tsc/_analyze_types_node.ts b/cli/tsc/_analyze_types_node.ts index 10af00ee83..1a77b6c547 100755 --- a/cli/tsc/_analyze_types_node.ts +++ b/cli/tsc/_analyze_types_node.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-env --allow-read --allow-write=. -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { ModuleDeclarationKind, Node, diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts index 428e4d1ed8..0389527f5b 100644 --- a/cli/tsc/compiler.d.ts +++ b/cli/tsc/compiler.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Contains types that can be used to validate and check `99_main_compiler.js` diff --git a/cli/tsc/diagnostics.rs b/cli/tsc/diagnostics.rs index fd0a027ae3..3f866e0c96 100644 --- a/cli/tsc/diagnostics.rs +++ b/cli/tsc/diagnostics.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::error::Error; use std::fmt; diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index d9f66f11a7..e98f68ea38 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/cli/tsc/dts/lib.deno.shared_globals.d.ts b/cli/tsc/dts/lib.deno.shared_globals.d.ts index 96790fb665..a469525270 100644 --- a/cli/tsc/dts/lib.deno.shared_globals.d.ts +++ b/cli/tsc/dts/lib.deno.shared_globals.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Documentation partially adapted from [MDN](https://developer.mozilla.org/), // by Mozilla Contributors, which is licensed under CC-BY-SA 2.5. diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index 6759856e6a..d207a92041 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/cli/tsc/dts/lib.deno.window.d.ts b/cli/tsc/dts/lib.deno.window.d.ts index 636e2b0fd0..8a516beaf6 100644 --- a/cli/tsc/dts/lib.deno.window.d.ts +++ b/cli/tsc/dts/lib.deno.window.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/cli/tsc/dts/lib.deno.worker.d.ts b/cli/tsc/dts/lib.deno.worker.d.ts index fa69cc57d6..c309417031 100644 --- a/cli/tsc/dts/lib.deno.worker.d.ts +++ b/cli/tsc/dts/lib.deno.worker.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/cli/tsc/dts/lib.deno_webgpu.d.ts b/cli/tsc/dts/lib.deno_webgpu.d.ts index 2deb63abc7..6dbfc57768 100644 --- a/cli/tsc/dts/lib.deno_webgpu.d.ts +++ b/cli/tsc/dts/lib.deno_webgpu.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-empty-interface diff --git a/cli/tsc/dts/lib.dom.extras.d.ts b/cli/tsc/dts/lib.dom.extras.d.ts index a6de789f56..3a1667a99c 100644 --- a/cli/tsc/dts/lib.dom.extras.d.ts +++ b/cli/tsc/dts/lib.dom.extras.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /* * This library contains DOM standards that are not currently included in the diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 734b5c7289..9f1dc3653a 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; diff --git a/cli/util/archive.rs b/cli/util/archive.rs index e2183d57ab..e3efd69adb 100644 --- a/cli/util/archive.rs +++ b/cli/util/archive.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fs; use std::path::Path; diff --git a/cli/util/checksum.rs b/cli/util/checksum.rs index c9c55ec2b4..b15380abd7 100644 --- a/cli/util/checksum.rs +++ b/cli/util/checksum.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use ring::digest::Context; use ring::digest::SHA256; diff --git a/cli/util/console.rs b/cli/util/console.rs index 74e6928a28..d22d41f5ea 100644 --- a/cli/util/console.rs +++ b/cli/util/console.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_runtime::ops::tty::ConsoleSize; diff --git a/cli/util/diff.rs b/cli/util/diff.rs index 0852abdbaf..a42da3a89a 100644 --- a/cli/util/diff.rs +++ b/cli/util/diff.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Write as _; diff --git a/cli/util/display.rs b/cli/util/display.rs index 244bd0740d..dff08f31fa 100644 --- a/cli/util/display.rs +++ b/cli/util/display.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::Write; diff --git a/cli/util/draw_thread.rs b/cli/util/draw_thread.rs index 80aa3959c2..a2d7681425 100644 --- a/cli/util/draw_thread.rs +++ b/cli/util/draw_thread.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::IsTerminal; use std::sync::Arc; diff --git a/cli/util/extract.rs b/cli/util/extract.rs index ff778248ea..825e81bb4b 100644 --- a/cli/util/extract.rs +++ b/cli/util/extract.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::BTreeSet; use std::fmt::Write as _; diff --git a/cli/util/file_watcher.rs b/cli/util/file_watcher.rs index 6596afa42c..ca769b70fe 100644 --- a/cli/util/file_watcher.rs +++ b/cli/util/file_watcher.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashSet; diff --git a/cli/util/fs.rs b/cli/util/fs.rs index 6c25879d6e..396e5dc146 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::Error; use std::io::ErrorKind; diff --git a/cli/util/logger.rs b/cli/util/logger.rs index a6f025790b..2bd4760ebd 100644 --- a/cli/util/logger.rs +++ b/cli/util/logger.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::Write; diff --git a/cli/util/mod.rs b/cli/util/mod.rs index f81a74c449..0578ecb423 100644 --- a/cli/util/mod.rs +++ b/cli/util/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Note: Only add code in this folder that has no application specific logic pub mod archive; diff --git a/cli/util/path.rs b/cli/util/path.rs index 539e1235a8..90b2df6a3c 100644 --- a/cli/util/path.rs +++ b/cli/util/path.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; diff --git a/cli/util/progress_bar/mod.rs b/cli/util/progress_bar/mod.rs index eb88492657..3b5bb20e55 100644 --- a/cli/util/progress_bar/mod.rs +++ b/cli/util/progress_bar/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::atomic::AtomicU64; use std::sync::atomic::Ordering; diff --git a/cli/util/progress_bar/renderer.rs b/cli/util/progress_bar/renderer.rs index aca745f817..3498c7ae0f 100644 --- a/cli/util/progress_bar/renderer.rs +++ b/cli/util/progress_bar/renderer.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Write; use std::sync::atomic::AtomicUsize; diff --git a/cli/util/result.rs b/cli/util/result.rs index 3203d04eb7..6a67416f26 100644 --- a/cli/util/result.rs +++ b/cli/util/result.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::convert::Infallible; diff --git a/cli/util/retry.rs b/cli/util/retry.rs index a8febe60de..ac36a380ed 100644 --- a/cli/util/retry.rs +++ b/cli/util/retry.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::future::Future; use std::time::Duration; diff --git a/cli/util/sync/async_flag.rs b/cli/util/sync/async_flag.rs index 2bdff63c04..a9ca46002d 100644 --- a/cli/util/sync/async_flag.rs +++ b/cli/util/sync/async_flag.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use tokio_util::sync::CancellationToken; diff --git a/cli/util/sync/mod.rs b/cli/util/sync/mod.rs index c3b2a315b0..74ec469533 100644 --- a/cli/util/sync/mod.rs +++ b/cli/util/sync/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod async_flag; mod sync_read_async_write_lock; diff --git a/cli/util/sync/sync_read_async_write_lock.rs b/cli/util/sync/sync_read_async_write_lock.rs index 8bd211aa72..48ad6bb863 100644 --- a/cli/util/sync/sync_read_async_write_lock.rs +++ b/cli/util/sync/sync_read_async_write_lock.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::parking_lot::RwLock; use deno_core::parking_lot::RwLockReadGuard; diff --git a/cli/util/sync/task_queue.rs b/cli/util/sync/task_queue.rs index b06fc7b4e4..4c5abfab3b 100644 --- a/cli/util/sync/task_queue.rs +++ b/cli/util/sync/task_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::LinkedList; use std::sync::Arc; diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs index 107b78a213..b5a288be7b 100644 --- a/cli/util/text_encoding.rs +++ b/cli/util/text_encoding.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::ops::Range; diff --git a/cli/util/unix.rs b/cli/util/unix.rs index 2c76a54c38..4b6ac1f320 100644 --- a/cli/util/unix.rs +++ b/cli/util/unix.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// Raise soft file descriptor limit to hard file descriptor limit. /// This is the difference between `ulimit -n` and `ulimit -n -H`. diff --git a/cli/util/v8.rs b/cli/util/v8.rs index 6e690e6f30..403d1955a5 100644 --- a/cli/util/v8.rs +++ b/cli/util/v8.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod convert; diff --git a/cli/util/v8/convert.rs b/cli/util/v8/convert.rs index 28107d9010..4d0cb732a8 100644 --- a/cli/util/v8/convert.rs +++ b/cli/util/v8/convert.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::v8; use deno_core::FromV8; diff --git a/cli/util/windows.rs b/cli/util/windows.rs index c23bec868f..7f516efbeb 100644 --- a/cli/util/windows.rs +++ b/cli/util/windows.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// Ensures that stdin, stdout, and stderr are open and have valid HANDLEs /// associated with them. There are many places where a `std::fs::File` is diff --git a/cli/version.rs b/cli/version.rs index 0cdb32102a..fb0fe98b29 100644 --- a/cli/version.rs +++ b/cli/version.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use once_cell::sync::Lazy; diff --git a/cli/worker.rs b/cli/worker.rs index ed166e6678..6289d00cf8 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; use std::path::PathBuf; diff --git a/ext/broadcast_channel/01_broadcast_channel.js b/ext/broadcast_channel/01_broadcast_channel.js index 1a4bb36474..a71ae465cf 100644 --- a/ext/broadcast_channel/01_broadcast_channel.js +++ b/ext/broadcast_channel/01_broadcast_channel.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index 4dea8f21e1..48b4127b0e 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_broadcast_channel" diff --git a/ext/broadcast_channel/in_memory_broadcast_channel.rs b/ext/broadcast_channel/in_memory_broadcast_channel.rs index 61dc68e17d..33803c74f4 100644 --- a/ext/broadcast_channel/in_memory_broadcast_channel.rs +++ b/ext/broadcast_channel/in_memory_broadcast_channel.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/ext/broadcast_channel/lib.deno_broadcast_channel.d.ts b/ext/broadcast_channel/lib.deno_broadcast_channel.d.ts index 339765ec9c..a43f8609b9 100644 --- a/ext/broadcast_channel/lib.deno_broadcast_channel.d.ts +++ b/ext/broadcast_channel/lib.deno_broadcast_channel.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/broadcast_channel/lib.rs b/ext/broadcast_channel/lib.rs index e4dbe9fcb4..4929153bfe 100644 --- a/ext/broadcast_channel/lib.rs +++ b/ext/broadcast_channel/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod in_memory_broadcast_channel; diff --git a/ext/cache/01_cache.js b/ext/cache/01_cache.js index 269261f401..55aac3a4d8 100644 --- a/ext/cache/01_cache.js +++ b/ext/cache/01_cache.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { op_cache_delete, diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index 96aec27576..cb4eef87df 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_cache" diff --git a/ext/cache/lib.deno_cache.d.ts b/ext/cache/lib.deno_cache.d.ts index f9e1818482..9995af7f66 100644 --- a/ext/cache/lib.deno_cache.d.ts +++ b/ext/cache/lib.deno_cache.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-var diff --git a/ext/cache/lib.rs b/ext/cache/lib.rs index 524d4cea05..6ee7380cf0 100644 --- a/ext/cache/lib.rs +++ b/ext/cache/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::path::PathBuf; diff --git a/ext/cache/sqlite.rs b/ext/cache/sqlite.rs index 469e3e51d6..8bd73b4799 100644 --- a/ext/cache/sqlite.rs +++ b/ext/cache/sqlite.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::PathBuf; use std::pin::Pin; diff --git a/ext/canvas/01_image.js b/ext/canvas/01_image.js index 3ea72db6ac..4b39c041b4 100644 --- a/ext/canvas/01_image.js +++ b/ext/canvas/01_image.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { internals, primordials } from "ext:core/mod.js"; import { op_image_decode_png, op_image_process } from "ext:core/ops"; diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index 7c7cc49b7c..0bc3dcb8fb 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_canvas" diff --git a/ext/canvas/lib.deno_canvas.d.ts b/ext/canvas/lib.deno_canvas.d.ts index c695ba5cd8..84d3cbdd42 100644 --- a/ext/canvas/lib.deno_canvas.d.ts +++ b/ext/canvas/lib.deno_canvas.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-var diff --git a/ext/canvas/lib.rs b/ext/canvas/lib.rs index 9ce5a5d257..533b8c3fb3 100644 --- a/ext/canvas/lib.rs +++ b/ext/canvas/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::PathBuf; diff --git a/ext/console/01_console.js b/ext/console/01_console.js index 3803492b90..a85faaccf1 100644 --- a/ext/console/01_console.js +++ b/ext/console/01_console.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/console/Cargo.toml b/ext/console/Cargo.toml index f68dd7d198..b4811265ba 100644 --- a/ext/console/Cargo.toml +++ b/ext/console/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_console" diff --git a/ext/console/internal.d.ts b/ext/console/internal.d.ts index 5f9627cf56..e90c9b6f11 100644 --- a/ext/console/internal.d.ts +++ b/ext/console/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/ext/console/lib.deno_console.d.ts b/ext/console/lib.deno_console.d.ts index 0c73972d36..54bbcab892 100644 --- a/ext/console/lib.deno_console.d.ts +++ b/ext/console/lib.deno_console.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any diff --git a/ext/console/lib.rs b/ext/console/lib.rs index d44e26857e..48a75329af 100644 --- a/ext/console/lib.rs +++ b/ext/console/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::PathBuf; use deno_core::op2; diff --git a/ext/cron/01_cron.ts b/ext/cron/01_cron.ts index b5c556a677..db193053d8 100644 --- a/ext/cron/01_cron.ts +++ b/ext/cron/01_cron.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; const { diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index 022a8418cf..821a2aa884 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_cron" diff --git a/ext/cron/interface.rs b/ext/cron/interface.rs index 50459b4afd..f92d601796 100644 --- a/ext/cron/interface.rs +++ b/ext/cron/interface.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use async_trait::async_trait; diff --git a/ext/cron/lib.rs b/ext/cron/lib.rs index 09c4e2d3f6..2c8d534d54 100644 --- a/ext/cron/lib.rs +++ b/ext/cron/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod interface; pub mod local; diff --git a/ext/cron/local.rs b/ext/cron/local.rs index 1110baadb8..d6213a7e36 100644 --- a/ext/cron/local.rs +++ b/ext/cron/local.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::OnceCell; use std::cell::RefCell; diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 63b1905145..5a9f32cb00 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index c283cc9277..8f7fea28d7 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_crypto" diff --git a/ext/crypto/decrypt.rs b/ext/crypto/decrypt.rs index 1140475183..8a00ffd8cd 100644 --- a/ext/crypto/decrypt.rs +++ b/ext/crypto/decrypt.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use aes::cipher::block_padding::Pkcs7; use aes::cipher::BlockDecryptMut; diff --git a/ext/crypto/ed25519.rs b/ext/crypto/ed25519.rs index da34b7d25d..d64b6904dd 100644 --- a/ext/crypto/ed25519.rs +++ b/ext/crypto/ed25519.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use base64::prelude::BASE64_URL_SAFE_NO_PAD; use base64::Engine; diff --git a/ext/crypto/encrypt.rs b/ext/crypto/encrypt.rs index 66b27657f8..f3464b5032 100644 --- a/ext/crypto/encrypt.rs +++ b/ext/crypto/encrypt.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use aes::cipher::block_padding::Pkcs7; use aes::cipher::BlockEncryptMut; diff --git a/ext/crypto/export_key.rs b/ext/crypto/export_key.rs index edf0d7239c..c4e41ef2da 100644 --- a/ext/crypto/export_key.rs +++ b/ext/crypto/export_key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use base64::prelude::BASE64_URL_SAFE_NO_PAD; use base64::Engine; diff --git a/ext/crypto/generate_key.rs b/ext/crypto/generate_key.rs index 3c0bd77c22..953e2f1df1 100644 --- a/ext/crypto/generate_key.rs +++ b/ext/crypto/generate_key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::unsync::spawn_blocking; diff --git a/ext/crypto/import_key.rs b/ext/crypto/import_key.rs index 3463ca2beb..4011f2536b 100644 --- a/ext/crypto/import_key.rs +++ b/ext/crypto/import_key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use base64::Engine; use deno_core::op2; diff --git a/ext/crypto/key.rs b/ext/crypto/key.rs index 6b3bf26f43..52a035ef01 100644 --- a/ext/crypto/key.rs +++ b/ext/crypto/key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use ring::agreement::Algorithm as RingAlgorithm; use ring::digest; diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts index 827c0224ce..3f4edb9245 100644 --- a/ext/crypto/lib.deno_crypto.d.ts +++ b/ext/crypto/lib.deno_crypto.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-var diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 396069e776..f468af5b07 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::num::NonZeroU32; use std::path::PathBuf; diff --git a/ext/crypto/shared.rs b/ext/crypto/shared.rs index f70d32856c..60ba560c98 100644 --- a/ext/crypto/shared.rs +++ b/ext/crypto/shared.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; diff --git a/ext/crypto/x25519.rs b/ext/crypto/x25519.rs index d2c4d986b9..80537d435f 100644 --- a/ext/crypto/x25519.rs +++ b/ext/crypto/x25519.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use curve25519_dalek::montgomery::MontgomeryPoint; use deno_core::op2; diff --git a/ext/crypto/x448.rs b/ext/crypto/x448.rs index 89bf48e28b..c582aa9661 100644 --- a/ext/crypto/x448.rs +++ b/ext/crypto/x448.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::ToJsBuffer; diff --git a/ext/fetch/20_headers.js b/ext/fetch/20_headers.js index e56a74c423..fb97adb13b 100644 --- a/ext/fetch/20_headers.js +++ b/ext/fetch/20_headers.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/21_formdata.js b/ext/fetch/21_formdata.js index 7d466b8e25..8b91a0fa47 100644 --- a/ext/fetch/21_formdata.js +++ b/ext/fetch/21_formdata.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/22_body.js b/ext/fetch/22_body.js index bb2bee77e2..f180bda18d 100644 --- a/ext/fetch/22_body.js +++ b/ext/fetch/22_body.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/22_http_client.js b/ext/fetch/22_http_client.js index 6a1243ee0b..b74979c4c6 100644 --- a/ext/fetch/22_http_client.js +++ b/ext/fetch/22_http_client.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/23_request.js b/ext/fetch/23_request.js index 61cac22d2e..9aa2f0fe5c 100644 --- a/ext/fetch/23_request.js +++ b/ext/fetch/23_request.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/23_response.js b/ext/fetch/23_response.js index 278dcb7dec..0a86e04310 100644 --- a/ext/fetch/23_response.js +++ b/ext/fetch/23_response.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/26_fetch.js b/ext/fetch/26_fetch.js index 12b9c4582b..f26e2e3fb9 100644 --- a/ext/fetch/26_fetch.js +++ b/ext/fetch/26_fetch.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/fetch/27_eventsource.js b/ext/fetch/27_eventsource.js index aadbb5fe71..99ea4073a5 100644 --- a/ext/fetch/27_eventsource.js +++ b/ext/fetch/27_eventsource.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index e6e4ded4af..ec82281bf4 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_fetch" diff --git a/ext/fetch/dns.rs b/ext/fetch/dns.rs index fdde4e17bb..e233021400 100644 --- a/ext/fetch/dns.rs +++ b/ext/fetch/dns.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::future::Future; use std::io; use std::net::SocketAddr; diff --git a/ext/fetch/fs_fetch_handler.rs b/ext/fetch/fs_fetch_handler.rs index c763d4e424..33293d0a27 100644 --- a/ext/fetch/fs_fetch_handler.rs +++ b/ext/fetch/fs_fetch_handler.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::rc::Rc; diff --git a/ext/fetch/internal.d.ts b/ext/fetch/internal.d.ts index 17565992f4..8ab38f9b62 100644 --- a/ext/fetch/internal.d.ts +++ b/ext/fetch/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/fetch/lib.deno_fetch.d.ts b/ext/fetch/lib.deno_fetch.d.ts index 8614dec899..2be844f0f8 100644 --- a/ext/fetch/lib.deno_fetch.d.ts +++ b/ext/fetch/lib.deno_fetch.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index 5abdfc3339..d1f3963743 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod dns; mod fs_fetch_handler; diff --git a/ext/fetch/proxy.rs b/ext/fetch/proxy.rs index ec1b1b13b7..3b371a07b1 100644 --- a/ext/fetch/proxy.rs +++ b/ext/fetch/proxy.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! Parts of this module should be able to be replaced with other crates //! eventually, once generic versions appear in hyper-util, et al. diff --git a/ext/fetch/tests.rs b/ext/fetch/tests.rs index 8d7436157e..4e023be4fa 100644 --- a/ext/fetch/tests.rs +++ b/ext/fetch/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::net::SocketAddr; use std::sync::atomic::AtomicUsize; diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index d3b07dc373..b8c12180c9 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index 9cd5c77013..0af0f4a131 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_ffi" diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs index dc1dc99ad9..001b925af5 100644 --- a/ext/ffi/call.rs +++ b/ext/ffi/call.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::ffi::c_void; diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs index 81b8f7dda2..c4b5e14842 100644 --- a/ext/ffi/callback.rs +++ b/ext/ffi/callback.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs index 8e6294e790..4eea2402a0 100644 --- a/ext/ffi/dlfcn.rs +++ b/ext/ffi/dlfcn.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/ffi/ir.rs b/ext/ffi/ir.rs index 4e6be13caf..7b2f167ce7 100644 --- a/ext/ffi/ir.rs +++ b/ext/ffi/ir.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ffi::c_void; use std::ptr; diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 9079b7ef9f..7fed3c32aa 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::mem::size_of; use std::os::raw::c_char; diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs index d7952a12b4..05bef40a89 100644 --- a/ext/ffi/repr.rs +++ b/ext/ffi/repr.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ffi::c_char; use std::ffi::c_void; diff --git a/ext/ffi/static.rs b/ext/ffi/static.rs index 472faf77c5..6ad7fe6d37 100644 --- a/ext/ffi/static.rs +++ b/ext/ffi/static.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/ext/ffi/symbol.rs b/ext/ffi/symbol.rs index cee1c7d33e..c4a68cf753 100644 --- a/ext/ffi/symbol.rs +++ b/ext/ffi/symbol.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::type_error; use deno_core::error::AnyError; diff --git a/ext/ffi/turbocall.rs b/ext/ffi/turbocall.rs index 38b4062ab7..499168dceb 100644 --- a/ext/ffi/turbocall.rs +++ b/ext/ffi/turbocall.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cmp::max; use std::ffi::c_void; diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js index 4e71acb1b2..74e3c87c17 100644 --- a/ext/fs/30_fs.js +++ b/ext/fs/30_fs.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index 1d0b623718..b7d22b7f93 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_fs" diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs index 80818887e3..0aae2b998f 100644 --- a/ext/fs/interface.rs +++ b/ext/fs/interface.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use core::str; use std::borrow::Cow; diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index 088d71c1f6..89bd5bc09a 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod interface; mod ops; diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index 655c2ca6b0..64be03ea61 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index 535859fb46..4845e9c7bc 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::disallowed_methods)] diff --git a/ext/fs/sync.rs b/ext/fs/sync.rs index cf87236558..2c07ba42df 100644 --- a/ext/fs/sync.rs +++ b/ext/fs/sync.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use inner::*; diff --git a/ext/http/00_serve.ts b/ext/http/00_serve.ts index 446533e910..6e5f25b473 100644 --- a/ext/http/00_serve.ts +++ b/ext/http/00_serve.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; const { diff --git a/ext/http/01_http.js b/ext/http/01_http.js index 9302bd8a0f..83983fa0ca 100644 --- a/ext/http/01_http.js +++ b/ext/http/01_http.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { diff --git a/ext/http/02_websocket.ts b/ext/http/02_websocket.ts index 96af4d4822..4d37b04a1d 100644 --- a/ext/http/02_websocket.ts +++ b/ext/http/02_websocket.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { internals, primordials } from "ext:core/mod.js"; import { op_http_websocket_accept_header } from "ext:core/ops"; const { diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index e7aaad2fc0..8f131b84e6 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_http" diff --git a/ext/http/benches/compressible.rs b/ext/http/benches/compressible.rs index 5ac09cb8bb..96b21512ba 100644 --- a/ext/http/benches/compressible.rs +++ b/ext/http/benches/compressible.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use bencher::benchmark_group; use bencher::benchmark_main; use bencher::Bencher; diff --git a/ext/http/compressible.rs b/ext/http/compressible.rs index ef5d830122..5c499f957d 100644 --- a/ext/http/compressible.rs +++ b/ext/http/compressible.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::str::FromStr; diff --git a/ext/http/fly_accept_encoding.rs b/ext/http/fly_accept_encoding.rs index a946481bd2..1be864fd3b 100644 --- a/ext/http/fly_accept_encoding.rs +++ b/ext/http/fly_accept_encoding.rs @@ -1,5 +1,5 @@ // Copyright 2018 Yoshua Wuyts. All rights reserved. MIT license. -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Forked from https://github.com/superfly/accept-encoding/blob/1cded757ec7ff3916e5bfe7441db76cdc48170dc/ // Forked to support both http 0.3 and http 1.0 crates. diff --git a/ext/http/http_next.rs b/ext/http/http_next.rs index 003190379d..d9861a0a6b 100644 --- a/ext/http/http_next.rs +++ b/ext/http/http_next.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; use std::ffi::c_void; diff --git a/ext/http/lib.rs b/ext/http/lib.rs index d582303167..e68bf3787d 100644 --- a/ext/http/lib.rs +++ b/ext/http/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/http/network_buffered_stream.rs b/ext/http/network_buffered_stream.rs index a195712d03..5882dd14e3 100644 --- a/ext/http/network_buffered_stream.rs +++ b/ext/http/network_buffered_stream.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io; use std::mem::MaybeUninit; diff --git a/ext/http/reader_stream.rs b/ext/http/reader_stream.rs index 8dc6c90ae7..b6985927f4 100644 --- a/ext/http/reader_stream.rs +++ b/ext/http/reader_stream.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::pin::Pin; use std::sync::atomic::AtomicBool; diff --git a/ext/http/request_body.rs b/ext/http/request_body.rs index b06b591c38..e7c9a06e2c 100644 --- a/ext/http/request_body.rs +++ b/ext/http/request_body.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::pin::Pin; use std::rc::Rc; diff --git a/ext/http/request_properties.rs b/ext/http/request_properties.rs index 6b89fa5a6a..9e60a22baf 100644 --- a/ext/http/request_properties.rs +++ b/ext/http/request_properties.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::net::Ipv4Addr; use std::net::SocketAddr; diff --git a/ext/http/response_body.rs b/ext/http/response_body.rs index 62430a3dea..ff264a8305 100644 --- a/ext/http/response_body.rs +++ b/ext/http/response_body.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::Write; use std::pin::Pin; use std::rc::Rc; diff --git a/ext/http/service.rs b/ext/http/service.rs index 52bd290390..3b7db49fc4 100644 --- a/ext/http/service.rs +++ b/ext/http/service.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::Cell; use std::cell::Ref; use std::cell::RefCell; diff --git a/ext/http/websocket_upgrade.rs b/ext/http/websocket_upgrade.rs index fdee19cc05..aae4a13883 100644 --- a/ext/http/websocket_upgrade.rs +++ b/ext/http/websocket_upgrade.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::marker::PhantomData; diff --git a/ext/io/12_io.js b/ext/io/12_io.js index 3cdcb113ba..b2556649ee 100644 --- a/ext/io/12_io.js +++ b/ext/io/12_io.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Interfaces 100% copied from Go. // Documentation liberally lifted from them too. diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 9298c654c1..782bd64444 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_io" diff --git a/ext/io/bi_pipe.rs b/ext/io/bi_pipe.rs index d8666e14a7..33c3267075 100644 --- a/ext/io/bi_pipe.rs +++ b/ext/io/bi_pipe.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::rc::Rc; diff --git a/ext/io/fs.rs b/ext/io/fs.rs index bd5dfd0bb9..ee0c7da5da 100644 --- a/ext/io/fs.rs +++ b/ext/io/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::fmt::Formatter; diff --git a/ext/io/lib.rs b/ext/io/lib.rs index 536a0346f1..f78e5a58fc 100644 --- a/ext/io/lib.rs +++ b/ext/io/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/io/pipe.rs b/ext/io/pipe.rs index 6ff449b944..5172bea10e 100644 --- a/ext/io/pipe.rs +++ b/ext/io/pipe.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io; use std::pin::Pin; use std::process::Stdio; diff --git a/ext/io/winpipe.rs b/ext/io/winpipe.rs index b3e659ea67..600eda4091 100644 --- a/ext/io/winpipe.rs +++ b/ext/io/winpipe.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io; use std::os::windows::io::RawHandle; use std::sync::atomic::AtomicU32; diff --git a/ext/kv/01_db.ts b/ext/kv/01_db.ts index c644ff7121..0575c2c414 100644 --- a/ext/kv/01_db.ts +++ b/ext/kv/01_db.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index c97aa75552..451fa50cf9 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_kv" diff --git a/ext/kv/config.rs b/ext/kv/config.rs index 7166bcbcc2..f762a74578 100644 --- a/ext/kv/config.rs +++ b/ext/kv/config.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[derive(Clone, Copy, Debug)] pub struct KvConfig { diff --git a/ext/kv/dynamic.rs b/ext/kv/dynamic.rs index 8db2b92926..33e3d4842f 100644 --- a/ext/kv/dynamic.rs +++ b/ext/kv/dynamic.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::rc::Rc; diff --git a/ext/kv/interface.rs b/ext/kv/interface.rs index 9737a9366d..fec0ef1afd 100644 --- a/ext/kv/interface.rs +++ b/ext/kv/interface.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::rc::Rc; diff --git a/ext/kv/lib.rs b/ext/kv/lib.rs index ce7509721a..82dda6d759 100644 --- a/ext/kv/lib.rs +++ b/ext/kv/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod config; pub mod dynamic; diff --git a/ext/kv/remote.rs b/ext/kv/remote.rs index b638ecbd4b..cb408ef644 100644 --- a/ext/kv/remote.rs +++ b/ext/kv/remote.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::marker::PhantomData; diff --git a/ext/kv/sqlite.rs b/ext/kv/sqlite.rs index c28cfdd932..a88c61d723 100644 --- a/ext/kv/sqlite.rs +++ b/ext/kv/sqlite.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index 5d726b3e31..a17fd9e0f6 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_napi" diff --git a/ext/napi/build.rs b/ext/napi/build.rs index 8705830a95..71686d6451 100644 --- a/ext/napi/build.rs +++ b/ext/napi/build.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. fn main() { let symbols_file_name = match std::env::consts::OS { diff --git a/ext/napi/function.rs b/ext/napi/function.rs index a128ad7900..681f014421 100644 --- a/ext/napi/function.rs +++ b/ext/napi/function.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use crate::*; #[repr(C)] diff --git a/ext/napi/js_native_api.rs b/ext/napi/js_native_api.rs index a5b0e0d324..80579081f3 100644 --- a/ext/napi/js_native_api.rs +++ b/ext/napi/js_native_api.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(non_upper_case_globals)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs index dd7e5929c0..3f495e05e6 100644 --- a/ext/napi/lib.rs +++ b/ext/napi/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] diff --git a/ext/napi/node_api.rs b/ext/napi/node_api.rs index abc3d32bd0..13ea0b3e62 100644 --- a/ext/napi/node_api.rs +++ b/ext/napi/node_api.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![deny(unsafe_op_in_unsafe_fn)] diff --git a/ext/napi/sym/Cargo.toml b/ext/napi/sym/Cargo.toml index 22228bd2f6..c832a1fa30 100644 --- a/ext/napi/sym/Cargo.toml +++ b/ext/napi/sym/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "napi_sym" diff --git a/ext/napi/sym/lib.rs b/ext/napi/sym/lib.rs index e2826306b9..94444d8541 100644 --- a/ext/napi/sym/lib.rs +++ b/ext/napi/sym/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use proc_macro::TokenStream; use quote::quote; diff --git a/ext/napi/util.rs b/ext/napi/util.rs index 592536b502..a913eade16 100644 --- a/ext/napi/util.rs +++ b/ext/napi/util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use libc::INT_MAX; use crate::*; diff --git a/ext/napi/uv.rs b/ext/napi/uv.rs index 3dda65e627..ef4ac495c3 100644 --- a/ext/napi/uv.rs +++ b/ext/napi/uv.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::mem::MaybeUninit; use std::ptr::addr_of_mut; diff --git a/ext/napi/value.rs b/ext/napi/value.rs index 5bdfef0a00..851a56e963 100644 --- a/ext/napi/value.rs +++ b/ext/napi/value.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::mem::transmute; use std::ops::Deref; diff --git a/ext/net/01_net.js b/ext/net/01_net.js index c3e5f9e5ca..3afbd031e6 100644 --- a/ext/net/01_net.js +++ b/ext/net/01_net.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { diff --git a/ext/net/02_tls.js b/ext/net/02_tls.js index 6dad965590..21d5512ebd 100644 --- a/ext/net/02_tls.js +++ b/ext/net/02_tls.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; const { internalRidSymbol } = core; diff --git a/ext/net/03_quic.js b/ext/net/03_quic.js index e100e7bd64..48b35866d2 100644 --- a/ext/net/03_quic.js +++ b/ext/net/03_quic.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_quic_accept, diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index eaee7bfb4b..a834d8c8f3 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_net" diff --git a/ext/net/io.rs b/ext/net/io.rs index 84f98668a9..3f12b92a9a 100644 --- a/ext/net/io.rs +++ b/ext/net/io.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::rc::Rc; diff --git a/ext/net/lib.deno_net.d.ts b/ext/net/lib.deno_net.d.ts index 958474cbbd..8e0c384168 100644 --- a/ext/net/lib.deno_net.d.ts +++ b/ext/net/lib.deno_net.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/ext/net/lib.rs b/ext/net/lib.rs index 18e00c5ac3..b2b87fc6ad 100644 --- a/ext/net/lib.rs +++ b/ext/net/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod io; pub mod ops; diff --git a/ext/net/ops.rs b/ext/net/ops.rs index f8f22cb41b..1fb0f39280 100644 --- a/ext/net/ops.rs +++ b/ext/net/ops.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs index 4d715a8647..5b8cd47751 100644 --- a/ext/net/ops_tls.rs +++ b/ext/net/ops_tls.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/net/ops_unix.rs b/ext/net/ops_unix.rs index 4d10d1b622..b347b81dc7 100644 --- a/ext/net/ops_unix.rs +++ b/ext/net/ops_unix.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/net/quic.rs b/ext/net/quic.rs index be9be2a817..676b2f06a1 100644 --- a/ext/net/quic.rs +++ b/ext/net/quic.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/net/raw.rs b/ext/net/raw.rs index a7134017c5..0312d661a5 100644 --- a/ext/net/raw.rs +++ b/ext/net/raw.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::rc::Rc; diff --git a/ext/net/resolve_addr.rs b/ext/net/resolve_addr.rs index 137af6c19f..9a757391ea 100644 --- a/ext/net/resolve_addr.rs +++ b/ext/net/resolve_addr.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::net::SocketAddr; use std::net::ToSocketAddrs; diff --git a/ext/net/tcp.rs b/ext/net/tcp.rs index 63baa8e4be..f2cd5f5705 100644 --- a/ext/net/tcp.rs +++ b/ext/net/tcp.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 82478e7f7c..a3936af735 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_node" diff --git a/ext/node/benchmarks/child_process_ipc.mjs b/ext/node/benchmarks/child_process_ipc.mjs index fa671d76f7..aa4e2699d7 100644 --- a/ext/node/benchmarks/child_process_ipc.mjs +++ b/ext/node/benchmarks/child_process_ipc.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { fork } from "node:child_process"; import process from "node:process"; diff --git a/ext/node/build.rs b/ext/node/build.rs index 041110f257..0d42f2cd76 100644 --- a/ext/node/build.rs +++ b/ext/node/build.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::env; diff --git a/ext/node/global.rs b/ext/node/global.rs index 4d6695431d..a89a3f9830 100644 --- a/ext/node/global.rs +++ b/ext/node/global.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::v8; use deno_core::v8::GetPropertyNamesArgs; diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 740cc3c136..d7aa82430d 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![deny(clippy::print_stderr)] #![deny(clippy::print_stdout)] diff --git a/ext/node/ops/blocklist.rs b/ext/node/ops/blocklist.rs index 5fce0d017e..bcb36fc97b 100644 --- a/ext/node/ops/blocklist.rs +++ b/ext/node/ops/blocklist.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashSet; diff --git a/ext/node/ops/buffer.rs b/ext/node/ops/buffer.rs index 01f878ec15..e3ae2b2391 100644 --- a/ext/node/ops/buffer.rs +++ b/ext/node/ops/buffer.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::anyhow::anyhow; use deno_core::anyhow::Result; diff --git a/ext/node/ops/crypto/cipher.rs b/ext/node/ops/crypto/cipher.rs index cedd092e81..500d8d1b4b 100644 --- a/ext/node/ops/crypto/cipher.rs +++ b/ext/node/ops/crypto/cipher.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/node/ops/crypto/dh.rs b/ext/node/ops/crypto/dh.rs index 3da2d15fec..88452c3660 100644 --- a/ext/node/ops/crypto/dh.rs +++ b/ext/node/ops/crypto/dh.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use num_bigint_dig::BigUint; use num_bigint_dig::RandBigInt; diff --git a/ext/node/ops/crypto/digest.rs b/ext/node/ops/crypto/digest.rs index dda62053bf..f9810e8c67 100644 --- a/ext/node/ops/crypto/digest.rs +++ b/ext/node/ops/crypto/digest.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::rc::Rc; diff --git a/ext/node/ops/crypto/keys.rs b/ext/node/ops/crypto/keys.rs index dfcd3d11bf..d982d96a8c 100644 --- a/ext/node/ops/crypto/keys.rs +++ b/ext/node/ops/crypto/keys.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/node/ops/crypto/md5_sha1.rs b/ext/node/ops/crypto/md5_sha1.rs index e2e4844913..c6c6eb1c83 100644 --- a/ext/node/ops/crypto/md5_sha1.rs +++ b/ext/node/ops/crypto/md5_sha1.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use core::fmt; use digest::core_api::AlgorithmName; diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index 2f650ba164..19a0bbfc40 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::future::Future; use std::rc::Rc; diff --git a/ext/node/ops/crypto/pkcs3.rs b/ext/node/ops/crypto/pkcs3.rs index 5772514608..6668148d23 100644 --- a/ext/node/ops/crypto/pkcs3.rs +++ b/ext/node/ops/crypto/pkcs3.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // PKCS #3: Diffie-Hellman Key Agreement Standard diff --git a/ext/node/ops/crypto/primes.rs b/ext/node/ops/crypto/primes.rs index 91297eb56c..65ffa7a7a0 100644 --- a/ext/node/ops/crypto/primes.rs +++ b/ext/node/ops/crypto/primes.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ops::Deref; diff --git a/ext/node/ops/crypto/sign.rs b/ext/node/ops/crypto/sign.rs index ccfd1a1165..e7c15d16b7 100644 --- a/ext/node/ops/crypto/sign.rs +++ b/ext/node/ops/crypto/sign.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use core::ops::Add; use ecdsa::der::MaxOverhead; diff --git a/ext/node/ops/crypto/x509.rs b/ext/node/ops/crypto/x509.rs index ac80afb938..23b19720e3 100644 --- a/ext/node/ops/crypto/x509.rs +++ b/ext/node/ops/crypto/x509.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ops::Deref; diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs index 0f6b7afb11..73db3ea2e5 100644 --- a/ext/node/ops/fs.rs +++ b/ext/node/ops/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::rc::Rc; diff --git a/ext/node/ops/http.rs b/ext/node/ops/http.rs index ce5fdc163e..ad6217b6a6 100644 --- a/ext/node/ops/http.rs +++ b/ext/node/ops/http.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/node/ops/http2.rs b/ext/node/ops/http2.rs index 53dada9f41..c6c6484477 100644 --- a/ext/node/ops/http2.rs +++ b/ext/node/ops/http2.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/node/ops/idna.rs b/ext/node/ops/idna.rs index eb037e6079..4ae1ce3954 100644 --- a/ext/node/ops/idna.rs +++ b/ext/node/ops/idna.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; diff --git a/ext/node/ops/inspector.rs b/ext/node/ops/inspector.rs index ecd1150e0c..03cfed4592 100644 --- a/ext/node/ops/inspector.rs +++ b/ext/node/ops/inspector.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::rc::Rc; diff --git a/ext/node/ops/ipc.rs b/ext/node/ops/ipc.rs index 8229ceadce..0eb3ae6aae 100644 --- a/ext/node/ops/ipc.rs +++ b/ext/node/ops/ipc.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use impl_::*; diff --git a/ext/node/ops/mod.rs b/ext/node/ops/mod.rs index e5ea8b4172..0b7be91860 100644 --- a/ext/node/ops/mod.rs +++ b/ext/node/ops/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod blocklist; pub mod buffer; diff --git a/ext/node/ops/os/cpus.rs b/ext/node/ops/os/cpus.rs index c96b383822..d9b28ce88c 100644 --- a/ext/node/ops/os/cpus.rs +++ b/ext/node/ops/os/cpus.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde::Serialize; diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs index 1cdf737f43..d45f67bd13 100644 --- a/ext/node/ops/os/mod.rs +++ b/ext/node/ops/os/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::mem::MaybeUninit; diff --git a/ext/node/ops/os/priority.rs b/ext/node/ops/os/priority.rs index 9a1ebcca70..f9e686ceba 100644 --- a/ext/node/ops/os/priority.rs +++ b/ext/node/ops/os/priority.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use impl_::*; diff --git a/ext/node/ops/perf_hooks.rs b/ext/node/ops/perf_hooks.rs index 256f879813..eca5fe2fa8 100644 --- a/ext/node/ops/perf_hooks.rs +++ b/ext/node/ops/perf_hooks.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::Cell; diff --git a/ext/node/ops/process.rs b/ext/node/ops/process.rs index 45c599bee2..0ef360af8c 100644 --- a/ext/node/ops/process.rs +++ b/ext/node/ops/process.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::OpState; diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs index c5e3afa87d..218079d4e1 100644 --- a/ext/node/ops/require.rs +++ b/ext/node/ops/require.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/node/ops/tls.rs b/ext/node/ops/tls.rs index 86b1779601..8b2845e023 100644 --- a/ext/node/ops/tls.rs +++ b/ext/node/ops/tls.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use base64::Engine; use deno_core::op2; use webpki_root_certs; diff --git a/ext/node/ops/util.rs b/ext/node/ops/util.rs index 1c177ac043..bc1d2c0588 100644 --- a/ext/node/ops/util.rs +++ b/ext/node/ops/util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::OpState; diff --git a/ext/node/ops/v8.rs b/ext/node/ops/v8.rs index 9c2dfa451a..8f4a70dccf 100644 --- a/ext/node/ops/v8.rs +++ b/ext/node/ops/v8.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr::NonNull; diff --git a/ext/node/ops/vm.rs b/ext/node/ops/vm.rs index 7720298e80..34eff8865c 100644 --- a/ext/node/ops/vm.rs +++ b/ext/node/ops/vm.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; diff --git a/ext/node/ops/vm_internal.rs b/ext/node/ops/vm_internal.rs index 4c500786c5..e8c1cc02f0 100644 --- a/ext/node/ops/vm_internal.rs +++ b/ext/node/ops/vm_internal.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::error::type_error; use deno_core::error::AnyError; diff --git a/ext/node/ops/winerror.rs b/ext/node/ops/winerror.rs index cb053774ef..5cbeddc5ae 100644 --- a/ext/node/ops/winerror.rs +++ b/ext/node/ops/winerror.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/ext/node/ops/worker_threads.rs b/ext/node/ops/worker_threads.rs index 48683be1e7..f7aa8c71cb 100644 --- a/ext/node/ops/worker_threads.rs +++ b/ext/node/ops/worker_threads.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; diff --git a/ext/node/ops/zlib/alloc.rs b/ext/node/ops/zlib/alloc.rs index d425a18d5e..6066ab6a8e 100644 --- a/ext/node/ops/zlib/alloc.rs +++ b/ext/node/ops/zlib/alloc.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Workaround for https://github.com/rust-lang/libz-sys/issues/55 // See https://github.com/rust-lang/flate2-rs/blob/31fb07820345691352aaa64f367c1e482ad9cfdc/src/ffi/c.rs#L60 diff --git a/ext/node/ops/zlib/brotli.rs b/ext/node/ops/zlib/brotli.rs index 7c798aa754..5d15df559f 100644 --- a/ext/node/ops/zlib/brotli.rs +++ b/ext/node/ops/zlib/brotli.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::io::Read; diff --git a/ext/node/ops/zlib/mod.rs b/ext/node/ops/zlib/mod.rs index 777faf6f86..b5277e7a34 100644 --- a/ext/node/ops/zlib/mod.rs +++ b/ext/node/ops/zlib/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/node/ops/zlib/mode.rs b/ext/node/ops/zlib/mode.rs index 41565f9b11..c0660a7c7a 100644 --- a/ext/node/ops/zlib/mode.rs +++ b/ext/node/ops/zlib/mode.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[derive(Debug, thiserror::Error)] #[error("bad argument")] diff --git a/ext/node/ops/zlib/stream.rs b/ext/node/ops/zlib/stream.rs index 949e1230d8..de7abcacbd 100644 --- a/ext/node/ops/zlib/stream.rs +++ b/ext/node/ops/zlib/stream.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ffi::c_int; use std::ops::Deref; diff --git a/ext/node/polyfill.rs b/ext/node/polyfill.rs index 556cb48a42..6e6e9d09c2 100644 --- a/ext/node/polyfill.rs +++ b/ext/node/polyfill.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// e.g. `is_builtin_node_module("assert")` pub fn is_builtin_node_module(module_name: &str) -> bool { diff --git a/ext/node/polyfills/00_globals.js b/ext/node/polyfills/00_globals.js index efe491acc1..8a896412bc 100644 --- a/ext/node/polyfills/00_globals.js +++ b/ext/node/polyfills/00_globals.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js index df73cad6b7..8f3201755f 100644 --- a/ext/node/polyfills/01_require.js +++ b/ext/node/polyfills/01_require.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/02_init.js b/ext/node/polyfills/02_init.js index b25f7ad574..a069bd828c 100644 --- a/ext/node/polyfills/02_init.js +++ b/ext/node/polyfills/02_init.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/_brotli.js b/ext/node/polyfills/_brotli.js index 108e5319a9..308cad42ad 100644 --- a/ext/node/polyfills/_brotli.js +++ b/ext/node/polyfills/_brotli.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/_events.d.ts b/ext/node/polyfills/_events.d.ts index 1b765041e5..98a6b9d2f4 100644 --- a/ext/node/polyfills/_events.d.ts +++ b/ext/node/polyfills/_events.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any // Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/9b9cd671114a2a5178809798d8e7f4d8ca6c2671/types/node/events.d.ts diff --git a/ext/node/polyfills/_events.mjs b/ext/node/polyfills/_events.mjs index ce7a8ebf24..feecad2743 100644 --- a/ext/node/polyfills/_events.mjs +++ b/ext/node/polyfills/_events.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/_fs/_fs_access.ts b/ext/node/polyfills/_fs/_fs_access.ts index 824386e64b..561e023cd8 100644 --- a/ext/node/polyfills/_fs/_fs_access.ts +++ b/ext/node/polyfills/_fs/_fs_access.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_appendFile.ts b/ext/node/polyfills/_fs/_fs_appendFile.ts index 7077898623..ed47ea5a83 100644 --- a/ext/node/polyfills/_fs/_fs_appendFile.ts +++ b/ext/node/polyfills/_fs/_fs_appendFile.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { CallbackWithError, diff --git a/ext/node/polyfills/_fs/_fs_chmod.ts b/ext/node/polyfills/_fs/_fs_chmod.ts index eec8c7a8a9..195b4f810a 100644 --- a/ext/node/polyfills/_fs/_fs_chmod.ts +++ b/ext/node/polyfills/_fs/_fs_chmod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_chown.ts b/ext/node/polyfills/_fs/_fs_chown.ts index 56364109d5..0056502f20 100644 --- a/ext/node/polyfills/_fs/_fs_chown.ts +++ b/ext/node/polyfills/_fs/_fs_chown.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_close.ts b/ext/node/polyfills/_fs/_fs_close.ts index fd01a0336a..476b3912be 100644 --- a/ext/node/polyfills/_fs/_fs_close.ts +++ b/ext/node/polyfills/_fs/_fs_close.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_common.ts b/ext/node/polyfills/_fs/_fs_common.ts index a29548bb36..9ec474afa9 100644 --- a/ext/node/polyfills/_fs/_fs_common.ts +++ b/ext/node/polyfills/_fs/_fs_common.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_constants.ts b/ext/node/polyfills/_fs/_fs_constants.ts index 0af75f072c..3d5d0b5898 100644 --- a/ext/node/polyfills/_fs/_fs_constants.ts +++ b/ext/node/polyfills/_fs/_fs_constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { fs } from "ext:deno_node/internal_binding/constants.ts"; diff --git a/ext/node/polyfills/_fs/_fs_copy.ts b/ext/node/polyfills/_fs/_fs_copy.ts index 0434bff4d1..87204e348c 100644 --- a/ext/node/polyfills/_fs/_fs_copy.ts +++ b/ext/node/polyfills/_fs/_fs_copy.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_cp.js b/ext/node/polyfills/_fs/_fs_cp.js index 52dd8f5056..29bc633224 100644 --- a/ext/node/polyfills/_fs/_fs_cp.js +++ b/ext/node/polyfills/_fs/_fs_cp.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { op_node_cp, op_node_cp_sync } from "ext:core/ops"; import { diff --git a/ext/node/polyfills/_fs/_fs_dir.ts b/ext/node/polyfills/_fs/_fs_dir.ts index ed051fb0bf..2a496562d6 100644 --- a/ext/node/polyfills/_fs/_fs_dir.ts +++ b/ext/node/polyfills/_fs/_fs_dir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import Dirent from "ext:deno_node/_fs/_fs_dirent.ts"; diff --git a/ext/node/polyfills/_fs/_fs_dirent.ts b/ext/node/polyfills/_fs/_fs_dirent.ts index d4ad6bb430..fce7a6e72d 100644 --- a/ext/node/polyfills/_fs/_fs_dirent.ts +++ b/ext/node/polyfills/_fs/_fs_dirent.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; export default class Dirent { diff --git a/ext/node/polyfills/_fs/_fs_exists.ts b/ext/node/polyfills/_fs/_fs_exists.ts index b5bbe235a4..6a285f5e60 100644 --- a/ext/node/polyfills/_fs/_fs_exists.ts +++ b/ext/node/polyfills/_fs/_fs_exists.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_fdatasync.ts b/ext/node/polyfills/_fs/_fs_fdatasync.ts index 0c3f50f1c6..02226e1c96 100644 --- a/ext/node/polyfills/_fs/_fs_fdatasync.ts +++ b/ext/node/polyfills/_fs/_fs_fdatasync.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_fstat.ts b/ext/node/polyfills/_fs/_fs_fstat.ts index 1a845dfff4..37c057c378 100644 --- a/ext/node/polyfills/_fs/_fs_fstat.ts +++ b/ext/node/polyfills/_fs/_fs_fstat.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_fsync.ts b/ext/node/polyfills/_fs/_fs_fsync.ts index 75d4b37569..a07f440778 100644 --- a/ext/node/polyfills/_fs/_fs_fsync.ts +++ b/ext/node/polyfills/_fs/_fs_fsync.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_ftruncate.ts b/ext/node/polyfills/_fs/_fs_ftruncate.ts index 79320137f9..6a8614b734 100644 --- a/ext/node/polyfills/_fs/_fs_ftruncate.ts +++ b/ext/node/polyfills/_fs/_fs_ftruncate.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_futimes.ts b/ext/node/polyfills/_fs/_fs_futimes.ts index 98cd1066c3..be274f4d11 100644 --- a/ext/node/polyfills/_fs/_fs_futimes.ts +++ b/ext/node/polyfills/_fs/_fs_futimes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_lchown.ts b/ext/node/polyfills/_fs/_fs_lchown.ts index 8611c8021d..293155f9d0 100644 --- a/ext/node/polyfills/_fs/_fs_lchown.ts +++ b/ext/node/polyfills/_fs/_fs_lchown.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_link.ts b/ext/node/polyfills/_fs/_fs_link.ts index a10860c12f..73ed890d36 100644 --- a/ext/node/polyfills/_fs/_fs_link.ts +++ b/ext/node/polyfills/_fs/_fs_link.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_lstat.ts b/ext/node/polyfills/_fs/_fs_lstat.ts index 6ce401444f..b9f80bfc51 100644 --- a/ext/node/polyfills/_fs/_fs_lstat.ts +++ b/ext/node/polyfills/_fs/_fs_lstat.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_lutimes.ts b/ext/node/polyfills/_fs/_fs_lutimes.ts index 2475c57149..3f8dd167c0 100644 --- a/ext/node/polyfills/_fs/_fs_lutimes.ts +++ b/ext/node/polyfills/_fs/_fs_lutimes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_mkdir.ts b/ext/node/polyfills/_fs/_fs_mkdir.ts index 06a8b35221..03d36629e5 100644 --- a/ext/node/polyfills/_fs/_fs_mkdir.ts +++ b/ext/node/polyfills/_fs/_fs_mkdir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_mkdtemp.ts b/ext/node/polyfills/_fs/_fs_mkdtemp.ts index 5498828b5b..46060971a8 100644 --- a/ext/node/polyfills/_fs/_fs_mkdtemp.ts +++ b/ext/node/polyfills/_fs/_fs_mkdtemp.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Node.js contributors. All rights reserved. MIT License. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_fs/_fs_open.ts b/ext/node/polyfills/_fs/_fs_open.ts index 31ca4bb619..85628ddcef 100644 --- a/ext/node/polyfills/_fs/_fs_open.ts +++ b/ext/node/polyfills/_fs/_fs_open.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_opendir.ts b/ext/node/polyfills/_fs/_fs_opendir.ts index 3280f0fd5c..51379fd451 100644 --- a/ext/node/polyfills/_fs/_fs_opendir.ts +++ b/ext/node/polyfills/_fs/_fs_opendir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_read.ts b/ext/node/polyfills/_fs/_fs_read.ts index df4f5e375d..c7f3acf4d8 100644 --- a/ext/node/polyfills/_fs/_fs_read.ts +++ b/ext/node/polyfills/_fs/_fs_read.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_readFile.ts b/ext/node/polyfills/_fs/_fs_readFile.ts index b1bc53675d..752a683ca7 100644 --- a/ext/node/polyfills/_fs/_fs_readFile.ts +++ b/ext/node/polyfills/_fs/_fs_readFile.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_readdir.ts b/ext/node/polyfills/_fs/_fs_readdir.ts index 59c802e424..4b34451a38 100644 --- a/ext/node/polyfills/_fs/_fs_readdir.ts +++ b/ext/node/polyfills/_fs/_fs_readdir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_readlink.ts b/ext/node/polyfills/_fs/_fs_readlink.ts index 08bea843fa..084dbc2d09 100644 --- a/ext/node/polyfills/_fs/_fs_readlink.ts +++ b/ext/node/polyfills/_fs/_fs_readlink.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_readv.ts b/ext/node/polyfills/_fs/_fs_readv.ts index 2259f029ae..4486959b76 100644 --- a/ext/node/polyfills/_fs/_fs_readv.ts +++ b/ext/node/polyfills/_fs/_fs_readv.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_realpath.ts b/ext/node/polyfills/_fs/_fs_realpath.ts index 4568748ba7..12f5f10a43 100644 --- a/ext/node/polyfills/_fs/_fs_realpath.ts +++ b/ext/node/polyfills/_fs/_fs_realpath.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_rename.ts b/ext/node/polyfills/_fs/_fs_rename.ts index 41569c80f3..7a84cf8d98 100644 --- a/ext/node/polyfills/_fs/_fs_rename.ts +++ b/ext/node/polyfills/_fs/_fs_rename.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_rm.ts b/ext/node/polyfills/_fs/_fs_rm.ts index 5730f8c413..cce2ab95c0 100644 --- a/ext/node/polyfills/_fs/_fs_rm.ts +++ b/ext/node/polyfills/_fs/_fs_rm.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_rmdir.ts b/ext/node/polyfills/_fs/_fs_rmdir.ts index 00c085ebd5..6a78e9e467 100644 --- a/ext/node/polyfills/_fs/_fs_rmdir.ts +++ b/ext/node/polyfills/_fs/_fs_rmdir.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_stat.ts b/ext/node/polyfills/_fs/_fs_stat.ts index f264746686..031c8dbb50 100644 --- a/ext/node/polyfills/_fs/_fs_stat.ts +++ b/ext/node/polyfills/_fs/_fs_stat.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_statfs.js b/ext/node/polyfills/_fs/_fs_statfs.js index 51da1ed684..90953c12b5 100644 --- a/ext/node/polyfills/_fs/_fs_statfs.js +++ b/ext/node/polyfills/_fs/_fs_statfs.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { BigInt } from "ext:deno_node/internal/primordials.mjs"; import { op_node_statfs } from "ext:core/ops"; diff --git a/ext/node/polyfills/_fs/_fs_symlink.ts b/ext/node/polyfills/_fs/_fs_symlink.ts index 350263423a..953697e44c 100644 --- a/ext/node/polyfills/_fs/_fs_symlink.ts +++ b/ext/node/polyfills/_fs/_fs_symlink.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_truncate.ts b/ext/node/polyfills/_fs/_fs_truncate.ts index a7e79b9c30..47b87eecdc 100644 --- a/ext/node/polyfills/_fs/_fs_truncate.ts +++ b/ext/node/polyfills/_fs/_fs_truncate.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_unlink.ts b/ext/node/polyfills/_fs/_fs_unlink.ts index 1d9aefa46f..d046c40f35 100644 --- a/ext/node/polyfills/_fs/_fs_unlink.ts +++ b/ext/node/polyfills/_fs/_fs_unlink.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_utimes.ts b/ext/node/polyfills/_fs/_fs_utimes.ts index 3fff4a4623..ff25fffed8 100644 --- a/ext/node/polyfills/_fs/_fs_utimes.ts +++ b/ext/node/polyfills/_fs/_fs_utimes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_watch.ts b/ext/node/polyfills/_fs/_fs_watch.ts index eb2cd8bfa2..8b92e9aa44 100644 --- a/ext/node/polyfills/_fs/_fs_watch.ts +++ b/ext/node/polyfills/_fs/_fs_watch.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_write.d.ts b/ext/node/polyfills/_fs/_fs_write.d.ts index 7171ec6dbd..cd8257ec35 100644 --- a/ext/node/polyfills/_fs/_fs_write.d.ts +++ b/ext/node/polyfills/_fs/_fs_write.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts import { BufferEncoding, ErrnoException } from "ext:deno_node/_global.d.ts"; diff --git a/ext/node/polyfills/_fs/_fs_write.mjs b/ext/node/polyfills/_fs/_fs_write.mjs index 254094c9ad..a0d0af6017 100644 --- a/ext/node/polyfills/_fs/_fs_write.mjs +++ b/ext/node/polyfills/_fs/_fs_write.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_fs/_fs_writeFile.ts b/ext/node/polyfills/_fs/_fs_writeFile.ts index dd324815b9..c76be8a6b8 100644 --- a/ext/node/polyfills/_fs/_fs_writeFile.ts +++ b/ext/node/polyfills/_fs/_fs_writeFile.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/_fs/_fs_writev.d.ts b/ext/node/polyfills/_fs/_fs_writev.d.ts index daa4dbbcf4..1ba5511ff1 100644 --- a/ext/node/polyfills/_fs/_fs_writev.d.ts +++ b/ext/node/polyfills/_fs/_fs_writev.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts import { ErrnoException } from "ext:deno_node/_global.d.ts"; diff --git a/ext/node/polyfills/_fs/_fs_writev.mjs b/ext/node/polyfills/_fs/_fs_writev.mjs index 055bce0b24..61807c7f68 100644 --- a/ext/node/polyfills/_fs/_fs_writev.mjs +++ b/ext/node/polyfills/_fs/_fs_writev.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_global.d.ts b/ext/node/polyfills/_global.d.ts index 8587acbbb2..1e610de08d 100644 --- a/ext/node/polyfills/_global.d.ts +++ b/ext/node/polyfills/_global.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { EventEmitter } from "ext:deno_node/_events.d.ts"; import { Buffer } from "node:buffer"; diff --git a/ext/node/polyfills/_http_agent.mjs b/ext/node/polyfills/_http_agent.mjs index 53bb64ed53..4de58cb406 100644 --- a/ext/node/polyfills/_http_agent.mjs +++ b/ext/node/polyfills/_http_agent.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_http_common.ts b/ext/node/polyfills/_http_common.ts index 86143b4dea..f2ca75ec1c 100644 --- a/ext/node/polyfills/_http_common.ts +++ b/ext/node/polyfills/_http_common.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_http_outgoing.ts b/ext/node/polyfills/_http_outgoing.ts index 5a9a8ad7e6..fcc6394eb8 100644 --- a/ext/node/polyfills/_http_outgoing.ts +++ b/ext/node/polyfills/_http_outgoing.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_http_server.ts b/ext/node/polyfills/_http_server.ts index c2867de0c6..cf80f1e6e0 100644 --- a/ext/node/polyfills/_http_server.ts +++ b/ext/node/polyfills/_http_server.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export enum STATUS_CODES { /** RFC 7231, 6.2.1 */ diff --git a/ext/node/polyfills/_next_tick.ts b/ext/node/polyfills/_next_tick.ts index af306a29c8..355b6ca6ba 100644 --- a/ext/node/polyfills/_next_tick.ts +++ b/ext/node/polyfills/_next_tick.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_process/exiting.ts b/ext/node/polyfills/_process/exiting.ts index 01991e9c96..c1a032b643 100644 --- a/ext/node/polyfills/_process/exiting.ts +++ b/ext/node/polyfills/_process/exiting.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore prefer-const export let _exiting = false; diff --git a/ext/node/polyfills/_process/process.ts b/ext/node/polyfills/_process/process.ts index 6f69139c98..06e48160de 100644 --- a/ext/node/polyfills/_process/process.ts +++ b/ext/node/polyfills/_process/process.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // The following are all the process APIs that don't depend on the stream module diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs index 3573956c9d..9a838e7d38 100644 --- a/ext/node/polyfills/_process/streams.mjs +++ b/ext/node/polyfills/_process/streams.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_readline.d.ts b/ext/node/polyfills/_readline.d.ts index fcaa327a05..256cb87887 100644 --- a/ext/node/polyfills/_readline.d.ts +++ b/ext/node/polyfills/_readline.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any // Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/cd61f5b4d3d143108569ec3f88adc0eb34b961c4/types/node/readline.d.ts diff --git a/ext/node/polyfills/_readline.mjs b/ext/node/polyfills/_readline.mjs index 2af717152b..3943736ea9 100644 --- a/ext/node/polyfills/_readline.mjs +++ b/ext/node/polyfills/_readline.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/_readline_shared_types.d.ts b/ext/node/polyfills/_readline_shared_types.d.ts index b58ad035e7..f88de9091c 100644 --- a/ext/node/polyfills/_readline_shared_types.d.ts +++ b/ext/node/polyfills/_readline_shared_types.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Part of https://github.com/DefinitelyTyped/DefinitelyTyped/blob/cd61f5b4d3d143108569ec3f88adc0eb34b961c4/types/node/readline.d.ts diff --git a/ext/node/polyfills/_stream.d.ts b/ext/node/polyfills/_stream.d.ts index d251d9bee2..6ae3494118 100644 --- a/ext/node/polyfills/_stream.d.ts +++ b/ext/node/polyfills/_stream.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any // Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/4f538975138678878fed5b2555c0672aa578ab7d/types/node/stream.d.ts diff --git a/ext/node/polyfills/_stream.mjs b/ext/node/polyfills/_stream.mjs index 02640abcd9..3fc79b69df 100644 --- a/ext/node/polyfills/_stream.mjs +++ b/ext/node/polyfills/_stream.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-fmt-ignore-file // deno-lint-ignore-file diff --git a/ext/node/polyfills/_tls_common.ts b/ext/node/polyfills/_tls_common.ts index d00c3629e1..4e5904b971 100644 --- a/ext/node/polyfills/_tls_common.ts +++ b/ext/node/polyfills/_tls_common.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file no-explicit-any diff --git a/ext/node/polyfills/_tls_wrap.ts b/ext/node/polyfills/_tls_wrap.ts index 9e5def9f2b..0edea1c053 100644 --- a/ext/node/polyfills/_tls_wrap.ts +++ b/ext/node/polyfills/_tls_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/_util/_util_callbackify.js b/ext/node/polyfills/_util/_util_callbackify.js index cb30f79150..ae83850bc0 100644 --- a/ext/node/polyfills/_util/_util_callbackify.js +++ b/ext/node/polyfills/_util/_util_callbackify.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. // diff --git a/ext/node/polyfills/_util/asserts.ts b/ext/node/polyfills/_util/asserts.ts index 8f90cf168a..71c3cc863d 100644 --- a/ext/node/polyfills/_util/asserts.ts +++ b/ext/node/polyfills/_util/asserts.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/_util/async.ts b/ext/node/polyfills/_util/async.ts index 0cacccacc7..445febcaaf 100644 --- a/ext/node/polyfills/_util/async.ts +++ b/ext/node/polyfills/_util/async.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is vendored from std/async/delay.ts // (with some modifications) diff --git a/ext/node/polyfills/_util/os.ts b/ext/node/polyfills/_util/os.ts index 421d5d9da0..f72dc662d9 100644 --- a/ext/node/polyfills/_util/os.ts +++ b/ext/node/polyfills/_util/os.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_build_os } from "ext:core/ops"; diff --git a/ext/node/polyfills/_util/std_asserts.ts b/ext/node/polyfills/_util/std_asserts.ts index 78b749f549..7623741379 100644 --- a/ext/node/polyfills/_util/std_asserts.ts +++ b/ext/node/polyfills/_util/std_asserts.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // vendored from std/assert/mod.ts import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_util/std_fmt_colors.ts b/ext/node/polyfills/_util/std_fmt_colors.ts index 5072b298ee..f1dc97f6ca 100644 --- a/ext/node/polyfills/_util/std_fmt_colors.ts +++ b/ext/node/polyfills/_util/std_fmt_colors.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file is vendored from std/fmt/colors.ts import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_util/std_testing_diff.ts b/ext/node/polyfills/_util/std_testing_diff.ts index 5155fd242c..1b680c6cca 100644 --- a/ext/node/polyfills/_util/std_testing_diff.ts +++ b/ext/node/polyfills/_util/std_testing_diff.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file was vendored from std/testing/_diff.ts import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/_utils.ts b/ext/node/polyfills/_utils.ts index 79d84e00f0..7f20782e2b 100644 --- a/ext/node/polyfills/_utils.ts +++ b/ext/node/polyfills/_utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/_zlib.mjs b/ext/node/polyfills/_zlib.mjs index 07fc440ef5..bb6bac1bd0 100644 --- a/ext/node/polyfills/_zlib.mjs +++ b/ext/node/polyfills/_zlib.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright (c) 2014-2015 Devon Govett // Forked from https://github.com/browserify/browserify-zlib diff --git a/ext/node/polyfills/_zlib_binding.mjs b/ext/node/polyfills/_zlib_binding.mjs index 069b7f8351..37125b88ac 100644 --- a/ext/node/polyfills/_zlib_binding.mjs +++ b/ext/node/polyfills/_zlib_binding.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/assert.ts b/ext/node/polyfills/assert.ts index 188c7a0c2f..48b4627044 100644 --- a/ext/node/polyfills/assert.ts +++ b/ext/node/polyfills/assert.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file ban-types prefer-primordials diff --git a/ext/node/polyfills/assert/strict.ts b/ext/node/polyfills/assert/strict.ts index 9837167627..883deaf087 100644 --- a/ext/node/polyfills/assert/strict.ts +++ b/ext/node/polyfills/assert/strict.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { strict } from "node:assert"; export { diff --git a/ext/node/polyfills/assertion_error.ts b/ext/node/polyfills/assertion_error.ts index ff1168dc30..b56cec2c1d 100644 --- a/ext/node/polyfills/assertion_error.ts +++ b/ext/node/polyfills/assertion_error.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. diff --git a/ext/node/polyfills/async_hooks.ts b/ext/node/polyfills/async_hooks.ts index 7a2f153dac..e01110bd3b 100644 --- a/ext/node/polyfills/async_hooks.ts +++ b/ext/node/polyfills/async_hooks.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/buffer.ts b/ext/node/polyfills/buffer.ts index efe3b07a97..38000c6edb 100644 --- a/ext/node/polyfills/buffer.ts +++ b/ext/node/polyfills/buffer.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @deno-types="./internal/buffer.d.ts" export { atob, diff --git a/ext/node/polyfills/child_process.ts b/ext/node/polyfills/child_process.ts index eda718ff34..184b29bd2b 100644 --- a/ext/node/polyfills/child_process.ts +++ b/ext/node/polyfills/child_process.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module implements 'child_process' module of Node.JS API. // ref: https://nodejs.org/api/child_process.html diff --git a/ext/node/polyfills/cluster.ts b/ext/node/polyfills/cluster.ts index 8abc2fedcd..67534676b4 100644 --- a/ext/node/polyfills/cluster.ts +++ b/ext/node/polyfills/cluster.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; diff --git a/ext/node/polyfills/console.ts b/ext/node/polyfills/console.ts index f74c9af165..1189d95064 100644 --- a/ext/node/polyfills/console.ts +++ b/ext/node/polyfills/console.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { Console } from "ext:deno_node/internal/console/constructor.mjs"; diff --git a/ext/node/polyfills/constants.ts b/ext/node/polyfills/constants.ts index e5004039b1..421c8b641a 100644 --- a/ext/node/polyfills/constants.ts +++ b/ext/node/polyfills/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Based on: https://github.com/nodejs/node/blob/0646eda/lib/constants.js diff --git a/ext/node/polyfills/crypto.ts b/ext/node/polyfills/crypto.ts index 908d21b006..ff3bdc7752 100644 --- a/ext/node/polyfills/crypto.ts +++ b/ext/node/polyfills/crypto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/dgram.ts b/ext/node/polyfills/dgram.ts index 99f4940ec2..56fb8d8f32 100644 --- a/ext/node/polyfills/dgram.ts +++ b/ext/node/polyfills/dgram.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/diagnostics_channel.js b/ext/node/polyfills/diagnostics_channel.js index 807c33e475..65533e0803 100644 --- a/ext/node/polyfills/diagnostics_channel.js +++ b/ext/node/polyfills/diagnostics_channel.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/dns.ts b/ext/node/polyfills/dns.ts index 78b934e602..b8e4af6eec 100644 --- a/ext/node/polyfills/dns.ts +++ b/ext/node/polyfills/dns.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/dns/promises.ts b/ext/node/polyfills/dns/promises.ts index 6bc539cba9..422ae05c64 100644 --- a/ext/node/polyfills/dns/promises.ts +++ b/ext/node/polyfills/dns/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/domain.ts b/ext/node/polyfills/domain.ts index 7093779966..daa613ede0 100644 --- a/ext/node/polyfills/domain.ts +++ b/ext/node/polyfills/domain.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // This code has been inspired by https://github.com/bevry/domain-browser/commit/8bce7f4a093966ca850da75b024239ad5d0b33c6 diff --git a/ext/node/polyfills/events.ts b/ext/node/polyfills/events.ts index 78f3d8768c..891f3d3b8d 100644 --- a/ext/node/polyfills/events.ts +++ b/ext/node/polyfills/events.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @deno-types="./_events.d.ts" export { addAbortListener, diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts index cbdc36afe5..76ff9ebd12 100644 --- a/ext/node/polyfills/fs.ts +++ b/ext/node/polyfills/fs.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { access, accessPromise, diff --git a/ext/node/polyfills/fs/promises.ts b/ext/node/polyfills/fs/promises.ts index a5125dac8d..f8d63e94e0 100644 --- a/ext/node/polyfills/fs/promises.ts +++ b/ext/node/polyfills/fs/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { promises as fsPromises } from "node:fs"; export const access = fsPromises.access; diff --git a/ext/node/polyfills/http.ts b/ext/node/polyfills/http.ts index e911535be5..f698ca01b3 100644 --- a/ext/node/polyfills/http.ts +++ b/ext/node/polyfills/http.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/http2.ts b/ext/node/polyfills/http2.ts index 1b3f74f6f6..ab2faa2d08 100644 --- a/ext/node/polyfills/http2.ts +++ b/ext/node/polyfills/http2.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/https.ts b/ext/node/polyfills/https.ts index fd700173eb..d958d8e746 100644 --- a/ext/node/polyfills/https.ts +++ b/ext/node/polyfills/https.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/inspector.js b/ext/node/polyfills/inspector.js index 7eb15ce917..bb2661b9cd 100644 --- a/ext/node/polyfills/inspector.js +++ b/ext/node/polyfills/inspector.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import process from "node:process"; diff --git a/ext/node/polyfills/inspector/promises.js b/ext/node/polyfills/inspector/promises.js index 3483e53f5e..a075eba001 100644 --- a/ext/node/polyfills/inspector/promises.js +++ b/ext/node/polyfills/inspector/promises.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import inspector from "node:inspector"; diff --git a/ext/node/polyfills/internal/assert.mjs b/ext/node/polyfills/internal/assert.mjs index 20ed511e02..01b5fad460 100644 --- a/ext/node/polyfills/internal/assert.mjs +++ b/ext/node/polyfills/internal/assert.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { ERR_INTERNAL_ASSERTION } from "ext:deno_node/internal/errors.ts"; function assert(value, message) { diff --git a/ext/node/polyfills/internal/async_hooks.ts b/ext/node/polyfills/internal/async_hooks.ts index a5d6728e33..a2f52e6979 100644 --- a/ext/node/polyfills/internal/async_hooks.ts +++ b/ext/node/polyfills/internal/async_hooks.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/blocklist.mjs b/ext/node/polyfills/internal/blocklist.mjs index 8a7c9c376a..d45592a8ff 100644 --- a/ext/node/polyfills/internal/blocklist.mjs +++ b/ext/node/polyfills/internal/blocklist.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/buffer.d.ts b/ext/node/polyfills/internal/buffer.d.ts index 2008dc2432..125deb0ef2 100644 --- a/ext/node/polyfills/internal/buffer.d.ts +++ b/ext/node/polyfills/internal/buffer.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright DefinitelyTyped contributors. All rights reserved. MIT license. /** diff --git a/ext/node/polyfills/internal/buffer.mjs b/ext/node/polyfills/internal/buffer.mjs index dd549221fa..033d8a1e1e 100644 --- a/ext/node/polyfills/internal/buffer.mjs +++ b/ext/node/polyfills/internal/buffer.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // Copyright Feross Aboukhadijeh, and other contributors. All rights reserved. MIT license. diff --git a/ext/node/polyfills/internal/child_process.ts b/ext/node/polyfills/internal/child_process.ts index cfff1079ff..17809cc559 100644 --- a/ext/node/polyfills/internal/child_process.ts +++ b/ext/node/polyfills/internal/child_process.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module implements 'child_process' module of Node.JS API. // ref: https://nodejs.org/api/child_process.html diff --git a/ext/node/polyfills/internal/cli_table.ts b/ext/node/polyfills/internal/cli_table.ts index 9826e524f6..c6991ed06d 100644 --- a/ext/node/polyfills/internal/cli_table.ts +++ b/ext/node/polyfills/internal/cli_table.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/console/constructor.mjs b/ext/node/polyfills/internal/console/constructor.mjs index ebf5cbec4f..0736fd4c47 100644 --- a/ext/node/polyfills/internal/console/constructor.mjs +++ b/ext/node/polyfills/internal/console/constructor.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/constants.ts b/ext/node/polyfills/internal/constants.ts index 521cea987c..3c83c0f0ef 100644 --- a/ext/node/polyfills/internal/constants.ts +++ b/ext/node/polyfills/internal/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { isWindows } from "ext:deno_node/_util/os.ts"; diff --git a/ext/node/polyfills/internal/crypto/_keys.ts b/ext/node/polyfills/internal/crypto/_keys.ts index e799862458..9a097f82e3 100644 --- a/ext/node/polyfills/internal/crypto/_keys.ts +++ b/ext/node/polyfills/internal/crypto/_keys.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file is here because to break a circular dependency between streams and // crypto. diff --git a/ext/node/polyfills/internal/crypto/_randomBytes.ts b/ext/node/polyfills/internal/crypto/_randomBytes.ts index e1dc5c5ac8..c8608a7c53 100644 --- a/ext/node/polyfills/internal/crypto/_randomBytes.ts +++ b/ext/node/polyfills/internal/crypto/_randomBytes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/crypto/_randomFill.mjs b/ext/node/polyfills/internal/crypto/_randomFill.mjs index 8ef864562f..20a6e1c022 100644 --- a/ext/node/polyfills/internal/crypto/_randomFill.mjs +++ b/ext/node/polyfills/internal/crypto/_randomFill.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/crypto/_randomInt.ts b/ext/node/polyfills/internal/crypto/_randomInt.ts index e08b3e9639..65a8bacf13 100644 --- a/ext/node/polyfills/internal/crypto/_randomInt.ts +++ b/ext/node/polyfills/internal/crypto/_randomInt.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_random_int } from "ext:core/ops"; import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/crypto/certificate.ts b/ext/node/polyfills/internal/crypto/certificate.ts index c15236c5cb..cd64ba36d9 100644 --- a/ext/node/polyfills/internal/crypto/certificate.ts +++ b/ext/node/polyfills/internal/crypto/certificate.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; diff --git a/ext/node/polyfills/internal/crypto/cipher.ts b/ext/node/polyfills/internal/crypto/cipher.ts index c1c5ce8901..dd1698f46e 100644 --- a/ext/node/polyfills/internal/crypto/cipher.ts +++ b/ext/node/polyfills/internal/crypto/cipher.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/constants.ts b/ext/node/polyfills/internal/crypto/constants.ts index d9c0b2d63b..77fe9bbf43 100644 --- a/ext/node/polyfills/internal/crypto/constants.ts +++ b/ext/node/polyfills/internal/crypto/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/diffiehellman.ts b/ext/node/polyfills/internal/crypto/diffiehellman.ts index a439306a97..fb0da4e60b 100644 --- a/ext/node/polyfills/internal/crypto/diffiehellman.ts +++ b/ext/node/polyfills/internal/crypto/diffiehellman.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/hash.ts b/ext/node/polyfills/internal/crypto/hash.ts index c42ca39892..3b3ec928fb 100644 --- a/ext/node/polyfills/internal/crypto/hash.ts +++ b/ext/node/polyfills/internal/crypto/hash.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/hkdf.ts b/ext/node/polyfills/internal/crypto/hkdf.ts index cb1dbee46b..282633ba26 100644 --- a/ext/node/polyfills/internal/crypto/hkdf.ts +++ b/ext/node/polyfills/internal/crypto/hkdf.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index b023ab1060..8d9a98e324 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/keys.ts b/ext/node/polyfills/internal/crypto/keys.ts index 932856df0e..a6ef6b156d 100644 --- a/ext/node/polyfills/internal/crypto/keys.ts +++ b/ext/node/polyfills/internal/crypto/keys.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/pbkdf2.ts b/ext/node/polyfills/internal/crypto/pbkdf2.ts index 4e58cb68b3..84a3032c75 100644 --- a/ext/node/polyfills/internal/crypto/pbkdf2.ts +++ b/ext/node/polyfills/internal/crypto/pbkdf2.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/crypto/random.ts b/ext/node/polyfills/internal/crypto/random.ts index a41b868190..9d8864aabc 100644 --- a/ext/node/polyfills/internal/crypto/random.ts +++ b/ext/node/polyfills/internal/crypto/random.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/scrypt.ts b/ext/node/polyfills/internal/crypto/scrypt.ts index ce8649bbe2..e20ad33011 100644 --- a/ext/node/polyfills/internal/crypto/scrypt.ts +++ b/ext/node/polyfills/internal/crypto/scrypt.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /* MIT License diff --git a/ext/node/polyfills/internal/crypto/sig.ts b/ext/node/polyfills/internal/crypto/sig.ts index a05f16478d..d75f522f37 100644 --- a/ext/node/polyfills/internal/crypto/sig.ts +++ b/ext/node/polyfills/internal/crypto/sig.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/types.ts b/ext/node/polyfills/internal/crypto/types.ts index 17b15127ef..60f913ca2d 100644 --- a/ext/node/polyfills/internal/crypto/types.ts +++ b/ext/node/polyfills/internal/crypto/types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { BufferEncoding } from "ext:deno_node/_global.d.ts"; diff --git a/ext/node/polyfills/internal/crypto/util.ts b/ext/node/polyfills/internal/crypto/util.ts index 6c925f6577..7e6f4a9b40 100644 --- a/ext/node/polyfills/internal/crypto/util.ts +++ b/ext/node/polyfills/internal/crypto/util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/crypto/x509.ts b/ext/node/polyfills/internal/crypto/x509.ts index 699e45a51b..a37db7365e 100644 --- a/ext/node/polyfills/internal/crypto/x509.ts +++ b/ext/node/polyfills/internal/crypto/x509.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/dgram.ts b/ext/node/polyfills/internal/dgram.ts index e34f92f1ea..d69baa7ef2 100644 --- a/ext/node/polyfills/internal/dgram.ts +++ b/ext/node/polyfills/internal/dgram.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/dns/promises.ts b/ext/node/polyfills/internal/dns/promises.ts index ee4248163d..b431c4502f 100644 --- a/ext/node/polyfills/internal/dns/promises.ts +++ b/ext/node/polyfills/internal/dns/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/dns/utils.ts b/ext/node/polyfills/internal/dns/utils.ts index 226fce93dd..a405d3f509 100644 --- a/ext/node/polyfills/internal/dns/utils.ts +++ b/ext/node/polyfills/internal/dns/utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/dtrace.ts b/ext/node/polyfills/internal/dtrace.ts index 3443aec7ff..716c48c7cc 100644 --- a/ext/node/polyfills/internal/dtrace.ts +++ b/ext/node/polyfills/internal/dtrace.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/error_codes.ts b/ext/node/polyfills/internal/error_codes.ts index 0603e9e114..638c0584c2 100644 --- a/ext/node/polyfills/internal/error_codes.ts +++ b/ext/node/polyfills/internal/error_codes.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Lazily initializes the error classes in this object. // This trick is necessary for avoiding circular dendencies between diff --git a/ext/node/polyfills/internal/errors.ts b/ext/node/polyfills/internal/errors.ts index d79232aed7..5d35f07dd8 100644 --- a/ext/node/polyfills/internal/errors.ts +++ b/ext/node/polyfills/internal/errors.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Node.js contributors. All rights reserved. MIT License. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/event_target.mjs b/ext/node/polyfills/internal/event_target.mjs index 4409b00ed4..80c6041c57 100644 --- a/ext/node/polyfills/internal/event_target.mjs +++ b/ext/node/polyfills/internal/event_target.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Node.js contributors. All rights reserved. MIT License. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/events/abort_listener.mjs b/ext/node/polyfills/internal/events/abort_listener.mjs index f1430489fa..0407b93595 100644 --- a/ext/node/polyfills/internal/events/abort_listener.mjs +++ b/ext/node/polyfills/internal/events/abort_listener.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { primordials } from "ext:deno_node/internal/test/binding.ts"; diff --git a/ext/node/polyfills/internal/fixed_queue.ts b/ext/node/polyfills/internal/fixed_queue.ts index 0a2209c8d5..d9355e73c5 100644 --- a/ext/node/polyfills/internal/fixed_queue.ts +++ b/ext/node/polyfills/internal/fixed_queue.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/fs/handle.ts b/ext/node/polyfills/internal/fs/handle.ts index ee035f2f5c..ace9a7635c 100644 --- a/ext/node/polyfills/internal/fs/handle.ts +++ b/ext/node/polyfills/internal/fs/handle.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/fs/streams.d.ts b/ext/node/polyfills/internal/fs/streams.d.ts index b4c235f9b5..8459120573 100644 --- a/ext/node/polyfills/internal/fs/streams.d.ts +++ b/ext/node/polyfills/internal/fs/streams.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright DefinitelyTyped contributors. All rights reserved. MIT license. // deno-lint-ignore-file no-explicit-any diff --git a/ext/node/polyfills/internal/fs/streams.mjs b/ext/node/polyfills/internal/fs/streams.mjs index 3b4409c4cf..5adb8da225 100644 --- a/ext/node/polyfills/internal/fs/streams.mjs +++ b/ext/node/polyfills/internal/fs/streams.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/fs/utils.mjs b/ext/node/polyfills/internal/fs/utils.mjs index b0ef34873e..9e8558dea2 100644 --- a/ext/node/polyfills/internal/fs/utils.mjs +++ b/ext/node/polyfills/internal/fs/utils.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/hide_stack_frames.ts b/ext/node/polyfills/internal/hide_stack_frames.ts index 4145189fff..93d7aca095 100644 --- a/ext/node/polyfills/internal/hide_stack_frames.ts +++ b/ext/node/polyfills/internal/hide_stack_frames.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/http.ts b/ext/node/polyfills/internal/http.ts index ddf082f451..d559442bbc 100644 --- a/ext/node/polyfills/internal/http.ts +++ b/ext/node/polyfills/internal/http.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/idna.ts b/ext/node/polyfills/internal/idna.ts index 93ed065cce..8a53b7e721 100644 --- a/ext/node/polyfills/internal/idna.ts +++ b/ext/node/polyfills/internal/idna.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/net.ts b/ext/node/polyfills/internal/net.ts index a3dcb3ed21..a14dcae9b8 100644 --- a/ext/node/polyfills/internal/net.ts +++ b/ext/node/polyfills/internal/net.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/normalize_encoding.mjs b/ext/node/polyfills/internal/normalize_encoding.mjs index a367337880..6ae322f118 100644 --- a/ext/node/polyfills/internal/normalize_encoding.mjs +++ b/ext/node/polyfills/internal/normalize_encoding.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/options.ts b/ext/node/polyfills/internal/options.ts index 4b62a11285..16ac62a69f 100644 --- a/ext/node/polyfills/internal/options.ts +++ b/ext/node/polyfills/internal/options.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/primordials.mjs b/ext/node/polyfills/internal/primordials.mjs index cddd1aa66d..027994a3d2 100644 --- a/ext/node/polyfills/internal/primordials.mjs +++ b/ext/node/polyfills/internal/primordials.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/process/per_thread.mjs b/ext/node/polyfills/internal/process/per_thread.mjs index b4db3f3691..9d784be153 100644 --- a/ext/node/polyfills/internal/process/per_thread.mjs +++ b/ext/node/polyfills/internal/process/per_thread.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/process/report.ts b/ext/node/polyfills/internal/process/report.ts index c4a1ff5616..5374c449fc 100644 --- a/ext/node/polyfills/internal/process/report.ts +++ b/ext/node/polyfills/internal/process/report.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/internal/querystring.ts b/ext/node/polyfills/internal/querystring.ts index a34e4e4b76..1824203c60 100644 --- a/ext/node/polyfills/internal/querystring.ts +++ b/ext/node/polyfills/internal/querystring.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/readline/callbacks.mjs b/ext/node/polyfills/internal/readline/callbacks.mjs index 5e3d7382fc..4f860776e0 100644 --- a/ext/node/polyfills/internal/readline/callbacks.mjs +++ b/ext/node/polyfills/internal/readline/callbacks.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs b/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs index e016fd1533..27a010b420 100644 --- a/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs +++ b/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/readline/interface.mjs b/ext/node/polyfills/internal/readline/interface.mjs index bdb832f881..9988f6c46b 100644 --- a/ext/node/polyfills/internal/readline/interface.mjs +++ b/ext/node/polyfills/internal/readline/interface.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/readline/promises.mjs b/ext/node/polyfills/internal/readline/promises.mjs index f1de6a2d31..21ab8b6d93 100644 --- a/ext/node/polyfills/internal/readline/promises.mjs +++ b/ext/node/polyfills/internal/readline/promises.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/readline/symbols.mjs b/ext/node/polyfills/internal/readline/symbols.mjs index 299cc8c9a7..7b935d1243 100644 --- a/ext/node/polyfills/internal/readline/symbols.mjs +++ b/ext/node/polyfills/internal/readline/symbols.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/readline/utils.mjs b/ext/node/polyfills/internal/readline/utils.mjs index b9372ab0ea..495c373638 100644 --- a/ext/node/polyfills/internal/readline/utils.mjs +++ b/ext/node/polyfills/internal/readline/utils.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/stream_base_commons.ts b/ext/node/polyfills/internal/stream_base_commons.ts index 1fabb73f47..050658d4f5 100644 --- a/ext/node/polyfills/internal/stream_base_commons.ts +++ b/ext/node/polyfills/internal/stream_base_commons.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/streams/add-abort-signal.mjs b/ext/node/polyfills/internal/streams/add-abort-signal.mjs index 2e66c86643..d6a3ca099e 100644 --- a/ext/node/polyfills/internal/streams/add-abort-signal.mjs +++ b/ext/node/polyfills/internal/streams/add-abort-signal.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/buffer_list.mjs b/ext/node/polyfills/internal/streams/buffer_list.mjs index db3a703388..cb9dba563b 100644 --- a/ext/node/polyfills/internal/streams/buffer_list.mjs +++ b/ext/node/polyfills/internal/streams/buffer_list.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/destroy.mjs b/ext/node/polyfills/internal/streams/destroy.mjs index 27420e78bf..6e60e20ace 100644 --- a/ext/node/polyfills/internal/streams/destroy.mjs +++ b/ext/node/polyfills/internal/streams/destroy.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/duplex.mjs b/ext/node/polyfills/internal/streams/duplex.mjs index b7c0b077ff..0424c3d17b 100644 --- a/ext/node/polyfills/internal/streams/duplex.mjs +++ b/ext/node/polyfills/internal/streams/duplex.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/end-of-stream.mjs b/ext/node/polyfills/internal/streams/end-of-stream.mjs index aebdb90bf3..be9bf0a50c 100644 --- a/ext/node/polyfills/internal/streams/end-of-stream.mjs +++ b/ext/node/polyfills/internal/streams/end-of-stream.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/lazy_transform.mjs b/ext/node/polyfills/internal/streams/lazy_transform.mjs index 3033ee6d4c..f5cf8ac48a 100644 --- a/ext/node/polyfills/internal/streams/lazy_transform.mjs +++ b/ext/node/polyfills/internal/streams/lazy_transform.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/passthrough.mjs b/ext/node/polyfills/internal/streams/passthrough.mjs index 8c7d4f3560..aa3f26a52c 100644 --- a/ext/node/polyfills/internal/streams/passthrough.mjs +++ b/ext/node/polyfills/internal/streams/passthrough.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/readable.mjs b/ext/node/polyfills/internal/streams/readable.mjs index c5411d6451..7c291f3932 100644 --- a/ext/node/polyfills/internal/streams/readable.mjs +++ b/ext/node/polyfills/internal/streams/readable.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/state.mjs b/ext/node/polyfills/internal/streams/state.mjs index 428492306b..599620ce7a 100644 --- a/ext/node/polyfills/internal/streams/state.mjs +++ b/ext/node/polyfills/internal/streams/state.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/transform.mjs b/ext/node/polyfills/internal/streams/transform.mjs index af368365cb..ac2bf6acc9 100644 --- a/ext/node/polyfills/internal/streams/transform.mjs +++ b/ext/node/polyfills/internal/streams/transform.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/utils.mjs b/ext/node/polyfills/internal/streams/utils.mjs index a2506bd5de..65551b129d 100644 --- a/ext/node/polyfills/internal/streams/utils.mjs +++ b/ext/node/polyfills/internal/streams/utils.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/streams/writable.mjs b/ext/node/polyfills/internal/streams/writable.mjs index c02e4e0e1d..7f1679e372 100644 --- a/ext/node/polyfills/internal/streams/writable.mjs +++ b/ext/node/polyfills/internal/streams/writable.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/test/binding.ts b/ext/node/polyfills/internal/test/binding.ts index cce56b922c..bbd78ec471 100644 --- a/ext/node/polyfills/internal/test/binding.ts +++ b/ext/node/polyfills/internal/test/binding.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { getBinding } from "ext:deno_node/internal_binding/mod.ts"; import type { BindingName } from "ext:deno_node/internal_binding/mod.ts"; diff --git a/ext/node/polyfills/internal/timers.mjs b/ext/node/polyfills/internal/timers.mjs index 363f55f693..84dd165e26 100644 --- a/ext/node/polyfills/internal/timers.mjs +++ b/ext/node/polyfills/internal/timers.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/url.ts b/ext/node/polyfills/internal/url.ts index 4b9da49ec0..82d7a8b4bf 100644 --- a/ext/node/polyfills/internal/url.ts +++ b/ext/node/polyfills/internal/url.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/util.mjs b/ext/node/polyfills/internal/util.mjs index a603a975ad..91de868920 100644 --- a/ext/node/polyfills/internal/util.mjs +++ b/ext/node/polyfills/internal/util.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal/util/comparisons.ts b/ext/node/polyfills/internal/util/comparisons.ts index 39e30c69aa..44f0bef007 100644 --- a/ext/node/polyfills/internal/util/comparisons.ts +++ b/ext/node/polyfills/internal/util/comparisons.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // deno-lint-ignore-file diff --git a/ext/node/polyfills/internal/util/debuglog.ts b/ext/node/polyfills/internal/util/debuglog.ts index bbaba7b6fc..dc05aff825 100644 --- a/ext/node/polyfills/internal/util/debuglog.ts +++ b/ext/node/polyfills/internal/util/debuglog.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal/util/inspect.mjs b/ext/node/polyfills/internal/util/inspect.mjs index ae797449bf..0587059122 100644 --- a/ext/node/polyfills/internal/util/inspect.mjs +++ b/ext/node/polyfills/internal/util/inspect.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal/util/parse_args/parse_args.js b/ext/node/polyfills/internal/util/parse_args/parse_args.js index 742f2a1f0d..c4a60bcd35 100644 --- a/ext/node/polyfills/internal/util/parse_args/parse_args.js +++ b/ext/node/polyfills/internal/util/parse_args/parse_args.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/util/parse_args/utils.js b/ext/node/polyfills/internal/util/parse_args/utils.js index 1ceed0b9e8..dd76fa1512 100644 --- a/ext/node/polyfills/internal/util/parse_args/utils.js +++ b/ext/node/polyfills/internal/util/parse_args/utils.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/internal/util/types.ts b/ext/node/polyfills/internal/util/types.ts index b8aca59680..9415c9db5f 100644 --- a/ext/node/polyfills/internal/util/types.ts +++ b/ext/node/polyfills/internal/util/types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. // diff --git a/ext/node/polyfills/internal/validators.mjs b/ext/node/polyfills/internal/validators.mjs index 12962b8432..0529dca546 100644 --- a/ext/node/polyfills/internal/validators.mjs +++ b/ext/node/polyfills/internal/validators.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/internal_binding/_libuv_winerror.ts b/ext/node/polyfills/internal_binding/_libuv_winerror.ts index 3ba7d9cdae..b84c60226c 100644 --- a/ext/node/polyfills/internal_binding/_libuv_winerror.ts +++ b/ext/node/polyfills/internal_binding/_libuv_winerror.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_sys_to_uv_error } from "ext:core/ops"; diff --git a/ext/node/polyfills/internal_binding/_listen.ts b/ext/node/polyfills/internal_binding/_listen.ts index 613fdd999d..06e13af79f 100644 --- a/ext/node/polyfills/internal_binding/_listen.ts +++ b/ext/node/polyfills/internal_binding/_listen.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal_binding/_node.ts b/ext/node/polyfills/internal_binding/_node.ts index bf6b776130..1e4f1d257e 100644 --- a/ext/node/polyfills/internal_binding/_node.ts +++ b/ext/node/polyfills/internal_binding/_node.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file contains C++ node globals accessed in internal binding calls /** diff --git a/ext/node/polyfills/internal_binding/_timingSafeEqual.ts b/ext/node/polyfills/internal_binding/_timingSafeEqual.ts index d9811c5505..f24e70b723 100644 --- a/ext/node/polyfills/internal_binding/_timingSafeEqual.ts +++ b/ext/node/polyfills/internal_binding/_timingSafeEqual.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal_binding/_utils.ts b/ext/node/polyfills/internal_binding/_utils.ts index 74dc3cbcd6..c3e5ca7adb 100644 --- a/ext/node/polyfills/internal_binding/_utils.ts +++ b/ext/node/polyfills/internal_binding/_utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal_binding/ares.ts b/ext/node/polyfills/internal_binding/ares.ts index 46afdfb195..807617aa94 100644 --- a/ext/node/polyfills/internal_binding/ares.ts +++ b/ext/node/polyfills/internal_binding/ares.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /* Copyright 1998 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this diff --git a/ext/node/polyfills/internal_binding/async_wrap.ts b/ext/node/polyfills/internal_binding/async_wrap.ts index affee03fab..4480e1a0a7 100644 --- a/ext/node/polyfills/internal_binding/async_wrap.ts +++ b/ext/node/polyfills/internal_binding/async_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/buffer.ts b/ext/node/polyfills/internal_binding/buffer.ts index 4b8e5388b2..7a543e6f03 100644 --- a/ext/node/polyfills/internal_binding/buffer.ts +++ b/ext/node/polyfills/internal_binding/buffer.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal_binding/cares_wrap.ts b/ext/node/polyfills/internal_binding/cares_wrap.ts index cbd0bb8ef6..6a864e5855 100644 --- a/ext/node/polyfills/internal_binding/cares_wrap.ts +++ b/ext/node/polyfills/internal_binding/cares_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/connection_wrap.ts b/ext/node/polyfills/internal_binding/connection_wrap.ts index ef07025e29..036d7a3e5b 100644 --- a/ext/node/polyfills/internal_binding/connection_wrap.ts +++ b/ext/node/polyfills/internal_binding/connection_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/constants.ts b/ext/node/polyfills/internal_binding/constants.ts index ccb0ba5702..5e1afb7222 100644 --- a/ext/node/polyfills/internal_binding/constants.ts +++ b/ext/node/polyfills/internal_binding/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_build_os } from "ext:core/ops"; diff --git a/ext/node/polyfills/internal_binding/crypto.ts b/ext/node/polyfills/internal_binding/crypto.ts index 3aabbbf344..e3d03a4b1a 100644 --- a/ext/node/polyfills/internal_binding/crypto.ts +++ b/ext/node/polyfills/internal_binding/crypto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; diff --git a/ext/node/polyfills/internal_binding/handle_wrap.ts b/ext/node/polyfills/internal_binding/handle_wrap.ts index 1b3036a7ef..61a34fbc06 100644 --- a/ext/node/polyfills/internal_binding/handle_wrap.ts +++ b/ext/node/polyfills/internal_binding/handle_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/http_parser.ts b/ext/node/polyfills/internal_binding/http_parser.ts index bad10d9851..ac087e24de 100644 --- a/ext/node/polyfills/internal_binding/http_parser.ts +++ b/ext/node/polyfills/internal_binding/http_parser.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/mod.ts b/ext/node/polyfills/internal_binding/mod.ts index ebbfc629f1..25a5a2c7c2 100644 --- a/ext/node/polyfills/internal_binding/mod.ts +++ b/ext/node/polyfills/internal_binding/mod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/internal_binding/node_file.ts b/ext/node/polyfills/internal_binding/node_file.ts index 6c134ec4bc..1e97ee5cf7 100644 --- a/ext/node/polyfills/internal_binding/node_file.ts +++ b/ext/node/polyfills/internal_binding/node_file.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/node_options.ts b/ext/node/polyfills/internal_binding/node_options.ts index ddacb28b6d..927fd39b94 100644 --- a/ext/node/polyfills/internal_binding/node_options.ts +++ b/ext/node/polyfills/internal_binding/node_options.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/pipe_wrap.ts b/ext/node/polyfills/internal_binding/pipe_wrap.ts index 3e366b3c76..3917be36ee 100644 --- a/ext/node/polyfills/internal_binding/pipe_wrap.ts +++ b/ext/node/polyfills/internal_binding/pipe_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/stream_wrap.ts b/ext/node/polyfills/internal_binding/stream_wrap.ts index 19c9357ce8..e66c737be0 100644 --- a/ext/node/polyfills/internal_binding/stream_wrap.ts +++ b/ext/node/polyfills/internal_binding/stream_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/string_decoder.ts b/ext/node/polyfills/internal_binding/string_decoder.ts index 7f8c954a35..4825ea0aa9 100644 --- a/ext/node/polyfills/internal_binding/string_decoder.ts +++ b/ext/node/polyfills/internal_binding/string_decoder.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Encodings } from "ext:deno_node/internal_binding/_node.ts"; const encodings = []; diff --git a/ext/node/polyfills/internal_binding/symbols.ts b/ext/node/polyfills/internal_binding/symbols.ts index 2b0656b0ea..affc63eb84 100644 --- a/ext/node/polyfills/internal_binding/symbols.ts +++ b/ext/node/polyfills/internal_binding/symbols.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/tcp_wrap.ts b/ext/node/polyfills/internal_binding/tcp_wrap.ts index d9f1c5356a..6c19e2dd29 100644 --- a/ext/node/polyfills/internal_binding/tcp_wrap.ts +++ b/ext/node/polyfills/internal_binding/tcp_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/types.ts b/ext/node/polyfills/internal_binding/types.ts index 2d301ba1b2..a0fd338600 100644 --- a/ext/node/polyfills/internal_binding/types.ts +++ b/ext/node/polyfills/internal_binding/types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // // Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. // diff --git a/ext/node/polyfills/internal_binding/udp_wrap.ts b/ext/node/polyfills/internal_binding/udp_wrap.ts index db6961ddb7..337dfe75c2 100644 --- a/ext/node/polyfills/internal_binding/udp_wrap.ts +++ b/ext/node/polyfills/internal_binding/udp_wrap.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/util.ts b/ext/node/polyfills/internal_binding/util.ts index c8fb32d8ca..9297e1b20c 100644 --- a/ext/node/polyfills/internal_binding/util.ts +++ b/ext/node/polyfills/internal_binding/util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/internal_binding/uv.ts b/ext/node/polyfills/internal_binding/uv.ts index 6cd70a7e85..cd868cb395 100644 --- a/ext/node/polyfills/internal_binding/uv.ts +++ b/ext/node/polyfills/internal_binding/uv.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/net.ts b/ext/node/polyfills/net.ts index b2b0c9857c..3f7603079e 100644 --- a/ext/node/polyfills/net.ts +++ b/ext/node/polyfills/net.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/os.ts b/ext/node/polyfills/os.ts index edc89ed2c3..bd4b285ba3 100644 --- a/ext/node/polyfills/os.ts +++ b/ext/node/polyfills/os.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/path.ts b/ext/node/polyfills/path.ts index 19ba2e26d3..255f9592a0 100644 --- a/ext/node/polyfills/path.ts +++ b/ext/node/polyfills/path.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export * from "ext:deno_node/path/mod.ts"; import m from "ext:deno_node/path/mod.ts"; export default m; diff --git a/ext/node/polyfills/path/_constants.ts b/ext/node/polyfills/path/_constants.ts index 3407515169..b18f90601f 100644 --- a/ext/node/polyfills/path/_constants.ts +++ b/ext/node/polyfills/path/_constants.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Alphabet chars. export const CHAR_UPPERCASE_A = 65; /* A */ diff --git a/ext/node/polyfills/path/_interface.ts b/ext/node/polyfills/path/_interface.ts index cb40bae12e..e37933962b 100644 --- a/ext/node/polyfills/path/_interface.ts +++ b/ext/node/polyfills/path/_interface.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** * A parsed path object generated by path.parse() or consumed by path.format(). diff --git a/ext/node/polyfills/path/_posix.ts b/ext/node/polyfills/path/_posix.ts index bf0b91d488..6a4aa2117a 100644 --- a/ext/node/polyfills/path/_posix.ts +++ b/ext/node/polyfills/path/_posix.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/path/_util.ts b/ext/node/polyfills/path/_util.ts index 9248c68ae5..1e95faea41 100644 --- a/ext/node/polyfills/path/_util.ts +++ b/ext/node/polyfills/path/_util.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/path/_win32.ts b/ext/node/polyfills/path/_win32.ts index 11c82e0eee..70a0d8a006 100644 --- a/ext/node/polyfills/path/_win32.ts +++ b/ext/node/polyfills/path/_win32.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/path/common.ts b/ext/node/polyfills/path/common.ts index ee2987307e..9c1c91a650 100644 --- a/ext/node/polyfills/path/common.ts +++ b/ext/node/polyfills/path/common.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/path/mod.ts b/ext/node/polyfills/path/mod.ts index e74c1da4db..5fd5e34e57 100644 --- a/ext/node/polyfills/path/mod.ts +++ b/ext/node/polyfills/path/mod.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported mostly from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { isWindows } from "ext:deno_node/_util/os.ts"; import _win32 from "ext:deno_node/path/_win32.ts"; diff --git a/ext/node/polyfills/path/posix.ts b/ext/node/polyfills/path/posix.ts index 2b6582ff6d..474d74afaf 100644 --- a/ext/node/polyfills/path/posix.ts +++ b/ext/node/polyfills/path/posix.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import path from "ext:deno_node/path/mod.ts"; diff --git a/ext/node/polyfills/path/separator.ts b/ext/node/polyfills/path/separator.ts index 36dce7df99..bb9eaf7bb4 100644 --- a/ext/node/polyfills/path/separator.ts +++ b/ext/node/polyfills/path/separator.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/path/win32.ts b/ext/node/polyfills/path/win32.ts index 02e6f97c5a..740a346508 100644 --- a/ext/node/polyfills/path/win32.ts +++ b/ext/node/polyfills/path/win32.ts @@ -1,6 +1,6 @@ // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import path from "ext:deno_node/path/mod.ts"; diff --git a/ext/node/polyfills/perf_hooks.ts b/ext/node/polyfills/perf_hooks.ts index ec76b3ce2d..af4e7b261f 100644 --- a/ext/node/polyfills/perf_hooks.ts +++ b/ext/node/polyfills/perf_hooks.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 647376d5cf..e91a2ee005 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/punycode.ts b/ext/node/polyfills/punycode.ts index adecdf4f9a..6e29dd81f9 100644 --- a/ext/node/polyfills/punycode.ts +++ b/ext/node/polyfills/punycode.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_node_idna_punycode_decode, diff --git a/ext/node/polyfills/querystring.js b/ext/node/polyfills/querystring.js index 5eb6a077aa..206c3f5f82 100644 --- a/ext/node/polyfills/querystring.js +++ b/ext/node/polyfills/querystring.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials diff --git a/ext/node/polyfills/readline.ts b/ext/node/polyfills/readline.ts index 5813dd3d9b..6b7e51fe9d 100644 --- a/ext/node/polyfills/readline.ts +++ b/ext/node/polyfills/readline.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @deno-types="./_readline.d.ts" import { diff --git a/ext/node/polyfills/readline/promises.ts b/ext/node/polyfills/readline/promises.ts index 76c8b350d4..0e5ae6b8bb 100644 --- a/ext/node/polyfills/readline/promises.ts +++ b/ext/node/polyfills/readline/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/repl.ts b/ext/node/polyfills/repl.ts index a7acc5b19a..c0e69d93eb 100644 --- a/ext/node/polyfills/repl.ts +++ b/ext/node/polyfills/repl.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/stream.ts b/ext/node/polyfills/stream.ts index 96262428f0..2c9821dee6 100644 --- a/ext/node/polyfills/stream.ts +++ b/ext/node/polyfills/stream.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // compose, destroy and isDisturbed are experimental APIs without // typings. They can be exposed once they are released as stable in Node diff --git a/ext/node/polyfills/stream/consumers.mjs b/ext/node/polyfills/stream/consumers.mjs index dc5d29f611..5f436c3e17 100644 --- a/ext/node/polyfills/stream/consumers.mjs +++ b/ext/node/polyfills/stream/consumers.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/stream/promises.mjs b/ext/node/polyfills/stream/promises.mjs index 1282ca8963..007000d883 100644 --- a/ext/node/polyfills/stream/promises.mjs +++ b/ext/node/polyfills/stream/promises.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { Stream } from "ext:deno_node/_stream.mjs"; diff --git a/ext/node/polyfills/stream/web.ts b/ext/node/polyfills/stream/web.ts index 9cb361f862..c0594c0cb7 100644 --- a/ext/node/polyfills/stream/web.ts +++ b/ext/node/polyfills/stream/web.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { ByteLengthQueuingStrategy, diff --git a/ext/node/polyfills/string_decoder.ts b/ext/node/polyfills/string_decoder.ts index b4a422e4b6..f8dfe8d9a8 100644 --- a/ext/node/polyfills/string_decoder.ts +++ b/ext/node/polyfills/string_decoder.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/sys.ts b/ext/node/polyfills/sys.ts index 4ee112caea..e10f174b6d 100644 --- a/ext/node/polyfills/sys.ts +++ b/ext/node/polyfills/sys.ts @@ -1,3 +1,3 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export * from "node:util"; export { default } from "node:util"; diff --git a/ext/node/polyfills/testing.ts b/ext/node/polyfills/testing.ts index 901c38ed36..39014533a5 100644 --- a/ext/node/polyfills/testing.ts +++ b/ext/node/polyfills/testing.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { PromisePrototypeThen } = primordials; diff --git a/ext/node/polyfills/timers.ts b/ext/node/polyfills/timers.ts index fa5f7a2042..9ef704a0b3 100644 --- a/ext/node/polyfills/timers.ts +++ b/ext/node/polyfills/timers.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/timers/promises.ts b/ext/node/polyfills/timers/promises.ts index b2896fe809..2ec2327330 100644 --- a/ext/node/polyfills/timers/promises.ts +++ b/ext/node/polyfills/timers/promises.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import timers from "node:timers"; export const setTimeout = timers.promises.setTimeout; diff --git a/ext/node/polyfills/tls.ts b/ext/node/polyfills/tls.ts index 4cfe9ebd63..345994236e 100644 --- a/ext/node/polyfills/tls.ts +++ b/ext/node/polyfills/tls.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills diff --git a/ext/node/polyfills/trace_events.ts b/ext/node/polyfills/trace_events.ts index bb3ea97459..4df631a4c5 100644 --- a/ext/node/polyfills/trace_events.ts +++ b/ext/node/polyfills/trace_events.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts"; diff --git a/ext/node/polyfills/tty.js b/ext/node/polyfills/tty.js index e906c5f677..1545ab12ec 100644 --- a/ext/node/polyfills/tty.js +++ b/ext/node/polyfills/tty.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_bootstrap_color_depth } from "ext:core/ops"; import { core, primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/url.ts b/ext/node/polyfills/url.ts index 4eeb0381f6..b2744ff6cc 100644 --- a/ext/node/polyfills/url.ts +++ b/ext/node/polyfills/url.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/ext/node/polyfills/util.ts b/ext/node/polyfills/util.ts index d82b288b03..06c75ef79a 100644 --- a/ext/node/polyfills/util.ts +++ b/ext/node/polyfills/util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/ext/node/polyfills/util/types.ts b/ext/node/polyfills/util/types.ts index 5f2ead19bb..fbf9092d9d 100644 --- a/ext/node/polyfills/util/types.ts +++ b/ext/node/polyfills/util/types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as types from "ext:deno_node/internal/util/types.ts"; export * from "ext:deno_node/internal/util/types.ts"; export default { ...types }; diff --git a/ext/node/polyfills/v8.ts b/ext/node/polyfills/v8.ts index e24a79ab5a..6d7892a724 100644 --- a/ext/node/polyfills/v8.ts +++ b/ext/node/polyfills/v8.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. /// diff --git a/ext/node/polyfills/vm.js b/ext/node/polyfills/vm.js index b64c847c58..72279abcf8 100644 --- a/ext/node/polyfills/vm.js +++ b/ext/node/polyfills/vm.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. import { Buffer } from "node:buffer"; diff --git a/ext/node/polyfills/wasi.ts b/ext/node/polyfills/wasi.ts index 4bdc776a4c..b948b0e35c 100644 --- a/ext/node/polyfills/wasi.ts +++ b/ext/node/polyfills/wasi.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/worker_threads.ts b/ext/node/polyfills/worker_threads.ts index dc844169c5..87de9a5a5c 100644 --- a/ext/node/polyfills/worker_threads.ts +++ b/ext/node/polyfills/worker_threads.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; diff --git a/ext/node/polyfills/zlib.ts b/ext/node/polyfills/zlib.ts index 6e5d02b5be..08a9238bd5 100644 --- a/ext/node/polyfills/zlib.ts +++ b/ext/node/polyfills/zlib.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { notImplemented } from "ext:deno_node/_utils.ts"; import { zlib as constants } from "ext:deno_node/internal_binding/constants.ts"; import { diff --git a/ext/telemetry/Cargo.toml b/ext/telemetry/Cargo.toml index fedaed6656..4328b707d3 100644 --- a/ext/telemetry/Cargo.toml +++ b/ext/telemetry/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_telemetry" diff --git a/ext/telemetry/lib.rs b/ext/telemetry/lib.rs index ddf364a366..eeff09ed88 100644 --- a/ext/telemetry/lib.rs +++ b/ext/telemetry/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/telemetry/telemetry.ts b/ext/telemetry/telemetry.ts index 86b4fe059d..31e052c28b 100644 --- a/ext/telemetry/telemetry.ts +++ b/ext/telemetry/telemetry.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { diff --git a/ext/telemetry/util.ts b/ext/telemetry/util.ts index 7e30d5d859..ac233f7a9f 100644 --- a/ext/telemetry/util.ts +++ b/ext/telemetry/util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import type { Span } from "ext:deno_telemetry/telemetry.ts"; diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index 6bf1b8ea03..53d85ba83c 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_tls" diff --git a/ext/tls/lib.rs b/ext/tls/lib.rs index a16e66ff92..63e78e3f83 100644 --- a/ext/tls/lib.rs +++ b/ext/tls/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::BufRead; use std::io::BufReader; use std::io::Cursor; diff --git a/ext/tls/tls_key.rs b/ext/tls/tls_key.rs index a11a2d8106..dfd2863e5e 100644 --- a/ext/tls/tls_key.rs +++ b/ext/tls/tls_key.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! These represent the various types of TLS keys we support for both client and server //! connections. diff --git a/ext/url/00_url.js b/ext/url/00_url.js index ec875da768..c853430d1a 100644 --- a/ext/url/00_url.js +++ b/ext/url/00_url.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/url/01_urlpattern.js b/ext/url/01_urlpattern.js index 6e27563089..5febef3332 100644 --- a/ext/url/01_urlpattern.js +++ b/ext/url/01_urlpattern.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index 9ca3ce6752..4dcb6f42ca 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_url" diff --git a/ext/url/benches/url_ops.rs b/ext/url/benches/url_ops.rs index acde8be6bb..9295a08d51 100644 --- a/ext/url/benches/url_ops.rs +++ b/ext/url/benches/url_ops.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; diff --git a/ext/url/internal.d.ts b/ext/url/internal.d.ts index 11bacb0e1b..69e0472e8e 100644 --- a/ext/url/internal.d.ts +++ b/ext/url/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/ext/url/lib.deno_url.d.ts b/ext/url/lib.deno_url.d.ts index 946c70607f..08fe74cd66 100644 --- a/ext/url/lib.deno_url.d.ts +++ b/ext/url/lib.deno_url.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/url/lib.rs b/ext/url/lib.rs index d012d35998..0e9ca5839a 100644 --- a/ext/url/lib.rs +++ b/ext/url/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod urlpattern; diff --git a/ext/url/urlpattern.rs b/ext/url/urlpattern.rs index 2ba159b896..88564625da 100644 --- a/ext/url/urlpattern.rs +++ b/ext/url/urlpattern.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use urlpattern::quirks; diff --git a/ext/web/00_infra.js b/ext/web/00_infra.js index 9a75f8fa58..8ca42e86c2 100644 --- a/ext/web/00_infra.js +++ b/ext/web/00_infra.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/01_dom_exception.js b/ext/web/01_dom_exception.js index db2996e0c6..730fda860f 100644 --- a/ext/web/01_dom_exception.js +++ b/ext/web/01_dom_exception.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/01_mimesniff.js b/ext/web/01_mimesniff.js index e60783bbe1..9a687a8305 100644 --- a/ext/web/01_mimesniff.js +++ b/ext/web/01_mimesniff.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/02_event.js b/ext/web/02_event.js index f6351c4b9e..810e48537d 100644 --- a/ext/web/02_event.js +++ b/ext/web/02_event.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module follows most of the WHATWG Living Standard for the DOM logic. // Many parts of the DOM are not implemented in Deno, but the logic for those diff --git a/ext/web/02_structured_clone.js b/ext/web/02_structured_clone.js index 776a8ee96e..453700fc41 100644 --- a/ext/web/02_structured_clone.js +++ b/ext/web/02_structured_clone.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/02_timers.js b/ext/web/02_timers.js index 6058febd59..c74f9baff9 100644 --- a/ext/web/02_timers.js +++ b/ext/web/02_timers.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_defer } from "ext:core/ops"; diff --git a/ext/web/03_abort_signal.js b/ext/web/03_abort_signal.js index 93b3cf0522..1f9ce42e1e 100644 --- a/ext/web/03_abort_signal.js +++ b/ext/web/03_abort_signal.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/04_global_interfaces.js b/ext/web/04_global_interfaces.js index 7c7f83b431..bda695b530 100644 --- a/ext/web/04_global_interfaces.js +++ b/ext/web/04_global_interfaces.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/05_base64.js b/ext/web/05_base64.js index e6796e1dc3..155201f853 100644 --- a/ext/web/05_base64.js +++ b/ext/web/05_base64.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/06_streams.js b/ext/web/06_streams.js index f3ac711fc7..0b9ae538e2 100644 --- a/ext/web/06_streams.js +++ b/ext/web/06_streams.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/06_streams_types.d.ts b/ext/web/06_streams_types.d.ts index fe05ee6e65..0a6cb6503a 100644 --- a/ext/web/06_streams_types.d.ts +++ b/ext/web/06_streams_types.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // ** Internal Interfaces ** diff --git a/ext/web/08_text_encoding.js b/ext/web/08_text_encoding.js index 3163c96282..8988c06c36 100644 --- a/ext/web/08_text_encoding.js +++ b/ext/web/08_text_encoding.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/09_file.js b/ext/web/09_file.js index 7c1d79ce31..cdad0b7396 100644 --- a/ext/web/09_file.js +++ b/ext/web/09_file.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/10_filereader.js b/ext/web/10_filereader.js index 2718606380..cecc6484a6 100644 --- a/ext/web/10_filereader.js +++ b/ext/web/10_filereader.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/12_location.js b/ext/web/12_location.js index ba0c47e2d1..cc1afb3d05 100644 --- a/ext/web/12_location.js +++ b/ext/web/12_location.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/web/13_message_port.js b/ext/web/13_message_port.js index 79fec9de2f..f96cd193f4 100644 --- a/ext/web/13_message_port.js +++ b/ext/web/13_message_port.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/14_compression.js b/ext/web/14_compression.js index 1adb205b22..de49d7ad3d 100644 --- a/ext/web/14_compression.js +++ b/ext/web/14_compression.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/web/15_performance.js b/ext/web/15_performance.js index f23e851246..967cdda470 100644 --- a/ext/web/15_performance.js +++ b/ext/web/15_performance.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { op_now, op_time_origin } from "ext:core/ops"; diff --git a/ext/web/16_image_data.js b/ext/web/16_image_data.js index 13df0d07be..bd69da7903 100644 --- a/ext/web/16_image_data.js +++ b/ext/web/16_image_data.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import * as webidl from "ext:deno_webidl/00_webidl.js"; diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index 44fb2e46bf..dd521ad037 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_web" diff --git a/ext/web/benches/encoding.rs b/ext/web/benches/encoding.rs index d0738c6452..42497ef3ce 100644 --- a/ext/web/benches/encoding.rs +++ b/ext/web/benches/encoding.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; diff --git a/ext/web/benches/timers_ops.rs b/ext/web/benches/timers_ops.rs index d39ee4eeae..a8a52ad916 100644 --- a/ext/web/benches/timers_ops.rs +++ b/ext/web/benches/timers_ops.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_async; use deno_bench_util::bench_or_profile; diff --git a/ext/web/blob.rs b/ext/web/blob.rs index bc64a0f27e..8723337093 100644 --- a/ext/web/blob.rs +++ b/ext/web/blob.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; diff --git a/ext/web/compression.rs b/ext/web/compression.rs index b6cc357839..650cc84085 100644 --- a/ext/web/compression.rs +++ b/ext/web/compression.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::io::Write; diff --git a/ext/web/internal.d.ts b/ext/web/internal.d.ts index b2aea80d9f..64a8633854 100644 --- a/ext/web/internal.d.ts +++ b/ext/web/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// /// diff --git a/ext/web/lib.deno_web.d.ts b/ext/web/lib.deno_web.d.ts index 8aafbad535..1fb003b66f 100644 --- a/ext/web/lib.deno_web.d.ts +++ b/ext/web/lib.deno_web.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/web/lib.rs b/ext/web/lib.rs index a031a81ecb..07b00f0049 100644 --- a/ext/web/lib.rs +++ b/ext/web/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod blob; mod compression; diff --git a/ext/web/message_port.rs b/ext/web/message_port.rs index c55b963e09..b2aad6776f 100644 --- a/ext/web/message_port.rs +++ b/ext/web/message_port.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/web/stream_resource.rs b/ext/web/stream_resource.rs index cb0ce44073..5613f57384 100644 --- a/ext/web/stream_resource.rs +++ b/ext/web/stream_resource.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; use std::cell::RefMut; diff --git a/ext/web/timers.rs b/ext/web/timers.rs index 696f352de0..7929b6050e 100644 --- a/ext/web/timers.rs +++ b/ext/web/timers.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This module helps deno implement timers and performance APIs. diff --git a/ext/webgpu/00_init.js b/ext/webgpu/00_init.js index 0f10847cef..81bb27286d 100644 --- a/ext/webgpu/00_init.js +++ b/ext/webgpu/00_init.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core } from "ext:core/mod.js"; diff --git a/ext/webgpu/01_webgpu.js b/ext/webgpu/01_webgpu.js index d371f49ea1..5ce34a5e7b 100644 --- a/ext/webgpu/01_webgpu.js +++ b/ext/webgpu/01_webgpu.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/webgpu/02_surface.js b/ext/webgpu/02_surface.js index ce723b891f..b0561469a9 100644 --- a/ext/webgpu/02_surface.js +++ b/ext/webgpu/02_surface.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check /// diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 3a491afcf8..9aa1b2370c 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_webgpu" diff --git a/ext/webgpu/binding.rs b/ext/webgpu/binding.rs index 6d0d11db3f..2849cf9bfe 100644 --- a/ext/webgpu/binding.rs +++ b/ext/webgpu/binding.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::rc::Rc; diff --git a/ext/webgpu/buffer.rs b/ext/webgpu/buffer.rs index d3c15d978a..e8e33244c9 100644 --- a/ext/webgpu/buffer.rs +++ b/ext/webgpu/buffer.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/webgpu/bundle.rs b/ext/webgpu/bundle.rs index 70bd64a77f..5fc147a44a 100644 --- a/ext/webgpu/bundle.rs +++ b/ext/webgpu/bundle.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/webgpu/byow.rs b/ext/webgpu/byow.rs index dfc1678aef..7c16c8a0d2 100644 --- a/ext/webgpu/byow.rs +++ b/ext/webgpu/byow.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ffi::c_void; #[cfg(any( diff --git a/ext/webgpu/command_encoder.rs b/ext/webgpu/command_encoder.rs index de1e6102c6..4b14345a2c 100644 --- a/ext/webgpu/command_encoder.rs +++ b/ext/webgpu/command_encoder.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/webgpu/compute_pass.rs b/ext/webgpu/compute_pass.rs index e0e11c19a5..22cd522d8a 100644 --- a/ext/webgpu/compute_pass.rs +++ b/ext/webgpu/compute_pass.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/webgpu/error.rs b/ext/webgpu/error.rs index 516c3c8c35..f022a56916 100644 --- a/ext/webgpu/error.rs +++ b/ext/webgpu/error.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::convert::From; use std::error::Error; diff --git a/ext/webgpu/lib.rs b/ext/webgpu/lib.rs index a5a8ea5b95..bdf0f39b63 100644 --- a/ext/webgpu/lib.rs +++ b/ext/webgpu/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![cfg(not(target_arch = "wasm32"))] #![warn(unsafe_op_in_unsafe_fn)] diff --git a/ext/webgpu/pipeline.rs b/ext/webgpu/pipeline.rs index 62c1e340c8..87b1610ad7 100644 --- a/ext/webgpu/pipeline.rs +++ b/ext/webgpu/pipeline.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; diff --git a/ext/webgpu/queue.rs b/ext/webgpu/queue.rs index 2d73f4fcc5..4f367f5469 100644 --- a/ext/webgpu/queue.rs +++ b/ext/webgpu/queue.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::rc::Rc; diff --git a/ext/webgpu/render_pass.rs b/ext/webgpu/render_pass.rs index e1bf2a5681..41d610c0f9 100644 --- a/ext/webgpu/render_pass.rs +++ b/ext/webgpu/render_pass.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/ext/webgpu/sampler.rs b/ext/webgpu/sampler.rs index caeccdd5f3..e4f73e93ac 100644 --- a/ext/webgpu/sampler.rs +++ b/ext/webgpu/sampler.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::rc::Rc; diff --git a/ext/webgpu/shader.rs b/ext/webgpu/shader.rs index 285a37f560..f57e24fa26 100644 --- a/ext/webgpu/shader.rs +++ b/ext/webgpu/shader.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::rc::Rc; diff --git a/ext/webgpu/surface.rs b/ext/webgpu/surface.rs index 7471d567a1..e23c5f182b 100644 --- a/ext/webgpu/surface.rs +++ b/ext/webgpu/surface.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::rc::Rc; diff --git a/ext/webgpu/texture.rs b/ext/webgpu/texture.rs index 4c95a4cb69..a354567de8 100644 --- a/ext/webgpu/texture.rs +++ b/ext/webgpu/texture.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::rc::Rc; diff --git a/ext/webidl/00_webidl.js b/ext/webidl/00_webidl.js index eb18cbcc3e..b3c3b299f0 100644 --- a/ext/webidl/00_webidl.js +++ b/ext/webidl/00_webidl.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Adapted from https://github.com/jsdom/webidl-conversions. // Copyright Domenic Denicola. Licensed under BSD-2-Clause License. diff --git a/ext/webidl/Cargo.toml b/ext/webidl/Cargo.toml index 60cb9f29f8..ab285c7204 100644 --- a/ext/webidl/Cargo.toml +++ b/ext/webidl/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_webidl" diff --git a/ext/webidl/benches/dict.js b/ext/webidl/benches/dict.js index 9e7367d62c..8aedc51235 100644 --- a/ext/webidl/benches/dict.js +++ b/ext/webidl/benches/dict.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/webidl/benches/dict.rs b/ext/webidl/benches/dict.rs index 1c0efc921c..aea491cf12 100644 --- a/ext/webidl/benches/dict.rs +++ b/ext/webidl/benches/dict.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_bench_util::bench_js_sync; use deno_bench_util::bench_or_profile; diff --git a/ext/webidl/internal.d.ts b/ext/webidl/internal.d.ts index 375d548d32..a884d982aa 100644 --- a/ext/webidl/internal.d.ts +++ b/ext/webidl/internal.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any /// diff --git a/ext/webidl/lib.rs b/ext/webidl/lib.rs index 51dbed33a5..7a3608def4 100644 --- a/ext/webidl/lib.rs +++ b/ext/webidl/lib.rs @@ -1,3 +1,3 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. deno_core::extension!(deno_webidl, esm = ["00_webidl.js"],); diff --git a/ext/websocket/01_websocket.js b/ext/websocket/01_websocket.js index 78572f5f00..e0db51f4be 100644 --- a/ext/websocket/01_websocket.js +++ b/ext/websocket/01_websocket.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/websocket/02_websocketstream.js b/ext/websocket/02_websocketstream.js index 838ae3d4ce..a1b76fb6c0 100644 --- a/ext/websocket/02_websocketstream.js +++ b/ext/websocket/02_websocketstream.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index 8b8359f074..a1f1c98ba1 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_websocket" diff --git a/ext/websocket/autobahn/autobahn_server.js b/ext/websocket/autobahn/autobahn_server.js index 7400c8d547..1c0d4bbaf6 100644 --- a/ext/websocket/autobahn/autobahn_server.js +++ b/ext/websocket/autobahn/autobahn_server.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { parseArgs } from "@std/cli/parse-args"; const { port } = parseArgs(Deno.args, { diff --git a/ext/websocket/autobahn/fuzzingclient.js b/ext/websocket/autobahn/fuzzingclient.js index fed0fd6aa9..8602319650 100644 --- a/ext/websocket/autobahn/fuzzingclient.js +++ b/ext/websocket/autobahn/fuzzingclient.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file diff --git a/ext/websocket/lib.deno_websocket.d.ts b/ext/websocket/lib.deno_websocket.d.ts index fb7ea6070c..28cf8b8ab3 100644 --- a/ext/websocket/lib.deno_websocket.d.ts +++ b/ext/websocket/lib.deno_websocket.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index 4dcead2a77..5819906da6 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::Cell; use std::cell::RefCell; diff --git a/ext/websocket/stream.rs b/ext/websocket/stream.rs index 6900922bb7..c2ac4ee5d8 100644 --- a/ext/websocket/stream.rs +++ b/ext/websocket/stream.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::ErrorKind; use std::pin::Pin; use std::task::ready; diff --git a/ext/webstorage/01_webstorage.js b/ext/webstorage/01_webstorage.js index 12abea8387..7adf190782 100644 --- a/ext/webstorage/01_webstorage.js +++ b/ext/webstorage/01_webstorage.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index 4f9795d098..2cae0d8e01 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_webstorage" diff --git a/ext/webstorage/lib.deno_webstorage.d.ts b/ext/webstorage/lib.deno_webstorage.d.ts index fa6d403dea..5f5aafa1b8 100644 --- a/ext/webstorage/lib.deno_webstorage.d.ts +++ b/ext/webstorage/lib.deno_webstorage.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any no-var diff --git a/ext/webstorage/lib.rs b/ext/webstorage/lib.rs index 6b14cbb08c..ca6b43a827 100644 --- a/ext/webstorage/lib.rs +++ b/ext/webstorage/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // NOTE to all: use **cached** prepared statements when interfacing with SQLite. diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 12c18d4452..71e75d5a1d 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_resolver" diff --git a/resolvers/deno/cjs.rs b/resolvers/deno/cjs.rs index 2ec253d41a..bae645b481 100644 --- a/resolvers/deno/cjs.rs +++ b/resolvers/deno/cjs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_media_type::MediaType; use node_resolver::errors::ClosestPkgJsonError; diff --git a/resolvers/deno/lib.rs b/resolvers/deno/lib.rs index c943aacdae..ab01f397fb 100644 --- a/resolvers/deno/lib.rs +++ b/resolvers/deno/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![deny(clippy::print_stderr)] #![deny(clippy::print_stdout)] diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index 3056a70f61..3ceec368ad 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; diff --git a/resolvers/deno/npm/local.rs b/resolvers/deno/npm/local.rs index aef476ad94..6322a4b3f7 100644 --- a/resolvers/deno/npm/local.rs +++ b/resolvers/deno/npm/local.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 7b4b09a842..1501c05941 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Debug; use std::path::PathBuf; diff --git a/resolvers/deno/sloppy_imports.rs b/resolvers/deno/sloppy_imports.rs index 6644222a8b..b6fbf487dd 100644 --- a/resolvers/deno/sloppy_imports.rs +++ b/resolvers/deno/sloppy_imports.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; diff --git a/resolvers/deno/sync.rs b/resolvers/deno/sync.rs index ebcf8509d5..43635e62b7 100644 --- a/resolvers/deno/sync.rs +++ b/resolvers/deno/sync.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use inner::*; diff --git a/resolvers/node/Cargo.toml b/resolvers/node/Cargo.toml index 1e35c0a355..bdb0ba2ab1 100644 --- a/resolvers/node/Cargo.toml +++ b/resolvers/node/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "node_resolver" diff --git a/resolvers/node/analyze.rs b/resolvers/node/analyze.rs index 7f21473c10..a6ba3927aa 100644 --- a/resolvers/node/analyze.rs +++ b/resolvers/node/analyze.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::BTreeSet; diff --git a/resolvers/node/errors.rs b/resolvers/node/errors.rs index 26b1a1d84a..4157bd4c85 100644 --- a/resolvers/node/errors.rs +++ b/resolvers/node/errors.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::fmt::Write; diff --git a/resolvers/node/lib.rs b/resolvers/node/lib.rs index 075f819ebb..7d8c98eafc 100644 --- a/resolvers/node/lib.rs +++ b/resolvers/node/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![deny(clippy::print_stderr)] #![deny(clippy::print_stdout)] diff --git a/resolvers/node/npm.rs b/resolvers/node/npm.rs index ab3a179426..f799d6ddee 100644 --- a/resolvers/node/npm.rs +++ b/resolvers/node/npm.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; use std::path::PathBuf; diff --git a/resolvers/node/package_json.rs b/resolvers/node/package_json.rs index ebbe099014..2e1d5110bd 100644 --- a/resolvers/node/package_json.rs +++ b/resolvers/node/package_json.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; diff --git a/resolvers/node/path.rs b/resolvers/node/path.rs index 8c2d35fadf..525aeb36ef 100644 --- a/resolvers/node/path.rs +++ b/resolvers/node/path.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Component; use std::path::Path; diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs index e2ee2799e6..d0b52213f3 100644 --- a/resolvers/node/resolution.rs +++ b/resolvers/node/resolution.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::path::Path; diff --git a/resolvers/node/sync.rs b/resolvers/node/sync.rs index e8add4e0fc..8cf06932ac 100644 --- a/resolvers/node/sync.rs +++ b/resolvers/node/sync.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use inner::*; diff --git a/resolvers/npm_cache/Cargo.toml b/resolvers/npm_cache/Cargo.toml index 48d0a32437..328355d340 100644 --- a/resolvers/npm_cache/Cargo.toml +++ b/resolvers/npm_cache/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_npm_cache" diff --git a/resolvers/npm_cache/fs_util.rs b/resolvers/npm_cache/fs_util.rs index d5a634e36f..625d83e24d 100644 --- a/resolvers/npm_cache/fs_util.rs +++ b/resolvers/npm_cache/fs_util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::ErrorKind; use std::path::Path; diff --git a/resolvers/npm_cache/lib.rs b/resolvers/npm_cache/lib.rs index 50614a239a..012c277e22 100644 --- a/resolvers/npm_cache/lib.rs +++ b/resolvers/npm_cache/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::io::ErrorKind; diff --git a/resolvers/npm_cache/registry_info.rs b/resolvers/npm_cache/registry_info.rs index 57e188200d..0637d75c19 100644 --- a/resolvers/npm_cache/registry_info.rs +++ b/resolvers/npm_cache/registry_info.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/resolvers/npm_cache/remote.rs b/resolvers/npm_cache/remote.rs index 538554612f..16eb0f6cd9 100644 --- a/resolvers/npm_cache/remote.rs +++ b/resolvers/npm_cache/remote.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use anyhow::bail; use anyhow::Context; diff --git a/resolvers/npm_cache/tarball.rs b/resolvers/npm_cache/tarball.rs index 3a7e9df8a9..49ca3bc7fd 100644 --- a/resolvers/npm_cache/tarball.rs +++ b/resolvers/npm_cache/tarball.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::sync::Arc; diff --git a/resolvers/npm_cache/tarball_extract.rs b/resolvers/npm_cache/tarball_extract.rs index affe93eaa4..cf408ac632 100644 --- a/resolvers/npm_cache/tarball_extract.rs +++ b/resolvers/npm_cache/tarball_extract.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashSet; diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index ca21547efc..9826a7459d 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_runtime" diff --git a/runtime/code_cache.rs b/runtime/code_cache.rs index b4a7ce188f..b7ee8a9ed6 100644 --- a/runtime/code_cache.rs +++ b/runtime/code_cache.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::ModuleSpecifier; diff --git a/runtime/errors.rs b/runtime/errors.rs index e0f807d3ae..7b61395866 100644 --- a/runtime/errors.rs +++ b/runtime/errors.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! There are many types of errors in Deno: //! - AnyError: a generic wrapper that can encapsulate any type of error. diff --git a/runtime/examples/extension/bootstrap.js b/runtime/examples/extension/bootstrap.js index 9461acb84a..78e0cb999f 100644 --- a/runtime/examples/extension/bootstrap.js +++ b/runtime/examples/extension/bootstrap.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { op_hello } from "ext:core/ops"; function hello() { op_hello("world"); diff --git a/runtime/examples/extension/main.js b/runtime/examples/extension/main.js index 4d6e4e3b7d..4a66aecf58 100644 --- a/runtime/examples/extension/main.js +++ b/runtime/examples/extension/main.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Hello world from JS!"); console.log(Deno.build); Extension.hello(); diff --git a/runtime/examples/extension/main.rs b/runtime/examples/extension/main.rs index a4ac85bf5e..9f0eb63b01 100644 --- a/runtime/examples/extension/main.rs +++ b/runtime/examples/extension/main.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/runtime/fmt_errors.rs b/runtime/fmt_errors.rs index 5af84c67bc..1a2bc5dfd3 100644 --- a/runtime/fmt_errors.rs +++ b/runtime/fmt_errors.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! This mod provides DenoError to unify errors across Deno. use std::fmt::Write as _; diff --git a/runtime/fs_util.rs b/runtime/fs_util.rs index 2fc34dd311..7788a97170 100644 --- a/runtime/fs_util.rs +++ b/runtime/fs_util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; use std::path::PathBuf; diff --git a/runtime/inspector_server.rs b/runtime/inspector_server.rs index cbe97738c7..75e9668db4 100644 --- a/runtime/inspector_server.rs +++ b/runtime/inspector_server.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Alias for the future `!` type. use core::convert::Infallible as Never; diff --git a/runtime/js.rs b/runtime/js.rs index a8384ceacf..55ab75b66b 100644 --- a/runtime/js.rs +++ b/runtime/js.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[cfg(not(feature = "include_js_files_for_snapshotting"))] pub static SOURCE_CODE_FOR_99_MAIN_JS: &str = include_str!("js/99_main.js"); diff --git a/runtime/js/01_errors.js b/runtime/js/01_errors.js index ea567a5d08..09fd82e867 100644 --- a/runtime/js/01_errors.js +++ b/runtime/js/01_errors.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; const { BadResource, Interrupted, NotCapable } = core; diff --git a/runtime/js/01_version.ts b/runtime/js/01_version.ts index 33a8f50cdd..779a886897 100644 --- a/runtime/js/01_version.ts +++ b/runtime/js/01_version.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; const { diff --git a/runtime/js/06_util.js b/runtime/js/06_util.js index bf71c371b9..658cfa69aa 100644 --- a/runtime/js/06_util.js +++ b/runtime/js/06_util.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { op_bootstrap_log_level } from "ext:core/ops"; diff --git a/runtime/js/10_permissions.js b/runtime/js/10_permissions.js index 831b6bf2ae..c835cc1c5a 100644 --- a/runtime/js/10_permissions.js +++ b/runtime/js/10_permissions.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/11_workers.js b/runtime/js/11_workers.js index 3853761920..fd36866936 100644 --- a/runtime/js/11_workers.js +++ b/runtime/js/11_workers.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/30_os.js b/runtime/js/30_os.js index f3dfda886d..166b983f45 100644 --- a/runtime/js/30_os.js +++ b/runtime/js/30_os.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/40_fs_events.js b/runtime/js/40_fs_events.js index 322ee6b3ca..25f397b840 100644 --- a/runtime/js/40_fs_events.js +++ b/runtime/js/40_fs_events.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_fs_events_open, op_fs_events_poll } from "ext:core/ops"; diff --git a/runtime/js/40_process.js b/runtime/js/40_process.js index e2cb1d95b2..fde97fac64 100644 --- a/runtime/js/40_process.js +++ b/runtime/js/40_process.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, internals, primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/40_signals.js b/runtime/js/40_signals.js index 41f25af677..161adfabbc 100644 --- a/runtime/js/40_signals.js +++ b/runtime/js/40_signals.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_signal_bind, op_signal_poll, op_signal_unbind } from "ext:core/ops"; diff --git a/runtime/js/40_tty.js b/runtime/js/40_tty.js index 72e7b68846..56f8721eaf 100644 --- a/runtime/js/40_tty.js +++ b/runtime/js/40_tty.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_console_size } from "ext:core/ops"; const { diff --git a/runtime/js/41_prompt.js b/runtime/js/41_prompt.js index 8460862d2e..e300f69a3c 100644 --- a/runtime/js/41_prompt.js +++ b/runtime/js/41_prompt.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { op_read_line_prompt } from "ext:core/ops"; const { diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 5511649279..e13a8d1307 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core } from "ext:core/mod.js"; import { diff --git a/runtime/js/98_global_scope_shared.js b/runtime/js/98_global_scope_shared.js index c01bde6fad..99bace7647 100644 --- a/runtime/js/98_global_scope_shared.js +++ b/runtime/js/98_global_scope_shared.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core } from "ext:core/mod.js"; diff --git a/runtime/js/98_global_scope_window.js b/runtime/js/98_global_scope_window.js index 098422f56f..c42a940a82 100644 --- a/runtime/js/98_global_scope_window.js +++ b/runtime/js/98_global_scope_window.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/98_global_scope_worker.js b/runtime/js/98_global_scope_worker.js index 4dc6157867..f10bb2830d 100644 --- a/runtime/js/98_global_scope_worker.js +++ b/runtime/js/98_global_scope_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index a11444bc36..03c662bcab 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Remove Intl.v8BreakIterator because it is a non-standard API. delete Intl.v8BreakIterator; diff --git a/runtime/lib.rs b/runtime/lib.rs index 1f449dc69a..3e48ec89c0 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub use deno_broadcast_channel; pub use deno_cache; diff --git a/runtime/ops/bootstrap.rs b/runtime/ops/bootstrap.rs index bbbddc61ba..b362217d2c 100644 --- a/runtime/ops/bootstrap.rs +++ b/runtime/ops/bootstrap.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::OpState; diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs index 1b89199e37..e8fb9f2cef 100644 --- a/runtime/ops/fs_events.rs +++ b/runtime/ops/fs_events.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/runtime/ops/http.rs b/runtime/ops/http.rs index 6e31576686..c9dc16cafe 100644 --- a/runtime/ops/http.rs +++ b/runtime/ops/http.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::rc::Rc; diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs index 67065b901b..438c3896e6 100644 --- a/runtime/ops/mod.rs +++ b/runtime/ops/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. pub mod bootstrap; pub mod fs_events; diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs index 3b767fd94f..a17b467cb7 100644 --- a/runtime/ops/os/mod.rs +++ b/runtime/ops/os/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::env; diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs index b5f9e284df..216637787c 100644 --- a/runtime/ops/permissions.rs +++ b/runtime/ops/permissions.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use ::deno_permissions::PermissionState; use ::deno_permissions::PermissionsContainer; diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index f4064367b9..cda0c73111 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; diff --git a/runtime/ops/runtime.rs b/runtime/ops/runtime.rs index 4b30ab8d8d..e95193167d 100644 --- a/runtime/ops/runtime.rs +++ b/runtime/ops/runtime.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::op2; use deno_core::ModuleSpecifier; diff --git a/runtime/ops/signal.rs b/runtime/ops/signal.rs index 85c883021c..dfb52463cd 100644 --- a/runtime/ops/signal.rs +++ b/runtime/ops/signal.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; #[cfg(unix)] diff --git a/runtime/ops/tty.rs b/runtime/ops/tty.rs index 0b3c2e0064..4843acccf3 100644 --- a/runtime/ops/tty.rs +++ b/runtime/ops/tty.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[cfg(unix)] use std::cell::RefCell; diff --git a/runtime/ops/web_worker.rs b/runtime/ops/web_worker.rs index 34466f8db9..5cde7d5373 100644 --- a/runtime/ops/web_worker.rs +++ b/runtime/ops/web_worker.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. mod sync_fetch; diff --git a/runtime/ops/web_worker/sync_fetch.rs b/runtime/ops/web_worker/sync_fetch.rs index a774e6db34..c9b622d31e 100644 --- a/runtime/ops/web_worker/sync_fetch.rs +++ b/runtime/ops/web_worker/sync_fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::sync::Arc; diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs index fafce5dccd..45285943eb 100644 --- a/runtime/ops/worker_host.rs +++ b/runtime/ops/worker_host.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; diff --git a/runtime/permissions.rs b/runtime/permissions.rs index 968c41560c..8b65c6b1b0 100644 --- a/runtime/permissions.rs +++ b/runtime/permissions.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; use std::path::PathBuf; diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index a7bd342a9c..b9259941aa 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "deno_permissions" diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 8730ba20d1..b36668b10c 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashSet; diff --git a/runtime/permissions/prompter.rs b/runtime/permissions/prompter.rs index 19f94434ca..94384427d1 100644 --- a/runtime/permissions/prompter.rs +++ b/runtime/permissions/prompter.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Write; use std::io::BufRead; diff --git a/runtime/shared.rs b/runtime/shared.rs index ce4350237f..1a9f2e66be 100644 --- a/runtime/shared.rs +++ b/runtime/shared.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Utilities shared between `build.rs` and the rest of the crate. use std::path::Path; diff --git a/runtime/signal.rs b/runtime/signal.rs index 0ef83d7de8..b11353b3b2 100644 --- a/runtime/signal.rs +++ b/runtime/signal.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[cfg(target_os = "windows")] #[derive(Debug, thiserror::Error)] diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index 751cc43f2d..8d008eeab0 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::io::Write; diff --git a/runtime/sys_info.rs b/runtime/sys_info.rs index d711e80ff0..e6bd16c0f3 100644 --- a/runtime/sys_info.rs +++ b/runtime/sys_info.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[cfg(target_family = "windows")] use std::sync::Once; diff --git a/runtime/tokio_util.rs b/runtime/tokio_util.rs index aa0282ece8..370b8a6d92 100644 --- a/runtime/tokio_util.rs +++ b/runtime/tokio_util.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Debug; use std::str::FromStr; diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 8902e0fe7e..270fc1ab9f 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::fmt; diff --git a/runtime/worker.rs b/runtime/worker.rs index a9a4440410..de29b66291 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; use std::rc::Rc; diff --git a/runtime/worker_bootstrap.rs b/runtime/worker_bootstrap.rs index f1e7dc05d0..ff90804f3b 100644 --- a/runtime/worker_bootstrap.rs +++ b/runtime/worker_bootstrap.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::thread; diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 1300066c64..cff778b2de 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "cli_tests" diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml index a5d2883ef2..bae9aa6bb5 100644 --- a/tests/ffi/Cargo.toml +++ b/tests/ffi/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "test_ffi" diff --git a/tests/ffi/src/lib.rs b/tests/ffi/src/lib.rs index 09c2afb3de..ca416fb980 100644 --- a/tests/ffi/src/lib.rs +++ b/tests/ffi/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/tests/ffi/tests/bench.js b/tests/ffi/tests/bench.js index 2b4fbd55ba..c4b935398a 100644 --- a/tests/ffi/tests/bench.js +++ b/tests/ffi/tests/bench.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); diff --git a/tests/ffi/tests/event_loop_integration.ts b/tests/ffi/tests/event_loop_integration.ts index e3914d9662..17c2d17f0b 100644 --- a/tests/ffi/tests/event_loop_integration.ts +++ b/tests/ffi/tests/event_loop_integration.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/ffi/tests/ffi_callback_errors.ts b/tests/ffi/tests/ffi_callback_errors.ts index 797ff236c1..3f165158b4 100644 --- a/tests/ffi/tests/ffi_callback_errors.ts +++ b/tests/ffi/tests/ffi_callback_errors.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/ffi/tests/ffi_types.ts b/tests/ffi/tests/ffi_types.ts index a996195c69..0ae51c61fb 100644 --- a/tests/ffi/tests/ffi_types.ts +++ b/tests/ffi/tests/ffi_types.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file // Only for testing types. Invoke with `deno install --entrypoint` diff --git a/tests/ffi/tests/integration_tests.rs b/tests/ffi/tests/integration_tests.rs index 34fcc16355..7e92641ae5 100644 --- a/tests/ffi/tests/integration_tests.rs +++ b/tests/ffi/tests/integration_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/tests/ffi/tests/test.js b/tests/ffi/tests/test.js index 074db81882..a93b648a2e 100644 --- a/tests/ffi/tests/test.js +++ b/tests/ffi/tests/test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file // Run using cargo test or `--v8-flags=--allow-natives-syntax` diff --git a/tests/ffi/tests/thread_safe_test.js b/tests/ffi/tests/thread_safe_test.js index fffa61a04f..519b0bd4fd 100644 --- a/tests/ffi/tests/thread_safe_test.js +++ b/tests/ffi/tests/thread_safe_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); diff --git a/tests/ffi/tests/thread_safe_test_worker.js b/tests/ffi/tests/thread_safe_test_worker.js index fc4854436e..390b4d8a3f 100644 --- a/tests/ffi/tests/thread_safe_test_worker.js +++ b/tests/ffi/tests/thread_safe_test_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); diff --git a/tests/integration/bench_tests.rs b/tests/integration/bench_tests.rs index 4ee029d648..b8d38a7f91 100644 --- a/tests/integration/bench_tests.rs +++ b/tests/integration/bench_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json::json; use deno_core::url::Url; diff --git a/tests/integration/cache_tests.rs b/tests/integration/cache_tests.rs index 4cddae1af1..3fbed3cbd4 100644 --- a/tests/integration/cache_tests.rs +++ b/tests/integration/cache_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util::TestContext; use test_util::TestContextBuilder; diff --git a/tests/integration/check_tests.rs b/tests/integration/check_tests.rs index a1fdf83403..15a2d96d04 100644 --- a/tests/integration/check_tests.rs +++ b/tests/integration/check_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_lockfile::NewLockfileOptions; use deno_semver::jsr::JsrDepPackageReq; diff --git a/tests/integration/compile_tests.rs b/tests/integration/compile_tests.rs index a715233933..e61a1ed9eb 100644 --- a/tests/integration/compile_tests.rs +++ b/tests/integration/compile_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json; use test_util as util; diff --git a/tests/integration/coverage_tests.rs b/tests/integration/coverage_tests.rs index ab18ef76d3..ab09d54267 100644 --- a/tests/integration/coverage_tests.rs +++ b/tests/integration/coverage_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json; use test_util as util; diff --git a/tests/integration/eval_tests.rs b/tests/integration/eval_tests.rs index 198be3a4e8..836cab900e 100644 --- a/tests/integration/eval_tests.rs +++ b/tests/integration/eval_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; diff --git a/tests/integration/flags_tests.rs b/tests/integration/flags_tests.rs index e233ca5baf..e819ea6c99 100644 --- a/tests/integration/flags_tests.rs +++ b/tests/integration/flags_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use util::assert_contains; diff --git a/tests/integration/fmt_tests.rs b/tests/integration/fmt_tests.rs index ccf54a4d0f..468c9cfbc8 100644 --- a/tests/integration/fmt_tests.rs +++ b/tests/integration/fmt_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json::json; use test_util as util; diff --git a/tests/integration/init_tests.rs b/tests/integration/init_tests.rs index a447e7bcef..5bbec687b0 100644 --- a/tests/integration/init_tests.rs +++ b/tests/integration/init_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use util::assert_contains; diff --git a/tests/integration/inspector_tests.rs b/tests/integration/inspector_tests.rs index c3586beb96..7c1be193a1 100644 --- a/tests/integration/inspector_tests.rs +++ b/tests/integration/inspector_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::BufRead; use std::process::ChildStderr; diff --git a/tests/integration/install_tests.rs b/tests/integration/install_tests.rs index b0c1e44778..29a65a70b8 100644 --- a/tests/integration/install_tests.rs +++ b/tests/integration/install_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use test_util::assert_contains; diff --git a/tests/integration/js_unit_tests.rs b/tests/integration/js_unit_tests.rs index 685c85e2fe..9ecec8b426 100644 --- a/tests/integration/js_unit_tests.rs +++ b/tests/integration/js_unit_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::BufRead; use std::io::BufReader; diff --git a/tests/integration/jsr_tests.rs b/tests/integration/jsr_tests.rs index d3fa5cd98f..95a9fcb437 100644 --- a/tests/integration/jsr_tests.rs +++ b/tests/integration/jsr_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_cache_dir::HttpCache; use deno_core::serde_json; diff --git a/tests/integration/jupyter_tests.rs b/tests/integration/jupyter_tests.rs index dc8c1a5aec..4ef993c157 100644 --- a/tests/integration/jupyter_tests.rs +++ b/tests/integration/jupyter_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::process::Output; use std::sync::Arc; diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 3641dd9230..23676c2f92 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::fs; use std::str::FromStr; diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index 37c7502284..a69bc65042 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/tests/integration/node_unit_tests.rs b/tests/integration/node_unit_tests.rs index c49699be6a..ef76e365a4 100644 --- a/tests/integration/node_unit_tests.rs +++ b/tests/integration/node_unit_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::BufRead; use std::io::BufReader; diff --git a/tests/integration/npm_tests.rs b/tests/integration/npm_tests.rs index 16e956d947..033d948b2e 100644 --- a/tests/integration/npm_tests.rs +++ b/tests/integration/npm_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json; use deno_core::serde_json::json; diff --git a/tests/integration/pm_tests.rs b/tests/integration/pm_tests.rs index e3db9006fa..e8985ebfb1 100644 --- a/tests/integration/pm_tests.rs +++ b/tests/integration/pm_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use deno_core::serde_json::json; use test_util::assert_contains; diff --git a/tests/integration/publish_tests.rs b/tests/integration/publish_tests.rs index b97479e78e..332b7c6fa6 100644 --- a/tests/integration/publish_tests.rs +++ b/tests/integration/publish_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::process::Command; diff --git a/tests/integration/repl_tests.rs b/tests/integration/repl_tests.rs index 9eceb2f05d..4faf629af5 100644 --- a/tests/integration/repl_tests.rs +++ b/tests/integration/repl_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use test_util::assert_contains; diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index 9edb29adae..ebb372418e 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::BufReader; use std::io::Cursor; diff --git a/tests/integration/serve_tests.rs b/tests/integration/serve_tests.rs index f3c8a31d93..8771930847 100644 --- a/tests/integration/serve_tests.rs +++ b/tests/integration/serve_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; diff --git a/tests/integration/shared_library_tests.rs b/tests/integration/shared_library_tests.rs index 4d33e6584e..d7cdf5b426 100644 --- a/tests/integration/shared_library_tests.rs +++ b/tests/integration/shared_library_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[cfg(all(target_os = "linux", target_arch = "x86_64"))] #[test] diff --git a/tests/integration/task_tests.rs b/tests/integration/task_tests.rs index f2e901228a..1db5376cdd 100644 --- a/tests/integration/task_tests.rs +++ b/tests/integration/task_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Most of the tests for this are in deno_task_shell. // These tests are intended to only test integration. diff --git a/tests/integration/test_tests.rs b/tests/integration/test_tests.rs index ca83682833..cfa9d9f2d6 100644 --- a/tests/integration/test_tests.rs +++ b/tests/integration/test_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use util::assert_contains; diff --git a/tests/integration/upgrade_tests.rs b/tests/integration/upgrade_tests.rs index 59dea8bf74..7748f0ca1a 100644 --- a/tests/integration/upgrade_tests.rs +++ b/tests/integration/upgrade_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::process::Command; use std::process::Stdio; diff --git a/tests/integration/watcher_tests.rs b/tests/integration/watcher_tests.rs index cd27062885..a180be2fb3 100644 --- a/tests/integration/watcher_tests.rs +++ b/tests/integration/watcher_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use flaky_test::flaky_test; use test_util as util; diff --git a/tests/lib.rs b/tests/lib.rs index 0a39b9f87d..d841ab9f3f 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1 +1 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. diff --git a/tests/napi/Cargo.toml b/tests/napi/Cargo.toml index e3de253683..e8a39a0cda 100644 --- a/tests/napi/Cargo.toml +++ b/tests/napi/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "test_napi" diff --git a/tests/napi/array_test.js b/tests/napi/array_test.js index 572d3a8994..c72e5d3849 100644 --- a/tests/napi/array_test.js +++ b/tests/napi/array_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/arraybuffer_test.js b/tests/napi/arraybuffer_test.js index f55b9a78c5..a11f761130 100644 --- a/tests/napi/arraybuffer_test.js +++ b/tests/napi/arraybuffer_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/async_test.js b/tests/napi/async_test.js index 4d9ad99c26..f3d258a543 100644 --- a/tests/napi/async_test.js +++ b/tests/napi/async_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/bigint_test.js b/tests/napi/bigint_test.js index 4a9ada2057..19c5317d75 100644 --- a/tests/napi/bigint_test.js +++ b/tests/napi/bigint_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/build.rs b/tests/napi/build.rs index c2fe86a531..73fce9172a 100644 --- a/tests/napi/build.rs +++ b/tests/napi/build.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. extern crate napi_build; diff --git a/tests/napi/callback_test.js b/tests/napi/callback_test.js index c132fefa18..3c56b11a28 100644 --- a/tests/napi/callback_test.js +++ b/tests/napi/callback_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/cleanup_hook_test.js b/tests/napi/cleanup_hook_test.js index 2c1f73e12b..8e4430eef7 100644 --- a/tests/napi/cleanup_hook_test.js +++ b/tests/napi/cleanup_hook_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/napi/coerce_test.js b/tests/napi/coerce_test.js index 64f0148016..91e8950980 100644 --- a/tests/napi/coerce_test.js +++ b/tests/napi/coerce_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/common.js b/tests/napi/common.js index 6dfdc873a8..7df63c4f90 100644 --- a/tests/napi/common.js +++ b/tests/napi/common.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export { assert, assertEquals, assertRejects, assertThrows } from "@std/assert"; export { fromFileUrl } from "@std/path"; diff --git a/tests/napi/date_test.js b/tests/napi/date_test.js index 49eec5cca1..2625010494 100644 --- a/tests/napi/date_test.js +++ b/tests/napi/date_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/env_test.js b/tests/napi/env_test.js index 72b868a668..0b9fad314b 100644 --- a/tests/napi/env_test.js +++ b/tests/napi/env_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/error_test.js b/tests/napi/error_test.js index 57acde5c16..cab36b0a30 100644 --- a/tests/napi/error_test.js +++ b/tests/napi/error_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/napi/init_test.js b/tests/napi/init_test.js index 9486824780..8cd5298dde 100644 --- a/tests/napi/init_test.js +++ b/tests/napi/init_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { assert, libSuffix } from "./common.js"; diff --git a/tests/napi/make_callback_test.js b/tests/napi/make_callback_test.js index b1f7912aea..60aa5ae4c7 100644 --- a/tests/napi/make_callback_test.js +++ b/tests/napi/make_callback_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/mem_test.js b/tests/napi/mem_test.js index bee8c194ea..2c8b0be75a 100644 --- a/tests/napi/mem_test.js +++ b/tests/napi/mem_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/module.c b/tests/napi/module.c index 1ae2ace5d3..9eb90887a8 100644 --- a/tests/napi/module.c +++ b/tests/napi/module.c @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. typedef struct napi_module { int nm_version; diff --git a/tests/napi/numbers_test.js b/tests/napi/numbers_test.js index 8a99c831d0..4914a24751 100644 --- a/tests/napi/numbers_test.js +++ b/tests/napi/numbers_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/object_test.js b/tests/napi/object_test.js index 6226b0138c..5ab81359c4 100644 --- a/tests/napi/object_test.js +++ b/tests/napi/object_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/napi/object_wrap_test.js b/tests/napi/object_wrap_test.js index ee6d4af86b..7c7eecbbd4 100644 --- a/tests/napi/object_wrap_test.js +++ b/tests/napi/object_wrap_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { assert, assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/promise_test.js b/tests/napi/promise_test.js index e4bbfee6b8..3b9ba15b67 100644 --- a/tests/napi/promise_test.js +++ b/tests/napi/promise_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/properties_test.js b/tests/napi/properties_test.js index 21a3555e8e..ee86eaa19b 100644 --- a/tests/napi/properties_test.js +++ b/tests/napi/properties_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/src/array.rs b/tests/napi/src/array.rs index 3769264ad0..b2aba94fff 100644 --- a/tests/napi/src/array.rs +++ b/tests/napi/src/array.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/arraybuffer.rs b/tests/napi/src/arraybuffer.rs index cf165f2f91..0fe39b53cc 100644 --- a/tests/napi/src/arraybuffer.rs +++ b/tests/napi/src/arraybuffer.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use napi_sys::*; diff --git a/tests/napi/src/async.rs b/tests/napi/src/async.rs index dd79da7f18..a91f7037b2 100644 --- a/tests/napi/src/async.rs +++ b/tests/napi/src/async.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::os::raw::c_char; use std::os::raw::c_void; diff --git a/tests/napi/src/bigint.rs b/tests/napi/src/bigint.rs index bea72c43c2..6e1d728b32 100644 --- a/tests/napi/src/bigint.rs +++ b/tests/napi/src/bigint.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/callback.rs b/tests/napi/src/callback.rs index 9f12730478..1ce1d688c2 100644 --- a/tests/napi/src/callback.rs +++ b/tests/napi/src/callback.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/coerce.rs b/tests/napi/src/coerce.rs index a022481d2a..e0fa50c89e 100644 --- a/tests/napi/src/coerce.rs +++ b/tests/napi/src/coerce.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/date.rs b/tests/napi/src/date.rs index 3a5a62b6f4..1db5bd088b 100644 --- a/tests/napi/src/date.rs +++ b/tests/napi/src/date.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/env.rs b/tests/napi/src/env.rs index 57f7469d69..b1b56191ec 100644 --- a/tests/napi/src/env.rs +++ b/tests/napi/src/env.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use napi_sys::*; diff --git a/tests/napi/src/error.rs b/tests/napi/src/error.rs index 9c421984c0..6de0d529b2 100644 --- a/tests/napi/src/error.rs +++ b/tests/napi/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/finalizer.rs b/tests/napi/src/finalizer.rs index 6b72dcfca7..56e0a326a7 100644 --- a/tests/napi/src/finalizer.rs +++ b/tests/napi/src/finalizer.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/lib.rs b/tests/napi/src/lib.rs index 8c6190ad3e..6162feded6 100644 --- a/tests/napi/src/lib.rs +++ b/tests/napi/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::all)] #![allow(clippy::print_stdout)] diff --git a/tests/napi/src/make_callback.rs b/tests/napi/src/make_callback.rs index 19948dce3d..c19d34e50c 100644 --- a/tests/napi/src/make_callback.rs +++ b/tests/napi/src/make_callback.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/mem.rs b/tests/napi/src/mem.rs index fd488da974..a05cd3abf4 100644 --- a/tests/napi/src/mem.rs +++ b/tests/napi/src/mem.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/numbers.rs b/tests/napi/src/numbers.rs index 00d68c9bf3..9c4174f727 100644 --- a/tests/napi/src/numbers.rs +++ b/tests/napi/src/numbers.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/object.rs b/tests/napi/src/object.rs index 93424b4b26..9014134803 100644 --- a/tests/napi/src/object.rs +++ b/tests/napi/src/object.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/object_wrap.rs b/tests/napi/src/object_wrap.rs index 66b1a9f926..11844917b5 100644 --- a/tests/napi/src/object_wrap.rs +++ b/tests/napi/src/object_wrap.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; diff --git a/tests/napi/src/primitives.rs b/tests/napi/src/primitives.rs index 7afa05157b..7717a0aaac 100644 --- a/tests/napi/src/primitives.rs +++ b/tests/napi/src/primitives.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/promise.rs b/tests/napi/src/promise.rs index 26bfbbd1fa..eab379d617 100644 --- a/tests/napi/src/promise.rs +++ b/tests/napi/src/promise.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; use std::ptr::addr_of_mut; diff --git a/tests/napi/src/properties.rs b/tests/napi/src/properties.rs index 3ef2290e67..4244c7ba06 100644 --- a/tests/napi/src/properties.rs +++ b/tests/napi/src/properties.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::ptr; diff --git a/tests/napi/src/strings.rs b/tests/napi/src/strings.rs index dd7ae35b38..027ae68176 100644 --- a/tests/napi/src/strings.rs +++ b/tests/napi/src/strings.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use napi_sys::ValueType::napi_string; use napi_sys::*; diff --git a/tests/napi/src/symbol.rs b/tests/napi/src/symbol.rs index 780a6dad0f..30c7cbde64 100644 --- a/tests/napi/src/symbol.rs +++ b/tests/napi/src/symbol.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use napi_sys::ValueType::napi_string; use napi_sys::*; diff --git a/tests/napi/src/tsfn.rs b/tests/napi/src/tsfn.rs index c8e885c6c3..830145501d 100644 --- a/tests/napi/src/tsfn.rs +++ b/tests/napi/src/tsfn.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This test performs initialization similar to napi-rs. // https://github.com/napi-rs/napi-rs/commit/a5a04a4e545f268769cc78e2bd6c45af4336aac3 diff --git a/tests/napi/src/typedarray.rs b/tests/napi/src/typedarray.rs index 2482c1dac6..95adf957e4 100644 --- a/tests/napi/src/typedarray.rs +++ b/tests/napi/src/typedarray.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use core::ffi::c_void; use std::os::raw::c_char; diff --git a/tests/napi/src/uv.rs b/tests/napi/src/uv.rs index 685b4040bd..45ca114adc 100644 --- a/tests/napi/src/uv.rs +++ b/tests/napi/src/uv.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::mem::MaybeUninit; use std::ptr; diff --git a/tests/napi/strings_test.js b/tests/napi/strings_test.js index 45cb133b28..01fc873a74 100644 --- a/tests/napi/strings_test.js +++ b/tests/napi/strings_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/symbol_test.js b/tests/napi/symbol_test.js index d8edec0232..3f0f277647 100644 --- a/tests/napi/symbol_test.js +++ b/tests/napi/symbol_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/tests/napi_tests.rs b/tests/napi/tests/napi_tests.rs index ff09565ff3..f97fdce289 100644 --- a/tests/napi/tests/napi_tests.rs +++ b/tests/napi/tests/napi_tests.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/tests/napi/typedarray_test.js b/tests/napi/typedarray_test.js index 25729754a5..f7887e4b1c 100644 --- a/tests/napi/typedarray_test.js +++ b/tests/napi/typedarray_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/napi/uv_test.js b/tests/napi/uv_test.js index af20b26493..f0ff613f31 100644 --- a/tests/napi/uv_test.js +++ b/tests/napi/uv_test.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; diff --git a/tests/node_compat/common.ts b/tests/node_compat/common.ts index 2982095a29..a86373d69f 100644 --- a/tests/node_compat/common.ts +++ b/tests/node_compat/common.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { partition } from "@std/collections/partition"; import { join } from "@std/path"; import * as JSONC from "@std/jsonc"; diff --git a/tests/node_compat/polyfill_globals.js b/tests/node_compat/polyfill_globals.js index f22143d9bd..8bbd5cc7df 100644 --- a/tests/node_compat/polyfill_globals.js +++ b/tests/node_compat/polyfill_globals.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { clearImmediate, diff --git a/tests/node_compat/runner.ts b/tests/node_compat/runner.ts index 56803fad44..6bc750447a 100644 --- a/tests/node_compat/runner.ts +++ b/tests/node_compat/runner.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import "./polyfill_globals.js"; import { createRequire } from "node:module"; import { toFileUrl } from "@std/path/to-file-url"; diff --git a/tests/node_compat/runner/challenge_new_test.ts b/tests/node_compat/runner/challenge_new_test.ts index 313bf60490..c95391d3e0 100644 --- a/tests/node_compat/runner/challenge_new_test.ts +++ b/tests/node_compat/runner/challenge_new_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console import { deadline } from "@std/async/deadline"; diff --git a/tests/node_compat/runner/setup.ts b/tests/node_compat/runner/setup.ts index b655633ead..d256842e14 100755 --- a/tests/node_compat/runner/setup.ts +++ b/tests/node_compat/runner/setup.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-read=. --allow-write=. --allow-run=git --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/node_compat/test.ts b/tests/node_compat/test.ts index 52d2b17169..fe6b2e879b 100644 --- a/tests/node_compat/test.ts +++ b/tests/node_compat/test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/node_compat/test_runner.rs b/tests/node_compat/test_runner.rs index 15749ca7fd..150b632b90 100644 --- a/tests/node_compat/test_runner.rs +++ b/tests/node_compat/test_runner.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use test_util as util; use util::deno_config_path; diff --git a/tests/registry/jsr/@std/assert/0.220.1/mod.ts b/tests/registry/jsr/@std/assert/0.220.1/mod.ts index fdcb56c8cf..44aa962030 100644 --- a/tests/registry/jsr/@std/assert/0.220.1/mod.ts +++ b/tests/registry/jsr/@std/assert/0.220.1/mod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** A library of assertion functions. * If the assertion is false an `AssertionError` will be thrown which will diff --git a/tests/registry/jsr/@std/assert/1.0.0/mod.ts b/tests/registry/jsr/@std/assert/1.0.0/mod.ts index fdcb56c8cf..44aa962030 100644 --- a/tests/registry/jsr/@std/assert/1.0.0/mod.ts +++ b/tests/registry/jsr/@std/assert/1.0.0/mod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** A library of assertion functions. * If the assertion is false an `AssertionError` will be thrown which will diff --git a/tests/registry/jsr/@std/http/1.0.0/mod.ts b/tests/registry/jsr/@std/http/1.0.0/mod.ts index 0a0e82847f..991765213a 100644 --- a/tests/registry/jsr/@std/http/1.0.0/mod.ts +++ b/tests/registry/jsr/@std/http/1.0.0/mod.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /** * Request handler for {@linkcode Route}. diff --git a/tests/registry/jsr/@std/path/0.220.1/_common/assert_path.ts b/tests/registry/jsr/@std/path/0.220.1/_common/assert_path.ts index 7033edcd1a..2d7f7f1b92 100644 --- a/tests/registry/jsr/@std/path/0.220.1/_common/assert_path.ts +++ b/tests/registry/jsr/@std/path/0.220.1/_common/assert_path.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright the Browserify authors. MIT License. export function assertPath(path?: string) { diff --git a/tests/registry/jsr/@std/path/0.220.1/_common/constants.ts b/tests/registry/jsr/@std/path/0.220.1/_common/constants.ts index 9bfd411b66..2dae0df89f 100644 --- a/tests/registry/jsr/@std/path/0.220.1/_common/constants.ts +++ b/tests/registry/jsr/@std/path/0.220.1/_common/constants.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. diff --git a/tests/registry/jsr/@std/path/0.220.1/_common/normalize.ts b/tests/registry/jsr/@std/path/0.220.1/_common/normalize.ts index 3a1a162845..a3d0a0caee 100644 --- a/tests/registry/jsr/@std/path/0.220.1/_common/normalize.ts +++ b/tests/registry/jsr/@std/path/0.220.1/_common/normalize.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { assertPath } from "./assert_path.ts"; diff --git a/tests/registry/jsr/@std/path/0.220.1/_common/normalize_string.ts b/tests/registry/jsr/@std/path/0.220.1/_common/normalize_string.ts index d8f0e090a6..dbcf59029b 100644 --- a/tests/registry/jsr/@std/path/0.220.1/_common/normalize_string.ts +++ b/tests/registry/jsr/@std/path/0.220.1/_common/normalize_string.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. diff --git a/tests/registry/jsr/@std/path/0.220.1/posix/_util.ts b/tests/registry/jsr/@std/path/0.220.1/posix/_util.ts index b446155df5..ff4f87c2aa 100644 --- a/tests/registry/jsr/@std/path/0.220.1/posix/_util.ts +++ b/tests/registry/jsr/@std/path/0.220.1/posix/_util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright the Browserify authors. MIT License. // Ported from https://github.com/browserify/path-browserify/ // This module is browser compatible. diff --git a/tests/registry/jsr/@std/path/0.220.1/posix/join.ts b/tests/registry/jsr/@std/path/0.220.1/posix/join.ts index 625762ab97..85bfb63794 100644 --- a/tests/registry/jsr/@std/path/0.220.1/posix/join.ts +++ b/tests/registry/jsr/@std/path/0.220.1/posix/join.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { assertPath } from "../_common/assert_path.ts"; diff --git a/tests/registry/jsr/@std/path/0.220.1/posix/normalize.ts b/tests/registry/jsr/@std/path/0.220.1/posix/normalize.ts index 8e88ad254b..40ccc59412 100644 --- a/tests/registry/jsr/@std/path/0.220.1/posix/normalize.ts +++ b/tests/registry/jsr/@std/path/0.220.1/posix/normalize.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { assertArg } from "../_common/normalize.ts"; diff --git a/tests/registry/jsr/@std/url/0.220.1/join.ts b/tests/registry/jsr/@std/url/0.220.1/join.ts index b9c8f19d31..b1f42a0a97 100644 --- a/tests/registry/jsr/@std/url/0.220.1/join.ts +++ b/tests/registry/jsr/@std/url/0.220.1/join.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { join as posixJoin } from "jsr:@std/path@^0.220.1/posix/join"; diff --git a/tests/registry/jsr/@std/url/0.220.1/normalize.ts b/tests/registry/jsr/@std/url/0.220.1/normalize.ts index e8d728435b..f9d89dd8f9 100644 --- a/tests/registry/jsr/@std/url/0.220.1/normalize.ts +++ b/tests/registry/jsr/@std/url/0.220.1/normalize.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This module is browser compatible. import { normalize as posixNormalize } from "jsr:@std/path@^0.220.1/posix/normalize"; diff --git a/tests/registry/npm/trim_registry_files.js b/tests/registry/npm/trim_registry_files.js index 608624b1d3..e6fbe1353f 100644 --- a/tests/registry/npm/trim_registry_files.js +++ b/tests/registry/npm/trim_registry_files.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-write=. --allow-read=. -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Run this to trim the registry.json files diff --git a/tests/specs/cli/otel_basic/basic.ts b/tests/specs/cli/otel_basic/basic.ts index 5c4ae43cd8..1f69277660 100644 --- a/tests/specs/cli/otel_basic/basic.ts +++ b/tests/specs/cli/otel_basic/basic.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { trace } from "npm:@opentelemetry/api@1.9.0"; import "jsr:@deno/otel@0.0.2/register"; diff --git a/tests/specs/cli/otel_basic/main.ts b/tests/specs/cli/otel_basic/main.ts index 634727cea7..608d1d3341 100644 --- a/tests/specs/cli/otel_basic/main.ts +++ b/tests/specs/cli/otel_basic/main.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const data = { spans: [], diff --git a/tests/specs/mod.rs b/tests/specs/mod.rs index 985a6c7c40..4da5a87d14 100644 --- a/tests/specs/mod.rs +++ b/tests/specs/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::BTreeMap; diff --git a/tests/specs/repl/console_log/093_console_log_format.js b/tests/specs/repl/console_log/093_console_log_format.js index 15022411ca..3ac9988023 100644 --- a/tests/specs/repl/console_log/093_console_log_format.js +++ b/tests/specs/repl/console_log/093_console_log_format.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. class Frac { constructor(num, den) { this.num = num; diff --git a/tests/specs/run/045_proxy/programmatic_proxy_client.ts b/tests/specs/run/045_proxy/programmatic_proxy_client.ts index 73af590c71..c238440755 100644 --- a/tests/specs/run/045_proxy/programmatic_proxy_client.ts +++ b/tests/specs/run/045_proxy/programmatic_proxy_client.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const client = Deno.createHttpClient({ proxy: { diff --git a/tests/specs/run/045_proxy/proxy_client.ts b/tests/specs/run/045_proxy/proxy_client.ts index 41deae2a5d..13fcc134ba 100644 --- a/tests/specs/run/045_proxy/proxy_client.ts +++ b/tests/specs/run/045_proxy/proxy_client.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const res = await fetch( "http://localhost:4545/run/045_mod.ts", ); diff --git a/tests/specs/run/045_proxy/proxy_test.ts b/tests/specs/run/045_proxy/proxy_test.ts index 22115a3aa8..04f4c9d06d 100644 --- a/tests/specs/run/045_proxy/proxy_test.ts +++ b/tests/specs/run/045_proxy/proxy_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const addr = Deno.args[1] || "localhost:4555"; function proxyServer() { diff --git a/tests/specs/run/finalization_registry/finalization_registry.js b/tests/specs/run/finalization_registry/finalization_registry.js index ee9dc384f5..76d68d6df5 100644 --- a/tests/specs/run/finalization_registry/finalization_registry.js +++ b/tests/specs/run/finalization_registry/finalization_registry.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. "use strict"; function assertEquals(a, b) { diff --git a/tests/specs/run/heapstats/heapstats.js b/tests/specs/run/heapstats/heapstats.js index b93c9c120d..c0dc0da81c 100644 --- a/tests/specs/run/heapstats/heapstats.js +++ b/tests/specs/run/heapstats/heapstats.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. "use strict"; function allocTest(alloc, allocAssert, deallocAssert) { diff --git a/tests/specs/run/tls_connecttls/textproto.ts b/tests/specs/run/tls_connecttls/textproto.ts index 9e0f5f5f0a..6b8ac92ecb 100644 --- a/tests/specs/run/tls_connecttls/textproto.ts +++ b/tests/specs/run/tls_connecttls/textproto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/tests/specs/run/tls_starttls/textproto.ts b/tests/specs/run/tls_starttls/textproto.ts index 9e0f5f5f0a..6b8ac92ecb 100644 --- a/tests/specs/run/tls_starttls/textproto.ts +++ b/tests/specs/run/tls_starttls/textproto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/tests/specs/run/worker_close_in_wasm_reactions/worker_close_in_wasm_reactions.js b/tests/specs/run/worker_close_in_wasm_reactions/worker_close_in_wasm_reactions.js index 2f62707eff..38508c31c4 100644 --- a/tests/specs/run/worker_close_in_wasm_reactions/worker_close_in_wasm_reactions.js +++ b/tests/specs/run/worker_close_in_wasm_reactions/worker_close_in_wasm_reactions.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // https://github.com/denoland/deno/issues/12263 // Test for a panic that happens when a worker is closed in the reactions of a diff --git a/tests/specs/run/worker_close_nested/close_nested_child.js b/tests/specs/run/worker_close_nested/close_nested_child.js index 97980c689e..1f2b2091a6 100644 --- a/tests/specs/run/worker_close_nested/close_nested_child.js +++ b/tests/specs/run/worker_close_nested/close_nested_child.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Starting the child worker"); diff --git a/tests/specs/run/worker_close_nested/close_nested_parent.js b/tests/specs/run/worker_close_nested/close_nested_parent.js index d1fe47553e..ddb9aec26d 100644 --- a/tests/specs/run/worker_close_nested/close_nested_parent.js +++ b/tests/specs/run/worker_close_nested/close_nested_parent.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Starting the parent worker"); diff --git a/tests/specs/run/worker_close_nested/worker_close_nested.js b/tests/specs/run/worker_close_nested/worker_close_nested.js index 8d9c88d1cf..179eb48fa1 100644 --- a/tests/specs/run/worker_close_nested/worker_close_nested.js +++ b/tests/specs/run/worker_close_nested/worker_close_nested.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Test that closing a worker which has living child workers will automatically // close the children. diff --git a/tests/specs/run/worker_close_race/close_race_worker.js b/tests/specs/run/worker_close_race/close_race_worker.js index 6964be34a0..945fed9dd0 100644 --- a/tests/specs/run/worker_close_race/close_race_worker.js +++ b/tests/specs/run/worker_close_race/close_race_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. setTimeout(() => { self.postMessage(""); diff --git a/tests/specs/run/worker_close_race/worker_close_race.js b/tests/specs/run/worker_close_race/worker_close_race.js index 188cd9ed88..b39062df50 100644 --- a/tests/specs/run/worker_close_race/worker_close_race.js +++ b/tests/specs/run/worker_close_race/worker_close_race.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // https://github.com/denoland/deno/issues/11416 // Test for a race condition between a worker's `close()` and the main thread's diff --git a/tests/specs/run/worker_drop_handle_race/worker_drop_handle_race.js b/tests/specs/run/worker_drop_handle_race/worker_drop_handle_race.js index ef9bcbe072..bfacc332ac 100644 --- a/tests/specs/run/worker_drop_handle_race/worker_drop_handle_race.js +++ b/tests/specs/run/worker_drop_handle_race/worker_drop_handle_race.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // https://github.com/denoland/deno/issues/11342 // Test for a panic that happens when the main thread's event loop finishes diff --git a/tests/specs/run/worker_drop_handle_race_terminate/worker_drop_handle_race_terminate.js b/tests/specs/run/worker_drop_handle_race_terminate/worker_drop_handle_race_terminate.js index 7c4e0b1099..85a3c51072 100644 --- a/tests/specs/run/worker_drop_handle_race_terminate/worker_drop_handle_race_terminate.js +++ b/tests/specs/run/worker_drop_handle_race_terminate/worker_drop_handle_race_terminate.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Test that the panic in https://github.com/denoland/deno/issues/11342 does not // happen when calling worker.terminate() after fixing diff --git a/tests/testdata/commonjs/example.js b/tests/testdata/commonjs/example.js index d2f89d3f02..0feedb12d4 100644 --- a/tests/testdata/commonjs/example.js +++ b/tests/testdata/commonjs/example.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore no-undef const processMod = require("process"); const osMod = require("node:os"); diff --git a/tests/testdata/run/textproto.ts b/tests/testdata/run/textproto.ts index 9e0f5f5f0a..6b8ac92ecb 100644 --- a/tests/testdata/run/textproto.ts +++ b/tests/testdata/run/textproto.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/tests/testdata/workers/close_nested_child.js b/tests/testdata/workers/close_nested_child.js index 97980c689e..1f2b2091a6 100644 --- a/tests/testdata/workers/close_nested_child.js +++ b/tests/testdata/workers/close_nested_child.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Starting the child worker"); diff --git a/tests/testdata/workers/close_nested_parent.js b/tests/testdata/workers/close_nested_parent.js index d1fe47553e..ddb9aec26d 100644 --- a/tests/testdata/workers/close_nested_parent.js +++ b/tests/testdata/workers/close_nested_parent.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. console.log("Starting the parent worker"); diff --git a/tests/testdata/workers/close_race_worker.js b/tests/testdata/workers/close_race_worker.js index 6964be34a0..945fed9dd0 100644 --- a/tests/testdata/workers/close_race_worker.js +++ b/tests/testdata/workers/close_race_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. setTimeout(() => { self.postMessage(""); diff --git a/tests/testdata/workers/http_worker.js b/tests/testdata/workers/http_worker.js index 27bc9c038c..fd11641feb 100644 --- a/tests/testdata/workers/http_worker.js +++ b/tests/testdata/workers/http_worker.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-deprecated-deno-api diff --git a/tests/unit/abort_controller_test.ts b/tests/unit/abort_controller_test.ts index 60ea6aa245..52a1bad803 100644 --- a/tests/unit/abort_controller_test.ts +++ b/tests/unit/abort_controller_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; diff --git a/tests/unit/blob_test.ts b/tests/unit/blob_test.ts index b578253142..b89f4703e2 100644 --- a/tests/unit/blob_test.ts +++ b/tests/unit/blob_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertStringIncludes } from "./test_util.ts"; import { concat } from "@std/bytes/concat"; diff --git a/tests/unit/body_test.ts b/tests/unit/body_test.ts index fb51fd0076..28a1c697b1 100644 --- a/tests/unit/body_test.ts +++ b/tests/unit/body_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertRejects } from "./test_util.ts"; // just a hack to get a body object diff --git a/tests/unit/broadcast_channel_test.ts b/tests/unit/broadcast_channel_test.ts index dce5f10867..f4efe68c4f 100644 --- a/tests/unit/broadcast_channel_test.ts +++ b/tests/unit/broadcast_channel_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; Deno.test("BroadcastChannel worker", async () => { diff --git a/tests/unit/build_test.ts b/tests/unit/build_test.ts index f697b64d39..9f4e3a5cbf 100644 --- a/tests/unit/build_test.ts +++ b/tests/unit/build_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; Deno.test(function buildInfo() { diff --git a/tests/unit/cache_api_test.ts b/tests/unit/cache_api_test.ts index 08f768e334..ec84e9c908 100644 --- a/tests/unit/cache_api_test.ts +++ b/tests/unit/cache_api_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/chmod_test.ts b/tests/unit/chmod_test.ts index 9ff6301e28..6d815ea1eb 100644 --- a/tests/unit/chmod_test.ts +++ b/tests/unit/chmod_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/chown_test.ts b/tests/unit/chown_test.ts index eda4d34037..99e7dd445a 100644 --- a/tests/unit/chown_test.ts +++ b/tests/unit/chown_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; // chown on Windows is noop for now, so ignore its testing on Windows diff --git a/tests/unit/command_test.ts b/tests/unit/command_test.ts index 8345548f85..268b0c25ba 100644 --- a/tests/unit/command_test.ts +++ b/tests/unit/command_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/console_test.ts b/tests/unit/console_test.ts index 06f5dd7e61..7b19e49cbd 100644 --- a/tests/unit/console_test.ts +++ b/tests/unit/console_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // TODO(ry) The unit test functions in this module are too coarse. They should // be broken up into smaller bits. diff --git a/tests/unit/copy_file_test.ts b/tests/unit/copy_file_test.ts index 9405184e33..fa4a9d837c 100644 --- a/tests/unit/copy_file_test.ts +++ b/tests/unit/copy_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; function readFileString(filename: string | URL): string { diff --git a/tests/unit/cron_test.ts b/tests/unit/cron_test.ts index 5f14f6c784..325ee5535d 100644 --- a/tests/unit/cron_test.ts +++ b/tests/unit/cron_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "./test_util.ts"; // @ts-ignore This is not publicly typed namespace, but it's there for sure. diff --git a/tests/unit/custom_event_test.ts b/tests/unit/custom_event_test.ts index b72084eb23..ddefd4cce6 100644 --- a/tests/unit/custom_event_test.ts +++ b/tests/unit/custom_event_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test(function customEventInitializedWithDetail() { diff --git a/tests/unit/dir_test.ts b/tests/unit/dir_test.ts index 1e702f549a..dbf88609c2 100644 --- a/tests/unit/dir_test.ts +++ b/tests/unit/dir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "./test_util.ts"; Deno.test({ permissions: { read: true } }, function dirCwdNotNull() { diff --git a/tests/unit/dom_exception_test.ts b/tests/unit/dom_exception_test.ts index 4e894f5fc8..2e0c9f71e1 100644 --- a/tests/unit/dom_exception_test.ts +++ b/tests/unit/dom_exception_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/error_stack_test.ts b/tests/unit/error_stack_test.ts index 7188b9f53d..fea8913494 100644 --- a/tests/unit/error_stack_test.ts +++ b/tests/unit/error_stack_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertMatch } from "./test_util.ts"; Deno.test(function errorStackMessageLine() { diff --git a/tests/unit/error_test.ts b/tests/unit/error_test.ts index bf0ef50627..48e1f7879b 100644 --- a/tests/unit/error_test.ts +++ b/tests/unit/error_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertThrows, fail } from "./test_util.ts"; Deno.test("Errors work", () => { diff --git a/tests/unit/esnext_test.ts b/tests/unit/esnext_test.ts index 1d5759aafa..2c597ef7ba 100644 --- a/tests/unit/esnext_test.ts +++ b/tests/unit/esnext_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; // TODO(@kitsonk) remove when we are no longer patching TypeScript to have diff --git a/tests/unit/event_source_test.ts b/tests/unit/event_source_test.ts index 242c12d6e8..2ce7178b00 100644 --- a/tests/unit/event_source_test.ts +++ b/tests/unit/event_source_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertStrictEquals } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/event_target_test.ts b/tests/unit/event_target_test.ts index 3f7d8ee24a..ca1c496143 100644 --- a/tests/unit/event_target_test.ts +++ b/tests/unit/event_target_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/event_test.ts b/tests/unit/event_test.ts index bd398fd410..d022e1c1d1 100644 --- a/tests/unit/event_test.ts +++ b/tests/unit/event_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertStringIncludes } from "./test_util.ts"; Deno.test(function eventInitializedWithType() { diff --git a/tests/unit/fetch_test.ts b/tests/unit/fetch_test.ts index 298a266903..094b963e19 100644 --- a/tests/unit/fetch_test.ts +++ b/tests/unit/fetch_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit/ffi_test.ts b/tests/unit/ffi_test.ts index 98338b15e6..9db0ad0c26 100644 --- a/tests/unit/ffi_test.ts +++ b/tests/unit/ffi_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/file_test.ts b/tests/unit/file_test.ts index 1af3a3f84d..38e4dce64a 100644 --- a/tests/unit/file_test.ts +++ b/tests/unit/file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; // deno-lint-ignore no-explicit-any diff --git a/tests/unit/filereader_test.ts b/tests/unit/filereader_test.ts index 158cf53835..21ca25ed48 100644 --- a/tests/unit/filereader_test.ts +++ b/tests/unit/filereader_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test(function fileReaderConstruct() { diff --git a/tests/unit/files_test.ts b/tests/unit/files_test.ts index c22d3deb77..aa211b224d 100644 --- a/tests/unit/files_test.ts +++ b/tests/unit/files_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/fs_events_test.ts b/tests/unit/fs_events_test.ts index 7489626b9f..d9bdf454d5 100644 --- a/tests/unit/fs_events_test.ts +++ b/tests/unit/fs_events_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows, delay } from "./test_util.ts"; diff --git a/tests/unit/get_random_values_test.ts b/tests/unit/get_random_values_test.ts index 75aaf4c1b2..47ba78a325 100644 --- a/tests/unit/get_random_values_test.ts +++ b/tests/unit/get_random_values_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertNotEquals, assertStrictEquals } from "./test_util.ts"; Deno.test(function getRandomValuesInt8Array() { diff --git a/tests/unit/globals_test.ts b/tests/unit/globals_test.ts index 6de228e1c9..84773dbf47 100644 --- a/tests/unit/globals_test.ts +++ b/tests/unit/globals_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-node-globals import { diff --git a/tests/unit/headers_test.ts b/tests/unit/headers_test.ts index ea72f784b5..969b2a36f4 100644 --- a/tests/unit/headers_test.ts +++ b/tests/unit/headers_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "./test_util.ts"; const { inspectArgs, diff --git a/tests/unit/http_test.ts b/tests/unit/http_test.ts index 355b155afd..809b36227b 100644 --- a/tests/unit/http_test.ts +++ b/tests/unit/http_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-nocheck `Deno.serveHttp()` was soft-removed in Deno 2. // deno-lint-ignore-file no-deprecated-deno-api diff --git a/tests/unit/image_bitmap_test.ts b/tests/unit/image_bitmap_test.ts index 0066311820..ca5b85c178 100644 --- a/tests/unit/image_bitmap_test.ts +++ b/tests/unit/image_bitmap_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; diff --git a/tests/unit/image_data_test.ts b/tests/unit/image_data_test.ts index 7156301a05..c6dfb0d372 100644 --- a/tests/unit/image_data_test.ts +++ b/tests/unit/image_data_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; diff --git a/tests/unit/internals_test.ts b/tests/unit/internals_test.ts index bb4c21793e..7ca9b6d336 100644 --- a/tests/unit/internals_test.ts +++ b/tests/unit/internals_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; Deno.test(function internalsExists() { diff --git a/tests/unit/intl_test.ts b/tests/unit/intl_test.ts index 6e4de378c9..e177dc45b0 100644 --- a/tests/unit/intl_test.ts +++ b/tests/unit/intl_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test("Intl.v8BreakIterator should be undefined", () => { diff --git a/tests/unit/jupyter_test.ts b/tests/unit/jupyter_test.ts index 07defe2305..e29bb2b300 100644 --- a/tests/unit/jupyter_test.ts +++ b/tests/unit/jupyter_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/kv_queue_test.ts b/tests/unit/kv_queue_test.ts index d92977169f..569eb92800 100644 --- a/tests/unit/kv_queue_test.ts +++ b/tests/unit/kv_queue_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertFalse } from "./test_util.ts"; Deno.test({}, async function queueTestDbClose() { diff --git a/tests/unit/kv_queue_test_no_db_close.ts b/tests/unit/kv_queue_test_no_db_close.ts index 947e1c5e62..7c4bbf271e 100644 --- a/tests/unit/kv_queue_test_no_db_close.ts +++ b/tests/unit/kv_queue_test_no_db_close.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertNotEquals } from "./test_util.ts"; Deno.test({ diff --git a/tests/unit/kv_queue_undelivered_test.ts b/tests/unit/kv_queue_undelivered_test.ts index 1fcefe7e26..2fbfefbd69 100644 --- a/tests/unit/kv_queue_undelivered_test.ts +++ b/tests/unit/kv_queue_undelivered_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; const sleep = (time: number) => new Promise((r) => setTimeout(r, time)); diff --git a/tests/unit/kv_test.ts b/tests/unit/kv_test.ts index e603bc196d..b47d3118c7 100644 --- a/tests/unit/kv_test.ts +++ b/tests/unit/kv_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/link_test.ts b/tests/unit/link_test.ts index dfa72479c5..5093c5720d 100644 --- a/tests/unit/link_test.ts +++ b/tests/unit/link_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/lint_plugin_test.ts b/tests/unit/lint_plugin_test.ts index 38a7e1b091..5b00f49d01 100644 --- a/tests/unit/lint_plugin_test.ts +++ b/tests/unit/lint_plugin_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; diff --git a/tests/unit/lint_selectors_test.ts b/tests/unit/lint_selectors_test.ts index 0909a4907a..c116871318 100644 --- a/tests/unit/lint_selectors_test.ts +++ b/tests/unit/lint_selectors_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert/equals"; import { diff --git a/tests/unit/make_temp_test.ts b/tests/unit/make_temp_test.ts index 32383387b5..d22b633091 100644 --- a/tests/unit/make_temp_test.ts +++ b/tests/unit/make_temp_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/message_channel_test.ts b/tests/unit/message_channel_test.ts index 0fcbc5e95d..bf7bb6443a 100644 --- a/tests/unit/message_channel_test.ts +++ b/tests/unit/message_channel_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // NOTE: these are just sometests to test the TypeScript types. Real coverage is // provided by WPT. import { assert, assertEquals } from "@std/assert"; diff --git a/tests/unit/mkdir_test.ts b/tests/unit/mkdir_test.ts index def77cd3e4..7b71e4a799 100644 --- a/tests/unit/mkdir_test.ts +++ b/tests/unit/mkdir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/navigator_test.ts b/tests/unit/navigator_test.ts index 5dcc423fa5..955c34dcc4 100644 --- a/tests/unit/navigator_test.ts +++ b/tests/unit/navigator_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; Deno.test(function navigatorNumCpus() { diff --git a/tests/unit/net_test.ts b/tests/unit/net_test.ts index cfa42b3d36..9c2819c30a 100644 --- a/tests/unit/net_test.ts +++ b/tests/unit/net_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/network_interfaces_test.ts b/tests/unit/network_interfaces_test.ts index 160efbfe60..675400365f 100644 --- a/tests/unit/network_interfaces_test.ts +++ b/tests/unit/network_interfaces_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; diff --git a/tests/unit/ops_test.ts b/tests/unit/ops_test.ts index 631e5c5736..60f67cdca6 100644 --- a/tests/unit/ops_test.ts +++ b/tests/unit/ops_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. const EXPECTED_OP_COUNT = 13; diff --git a/tests/unit/os_test.ts b/tests/unit/os_test.ts index a70796505f..36b6daad2f 100644 --- a/tests/unit/os_test.ts +++ b/tests/unit/os_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/path_from_url_test.ts b/tests/unit/path_from_url_test.ts index b3a6406bcb..3785f2bbb5 100644 --- a/tests/unit/path_from_url_test.ts +++ b/tests/unit/path_from_url_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/performance_test.ts b/tests/unit/performance_test.ts index fa056c7f54..22f51a4ce6 100644 --- a/tests/unit/performance_test.ts +++ b/tests/unit/performance_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/permissions_test.ts b/tests/unit/permissions_test.ts index f981b10fd6..8bb1633423 100644 --- a/tests/unit/permissions_test.ts +++ b/tests/unit/permissions_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/process_test.ts b/tests/unit/process_test.ts index 5cbab3b4c5..17589d8581 100644 --- a/tests/unit/process_test.ts +++ b/tests/unit/process_test.ts @@ -1,5 +1,5 @@ // deno-lint-ignore-file no-deprecated-deno-api -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/progressevent_test.ts b/tests/unit/progressevent_test.ts index 809c2ad391..3e7aa9ec15 100644 --- a/tests/unit/progressevent_test.ts +++ b/tests/unit/progressevent_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test(function progressEventConstruct() { diff --git a/tests/unit/promise_hooks_test.ts b/tests/unit/promise_hooks_test.ts index f7c44155d1..7fd7a8f51d 100644 --- a/tests/unit/promise_hooks_test.ts +++ b/tests/unit/promise_hooks_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; diff --git a/tests/unit/quic_test.ts b/tests/unit/quic_test.ts index f5423327de..63163e15d6 100644 --- a/tests/unit/quic_test.ts +++ b/tests/unit/quic_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; diff --git a/tests/unit/read_dir_test.ts b/tests/unit/read_dir_test.ts index b00495eb45..9c5e6da79f 100644 --- a/tests/unit/read_dir_test.ts +++ b/tests/unit/read_dir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/read_file_test.ts b/tests/unit/read_file_test.ts index 7123833e9c..5716816b37 100644 --- a/tests/unit/read_file_test.ts +++ b/tests/unit/read_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/read_link_test.ts b/tests/unit/read_link_test.ts index c89ffe4927..7ecee2d567 100644 --- a/tests/unit/read_link_test.ts +++ b/tests/unit/read_link_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, diff --git a/tests/unit/read_text_file_test.ts b/tests/unit/read_text_file_test.ts index 1ec57bde35..39cd3e798c 100644 --- a/tests/unit/read_text_file_test.ts +++ b/tests/unit/read_text_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/real_path_test.ts b/tests/unit/real_path_test.ts index 7832846308..79dd0c084f 100644 --- a/tests/unit/real_path_test.ts +++ b/tests/unit/real_path_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/ref_unref_test.ts b/tests/unit/ref_unref_test.ts index 6f5bcf0a77..751f2af387 100644 --- a/tests/unit/ref_unref_test.ts +++ b/tests/unit/ref_unref_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertNotEquals, execCode } from "./test_util.ts"; diff --git a/tests/unit/remove_test.ts b/tests/unit/remove_test.ts index 261ff6bd05..38421f8610 100644 --- a/tests/unit/remove_test.ts +++ b/tests/unit/remove_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertRejects, assertThrows } from "./test_util.ts"; const REMOVE_METHODS = ["remove", "removeSync"] as const; diff --git a/tests/unit/rename_test.ts b/tests/unit/rename_test.ts index 3162c699c6..bacd54c675 100644 --- a/tests/unit/rename_test.ts +++ b/tests/unit/rename_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/request_test.ts b/tests/unit/request_test.ts index fe34c20a50..fa7619cd93 100644 --- a/tests/unit/request_test.ts +++ b/tests/unit/request_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertStringIncludes } from "./test_util.ts"; Deno.test(async function fromInit() { diff --git a/tests/unit/response_test.ts b/tests/unit/response_test.ts index bbdd5f481d..383790af3a 100644 --- a/tests/unit/response_test.ts +++ b/tests/unit/response_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/serve_test.ts b/tests/unit/serve_test.ts index f5896bc64b..7aefd5b7d3 100644 --- a/tests/unit/serve_test.ts +++ b/tests/unit/serve_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit/signal_test.ts b/tests/unit/signal_test.ts index 8923aa75bf..bfaa9aac90 100644 --- a/tests/unit/signal_test.ts +++ b/tests/unit/signal_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, delay } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/stat_test.ts b/tests/unit/stat_test.ts index 0609035b41..0f10f6e2d6 100644 --- a/tests/unit/stat_test.ts +++ b/tests/unit/stat_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/stdio_test.ts b/tests/unit/stdio_test.ts index d24fdc8efa..c605ccb9ad 100644 --- a/tests/unit/stdio_test.ts +++ b/tests/unit/stdio_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test(async function stdioStdinRead() { diff --git a/tests/unit/streams_test.ts b/tests/unit/streams_test.ts index 53225a1553..84a87d166d 100644 --- a/tests/unit/streams_test.ts +++ b/tests/unit/streams_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, diff --git a/tests/unit/structured_clone_test.ts b/tests/unit/structured_clone_test.ts index 6e0473f9a9..fc5d68e9da 100644 --- a/tests/unit/structured_clone_test.ts +++ b/tests/unit/structured_clone_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/symbol_test.ts b/tests/unit/symbol_test.ts index 54db7f5bae..2dca022b14 100644 --- a/tests/unit/symbol_test.ts +++ b/tests/unit/symbol_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert } from "./test_util.ts"; // Test that `Symbol.metadata` is defined. This file can be removed when V8 diff --git a/tests/unit/symlink_test.ts b/tests/unit/symlink_test.ts index 47a685ec61..cdf0ea5bbd 100644 --- a/tests/unit/symlink_test.ts +++ b/tests/unit/symlink_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertRejects, diff --git a/tests/unit/test_util.ts b/tests/unit/test_util.ts index a987cb5427..6e7865ea7a 100644 --- a/tests/unit/test_util.ts +++ b/tests/unit/test_util.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as colors from "@std/fmt/colors"; import { assert } from "@std/assert"; diff --git a/tests/unit/testing_test.ts b/tests/unit/testing_test.ts index 51372c42b0..96104df9c4 100644 --- a/tests/unit/testing_test.ts +++ b/tests/unit/testing_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; Deno.test(function testWrongOverloads() { diff --git a/tests/unit/text_encoding_test.ts b/tests/unit/text_encoding_test.ts index 719e5907e4..1f378b2125 100644 --- a/tests/unit/text_encoding_test.ts +++ b/tests/unit/text_encoding_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/timers_test.ts b/tests/unit/timers_test.ts index 580d8c524e..29d338c761 100644 --- a/tests/unit/timers_test.ts +++ b/tests/unit/timers_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit/tls_sni_test.ts b/tests/unit/tls_sni_test.ts index a8d51108e7..3874753ded 100644 --- a/tests/unit/tls_sni_test.ts +++ b/tests/unit/tls_sni_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects } from "./test_util.ts"; // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol const { resolverSymbol, serverNameSymbol } = Deno[Deno.internal]; diff --git a/tests/unit/tls_test.ts b/tests/unit/tls_test.ts index 219f4a4508..53db347a13 100644 --- a/tests/unit/tls_test.ts +++ b/tests/unit/tls_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/truncate_test.ts b/tests/unit/truncate_test.ts index cebd6e8ee1..ae41b82271 100644 --- a/tests/unit/truncate_test.ts +++ b/tests/unit/truncate_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/tty_color_test.ts b/tests/unit/tty_color_test.ts index 6f26891e37..a1ca43951c 100644 --- a/tests/unit/tty_color_test.ts +++ b/tests/unit/tty_color_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; // Note tests for Deno.stdin.setRaw is in integration tests. diff --git a/tests/unit/tty_test.ts b/tests/unit/tty_test.ts index 1d29a0b706..352c313b43 100644 --- a/tests/unit/tty_test.ts +++ b/tests/unit/tty_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-deprecated-deno-api diff --git a/tests/unit/umask_test.ts b/tests/unit/umask_test.ts index 0e97f0d353..c7cdd68e4c 100644 --- a/tests/unit/umask_test.ts +++ b/tests/unit/umask_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/url_search_params_test.ts b/tests/unit/url_search_params_test.ts index d682c291a9..9846c7a91c 100644 --- a/tests/unit/url_search_params_test.ts +++ b/tests/unit/url_search_params_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; Deno.test(function urlSearchParamsWithMultipleSpaces() { diff --git a/tests/unit/url_test.ts b/tests/unit/url_test.ts index b0dc86232b..5643fbd258 100644 --- a/tests/unit/url_test.ts +++ b/tests/unit/url_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/urlpattern_test.ts b/tests/unit/urlpattern_test.ts index 3c1fb0cf19..4d815cd042 100644 --- a/tests/unit/urlpattern_test.ts +++ b/tests/unit/urlpattern_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; import { assertType, IsExact } from "@std/testing/types"; diff --git a/tests/unit/utime_test.ts b/tests/unit/utime_test.ts index 7a1fee74eb..5669499aaf 100644 --- a/tests/unit/utime_test.ts +++ b/tests/unit/utime_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, diff --git a/tests/unit/version_test.ts b/tests/unit/version_test.ts index 307295ad87..fd570fb16d 100644 --- a/tests/unit/version_test.ts +++ b/tests/unit/version_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "./test_util.ts"; diff --git a/tests/unit/wasm_test.ts b/tests/unit/wasm_test.ts index 8ee9392f93..80bc468f7d 100644 --- a/tests/unit/wasm_test.ts +++ b/tests/unit/wasm_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertRejects } from "./test_util.ts"; diff --git a/tests/unit/webcrypto_test.ts b/tests/unit/webcrypto_test.ts index 09552a0587..bc5b307de5 100644 --- a/tests/unit/webcrypto_test.ts +++ b/tests/unit/webcrypto_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit/webgpu_test.ts b/tests/unit/webgpu_test.ts index aac75d3420..f7bfc0bbc2 100644 --- a/tests/unit/webgpu_test.ts +++ b/tests/unit/webgpu_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/websocket_test.ts b/tests/unit/websocket_test.ts index d9878828db..1c8c4581bf 100644 --- a/tests/unit/websocket_test.ts +++ b/tests/unit/websocket_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows, fail } from "./test_util.ts"; const servePort = 4248; diff --git a/tests/unit/websocketstream_test.ts.disabled b/tests/unit/websocketstream_test.ts.disabled index 4f8c321c92..7a64318d47 100644 --- a/tests/unit/websocketstream_test.ts.disabled +++ b/tests/unit/websocketstream_test.ts.disabled @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, diff --git a/tests/unit/webstorage_test.ts b/tests/unit/webstorage_test.ts index aa832b1c4b..1c2f9cfa34 100644 --- a/tests/unit/webstorage_test.ts +++ b/tests/unit/webstorage_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any import { assert, assertEquals, assertThrows } from "./test_util.ts"; diff --git a/tests/unit/worker_permissions_test.ts b/tests/unit/worker_permissions_test.ts index 28bf9f92af..20736357f5 100644 --- a/tests/unit/worker_permissions_test.ts +++ b/tests/unit/worker_permissions_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; Deno.test( diff --git a/tests/unit/worker_test.ts b/tests/unit/worker_test.ts index 42c257282c..c0777bb78b 100644 --- a/tests/unit/worker_test.ts +++ b/tests/unit/worker_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit/write_file_test.ts b/tests/unit/write_file_test.ts index 15e462cca9..53a5434e04 100644 --- a/tests/unit/write_file_test.ts +++ b/tests/unit/write_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit/write_text_file_test.ts b/tests/unit/write_text_file_test.ts index 9e1b75326b..f8209250a1 100644 --- a/tests/unit/write_text_file_test.ts +++ b/tests/unit/write_text_file_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit_node/_fs/_fs_access_test.ts b/tests/unit_node/_fs/_fs_access_test.ts index 0881769f2c..1333d9b7c8 100644 --- a/tests/unit_node/_fs/_fs_access_test.ts +++ b/tests/unit_node/_fs/_fs_access_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as fs from "node:fs"; import { assertRejects, assertThrows } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_appendFile_test.ts b/tests/unit_node/_fs/_fs_appendFile_test.ts index 5ee8eabed4..d6f45786cb 100644 --- a/tests/unit_node/_fs/_fs_appendFile_test.ts +++ b/tests/unit_node/_fs/_fs_appendFile_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { appendFile, appendFileSync } from "node:fs"; import { fromFileUrl } from "@std/path"; diff --git a/tests/unit_node/_fs/_fs_chmod_test.ts b/tests/unit_node/_fs/_fs_chmod_test.ts index 97dc9ecdda..db097397e2 100644 --- a/tests/unit_node/_fs/_fs_chmod_test.ts +++ b/tests/unit_node/_fs/_fs_chmod_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertRejects, assertThrows, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { chmod, chmodSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_chown_test.ts b/tests/unit_node/_fs/_fs_chown_test.ts index 6cb2f576ad..d9e6870ac6 100644 --- a/tests/unit_node/_fs/_fs_chown_test.ts +++ b/tests/unit_node/_fs/_fs_chown_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { chown, chownSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_close_test.ts b/tests/unit_node/_fs/_fs_close_test.ts index 8880bc0461..62f2d4f05e 100644 --- a/tests/unit_node/_fs/_fs_close_test.ts +++ b/tests/unit_node/_fs/_fs_close_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertThrows, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { close, closeSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_copy_test.ts b/tests/unit_node/_fs/_fs_copy_test.ts index 22f30ea373..b52f1951c6 100644 --- a/tests/unit_node/_fs/_fs_copy_test.ts +++ b/tests/unit_node/_fs/_fs_copy_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { assert } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_dir_test.ts b/tests/unit_node/_fs/_fs_dir_test.ts index 0f41543e92..7da76e50fb 100644 --- a/tests/unit_node/_fs/_fs_dir_test.ts +++ b/tests/unit_node/_fs/_fs_dir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { Dir as DirOrig, type Dirent } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_dirent_test.ts b/tests/unit_node/_fs/_fs_dirent_test.ts index 093617fafd..a709163f4a 100644 --- a/tests/unit_node/_fs/_fs_dirent_test.ts +++ b/tests/unit_node/_fs/_fs_dirent_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertThrows } from "@std/assert"; import { Dirent as Dirent_ } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_exists_test.ts b/tests/unit_node/_fs/_fs_exists_test.ts index 7ed7584ae6..3de7afdc18 100644 --- a/tests/unit_node/_fs/_fs_exists_test.ts +++ b/tests/unit_node/_fs/_fs_exists_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertStringIncludes } from "@std/assert"; import { exists, existsSync } from "node:fs"; import { promisify } from "node:util"; diff --git a/tests/unit_node/_fs/_fs_fdatasync_test.ts b/tests/unit_node/_fs/_fs_fdatasync_test.ts index 40ca2969f9..59c2b5657f 100644 --- a/tests/unit_node/_fs/_fs_fdatasync_test.ts +++ b/tests/unit_node/_fs/_fs_fdatasync_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { fdatasync, fdatasyncSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_fstat_test.ts b/tests/unit_node/_fs/_fs_fstat_test.ts index afcac836f6..79e5106525 100644 --- a/tests/unit_node/_fs/_fs_fstat_test.ts +++ b/tests/unit_node/_fs/_fs_fstat_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { fstat, fstatSync } from "node:fs"; import { fail } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_fsync_test.ts b/tests/unit_node/_fs/_fs_fsync_test.ts index 3c1509410e..cd01786a2f 100644 --- a/tests/unit_node/_fs/_fs_fsync_test.ts +++ b/tests/unit_node/_fs/_fs_fsync_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { fsync, fsyncSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_ftruncate_test.ts b/tests/unit_node/_fs/_fs_ftruncate_test.ts index 974f8f168e..b51ac6c2ca 100644 --- a/tests/unit_node/_fs/_fs_ftruncate_test.ts +++ b/tests/unit_node/_fs/_fs_ftruncate_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { ftruncate, ftruncateSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_futimes_test.ts b/tests/unit_node/_fs/_fs_futimes_test.ts index 9b0c64efd6..207610abed 100644 --- a/tests/unit_node/_fs/_fs_futimes_test.ts +++ b/tests/unit_node/_fs/_fs_futimes_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { futimes, futimesSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_handle_test.ts b/tests/unit_node/_fs/_fs_handle_test.ts index 84d72c0745..61214bde26 100644 --- a/tests/unit_node/_fs/_fs_handle_test.ts +++ b/tests/unit_node/_fs/_fs_handle_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { Buffer } from "node:buffer"; import * as fs from "node:fs/promises"; diff --git a/tests/unit_node/_fs/_fs_link_test.ts b/tests/unit_node/_fs/_fs_link_test.ts index 3062db7a67..fca1dfe869 100644 --- a/tests/unit_node/_fs/_fs_link_test.ts +++ b/tests/unit_node/_fs/_fs_link_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { assert, assertEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_lstat_test.ts b/tests/unit_node/_fs/_fs_lstat_test.ts index 6a42443452..196d5f23b7 100644 --- a/tests/unit_node/_fs/_fs_lstat_test.ts +++ b/tests/unit_node/_fs/_fs_lstat_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { lstat, lstatSync } from "node:fs"; import { fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_mkdir_test.ts b/tests/unit_node/_fs/_fs_mkdir_test.ts index b9f6adf44c..d3476a23a7 100644 --- a/tests/unit_node/_fs/_fs_mkdir_test.ts +++ b/tests/unit_node/_fs/_fs_mkdir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { assert } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_mkdtemp_test.ts b/tests/unit_node/_fs/_fs_mkdtemp_test.ts index 47530b495a..4e130d4b9d 100644 --- a/tests/unit_node/_fs/_fs_mkdtemp_test.ts +++ b/tests/unit_node/_fs/_fs_mkdtemp_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertRejects, assertThrows } from "@std/assert"; import { EncodingOption, existsSync, mkdtemp, mkdtempSync } from "node:fs"; import { env } from "node:process"; diff --git a/tests/unit_node/_fs/_fs_open_test.ts b/tests/unit_node/_fs/_fs_open_test.ts index b4679cbab6..cd8cdf791b 100644 --- a/tests/unit_node/_fs/_fs_open_test.ts +++ b/tests/unit_node/_fs/_fs_open_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { O_APPEND, O_CREAT, diff --git a/tests/unit_node/_fs/_fs_opendir_test.ts b/tests/unit_node/_fs/_fs_opendir_test.ts index e22a47a702..37200d364f 100644 --- a/tests/unit_node/_fs/_fs_opendir_test.ts +++ b/tests/unit_node/_fs/_fs_opendir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit_node/_fs/_fs_readFile_test.ts b/tests/unit_node/_fs/_fs_readFile_test.ts index a75f12d1f6..0903777bca 100644 --- a/tests/unit_node/_fs/_fs_readFile_test.ts +++ b/tests/unit_node/_fs/_fs_readFile_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { promises, readFile, readFileSync } from "node:fs"; import * as path from "@std/path"; diff --git a/tests/unit_node/_fs/_fs_read_test.ts b/tests/unit_node/_fs/_fs_read_test.ts index 867ec01c5c..6def1ff71d 100644 --- a/tests/unit_node/_fs/_fs_read_test.ts +++ b/tests/unit_node/_fs/_fs_read_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// import { assert, diff --git a/tests/unit_node/_fs/_fs_readdir_test.ts b/tests/unit_node/_fs/_fs_readdir_test.ts index 3e36b1dc2d..b5071b5777 100644 --- a/tests/unit_node/_fs/_fs_readdir_test.ts +++ b/tests/unit_node/_fs/_fs_readdir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertNotEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { readdir, readdirSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_readlink_test.ts b/tests/unit_node/_fs/_fs_readlink_test.ts index 23c5e3896e..9443f2903f 100644 --- a/tests/unit_node/_fs/_fs_readlink_test.ts +++ b/tests/unit_node/_fs/_fs_readlink_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { readlink, readlinkSync } from "node:fs"; import { assert, assertEquals } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_realpath_test.ts b/tests/unit_node/_fs/_fs_realpath_test.ts index b0285cf545..25d266c4bf 100644 --- a/tests/unit_node/_fs/_fs_realpath_test.ts +++ b/tests/unit_node/_fs/_fs_realpath_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as path from "@std/path"; import { assertEquals } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_rename_test.ts b/tests/unit_node/_fs/_fs_rename_test.ts index 76d2e13141..91185e02a2 100644 --- a/tests/unit_node/_fs/_fs_rename_test.ts +++ b/tests/unit_node/_fs/_fs_rename_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { rename, renameSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_rm_test.ts b/tests/unit_node/_fs/_fs_rm_test.ts index 6ded2e5645..da02b60f30 100644 --- a/tests/unit_node/_fs/_fs_rm_test.ts +++ b/tests/unit_node/_fs/_fs_rm_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertRejects, assertThrows, fail } from "@std/assert"; import { rm, rmSync } from "node:fs"; import { existsSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_rmdir_test.ts b/tests/unit_node/_fs/_fs_rmdir_test.ts index db0c692a40..90d2c7c8a8 100644 --- a/tests/unit_node/_fs/_fs_rmdir_test.ts +++ b/tests/unit_node/_fs/_fs_rmdir_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { rmdir, rmdirSync } from "node:fs"; import { existsSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_stat_test.ts b/tests/unit_node/_fs/_fs_stat_test.ts index 3cbbe940b0..d10521d0cd 100644 --- a/tests/unit_node/_fs/_fs_stat_test.ts +++ b/tests/unit_node/_fs/_fs_stat_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { BigIntStats, stat, Stats, statSync } from "node:fs"; import { assert, assertEquals, fail } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_statfs_test.ts b/tests/unit_node/_fs/_fs_statfs_test.ts index fc85bcf174..51cbb0e552 100644 --- a/tests/unit_node/_fs/_fs_statfs_test.ts +++ b/tests/unit_node/_fs/_fs_statfs_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as fs from "node:fs"; import { assertEquals, assertRejects } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_symlink_test.ts b/tests/unit_node/_fs/_fs_symlink_test.ts index dfd6adb51a..d4a86bfc0f 100644 --- a/tests/unit_node/_fs/_fs_symlink_test.ts +++ b/tests/unit_node/_fs/_fs_symlink_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertThrows, fail } from "@std/assert"; import { symlink, symlinkSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_truncate_test.ts b/tests/unit_node/_fs/_fs_truncate_test.ts index 67bd021e45..ebb33a0c29 100644 --- a/tests/unit_node/_fs/_fs_truncate_test.ts +++ b/tests/unit_node/_fs/_fs_truncate_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { truncate, truncateSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_unlink_test.ts b/tests/unit_node/_fs/_fs_unlink_test.ts index f96217f8ef..a48ef58bc7 100644 --- a/tests/unit_node/_fs/_fs_unlink_test.ts +++ b/tests/unit_node/_fs/_fs_unlink_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, fail } from "@std/assert"; import { existsSync } from "node:fs"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; diff --git a/tests/unit_node/_fs/_fs_utimes_test.ts b/tests/unit_node/_fs/_fs_utimes_test.ts index 717d511c06..56233973ac 100644 --- a/tests/unit_node/_fs/_fs_utimes_test.ts +++ b/tests/unit_node/_fs/_fs_utimes_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows, fail } from "@std/assert"; import { utimes, utimesSync } from "node:fs"; diff --git a/tests/unit_node/_fs/_fs_watch_test.ts b/tests/unit_node/_fs/_fs_watch_test.ts index 963e0889f1..d611202066 100644 --- a/tests/unit_node/_fs/_fs_watch_test.ts +++ b/tests/unit_node/_fs/_fs_watch_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { unwatchFile, watch, watchFile } from "node:fs"; import { watch as watchPromise } from "node:fs/promises"; import { assert, assertEquals } from "@std/assert"; diff --git a/tests/unit_node/_fs/_fs_writeFile_test.ts b/tests/unit_node/_fs/_fs_writeFile_test.ts index 2733a2df0b..503faf8f4c 100644 --- a/tests/unit_node/_fs/_fs_writeFile_test.ts +++ b/tests/unit_node/_fs/_fs_writeFile_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, diff --git a/tests/unit_node/_fs/_fs_write_test.ts b/tests/unit_node/_fs/_fs_write_test.ts index 400fce73aa..6a5d4c9c26 100644 --- a/tests/unit_node/_fs/_fs_write_test.ts +++ b/tests/unit_node/_fs/_fs_write_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit_node/_test_utils.ts b/tests/unit_node/_test_utils.ts index c451ccd84b..a78444fd4a 100644 --- a/tests/unit_node/_test_utils.ts +++ b/tests/unit_node/_test_utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertStringIncludes } from "@std/assert"; diff --git a/tests/unit_node/assert_test.ts b/tests/unit_node/assert_test.ts index dc565458f6..f6dc18836d 100644 --- a/tests/unit_node/assert_test.ts +++ b/tests/unit_node/assert_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as assert from "node:assert"; Deno.test("[node/assert] .throws() compares Error instance", () => { diff --git a/tests/unit_node/assertion_error_test.ts b/tests/unit_node/assertion_error_test.ts index 4f8fe70ba5..60b0020d90 100644 --- a/tests/unit_node/assertion_error_test.ts +++ b/tests/unit_node/assertion_error_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { stripAnsiCode } from "@std/fmt/colors"; import { assert, assertStrictEquals } from "@std/assert"; import { AssertionError } from "node:assert"; diff --git a/tests/unit_node/async_hooks_test.ts b/tests/unit_node/async_hooks_test.ts index edad57bf76..d76898bcf5 100644 --- a/tests/unit_node/async_hooks_test.ts +++ b/tests/unit_node/async_hooks_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { AsyncLocalStorage, AsyncResource } from "node:async_hooks"; import process from "node:process"; import { setImmediate } from "node:timers"; diff --git a/tests/unit_node/buffer_test.ts b/tests/unit_node/buffer_test.ts index bd7f6edb17..3929769ffa 100644 --- a/tests/unit_node/buffer_test.ts +++ b/tests/unit_node/buffer_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { assertEquals, assertThrows } from "@std/assert"; diff --git a/tests/unit_node/child_process_test.ts b/tests/unit_node/child_process_test.ts index 0ea3c46cf0..bd875ad419 100644 --- a/tests/unit_node/child_process_test.ts +++ b/tests/unit_node/child_process_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import CP from "node:child_process"; import { Buffer } from "node:buffer"; diff --git a/tests/unit_node/cluster_test.ts b/tests/unit_node/cluster_test.ts index d9a59ae63a..e0335c4824 100644 --- a/tests/unit_node/cluster_test.ts +++ b/tests/unit_node/cluster_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import cluster from "node:cluster"; import * as clusterNamed from "node:cluster"; diff --git a/tests/unit_node/console_test.ts b/tests/unit_node/console_test.ts index 25d4a78e57..a9e3551074 100644 --- a/tests/unit_node/console_test.ts +++ b/tests/unit_node/console_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import vm from "node:vm"; import { stripAnsiCode } from "@std/fmt/colors"; diff --git a/tests/unit_node/crypto/crypto_cipher_gcm_test.ts b/tests/unit_node/crypto/crypto_cipher_gcm_test.ts index 16f6f56a9c..dd02ee5e32 100644 --- a/tests/unit_node/crypto/crypto_cipher_gcm_test.ts +++ b/tests/unit_node/crypto/crypto_cipher_gcm_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import crypto from "node:crypto"; import { Buffer } from "node:buffer"; diff --git a/tests/unit_node/crypto/crypto_cipher_test.ts b/tests/unit_node/crypto/crypto_cipher_test.ts index e40625c5a4..bc001c8d0f 100644 --- a/tests/unit_node/crypto/crypto_cipher_test.ts +++ b/tests/unit_node/crypto/crypto_cipher_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import crypto from "node:crypto"; import { Buffer } from "node:buffer"; import { Readable } from "node:stream"; diff --git a/tests/unit_node/crypto/crypto_hash_test.ts b/tests/unit_node/crypto/crypto_hash_test.ts index 0d4e307096..0a3fd2245e 100644 --- a/tests/unit_node/crypto/crypto_hash_test.ts +++ b/tests/unit_node/crypto/crypto_hash_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { createHash, createHmac, getHashes } from "node:crypto"; import { Buffer } from "node:buffer"; import { Readable } from "node:stream"; diff --git a/tests/unit_node/crypto/crypto_hkdf_test.ts b/tests/unit_node/crypto/crypto_hkdf_test.ts index 34102fd03d..dc128db584 100644 --- a/tests/unit_node/crypto/crypto_hkdf_test.ts +++ b/tests/unit_node/crypto/crypto_hkdf_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { hkdfSync } from "node:crypto"; import { assertEquals } from "@std/assert"; import { Buffer } from "node:buffer"; diff --git a/tests/unit_node/crypto/crypto_import_export.ts b/tests/unit_node/crypto/crypto_import_export.ts index fc41cbacc2..458178d578 100644 --- a/tests/unit_node/crypto/crypto_import_export.ts +++ b/tests/unit_node/crypto/crypto_import_export.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import crypto, { KeyFormat } from "node:crypto"; import path from "node:path"; import { Buffer } from "node:buffer"; diff --git a/tests/unit_node/crypto/crypto_key_test.ts b/tests/unit_node/crypto/crypto_key_test.ts index 82306d02fe..ac54a35419 100644 --- a/tests/unit_node/crypto/crypto_key_test.ts +++ b/tests/unit_node/crypto/crypto_key_test.ts @@ -1,6 +1,6 @@ // deno-lint-ignore-file no-explicit-any -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { createECDH, createHmac, diff --git a/tests/unit_node/crypto/crypto_misc_test.ts b/tests/unit_node/crypto/crypto_misc_test.ts index 9f72683398..3c49edd388 100644 --- a/tests/unit_node/crypto/crypto_misc_test.ts +++ b/tests/unit_node/crypto/crypto_misc_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { randomFillSync, randomUUID, timingSafeEqual } from "node:crypto"; import { Buffer } from "node:buffer"; import { assert, assertEquals, assertThrows } from "../../unit/test_util.ts"; diff --git a/tests/unit_node/crypto/crypto_pbkdf2_test.ts b/tests/unit_node/crypto/crypto_pbkdf2_test.ts index c937eb0c47..5eb4b82210 100644 --- a/tests/unit_node/crypto/crypto_pbkdf2_test.ts +++ b/tests/unit_node/crypto/crypto_pbkdf2_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { pbkdf2, pbkdf2Sync } from "node:crypto"; import { assert, assertEquals } from "@std/assert"; import nodeFixtures from "../testdata/crypto_digest_fixtures.json" with { diff --git a/tests/unit_node/crypto/crypto_scrypt_test.ts b/tests/unit_node/crypto/crypto_scrypt_test.ts index 03c08909ea..3f4d1437f0 100644 --- a/tests/unit_node/crypto/crypto_scrypt_test.ts +++ b/tests/unit_node/crypto/crypto_scrypt_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { scrypt, scryptSync } from "node:crypto"; import { Buffer } from "node:buffer"; import { assertEquals } from "@std/assert"; diff --git a/tests/unit_node/crypto/crypto_sign_test.ts b/tests/unit_node/crypto/crypto_sign_test.ts index 97c80b28af..24b049ca36 100644 --- a/tests/unit_node/crypto/crypto_sign_test.ts +++ b/tests/unit_node/crypto/crypto_sign_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "@std/assert"; import { diff --git a/tests/unit_node/crypto/generate_fixture.mjs b/tests/unit_node/crypto/generate_fixture.mjs index 3724fe4aff..3fb78e82c4 100644 --- a/tests/unit_node/crypto/generate_fixture.mjs +++ b/tests/unit_node/crypto/generate_fixture.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Run this file with `node` to regenerate the testdata/crypto_digest_fixtures.json file. import { readFileSync, writeFileSync } from "node:fs"; diff --git a/tests/unit_node/crypto/generate_keys.mjs b/tests/unit_node/crypto/generate_keys.mjs index 29d9f570f3..d422729a29 100644 --- a/tests/unit_node/crypto/generate_keys.mjs +++ b/tests/unit_node/crypto/generate_keys.mjs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { writeFileSync } from "node:fs"; import { join } from "node:path"; diff --git a/tests/unit_node/dgram_test.ts b/tests/unit_node/dgram_test.ts index 2ccdfbfb7a..521b2448ad 100644 --- a/tests/unit_node/dgram_test.ts +++ b/tests/unit_node/dgram_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import { execCode } from "../unit/test_util.ts"; diff --git a/tests/unit_node/domain_test.ts b/tests/unit_node/domain_test.ts index ddc56efae3..59b2c57b77 100644 --- a/tests/unit_node/domain_test.ts +++ b/tests/unit_node/domain_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Copyright © Benjamin Lupton // This code has been forked by https://github.com/bevry/domain-browser/commit/8bce7f4a093966ca850da75b024239ad5d0b33c6 diff --git a/tests/unit_node/events_test.ts b/tests/unit_node/events_test.ts index 8cfef6319a..152ace787b 100644 --- a/tests/unit_node/events_test.ts +++ b/tests/unit_node/events_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import events, { addAbortListener, EventEmitter } from "node:events"; diff --git a/tests/unit_node/fetch_test.ts b/tests/unit_node/fetch_test.ts index 399d6052a5..d005fd1d6e 100644 --- a/tests/unit_node/fetch_test.ts +++ b/tests/unit_node/fetch_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import { createReadStream } from "node:fs"; diff --git a/tests/unit_node/fs_test.ts b/tests/unit_node/fs_test.ts index 32bea40e75..2dcd45529f 100644 --- a/tests/unit_node/fs_test.ts +++ b/tests/unit_node/fs_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// import { assert, assertEquals, assertRejects, assertThrows } from "@std/assert"; diff --git a/tests/unit_node/http2_test.ts b/tests/unit_node/http2_test.ts index 90f2388124..698e6b4ebe 100644 --- a/tests/unit_node/http2_test.ts +++ b/tests/unit_node/http2_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit_node/http_test.ts b/tests/unit_node/http_test.ts index f30a4a20a3..7478546617 100644 --- a/tests/unit_node/http_test.ts +++ b/tests/unit_node/http_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit_node/inspector_test.ts b/tests/unit_node/inspector_test.ts index 0eb3f5a07b..ed94332108 100644 --- a/tests/unit_node/inspector_test.ts +++ b/tests/unit_node/inspector_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import inspector, { Session } from "node:inspector"; import inspectorPromises, { Session as SessionPromise, diff --git a/tests/unit_node/internal/_randomBytes_test.ts b/tests/unit_node/internal/_randomBytes_test.ts index ff3acf5e38..d479f44e76 100644 --- a/tests/unit_node/internal/_randomBytes_test.ts +++ b/tests/unit_node/internal/_randomBytes_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals, assertRejects, assertThrows } from "@std/assert"; import { assertCallbackErrorUncaught } from "../_test_utils.ts"; import { pseudoRandomBytes, randomBytes } from "node:crypto"; diff --git a/tests/unit_node/internal/_randomFill_test.ts b/tests/unit_node/internal/_randomFill_test.ts index 7b590666cd..d865b75f39 100644 --- a/tests/unit_node/internal/_randomFill_test.ts +++ b/tests/unit_node/internal/_randomFill_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Buffer } from "node:buffer"; import { randomFill, randomFillSync } from "node:crypto"; import { assertEquals, assertNotEquals, assertThrows } from "@std/assert"; diff --git a/tests/unit_node/internal/_randomInt_test.ts b/tests/unit_node/internal/_randomInt_test.ts index b0d9d771ee..ec2f869551 100644 --- a/tests/unit_node/internal/_randomInt_test.ts +++ b/tests/unit_node/internal/_randomInt_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { randomInt } from "node:crypto"; import { assert, assertThrows } from "@std/assert"; diff --git a/tests/unit_node/module_test.ts b/tests/unit_node/module_test.ts index 96c3504bd2..728a339fb6 100644 --- a/tests/unit_node/module_test.ts +++ b/tests/unit_node/module_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { builtinModules, diff --git a/tests/unit_node/net_test.ts b/tests/unit_node/net_test.ts index 83d751866f..1142e6201d 100644 --- a/tests/unit_node/net_test.ts +++ b/tests/unit_node/net_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tests/unit_node/os_test.ts b/tests/unit_node/os_test.ts index 78636e755d..99ad82c031 100644 --- a/tests/unit_node/os_test.ts +++ b/tests/unit_node/os_test.ts @@ -1,5 +1,5 @@ // deno-lint-ignore-file no-undef -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import os from "node:os"; import { diff --git a/tests/unit_node/path_test.ts b/tests/unit_node/path_test.ts index bd0711334d..63aa5ee2c1 100644 --- a/tests/unit_node/path_test.ts +++ b/tests/unit_node/path_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import path from "node:path"; import posix from "node:path/posix"; diff --git a/tests/unit_node/perf_hooks_test.ts b/tests/unit_node/perf_hooks_test.ts index 83d0062228..a77dd08538 100644 --- a/tests/unit_node/perf_hooks_test.ts +++ b/tests/unit_node/perf_hooks_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as perfHooks from "node:perf_hooks"; import { monitorEventLoopDelay, diff --git a/tests/unit_node/process_test.ts b/tests/unit_node/process_test.ts index 49de2dce1d..592bd6497f 100644 --- a/tests/unit_node/process_test.ts +++ b/tests/unit_node/process_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-undef no-console diff --git a/tests/unit_node/punycode_test.ts b/tests/unit_node/punycode_test.ts index fffa26164f..d4fa5b50a0 100644 --- a/tests/unit_node/punycode_test.ts +++ b/tests/unit_node/punycode_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import * as punycode from "node:punycode"; import { assertEquals } from "@std/assert"; diff --git a/tests/unit_node/querystring_test.ts b/tests/unit_node/querystring_test.ts index 09672df370..d57941264c 100644 --- a/tests/unit_node/querystring_test.ts +++ b/tests/unit_node/querystring_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import { parse, stringify } from "node:querystring"; diff --git a/tests/unit_node/readline_test.ts b/tests/unit_node/readline_test.ts index 68f0cd7c92..02fd6958bf 100644 --- a/tests/unit_node/readline_test.ts +++ b/tests/unit_node/readline_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { createInterface, Interface } from "node:readline"; import { assertInstanceOf } from "@std/assert"; import { Readable, Writable } from "node:stream"; diff --git a/tests/unit_node/repl_test.ts b/tests/unit_node/repl_test.ts index 693fdcc9eb..d0759f442f 100644 --- a/tests/unit_node/repl_test.ts +++ b/tests/unit_node/repl_test.ts @@ -1,5 +1,5 @@ // deno-lint-ignore-file no-undef -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import repl from "node:repl"; import { assert } from "@std/assert"; diff --git a/tests/unit_node/stream_test.ts b/tests/unit_node/stream_test.ts index b8542f6cfd..9c58ae03d6 100644 --- a/tests/unit_node/stream_test.ts +++ b/tests/unit_node/stream_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "@std/assert"; import { fromFileUrl, relative } from "@std/path"; diff --git a/tests/unit_node/string_decoder_test.ts b/tests/unit_node/string_decoder_test.ts index 34fbdd8810..1062373422 100644 --- a/tests/unit_node/string_decoder_test.ts +++ b/tests/unit_node/string_decoder_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "@std/assert"; import { Buffer } from "node:buffer"; import { StringDecoder } from "node:string_decoder"; diff --git a/tests/unit_node/timers_test.ts b/tests/unit_node/timers_test.ts index ecff32e763..43a3338096 100644 --- a/tests/unit_node/timers_test.ts +++ b/tests/unit_node/timers_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, fail } from "@std/assert"; import * as timers from "node:timers"; diff --git a/tests/unit_node/tls_test.ts b/tests/unit_node/tls_test.ts index 627b948cd1..f34d9efb5b 100644 --- a/tests/unit_node/tls_test.ts +++ b/tests/unit_node/tls_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit_node/tty_test.ts b/tests/unit_node/tty_test.ts index df2888ddd0..9acf9be614 100644 --- a/tests/unit_node/tty_test.ts +++ b/tests/unit_node/tty_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any import { assert } from "@std/assert"; diff --git a/tests/unit_node/util_test.ts b/tests/unit_node/util_test.ts index af174b0f4d..0f83bb1b9a 100644 --- a/tests/unit_node/util_test.ts +++ b/tests/unit_node/util_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit_node/v8_test.ts b/tests/unit_node/v8_test.ts index f7208fb0ef..7262ccd1fb 100644 --- a/tests/unit_node/v8_test.ts +++ b/tests/unit_node/v8_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { cachedDataVersionTag, deserialize, diff --git a/tests/unit_node/vm_test.ts b/tests/unit_node/vm_test.ts index 85b9556637..2876208e2f 100644 --- a/tests/unit_node/vm_test.ts +++ b/tests/unit_node/vm_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals, assertThrows } from "@std/assert"; import { createContext, diff --git a/tests/unit_node/wasi_test.ts b/tests/unit_node/wasi_test.ts index 6af2d4b1db..ae174e961b 100644 --- a/tests/unit_node/wasi_test.ts +++ b/tests/unit_node/wasi_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import wasi from "node:wasi"; import { assertThrows } from "@std/assert"; diff --git a/tests/unit_node/worker_threads_test.ts b/tests/unit_node/worker_threads_test.ts index 5f38d51d4d..c994b91eb1 100644 --- a/tests/unit_node/worker_threads_test.ts +++ b/tests/unit_node/worker_threads_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, diff --git a/tests/unit_node/zlib_test.ts b/tests/unit_node/zlib_test.ts index de2d2450d1..fb066a30d1 100644 --- a/tests/unit_node/zlib_test.ts +++ b/tests/unit_node/zlib_test.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertEquals } from "@std/assert"; import { fromFileUrl, relative } from "@std/path"; diff --git a/tests/util/server/Cargo.toml b/tests/util/server/Cargo.toml index efc81da17c..aec9f6e91c 100644 --- a/tests/util/server/Cargo.toml +++ b/tests/util/server/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2025 the Deno authors. MIT license. [package] name = "test_server" diff --git a/tests/util/server/src/assertions.rs b/tests/util/server/src/assertions.rs index c8b8845f4c..9eb39fb4a8 100644 --- a/tests/util/server/src/assertions.rs +++ b/tests/util/server/src/assertions.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io::Write; diff --git a/tests/util/server/src/builders.rs b/tests/util/server/src/builders.rs index 1cc1af2812..d5df8d09c8 100644 --- a/tests/util/server/src/builders.rs +++ b/tests/util/server/src/builders.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; diff --git a/tests/util/server/src/factory.rs b/tests/util/server/src/factory.rs index 4b972d9b0e..a13d673971 100644 --- a/tests/util/server/src/factory.rs +++ b/tests/util/server/src/factory.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashSet; use std::path::PathBuf; diff --git a/tests/util/server/src/fs.rs b/tests/util/server/src/fs.rs index 4bb3a7dd8c..19b94731eb 100644 --- a/tests/util/server/src/fs.rs +++ b/tests/util/server/src/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashSet; diff --git a/tests/util/server/src/https.rs b/tests/util/server/src/https.rs index ab90800a45..f3fc1291fe 100644 --- a/tests/util/server/src/https.rs +++ b/tests/util/server/src/https.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::io; use std::num::NonZeroUsize; use std::result::Result; diff --git a/tests/util/server/src/lib.rs b/tests/util/server/src/lib.rs index 6361dac16d..477568ab1b 100644 --- a/tests/util/server/src/lib.rs +++ b/tests/util/server/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::env; diff --git a/tests/util/server/src/lsp.rs b/tests/util/server/src/lsp.rs index 9da7042e20..8b16d17c6f 100644 --- a/tests/util/server/src/lsp.rs +++ b/tests/util/server/src/lsp.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::collections::HashSet; diff --git a/tests/util/server/src/macros.rs b/tests/util/server/src/macros.rs index e076583f19..52c84acf00 100644 --- a/tests/util/server/src/macros.rs +++ b/tests/util/server/src/macros.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #[macro_export] // https://stackoverflow.com/questions/38088067/equivalent-of-func-or-function-in-rust diff --git a/tests/util/server/src/npm.rs b/tests/util/server/src/npm.rs index 0261b2532c..da9a59cf2f 100644 --- a/tests/util/server/src/npm.rs +++ b/tests/util/server/src/npm.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; use std::fs; diff --git a/tests/util/server/src/pty.rs b/tests/util/server/src/pty.rs index fa22c4ecc8..d72617cd5e 100644 --- a/tests/util/server/src/pty.rs +++ b/tests/util/server/src/pty.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; diff --git a/tests/util/server/src/servers/grpc.rs b/tests/util/server/src/servers/grpc.rs index 144afc06a3..f097fbd595 100644 --- a/tests/util/server/src/servers/grpc.rs +++ b/tests/util/server/src/servers/grpc.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use futures::StreamExt; use h2; diff --git a/tests/util/server/src/servers/hyper_utils.rs b/tests/util/server/src/servers/hyper_utils.rs index dfb9464abe..a8b30ac24e 100644 --- a/tests/util/server/src/servers/hyper_utils.rs +++ b/tests/util/server/src/servers/hyper_utils.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::convert::Infallible; use std::io; diff --git a/tests/util/server/src/servers/jsr_registry.rs b/tests/util/server/src/servers/jsr_registry.rs index 418a0ebe29..b4b045087c 100644 --- a/tests/util/server/src/servers/jsr_registry.rs +++ b/tests/util/server/src/servers/jsr_registry.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/util/server/src/servers/mod.rs b/tests/util/server/src/servers/mod.rs index 4c8901b7cc..03f327319c 100644 --- a/tests/util/server/src/servers/mod.rs +++ b/tests/util/server/src/servers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // Usage: provide a port as argument to run hyper_hello benchmark server // otherwise this starts multiple servers on many ports for test endpoints. diff --git a/tests/util/server/src/servers/nodejs_org_mirror.rs b/tests/util/server/src/servers/nodejs_org_mirror.rs index 521e79d3c4..e3e94757c0 100644 --- a/tests/util/server/src/servers/nodejs_org_mirror.rs +++ b/tests/util/server/src/servers/nodejs_org_mirror.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. //! Server for NodeJS header tarballs, used by `node-gyp` in tests to download headers //! diff --git a/tests/util/server/src/servers/npm_registry.rs b/tests/util/server/src/servers/npm_registry.rs index fa8fa6a2f5..c814b83340 100644 --- a/tests/util/server/src/servers/npm_registry.rs +++ b/tests/util/server/src/servers/npm_registry.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::convert::Infallible; use std::net::Ipv6Addr; diff --git a/tests/util/server/src/servers/ws.rs b/tests/util/server/src/servers/ws.rs index f2637402fa..2b9189f3e1 100644 --- a/tests/util/server/src/servers/ws.rs +++ b/tests/util/server/src/servers/ws.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::pin::Pin; use std::result::Result; diff --git a/tests/util/server/src/spawn.rs b/tests/util/server/src/spawn.rs index c76168db53..936da7c785 100644 --- a/tests/util/server/src/spawn.rs +++ b/tests/util/server/src/spawn.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. use std::convert::Infallible; use anyhow::Error; diff --git a/tests/util/server/src/test_server.rs b/tests/util/server/src/test_server.rs index 3ae3eaa7d9..a118318e6f 100644 --- a/tests/util/server/src/test_server.rs +++ b/tests/util/server/src/test_server.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] diff --git a/tests/wpt/runner/runner.ts b/tests/wpt/runner/runner.ts index 6e654fd334..e124d28c7a 100644 --- a/tests/wpt/runner/runner.ts +++ b/tests/wpt/runner/runner.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { delay, join, diff --git a/tests/wpt/runner/testharnessreport.js b/tests/wpt/runner/testharnessreport.js index 7cc6a9e2dd..f5fce1efe3 100644 --- a/tests/wpt/runner/testharnessreport.js +++ b/tests/wpt/runner/testharnessreport.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. window.add_result_callback(({ message, name, stack, status }) => { const data = new TextEncoder().encode( diff --git a/tests/wpt/runner/utils.ts b/tests/wpt/runner/utils.ts index 140c388ec2..1d9426aed9 100644 --- a/tests/wpt/runner/utils.ts +++ b/tests/wpt/runner/utils.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. /// FLAGS import { parseArgs } from "@std/cli/parse-args"; diff --git a/tests/wpt/wpt.ts b/tests/wpt/wpt.ts index b13a10cf4c..ec4a77fba1 100755 --- a/tests/wpt/wpt.ts +++ b/tests/wpt/wpt.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-write --allow-read --allow-net --allow-env --allow-run --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/build_bench.ts b/tools/build_bench.ts index 400737561b..ccc72410ba 100755 --- a/tools/build_bench.ts +++ b/tools/build_bench.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-env --allow-read --allow-write --allow-run=git,cargo -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import $ from "https://deno.land/x/dax@0.32.0/mod.ts"; diff --git a/tools/build_benchmark_jsons.js b/tools/build_benchmark_jsons.js index 64310f75a7..6ed7cb0d83 100755 --- a/tools/build_benchmark_jsons.js +++ b/tools/build_benchmark_jsons.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { buildPath, existsSync, join } from "./util.js"; const currentDataFile = join(buildPath(), "bench.json"); diff --git a/tools/copyright_checker.js b/tools/copyright_checker.js index d7d196bc44..9ac84e3ec5 100755 --- a/tools/copyright_checker.js +++ b/tools/copyright_checker.js @@ -1,11 +1,11 @@ #!/usr/bin/env -S deno run --allow-read=. --allow-run=git --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console import { getSources, ROOT_PATH } from "./util.js"; -const copyrightYear = 2024; +const copyrightYear = 2025; const buffer = new Uint8Array(1024); const textDecoder = new TextDecoder(); @@ -63,7 +63,7 @@ export async function checkCopyright() { const ACCEPTABLE_LINES = /^(\/\/ deno-lint-.*|\/\/ Copyright.*|\/\/ Ported.*|\s*|#!\/.*)\n/; const COPYRIGHT_LINE = - `Copyright 2018-${copyrightYear} the Deno authors. All rights reserved. MIT license.`; + `Copyright 2018-${copyrightYear} the Deno authors. MIT license.`; const TOML_COPYRIGHT_LINE = "# " + COPYRIGHT_LINE; const C_STYLE_COPYRIGHT_LINE = "// " + COPYRIGHT_LINE; diff --git a/tools/format.js b/tools/format.js index b29667ca77..02211cdaf8 100755 --- a/tools/format.js +++ b/tools/format.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-all --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { join, ROOT_PATH } from "./util.js"; const subcommand = Deno.args.includes("--check") ? "check" : "fmt"; diff --git a/tools/generate_types_deno.ts b/tools/generate_types_deno.ts index 265b6f5371..e88b4bfa6f 100755 --- a/tools/generate_types_deno.ts +++ b/tools/generate_types_deno.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This script is used to generate the @types/deno package on DefinitelyTyped. diff --git a/tools/install_prebuilt.js b/tools/install_prebuilt.js index 0c983d405d..c7a58c4691 100755 --- a/tools/install_prebuilt.js +++ b/tools/install_prebuilt.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --unstable --allow-write --allow-read --allow-net --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { getPrebuilt } from "./util.js"; const args = Deno.args.slice(); diff --git a/tools/jsdoc_checker.js b/tools/jsdoc_checker.js index 241d04273b..034782136c 100755 --- a/tools/jsdoc_checker.js +++ b/tools/jsdoc_checker.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-read --allow-env --allow-sys --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { Node, Project, ts } from "npm:ts-morph@22.0.0"; import { join, ROOT_PATH } from "./util.js"; diff --git a/tools/lint.js b/tools/lint.js index 5bc3f2654f..3f548d5c3a 100755 --- a/tools/lint.js +++ b/tools/lint.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-all --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/napi/generate_symbols_lists.js b/tools/napi/generate_symbols_lists.js index efb0edc043..42942777f6 100755 --- a/tools/napi/generate_symbols_lists.js +++ b/tools/napi/generate_symbols_lists.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --allow-read --allow-write -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import exports from "../../ext/napi/sym/symbol_exports.json" with { type: "json", diff --git a/tools/ops.d.ts b/tools/ops.d.ts index 8acb1e5883..1d6dc8304a 100644 --- a/tools/ops.d.ts +++ b/tools/ops.d.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This file is intentionally empty - that puts this file into script mode, // which then allows all symbols to be imported from the file. diff --git a/tools/release/00_start_release.ts b/tools/release/00_start_release.ts index 125a76af66..a7a5d22a40 100755 --- a/tools/release/00_start_release.ts +++ b/tools/release/00_start_release.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --quiet --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/release/01_bump_crate_versions.ts b/tools/release/01_bump_crate_versions.ts index bef8011ba1..ddbc785c75 100755 --- a/tools/release/01_bump_crate_versions.ts +++ b/tools/release/01_bump_crate_versions.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { DenoWorkspace } from "./deno_workspace.ts"; import { $, GitLogOutput, semver } from "./deps.ts"; diff --git a/tools/release/02_create_pr.ts b/tools/release/02_create_pr.ts index 5ef64cd144..97f50f0d44 100755 --- a/tools/release/02_create_pr.ts +++ b/tools/release/02_create_pr.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { DenoWorkspace } from "./deno_workspace.ts"; import { $, createOctoKit, getGitHubRepository } from "./deps.ts"; diff --git a/tools/release/03_publish_crates.ts b/tools/release/03_publish_crates.ts index ecfb75e796..17ab2f71f7 100755 --- a/tools/release/03_publish_crates.ts +++ b/tools/release/03_publish_crates.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/release/04_post_publish.ts b/tools/release/04_post_publish.ts index 2fa9e73561..42bf2dbafd 100755 --- a/tools/release/04_post_publish.ts +++ b/tools/release/04_post_publish.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { DenoWorkspace } from "./deno_workspace.ts"; import { $, createOctoKit, getGitHubRepository } from "./deps.ts"; diff --git a/tools/release/05_create_release_notes.ts b/tools/release/05_create_release_notes.ts index 9ea9ade31f..383f5ba089 100755 --- a/tools/release/05_create_release_notes.ts +++ b/tools/release/05_create_release_notes.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { $ } from "./deps.ts"; import { DenoWorkspace } from "./deno_workspace.ts"; diff --git a/tools/release/deno_workspace.ts b/tools/release/deno_workspace.ts index e55a02b73f..e5d9f1b928 100644 --- a/tools/release/deno_workspace.ts +++ b/tools/release/deno_workspace.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { $, ReleasesMdFile, Repo } from "./deps.ts"; diff --git a/tools/release/deps.ts b/tools/release/deps.ts index 568830a74b..17177870d7 100644 --- a/tools/release/deps.ts +++ b/tools/release/deps.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. export * from "https://raw.githubusercontent.com/denoland/automation/0.19.0/mod.ts"; export * from "https://raw.githubusercontent.com/denoland/automation/0.19.0/github_actions.ts"; diff --git a/tools/release/npm/bin.cjs b/tools/release/npm/bin.cjs index 984aa350f1..aaa9dd343c 100644 --- a/tools/release/npm/bin.cjs +++ b/tools/release/npm/bin.cjs @@ -1,5 +1,5 @@ #!/usr/bin/env node -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // @ts-check const path = require("path"); diff --git a/tools/release/npm/build.ts b/tools/release/npm/build.ts index b1f1c45cbf..606ff6dbe8 100755 --- a/tools/release/npm/build.ts +++ b/tools/release/npm/build.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // NOTICE: This deployment/npm folder was lifted from https://github.com/dprint/dprint/blob/0ba79811cc96d2dee8e0cf766a8c8c0fc44879c2/deployment/npm/ // with permission (Copyright 2019-2023 David Sherret) import $ from "jsr:@david/dax@^0.42.0"; diff --git a/tools/release/npm/install.cjs b/tools/release/npm/install.cjs index 8bf9aabe47..5546818941 100644 --- a/tools/release/npm/install.cjs +++ b/tools/release/npm/install.cjs @@ -1,5 +1,5 @@ // @ts-check -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. "use strict"; require("./install_api.cjs").runInstall(); diff --git a/tools/release/npm/install_api.cjs b/tools/release/npm/install_api.cjs index 026d8ccc45..d2571839bc 100644 --- a/tools/release/npm/install_api.cjs +++ b/tools/release/npm/install_api.cjs @@ -1,5 +1,5 @@ // @ts-check -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. "use strict"; const fs = require("fs"); diff --git a/tools/release/promote_to_release.ts b/tools/release/promote_to_release.ts index 046f4d33a8..bca798d22c 100755 --- a/tools/release/promote_to_release.ts +++ b/tools/release/promote_to_release.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/upload_wptfyi.js b/tools/upload_wptfyi.js index 23dd4c6602..b08f352329 100644 --- a/tools/upload_wptfyi.js +++ b/tools/upload_wptfyi.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // This script pushes new WPT results to wpt.fyi. When the `--ghstatus` flag is // passed, will automatically add a status check to the commit with a link to diff --git a/tools/util.js b/tools/util.js index 8669337bff..07f405098c 100644 --- a/tools/util.js +++ b/tools/util.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/verify_pr_title.js b/tools/verify_pr_title.js index 90a045d3f4..dd623bb630 100644 --- a/tools/verify_pr_title.js +++ b/tools/verify_pr_title.js @@ -1,4 +1,4 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. // deno-lint-ignore-file no-console diff --git a/tools/wgpu_sync.js b/tools/wgpu_sync.js index 1d9e180d52..995a4b5975 100755 --- a/tools/wgpu_sync.js +++ b/tools/wgpu_sync.js @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run --unstable --allow-read --allow-write --allow-run --config=tests/config/deno.json -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2025 the Deno authors. MIT license. import { join, ROOT_PATH } from "./util.js"; From 29654133742fa6e0043fdac5c9ee63f095171f1c Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Tue, 31 Dec 2024 11:19:00 -0800 Subject: [PATCH 052/107] perf: build denort with `panic = "abort"` for releases (#27507) This PR changes CI to build denort with a separate, new build profile `release-slim` that disables unwinding and strips symbols. This reduces the size of denort by about 10% current denort: ``` FILE SIZE VM SIZE -------------- -------------- 58.1% 39.3Mi 57.9% 39.3Mi __TEXT,__text 31.5% 21.3Mi 31.4% 21.3Mi __TEXT,__const 2.5% 1.68Mi 2.5% 1.68Mi __DATA_CONST,__const 2.4% 1.62Mi 2.4% 1.62Mi __TEXT,__eh_frame 2.4% 1.60Mi 2.4% 1.60Mi __TEXT,__gcc_except_tab 0.9% 610Ki 0.9% 610Ki __TEXT,__cstring 0.8% 536Ki 0.8% 536Ki Code Signature 0.7% 507Ki 0.7% 507Ki __TEXT,__unwind_info 0.3% 207Ki 0.3% 207Ki Function Start Addresses 0.2% 165Ki 0.2% 165Ki __DATA,__data 0.0% 0 0.2% 153Ki __DATA,__bss 0.1% 51.0Ki 0.1% 51.0Ki Rebase Info 0.1% 45.3Ki 0.1% 45.3Ki __TEXT,__literals 0.0% 31.4Ki 0.1% 36.8Ki [15 Others] 0.0% 25.6Ki 0.0% 25.7Ki [__TEXT] 0.0% 19.3Ki 0.0% 20.2Ki [__DATA] 0.0% 8.11Ki 0.0% 8.11Ki Lazy Binding Info 0.0% 8 0.0% 8.08Ki [__LINKEDIT] 0.0% 6.84Ki 0.0% 6.84Ki Symbol Table 0.0% 5.55Ki 0.0% 5.55Ki String Table 0.0% 5.53Ki 0.0% 5.53Ki __TEXT,__ustring 100.0% 67.6Mi 100.0% 67.8Mi TOTAL ``` built with this PR: ``` FILE SIZE VM SIZE -------------- -------------- 59.6% 36.6Mi 59.5% 36.6Mi __TEXT,__text 34.6% 21.3Mi 34.5% 21.3Mi __TEXT,__const 2.7% 1.68Mi 2.7% 1.68Mi __DATA_CONST,__const 1.0% 610Ki 1.0% 610Ki __TEXT,__cstring 0.8% 487Ki 0.8% 487Ki Code Signature 0.3% 193Ki 0.3% 193Ki Function Start Addresses 0.3% 165Ki 0.3% 165Ki __DATA,__data 0.0% 0 0.2% 153Ki __DATA,__bss 0.2% 152Ki 0.2% 152Ki __TEXT,__unwind_info 0.1% 69.5Ki 0.1% 69.5Ki __TEXT,__eh_frame 0.1% 50.9Ki 0.1% 50.9Ki Rebase Info 0.1% 45.3Ki 0.1% 45.3Ki __TEXT,__literals 0.1% 34.1Ki 0.1% 39.5Ki [15 Others] 0.0% 19.3Ki 0.0% 20.2Ki [__DATA] 0.0% 19.6Ki 0.0% 19.7Ki [__TEXT] 0.0% 16.6Ki 0.0% 16.6Ki __TEXT,__gcc_except_tab 0.0% 8.09Ki 0.0% 8.09Ki Lazy Binding Info 0.0% 8 0.0% 7.69Ki [__LINKEDIT] 0.0% 6.83Ki 0.0% 6.83Ki Symbol Table 0.0% 5.77Ki 0.0% 5.77Ki [__DATA_CONST] 0.0% 5.53Ki 0.0% 5.53Ki __TEXT,__ustring 100.0% 61.4Mi 100.0% 61.6Mi TOTAL ``` A caveat is that this will increase release build times in CI since it requires building twice - once with unwinding and once without --- .github/workflows/ci.generate.ts | 30 +++++++++++++++++++++++------- .github/workflows/ci.yml | 25 ++++++++++++++++++------- Cargo.toml | 5 +++++ 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index d43d80d8bc..3cc559ec90 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -716,6 +716,19 @@ const ci = { "df -h", ].join("\n"), }, + { + name: "Build denort release", + if: [ + "matrix.job == 'test' &&", + "matrix.profile == 'release' &&", + "github.repository == 'denoland/deno'", + ].join("\n"), + run: [ + "df -h", + "cargo build --profile=release-slim --locked --bin denort", + "df -h", + ].join("\n"), + }, { // Run a minimal check to ensure that binary is not corrupted, regardless // of our build mode @@ -762,10 +775,11 @@ const ci = { "cd target/release", "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", - "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", + "cd ../release-slim", + "zip -r ../release/denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort", + "cd ../release", + "shasum -a 256 denort-${{ matrix.arch }}-unknown-linux-gnu.zip > denort-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum", ].join("\n"), }, { @@ -790,8 +804,9 @@ const ci = { "cd target/release", "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", - "zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort", + "cd ../release-slim", + "zip -r ../release/denort-${{ matrix.arch }}-apple-darwin.zip denort", + "cd ../release", "shasum -a 256 denort-${{ matrix.arch }}-apple-darwin.zip > denort-${{ matrix.arch }}-apple-darwin.zip.sha256sum", ] .join("\n"), @@ -808,8 +823,9 @@ const ci = { run: [ "Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip", "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", + + "Compress-Archive -CompressionLevel Optimal -Force -Path target/release-slim/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"), }, { diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc1aa89669..4fbaf82fcb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -419,6 +419,15 @@ jobs: df -h cargo build --release --locked --all-targets df -h + - name: Build denort release + if: |- + !(matrix.skip) && (matrix.job == 'test' && + matrix.profile == 'release' && + github.repository == 'denoland/deno') + run: |- + df -h + cargo build --profile=release-slim --locked --bin denort + df -h - name: Check deno binary if: '!(matrix.skip) && (matrix.job == ''test'')' run: 'target/${{ matrix.profile }}/deno eval "console.log(1+2)" | grep 3' @@ -448,10 +457,11 @@ jobs: cd target/release 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 - 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 + cd ../release-slim + zip -r ../release/denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort + cd ../release + shasum -a 256 denort-${{ matrix.arch }}-unknown-linux-gnu.zip > denort-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum - name: Pre-release (mac) if: |- !(matrix.skip) && (matrix.os == 'macos' && @@ -467,8 +477,9 @@ jobs: cd target/release 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 - zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort + cd ../release-slim + zip -r ../release/denort-${{ matrix.arch }}-apple-darwin.zip denort + cd ../release shasum -a 256 denort-${{ matrix.arch }}-apple-darwin.zip > denort-${{ matrix.arch }}-apple-darwin.zip.sha256sum - name: Pre-release (windows) if: |- @@ -480,8 +491,8 @@ jobs: run: |- Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip 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 + Compress-Archive -CompressionLevel Optimal -Force -Path target/release-slim/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' && diff --git a/Cargo.toml b/Cargo.toml index 1a7c04dbef..e4b9e0f496 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -251,6 +251,11 @@ incremental = true lto = true opt-level = 'z' # Optimize for size +[profile.release-slim] +inherits = "release" +panic = "abort" +strip = "symbols" + # Build release with debug symbols: cargo build --profile=release-with-debug [profile.release-with-debug] inherits = "release" From 8e618e1b2af244664774c8e3b18278b1247c8cd0 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 31 Dec 2024 15:36:57 -0500 Subject: [PATCH 053/107] fix(npm): deterministically output tags to initialized file (#27514) The tags were being sorted in a random order due to the package_reqs hashmap --- cli/npm/managed/resolvers/local.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 1e4e33ff3f..eb806ad02c 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -7,6 +7,7 @@ use std::cell::RefCell; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::BTreeMap; +use std::collections::BTreeSet; use std::collections::HashMap; use std::collections::HashSet; use std::fs; @@ -369,10 +370,10 @@ async fn sync_resolution_with_fs( ); let packages_with_deprecation_warnings = Arc::new(Mutex::new(Vec::new())); - let mut package_tags: HashMap<&PackageNv, Vec<&str>> = HashMap::new(); + let mut package_tags: HashMap<&PackageNv, BTreeSet<&str>> = HashMap::new(); for (package_req, package_nv) in snapshot.package_reqs() { if let Some(tag) = package_req.version_req.tag() { - package_tags.entry(package_nv).or_default().push(tag); + package_tags.entry(package_nv).or_default().insert(tag); } } @@ -392,7 +393,17 @@ async fn sync_resolution_with_fs( let folder_path = deno_local_registry_dir.join(&package_folder_name); let tags = package_tags .get(&package.id.nv) - .map(|tags| tags.join(",")) + .map(|tags| { + capacity_builder::StringBuilder::::build(|builder| { + for (i, tag) in tags.iter().enumerate() { + if i > 0 { + builder.append(',') + } + builder.append(*tag); + } + }) + .unwrap() + }) .unwrap_or_default(); enum PackageFolderState { UpToDate, From 7d66018874fc26d552657487c36913dd7947d235 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:08:15 -0800 Subject: [PATCH 054/107] chore: fix windows pre release CI job (#27518) See https://github.com/denoland/deno/actions/runs/12563186693/job/35024695065. Broke in #27507. --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index 3cc559ec90..cf023a6feb 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -825,7 +825,7 @@ const ci = { "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-slim/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", + "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"), }, { diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4fbaf82fcb..bcf83f0f13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -492,7 +492,7 @@ jobs: Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip 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-slim/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 + 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' && From 79c0b2ce73afac8532ed2844b1aed0eeccf0ce70 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Thu, 2 Jan 2025 13:31:47 +0000 Subject: [PATCH 055/107] fix(lsp): css preprocessor formatting (#27526) --- cli/lsp/documents.rs | 12 ++++++ tests/integration/lsp_tests.rs | 78 +++++++++++++++++++++++++++++++--- tests/util/server/src/lsp.rs | 3 ++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index a8398b4f2f..39408e2589 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -64,6 +64,9 @@ pub enum LanguageId { Markdown, Html, Css, + Scss, + Sass, + Less, Yaml, Sql, Svelte, @@ -86,6 +89,9 @@ impl LanguageId { LanguageId::Markdown => Some("md"), LanguageId::Html => Some("html"), LanguageId::Css => Some("css"), + LanguageId::Scss => Some("scss"), + LanguageId::Sass => Some("sass"), + LanguageId::Less => Some("less"), LanguageId::Yaml => Some("yaml"), LanguageId::Sql => Some("sql"), LanguageId::Svelte => Some("svelte"), @@ -107,6 +113,9 @@ impl LanguageId { LanguageId::Markdown => Some("text/markdown"), LanguageId::Html => Some("text/html"), LanguageId::Css => Some("text/css"), + LanguageId::Scss => None, + LanguageId::Sass => None, + LanguageId::Less => None, LanguageId::Yaml => Some("application/yaml"), LanguageId::Sql => None, LanguageId::Svelte => None, @@ -140,6 +149,9 @@ impl FromStr for LanguageId { "markdown" => Ok(Self::Markdown), "html" => Ok(Self::Html), "css" => Ok(Self::Css), + "scss" => Ok(Self::Scss), + "sass" => Ok(Self::Sass), + "less" => Ok(Self::Less), "yaml" => Ok(Self::Yaml), "sql" => Ok(Self::Sql), "svelte" => Ok(Self::Svelte), diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 23676c2f92..9efb34f337 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -11889,13 +11889,22 @@ fn lsp_format_html() { fn lsp_format_css() { let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); - let file = source_file(temp_dir.path().join("file.css"), " foo {}"); + let css_file = source_file(temp_dir.path().join("file.css"), " foo {}\n"); + let scss_file = source_file(temp_dir.path().join("file.scss"), " $font-stack: Helvetica, sans-serif;\n\nbody {\n font: 100% $font-stack;\n}\n"); + let sass_file = source_file( + temp_dir.path().join("file.sass"), + " $font-stack: Helvetica, sans-serif\n\nbody\n font: 100% $font-stack\n", + ); + let less_file = source_file( + temp_dir.path().join("file.less"), + " @width: 10px;\n\n#header {\n width: @width;\n}\n", + ); let mut client = context.new_lsp_command().build(); client.initialize_default(); let res = client.write_request( "textDocument/formatting", json!({ - "textDocument": { "uri": file.url() }, + "textDocument": { "uri": css_file.url() }, "options": { "tabSize": 2, "insertSpaces": true, @@ -11912,12 +11921,71 @@ fn lsp_format_css() { }, "newText": "", }, + ]), + ); + let res = client.write_request( + "textDocument/formatting", + json!({ + "textDocument": { "uri": scss_file.url() }, + "options": { + "tabSize": 2, + "insertSpaces": true, + }, + }), + ); + assert_eq!( + res, + json!([ { "range": { - "start": { "line": 0, "character": 8 }, - "end": { "line": 0, "character": 8 }, + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 2 }, }, - "newText": "\n", + "newText": "", + }, + ]), + ); + let res = client.write_request( + "textDocument/formatting", + json!({ + "textDocument": { "uri": sass_file.url() }, + "options": { + "tabSize": 2, + "insertSpaces": true, + }, + }), + ); + assert_eq!( + res, + json!([ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 2 }, + }, + "newText": "", + }, + ]), + ); + let res = client.write_request( + "textDocument/formatting", + json!({ + "textDocument": { "uri": less_file.url() }, + "options": { + "tabSize": 2, + "insertSpaces": true, + }, + }), + ); + assert_eq!( + res, + json!([ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 2 }, + }, + "newText": "", }, ]), ); diff --git a/tests/util/server/src/lsp.rs b/tests/util/server/src/lsp.rs index 8b16d17c6f..12593578c3 100644 --- a/tests/util/server/src/lsp.rs +++ b/tests/util/server/src/lsp.rs @@ -1289,6 +1289,9 @@ impl SourceFile { "md" => "markdown", "html" => "html", "css" => "css", + "scss" => "scss", + "sass" => "sass", + "less" => "less", "yaml" => "yaml", "sql" => "sql", "svelte" => "svelte", From 9215aa60a6e043b4ddb8ed6c43644cc1b11a5f58 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 2 Jan 2025 10:05:52 -0500 Subject: [PATCH 056/107] refactor(node/npm): separate out permission check from npm resolvers (#27511) Decouples permissions from the npm resolvers (towards moving the resolvers out of the cli crate) --- cli/factory.rs | 20 ++++- cli/module_loader.rs | 21 ++++-- cli/npm/byonm.rs | 18 ----- cli/npm/managed/mod.rs | 10 +-- cli/npm/managed/resolvers/common.rs | 110 ---------------------------- cli/npm/managed/resolvers/global.rs | 37 +++++----- cli/npm/managed/resolvers/local.rs | 17 ----- cli/npm/managed/resolvers/mod.rs | 1 - cli/npm/mod.rs | 11 +-- cli/npm/permission_checker.rs | 105 ++++++++++++++++++++++++++ cli/standalone/mod.rs | 24 +++++- tools/generate_types_deno.ts | 2 +- 12 files changed, 185 insertions(+), 191 deletions(-) create mode 100644 cli/npm/permission_checker.rs diff --git a/cli/factory.rs b/cli/factory.rs index fc6bca33fd..c507d8388d 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -71,6 +71,8 @@ use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CreateInNpmPkgCheckerOptions; +use crate::npm::NpmRegistryReadPermissionChecker; +use crate::npm::NpmRegistryReadPermissionCheckerMode; use crate::resolver::CjsTracker; use crate::resolver::CliDenoResolver; use crate::resolver::CliNpmReqResolver; @@ -941,6 +943,19 @@ impl CliFactory { let cjs_tracker = self.cjs_tracker()?.clone(); let pkg_json_resolver = self.pkg_json_resolver().clone(); let npm_req_resolver = self.npm_req_resolver().await?; + let npm_registry_permission_checker = { + let mode = if cli_options.use_byonm() { + NpmRegistryReadPermissionCheckerMode::Byonm + } else if let Some(node_modules_dir) = cli_options.node_modules_dir_path() + { + NpmRegistryReadPermissionCheckerMode::Local(node_modules_dir.clone()) + } else { + NpmRegistryReadPermissionCheckerMode::Global( + self.npm_cache_dir()?.root_dir().to_path_buf(), + ) + }; + Arc::new(NpmRegistryReadPermissionChecker::new(self.sys(), mode)) + }; Ok(CliMainWorkerFactory::new( self.blob_store().clone(), @@ -968,13 +983,14 @@ impl CliFactory { self.module_load_preparer().await?.clone(), node_code_translator.clone(), node_resolver.clone(), - npm_req_resolver.clone(), - cli_npm_resolver.clone(), NpmModuleLoader::new( self.cjs_tracker()?.clone(), fs.clone(), node_code_translator.clone(), ), + npm_registry_permission_checker, + npm_req_resolver.clone(), + cli_npm_resolver.clone(), self.parsed_source_cache().clone(), self.resolver().await?.clone(), self.sys(), diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 174e06e266..8256c56781 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -66,6 +66,7 @@ use crate::graph_util::ModuleGraphBuilder; use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; +use crate::npm::NpmRegistryReadPermissionChecker; use crate::resolver::CjsTracker; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; @@ -221,9 +222,10 @@ struct SharedCliModuleLoaderState { module_load_preparer: Arc, node_code_translator: Arc, node_resolver: Arc, + npm_module_loader: NpmModuleLoader, + npm_registry_permission_checker: Arc, npm_req_resolver: Arc, npm_resolver: Arc, - npm_module_loader: NpmModuleLoader, parsed_source_cache: Arc, resolver: Arc, sys: CliSys, @@ -281,9 +283,10 @@ impl CliModuleLoaderFactory { module_load_preparer: Arc, node_code_translator: Arc, node_resolver: Arc, + npm_module_loader: NpmModuleLoader, + npm_registry_permission_checker: Arc, npm_req_resolver: Arc, npm_resolver: Arc, - npm_module_loader: NpmModuleLoader, parsed_source_cache: Arc, resolver: Arc, sys: CliSys, @@ -307,9 +310,10 @@ impl CliModuleLoaderFactory { module_load_preparer, node_code_translator, node_resolver, + npm_module_loader, + npm_registry_permission_checker, npm_req_resolver, npm_resolver, - npm_module_loader, parsed_source_cache, resolver, sys, @@ -348,7 +352,10 @@ impl CliModuleLoaderFactory { sys: self.shared.sys.clone(), graph_container, in_npm_pkg_checker: self.shared.in_npm_pkg_checker.clone(), - npm_resolver: self.shared.npm_resolver.clone(), + npm_registry_permission_checker: self + .shared + .npm_registry_permission_checker + .clone(), }); CreateModuleLoaderResult { module_loader, @@ -1095,7 +1102,7 @@ struct CliNodeRequireLoader { sys: CliSys, graph_container: TGraphContainer, in_npm_pkg_checker: Arc, - npm_resolver: Arc, + npm_registry_permission_checker: Arc, } impl NodeRequireLoader @@ -1112,7 +1119,9 @@ impl NodeRequireLoader return Ok(std::borrow::Cow::Borrowed(path)); } } - self.npm_resolver.ensure_read_permission(permissions, path) + self + .npm_registry_permission_checker + .ensure_read_permission(permissions, path) } fn load_text_file_lossy( diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index 7a0a450c11..2c11a417f3 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -1,15 +1,12 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::borrow::Cow; use std::path::Path; use std::sync::Arc; -use deno_core::error::AnyError; use deno_core::serde_json; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmNpmResolverCreateOptions; use deno_resolver::npm::CliNpmReqResolver; -use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use node_resolver::NpmPackageFolderResolver; @@ -73,21 +70,6 @@ impl CliNpmResolver for CliByonmNpmResolver { self.root_node_modules_dir() } - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError> { - if !path - .components() - .any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules") - { - permissions.check_read_path(path).map_err(Into::into) - } else { - Ok(Cow::Borrowed(path)) - } - } - fn check_state_hash(&self) -> Option { // it is very difficult to determine the check state hash for byonm // so we just return None to signify check caching is not supported diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 4a4d593bbf..55421f41e8 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -24,7 +24,6 @@ use deno_npm_cache::NpmCacheSetting; use deno_path_util::fs::canonicalize_path_maybe_not_exists; use deno_resolver::npm::CliNpmReqResolver; use deno_runtime::colors; -use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; @@ -167,6 +166,7 @@ fn create_inner( sys.clone(), npm_rc.clone(), )); + let fs_resolver = create_npm_fs_resolver( npm_cache.clone(), &npm_install_deps_provider, @@ -754,14 +754,6 @@ impl CliNpmResolver for ManagedCliNpmResolver { self.fs_resolver.node_modules_path() } - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError> { - self.fs_resolver.ensure_read_permission(permissions, path) - } - fn check_state_hash(&self) -> Option { // We could go further and check all the individual // npm packages, but that's probably overkill. diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs index 6a859ea9fd..66d991bd49 100644 --- a/cli/npm/managed/resolvers/common.rs +++ b/cli/npm/managed/resolvers/common.rs @@ -3,30 +3,17 @@ pub mod bin_entries; pub mod lifecycle_scripts; -use std::borrow::Cow; -use std::collections::HashMap; -use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; -use std::sync::Arc; -use std::sync::Mutex; use async_trait::async_trait; use deno_ast::ModuleSpecifier; -use deno_core::anyhow::Context; use deno_core::error::AnyError; -use deno_core::futures; -use deno_core::futures::StreamExt; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_runtime::deno_node::NodePermissions; use node_resolver::errors::PackageFolderResolveError; -use sys_traits::FsCanonicalize; use super::super::PackageCaching; -use crate::npm::CliNpmTarballCache; -use crate::sys::CliSys; /// Part of the resolution that interacts with the file system. #[async_trait(?Send)] @@ -63,101 +50,4 @@ pub trait NpmPackageFsResolver: Send + Sync { &self, caching: PackageCaching<'a>, ) -> Result<(), AnyError>; - - #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError>; -} - -#[derive(Debug)] -pub struct RegistryReadPermissionChecker { - sys: CliSys, - cache: Mutex>, - registry_path: PathBuf, -} - -impl RegistryReadPermissionChecker { - pub fn new(sys: CliSys, registry_path: PathBuf) -> Self { - Self { - sys, - registry_path, - cache: Default::default(), - } - } - - pub fn ensure_registry_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError> { - if permissions.query_read_all() { - return Ok(Cow::Borrowed(path)); // skip permissions checks below - } - - // allow reading if it's in the node_modules - let is_path_in_node_modules = path.starts_with(&self.registry_path) - && path - .components() - .all(|c| !matches!(c, std::path::Component::ParentDir)); - - if is_path_in_node_modules { - let mut cache = self.cache.lock().unwrap(); - let mut canonicalize = - |path: &Path| -> Result, AnyError> { - match cache.get(path) { - Some(canon) => Ok(Some(canon.clone())), - None => match self.sys.fs_canonicalize(path) { - Ok(canon) => { - cache.insert(path.to_path_buf(), canon.clone()); - Ok(Some(canon)) - } - Err(e) => { - if e.kind() == ErrorKind::NotFound { - return Ok(None); - } - Err(AnyError::from(e)).with_context(|| { - format!("failed canonicalizing '{}'", path.display()) - }) - } - }, - } - }; - if let Some(registry_path_canon) = canonicalize(&self.registry_path)? { - if let Some(path_canon) = canonicalize(path)? { - if path_canon.starts_with(registry_path_canon) { - return Ok(Cow::Owned(path_canon)); - } - } else if path.starts_with(registry_path_canon) - || path.starts_with(&self.registry_path) - { - return Ok(Cow::Borrowed(path)); - } - } - } - - permissions.check_read_path(path).map_err(Into::into) - } -} - -/// Caches all the packages in parallel. -pub async fn cache_packages( - packages: &[NpmResolutionPackage], - tarball_cache: &Arc, -) -> Result<(), AnyError> { - let mut futures_unordered = futures::stream::FuturesUnordered::new(); - for package in packages { - futures_unordered.push(async move { - tarball_cache - .ensure_package(&package.id.nv, &package.dist) - .await - }); - } - while let Some(result) = futures_unordered.next().await { - // surface the first error - result?; - } - Ok(()) } diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs index 18b7911c2f..417345cefe 100644 --- a/cli/npm/managed/resolvers/global.rs +++ b/cli/npm/managed/resolvers/global.rs @@ -10,27 +10,25 @@ use std::sync::Arc; use async_trait::async_trait; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; +use deno_core::futures::stream::FuturesUnordered; +use deno_core::futures::StreamExt; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; -use deno_runtime::deno_node::NodePermissions; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::ReferrerNotFoundError; use super::super::resolution::NpmResolution; -use super::common::cache_packages; use super::common::lifecycle_scripts::LifecycleScriptsStrategy; use super::common::NpmPackageFsResolver; -use super::common::RegistryReadPermissionChecker; use crate::args::LifecycleScriptsConfig; use crate::cache::FastInsecureHasher; use crate::colors; use crate::npm::managed::PackageCaching; use crate::npm::CliNpmCache; use crate::npm::CliNpmTarballCache; -use crate::sys::CliSys; /// Resolves packages from the global npm cache. #[derive(Debug)] @@ -39,7 +37,6 @@ pub struct GlobalNpmPackageResolver { tarball_cache: Arc, resolution: Arc, system_info: NpmSystemInfo, - registry_read_permission_checker: RegistryReadPermissionChecker, lifecycle_scripts: LifecycleScriptsConfig, } @@ -48,15 +45,10 @@ impl GlobalNpmPackageResolver { cache: Arc, tarball_cache: Arc, resolution: Arc, - sys: CliSys, system_info: NpmSystemInfo, lifecycle_scripts: LifecycleScriptsConfig, ) -> Self { Self { - registry_read_permission_checker: RegistryReadPermissionChecker::new( - sys, - cache.root_dir_path().to_path_buf(), - ), cache, tarball_cache, resolution, @@ -186,16 +178,25 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { Ok(()) } +} - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError> { - self - .registry_read_permission_checker - .ensure_registry_read_permission(permissions, path) +async fn cache_packages( + packages: &[NpmResolutionPackage], + tarball_cache: &Arc, +) -> Result<(), AnyError> { + let mut futures_unordered = FuturesUnordered::new(); + for package in packages { + futures_unordered.push(async move { + tarball_cache + .ensure_package(&package.id.nv, &package.dist) + .await + }); } + while let Some(result) = futures_unordered.next().await { + // surface the first error + result?; + } + Ok(()) } struct GlobalLifecycleScripts<'a> { diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index eb806ad02c..63f0e4f36c 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -33,7 +33,6 @@ use deno_npm::NpmSystemInfo; use deno_path_util::fs::atomic_write_file_with_retries; use deno_path_util::fs::canonicalize_path_maybe_not_exists; use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder; -use deno_runtime::deno_node::NodePermissions; use deno_semver::package::PackageNv; use deno_semver::StackString; use node_resolver::errors::PackageFolderResolveError; @@ -47,7 +46,6 @@ use sys_traits::FsMetadata; use super::super::resolution::NpmResolution; use super::common::bin_entries; use super::common::NpmPackageFsResolver; -use super::common::RegistryReadPermissionChecker; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; use crate::cache::CACHE_PERM; @@ -75,7 +73,6 @@ pub struct LocalNpmPackageResolver { root_node_modules_path: PathBuf, root_node_modules_url: Url, system_info: NpmSystemInfo, - registry_read_permission_checker: RegistryReadPermissionChecker, lifecycle_scripts: LifecycleScriptsConfig, } @@ -98,10 +95,6 @@ impl LocalNpmPackageResolver { progress_bar, resolution, tarball_cache, - registry_read_permission_checker: RegistryReadPermissionChecker::new( - sys.clone(), - node_modules_folder.clone(), - ), sys, root_node_modules_url: Url::from_directory_path(&node_modules_folder) .unwrap(), @@ -275,16 +268,6 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { ) .await } - - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError> { - self - .registry_read_permission_checker - .ensure_registry_read_permission(permissions, path) - } } /// `node_modules/.deno//node_modules/` diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs index b52a7e0e39..cc4c735c7c 100644 --- a/cli/npm/managed/resolvers/mod.rs +++ b/cli/npm/managed/resolvers/mod.rs @@ -48,7 +48,6 @@ pub fn create_npm_fs_resolver( npm_cache, tarball_cache, resolution, - sys, system_info, lifecycle_scripts, )), diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index e0c10fa7a7..710c24f98d 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -2,8 +2,8 @@ mod byonm; mod managed; +mod permission_checker; -use std::borrow::Cow; use std::path::Path; use std::sync::Arc; @@ -17,7 +17,6 @@ use deno_resolver::npm::ByonmInNpmPackageChecker; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::CliNpmReqResolver; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::deno_node::NodePermissions; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; @@ -34,6 +33,8 @@ pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::ManagedCliNpmResolver; pub use self::managed::PackageCaching; +pub use self::permission_checker::NpmRegistryReadPermissionChecker; +pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode; use crate::file_fetcher::CliFileFetcher; use crate::http_util::HttpClientProvider; use crate::sys::CliSys; @@ -183,12 +184,6 @@ pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver { fn root_node_modules_path(&self) -> Option<&Path>; - fn ensure_read_permission<'a>( - &self, - permissions: &mut dyn NodePermissions, - path: &'a Path, - ) -> Result, AnyError>; - /// Returns a hash returning the state of the npm resolver /// or `None` if the state currently can't be determined. fn check_state_hash(&self) -> Option; diff --git a/cli/npm/permission_checker.rs b/cli/npm/permission_checker.rs new file mode 100644 index 0000000000..01fed08954 --- /dev/null +++ b/cli/npm/permission_checker.rs @@ -0,0 +1,105 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::collections::HashMap; +use std::io::ErrorKind; +use std::path::Path; +use std::path::PathBuf; + +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::parking_lot::Mutex; +use deno_runtime::deno_node::NodePermissions; +use sys_traits::FsCanonicalize; + +use crate::sys::CliSys; + +#[derive(Debug)] +pub enum NpmRegistryReadPermissionCheckerMode { + Byonm, + Global(PathBuf), + Local(PathBuf), +} + +#[derive(Debug)] +pub struct NpmRegistryReadPermissionChecker { + sys: CliSys, + cache: Mutex>, + mode: NpmRegistryReadPermissionCheckerMode, +} + +impl NpmRegistryReadPermissionChecker { + pub fn new(sys: CliSys, mode: NpmRegistryReadPermissionCheckerMode) -> Self { + Self { + sys, + cache: Default::default(), + mode, + } + } + + #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] + pub fn ensure_read_permission<'a>( + &self, + permissions: &mut dyn NodePermissions, + path: &'a Path, + ) -> Result, AnyError> { + if permissions.query_read_all() { + return Ok(Cow::Borrowed(path)); // skip permissions checks below + } + + match &self.mode { + NpmRegistryReadPermissionCheckerMode::Byonm => { + if path.components().any(|c| c.as_os_str() == "node_modules") { + Ok(Cow::Borrowed(path)) + } else { + permissions.check_read_path(path).map_err(Into::into) + } + } + NpmRegistryReadPermissionCheckerMode::Global(registry_path) + | NpmRegistryReadPermissionCheckerMode::Local(registry_path) => { + // allow reading if it's in the node_modules + let is_path_in_node_modules = path.starts_with(registry_path) + && path + .components() + .all(|c| !matches!(c, std::path::Component::ParentDir)); + + if is_path_in_node_modules { + let mut cache = self.cache.lock(); + let mut canonicalize = + |path: &Path| -> Result, AnyError> { + match cache.get(path) { + Some(canon) => Ok(Some(canon.clone())), + None => match self.sys.fs_canonicalize(path) { + Ok(canon) => { + cache.insert(path.to_path_buf(), canon.clone()); + Ok(Some(canon)) + } + Err(e) => { + if e.kind() == ErrorKind::NotFound { + return Ok(None); + } + Err(AnyError::from(e)).with_context(|| { + format!("failed canonicalizing '{}'", path.display()) + }) + } + }, + } + }; + if let Some(registry_path_canon) = canonicalize(registry_path)? { + if let Some(path_canon) = canonicalize(path)? { + if path_canon.starts_with(registry_path_canon) { + return Ok(Cow::Owned(path_canon)); + } + } else if path.starts_with(registry_path_canon) + || path.starts_with(registry_path) + { + return Ok(Cow::Borrowed(path)); + } + } + } + + permissions.check_read_path(path).map_err(Into::into) + } + } + } +} diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 4768c742f1..6ed192071f 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -6,6 +6,7 @@ #![allow(unused_imports)] use std::borrow::Cow; +use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; @@ -88,6 +89,8 @@ use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CreateInNpmPkgCheckerOptions; +use crate::npm::NpmRegistryReadPermissionChecker; +use crate::npm::NpmRegistryReadPermissionCheckerMode; use crate::resolver::CjsTracker; use crate::resolver::CliNpmReqResolver; use crate::resolver::NpmModuleLoader; @@ -123,6 +126,7 @@ struct SharedModuleLoaderState { node_code_translator: Arc, node_resolver: Arc, npm_module_loader: Arc, + npm_registry_permission_checker: NpmRegistryReadPermissionChecker, npm_req_resolver: Arc, npm_resolver: Arc, source_maps: SourceMapStore, @@ -557,7 +561,7 @@ impl NodeRequireLoader for EmbeddedModuleLoader { self .shared - .npm_resolver + .npm_registry_permission_checker .ensure_read_permission(permissions, path) } @@ -662,6 +666,23 @@ pub async fn run( let npm_global_cache_dir = root_path.join(".deno_compile_node_modules"); let cache_setting = CacheSetting::Only; let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone())); + let npm_registry_permission_checker = { + let mode = match &metadata.node_modules { + Some(binary::NodeModules::Managed { + node_modules_dir: Some(path), + }) => NpmRegistryReadPermissionCheckerMode::Local(PathBuf::from(path)), + Some(binary::NodeModules::Byonm { .. }) => { + NpmRegistryReadPermissionCheckerMode::Byonm + } + Some(binary::NodeModules::Managed { + node_modules_dir: None, + }) + | None => NpmRegistryReadPermissionCheckerMode::Global( + npm_global_cache_dir.clone(), + ), + }; + NpmRegistryReadPermissionChecker::new(sys.clone(), mode) + }; let (in_npm_pkg_checker, npm_resolver) = match metadata.node_modules { Some(binary::NodeModules::Managed { node_modules_dir }) => { // create an npmrc that uses the fake npm_registry_url to resolve packages @@ -889,6 +910,7 @@ pub async fn run( fs.clone(), node_code_translator, )), + npm_registry_permission_checker, npm_resolver: npm_resolver.clone(), npm_req_resolver, source_maps, diff --git a/tools/generate_types_deno.ts b/tools/generate_types_deno.ts index e88b4bfa6f..fa60f51a4b 100755 --- a/tools/generate_types_deno.ts +++ b/tools/generate_types_deno.ts @@ -76,7 +76,7 @@ async function createDenoDtsFile() { file.insertStatements( 0, - "// Copyright 2018-2024 the Deno authors. MIT license.\n\n", + "// Copyright 2018-2025 the Deno authors. MIT license.\n\n", ); file.saveSync(); From 225c3dea874e9e94e24abfa176c00cf65fa2e80a Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 2 Jan 2025 10:06:12 -0500 Subject: [PATCH 057/107] refactor: update some fs_util functions to use sys_traits (#27515) This is in preparation for extracting out these functions from the CLI crate. A side benefit is these functions will now work in Wasm. --- Cargo.lock | 10 +- Cargo.toml | 4 +- cli/npm/managed/resolvers/local.rs | 18 ++- cli/standalone/file_system.rs | 80 +++++++++++--- cli/standalone/virtual_fs.rs | 1 + cli/sys.rs | 70 +++++++----- cli/util/fs.rs | 170 +++++++++++++---------------- 7 files changed, 205 insertions(+), 148 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bad4a8ea2c..30f4f0d394 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4519,12 +4519,12 @@ dependencies = [ [[package]] name = "junction" -version = "0.2.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be39922b087cecaba4e2d5592dedfc8bda5d4a5a1231f143337cca207950b61d" +checksum = "72bbdfd737a243da3dfc1f99ee8d6e166480f17ab4ac84d7c34aacd73fc7bd16" dependencies = [ "scopeguard", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -7680,9 +7680,9 @@ dependencies = [ [[package]] name = "sys_traits" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6683465f4e1d8fd75069cbc36c646258c05b7d8d6676bcb5d71968b99b7d5ae2" +checksum = "b1c12873696bde6de3aea3cd27de8e52897177c5b368a6a30987fd4926e30f85" dependencies = [ "filetime", "getrandom", diff --git a/Cargo.toml b/Cargo.toml index e4b9e0f496..117a1bf6a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -193,7 +193,7 @@ slab = "0.4" smallvec = "1.8" socket2 = { version = "0.5.3", features = ["all"] } spki = "0.7.2" -sys_traits = "=0.1.4" +sys_traits = "=0.1.6" tar = "=0.4.40" tempfile = "3.4.0" termcolor = "1.1.3" @@ -240,7 +240,7 @@ syn = { version = "2", features = ["full", "extra-traits"] } nix = "=0.27.1" # windows deps -junction = "=0.2.0" +junction = "=1.2.0" winapi = "=0.3.9" windows-sys = { version = "0.59.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_Security", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry", "Win32_System_Kernel", "Win32_System_Threading", "Win32_UI", "Win32_UI_Shell"] } winres = "=0.1.12" diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 63f0e4f36c..1a4ec57a69 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -433,7 +433,11 @@ async fn sync_resolution_with_fs( deno_core::unsync::spawn_blocking({ let package_path = package_path.clone(); move || { - clone_dir_recursive(&cache_folder, &package_path)?; + clone_dir_recursive( + &crate::sys::CliSys::default(), + &cache_folder, + &package_path, + )?; // write out a file that indicates this folder has been initialized fs::write(initialized_file, tags)?; @@ -490,7 +494,11 @@ async fn sync_resolution_with_fs( &package.id.nv.name, ); - clone_dir_recursive(&source_path, &package_path)?; + clone_dir_recursive( + &crate::sys::CliSys::default(), + &source_path, + &package_path, + )?; // write out a file that indicates this folder has been initialized fs::write(initialized_file, "")?; } @@ -1057,7 +1065,8 @@ fn symlink_package_dir( } #[cfg(not(windows))] { - symlink_dir(&old_path_relative, new_path).map_err(Into::into) + symlink_dir(&crate::sys::CliSys::default(), &old_path_relative, new_path) + .map_err(Into::into) } } @@ -1079,7 +1088,8 @@ fn junction_or_symlink_dir( .context("Failed creating junction in node_modules folder"); } - match symlink_dir(old_path_relative, new_path) { + match symlink_dir(&crate::sys::CliSys::default(), old_path_relative, new_path) + { Ok(()) => Ok(()), Err(symlink_err) if symlink_err.kind() == std::io::ErrorKind::PermissionDenied => diff --git a/cli/standalone/file_system.rs b/cli/standalone/file_system.rs index f218277bef..b04db88c90 100644 --- a/cli/standalone/file_system.rs +++ b/cli/standalone/file_system.rs @@ -23,6 +23,7 @@ use sys_traits::boxed::BoxedFsDirEntry; use sys_traits::boxed::BoxedFsMetadataValue; use sys_traits::boxed::FsMetadataBoxed; use sys_traits::boxed::FsReadDirBoxed; +use sys_traits::FsCopy; use sys_traits::FsMetadata; use super::virtual_fs::FileBackedVfs; @@ -47,24 +48,32 @@ impl DenoCompileFileSystem { } } - fn copy_to_real_path(&self, oldpath: &Path, newpath: &Path) -> FsResult<()> { + fn copy_to_real_path( + &self, + oldpath: &Path, + newpath: &Path, + ) -> std::io::Result { let old_file = self.0.file_entry(oldpath)?; let old_file_bytes = self.0.read_file_all(old_file, VfsFileSubDataKind::Raw)?; - RealFs.write_file_sync( - newpath, - OpenOptions { - read: false, - write: true, - create: true, - truncate: true, - append: false, - create_new: false, - mode: None, - }, - None, - &old_file_bytes, - ) + let len = old_file_bytes.len() as u64; + RealFs + .write_file_sync( + newpath, + OpenOptions { + read: false, + write: true, + create: true, + truncate: true, + append: false, + create_new: false, + mode: None, + }, + None, + &old_file_bytes, + ) + .map_err(|err| err.into_io_error())?; + Ok(len) } } @@ -191,7 +200,10 @@ impl FileSystem for DenoCompileFileSystem { fn copy_file_sync(&self, oldpath: &Path, newpath: &Path) -> FsResult<()> { self.error_if_in_vfs(newpath)?; if self.0.is_path_within(oldpath) { - self.copy_to_real_path(oldpath, newpath) + self + .copy_to_real_path(oldpath, newpath) + .map(|_| ()) + .map_err(FsError::Io) } else { RealFs.copy_file_sync(oldpath, newpath) } @@ -206,6 +218,8 @@ impl FileSystem for DenoCompileFileSystem { let fs = self.clone(); tokio::task::spawn_blocking(move || { fs.copy_to_real_path(&oldpath, &newpath) + .map(|_| ()) + .map_err(FsError::Io) }) .await? } else { @@ -593,6 +607,32 @@ impl sys_traits::BaseFsMetadata for DenoCompileFileSystem { } } +impl sys_traits::BaseFsCopy for DenoCompileFileSystem { + #[inline] + fn base_fs_copy(&self, from: &Path, to: &Path) -> std::io::Result { + self + .error_if_in_vfs(to) + .map_err(|err| err.into_io_error())?; + if self.0.is_path_within(from) { + self.copy_to_real_path(from, to) + } else { + #[allow(clippy::disallowed_types)] // ok because we're implementing the fs + sys_traits::impls::RealSys.fs_copy(from, to) + } + } +} + +impl sys_traits::BaseFsCloneFile for DenoCompileFileSystem { + fn base_fs_clone_file( + &self, + _from: &Path, + _to: &Path, + ) -> std::io::Result<()> { + // will cause a fallback in the code that uses this + Err(not_supported("cloning files")) + } +} + impl sys_traits::BaseFsCreateDir for DenoCompileFileSystem { #[inline] fn base_fs_create_dir( @@ -794,6 +834,14 @@ impl sys_traits::BaseFsOpen for DenoCompileFileSystem { } } +impl sys_traits::BaseFsSymlinkDir for DenoCompileFileSystem { + fn base_fs_symlink_dir(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + self + .symlink_sync(src, dst, Some(FsFileType::Directory)) + .map_err(|err| err.into_io_error()) + } +} + impl sys_traits::SystemRandom for DenoCompileFileSystem { #[inline] fn sys_random(&self, buf: &mut [u8]) -> std::io::Result<()> { diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index fab4fad83c..e167b153a7 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -1685,6 +1685,7 @@ mod test { temp_dir.write("src/a.txt", "data"); temp_dir.write("src/b.txt", "data"); util::fs::symlink_dir( + &crate::sys::CliSys::default(), temp_dir_path.join("src/nested/sub_dir").as_path(), temp_dir_path.join("src/sub_dir_link").as_path(), ) diff --git a/cli/sys.rs b/cli/sys.rs index f23bbc9dc8..718e9981e2 100644 --- a/cli/sys.rs +++ b/cli/sys.rs @@ -7,6 +7,8 @@ // denort or the deno binary. We should extract out denort to a separate binary. use std::borrow::Cow; +use std::path::Path; +use std::path::PathBuf; use sys_traits::boxed::BoxedFsDirEntry; use sys_traits::boxed::BoxedFsFile; @@ -35,12 +37,35 @@ impl Default for CliSys { impl deno_runtime::deno_node::ExtNodeSys for CliSys {} +impl sys_traits::BaseFsCloneFile for CliSys { + fn base_fs_clone_file(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_clone_file(src, dst), + Self::DenoCompile(sys) => sys.base_fs_clone_file(src, dst), + } + } +} + +impl sys_traits::BaseFsSymlinkDir for CliSys { + fn base_fs_symlink_dir(&self, src: &Path, dst: &Path) -> std::io::Result<()> { + match self { + Self::Real(sys) => sys.base_fs_symlink_dir(src, dst), + Self::DenoCompile(sys) => sys.base_fs_symlink_dir(src, dst), + } + } +} + +impl sys_traits::BaseFsCopy for CliSys { + fn base_fs_copy(&self, src: &Path, dst: &Path) -> std::io::Result { + match self { + Self::Real(sys) => sys.base_fs_copy(src, dst), + Self::DenoCompile(sys) => sys.base_fs_copy(src, dst), + } + } +} + impl sys_traits::BaseFsHardLink for CliSys { - fn base_fs_hard_link( - &self, - src: &std::path::Path, - dst: &std::path::Path, - ) -> std::io::Result<()> { + fn base_fs_hard_link(&self, src: &Path, dst: &Path) -> std::io::Result<()> { match self { Self::Real(sys) => sys.base_fs_hard_link(src, dst), Self::DenoCompile(sys) => sys.base_fs_hard_link(src, dst), @@ -49,10 +74,7 @@ impl sys_traits::BaseFsHardLink for CliSys { } impl sys_traits::BaseFsRead for CliSys { - fn base_fs_read( - &self, - p: &std::path::Path, - ) -> std::io::Result> { + fn base_fs_read(&self, p: &Path) -> std::io::Result> { match self { Self::Real(sys) => sys.base_fs_read(p), Self::DenoCompile(sys) => sys.base_fs_read(p), @@ -65,7 +87,7 @@ impl sys_traits::BaseFsReadDir for CliSys { fn base_fs_read_dir( &self, - p: &std::path::Path, + p: &Path, ) -> std::io::Result< Box> + '_>, > { @@ -77,10 +99,7 @@ impl sys_traits::BaseFsReadDir for CliSys { } impl sys_traits::BaseFsCanonicalize for CliSys { - fn base_fs_canonicalize( - &self, - p: &std::path::Path, - ) -> std::io::Result { + fn base_fs_canonicalize(&self, p: &Path) -> std::io::Result { match self { Self::Real(sys) => sys.base_fs_canonicalize(p), Self::DenoCompile(sys) => sys.base_fs_canonicalize(p), @@ -91,10 +110,7 @@ impl sys_traits::BaseFsCanonicalize for CliSys { impl sys_traits::BaseFsMetadata for CliSys { type Metadata = BoxedFsMetadataValue; - fn base_fs_metadata( - &self, - path: &std::path::Path, - ) -> std::io::Result { + fn base_fs_metadata(&self, path: &Path) -> std::io::Result { match self { Self::Real(sys) => sys.fs_metadata_boxed(path), Self::DenoCompile(sys) => sys.fs_metadata_boxed(path), @@ -103,7 +119,7 @@ impl sys_traits::BaseFsMetadata for CliSys { fn base_fs_symlink_metadata( &self, - path: &std::path::Path, + path: &Path, ) -> std::io::Result { match self { Self::Real(sys) => sys.fs_symlink_metadata_boxed(path), @@ -115,7 +131,7 @@ impl sys_traits::BaseFsMetadata for CliSys { impl sys_traits::BaseFsCreateDir for CliSys { fn base_fs_create_dir( &self, - p: &std::path::Path, + p: &Path, options: &CreateDirOptions, ) -> std::io::Result<()> { match self { @@ -130,7 +146,7 @@ impl sys_traits::BaseFsOpen for CliSys { fn base_fs_open( &self, - path: &std::path::Path, + path: &Path, options: &sys_traits::OpenOptions, ) -> std::io::Result { match self { @@ -141,7 +157,7 @@ impl sys_traits::BaseFsOpen for CliSys { } impl sys_traits::BaseFsRemoveFile for CliSys { - fn base_fs_remove_file(&self, p: &std::path::Path) -> std::io::Result<()> { + fn base_fs_remove_file(&self, p: &Path) -> std::io::Result<()> { match self { Self::Real(sys) => sys.base_fs_remove_file(p), Self::DenoCompile(sys) => sys.base_fs_remove_file(p), @@ -150,11 +166,7 @@ impl sys_traits::BaseFsRemoveFile for CliSys { } impl sys_traits::BaseFsRename for CliSys { - fn base_fs_rename( - &self, - old: &std::path::Path, - new: &std::path::Path, - ) -> std::io::Result<()> { + fn base_fs_rename(&self, old: &Path, new: &Path) -> std::io::Result<()> { match self { Self::Real(sys) => sys.base_fs_rename(old, new), Self::DenoCompile(sys) => sys.base_fs_rename(old, new), @@ -190,7 +202,7 @@ impl sys_traits::ThreadSleep for CliSys { } impl sys_traits::EnvCurrentDir for CliSys { - fn env_current_dir(&self) -> std::io::Result { + fn env_current_dir(&self) -> std::io::Result { match self { Self::Real(sys) => sys.env_current_dir(), Self::DenoCompile(sys) => sys.env_current_dir(), @@ -211,7 +223,7 @@ impl sys_traits::BaseEnvVar for CliSys { } impl sys_traits::EnvHomeDir for CliSys { - fn env_home_dir(&self) -> Option { + fn env_home_dir(&self) -> Option { #[allow(clippy::disallowed_types)] // ok because sys impl sys_traits::impls::RealSys.env_home_dir() } diff --git a/cli/util/fs.rs b/cli/util/fs.rs index 396e5dc146..61f1786ee9 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -17,6 +17,9 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::unsync::spawn_blocking; use deno_core::ModuleSpecifier; +use sys_traits::FsCreateDirAll; +use sys_traits::FsDirEntry; +use sys_traits::FsSymlinkDir; use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; @@ -148,87 +151,74 @@ pub async fn remove_dir_all_if_exists(path: &Path) -> std::io::Result<()> { } } -mod clone_dir_imp { - - #[cfg(target_vendor = "apple")] - mod apple { - use std::os::unix::ffi::OsStrExt; - use std::path::Path; - - use deno_core::error::AnyError; - - use super::super::copy_dir_recursive; - fn clonefile(from: &Path, to: &Path) -> std::io::Result<()> { - let from = std::ffi::CString::new(from.as_os_str().as_bytes())?; - let to = std::ffi::CString::new(to.as_os_str().as_bytes())?; - // SAFETY: `from` and `to` are valid C strings. - let ret = unsafe { libc::clonefile(from.as_ptr(), to.as_ptr(), 0) }; - if ret != 0 { - return Err(std::io::Error::last_os_error()); - } - Ok(()) - } - - pub fn clone_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { - if let Some(parent) = to.parent() { - std::fs::create_dir_all(parent)?; - } - // Try to clone the whole directory - if let Err(err) = clonefile(from, to) { - if err.kind() != std::io::ErrorKind::AlreadyExists { - log::warn!( - "Failed to clone dir {:?} to {:?} via clonefile: {}", - from, - to, - err - ); - } - // clonefile won't overwrite existing files, so if the dir exists - // we need to handle it recursively. - copy_dir_recursive(from, to)?; - } - - Ok(()) - } - } - - #[cfg(target_vendor = "apple")] - pub(super) use apple::clone_dir_recursive; - - #[cfg(not(target_vendor = "apple"))] - pub(super) fn clone_dir_recursive( - from: &std::path::Path, - to: &std::path::Path, - ) -> Result<(), deno_core::error::AnyError> { - use crate::sys::CliSys; - - if let Err(e) = - deno_npm_cache::hard_link_dir_recursive(&CliSys::default(), from, to) - { - log::debug!("Failed to hard link dir {:?} to {:?}: {}", from, to, e); - super::copy_dir_recursive(from, to)?; - } - - Ok(()) - } -} - /// Clones a directory to another directory. The exact method /// is not guaranteed - it may be a hardlink, copy, or other platform-specific /// operation. /// /// Note: Does not handle symlinks. -pub fn clone_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { - clone_dir_imp::clone_dir_recursive(from, to) +pub fn clone_dir_recursive< + TSys: sys_traits::FsCopy + + sys_traits::FsCloneFile + + sys_traits::FsCloneFile + + sys_traits::FsCreateDir + + sys_traits::FsHardLink + + sys_traits::FsReadDir + + sys_traits::FsRemoveFile + + sys_traits::ThreadSleep, +>( + sys: &TSys, + from: &Path, + to: &Path, +) -> Result<(), AnyError> { + if cfg!(target_vendor = "apple") { + if let Some(parent) = to.parent() { + sys.fs_create_dir_all(parent)?; + } + // Try to clone the whole directory + if let Err(err) = sys.fs_clone_file(from, to) { + if !matches!( + err.kind(), + std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::Unsupported + ) { + log::warn!( + "Failed to clone dir {:?} to {:?} via clonefile: {}", + from, + to, + err + ); + } + // clonefile won't overwrite existing files, so if the dir exists + // we need to handle it recursively. + copy_dir_recursive(sys, from, to)?; + } + } else if let Err(e) = deno_npm_cache::hard_link_dir_recursive(sys, from, to) + { + log::debug!("Failed to hard link dir {:?} to {:?}: {}", from, to, e); + copy_dir_recursive(sys, from, to)?; + } + + Ok(()) } /// Copies a directory to another directory. /// /// Note: Does not handle symlinks. -pub fn copy_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { - std::fs::create_dir_all(to) +pub fn copy_dir_recursive< + TSys: sys_traits::FsCopy + + sys_traits::FsCloneFile + + sys_traits::FsCreateDir + + sys_traits::FsHardLink + + sys_traits::FsReadDir, +>( + sys: &TSys, + from: &Path, + to: &Path, +) -> Result<(), AnyError> { + sys + .fs_create_dir_all(to) .with_context(|| format!("Creating {}", to.display()))?; - let read_dir = std::fs::read_dir(from) + let read_dir = sys + .fs_read_dir(from) .with_context(|| format!("Reading {}", from.display()))?; for entry in read_dir { @@ -238,11 +228,11 @@ pub fn copy_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { let new_to = to.join(entry.file_name()); if file_type.is_dir() { - copy_dir_recursive(&new_from, &new_to).with_context(|| { + copy_dir_recursive(sys, &new_from, &new_to).with_context(|| { format!("Dir {} to {}", new_from.display(), new_to.display()) })?; } else if file_type.is_file() { - std::fs::copy(&new_from, &new_to).with_context(|| { + sys.fs_copy(&new_from, &new_to).with_context(|| { format!("Copying {} to {}", new_from.display(), new_to.display()) })?; } @@ -251,7 +241,11 @@ pub fn copy_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { Ok(()) } -pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> { +pub fn symlink_dir( + sys: &TSys, + oldpath: &Path, + newpath: &Path, +) -> Result<(), Error> { let err_mapper = |err: Error, kind: Option| { Error::new( kind.unwrap_or_else(|| err.kind()), @@ -263,26 +257,18 @@ pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> { ), ) }; - #[cfg(unix)] - { - use std::os::unix::fs::symlink; - symlink(oldpath, newpath).map_err(|e| err_mapper(e, None))?; - } - #[cfg(not(unix))] - { - use std::os::windows::fs::symlink_dir; - symlink_dir(oldpath, newpath).map_err(|err| { - if let Some(code) = err.raw_os_error() { - if code as u32 == winapi::shared::winerror::ERROR_PRIVILEGE_NOT_HELD - || code as u32 == winapi::shared::winerror::ERROR_INVALID_FUNCTION - { - return err_mapper(err, Some(ErrorKind::PermissionDenied)); - } + + sys.fs_symlink_dir(oldpath, newpath).map_err(|err| { + #[cfg(windows)] + if let Some(code) = err.raw_os_error() { + if code as u32 == winapi::shared::winerror::ERROR_PRIVILEGE_NOT_HELD + || code as u32 == winapi::shared::winerror::ERROR_INVALID_FUNCTION + { + return err_mapper(err, Some(ErrorKind::PermissionDenied)); } - err_mapper(err, None) - })?; - } - Ok(()) + } + err_mapper(err, None) + }) } /// Gets the total size (in bytes) of a directory. From 0457c38d4fad9834770377bfd4e4e6a4d6ecadf7 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 2 Jan 2025 10:06:35 -0500 Subject: [PATCH 058/107] refactor: remove use of home crate (#27516) The two places mentioned in the issue are now consolidated. Closes https://github.com/denoland/deno/issues/24385 --- Cargo.lock | 21 ++++----------------- Cargo.toml | 2 +- ext/node/Cargo.toml | 3 +-- ext/node/ops/os/mod.rs | 7 ++++++- runtime/permissions/lib.rs | 3 +-- 5 files changed, 13 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30f4f0d394..eaba6115da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1355,7 +1355,7 @@ dependencies = [ "typed-arena", "uuid", "walkdir", - "which 4.4.2", + "which", "winapi", "winres", "zeromq", @@ -1996,7 +1996,6 @@ dependencies = [ "faster-hex", "h2 0.4.4", "hkdf", - "home", "http 1.1.0", "http-body-util", "hyper 1.4.1", @@ -2179,7 +2178,7 @@ dependencies = [ "percent-encoding", "serde", "thiserror 2.0.3", - "which 4.4.2", + "which", "winapi", ] @@ -2268,7 +2267,7 @@ dependencies = [ "tokio-metrics", "twox-hash", "uuid", - "which 4.4.2", + "which", "winapi", "windows-sys 0.59.0", ] @@ -8432,7 +8431,7 @@ dependencies = [ "miniz_oxide", "once_cell", "paste", - "which 6.0.1", + "which", ] [[package]] @@ -8742,18 +8741,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "which" version = "6.0.1" diff --git a/Cargo.toml b/Cargo.toml index 117a1bf6a8..722fe10c60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -212,7 +212,7 @@ url = { version = "2.5", features = ["serde", "expose_internals"] } uuid = { version = "1.3.0", features = ["v4"] } webpki-root-certs = "0.26.5" webpki-roots = "0.26" -which = "4.2.5" +which = "6" yoke = { version = "0.7.4", features = ["derive"] } zeromq = { version = "=0.4.1", default-features = false, features = ["tcp-transport", "tokio-runtime"] } zstd = "=0.12.4" diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index a3936af735..0f6ae1d7ab 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -49,7 +49,6 @@ errno = "0.2.8" faster-hex.workspace = true h2.workspace = true hkdf.workspace = true -home = "0.5.9" http.workspace = true http-body-util.workspace = true hyper.workspace = true @@ -93,7 +92,7 @@ simd-json = "0.14.0" sm3 = "0.4.2" spki.workspace = true stable_deref_trait = "1.2.0" -sys_traits = { workspace = true, features = ["real"] } +sys_traits = { workspace = true, features = ["real", "winapi", "libc"] } thiserror.workspace = true tokio.workspace = true tokio-eld = "0.2" diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs index d45f67bd13..944f950607 100644 --- a/ext/node/ops/os/mod.rs +++ b/ext/node/ops/os/mod.rs @@ -4,6 +4,7 @@ use std::mem::MaybeUninit; use deno_core::op2; use deno_core::OpState; +use sys_traits::EnvHomeDir; use crate::NodePermissions; @@ -282,5 +283,9 @@ where permissions.check_sys("homedir", "node:os.homedir()")?; } - Ok(home::home_dir().map(|path| path.to_string_lossy().to_string())) + Ok( + sys_traits::impls::RealSys + .env_home_dir() + .map(|path| path.to_string_lossy().to_string()), + ) } diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index b36668b10c..8ab4058e79 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -1510,11 +1510,10 @@ impl AllowRunDescriptor { match which::which_in(text, std::env::var_os("PATH"), cwd) { Ok(path) => path, Err(err) => match err { - which::Error::BadAbsolutePath | which::Error::BadRelativePath => { + which::Error::CannotGetCurrentDirAndPathListEmpty => { return Err(err); } which::Error::CannotFindBinaryPath - | which::Error::CannotGetCurrentDir | which::Error::CannotCanonicalize => { return Ok(AllowRunDescriptorParseResult::Unresolved(Box::new(err))) } From 2092f0c69748f589ce1deee285a0f92cf485ad10 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 2 Jan 2025 16:55:03 -0500 Subject: [PATCH 059/107] fix(permissions): implicit `--allow-import` when using `--cached-only` (#27530) `--cached-only` cannot communicate with a remote server Closes https://github.com/denoland/deno/issues/27498 --- cli/args/flags.rs | 120 -------------- cli/args/mod.rs | 150 ++++++++++++++++-- cli/standalone/binary.rs | 5 +- cli/standalone/mod.rs | 3 +- tests/specs/info/import_map/__test__.jsonc | 10 +- .../allow_import_cached_only/__test__.jsonc | 22 +++ .../allow_import_cached_only/deno.jsonc | 3 + .../allow_import_cached_only/fail.out | 2 + .../allow_import_cached_only/main.ts | 1 + .../allow_import_cached_only/success.out | 1 + .../allow_import_info/__test__.jsonc | 22 +++ .../allow_import_info/import_allowed.out | 8 + .../allow_import_info/import_not_allowed.out | 7 + .../permission/allow_import_info/main.ts | 1 + .../permission/allow_import_info/success.out | 7 + 15 files changed, 214 insertions(+), 148 deletions(-) create mode 100644 tests/specs/permission/allow_import_cached_only/__test__.jsonc create mode 100644 tests/specs/permission/allow_import_cached_only/deno.jsonc create mode 100644 tests/specs/permission/allow_import_cached_only/fail.out create mode 100644 tests/specs/permission/allow_import_cached_only/main.ts create mode 100644 tests/specs/permission/allow_import_cached_only/success.out create mode 100644 tests/specs/permission/allow_import_info/__test__.jsonc create mode 100644 tests/specs/permission/allow_import_info/import_allowed.out create mode 100644 tests/specs/permission/allow_import_info/import_not_allowed.out create mode 100644 tests/specs/permission/allow_import_info/main.ts create mode 100644 tests/specs/permission/allow_import_info/success.out diff --git a/cli/args/flags.rs b/cli/args/flags.rs index c57b81ae6d..fb64b4eeaa 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1,6 +1,5 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::borrow::Cow; use std::collections::HashSet; use std::env; use std::ffi::OsString; @@ -34,7 +33,6 @@ use deno_core::url::Url; use deno_graph::GraphKind; use deno_path_util::normalize_path; use deno_path_util::url_to_file_path; -use deno_runtime::deno_permissions::PermissionsOptions; use deno_runtime::deno_permissions::SysDescriptor; use deno_telemetry::OtelConfig; use deno_telemetry::OtelConsoleConfig; @@ -44,8 +42,6 @@ use serde::Deserialize; use serde::Serialize; use super::flags_net; -use super::jsr_url; -use crate::args::resolve_no_prompt; use crate::util::fs::canonicalize_path; #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -692,97 +688,6 @@ impl PermissionFlags { || self.deny_write.is_some() || self.allow_import.is_some() } - - pub fn to_options(&self, cli_arg_urls: &[Cow]) -> PermissionsOptions { - fn handle_allow( - allow_all: bool, - value: Option, - ) -> Option { - if allow_all { - assert!(value.is_none()); - Some(T::default()) - } else { - value - } - } - - fn handle_imports( - cli_arg_urls: &[Cow], - imports: Option>, - ) -> Option> { - if imports.is_some() { - return imports; - } - - let builtin_allowed_import_hosts = [ - "jsr.io:443", - "deno.land:443", - "esm.sh:443", - "cdn.jsdelivr.net:443", - "raw.githubusercontent.com:443", - "gist.githubusercontent.com:443", - ]; - - let mut imports = - Vec::with_capacity(builtin_allowed_import_hosts.len() + 1); - imports - .extend(builtin_allowed_import_hosts.iter().map(|s| s.to_string())); - - // also add the JSR_URL env var - if let Some(jsr_host) = allow_import_host_from_url(jsr_url()) { - imports.push(jsr_host); - } - // include the cli arg urls - for url in cli_arg_urls { - if let Some(host) = allow_import_host_from_url(url) { - imports.push(host); - } - } - - Some(imports) - } - - PermissionsOptions { - allow_all: self.allow_all, - allow_env: handle_allow(self.allow_all, self.allow_env.clone()), - deny_env: self.deny_env.clone(), - allow_net: handle_allow(self.allow_all, self.allow_net.clone()), - deny_net: self.deny_net.clone(), - allow_ffi: handle_allow(self.allow_all, self.allow_ffi.clone()), - deny_ffi: self.deny_ffi.clone(), - allow_read: handle_allow(self.allow_all, self.allow_read.clone()), - deny_read: self.deny_read.clone(), - allow_run: handle_allow(self.allow_all, self.allow_run.clone()), - deny_run: self.deny_run.clone(), - allow_sys: handle_allow(self.allow_all, self.allow_sys.clone()), - deny_sys: self.deny_sys.clone(), - allow_write: handle_allow(self.allow_all, self.allow_write.clone()), - deny_write: self.deny_write.clone(), - allow_import: handle_imports( - cli_arg_urls, - handle_allow(self.allow_all, self.allow_import.clone()), - ), - prompt: !resolve_no_prompt(self), - } - } -} - -/// Gets the --allow-import host from the provided url -fn allow_import_host_from_url(url: &Url) -> Option { - let host = url.host()?; - if let Some(port) = url.port() { - Some(format!("{}:{}", host, port)) - } else { - use deno_core::url::Host::*; - match host { - Domain(domain) if domain == "jsr.io" && url.scheme() == "https" => None, - _ => match url.scheme() { - "https" => Some(format!("{}:443", host)), - "http" => Some(format!("{}:80", host)), - _ => None, - }, - } - } } fn join_paths(allowlist: &[String], d: &str) -> String { @@ -11549,8 +11454,6 @@ mod tests { ..Default::default() } ); - // just make sure this doesn't panic - let _ = flags.permissions.to_options(&[]); } #[test] @@ -11626,29 +11529,6 @@ Usage: deno repl [OPTIONS] [-- [ARGS]...]\n" ) } - #[test] - fn test_allow_import_host_from_url() { - fn parse(text: &str) -> Option { - allow_import_host_from_url(&Url::parse(text).unwrap()) - } - - assert_eq!(parse("https://jsr.io"), None); - assert_eq!( - parse("http://127.0.0.1:4250"), - Some("127.0.0.1:4250".to_string()) - ); - assert_eq!(parse("http://jsr.io"), Some("jsr.io:80".to_string())); - assert_eq!( - parse("https://example.com"), - Some("example.com:443".to_string()) - ); - assert_eq!( - parse("http://example.com"), - Some("example.com:80".to_string()) - ); - assert_eq!(parse("file:///example.com"), None); - } - #[test] fn allow_all_conflicts_allow_perms() { let flags = [ diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 1ad61a0a78..35f79a9c3e 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1526,20 +1526,100 @@ impl CliOptions { self.flags.no_npm } - pub fn permission_flags(&self) -> &PermissionFlags { - &self.flags.permissions - } - pub fn permissions_options(&self) -> PermissionsOptions { - fn files_to_urls(files: &[String]) -> Vec> { - files - .iter() - .filter_map(|f| Url::parse(f).ok().map(Cow::Owned)) - .collect() + // bury this in here to ensure people use cli_options.permissions_options() + fn flags_to_options(flags: &PermissionFlags) -> PermissionsOptions { + fn handle_allow( + allow_all: bool, + value: Option, + ) -> Option { + if allow_all { + assert!(value.is_none()); + Some(T::default()) + } else { + value + } + } + + PermissionsOptions { + allow_all: flags.allow_all, + allow_env: handle_allow(flags.allow_all, flags.allow_env.clone()), + deny_env: flags.deny_env.clone(), + allow_net: handle_allow(flags.allow_all, flags.allow_net.clone()), + deny_net: flags.deny_net.clone(), + allow_ffi: handle_allow(flags.allow_all, flags.allow_ffi.clone()), + deny_ffi: flags.deny_ffi.clone(), + allow_read: handle_allow(flags.allow_all, flags.allow_read.clone()), + deny_read: flags.deny_read.clone(), + allow_run: handle_allow(flags.allow_all, flags.allow_run.clone()), + deny_run: flags.deny_run.clone(), + allow_sys: handle_allow(flags.allow_all, flags.allow_sys.clone()), + deny_sys: flags.deny_sys.clone(), + allow_write: handle_allow(flags.allow_all, flags.allow_write.clone()), + deny_write: flags.deny_write.clone(), + allow_import: handle_allow(flags.allow_all, flags.allow_import.clone()), + prompt: !resolve_no_prompt(flags), + } } - // get a list of urls to imply for --allow-import - let cli_arg_urls = self + let mut permissions_options = flags_to_options(&self.flags.permissions); + self.augment_import_permissions(&mut permissions_options); + permissions_options + } + + fn augment_import_permissions(&self, options: &mut PermissionsOptions) { + // do not add if the user specified --allow-all or --allow-import + if !options.allow_all && options.allow_import.is_none() { + options.allow_import = Some(self.implicit_allow_import()); + } + } + + fn implicit_allow_import(&self) -> Vec { + // allow importing from anywhere when using cached only + if self.cache_setting() == CacheSetting::Only { + vec![] // allow all imports + } else { + // implicitly allow some trusted hosts and the CLI arg urls + let cli_arg_urls = self.get_cli_arg_urls(); + let builtin_allowed_import_hosts = [ + "jsr.io:443", + "deno.land:443", + "esm.sh:443", + "cdn.jsdelivr.net:443", + "raw.githubusercontent.com:443", + "gist.githubusercontent.com:443", + ]; + let mut imports = Vec::with_capacity( + builtin_allowed_import_hosts.len() + cli_arg_urls.len() + 1, + ); + imports + .extend(builtin_allowed_import_hosts.iter().map(|s| s.to_string())); + // also add the JSR_URL env var + if let Some(jsr_host) = allow_import_host_from_url(jsr_url()) { + if jsr_host != "jsr.io:443" { + imports.push(jsr_host); + } + } + // include the cli arg urls + for url in cli_arg_urls { + if let Some(host) = allow_import_host_from_url(&url) { + imports.push(host); + } + } + imports + } + } + + fn get_cli_arg_urls(&self) -> Vec> { + fn files_to_urls(files: &[String]) -> Vec> { + files.iter().filter_map(|f| file_to_url(f)).collect() + } + + fn file_to_url(file: &str) -> Option> { + Url::parse(file).ok().map(Cow::Owned) + } + + self .resolve_main_module() .ok() .map(|url| vec![Cow::Borrowed(url)]) @@ -1551,18 +1631,18 @@ impl CliOptions { Some(files_to_urls(&check_flags.files)) } DenoSubcommand::Install(InstallFlags::Global(flags)) => { - Url::parse(&flags.module_url) - .ok() - .map(|url| vec![Cow::Owned(url)]) + file_to_url(&flags.module_url).map(|url| vec![url]) } DenoSubcommand::Doc(DocFlags { source_files: DocSourceFileFlag::Paths(paths), .. }) => Some(files_to_urls(paths)), + DenoSubcommand::Info(InfoFlags { + file: Some(file), .. + }) => file_to_url(file).map(|url| vec![url]), _ => None, }) - .unwrap_or_default(); - self.flags.permissions.to_options(&cli_arg_urls) + .unwrap_or_default() } pub fn reload_flag(&self) -> bool { @@ -1998,6 +2078,20 @@ fn load_env_variables_from_env_file(filename: Option<&Vec>) { } } +/// Gets the --allow-import host from the provided url +fn allow_import_host_from_url(url: &Url) -> Option { + let host = url.host()?; + if let Some(port) = url.port() { + Some(format!("{}:{}", host, port)) + } else { + match url.scheme() { + "https" => Some(format!("{}:443", host)), + "http" => Some(format!("{}:80", host)), + _ => None, + } + } +} + #[derive(Debug, Clone, Copy)] pub enum NpmCachingStrategy { Eager, @@ -2005,7 +2099,7 @@ pub enum NpmCachingStrategy { Manual, } -pub(crate) fn otel_runtime_config() -> OtelRuntimeConfig { +pub fn otel_runtime_config() -> OtelRuntimeConfig { OtelRuntimeConfig { runtime_name: Cow::Borrowed("deno"), runtime_version: Cow::Borrowed(crate::version::DENO_VERSION_INFO.deno), @@ -2102,4 +2196,26 @@ mod test { let reg_api_url = jsr_api_url(); assert!(reg_api_url.as_str().ends_with('/')); } + + #[test] + fn test_allow_import_host_from_url() { + fn parse(text: &str) -> Option { + allow_import_host_from_url(&Url::parse(text).unwrap()) + } + + assert_eq!( + parse("http://127.0.0.1:4250"), + Some("127.0.0.1:4250".to_string()) + ); + assert_eq!(parse("http://jsr.io"), Some("jsr.io:80".to_string())); + assert_eq!( + parse("https://example.com"), + Some("example.com:443".to_string()) + ); + assert_eq!( + parse("http://example.com"), + Some("example.com:80".to_string()) + ); + assert_eq!(parse("file:///example.com"), None); + } } diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 7ca25fca51..f07c645d89 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -51,6 +51,7 @@ use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_io::fs::FsError; use deno_runtime::deno_node::PackageJson; +use deno_runtime::deno_permissions::PermissionsOptions; use deno_semver::npm::NpmVersionReqParseError; use deno_semver::package::PackageReq; use deno_semver::Version; @@ -188,7 +189,7 @@ pub struct Metadata { pub argv: Vec, pub seed: Option, pub code_cache_key: Option, - pub permissions: PermissionFlags, + pub permissions: PermissionsOptions, pub location: Option, pub v8_flags: Vec, pub log_level: Option, @@ -793,7 +794,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { seed: self.cli_options.seed(), code_cache_key, location: self.cli_options.location_flag().clone(), - permissions: self.cli_options.permission_flags().clone(), + permissions: self.cli_options.permissions_options(), v8_flags: self.cli_options.v8_flags().clone(), unsafely_ignore_certificate_errors: self .cli_options diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 6ed192071f..1e21e69eb9 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -920,8 +920,7 @@ pub async fn run( }; let permissions = { - let mut permissions = - metadata.permissions.to_options(/* cli_arg_urls */ &[]); + let mut permissions = metadata.permissions; // grant read access to the vfs match &mut permissions.allow_read { Some(vec) if vec.is_empty() => { diff --git a/tests/specs/info/import_map/__test__.jsonc b/tests/specs/info/import_map/__test__.jsonc index 7aba603e0b..725276925e 100644 --- a/tests/specs/info/import_map/__test__.jsonc +++ b/tests/specs/info/import_map/__test__.jsonc @@ -1,9 +1,5 @@ { - "steps": [ - { - "args": "info preact/debug", - "output": "with_import_map.out", - "exitCode": 0 - } - ] + "args": "info preact/debug", + "output": "with_import_map.out", + "exitCode": 0 } diff --git a/tests/specs/permission/allow_import_cached_only/__test__.jsonc b/tests/specs/permission/allow_import_cached_only/__test__.jsonc new file mode 100644 index 0000000000..a86a8796c2 --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/__test__.jsonc @@ -0,0 +1,22 @@ +{ + "tempDir": true, + "tests": { + "no_flag": { + // ensure what we're testing will fail without the flags + "args": "run main.ts", + "output": "fail.out", + "exitCode": 1 + }, + "with_flags": { + "steps": [{ + "args": "cache --allow-import main.ts", + "output": "[WILDLINE]", + "exitCode": 0 + }, { + "args": "run --cached-only main.ts", + "output": "success.out", + "exitCode": 0 + }] + } + } +} diff --git a/tests/specs/permission/allow_import_cached_only/deno.jsonc b/tests/specs/permission/allow_import_cached_only/deno.jsonc new file mode 100644 index 0000000000..090481af96 --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/deno.jsonc @@ -0,0 +1,3 @@ +{ + "lock": true +} diff --git a/tests/specs/permission/allow_import_cached_only/fail.out b/tests/specs/permission/allow_import_cached_only/fail.out new file mode 100644 index 0000000000..517f53b9f4 --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/fail.out @@ -0,0 +1,2 @@ +error: Requires import access to "localhost:4545", run again with the --allow-import flag + at file:///[WILDLINE]/main.ts:1:8 diff --git a/tests/specs/permission/allow_import_cached_only/main.ts b/tests/specs/permission/allow_import_cached_only/main.ts new file mode 100644 index 0000000000..7556f22667 --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/main.ts @@ -0,0 +1 @@ +import "http://localhost:4545/welcome.ts"; diff --git a/tests/specs/permission/allow_import_cached_only/success.out b/tests/specs/permission/allow_import_cached_only/success.out new file mode 100644 index 0000000000..8432170eee --- /dev/null +++ b/tests/specs/permission/allow_import_cached_only/success.out @@ -0,0 +1 @@ +Welcome to Deno! diff --git a/tests/specs/permission/allow_import_info/__test__.jsonc b/tests/specs/permission/allow_import_info/__test__.jsonc new file mode 100644 index 0000000000..adcd0f2190 --- /dev/null +++ b/tests/specs/permission/allow_import_info/__test__.jsonc @@ -0,0 +1,22 @@ +{ + "envs": { + "JSR_URL": "" + }, + "tests": { + "implicit": { + "args": "info http://localhost:4545/welcome.ts", + "output": "success.out", + "exitCode": 0 + }, + "via_import_not_allowed": { + "args": "info main.ts", + "output": "import_not_allowed.out", + "exitCode": 0 + }, + "via_import_allowed": { + "args": "info --allow-import main.ts", + "output": "import_allowed.out", + "exitCode": 0 + } + } +} diff --git a/tests/specs/permission/allow_import_info/import_allowed.out b/tests/specs/permission/allow_import_info/import_allowed.out new file mode 100644 index 0000000000..95b61b27ef --- /dev/null +++ b/tests/specs/permission/allow_import_info/import_allowed.out @@ -0,0 +1,8 @@ +Download http://localhost:4545/welcome.ts +local: [WILDLINE] +type: TypeScript +dependencies: [WILDLINE] +size: [WILDLINE] + +file:///[WILDLINE]/main.ts ([WILDLINE]) +└── http://localhost:4545/welcome.ts ([WILDLINE]B) diff --git a/tests/specs/permission/allow_import_info/import_not_allowed.out b/tests/specs/permission/allow_import_info/import_not_allowed.out new file mode 100644 index 0000000000..c73eced93a --- /dev/null +++ b/tests/specs/permission/allow_import_info/import_not_allowed.out @@ -0,0 +1,7 @@ +local: [WILDLINE] +type: TypeScript +dependencies: [WILDLINE] +size: [WILDLINE] + +file:///[WILDLINE]/allow_import_info/main.ts ([WILDLINE]) +└── http://localhost:4545/welcome.ts (not capable, requires --allow-import) diff --git a/tests/specs/permission/allow_import_info/main.ts b/tests/specs/permission/allow_import_info/main.ts new file mode 100644 index 0000000000..7556f22667 --- /dev/null +++ b/tests/specs/permission/allow_import_info/main.ts @@ -0,0 +1 @@ +import "http://localhost:4545/welcome.ts"; diff --git a/tests/specs/permission/allow_import_info/success.out b/tests/specs/permission/allow_import_info/success.out new file mode 100644 index 0000000000..1b43d71444 --- /dev/null +++ b/tests/specs/permission/allow_import_info/success.out @@ -0,0 +1,7 @@ +Download http://localhost:4545/welcome.ts +local: [WILDLINE] +type: TypeScript +dependencies: [WILDLINE] +size: [WILDLINE] + +http://localhost:4545/welcome.ts ([WILDLINE]B) From e9af7f8ebd3c2b187080a6d344bca9dc93f15fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 2 Jan 2025 23:56:36 +0000 Subject: [PATCH 060/107] refactor: Use 'await using' in serve_test (#27532) To make sure the servers are torn down when they throw an error during test execution. --- tests/unit/serve_test.ts | 167 +++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 84 deletions(-) diff --git a/tests/unit/serve_test.ts b/tests/unit/serve_test.ts index 7aefd5b7d3..88aecefbcb 100644 --- a/tests/unit/serve_test.ts +++ b/tests/unit/serve_test.ts @@ -387,7 +387,7 @@ Deno.test(async function httpServerCanResolveHostnames() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_req) => new Response("ok"), hostname: "localhost", port: servePort, @@ -410,7 +410,7 @@ Deno.test(async function httpServerRejectsOnAddrInUse() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_req) => new Response("ok"), hostname: "localhost", port: servePort, @@ -441,7 +441,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() { const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request, { remoteAddr }) => { // FIXME(bartlomieju): // make sure that request can be inspected @@ -483,7 +483,7 @@ Deno.test( const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); const listener = Deno.listen({ port: servePort }); - const server = serveHttpOnListener( + await using server = serveHttpOnListener( listener, ac.signal, async ( @@ -532,7 +532,7 @@ Deno.test( headers: { "connection": "close" }, }); - const server = serveHttpOnConnection( + await using server = serveHttpOnConnection( await acceptPromise, ac.signal, async ( @@ -572,7 +572,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerOnError() { const { promise, resolve } = Promise.withResolvers(); let requestStash: Request | null; - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request: Request) => { requestStash = request; await new Promise((r) => setTimeout(r, 100)); @@ -607,7 +607,7 @@ Deno.test( // deno-lint-ignore no-unused-vars let requestStash: Request | null; - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request: Request) => { requestStash = request; await new Promise((r) => setTimeout(r, 100)); @@ -640,7 +640,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const response = new Response("Hello World"); let hadError = false; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { return response; }, @@ -684,7 +684,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); let hadError = false; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { return Response.error(); }, @@ -717,7 +717,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload1() { const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ port: servePort, signal: ac.signal, onListen: onListen(listeningDeferred.resolve), @@ -752,7 +752,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload2() { const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ port: servePort, signal: ac.signal, onListen: onListen(listeningDeferred.resolve), @@ -807,7 +807,7 @@ Deno.test( Deno.test({ permissions: { net: true } }, async function httpServerPort0() { const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler() { return new Response("Hello World"); }, @@ -841,7 +841,7 @@ Deno.test( }; try { - const server = Deno.serve({ + await using server = Deno.serve({ handler() { return new Response("Hello World"); }, @@ -866,7 +866,7 @@ Deno.test( const ac = new AbortController(); let headers: Headers; - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { await request.text(); headers = request.headers; @@ -896,7 +896,7 @@ Deno.test( ); Deno.test({ permissions: { net: true } }, async function validPortString() { - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_request) => new Response(), port: "4501" as unknown as number, }); @@ -921,7 +921,7 @@ Deno.test({ permissions: { net: true } }, async function ipv6Hostname() { }; try { - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response(), hostname: "::1", port: 0, @@ -1017,7 +1017,7 @@ function createUrlTest( const listeningDeferred = Promise.withResolvers(); const urlDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request: Request) => { urlDeferred.resolve(request.url); return new Response(""); @@ -1117,7 +1117,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.body, null); deferred.resolve(); @@ -1157,7 +1157,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { await assertRejects(async () => { await request.text(); @@ -1221,7 +1221,7 @@ function createStreamTest(count: number, delay: number, action: string) { Deno.test(`httpServerStreamCount${count}Delay${delay}${action}`, async () => { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_request) => { return new Response(makeStream(count, delay)); }, @@ -1275,7 +1275,7 @@ Deno.test( writer.close(); const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { const reqBody = await request.text(); assertEquals("hello world", reqBody); @@ -1303,7 +1303,7 @@ Deno.test( Deno.test({ permissions: { net: true } }, async function httpServerClose() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response("ok"), port: servePort, signal: ac.signal, @@ -1323,7 +1323,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerCloseGet() { const listeningDeferred = Promise.withResolvers(); const requestDeferred = Promise.withResolvers(); const responseDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async () => { requestDeferred.resolve(); await new Promise((r) => setTimeout(r, 500)); @@ -1349,13 +1349,12 @@ Deno.test({ permissions: { net: true } }, async function httpServerCloseGet() { await server.finished; }); -// FIXME: Deno.test( { permissions: { net: true } }, async function httpServerEmptyBlobResponse() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response(new Blob([])), port: servePort, signal: ac.signal, @@ -1380,7 +1379,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); const errorDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { const body = new ReadableStream({ start(controller) { @@ -1421,7 +1420,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response("韓國".repeat(10)), port: servePort, signal: ac.signal, @@ -1456,7 +1455,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() { const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); const doneDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, @@ -1501,7 +1500,7 @@ Deno.test( async function httpServerWebSocketRaw() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { const { conn, response } = upgradeHttpRaw(request); const buf = new Uint8Array(1024); @@ -1581,7 +1580,7 @@ Deno.test( const ac = new AbortController(); const done = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, @@ -1635,7 +1634,7 @@ Deno.test( const ac = new AbortController(); const done = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, @@ -1673,7 +1672,7 @@ Deno.test( const ac = new AbortController(); const done = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, @@ -1723,7 +1722,7 @@ Deno.test( const ac = new AbortController(); let headers: Headers; - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { headers = request.headers; deferred.resolve(); @@ -1762,7 +1761,7 @@ Deno.test( let headers: Headers; let text: string; - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { headers = request.headers; text = await request.text(); @@ -1807,7 +1806,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response(""); @@ -1858,7 +1857,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, signal: ac.signal }, (request: Request) => { assert(request.body); @@ -1889,7 +1888,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, signal: ac.signal }, (request: Request) => { assert(request.body); @@ -2005,7 +2004,7 @@ Deno.test( }).pipeThrough(new TextEncoderStream()); } - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response(periodicStream()); @@ -2037,7 +2036,7 @@ Deno.test( { permissions: { net: true } }, async function httpLargeReadableStreamChunk() { const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler() { return new Response( new ReadableStream({ @@ -2077,7 +2076,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const deferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.headers.get("X-Header-Test"), "á"); deferred.resolve(); @@ -2123,7 +2122,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { // FIXME: // assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`); @@ -2177,7 +2176,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(await request.text(), ""); assertEquals(request.headers.get("cookie"), "foo=bar; bar=foo"); @@ -2221,7 +2220,7 @@ Deno.test( const hostname = "localhost"; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response("ok"); @@ -2256,7 +2255,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.body, null); deferred.resolve(); @@ -2292,7 +2291,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.method, "GET"); assertEquals(request.headers.get("host"), "deno.land"); @@ -2326,7 +2325,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.method, "GET"); assertEquals(request.headers.get("server"), "hello\tworld"); @@ -2360,7 +2359,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(request.method, "GET"); assertEquals(await request.text(), ""); @@ -2396,7 +2395,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(request.method, "POST"); assertEquals(await request.text(), "I'm a good request."); @@ -2443,7 +2442,7 @@ function createServerLengthTest(name: string, testCase: TestCase) { const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { assertEquals(request.method, "GET"); deferred.resolve(); @@ -2575,7 +2574,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(request.method, "POST"); assertEquals(request.headers.get("content-length"), "5"); @@ -2611,7 +2610,7 @@ Deno.test( async function httpServerPostWithInvalidPrefixContentLength() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { throw new Error("unreachable"); }, @@ -2651,7 +2650,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals(request.method, "POST"); assertEquals(await request.text(), "qwert"); @@ -2688,7 +2687,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (r) => { deferred.resolve(); assertEquals(await r.text(), "12345"); @@ -2724,7 +2723,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response("NaN".repeat(100)); @@ -2867,7 +2866,7 @@ for (const testCase of compressionTestCases) { const deferred = Promise.withResolvers(); const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (_request) => { const f = await makeTempFile(testCase.length); deferred.resolve(); @@ -2923,7 +2922,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (request) => { assertEquals( await request.bytes(), @@ -2971,7 +2970,7 @@ for (const delay of ["delay", "nodelay"]) { const listeningDeferred = Promise.withResolvers(); const waitForAbort = Promise.withResolvers(); const waitForRequest = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ port: servePort, signal: ac.signal, onListen: onListen(listeningDeferred.resolve), @@ -3121,7 +3120,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const hostname = "127.0.0.1"; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response("Hello World"), hostname, port: servePort, @@ -3151,7 +3150,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const hostname = "127.0.0.1"; - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => new Response("Hello World"), hostname, port: servePort, @@ -3186,7 +3185,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const deferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { assertEquals(await req.text(), ""); deferred.resolve(); @@ -3221,7 +3220,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { throw new Error("oops"); }, @@ -3268,7 +3267,7 @@ Deno.test( async function httpServer204ResponseDoesntSendContentLength() { const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_request) => new Response(null, { status: 204 }), port: servePort, signal: ac.signal, @@ -3298,7 +3297,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { deferred.resolve(); return new Response(null, { status: 304 }); @@ -3343,7 +3342,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { deferred.resolve(); assertEquals(await req.text(), "hello"); @@ -3404,7 +3403,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { deferred.resolve(); assertEquals(await req.text(), ""); @@ -3458,7 +3457,7 @@ for (const [name, req] of badRequests) { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { throw new Error("oops"); }, @@ -3505,7 +3504,7 @@ Deno.test( let reqCount = -1; let timerId: number | undefined; - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_req) => { reqCount++; if (reqCount === 0) { @@ -3600,7 +3599,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); let count = 0; - const server = Deno.serve({ + await using server = Deno.serve({ async onListen({ port }: { port: number }) { const res1 = await fetch(`http://localhost:${port}/`); assertEquals(await res1.text(), "hello world 1"); @@ -3630,7 +3629,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { const cloned = req.clone(); assertEquals(req.headers, cloned.headers); @@ -3684,7 +3683,7 @@ Deno.test( const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { await req.text(); @@ -3733,7 +3732,7 @@ Deno.test({ const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: async (req) => { const _reader = req.body?.getReader(); @@ -3780,7 +3779,7 @@ Deno.test( async function testIssue16567() { const ac = new AbortController(); const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ async onListen({ port }) { const res1 = await fetch(`http://localhost:${port}/`); assertEquals((await res1.text()).length, 40 * 50_000); @@ -3947,7 +3946,7 @@ Deno.test( }, async function httpServeCurlH2C() { const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, signal: ac.signal }, () => new Response("hello world!"), ); @@ -3982,7 +3981,7 @@ Deno.test( const ac = new AbortController(); const { resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: () => { const response = new Response("Hello World", { headers: { @@ -4025,7 +4024,7 @@ Deno.test( }, async function httpsServeCurlH2C() { const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { signal: ac.signal, port: servePort, @@ -4082,7 +4081,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); const filePath = tmpUnixSocketPath(); - const server = Deno.serve( + await using server = Deno.serve( { signal: ac.signal, path: filePath, @@ -4115,7 +4114,7 @@ Deno.test( const listeningDeferred = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, onListen: onListen(listeningDeferred.resolve), @@ -4151,7 +4150,7 @@ Deno.test( const { promise, resolve } = Promise.withResolvers(); const ac = new AbortController(); - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, onListen: onListen(resolve), @@ -4187,7 +4186,7 @@ Deno.test( let timer: number | undefined = undefined; let _controller; - const server = Deno.serve( + await using server = Deno.serve( { port: servePort, onListen: onListen(resolve), @@ -4237,7 +4236,7 @@ Deno.test({ await assertRejects( async () => { const ac = new AbortController(); - const server = Deno.serve({ + await using server = Deno.serve({ path: "path/to/socket", handler: (_req) => new Response("Hello, world"), signal: ac.signal, @@ -4260,7 +4259,7 @@ Deno.test({ }, async () => { const { promise, resolve } = Promise.withResolvers<{ hostname: string }>(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (_) => new Response("ok"), hostname: "0.0.0.0", port: 0, @@ -4278,7 +4277,7 @@ Deno.test({ let cancelled = false; - const server = Deno.serve({ + await using server = Deno.serve({ hostname: "0.0.0.0", port: servePort, onListen: () => resolve(), @@ -4305,7 +4304,7 @@ Deno.test({ }, async () => { const { promise, resolve } = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ hostname: "0.0.0.0", port: servePort, onListen: () => resolve(), @@ -4335,7 +4334,7 @@ Deno.test( const ac = new AbortController(); const listeningDeferred = Promise.withResolvers(); const doneDeferred = Promise.withResolvers(); - const server = Deno.serve({ + await using server = Deno.serve({ handler: (request) => { const { response, From 18b813b93ffdbacecde6a064549205114812a22d Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Fri, 3 Jan 2025 18:30:41 +0000 Subject: [PATCH 061/107] fix(check): line-break between diagnostic message chain entries (#27543) --- cli/tsc/diagnostics.rs | 2 +- .../check/message_chain_formatting/__test__.jsonc | 6 ++++++ .../message_chain_formatting.out | 10 ++++++++++ .../message_chain_formatting.ts | 8 ++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/specs/check/message_chain_formatting/__test__.jsonc create mode 100644 tests/specs/check/message_chain_formatting/message_chain_formatting.out create mode 100644 tests/specs/check/message_chain_formatting/message_chain_formatting.ts diff --git a/cli/tsc/diagnostics.rs b/cli/tsc/diagnostics.rs index 3f866e0c96..cda41525c2 100644 --- a/cli/tsc/diagnostics.rs +++ b/cli/tsc/diagnostics.rs @@ -90,9 +90,9 @@ impl DiagnosticMessageChain { s.push_str(&" ".repeat(level * 2)); s.push_str(&self.message_text); if let Some(next) = &self.next { - s.push('\n'); let arr = next.clone(); for dm in arr { + s.push('\n'); s.push_str(&dm.format_message(level + 1)); } } diff --git a/tests/specs/check/message_chain_formatting/__test__.jsonc b/tests/specs/check/message_chain_formatting/__test__.jsonc new file mode 100644 index 0000000000..1b5b49d8a9 --- /dev/null +++ b/tests/specs/check/message_chain_formatting/__test__.jsonc @@ -0,0 +1,6 @@ +// Regression test for https://github.com/denoland/deno/issues/27411. +{ + "args": "check --quiet message_chain_formatting.ts", + "output": "message_chain_formatting.out", + "exitCode": 1 +} diff --git a/tests/specs/check/message_chain_formatting/message_chain_formatting.out b/tests/specs/check/message_chain_formatting/message_chain_formatting.out new file mode 100644 index 0000000000..ca5c646ccb --- /dev/null +++ b/tests/specs/check/message_chain_formatting/message_chain_formatting.out @@ -0,0 +1,10 @@ +error: TS2769 [ERROR]: No overload matches this call. + Overload 1 of 3, '(s: string, b: boolean): void', gave the following error. + Argument of type 'number' is not assignable to parameter of type 'boolean'. + Overload 2 of 3, '(ss: string[], b: boolean): void', gave the following error. + Argument of type 'string' is not assignable to parameter of type 'string[]'. + Overload 3 of 3, '(ss: string[], b: Date): void', gave the following error. + Argument of type 'string' is not assignable to parameter of type 'string[]'. +foo("hello", 42); +~~~ + at [WILDLINE]/message_chain_formatting.ts:8:1 diff --git a/tests/specs/check/message_chain_formatting/message_chain_formatting.ts b/tests/specs/check/message_chain_formatting/message_chain_formatting.ts new file mode 100644 index 0000000000..ed342629ea --- /dev/null +++ b/tests/specs/check/message_chain_formatting/message_chain_formatting.ts @@ -0,0 +1,8 @@ +function foo(s: string, b: boolean): void; +function foo(ss: string[], b: boolean): void; +function foo(ss: string[], b: Date): void; +function foo(sOrSs: string | string[], b: boolean | Date): void { + console.log(sOrSs, b); +} + +foo("hello", 42); From 89c92b84fadf99e2e2d9ba6b9f469fa8991319d7 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 3 Jan 2025 16:49:56 -0500 Subject: [PATCH 062/107] fix(check): move module not found errors to typescript diagnostics (#27533) Instead of hard erroring, we now surface module not found errors as TypeScript diagnostics (we have yet to show the source code of the error, but something we can improve over time). --- Cargo.lock | 8 +- Cargo.toml | 2 +- cli/Cargo.toml | 2 +- cli/errors.rs | 84 ++-- cli/factory.rs | 1 + cli/graph_util.rs | 108 ++-- cli/module_loader.rs | 20 +- cli/tools/check.rs | 467 ++++++++++-------- cli/tsc/99_main_compiler.js | 49 +- cli/tsc/diagnostics.rs | 49 ++ cli/tsc/mod.rs | 73 +-- tests/integration/jsr_tests.rs | 3 +- tests/specs/check/css_import/__test__.jsonc | 4 + tests/specs/check/css_import/not_exists.out | 3 +- .../dts_importing_non_existent/check.out | 3 +- .../__test__.jsonc | 14 + .../check_all.out | 5 + .../import_remote.ts | 3 + .../check/module_not_found/__test__.jsonc | 24 + tests/specs/check/module_not_found/main.out | 9 + tests/specs/check/module_not_found/main.ts | 5 + .../module_not_found/missing_local_root.out | 2 + .../module_not_found/missing_remote_root.out | 3 + .../types_resolved_relative_config/main.out | 3 +- .../sloppy_imports_not_enabled.out | 5 +- .../jsx_import_source_error.out | 5 +- .../reference_types_error.js.out | 5 +- .../reference_types_error.js.out | 5 +- tests/specs/run/sloppy_imports/no_sloppy.out | 26 +- .../import_file_not_found/__test__.jsonc | 15 +- .../import_file_not_found/check.out | 4 + .../wasm_module/import_file_not_found/main.js | 1 + .../import_file_not_found/main.out | 2 +- .../__test__.jsonc | 15 +- .../import_named_export_not_found/check.out | 9 + .../import_named_export_not_found/main.js | 1 + tests/testdata/check/import_non_existent.ts | 5 + 37 files changed, 684 insertions(+), 358 deletions(-) create mode 100644 tests/specs/check/import_non_existent_in_remote/__test__.jsonc create mode 100644 tests/specs/check/import_non_existent_in_remote/check_all.out create mode 100644 tests/specs/check/import_non_existent_in_remote/import_remote.ts create mode 100644 tests/specs/check/module_not_found/__test__.jsonc create mode 100644 tests/specs/check/module_not_found/main.out create mode 100644 tests/specs/check/module_not_found/main.ts create mode 100644 tests/specs/check/module_not_found/missing_local_root.out create mode 100644 tests/specs/check/module_not_found/missing_remote_root.out create mode 100644 tests/specs/run/wasm_module/import_file_not_found/check.out create mode 100644 tests/specs/run/wasm_module/import_named_export_not_found/check.out create mode 100644 tests/testdata/check/import_non_existent.ts diff --git a/Cargo.lock b/Cargo.lock index eaba6115da..b054e9fe7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,9 +1750,9 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.86.6" +version = "0.86.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83af194ca492ea7b624d21055f933676d3f3d27586de93be31c8f1babcc73510" +checksum = "ace3acf321fac446636ae605b01723f2120b40ab3d32c6836aeb7d603a8e08f9" dependencies = [ "anyhow", "async-trait", @@ -1905,9 +1905,9 @@ dependencies = [ [[package]] name = "deno_media_type" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa135b8a9febc9a51c16258e294e268a1276750780d69e46edb31cced2826e4" +checksum = "a417f8bd3f1074185c4c8ccb6ea6261ae173781596cc358e68ad07aaac11009d" dependencies = [ "data-url", "serde", diff --git a/Cargo.toml b/Cargo.toml index 722fe10c60..fa2813caed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ deno_core = { version = "0.327.0" } deno_bench_util = { version = "0.178.0", path = "./bench_util" } deno_config = { version = "=0.42.0", features = ["workspace", "sync"] } deno_lockfile = "=0.24.0" -deno_media_type = { version = "0.2.0", features = ["module_specifier"] } +deno_media_type = { version = "0.2.3", features = ["module_specifier"] } deno_npm = "=0.27.0" deno_path_util = "=0.3.0" deno_permissions = { version = "0.43.0", path = "./runtime/permissions" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 3ca1afc872..a2245b0806 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -74,7 +74,7 @@ deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] } deno_error.workspace = true -deno_graph = { version = "=0.86.6" } +deno_graph = { version = "=0.86.7" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true deno_npm.workspace = true diff --git a/cli/errors.rs b/cli/errors.rs index ead1ccf127..6500efec50 100644 --- a/cli/errors.rs +++ b/cli/errors.rs @@ -26,51 +26,55 @@ fn get_diagnostic_class(_: &ParseDiagnostic) -> &'static str { "SyntaxError" } -fn get_module_graph_error_class(err: &ModuleGraphError) -> &'static str { - use deno_graph::JsrLoadError; - use deno_graph::NpmLoadError; - +pub fn get_module_graph_error_class(err: &ModuleGraphError) -> &'static str { match err { ModuleGraphError::ResolutionError(err) | ModuleGraphError::TypesResolutionError(err) => { get_resolution_error_class(err) } - ModuleGraphError::ModuleError(err) => match err { - ModuleError::InvalidTypeAssertion { .. } => "SyntaxError", - ModuleError::ParseErr(_, diagnostic) => get_diagnostic_class(diagnostic), - ModuleError::WasmParseErr(..) => "SyntaxError", - ModuleError::UnsupportedMediaType { .. } - | ModuleError::UnsupportedImportAttributeType { .. } => "TypeError", - ModuleError::Missing(_, _) | ModuleError::MissingDynamic(_, _) => { - "NotFound" - } - ModuleError::LoadingErr(_, _, err) => match err { - ModuleLoadError::Loader(err) => get_error_class_name(err.as_ref()), - ModuleLoadError::HttpsChecksumIntegrity(_) - | ModuleLoadError::TooManyRedirects => "Error", - ModuleLoadError::NodeUnknownBuiltinModule(_) => "NotFound", - ModuleLoadError::Decode(_) => "TypeError", - ModuleLoadError::Npm(err) => match err { - NpmLoadError::NotSupportedEnvironment - | NpmLoadError::PackageReqResolution(_) - | NpmLoadError::RegistryInfo(_) => "Error", - NpmLoadError::PackageReqReferenceParse(_) => "TypeError", - }, - ModuleLoadError::Jsr(err) => match err { - JsrLoadError::UnsupportedManifestChecksum - | JsrLoadError::PackageFormat(_) => "TypeError", - JsrLoadError::ContentLoadExternalSpecifier - | JsrLoadError::ContentLoad(_) - | JsrLoadError::ContentChecksumIntegrity(_) - | JsrLoadError::PackageManifestLoad(_, _) - | JsrLoadError::PackageVersionManifestChecksumIntegrity(..) - | JsrLoadError::PackageVersionManifestLoad(_, _) - | JsrLoadError::RedirectInPackage(_) => "Error", - JsrLoadError::PackageNotFound(_) - | JsrLoadError::PackageReqNotFound(_) - | JsrLoadError::PackageVersionNotFound(_) - | JsrLoadError::UnknownExport { .. } => "NotFound", - }, + ModuleGraphError::ModuleError(err) => get_module_error_class(err), + } +} + +pub fn get_module_error_class(err: &ModuleError) -> &'static str { + use deno_graph::JsrLoadError; + use deno_graph::NpmLoadError; + + match err { + ModuleError::InvalidTypeAssertion { .. } => "SyntaxError", + ModuleError::ParseErr(_, diagnostic) => get_diagnostic_class(diagnostic), + ModuleError::WasmParseErr(..) => "SyntaxError", + ModuleError::UnsupportedMediaType { .. } + | ModuleError::UnsupportedImportAttributeType { .. } => "TypeError", + ModuleError::Missing(_, _) | ModuleError::MissingDynamic(_, _) => { + "NotFound" + } + ModuleError::LoadingErr(_, _, err) => match err { + ModuleLoadError::Loader(err) => get_error_class_name(err.as_ref()), + ModuleLoadError::HttpsChecksumIntegrity(_) + | ModuleLoadError::TooManyRedirects => "Error", + ModuleLoadError::NodeUnknownBuiltinModule(_) => "NotFound", + ModuleLoadError::Decode(_) => "TypeError", + ModuleLoadError::Npm(err) => match err { + NpmLoadError::NotSupportedEnvironment + | NpmLoadError::PackageReqResolution(_) + | NpmLoadError::RegistryInfo(_) => "Error", + NpmLoadError::PackageReqReferenceParse(_) => "TypeError", + }, + ModuleLoadError::Jsr(err) => match err { + JsrLoadError::UnsupportedManifestChecksum + | JsrLoadError::PackageFormat(_) => "TypeError", + JsrLoadError::ContentLoadExternalSpecifier + | JsrLoadError::ContentLoad(_) + | JsrLoadError::ContentChecksumIntegrity(_) + | JsrLoadError::PackageManifestLoad(_, _) + | JsrLoadError::PackageVersionManifestChecksumIntegrity(..) + | JsrLoadError::PackageVersionManifestLoad(_, _) + | JsrLoadError::RedirectInPackage(_) => "Error", + JsrLoadError::PackageNotFound(_) + | JsrLoadError::PackageReqNotFound(_) + | JsrLoadError::PackageVersionNotFound(_) + | JsrLoadError::UnknownExport { .. } => "NotFound", }, }, } diff --git a/cli/factory.rs b/cli/factory.rs index c507d8388d..4ae1d94ea8 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -753,6 +753,7 @@ impl CliFactory { self.module_graph_builder().await?.clone(), self.node_resolver().await?.clone(), self.npm_resolver().await?.clone(), + self.sys(), ))) }) .await diff --git a/cli/graph_util.rs b/cli/graph_util.rs index fb933bdac8..ac9e75cff0 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -50,6 +50,7 @@ use crate::cache::ModuleInfoCache; use crate::cache::ParsedSourceCache; use crate::colors; use crate::errors::get_error_class_name; +use crate::errors::get_module_graph_error_class; use crate::file_fetcher::CliFileFetcher; use crate::npm::CliNpmResolver; use crate::resolver::CjsTracker; @@ -164,29 +165,15 @@ pub fn graph_walk_errors<'a>( roots.contains(error.specifier()) } }; - let mut message = match &error { - ModuleGraphError::ResolutionError(resolution_error) => { - enhanced_resolution_error_message(resolution_error) - } - ModuleGraphError::TypesResolutionError(resolution_error) => { - format!( - "Failed resolving types. {}", - enhanced_resolution_error_message(resolution_error) - ) - } - ModuleGraphError::ModuleError(error) => { - enhanced_integrity_error_message(error) - .or_else(|| enhanced_sloppy_imports_error_message(sys, error)) - .unwrap_or_else(|| format_deno_graph_error(error)) - } - }; - - if let Some(range) = error.maybe_range() { - if !is_root && !range.specifier.as_str().contains("/$deno$eval") { - message.push_str("\n at "); - message.push_str(&format_range_with_colors(range)); - } - } + let message = enhance_graph_error( + sys, + &error, + if is_root { + EnhanceGraphErrorMode::HideRange + } else { + EnhanceGraphErrorMode::ShowRange + }, + ); if graph.graph_kind() == GraphKind::TypesOnly && matches!( @@ -198,10 +185,61 @@ pub fn graph_walk_errors<'a>( return None; } - Some(custom_error(get_error_class_name(&error.into()), message)) + if graph.graph_kind().include_types() + && (message.contains(RUN_WITH_SLOPPY_IMPORTS_MSG) + || matches!( + error, + ModuleGraphError::ModuleError(ModuleError::Missing(..)) + )) + { + // ignore and let typescript surface this as a diagnostic instead + log::debug!("Ignoring: {}", message); + return None; + } + + Some(custom_error(get_module_graph_error_class(&error), message)) }) } +#[derive(Debug, PartialEq, Eq)] +pub enum EnhanceGraphErrorMode { + ShowRange, + HideRange, +} + +pub fn enhance_graph_error( + sys: &CliSys, + error: &ModuleGraphError, + mode: EnhanceGraphErrorMode, +) -> String { + let mut message = match &error { + ModuleGraphError::ResolutionError(resolution_error) => { + enhanced_resolution_error_message(resolution_error) + } + ModuleGraphError::TypesResolutionError(resolution_error) => { + format!( + "Failed resolving types. {}", + enhanced_resolution_error_message(resolution_error) + ) + } + ModuleGraphError::ModuleError(error) => { + enhanced_integrity_error_message(error) + .or_else(|| enhanced_sloppy_imports_error_message(sys, error)) + .unwrap_or_else(|| format_deno_graph_error(error)) + } + }; + + if let Some(range) = error.maybe_range() { + if mode == EnhanceGraphErrorMode::ShowRange + && !range.specifier.as_str().contains("/$deno$eval") + { + message.push_str("\n at "); + message.push_str(&format_range_with_colors(range)); + } + } + message +} + pub fn graph_exit_integrity_errors(graph: &ModuleGraph) { for error in graph.module_errors() { exit_for_integrity_error(error); @@ -835,6 +873,9 @@ pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String { message } +static RUN_WITH_SLOPPY_IMPORTS_MSG: &str = + "or run with --unstable-sloppy-imports"; + fn enhanced_sloppy_imports_error_message( sys: &CliSys, error: &ModuleError, @@ -842,11 +883,9 @@ fn enhanced_sloppy_imports_error_message( match error { ModuleError::LoadingErr(specifier, _, ModuleLoadError::Loader(_)) // ex. "Is a directory" error | ModuleError::Missing(specifier, _) => { - let additional_message = CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(sys.clone())) - .resolve(specifier, SloppyImportsResolutionKind::Execution)? - .as_suggestion_message(); + let additional_message = maybe_additional_sloppy_imports_message(sys, specifier)?; Some(format!( - "{} {} or run with --unstable-sloppy-imports", + "{} {}", error, additional_message, )) @@ -855,6 +894,19 @@ fn enhanced_sloppy_imports_error_message( } } +pub fn maybe_additional_sloppy_imports_message( + sys: &CliSys, + specifier: &ModuleSpecifier, +) -> Option { + Some(format!( + "{} {}", + CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(sys.clone())) + .resolve(specifier, SloppyImportsResolutionKind::Execution)? + .as_suggestion_message(), + RUN_WITH_SLOPPY_IMPORTS_MSG + )) +} + fn enhanced_integrity_error_message(err: &ModuleError) -> Option { match err { ModuleError::LoadingErr( diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 8256c56781..446397cad1 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -36,6 +36,7 @@ use deno_graph::JsModule; use deno_graph::JsonModule; use deno_graph::Module; use deno_graph::ModuleGraph; +use deno_graph::ModuleGraphError; use deno_graph::Resolution; use deno_graph::WasmModule; use deno_runtime::code_cache; @@ -58,10 +59,13 @@ use crate::cache::CodeCache; use crate::cache::FastInsecureHasher; use crate::cache::ParsedSourceCache; use crate::emit::Emitter; +use crate::errors::get_module_error_class; use crate::graph_container::MainModuleGraphContainer; use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; +use crate::graph_util::enhance_graph_error; use crate::graph_util::CreateGraphOptions; +use crate::graph_util::EnhanceGraphErrorMode; use crate::graph_util::ModuleGraphBuilder; use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; @@ -703,7 +707,21 @@ impl unreachable!("Deno bug. {} was misconfigured internally.", specifier); } - match graph.get(specifier) { + let maybe_module = match graph.try_get(specifier) { + Ok(module) => module, + Err(err) => { + return Err(custom_error( + get_module_error_class(err), + enhance_graph_error( + &self.shared.sys, + &ModuleGraphError::ModuleError(err.clone()), + EnhanceGraphErrorMode::ShowRange, + ), + )) + } + }; + + match maybe_module { Some(deno_graph::Module::Json(JsonModule { source, media_type, diff --git a/cli/tools/check.rs b/cli/tools/check.rs index f3df54626a..1ee3f1782b 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -8,7 +8,9 @@ use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; use deno_graph::Module; +use deno_graph::ModuleError; use deno_graph::ModuleGraph; +use deno_graph::ModuleLoadError; use deno_terminal::colors; use once_cell::sync::Lazy; use regex::Regex; @@ -26,10 +28,12 @@ use crate::cache::Caches; use crate::cache::FastInsecureHasher; use crate::cache::TypeCheckCache; use crate::factory::CliFactory; +use crate::graph_util::maybe_additional_sloppy_imports_message; use crate::graph_util::BuildFastCheckGraphOptions; use crate::graph_util::ModuleGraphBuilder; use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; +use crate::sys::CliSys; use crate::tsc; use crate::tsc::Diagnostics; use crate::tsc::TypeCheckingCjsTracker; @@ -105,6 +109,7 @@ pub struct TypeChecker { module_graph_builder: Arc, node_resolver: Arc, npm_resolver: Arc, + sys: CliSys, } impl TypeChecker { @@ -115,6 +120,7 @@ impl TypeChecker { module_graph_builder: Arc, node_resolver: Arc, npm_resolver: Arc, + sys: CliSys, ) -> Self { Self { caches, @@ -123,6 +129,7 @@ impl TypeChecker { module_graph_builder, node_resolver, npm_resolver, + sys, } } @@ -177,26 +184,47 @@ impl TypeChecker { let type_check_mode = options.type_check_mode; let ts_config = ts_config_result.ts_config; - let maybe_check_hash = match self.npm_resolver.check_state_hash() { - Some(npm_check_hash) => { - match get_check_hash( - &graph, - npm_check_hash, - type_check_mode, - &ts_config, - ) { - CheckHashResult::NoFiles => { - return Ok((graph.into(), Default::default())) - } - CheckHashResult::Hash(hash) => Some(hash), - } - } - None => None, // we can't determine a check hash - }; - - // do not type check if we know this is type checked let cache = TypeCheckCache::new(self.caches.type_checking_cache_db()); + let check_js = ts_config.get_check_js(); + + // add fast check to the graph before getting the roots + if options.build_fast_check_graph { + self.module_graph_builder.build_fast_check_graph( + &mut graph, + BuildFastCheckGraphOptions { + workspace_fast_check: deno_graph::WorkspaceFastCheckOption::Disabled, + }, + )?; + } + + let filter_remote_diagnostics = |d: &tsc::Diagnostic| { + if self.is_remote_diagnostic(d) { + type_check_mode == TypeCheckMode::All && d.include_when_remote() + } else { + true + } + }; + let TscRoots { + roots: root_names, + missing_diagnostics, + maybe_check_hash, + } = get_tsc_roots( + &self.sys, + &graph, + check_js, + self.npm_resolver.check_state_hash(), + type_check_mode, + &ts_config, + ); + + let missing_diagnostics = + missing_diagnostics.filter(filter_remote_diagnostics); + + if root_names.is_empty() && missing_diagnostics.is_empty() { + return Ok((graph.into(), Default::default())); + } if !options.reload { + // do not type check if we know this is type checked if let Some(check_hash) = maybe_check_hash { if cache.has_check_hash(check_hash) { log::debug!("Already type checked."); @@ -214,7 +242,6 @@ impl TypeChecker { ); } - let check_js = ts_config.get_check_js(); // while there might be multiple roots, we can't "merge" the build info, so we // try to retrieve the build info for first root, which is the most common use // case. @@ -226,27 +253,15 @@ impl TypeChecker { // to make tsc build info work, we need to consistently hash modules, so that // tsc can better determine if an emit is still valid or not, so we provide // that data here. - let hash_data = FastInsecureHasher::new_deno_versioned() + let tsconfig_hash_data = FastInsecureHasher::new_deno_versioned() .write(&ts_config.as_bytes()) .finish(); - - // add fast check to the graph before getting the roots - if options.build_fast_check_graph { - self.module_graph_builder.build_fast_check_graph( - &mut graph, - BuildFastCheckGraphOptions { - workspace_fast_check: deno_graph::WorkspaceFastCheckOption::Disabled, - }, - )?; - } - - let root_names = get_tsc_roots(&graph, check_js); let graph = Arc::new(graph); let response = tsc::exec(tsc::Request { config: ts_config, debug: self.cli_options.log_level() == Some(log::Level::Debug), graph: graph.clone(), - hash_data, + hash_data: tsconfig_hash_data, maybe_npm: Some(tsc::RequestNpmState { cjs_tracker: self.cjs_tracker.clone(), node_resolver: self.node_resolver.clone(), @@ -257,13 +272,11 @@ impl TypeChecker { check_mode: type_check_mode, })?; - let mut diagnostics = response.diagnostics.filter(|d| { - if self.is_remote_diagnostic(d) { - type_check_mode == TypeCheckMode::All && d.include_when_remote() - } else { - true - } - }); + let response_diagnostics = + response.diagnostics.filter(filter_remote_diagnostics); + + let mut diagnostics = missing_diagnostics; + diagnostics.extend(response_diagnostics); diagnostics.apply_fast_check_source_maps(&graph); @@ -297,108 +310,10 @@ impl TypeChecker { } } -enum CheckHashResult { - Hash(CacheDBHash), - NoFiles, -} - -/// Gets a hash of the inputs for type checking. This can then -/// be used to tell -fn get_check_hash( - graph: &ModuleGraph, - package_reqs_hash: u64, - type_check_mode: TypeCheckMode, - ts_config: &TsConfig, -) -> CheckHashResult { - let mut hasher = FastInsecureHasher::new_deno_versioned(); - hasher.write_u8(match type_check_mode { - TypeCheckMode::All => 0, - TypeCheckMode::Local => 1, - TypeCheckMode::None => 2, - }); - hasher.write(&ts_config.as_bytes()); - - let check_js = ts_config.get_check_js(); - let mut has_file = false; - let mut has_file_to_type_check = false; - // this iterator of modules is already deterministic, so no need to sort it - for module in graph.modules() { - match module { - Module::Js(module) => { - let ts_check = has_ts_check(module.media_type, &module.source); - if ts_check { - has_file_to_type_check = true; - } - - match module.media_type { - MediaType::TypeScript - | MediaType::Dts - | MediaType::Dmts - | MediaType::Dcts - | MediaType::Mts - | MediaType::Cts - | MediaType::Tsx => { - has_file = true; - has_file_to_type_check = true; - } - MediaType::JavaScript - | MediaType::Mjs - | MediaType::Cjs - | MediaType::Jsx => { - has_file = true; - if !check_js && !ts_check { - continue; - } - } - MediaType::Json - | MediaType::Css - | MediaType::SourceMap - | MediaType::Wasm - | MediaType::Unknown => continue, - } - - hasher.write_str(module.specifier.as_str()); - hasher.write_str( - // the fast check module will only be set when publishing - module - .fast_check_module() - .map(|s| s.source.as_ref()) - .unwrap_or(&module.source), - ); - } - Module::Node(_) => { - // the @types/node package will be in the resolved - // snapshot below so don't bother including it here - } - Module::Npm(_) => { - // don't bother adding this specifier to the hash - // because what matters is the resolved npm snapshot, - // which is hashed below - } - Module::Json(module) => { - has_file_to_type_check = true; - hasher.write_str(module.specifier.as_str()); - hasher.write_str(&module.source); - } - Module::Wasm(module) => { - has_file_to_type_check = true; - hasher.write_str(module.specifier.as_str()); - hasher.write_str(&module.source_dts); - } - Module::External(module) => { - hasher.write_str(module.specifier.as_str()); - } - } - } - - hasher.write_hashable(package_reqs_hash); - - if !has_file || !check_js && !has_file_to_type_check { - // no files to type check - CheckHashResult::NoFiles - } else { - CheckHashResult::Hash(CacheDBHash::new(hasher.finish())) - } +struct TscRoots { + roots: Vec<(ModuleSpecifier, MediaType)>, + missing_diagnostics: tsc::Diagnostics, + maybe_check_hash: Option, } /// Transform the graph into root specifiers that we can feed `tsc`. We have to @@ -408,52 +323,115 @@ fn get_check_hash( /// the roots, so they get type checked and optionally emitted, /// otherwise they would be ignored if only imported into JavaScript. fn get_tsc_roots( + sys: &CliSys, graph: &ModuleGraph, check_js: bool, -) -> Vec<(ModuleSpecifier, MediaType)> { + npm_cache_state_hash: Option, + type_check_mode: TypeCheckMode, + ts_config: &TsConfig, +) -> TscRoots { fn maybe_get_check_entry( module: &deno_graph::Module, check_js: bool, + hasher: Option<&mut FastInsecureHasher>, ) -> Option<(ModuleSpecifier, MediaType)> { match module { - Module::Js(module) => match module.media_type { - MediaType::TypeScript - | MediaType::Tsx - | MediaType::Mts - | MediaType::Cts - | MediaType::Dts - | MediaType::Dmts - | MediaType::Dcts => { - Some((module.specifier.clone(), module.media_type)) - } - MediaType::JavaScript - | MediaType::Mjs - | MediaType::Cjs - | MediaType::Jsx => { - if check_js || has_ts_check(module.media_type, &module.source) { + Module::Js(module) => { + let result = match module.media_type { + MediaType::TypeScript + | MediaType::Tsx + | MediaType::Mts + | MediaType::Cts + | MediaType::Dts + | MediaType::Dmts + | MediaType::Dcts => { Some((module.specifier.clone(), module.media_type)) - } else { - None + } + MediaType::JavaScript + | MediaType::Mjs + | MediaType::Cjs + | MediaType::Jsx => { + if check_js || has_ts_check(module.media_type, &module.source) { + Some((module.specifier.clone(), module.media_type)) + } else { + None + } + } + MediaType::Json + | MediaType::Wasm + | MediaType::Css + | MediaType::SourceMap + | MediaType::Unknown => None, + }; + if result.is_some() { + if let Some(hasher) = hasher { + hasher.write_str(module.specifier.as_str()); + hasher.write_str( + // the fast check module will only be set when publishing + module + .fast_check_module() + .map(|s| s.source.as_ref()) + .unwrap_or(&module.source), + ); } } - MediaType::Json - | MediaType::Wasm - | MediaType::Css - | MediaType::SourceMap - | MediaType::Unknown => None, - }, - Module::Wasm(module) => Some((module.specifier.clone(), MediaType::Dmts)), - Module::External(_) - | Module::Node(_) - | Module::Npm(_) - | Module::Json(_) => None, + result + } + Module::Node(_) => { + // the @types/node package will be in the resolved + // snapshot so don't bother including it in the hash + None + } + Module::Npm(_) => { + // don't bother adding this specifier to the hash + // because what matters is the resolved npm snapshot, + // which is hashed below + None + } + Module::Json(module) => { + if let Some(hasher) = hasher { + hasher.write_str(module.specifier.as_str()); + hasher.write_str(&module.source); + } + None + } + Module::Wasm(module) => { + if let Some(hasher) = hasher { + hasher.write_str(module.specifier.as_str()); + hasher.write_str(&module.source_dts); + } + Some((module.specifier.clone(), MediaType::Dmts)) + } + Module::External(module) => { + if let Some(hasher) = hasher { + hasher.write_str(module.specifier.as_str()); + } + None + } } } - let mut result = Vec::with_capacity(graph.specifiers_count()); + let mut result = TscRoots { + roots: Vec::with_capacity(graph.specifiers_count()), + missing_diagnostics: Default::default(), + maybe_check_hash: None, + }; + let mut maybe_hasher = npm_cache_state_hash.map(|npm_cache_state_hash| { + let mut hasher = FastInsecureHasher::new_deno_versioned(); + hasher.write_hashable(npm_cache_state_hash); + hasher.write_u8(match type_check_mode { + TypeCheckMode::All => 0, + TypeCheckMode::Local => 1, + TypeCheckMode::None => 2, + }); + hasher.write_hashable(graph.has_node_specifier); + hasher.write(&ts_config.as_bytes()); + hasher + }); + if graph.has_node_specifier { // inject a specifier that will resolve node types - result.push(( + result.roots.push(( ModuleSpecifier::parse("asset:///node_types.d.ts").unwrap(), MediaType::Dts, )); @@ -464,65 +442,134 @@ fn get_tsc_roots( let mut pending = VecDeque::new(); // put in the global types first so that they're resolved before anything else - for import in graph.imports.values() { - for dep in import.dependencies.values() { - let specifier = dep.get_type().or_else(|| dep.get_code()); - if let Some(specifier) = &specifier { - let specifier = graph.resolve(specifier); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); - } - } + let get_import_specifiers = || { + graph + .imports + .values() + .flat_map(|i| i.dependencies.values()) + .filter_map(|dep| dep.get_type().or_else(|| dep.get_code())) + }; + for specifier in get_import_specifiers() { + let specifier = graph.resolve(specifier); + if seen.insert(specifier) { + pending.push_back((specifier, false)); } } // then the roots for root in &graph.roots { let specifier = graph.resolve(root); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); + if seen.insert(specifier) { + pending.push_back((specifier, false)); } } // now walk the graph that only includes the fast check dependencies - while let Some(specifier) = pending.pop_front() { - let Some(module) = graph.get(specifier) else { - continue; + while let Some((specifier, is_dynamic)) = pending.pop_front() { + let module = match graph.try_get(specifier) { + Ok(Some(module)) => module, + Ok(None) => continue, + Err(ModuleError::Missing(specifier, maybe_range)) => { + if !is_dynamic { + result + .missing_diagnostics + .push(tsc::Diagnostic::from_missing_error( + specifier, + maybe_range.as_ref(), + maybe_additional_sloppy_imports_message(sys, specifier), + )); + } + continue; + } + Err(ModuleError::LoadingErr( + specifier, + maybe_range, + ModuleLoadError::Loader(_), + )) => { + // these will be errors like attempting to load a directory + if !is_dynamic { + result + .missing_diagnostics + .push(tsc::Diagnostic::from_missing_error( + specifier, + maybe_range.as_ref(), + maybe_additional_sloppy_imports_message(sys, specifier), + )); + } + continue; + } + Err(_) => continue, }; - if let Some(entry) = maybe_get_check_entry(module, check_js) { - result.push(entry); + if is_dynamic && !seen.insert(specifier) { + continue; } - if let Some(module) = module.js() { - let deps = module.dependencies_prefer_fast_check(); + if let Some(entry) = + maybe_get_check_entry(module, check_js, maybe_hasher.as_mut()) + { + result.roots.push(entry); + } + + let mut maybe_module_dependencies = None; + let mut maybe_types_dependency = None; + if let Module::Js(module) = module { + maybe_module_dependencies = Some(module.dependencies_prefer_fast_check()); + maybe_types_dependency = module + .maybe_types_dependency + .as_ref() + .and_then(|d| d.dependency.ok()); + } else if let Module::Wasm(module) = module { + maybe_module_dependencies = Some(&module.dependencies); + } + + fn handle_specifier<'a>( + graph: &'a ModuleGraph, + seen: &mut HashSet<&'a ModuleSpecifier>, + pending: &mut VecDeque<(&'a ModuleSpecifier, bool)>, + specifier: &'a ModuleSpecifier, + is_dynamic: bool, + ) { + let specifier = graph.resolve(specifier); + if is_dynamic { + if !seen.contains(specifier) { + pending.push_back((specifier, true)); + } + } else if seen.insert(specifier) { + pending.push_back((specifier, false)); + } + } + + if let Some(deps) = maybe_module_dependencies { for dep in deps.values() { // walk both the code and type dependencies if let Some(specifier) = dep.get_code() { - let specifier = graph.resolve(specifier); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); - } + handle_specifier( + graph, + &mut seen, + &mut pending, + specifier, + dep.is_dynamic, + ); } if let Some(specifier) = dep.get_type() { - let specifier = graph.resolve(specifier); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); - } - } - } - - if let Some(dep) = module - .maybe_types_dependency - .as_ref() - .and_then(|d| d.dependency.ok()) - { - let specifier = graph.resolve(&dep.specifier); - if seen.insert(specifier.clone()) { - pending.push_back(specifier); + handle_specifier( + graph, + &mut seen, + &mut pending, + specifier, + dep.is_dynamic, + ); } } } + + if let Some(dep) = maybe_types_dependency { + handle_specifier(graph, &mut seen, &mut pending, &dep.specifier, false); + } } + result.maybe_check_hash = + maybe_hasher.map(|hasher| CacheDBHash::new(hasher.finish())); + result } diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index d532de265f..25813c3f9d 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -409,9 +409,20 @@ delete Object.prototype.__proto__; messageText = formatMessage(msgText, ri.code); } if (start !== undefined && length !== undefined && file) { - const startPos = file.getLineAndCharacterOfPosition(start); - const sourceLine = file.getFullText().split("\n")[startPos.line]; - const fileName = file.fileName; + let startPos = file.getLineAndCharacterOfPosition(start); + let sourceLine = file.getFullText().split("\n")[startPos.line]; + const originalFileName = file.fileName; + const fileName = ops.op_remap_specifier + ? (ops.op_remap_specifier(file.fileName) ?? file.fileName) + : file.fileName; + // Bit of a hack to detect when we have a .wasm file and want to hide + // the .d.ts text. This is not perfect, but will work in most scenarios + if ( + fileName.endsWith(".wasm") && originalFileName.endsWith(".wasm.d.mts") + ) { + startPos = { line: 0, character: 0 }; + sourceLine = undefined; + } return { start: startPos, end: file.getLineAndCharacterOfPosition(start + length), @@ -475,6 +486,9 @@ delete Object.prototype.__proto__; 2792, // TS2307: Cannot find module '{0}' or its corresponding type declarations. 2307, + // Relative import errors to add an extension + 2834, + 2835, // TS5009: Cannot find the common subdirectory path for the input files. 5009, // TS5055: Cannot write file @@ -1037,24 +1051,27 @@ delete Object.prototype.__proto__; configFileParsingDiagnostics, }); - const checkFiles = localOnly - ? rootNames - .filter((n) => !n.startsWith("http")) - .map((checkName) => { - const sourceFile = program.getSourceFile(checkName); - if (sourceFile == null) { - throw new Error("Could not find source file for: " + checkName); - } - return sourceFile; - }) - : undefined; + let checkFiles = undefined; + + if (localOnly) { + const checkFileNames = new Set(); + checkFiles = []; + + for (const checkName of rootNames) { + if (checkName.startsWith("http")) { + continue; + } + const sourceFile = program.getSourceFile(checkName); + if (sourceFile != null) { + checkFiles.push(sourceFile); + } + checkFileNames.add(checkName); + } - if (checkFiles != null) { // When calling program.getSemanticDiagnostics(...) with a source file, we // need to call this code first in order to get it to invalidate cached // diagnostics correctly. This is what program.getSemanticDiagnostics() // does internally when calling without any arguments. - const checkFileNames = new Set(checkFiles.map((f) => f.fileName)); while ( program.getSemanticDiagnosticsOfNextAffectedFile( undefined, diff --git a/cli/tsc/diagnostics.rs b/cli/tsc/diagnostics.rs index cda41525c2..ac93c8575d 100644 --- a/cli/tsc/diagnostics.rs +++ b/cli/tsc/diagnostics.rs @@ -110,6 +110,15 @@ pub struct Position { pub character: u64, } +impl Position { + pub fn from_deno_graph(deno_graph_position: deno_graph::Position) -> Self { + Self { + line: deno_graph_position.line as u64, + character: deno_graph_position.character as u64, + } + } +} + #[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Diagnostic { @@ -142,6 +151,38 @@ pub struct Diagnostic { } impl Diagnostic { + pub fn from_missing_error( + specifier: &ModuleSpecifier, + maybe_range: Option<&deno_graph::Range>, + additional_message: Option, + ) -> Self { + Self { + category: DiagnosticCategory::Error, + code: 2307, + start: maybe_range.map(|r| Position::from_deno_graph(r.range.start)), + end: maybe_range.map(|r| Position::from_deno_graph(r.range.end)), + original_source_start: None, // will be applied later + message_text: Some(format!( + "Cannot find module '{}'.{}{}", + specifier, + if additional_message.is_none() { + "" + } else { + " " + }, + additional_message.unwrap_or_default() + )), + message_chain: None, + source: None, + source_line: None, + file_name: maybe_range.map(|r| r.specifier.to_string()), + related_information: None, + reports_deprecated: None, + reports_unnecessary: None, + other: Default::default(), + } + } + /// If this diagnostic should be included when it comes from a remote module. pub fn include_when_remote(&self) -> bool { /// TS6133: value is declared but its value is never read (noUnusedParameters and noUnusedLocals) @@ -299,6 +340,14 @@ impl Diagnostics { }); } + pub fn push(&mut self, diagnostic: Diagnostic) { + self.0.push(diagnostic); + } + + pub fn extend(&mut self, diagnostic: Diagnostics) { + self.0.extend(diagnostic.0); + } + /// Return a set of diagnostics where only the values where the predicate /// returns `true` are included. pub fn filter

(self, predicate: P) -> Self diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 9f1dc3653a..3176c50d5c 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -129,6 +129,7 @@ fn get_asset_texts_from_new_runtime() -> Result, AnyError> { op_emit, op_is_node_file, op_load, + op_remap_specifier, op_resolve, op_respond, ] @@ -275,30 +276,6 @@ fn hash_url(specifier: &ModuleSpecifier, media_type: MediaType) -> String { ) } -/// If the provided URLs derivable tsc media type doesn't match the media type, -/// we will add an extension to the output. This is to avoid issues with -/// specifiers that don't have extensions, that tsc refuses to emit because they -/// think a `.js` version exists, when it doesn't. -fn maybe_remap_specifier( - specifier: &ModuleSpecifier, - media_type: MediaType, -) -> Option { - let path = if specifier.scheme() == "file" { - if let Ok(path) = specifier.to_file_path() { - path - } else { - PathBuf::from(specifier.path()) - } - } else { - PathBuf::from(specifier.path()) - }; - if path.extension().is_none() { - Some(format!("{}{}", specifier, media_type.as_ts_extension())) - } else { - None - } -} - #[derive(Debug, Clone, Default, Eq, PartialEq)] pub struct EmittedFile { pub data: String, @@ -316,7 +293,7 @@ pub fn into_specifier_and_media_type( (specifier, media_type) } None => ( - Url::parse("internal:///missing_dependency.d.ts").unwrap(), + Url::parse(MISSING_DEPENDENCY_SPECIFIER).unwrap(), MediaType::Dts, ), } @@ -422,6 +399,8 @@ struct State { maybe_tsbuildinfo: Option, maybe_response: Option, maybe_npm: Option, + // todo(dsherret): it looks like the remapped_specifiers and + // root_map could be combined... what is the point of the separation? remapped_specifiers: HashMap, root_map: HashMap, current_dir: PathBuf, @@ -463,6 +442,16 @@ impl State { current_dir, } } + + pub fn maybe_remapped_specifier( + &self, + specifier: &str, + ) -> Option<&ModuleSpecifier> { + self + .remapped_specifiers + .get(specifier) + .or_else(|| self.root_map.get(specifier)) + } } fn normalize_specifier( @@ -607,10 +596,7 @@ fn op_load_inner( maybe_source.map(Cow::Borrowed) } else { let specifier = if let Some(remapped_specifier) = - state.remapped_specifiers.get(load_specifier) - { - remapped_specifier - } else if let Some(remapped_specifier) = state.root_map.get(load_specifier) + state.maybe_remapped_specifier(load_specifier) { remapped_specifier } else { @@ -713,6 +699,18 @@ pub struct ResolveArgs { pub specifiers: Vec<(bool, String)>, } +#[op2] +#[string] +fn op_remap_specifier( + state: &mut OpState, + #[string] specifier: &str, +) -> Option { + let state = state.borrow::(); + state + .maybe_remapped_specifier(specifier) + .map(|url| url.to_string()) +} + #[op2] #[serde] fn op_resolve( @@ -732,11 +730,9 @@ fn op_resolve_inner( let mut resolved: Vec<(String, &'static str)> = Vec::with_capacity(args.specifiers.len()); let referrer = if let Some(remapped_specifier) = - state.remapped_specifiers.get(&args.base) + state.maybe_remapped_specifier(&args.base) { remapped_specifier.clone() - } else if let Some(remapped_base) = state.root_map.get(&args.base) { - remapped_base.clone() } else { normalize_specifier(&args.base, &state.current_dir).context( "Error converting a string module specifier for \"op_resolve\".", @@ -759,8 +755,12 @@ fn op_resolve_inner( } let resolved_dep = referrer_module - .and_then(|m| m.js()) - .and_then(|m| m.dependencies_prefer_fast_check().get(&specifier)) + .and_then(|m| match m { + Module::Js(m) => m.dependencies_prefer_fast_check().get(&specifier), + Module::Json(_) => None, + Module::Wasm(m) => m.dependencies.get(&specifier), + Module::Npm(_) | Module::Node(_) | Module::External(_) => None, + }) .and_then(|d| d.maybe_type.ok().or_else(|| d.maybe_code.ok())); let resolution_mode = if is_cjs { ResolutionMode::Require @@ -816,7 +816,7 @@ fn op_resolve_inner( } _ => { if let Some(specifier_str) = - maybe_remap_specifier(&specifier, media_type) + mapped_specifier_for_tsc(&specifier, media_type) { state .remapped_specifiers @@ -840,7 +840,7 @@ fn op_resolve_inner( MediaType::Dts.as_ts_extension(), ), }; - log::debug!("Resolved {} to {:?}", specifier, result); + log::debug!("Resolved {} from {} to {:?}", specifier, referrer, result); resolved.push(result); } @@ -1072,6 +1072,7 @@ pub fn exec(request: Request) -> Result { op_emit, op_is_node_file, op_load, + op_remap_specifier, op_resolve, op_respond, ], diff --git a/tests/integration/jsr_tests.rs b/tests/integration/jsr_tests.rs index 95a9fcb437..0902b7d0a2 100644 --- a/tests/integration/jsr_tests.rs +++ b/tests/integration/jsr_tests.rs @@ -66,12 +66,11 @@ fn fast_check_cache() { // ensure cache works let output = check_debug_cmd.run(); assert_contains!(output.combined_output(), "Already type checked."); - let building_fast_check_msg = "Building fast check graph"; - assert_not_contains!(output.combined_output(), building_fast_check_msg); // now validated type_check_cache_path.remove_file(); let output = check_debug_cmd.run(); + let building_fast_check_msg = "Building fast check graph"; assert_contains!(output.combined_output(), building_fast_check_msg); assert_contains!( output.combined_output(), diff --git a/tests/specs/check/css_import/__test__.jsonc b/tests/specs/check/css_import/__test__.jsonc index 629dcd3833..4e16560ec2 100644 --- a/tests/specs/check/css_import/__test__.jsonc +++ b/tests/specs/check/css_import/__test__.jsonc @@ -10,6 +10,10 @@ "args": "check not_exists.ts", "output": "not_exists.out", "exitCode": 1 + }, { + "args": "run --check not_exists.ts", + "output": "not_exists.out", + "exitCode": 1 }, { "args": "check exists_and_try_uses.ts", "output": "exists_and_try_uses.out", diff --git a/tests/specs/check/css_import/not_exists.out b/tests/specs/check/css_import/not_exists.out index 95fd14668e..1e9dce6b70 100644 --- a/tests/specs/check/css_import/not_exists.out +++ b/tests/specs/check/css_import/not_exists.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDLINE]/not_exists.css". +Check [WILDLINE]exists.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/not_exists.css'. at file:///[WILDLINE]/not_exists.ts:1:8 diff --git a/tests/specs/check/dts_importing_non_existent/check.out b/tests/specs/check/dts_importing_non_existent/check.out index 80ec9593b0..65e27bce83 100644 --- a/tests/specs/check/dts_importing_non_existent/check.out +++ b/tests/specs/check/dts_importing_non_existent/check.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDLINE]/test". +Check file:///[WILDLINE]/dts_importing_non_existent/index.js +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/test'. at file:///[WILDLINE]/index.d.ts:1:22 diff --git a/tests/specs/check/import_non_existent_in_remote/__test__.jsonc b/tests/specs/check/import_non_existent_in_remote/__test__.jsonc new file mode 100644 index 0000000000..39cd37ffc0 --- /dev/null +++ b/tests/specs/check/import_non_existent_in_remote/__test__.jsonc @@ -0,0 +1,14 @@ +{ + "tests": { + "not_all": { + "args": "check --allow-import import_remote.ts", + "output": "[WILDCARD]", + "exitCode": 0 + }, + "all": { + "args": "check --all --allow-import import_remote.ts", + "output": "check_all.out", + "exitCode": 1 + } + } +} diff --git a/tests/specs/check/import_non_existent_in_remote/check_all.out b/tests/specs/check/import_non_existent_in_remote/check_all.out new file mode 100644 index 0000000000..a3c3b1759c --- /dev/null +++ b/tests/specs/check/import_non_existent_in_remote/check_all.out @@ -0,0 +1,5 @@ +Download http://localhost:4545/check/import_non_existent.ts +Download http://localhost:4545/check/non-existent-module.ts +Check file:///[WILDLINE]/import_remote.ts +error: TS2307 [ERROR]: Cannot find module 'http://localhost:4545/check/non-existent-module.ts'. + at http://localhost:4545/check/import_non_existent.ts:1:22 diff --git a/tests/specs/check/import_non_existent_in_remote/import_remote.ts b/tests/specs/check/import_non_existent_in_remote/import_remote.ts new file mode 100644 index 0000000000..47c5c654b8 --- /dev/null +++ b/tests/specs/check/import_non_existent_in_remote/import_remote.ts @@ -0,0 +1,3 @@ +import { Other } from "http://localhost:4545/check/import_non_existent.ts"; + +console.log(Other); diff --git a/tests/specs/check/module_not_found/__test__.jsonc b/tests/specs/check/module_not_found/__test__.jsonc new file mode 100644 index 0000000000..5e7cfa2e59 --- /dev/null +++ b/tests/specs/check/module_not_found/__test__.jsonc @@ -0,0 +1,24 @@ +{ + "tests": { + "check": { + "args": "check --allow-import main.ts", + "output": "main.out", + "exitCode": 1 + }, + "run": { + "args": "run --check --allow-import main.ts", + "output": "main.out", + "exitCode": 1 + }, + "missing_local_root": { + "args": "check --allow-import non_existent.ts", + "output": "missing_local_root.out", + "exitCode": 1 + }, + "missing_remote_root": { + "args": "check --allow-import http://localhost:4545/missing_non_existent.ts", + "output": "missing_remote_root.out", + "exitCode": 1 + } + } +} diff --git a/tests/specs/check/module_not_found/main.out b/tests/specs/check/module_not_found/main.out new file mode 100644 index 0000000000..6c16183560 --- /dev/null +++ b/tests/specs/check/module_not_found/main.out @@ -0,0 +1,9 @@ +Download http://localhost:4545/remote.ts +Check file:///[WILDLINE]/module_not_found/main.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/other.js'. + at file:///[WILDLINE]/main.ts:1:22 + +TS2307 [ERROR]: Cannot find module 'http://localhost:4545/remote.ts'. + at file:///[WILDLINE]/main.ts:2:24 + +Found 2 errors. diff --git a/tests/specs/check/module_not_found/main.ts b/tests/specs/check/module_not_found/main.ts new file mode 100644 index 0000000000..cec9512569 --- /dev/null +++ b/tests/specs/check/module_not_found/main.ts @@ -0,0 +1,5 @@ +import { Test } from "./other.js"; +import { Remote } from "http://localhost:4545/remote.ts"; + +console.log(new Test()); +console.log(new Remote()); diff --git a/tests/specs/check/module_not_found/missing_local_root.out b/tests/specs/check/module_not_found/missing_local_root.out new file mode 100644 index 0000000000..34b150c9a3 --- /dev/null +++ b/tests/specs/check/module_not_found/missing_local_root.out @@ -0,0 +1,2 @@ +Check file:///[WILDLINE]/non_existent.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/non_existent.ts'. diff --git a/tests/specs/check/module_not_found/missing_remote_root.out b/tests/specs/check/module_not_found/missing_remote_root.out new file mode 100644 index 0000000000..e408938e41 --- /dev/null +++ b/tests/specs/check/module_not_found/missing_remote_root.out @@ -0,0 +1,3 @@ +Download http://localhost:4545/missing_non_existent.ts +Check http://localhost:4545/missing_non_existent.ts +error: TS2307 [ERROR]: Cannot find module 'http://localhost:4545/missing_non_existent.ts'. diff --git a/tests/specs/check/types_resolved_relative_config/main.out b/tests/specs/check/types_resolved_relative_config/main.out index 212e1224ca..5763d3298c 100644 --- a/tests/specs/check/types_resolved_relative_config/main.out +++ b/tests/specs/check/types_resolved_relative_config/main.out @@ -1,3 +1,4 @@ [# It should be resolving relative the config in sub_dir instead of the cwd] -error: Module not found "file:///[WILDLINE]/sub_dir/a.d.ts". +Check file:///[WILDLINE]/main.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/sub_dir/a.d.ts'. at file:///[WILDLINE]/sub_dir/deno.json:1:1 diff --git a/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out b/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out index 4eacbea655..8388e4751e 100644 --- a/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out +++ b/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out @@ -1,2 +1,3 @@ -error: [WILDCARD] Maybe specify path to 'index.ts' file in directory instead or run with --unstable-sloppy-imports - at file:///[WILDCARD]/mod.ts:1:20 +Check file:///[WILDLINE]/mod.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/b'. Maybe specify path to 'index.ts' file in directory instead or run with --unstable-sloppy-imports + at file:///[WILDLINE]/mod.ts:1:20 diff --git a/tests/specs/run/jsx_import_source/jsx_import_source_error.out b/tests/specs/run/jsx_import_source/jsx_import_source_error.out index 634a5b09ba..cb673c6bc9 100644 --- a/tests/specs/run/jsx_import_source/jsx_import_source_error.out +++ b/tests/specs/run/jsx_import_source/jsx_import_source_error.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDCARD]/nonexistent/jsx-runtime". - at file:///[WILDCARD]/jsx_import_source_no_pragma.tsx:1:1 +Check file:///[WILDLINE]/jsx_import_source_no_pragma.tsx +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDCARD]/nonexistent/jsx-runtime'. + at file:///[WILDLINE]/jsx_import_source_no_pragma.tsx:1:1 diff --git a/tests/specs/run/reference_types_error/reference_types_error.js.out b/tests/specs/run/reference_types_error/reference_types_error.js.out index 86055f3ac3..3f22354915 100644 --- a/tests/specs/run/reference_types_error/reference_types_error.js.out +++ b/tests/specs/run/reference_types_error/reference_types_error.js.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDCARD]/nonexistent.d.ts". - at file:///[WILDCARD]/reference_types_error.js:1:22 +Check file:///[WILDLINE]/reference_types_error.js +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/nonexistent.d.ts'. + at file:///[WILDLINE]/reference_types_error.js:1:22 diff --git a/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out b/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out index 86055f3ac3..3f22354915 100644 --- a/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out +++ b/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out @@ -1,2 +1,3 @@ -error: Module not found "file:///[WILDCARD]/nonexistent.d.ts". - at file:///[WILDCARD]/reference_types_error.js:1:22 +Check file:///[WILDLINE]/reference_types_error.js +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/nonexistent.d.ts'. + at file:///[WILDLINE]/reference_types_error.js:1:22 diff --git a/tests/specs/run/sloppy_imports/no_sloppy.out b/tests/specs/run/sloppy_imports/no_sloppy.out index d3a205e990..f28d9181ff 100644 --- a/tests/specs/run/sloppy_imports/no_sloppy.out +++ b/tests/specs/run/sloppy_imports/no_sloppy.out @@ -1,2 +1,26 @@ -error: Module not found "file:///[WILDCARD]/a.js". Maybe change the extension to '.ts' or run with --unstable-sloppy-imports +Check file:///[WILDLINE]/main.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/a.js'. Maybe change the extension to '.ts' or run with --unstable-sloppy-imports at file:///[WILDLINE]/main.ts:1:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/b'. Maybe add a '.js' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:2:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/c'. Maybe add a '.mts' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:3:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/d'. Maybe add a '.mjs' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:4:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/e'. Maybe add a '.tsx' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:5:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/e.js'. Maybe change the extension to '.tsx' or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:6:21 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/f'. Maybe add a '.jsx' extension or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:7:20 + +TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/dir'. Maybe specify path to 'index.tsx' file in directory instead or run with --unstable-sloppy-imports + at file:///[WILDLINE]/main.ts:8:20 + +Found 8 errors. diff --git a/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc b/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc index a27fcfa82b..0141f9828c 100644 --- a/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc +++ b/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc @@ -1,5 +1,14 @@ { - "args": "--allow-import main.js", - "output": "main.out", - "exitCode": 1 + "tests": { + "run": { + "args": "--allow-import main.js", + "output": "main.out", + "exitCode": 1 + }, + "check": { + "args": "check --all --allow-import main.js", + "output": "check.out", + "exitCode": 1 + } + } } diff --git a/tests/specs/run/wasm_module/import_file_not_found/check.out b/tests/specs/run/wasm_module/import_file_not_found/check.out new file mode 100644 index 0000000000..59c052297c --- /dev/null +++ b/tests/specs/run/wasm_module/import_file_not_found/check.out @@ -0,0 +1,4 @@ +Download http://localhost:4545/wasm/math_with_import.wasm +Check file:///[WILDLINE]/main.js +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/local_math.ts'. + at http://localhost:4545/wasm/math_with_import.wasm:1:87 diff --git a/tests/specs/run/wasm_module/import_file_not_found/main.js b/tests/specs/run/wasm_module/import_file_not_found/main.js index 9ad66df35b..b55405bd31 100644 --- a/tests/specs/run/wasm_module/import_file_not_found/main.js +++ b/tests/specs/run/wasm_module/import_file_not_found/main.js @@ -1,3 +1,4 @@ +// @ts-check import { add, subtract, diff --git a/tests/specs/run/wasm_module/import_file_not_found/main.out b/tests/specs/run/wasm_module/import_file_not_found/main.out index 54343673f1..ed021b9a21 100644 --- a/tests/specs/run/wasm_module/import_file_not_found/main.out +++ b/tests/specs/run/wasm_module/import_file_not_found/main.out @@ -1,3 +1,3 @@ Download http://localhost:4545/wasm/math_with_import.wasm error: Module not found "file:///[WILDLINE]/local_math.ts". - at http://localhost:4545/wasm/math_with_import.wasm:1:8 + at http://localhost:4545/wasm/math_with_import.wasm:1:87 diff --git a/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc b/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc index a27fcfa82b..0141f9828c 100644 --- a/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc +++ b/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc @@ -1,5 +1,14 @@ { - "args": "--allow-import main.js", - "output": "main.out", - "exitCode": 1 + "tests": { + "run": { + "args": "--allow-import main.js", + "output": "main.out", + "exitCode": 1 + }, + "check": { + "args": "check --all --allow-import main.js", + "output": "check.out", + "exitCode": 1 + } + } } diff --git a/tests/specs/run/wasm_module/import_named_export_not_found/check.out b/tests/specs/run/wasm_module/import_named_export_not_found/check.out new file mode 100644 index 0000000000..d7cc2ea0fb --- /dev/null +++ b/tests/specs/run/wasm_module/import_named_export_not_found/check.out @@ -0,0 +1,9 @@ +Download http://localhost:4545/wasm/math_with_import.wasm +Check file:///[WILDLINE]/main.js +error: TS2305 [ERROR]: Module '"file:///[WILDLINE]/local_math.ts"' has no exported member '"add"'. + at http://localhost:4545/wasm/math_with_import.wasm:1:1 + +TS2305 [ERROR]: Module '"file:///[WILDLINE]/local_math.ts"' has no exported member '"subtract"'. + at http://localhost:4545/wasm/math_with_import.wasm:1:1 + +Found 2 errors. diff --git a/tests/specs/run/wasm_module/import_named_export_not_found/main.js b/tests/specs/run/wasm_module/import_named_export_not_found/main.js index 9ad66df35b..b55405bd31 100644 --- a/tests/specs/run/wasm_module/import_named_export_not_found/main.js +++ b/tests/specs/run/wasm_module/import_named_export_not_found/main.js @@ -1,3 +1,4 @@ +// @ts-check import { add, subtract, diff --git a/tests/testdata/check/import_non_existent.ts b/tests/testdata/check/import_non_existent.ts new file mode 100644 index 0000000000..ae511bca8a --- /dev/null +++ b/tests/testdata/check/import_non_existent.ts @@ -0,0 +1,5 @@ +import { Test } from "./non-existent-module.ts"; + +console.log(Test); + +export class Other {} From 7cabd02c59c969a74d043e80928110d0e5c21aab Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Sat, 4 Jan 2025 10:04:14 +1100 Subject: [PATCH 063/107] fix(kv): improve backoff error message and inline documentation (#27537) Ref: #27536 --- cli/tsc/dts/lib.deno.unstable.d.ts | 3 ++- ext/kv/01_db.ts | 8 ++++++-- tests/unit/kv_test.ts | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index d207a92041..dbe4bace0c 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -293,7 +293,8 @@ declare namespace Deno { * executions. Each element in the array represents the number of milliseconds * to wait before retrying the execution. For example, `[1000, 5000, 10000]` * means that a failed execution will be retried at most 3 times, with 1 - * second, 5 seconds, and 10 seconds delay between each retry. + * second, 5 seconds, and 10 seconds delay between each retry. There is a + * limit of 5 retries and a maximum interval of 1 hour (3600000 milliseconds). * * @category Cloud * @experimental diff --git a/ext/kv/01_db.ts b/ext/kv/01_db.ts index 0575c2c414..37d4c58c11 100644 --- a/ext/kv/01_db.ts +++ b/ext/kv/01_db.ts @@ -77,7 +77,9 @@ const maxQueueBackoffInterval = 60 * 60 * 1000; function validateBackoffSchedule(backoffSchedule: number[]) { if (backoffSchedule.length > maxQueueBackoffIntervals) { - throw new TypeError("Invalid backoffSchedule"); + throw new TypeError( + `Invalid backoffSchedule, max ${maxQueueBackoffIntervals} intervals allowed`, + ); } for (let i = 0; i < backoffSchedule.length; ++i) { const interval = backoffSchedule[i]; @@ -85,7 +87,9 @@ function validateBackoffSchedule(backoffSchedule: number[]) { interval < 0 || interval > maxQueueBackoffInterval || NumberIsNaN(interval) ) { - throw new TypeError("Invalid backoffSchedule"); + throw new TypeError( + `Invalid backoffSchedule, interval at index ${i} is invalid`, + ); } } } diff --git a/tests/unit/kv_test.ts b/tests/unit/kv_test.ts index b47d3118c7..47e1305c94 100644 --- a/tests/unit/kv_test.ts +++ b/tests/unit/kv_test.ts @@ -1951,14 +1951,14 @@ dbTest("Invalid backoffSchedule", async (db) => { await db.enqueue("foo", { backoffSchedule: [1, 1, 1, 1, 1, 1] }); }, TypeError, - "Invalid backoffSchedule", + "Invalid backoffSchedule, max 5 intervals allowed", ); await assertRejects( async () => { await db.enqueue("foo", { backoffSchedule: [3600001] }); }, TypeError, - "Invalid backoffSchedule", + "Invalid backoffSchedule, interval at index 0 is invalid", ); }); From 9ad0d4c3db85ce9f8007dc088d164bcc7f544708 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Mon, 6 Jan 2025 16:12:21 +0900 Subject: [PATCH 064/107] fix(ext/http): improve error message when underlying resource of request body unavailable (#27463) The error message is currently `Bad Resource ID`. This commit changes it to `Cannot read request body as underlying resource unavailable` closes #27133 --- ext/http/00_serve.ts | 20 +++++++++++++++++- ext/web/06_streams.js | 8 +++++-- tests/unit/serve_test.ts | 45 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/ext/http/00_serve.ts b/ext/http/00_serve.ts index 6e5f25b473..0ef06d1902 100644 --- a/ext/http/00_serve.ts +++ b/ext/http/00_serve.ts @@ -370,7 +370,25 @@ class InnerRequest { return null; } this.#streamRid = op_http_read_request_body(this.#external); - this.#body = new InnerBody(readableStreamForRid(this.#streamRid, false)); + this.#body = new InnerBody( + readableStreamForRid( + this.#streamRid, + false, + undefined, + (controller, error) => { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + // TODO(kt3k): We would like to pass `error` as `cause` when BadResource supports it. + controller.error( + new error.constructor( + `Cannot read request body as underlying resource unavailable`, + ), + ); + } else { + controller.error(error); + } + }, + ), + ); return this.#body; } diff --git a/ext/web/06_streams.js b/ext/web/06_streams.js index 0b9ae538e2..950d46e829 100644 --- a/ext/web/06_streams.js +++ b/ext/web/06_streams.js @@ -908,7 +908,7 @@ const _original = Symbol("[[original]]"); * @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true. * @returns {ReadableStream} */ -function readableStreamForRid(rid, autoClose = true, Super) { +function readableStreamForRid(rid, autoClose = true, Super, onError) { const stream = new (Super ?? ReadableStream)(_brand); stream[_resourceBacking] = { rid, autoClose }; @@ -947,7 +947,11 @@ function readableStreamForRid(rid, autoClose = true, Super) { controller.byobRequest.respond(bytesRead); } } catch (e) { - controller.error(e); + if (onError) { + onError(controller, e); + } else { + controller.error(e); + } tryClose(); } }, diff --git a/tests/unit/serve_test.ts b/tests/unit/serve_test.ts index 88aecefbcb..09616b0151 100644 --- a/tests/unit/serve_test.ts +++ b/tests/unit/serve_test.ts @@ -2,7 +2,7 @@ // deno-lint-ignore-file no-console -import { assertMatch, assertRejects } from "@std/assert"; +import { assertIsError, assertMatch, assertRejects } from "@std/assert"; import { Buffer, BufReader, BufWriter, type Reader } from "@std/io"; import { TextProtoReader } from "../testdata/run/textproto.ts"; import { @@ -4378,3 +4378,46 @@ Deno.test( await server.finished; }, ); + +Deno.test({ + name: + "req.body.getReader().read() throws the error with reasonable error message", +}, async () => { + const { promise, resolve, reject } = Promise.withResolvers(); + const server = Deno.serve({ onListen, port: 0 }, async (req) => { + const reader = req.body!.getReader(); + + try { + while (true) { + const { done } = await reader.read(); + if (done) break; + } + } catch (e) { + // deno-lint-ignore no-explicit-any + resolve(e as any); + } + + reject(new Error("Should not reach here")); + server.shutdown(); + return new Response(); + }); + + async function onListen({ port }: { port: number }) { + const body = "a".repeat(1000); + const request = `POST / HTTP/1.1\r\n` + + `Host: 127.0.0.1:${port}\r\n` + + `Content-Length: 1000\r\n` + + "\r\n" + body; + + const connection = await Deno.connect({ hostname: "127.0.0.1", port }); + await connection.write(new TextEncoder().encode(request)); + connection.close(); + } + await server.finished; + const e = await promise; + assertIsError( + e, + Deno.errors.BadResource, + "Cannot read request body as underlying resource unavailable", + ); +}); From 4b35ba6b13c8fb33629707797962898a138e4140 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Mon, 6 Jan 2025 14:28:29 +0100 Subject: [PATCH 065/107] feat(unstable): replace SpanExporter with TracerProvider (#27473) --- cli/main.rs | 5 +- cli/mainrt.rs | 5 +- cli/tsc/dts/lib.deno.unstable.d.ts | 31 +- ext/fetch/26_fetch.js | 37 +- ext/http/00_serve.ts | 61 +- ext/telemetry/lib.rs | 1063 ++++++++++------- ext/telemetry/telemetry.ts | 921 ++++++-------- .../jsr/@deno/otel/0.0.2/src/index.ts | 31 +- tests/specs/cli/otel_basic/basic.out | 26 +- tests/specs/cli/otel_basic/context.ts | 4 +- tests/specs/cli/otel_basic/main.ts | 9 +- tests/specs/cli/otel_basic/metric.ts | 2 +- 12 files changed, 1142 insertions(+), 1053 deletions(-) diff --git a/cli/main.rs b/cli/main.rs index 2e55d9d286..7db471932d 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -447,8 +447,9 @@ fn resolve_flags_and_init( } }; - deno_telemetry::init(crate::args::otel_runtime_config())?; - util::logger::init(flags.log_level, Some(flags.otel_config())); + let otel_config = flags.otel_config(); + deno_telemetry::init(crate::args::otel_runtime_config(), &otel_config)?; + util::logger::init(flags.log_level, Some(otel_config)); // TODO(bartlomieju): remove in Deno v2.5 and hard error then. if flags.unstable_config.legacy_flag_enabled { diff --git a/cli/mainrt.rs b/cli/mainrt.rs index ce6fddaee9..1279554514 100644 --- a/cli/mainrt.rs +++ b/cli/mainrt.rs @@ -89,7 +89,10 @@ fn main() { let future = async move { match standalone { Ok(Some(data)) => { - deno_telemetry::init(crate::args::otel_runtime_config())?; + deno_telemetry::init( + crate::args::otel_runtime_config(), + &data.metadata.otel_config, + )?; util::logger::init( data.metadata.log_level, Some(data.metadata.otel_config.clone()), diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index dbe4bace0c..3de9845fc8 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -1301,20 +1301,43 @@ declare namespace Deno { */ export namespace telemetry { /** - * A SpanExporter compatible with OpenTelemetry.js - * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_sdk_trace_base.SpanExporter.html + * A TracerProvider compatible with OpenTelemetry.js + * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.TracerProvider.html + * + * This is a singleton object that implements the OpenTelemetry + * TracerProvider interface. + * * @category Telemetry * @experimental */ - export class SpanExporter {} + // deno-lint-ignore no-explicit-any + export const tracerProvider: any; /** * A ContextManager compatible with OpenTelemetry.js * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.ContextManager.html + * + * This is a singleton object that implements the OpenTelemetry + * ContextManager interface. + * * @category Telemetry * @experimental */ - export class ContextManager {} + // deno-lint-ignore no-explicit-any + export const contextManager: any; + + /** + * A MeterProvider compatible with OpenTelemetry.js + * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.MeterProvider.html + * + * This is a singleton object that implements the OpenTelemetry + * MeterProvider interface. + * + * @category Telemetry + * @experimental + */ + // deno-lint-ignore no-explicit-any + export const meterProvider: any; export {}; // only export exports } diff --git a/ext/fetch/26_fetch.js b/ext/fetch/26_fetch.js index f26e2e3fb9..7d5f5ea2f7 100644 --- a/ext/fetch/26_fetch.js +++ b/ext/fetch/26_fetch.js @@ -59,10 +59,9 @@ import { } from "ext:deno_fetch/23_response.js"; import * as abortSignal from "ext:deno_web/03_abort_signal.js"; import { - endSpan, + builtinTracer, enterSpan, - exitSpan, - Span, + restoreContext, TRACING_ENABLED, } from "ext:deno_telemetry/telemetry.ts"; import { @@ -320,10 +319,10 @@ function httpRedirectFetch(request, response, terminator) { // Drop confidential headers when redirecting to a less secure protocol // or to a different domain that is not a superdomain if ( - locationURL.protocol !== currentURL.protocol && - locationURL.protocol !== "https:" || - locationURL.host !== currentURL.host && - !isSubdomain(locationURL.host, currentURL.host) + (locationURL.protocol !== currentURL.protocol && + locationURL.protocol !== "https:") || + (locationURL.host !== currentURL.host && + !isSubdomain(locationURL.host, currentURL.host)) ) { for (let i = 0; i < request.headerList.length; i++) { if ( @@ -352,10 +351,11 @@ function httpRedirectFetch(request, response, terminator) { */ function fetch(input, init = { __proto__: null }) { let span; + let context; try { if (TRACING_ENABLED) { - span = new Span("fetch", { kind: 2 }); - enterSpan(span); + span = builtinTracer().startSpan("fetch", { kind: 2 }); + context = enterSpan(span); } // There is an async dispatch later that causes a stack trace disconnect. @@ -454,9 +454,7 @@ function fetch(input, init = { __proto__: null }) { await opPromise; return result; } finally { - if (span) { - endSpan(span); - } + span?.end(); } })(); } @@ -469,19 +467,17 @@ function fetch(input, init = { __proto__: null }) { // XXX: This should always be true, otherwise `opPromise` would be present. if (op_fetch_promise_is_settled(result)) { // It's already settled. - endSpan(span); + span?.end(); } else { // Not settled yet, we can return a new wrapper promise. return SafePromisePrototypeFinally(result, () => { - endSpan(span); + span?.end(); }); } } return result; } finally { - if (span) { - exitSpan(span); - } + if (context) restoreContext(context); } } @@ -508,8 +504,11 @@ function abortFetch(request, responseObject, error) { */ function isSubdomain(subdomain, domain) { const dot = subdomain.length - domain.length - 1; - return dot > 0 && subdomain[dot] === "." && - StringPrototypeEndsWith(subdomain, domain); + return ( + dot > 0 && + subdomain[dot] === "." && + StringPrototypeEndsWith(subdomain, domain) + ); } /** diff --git a/ext/http/00_serve.ts b/ext/http/00_serve.ts index 0ef06d1902..5ce0a4bf7f 100644 --- a/ext/http/00_serve.ts +++ b/ext/http/00_serve.ts @@ -43,10 +43,7 @@ const { Uint8Array, Promise, } = primordials; -const { - getAsyncContext, - setAsyncContext, -} = core; +const { getAsyncContext, setAsyncContext } = core; import { InnerBody } from "ext:deno_fetch/22_body.js"; import { Event } from "ext:deno_web/02_event.js"; @@ -90,9 +87,8 @@ import { import { hasTlsKeyPairOptions, listenTls } from "ext:deno_net/02_tls.js"; import { SymbolAsyncDispose } from "ext:deno_web/00_infra.js"; import { - endSpan, + builtinTracer, enterSpan, - Span, TRACING_ENABLED, } from "ext:deno_telemetry/telemetry.ts"; import { @@ -288,28 +284,28 @@ class InnerRequest { // * is valid for OPTIONS if (path === "*") { - return this.#urlValue = "*"; + return (this.#urlValue = "*"); } // If the path is empty, return the authority (valid for CONNECT) if (path == "") { - return this.#urlValue = this.#methodAndUri[1]; + return (this.#urlValue = this.#methodAndUri[1]); } // CONNECT requires an authority if (this.#methodAndUri[0] == "CONNECT") { - return this.#urlValue = this.#methodAndUri[1]; + return (this.#urlValue = this.#methodAndUri[1]); } const hostname = this.#methodAndUri[1]; if (hostname) { // Construct a URL from the scheme, the hostname, and the path - return this.#urlValue = this.#context.scheme + hostname + path; + return (this.#urlValue = this.#context.scheme + hostname + path); } // Construct a URL from the scheme, the fallback hostname, and the path - return this.#urlValue = this.#context.scheme + this.#context.fallbackHost + - path; + return (this.#urlValue = this.#context.scheme + this.#context.fallbackHost + + path); } get completed() { @@ -414,10 +410,7 @@ class InnerRequest { return; } - PromisePrototypeThen( - op_http_request_on_cancel(this.#external), - callback, - ); + PromisePrototypeThen(op_http_request_on_cancel(this.#external), callback); } } @@ -521,12 +514,7 @@ function fastSyncResponseOrStream( autoClose = true; } PromisePrototypeThen( - op_http_set_response_body_resource( - req, - rid, - autoClose, - status, - ), + op_http_set_response_body_resource(req, rid, autoClose, status), (success) => { innerRequest?.close(success); op_http_close_after_finish(req); @@ -556,10 +544,7 @@ function mapToCallback(context, callback, onError) { updateSpanFromRequest(span, request); } - response = await callback( - request, - new ServeHandlerInfo(innerRequest), - ); + response = await callback(request, new ServeHandlerInfo(innerRequest)); // Throwing Error if the handler return value is not a Response class if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) { @@ -636,12 +621,12 @@ function mapToCallback(context, callback, onError) { mapped = function (req, _span) { const oldCtx = getAsyncContext(); setAsyncContext(context.asyncContext); - const span = new Span("deno.serve", { kind: 1 }); + const span = builtinTracer().startSpan("deno.serve", { kind: 1 }); try { enterSpan(span); return SafePromisePrototypeFinally( origMapped(req, span), - () => endSpan(span), + () => span.end(), ); } finally { // equiv to exitSpan. @@ -688,7 +673,7 @@ function formatHostName(hostname: string): string { // because browsers in Windows don't resolve "0.0.0.0". // See the discussion in https://github.com/denoland/deno_std/issues/1165 if ( - (Deno.build.os === "windows") && + Deno.build.os === "windows" && (hostname == "0.0.0.0" || hostname == "::") ) { return "localhost"; @@ -730,11 +715,12 @@ function serve(arg1, arg2) { const wantsHttps = hasTlsKeyPairOptions(options); const wantsUnix = ObjectHasOwn(options, "path"); const signal = options.signal; - const onError = options.onError ?? function (error) { - // deno-lint-ignore no-console - console.error(error); - return internalServerError(); - }; + const onError = options.onError ?? + function (error) { + // deno-lint-ignore no-console + console.error(error); + return internalServerError(); + }; if (wantsUnix) { const listener = listen({ @@ -843,10 +829,7 @@ function serveHttpOn(context, addr, callback) { const promiseErrorHandler = (error) => { // Abnormal exit // deno-lint-ignore no-console - console.error( - "Terminating Deno.serve loop due to unexpected error", - error, - ); + console.error("Terminating Deno.serve loop due to unexpected error", error); context.close(); }; @@ -964,7 +947,7 @@ function registerDeclarativeServer(exports) { port: servePort, hostname: serveHost, [kLoadBalanced]: (serveIsMain && serveWorkerCount > 1) || - (serveWorkerCount !== null), + serveWorkerCount !== null, onListen: ({ port, hostname }) => { if (serveIsMain) { const nThreads = serveWorkerCount > 1 diff --git a/ext/telemetry/lib.rs b/ext/telemetry/lib.rs index eeff09ed88..261e93124d 100644 --- a/ext/telemetry/lib.rs +++ b/ext/telemetry/lib.rs @@ -1,5 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. +#![allow(clippy::too_many_arguments)] + use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; @@ -7,6 +9,7 @@ use std::env; use std::fmt::Debug; use std::pin::Pin; use std::rc::Rc; +use std::sync::atomic::AtomicU64; use std::sync::Arc; use std::sync::Mutex; use std::task::Context; @@ -17,6 +20,8 @@ use std::time::SystemTime; use deno_core::anyhow; use deno_core::anyhow::anyhow; +use deno_core::error::type_error; +use deno_core::error::AnyError; use deno_core::futures::channel::mpsc; use deno_core::futures::channel::mpsc::UnboundedSender; use deno_core::futures::future::BoxFuture; @@ -35,7 +40,7 @@ use opentelemetry::logs::LogRecord as LogRecordTrait; use opentelemetry::logs::Severity; use opentelemetry::metrics::AsyncInstrumentBuilder; use opentelemetry::metrics::InstrumentBuilder; -use opentelemetry::metrics::MeterProvider; +use opentelemetry::metrics::MeterProvider as _; use opentelemetry::otel_debug; use opentelemetry::otel_error; use opentelemetry::trace::SpanContext; @@ -44,6 +49,8 @@ use opentelemetry::trace::SpanKind; use opentelemetry::trace::Status as SpanStatus; use opentelemetry::trace::TraceFlags; use opentelemetry::trace::TraceId; +use opentelemetry::trace::TraceState; +use opentelemetry::InstrumentationScope; use opentelemetry::Key; use opentelemetry::KeyValue; use opentelemetry::StringValue; @@ -63,7 +70,11 @@ use opentelemetry_sdk::metrics::MetricResult; use opentelemetry_sdk::metrics::SdkMeterProvider; use opentelemetry_sdk::metrics::Temporality; use opentelemetry_sdk::trace::BatchSpanProcessor; -use opentelemetry_sdk::trace::SpanProcessor; +use opentelemetry_sdk::trace::IdGenerator; +use opentelemetry_sdk::trace::RandomIdGenerator; +use opentelemetry_sdk::trace::SpanEvents; +use opentelemetry_sdk::trace::SpanLinks; +use opentelemetry_sdk::trace::SpanProcessor as _; use opentelemetry_sdk::Resource; use opentelemetry_semantic_conventions::resource::PROCESS_RUNTIME_NAME; use opentelemetry_semantic_conventions::resource::PROCESS_RUNTIME_VERSION; @@ -79,23 +90,11 @@ deno_core::extension!( deno_telemetry, ops = [ op_otel_log, - op_otel_instrumentation_scope_create_and_enter, - op_otel_instrumentation_scope_enter, - op_otel_instrumentation_scope_enter_builtin, - op_otel_span_start, - op_otel_span_continue, - op_otel_span_attribute, + op_otel_log_foreign, + op_otel_span_attribute1, op_otel_span_attribute2, op_otel_span_attribute3, - op_otel_span_set_dropped, - op_otel_span_flush, - op_otel_metric_create_counter, - op_otel_metric_create_up_down_counter, - op_otel_metric_create_gauge, - op_otel_metric_create_histogram, - op_otel_metric_create_observable_counter, - op_otel_metric_create_observable_gauge, - op_otel_metric_create_observable_up_down_counter, + op_otel_span_update_name, op_otel_metric_attribute3, op_otel_metric_record0, op_otel_metric_record1, @@ -108,6 +107,7 @@ deno_core::extension!( op_otel_metric_wait_to_observe, op_otel_metric_observation_done, ], + objects = [OtelTracer, OtelMeter, OtelSpan], esm = ["telemetry.ts", "util.ts"], ); @@ -550,19 +550,20 @@ mod hyper_client { } } -struct Processors { - spans: BatchSpanProcessor, - logs: BatchLogProcessor, +struct OtelGlobals { + span_processor: BatchSpanProcessor, + log_processor: BatchLogProcessor, + id_generator: DenoIdGenerator, meter_provider: SdkMeterProvider, + builtin_instrumentation_scope: InstrumentationScope, } -static OTEL_PROCESSORS: OnceCell = OnceCell::new(); +static OTEL_GLOBALS: OnceCell = OnceCell::new(); -static BUILT_IN_INSTRUMENTATION_SCOPE: OnceCell< - opentelemetry::InstrumentationScope, -> = OnceCell::new(); - -pub fn init(rt_config: OtelRuntimeConfig) -> anyhow::Result<()> { +pub fn init( + rt_config: OtelRuntimeConfig, + config: &OtelConfig, +) -> anyhow::Result<()> { // Parse the `OTEL_EXPORTER_OTLP_PROTOCOL` variable. The opentelemetry_* // crates don't do this automatically. // TODO(piscisaureus): enable GRPC support. @@ -668,21 +669,26 @@ pub fn init(rt_config: OtelRuntimeConfig) -> anyhow::Result<()> { BatchLogProcessor::builder(log_exporter, OtelSharedRuntime).build(); log_processor.set_resource(&resource); - OTEL_PROCESSORS - .set(Processors { - spans: span_processor, - logs: log_processor, - meter_provider, - }) - .map_err(|_| anyhow!("failed to init otel"))?; - let builtin_instrumentation_scope = opentelemetry::InstrumentationScope::builder("deno") .with_version(rt_config.runtime_version.clone()) .build(); - BUILT_IN_INSTRUMENTATION_SCOPE - .set(builtin_instrumentation_scope) - .map_err(|_| anyhow!("failed to init otel"))?; + + let id_generator = if config.deterministic { + DenoIdGenerator::deterministic() + } else { + DenoIdGenerator::random() + }; + + OTEL_GLOBALS + .set(OtelGlobals { + log_processor, + span_processor, + id_generator, + meter_provider, + builtin_instrumentation_scope, + }) + .map_err(|_| anyhow!("failed to set otel globals"))?; Ok(()) } @@ -691,11 +697,12 @@ pub fn init(rt_config: OtelRuntimeConfig) -> anyhow::Result<()> { /// `process::exit()`, to ensure that all OpenTelemetry logs are properly /// flushed before the process terminates. pub fn flush() { - if let Some(Processors { - spans, - logs, + if let Some(OtelGlobals { + span_processor: spans, + log_processor: logs, meter_provider, - }) = OTEL_PROCESSORS.get() + .. + }) = OTEL_GLOBALS.get() { let _ = spans.force_flush(); let _ = logs.force_flush(); @@ -706,7 +713,12 @@ pub fn flush() { pub fn handle_log(record: &log::Record) { use log::Level; - let Some(Processors { logs, .. }) = OTEL_PROCESSORS.get() else { + let Some(OtelGlobals { + log_processor: logs, + builtin_instrumentation_scope, + .. + }) = OTEL_GLOBALS.get() + else { return; }; @@ -756,10 +768,58 @@ pub fn handle_log(record: &log::Record) { let _ = record.key_values().visit(&mut Visitor(&mut log_record)); - logs.emit( - &mut log_record, - BUILT_IN_INSTRUMENTATION_SCOPE.get().unwrap(), - ); + logs.emit(&mut log_record, builtin_instrumentation_scope); +} + +#[derive(Debug)] +enum DenoIdGenerator { + Random(RandomIdGenerator), + Deterministic { + next_trace_id: AtomicU64, + next_span_id: AtomicU64, + }, +} + +impl IdGenerator for DenoIdGenerator { + fn new_trace_id(&self) -> TraceId { + match self { + Self::Random(generator) => generator.new_trace_id(), + Self::Deterministic { next_trace_id, .. } => { + let id = + next_trace_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let bytes = id.to_be_bytes(); + let bytes = [ + 0, 0, 0, 0, 0, 0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + ]; + TraceId::from_bytes(bytes) + } + } + } + + fn new_span_id(&self) -> SpanId { + match self { + Self::Random(generator) => generator.new_span_id(), + Self::Deterministic { next_span_id, .. } => { + let id = + next_span_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + SpanId::from_bytes(id.to_be_bytes()) + } + } + } +} + +impl DenoIdGenerator { + fn random() -> Self { + Self::Random(RandomIdGenerator::default()) + } + + fn deterministic() -> Self { + Self::Deterministic { + next_trace_id: AtomicU64::new(1), + next_span_id: AtomicU64::new(1), + } + } } fn parse_trace_id( @@ -851,6 +911,9 @@ macro_rules! attr_raw { } else if let Ok(bigint) = $value.try_cast::() { let (i64_value, _lossless) = bigint.i64_value(); Some(Value::I64(i64_value)) + } else if let Ok(_array) = $value.try_cast::() { + // TODO: implement array attributes + None } else { None }; @@ -876,48 +939,66 @@ macro_rules! attr { }; } -#[derive(Debug, Clone)] -struct InstrumentationScope(opentelemetry::InstrumentationScope); - -impl deno_core::GarbageCollected for InstrumentationScope {} - -#[op2] -#[cppgc] -fn op_otel_instrumentation_scope_create_and_enter( - state: &mut OpState, - #[string] name: String, - #[string] version: Option, - #[string] schema_url: Option, -) -> InstrumentationScope { - let mut builder = opentelemetry::InstrumentationScope::builder(name); - if let Some(version) = version { - builder = builder.with_version(version); - } - if let Some(schema_url) = schema_url { - builder = builder.with_schema_url(schema_url); - } - let scope = InstrumentationScope(builder.build()); - state.put(scope.clone()); - scope -} - #[op2(fast)] -fn op_otel_instrumentation_scope_enter( - state: &mut OpState, - #[cppgc] scope: &InstrumentationScope, +fn op_otel_log<'s>( + scope: &mut v8::HandleScope<'s>, + message: v8::Local<'s, v8::Value>, + #[smi] level: i32, + span: v8::Local<'s, v8::Value>, ) { - state.put(scope.clone()); -} + let Some(OtelGlobals { + log_processor, + builtin_instrumentation_scope, + .. + }) = OTEL_GLOBALS.get() + else { + return; + }; -#[op2(fast)] -fn op_otel_instrumentation_scope_enter_builtin(state: &mut OpState) { - if let Some(scope) = BUILT_IN_INSTRUMENTATION_SCOPE.get() { - state.put(InstrumentationScope(scope.clone())); + // Convert the integer log level that ext/console uses to the corresponding + // OpenTelemetry log severity. + let severity = match level { + ..=0 => Severity::Debug, + 1 => Severity::Info, + 2 => Severity::Warn, + 3.. => Severity::Error, + }; + + let mut log_record = LogRecord::default(); + log_record.set_observed_timestamp(SystemTime::now()); + let Ok(message) = message.try_cast() else { + return; + }; + log_record.set_body(owned_string(scope, message).into()); + log_record.set_severity_number(severity); + log_record.set_severity_text(severity.name()); + if let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + { + let state = span.0.borrow(); + match &**state { + OtelSpanState::Recording(span) => { + log_record.set_trace_context( + span.span_context.trace_id(), + span.span_context.span_id(), + Some(span.span_context.trace_flags()), + ); + } + OtelSpanState::Done(span_context) => { + log_record.set_trace_context( + span_context.trace_id(), + span_context.span_id(), + Some(span_context.trace_flags()), + ); + } + } } + + log_processor.emit(&mut log_record, builtin_instrumentation_scope); } #[op2(fast)] -fn op_otel_log( +fn op_otel_log_foreign( scope: &mut v8::HandleScope<'_>, #[string] message: String, #[smi] level: i32, @@ -925,10 +1006,12 @@ fn op_otel_log( span_id: v8::Local<'_, v8::Value>, #[smi] trace_flags: u8, ) { - let Some(Processors { logs, .. }) = OTEL_PROCESSORS.get() else { - return; - }; - let Some(instrumentation_scope) = BUILT_IN_INSTRUMENTATION_SCOPE.get() else { + let Some(OtelGlobals { + log_processor, + builtin_instrumentation_scope, + .. + }) = OTEL_GLOBALS.get() + else { return; }; @@ -958,7 +1041,7 @@ fn op_otel_log( ); } - logs.emit(&mut log_record, instrumentation_scope); + log_processor.emit(&mut log_record, builtin_instrumentation_scope); } fn owned_string<'s>( @@ -974,136 +1057,328 @@ fn owned_string<'s>( } } -struct TemporarySpan(SpanData); +struct OtelTracer(InstrumentationScope); -#[allow(clippy::too_many_arguments)] -#[op2(fast)] -fn op_otel_span_start<'s>( - scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - trace_id: v8::Local<'s, v8::Value>, - span_id: v8::Local<'s, v8::Value>, - parent_span_id: v8::Local<'s, v8::Value>, - #[smi] span_kind: u8, - name: v8::Local<'s, v8::Value>, - start_time: f64, - end_time: f64, -) -> Result<(), anyhow::Error> { - if let Some(temporary_span) = state.try_take::() { - let Some(Processors { spans, .. }) = OTEL_PROCESSORS.get() else { - return Ok(()); - }; - spans.on_end(temporary_span.0); - }; +impl deno_core::GarbageCollected for OtelTracer {} - let Some(InstrumentationScope(instrumentation_scope)) = - state.try_borrow::() - else { - return Err(anyhow!("instrumentation scope not available")); - }; - - let trace_id = parse_trace_id(scope, trace_id); - if trace_id == TraceId::INVALID { - return Err(anyhow!("invalid trace_id")); +#[op2] +impl OtelTracer { + #[constructor] + #[cppgc] + fn new( + #[string] name: String, + #[string] version: Option, + #[string] schema_url: Option, + ) -> OtelTracer { + let mut builder = opentelemetry::InstrumentationScope::builder(name); + if let Some(version) = version { + builder = builder.with_version(version); + } + if let Some(schema_url) = schema_url { + builder = builder.with_schema_url(schema_url); + } + let scope = builder.build(); + OtelTracer(scope) } - let span_id = parse_span_id(scope, span_id); - if span_id == SpanId::INVALID { - return Err(anyhow!("invalid span_id")); + #[static_method] + #[cppgc] + fn builtin() -> OtelTracer { + let OtelGlobals { + builtin_instrumentation_scope, + .. + } = OTEL_GLOBALS.get().unwrap(); + OtelTracer(builtin_instrumentation_scope.clone()) } - let parent_span_id = parse_span_id(scope, parent_span_id); - - let name = owned_string(scope, name.try_cast()?); - - let temporary_span = TemporarySpan(SpanData { - span_context: SpanContext::new( - trace_id, - span_id, - TraceFlags::SAMPLED, - false, - Default::default(), - ), - parent_span_id, - span_kind: match span_kind { + #[cppgc] + fn start_span<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + #[cppgc] parent: Option<&OtelSpan>, + name: v8::Local<'s, v8::Value>, + #[smi] span_kind: u8, + start_time: Option, + #[smi] attribute_count: usize, + ) -> Result { + let OtelGlobals { id_generator, .. } = OTEL_GLOBALS.get().unwrap(); + let span_context; + let parent_span_id; + match parent { + Some(parent) => { + let parent = parent.0.borrow(); + let parent_span_context = match &**parent { + OtelSpanState::Recording(span) => &span.span_context, + OtelSpanState::Done(span_context) => span_context, + }; + span_context = SpanContext::new( + parent_span_context.trace_id(), + id_generator.new_span_id(), + TraceFlags::SAMPLED, + false, + parent_span_context.trace_state().clone(), + ); + parent_span_id = parent_span_context.span_id(); + } + None => { + span_context = SpanContext::new( + id_generator.new_trace_id(), + id_generator.new_span_id(), + TraceFlags::SAMPLED, + false, + TraceState::NONE, + ); + parent_span_id = SpanId::INVALID; + } + } + let name = owned_string(scope, name.try_cast()?); + let span_kind = match span_kind { 0 => SpanKind::Internal, 1 => SpanKind::Server, 2 => SpanKind::Client, 3 => SpanKind::Producer, 4 => SpanKind::Consumer, _ => return Err(anyhow!("invalid span kind")), - }, - name: Cow::Owned(name), - start_time: SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(start_time)) - .ok_or_else(|| anyhow!("invalid start time"))?, - end_time: SystemTime::UNIX_EPOCH - .checked_add(std::time::Duration::from_secs_f64(end_time)) - .ok_or_else(|| anyhow!("invalid start time"))?, - attributes: Vec::new(), - dropped_attributes_count: 0, - events: Default::default(), - links: Default::default(), - status: SpanStatus::Unset, - instrumentation_scope: instrumentation_scope.clone(), - }); - state.put(temporary_span); + }; + let start_time = start_time + .map(|start_time| { + SystemTime::UNIX_EPOCH + .checked_add(std::time::Duration::from_secs_f64(start_time)) + .ok_or_else(|| anyhow!("invalid start time")) + }) + .unwrap_or_else(|| Ok(SystemTime::now()))?; + let span_data = SpanData { + span_context, + parent_span_id, + span_kind, + name: Cow::Owned(name), + start_time, + end_time: SystemTime::UNIX_EPOCH, + attributes: Vec::with_capacity(attribute_count), + dropped_attributes_count: 0, + status: SpanStatus::Unset, + events: SpanEvents::default(), + links: SpanLinks::default(), + instrumentation_scope: self.0.clone(), + }; + Ok(OtelSpan(RefCell::new(Box::new(OtelSpanState::Recording( + span_data, + ))))) + } - Ok(()) + #[cppgc] + fn start_span_foreign<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + parent_trace_id: v8::Local<'s, v8::Value>, + parent_span_id: v8::Local<'s, v8::Value>, + name: v8::Local<'s, v8::Value>, + #[smi] span_kind: u8, + start_time: Option, + #[smi] attribute_count: usize, + ) -> Result { + let parent_trace_id = parse_trace_id(scope, parent_trace_id); + if parent_trace_id == TraceId::INVALID { + return Err(anyhow!("invalid trace id")); + }; + let parent_span_id = parse_span_id(scope, parent_span_id); + if parent_span_id == SpanId::INVALID { + return Err(anyhow!("invalid span id")); + }; + let OtelGlobals { id_generator, .. } = OTEL_GLOBALS.get().unwrap(); + let span_context = SpanContext::new( + parent_trace_id, + id_generator.new_span_id(), + TraceFlags::SAMPLED, + false, + TraceState::NONE, + ); + let name = owned_string(scope, name.try_cast()?); + let span_kind = match span_kind { + 0 => SpanKind::Internal, + 1 => SpanKind::Server, + 2 => SpanKind::Client, + 3 => SpanKind::Producer, + 4 => SpanKind::Consumer, + _ => return Err(anyhow!("invalid span kind")), + }; + let start_time = start_time + .map(|start_time| { + SystemTime::UNIX_EPOCH + .checked_add(std::time::Duration::from_secs_f64(start_time)) + .ok_or_else(|| anyhow!("invalid start time")) + }) + .unwrap_or_else(|| Ok(SystemTime::now()))?; + let span_data = SpanData { + span_context, + parent_span_id, + span_kind, + name: Cow::Owned(name), + start_time, + end_time: SystemTime::UNIX_EPOCH, + attributes: Vec::with_capacity(attribute_count), + dropped_attributes_count: 0, + status: SpanStatus::Unset, + events: SpanEvents::default(), + links: SpanLinks::default(), + instrumentation_scope: self.0.clone(), + }; + Ok(OtelSpan(RefCell::new(Box::new(OtelSpanState::Recording( + span_data, + ))))) + } } -#[op2(fast)] -fn op_otel_span_continue( - state: &mut OpState, - #[smi] status: u8, - #[string] error_description: Cow<'_, str>, -) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.status = match status { +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct JsSpanContext { + trace_id: Box, + span_id: Box, + trace_flags: u8, +} + +// boxed because of https://github.com/denoland/rusty_v8/issues/1676 +#[derive(Debug)] +struct OtelSpan(RefCell>); + +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +enum OtelSpanState { + Recording(SpanData), + Done(SpanContext), +} + +impl deno_core::GarbageCollected for OtelSpan {} + +#[op2] +impl OtelSpan { + #[constructor] + #[cppgc] + fn new() -> Result { + Err(type_error("OtelSpan can not be constructed.")) + } + + #[serde] + fn span_context(&self) -> JsSpanContext { + let state = self.0.borrow(); + let span_context = match &**state { + OtelSpanState::Recording(span) => &span.span_context, + OtelSpanState::Done(span_context) => span_context, + }; + JsSpanContext { + trace_id: format!("{:?}", span_context.trace_id()).into(), + span_id: format!("{:?}", span_context.span_id()).into(), + trace_flags: span_context.trace_flags().to_u8(), + } + } + + #[fast] + fn set_status<'s>( + &self, + #[smi] status: u8, + #[string] error_description: String, + ) -> Result<(), AnyError> { + let mut state = self.0.borrow_mut(); + let OtelSpanState::Recording(span) = &mut **state else { + return Ok(()); + }; + span.status = match status { 0 => SpanStatus::Unset, 1 => SpanStatus::Ok, 2 => SpanStatus::Error { - description: Cow::Owned(error_description.into_owned()), + description: Cow::Owned(error_description), }, - _ => return, + _ => return Err(type_error("invalid span status code")), }; + Ok(()) + } + + #[fast] + fn drop_event(&self) { + let mut state = self.0.borrow_mut(); + match &mut **state { + OtelSpanState::Recording(span) => { + span.events.dropped_count += 1; + } + OtelSpanState::Done(_) => {} + } + } + + #[fast] + fn drop_link(&self) { + let mut state = self.0.borrow_mut(); + match &mut **state { + OtelSpanState::Recording(span) => { + span.links.dropped_count += 1; + } + OtelSpanState::Done(_) => {} + } + } + + #[fast] + fn end(&self, end_time: f64) { + let end_time = if end_time.is_nan() { + SystemTime::now() + } else { + SystemTime::UNIX_EPOCH + .checked_add(Duration::from_secs_f64(end_time)) + .unwrap() + }; + + let mut state = self.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + let span_context = span.span_context.clone(); + if let OtelSpanState::Recording(mut span) = *std::mem::replace( + &mut *state, + Box::new(OtelSpanState::Done(span_context)), + ) { + span.end_time = end_time; + let Some(OtelGlobals { span_processor, .. }) = OTEL_GLOBALS.get() + else { + return; + }; + span_processor.on_end(span); + } + } } } #[op2(fast)] -fn op_otel_span_attribute<'s>( +fn op_otel_span_attribute1<'s>( scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, + span: v8::Local<'_, v8::Value>, key: v8::Local<'s, v8::Value>, value: v8::Local<'s, v8::Value>, ) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.attributes.reserve_exact( - (capacity as usize) - .saturating_sub(temporary_span.0.attributes.capacity()), - ); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key, value); + let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + else { + return; + }; + let mut state = span.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + attr!(scope, span.attributes => span.dropped_attributes_count, key, value); } } #[op2(fast)] fn op_otel_span_attribute2<'s>( scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, + span: v8::Local<'_, v8::Value>, key1: v8::Local<'s, v8::Value>, value1: v8::Local<'s, v8::Value>, key2: v8::Local<'s, v8::Value>, value2: v8::Local<'s, v8::Value>, ) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.attributes.reserve_exact( - (capacity as usize) - .saturating_sub(temporary_span.0.attributes.capacity()), - ); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2); + let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + else { + return; + }; + let mut state = span.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + attr!(scope, span.attributes => span.dropped_attributes_count, key1, value1); + attr!(scope, span.attributes => span.dropped_attributes_count, key2, value2); } } @@ -1111,8 +1386,7 @@ fn op_otel_span_attribute2<'s>( #[op2(fast)] fn op_otel_span_attribute3<'s>( scope: &mut v8::HandleScope<'s>, - state: &mut OpState, - #[smi] capacity: u32, + span: v8::Local<'_, v8::Value>, key1: v8::Local<'s, v8::Value>, value1: v8::Local<'s, v8::Value>, key2: v8::Local<'s, v8::Value>, @@ -1120,42 +1394,208 @@ fn op_otel_span_attribute3<'s>( key3: v8::Local<'s, v8::Value>, value3: v8::Local<'s, v8::Value>, ) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.attributes.reserve_exact( - (capacity as usize) - .saturating_sub(temporary_span.0.attributes.capacity()), - ); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2); - attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key3, value3); + let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + else { + return; + }; + let mut state = span.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + attr!(scope, span.attributes => span.dropped_attributes_count, key1, value1); + attr!(scope, span.attributes => span.dropped_attributes_count, key2, value2); + attr!(scope, span.attributes => span.dropped_attributes_count, key3, value3); } } #[op2(fast)] -fn op_otel_span_set_dropped( - state: &mut OpState, - #[smi] dropped_attributes_count: u32, - #[smi] dropped_links_count: u32, - #[smi] dropped_events_count: u32, +fn op_otel_span_update_name<'s>( + scope: &mut v8::HandleScope<'s>, + span: v8::Local<'s, v8::Value>, + name: v8::Local<'s, v8::Value>, ) { - if let Some(temporary_span) = state.try_borrow_mut::() { - temporary_span.0.dropped_attributes_count += dropped_attributes_count; - temporary_span.0.links.dropped_count += dropped_links_count; - temporary_span.0.events.dropped_count += dropped_events_count; + let Ok(name) = name.try_cast() else { + return; + }; + let name = owned_string(scope, name); + let Some(span) = + deno_core::_ops::try_unwrap_cppgc_object::(scope, span) + else { + return; + }; + let mut state = span.0.borrow_mut(); + if let OtelSpanState::Recording(span) = &mut **state { + span.name = Cow::Owned(name) } } -#[op2(fast)] -fn op_otel_span_flush(state: &mut OpState) { - let Some(temporary_span) = state.try_take::() else { - return; - }; +struct OtelMeter(opentelemetry::metrics::Meter); - let Some(Processors { spans, .. }) = OTEL_PROCESSORS.get() else { - return; - }; +impl deno_core::GarbageCollected for OtelMeter {} - spans.on_end(temporary_span.0); +#[op2] +impl OtelMeter { + #[constructor] + #[cppgc] + fn new( + #[string] name: String, + #[string] version: Option, + #[string] schema_url: Option, + ) -> OtelMeter { + let mut builder = opentelemetry::InstrumentationScope::builder(name); + if let Some(version) = version { + builder = builder.with_version(version); + } + if let Some(schema_url) = schema_url { + builder = builder.with_schema_url(schema_url); + } + let scope = builder.build(); + let meter = OTEL_GLOBALS + .get() + .unwrap() + .meter_provider + .meter_with_scope(scope); + OtelMeter(meter) + } + + #[cppgc] + fn create_counter<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_instrument( + |name| self.0.f64_counter(name), + |i| Instrument::Counter(i.build()), + scope, + name, + description, + unit, + ) + } + + #[cppgc] + fn create_up_down_counter<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_instrument( + |name| self.0.f64_up_down_counter(name), + |i| Instrument::UpDownCounter(i.build()), + scope, + name, + description, + unit, + ) + } + + #[cppgc] + fn create_gauge<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_instrument( + |name| self.0.f64_gauge(name), + |i| Instrument::Gauge(i.build()), + scope, + name, + description, + unit, + ) + } + + #[cppgc] + fn create_histogram<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + #[serde] boundaries: Option>, + ) -> Result { + let name = owned_string(scope, name.try_cast()?); + let mut builder = self.0.f64_histogram(name); + if !description.is_null_or_undefined() { + let description = owned_string(scope, description.try_cast()?); + builder = builder.with_description(description); + }; + if !unit.is_null_or_undefined() { + let unit = owned_string(scope, unit.try_cast()?); + builder = builder.with_unit(unit); + }; + if let Some(boundaries) = boundaries { + builder = builder.with_boundaries(boundaries); + } + + Ok(Instrument::Histogram(builder.build())) + } + + #[cppgc] + fn create_observable_counter<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_async_instrument( + |name| self.0.f64_observable_counter(name), + |i| { + i.build(); + }, + scope, + name, + description, + unit, + ) + } + + #[cppgc] + fn create_observable_up_down_counter<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_async_instrument( + |name| self.0.f64_observable_up_down_counter(name), + |i| { + i.build(); + }, + scope, + name, + description, + unit, + ) + } + + #[cppgc] + fn create_observable_gauge<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + name: v8::Local<'s, v8::Value>, + description: v8::Local<'s, v8::Value>, + unit: v8::Local<'s, v8::Value>, + ) -> Result { + create_async_instrument( + |name| self.0.f64_observable_gauge(name), + |i| { + i.build(); + }, + scope, + name, + description, + unit, + ) + } } enum Instrument { @@ -1168,32 +1608,16 @@ enum Instrument { impl GarbageCollected for Instrument {} -fn create_instrument<'a, T>( - cb: impl FnOnce( - &'_ opentelemetry::metrics::Meter, - String, - ) -> InstrumentBuilder<'_, T>, - cb2: impl FnOnce(InstrumentBuilder<'_, T>) -> Instrument, - state: &mut OpState, +fn create_instrument<'a, 'b, T>( + cb: impl FnOnce(String) -> InstrumentBuilder<'b, T>, + cb2: impl FnOnce(InstrumentBuilder<'b, T>) -> Instrument, scope: &mut v8::HandleScope<'a>, name: v8::Local<'a, v8::Value>, description: v8::Local<'a, v8::Value>, unit: v8::Local<'a, v8::Value>, ) -> Result { - let Some(InstrumentationScope(instrumentation_scope)) = - state.try_borrow::() - else { - return Err(anyhow!("instrumentation scope not available")); - }; - - let meter = OTEL_PROCESSORS - .get() - .unwrap() - .meter_provider - .meter_with_scope(instrumentation_scope.clone()); - let name = owned_string(scope, name.try_cast()?); - let mut builder = cb(&meter, name); + let mut builder = cb(name); if !description.is_null_or_undefined() { let description = owned_string(scope, description.try_cast()?); builder = builder.with_description(description); @@ -1206,131 +1630,16 @@ fn create_instrument<'a, T>( Ok(cb2(builder)) } -#[op2] -#[cppgc] -fn op_otel_metric_create_counter<'s>( - state: &mut OpState, - scope: &mut v8::HandleScope<'s>, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, -) -> Result { - create_instrument( - |meter, name| meter.f64_counter(name), - |i| Instrument::Counter(i.build()), - state, - scope, - name, - description, - unit, - ) -} - -#[op2] -#[cppgc] -fn op_otel_metric_create_up_down_counter<'s>( - state: &mut OpState, - scope: &mut v8::HandleScope<'s>, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, -) -> Result { - create_instrument( - |meter, name| meter.f64_up_down_counter(name), - |i| Instrument::UpDownCounter(i.build()), - state, - scope, - name, - description, - unit, - ) -} - -#[op2] -#[cppgc] -fn op_otel_metric_create_gauge<'s>( - state: &mut OpState, - scope: &mut v8::HandleScope<'s>, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, -) -> Result { - create_instrument( - |meter, name| meter.f64_gauge(name), - |i| Instrument::Gauge(i.build()), - state, - scope, - name, - description, - unit, - ) -} - -#[op2] -#[cppgc] -fn op_otel_metric_create_histogram<'s>( - state: &mut OpState, - scope: &mut v8::HandleScope<'s>, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, - #[serde] boundaries: Option>, -) -> Result { - let Some(InstrumentationScope(instrumentation_scope)) = - state.try_borrow::() - else { - return Err(anyhow!("instrumentation scope not available")); - }; - - let meter = OTEL_PROCESSORS - .get() - .unwrap() - .meter_provider - .meter_with_scope(instrumentation_scope.clone()); - - let name = owned_string(scope, name.try_cast()?); - let mut builder = meter.f64_histogram(name); - if !description.is_null_or_undefined() { - let description = owned_string(scope, description.try_cast()?); - builder = builder.with_description(description); - }; - if !unit.is_null_or_undefined() { - let unit = owned_string(scope, unit.try_cast()?); - builder = builder.with_unit(unit); - }; - if let Some(boundaries) = boundaries { - builder = builder.with_boundaries(boundaries); - } - - Ok(Instrument::Histogram(builder.build())) -} - -fn create_async_instrument<'a, T>( - cb: impl FnOnce( - &'_ opentelemetry::metrics::Meter, - String, - ) -> AsyncInstrumentBuilder<'_, T, f64>, - cb2: impl FnOnce(AsyncInstrumentBuilder<'_, T, f64>), - state: &mut OpState, +fn create_async_instrument<'a, 'b, T>( + cb: impl FnOnce(String) -> AsyncInstrumentBuilder<'b, T, f64>, + cb2: impl FnOnce(AsyncInstrumentBuilder<'b, T, f64>), scope: &mut v8::HandleScope<'a>, name: v8::Local<'a, v8::Value>, description: v8::Local<'a, v8::Value>, unit: v8::Local<'a, v8::Value>, ) -> Result { - let Some(InstrumentationScope(instrumentation_scope)) = - state.try_borrow::() - else { - return Err(anyhow!("instrumentation scope not available")); - }; - - let meter = OTEL_PROCESSORS - .get() - .unwrap() - .meter_provider - .meter_with_scope(instrumentation_scope.clone()); - let name = owned_string(scope, name.try_cast()?); - let mut builder = cb(&meter, name); + let mut builder = cb(name); if !description.is_null_or_undefined() { let description = owned_string(scope, description.try_cast()?); builder = builder.with_description(description); @@ -1356,72 +1665,6 @@ fn create_async_instrument<'a, T>( Ok(Instrument::Observable(data_share)) } -#[op2] -#[cppgc] -fn op_otel_metric_create_observable_counter<'s>( - state: &mut OpState, - scope: &mut v8::HandleScope<'s>, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, -) -> Result { - create_async_instrument( - |meter, name| meter.f64_observable_counter(name), - |i| { - i.build(); - }, - state, - scope, - name, - description, - unit, - ) -} - -#[op2] -#[cppgc] -fn op_otel_metric_create_observable_up_down_counter<'s>( - state: &mut OpState, - scope: &mut v8::HandleScope<'s>, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, -) -> Result { - create_async_instrument( - |meter, name| meter.f64_observable_up_down_counter(name), - |i| { - i.build(); - }, - state, - scope, - name, - description, - unit, - ) -} - -#[op2] -#[cppgc] -fn op_otel_metric_create_observable_gauge<'s>( - state: &mut OpState, - scope: &mut v8::HandleScope<'s>, - name: v8::Local<'s, v8::Value>, - description: v8::Local<'s, v8::Value>, - unit: v8::Local<'s, v8::Value>, -) -> Result { - create_async_instrument( - |meter, name| meter.f64_observable_gauge(name), - |i| { - i.build(); - }, - state, - scope, - name, - description, - unit, - ) -} - struct MetricAttributes { attributes: Vec, } diff --git a/ext/telemetry/telemetry.ts b/ext/telemetry/telemetry.ts index 31e052c28b..f4277c3722 100644 --- a/ext/telemetry/telemetry.ts +++ b/ext/telemetry/telemetry.ts @@ -2,19 +2,9 @@ import { core, primordials } from "ext:core/mod.js"; import { - op_crypto_get_random_values, - op_otel_instrumentation_scope_create_and_enter, - op_otel_instrumentation_scope_enter, - op_otel_instrumentation_scope_enter_builtin, op_otel_log, + op_otel_log_foreign, op_otel_metric_attribute3, - op_otel_metric_create_counter, - op_otel_metric_create_gauge, - op_otel_metric_create_histogram, - op_otel_metric_create_observable_counter, - op_otel_metric_create_observable_gauge, - op_otel_metric_create_observable_up_down_counter, - op_otel_metric_create_up_down_counter, op_otel_metric_observable_record0, op_otel_metric_observable_record1, op_otel_metric_observable_record2, @@ -25,45 +15,39 @@ import { op_otel_metric_record2, op_otel_metric_record3, op_otel_metric_wait_to_observe, - op_otel_span_attribute, + op_otel_span_attribute1, op_otel_span_attribute2, op_otel_span_attribute3, - op_otel_span_continue, - op_otel_span_flush, - op_otel_span_set_dropped, - op_otel_span_start, + op_otel_span_update_name, + OtelMeter, + OtelSpan, + OtelTracer, } from "ext:core/ops"; import { Console } from "ext:deno_console/01_console.js"; -import { performance } from "ext:deno_web/15_performance.js"; const { - Array, + ArrayIsArray, ArrayPrototypePush, + DatePrototype, + DatePrototypeGetTime, Error, - ObjectAssign, ObjectDefineProperty, ObjectEntries, + ObjectKeys, ObjectPrototypeIsPrototypeOf, ReflectApply, SafeIterator, SafeMap, SafePromiseAll, SafeSet, - SafeWeakMap, - SafeWeakRef, SafeWeakSet, - String, - StringPrototypePadStart, SymbolFor, - TypedArrayPrototypeSubarray, - Uint8Array, - WeakRefPrototypeDeref, + TypeError, } = primordials; const { AsyncVariable, setAsyncContext } = core; export let TRACING_ENABLED = false; export let METRICS_ENABLED = false; -let DETERMINISTIC = false; // Note: These start at 0 in the JS library, // but start at 1 when serialized with JSON. @@ -90,8 +74,6 @@ interface SpanContext { traceState?: TraceState; } -type HrTime = [number, number]; - enum SpanStatusCode { UNSET = 0, OK = 1, @@ -103,7 +85,7 @@ interface SpanStatus { message?: string; } -export type AttributeValue = +type AttributeValue = | string | number | boolean @@ -117,9 +99,14 @@ interface Attributes { type SpanAttributes = Attributes; +type TimeInput = [number, number] | number | Date; + interface SpanOptions { - attributes?: Attributes; kind?: SpanKind; + attributes?: Attributes; + links?: Link[]; + startTime?: TimeInput; + root?: boolean; } interface Link { @@ -128,13 +115,6 @@ interface Link { droppedAttributesCount?: number; } -interface TimedEvent { - time: HrTime; - name: string; - attributes?: SpanAttributes; - droppedAttributesCount?: number; -} - interface IArrayValue { values: IAnyValue[]; } @@ -157,482 +137,322 @@ interface IKeyValue { key: string; value: IAnyValue; } -interface IResource { - attributes: IKeyValue[]; - droppedAttributesCount: number; -} - -interface InstrumentationLibrary { - readonly name: string; - readonly version?: string; - readonly schemaUrl?: string; -} - -interface ReadableSpan { - readonly name: string; - readonly kind: SpanKind; - readonly spanContext: () => SpanContext; - readonly parentSpanId?: string; - readonly startTime: HrTime; - readonly endTime: HrTime; - readonly status: SpanStatus; - readonly attributes: SpanAttributes; - readonly links: Link[]; - readonly events: TimedEvent[]; - readonly duration: HrTime; - readonly ended: boolean; - readonly resource: IResource; - readonly instrumentationLibrary: InstrumentationLibrary; - readonly droppedAttributesCount: number; - readonly droppedEventsCount: number; - readonly droppedLinksCount: number; -} - -enum ExportResultCode { - SUCCESS = 0, - FAILED = 1, -} - -interface ExportResult { - code: ExportResultCode; - error?: Error; -} function hrToSecs(hr: [number, number]): number { - return ((hr[0] * 1e3 + hr[1] / 1e6) / 1000); + return (hr[0] * 1e3 + hr[1] / 1e6) / 1000; } -const TRACE_FLAG_SAMPLED = 1 << 0; +export function enterSpan(span: Span): Context | undefined { + if (!span.isRecording()) return undefined; + const context = (CURRENT.get() || ROOT_CONTEXT).setValue(SPAN_KEY, span); + return CURRENT.enter(context); +} -const instrumentationScopes = new SafeWeakMap< - InstrumentationLibrary, - { __key: "instrumentation-library" } ->(); -let activeInstrumentationLibrary: WeakRef | null = null; +export function restoreContext(context: Context): void { + setAsyncContext(context); +} -function activateInstrumentationLibrary( - instrumentationLibrary: InstrumentationLibrary, -) { - if ( - !activeInstrumentationLibrary || - WeakRefPrototypeDeref(activeInstrumentationLibrary) !== - instrumentationLibrary +function isDate(value: unknown): value is Date { + return ObjectPrototypeIsPrototypeOf(value, DatePrototype); +} + +interface OtelTracer { + __key: "tracer"; + + // deno-lint-ignore no-misused-new + new (name: string, version?: string, schemaUrl?: string): OtelTracer; + + startSpan( + parent: OtelSpan | undefined, + name: string, + spanKind: SpanKind, + startTime: number | undefined, + attributeCount: number, + ): OtelSpan; + + startSpanForeign( + parentTraceId: string, + parentSpanId: string, + name: string, + spanKind: SpanKind, + startTime: number | undefined, + attributeCount: number, + ): OtelSpan; +} + +interface OtelSpan { + __key: "span"; + + spanContext(): SpanContext; + setStatus(status: SpanStatusCode, errorDescription: string): void; + dropEvent(): void; + dropLink(): void; + end(endTime: number): void; +} + +interface TracerOptions { + schemaUrl?: string; +} + +class TracerProvider { + constructor() { + throw new TypeError("TracerProvider can not be constructed"); + } + + static getTracer( + name: string, + version?: string, + options?: TracerOptions, + ): Tracer { + const tracer = new OtelTracer(name, version, options?.schemaUrl); + return new Tracer(tracer); + } +} + +class Tracer { + #tracer: OtelTracer; + + constructor(tracer: OtelTracer) { + this.#tracer = tracer; + } + + startActiveSpan unknown>( + name: string, + fn: F, + ): ReturnType; + startActiveSpan unknown>( + name: string, + options: SpanOptions, + fn: F, + ): ReturnType; + startActiveSpan unknown>( + name: string, + options: SpanOptions, + context: Context, + fn: F, + ): ReturnType; + startActiveSpan unknown>( + name: string, + optionsOrFn: SpanOptions | F, + fnOrContext?: F | Context, + maybeFn?: F, ) { - activeInstrumentationLibrary = new SafeWeakRef(instrumentationLibrary); - if (instrumentationLibrary === BUILTIN_INSTRUMENTATION_LIBRARY) { - op_otel_instrumentation_scope_enter_builtin(); + let options; + let context; + let fn; + if (typeof optionsOrFn === "function") { + options = undefined; + fn = optionsOrFn; + } else if (typeof fnOrContext === "function") { + options = optionsOrFn; + fn = fnOrContext; + } else if (typeof maybeFn === "function") { + options = optionsOrFn; + context = fnOrContext; + fn = maybeFn; } else { - let instrumentationScope = instrumentationScopes - .get(instrumentationLibrary); - - if (instrumentationScope === undefined) { - instrumentationScope = op_otel_instrumentation_scope_create_and_enter( - instrumentationLibrary.name, - instrumentationLibrary.version, - instrumentationLibrary.schemaUrl, - ) as { __key: "instrumentation-library" }; - instrumentationScopes.set( - instrumentationLibrary, - instrumentationScope, - ); - } else { - op_otel_instrumentation_scope_enter( - instrumentationScope, - ); - } + throw new Error("startActiveSpan requires a function argument"); } - } -} - -function submitSpan( - spanId: string | Uint8Array, - traceId: string | Uint8Array, - traceFlags: number, - parentSpanId: string | Uint8Array | null, - span: Omit< - ReadableSpan, - | "spanContext" - | "startTime" - | "endTime" - | "parentSpanId" - | "duration" - | "ended" - | "resource" - >, - startTime: number, - endTime: number, -) { - if (!TRACING_ENABLED) return; - if (!(traceFlags & TRACE_FLAG_SAMPLED)) return; - - // TODO(@lucacasonato): `resource` is ignored for now, should we implement it? - - activateInstrumentationLibrary(span.instrumentationLibrary); - - op_otel_span_start( - traceId, - spanId, - parentSpanId, - span.kind, - span.name, - startTime, - endTime, - ); - - const status = span.status; - if (status !== null && status.code !== 0) { - op_otel_span_continue(status.code, status.message ?? ""); - } - - const attributeKvs = ObjectEntries(span.attributes); - let i = 0; - while (i < attributeKvs.length) { - if (i + 2 < attributeKvs.length) { - op_otel_span_attribute3( - attributeKvs.length, - attributeKvs[i][0], - attributeKvs[i][1], - attributeKvs[i + 1][0], - attributeKvs[i + 1][1], - attributeKvs[i + 2][0], - attributeKvs[i + 2][1], - ); - i += 3; - } else if (i + 1 < attributeKvs.length) { - op_otel_span_attribute2( - attributeKvs.length, - attributeKvs[i][0], - attributeKvs[i][1], - attributeKvs[i + 1][0], - attributeKvs[i + 1][1], - ); - i += 2; + if (options?.root) { + context = undefined; } else { - op_otel_span_attribute( - attributeKvs.length, - attributeKvs[i][0], - attributeKvs[i][1], + context = context ?? CURRENT.get(); + } + const span = this.startSpan(name, options, context); + const ctx = CURRENT.enter(context.setValue(SPAN_KEY, span)); + try { + return ReflectApply(fn, undefined, [span]); + } finally { + setAsyncContext(ctx); + } + } + + startSpan(name: string, options?: SpanOptions, context?: Context): Span { + if (options?.root) { + context = undefined; + } else { + context = context ?? CURRENT.get(); + } + + let startTime = options?.startTime; + if (startTime && ArrayIsArray(startTime)) { + startTime = hrToSecs(startTime); + } else if (startTime && isDate(startTime)) { + startTime = DatePrototypeGetTime(startTime); + } + + const parentSpan = context?.getValue(SPAN_KEY) as + | Span + | { spanContext(): SpanContext } + | undefined; + const attributesCount = options?.attributes + ? ObjectKeys(options.attributes).length + : 0; + const parentOtelSpan: OtelSpan | null | undefined = parentSpan !== undefined + ? getOtelSpan(parentSpan) ?? undefined + : undefined; + let otelSpan: OtelSpan; + if (parentOtelSpan || !parentSpan) { + otelSpan = this.#tracer.startSpan( + parentOtelSpan, + name, + options?.kind ?? 0, + startTime, + attributesCount, + ); + } else { + const spanContext = parentSpan.spanContext(); + otelSpan = this.#tracer.startSpanForeign( + spanContext.traceId, + spanContext.spanId, + name, + options?.kind ?? 0, + startTime, + attributesCount, ); - i += 1; } + const span = new Span(otelSpan); + if (options?.links) span.addLinks(options?.links); + if (options?.attributes) span.setAttributes(options?.attributes); + return span; } - - // TODO(@lucacasonato): implement links - // TODO(@lucacasonato): implement events - - const droppedAttributesCount = span.droppedAttributesCount; - const droppedLinksCount = span.droppedLinksCount + span.links.length; - const droppedEventsCount = span.droppedEventsCount + span.events.length; - if ( - droppedAttributesCount > 0 || droppedLinksCount > 0 || - droppedEventsCount > 0 - ) { - op_otel_span_set_dropped( - droppedAttributesCount, - droppedLinksCount, - droppedEventsCount, - ); - } - - op_otel_span_flush(); -} - -const now = () => (performance.timeOrigin + performance.now()) / 1000; - -const SPAN_ID_BYTES = 8; -const TRACE_ID_BYTES = 16; - -const INVALID_TRACE_ID = new Uint8Array(TRACE_ID_BYTES); -const INVALID_SPAN_ID = new Uint8Array(SPAN_ID_BYTES); - -const NO_ASYNC_CONTEXT = {}; - -let otelLog: (message: string, level: number) => void; - -const hexSliceLookupTable = (function () { - const alphabet = "0123456789abcdef"; - const table = new Array(256); - for (let i = 0; i < 16; ++i) { - const i16 = i * 16; - for (let j = 0; j < 16; ++j) { - table[i16 + j] = alphabet[i] + alphabet[j]; - } - } - return table; -})(); - -function bytesToHex(bytes: Uint8Array): string { - let out = ""; - for (let i = 0; i < bytes.length; i += 1) { - out += hexSliceLookupTable[bytes[i]]; - } - return out; } const SPAN_KEY = SymbolFor("OpenTelemetry Context Key SPAN"); -const BUILTIN_INSTRUMENTATION_LIBRARY: InstrumentationLibrary = {} as never; +let getOtelSpan: (span: object) => OtelSpan | null | undefined; -let COUNTER = 1; - -export let enterSpan: (span: Span) => void; -export let exitSpan: (span: Span) => void; -export let endSpan: (span: Span) => void; - -export class Span { - #traceId: string | Uint8Array; - #spanId: string | Uint8Array; - #traceFlags = TRACE_FLAG_SAMPLED; - - #spanContext: SpanContext | null = null; - - #parentSpanId: string | Uint8Array | null = null; - #parentSpanIdString: string | null = null; - - #recording = TRACING_ENABLED; - - #kind: number = SpanKind.INTERNAL; - #name: string; - #startTime: number; - #status: { code: number; message?: string } | null = null; - #attributes: Attributes = { __proto__: null } as never; - - #droppedEventsCount = 0; - #droppedLinksCount = 0; - - #asyncContext = NO_ASYNC_CONTEXT; +class Span { + #otelSpan: OtelSpan | null; + #spanContext: SpanContext | undefined; static { - otelLog = function otelLog(message, level) { - let traceId = null; - let spanId = null; - let traceFlags = 0; - const span = CURRENT.get()?.getValue(SPAN_KEY); - if (span) { - // The lint is wrong, we can not use anything but `in` here because this - // is a private field. - // deno-lint-ignore prefer-primordials - if (#traceId in span) { - traceId = span.#traceId; - spanId = span.#spanId; - traceFlags = span.#traceFlags; - } else { - const context = span.spanContext(); - traceId = context.traceId; - spanId = context.spanId; - traceFlags = context.traceFlags; - } - } - return op_otel_log(message, level, traceId, spanId, traceFlags); - }; - - enterSpan = (span: Span) => { - if (!span.#recording) return; - const context = (CURRENT.get() || ROOT_CONTEXT).setValue(SPAN_KEY, span); - span.#asyncContext = CURRENT.enter(context); - }; - - exitSpan = (span: Span) => { - if (!span.#recording) return; - if (span.#asyncContext === NO_ASYNC_CONTEXT) return; - setAsyncContext(span.#asyncContext); - span.#asyncContext = NO_ASYNC_CONTEXT; - }; - - endSpan = (span: Span) => { - const endTime = now(); - submitSpan( - span.#spanId, - span.#traceId, - span.#traceFlags, - span.#parentSpanId, - { - name: span.#name, - kind: span.#kind, - status: span.#status ?? { code: 0 }, - attributes: span.#attributes, - events: [], - links: [], - droppedAttributesCount: 0, - droppedEventsCount: span.#droppedEventsCount, - droppedLinksCount: span.#droppedLinksCount, - instrumentationLibrary: BUILTIN_INSTRUMENTATION_LIBRARY, - }, - span.#startTime, - endTime, - ); - }; + // deno-lint-ignore prefer-primordials + getOtelSpan = (span) => (#otelSpan in span ? span.#otelSpan : undefined); } - constructor( - name: string, - options?: SpanOptions, - ) { - if (!this.isRecording) { - this.#name = ""; - this.#startTime = 0; - this.#traceId = INVALID_TRACE_ID; - this.#spanId = INVALID_SPAN_ID; - this.#traceFlags = 0; - return; - } - - this.#name = name; - this.#startTime = now(); - this.#attributes = options?.attributes ?? { __proto__: null } as never; - this.#kind = options?.kind ?? SpanKind.INTERNAL; - - const currentSpan: Span | { - spanContext(): { traceId: string; spanId: string }; - } = CURRENT.get()?.getValue(SPAN_KEY); - if (currentSpan) { - if (DETERMINISTIC) { - this.#spanId = StringPrototypePadStart(String(COUNTER++), 16, "0"); - } else { - this.#spanId = new Uint8Array(SPAN_ID_BYTES); - op_crypto_get_random_values(this.#spanId); - } - // deno-lint-ignore prefer-primordials - if (#traceId in currentSpan) { - this.#traceId = currentSpan.#traceId; - this.#parentSpanId = currentSpan.#spanId; - } else { - const context = currentSpan.spanContext(); - this.#traceId = context.traceId; - this.#parentSpanId = context.spanId; - } - } else { - if (DETERMINISTIC) { - this.#traceId = StringPrototypePadStart(String(COUNTER++), 32, "0"); - this.#spanId = StringPrototypePadStart(String(COUNTER++), 16, "0"); - } else { - const buffer = new Uint8Array(TRACE_ID_BYTES + SPAN_ID_BYTES); - op_crypto_get_random_values(buffer); - this.#traceId = TypedArrayPrototypeSubarray(buffer, 0, TRACE_ID_BYTES); - this.#spanId = TypedArrayPrototypeSubarray(buffer, TRACE_ID_BYTES); - } - } + constructor(otelSpan: OtelSpan | null) { + this.#otelSpan = otelSpan; } spanContext() { if (!this.#spanContext) { - this.#spanContext = { - traceId: typeof this.#traceId === "string" - ? this.#traceId - : bytesToHex(this.#traceId), - spanId: typeof this.#spanId === "string" - ? this.#spanId - : bytesToHex(this.#spanId), - traceFlags: this.#traceFlags, - }; + if (this.#otelSpan) { + this.#spanContext = this.#otelSpan.spanContext(); + } else { + this.#spanContext = { + traceId: "00000000000000000000000000000000", + spanId: "0000000000000000", + traceFlags: 0, + }; + } } return this.#spanContext; } - get parentSpanId() { - if (!this.#parentSpanIdString && this.#parentSpanId) { - if (typeof this.#parentSpanId === "string") { - this.#parentSpanIdString = this.#parentSpanId; - } else { - this.#parentSpanIdString = bytesToHex(this.#parentSpanId); - } - } - return this.#parentSpanIdString; - } - - setAttribute(name: string, value: AttributeValue) { - if (this.#recording) this.#attributes[name] = value; + addEvent( + _name: string, + _attributesOrStartTime?: Attributes | TimeInput, + _startTime?: TimeInput, + ): Span { + this.#otelSpan?.dropEvent(); return this; } - setAttributes(attributes: Attributes) { - if (this.#recording) ObjectAssign(this.#attributes, attributes); + addLink(_link: Link): Span { + this.#otelSpan?.dropLink(); return this; } - setStatus(status: { code: number; message?: string }) { - if (this.#recording) { - if (status.code === 0) { - this.#status = null; - } else if (status.code > 2) { - throw new Error("Invalid status code"); - } else { - this.#status = status; - } + addLinks(links: Link[]): Span { + for (let i = 0; i < links.length; i++) { + this.#otelSpan?.dropLink(); } return this; } - updateName(name: string) { - if (this.#recording) this.#name = name; + end(endTime?: TimeInput): void { + if (endTime && ArrayIsArray(endTime)) { + endTime = hrToSecs(endTime); + } else if (endTime && isDate(endTime)) { + endTime = DatePrototypeGetTime(endTime); + } + this.#otelSpan?.end(endTime || NaN); + } + + isRecording(): boolean { + return this.#otelSpan !== undefined; + } + + // deno-lint-ignore no-explicit-any + recordException(_exception: any, _time?: TimeInput): Span { + this.#otelSpan?.dropEvent(); return this; } - addEvent(_name: never) { - // TODO(@lucacasonato): implement events - if (this.#recording) this.#droppedEventsCount += 1; + setAttribute(key: string, value: AttributeValue): Span { + if (!this.#otelSpan) return this; + op_otel_span_attribute1(this.#otelSpan, key, value); return this; } - addLink(_link: never) { - // TODO(@lucacasonato): implement links - if (this.#recording) this.#droppedLinksCount += 1; - return this; - } - - addLinks(links: never[]) { - // TODO(@lucacasonato): implement links - if (this.#recording) this.#droppedLinksCount += links.length; - return this; - } - - isRecording() { - return this.#recording; - } -} - -// Exporter compatible with opentelemetry js library -class SpanExporter { - export( - spans: ReadableSpan[], - resultCallback: (result: ExportResult) => void, - ) { - try { - for (let i = 0; i < spans.length; i += 1) { - const span = spans[i]; - const context = span.spanContext(); - submitSpan( - context.spanId, - context.traceId, - context.traceFlags, - span.parentSpanId ?? null, - span, - hrToSecs(span.startTime), - hrToSecs(span.endTime), + setAttributes(attributes: Attributes): Span { + if (!this.#otelSpan) return this; + const attributeKvs = ObjectEntries(attributes); + let i = 0; + while (i < attributeKvs.length) { + if (i + 2 < attributeKvs.length) { + op_otel_span_attribute3( + this.#otelSpan, + attributeKvs[i][0], + attributeKvs[i][1], + attributeKvs[i + 1][0], + attributeKvs[i + 1][1], + attributeKvs[i + 2][0], + attributeKvs[i + 2][1], ); + i += 3; + } else if (i + 1 < attributeKvs.length) { + op_otel_span_attribute2( + this.#otelSpan, + attributeKvs[i][0], + attributeKvs[i][1], + attributeKvs[i + 1][0], + attributeKvs[i + 1][1], + ); + i += 2; + } else { + op_otel_span_attribute1( + this.#otelSpan, + attributeKvs[i][0], + attributeKvs[i][1], + ); + i += 1; } - resultCallback({ code: 0 }); - } catch (error) { - resultCallback({ - code: 1, - error: ObjectPrototypeIsPrototypeOf(error, Error) - ? error as Error - : new Error(String(error)), - }); } + return this; } - async shutdown() {} + setStatus(status: SpanStatus): Span { + this.#otelSpan?.setStatus(status.code, status.message ?? ""); + return this; + } - async forceFlush() {} + updateName(name: string): Span { + if (!this.#otelSpan) return this; + op_otel_span_update_name(this.#otelSpan, name); + return this; + } } const CURRENT = new AsyncVariable(); class Context { + // @ts-ignore __proto__ is not supported in TypeScript #data: Record = { __proto__: null }; constructor(data?: Record | null | undefined) { + // @ts-ignore __proto__ is not supported in TypeScript this.#data = { __proto__: null, ...data }; } @@ -658,11 +478,15 @@ const ROOT_CONTEXT = new Context(); // Context manager for opentelemetry js library class ContextManager { - active(): Context { + constructor() { + throw new TypeError("ContextManager can not be constructed"); + } + + static active(): Context { return CURRENT.get() ?? ROOT_CONTEXT; } - with ReturnType>( + static with ReturnType>( context: Context, fn: F, thisArg?: ThisParameterType, @@ -677,7 +501,7 @@ class ContextManager { } // deno-lint-ignore no-explicit-any - bind any>( + static bind any>( context: Context, target: T, ): T { @@ -691,11 +515,11 @@ class ContextManager { }) as T; } - enable() { + static enable() { return this; } - disable() { + static disable() { return this; } } @@ -729,9 +553,50 @@ interface MetricAdvice { explicitBucketBoundaries?: number[]; } -export class MeterProvider { - getMeter(name: string, version?: string, options?: MeterOptions): Meter { - return new Meter({ name, version, schemaUrl: options?.schemaUrl }); +interface OtelMeter { + __key: "meter"; + createCounter(name: string, description?: string, unit?: string): Instrument; + createUpDownCounter( + name: string, + description?: string, + unit?: string, + ): Instrument; + createGauge(name: string, description?: string, unit?: string): Instrument; + createHistogram( + name: string, + description?: string, + unit?: string, + explicitBucketBoundaries?: number[], + ): Instrument; + createObservableCounter( + name: string, + description?: string, + unit?: string, + ): Instrument; + createObservableUpDownCounter( + name: string, + description?: string, + unit?: string, + ): Instrument; + createObservableGauge( + name: string, + description?: string, + unit?: string, + ): Instrument; +} + +class MeterProvider { + constructor() { + throw new TypeError("MeterProvider can not be constructed"); + } + + static getMeter( + name: string, + version?: string, + options?: MeterOptions, + ): Meter { + const meter = new OtelMeter(name, version, options?.schemaUrl); + return new Meter(meter); } } @@ -777,22 +642,18 @@ const BATCH_CALLBACKS = new SafeMap< const INDIVIDUAL_CALLBACKS = new SafeMap>(); class Meter { - #instrumentationLibrary: InstrumentationLibrary; + #meter: OtelMeter; - constructor(instrumentationLibrary: InstrumentationLibrary) { - this.#instrumentationLibrary = instrumentationLibrary; + constructor(meter: OtelMeter) { + this.#meter = meter; } - createCounter( - name: string, - options?: MetricOptions, - ): Counter { + createCounter(name: string, options?: MetricOptions): Counter { if (options?.valueType !== undefined && options?.valueType !== 1) { throw new Error("Only valueType: DOUBLE is supported"); } if (!METRICS_ENABLED) return new Counter(null, false); - activateInstrumentationLibrary(this.#instrumentationLibrary); - const instrument = op_otel_metric_create_counter( + const instrument = this.#meter.createCounter( name, // deno-lint-ignore prefer-primordials options?.description, @@ -801,16 +662,12 @@ class Meter { return new Counter(instrument, false); } - createUpDownCounter( - name: string, - options?: MetricOptions, - ): Counter { + createUpDownCounter(name: string, options?: MetricOptions): Counter { if (options?.valueType !== undefined && options?.valueType !== 1) { throw new Error("Only valueType: DOUBLE is supported"); } if (!METRICS_ENABLED) return new Counter(null, true); - activateInstrumentationLibrary(this.#instrumentationLibrary); - const instrument = op_otel_metric_create_up_down_counter( + const instrument = this.#meter.createUpDownCounter( name, // deno-lint-ignore prefer-primordials options?.description, @@ -819,16 +676,12 @@ class Meter { return new Counter(instrument, true); } - createGauge( - name: string, - options?: MetricOptions, - ): Gauge { + createGauge(name: string, options?: MetricOptions): Gauge { if (options?.valueType !== undefined && options?.valueType !== 1) { throw new Error("Only valueType: DOUBLE is supported"); } if (!METRICS_ENABLED) return new Gauge(null); - activateInstrumentationLibrary(this.#instrumentationLibrary); - const instrument = op_otel_metric_create_gauge( + const instrument = this.#meter.createGauge( name, // deno-lint-ignore prefer-primordials options?.description, @@ -837,16 +690,12 @@ class Meter { return new Gauge(instrument); } - createHistogram( - name: string, - options?: MetricOptions, - ): Histogram { + createHistogram(name: string, options?: MetricOptions): Histogram { if (options?.valueType !== undefined && options?.valueType !== 1) { throw new Error("Only valueType: DOUBLE is supported"); } if (!METRICS_ENABLED) return new Histogram(null); - activateInstrumentationLibrary(this.#instrumentationLibrary); - const instrument = op_otel_metric_create_histogram( + const instrument = this.#meter.createHistogram( name, // deno-lint-ignore prefer-primordials options?.description, @@ -856,16 +705,12 @@ class Meter { return new Histogram(instrument); } - createObservableCounter( - name: string, - options?: MetricOptions, - ): Observable { + createObservableCounter(name: string, options?: MetricOptions): Observable { if (options?.valueType !== undefined && options?.valueType !== 1) { throw new Error("Only valueType: DOUBLE is supported"); } if (!METRICS_ENABLED) new Observable(new ObservableResult(null, true)); - activateInstrumentationLibrary(this.#instrumentationLibrary); - const instrument = op_otel_metric_create_observable_counter( + const instrument = this.#meter.createObservableCounter( name, // deno-lint-ignore prefer-primordials options?.description, @@ -874,24 +719,6 @@ class Meter { return new Observable(new ObservableResult(instrument, true)); } - createObservableGauge( - name: string, - options?: MetricOptions, - ): Observable { - if (options?.valueType !== undefined && options?.valueType !== 1) { - throw new Error("Only valueType: DOUBLE is supported"); - } - if (!METRICS_ENABLED) new Observable(new ObservableResult(null, false)); - activateInstrumentationLibrary(this.#instrumentationLibrary); - const instrument = op_otel_metric_create_observable_gauge( - name, - // deno-lint-ignore prefer-primordials - options?.description, - options?.unit, - ) as Instrument; - return new Observable(new ObservableResult(instrument, false)); - } - createObservableUpDownCounter( name: string, options?: MetricOptions, @@ -900,8 +727,21 @@ class Meter { throw new Error("Only valueType: DOUBLE is supported"); } if (!METRICS_ENABLED) new Observable(new ObservableResult(null, false)); - activateInstrumentationLibrary(this.#instrumentationLibrary); - const instrument = op_otel_metric_create_observable_up_down_counter( + const instrument = this.#meter.createObservableUpDownCounter( + name, + // deno-lint-ignore prefer-primordials + options?.description, + options?.unit, + ) as Instrument; + return new Observable(new ObservableResult(instrument, false)); + } + + createObservableGauge(name: string, options?: MetricOptions): Observable { + if (options?.valueType !== undefined && options?.valueType !== 1) { + throw new Error("Only valueType: DOUBLE is supported"); + } + if (!METRICS_ENABLED) new Observable(new ObservableResult(null, false)); + const instrument = this.#meter.createObservableGauge( name, // deno-lint-ignore prefer-primordials options?.description, @@ -987,12 +827,7 @@ function record( ); i += 2; } else if (remaining === 1) { - op_otel_metric_record1( - instrument, - value, - attrs[i][0], - attrs[i][1], - ); + op_otel_metric_record1(instrument, value, attrs[i][0], attrs[i][1]); i += 1; } } @@ -1212,24 +1047,46 @@ const otelConsoleConfig = { replace: 2, }; +function otelLog(message: string, level: number) { + const currentSpan = CURRENT.get()?.getValue(SPAN_KEY); + const otelSpan = currentSpan !== undefined + ? getOtelSpan(currentSpan) + : undefined; + if (otelSpan || currentSpan === undefined) { + op_otel_log(message, level, otelSpan); + } else { + const spanContext = currentSpan.spanContext(); + op_otel_log_foreign( + message, + level, + spanContext.traceId, + spanContext.spanId, + spanContext.traceFlags, + ); + } +} + +let builtinTracerCache: Tracer; + +export function builtinTracer(): Tracer { + if (!builtinTracerCache) { + builtinTracerCache = new Tracer(OtelTracer.builtin()); + } + return builtinTracerCache; +} + export function bootstrap( config: [ 0 | 1, 0 | 1, - typeof otelConsoleConfig[keyof typeof otelConsoleConfig], + (typeof otelConsoleConfig)[keyof typeof otelConsoleConfig], 0 | 1, ], ): void { - const { - 0: tracingEnabled, - 1: metricsEnabled, - 2: consoleConfig, - 3: deterministic, - } = config; + const { 0: tracingEnabled, 1: metricsEnabled, 2: consoleConfig } = config; TRACING_ENABLED = tracingEnabled === 1; METRICS_ENABLED = metricsEnabled === 1; - DETERMINISTIC = deterministic === 1; switch (consoleConfig) { case otelConsoleConfig.capture: @@ -1248,7 +1105,7 @@ export function bootstrap( } export const telemetry = { - SpanExporter, - ContextManager, - MeterProvider, + tracerProvider: TracerProvider, + contextManager: ContextManager, + meterProvider: MeterProvider, }; diff --git a/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts b/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts index 9c44457832..da72260d42 100644 --- a/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts +++ b/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts @@ -1,38 +1,15 @@ // Copyright 2024-2024 the Deno authors. All rights reserved. MIT license. -import { context } from "npm:@opentelemetry/api@1"; -import { - BasicTracerProvider, - SimpleSpanProcessor, -} from "npm:@opentelemetry/sdk-trace-base@1"; +import { context, trace, metrics } from "npm:@opentelemetry/api@1"; // @ts-ignore Deno.telemetry is not typed yet const telemetry = Deno.telemetry ?? Deno.tracing; -let COUNTER = 1; - /** * Register `Deno.telemetry` with the OpenTelemetry library. */ export function register() { - context.setGlobalContextManager( - new telemetry.ContextManager() ?? telemetry.ContextManager(), - ); - - const provider = new BasicTracerProvider({ - idGenerator: Deno.env.get("DENO_UNSTABLE_OTEL_DETERMINISTIC") === "1" ? { - generateSpanId() { - return "1" + String(COUNTER++).padStart(15, "0"); - }, - generateTraceId() { - return "1" + String(COUNTER++).padStart(31, "0"); - } - } : undefined - }); - - // @ts-ignore Deno.tracing is not typed yet - const exporter = new telemetry.SpanExporter(); - provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); - - provider.register(); + context.setGlobalContextManager(telemetry.contextManager); + trace.setGlobalTracerProvider(telemetry.tracerProvider); + metrics.setGlobalMeterProvider(telemetry.meterProvider); } diff --git a/tests/specs/cli/otel_basic/basic.out b/tests/specs/cli/otel_basic/basic.out index 1e82ba59b3..c16f57a8fc 100644 --- a/tests/specs/cli/otel_basic/basic.out +++ b/tests/specs/cli/otel_basic/basic.out @@ -2,7 +2,7 @@ "spans": [ { "traceId": "00000000000000000000000000000001", - "spanId": "0000000000000002", + "spanId": "0000000000000001", "traceState": "", "parentSpanId": "", "flags": 1, @@ -59,8 +59,8 @@ } }, { - "traceId": "00000000000000000000000000000003", - "spanId": "0000000000000004", + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000002", "traceState": "", "parentSpanId": "", "flags": 1, @@ -117,10 +117,10 @@ } }, { - "traceId": "00000000000000000000000000000003", - "spanId": "1000000000000001", + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000003", "traceState": "", - "parentSpanId": "0000000000000004", + "parentSpanId": "0000000000000002", "flags": 1, "name": "outer span", "kind": 1, @@ -138,10 +138,10 @@ } }, { - "traceId": "00000000000000000000000000000003", - "spanId": "1000000000000002", + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000004", "traceState": "", - "parentSpanId": "1000000000000001", + "parentSpanId": "0000000000000003", "flags": 1, "name": "inner span", "kind": 1, @@ -171,8 +171,8 @@ "attributes": [], "droppedAttributesCount": 0, "flags": 1, - "traceId": "00000000000000000000000000000003", - "spanId": "1000000000000002" + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000004" }, { "timeUnixNano": "0", @@ -185,8 +185,8 @@ "attributes": [], "droppedAttributesCount": 0, "flags": 1, - "traceId": "00000000000000000000000000000003", - "spanId": "1000000000000002" + "traceId": "00000000000000000000000000000002", + "spanId": "0000000000000004" } ], "metrics": [] diff --git a/tests/specs/cli/otel_basic/context.ts b/tests/specs/cli/otel_basic/context.ts index cef0dbd81a..16b08835ff 100644 --- a/tests/specs/cli/otel_basic/context.ts +++ b/tests/specs/cli/otel_basic/context.ts @@ -2,9 +2,7 @@ import { assertEquals } from "@std/assert"; const { ContextManager } = Deno.telemetry; -const cm = new ContextManager(); - -const a = cm.active(); +const a = ContextManager.active(); const b = a.setValue("b", 1); const c = b.setValue("c", 2); diff --git a/tests/specs/cli/otel_basic/main.ts b/tests/specs/cli/otel_basic/main.ts index 608d1d3341..921c39911b 100644 --- a/tests/specs/cli/otel_basic/main.ts +++ b/tests/specs/cli/otel_basic/main.ts @@ -21,8 +21,13 @@ const server = Deno.serve( stdout: "null", }); const child = command.spawn(); - child.output() - .then(() => server.shutdown()) + child.status + .then((status) => { + if (status.signal) { + throw new Error("child process failed: " + JSON.stringify(status)); + } + return server.shutdown(); + }) .then(() => { data.logs.sort((a, b) => Number( diff --git a/tests/specs/cli/otel_basic/metric.ts b/tests/specs/cli/otel_basic/metric.ts index 2b472a6fb8..f95c0cb802 100644 --- a/tests/specs/cli/otel_basic/metric.ts +++ b/tests/specs/cli/otel_basic/metric.ts @@ -1,6 +1,6 @@ import { metrics } from "npm:@opentelemetry/api@1"; -metrics.setGlobalMeterProvider(new Deno.telemetry.MeterProvider()); +metrics.setGlobalMeterProvider(Deno.telemetry.meterProvider); const meter = metrics.getMeter("m"); From ccd375802a2f7d2571d658b61b0ce40152dc4ed2 Mon Sep 17 00:00:00 2001 From: snek Date: Mon, 6 Jan 2025 15:24:59 +0100 Subject: [PATCH 066/107] refactor(quic): introduce endpoint, 0rtt, cleanup (#27444) A QUIC endpoint is a UDP socket which multiplexes QUIC sessions, which may be initiated in either direction. This PR exposes endpoints and moves things around as needed. Now that endpoints can be reused between client connections, we have a way to share tls tickets between them and allow 0rtt. This interface currently works by conditionally returning a promise. Also cleaned up the rust op names, fixed some lingering problems in the data transmission, and switched to explicit error types. --- ext/net/03_quic.js | 576 ++++++++++++++++++++---------------- ext/net/lib.deno_net.d.ts | 325 ++++++++++++++------- ext/net/lib.rs | 44 +-- ext/net/quic.rs | 596 ++++++++++++++++++++++++++------------ runtime/errors.rs | 23 ++ runtime/fmt_errors.rs | 2 +- runtime/js/90_deno_ns.js | 36 ++- tests/unit/quic_test.ts | 122 +++++--- 8 files changed, 1128 insertions(+), 596 deletions(-) diff --git a/ext/net/03_quic.js b/ext/net/03_quic.js index 48b35866d2..d74d356edb 100644 --- a/ext/net/03_quic.js +++ b/ext/net/03_quic.js @@ -1,33 +1,42 @@ // Copyright 2018-2025 the Deno authors. MIT license. import { core, primordials } from "ext:core/mod.js"; import { - op_quic_accept, - op_quic_accept_bi, - op_quic_accept_incoming, - op_quic_accept_uni, - op_quic_close_connection, - op_quic_close_endpoint, - op_quic_connect, + op_quic_connecting_0rtt, + op_quic_connecting_1rtt, + op_quic_connection_accept_bi, + op_quic_connection_accept_uni, + op_quic_connection_close, op_quic_connection_closed, + op_quic_connection_get_max_datagram_size, op_quic_connection_get_protocol, op_quic_connection_get_remote_addr, + op_quic_connection_get_server_name, + op_quic_connection_handshake, + op_quic_connection_open_bi, + op_quic_connection_open_uni, + op_quic_connection_read_datagram, + op_quic_connection_send_datagram, + op_quic_endpoint_close, + op_quic_endpoint_connect, + op_quic_endpoint_create, op_quic_endpoint_get_addr, - op_quic_get_send_stream_priority, + op_quic_endpoint_listen, op_quic_incoming_accept, + op_quic_incoming_accept_0rtt, op_quic_incoming_ignore, op_quic_incoming_local_ip, op_quic_incoming_refuse, op_quic_incoming_remote_addr, op_quic_incoming_remote_addr_validated, - op_quic_listen, - op_quic_max_datagram_size, - op_quic_open_bi, - op_quic_open_uni, - op_quic_read_datagram, - op_quic_send_datagram, - op_quic_set_send_stream_priority, + op_quic_listener_accept, + op_quic_listener_stop, + op_quic_recv_stream_get_id, + op_quic_send_stream_get_id, + op_quic_send_stream_get_priority, + op_quic_send_stream_set_priority, } from "ext:core/ops"; import { + getReadableStreamResourceBacking, getWritableStreamResourceBacking, ReadableStream, readableStreamForRid, @@ -39,29 +48,297 @@ const { BadResourcePrototype, } = core; const { - Uint8Array, - TypedArrayPrototypeSubarray, + ObjectPrototypeIsPrototypeOf, + PromisePrototypeThen, + Symbol, SymbolAsyncIterator, SafePromisePrototypeFinally, - ObjectPrototypeIsPrototypeOf, } = primordials; +let getEndpointResource; + +function transportOptions({ + keepAliveInterval, + maxIdleTimeout, + maxConcurrentBidirectionalStreams, + maxConcurrentUnidirectionalStreams, + preferredAddressV4, + preferredAddressV6, + congestionControl, +}) { + return { + keepAliveInterval, + maxIdleTimeout, + maxConcurrentBidirectionalStreams, + maxConcurrentUnidirectionalStreams, + preferredAddressV4, + preferredAddressV6, + congestionControl, + }; +} + +const kRid = Symbol("rid"); + +class QuicEndpoint { + #endpoint; + + constructor( + { hostname = "::", port = 0, [kRid]: rid } = { __proto__: null }, + ) { + this.#endpoint = rid ?? op_quic_endpoint_create({ hostname, port }, true); + } + + get addr() { + return op_quic_endpoint_get_addr(this.#endpoint); + } + + listen(options) { + const keyPair = loadTlsKeyPair("Deno.QuicEndpoint.listen", { + cert: options.cert, + key: options.key, + }); + const listener = op_quic_endpoint_listen( + this.#endpoint, + { alpnProtocols: options.alpnProtocols }, + transportOptions(options), + keyPair, + ); + return new QuicListener(listener, this); + } + + close({ closeCode = 0, reason = "" } = { __proto__: null }) { + op_quic_endpoint_close(this.#endpoint, closeCode, reason); + } + + static { + getEndpointResource = (e) => e.#endpoint; + } +} + +class QuicListener { + #listener; + #endpoint; + + constructor(listener, endpoint) { + this.#listener = listener; + this.#endpoint = endpoint; + } + + get endpoint() { + return this.#endpoint; + } + + async incoming() { + const incoming = await op_quic_listener_accept(this.#listener); + return new QuicIncoming(incoming, this.#endpoint); + } + + async accept() { + const incoming = await this.incoming(); + const connection = await incoming.accept(); + return connection; + } + + async next() { + try { + const connection = await this.accept(); + return { value: connection, done: false }; + } catch (error) { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + return { value: undefined, done: true }; + } + throw error; + } + } + + [SymbolAsyncIterator]() { + return this; + } + + stop() { + op_quic_listener_stop(this.#listener); + } +} + +class QuicIncoming { + #incoming; + #endpoint; + + constructor(incoming, endpoint) { + this.#incoming = incoming; + this.#endpoint = endpoint; + } + + get localIp() { + return op_quic_incoming_local_ip(this.#incoming); + } + + get remoteAddr() { + return op_quic_incoming_remote_addr(this.#incoming); + } + + get remoteAddressValidated() { + return op_quic_incoming_remote_addr_validated(this.#incoming); + } + + accept(options) { + const tOptions = options ? transportOptions(options) : null; + if (options?.zeroRtt) { + const conn = op_quic_incoming_accept_0rtt( + this.#incoming, + tOptions, + ); + return new QuicConn(conn, this.#endpoint); + } + return PromisePrototypeThen( + op_quic_incoming_accept(this.#incoming, tOptions), + (conn) => new QuicConn(conn, this.#endpoint), + ); + } + + refuse() { + op_quic_incoming_refuse(this.#incoming); + } + + ignore() { + op_quic_incoming_ignore(this.#incoming); + } +} + +class QuicConn { + #resource; + #bidiStream = null; + #uniStream = null; + #closed; + #handshake; + #endpoint; + + constructor(resource, endpoint) { + this.#resource = resource; + this.#endpoint = endpoint; + + this.#closed = op_quic_connection_closed(this.#resource); + core.unrefOpPromise(this.#closed); + } + + get endpoint() { + return this.#endpoint; + } + + get protocol() { + return op_quic_connection_get_protocol(this.#resource); + } + + get remoteAddr() { + return op_quic_connection_get_remote_addr(this.#resource); + } + + get serverName() { + return op_quic_connection_get_server_name(this.#resource); + } + + async createBidirectionalStream( + { sendOrder, waitUntilAvailable } = { __proto__: null }, + ) { + const { 0: txRid, 1: rxRid } = await op_quic_connection_open_bi( + this.#resource, + waitUntilAvailable ?? false, + ); + if (sendOrder !== null && sendOrder !== undefined) { + op_quic_send_stream_set_priority(txRid, sendOrder); + } + return new QuicBidirectionalStream(txRid, rxRid, this.#closed); + } + + async createUnidirectionalStream( + { sendOrder, waitUntilAvailable } = { __proto__: null }, + ) { + const rid = await op_quic_connection_open_uni( + this.#resource, + waitUntilAvailable ?? false, + ); + if (sendOrder !== null && sendOrder !== undefined) { + op_quic_send_stream_set_priority(rid, sendOrder); + } + return writableStream(rid, this.#closed); + } + + get incomingBidirectionalStreams() { + if (this.#bidiStream === null) { + this.#bidiStream = ReadableStream.from( + bidiStream(this.#resource, this.#closed), + ); + } + return this.#bidiStream; + } + + get incomingUnidirectionalStreams() { + if (this.#uniStream === null) { + this.#uniStream = ReadableStream.from( + uniStream(this.#resource, this.#closed), + ); + } + return this.#uniStream; + } + + get maxDatagramSize() { + return op_quic_connection_get_max_datagram_size(this.#resource); + } + + async readDatagram() { + const buffer = await op_quic_connection_read_datagram(this.#resource); + return buffer; + } + + async sendDatagram(data) { + await op_quic_connection_send_datagram(this.#resource, data); + } + + get handshake() { + if (!this.#handshake) { + this.#handshake = op_quic_connection_handshake(this.#resource); + } + return this.#handshake; + } + + get closed() { + core.refOpPromise(this.#closed); + return this.#closed; + } + + close({ closeCode = 0, reason = "" } = { __proto__: null }) { + op_quic_connection_close(this.#resource, closeCode, reason); + } +} + class QuicSendStream extends WritableStream { get sendOrder() { - return op_quic_get_send_stream_priority( + return op_quic_send_stream_get_priority( getWritableStreamResourceBacking(this).rid, ); } set sendOrder(p) { - op_quic_set_send_stream_priority( + op_quic_send_stream_set_priority( getWritableStreamResourceBacking(this).rid, p, ); } + + get id() { + return op_quic_send_stream_get_id( + getWritableStreamResourceBacking(this).rid, + ); + } } -class QuicReceiveStream extends ReadableStream {} +class QuicReceiveStream extends ReadableStream { + get id() { + return op_quic_recv_stream_get_id( + getReadableStreamResourceBacking(this).rid, + ); + } +} function readableStream(rid, closed) { // stream can be indirectly closed by closing connection. @@ -100,7 +377,7 @@ class QuicBidirectionalStream { async function* bidiStream(conn, closed) { try { while (true) { - const r = await op_quic_accept_bi(conn); + const r = await op_quic_connection_accept_bi(conn); yield new QuicBidirectionalStream(r[0], r[1], closed); } } catch (error) { @@ -114,7 +391,7 @@ async function* bidiStream(conn, closed) { async function* uniStream(conn, closed) { try { while (true) { - const uniRid = await op_quic_accept_uni(conn); + const uniRid = await op_quic_connection_accept_uni(conn); yield readableStream(uniRid, closed); } } catch (error) { @@ -125,241 +402,48 @@ async function* uniStream(conn, closed) { } } -class QuicConn { - #resource; - #bidiStream = null; - #uniStream = null; - #closed; - - constructor(resource) { - this.#resource = resource; - - this.#closed = op_quic_connection_closed(this.#resource); - core.unrefOpPromise(this.#closed); - } - - get protocol() { - return op_quic_connection_get_protocol(this.#resource); - } - - get remoteAddr() { - return op_quic_connection_get_remote_addr(this.#resource); - } - - async createBidirectionalStream( - { sendOrder, waitUntilAvailable } = { __proto__: null }, - ) { - const { 0: txRid, 1: rxRid } = await op_quic_open_bi( - this.#resource, - waitUntilAvailable ?? false, - ); - if (sendOrder !== null && sendOrder !== undefined) { - op_quic_set_send_stream_priority(txRid, sendOrder); - } - return new QuicBidirectionalStream(txRid, rxRid, this.#closed); - } - - async createUnidirectionalStream( - { sendOrder, waitUntilAvailable } = { __proto__: null }, - ) { - const rid = await op_quic_open_uni( - this.#resource, - waitUntilAvailable ?? false, - ); - if (sendOrder !== null && sendOrder !== undefined) { - op_quic_set_send_stream_priority(rid, sendOrder); - } - return writableStream(rid, this.#closed); - } - - get incomingBidirectionalStreams() { - if (this.#bidiStream === null) { - this.#bidiStream = ReadableStream.from( - bidiStream(this.#resource, this.#closed), - ); - } - return this.#bidiStream; - } - - get incomingUnidirectionalStreams() { - if (this.#uniStream === null) { - this.#uniStream = ReadableStream.from( - uniStream(this.#resource, this.#closed), - ); - } - return this.#uniStream; - } - - get maxDatagramSize() { - return op_quic_max_datagram_size(this.#resource); - } - - async readDatagram(p) { - const view = p || new Uint8Array(this.maxDatagramSize); - const nread = await op_quic_read_datagram(this.#resource, view); - return TypedArrayPrototypeSubarray(view, 0, nread); - } - - async sendDatagram(data) { - await op_quic_send_datagram(this.#resource, data); - } - - get closed() { - core.refOpPromise(this.#closed); - return this.#closed; - } - - close({ closeCode, reason }) { - op_quic_close_connection(this.#resource, closeCode, reason); - } -} - -class QuicIncoming { - #incoming; - - constructor(incoming) { - this.#incoming = incoming; - } - - get localIp() { - return op_quic_incoming_local_ip(this.#incoming); - } - - get remoteAddr() { - return op_quic_incoming_remote_addr(this.#incoming); - } - - get remoteAddressValidated() { - return op_quic_incoming_remote_addr_validated(this.#incoming); - } - - async accept() { - const conn = await op_quic_incoming_accept(this.#incoming); - return new QuicConn(conn); - } - - refuse() { - op_quic_incoming_refuse(this.#incoming); - } - - ignore() { - op_quic_incoming_ignore(this.#incoming); - } -} - -class QuicListener { - #endpoint; - - constructor(endpoint) { - this.#endpoint = endpoint; - } - - get addr() { - return op_quic_endpoint_get_addr(this.#endpoint); - } - - async accept() { - const conn = await op_quic_accept(this.#endpoint); - return new QuicConn(conn); - } - - async incoming() { - const incoming = await op_quic_accept_incoming(this.#endpoint); - return new QuicIncoming(incoming); - } - - async next() { - let conn; - try { - conn = await this.accept(); - } catch (error) { - if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { - return { value: undefined, done: true }; - } - throw error; - } - return { value: conn, done: false }; - } - - [SymbolAsyncIterator]() { - return this; - } - - close({ closeCode, reason }) { - op_quic_close_endpoint(this.#endpoint, closeCode, reason); - } -} - -async function listenQuic( - { - hostname, - port, - cert, - key, - alpnProtocols, - keepAliveInterval, - maxIdleTimeout, - maxConcurrentBidirectionalStreams, - maxConcurrentUnidirectionalStreams, - }, -) { - hostname = hostname || "0.0.0.0"; - const keyPair = loadTlsKeyPair("Deno.listenQuic", { cert, key }); - const endpoint = await op_quic_listen( - { hostname, port }, - { alpnProtocols }, +function connectQuic(options) { + const endpoint = options.endpoint ?? + new QuicEndpoint({ + [kRid]: op_quic_endpoint_create({ hostname: "::", port: 0 }, 0, false), + }); + const keyPair = loadTlsKeyPair("Deno.connectQuic", { + cert: options.cert, + key: options.key, + }); + const connecting = op_quic_endpoint_connect( + getEndpointResource(endpoint), { - keepAliveInterval, - maxIdleTimeout, - maxConcurrentBidirectionalStreams, - maxConcurrentUnidirectionalStreams, + addr: { + hostname: options.hostname, + port: options.port, + }, + caCerts: options.caCerts, + alpnProtocols: options.alpnProtocols, + serverName: options.serverName, }, + transportOptions(options), keyPair, ); - return new QuicListener(endpoint); -} -async function connectQuic( - { - hostname, - port, - serverName, - caCerts, - cert, - key, - alpnProtocols, - keepAliveInterval, - maxIdleTimeout, - maxConcurrentBidirectionalStreams, - maxConcurrentUnidirectionalStreams, - congestionControl, - }, -) { - const keyPair = loadTlsKeyPair("Deno.connectQuic", { cert, key }); - const conn = await op_quic_connect( - { hostname, port }, - { - caCerts, - alpnProtocols, - serverName, - }, - { - keepAliveInterval, - maxIdleTimeout, - maxConcurrentBidirectionalStreams, - maxConcurrentUnidirectionalStreams, - congestionControl, - }, - keyPair, + if (options.zeroRtt) { + const conn = op_quic_connecting_0rtt(connecting); + if (conn) { + return new QuicConn(conn, endpoint); + } + } + + return PromisePrototypeThen( + op_quic_connecting_1rtt(connecting), + (conn) => new QuicConn(conn, endpoint), ); - return new QuicConn(conn); } export { connectQuic, - listenQuic, QuicBidirectionalStream, QuicConn, + QuicEndpoint, QuicIncoming, QuicListener, QuicReceiveStream, diff --git a/ext/net/lib.deno_net.d.ts b/ext/net/lib.deno_net.d.ts index 8e0c384168..30d3357395 100644 --- a/ext/net/lib.deno_net.d.ts +++ b/ext/net/lib.deno_net.d.ts @@ -450,6 +450,24 @@ declare namespace Deno { options?: StartTlsOptions, ): Promise; + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicEndpointOptions { + /** + * A literal IP address or host name that can be resolved to an IP address. + * @default {"::"} + */ + hostname?: string; + /** + * The port to bind to. + * @default {0} + */ + port?: number; + } + /** * **UNSTABLE**: New API, yet to be vetted. * @experimental @@ -479,53 +497,20 @@ declare namespace Deno { * @default {100} */ maxConcurrentUnidirectionalStreams?: number; - } - - /** - * **UNSTABLE**: New API, yet to be vetted. - * @experimental - * @category Network - */ - export interface ListenQuicOptions extends QuicTransportOptions { - /** The port to connect to. */ - port: number; /** - * A literal IP address or host name that can be resolved to an IP address. - * @default {"0.0.0.0"} + * The congestion control algorithm used when sending data over this connection. + * @default {"default"} */ - hostname?: string; - /** Server private key in PEM format */ - key: string; - /** Cert chain in PEM format */ - cert: string; - /** Application-Layer Protocol Negotiation (ALPN) protocols to announce to - * the client. QUIC requires the use of ALPN. - */ - alpnProtocols: string[]; + congestionControl?: "throughput" | "low-latency" | "default"; } - /** - * **UNSTABLE**: New API, yet to be vetted. - * Listen announces on the local transport address over QUIC. - * - * ```ts - * const lstnr = await Deno.listenQuic({ port: 443, cert: "...", key: "...", alpnProtocols: ["h3"] }); - * ``` - * - * Requires `allow-net` permission. - * - * @experimental - * @tags allow-net - * @category Network - */ - export function listenQuic(options: ListenQuicOptions): Promise; - /** * **UNSTABLE**: New API, yet to be vetted. * @experimental * @category Network */ - export interface ConnectQuicOptions extends QuicTransportOptions { + export interface ConnectQuicOptions + extends QuicTransportOptions { /** The port to connect to. */ port: number; /** A literal IP address or host name that can be resolved to an IP address. */ @@ -543,30 +528,73 @@ declare namespace Deno { * Must be in PEM format. */ caCerts?: string[]; /** - * The congestion control algorithm used when sending data over this connection. + * If no endpoint is provided, a new one is bound on an ephemeral port. */ - congestionControl?: "throughput" | "low-latency"; + endpoint?: QuicEndpoint; + /** + * Attempt to convert the connection into 0-RTT. Any data sent before + * the TLS handshake completes is vulnerable to replay attacks. + * @default {false} + */ + zeroRtt?: ZRTT; } /** * **UNSTABLE**: New API, yet to be vetted. - * Establishes a secure connection over QUIC using a hostname and port. The - * cert file is optional and if not included Mozilla's root certificates will - * be used. See also https://github.com/ctz/webpki-roots for specifics. - * - * ```ts - * const caCert = await Deno.readTextFile("./certs/my_custom_root_CA.pem"); - * const conn1 = await Deno.connectQuic({ hostname: "example.com", port: 443, alpnProtocols: ["h3"] }); - * const conn2 = await Deno.connectQuic({ caCerts: [caCert], hostname: "example.com", port: 443, alpnProtocols: ["h3"] }); - * ``` - * - * Requires `allow-net` permission. - * * @experimental - * @tags allow-net * @category Network */ - export function connectQuic(options: ConnectQuicOptions): Promise; + export interface QuicServerTransportOptions extends QuicTransportOptions { + /** + * Preferred IPv4 address to be communicated to the client during + * handshaking. If the client is able to reach this address it will switch + * to it. + * @default {undefined} + */ + preferredAddressV4?: string; + /** + * Preferred IPv6 address to be communicated to the client during + * handshaking. If the client is able to reach this address it will switch + * to it. + * @default {undefined} + */ + preferredAddressV6?: string; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicListenOptions extends QuicServerTransportOptions { + /** Application-Layer Protocol Negotiation (ALPN) protocols to announce to + * the client. QUIC requires the use of ALPN. + */ + alpnProtocols: string[]; + /** Server private key in PEM format */ + key: string; + /** Cert chain in PEM format */ + cert: string; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export interface QuicAcceptOptions + extends QuicServerTransportOptions { + /** Application-Layer Protocol Negotiation (ALPN) protocols to announce to + * the client. QUIC requires the use of ALPN. + */ + alpnProtocols?: string[]; + /** + * Convert this connection into 0.5-RTT at the cost of weakened security, as + * 0.5-RTT data may be sent before TLS client authentication has occurred. + * @default {false} + */ + zeroRtt?: ZRTT; + } /** * **UNSTABLE**: New API, yet to be vetted. @@ -582,14 +610,93 @@ declare namespace Deno { /** * **UNSTABLE**: New API, yet to be vetted. - * An incoming connection for which the server has not yet begun its part of the handshake. + * + * @experimental + * @category Network + */ + export interface QuicSendStreamOptions { + /** Indicates the send priority of this stream relative to other streams for + * which the value has been set. + * @default {0} + */ + sendOrder?: number; + /** Wait until there is sufficient flow credit to create the stream. + * @default {false} + */ + waitUntilAvailable?: boolean; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * @experimental + * @category Network + */ + export class QuicEndpoint { + /** + * Create a QUIC endpoint which may be used for client or server connections. + * + * Requires `allow-net` permission. + * + * @experimental + * @tags allow-net + * @category Network + */ + constructor(options?: QuicEndpointOptions); + + /** Return the address of the `QuicListener`. */ + readonly addr: NetAddr; + + /** + * **UNSTABLE**: New API, yet to be vetted. + * Listen announces on the local transport address over QUIC. + * + * @experimental + * @category Network + */ + listen(options: QuicListenOptions): QuicListener; + + /** + * Closes the endpoint. All associated connections will be closed and incoming + * connections will be rejected. + */ + close(info?: QuicCloseInfo): void; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * Specialized listener that accepts QUIC connections. + * + * @experimental + * @category Network + */ + export interface QuicListener extends AsyncIterable { + /** Waits for and resolves to the next incoming connection. */ + incoming(): Promise; + + /** Wait for the next incoming connection and accepts it. */ + accept(): Promise; + + /** Stops the listener. This does not close the endpoint. */ + stop(): void; + + [Symbol.asyncIterator](): AsyncIterableIterator; + + /** The endpoint for this listener. */ + readonly endpoint: QuicEndpoint; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * An incoming connection for which the server has not yet begun its part of + * the handshake. * * @experimental * @category Network */ export interface QuicIncoming { /** - * The local IP address which was used when the peer established the connection. + * The local IP address which was used when the peer established the + * connection. */ readonly localIp: string; @@ -599,14 +706,17 @@ declare namespace Deno { readonly remoteAddr: NetAddr; /** - * Whether the socket address that is initiating this connection has proven that they can receive traffic. + * Whether the socket address that is initiating this connection has proven + * that they can receive traffic. */ readonly remoteAddressValidated: boolean; /** * Accept this incoming connection. */ - accept(): Promise; + accept( + options?: QuicAcceptOptions, + ): ZRTT extends true ? QuicConn : Promise; /** * Refuse this incoming connection. @@ -619,48 +729,6 @@ declare namespace Deno { ignore(): void; } - /** - * **UNSTABLE**: New API, yet to be vetted. - * Specialized listener that accepts QUIC connections. - * - * @experimental - * @category Network - */ - export interface QuicListener extends AsyncIterable { - /** Return the address of the `QuicListener`. */ - readonly addr: NetAddr; - - /** Waits for and resolves to the next connection to the `QuicListener`. */ - accept(): Promise; - - /** Waits for and resolves to the next incoming request to the `QuicListener`. */ - incoming(): Promise; - - /** Close closes the listener. Any pending accept promises will be rejected - * with errors. */ - close(info: QuicCloseInfo): void; - - [Symbol.asyncIterator](): AsyncIterableIterator; - } - - /** - * **UNSTABLE**: New API, yet to be vetted. - * - * @experimental - * @category Network - */ - export interface QuicSendStreamOptions { - /** Indicates the send priority of this stream relative to other streams for - * which the value has been set. - * @default {undefined} - */ - sendOrder?: number; - /** Wait until there is sufficient flow credit to create the stream. - * @default {false} - */ - waitUntilAvailable?: boolean; - } - /** * **UNSTABLE**: New API, yet to be vetted. * @@ -670,7 +738,7 @@ declare namespace Deno { export interface QuicConn { /** Close closes the listener. Any pending accept promises will be rejected * with errors. */ - close(info: QuicCloseInfo): void; + close(info?: QuicCloseInfo): void; /** Opens and returns a bidirectional stream. */ createBidirectionalStream( options?: QuicSendStreamOptions, @@ -682,17 +750,25 @@ declare namespace Deno { /** Send a datagram. The provided data cannot be larger than * `maxDatagramSize`. */ sendDatagram(data: Uint8Array): Promise; - /** Receive a datagram. If no buffer is provider, one will be allocated. - * The size of the provided buffer should be at least `maxDatagramSize`. */ - readDatagram(buffer?: Uint8Array): Promise; + /** Receive a datagram. */ + readDatagram(): Promise; + /** The endpoint for this connection. */ + readonly endpoint: QuicEndpoint; + /** Returns a promise that resolves when the TLS handshake is complete. */ + readonly handshake: Promise; /** Return the remote address for the connection. Clients may change * addresses at will, for example when switching to a cellular internet * connection. */ readonly remoteAddr: NetAddr; - /** The negotiated ALPN protocol, if provided. */ + /** + * The negotiated ALPN protocol, if provided. Only available after the + * handshake is complete. */ readonly protocol: string | undefined; + /** The negotiated server name. Only available on the server after the + * handshake is complete. */ + readonly serverName: string | undefined; /** Returns a promise that resolves when the connection is closed. */ readonly closed: Promise; /** A stream of bidirectional streams opened by the peer. */ @@ -728,6 +804,11 @@ declare namespace Deno { /** Indicates the send priority of this stream relative to other streams for * which the value has been set. */ sendOrder: number; + + /** + * 62-bit stream ID, unique within this connection. + */ + readonly id: bigint; } /** @@ -736,7 +817,39 @@ declare namespace Deno { * @experimental * @category Network */ - export interface QuicReceiveStream extends ReadableStream {} + export interface QuicReceiveStream extends ReadableStream { + /** + * 62-bit stream ID, unique within this connection. + */ + readonly id: bigint; + } + + /** + * **UNSTABLE**: New API, yet to be vetted. + * Establishes a secure connection over QUIC using a hostname and port. The + * cert file is optional and if not included Mozilla's root certificates will + * be used. See also https://github.com/ctz/webpki-roots for specifics. + * + * ```ts + * const caCert = await Deno.readTextFile("./certs/my_custom_root_CA.pem"); + * const conn1 = await Deno.connectQuic({ hostname: "example.com", port: 443, alpnProtocols: ["h3"] }); + * const conn2 = await Deno.connectQuic({ caCerts: [caCert], hostname: "example.com", port: 443, alpnProtocols: ["h3"] }); + * ``` + * + * If an endpoint is shared among many connections, 0-RTT can be enabled. + * When 0-RTT is successful, a QuicConn will be synchronously returned + * and data can be sent immediately with it. **Any data sent before the + * TLS handshake completes is vulnerable to replay attacks.** + * + * Requires `allow-net` permission. + * + * @experimental + * @tags allow-net + * @category Network + */ + export function connectQuic( + options: ConnectQuicOptions, + ): ZRTT extends true ? (QuicConn | Promise) : Promise; export {}; // only export exports } diff --git a/ext/net/lib.rs b/ext/net/lib.rs index b2b87fc6ad..726c51b749 100644 --- a/ext/net/lib.rs +++ b/ext/net/lib.rs @@ -20,6 +20,7 @@ use deno_core::OpState; use deno_permissions::PermissionCheckError; use deno_tls::rustls::RootCertStore; use deno_tls::RootCertStoreProvider; +pub use quic::QuicError; pub const UNSTABLE_FEATURE_NAME: &str = "net"; @@ -161,33 +162,42 @@ deno_core::extension!(deno_net, ops_unix::op_net_recv_unixpacket, ops_unix::op_net_send_unixpacket

, - quic::op_quic_accept, - quic::op_quic_accept_bi, - quic::op_quic_accept_incoming, - quic::op_quic_accept_uni, - quic::op_quic_close_connection, - quic::op_quic_close_endpoint, + quic::op_quic_connecting_0rtt, + quic::op_quic_connecting_1rtt, + quic::op_quic_connection_accept_bi, + quic::op_quic_connection_accept_uni, + quic::op_quic_connection_close, quic::op_quic_connection_closed, quic::op_quic_connection_get_protocol, quic::op_quic_connection_get_remote_addr, - quic::op_quic_connect

, + quic::op_quic_connection_get_server_name, + quic::op_quic_connection_handshake, + quic::op_quic_connection_open_bi, + quic::op_quic_connection_open_uni, + quic::op_quic_connection_get_max_datagram_size, + quic::op_quic_connection_read_datagram, + quic::op_quic_connection_send_datagram, + quic::op_quic_endpoint_close, + quic::op_quic_endpoint_connect

, + quic::op_quic_endpoint_create

, quic::op_quic_endpoint_get_addr, - quic::op_quic_get_send_stream_priority, + quic::op_quic_endpoint_listen, quic::op_quic_incoming_accept, - quic::op_quic_incoming_refuse, + quic::op_quic_incoming_accept_0rtt, quic::op_quic_incoming_ignore, quic::op_quic_incoming_local_ip, + quic::op_quic_incoming_refuse, quic::op_quic_incoming_remote_addr, quic::op_quic_incoming_remote_addr_validated, - quic::op_quic_listen

, - quic::op_quic_max_datagram_size, - quic::op_quic_open_bi, - quic::op_quic_open_uni, - quic::op_quic_read_datagram, - quic::op_quic_send_datagram, - quic::op_quic_set_send_stream_priority, + quic::op_quic_listener_accept, + quic::op_quic_listener_stop, + quic::op_quic_recv_stream_get_id, + quic::op_quic_send_stream_get_id, + quic::op_quic_send_stream_get_priority, + quic::op_quic_send_stream_set_priority, ], - esm = [ "01_net.js", "02_tls.js", "03_quic.js" ], + esm = [ "01_net.js", "02_tls.js" ], + lazy_loaded_esm = [ "03_quic.js" ], options = { root_cert_store_provider: Option>, unsafely_ignore_certificate_errors: Option>, diff --git a/ext/net/quic.rs b/ext/net/quic.rs index 676b2f06a1..8b06d46d19 100644 --- a/ext/net/quic.rs +++ b/ext/net/quic.rs @@ -4,24 +4,24 @@ use std::borrow::Cow; use std::cell::RefCell; use std::future::Future; use std::net::IpAddr; -use std::net::Ipv4Addr; use std::net::Ipv6Addr; +use std::net::SocketAddr; use std::net::SocketAddrV4; use std::net::SocketAddrV6; use std::pin::pin; use std::rc::Rc; +use std::sync::atomic::AtomicI32; +use std::sync::atomic::Ordering; use std::sync::Arc; use std::task::Context; use std::task::Poll; use std::time::Duration; -use deno_core::error::bad_resource; -use deno_core::error::generic_error; -use deno_core::error::AnyError; use deno_core::futures::task::noop_waker_ref; use deno_core::op2; use deno_core::AsyncRefCell; use deno_core::AsyncResult; +use deno_core::BufMutView; use deno_core::BufView; use deno_core::GarbageCollected; use deno_core::JsBuffer; @@ -30,20 +30,68 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::WriteOutcome; +use deno_permissions::PermissionCheckError; use deno_tls::create_client_config; use deno_tls::SocketUse; +use deno_tls::TlsError; use deno_tls::TlsKeys; use deno_tls::TlsKeysHolder; use quinn::crypto::rustls::QuicClientConfig; use quinn::crypto::rustls::QuicServerConfig; +use quinn::rustls::client::ClientSessionMemoryCache; +use quinn::rustls::client::ClientSessionStore; +use quinn::rustls::client::Resumption; use serde::Deserialize; use serde::Serialize; -use crate::resolve_addr::resolve_addr; +use crate::resolve_addr::resolve_addr_sync; use crate::DefaultTlsOptions; use crate::NetPermissions; use crate::UnsafelyIgnoreCertificateErrors; +#[derive(Debug, thiserror::Error)] +pub enum QuicError { + #[error("Endpoint created by 'connectQuic' cannot be used for listening")] + CannotListen, + #[error("key and cert are required")] + MissingTlsKey, + #[error("Duration is invalid")] + InvalidDuration, + #[error("Unable to resolve hostname")] + UnableToResolve, + #[error("{0}")] + StdIo(#[from] std::io::Error), + #[error("{0}")] + PermissionCheck(#[from] PermissionCheckError), + #[error("{0}")] + VarIntBoundsExceeded(#[from] quinn::VarIntBoundsExceeded), + #[error("{0}")] + Rustls(#[from] quinn::rustls::Error), + #[error("{0}")] + Tls(#[from] TlsError), + #[error("{0}")] + ConnectionError(#[from] quinn::ConnectionError), + #[error("{0}")] + ConnectError(#[from] quinn::ConnectError), + #[error("{0}")] + SendDatagramError(#[from] quinn::SendDatagramError), + #[error("{0}")] + ClosedStream(#[from] quinn::ClosedStream), + #[error("Invalid {0} resource")] + BadResource(&'static str), + #[error("Connection has reached the maximum number of concurrent outgoing {0} streams")] + MaxStreams(&'static str), + #[error("{0}")] + Core(#[from] deno_core::error::AnyError), +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct CloseInfo { + close_code: u64, + reason: String, +} + #[derive(Debug, Deserialize, Serialize)] struct Addr { hostname: String, @@ -56,7 +104,7 @@ struct ListenArgs { alpn_protocols: Option>, } -#[derive(Deserialize)] +#[derive(Deserialize, Default, PartialEq)] #[serde(rename_all = "camelCase")] struct TransportConfig { keep_alive_interval: Option, @@ -69,9 +117,9 @@ struct TransportConfig { } impl TryInto for TransportConfig { - type Error = AnyError; + type Error = QuicError; - fn try_into(self) -> Result { + fn try_into(self) -> Result { let mut cfg = quinn::TransportConfig::default(); if let Some(interval) = self.keep_alive_interval { @@ -79,7 +127,11 @@ impl TryInto for TransportConfig { } if let Some(timeout) = self.max_idle_timeout { - cfg.max_idle_timeout(Some(Duration::from_millis(timeout).try_into()?)); + cfg.max_idle_timeout(Some( + Duration::from_millis(timeout) + .try_into() + .map_err(|_| QuicError::InvalidDuration)?, + )); } if let Some(max) = self.max_concurrent_bidirectional_streams { @@ -111,34 +163,119 @@ impl TryInto for TransportConfig { } } -struct EndpointResource(quinn::Endpoint, Arc); +fn apply_server_transport_config( + config: &mut quinn::ServerConfig, + transport_config: TransportConfig, +) -> Result<(), QuicError> { + config.preferred_address_v4(transport_config.preferred_address_v4); + config.preferred_address_v6(transport_config.preferred_address_v6); + config.transport_config(Arc::new(transport_config.try_into()?)); + Ok(()) +} + +struct EndpointResource { + endpoint: quinn::Endpoint, + can_listen: bool, + session_store: Arc, +} impl GarbageCollected for EndpointResource {} -#[op2(async)] +#[op2] #[cppgc] -pub(crate) async fn op_quic_listen( +pub(crate) fn op_quic_endpoint_create( state: Rc>, #[serde] addr: Addr, - #[serde] args: ListenArgs, - #[serde] transport_config: TransportConfig, - #[cppgc] keys: &TlsKeysHolder, -) -> Result + can_listen: bool, +) -> Result where NP: NetPermissions + 'static, { - state - .borrow_mut() - .borrow_mut::() - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenQuic()")?; - - let addr = resolve_addr(&addr.hostname, addr.port) - .await? + let addr = resolve_addr_sync(&addr.hostname, addr.port)? .next() - .ok_or_else(|| generic_error("No resolved address found"))?; + .ok_or_else(|| QuicError::UnableToResolve)?; + + if can_listen { + state.borrow_mut().borrow_mut::().check_net( + &(&addr.ip().to_string(), Some(addr.port())), + "new Deno.QuicEndpoint()", + )?; + } else { + // If this is not a can-listen, assert that we will bind to an ephemeral port. + assert_eq!( + addr, + SocketAddr::from(( + IpAddr::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), + 0 + )) + ); + } + + let config = quinn::EndpointConfig::default(); + let socket = std::net::UdpSocket::bind(addr)?; + let endpoint = quinn::Endpoint::new( + config, + None, + socket, + quinn::default_runtime().unwrap(), + )?; + + Ok(EndpointResource { + endpoint, + can_listen, + session_store: Arc::new(ClientSessionMemoryCache::new(256)), + }) +} + +#[op2] +#[serde] +pub(crate) fn op_quic_endpoint_get_addr( + #[cppgc] endpoint: &EndpointResource, +) -> Result { + let addr = endpoint.endpoint.local_addr()?; + let addr = Addr { + hostname: format!("{}", addr.ip()), + port: addr.port(), + }; + Ok(addr) +} + +#[op2(fast)] +pub(crate) fn op_quic_endpoint_close( + #[cppgc] endpoint: &EndpointResource, + #[bigint] close_code: u64, + #[string] reason: String, +) -> Result<(), QuicError> { + endpoint + .endpoint + .close(quinn::VarInt::from_u64(close_code)?, reason.as_bytes()); + Ok(()) +} + +struct ListenerResource(quinn::Endpoint, Arc); + +impl Drop for ListenerResource { + fn drop(&mut self) { + self.0.set_server_config(None); + } +} + +impl GarbageCollected for ListenerResource {} + +#[op2] +#[cppgc] +pub(crate) fn op_quic_endpoint_listen( + #[cppgc] endpoint: &EndpointResource, + #[serde] args: ListenArgs, + #[serde] transport_config: TransportConfig, + #[cppgc] keys: &TlsKeysHolder, +) -> Result { + if !endpoint.can_listen { + return Err(QuicError::CannotListen); + } let TlsKeys::Static(deno_tls::TlsKey(cert, key)) = keys.take() else { - unreachable!() + return Err(QuicError::MissingTlsKey); }; let mut crypto = @@ -148,6 +285,9 @@ where .with_no_client_auth() .with_single_cert(cert.clone(), key.clone_key())?; + // required by QUIC spec. + crypto.max_early_data_size = u32::MAX; + if let Some(alpn_protocols) = args.alpn_protocols { crypto.alpn_protocols = alpn_protocols .into_iter() @@ -155,66 +295,24 @@ where .collect(); } - let server_config = Arc::new(QuicServerConfig::try_from(crypto)?); + let server_config = Arc::new( + QuicServerConfig::try_from(crypto).expect("TLS13 is explicitly configured"), + ); let mut config = quinn::ServerConfig::with_crypto(server_config.clone()); - config.preferred_address_v4(transport_config.preferred_address_v4); - config.preferred_address_v6(transport_config.preferred_address_v6); - config.transport_config(Arc::new(transport_config.try_into()?)); - let endpoint = quinn::Endpoint::server(config, addr)?; + apply_server_transport_config(&mut config, transport_config)?; - Ok(EndpointResource(endpoint, server_config)) + endpoint.endpoint.set_server_config(Some(config)); + + Ok(ListenerResource(endpoint.endpoint.clone(), server_config)) } -#[op2] -#[serde] -pub(crate) fn op_quic_endpoint_get_addr( - #[cppgc] endpoint: &EndpointResource, -) -> Result { - let addr = endpoint.0.local_addr()?; - let addr = Addr { - hostname: format!("{}", addr.ip()), - port: addr.port(), - }; - Ok(addr) -} - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -struct CloseInfo { - close_code: u64, - reason: String, -} - -#[op2(fast)] -pub(crate) fn op_quic_close_endpoint( - #[cppgc] endpoint: &EndpointResource, - #[bigint] close_code: u64, - #[string] reason: String, -) -> Result<(), AnyError> { - endpoint - .0 - .close(quinn::VarInt::from_u64(close_code)?, reason.as_bytes()); - Ok(()) -} - -struct ConnectionResource(quinn::Connection); +struct ConnectionResource( + quinn::Connection, + RefCell>, +); impl GarbageCollected for ConnectionResource {} -#[op2(async)] -#[cppgc] -pub(crate) async fn op_quic_accept( - #[cppgc] endpoint: &EndpointResource, -) -> Result { - match endpoint.0.accept().await { - Some(incoming) => { - let conn = incoming.accept()?.await?; - Ok(ConnectionResource(conn)) - } - None => Err(bad_resource("QuicListener is closed")), - } -} - struct IncomingResource( RefCell>, Arc, @@ -224,25 +322,30 @@ impl GarbageCollected for IncomingResource {} #[op2(async)] #[cppgc] -pub(crate) async fn op_quic_accept_incoming( - #[cppgc] endpoint: &EndpointResource, -) -> Result { - match endpoint.0.accept().await { +pub(crate) async fn op_quic_listener_accept( + #[cppgc] resource: &ListenerResource, +) -> Result { + match resource.0.accept().await { Some(incoming) => Ok(IncomingResource( RefCell::new(Some(incoming)), - endpoint.1.clone(), + resource.1.clone(), )), - None => Err(bad_resource("QuicListener is closed")), + None => Err(QuicError::BadResource("QuicListener")), } } +#[op2(fast)] +pub(crate) fn op_quic_listener_stop(#[cppgc] resource: &ListenerResource) { + resource.0.set_server_config(None); +} + #[op2] #[string] pub(crate) fn op_quic_incoming_local_ip( #[cppgc] incoming_resource: &IncomingResource, -) -> Result, AnyError> { +) -> Result, QuicError> { let Some(incoming) = incoming_resource.0.borrow_mut().take() else { - return Err(bad_resource("QuicIncoming already used")); + return Err(QuicError::BadResource("QuicIncoming")); }; Ok(incoming.local_ip().map(|ip| ip.to_string())) } @@ -251,9 +354,9 @@ pub(crate) fn op_quic_incoming_local_ip( #[serde] pub(crate) fn op_quic_incoming_remote_addr( #[cppgc] incoming_resource: &IncomingResource, -) -> Result { +) -> Result { let Some(incoming) = incoming_resource.0.borrow_mut().take() else { - return Err(bad_resource("QuicIncoming already used")); + return Err(QuicError::BadResource("QuicIncoming")); }; let addr = incoming.remote_address(); Ok(Addr { @@ -265,43 +368,66 @@ pub(crate) fn op_quic_incoming_remote_addr( #[op2(fast)] pub(crate) fn op_quic_incoming_remote_addr_validated( #[cppgc] incoming_resource: &IncomingResource, -) -> Result { +) -> Result { let Some(incoming) = incoming_resource.0.borrow_mut().take() else { - return Err(bad_resource("QuicIncoming already used")); + return Err(QuicError::BadResource("QuicIncoming")); }; Ok(incoming.remote_address_validated()) } +fn quic_incoming_accept( + incoming_resource: &IncomingResource, + transport_config: Option, +) -> Result { + let Some(incoming) = incoming_resource.0.borrow_mut().take() else { + return Err(QuicError::BadResource("QuicIncoming")); + }; + match transport_config { + Some(transport_config) if transport_config != Default::default() => { + let mut config = + quinn::ServerConfig::with_crypto(incoming_resource.1.clone()); + apply_server_transport_config(&mut config, transport_config)?; + Ok(incoming.accept_with(Arc::new(config))?) + } + _ => Ok(incoming.accept()?), + } +} + #[op2(async)] #[cppgc] pub(crate) async fn op_quic_incoming_accept( #[cppgc] incoming_resource: &IncomingResource, #[serde] transport_config: Option, -) -> Result { - let Some(incoming) = incoming_resource.0.borrow_mut().take() else { - return Err(bad_resource("QuicIncoming already used")); - }; - let conn = match transport_config { - Some(transport_config) => { - let mut config = - quinn::ServerConfig::with_crypto(incoming_resource.1.clone()); - config.preferred_address_v4(transport_config.preferred_address_v4); - config.preferred_address_v6(transport_config.preferred_address_v6); - config.transport_config(Arc::new(transport_config.try_into()?)); - incoming.accept_with(Arc::new(config))?.await? +) -> Result { + let connecting = quic_incoming_accept(incoming_resource, transport_config)?; + let conn = connecting.await?; + Ok(ConnectionResource(conn, RefCell::new(None))) +} + +#[op2] +#[cppgc] +pub(crate) fn op_quic_incoming_accept_0rtt( + #[cppgc] incoming_resource: &IncomingResource, + #[serde] transport_config: Option, +) -> Result { + let connecting = quic_incoming_accept(incoming_resource, transport_config)?; + match connecting.into_0rtt() { + Ok((conn, zrtt_accepted)) => { + Ok(ConnectionResource(conn, RefCell::new(Some(zrtt_accepted)))) } - None => incoming.accept()?.await?, - }; - Ok(ConnectionResource(conn)) + Err(_connecting) => { + unreachable!("0.5-RTT always succeeds"); + } + } } #[op2] #[serde] pub(crate) fn op_quic_incoming_refuse( #[cppgc] incoming: &IncomingResource, -) -> Result<(), AnyError> { +) -> Result<(), QuicError> { let Some(incoming) = incoming.0.borrow_mut().take() else { - return Err(bad_resource("QuicIncoming already used")); + return Err(QuicError::BadResource("QuicIncoming")); }; incoming.refuse(); Ok(()) @@ -311,43 +437,47 @@ pub(crate) fn op_quic_incoming_refuse( #[serde] pub(crate) fn op_quic_incoming_ignore( #[cppgc] incoming: &IncomingResource, -) -> Result<(), AnyError> { +) -> Result<(), QuicError> { let Some(incoming) = incoming.0.borrow_mut().take() else { - return Err(bad_resource("QuicIncoming already used")); + return Err(QuicError::BadResource("QuicIncoming")); }; incoming.ignore(); Ok(()) } +struct ConnectingResource(RefCell>); + +impl GarbageCollected for ConnectingResource {} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct ConnectArgs { + addr: Addr, ca_certs: Option>, alpn_protocols: Option>, server_name: Option, } -#[op2(async)] +#[op2] #[cppgc] -pub(crate) async fn op_quic_connect( +pub(crate) fn op_quic_endpoint_connect( state: Rc>, - #[serde] addr: Addr, + #[cppgc] endpoint: &EndpointResource, #[serde] args: ConnectArgs, #[serde] transport_config: TransportConfig, #[cppgc] key_pair: &TlsKeysHolder, -) -> Result +) -> Result where NP: NetPermissions + 'static, { - state - .borrow_mut() - .borrow_mut::() - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectQuic()")?; + state.borrow_mut().borrow_mut::().check_net( + &(&args.addr.hostname, Some(args.addr.port)), + "Deno.connectQuic()", + )?; - let sock_addr = resolve_addr(&addr.hostname, addr.port) - .await? + let sock_addr = resolve_addr_sync(&args.addr.hostname, args.addr.port)? .next() - .ok_or_else(|| generic_error("No resolved address found"))?; + .ok_or_else(|| QuicError::UnableToResolve)?; let root_cert_store = state .borrow() @@ -379,24 +509,50 @@ where alpn_protocols.into_iter().map(|s| s.into_bytes()).collect(); } - let client_config = QuicClientConfig::try_from(tls_config)?; + tls_config.enable_early_data = true; + tls_config.resumption = Resumption::store(endpoint.session_store.clone()); + + let client_config = + QuicClientConfig::try_from(tls_config).expect("TLS13 supported"); let mut client_config = quinn::ClientConfig::new(Arc::new(client_config)); client_config.transport_config(Arc::new(transport_config.try_into()?)); - let local_addr = match sock_addr.ip() { - IpAddr::V4(_) => IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), - IpAddr::V6(_) => IpAddr::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), + let connecting = endpoint.endpoint.connect_with( + client_config, + sock_addr, + &args.server_name.unwrap_or(args.addr.hostname), + )?; + + Ok(ConnectingResource(RefCell::new(Some(connecting)))) +} + +#[op2(async)] +#[cppgc] +pub(crate) async fn op_quic_connecting_1rtt( + #[cppgc] connecting: &ConnectingResource, +) -> Result { + let Some(connecting) = connecting.0.borrow_mut().take() else { + return Err(QuicError::BadResource("QuicConnecting")); }; + let conn = connecting.await?; + Ok(ConnectionResource(conn, RefCell::new(None))) +} - let conn = quinn::Endpoint::client((local_addr, 0).into())? - .connect_with( - client_config, - sock_addr, - &args.server_name.unwrap_or(addr.hostname), - )? - .await?; - - Ok(ConnectionResource(conn)) +#[op2] +#[cppgc] +pub(crate) fn op_quic_connecting_0rtt( + #[cppgc] connecting_res: &ConnectingResource, +) -> Option { + let connecting = connecting_res.0.borrow_mut().take()?; + match connecting.into_0rtt() { + Ok((conn, zrtt_accepted)) => { + Some(ConnectionResource(conn, RefCell::new(Some(zrtt_accepted)))) + } + Err(connecting) => { + *connecting_res.0.borrow_mut() = Some(connecting); + None + } + } } #[op2] @@ -412,11 +568,23 @@ pub(crate) fn op_quic_connection_get_protocol( .map(|p| String::from_utf8_lossy(&p).into_owned()) } +#[op2] +#[string] +pub(crate) fn op_quic_connection_get_server_name( + #[cppgc] connection: &ConnectionResource, +) -> Option { + connection + .0 + .handshake_data() + .and_then(|h| h.downcast::().ok()) + .and_then(|h| h.server_name) +} + #[op2] #[serde] pub(crate) fn op_quic_connection_get_remote_addr( #[cppgc] connection: &ConnectionResource, -) -> Result { +) -> Result { let addr = connection.0.remote_address(); Ok(Addr { hostname: format!("{}", addr.ip()), @@ -425,11 +593,11 @@ pub(crate) fn op_quic_connection_get_remote_addr( } #[op2(fast)] -pub(crate) fn op_quic_close_connection( +pub(crate) fn op_quic_connection_close( #[cppgc] connection: &ConnectionResource, #[bigint] close_code: u64, #[string] reason: String, -) -> Result<(), AnyError> { +) -> Result<(), QuicError> { connection .0 .close(quinn::VarInt::from_u64(close_code)?, reason.as_bytes()); @@ -440,7 +608,7 @@ pub(crate) fn op_quic_close_connection( #[serde] pub(crate) async fn op_quic_connection_closed( #[cppgc] connection: &ConnectionResource, -) -> Result { +) -> Result { let e = connection.0.closed().await; match e { quinn::ConnectionError::LocallyClosed => Ok(CloseInfo { @@ -455,11 +623,29 @@ pub(crate) async fn op_quic_connection_closed( } } -struct SendStreamResource(AsyncRefCell); +#[op2(async)] +pub(crate) async fn op_quic_connection_handshake( + #[cppgc] connection: &ConnectionResource, +) { + let Some(zrtt_accepted) = connection.1.borrow_mut().take() else { + return; + }; + zrtt_accepted.await; +} + +struct SendStreamResource { + stream: AsyncRefCell, + stream_id: quinn::StreamId, + priority: AtomicI32, +} impl SendStreamResource { fn new(stream: quinn::SendStream) -> Self { - Self(AsyncRefCell::new(stream)) + Self { + stream_id: stream.id(), + priority: AtomicI32::new(stream.priority().unwrap_or(0)), + stream: AsyncRefCell::new(stream), + } } } @@ -470,18 +656,28 @@ impl Resource for SendStreamResource { fn write(self: Rc, view: BufView) -> AsyncResult { Box::pin(async move { - let mut r = RcRef::map(self, |r| &r.0).borrow_mut().await; - let nwritten = r.write(&view).await?; + let mut stream = + RcRef::map(self.clone(), |r| &r.stream).borrow_mut().await; + stream.set_priority(self.priority.load(Ordering::Relaxed))?; + let nwritten = stream.write(&view).await?; Ok(WriteOutcome::Partial { nwritten, view }) }) } + + fn close(self: Rc) {} } -struct RecvStreamResource(AsyncRefCell); +struct RecvStreamResource { + stream: AsyncRefCell, + stream_id: quinn::StreamId, +} impl RecvStreamResource { fn new(stream: quinn::RecvStream) -> Self { - Self(AsyncRefCell::new(stream)) + Self { + stream_id: stream.id(), + stream: AsyncRefCell::new(stream), + } } } @@ -492,21 +688,40 @@ impl Resource for RecvStreamResource { fn read(self: Rc, limit: usize) -> AsyncResult { Box::pin(async move { - let mut r = RcRef::map(self, |r| &r.0).borrow_mut().await; + let mut r = RcRef::map(self, |r| &r.stream).borrow_mut().await; let mut data = vec![0; limit]; let nread = r.read(&mut data).await?.unwrap_or(0); data.truncate(nread); Ok(BufView::from(data)) }) } + + fn read_byob( + self: Rc, + mut buf: BufMutView, + ) -> AsyncResult<(usize, BufMutView)> { + Box::pin(async move { + let mut r = RcRef::map(self, |r| &r.stream).borrow_mut().await; + let nread = r.read(&mut buf).await?.unwrap_or(0); + Ok((nread, buf)) + }) + } + + fn shutdown(self: Rc) -> AsyncResult<()> { + Box::pin(async move { + let mut r = RcRef::map(self, |r| &r.stream).borrow_mut().await; + r.stop(quinn::VarInt::from(0u32))?; + Ok(()) + }) + } } #[op2(async)] #[serde] -pub(crate) async fn op_quic_accept_bi( +pub(crate) async fn op_quic_connection_accept_bi( #[cppgc] connection: &ConnectionResource, state: Rc>, -) -> Result<(ResourceId, ResourceId), AnyError> { +) -> Result<(ResourceId, ResourceId), QuicError> { match connection.0.accept_bi().await { Ok((tx, rx)) => { let mut state = state.borrow_mut(); @@ -517,7 +732,7 @@ pub(crate) async fn op_quic_accept_bi( Err(e) => match e { quinn::ConnectionError::LocallyClosed | quinn::ConnectionError::ApplicationClosed(..) => { - Err(bad_resource("QuicConn is closed")) + Err(QuicError::BadResource("QuicConnection")) } _ => Err(e.into()), }, @@ -526,11 +741,11 @@ pub(crate) async fn op_quic_accept_bi( #[op2(async)] #[serde] -pub(crate) async fn op_quic_open_bi( +pub(crate) async fn op_quic_connection_open_bi( #[cppgc] connection: &ConnectionResource, state: Rc>, wait_for_available: bool, -) -> Result<(ResourceId, ResourceId), AnyError> { +) -> Result<(ResourceId, ResourceId), QuicError> { let (tx, rx) = if wait_for_available { connection.0.open_bi().await? } else { @@ -539,7 +754,7 @@ pub(crate) async fn op_quic_open_bi( match pin!(connection.0.open_bi()).poll(&mut cx) { Poll::Ready(r) => r?, Poll::Pending => { - return Err(generic_error("Connection has reached the maximum number of outgoing concurrent bidirectional streams")); + return Err(QuicError::MaxStreams("bidirectional")); } } }; @@ -551,10 +766,10 @@ pub(crate) async fn op_quic_open_bi( #[op2(async)] #[serde] -pub(crate) async fn op_quic_accept_uni( +pub(crate) async fn op_quic_connection_accept_uni( #[cppgc] connection: &ConnectionResource, state: Rc>, -) -> Result { +) -> Result { match connection.0.accept_uni().await { Ok(rx) => { let rid = state @@ -566,7 +781,7 @@ pub(crate) async fn op_quic_accept_uni( Err(e) => match e { quinn::ConnectionError::LocallyClosed | quinn::ConnectionError::ApplicationClosed(..) => { - Err(bad_resource("QuicConn is closed")) + Err(QuicError::BadResource("QuicConnection")) } _ => Err(e.into()), }, @@ -575,11 +790,11 @@ pub(crate) async fn op_quic_accept_uni( #[op2(async)] #[serde] -pub(crate) async fn op_quic_open_uni( +pub(crate) async fn op_quic_connection_open_uni( #[cppgc] connection: &ConnectionResource, state: Rc>, wait_for_available: bool, -) -> Result { +) -> Result { let tx = if wait_for_available { connection.0.open_uni().await? } else { @@ -588,7 +803,7 @@ pub(crate) async fn op_quic_open_uni( match pin!(connection.0.open_uni()).poll(&mut cx) { Poll::Ready(r) => r?, Poll::Pending => { - return Err(generic_error("Connection has reached the maximum number of outgoing concurrent unidirectional streams")); + return Err(QuicError::MaxStreams("unidirectional")); } } }; @@ -600,63 +815,80 @@ pub(crate) async fn op_quic_open_uni( } #[op2(async)] -pub(crate) async fn op_quic_send_datagram( +pub(crate) async fn op_quic_connection_send_datagram( #[cppgc] connection: &ConnectionResource, #[buffer] buf: JsBuffer, -) -> Result<(), AnyError> { +) -> Result<(), QuicError> { connection.0.send_datagram_wait(buf.to_vec().into()).await?; Ok(()) } #[op2(async)] -pub(crate) async fn op_quic_read_datagram( +#[buffer] +pub(crate) async fn op_quic_connection_read_datagram( #[cppgc] connection: &ConnectionResource, - #[buffer] mut buf: JsBuffer, -) -> Result { +) -> Result, QuicError> { let data = connection.0.read_datagram().await?; - buf[0..data.len()].copy_from_slice(&data); - Ok(data.len() as _) + Ok(data.into()) } #[op2(fast)] -pub(crate) fn op_quic_max_datagram_size( +pub(crate) fn op_quic_connection_get_max_datagram_size( #[cppgc] connection: &ConnectionResource, -) -> Result { +) -> Result { Ok(connection.0.max_datagram_size().unwrap_or(0) as _) } #[op2(fast)] -pub(crate) fn op_quic_get_send_stream_priority( +pub(crate) fn op_quic_send_stream_get_priority( state: Rc>, #[smi] rid: ResourceId, -) -> Result { +) -> Result { let resource = state .borrow() .resource_table .get::(rid)?; - let r = RcRef::map(resource, |r| &r.0).try_borrow(); - match r { - Some(s) => Ok(s.priority()?), - None => Err(generic_error("Unable to get priority")), - } + Ok(resource.priority.load(Ordering::Relaxed)) } #[op2(fast)] -pub(crate) fn op_quic_set_send_stream_priority( +pub(crate) fn op_quic_send_stream_set_priority( state: Rc>, #[smi] rid: ResourceId, priority: i32, -) -> Result<(), AnyError> { +) -> Result<(), QuicError> { let resource = state .borrow() .resource_table .get::(rid)?; - let r = RcRef::map(resource, |r| &r.0).try_borrow(); - match r { - Some(s) => { - s.set_priority(priority)?; - Ok(()) - } - None => Err(generic_error("Unable to set priority")), - } + resource.priority.store(priority, Ordering::Relaxed); + Ok(()) +} + +#[op2(fast)] +#[bigint] +pub(crate) fn op_quic_send_stream_get_id( + state: Rc>, + #[smi] rid: ResourceId, +) -> Result { + let resource = state + .borrow() + .resource_table + .get::(rid)?; + let stream_id = quinn::VarInt::from(resource.stream_id).into_inner(); + Ok(stream_id) +} + +#[op2(fast)] +#[bigint] +pub(crate) fn op_quic_recv_stream_get_id( + state: Rc>, + #[smi] rid: ResourceId, +) -> Result { + let resource = state + .borrow() + .resource_table + .get::(rid)?; + let stream_id = quinn::VarInt::from(resource.stream_id).into_inner(); + Ok(stream_id) } diff --git a/runtime/errors.rs b/runtime/errors.rs index 7b61395866..feb4942967 100644 --- a/runtime/errors.rs +++ b/runtime/errors.rs @@ -47,6 +47,7 @@ use deno_kv::KvErrorKind; use deno_kv::KvMutationError; use deno_napi::NApiError; use deno_net::ops::NetError; +use deno_net::QuicError; use deno_permissions::ChildPermissionError; use deno_permissions::NetDescriptorFromUrlParseError; use deno_permissions::PathResolveError; @@ -1589,6 +1590,27 @@ fn get_sync_fetch_error(error: &SyncFetchError) -> &'static str { } } +fn get_quic_error_class(error: &QuicError) -> &'static str { + match error { + QuicError::CannotListen => "Error", + QuicError::MissingTlsKey => "TypeError", + QuicError::InvalidDuration => "TypeError", + QuicError::UnableToResolve => "Error", + QuicError::StdIo(e) => get_io_error_class(e), + QuicError::PermissionCheck(e) => get_permission_check_error_class(e), + QuicError::VarIntBoundsExceeded(_) => "RangeError", + QuicError::Rustls(_) => "Error", + QuicError::Tls(e) => get_tls_error_class(e), + QuicError::ConnectionError(_) => "Error", + QuicError::ConnectError(_) => "Error", + QuicError::SendDatagramError(_) => "Error", + QuicError::ClosedStream(_) => "BadResource", + QuicError::BadResource(_) => "BadResource", + QuicError::MaxStreams(_) => "RangeError", + QuicError::Core(e) => get_error_class_name(e).unwrap_or("Error"), + } +} + pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { deno_core::error::get_custom_error_class(e) .or_else(|| { @@ -1824,6 +1846,7 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { e.downcast_ref::() .map(get_net_map_error) }) + .or_else(|| e.downcast_ref::().map(get_quic_error_class)) .or_else(|| { e.downcast_ref::() .map(get_broadcast_channel_error) diff --git a/runtime/fmt_errors.rs b/runtime/fmt_errors.rs index 1a2bc5dfd3..6aa4765829 100644 --- a/runtime/fmt_errors.rs +++ b/runtime/fmt_errors.rs @@ -423,7 +423,7 @@ fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec { "Run again with `--unstable-webgpu` flag to enable this API.", ), ]; - } else if msg.contains("listenQuic is not a function") { + } else if msg.contains("QuicEndpoint is not a constructor") { return vec![ FixSuggestion::info("listenQuic is an unstable API."), FixSuggestion::hint( diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index e13a8d1307..8127ac9618 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -1,6 +1,6 @@ // Copyright 2018-2025 the Deno authors. MIT license. -import { core } from "ext:core/mod.js"; +import { core, primordials } from "ext:core/mod.js"; import { op_net_listen_udp, op_net_listen_unixpacket, @@ -13,7 +13,6 @@ import * as console from "ext:deno_console/01_console.js"; import * as ffi from "ext:deno_ffi/00_ffi.js"; import * as net from "ext:deno_net/01_net.js"; import * as tls from "ext:deno_net/02_tls.js"; -import * as quic from "ext:deno_net/03_quic.js"; import * as serve from "ext:deno_http/00_serve.ts"; import * as http from "ext:deno_http/01_http.js"; import * as websocket from "ext:deno_http/02_websocket.ts"; @@ -32,6 +31,10 @@ import * as cron from "ext:deno_cron/01_cron.ts"; import * as webgpuSurface from "ext:deno_webgpu/02_surface.js"; import * as telemetry from "ext:deno_telemetry/telemetry.ts"; +const { ObjectDefineProperties } = primordials; + +const loadQuic = core.createLazyLoader("ext:deno_net/03_quic.js"); + const denoNs = { Process: process.Process, run: process.run, @@ -175,17 +178,28 @@ denoNsUnstableById[unstableIds.net] = { op_net_listen_udp, op_net_listen_unixpacket, ), - - connectQuic: quic.connectQuic, - listenQuic: quic.listenQuic, - QuicBidirectionalStream: quic.QuicBidirectionalStream, - QuicConn: quic.QuicConn, - QuicListener: quic.QuicListener, - QuicReceiveStream: quic.QuicReceiveStream, - QuicSendStream: quic.QuicSendStream, - QuicIncoming: quic.QuicIncoming, }; +ObjectDefineProperties(denoNsUnstableById[unstableIds.net], { + connectQuic: core.propWritableLazyLoaded((q) => q.connectQuic, loadQuic), + QuicEndpoint: core.propWritableLazyLoaded((q) => q.QuicEndpoint, loadQuic), + QuicBidirectionalStream: core.propWritableLazyLoaded( + (q) => q.QuicBidirectionalStream, + loadQuic, + ), + QuicConn: core.propWritableLazyLoaded((q) => q.QuicConn, loadQuic), + QuicListener: core.propWritableLazyLoaded((q) => q.QuicListener, loadQuic), + QuicReceiveStream: core.propWritableLazyLoaded( + (q) => q.QuicReceiveStream, + loadQuic, + ), + QuicSendStream: core.propWritableLazyLoaded( + (q) => q.QuicSendStream, + loadQuic, + ), + QuicIncoming: core.propWritableLazyLoaded((q) => q.QuicIncoming, loadQuic), +}); + // denoNsUnstableById[unstableIds.unsafeProto] = { __proto__: null } denoNsUnstableById[unstableIds.webgpu] = { diff --git a/tests/unit/quic_test.ts b/tests/unit/quic_test.ts index 63163e15d6..f87a38cecc 100644 --- a/tests/unit/quic_test.ts +++ b/tests/unit/quic_test.ts @@ -1,28 +1,32 @@ // Copyright 2018-2025 the Deno authors. MIT license. -import { assertEquals } from "./test_util.ts"; +import { assert, assertEquals } from "./test_util.ts"; const cert = Deno.readTextFileSync("tests/testdata/tls/localhost.crt"); const key = Deno.readTextFileSync("tests/testdata/tls/localhost.key"); const caCerts = [Deno.readTextFileSync("tests/testdata/tls/RootCA.pem")]; -async function pair(opt?: Deno.QuicTransportOptions): Promise< - [Deno.QuicConn, Deno.QuicConn, Deno.QuicListener] -> { - const listener = await Deno.listenQuic({ - hostname: "localhost", - port: 0, +interface Pair { + server: Deno.QuicConn; + client: Deno.QuicConn; + endpoint: Deno.QuicEndpoint; +} + +async function pair(opt?: Deno.QuicTransportOptions): Promise { + const endpoint = new Deno.QuicEndpoint({ hostname: "localhost" }); + const listener = endpoint.listen({ cert, key, alpnProtocols: ["deno-test"], ...opt, }); + assertEquals(endpoint, listener.endpoint); const [server, client] = await Promise.all([ listener.accept(), Deno.connectQuic({ hostname: "localhost", - port: listener.addr.port, + port: endpoint.addr.port, caCerts, alpnProtocols: ["deno-test"], ...opt, @@ -31,13 +35,14 @@ async function pair(opt?: Deno.QuicTransportOptions): Promise< assertEquals(server.protocol, "deno-test"); assertEquals(client.protocol, "deno-test"); - assertEquals(client.remoteAddr, listener.addr); + assertEquals(client.remoteAddr, endpoint.addr); + assertEquals(server.serverName, "localhost"); - return [server, client, listener]; + return { server, client, endpoint }; } Deno.test("bidirectional stream", async () => { - const [server, client, listener] = await pair(); + const { server, client, endpoint } = await pair(); const encoded = (new TextEncoder()).encode("hi!"); @@ -57,12 +62,12 @@ Deno.test("bidirectional stream", async () => { assertEquals(data, encoded); } - listener.close({ closeCode: 0, reason: "" }); - client.close({ closeCode: 0, reason: "" }); + client.close(); + endpoint.close(); }); Deno.test("unidirectional stream", async () => { - const [server, client, listener] = await pair(); + const { server, client, endpoint } = await pair(); const encoded = (new TextEncoder()).encode("hi!"); @@ -82,12 +87,12 @@ Deno.test("unidirectional stream", async () => { assertEquals(data, encoded); } - listener.close({ closeCode: 0, reason: "" }); - client.close({ closeCode: 0, reason: "" }); + endpoint.close(); + client.close(); }); Deno.test("datagrams", async () => { - const [server, client, listener] = await pair(); + const { server, client, endpoint } = await pair(); const encoded = (new TextEncoder()).encode("hi!"); @@ -96,22 +101,20 @@ Deno.test("datagrams", async () => { const data = await client.readDatagram(); assertEquals(data, encoded); - listener.close({ closeCode: 0, reason: "" }); - client.close({ closeCode: 0, reason: "" }); + endpoint.close(); + client.close(); }); Deno.test("closing", async () => { - const [server, client, listener] = await pair(); + const { server, client } = await pair(); server.close({ closeCode: 42, reason: "hi!" }); assertEquals(await client.closed, { closeCode: 42, reason: "hi!" }); - - listener.close({ closeCode: 0, reason: "" }); }); Deno.test("max concurrent streams", async () => { - const [server, client, listener] = await pair({ + const { server, client, endpoint } = await pair({ maxConcurrentBidirectionalStreams: 1, maxConcurrentUnidirectionalStreams: 1, }); @@ -136,15 +139,13 @@ Deno.test("max concurrent streams", async () => { }); } - listener.close({ closeCode: 0, reason: "" }); - server.close({ closeCode: 0, reason: "" }); - client.close({ closeCode: 0, reason: "" }); + endpoint.close(); + client.close(); }); Deno.test("incoming", async () => { - const listener = await Deno.listenQuic({ - hostname: "localhost", - port: 0, + const endpoint = new Deno.QuicEndpoint({ hostname: "localhost" }); + const listener = endpoint.listen({ cert, key, alpnProtocols: ["deno-test"], @@ -153,7 +154,7 @@ Deno.test("incoming", async () => { const connect = () => Deno.connectQuic({ hostname: "localhost", - port: listener.addr.port, + port: endpoint.addr.port, caCerts, alpnProtocols: ["deno-test"], }); @@ -165,8 +166,63 @@ Deno.test("incoming", async () => { assertEquals(server.protocol, "deno-test"); assertEquals(client.protocol, "deno-test"); - assertEquals(client.remoteAddr, listener.addr); + assertEquals(client.remoteAddr, endpoint.addr); - listener.close({ closeCode: 0, reason: "" }); - client.close({ closeCode: 0, reason: "" }); + endpoint.close(); + client.close(); +}); + +Deno.test("0rtt", async () => { + const sEndpoint = new Deno.QuicEndpoint({ hostname: "localhost" }); + const listener = sEndpoint.listen({ + cert, + key, + alpnProtocols: ["deno-test"], + }); + + (async () => { + while (true) { + let incoming; + try { + incoming = await listener.incoming(); + } catch (e) { + if (e instanceof Deno.errors.BadResource) { + break; + } + throw e; + } + const conn = incoming.accept({ zeroRtt: true }); + conn.handshake.then(() => { + conn.close(); + }); + } + })(); + + const endpoint = new Deno.QuicEndpoint(); + + const c1 = await Deno.connectQuic({ + hostname: "localhost", + port: sEndpoint.addr.port, + caCerts, + alpnProtocols: ["deno-test"], + endpoint, + }); + + await c1.closed; + + const c2 = Deno.connectQuic({ + hostname: "localhost", + port: sEndpoint.addr.port, + caCerts, + alpnProtocols: ["deno-test"], + zeroRtt: true, + endpoint, + }); + + assert(!(c2 instanceof Promise), "0rtt should be accepted"); + + await c2.closed; + + sEndpoint.close(); + endpoint.close(); }); From f4839966585b610848bdda676561ac96f2c85062 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Mon, 6 Jan 2025 17:00:32 +0100 Subject: [PATCH 067/107] feat(unstable): no config npm:@opentelemetry/api integration (#27541) After this PR, one does not need to import `jsr:@deno/otel` anymore. --- cli/tsc/dts/lib.deno.unstable.d.ts | 11 +++++------ ext/telemetry/telemetry.ts | 18 ++++++++++++++++++ tests/registry/jsr/@deno/otel/0.0.2/deno.json | 14 -------------- .../registry/jsr/@deno/otel/0.0.2/src/index.ts | 15 --------------- .../jsr/@deno/otel/0.0.2/src/register.ts | 5 ----- tests/registry/jsr/@deno/otel/0.0.2_meta.json | 6 ------ tests/registry/jsr/@deno/otel/meta.json | 8 -------- .../@opentelemetry/sdk-metrics/registry.json | 1 - .../sdk-trace-base/registry.json | 1 - tests/specs/cli/otel_basic/basic.ts | 1 - 10 files changed, 23 insertions(+), 57 deletions(-) delete mode 100644 tests/registry/jsr/@deno/otel/0.0.2/deno.json delete mode 100644 tests/registry/jsr/@deno/otel/0.0.2/src/index.ts delete mode 100644 tests/registry/jsr/@deno/otel/0.0.2/src/register.ts delete mode 100644 tests/registry/jsr/@deno/otel/0.0.2_meta.json delete mode 100644 tests/registry/jsr/@deno/otel/meta.json delete mode 100644 tests/registry/npm/@opentelemetry/sdk-metrics/registry.json delete mode 100644 tests/registry/npm/@opentelemetry/sdk-trace-base/registry.json diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index 3de9845fc8..bd32845a6a 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -1268,16 +1268,15 @@ declare namespace Deno { * OpenTelemetry API. This is done using the official OpenTelemetry package * for JavaScript: * [`npm:@opentelemetry/api`](https://opentelemetry.io/docs/languages/js/). - * Deno integrates with this package to provide trace context propagation - * between native Deno APIs (like `Deno.serve` or `fetch`) and custom user - * code. Deno also provides APIs that allow exporting custom telemetry data - * via the same OTLP channel used by the Deno runtime. This is done using the - * [`jsr:@deno/otel`](https://jsr.io/@deno/otel) package. + * Deno integrates with this package to provide tracing, metrics, and trace + * context propagation between native Deno APIs (like `Deno.serve` or `fetch`) + * and custom user code. Deno automatically registers the providers with the + * OpenTelemetry API, so users can start creating custom traces, metrics, and + * logs without any additional setup. * * @example Using OpenTelemetry API to create custom traces * ```ts,ignore * import { trace } from "npm:@opentelemetry/api@1"; - * import "jsr:@deno/otel@0.0.2/register"; * * const tracer = trace.getTracer("example-tracer"); * diff --git a/ext/telemetry/telemetry.ts b/ext/telemetry/telemetry.ts index f4277c3722..bea16f49d4 100644 --- a/ext/telemetry/telemetry.ts +++ b/ext/telemetry/telemetry.ts @@ -1075,6 +1075,11 @@ export function builtinTracer(): Tracer { return builtinTracerCache; } +// We specify a very high version number, to allow any `@opentelemetry/api` +// version to load this module. This does cause @opentelemetry/api to not be +// able to register anything itself with the global registration methods. +const OTEL_API_COMPAT_VERSION = "1.999.999"; + export function bootstrap( config: [ 0 | 1, @@ -1102,6 +1107,19 @@ export function bootstrap( default: break; } + + if (TRACING_ENABLED || METRICS_ENABLED) { + const otel = globalThis[SymbolFor("opentelemetry.js.api.1")] ??= { + version: OTEL_API_COMPAT_VERSION, + }; + if (TRACING_ENABLED) { + otel.trace = TracerProvider; + otel.context = ContextManager; + } + if (METRICS_ENABLED) { + otel.metrics = MeterProvider; + } + } } export const telemetry = { diff --git a/tests/registry/jsr/@deno/otel/0.0.2/deno.json b/tests/registry/jsr/@deno/otel/0.0.2/deno.json deleted file mode 100644 index cfa44a7d07..0000000000 --- a/tests/registry/jsr/@deno/otel/0.0.2/deno.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "@deno/otel", - "version": "0.0.2", - "exports": { - ".": "./src/index.ts", - "./register": "./src/register.ts" - }, - "tasks": { - "check:license": "deno run -A tools/check_license.ts", - "check:docs": "deno doc --lint src/index.ts", - "check": "deno task check:license --check", - "ok": "deno fmt --check && deno lint && deno task check" - } -} diff --git a/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts b/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts deleted file mode 100644 index da72260d42..0000000000 --- a/tests/registry/jsr/@deno/otel/0.0.2/src/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2024-2024 the Deno authors. All rights reserved. MIT license. - -import { context, trace, metrics } from "npm:@opentelemetry/api@1"; - -// @ts-ignore Deno.telemetry is not typed yet -const telemetry = Deno.telemetry ?? Deno.tracing; - -/** - * Register `Deno.telemetry` with the OpenTelemetry library. - */ -export function register() { - context.setGlobalContextManager(telemetry.contextManager); - trace.setGlobalTracerProvider(telemetry.tracerProvider); - metrics.setGlobalMeterProvider(telemetry.meterProvider); -} diff --git a/tests/registry/jsr/@deno/otel/0.0.2/src/register.ts b/tests/registry/jsr/@deno/otel/0.0.2/src/register.ts deleted file mode 100644 index 5443707076..0000000000 --- a/tests/registry/jsr/@deno/otel/0.0.2/src/register.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2024 the Deno authors. All rights reserved. MIT license. - -import { register } from "./index.ts"; - -register(); diff --git a/tests/registry/jsr/@deno/otel/0.0.2_meta.json b/tests/registry/jsr/@deno/otel/0.0.2_meta.json deleted file mode 100644 index 79c28d61d1..0000000000 --- a/tests/registry/jsr/@deno/otel/0.0.2_meta.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "exports": { - ".": "./src/index.ts", - "./register": "./src/register.ts" - } -} \ No newline at end of file diff --git a/tests/registry/jsr/@deno/otel/meta.json b/tests/registry/jsr/@deno/otel/meta.json deleted file mode 100644 index 1cb49741a1..0000000000 --- a/tests/registry/jsr/@deno/otel/meta.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "scope": "deno", - "name": "otel", - "latest": "0.0.2", - "versions": { - "0.0.2": {} - } -} \ No newline at end of file diff --git a/tests/registry/npm/@opentelemetry/sdk-metrics/registry.json b/tests/registry/npm/@opentelemetry/sdk-metrics/registry.json deleted file mode 100644 index 1e55892f95..0000000000 --- a/tests/registry/npm/@opentelemetry/sdk-metrics/registry.json +++ /dev/null @@ -1 +0,0 @@ -{"_id":"@opentelemetry/sdk-metrics","_rev":"32-fd2f541de5aecbe413589147b6cc22fc","name":"@opentelemetry/sdk-metrics","dist-tags":{"next":"1.8.0","latest":"1.28.0"},"versions":{"0.32.0":{"name":"@opentelemetry/sdk-metrics","version":"0.32.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@0.32.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"463cd3a2b267f044db9aaab85887a171710345a0","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-0.32.0.tgz","fileCount":480,"integrity":"sha512-zC9RCOIsXRqOHWmWfcxArtDHbip2/jaIH1yu/OKau/shDZYFluAxY6zAEYIb4YEAzKKEF+fpaoRgpodDWNGVGA==","signatures":[{"sig":"MEUCIHBrzbDjk66vVi3mOQOqt4aIRr90QWqLXe7z6pqSOS6GAiEAxRW5Nt/5Uo5aJhfbXMPZUZSd1f5UYsLkrC5WVFl9u0o=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1364783,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjBmODACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqU5Q//X8u5PAiNpAJrFIXflu/338sR9oMsluNoiuoPba0zw6ikiPUv\r\nXCYlQMw1XBSE63X2CVpSDakAhG4WtkWXbsi2zRCfOLUze5CuQPf+TqikvwM/\r\nFOGeHl67KjYw20u1K0H0kxB1qAsBQNBV0lWIM8aF6Yty2J05kP7dACmT6o4v\r\nUn6n2+fjQFMlhRA0WeCrEk+6usmFQDBaZSieZofKGm9C3KCeasPZRfnAJZq3\r\nQvbINhGXDOfFdXrwQPNRqZEIDI5/9frr9dJuRsL+h59AEBizdkezofZGvJPr\r\n860MZqidZhyrzC8NBpodJK0sGxQutbssmSKDT2dptVEd9jjj7mj7iKhNPEFy\r\ntUQDgD/4ltCoLyqraDQi+twaE9gFDhol6EwwdRTIMV8pvazMXFuQ1ik6lsC8\r\nPMt4UDYjtOwDkOGqOjtK/AebAOkJWhoEOuz3znl4VAowL5+qPblVRUEOm7HY\r\nKM/I5DEJltVpWJymDlvRNyvN/ysah+p3b6QtAz5ZcXlipFBh/qFozY7GpemB\r\nIHNryYjSZiAvRCsbnWgHsNg2dIDHJQvN440q6cWfX49Hs03r1rYuKlJjkBpy\r\nFFxrzMAoUejzJ/4j9lVc0rfacQrIoUtQ+j4uLJVPCqy4BPk1LMTnyWY16zb4\r\nIvRTJJH4pe1bz0ucrcj7GawkrVRaoxvNO2U=\r\n=wJDK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"a5abee69119cc41d9d34f6beb5c1826eef1ac0dd","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../../","compile":"tsc --build tsconfig.all.json","version":"node ../../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"Work in progress OpenTelemetry metrics SDK","directories":{},"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.6.0","@opentelemetry/resources":"1.6.0","@opentelemetry/api-metrics":"0.32.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"9.1.1","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_0.32.0_1661363075429_0.18961817621798693","host":"s3://npm-registry-packages"}},"0.33.0":{"name":"@opentelemetry/sdk-metrics","version":"0.33.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@0.33.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"c4e51decc6e3bb0e1e97c7b081955d357e46c2fe","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-0.33.0.tgz","fileCount":489,"integrity":"sha512-ZXPixOlTd/FHLwpkmm5nTpJE7bZOPfmbSz8hBVFCEHkXE1aKEKaM38UFnZ+2xzOY1tDsDwyxEiiBiDX8y3039A==","signatures":[{"sig":"MEUCIBeHiozEczRdIpEbB1UwgCro3jj+tB2iGk+FQ+CZuc3LAiEA97t2teODixmkZuRZk4z+IPqaULq8SBNRrV8c3JVlTys=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1425603,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjJGjCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrNERAAjSfh3/c433lR4GMa9u1DjMIVy7MYOZe0YftxnQ9VPGs2jgfa\r\nWC4r/3Qbantsq49xoVj8GjmiZnIrhzm833P8L7lNLes8LDtI8TEbv+6SPfsq\r\nRLBvUdPHww4UlbfShEo5hnGXVIC3qudVRlvGv2DKxFD9AF2QZszGKsrNQ+P8\r\nB5A/lWp4dqY+IhB0xMk3y7asdEggiF/isxMsCGhNFpDiyL8iHQWuW3ylt5pq\r\nZmPLY23ax1kWkTSvj7Us9X7tgIjGuGgzU+cBSf29WI0L2gyVu5TBBRk5NY5x\r\nryXCnzi1MoNdY669ToCEVcCxJoWb4DY9Mg2ihhJq2argdUPzwEBThC1EecAP\r\nJ/786fw+PKmQAzKGVGEXRmDZljF4+3Zk/KIujlHZK7RP6h1kaMCRAKYwnBLT\r\nR9v3W4ljHnbPgV0OmX+W0suV8oEoYtIJxe/7yyznWsLxcndndZq6WFqzcJQN\r\nVdPk6kxtXdSx7hzcnn1CXxckq+fiG0EdZX32ZRGEi1X4EXmTeNL8s2CMiVUl\r\ndGQm+vbP517Nj2oQvOcnO+YSf55XGNtv49cbusZ8JHVGQhHE26HXCK6A2ci1\r\nhEgPOMtjRH43f3kncjdQXW/vTsS/vMiacwyM94686CkEOaJJNnTv8R2DlV2s\r\nkvO54QGc0KDggcsxEqzpfo9OuQEFKWUe0lU=\r\n=fhUv\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"ad88c3d9aa0100fe259b93f4b660e84417b757ac","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../../","compile":"tsc --build tsconfig.all.json","version":"node ../../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"Work in progress OpenTelemetry metrics SDK","directories":{},"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.7.0","@opentelemetry/resources":"1.7.0","@opentelemetry/api-metrics":"0.33.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"9.1.1","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_0.33.0_1663330498049_0.15542963522751552","host":"s3://npm-registry-packages"}},"1.8.0":{"name":"@opentelemetry/sdk-metrics","version":"1.8.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.8.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"d061060f03861ab3f345d0f924922bc1a6396157","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.8.0.tgz","fileCount":489,"integrity":"sha512-+KYb+uj0vHhl8xzJO+oChS4oP1e+/2Wl3SXoHoIdcEjd1TQfDV+lxOm4oqxWq6wykXvI35/JHyejxSoT+qxGmg==","signatures":[{"sig":"MEYCIQDsOqqaWHTqJVYLyeRb+ZNiGkJbd34UCyCqyHX6UgwbCAIhAMvGlF7I5klQng1omsJ/Nk8Nzz0TlqjJqpvJj76kcBV2","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1416962,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjbANeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqikQ/9EWJ03wBF//CwkzW46y4mBkSgukPe+k9sfS4+cd4/+nErVF2O\r\nMTKRhSpoDQcVLBdmTfeaObKRmgYg2p3PeOrGPphcfkEmZjrRv6342ubUWjRW\r\nm39DSPq8NmZp7J5hRf2hLYSg6tXKZnflCmj+T8upYC/KmW7kOKtx/B3tTy7c\r\nq6q1Kc6vBToqid7yNdFLsIYTjVHM8xSvtg0QVx84S6mtzR2iO8uAzL3ucu34\r\nfLXyN2MzbqJIO516Jt1vE0ix9q/xmt7TrbqCi5k5yZ8cIOianx1Yl7MFGOAX\r\nKwaGILDNoBnb7c8mQITWnP286rsO4NMNnBoXt7ZJCmIavKe8XlbxFZAwuM/q\r\nl0bC9nG8l4r+182xF5XKV2/wnRPQ3j8+uQyBx/8+7YGoIVbowgd4GvJEZGRm\r\n83hxM4/xGxmHiWbkngJlyspY7s86o1MTuwPgwns73cgAhNZsuti2SVgtdoFc\r\n4prfRtGRXUtHJkBQMItiPDEG+Mnfq9hBxHh2F1zuvDLzyN93nCs22gBY79jT\r\n33kticfFECGRLhuCIhZIuV+yzBu4ciYXk0fg9hMY6wagCqQ+tPRs4HPqO92f\r\nuVGhapMpKJPclhvOvlbY4d4Ixm4mo5rrnJx0BPkn445hV6JwJbUC3PAU9yPp\r\nJhIpEHNdZQc8ntThHRpYRCSBmecy8YkzSdo=\r\n=haC6\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"7972edf6659fb6e0d5928a5cf7a35f26683e168f","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"Work in progress OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.8.0","@opentelemetry/resources":"1.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.4.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.4.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.8.0_1668023134085_0.198580524230221","host":"s3://npm-registry-packages"}},"1.9.0":{"name":"@opentelemetry/sdk-metrics","version":"1.9.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.9.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"1c3a51abeb7f45ea25b91daf7e05e43d25ddd20a","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.9.0.tgz","fileCount":489,"integrity":"sha512-fSlJWhp86kCan1zuxdH6LTyUBYlohQwDKnxep5qHMnRTNErkYmdjgsmjZvSMdAfUFtQqfZmTXe2Lap7a5AIf9Q==","signatures":[{"sig":"MEUCIGoaNR3oyipKjYbn2fxyiZ4BKdIFiSOmx699LRzKo0vWAiEAyTvGUxH0WbB1bXUpv9AlkibTlQ51uAHPzOa9yFD7L7A=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1432686,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjvy41ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrSjg//czBDsjkHKhMQ+UP70UHKKTG+xRRSxL0WB8vcvSu2U1VFJldP\r\nBiEOUKKfXMNMTlwB6fuo8mdBnwT/Pn6rmbYyW30mkRtc0n9rpakFtdvASSUg\r\nSsLAROHI+NpaNG3lygIpwXu6wLNGzh1ejL8/1/sfRFKBZIGlb/4l9eHtLj8z\r\nNEZ2r0IAOVOQ2JWnMon0gSM1yjplES4pF30tt5j+UeJbNqgbu9CxD8mUOt/E\r\nV4KT3XawHrjMB5VmPlUhZ66OwbF4P7dZF3ukETA/ezK2XBQeUBBNCiJbW6C0\r\np6WdlJZ/1chyseR9hKiBm8bOrx9XQlNcKyM8brOL9G0joCa/YDX12z0lK5xg\r\nlv/As+8IcQUBkcMsBnzl/BVZUnVQ/qacyWXsYpuMmSCfCOCcCAfLXP/kkVDG\r\nNtNpb3RWySMTuqhYBNxQ2wSBmu0TnXxc3y7ubWfqVNh8SJu9kLZQo8fRFyrM\r\nrVATAIjhFPpZAGBeDca3YHuElfFhXdxtklvHX4ATh3yN0DlVqaaWxQXp6W/I\r\n671fbqRQWCy29YlLoS7k4WR8CGkvNFPYedOgIPinE/g3Qv4oTF0z8vcFYI9G\r\nP2PyfHSre1bHm/5rTIiB9xWEK+XUb+9uV/33gGfTkZqz+6oGd4E0pg9q16cy\r\nEZrSABE83nfpxgpbmLMjACClgyQWshGNr4I=\r\n=qh/2\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"08f597f3a3d71a4852b0afbba120af15ca038121","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.9.0","@opentelemetry/resources":"1.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"3.0.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.9.0_1673473589677_0.62979083795101","host":"s3://npm-registry-packages"}},"1.9.1":{"name":"@opentelemetry/sdk-metrics","version":"1.9.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.9.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"babc162a81df9884c16b1e67c2dd26ab478f3080","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.9.1.tgz","fileCount":543,"integrity":"sha512-AyhKDcA8NuV7o1+9KvzRMxNbATJ8AcrutKilJ6hWSo9R5utnzxgffV4y+Hp4mJn84iXxkv+CBb99GOJ2A5OMzA==","signatures":[{"sig":"MEYCIQCYUyFLpMVK/wDHg6lU87nZ3MQB9nQh8JvM/VryTCdksgIhAKZDK2iXOPbTaQ6GOK3qt6u560bXhXh+VmFjYH9uiaHo","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1556782,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj1+KFACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpp3w//cVciehkq5CKoOUjcEovwgleHJd+eCwOEc40/05fiVn6NmDuW\r\nHAs1UekoxG9cc+MHhvp/38o5o1WFK2sFGXO7ymvMluXheGXx7nI6U+qnqkb9\r\nWWvI53gNcfN5GUgJGarHX6kt+DoyV5+0ig0R/2U37hk6DWm89kZArd+X8xEI\r\nD+2GADx/m1uTUO/PJnaJ8LB1LLK2SJ2vQmViYTP8icykDzIJT5flZyBeb+hk\r\nRqgaFcx7Qa+XecZIjTUXfY5ZQCOc5IYf3JAVea/0kAosP63vSaK9TU+p4GG6\r\n4mYBEANc4Inn4ocav0UoYb5kDNntNo/wAusSI4gH/sByMaQhW/EGy9qmKTA+\r\nKpsIPqZO/FPXZEUJZsEUBOD7GxGp8rUB38JEvW3d3ACDFPfVl/AAuIJ4wipt\r\nM4cKsHtewTdDbDGrkO7o9EDG+OACTTBbvAQ/vxWoC8lDZ+vVWbc0pT+taLcs\r\nlimhIk0XDHJGZ+vxL/T7ib0nOCE1qgt6TL5F0Mmx/Juen4Go2NhOa9dbZG1Y\r\nSI6b/SXe5izJQR6vGhyx2mo3OOXrBQmQc/jjcJbwaRRzLshUY/GmZeqrsPLO\r\n91kJNQIUfFRZLhY0tLPlUaYoFjUY+HL4d0RHWKxLoenZ1POPTBwbrfTpwTRD\r\nOTqOB4P/zL3AutUyuX+FOPevSBguIKxq7wA=\r\n=AXfq\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"279458e7ddf16f7ddca5fe60c78672e05fafce66","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.9.1","@opentelemetry/resources":"1.9.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.9.1_1675092613192_0.7361446399809115","host":"s3://npm-registry-packages"}},"1.10.0":{"name":"@opentelemetry/sdk-metrics","version":"1.10.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.10.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"627f164036ad053551b3e75447adf9a902b066aa","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.10.0.tgz","fileCount":543,"integrity":"sha512-vOB3khvj894ppOwafNqKNavpChZYR2S2IZyy8PmL0DwNgFPGYwkWxZkv7YJduBNzcCd+Ao+ug93jdSFFhnpIhQ==","signatures":[{"sig":"MEUCIQDL/qHYi9VPBH3poMg/gvlnmTAxuK7zGkYo7FXRK1XAMgIgPAiYWDmawt1pshH1UtUsHcw9izgXdV8pWQMROlpkKEQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1571910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkD0cTACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpZ/w/9G8OISP56mEsFRcrjWT+zXdW4zgKR3RBzQcZ+RjfqhV2Gq51K\r\nkwhYJ/bIk/xXtSJkpZ+gHjQIy1lwNXU9ux62RcZXzO/EcDLEg1aDDBD+d7G4\r\naYN81H49JVfrstGQzxduGK1vamLm2EBg0PbQGK7r8oNiNkK/Rt+qRY0ZYi6f\r\neYFhc8Nvbh9MKquBU21z/En/IhrxlbUQyetJ0TKfoonVsoj9VK1F7BBf/7cD\r\nYirI9GZzNKfk+y3IPmQ3aalntJmoN+aWWGYFn3hbE2IIo48N1EOkv3ZPXLwm\r\nHIaEN4MbK1vIjbjaS0LHC6lWYDooa5GCvVEXSl1ZJimyIpdx3FV5z0mVgusJ\r\nyb2PuPPm2okM7FM4xg0sDOqajIBbTy253Q8F+dqEBiZah1ELX/HTXV+3TG8l\r\nUJjlP6oE1CQwW43F+o46Pmf9eF0qi3pPEPwFpuX6JQfnWgWrAgR4LAhAh1UA\r\nhbVTVTBixGvNCZFvMh8Irbj3DNz/6pj2h5akehMBbaENpKj8g8Ij/tFIH7ho\r\nVXKGbDTUrnJPAcm4q1lghjiuBOgvJ3mZxacCayI+5daZBpL1rzXp9TrMSZqq\r\nsk5NuYgpfwBunGAjyeH3J4BirEKMUQElOKUr0oPvGYz11Rz6cs2Xa3U679q6\r\nZ7ZlwIHa3hghQeF62mlagpDWNJ1MweZb3zU=\r\n=fjdK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"56e6b1bb890f844b8963a146780d0b9cfa8abd0d","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.10.0","@opentelemetry/resources":"1.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.10.0_1678722835367_0.34217119288271447","host":"s3://npm-registry-packages"}},"1.10.1":{"name":"@opentelemetry/sdk-metrics","version":"1.10.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.10.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"1389a8524ba59dc2e1d9cf627d504119c111fca1","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.10.1.tgz","fileCount":543,"integrity":"sha512-ARAD4e6lZhLwstwW+1HG2Q3XuYFA/t8vn10KK/mA4em1pZYKFn64c45RJZJcntxWp4wOZRbp9iL1RXsg7zIjow==","signatures":[{"sig":"MEQCIF7l8lYSyGXYbwwaiNKB4rSrfukNeC9FI7PrmRetWSCaAiAEvWmV3Mebg7p+TvQxAN44ZzolcH3+uz+10I6dLs+2BA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1571910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkGIV6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrjQg//V5N9A3eIarDzrRPXxYMuJ3TO2qYzZxGLjCSbFBab/UFBKK2g\r\n4KS7ImLvfoP7k9S5xKMEQlCgG+0dGWt3SFELbhpLmwjVPM7OUcfF+es8dfAE\r\nQC6aLfq5juKIz/hcNBUGPZfauUQqReb+KptJUlCfvIW1RUi12dCoXSLm89/r\r\nXOUbhsIo3v+Lj5HBvn4lIl/3iJ07SaB3ehaVtypnSmpcgl2sWyMv77USvagd\r\nENNV4IbUFYM9YRnMFCWgSxtZtFDKmkLypkvUyu9jepecvgbXT+J4Z8hl6zgo\r\nv2Sqbi5PjkY035ZgSHqQ09yHGg5azUwn8AN0Yecqq88Mrqt35As1kdJLvrXg\r\nDK5lDQaxPqtaMSzLAg34Ck/Cng8HANjiKGaRA5ITCuJhoVMRt9va+mHscMwv\r\ngdfkJHKyq3wlGPZAvroDkljKROic2cYZv35zSwHXAoQ1PxYgEvbtxYDxsxPk\r\nSv5azrVgUBpKBM33EE+MMQ3AeizxdjCHnl8mPgv5u4C26MfdEd5f8cOKt9BS\r\nlhvukjFyU9DjoyMypt+LZWjWz1WVxircRPkXwSQa7t2FIGSV5XHL5nHSrBt4\r\nam5WwdCTJntt5jdeiL5RHN0myvOBDPB10G9ZoaogPjGUIXJK/XERuVD4/e9w\r\nJMVSLW8zW0CgZaAGWoaA4KhhW2OYXC/OdUk=\r\n=O4i4\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"486df99906958de9b8b666839785b00160a6a229","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.10.1","@opentelemetry/resources":"1.10.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.10.1_1679328634706_0.9289571491726534","host":"s3://npm-registry-packages"}},"1.11.0":{"name":"@opentelemetry/sdk-metrics","version":"1.11.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.11.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"0fe347fb39a802ec270315cba0eba2e3ce64c4a2","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.11.0.tgz","fileCount":561,"integrity":"sha512-knuq3pwU0+46FEMdw9Ses+alXL9cbcLUUTdYBBBsaKkqKwoVMHfhBufW7u6YCu4i+47Wg6ZZTN/eGc4LbTbK5Q==","signatures":[{"sig":"MEUCICcPE7ow3GH80M4VwObQJPDkCO3SK4VTYmZkd1STJ+uZAiEA35ZtKgFt5GMZa3WnCxdCI7yWWkwAk9QYu88ktlLJYVk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1839857,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkJaswACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrzcg/9FdLBYGIFXRPNUEc+CsQRabaC90KOwOp+7HV39/LXozs0FSB1\r\niKe3uUkfFBCLhcRSCQns1EVifv+Xda02Wa/Y0r28uhKc1y/N7uGQWLFpR7Iu\r\ngVVKTxpLt2QPsTYyQuuj4a6XGpGk7frKzv+uss7ByMKXUsC7YmzMS1DUJt+4\r\nekB8LXK7MEMrsN1ze0pp9+r9KTPGMDQXT9I64a/AuWR7IhM4RuPqjpriq/7R\r\nV38kOjgp4I6uj1H4hAB3FQ1NwdVOSajrDQ2HHLUvT4lv22LKIABQr0TDtQsN\r\naJq9rK7IfWSAE5zj5q9t8VfpNkhph3pFPPgg/l1Mnryr+VYhvErInyIuuPcM\r\n6X3csId2wFKauJR4oSXTLDxE6IMfvPk1ln2aXe3u/bSDrjEkd8bJvAS45yhW\r\n8JxegGRcA3SMuTtJcy85S6mVc/rksLjDSGDGpBaL4eYnR1qQ1hGYLljdpbuy\r\nbDRNcL2ZERMu3CmmXsFEmDqfTdFaMIzGTXXN20+ti6HtcBL9WFsKgVudxD5O\r\nTU40gKMtkAc+kfQ7cheqj+WjbH6ED0dPA7BHCE8Mo7kQTuGCVR1pMZZHh50h\r\nb/CaOKc7PCzVltN0PRnNPouwHzSM00LFqU4O2dvfnDkM6yyNJ86Je01ls6Fu\r\nQooqG9QlA64TxWqtWwXfGRY/w5p7wZSrXrE=\r\n=KFj4\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"1328ee04ae78f9f6cf143af7050c00aaa6d2eb3b","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.11.0","@opentelemetry/resources":"1.11.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.11.0_1680190255856_0.4488883258029981","host":"s3://npm-registry-packages"}},"1.12.0":{"name":"@opentelemetry/sdk-metrics","version":"1.12.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.12.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"52c135b8ca6af677a3e93b6721bc866a74c98b4b","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.12.0.tgz","fileCount":561,"integrity":"sha512-zOy88Jfk88eTxqu+9ypHLs184dGydJocSWtvWMY10QKVVaxhC3SLKa0uxI/zBtD9S+x0LP65wxrTSfSoUNtCOA==","signatures":[{"sig":"MEQCIEAfZBHAMQVNInXN1E0ihfClv5NylKPFc0jrD34SRJMvAiBzpMnq2Iu5C00sNF1GPtmv10V2RrlKn7msJG8pBtnd5g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1839857,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkOEYtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmobXQ/6AsboU2mCpdhs/tPwnXUqcjmoBV3/aVWoXwM22/jxCszCX8vk\r\nRFoTQHvvMZOMjqOLOPW2S3U2sPumqm7tj7V2aT/tVoZ7En09afYCbKO1fClj\r\ndtm9zB4H/GeL9uT4j8E7mFzPGMkgl/Wo8xZzrifKGknDwoO5sITFwXYCb1XI\r\nVprJn7eNMQEeMuGNd8WG2SFiI+VwVDfRz9eCmaI6ozABBw+Xd/Ae2SzPRfqo\r\nlbV46b6MN47oJHPNNjvPDPpQ8PUu+usZ2CZZR0/DYvHryjJnsTEaBOEqefXl\r\nvL7ommVdZW6DNSzMEQ5k4DQIMIHrvYYL/j8Cwkl7UcpmzrXgWoHNfQLlP3zk\r\nOqHvW+AEAYeiAs1ZrLRwNId+SpdNdERohMR70BxyU1ZXXx6NcpzJv6V+a+4q\r\nrtfX4oazI7rldM1BHO3sJM3g0cCyUtllFGTb5Mg8EI1qXBZppQF33jLvj20n\r\n4Ulb6wpsteaDDlBOK1TmN0s8VftM4ekrz2b5+UvE5yYq7RX+FVRFWb8U5xiZ\r\nHRc/5H4txVGZXLFvmSCJm6WmzUy83njG0qSF3wS6dOetNRXlrJrhS0tnHBqM\r\nnC4RbTqtdrp4EDi4F521hcdm6uo2cAmCxHbVhwgL+3lM2me9VmAtxB/BJacp\r\nvupg+95/zDYgsEWSTaFE/0t7TGuFe0UJ9j4=\r\n=5rhT\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"a04090010ee18e17487b449984807cc2b7b6e3e6","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.12.0","@opentelemetry/resources":"1.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.12.0_1681409581531_0.31698151736882196","host":"s3://npm-registry-packages"}},"1.13.0":{"name":"@opentelemetry/sdk-metrics","version":"1.13.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.13.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"4e859107a7a4389bcda7b37d3952bc7dd34211d7","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.13.0.tgz","fileCount":561,"integrity":"sha512-MOjZX6AnSOqLliCcZUrb+DQKjAWXBiGeICGbHAGe5w0BB18PJIeIo995lO5JSaFfHpmUMgJButTPfJJD27W3Vg==","signatures":[{"sig":"MEYCIQCUeO+qPBpw+CVNlqlTfDO++08VfOb0W3UdO9pIYolUCAIhAJH6JOYZuj9SxAwhyK86XMhqrxGloVuk7E7pzmTPdjJr","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1839834},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"8fc76896595aac912bf9e15d4f19c167317844c8","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.13.0","@opentelemetry/resources":"1.13.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.13.0_1683811806809_0.022001801192962578","host":"s3://npm-registry-packages"}},"1.14.0":{"name":"@opentelemetry/sdk-metrics","version":"1.14.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.14.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"ee51d72eb32a74108e6632681ce2df46cddc0714","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.14.0.tgz","fileCount":561,"integrity":"sha512-F0JXmLqT4LmsaiaE28fl0qMtc5w0YuMWTHt1hnANTNX8hxW4IKSv9+wrYG7BZd61HEbPm032Re7fXyzzNA6nIw==","signatures":[{"sig":"MEUCIQDZy6AZC4gsrIqvrLyc0L21UL/+PakTzlDTNsuwjRpDhwIgMSf9ZJoCKDB8uD708TqJtggoPZ9cpVP/LIthheTWDWM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1839834},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"edebbcc757535bc88f01340409dbbecc0bb6ccf8","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"4.6.2","@opentelemetry/core":"1.14.0","@opentelemetry/resources":"1.14.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","codecov":"3.8.3","ts-mocha":"10.0.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.14.0_1686031255514_0.6205545095237606","host":"s3://npm-registry-packages"}},"1.15.0":{"name":"@opentelemetry/sdk-metrics","version":"1.15.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.15.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e47ad688882fc2daedcbbe3db16a5c110feb23e8","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.0.tgz","fileCount":561,"integrity":"sha512-fFUnAcPvlXO39nlIduGuaeCuiZyFtSLCn9gW/0djFRO5DFst4m4gcT6+llXvNWuUvtGB49s56NP10B9IZRN0Rw==","signatures":[{"sig":"MEQCIA6mPFnkdT47rCrHsCh4ePv+jy+q7abH27EHpIw2DKE0AiB9rZUo59MjzXY9Uih4EbPVSQZ5I/uFRmYTdzVsP7a68g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1803014},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"06e919d6c909e8cc8e28b6624d9843f401d9b059","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.1/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"tslib":"^2.3.1","lodash.merge":"^4.6.2","@opentelemetry/core":"1.15.0","@opentelemetry/resources":"1.15.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.1","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","ts-mocha":"10.0.0","cross-var":"1.1.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.15","karma-webpack":"4.0.2","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","karma-chrome-launcher":"3.1.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.15.0_1688642828881_0.612950022765163","host":"s3://npm-registry-packages"}},"1.15.1":{"name":"@opentelemetry/sdk-metrics","version":"1.15.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.15.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e0d2844191ecd7fce3fccf18ae50ed35389f0885","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.1.tgz","fileCount":561,"integrity":"sha512-ojcrzexOQfto83NvKfIvsJap4SHH3ZvLjsDGhQ04AfvWWGR7mPcqLSlLedoSkEdIe0k1H6uBEsHBtIprkMpTHA==","signatures":[{"sig":"MEYCIQCBwveTbaE79v4tJk8CdBffRh5H6Loc8hSu+ysnZzK1YAIhAMHsh5T/As5lzxnmYm6j7V704gjjvyvEm2CaH1bpx5cO","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1841179},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"9f71800fdc2a5ee5055684037a12498af71955f2","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.3/node@v18.4.0+x64 (darwin)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.4.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.15.1","@opentelemetry/resources":"1.15.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.3","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.9","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.1","@types/sinon":"10.0.15","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.15.1_1690209168814_0.4696021233708716","host":"s3://npm-registry-packages"}},"1.15.2":{"name":"@opentelemetry/sdk-metrics","version":"1.15.2","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.15.2","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"eadd0a049de9cd860e1e0d49eea01156469c4b60","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.2.tgz","fileCount":561,"integrity":"sha512-9aIlcX8GnhcsAHW/Wl8bzk4ZnWTpNlLtud+fxUfBtFATu6OZ6TrGrF4JkT9EVrnoxwtPIDtjHdEsSjOqisY/iA==","signatures":[{"sig":"MEUCIQCFKX9DaFzjXDXPu/N+lf5A+VZoJEIuX5BlwqR2YxqIPQIgTriFUfsIieNy4ajXosd23GlEnVapJ9nRDMUvRFJYIGk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1848194},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"48fb15862e801b742059a3e39dbcc8ef4c10b2e2","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.4/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.15.2","@opentelemetry/resources":"1.15.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.4","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.10","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.5.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.15.2_1691500878806_0.21964542929012487","host":"s3://npm-registry-packages"}},"1.16.0":{"name":"@opentelemetry/sdk-metrics","version":"1.16.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.16.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"6c413c6abb1d68dbfe59984384d4031feeccbe1e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.16.0.tgz","fileCount":561,"integrity":"sha512-58obaKzBY0CB6ZQS/sxcGvihqZk2zL2KDCQe734NofVfE7JpKMn/TtyzA8O4nw9sXIO2N9Wx2zzKRyGFXVGrcw==","signatures":[{"sig":"MEUCIGu4l4M/yMtx2D8TpJeVo1nvcoCzRr6o5+wZtJj2W23YAiEAzPdInj1ng6V1HXJhfyT/sRzxXuC3taYmiuKCASO4G2g=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1872545},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"5fcd8cf136e2235903dde3df9ba03ced594f0e95","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.16.0","@opentelemetry/resources":"1.16.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.17","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.6.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.6.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.16.0_1694434471320_0.42251452448444704","host":"s3://npm-registry-packages"}},"1.17.0":{"name":"@opentelemetry/sdk-metrics","version":"1.17.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.17.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e51d39e0bb749780d17f9b1df12f0490438dec1a","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.17.0.tgz","fileCount":561,"integrity":"sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==","signatures":[{"sig":"MEQCIEGFsgaNt88vPUiItoys14mI96KUmsfCU3V9/M9zEFrHAiAgibedup1xc0Uh7HFSGEe8a8IEZbYsRxYQhUM7mMJJmQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1872545},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"faf939c77591f709afbc23fadbe629c9d3607ef6","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.17.0","@opentelemetry/resources":"1.17.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.17","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.7.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.7.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.17.0_1694524354547_0.27289065421627656","host":"s3://npm-registry-packages"}},"1.17.1":{"name":"@opentelemetry/sdk-metrics","version":"1.17.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.17.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"9c4d13d845bcc82be8684050d9db7cce10f61580","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.17.1.tgz","fileCount":561,"integrity":"sha512-eHdpsMCKhKhwznxvEfls8Wv3y4ZBWkkXlD3m7vtHIiWBqsMHspWSfie1s07mM45i/bBCf6YBMgz17FUxIXwmZA==","signatures":[{"sig":"MEUCIQC6T+my/2xzI2vaedMangtbqdpdAHiTTnwC85I9zGxvdwIgJwgxV8Jz14/su/N16gkO8rU//itJPXSzxcx7YCPtM6U=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1883659},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"f8e187b473274cc2011e7385992f07d319d667dc","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.17.1","@opentelemetry/resources":"1.17.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.20","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.2","@types/sinon":"10.0.18","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.7.0","@types/lodash.merge":"4.6.7","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.7.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.17.1_1696947498625_0.4798664177082872","host":"s3://npm-registry-packages"}},"1.18.0":{"name":"@opentelemetry/sdk-metrics","version":"1.18.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.18.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"f84fffcabdb0e9504e3b219635c1099aabc9e207","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.18.0.tgz","fileCount":561,"integrity":"sha512-wK5zdNCo5cJvZog/lsqXCg9/Dt9UeNXQsskgqX8Yz+40t13Kb5CKFFkAMU8tNUxkvidHnD6G6sT6xeVCHQYe4A==","signatures":[{"sig":"MEUCIQCMiT/6j2k3CLJNS9PVe/gMQM/shgZfXDwNLg9l7E3QdgIgFnmCSIgxrXYgrtIppJgyKJkGNUzsbBET1HwAZBRXfG8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1890396},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"73b446688f10fd8dc4cf403a085f0a39070df7b4","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.18.0","@opentelemetry/resources":"1.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.20","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.3","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.8","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.18.0_1699353886784_0.2402177673494048","host":"s3://npm-registry-packages"}},"1.18.1":{"name":"@opentelemetry/sdk-metrics","version":"1.18.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.18.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"1dd334744a1e5d2eec27e9e9765c73cd2f43aef3","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.18.1.tgz","fileCount":561,"integrity":"sha512-TEFgeNFhdULBYiCoHbz31Y4PDsfjjxRp8Wmdp6ybLQZPqMNEb+dRq+XN8Xw3ivIgTaf9gYsomgV5ensX99RuEQ==","signatures":[{"sig":"MEUCIQDZiuGHcrjzMGF/TJ452D+P3TvNuhbyNQDLJOkdYsDPFQIgKtcP2GNoTjs9raH1wfRgj93Kw4tqRy7FLSmt98YetyQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1892649},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"f665499096189390e691cf1a772e677fa67812d7","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.18.1","@opentelemetry/resources":"1.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.22.20","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"4.10.0","@types/mocha":"10.0.3","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.9.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.8","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.18.1_1699466949698_0.2731195093463463","host":"s3://npm-registry-packages"}},"1.19.0":{"name":"@opentelemetry/sdk-metrics","version":"1.19.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.19.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"fe8029af29402563eb8dba75a85fc02006ea92c4","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.19.0.tgz","fileCount":561,"integrity":"sha512-FiMii40zr0Fmys4F1i8gmuCvbinBnBsDeGBr4FQemOf0iPCLytYQm5AZJ/nn4xSc71IgKBQwTFQRAGJI7JvZ4Q==","signatures":[{"sig":"MEUCIQCQbl3KfCIEDQc8lHtdKlCRprXMXq1iqFxWHzS7fhRAMQIgOOpi/ti3SCJMstM0WHGr81bNA0QPpltVs/+OuHKDlyw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1892648},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"d3c311aec24137084dc820805a2597e120335672","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.19.0","@opentelemetry/resources":"1.19.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.19.0_1702557329935_0.360745159718469","host":"s3://npm-registry-packages"}},"1.20.0":{"name":"@opentelemetry/sdk-metrics","version":"1.20.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.20.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"472d723d986a0a0cc1ee1170ed086dc18269d7e0","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.20.0.tgz","fileCount":561,"integrity":"sha512-07bFOQUrpN/Q5biJ/cuBePztKwkc1VGkFblZxAcVkuvCLDAPJfsyr0NNWegWeYe0bpGt1jmXScpUWnVD+t8Q0w==","signatures":[{"sig":"MEUCIC3XY/+kezXMIGl3icXMEXdu0pKWhpKhJ7kpzNi6cW5lAiEAtKlujqgWTX99ep+IDoNQKi5a4BEjbPjw6yGUGzBa6hU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1892648},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"57008533aba7ccd51ea80f38ff4f29404d47eb9c","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.20.0","@opentelemetry/resources":"1.20.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.20.0_1705313747415_0.3264673460411951","host":"s3://npm-registry-packages"}},"1.21.0":{"name":"@opentelemetry/sdk-metrics","version":"1.21.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.21.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"40d71aaec5b696e58743889ce6d5bf2593f9a23d","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.21.0.tgz","fileCount":561,"integrity":"sha512-on1jTzIHc5DyWhRP+xpf+zrgrREXcHBH4EDAfaB5mIG7TWpKxNXooQ1JCylaPsswZUv4wGnVTinr4HrBdGARAQ==","signatures":[{"sig":"MEQCIGpBDMhU8lTH5TE6aUTa3/f+ZAAJNZyvJR4W+y34uKgyAiA9nbAm4W2G9iFlp1OSXMtF3+Qs3M8RTAz9dMzR/gPTXA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1898573},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"828f2ed730e4d26d71f92e220f96b60a552a673a","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.21.0","@opentelemetry/resources":"1.21.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.8.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.21.0_1706249469816_0.2681154592027921","host":"s3://npm-registry-packages"}},"1.22.0":{"name":"@opentelemetry/sdk-metrics","version":"1.22.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.22.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b94c62403013e4c72b96dc747d71d786073efafc","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.22.0.tgz","fileCount":561,"integrity":"sha512-k6iIx6H3TZ+BVMr2z8M16ri2OxWaljg5h8ihGJxi/KQWcjign6FEaEzuigXt5bK9wVEhqAcWLCfarSftaNWkkg==","signatures":[{"sig":"MEUCIQDNXgVv9Uxbxg3/33/6F66wijwbwDd7GOiqKURWpli14gIgE4NQsN1CjuPkQcHMyQ25CAKybwbYPJODQxdspPHt3+E=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1903538},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"7be35c7845e206b27b682e8ce1cee850b09cec04","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.22.0","@opentelemetry/resources":"1.22.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.9.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.22.0_1709198294535_0.21078722486314505","host":"s3://npm-registry-packages"}},"1.23.0":{"name":"@opentelemetry/sdk-metrics","version":"1.23.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.23.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b4cf3cc86b6dedf5c438c67c829df7399bf64be1","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.23.0.tgz","fileCount":561,"integrity":"sha512-4OkvW6+wST4h6LFG23rXSTf6nmTf201h9dzq7bE0z5R9ESEVLERZz6WXwE7PSgg1gdjlaznm1jLJf8GttypFDg==","signatures":[{"sig":"MEUCIQCRviOtBHY4cxAbFmlSe5/d1UlZwJANaHyzFmHvAstAfgIgcfcrCMSSgbzKnYncXB5RxmguAOFxcmRGxTgWETblVvM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914895},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"5231aa255047fbc6ee3d6a299f4423ab2f8a5fbc","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.23.0","@opentelemetry/resources":"1.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.9.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.23.0_1712131805787_0.6556898049242013","host":"s3://npm-registry-packages"}},"1.24.0":{"name":"@opentelemetry/sdk-metrics","version":"1.24.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.24.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"205c19b6d18e385039d0a261c784a203c644fc28","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.24.0.tgz","fileCount":561,"integrity":"sha512-4tJ+E6N019OZVB/nUW/LoK9xHxfeh88TCoaTqHeLBE9wLYfi6irWW6J9cphMav7J8Qk0D5b7/RM4VEY4dArWOA==","signatures":[{"sig":"MEUCIFw7C6N1PHTi2sUnPwYd6pb4MEYB0sdDPAfLyVHSEEL0AiEAhRBkWnU/RMsb1e2pd8JuT67Zi9eH87TbDc40JN74Plg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914895},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"3ab4f765d8d696327b7d139ae6a45e7bd7edd924","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.24.0","@opentelemetry/resources":"1.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.9.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.24.0_1713969585182_0.37633556794494494","host":"s3://npm-registry-packages"}},"1.24.1":{"name":"@opentelemetry/sdk-metrics","version":"1.24.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.24.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"82ee3069b2ca9bb7c1e91272ff81536dc2e9bc8d","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.24.1.tgz","fileCount":561,"integrity":"sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==","signatures":[{"sig":"MEUCIQCTMSUw6bs+C6f+azmvQBAAOS2F2zkdYmTaUA0WsSSESgIgVgEsIIBcrrCeUZGl8a4ziCe01KmMiEcyeTk5/btVJww=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914895},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"41c2626fe0ed03e2e83bd79ee43c9bdf0ffd80d8","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.24.1","@opentelemetry/resources":"1.24.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@babel/core":"7.23.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.22.20","@opentelemetry/api":">=1.3.0 <1.9.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.24.1_1715093558847_0.03178144750682588","host":"s3://npm-registry-packages"}},"1.25.0":{"name":"@opentelemetry/sdk-metrics","version":"1.25.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.25.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"0c954d580c17821ae4385d29447718df09e80b79","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.0.tgz","fileCount":561,"integrity":"sha512-IF+Sv4VHgBr/BPMKabl+GouJIhEqAOexCHgXVTISdz3q9P9H/uA8ScCF+22gitQ69aFtESbdYOV+Fen5+avQng==","signatures":[{"sig":"MEUCIQCTGV0/dFktbxE5zP6XFH0Eu0/cZ7CGkU7c5BAAq4CVjAIgG3QIuMsMnEWURsoo1SS0GKQqtBcHiCbSGaaBzrbsXaE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914152},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"c4d3351b6b3f5593c8d7cbfec97b45cea9fe1511","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.25.0","@opentelemetry/resources":"1.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.3","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@babel/core":"7.24.6","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"17.0.3","karma-webpack":"5.0.1","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.24.6","@opentelemetry/api":">=1.3.0 <1.10.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.25.0_1717607758345_0.42159719696026543","host":"s3://npm-registry-packages"}},"1.25.1":{"name":"@opentelemetry/sdk-metrics","version":"1.25.1","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.25.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"50c985ec15557a9654334e7fa1018dc47a8a56b7","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz","fileCount":561,"integrity":"sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==","signatures":[{"sig":"MEUCIQCNZXU0MM/EDXNSq8ZV/6psCW97vGmYrurc/e/iYg7LOAIgS3/fl7W9LtXeIauAqM5ZL24hsmc39zaKuX+90I3Edn8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1914152},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"0608f405573901e54db01e44c533009cf28be262","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.25.1","@opentelemetry/resources":"1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.3","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@babel/core":"7.24.7","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.6","@types/sinon":"17.0.3","karma-webpack":"5.0.1","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.24.7","@opentelemetry/api":">=1.3.0 <1.10.0","@types/lodash.merge":"4.6.9","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.25.1_1718875163722_0.02638450999168529","host":"s3://npm-registry-packages"}},"1.26.0":{"name":"@opentelemetry/sdk-metrics","version":"1.26.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.26.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"37bb0afb1d4447f50aab9cdd05db6f2d8b86103e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.26.0.tgz","fileCount":561,"integrity":"sha512-0SvDXmou/JjzSDOjUmetAAvcKQW6ZrvosU0rkbDGpXvvZN+pQF6JbK/Kd4hNdK4q/22yeruqvukXEJyySTzyTQ==","signatures":[{"sig":"MEQCICUUc6/yySv2TiNLcq7AT6pbNR/Hi6zEZwTkoKabjKDXAiBJffQVgekh2F9ABx6OLgxEpstV/+ZyN55vBGwjKtK3bg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1922196},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"720bc8c70d47029cb6b41a34ffdc3d25cbaa2f80","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc mocha 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.20.4","dependencies":{"@opentelemetry/core":"1.26.0","@opentelemetry/resources":"1.26.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.4","lerna":"6.6.2","mocha":"10.7.3","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@babel/core":"7.25.2","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.7","@types/sinon":"17.0.3","karma-webpack":"5.0.1","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.25.3","@opentelemetry/api":">=1.3.0 <1.10.0","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"7.0.0","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.26.0_1724836642462_0.8363214110577704","host":"s3://npm-registry-packages"}},"1.27.0":{"name":"@opentelemetry/sdk-metrics","version":"1.27.0","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-metrics@1.27.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"fb4f55017dc95a95ee00260262952b18e3e7c25c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.27.0.tgz","fileCount":561,"integrity":"sha512-JzWgzlutoXCydhHWIbLg+r76m+m3ncqvkCcsswXAQ4gqKS+LOHKhq+t6fx1zNytvLuaOUBur7EvWxECc4jPQKg==","signatures":[{"sig":"MEQCICee8j7NTKwwuMDYrj9aQl8y/q4kq6aAAzlgQtPxV3QOAiBvOudBQcJ+lEOSc3vaMLEPFzNp4YjETGVxdyID8KO5GQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@opentelemetry%2fsdk-metrics@1.27.0","provenance":{"predicateType":"https://slsa.dev/provenance/v0.2"}},"unpackedSize":1920417},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","engines":{"node":">=14"},"gitHead":"eb3ca4fb07ee31c62093f5fcec56575573c902ce","scripts":{"tdd":"npm run test -- --watch-extensions ts --watch","lint":"eslint . --ext .ts","test":"nyc mocha 'test/**/*.test.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"node ../../scripts/version-update.js","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","description":"OpenTelemetry metrics SDK","directories":{},"sideEffects":false,"_nodeVersion":"18.20.4","dependencies":{"@opentelemetry/core":"1.27.0","@opentelemetry/resources":"1.27.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.4","lerna":"6.6.2","mocha":"10.7.3","sinon":"15.1.2","webpack":"5.94.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@babel/core":"7.25.2","@types/node":"18.6.5","karma-mocha":"2.0.1","webpack-cli":"5.1.4","@types/mocha":"10.0.8","@types/sinon":"17.0.3","karma-webpack":"5.0.1","webpack-merge":"5.10.0","karma-coverage":"2.2.1","@babel/preset-env":"7.25.4","@opentelemetry/api":">=1.3.0 <1.10.0","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"7.0.0","karma-chrome-launcher":"3.1.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-metrics_1.27.0_1729695104075_0.2954021766849051","host":"s3://npm-registry-packages"}},"1.28.0":{"name":"@opentelemetry/sdk-metrics","version":"1.28.0","description":"OpenTelemetry metrics SDK","main":"build/src/index.js","module":"build/esm/index.js","esnext":"build/esnext/index.js","types":"build/src/index.d.ts","repository":{"type":"git","url":"git+https://github.com/open-telemetry/opentelemetry-js.git"},"scripts":{"prepublishOnly":"npm run compile","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","test":"nyc mocha 'test/**/*.test.ts'","test:browser":"karma start --single-run","tdd":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","lint":"eslint . --ext .ts","lint:fix":"eslint . --ext .ts --fix","version":"node ../../scripts/version-update.js","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","prewatch":"node ../../scripts/version-update.js","peer-api-check":"node ../../scripts/peer-api-check.js","align-api-deps":"node ../../scripts/align-api-deps.js"},"keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","engines":{"node":">=14"},"publishConfig":{"access":"public"},"devDependencies":{"@babel/core":"7.26.0","@babel/preset-env":"7.26.0","@opentelemetry/api":">=1.3.0 <1.10.0","@types/mocha":"10.0.9","@types/node":"18.6.5","@types/sinon":"17.0.3","babel-plugin-istanbul":"7.0.0","cross-var":"1.1.0","karma":"6.4.4","karma-chrome-launcher":"3.1.0","karma-coverage":"2.2.1","karma-mocha":"2.0.1","karma-spec-reporter":"0.0.36","karma-webpack":"5.0.1","lerna":"6.6.2","mocha":"10.8.2","nyc":"15.1.0","sinon":"15.1.2","ts-loader":"9.5.1","typescript":"4.4.4","webpack":"5.96.1","webpack-cli":"5.1.4","webpack-merge":"5.10.0"},"peerDependencies":{"@opentelemetry/api":">=1.3.0 <1.10.0"},"dependencies":{"@opentelemetry/core":"1.28.0","@opentelemetry/resources":"1.28.0"},"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","sideEffects":false,"gitHead":"4b1ad3fda0cde58907e30fab25c3c767546708e5","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"_id":"@opentelemetry/sdk-metrics@1.28.0","_nodeVersion":"18.20.4","_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","dist":{"integrity":"sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==","shasum":"257b5295bbe9de1ad31c5e8cb43a660c25911d20","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz","fileCount":561,"unpackedSize":1922324,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@opentelemetry%2fsdk-metrics@1.28.0","provenance":{"predicateType":"https://slsa.dev/provenance/v0.2"}},"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH/Z6d+HyDNtyNUtpuzVdAyqhlTCfvqGItbZ06UPzbGwAiEA1ZqqIIyQTA5ZM/DN/dHtZc0GZcZv5TtaAtdiIAF0eZI="}]},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"directories":{},"maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdk-metrics_1.28.0_1731926513320_0.5425586601271688"},"_hasShrinkwrap":false}},"time":{"created":"2022-08-24T17:44:35.369Z","modified":"2024-11-18T10:41:53.964Z","0.32.0":"2022-08-24T17:44:35.717Z","0.33.0":"2022-09-16T12:14:58.301Z","1.8.0":"2022-11-09T19:45:34.297Z","1.9.0":"2023-01-11T21:46:29.914Z","1.9.1":"2023-01-30T15:30:13.362Z","1.10.0":"2023-03-13T15:53:55.612Z","1.10.1":"2023-03-20T16:10:34.822Z","1.11.0":"2023-03-30T15:30:56.074Z","1.12.0":"2023-04-13T18:13:01.808Z","1.13.0":"2023-05-11T13:30:07.116Z","1.14.0":"2023-06-06T06:00:55.809Z","1.15.0":"2023-07-06T11:27:09.167Z","1.15.1":"2023-07-24T14:32:49.159Z","1.15.2":"2023-08-08T13:21:18.974Z","1.16.0":"2023-09-11T12:14:31.689Z","1.17.0":"2023-09-12T13:12:34.838Z","1.17.1":"2023-10-10T14:18:19.024Z","1.18.0":"2023-11-07T10:44:47.115Z","1.18.1":"2023-11-08T18:09:09.980Z","1.19.0":"2023-12-14T12:35:30.166Z","1.20.0":"2024-01-15T10:15:47.729Z","1.21.0":"2024-01-26T06:11:10.039Z","1.22.0":"2024-02-29T09:18:14.841Z","1.23.0":"2024-04-03T08:10:06.050Z","1.24.0":"2024-04-24T14:39:45.460Z","1.24.1":"2024-05-07T14:52:39.090Z","1.25.0":"2024-06-05T17:15:58.569Z","1.25.1":"2024-06-20T09:19:23.925Z","1.26.0":"2024-08-28T09:17:22.703Z","1.27.0":"2024-10-23T14:51:44.571Z","1.28.0":"2024-11-18T10:41:53.564Z"},"bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics","keywords":["opentelemetry","nodejs","metrics","stats","profiling"],"repository":{"type":"git","url":"git+https://github.com/open-telemetry/opentelemetry-js.git"},"description":"OpenTelemetry metrics SDK","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"readme":"# OpenTelemetry Metrics SDK\n\n[![NPM Published Version][npm-img]][npm-url]\n[![Apache License][license-image]][license-image]\n\nThis module contains the Metrics SDK of [opentelemetry-js](https://github.com/open-telemetry/opentelemetry-js).\n\nUsed standalone, this module provides methods for manual instrumentation of code, offering full control over recording metrics for client-side JavaScript (browser) and Node.js.\n\nIt does **not** provide automated instrumentation of known libraries or host environment metrics out-of-the-box.\n\n## Installation\n\n```bash\nnpm install --save @opentelemetry/api\nnpm install --save @opentelemetry/sdk-metrics\n```\n\n## Usage\n\nThe basic setup of the SDK can be seen as followings:\n\n```js\nconst opentelemetry = require('@opentelemetry/api');\nconst { MeterProvider } = require('@opentelemetry/sdk-metrics');\n\n// To create an instrument, you first need to initialize the Meter provider.\n// NOTE: The default OpenTelemetry meter provider does not record any metric instruments.\n// Registering a working meter provider allows the API methods to record instruments.\nopentelemetry.metrics.setGlobalMeterProvider(new MeterProvider());\n\n// To record a metric event, we used the global singleton meter to create an instrument.\nconst counter = opentelemetry.metrics.getMeter('default').createCounter('foo');\n\n// record a metric event.\ncounter.add(1, { attributeKey: 'attribute-value' });\n```\n\nIn conditions, we may need to setup an async instrument to observe costly events:\n\n```js\n// Creating an async instrument, similar to synchronous instruments\nconst observableCounter = opentelemetry.metrics.getMeter('default')\n .createObservableCounter('observable-counter');\n\n// Register a single-instrument callback to the async instrument.\nobservableCounter.addCallback(async (observableResult) => {\n // ... do async stuff\n observableResult.observe(1, { attributeKey: 'attribute-value' });\n});\n\n// Register a multi-instrument callback and associate it with a set of async instruments.\nopentelemetry.metrics.getMeter('default')\n .addBatchObservableCallback(batchObservableCallback, [ observableCounter ]);\nasync function batchObservableCallback(batchObservableResult) {\n // ... do async stuff\n batchObservableResult.observe(observableCounter, 1, { attributeKey: 'attribute-value' });\n}\n```\n\nViews can be registered when instantiating a `MeterProvider`:\n\n```js\nconst meterProvider = new MeterProvider({\n views: [\n // override the bucket boundaries on `my.histogram` to [0, 50, 100]\n new View({ aggregation: new ExplicitBucketHistogramAggregation([0, 50, 100]), instrumentName: 'my.histogram'}),\n // rename 'my.counter' to 'my.renamed.counter'\n new View({ name: 'my.renamed.counter', instrumentName: 'my.counter'})\n ]\n})\n```\n\n## Example\n\nSee [examples/prometheus](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/examples/prometheus) for an end-to-end example, including exporting metrics.\n\n## Useful links\n\n- For more information on OpenTelemetry, visit: \n- For more about OpenTelemetry JavaScript: \n- For help or feedback on this project, join us in [GitHub Discussions][discussions-url]\n\n## License\n\nApache 2.0 - See [LICENSE][license-url] for more information.\n\n[discussions-url]: https://github.com/open-telemetry/opentelemetry-js/discussions\n[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/main/LICENSE\n[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat\n[npm-url]: https://www.npmjs.com/package/@opentelemetry/sdk-metrics\n[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fsdk%2Dmetrics.svg\n","readmeFilename":"README.md"} \ No newline at end of file diff --git a/tests/registry/npm/@opentelemetry/sdk-trace-base/registry.json b/tests/registry/npm/@opentelemetry/sdk-trace-base/registry.json deleted file mode 100644 index df5892a53b..0000000000 --- a/tests/registry/npm/@opentelemetry/sdk-trace-base/registry.json +++ /dev/null @@ -1 +0,0 @@ -{"_id":"@opentelemetry/sdk-trace-base","_rev":"65-20c7afee9d8d681944f1419799e365d9","name":"@opentelemetry/sdk-trace-base","dist-tags":{"canary":"0.25.1-alpha.23","next":"1.8.0","latest":"1.28.0"},"versions":{"0.24.1-alpha.4":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.4","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.4","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"8ad47864d4fd534b5a24f3d9b36aa91b348586f1","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.4.tgz","fileCount":147,"integrity":"sha512-dRMAseFliUOYuKoHha+3/qdNsU0JyY8085xzrRyJP7TvXo6KQKzotRCtMTMvXgv3UOufeZTMHyO13J3cuB+eSg==","signatures":[{"sig":"MEQCIA7Tcl+88PcVrNMkgkaAbIhBNLaDuB6ol0YEz/lQu/SPAiB9VqTx7RX1pJT9c2zbFfw0RPOaHeljtad8c+NBp9C5HA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":230456,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDDvUCRA9TVsSAnZWagAAwZMP/iLkavfZlJi3bwI7VOqY\n9dMwmFZXKZvBsrH2uGGD025BEAtA1p2NTARa1h2UTr+Zx5yI04ziJUkuZ7MH\n4t7QpMtHJCOAzavVzSFuy3WNFjG7qq8sK20loQL1NpmwVCC/Fz2+EohD5Wkj\nmxmizEdH+DkUNpKbsuLMlEYkPz3ZGW+h4b4nkeCt3MylOvuO0/1wW+NOvjk+\np8baQh3roC+JFZ445yQAQek90YylXzvtvhMuf4NYSedtfABAB3w08iy9I/sZ\nR+KlcC3px8zMsxUq2BdqV3EOiTwaqAZbCfaaPma1yjTSxHum1C2HuWjkUqMc\nxG/PctI398iEeAe0YtwKqK+BrjdV3RnlHNSS0rOZiRDqAqBKqPu8uvB43g5e\nhpEJW6QG34KGfO85mNgJwp8gMtrhQ30o5cetTJ6GBQBjySOgF5SUN4DCQmmb\nsZyR3UHNarAQIQWR5A2DTeqK60BTz2jjCKMIA+EGerhDbEm4kgc62Hml1O+a\nFzqycPereLxTLqRQybK0CSSe8IaZkdUQxir8FMH1UFkzM5jlQINpsa80u1fy\nDpVZ8QDFf2Yv74IqMmHjtOSrbh81IWx7GEAslvyNyvz8TxtpcVWpntd2Tveh\nzJwqF6xwYbFzc7kKYA7R9MpaPoQLeHvxFpPJjasjcswVDRxTEnonXXqBxHHu\nK/LE\r\n=dotJ\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"a8d39317b5daad727f2116ca314db0d1420ec488","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.4+a8d39317","@opentelemetry/resources":"^0.24.1-alpha.4+a8d39317","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.6","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.4_1628191700214_0.39586224494227173","host":"s3://npm-registry-packages"}},"0.24.1-alpha.5":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.5","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.5","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"5801c08684013d9cd72ce6ca781f0fea2f5eb776","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.5.tgz","fileCount":147,"integrity":"sha512-V5N8VEc6IUQP9CH5NPOPjdwhnX94Bboe+AKje/aQLyageVYoHFxkqLklI3EfbeuDCAoDQ1cSRC2PuqEQJGlBBg==","signatures":[{"sig":"MEQCIGoA1de87lUaFgMYQO6M31GSBowdvSJUcfMWYmZjl9WbAiBz4e7nYoJs/y8Eg/7vOVFT5uf6fpzxdgjqPpswdYAcLA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":230473,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDR2bCRA9TVsSAnZWagAAMmIP/iCQEezryq1P/10oM7l/\nQvh6jsYykBh9vZvXDvExS8TPKO49HZojjwFNBraqmJJSjQKgxIIB939N7EQw\n5Swfp9dUEMx71YaiigGF9r3pwQ15R+sN+HJPnTKEXIXvoTlutmTY1TDb1SGV\nhL+pviT97v9sUeT4NOphZ+eWkEzGepM/VM4No5O2w0KPFCsMu7cXFLC0UZPQ\n9DEHY+zAtpwJ9J0oSpQO4ci4cj7cp+6pKHGNxSzZPbkHwT9F8pCjM0CXx2O9\nnHs/HQhD99BIGyrmaQ45Q9lz2LOwIdFvtcPMZOfnfSYMM2yOvU7svHFugFCM\n3dbnPBp8U9gk7CMzDfR098vwQ+naW+1fovStECk/cLCzGPVTrJOMs6oknvnk\nuAHeyv/TI6THT+VyIphzlZu1R/NodGTkjV9oTOl4KodIfCIJ7nVm5mNlR2Ym\nlYvbKQWcLdrtbftq8Yzberd48DOYa3tgj7HKAZOZL/Op8jnElpK+M1gApkBa\nUOJ/TYyrAVhOXhPv4IItUKj9unTUn0ixT+zW41agKvOoEc0oNuj5SydsB5HZ\nUC88UQPNL+zWxtf3YEUYQh/cfgcFTzS0Oe3L9n1CeBYUi+j9JO9kvFMA49IC\no8a7MXMeANuxSynGSXQF8TH5VV7yLkiHEmbEadcal+GUpD0b/jhY6UjS1MAp\nwKmg\r\n=ogTt\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"b69ff35e90a33d20a0154dcd326f1467dfd39e2a","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.5+b69ff35e","@opentelemetry/resources":"^0.24.1-alpha.5+b69ff35e","@opentelemetry/semantic-conventions":"^0.24.1-alpha.5+b69ff35e"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.5_1628249499147_0.1555991669652872","host":"s3://npm-registry-packages"}},"0.24.1-alpha.7":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.7","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.7","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e1a60e391fdfaeb647b83318319f15e6b80fac73","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.7.tgz","fileCount":147,"integrity":"sha512-tenqp2xjKQqTxoO4+1S/VEDmdIJYIoWlOnCskibN86Am6UaSxryDtEaXzv4yVAoQJHF6c7QA308dgV45rAU4Ng==","signatures":[{"sig":"MEYCIQC0BMKRFFm4Xntj28BRWnEKOrdJMTRwty3ypoxV3T45+gIhAOyfi84y+2/dZdPwkzS8/GRgattYPgeoW6DoBy83c3eE","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":230473,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDouQCRA9TVsSAnZWagAAKWEP/1nv1RMnwNq8l2paxhGt\nMg2271/nrnAfCYD/H6+HSo/5I9AbLrHZKzN+QL+BDRC16OHnpCSEpjRqzuy3\nW8Gw38fSwWV5f72l5hs9pzSfqEpXGwobGlHLONHsrEnLgd6GIGNZhwNp/UjQ\nSEJ8Z93Fg/Yp5WwbsTuAHpiSaJ8IEHIlmTA15d93OJAZ8O1qOcAW2BszacIV\ngrWOzFtnuD29o36rQtO4Q8FMyTseCMfoxGqDcTXM9UweXq26A42QbPD2mx9W\nCaPNd5gA5RcA9KWI0pEM9SBtngmbVUA3wPh/T67eZ+xV5wUIxxc3zkz0Z8/2\n0XQDEG9Hd6vYFe2x9Sybj1bWCievDMMmrwjNCIMoBKCZJsBVLd0RsFSwb7/P\nYCxq7MmZhKAFkt7c5W2aUL9wOQEVlAmT7sxez5m6kgxNtxbC3kZn7+vEfEp6\nXPekhMdVHnIdeADWLEn1l1KWXghpE1eK0jUIzs4FNJzCBGvIRHBRVmVaglvH\nn1krQ+6kga6APLro4T+IwbrneSNMTKmmiKSVxW9dZAY0ECz9OWaDPvib8d7B\nSY8q0BHDcZCysnfwhMdmqDoGr0s0GPV2tvdhUv1k0fiFjd627uLh60TgwcH9\nic0sNdBBd2blhygx2TEtBXFurcnjScvNprRQCQQtgS6D7YOUVcAdQaIwAmgD\n3o9l\r\n=JzYM\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"365d502eaa0ba1f9799998f050d36073e943032e","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.7+365d502e","@opentelemetry/resources":"^0.24.1-alpha.7+365d502e","@opentelemetry/semantic-conventions":"^0.24.1-alpha.7+365d502e"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.7_1628343184139_0.5476851490956296","host":"s3://npm-registry-packages"}},"0.24.1-alpha.9":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.9","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.9","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"40822431d49ba09e480715241ce32a600ebbd2b5","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.9.tgz","fileCount":147,"integrity":"sha512-yU1c7NvvJMbNxlzL59OZ2mf1PYPcqNkxXcAL5D4UD04PfmS6d/NIVfzYmAs/leGhZukMiTofHn/KKw2RTs2cdA==","signatures":[{"sig":"MEUCICM6jKq78kih88ikV05KI25KywAyHyO6jp3PBZGmFbIqAiEA9UFwNt6nOazWH8/ZCGq8gGixg6P9FFMRuG3B8XiE62Y=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231300,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDpQ/CRA9TVsSAnZWagAAdfcP/RBEDRX9XNfxwhOEx3Xc\nZyDOEW/F9iWRJ6IMmUPB6xjThi6h3I8pdAeXqgRTCDEJdBLuT6Ja0dXJd26X\niBanP3KBWQAZubTDzSGlR31BLg2oeFPZvcuKHmfEHXCB7hyLCnI76WO/71P1\nL3lj60b3jw8/F0y9HKg0bKVhJSnCoNSTKIt5KcH9hLbOrPusRvWc8t9CRCfX\nAaHDlGz81M+AgIIr2ibMH8z5OrRK1U9Bd5lUD1V4pgxujH+tjeq27KXI//JX\npHtJlv/SbELg/0pU0JrqZKQpS5TfwV/L2Z8qWXQmDYMnxdYcUa+tjDOwo/zs\nFLIs4OecNpFpZAyEVeVzGMgbgcGgTOAGixZOzDGBqL7ZbOqKHn/njvA3WX70\nG/nZKBThXAl29+Kl2OHk5i0FfO4BDUZWN6KqpFX7zVnUJIlpVj/5DCDo7gc9\nOGCp8pNpN/kSqocaD8XeQ8U4CAoWUqQ5g2/fnLl4Mui191Rngtp7q8RiswIf\n2qAaWf1AJvRVr3nDycfrhw2FPU2sMgMSTHnWEVpS30mfkPwPaR68rkYQvzYx\na+eH7qvwXwnIBtUfb1Dal3njr8g34NfOp7rBUONHs6JFV07Jq/4IVLvkoGWI\nmZ0GqVIn8iqrAkfHef2fTPUQr+i89L6ItXiFdfrPXL29qpNwPd9CfdIfx8Qm\nbi/V\r\n=h0kW\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"46a42a18570da8a0b2ae027c80018ebfb6c8096f","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.0","@opentelemetry/resources":"^0.24.0","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.9_1628345406996_0.3167053382863443","host":"s3://npm-registry-packages"}},"0.24.1-alpha.14":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.14","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.14","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b670fd695883a066d7783c43af630c5e6b13d558","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.14.tgz","fileCount":147,"integrity":"sha512-98OBTMreg/uqFobh5u3A95fILVzaRDsixo4YR/QwIbjslLjyXt2+iJgUOtMN9W4O62tp4eeedrGqnINpOwfZPQ==","signatures":[{"sig":"MEUCIQC0zLhhndrpHsprXHc/NlLTppr59Plb6Uj3PNUl7lgN5gIgMwFK/IGoPqqcXjXWMAdWJqAA5OtrmqWa/MKWdnqdFwE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231337,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhE+POCRA9TVsSAnZWagAAgpUP+gKVAuimNG145WYxTnlQ\n2aMnt4P8TqAKtSToIaJtUZLCkQypCs+CM8EtQcJfbwh4xgQFLdqGxA6D4gPp\ntQ22hVMQlp4TyM56aepWk50Egh1niZaJJG8fzbIwHhEOiP54mD+2yP6wGE9S\nBZZO7ytJBOg6jlGum7KnGvgqVTF+h4h6kK+4rxmH+/HpyOwZLk+JGsABQLQl\nrnpglO6QzMocf0uuEkcK/BVHMd6cD8MLOaJ/ZSFD1UdvaVE+pyk3xuoK+3/u\nTtoUmgYZxObBRNBbHuhNfKzQCULFzMElnFpAZWk3dYo6AG/P5+N2ARpCjfZJ\n0jHyeeElYdK3j6lL0mjfTmYNYs+4DlFjU+Yz6jQevjLSJaGopWnXTaekVZwq\nSx010v3QPh2nOTfGci7eFwuILNrHQRy8MWq30rw99li55z2Uoa68kWIbDPsn\nfsmhTh8k4VWFVF3TSWDEZuLBbU/n+vVhVRkLk56bXsxWZ7xQOSYWAOQGbjJS\nmztmVq/5jC+4WGeZM9Z3geCbo/QlJrUampC6L5izl+3PIXanbuOw/PYISJ5b\nteE1wjWbdqTfA+8zABiI506kR6ssXZHi3w6TPJA4x3Jq80vIFfmv5h6rF9Oo\nEW4xBx0fyxpOruWeY6YZNOc0uGXOZpR6FXXiAzaSr6l256cJ/6rVGpPNRevn\n0vgT\r\n=lqQF\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"4553b29d4a04b5b7e4bf87cad64dc2fc8c740d8e","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.14+4553b29d","@opentelemetry/resources":"^0.24.1-alpha.14+4553b29d","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.14_1628693454833_0.767569219106649","host":"s3://npm-registry-packages"}},"0.24.1-alpha.18":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.18","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.18","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"d06ff14f49cea7c13b31bc3553c1315d9c434a9b","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.18.tgz","fileCount":147,"integrity":"sha512-QE0ZqebO4BJpm611b0165kvR40LkHvTfps406bv/wDO7SNz/nMXIv2sUKhJBqGSRK+2/jUG0vMvpOkBK7HxRZQ==","signatures":[{"sig":"MEUCIFToWHfCxokuNtSvaj4t8JR3GTRFdGOLf1iy8g33VW1vAiEAlSReugAeVdszYsfnrs+VI2Dd5XxVT4++YH7PJZnUMyU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231319,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhF3vJCRA9TVsSAnZWagAAiRwP/1YNDd+IH2bLR2ut+boE\n6PAa/4KiFKMpw4j4Go89kGpTfOBz5nTZWQu4+A9omScvVvBY7XBpQLqRTyRl\n1fmS3lSPELQtMv+102sIcGkz8zvnow5tVg2HNUBMG2xLXDSjR7LX9ruoMKgY\nAqMTCjSRxvTey6s+N0PBrpGZio+7oaQQocDqxKitiLkMX8EiHtcieY1zAskC\nElrtb2hg2najiDB3U1LKehREnNpbgjT3MWAiIJDWpyaL4SCG0zl127ERZfNO\ngv3y8m3S2ndB3MrKghC5bQ0xxfMYjBWGfVh5zPZAjiRwMAjXyXVeLgiHioH/\nwKWwQcWqEAcFOh3z/S0Xkx/MDNXZL+yQCXtE7WOzxvb+GT9errzYPFMzyBUq\nG+wShRLftqrEGzbo36fCHULYXGEaxfm2TF6HF/wTyR8JsNsNSzX2szdvUnG2\noWNE1owerG13zMQPqQqNk0H4qZbvLXE8F4MWPCVdsaWuyxX1Lhll9eknbBq4\nx5RQxfBQNGrI1Iw2sR8wGkMO7liZRqLBkAa/FlO0M0/n5ODxqT9QMBPk/I+2\nlT/fg1k6BuO7I3rgVAAYPDqv7nHLD1hCKbZ9dnkonZPssHxtY5ZLedoYk/G6\nfCSklauAiAjmvvZvVDzvxLtaYOfNnF0O+dyi5va77RMaaLu5AjHmG3Zv+9TR\nBNgS\r\n=gw2o\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"bdb12daeb2e4ca3761d1411125f5d883471709ce","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.0","@opentelemetry/resources":"^0.24.1-alpha.18+bdb12dae","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.18_1628928969813_0.9380634139248278","host":"s3://npm-registry-packages"}},"0.24.1-alpha.20":{"name":"@opentelemetry/sdk-trace-base","version":"0.24.1-alpha.20","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.24.1-alpha.20","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"de349f8b90e4987906b14f064680403eefd73f40","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.24.1-alpha.20.tgz","fileCount":147,"integrity":"sha512-T4JgUpq3fRoHqL8RI6vghZ0Rf8Yusm7y5j+ZeS4gKq31KedB2AzZBNK6jRWprJc22IJ5/YP+0HTBA17euvlkCg==","signatures":[{"sig":"MEYCIQCVUqO1X93DZhqXTmIXc+aU8CiF0cDSHzoHjXz/1VsyRwIhAKBtjWBbfY4bKko6tzQDg1dDWc6N9gZQ7wecvsRn4PAO","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231337,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHCT7CRA9TVsSAnZWagAAL38P/RXZXrIkyb9DVgqFbcRH\nzNfnDhVtGTMNJbgXkZdVATXsWy4X6IAiKKZEc7rYIRjaYslOKdYyB5lgqTty\n8BL1J1LNRIXu2H/X/qviyGcV1P0+M1otFZ1v/edn8qCbO8iWjLHJ23Guweb8\nxQfkKeZEETeL13KzxZhm6Gy328e3o0Bd5fZBnyZ9jG0vWesS1HBkjHqBxLKd\ng0qADSfgPtSSlJxKweLGqrqQBKjHpZtBmOKJdXLgLHKEsSfPxgl8Si5tHRPa\nvLX8zYVDFCk8rryu5fqLpWe5d2o0RQRzWnVcw3FA56CZBxUpvNR3WlgFOgil\nWntcmJoGH1uUwWq3d6VqIPfVrapMiQknmGI3+HgzjzWFIphGaScawwnOjIVl\nWlXYR7CECUSrGqa1jwtspbfNO0SfIg922CBxWsC6iysJLsyfa6ziPyzwjJRB\nqnmc/bYyIpbEYiQ+4dOT1TJIK8ewtRJcTAVcDwl9/VSsn+GZ76/NdZldD+/p\n/tg8WZfPfp4cBjLhwBoBZ+a+rJ1naB64wc66J7aIfM2eRpUY0TexCGYx4USz\nzON5xTVvqlzpqvJKU+K+HctdzBsv0wxYs4/kQ4suR0NqfFVvpB2BZIe9KzH0\n/f4U9smqyocc0yHybZxG6HbF2HsbRR0vn+Yn1iQdieh8g16Wljk1vtq+QhDL\n6rYy\r\n=9wQp\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"f12913899ff5c588e10830e5ba7183d9115c3442","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.24.1-alpha.20+f1291389","@opentelemetry/resources":"^0.24.1-alpha.20+f1291389","@opentelemetry/semantic-conventions":"^0.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.24.1-alpha.20_1629234427062_0.7964990125133731","host":"s3://npm-registry-packages"}},"0.25.1-alpha.21":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.21","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.21","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"90130b88074239b003905ee68653a8a62aad8ca2","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.21.tgz","fileCount":147,"integrity":"sha512-W44WsBbrmkNKzHlbrjnoHlE8JLl419V5Vdhave+IJ6AmAdmsZQi7i99DgfcVVCyPiXDEXRGT1D6VI6d6MaRWHA==","signatures":[{"sig":"MEYCIQDNiLqpu8u+Cs1lTRYS6RSu8slzRafP58p5LM6Nx3wOzQIhAMhg8xKg2GTIQic/3j4xSjcKfJuZRhndoRhISTpOY7wh","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231355,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHWqqCRA9TVsSAnZWagAAOAoP/R+kSqbk2lLBLgSfS4tM\nW3P+ESNSTUKhzNGPD7zvhtFXa6p4lzLLUk6q8AIviCtXfHg8FryZL/94r0Sg\n2o9+PEY2MoR8dOdMw95ywLXJa4UfaY1QUOFaMYCV5fGigEfhLZXlniauYVEW\n2LQb5dTdiTsXGW3FLdnqLYPAtsnE7NQ/aoaDrHGIAThtSMnv97VMpz5lKk/Z\nEeVWdzLQ/VZjxtxxbgrnk8VqcjYq0bz49PC2HdzqeSq1EOT7hShW9cZ/BIkf\nKkD5vZZA90fvgduG0as7yc+KwMVeqGTkTcecdykS/cflls0ptJ9UDOvTpYgZ\nrSggs9J5/TW6UE62HNYyiSzNbKFC94OX+SactQ0zRp72yJs74wpD4UpQut8c\nHoeqLsmrWh7Xtz5uvgwt4100uhW2FV15jkwinhMMK4IYcIV+iN0M9CiouKbt\nA9wD0dOMlvC3rifnF8fn5D0PYe69QkEli6gJxrsiRfzOAXoO52KutFcDsQPW\n12U6XfLNhdgoL9kahXgEa+g/8U8s4IUXVYztF3DqIKRMBkGhf1R8cOwL0lo1\nIGYp3BmpyF7NQ3IKmV4mn/4d/qjXu1AetYErAIjcktgs8tCgR2Uz0+6A0pCR\nh6Qc1ZOlinidoiuzlLem7VUIQsuP26zaD6xJgf8BWg1Oekjroy3+KuL2S/XT\nwnjN\r\n=ah+v\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"0ef1fc28d366b74d98b73b5d6334ffdc75342fe2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.4","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.21+0ef1fc28","@opentelemetry/resources":"^0.25.1-alpha.21+0ef1fc28","@opentelemetry/semantic-conventions":"^0.25.1-alpha.21+0ef1fc28"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.21_1629317802400_0.8977428987171272","host":"s3://npm-registry-packages"}},"0.25.0":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.0","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"4393903a7db8a5ae81a99c4a34121df67e4fdfbe","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.0.tgz","fileCount":147,"integrity":"sha512-TInkLSF/ThM3GNVM+9tgnCVjyNLnRxvAkG585Fhu0HNwaEtCTUwI0r7AvMRIREOreeRWttBG6kvT0LOKdo8yjw==","signatures":[{"sig":"MEYCIQCti4VKK/PEN3BO7Ga65nTpCQWZUAuemjlsTmlgLCCXYQIhANG5iAycgkYjQtzcEkRDDSLBlDon1b+0nKg68xzEpiov","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":231280,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHXi+CRA9TVsSAnZWagAAZnQP/2uVRo0am5dw3Xuz6WG4\nQhlmd9qcn6htXQYGAPH7eJ6ctlEl0xtSXDbkCf0IL2RU62nAhac794wKLdfp\ned9yI5B9HghS5LdapNWxeOVJbUhlfHUo3GKMagps1CTkpuElW/bPbWPgK5JM\nuRbHCx9O0MdY1iSzQm6yMd38xjI8EzA6RSo8xg0Z4AkH8OwANCsNvblW28OG\n9UZe+iAWYKOo0bJbe0WB1zKb41YMAhKczKMvo+yoHFPfsuyZYErktOsfje10\nMHuO/LmcSClwrX49MyDmXT3OKb6p6/bKubCqNvYWWQx/jDTua4Hjmj8OgSXh\nziD8rNObdGCLAqltz/buPhb/B7N27f6jzjB5jNkm/LVmL9Ujfkvwth9vfqk5\nE9g/x/pjHvyTaAX42UtJov8hDy9iEaQUqKIaUoDa5URlgzpYXaV4Hz5Cahc7\n7JyUi7M5cDK535/rbEfXUErpstOmpvvv0ATPhZuqNBzAOkAViyuLRgjFEVqO\nSOLCL/DCmhwcO3KOIzJRH0Z0h5CojstFXbCKsA0E0uLNLAl1UnzgAPFEbt/2\nmlhjPRIThzYOFwJysMXAOaJNtjk4ZBSlXan0t1sxj2eQplG+1gxpin29yDfB\n8gEKMkW0V6IZ0PVMwN/b0PSHvcnuOPEg1A1WKvW09xe/h44ww6z+Tf2VfeAu\nV/HP\r\n=aFb+\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"0ef1fc28d366b74d98b73b5d6334ffdc75342fe2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.8.0+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.8.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"0.25.0","@opentelemetry/resources":"0.25.0","@opentelemetry/semantic-conventions":"0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.0_1629321406548_0.048648051511765544","host":"s3://npm-registry-packages"}},"0.25.1-alpha.2":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.2","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.2","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"4de3a67970ff6eadf12b06710f5ac77e6c407d89","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.2.tgz","fileCount":147,"integrity":"sha512-OhXgn/tsEtvxOVZ5SQXIAV3d6pFd5rMMTPRsUK8QTHsSBVSOsKALygTvcRwPQJlUdSdJkxGA6NfUg8pOx1OjHw==","signatures":[{"sig":"MEYCIQCR/JgLC31LcnbSaR3Sn6LRjqitnzJq1b7eT4nBZ/apcAIhANLoKitgEA5BztqHIcLVhrvML97Bk2baz8xORcry+cG/","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238819,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJBX2CRA9TVsSAnZWagAAUUYP/1l0sLT+uC/JTj1TfINp\ndWHrPZhqeMMiEVARoPTILwDF2mZ6lWUn4OWe2m7waueZhQw1i9mlD6QKtNeA\nkps1z+L/WUHqlPzT2HKHG5JTi2OwkNfoO5lNoU9tpuvhd7t7/PWSg3O3S2rY\nBSPt+0pOne0mBD6uRi4yO9X4iasOS31QZJvNmkEdCBRy5K0yx2u1m7LzLzcA\nfrb49X5PJMsAywkJKEyExq0nbkcllod5fw36noniLe5uXiScWwrGohxWlnL2\nEzgRqYqcoWMZg6MRVoh3K/VwymhIyxfB/kailGv4dr6wU0dUpiBwVtBdADW8\nkoMx2LS/vDTva2sMp/W/4P5mt3Wqcy503X07N9V10vQolUvRR0hcoXV+OU41\neRbZVWdFWXo4Q5I4D3E5UTgIAloSlVNkanra66/p3nNWtTLakNcf9EfQRl/P\n34n5E8eTN3Wl0O8NiSimEVHexew26ac9aSEj74BuKBWEntPDQqGAWS5cg5/t\nzUCFSzDIjMNDlNRBH8bprcJKNWsG0V3ko9LsngyW8eA+CCwRhEOAcOqvdEwb\nfeBK3PC7TMNo4WuNufVgCm8ejH+yP9u7c3dTtU4Z60y/BGvvLdMZA9UMhABD\nFEUQ0lGoOVX2ALBNQh5KSMk/085WuGWj3dT5E83OKnVy1uD3mgslbdcNpj/Q\neOKC\r\n=tFMf\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"78a78c093c2df24b66c47af4e037da9a6098fedb","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.2+78a78c09","@opentelemetry/resources":"^0.25.1-alpha.2+78a78c09","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.9","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.2_1629754870123_0.17987001772703892","host":"s3://npm-registry-packages"}},"0.25.1-alpha.4":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.4","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.4","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"29ba973f0f357c64da5ece13e2565f2745500ef3","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.4.tgz","fileCount":147,"integrity":"sha512-xwUOlftFr1dN4Utp9Scu0+eDbQ9oEsQPJ5cUu6I5WW8wxURqV/d4f6UTL1bmTxm5x5FlS9ggQSVBiqHeWoAZEg==","signatures":[{"sig":"MEUCIDIO3X0Ip0Dqcxe7kAgyn4rie4xDhB062Pc/p6GnpywmAiEA0kK9ptzzM93v2jgnVtEt857r0cBWxnTxxDaE585k7vI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238837,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJUl9CRA9TVsSAnZWagAAsmQQAJp5bnBhFSjaYGNcLpjk\nB5rAh/7iqHO4cK/jNHYrlao+r6fp7rhFItt2PLoLrmuhl0TXEVAKbGp+GTIp\nf0Sz1A58zy+7dBlRK3X8VIYNpg+L0m+ujLNzk+DLbpSEHMLDXfhZ62DvYvpc\n0J6IPGhnTdrd/+HlHuhSzduClCAgmLsB/wZZo+GjR0nG52GKv2ZP/l5+ZAfN\n7FOiXgEp3xmYTyNrqfx6gRXpCXyjs4Ot4vAMf+DLsum0fkWtBvT8LyMJSuQs\nZHXEQGzbiBYCJELA7agamKBv5rxPIBYuELbDJXjDNwDL/pQDnB8xUTNoOjIL\nfG3B1m9bE2+6D8CZVI3ItfWmVZw9XhARocXZ6DYaQW9xuJ23ScSgN+CKUK2o\nyls8V5PwBadpJg59jQiAfOvWHCNPWSAGGttIXMbMfKTJ5v4iFv+sHHggS3h4\nvvmkvpUMPkBFHmlBMQLvQcqTuS+hffAylqelzHO445zEU3kDB7LfXy7v7Rx1\nH0zjn1cd0U1kkpGXrpIGW1czR5vChsVcDnUCmS8HzQmSU0+l42N8prjLPo73\nOHoHbGctUsX9j9IOhqIgc5GupPs6x4dJgzbYpQvtXygIra8kxl8bLlZLsJzy\n1CM+5GfaC8CeDreDh0/Y4fTutqMTV/s6ryMFupq43x+ONvyLU4Yf/e9O8bee\nn8f7\r\n=EjCd\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"3cbd530f2ee5c06376210402eb87ec9e362853c5","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.4+3cbd530f","@opentelemetry/resources":"^0.25.1-alpha.4+3cbd530f","@opentelemetry/semantic-conventions":"^0.25.1-alpha.4+3cbd530f"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.4_1629833597428_0.24765430996019488","host":"s3://npm-registry-packages"}},"0.25.1-alpha.7":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.7","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.7","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"2df18432d5a71f0e770a8f5bcc5c84eaf4c0c580","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.7.tgz","fileCount":147,"integrity":"sha512-3UrzA71WObun2L+NbrO6iJGQqSGnXqLKT04owUtWO1fVUVaYulTy45zu7caA4Y8GrO7BKiCglzICu8SfQ8bwxQ==","signatures":[{"sig":"MEUCIQCD4OSgZ7jj2n+j9BFZLCDxoe3drQp5qh+1CxEqKGC11QIgYQHLNxm/CR/1WEeJNhFs4v4oor/75XOXZdOmw0ln0oc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238786,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhKSzQCRA9TVsSAnZWagAAp5YP/jVVh5NPgHRzsGtznFca\no/xeMefN3jRoaXbhtbmQbLs14jL3/AuJ3yn0rSx2u62jFSchuEzGh9i13MBH\no4PrKBlzOwnvYtoDjlQ7ys1ACuusXh+ExTwiPO5X5bFnahDVXzfgTpjzK9GG\nb3bWA6O5DzmaX6UA/929snOdPssOBSFYCEBBWonwBmBBJ+IMEjSPy55RChHz\nw8GVt5A0IF5W2ZaLVdScdVuRCoPAZ6LRd8bpI/H5CnmFPxtfX1YW4zjZh89g\n3qWuXKGQM1wtsJT3oesgty/guyiCCrqmeS/fxbY0+L7F5FirWC6Aj0XsGy/j\n+iZYPl/Y41Oi4EixjREKMm/S4lckyMY0gv3EhBMrkg3jGBRZAYIMR3JKW9pN\n9VTGNpG3Fd32ZyViRXtDcfxMUr2P4+yaxHRm6ERFwtTeFuBTgEXWyHMsSWIc\nONmZ1iFb2gvRPh6E9UVpnf3mSjujXIBRzgyjAXNuStbeIXO9MZI+R57Q2PsN\n+vYY1qtHv2/ygw/2Wu985Ky1L0kqOKQwZ1HnX8hvvn0Dd5cPSUlHtA8Oi2HM\n+H+Jiz85aeXsHuFb2beINmoV3g4IO1HKsfrYXxRZOskLHD1BJ4SIZQ+ohhQV\ns+NIaNtRmCLgm81Kila2zo7sstnpVc/Dmv+EqS7KUjYFZFvqSk2RDvKexuIV\nHV7m\r\n=bXAb\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"dfb597def863b15b37b24d965018e8c92d2ee70c","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.0","@opentelemetry/resources":"^0.25.0","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.7_1630088400146_0.9518154290635452","host":"s3://npm-registry-packages"}},"0.25.1-alpha.12":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.12","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.12","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"690d9c2a6996aa8750fd0337ef74b5e30153e965","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.12.tgz","fileCount":147,"integrity":"sha512-L/bR58bGua4XoVFuBzOmh91UGrM5rhPF8n6LiWPe41EWJv4qyXAJGaCSAJvGjgM+QhKq10ovLIP3MBQnagZEcQ==","signatures":[{"sig":"MEUCIDcQU49VPQ2p1OxrCZnRJRihRmBNOGV76F9zIkvMPDmzAiEAnWJ1gaHQf3MPAi0itikgidfssSjLWuLlL6naPsd085s=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238817,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhLT3xCRA9TVsSAnZWagAAfm8P/iVFL3CAczgQ4gh3XANX\nxkvZ72MAb69BWMUzzZcUXgHg7h7mJtwFP6AiZtXjwqUt38mVicSTqAjkMxj3\n0v5KbSYCmj/TygAvdFjeGtUnohA+aTx+44GA4LGYMh7yCXwg05hM8kuL/Af1\nZO3PbVkW+zzM+BIoO2rrv2DR+gm8TMhQmT2842k1D8+IbgB98cKpBf7a5qVv\nmPM6fWCVa4WooTwOlJdBeMzfj44yNhhFdvEzOt8asaHtF+EiSzmm9fPi9woT\n1ALWnpy9V4fp21wcNNvbDzfoHqb7A/OTlu+MMntLFmEOzs18HPOL+AFJrpKu\nL+eP0trSPZJF+cMdlwmkxui3J/glPGAg6KEIjJnw2lz5H0w++6nbt91WXeWy\nPeof54TJ6zjjivE+Lukl1s/DcrHWh1oUaPbmvZesasB2AiEw2CeN9waFHSAt\nlSM0Z5W0d6PYzr0OxtTCzVgCQiEG3i5p9sWuBdDktdC/e1fYX8at9KB2amfw\nTHuadJ9LDZpsDPann/BpXw0KdYLPNMecKTwfLAoF91RXCoSJ9lRlGB+6LiX6\nrotDmt5uEpKWS9QkhfOaOZTnNErdWPKfbk8B+ZhdYQ/ACCbDBbHeB04iL7vZ\nNvAH0OQwIR0XmbPuxgaNSqgnuSyPDm2RVp9y86ZgliuEZu/Rv1bBUi/abbS8\nNuqF\r\n=egTX\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"7d2c4aaeb08e6c680f8b46cefcdfe955d7abe4b2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.12+7d2c4aae","@opentelemetry/resources":"^0.25.1-alpha.12+7d2c4aae","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.12_1630354928769_0.21178081484243938","host":"s3://npm-registry-packages"}},"0.25.1-alpha.13":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.13","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.13","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"df007e5f5653f164069be102c6bcdb5ced6669c1","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.13.tgz","fileCount":147,"integrity":"sha512-7PotUD3giX/eHFbNvp5UJaQ+YqZ0u3SyLneG7YKk9uyp3YYlU7UFfcBfh3x0z22FPdMArJBefZEHymbKTy3DRA==","signatures":[{"sig":"MEUCIQCpK5kc70OjUqwOaLJEQ20suyEP78oq1igPUb6imueg/wIgeJOCs8SP/YX3PR6BgVbVriWZkIC84r5jJDE1x7uWqN4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":238972,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhLUJ6CRA9TVsSAnZWagAAnOMP/RLKjP8Dx0ueknV/3Hx9\naaZtc6QtAxqzRoSLIp09C9EHhbXHEu/VdCFKYnfWmqdhraU90m+RDULLepmN\ntXFJr/sK3hSE6rhgjPxsaZ+mPhE/FxBvfF4K/TPRFOVrdprNasw0KM5CjVFW\n+oM0rbgasDZk9h/ir030Fw5XxzxMFdWFGhOFV7TITIngA2l3aMQwdRWmm5Zx\nP2jiYTBw+JDkBO2wrB3c4nfQmuheKlvHx7trAmmBDktgKluMLsMMYagYlwt9\nqf+AArAUBn+R8NEcBoT14rtl+h8kfKeatJ2BOzgidACkDcSEAiKG/UhU08nn\n6XyFbVLBqb4pfLWN8jeu2WR1CFPvOtfppmV0fOPDPfSnDhnyXP+FpKKo32Bf\nCk7yhyCqGHmSfYD9EDCMed+9UEjTHY5dDjPOPO7z5nJIFgo+Fs9ax5HtNh9U\nnQQOzpKQlwWmrVOheENybm9slkFaQLvGzziZNGZMG62/X/D+fvRUCK/V8vo5\nz94icTsRxCZ4pSbIIb9+Bj9lh4Bx23DuUO+UaMma0FhVImsOhjWY1aCEiV3B\n+hAtC13RON4qT7pqqtrG/5rIKEET53U0gdoggJaV6aiULeoQD5LNWv7SOiEE\nEnnBXPqScP2/n3TtwzoupT2PumMmTFBPKyqel6htr1JhhoJPW5gA/Itq1eTR\npQRk\r\n=wdjK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"f0caa22ffcb26af2a2f05260f138a494e120a955","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.5+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.5","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.13+f0caa22f","@opentelemetry/resources":"^0.25.1-alpha.13+f0caa22f","@opentelemetry/semantic-conventions":"^0.25.1-alpha.13+f0caa22f"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.13_1630356089912_0.49133518504216656","host":"s3://npm-registry-packages"}},"0.25.1-alpha.16":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.16","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.16","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"3e59bd15213c7ed7a95407ac657190df9013f2bb","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.16.tgz","fileCount":147,"integrity":"sha512-xYPEm9jw1p9+Y7SPAHDcWIC6t4sewzOxfu5DqMxZE+Ze0MnQMatwCOvdO2EPMy3wkeFY7NJJjxlavVpzXCAVMw==","signatures":[{"sig":"MEUCIQDFwR8aIHNvMiRyvUYQQVlvKKFGnsfwCD+oVEztecBHbwIgFogvaGvTP+WztOJIkbiQukLk4FTcPJXQmxX1hSpbDNw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":239036,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMyi2CRA9TVsSAnZWagAAVkoP/iuad8ncSZJBEpyUK+Sy\nKYe4lSKqs2mQNAW//AUNRGAWbY95PLIliKCZvserpg1TQlcNJGou2aVsNiaA\nARU5FnQXb3VNgrVBlYGcbyJWmlZ8kGwwxFigb/q9vV6TmLdKUtaOBD+9eGOD\nnz3+5LXPV62uLGGs9L1eX6WgqAlEJRab0IsVGoSN6hqLu+8GnO1cE0zzuc6j\nlDces+kFs6UuYbADMSjnRmac70xkMDuDYWYBycW8Vu/LttXVklSemCtERjgL\nwjU5SIEqyGLmGyp5+YMvO4R31FYThwG2Vxumj10HocJoWi2nEHys+Hhu6fSL\nr6imKBDFxzAXNPtKm8dWpAtPYbSYr7w5D3lPmln45PRdoPXqqD5lR25ywGYp\nXZMi1UaQP/eWZ6kXFsWHwy+VyZT2aX/51BOHFYxlqhtlEFX4aGb4qwEzzP3/\nFnKltC+lEHykB/hSGiMjuXBcnqo47PDpIPSBNKtqZ/K+vnuDr9jAh/lBFdgk\nzNaE1ut507pNckfk77+QVnqyTH+abHmQZkjBUqPEY7Ljuq6IzGiQvPXmR9yq\nM1acS9Kbj2qxlmN8TmXRm0MD+Ncy7Ymayk4E5bkw6CI/mwLF6Ao7LsLGNWbJ\nvk8tOt3HCBm0z12FzQiEAZSZQMcb7ylznYMfNOFE71N4F+OhCP9cbjG4jfNE\nMw6a\r\n=sEuG\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"63f6701081e3e4a7eb964bb82cbd8cbc2eaad347","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.6+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.6","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.0","@opentelemetry/resources":"^0.25.0","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.16_1630742710731_0.6843385580327839","host":"s3://npm-registry-packages"}},"0.25.1-alpha.23":{"name":"@opentelemetry/sdk-trace-base","version":"0.25.1-alpha.23","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.25.1-alpha.23","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"678ad69cfe2826f4f497f1fe0484576db5d23e88","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.1-alpha.23.tgz","fileCount":147,"integrity":"sha512-+nwTWJZoNWoc+zAE4yS+U9q1+qGD3sJGP3qUW9CmObVVW15ySP1tl3BDHiBFcB7ILQAhx/ObeS+pWZN9CL5JTw==","signatures":[{"sig":"MEUCIQCOW2HsF3Q8kw7GWYO+2Xm8AjoSiyYLsaMgzRUUWwVPNwIgFKJkmK5rUEfZxZM+R7Xg991DAuwoJWytg4sPsNJE6iQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":244906,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhOTXwCRA9TVsSAnZWagAAt4gQAJ4KGXGYny0wx+AEs5o+\nhpOUqvkyxY0FZBmS/XlF6Rx1MnH3zubA4925uZcK2giaQOMn3Ap5k3UkeDjP\nxT+btSm5hZvjnEjFBhA1zAo4Z1Vz2hqdj6Bc85RU5mUCFDNV8w899h8Xj/F/\nhwSLJwuPEL/MnwptLXf9NEvy9S+nMNy02gt7sbf/fK5dogJXxHAuZXkoHQst\nRl5U0pYESJtFCStYitzP9/vdsxjIv3pbPohrFxym1ifRKi7Is/YZf7Ege81R\nTLGX9aMH70AQnR75LwvPq2ApxQ2F2xbwDOhE6gbVKH+8F8jLBm7S5OBMzEwp\n6/vepagI7gU9xQRu+4j6XbYurnJf2ZmnulOFmARz98hEyWAvtEd9zN0E8zVP\nMjPO89/UVixzYUbXJz5e9EUP60/CoT8ETl4eHF3mmX0/FXmde0xG6KNKEI+u\nd950FsxHQg+WBfcC02eaNsjyYvavO+zxtAIkehnFOfXe1Mgb1/KdTKUhsm6d\nkKR2loyRyLVYxPYCq1YEq/a/NJCJZPofYqJ7nJA/0Y6gpgwKG1rXzPN1kcNQ\n2bZYQDwSCmAsVmBYFCb1fRTzM6w7v3a8/vFKcv4Mf+fZoaW8YrKlxm58lXqL\nOsdPIpiiH6ACsk4JvHTPCxoI+CpDwxCPDl7uu59VdF9ZgIv2M8RJr6EwQFKR\nBa93\r\n=b4/p\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"feea5167c15c41f0aeedc60959e36c18315c7ede","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.17.6+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.17.6","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"^0.25.1-alpha.23+feea5167","@opentelemetry/resources":"^0.25.1-alpha.23+feea5167","@opentelemetry/semantic-conventions":"^0.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.25.1-alpha.23_1631139312167_0.5525742629314336","host":"s3://npm-registry-packages"}},"0.26.0":{"name":"@opentelemetry/sdk-trace-base","version":"0.26.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@0.26.0","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"9f8a0b7e290d63eee67c5a5be921ed21d293c70c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.26.0.tgz","fileCount":147,"integrity":"sha512-SqV5pccxJTekOXdE1jp5VWxZ7GYw8rrHy7g0LcxLzn+D8YJ9ZBhE481VrP9EsjyLlJRhicv+z2g1XFxpMkQdmA==","signatures":[{"sig":"MEYCIQDSU95sy8kG+hL60kiMOWHMrqvjMCzH1W9eKUABO3bf7wIhALdpLXNwZ59gj3Dz+YO1EUu6ZL6nTEr5VqN09jbTbkrV","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":244852},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"fa2e897587a2441205fd085772d80a0a225ee78e","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.8.0+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.8.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"0.26.0","@opentelemetry/resources":"0.26.0","@opentelemetry/semantic-conventions":"0.26.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_0.26.0_1633005332268_0.16476052719806322","host":"s3://npm-registry-packages"}},"1.0.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.0.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.0.0","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"f025d517fa2386ed2ccd534dfafa894ae323dc2e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.0.0.tgz","fileCount":147,"integrity":"sha512-/rXoyQlDlJTJ4SOVAbP0Gpj89B8oZ2hJApYG2Dq5klkgFAtDifN8271TIzwtM8/ET8HUhgx9eyoUJi42LhIesg==","signatures":[{"sig":"MEYCIQD0Zq61lp2goYcIDSNYrkSWaH2vcZkgym51d8Qv0RnyegIhAIP2T7OO8FtZ4nr9eNSN4CCmgB6oUCf+qdYOyzGHX0Cy","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":244844},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"69b925d142a4405c7c6bec7deadd8b4e96c7d5d6","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.8.0+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.8.0","dependencies":{"lodash.merge":"^4.6.2","@opentelemetry/core":"1.0.0","@opentelemetry/resources":"1.0.0","@opentelemetry/semantic-conventions":"1.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"5.2.3","mocha":"7.2.0","sinon":"11.1.2","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"^1.0.2","@types/webpack-env":"1.16.2","@types/lodash.merge":"4.6.6","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.0.0_1633035223758_0.4156184977353712","host":"s3://npm-registry-packages"}},"1.0.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.0.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.0.1","maintainers":[{"name":"mayurkale22","email":"mayurkale22@gmail.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"},{"name":"obecny","email":"bobecny@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b88c72ac768eed58baab41552ce9070c57d5b7bf","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.0.1.tgz","fileCount":147,"integrity":"sha512-JVSAepTpW7dnqfV7XFN0zHj1jXGNd5OcvIGQl76buogqffdgJdgJWQNrOuUJaus56zrOtlzqFH+YtMA9RGEg8w==","signatures":[{"sig":"MEYCIQCC9fuVQZArSWRg+N89DDvJIHnMbymcyIIBAUzJymYEJAIhAKK+sjb0Wu3J5VvFZC39vqGlZ+/poFxV7Xoruc1qW6dd","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":375519,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh2UqlCRA9TVsSAnZWagAAatQP/iWXTWUBoGG4nIFmKkxS\n20Y1VIKi5N3udvMq+gwnTxYXD0sTjV01spACjY86atMx6ZHRn/0CfjaziMkU\nE4VdMnWOceoQlSzqWDLa9NNlAhmH6GV/+c36C9uhoVL8SDemHpX2wvIwv2cI\nyLCc5bdR82lnLePoFRAc34V/7jJHW3ryIbCe4eH/8Y23lNDaL1ofKXitYjfv\n1yOlvTK65xJsdQ+8CmBBQfdMqqpaw7ahrH6CxgXj//9Z3egZN7qiAhTjJWK9\n5midnB313D3jiaS2OKkb/MFrIfIuOpcL9Bd1TciKOTbDTlkFjgL/EpQ1MGSA\nAQJohsQeOYqvFIuouG+56qRqS094kMgvVYkQXLqTVHRvzxptc7Ai1H1YdKCh\nHEdWBIHzM/zdp/wKMDz1IjNrRefK24/yfWxv1YYPoCnPEQ2ZGHmfgMX4UiNH\nLiaVp8rVT6Lnl3hALEuJZrRP1Rxl0lbfPFb/ZxWv457LRJ2jsX58MsTrSScO\n0id/wDTYJcuzzX/auxRzTTfZy/vQtjQyy48tlAZbnJ8/EnTpNU+2whsWsSYR\nfUS8AeW4L5gXw1rfqJiz76QPND0ea7oa3eiiaXCqOHAvcalMzeYfdR/Eye8D\nCSky571jLOzqrHOaFsude/F9jvXgGEMUtqRbKafWNxmkjaFF6FXqyucYcLgh\nlZOZ\r\n=aKC/\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"9cf402ee4231ee1446884b5f59958ceafc1b5188","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-filtered-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.0.1","@opentelemetry/resources":"1.0.1","@opentelemetry/semantic-conventions":"1.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.7","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.3.5","@types/node":"14.17.11","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.2","karma-webpack":"4.0.2","@opentelemetry/api":"~1.0.3","@types/webpack-env":"1.16.2","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.1.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.0.1_1636642284812_0.1678333829919234","host":"s3://npm-registry-packages"}},"1.1.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.1.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.1.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"78833810991679a634f440aa3055b22fab9c033e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.1.0.tgz","fileCount":75,"integrity":"sha512-L8WTUKVG+E3IVTTJ0FoDo5YwsEkSOGMFOCBJKFH8O7uV1bFny52ASyWaNrhhl5kRPGlkHtsUnWkgnQm9Ne3lRA==","signatures":[{"sig":"MEYCIQCxrsqZRfunV5YR0FGHmGWA6ok09L/bkBxVJhAyHioH2QIhAM4ZoSV///YZjO2/7AZIhJOQmcwMrLNQo/dE+EgbnRA9","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":194582,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiND6VACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrXIQ/9FtTOkR2g4MFiDgnehThC4HE6EDI3YhK5hVSeDlu8a5kEC/Dr\r\nfaNCFDev7W7G07hhnRYf6OaYMXtZHAMSJ3mjIUIJ8HE/R898GPYZi5udmgxY\r\ngPzF0B3GIKNT6fFTnvKQ+auoQUxfa96V5teMUAXWgWvQkgAAcPgtETbG4BlS\r\nGwuXmfxovxsYyI61VvNz3yUNA9MybAGN49Sv7RW13aA95ENT8nnQwBVRHRc+\r\nEViVyVe7cDHJvxvDuGOPcmtqxGTP6Q9JRq81K4VI1P3GQsS8vCWEND0gEE1q\r\nUJkU6QNwO7QuEsOMFklhS/bLruc7kvUpwr2y9+2lp+fHpCVPF1zLzMzyV8+9\r\nzemMXzG/afyRFAjwpYk1VLhRXL+dbwL+hFn31TrwBI+NqkFvB4Bndm/D7IsM\r\n8xNJZm/CsguwKj4oq2fJcCWpGxKZPw1u0a2RoqXabWoF8OoRE+b8DM8e79w0\r\n+ol/VgyOfBL8B8td7RuxN/xzIwCepSMZ5A7RWdkmhDJmHtdOxhtH8xDcekqq\r\n/1PEhvJeqBnqEvV2H0S9zCAZrAHPoVjJSSwPaDSc9guJeeKYSIg6wWcOrQzU\r\nYXU1Thvv4hILbfUK0tc3mdp9MDVSrMxbHnyRjV0B3Yyh/YUYWUddZ6T/imBP\r\ndjQxrAeNtrmBbkWmtXsflLikHSPU30Mqzgo=\r\n=scwK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"f384303ac469914d0dbafde0758ccdae473f336e","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.1.0","@opentelemetry/resources":"1.1.0","@opentelemetry/semantic-conventions":"1.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":"~1.1.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.1.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.1.0_1647591061708_0.7080112200023543","host":"s3://npm-registry-packages"}},"1.1.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.1.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.1.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"2277e44a8b90815bb3c23515cae9de57ce902595","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.1.1.tgz","fileCount":219,"integrity":"sha512-nj5kFly/d6V2UXZNi3jCaRBw44/7Z91xH0PcemXJTO3B6gyMx8zIHXdnECxrTVR1pglDWYCGs84uXPavu5SULw==","signatures":[{"sig":"MEUCIQDXcQWtqYJJV5tMuOB7wmN/al1uzJBLOH5LbMyGcrtHPwIgMBptxEBJsLebjv3XbdtlJ1TDGW/M5LwieOF/Xd6rpUE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":552814,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiOij/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpaKA//T0QbIUoelYuNPxDaBDM0ieilM+eh5Nyy8dP3LRocnfOfGVo6\r\n1+zkp9C58SOO2k0uMNgJ8BcMgXxBd+zcS5W+/J33LCvbP2ZpCMBQrOGa6kny\r\ndV5FZRE73sUBCDkY+/81ehyOiXXLW46313zGCBgpX9p/HwivTjT16PNYrr6k\r\n3ZZiQmb9Tm68VnVLOENIqTspRHzjq7T8+9c3fmdLmiNdrhP6k3y0GE2vdg0P\r\noHNrGD2NQCARK1RLMJNF9a/8/hK6dNERoQKWydjV5y6fTV8HrIHsVLzrNvXs\r\n0YF1N8ZJgHUoqYsTdKQf92kr/j8sgMsUFxx3cku2t9qcQHffY5GxmPgn/fTE\r\n8bZZ7mPhmsf8aXih/F24cInMD/Eu8mKktpov+agemmD5RFEMTUWeDV481din\r\nFOUCRJUDTSserEa580XBRkUsdUr/Gb6dm9JB8mxGQwIWJ6ZUW2K+RqAb+FdR\r\nMCDShq+C8YSwhSIaTeCIJce8NX+ca9NtjUO1sIPqVNIuisyO6NOz0keeVKXH\r\nO0xRTz5l8Jog/Gehka1hwbtUtfcVswEyZg4SvVKWmcrb0HYVW+e4dClEBr1d\r\nAp2QgpnUaIhcVNwjuMkSrGqiTvA+ehBvWMPk6r2zi60fHImod/+0d462yI9D\r\n2rgR//laEPnSI8sHJx7q33Yi1s5qXMUqzaA=\r\n=2PCq\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.0.0"},"gitHead":"b0f8a2d36e6d1d3090c3d2380608d2102c826e0b","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.1.1","@opentelemetry/resources":"1.1.1","@opentelemetry/semantic-conventions":"1.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":"~1.1.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.1.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.1.1_1647978750976_0.4451688237419007","host":"s3://npm-registry-packages"}},"1.2.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.2.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.2.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"aac5b79dbaced92a886fb2e348e54f5b681205ed","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.2.0.tgz","fileCount":219,"integrity":"sha512-eHrG9c9OhoDhUmMe63Qzgpcvlgxr2L7BFBbbj2DdZu3vGstayytTT6TDv6mz727lXBqR1HXMbqTGVafS07r3bg==","signatures":[{"sig":"MEQCIHxDeRJ1Y7SkEGT1p7MiTG4y2cOugUZZeZ+XhrikR+tTAiBWEgxMhwJy574hneXGgEi37Y37vhBQlNd/xvWTQr/SVw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":556102,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYsI/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmonHBAAj44NYQVANyFTg3YLzIsIJhityCIuLvyhLfar3YpD7IkGrrhZ\r\nT9U/D2fN2wdSfoeD/Z7dGu89Y5/AmmPWWEEe1SV4aPY9u3mXwiS6E27zX5sg\r\nyLkIHYphlU1bDlq/FHYpp5v9ptK6SJa4epgNBFrBNfyQg2vT9EWyyB9W5jOH\r\nQ10w9oEg3DY9MtH06PPh+VLIJqslxHFFTaEC5nDIz/+yfqXjJb2F11G7TL2E\r\nSoisfBXai9rNnUebsTLc8r2J+mWwlcZWHThTZQkeBw0eAw7BWMPIaWWnbl6k\r\nzNS686ldebx1dhQjD2qkuUabqHh4EuJoMvl7KCCYgFjWmRhXv5q9zog5HpsL\r\nkDSCxnLVP4uEjnKcQ7tw2Vru6+7AejaanVDeUVkVmW9MCNCZgyFQrDvq/I6e\r\nnVLTjrh+nLY1b6ulsliiwi0uUglQl6rgX8ei3oe1C/+fKg0H5n4ysL0Wdkak\r\nlEnaryQ9b5Mu+HYOLC4leatb+Ob+Tzre+aspQ8E+I+TbFRPqEYwAk9qTC/27\r\nltLXlSNb3sCzJXIrPg/fRQXDmi3Mq+j5Et3Xr9XiifQLwq+odpd7bh7tJ468\r\nzEiU+7v17c4vB/sq1diy3DL4H+qIcwVTffl5PRDY2p2Mon66Nruumx7vgxnd\r\nCYcdeAhqcXuo2O6XPltblnFADHHs56BOMCo=\r\n=MSTB\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.12.0"},"gitHead":"a0a670a03fd35b0799bee8cc466f79e93b5b6dd2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.2.0","@opentelemetry/resources":"1.2.0","@opentelemetry/semantic-conventions":"1.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"8.0.0","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.2.0_1650639423630_0.5047606523204053","host":"s3://npm-registry-packages"}},"1.3.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.3.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.3.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"eebf6f553e49ceb309d346b8de7c9257686bbec3","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.0.tgz","fileCount":219,"integrity":"sha512-BkJEVdx46Boumgy0u6v+pm7spjOq7wCmClDFnHvQs8BS3xjmfwdiReoJFcSjo0cSlxkTJh6el8Or2l2nE3kArw==","signatures":[{"sig":"MEUCIGz/0H3rGWRaHX0jlcn+6uukc3qzgIuKUgwQEgJ/3hoqAiEAzMR9u54IyMmxnMYkD/aoKGBz0H1UhWhby2nzZXGtqFw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":556534,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJikSlkACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpNxw/9HOqfRpUb4IKDqrn+hwiDh+sC4Nbwoz1NLjZVNN8lCqDA7Bt6\r\n7j+sgVk3xxbuTQAKxpIst+8vYkEbY3QiaYEU3Xs+inT92ky2J29s0NUE1u5j\r\n4G8g/vHIuAVb2q6KE1iWi+kTynYE+7LbO7Ao7nRfpqlorKVlMjIOQNCaiOAW\r\n6IwdU34TJXrT3Qya6Z/jWjFnC0e/gG+omiBOnv+UNemQqW5Psz60bRttRAQJ\r\nhY/9U7tgptiurrkCZbj4j5/DsBvLfJKuoXg0nE4jI1Nw5H0qIW0PP8ifZAtx\r\niyw9hoFAeOWb6eA6efQ/UJwXpXEBz/ViX5bIckA/QmDgZPIYrVT2/irowhYY\r\nR9/CAxkSu2UX6tz5lKmQZcYSCmpR2gIvtKV58MwAsH2g5SSkW2biqB0XdTtr\r\nAERTzqJtgrr/kKsSJXtGuBJQ6aRmIUSBGmQUHlU/mkfr+dMfCFxZvjGU5wwl\r\nTfIsifIGkVpytY4YUrQHDEiOA4FfbYNYIA5j8rQVhzcsXAe1kk3iL6+9wQsd\r\nDrI72fSs2KsxPjLhlEyu4LsLhJ51QPdDz9ml96zd2YzCuW1zUrYQ4cPiJuJU\r\nvN4LyaSB5VCMkgpgj9SmcIQsqB9X3JNBr/+2ajElF5HawwmI6NlzADfOQ4F9\r\nAozt+rNMEJKi4bnqab+pMfrux8B7nx3++Os=\r\n=7Hm/\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.12.0"},"gitHead":"eda0b092db484855ded8b4837ba7fc19a377c5a7","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.3.0","@opentelemetry/resources":"1.3.0","@opentelemetry/semantic-conventions":"1.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"9.0.2","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.3.0_1653680484654_0.37952356793618636","host":"s3://npm-registry-packages"}},"1.3.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.3.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.3.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"958083dbab928eefd17848959ac8810c787bec7f","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz","fileCount":219,"integrity":"sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ==","signatures":[{"sig":"MEQCIALi65Rm/zKG8ZBolugN7azramCkJZwJFqjYpR2IuUmHAiAtSSXm3hpjXUpwcBWKdCGjLnayhLgb27nqMu3p74uF9Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":556534,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJinmLqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo1lhAAiUCOLc4qxk934XKQhQW8My+vNgqPeT/tMdJiJ63yEvtbTwQ+\r\n8elNZzkUfc9a2n04eOKZTpcZBdLmdz8kr/L7+SGXVA3pGGRmvOFp29YTVUQ3\r\nN+/Ub3SVTDyyA9vTIss7XlJYUXvWXp3mEDwfgS2i9VVj5gCYyC5zt/LjYP6s\r\ndnb3OlASri2XaZPDzh/QOhkT4HZUbqhnqGCSJc+8fU6IhyrL7mOnS+oX4o+U\r\nWG2Y0wvx9MlkDX4+QwJdrRlDcG7KZjdoSn6p3ORgy8/UJVAGGoUHI4yPDnFC\r\nu+tEpRgnuzKRvtsyywrWx0h0c+fBxV1cCIrHBxPsWK/TlypEv9WbuD17vdz+\r\n4Jm2ljTlDgZ+K4xnYqIV6GFTZosmUiRbhiX76cRGL0PWQAueUwaBBjHhkloY\r\n4lGbLKWxYFBQglSYwYk/QJg1Cn4hfeMspRY2BbG1MgEdCK8hkOVOhq3Cl7Ic\r\nLDBaZ/H5pH1RrWcK/OPRFuta0lqgt65FkEHl/Xkh55v+l4N5yIOJD28CxXvl\r\nsJFWeWX1xluXfMXmtat6qMBZleIvDjtmLpY4G1VzwR85IKWURJb3ePVg5bvy\r\naf/JYHg8yO4mYcfGx7PmvPIH/CJryVK/kIr+KJmH0M5DYDYPMJD6gQbrFPon\r\n2LSUFlqN+LkjbZPXM29ovaaeO24sXaCEvbI=\r\n=lwEL\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=8.12.0"},"gitHead":"51afd54bd63e46d5d530266761144c7be2f6b3a7","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.3.1","@opentelemetry/resources":"1.3.1","@opentelemetry/semantic-conventions":"1.3.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"9.0.2","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.3.1_1654547177779_0.44314908261854846","host":"s3://npm-registry-packages"}},"1.4.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.4.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.4.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js#readme","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e54d09c1258cd53d3fe726053ed1cbda9d74f023","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.4.0.tgz","fileCount":219,"integrity":"sha512-l7EEjcOgYlKWK0hfxz4Jtkkk2DuGiqBDWmRZf7g2Is9RVneF1IgcrbYZTKGaVfBKA7lPuVtUiQ2qTv3R+dKJrw==","signatures":[{"sig":"MEYCIQCLqLk4sGs7ZlVm8eZVQO/gEw5B3TPiumDqB01dNJpsJgIhAI7a5bQFkGJly7Ar3CdIQul5vtwyWdO8IL4xekGMZz1g","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":557399,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJixe1qACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpkJQ/9E4JUmYpvdZvT8BmFGBAigK7zC7aBNaV3+jZoUnZBtxnS5pv2\r\nqdydK5nhXffwCWeFMEqdI6Ab5vWmUQJsBWk3PsV2FqtVM1ivv3aWeWLlTFAO\r\nR1wmFQVk9kIBDGVz21klCFauyhbHbj0OdYYlFFCjfMPZ1pfOHjyqNbE3HMYr\r\ny9dxnK9yffCB0U4yfB5GnEj99xC1AvXSkBDm6UFQLfhfioW3iCMfUi4kWPKd\r\nEwrrRg2XgsrNi++xM2iK2tLu3cgcmRnAbo1CriwE40ffSqohVMs6nohicdi7\r\n4V/j+buJJO6gCm9sRnrdmhvvHy6cExNqaE+qF9nJn2phL7YS3bIzjtq1hQyg\r\nvpwIe4nE8mZ7a/2cDCsIGK9uIcvsvgwbkHbH93q1J/l5MasVu2xTTn30bVjX\r\n1T0L0TE8b7193vjf41eki3YGPDC8pgdjdyzE+fB1WQGyogOpcNacwVbz+AmI\r\nZ+9pyjvBhLcMrZByLl6oKtMjosiJi+IAuoUnDDHOQ7GSFOnGKshg9/trbv3r\r\nkQP98YJcnIwc7aKSBYF59C1lSjbLJenJ8Ni5koiipFUQbnz53NLrshMlXq2V\r\nD71MkIfw9MrK15S7W/1uHY46t1B09zkdUC7ILQXXk1/9hi23D4qAn0YwPqNb\r\nRv34ZtYPfb/XaA4b4W05tdN5FqAxmeMuUrY=\r\n=DRGf\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"e39ab883b18636238ef0fd741df4ce5ed53e8d04","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.4.0","@opentelemetry/resources":"1.4.0","@opentelemetry/semantic-conventions":"1.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"9.0.2","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.4.0_1657138538116_0.08054665516123172","host":"s3://npm-registry-packages"}},"1.5.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.5.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.5.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"259439009fff5637e7a379ece7446ce5beb84b77","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.5.0.tgz","fileCount":219,"integrity":"sha512-6lx7YDf67HSQYuWnvq3XgSrWikDJLiGCbrpUP6UWJ5Z47HLcJvwZPRH+cQGJu1DFS3dT2cV3GpAR75/OofPNHQ==","signatures":[{"sig":"MEUCIQC1uiO4Uby5qJ5Q4RGt5KojX/NKiuQhrT6XzcGmN5YImAIgYNHGvd7qtO1rZ6/NgtNYF0j2DTITyPg1IWTufbasY3c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":561100,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi4FQAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrCfg//WNcotdRula30Z5YkzhkmwWHZyiBWnHIHOBEOgn36AolkGWE5\r\nM97A0rZ3cfW4DrbdCIjEaavWUjvHpAzER8ta+DiqigspFvT+ko8g5v0fMw2N\r\nbPOXkn0E0zVPCunYFPrcbiG8GEgETjvtPnOlofH6McdgXOyt5tUS/GEI4LwY\r\nObg5BzYs6hRc86X+aQvrLwCA9EroWzfLr4XbmeAkBE5ZEYq0bO/WTz3EujY8\r\nYxW8GnNaxJT70vYlczo5+6IGBASFg1bpALJyidUroNRHDRRQM/MQFZydXaKV\r\nWLEA/1fwTBEB6sfsKY+nzD0tMIRxpZzHvm+qqAO4wEnm8UVx5Y4Oe24baJaZ\r\nYw/Mpi3A8xj/5Glf4G6F0WAuKxEXfbNJ3/OUraE4IofrSKKvv/4JhNxOw+kA\r\nVKX+AnRCdBSPduR/xAyHMQke4Q3YCk50lwSLXYoZeo+VnEYPBe0/hgmFRqzK\r\ngXOsR9ZhxWJ65N3UuOiBYu69OlT31OTxlvDxDOd/SjSngpWeLFEbyq6NPxfF\r\nMkcsnYy6WSaythblJdUebz42u8CX//JSAfT40g2jidnniQgjE35EzrvgX6dp\r\nhB2UoTxrz44DD40jQLAivEMDxL9Sx+GZ1HafxBoxDnp5lgeRaj4r2IBNz4Lp\r\nhCXz5P1ZCR9Ut3YLnhqMRSfMZqvJDQnsD0E=\r\n=lvpO\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"776656b6e0cd0fc49a52b734702bc77110e483f1","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/3.22.1/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.5.0","@opentelemetry/resources":"1.5.0","@opentelemetry/semantic-conventions":"1.5.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"7.2.0","sinon":"12.0.1","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"9.0.2","ts-loader":"8.3.0","typescript":"4.4.4","@types/node":"14.17.33","karma-mocha":"2.0.1","@types/mocha":"8.2.3","@types/sinon":"10.0.6","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.2.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.5.0_1658868735873_0.36948937296896256","host":"s3://npm-registry-packages"}},"1.6.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.6.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.6.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"8b1511c0b0f3e6015e345f5ed4a683adf03e3e3c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.6.0.tgz","fileCount":291,"integrity":"sha512-yx/uuzHdT0QNRSEbCgXHc0GONk90uvaFcPGaNowIFSl85rTp4or4uIIMkG7R8ckj8xWjDSjsaztH6yQxoZrl5g==","signatures":[{"sig":"MEUCIFGZxkPPycUwga6941/1+5vsL0g57z0IOs9srY9QVTFgAiEA4/3DbZJ+xNNnMd1k9gRasfX+VKzueDJPWlXaDvb6TLY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":703237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjBmODACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqCCA//UmDuN/9SBNkotpVIKMbNS/WfQYETlNXctdnZHBP88etrH+Ax\r\nQaM+cxOLvoO8VgneTZa2YcufJmZamgf0N5nnfUBABvNqOxDXfFlnXpgNR+S5\r\n6E1ldhx1sZ286kQqhQfrCSpENUdLxm3puzlJvnOzDrCUBqSAIL4vrE2pifuy\r\nngWrs2LCz+KBb0Oo9Zj+GvXOYKZaITZNVwR5y91aOzzyvhMj7aPumgWpwBOz\r\nnley7aO7CPXY8PyZ6FUUV/5YySSyLxtrGaUzTpwxkAmo8Rp9cB+xDyCKb5hd\r\n1XIZ53XH+XFUH9bK6LMW/NsANgcPvWT+gnXdK9OKg5orV3XEj7ftAWt/r9E4\r\nIWQsv1VO/k1aw4wikslDghIHd9DQGUq8f3qTbZDcIJhoSvfuUrIJWlE6zehv\r\nDWG4nGl9515eNnVvWFGuXLJUZPQPnZrCYS5ocD9viqOGj8Ix6Dp8yEkHA0vm\r\nNQttwwELXX03r0VeEQcFpxXwRzS7avmpRqDfedpl2TJrF/4Bo8TLBkBP1Ht6\r\nVFc408x3eSA3RvJ7gFdhvgqjqYLSLuVP1D/ps66pMi5SG7ykJ3Za05cBznbs\r\nJMp5L33qYYWHjAi2uOm/a0tVpmJMRO3QyLNF24Y4zbuV3apqsXk522MSWWPZ\r\nFsh1V6H5vRKAFEdP5XrBfq1qzaAkwPbw66k=\r\n=8qYD\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"a5abee69119cc41d9d34f6beb5c1826eef1ac0dd","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.6.0","@opentelemetry/resources":"1.6.0","@opentelemetry/semantic-conventions":"1.6.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"9.1.1","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.3.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.3.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.6.0_1661363075313_0.24925341140796742","host":"s3://npm-registry-packages"}},"1.7.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.7.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.7.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"b498424e0c6340a9d80de63fd408c5c2130a60a5","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz","fileCount":291,"integrity":"sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg==","signatures":[{"sig":"MEQCICxQm5dNE2obRrewbmTbvDUPKjpEDqwe9DMSkjsL+ll7AiBBeL09XWESUAsZEFJqEb/czB9xRqxGSU7ZtYkIxFctxQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":710870,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjJGjCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmprZg//eVXy8Y9itH7B5owMNIWDUAuuXFpw5t4j4E3Eqsa8PL4MlfLz\r\n3Q8FGv/gMVPNSxTK+Q5olSgI/Dp4d5KuWEzTBPtU2kSSZFQ2hgeMdeLR5zkG\r\n/R0kcPL8z+iFxMsA6eO7uImf5Y9frntttn/el5jfw87X+qjXjV2jwqKJmADa\r\ngQ0TtRLb34gP/LReTYaE6P9pZXAgldTSLku65ztF74lHvPE5Q7HvHs+75uw5\r\nsAKGbVsA5yedjXZ+v8eIE8WL7aQer9VvtkPRXVlFScvim0CCChn5+45seH7c\r\nU+PMzcc97Y4Rg7ntxFy/lcW3OKQaO5yl+gss/WEDx0o7xwlNkITbePhNH0Rv\r\npTBpdcMNeZk1ITbmG1ri8DgjHFJWL5O7pao4yfgyzuFkM4SUYujEZB/Thfek\r\n//fZv8H+t6Ms+MFMciOE++GJS+GVXVgyNXFUWIUWF1eMeO4Y285LygJv453J\r\nN7sld0hrMn4fItZW61Vmj/sRaQuIM8NLUjHTY1jkBFRE39nsrIkWxNJRqjcv\r\n9DQOlzhSAOFOY1EINpYJeW9EAIQkwZ/w0UOd+HPmVDm5bGbCnvqEpiyluj3c\r\nClZr/yjpFoMm9Mx+FwhLaZjW5IXJe1ek9QFDDP61tGWQviV6jNHq5RxjOwm9\r\nOJ2F819hWrl66GKa02rO+gqNugRkAJxpgjE=\r\n=3Nls\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"ad88c3d9aa0100fe259b93f4b660e84417b757ac","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.7.0","@opentelemetry/resources":"1.7.0","@opentelemetry/semantic-conventions":"1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"9.1.1","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.3.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.3.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.7.0_1663330497999_0.322341921882334","host":"s3://npm-registry-packages"}},"1.8.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.8.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.8.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"70713aab90978a16dea188c8335209f857be7384","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.8.0.tgz","fileCount":291,"integrity":"sha512-iH41m0UTddnCKJzZx3M85vlhKzRcmT48pUeBbnzsGrq4nIay1oWVHKM5nhB5r8qRDGvd/n7f/YLCXClxwM0tvA==","signatures":[{"sig":"MEUCIGGgOVCWV9iMP+xpoW8qU84MMmAwLSfneZNUCzPnTNKKAiEAzWhHXdc3EDzlA7f34ajtUzAM95QQ/ampFalpD3c5vjw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":709861,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjbANeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrSvw//ewghTwxojDN+0h1tzWihXLoVTgDtKteumUYoytRWe8RqPLl7\r\nKuzxG5g7KLwqsojCfHp250ofMUwTVR36RuHZTarjpaJ1z3gC/EbROPfsCy2K\r\nIdxiZJLS+5rycgn+P/rqH69Ghqy+O6E1qxAVWNZ3S4px3wGivliDbpj4YlaN\r\nBoTVTuk8THoeg6us5RmM4WMx2lJNIliMtbOF1spyFEL8jfWrhfQFcMGftFAQ\r\nXSSn5fJ0iFWYDkD7psF9MQ/iJW7nEG4Cn4ITjRt0cRgIMUtDRGPJKtY9IwUj\r\nAtZudNs7KQYrZiT3WvaerL4yq6d0iY6lbDXiTvtKof/bRt2xciCaF+5CG3M5\r\nV4JxKS0/YwilsMKebUF9Uz7m5Y/bKr20uMFNgdSSp0vDeReOlcbSOqm1JGf5\r\nH1G1PUnZ7rvuhuYA512plL6sieLNmyhTgktMSL3nMPP9+phj6BEIdWI0eLfV\r\n0xz97XYI+YgKqS2U35vh01qwgJMDcHy1v20e+IVEFi/f6bUUd9NwVJ8y0IXG\r\n3hUikhaCFADqQHDHzNp6mfdNRkJSGzc9zD4mNKBQ+Dg8xEWNQwDq2pjCiKfI\r\nSey6HSU4D39eLnxPV/sB7+NQ/wav1w0vtFUg2GWQl7yQmI/OOROiBfyKYBeW\r\neL4oPyD0Beldz9ejMY4gT8aX6Y+KGU6mc9g=\r\n=fY0q\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"7972edf6659fb6e0d5928a5cf7a35f26683e168f","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.all.json","watch":"tsc --build --watch tsconfig.all.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.all.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/5.4.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.8.0","@opentelemetry/resources":"1.8.0","@opentelemetry/semantic-conventions":"1.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"14.0.0","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.4.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.4.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.8.0_1668023134059_0.3756471246154027","host":"s3://npm-registry-packages"}},"1.9.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.9.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.9.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"07aad8d3b484f24e45ad6347f74a66d12d69bf00","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.9.0.tgz","fileCount":291,"integrity":"sha512-glNgtJjxAIrDku8DG5Xu3nBK25rT+hkyg7yuXh8RUurp/4BcsXjMyVqpyJvb2kg+lxAX73VJBhncRKGHn9t8QQ==","signatures":[{"sig":"MEUCIQCk148UUg3KdpJvI14wehqmYDos3NaYmQqsaz+40gEBvAIgbGW9IAjfLk1D9uLL1eXth9ZQpdSfqpmZsYm/mJOGug4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":720397,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjvy41ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrHzw/+MRKqvOeTqf1fwQhfOP+OYxGNWn+/HFIV48ZraIw/eJnPNm9n\r\nrfPXgMjJwhMeRRR4veXg7NzMGhwDub7uMwjpxrFZzvWVLU5XHnuCu0boAGt1\r\nkWF4/KulBnxgOj/6hqJ/+M4/L4zoE+Pb4RlPe7lbqmIl56ZmfWhah28okgnC\r\nqNbR5vgAOAX1NEjrkA52kke1H/S+wTM+/D/IwaSTd9fReOauEEaGRX9M7eJf\r\ncMDSFpiwY3aqWgw8Qjc8XOOox0zi75avU56CjhN4P7SnlvLIQqCdiWa/INA2\r\nCYEFE8KFlwKBXai+WXUms6mTpXG/pSn16/mf44KHh2D0tmXG83FgsTRzex6O\r\nq0bKGX7+UnX1k6opShIWcZF1pekjU83nQwc8c5IKc+7NABXXCYy7bksVwsG3\r\nXW1w06RhJjQcLkydPwfXdSTD7P8zVHo7IlFrTrb9kc+biA2xqr0o7Jh+0nc+\r\n0cGsrohmazhmaRM9VIzhh22XdO+s9ajQgNNg6YEYPw8Tqtk1JIPE5Tj2VmA0\r\nduGpt2CQPeT3xrdnYtA4jJLBAVCCtNO+oBmhxvo9UWNcwf3BM+egVGZr2pDj\r\ng2BOlIUj96iuQ89lpGkrHTTsICxpiZo6XhKzvi0Pyz/kU7vLalYzOFX36Kp3\r\nfrVodW+vrNvDp2xCbRtezdDvCAVbM76VsO0=\r\n=rMZT\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"08f597f3a3d71a4852b0afbba120af15ca038121","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.9.0","@opentelemetry/resources":"1.9.0","@opentelemetry/semantic-conventions":"1.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"3.0.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.9.0_1673473589665_0.6742613306845624","host":"s3://npm-registry-packages"}},"1.9.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.9.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.9.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"c349491b432a7e0ae7316f0b48b2d454d79d2b84","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.9.1.tgz","fileCount":291,"integrity":"sha512-Y9gC5M1efhDLYHeeo2MWcDDMmR40z6QpqcWnPCm4Dmh+RHAMf4dnEBBntIe1dDpor686kyU6JV1D29ih1lZpsQ==","signatures":[{"sig":"MEQCIDCpHT2SvFd7SvoiA/2qcUtYH9zFjwJCGPBPCaE/O0/XAiAg9ObFHTNytJHMrIs2fZSuBwq6zkrVyW0zuSXKRpu3Cw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":721180,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj1+KDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoLHRAAg2YSezWIehGO+yqyz36zRSewcUwKhipwLyD79qHVT8biD3OZ\r\n+LouyTG+pKoIfA0ht/AxcwP/KPKd4HfApZX//J2F5up8Y2HXg4ttHF3ez/MI\r\n59pdZYzIHwvcV24CdShaviNcw1wVF5+fGBdP/AqDDHu+TzTqSyb8p0SLTKnU\r\nm6dqBu0O7/30a7lRuEacaE/RTN10fpXpQzseVyq+yY4fxnihW5WJ4wmKKgz3\r\nN/WcKOla4/hdeEpeQaBMsud+VxN1VTNor8Co+RyB7+T6pHFSIWcuUY4ED0QW\r\n8D9oHXPND8P8AqQG2V3oh/3J7EOsdfqaAhzbURnX/DYPw5GPwvEXHL2xg9SM\r\nEVnwf/amUsxp+KaLbLmJnLdUebv+YSYzU4Cq0b7lgF2Y3ETt4UHzC2aKP1f+\r\n2Qq7VM3XWobSfVQATnb9G7mT3ceET6hhWwx/2bCk8bL8z2nr1A/VFIcGR1/Z\r\njTKUuuf0tWndCgQcAiMgBqsY9gVc5k49BXILciDslFi/VAFPzPNLdgXTJLoc\r\nygkcbFFC/qshloubv3yjTyxbb4sPC8JHaJy7z+JneWFYZIVG1phNiRbUqUNv\r\neasg3/aSyGvuSXZSyE2tEKTvAt0wtOUpnk/LelZoD8xO11whUPlWz5bk5le/\r\nxuTo6bdKcBCPUftxCcET15ohn56htRaQssg=\r\n=4lA/\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"279458e7ddf16f7ddca5fe60c78672e05fafce66","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.9.1","@opentelemetry/resources":"1.9.1","@opentelemetry/semantic-conventions":"1.9.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.9.1_1675092611489_0.40379905656518345","host":"s3://npm-registry-packages"}},"1.10.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.10.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.10.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"abbffa0ae39234f4c441357edc3f4da45dc73ef5","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.10.0.tgz","fileCount":291,"integrity":"sha512-X4rRShtVQ893LCU4GNKS1TKFua9nSjVmo0VJvigfSFSOmyyOLfiyTWmVL9MKV7Ws0HqLOIWJixJY0x28fw3Tzg==","signatures":[{"sig":"MEUCIQCfH/YhGxM8fNPGzqv85ZBEv+TXK2xG4B3l4BxNeprrcwIgNgdeMoB45zsX6FCHz9OKjjZgeiZpELL7Pl8Jj8iVfsU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":753566,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkD0cLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrtJxAAjO1H0f8JKNzc9enF2Gb4+9qh/KuB+N6QinsJR5uIKghmG04L\r\nTyyCyEBIlrt5+C8U19xXZRbrtSTfyHD+lv7WRrSeV/fL6tOEV5oHwn0BhijS\r\nJB+Ng65UWSGtPiv9fnsczZN+pluUwzWcpn1eKdM/ev/7j0jFnxbGFJf8XfLb\r\nep+rdVtn2PBZfe66YZP9LtE7nCaHVYCNFdqkXl8PKeyYisl7FJefVRcxejOw\r\nIK1m8uChlMYtjDlMCkg3IrIfiEqlFoAoQinp2gPTEuSM9Z8R6WIW+rc5B680\r\n8MFyMQVy8U44vhZjnLb0fwIAzHpCEzXrsjQ6kwXU9ZhOOw6k6zq/F90W545+\r\nxc0yDu+EQLPLFwSYgVAD8Iutc2OkRWYDBUn2+QLWIc49HSq/XfMyUV36qDpM\r\nNa9JwV5Hzh9sfEPgvnKFDXDz1t/kYP1Y5D2h1Z0vvPYIGfxfpwnhKOtI4jHC\r\nwnnwBxFlBWbyxQ4TUWjV3R8HeChsO3KZ7Dcocjjkhr+dGZbRQwx5j/ZcRgb2\r\n1WS/kKpbXELlv7F16k+myxXVMSughmSmyTBvQesOMODviXrG9QvZ/k6hQDzn\r\n0aIdj7QFVsBqQ8QfLScLcQoGdxaD7PAImPFAJo8IwRIApGUFGoJIMkTAxQnB\r\n3An0ctyqyIqewKwSMDQ/kQkCAZo7nNMKF/k=\r\n=4uMa\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"56e6b1bb890f844b8963a146780d0b9cfa8abd0d","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.10.0","@opentelemetry/resources":"1.10.0","@opentelemetry/semantic-conventions":"1.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.10.0_1678722826858_0.7356588240446413","host":"s3://npm-registry-packages"}},"1.10.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.10.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.10.1","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"fce06a810f9052d3c1b935d134979f4254bc8ae2","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.10.1.tgz","fileCount":291,"integrity":"sha512-jutSP5t22wrPKReJKzI5uKht4mJ4cQdF/mGFJkN+emFFsDXru9CuFv/NfUrD0jEqoaaiqjcZtPSyTzMgu9LXvw==","signatures":[{"sig":"MEUCIC5TkRQU4u6vAJo35V80QPiCUtJjFb/AQ6lMRRW3pWVXAiEAvM5UwlwBdiTS/miwGRjsCy4RwNj1Vmv0E44uh035k4s=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":754653,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkGIV6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoD9Q/9HrqY+zXO5UB2Ajjrnffg1MgeYDLd+x0xsRVwooyC2HXp52/8\r\nmuP2SXq0JbSZOwbqQhSs6r/KqLt2avtTufh20YqFScGBvE0ajk+iPwdViznh\r\nBoWgA7m/cSTfazKwa/y01+rP7u/dHhMhUyp2VuX2Ml+6uytN3+ddvNgrPoB/\r\nV6V/V+G1StRlkrXj/yrI5kwtdbd2lDqsPErYhEWq/JnBamEckJYDUukJHftP\r\nOiYLvPEJGD8nDnZyFZM411BZzMi2c2eAXDTCgdBfczwdgHDq0hi2bBheMb84\r\nE9XDZO0roA+8qqwPOdII+ZhTM+Zy7AWVpgXYzYyn2H+zqt0kKYGAVum01B2P\r\nVwtG8UVJj/0/SXiKpzqaHNeRp1bG3Wwz/SgOdGWO078IoQVGOlQcldCbSc/9\r\nPU+OojbQ29jzwOOXRGq3Gr1S3CWshuDUM/V/gKPYJLQDL97VcP6qgmrGKYjw\r\n4phRWsFMpVtq4vLpciCThNZfdcCpIHKEW393pVNcpoYh8zyrucfR+UNt6gdB\r\nHOuK81d1sf67yI1lj6vuxYBL1Sg2PppgR+kuH8yat+XGV2PHdIp3g3gjaEx2\r\ndBp1tMLkrI8EW5TIgFusMyNuYq6+fH4VQ+6KL769f9zJi6wt5833VS1YjCK8\r\nh3pXzU0oMH3vOTOi+K+MgFdezKn399P7X3s=\r\n=ydap\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"486df99906958de9b8b666839785b00160a6a229","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.10.1","@opentelemetry/resources":"1.10.1","@opentelemetry/semantic-conventions":"1.10.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.10.1_1679328634722_0.677777763334195","host":"s3://npm-registry-packages"}},"1.11.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.11.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.11.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"a962dbe74ae28442501ce804d4a270892e70d862","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.11.0.tgz","fileCount":291,"integrity":"sha512-DV8e5/Qo42V8FMBlQ0Y0Liv6Hl/Pp5bAZ73s7r1euX8w4bpRes1B7ACiA4yujADbWMJxBgSo4fGbi4yjmTMG2A==","signatures":[{"sig":"MEUCIATNLGlPj7nprM/PqoK7yebouzxOOVHabs3pLCi2zF81AiEAnpwJz0aXZGwAS8cwf5tUXbH3Z5M719jhlqJKLHhclE4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":754653,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkJaswACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoBlg//cAe3wMtepVB6AkYMsdLNGEQdJNykDrEO4K0w2cJORD1OoOSw\r\nRKY69x4MsneZ7DoTy1Yyvrro4nFZVaXcMR4W2I2/VyCM9zuMYdfXfeYW8Xn2\r\nxlBmjjIvotMn3FMLwxd44Xc7RRXgqOYp+F+oYPfxxyB4bLtIjg9UCRWIUkfs\r\nxeHvkfxXhlSE/Ffl+o4+6S5JR8bPRcLKltwfM3sBlySUZ619M2t/3RdgXuN4\r\nKpZM8cj/+8w47iRnPEC5u+mxMgsUiJOPRdMNV8MLR9gAVrt+aycAGs0DveJ+\r\nW800yX76KLskPzXMOhLjY40uAfTmf7ZSMLySPTmjHB86Ao7a+VRof3Lp7F7W\r\nAl53WeR8ktsMkUgol6bYBUxQ6gpaUBcup8OVTq3UvIV0tc1nW9xGift4uyqV\r\n3Zx71db8n7DWQLx5djFNVcGxo4LGBnoac6yID7sAXZmFPKO5wgik9jcSvaes\r\nMefz5zOjllRyaggfDu0qzMl/pV8rP4U/HDyFsUBFkbVrs0H1gq7p4MWOzvA5\r\no+TIwiRqhkNVieFqBguPz6wC6R+xmGDfuMT1OVlErJ37xulLctcFR97cVvo8\r\nNhs/zg+VzSlLA2ffRMZJY8j2xFIIknTNW2XbxO0ynIuw2Qac/ubMyv3L607p\r\njN2vgVxGOy4ijQ4VOX7QzQTJOW9eOnjw9UQ=\r\n=Bvyk\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"1328ee04ae78f9f6cf143af7050c00aaa6d2eb3b","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.11.0","@opentelemetry/resources":"1.11.0","@opentelemetry/semantic-conventions":"1.11.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.11.0_1680190255784_0.000785890780287346","host":"s3://npm-registry-packages"}},"1.12.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.12.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.12.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"62b895dbb5900048a85e4899c38fec5585447d4b","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.12.0.tgz","fileCount":291,"integrity":"sha512-pfCOB3tNDlYVoWuz4D7Ji+Jmy9MHnATWHVpkERdCEiwUGEZ+4IvNPXUcPc37wJVmMpjGLeaWgPPrie0KIpWf1A==","signatures":[{"sig":"MEUCIQDGQNlFDI4aeratpSD5OxDMShZWpHdYTVjWCBnYfpqT7wIgS20kS7BJXFaKBMCcL57pG64sBXgcstHOb/QvJJbZc4c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":755384,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkOEYtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr1VxAAgK9Cgr/apKKrJ/8l0FYhazkWFDVijdj6ImBxtPgDYEUJgRCa\r\nXWPxQDsQLJ/n+ZSKLJ9e115m+2mfaLCg0ECNePS29s0tCwNiC0pS+jC5EJxm\r\nDAfJgqHg58HDE1ZwEvK2ssIgz66oCTBS59WwTMARovA4kBn9TyViR9k6wRGU\r\ndJX5LqLpt8E/1MymOyqo9/2HoqCyb1EiKzB/Ur8VdDnp4a/6yKC0r18d0ju3\r\nG6FfNyvgBwrdqOoIbUcut1bwbomwjplJZ63sIKUbC4h/DThjsYOd1jeek4N/\r\nR88hh4GEpZCWW9yHYrQ5oTYiVRtc8DKo+FstoKULy49suV9998uh50vlS+rR\r\nfDvRBInZIi1weDqJvjZVRw2rC24uoTOECiPulSKmrYsIF+jAREqJ1MwOLPdx\r\nvhnPsAmPF0gmRlWpHoXeYxG8NhfCjGycXJM3XcXIjX6+6FgSxKxDPGxiYIP0\r\nFHA7TP6N26re/AcKB1BhbjblKq9znqYbJ4vVG6xfFUEPC6akKwDwBNkwTPN/\r\n3Nq9B5fhF4IB02jHCu2NSGAyWeEXICD349DITHgpeY+WXRK/0Ch/FvKsAiQ2\r\n8DLal2y8VAMn1rX+18wbiJeDgUP0x88y8joyR+SRLlRb0BGbOgZarJ5t6rKY\r\nU8gfYC/v0GR0zNX8sPSWtuZxdQRabqIODas=\r\n=D7K4\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"a04090010ee18e17487b449984807cc2b7b6e3e6","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v14.18.1+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"14.18.1","dependencies":{"@opentelemetry/core":"1.12.0","@opentelemetry/resources":"1.12.0","@opentelemetry/semantic-conventions":"1.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","rimraf":"4.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.12.0_1681409581369_0.41803382504150455","host":"s3://npm-registry-packages"}},"1.13.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.13.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.13.0","maintainers":[{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"096cc2759430d880c5d886e009df2605097403dc","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz","fileCount":291,"integrity":"sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==","signatures":[{"sig":"MEUCIQCUuJ4B96xwpSLT8DFKAofaJDlGrXekNXcbsCsQRyYcBQIgdFAq/sIQjSsvEbReUkhFeNBdPC5uB4GXp48ocXDFpTU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":755361},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"8fc76896595aac912bf9e15d4f19c167317844c8","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.13.0","@opentelemetry/resources":"1.13.0","@opentelemetry/semantic-conventions":"1.13.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.13.0_1683811806724_0.20967276776702293","host":"s3://npm-registry-packages"}},"1.14.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.14.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.14.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"831af08f002228a11e577ff860eb6059c8b80fb7","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.14.0.tgz","fileCount":291,"integrity":"sha512-NzRGt3PS+HPKfQYMb6Iy8YYc5OKA73qDwci/6ujOIvyW9vcqBJSWbjZ8FeLEAmuatUB5WrRhEKu9b0sIiIYTrQ==","signatures":[{"sig":"MEQCIGq2NLJF230csI91/HmEoC3vV+PDJAfR6T+ZfR1K5MhUAiBE0zHURoBg3oiLXEQFSYgZRPg4mT6sh7zOyIBPLwqTrQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":759415},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"edebbcc757535bc88f01340409dbbecc0bb6ccf8","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"lerna run version --scope $(npm pkg get name) --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.0.3/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.14.0","@opentelemetry/resources":"1.14.0","@opentelemetry/semantic-conventions":"1.14.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.3.16","mocha":"10.0.0","sinon":"15.0.0","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.0","@types/sinon":"10.0.13","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.32","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.14.0_1686031254973_0.05353905101465628","host":"s3://npm-registry-packages"}},"1.15.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.15.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.15.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"92340ded8f9fec1aaa63afb40c6e7e01769c2852","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.0.tgz","fileCount":291,"integrity":"sha512-udt1c9VHipbZwvCPIQR1VLg25Z4AMR/g0X8KmcInbFruGWQ/lptVPkz3yvWAsGSta5yHNQ3uoPwcyCygGnQ6Lg==","signatures":[{"sig":"MEQCICYANwEi+W0pUvfm1ry5U2W2dfO2D8QKOxJ6ASiMd9fAAiAJVx8yq45ynFVPddGZXUIDiB+f460P0lH8/PukjsJa9Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":751257},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"06e919d6c909e8cc8e28b6624d9843f401d9b059","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"nyc karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"nyc karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.1/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"tslib":"^2.3.1","@opentelemetry/core":"1.15.0","@opentelemetry/resources":"1.15.0","@opentelemetry/semantic-conventions":"1.15.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.1","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.15","karma-webpack":"4.0.2","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","istanbul-instrumenter-loader":"3.0.1","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0","karma-coverage-istanbul-reporter":"3.0.3"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.15.0_1688642828714_0.7763914571035175","host":"s3://npm-registry-packages"}},"1.15.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.15.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.15.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"8eabc0827769d91ac86cde8a86ebf0bf2a7d22ad","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.1.tgz","fileCount":291,"integrity":"sha512-5hccBe2yXzzXyExJNkTsIzDe1AM7HK0al+y/D2yEpslJqS1HUzsUSuCMY7Z4+Sfz5Gf0kTa6KYEt1QUQppnoBA==","signatures":[{"sig":"MEQCIAMA9wUFC89WxydgdbzsPHJAC6CJZrcaOKd6WZiMyomlAiAmC3lL7KJpsgZkvBN+kvQdR+/QBXU5aH853Mw0xGsxpQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":759437},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"9f71800fdc2a5ee5055684037a12498af71955f2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.3/node@v18.4.0+x64 (darwin)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.4.0","dependencies":{"@opentelemetry/core":"1.15.1","@opentelemetry/resources":"1.15.1","@opentelemetry/semantic-conventions":"1.15.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.3","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.15","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.15.1_1690209168495_0.9723185712242157","host":"s3://npm-registry-packages"}},"1.15.2":{"name":"@opentelemetry/sdk-trace-base","version":"1.15.2","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.15.2","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"4821f94033c55a6c8bbd35ae387b715b6108517a","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz","fileCount":291,"integrity":"sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ==","signatures":[{"sig":"MEUCIEIHVFRfdpxnv3EMAJ4ftYQ+AlgYc1/cZ62e/gv8tkELAiEAnsGkf0w1TCg+Nh+zO9gX/GmMEANl7JYCnR3A8+/VUTY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":759437},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"48fb15862e801b742059a3e39dbcc8ef4c10b2e2","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.4/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.15.2","@opentelemetry/resources":"1.15.2","@opentelemetry/semantic-conventions":"1.15.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.4","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.5.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.15.2_1691500880621_0.5765565023863921","host":"s3://npm-registry-packages"}},"1.16.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.16.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.16.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"243d69767d44646e1d16baa425c35dbabd959c4e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.16.0.tgz","fileCount":291,"integrity":"sha512-UvV8v8cN0Bx5BI40IJ+sMWcbwWekPa9ngMHSOfCWtSAVKbzwFdDV4Jrs/ejC6uR/SI6CKFQB9ItHp/0nZzVbIQ==","signatures":[{"sig":"MEUCIE0CtdHOn5oxhEmmjk8QOgpa9uN1UcGaKdPZ4BPeSZHEAiEAxBrVmbuOmnEOtXfyBV6smxPj0Fzkzn/FiYy0SDpi+iI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":762780},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"5fcd8cf136e2235903dde3df9ba03ced594f0e95","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.16.0","@opentelemetry/resources":"1.16.0","@opentelemetry/semantic-conventions":"1.16.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.6.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.6.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.16.0_1694434471110_0.20239220544943004","host":"s3://npm-registry-packages"}},"1.17.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.17.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.17.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"05a21763c9efa72903c20b8930293cdde344b681","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.17.0.tgz","fileCount":291,"integrity":"sha512-2T5HA1/1iE36Q9eg6D4zYlC4Y4GcycI1J6NsHPKZY9oWfAxWsoYnRlkPfUqyY5XVtocCo/xHpnJvGNHwzT70oQ==","signatures":[{"sig":"MEUCIQDRl4V5Q9HD6NsW3DtOOe+96ou8MwSUKBYi1McLNudswwIgcqEqisIWT6U7qrLdIRH+kQFG05+JTW3HQaitOHqTY5A=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":759485},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"faf939c77591f709afbc23fadbe629c9d3607ef6","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.17.0","@opentelemetry/resources":"1.17.0","@opentelemetry/semantic-conventions":"1.17.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.1","@types/sinon":"10.0.16","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.7.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.7.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.17.0_1694524353106_0.9941234111275701","host":"s3://npm-registry-packages"}},"1.17.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.17.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.17.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"8ede213df8b0c957028a869c66964e535193a4fd","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.17.1.tgz","fileCount":291,"integrity":"sha512-pfSJJSjZj5jkCJUQZicSpzN8Iz9UKMryPWikZRGObPnJo6cUSoKkjZh6BM3j+D47G4olMBN+YZKYqkFM1L6zNA==","signatures":[{"sig":"MEQCICJ2bNHS5YUGFypz4AII9j8tSEQ9DlGT6kqLjtoOEsCGAiBvV/Vq4IZpDkaHq2O9HmyppmnPVxpIxrg+Jy1T5IiHDA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":763072},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"f8e187b473274cc2011e7385992f07d319d667dc","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/7.1.5/node@v18.12.1+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.12.1","dependencies":{"@opentelemetry/core":"1.17.1","@opentelemetry/resources":"1.17.1","@opentelemetry/semantic-conventions":"1.17.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"7.1.5","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.2","@types/sinon":"10.0.18","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.7.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0","@opentelemetry/resources_1.9.0":"npm:@opentelemetry/resources@1.9.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.7.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.17.1_1696947498602_0.8314715735448981","host":"s3://npm-registry-packages"}},"1.18.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.18.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.18.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"09e420d24465aaee8e21a8a9a3c4fa2e6f0fd08e","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.18.0.tgz","fileCount":291,"integrity":"sha512-OThpwn8JeU4q7exo0e8kQqs5BZGKQ9NNkes66RCs7yhUKShHEKQIYl/A3+xnGzMrbrtgogcf84brH8XD4ahjMg==","signatures":[{"sig":"MEUCIFQOMg1gSyToxhdxU8T3AviTyjEEw2MbV3NIAaAUmbOCAiEA5XDBvAFEwa4c3eHldor7UtuSWc0QH6+9JPAUmc9laKE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":762996},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"73b446688f10fd8dc4cf403a085f0a39070df7b4","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"@opentelemetry/core":"1.18.0","@opentelemetry/resources":"1.18.0","@opentelemetry/semantic-conventions":"1.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.3","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.18.0_1699353886594_0.7186034490338462","host":"s3://npm-registry-packages"}},"1.18.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.18.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.18.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"256605d90b202002d5672305c66dbcf377132379","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.18.1.tgz","fileCount":291,"integrity":"sha512-tRHfDxN5dO+nop78EWJpzZwHsN1ewrZRVVwo03VJa3JQZxToRDH29/+MB24+yoa+IArerdr7INFJiX/iN4gjqg==","signatures":[{"sig":"MEUCIQDGgWjUUen2F48lorSLadmIOOAKIpssyEX1QX3AkPnEcgIgZ/6Bk9XnhJDqwrVWwaBzBowhdIMF0BlbAA4G9pfiu3s=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":762996},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"f665499096189390e691cf1a772e677fa67812d7","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"@opentelemetry/core":"1.18.1","@opentelemetry/resources":"1.18.1","@opentelemetry/semantic-conventions":"1.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"4.46.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.3","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.18.1_1699466949548_0.8982337926756658","host":"s3://npm-registry-packages"}},"1.19.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.19.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.19.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"87e629e7080945d955d53c2c12352915f5797cd3","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.19.0.tgz","fileCount":291,"integrity":"sha512-+IRvUm+huJn2KqfFW3yW/cjvRwJ8Q7FzYHoUNx5Fr0Lws0LxjMJG1uVB8HDpLwm7mg5XXH2M5MF+0jj5cM8BpQ==","signatures":[{"sig":"MEUCIQCvZye8IyirUNdQZpWpKxno3ZitChFbue9awFFtHELllgIgNyloI4Qyzwns38NU8u3WC0Li1KHoH9j2JOpghwv/it0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":764145},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"d3c311aec24137084dc820805a2597e120335672","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.18.2+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.18.2","dependencies":{"@opentelemetry/core":"1.19.0","@opentelemetry/resources":"1.19.0","@opentelemetry/semantic-conventions":"1.19.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.19.0_1702557329886_0.8961281946707333","host":"s3://npm-registry-packages"}},"1.20.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.20.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.20.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"1771bf7a214924fe1f27ef50395f763b65aae220","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.20.0.tgz","fileCount":291,"integrity":"sha512-BAIZ0hUgnhdb3OBQjn1FKGz/Iwie4l+uOMKklP7FGh7PTqEAbbzDNMJKaZQh6KepF7Fq+CZDRKslD3yrYy2Tzw==","signatures":[{"sig":"MEYCIQCHh3POrnMKj+uhqmw18P6kbzsRGC80c1E4qJMfjLsMDQIhAMGITPFwWQeaDCcCWHsASv07JnvLZjRORCDpN/OnUc8V","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":765977},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"57008533aba7ccd51ea80f38ff4f29404d47eb9c","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.20.0","@opentelemetry/resources":"1.20.0","@opentelemetry/semantic-conventions":"1.20.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.20.0_1705313747279_0.982043777281284","host":"s3://npm-registry-packages"}},"1.21.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.21.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.21.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"ffad912e453a92044fb220bd5d2f6743bf37bb8a","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.21.0.tgz","fileCount":291,"integrity":"sha512-yrElGX5Fv0umzp8Nxpta/XqU71+jCAyaLk34GmBzNcrW43nqbrqvdPs4gj4MVy/HcTjr6hifCDCYA3rMkajxxA==","signatures":[{"sig":"MEUCIQCqCEvDM0Ok1Pc+0BKmVi/qGM49XKfMGKMFwmzj72uEbQIgQj2iOSZQ1GktYTNsHsyb8mpoMElXqAgH4R2tc1TYjzg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":765995},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"828f2ed730e4d26d71f92e220f96b60a552a673a","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.21.0","@opentelemetry/resources":"1.21.0","@opentelemetry/semantic-conventions":"1.21.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.8.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.21.0_1706249469569_0.5490245065158903","host":"s3://npm-registry-packages"}},"1.22.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.22.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.22.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"7833bf2493a7b49461915ca32aa2884c87afd78c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.22.0.tgz","fileCount":291,"integrity":"sha512-pfTuSIpCKONC6vkTpv6VmACxD+P1woZf4q0K46nSUvXFvOFqjBYKFaAMkKD3M1mlKUUh0Oajwj35qNjMl80m1Q==","signatures":[{"sig":"MEYCIQCIbs8fApIflGCI1DVtJx4Od7v+UAgxdvD1HYs3Knyn8wIhANZPfqD9CmErmsPHuf+xvLsoRdhkFg7m60o2ydsbiYlH","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":765995},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"7be35c7845e206b27b682e8ce1cee850b09cec04","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.22.0","@opentelemetry/resources":"1.22.0","@opentelemetry/semantic-conventions":"1.22.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.9.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.22.0_1709198294393_0.7751101530396496","host":"s3://npm-registry-packages"}},"1.23.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.23.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.23.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"ff0a0f8ec47205e0b14b3b765ea2a34de1ad01dd","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.23.0.tgz","fileCount":291,"integrity":"sha512-PzBmZM8hBomUqvCddF/5Olyyviayka44O5nDWq673np3ctnvwMOvNrsUORZjKja1zJbwEuD9niAGbnVrz3jwRQ==","signatures":[{"sig":"MEYCIQCkDxxFrTKMnaGS3uSnxIeqIYW9GUKUsumBHH/5FiMIQQIhAJl43qgE1ESEcqc99k140Qw3ZUyhYy98yujb5J2vrPVz","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":769572},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"5231aa255047fbc6ee3d6a299f4423ab2f8a5fbc","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.23.0","@opentelemetry/resources":"1.23.0","@opentelemetry/semantic-conventions":"1.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.9.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.23.0_1712131805476_0.8181787837823578","host":"s3://npm-registry-packages"}},"1.24.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.24.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.24.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"e2de869e33fd224f6d9f39bafa4172074d1086c8","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.24.0.tgz","fileCount":291,"integrity":"sha512-H9sLETZ4jw9UJ3totV8oM5R0m4CW0ZIOLfp4NV3g0CM8HD5zGZcaW88xqzWDgiYRpctFxd+WmHtGX/Upoa2vRg==","signatures":[{"sig":"MEUCIQC+rEv+MmjVPnMGlU3wd57V5QFFVnW9EfX1PXPig1UglQIgdXMUaQB3puehbvSfKE7sDFk+XhwNKT3dOTs+Pe7BQaA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":770265},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"3ab4f765d8d696327b7d139ae6a45e7bd7edd924","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.24.0","@opentelemetry/resources":"1.24.0","@opentelemetry/semantic-conventions":"1.24.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.9.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.24.0_1713969581982_0.6686664599417391","host":"s3://npm-registry-packages"}},"1.24.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.24.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.24.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"dc2ab89126e75e442913fb5af98803fde67b2536","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.24.1.tgz","fileCount":291,"integrity":"sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==","signatures":[{"sig":"MEUCIQDTLWH2gw+yHzMuSStW+CjV5Qls++vE6j4ZyXWI8kIlXAIgcZvxmlQmkjw5cG6QCa7MTuRX04iBX07fk3qYK4/O4W0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":770265},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"41c2626fe0ed03e2e83bd79ee43c9bdf0ffd80d8","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.24.1","@opentelemetry/resources":"1.24.1","@opentelemetry/semantic-conventions":"1.24.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.2","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"8.4.0","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"10.0.20","karma-webpack":"4.0.2","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.9.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.24.1_1715093558704_0.0518702076931048","host":"s3://npm-registry-packages"}},"1.25.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.25.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.25.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"263f9ce19001c5cd7a814d0eb40ebc6469ae763d","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.0.tgz","fileCount":291,"integrity":"sha512-6+g2fiRQUG39guCsKVeY8ToeuUf3YUnPkN6DXRA1qDmFLprlLvZm9cS6+chgbW70cZJ406FTtSCDnJwxDC5sGQ==","signatures":[{"sig":"MEQCIHH4reSQvuavsAESwWCOjKs3174GY97NApvIwo8zkZdnAiAkX4evqPLOZGIcO2364Kma0IOukWxk2s4rZ7Q1/KWdIA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":772352},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"c4d3351b6b3f5593c8d7cbfec97b45cea9fe1511","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.25.0","@opentelemetry/resources":"1.25.0","@opentelemetry/semantic-conventions":"1.25.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.3","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"17.0.3","karma-webpack":"5.0.1","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.10.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.25.0_1717607756250_0.3809624034816559","host":"s3://npm-registry-packages"}},"1.25.1":{"name":"@opentelemetry/sdk-trace-base","version":"1.25.1","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.25.1","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"cbc1e60af255655d2020aa14cde17b37bd13df37","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz","fileCount":291,"integrity":"sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==","signatures":[{"sig":"MEYCIQCTj2PAtWjS/RCNjK8Gf9jjrAGZC45q4Xe4UC7g9f7wQQIhAMVEucKOdO/kvVF98e1NxnacXHLACTuS62IDNJctBSeh","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":774722},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"0608f405573901e54db01e44c533009cf28be262","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.19.0+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.19.0","dependencies":{"@opentelemetry/core":"1.25.1","@opentelemetry/resources":"1.25.1","@opentelemetry/semantic-conventions":"1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.3","lerna":"6.6.2","mocha":"10.2.0","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","ts-mocha":"10.0.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.6","@types/sinon":"17.0.3","karma-webpack":"5.0.1","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.10.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"6.1.1","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.25.1_1718875161655_0.9029425309207557","host":"s3://npm-registry-packages"}},"1.26.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.26.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.26.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"0c913bc6d2cfafd901de330e4540952269ae579c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.26.0.tgz","fileCount":291,"integrity":"sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw==","signatures":[{"sig":"MEYCIQDBmAKg/eAuae/sOfGJddg2bp6XQnx7eLvjy23V8aEtGgIhAN8fMKmoqU5I46KS49TZSYtdGwxd/ucUS3lMOUNFbWO8","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":782749},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"720bc8c70d47029cb6b41a34ffdc3d25cbaa2f80","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc mocha 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","codecov":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run","codecov:webworker":"nyc report --reporter=json && codecov -f coverage/*.json -p ../../"},"_npmUser":{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.20.4","dependencies":{"@opentelemetry/core":"1.26.0","@opentelemetry/resources":"1.26.0","@opentelemetry/semantic-conventions":"1.27.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.4","lerna":"6.6.2","mocha":"10.7.3","sinon":"15.1.2","codecov":"3.8.3","webpack":"5.89.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.7","@types/sinon":"17.0.3","karma-webpack":"5.0.1","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.10.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"7.0.0","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.26.0_1724836639726_0.4881073465309367","host":"s3://npm-registry-packages"}},"1.27.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.27.0","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","_id":"@opentelemetry/sdk-trace-base@1.27.0","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"dist":{"shasum":"2276e4cd0d701a8faba77382b2938853a0907b54","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.27.0.tgz","fileCount":291,"integrity":"sha512-btz6XTQzwsyJjombpeqCX6LhiMQYpzt2pIYNPnw0IPO/3AhT6yjnf8Mnv3ZC2A4eRYOjqrg+bfaXg9XHDRJDWQ==","signatures":[{"sig":"MEUCIEZDRp9Rw7P7jVlsdY/bPk1oroAOlQqBPxMQtoLYLwDRAiEAyNSVcrfK4Enpjkh9EGRl4Eggqu0GYDvo0upx7fyeD2o=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@opentelemetry%2fsdk-trace-base@1.27.0","provenance":{"predicateType":"https://slsa.dev/provenance/v0.2"}},"unpackedSize":787865},"main":"build/src/index.js","types":"build/src/index.d.ts","esnext":"build/esnext/index.js","module":"build/esm/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js"},"engines":{"node":">=14"},"gitHead":"eb3ca4fb07ee31c62093f5fcec56575573c902ce","scripts":{"tdd":"npm run tdd:node","lint":"eslint . --ext .ts","test":"nyc mocha 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","version":"node ../../scripts/version-update.js","lint:fix":"eslint . --ext .ts --fix","prewatch":"npm run precompile","tdd:node":"npm run test -- --watch-extensions ts --watch","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd:browser":"karma start","test:browser":"karma start --single-run","align-api-deps":"node ../../scripts/align-api-deps.js","peer-api-check":"node ../../scripts/peer-api-check.js","prepublishOnly":"npm run compile","test:webworker":"karma start karma.worker.js --single-run"},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"repository":{"url":"git+https://github.com/open-telemetry/opentelemetry-js.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","description":"OpenTelemetry Tracing","directories":{},"sideEffects":false,"_nodeVersion":"18.20.4","dependencies":{"@opentelemetry/core":"1.27.0","@opentelemetry/resources":"1.27.0","@opentelemetry/semantic-conventions":"1.27.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"15.1.0","karma":"6.4.4","lerna":"6.6.2","mocha":"10.7.3","sinon":"15.1.2","webpack":"5.94.0","cross-var":"1.1.0","ts-loader":"9.5.1","typescript":"4.4.4","@types/node":"18.6.5","karma-mocha":"2.0.1","@types/mocha":"10.0.8","@types/sinon":"17.0.3","karma-webpack":"5.0.1","karma-coverage":"2.2.1","@opentelemetry/api":">=1.0.0 <1.10.0","@types/webpack-env":"1.16.3","karma-spec-reporter":"0.0.36","babel-plugin-istanbul":"7.0.0","karma-chrome-launcher":"3.1.0","karma-mocha-webworker":"1.3.0"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk-trace-base_1.27.0_1729695101194_0.8545382921292937","host":"s3://npm-registry-packages"}},"1.28.0":{"name":"@opentelemetry/sdk-trace-base","version":"1.28.0","description":"OpenTelemetry Tracing","main":"build/src/index.js","module":"build/esm/index.js","esnext":"build/esnext/index.js","browser":{"./src/platform/index.ts":"./src/platform/browser/index.ts","./build/esm/platform/index.js":"./build/esm/platform/browser/index.js","./build/esnext/platform/index.js":"./build/esnext/platform/browser/index.js","./build/src/platform/index.js":"./build/src/platform/browser/index.js"},"types":"build/src/index.d.ts","repository":{"type":"git","url":"git+https://github.com/open-telemetry/opentelemetry-js.git"},"scripts":{"prepublishOnly":"npm run compile","compile":"tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json","clean":"tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json","test":"nyc mocha 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'","test:browser":"karma start --single-run","test:webworker":"karma start karma.worker.js --single-run","test:bench":"node test/performance/benchmark/index.js | tee .benchmark-results.txt","tdd":"npm run tdd:node","tdd:node":"npm run test -- --watch-extensions ts --watch","tdd:browser":"karma start","lint":"eslint . --ext .ts","lint:fix":"eslint . --ext .ts --fix","version":"node ../../scripts/version-update.js","watch":"tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json","precompile":"cross-var lerna run version --scope $npm_package_name --include-dependencies","prewatch":"npm run precompile","peer-api-check":"node ../../scripts/peer-api-check.js","align-api-deps":"node ../../scripts/align-api-deps.js"},"keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","engines":{"node":">=14"},"publishConfig":{"access":"public"},"devDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0","@types/mocha":"10.0.9","@types/node":"18.6.5","@types/sinon":"17.0.3","@types/webpack-env":"1.16.3","babel-plugin-istanbul":"7.0.0","cross-var":"1.1.0","karma":"6.4.4","karma-chrome-launcher":"3.1.0","karma-coverage":"2.2.1","karma-mocha":"2.0.1","karma-mocha-webworker":"1.3.0","karma-spec-reporter":"0.0.36","karma-webpack":"5.0.1","lerna":"6.6.2","mocha":"10.8.2","nyc":"15.1.0","sinon":"15.1.2","ts-loader":"9.5.1","typescript":"4.4.4","webpack":"5.96.1"},"peerDependencies":{"@opentelemetry/api":">=1.0.0 <1.10.0"},"dependencies":{"@opentelemetry/core":"1.28.0","@opentelemetry/resources":"1.28.0","@opentelemetry/semantic-conventions":"1.27.0"},"homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","sideEffects":false,"gitHead":"4b1ad3fda0cde58907e30fab25c3c767546708e5","bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"_id":"@opentelemetry/sdk-trace-base@1.28.0","_nodeVersion":"18.20.4","_npmVersion":"lerna/6.6.2/node@v18.20.4+x64 (linux)","dist":{"integrity":"sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==","shasum":"6195dc8cd78bd74394cf54c67c5cbd8d1528516c","tarball":"https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz","fileCount":291,"unpackedSize":796305,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@opentelemetry%2fsdk-trace-base@1.28.0","provenance":{"predicateType":"https://slsa.dev/provenance/v0.2"}},"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEyv+TGfQvTgAsZnjL3L4p9Dh+f6tk6Ixvo16zD4UiDQAiEA5yYDGo+Hjd6DCfSj66EP/AB3HNzp6+ehs7BRV0DC9ws="}]},"_npmUser":{"name":"dyladan","email":"dyladan@gmail.com"},"directories":{},"maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdk-trace-base_1.28.0_1731926510582_0.963577670721244"},"_hasShrinkwrap":false}},"time":{"created":"2021-08-05T19:28:20.129Z","modified":"2024-11-18T10:41:51.253Z","0.24.1-alpha.4":"2021-08-05T19:28:20.395Z","0.24.1-alpha.5":"2021-08-06T11:31:39.286Z","0.24.1-alpha.7":"2021-08-07T13:33:04.337Z","0.24.1-alpha.9":"2021-08-07T14:10:07.190Z","0.24.1-alpha.14":"2021-08-11T14:50:54.966Z","0.24.1-alpha.18":"2021-08-14T08:16:09.993Z","0.24.1-alpha.20":"2021-08-17T21:07:07.249Z","0.25.1-alpha.21":"2021-08-18T20:16:42.539Z","0.25.0":"2021-08-18T21:16:46.735Z","0.25.1-alpha.2":"2021-08-23T21:41:10.378Z","0.25.1-alpha.4":"2021-08-24T19:33:17.584Z","0.25.1-alpha.7":"2021-08-27T18:20:00.339Z","0.25.1-alpha.12":"2021-08-30T20:22:09.042Z","0.25.1-alpha.13":"2021-08-30T20:41:30.144Z","0.25.1-alpha.16":"2021-09-04T08:05:10.908Z","0.25.1-alpha.23":"2021-09-08T22:15:12.367Z","0.26.0":"2021-09-30T12:35:32.508Z","1.0.0":"2021-09-30T20:53:43.948Z","1.0.1":"2021-11-11T14:51:24.979Z","1.1.0":"2022-03-18T08:11:01.831Z","1.1.1":"2022-03-22T19:52:31.108Z","1.2.0":"2022-04-22T14:57:03.801Z","1.3.0":"2022-05-27T19:41:24.857Z","1.3.1":"2022-06-06T20:26:17.973Z","1.4.0":"2022-07-06T20:15:38.341Z","1.5.0":"2022-07-26T20:52:16.066Z","1.6.0":"2022-08-24T17:44:35.505Z","1.7.0":"2022-09-16T12:14:58.183Z","1.8.0":"2022-11-09T19:45:34.290Z","1.9.0":"2023-01-11T21:46:29.878Z","1.9.1":"2023-01-30T15:30:11.676Z","1.10.0":"2023-03-13T15:53:47.027Z","1.10.1":"2023-03-20T16:10:34.940Z","1.11.0":"2023-03-30T15:30:56.117Z","1.12.0":"2023-04-13T18:13:01.576Z","1.13.0":"2023-05-11T13:30:06.916Z","1.14.0":"2023-06-06T06:00:55.192Z","1.15.0":"2023-07-06T11:27:08.851Z","1.15.1":"2023-07-24T14:32:48.689Z","1.15.2":"2023-08-08T13:21:20.770Z","1.16.0":"2023-09-11T12:14:31.274Z","1.17.0":"2023-09-12T13:12:33.338Z","1.17.1":"2023-10-10T14:18:18.968Z","1.18.0":"2023-11-07T10:44:46.767Z","1.18.1":"2023-11-08T18:09:10.048Z","1.19.0":"2023-12-14T12:35:30.035Z","1.20.0":"2024-01-15T10:15:47.496Z","1.21.0":"2024-01-26T06:11:09.767Z","1.22.0":"2024-02-29T09:18:14.609Z","1.23.0":"2024-04-03T08:10:05.643Z","1.24.0":"2024-04-24T14:39:42.149Z","1.24.1":"2024-05-07T14:52:38.897Z","1.25.0":"2024-06-05T17:15:56.531Z","1.25.1":"2024-06-20T09:19:21.851Z","1.26.0":"2024-08-28T09:17:19.852Z","1.27.0":"2024-10-23T14:51:41.469Z","1.28.0":"2024-11-18T10:41:50.759Z"},"bugs":{"url":"https://github.com/open-telemetry/opentelemetry-js/issues"},"author":{"name":"OpenTelemetry Authors"},"license":"Apache-2.0","homepage":"https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base","keywords":["opentelemetry","nodejs","tracing","profiling","metrics","stats"],"repository":{"type":"git","url":"git+https://github.com/open-telemetry/opentelemetry-js.git"},"description":"OpenTelemetry Tracing","maintainers":[{"name":"pichlermarc","email":"marc.pichler@dynatrace.com"},{"name":"bogdandrutu","email":"bogdandrutu@gmail.com"},{"name":"dyladan","email":"dyladan@gmail.com"}],"readme":"# OpenTelemetry Tracing SDK\n\n[![NPM Published Version][npm-img]][npm-url]\n[![Apache License][license-image]][license-image]\n\nThe `tracing` module contains the foundation for all tracing SDKs of [opentelemetry-js](https://github.com/open-telemetry/opentelemetry-js).\n\nUsed standalone, this module provides methods for manual instrumentation of code, offering full control over span creation for client-side JavaScript (browser) and Node.js.\n\nIt does **not** provide automated instrumentation of known libraries, context propagation for asynchronous invocations or distributed-context out-of-the-box.\n\nFor automated instrumentation for Node.js, please see\n[@opentelemetry/sdk-trace-node](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-node).\n\n## Installation\n\n```bash\nnpm install --save @opentelemetry/api\nnpm install --save @opentelemetry/sdk-trace-base\n```\n\n## Usage\n\n```js\nconst opentelemetry = require('@opentelemetry/api');\nconst { BasicTracerProvider } = require('@opentelemetry/sdk-trace-base');\n\n// To start a trace, you first need to initialize the Tracer provider.\n// NOTE: The default OpenTelemetry tracer provider does not record any tracing information.\n// Registering a working tracer provider allows the API methods to record traces.\nnew BasicTracerProvider().register();\n\n// To create a span in a trace, we used the global singleton tracer to start a new span.\nconst span = opentelemetry.trace.getTracer('default').startSpan('foo');\n\n// Set a span attribute\nspan.setAttribute('key', 'value');\n\n// We must end the spans so they become available for exporting.\nspan.end();\n```\n\n## Config\n\nTracing configuration is a merge of user supplied configuration with both the default\nconfiguration as specified in [config.ts](./src/config.ts) and an\nenvironmentally configurable sampling (via `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG`).\n\n## Built-in Samplers\n\nSampler is used to make decisions on `Span` sampling.\n\n### AlwaysOn Sampler\n\nSamples every trace regardless of upstream sampling decisions.\n\n> This is used as a default Sampler\n\n```js\nconst {\n AlwaysOnSampler,\n BasicTracerProvider,\n} = require(\"@opentelemetry/sdk-trace-base\");\n\nconst tracerProvider = new BasicTracerProvider({\n sampler: new AlwaysOnSampler()\n});\n```\n\n### AlwaysOff Sampler\n\nDoesn't sample any trace, regardless of upstream sampling decisions.\n\n```js\nconst {\n AlwaysOffSampler,\n BasicTracerProvider,\n} = require(\"@opentelemetry/sdk-trace-base\");\n\nconst tracerProvider = new BasicTracerProvider({\n sampler: new AlwaysOffSampler()\n});\n```\n\n### TraceIdRatioBased Sampler\n\nSamples some percentage of traces, calculated deterministically using the trace ID.\nAny trace that would be sampled at a given percentage will also be sampled at any higher percentage.\n\nThe `TraceIDRatioSampler` may be used with the `ParentBasedSampler` to respect the sampled flag of an incoming trace.\n\n```js\nconst {\n BasicTracerProvider,\n TraceIdRatioBasedSampler,\n} = require(\"@opentelemetry/sdk-trace-base\");\n\nconst tracerProvider = new BasicTracerProvider({\n // See details of ParentBasedSampler below\n sampler: new ParentBasedSampler({\n // Trace ID Ratio Sampler accepts a positional argument\n // which represents the percentage of traces which should\n // be sampled.\n root: new TraceIdRatioBasedSampler(0.5)\n });\n});\n```\n\n### ParentBased Sampler\n\n- This is a composite sampler. `ParentBased` helps distinguished between the\nfollowing cases:\n - No parent (root span).\n - Remote parent with `sampled` flag `true`\n - Remote parent with `sampled` flag `false`\n - Local parent with `sampled` flag `true`\n - Local parent with `sampled` flag `false`\n\nRequired parameters:\n\n- `root(Sampler)` - Sampler called for spans with no parent (root spans)\n\nOptional parameters:\n\n- `remoteParentSampled(Sampler)` (default: `AlwaysOn`)\n- `remoteParentNotSampled(Sampler)` (default: `AlwaysOff`)\n- `localParentSampled(Sampler)` (default: `AlwaysOn`)\n- `localParentNotSampled(Sampler)` (default: `AlwaysOff`)\n\n|Parent| parent.isRemote() | parent.isSampled()| Invoke sampler|\n|--|--|--|--|\n|absent| n/a | n/a |`root()`|\n|present|true|true|`remoteParentSampled()`|\n|present|true|false|`remoteParentNotSampled()`|\n|present|false|true|`localParentSampled()`|\n|present|false|false|`localParentNotSampled()`|\n\n```js\nconst {\n AlwaysOffSampler,\n BasicTracerProvider,\n ParentBasedSampler,\n TraceIdRatioBasedSampler,\n} = require(\"@opentelemetry/sdk-trace-base\");\n\nconst tracerProvider = new BasicTracerProvider({\n sampler: new ParentBasedSampler({\n // By default, the ParentBasedSampler will respect the parent span's sampling\n // decision. This is configurable by providing a different sampler to use\n // based on the situation. See configuration details above.\n //\n // This will delegate the sampling decision of all root traces (no parent)\n // to the TraceIdRatioBasedSampler.\n // See details of TraceIdRatioBasedSampler above.\n root: new TraceIdRatioBasedSampler(0.5)\n })\n});\n```\n\n## Example\n\nSee [examples/basic-tracer-node](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/basic-tracer-node) for an end-to-end example, including exporting created spans.\n\n## Useful links\n\n- For more information on OpenTelemetry, visit: \n- For more about OpenTelemetry JavaScript: \n- For help or feedback on this project, join us in [GitHub Discussions][discussions-url]\n\n## License\n\nApache 2.0 - See [LICENSE][license-url] for more information.\n\n[discussions-url]: https://github.com/open-telemetry/opentelemetry-js/discussions\n[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/main/LICENSE\n[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat\n[npm-url]: https://www.npmjs.com/package/@opentelemetry/sdk-trace-base\n[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fsdk-trace-base.svg\n","readmeFilename":"README.md"} \ No newline at end of file diff --git a/tests/specs/cli/otel_basic/basic.ts b/tests/specs/cli/otel_basic/basic.ts index 1f69277660..6f19867d96 100644 --- a/tests/specs/cli/otel_basic/basic.ts +++ b/tests/specs/cli/otel_basic/basic.ts @@ -1,7 +1,6 @@ // Copyright 2018-2025 the Deno authors. MIT license. import { trace } from "npm:@opentelemetry/api@1.9.0"; -import "jsr:@deno/otel@0.0.2/register"; const tracer = trace.getTracer("example-tracer"); From 888ab9f4f75fdba41d07e42c1d26c6c18800dbb4 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 7 Jan 2025 07:18:45 +0900 Subject: [PATCH 068/107] test(ext/node): disable flaky dgram tests (#27549) Closes #27316 --- tests/node_compat/config.jsonc | 8 +-- tests/node_compat/runner/TODO.md | 5 +- .../parallel/test-dgram-send-empty-array.js | 32 ------------ .../parallel/test-dgram-send-empty-buffer.js | 50 ------------------- .../parallel/test-dgram-send-empty-packet.js | 36 ------------- 5 files changed, 9 insertions(+), 122 deletions(-) delete mode 100644 tests/node_compat/test/parallel/test-dgram-send-empty-array.js delete mode 100644 tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js delete mode 100644 tests/node_compat/test/parallel/test-dgram-send-empty-packet.js diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc index cda2923789..c2edc32ef6 100644 --- a/tests/node_compat/config.jsonc +++ b/tests/node_compat/config.jsonc @@ -368,9 +368,11 @@ "test-dgram-send-callback-multi-buffer.js", "test-dgram-send-callback-recursive.js", "test-dgram-send-default-host.js", - "test-dgram-send-empty-array.js", - "test-dgram-send-empty-buffer.js", - "test-dgram-send-empty-packet.js", + // TODO(kt3k): These tests are flaky on macOS CI. + // https://github.com/denoland/deno/issues/27316 + // "test-dgram-send-empty-array.js", + // "test-dgram-send-empty-buffer.js", + // "test-dgram-send-empty-packet.js", "test-dgram-send-error.js", "test-dgram-send-invalid-msg-type.js", "test-dgram-send-multi-buffer-copy.js", diff --git a/tests/node_compat/runner/TODO.md b/tests/node_compat/runner/TODO.md index 8ad00c9bfd..c885622d6f 100644 --- a/tests/node_compat/runner/TODO.md +++ b/tests/node_compat/runner/TODO.md @@ -1,7 +1,7 @@ # Remaining Node Tests -1155 tests out of 3681 have been ported from Node 20.11.1 (31.38% ported, 69.14% remaining). +1152 tests out of 3681 have been ported from Node 20.11.1 (31.30% ported, 69.22% remaining). NOTE: This file should not be manually edited. Please edit `tests/node_compat/config.json` and run `deno task setup` in `tests/node_compat/runner` dir instead. @@ -499,6 +499,9 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co - [parallel/test-dgram-multicast-set-interface.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-multicast-set-interface.js) - [parallel/test-dgram-multicast-setTTL.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-multicast-setTTL.js) - [parallel/test-dgram-send-address-types.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-address-types.js) +- [parallel/test-dgram-send-empty-array.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-empty-array.js) +- [parallel/test-dgram-send-empty-buffer.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-empty-buffer.js) +- [parallel/test-dgram-send-empty-packet.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-empty-packet.js) - [parallel/test-dgram-send-queue-info.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-send-queue-info.js) - [parallel/test-dgram-sendto.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-sendto.js) - [parallel/test-dgram-setBroadcast.js](https://github.com/nodejs/node/tree/v20.11.1/test/parallel/test-dgram-setBroadcast.js) diff --git a/tests/node_compat/test/parallel/test-dgram-send-empty-array.js b/tests/node_compat/test/parallel/test-dgram-send-empty-array.js deleted file mode 100644 index cf2508d9ca..0000000000 --- a/tests/node_compat/test/parallel/test-dgram-send-empty-array.js +++ /dev/null @@ -1,32 +0,0 @@ -// deno-fmt-ignore-file -// deno-lint-ignore-file - -// Copyright Joyent and Node contributors. All rights reserved. MIT license. -// Taken from Node 20.11.1 -// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually. - -'use strict'; - -const common = require('../common'); - -const assert = require('assert'); -const dgram = require('dgram'); - -const client = dgram.createSocket('udp4'); - -let interval; - -client.on('message', common.mustCall(function onMessage(buf, info) { - const expected = Buffer.alloc(0); - assert.ok(buf.equals(expected), `Expected empty message but got ${buf}`); - clearInterval(interval); - client.close(); -})); - -client.on('listening', common.mustCall(function() { - interval = setInterval(function() { - client.send([], client.address().port, common.localhostIPv4); - }, 10); -})); - -client.bind(0); diff --git a/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js b/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js deleted file mode 100644 index de5101cbd2..0000000000 --- a/tests/node_compat/test/parallel/test-dgram-send-empty-buffer.js +++ /dev/null @@ -1,50 +0,0 @@ -// deno-fmt-ignore-file -// deno-lint-ignore-file - -// Copyright Joyent and Node contributors. All rights reserved. MIT license. -// Taken from Node 20.11.1 -// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually. - -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; -const common = require('../common'); - -const assert = require('assert'); -const dgram = require('dgram'); - -const client = dgram.createSocket('udp4'); - -client.bind(0, common.mustCall(function() { - const port = this.address().port; - - client.on('message', common.mustCall(function onMessage(buffer) { - assert.strictEqual(buffer.length, 0); - clearInterval(interval); - client.close(); - })); - - const buf = Buffer.alloc(0); - const interval = setInterval(function() { - client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall()); - }, 10); -})); diff --git a/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js b/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js deleted file mode 100644 index 01f7a459ce..0000000000 --- a/tests/node_compat/test/parallel/test-dgram-send-empty-packet.js +++ /dev/null @@ -1,36 +0,0 @@ -// deno-fmt-ignore-file -// deno-lint-ignore-file - -// Copyright Joyent and Node contributors. All rights reserved. MIT license. -// Taken from Node 20.11.1 -// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually. - -'use strict'; -const common = require('../common'); - -const assert = require('assert'); -const dgram = require('dgram'); - -const client = dgram.createSocket('udp4'); - -client.bind(0, common.mustCall(function() { - - client.on('message', common.mustCall(callback)); - - const port = this.address().port; - const buf = Buffer.alloc(1); - - const interval = setInterval(function() { - client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall(callback)); - }, 10); - - function callback(firstArg) { - // If client.send() callback, firstArg should be null. - // If client.on('message') listener, firstArg should be a 0-length buffer. - if (firstArg instanceof Buffer) { - assert.strictEqual(firstArg.length, 0); - clearInterval(interval); - client.close(); - } - } -})); From 6750aa61eb39ee7d893eac1dcdfbdb85299ad830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 6 Jan 2025 22:33:52 +0000 Subject: [PATCH 069/107] ci: increase timeout to 240 minutes (#27571) With https://github.com/denoland/deno/pull/27507 landed we need to do two builds that don't really share much codegen units. This should be reverted once we move to self-hosted runner on `main` (https://github.com/denoland/deno/pull/27568) --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index cf023a6feb..52be483f34 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -360,7 +360,7 @@ const ci = { needs: ["pre_build"], if: "${{ needs.pre_build.outputs.skip_build != 'true' }}", "runs-on": "${{ matrix.runner }}", - "timeout-minutes": 180, + "timeout-minutes": 240, defaults: { run: { // GH actions does not fail fast by default on diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcf83f0f13..bb94c04614 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: - pre_build if: '${{ needs.pre_build.outputs.skip_build != ''true'' }}' runs-on: '${{ matrix.runner }}' - timeout-minutes: 180 + timeout-minutes: 240 defaults: run: shell: bash From b6f2646c1cfe9dcd6e6dcd93159b5dd34b5a6e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 6 Jan 2025 23:56:36 +0000 Subject: [PATCH 070/107] refactor: make `IncrementalCache` accept a `CacheDBHash` (#27570) --- cli/cache/cache_db.rs | 4 ++-- cli/cache/incremental.rs | 21 ++++++++++++--------- cli/cache/mod.rs | 2 +- cli/cache/module_info.rs | 4 ++-- cli/node.rs | 2 +- cli/tools/fmt.rs | 3 ++- cli/tools/lint/mod.rs | 3 ++- 7 files changed, 22 insertions(+), 17 deletions(-) diff --git a/cli/cache/cache_db.rs b/cli/cache/cache_db.rs index 02394d4cfd..7fd66e9333 100644 --- a/cli/cache/cache_db.rs +++ b/cli/cache/cache_db.rs @@ -25,12 +25,12 @@ impl CacheDBHash { Self(hash) } - pub fn from_source(source: impl std::hash::Hash) -> Self { + pub fn from_hashable(hashable: impl std::hash::Hash) -> Self { Self::new( // always write in the deno version just in case // the clearing on deno version change doesn't work FastInsecureHasher::new_deno_versioned() - .write_hashable(source) + .write_hashable(hashable) .finish(), ) } diff --git a/cli/cache/incremental.rs b/cli/cache/incremental.rs index 9ba343f273..f430c1266f 100644 --- a/cli/cache/incremental.rs +++ b/cli/cache/incremental.rs @@ -34,12 +34,16 @@ pub static INCREMENTAL_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration { pub struct IncrementalCache(IncrementalCacheInner); impl IncrementalCache { - pub fn new( + pub fn new( db: CacheDB, - state: &TState, + state_hash: CacheDBHash, initial_file_paths: &[PathBuf], ) -> Self { - IncrementalCache(IncrementalCacheInner::new(db, state, initial_file_paths)) + IncrementalCache(IncrementalCacheInner::new( + db, + state_hash, + initial_file_paths, + )) } pub fn is_file_same(&self, file_path: &Path, file_text: &str) -> bool { @@ -67,12 +71,11 @@ struct IncrementalCacheInner { } impl IncrementalCacheInner { - pub fn new( + pub fn new( db: CacheDB, - state: &TState, + state_hash: CacheDBHash, initial_file_paths: &[PathBuf], ) -> Self { - let state_hash = CacheDBHash::from_source(state); let sql_cache = SqlIncrementalCache::new(db, state_hash); Self::from_sql_incremental_cache(sql_cache, initial_file_paths) } @@ -112,13 +115,13 @@ impl IncrementalCacheInner { pub fn is_file_same(&self, file_path: &Path, file_text: &str) -> bool { match self.previous_hashes.get(file_path) { - Some(hash) => *hash == CacheDBHash::from_source(file_text), + Some(hash) => *hash == CacheDBHash::from_hashable(file_text), None => false, } } pub fn update_file(&self, file_path: &Path, file_text: &str) { - let hash = CacheDBHash::from_source(file_text); + let hash = CacheDBHash::from_hashable(file_text); if let Some(previous_hash) = self.previous_hashes.get(file_path) { if *previous_hash == hash { return; // do not bother updating the db file because nothing has changed @@ -262,7 +265,7 @@ mod test { let sql_cache = SqlIncrementalCache::new(conn, CacheDBHash::new(1)); let file_path = PathBuf::from("/mod.ts"); let file_text = "test"; - let file_hash = CacheDBHash::from_source(file_text); + let file_hash = CacheDBHash::from_hashable(file_text); sql_cache.set_source_hash(&file_path, file_hash).unwrap(); let cache = IncrementalCacheInner::from_sql_incremental_cache( sql_cache, diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index 868811c587..fdd8fcf40c 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -298,7 +298,7 @@ impl Loader for FetchCacher { module_info: &deno_graph::ModuleInfo, ) { log::debug!("Caching module info for {}", specifier); - let source_hash = CacheDBHash::from_source(source); + let source_hash = CacheDBHash::from_hashable(source); let result = self.module_info_cache.set_module_info( specifier, media_type, diff --git a/cli/cache/module_info.rs b/cli/cache/module_info.rs index 671e7e3dc8..63f52c06f9 100644 --- a/cli/cache/module_info.rs +++ b/cli/cache/module_info.rs @@ -194,7 +194,7 @@ impl<'a> ModuleInfoCacheModuleAnalyzer<'a> { source: &Arc, ) -> Result { // attempt to load from the cache - let source_hash = CacheDBHash::from_source(source); + let source_hash = CacheDBHash::from_hashable(source); if let Some(info) = self.load_cached_module_info(specifier, media_type, source_hash) { @@ -228,7 +228,7 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> { media_type: MediaType, ) -> Result { // attempt to load from the cache - let source_hash = CacheDBHash::from_source(&source); + let source_hash = CacheDBHash::from_hashable(&source); if let Some(info) = self.load_cached_module_info(specifier, media_type, source_hash) { diff --git a/cli/node.rs b/cli/node.rs index e0feb557a7..aa44dcab18 100644 --- a/cli/node.rs +++ b/cli/node.rs @@ -68,7 +68,7 @@ impl CliCjsCodeAnalyzer { specifier: &ModuleSpecifier, source: &str, ) -> Result { - let source_hash = CacheDBHash::from_source(source); + let source_hash = CacheDBHash::from_hashable(source); if let Some(analysis) = self.cache.get_cjs_analysis(specifier.as_str(), source_hash) { diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 29db06e97f..3411d557bf 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -43,6 +43,7 @@ use crate::args::FmtOptions; use crate::args::FmtOptionsConfig; use crate::args::ProseWrap; use crate::args::UnstableFmtOptions; +use crate::cache::CacheDBHash; use crate::cache::Caches; use crate::cache::IncrementalCache; use crate::colors; @@ -202,7 +203,7 @@ async fn format_files( let paths = paths_with_options.paths; let incremental_cache = Arc::new(IncrementalCache::new( caches.fmt_incremental_cache_db(), - &(&fmt_options.options, &fmt_options.unstable), // cache key + CacheDBHash::from_hashable((&fmt_options.options, &fmt_options.unstable)), &paths, )); formatter diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index 8b8702c0f8..e7b16f0283 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -39,6 +39,7 @@ use crate::args::Flags; use crate::args::LintFlags; use crate::args::LintOptions; use crate::args::WorkspaceLintOptions; +use crate::cache::CacheDBHash; use crate::cache::Caches; use crate::cache::IncrementalCache; use crate::colors; @@ -291,7 +292,7 @@ impl WorkspaceLinter { lint_rules.incremental_cache_state().map(|state| { Arc::new(IncrementalCache::new( self.caches.lint_incremental_cache_db(), - &state, + CacheDBHash::from_hashable(&state), &paths, )) }); From b7fb5a5547dcc83a552d595895484c2b2dba2b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 7 Jan 2025 02:32:51 +0000 Subject: [PATCH 071/107] Revert "perf: build denort with panic = "abort" for releases (#27507)" (#27573) Also reverts #27518 The reason is that it takes too long to build these two binaries on Mac ARM runners as it stands. We're gonna try to reland this next week, after sorting out situation with these runners. --- .github/workflows/ci.generate.ts | 28 ++++++---------------------- .github/workflows/ci.yml | 23 ++++++----------------- Cargo.toml | 5 ----- 3 files changed, 12 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index 52be483f34..c8980180a0 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -716,19 +716,6 @@ const ci = { "df -h", ].join("\n"), }, - { - name: "Build denort release", - if: [ - "matrix.job == 'test' &&", - "matrix.profile == 'release' &&", - "github.repository == 'denoland/deno'", - ].join("\n"), - run: [ - "df -h", - "cargo build --profile=release-slim --locked --bin denort", - "df -h", - ].join("\n"), - }, { // Run a minimal check to ensure that binary is not corrupted, regardless // of our build mode @@ -775,11 +762,10 @@ const ci = { "cd target/release", "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", - "./deno types > lib.deno.d.ts", - "cd ../release-slim", - "zip -r ../release/denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort", - "cd ../release", + "strip denort", + "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"), }, { @@ -804,9 +790,8 @@ const ci = { "cd target/release", "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", - "cd ../release-slim", - "zip -r ../release/denort-${{ matrix.arch }}-apple-darwin.zip denort", - "cd ../release", + "strip denort", + "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"), @@ -823,8 +808,7 @@ const ci = { run: [ "Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip", "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-slim/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip", + "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"), }, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb94c04614..524af7ac36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -419,15 +419,6 @@ jobs: df -h cargo build --release --locked --all-targets df -h - - name: Build denort release - if: |- - !(matrix.skip) && (matrix.job == 'test' && - matrix.profile == 'release' && - github.repository == 'denoland/deno') - run: |- - df -h - cargo build --profile=release-slim --locked --bin denort - df -h - name: Check deno binary if: '!(matrix.skip) && (matrix.job == ''test'')' run: 'target/${{ matrix.profile }}/deno eval "console.log(1+2)" | grep 3' @@ -457,11 +448,10 @@ jobs: cd target/release 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 - ./deno types > lib.deno.d.ts - cd ../release-slim - zip -r ../release/denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort - cd ../release + strip denort + 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: |- !(matrix.skip) && (matrix.os == 'macos' && @@ -477,9 +467,8 @@ jobs: cd target/release 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 - cd ../release-slim - zip -r ../release/denort-${{ matrix.arch }}-apple-darwin.zip denort - cd ../release + strip denort + 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: |- @@ -491,7 +480,7 @@ jobs: run: |- Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip 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-slim/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip + 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: |- diff --git a/Cargo.toml b/Cargo.toml index fa2813caed..8e7d446b01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -251,11 +251,6 @@ incremental = true lto = true opt-level = 'z' # Optimize for size -[profile.release-slim] -inherits = "release" -panic = "abort" -strip = "symbols" - # Build release with debug symbols: cargo build --profile=release-with-debug [profile.release-with-debug] inherits = "release" From 8cda4cf53da9a099368230c069b04dfb89d6712c Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 7 Jan 2025 15:58:14 +0200 Subject: [PATCH 072/107] feat(node/fs): Add a chmod method to the FileHandle class (#27522) Add the chmod method to the FileHandle class in node compat as part of #25554 --- ext/node/polyfills/_fs/_fs_open.ts | 2 +- ext/node/polyfills/internal/fs/handle.ts | 19 +++++++++++++++---- tests/unit_node/_fs/_fs_handle_test.ts | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/ext/node/polyfills/_fs/_fs_open.ts b/ext/node/polyfills/_fs/_fs_open.ts index 85628ddcef..12f8ed7035 100644 --- a/ext/node/polyfills/_fs/_fs_open.ts +++ b/ext/node/polyfills/_fs/_fs_open.ts @@ -153,7 +153,7 @@ export function openPromise( return new Promise((resolve, reject) => { open(path, flags, mode, (err, fd) => { if (err) reject(err); - else resolve(new FileHandle(fd)); + else resolve(new FileHandle(fd, path)); }); }); } diff --git a/ext/node/polyfills/internal/fs/handle.ts b/ext/node/polyfills/internal/fs/handle.ts index ace9a7635c..2d787a9f3b 100644 --- a/ext/node/polyfills/internal/fs/handle.ts +++ b/ext/node/polyfills/internal/fs/handle.ts @@ -5,7 +5,7 @@ import { EventEmitter } from "node:events"; import { Buffer } from "node:buffer"; -import { promises, read, write } from "node:fs"; +import { Mode, promises, read, write } from "node:fs"; export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts"; import { BinaryOptionsArgument, @@ -26,11 +26,15 @@ interface ReadResult { buffer: Buffer; } +type Path = string | Buffer | URL; export class FileHandle extends EventEmitter { #rid: number; - constructor(rid: number) { + #path: Path; + + constructor(rid: number, path: Path) { super(); this.#rid = rid; + this.#path = path; } get fd() { @@ -144,17 +148,24 @@ export class FileHandle extends EventEmitter { stat(options?: { bigint: boolean }): Promise { return fsCall(promises.fstat, this, options); } + chmod(mode: Mode): Promise { + assertNotClosed(this, promises.chmod.name); + return promises.chmod(this.#path, mode); + } } -function fsCall(fn, handle, ...args) { +function assertNotClosed(handle: FileHandle, syscall: string) { if (handle.fd === -1) { const err = new Error("file closed"); throw Object.assign(err, { code: "EBADF", - syscall: fn.name, + syscall, }); } +} +function fsCall(fn, handle, ...args) { + assertNotClosed(handle, fn.name); return fn(handle.fd, ...args); } diff --git a/tests/unit_node/_fs/_fs_handle_test.ts b/tests/unit_node/_fs/_fs_handle_test.ts index 61214bde26..e4a41f8ba7 100644 --- a/tests/unit_node/_fs/_fs_handle_test.ts +++ b/tests/unit_node/_fs/_fs_handle_test.ts @@ -199,3 +199,21 @@ Deno.test( assertEquals(data.length, 0); }, ); + +Deno.test({ + name: "[node/fs filehandle.chmod] Change the permissions of the file", + ignore: Deno.build.os === "windows", + async fn() { + const fileHandle = await fs.open(testData); + + const readOnly = 0o444; + await fileHandle.chmod(readOnly.toString(8)); + assertEquals(Deno.statSync(testData).mode! & 0o777, readOnly); + + const readWrite = 0o666; + await fileHandle.chmod(readWrite.toString(8)); + assertEquals(Deno.statSync(testData).mode! & 0o777, readWrite); + + await fileHandle.close(); + }, +}); From b5e4a303d5dd74e9d82c6c181b98a5e532b5deda Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Tue, 7 Jan 2025 19:04:06 +0000 Subject: [PATCH 073/107] fix(lsp): don't skip dirs with enabled subdirs (#27580) --- cli/lsp/config.rs | 3 ++- cli/lsp/language_server.rs | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index afc976b8fe..2aaba928ca 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -853,7 +853,8 @@ impl Settings { Some(false) } else if let Some(enable_paths) = &enable_paths { for enable_path in enable_paths { - if path.starts_with(enable_path) { + // Also enable if the checked path is a dir containing an enabled path. + if path.starts_with(enable_path) || enable_path.starts_with(&path) { return Some(true); } } diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 958c3a4606..6861d9dd63 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -4005,12 +4005,14 @@ mod tests { temp_dir.write("root1/target/main.ts", ""); // no, because there is a Cargo.toml in the root directory temp_dir.create_dir_all("root2/folder"); + temp_dir.create_dir_all("root2/folder2/inner_folder"); temp_dir.create_dir_all("root2/sub_folder"); temp_dir.create_dir_all("root2/root2.1"); temp_dir.write("root2/file1.ts", ""); // yes, enabled temp_dir.write("root2/file2.ts", ""); // no, not enabled temp_dir.write("root2/folder/main.ts", ""); // yes, enabled temp_dir.write("root2/folder/other.ts", ""); // no, disabled + temp_dir.write("root2/folder2/inner_folder/main.ts", ""); // yes, enabled (regression test for https://github.com/denoland/vscode_deno/issues/1239) temp_dir.write("root2/sub_folder/a.js", ""); // no, not enabled temp_dir.write("root2/sub_folder/b.ts", ""); // no, not enabled temp_dir.write("root2/sub_folder/c.js", ""); // no, not enabled @@ -4051,6 +4053,7 @@ mod tests { enable_paths: Some(vec![ "file1.ts".to_string(), "folder".to_string(), + "folder2/inner_folder".to_string(), ]), disable_paths: vec!["folder/other.ts".to_string()], ..Default::default() @@ -4101,6 +4104,10 @@ mod tests { temp_dir.url().join("root1/folder/mod.ts").unwrap(), temp_dir.url().join("root2/folder/main.ts").unwrap(), temp_dir.url().join("root2/root2.1/main.ts").unwrap(), + temp_dir + .url() + .join("root2/folder2/inner_folder/main.ts") + .unwrap(), ]) ); } From 3f5cad38aa5dfd0852a3f51ae1e559b5ea6f6157 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 7 Jan 2025 15:34:34 -0500 Subject: [PATCH 074/107] fix(no-slow-types): handle rest param with internal assignments (#27581) Closes #27575 --- Cargo.lock | 5 +++-- cli/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b054e9fe7c..e1798fd677 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,15 +1750,16 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.86.7" +version = "0.86.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace3acf321fac446636ae605b01723f2120b40ab3d32c6836aeb7d603a8e08f9" +checksum = "4ceaff8e0b1ea21369c1aff28d75fed0c4194896195cf57c2fbee4f7db3e4c42" dependencies = [ "anyhow", "async-trait", "capacity_builder 0.5.0", "data-url", "deno_ast", + "deno_media_type", "deno_path_util 0.3.0", "deno_semver", "deno_unsync", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a2245b0806..2adbbfc604 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -74,7 +74,7 @@ deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] } deno_error.workspace = true -deno_graph = { version = "=0.86.7" } +deno_graph = { version = "=0.86.8" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true deno_npm.workspace = true From cabdfa8c2dd21ad022975dc3777f7de514ceb17d Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Wed, 8 Jan 2025 00:21:50 +0100 Subject: [PATCH 075/107] fix(lint): fix single char selectors being ignored (#27576) The selector splitting code that's used for JS linting plugins didn't properly account for selectors being a single character. This can happen in the case of `*`. Instead of comparing against the length, we'll now check if the remaining string portion is not empty, which is more robust. It also allows us to detect trailing whitespace, which we didn't before. --- cli/js/40_lint_selector.js | 5 +++-- tests/unit/lint_selectors_test.ts | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cli/js/40_lint_selector.js b/cli/js/40_lint_selector.js index 7b94c4f960..c4cd523f9f 100644 --- a/cli/js/40_lint_selector.js +++ b/cli/js/40_lint_selector.js @@ -406,8 +406,9 @@ export function splitSelectors(input) { } } - if (last < input.length - 1) { - out.push(input.slice(last).trim()); + const remaining = input.slice(last).trim(); + if (remaining.length > 0) { + out.push(remaining); } return out; diff --git a/tests/unit/lint_selectors_test.ts b/tests/unit/lint_selectors_test.ts index c116871318..e9e4dacb4e 100644 --- a/tests/unit/lint_selectors_test.ts +++ b/tests/unit/lint_selectors_test.ts @@ -20,6 +20,9 @@ import { import { assertThrows } from "@std/assert"; Deno.test("splitSelectors", () => { + assertEquals(splitSelectors("*"), ["*"]); + assertEquals(splitSelectors("*,*"), ["*", "*"]); + assertEquals(splitSelectors("*,* "), ["*", "*"]); assertEquals(splitSelectors("foo"), ["foo"]); assertEquals(splitSelectors("foo, bar"), ["foo", "bar"]); assertEquals(splitSelectors("foo:f(bar, baz)"), ["foo:f(bar, baz)"]); From 1661ddd9cab350fe855da0a9d96178407dce5c51 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Tue, 7 Jan 2025 20:14:57 -0800 Subject: [PATCH 076/107] fix(ext/node): have `process` global available in Node context (#27562) This commit makes `process` global always available in Node context. `process` global was previously available explicitly in `deno_node`, but then got removed in #25291 and made globally available regardless of whether it's in Deno or Node context, so this commit does not have any effect on Deno CLI. However, for users who want to use `deno_node` ext only, it makes sense to have `process` available to simulate the Node environment individually. This change may bring some negative performance impact. To measure how large the impact would be, a very simple benchmark was performed whose results can be found at https://github.com/magurotuna/process_global_bench. --- ext/node/global.rs | 5 ++++- ext/node/polyfills/01_require.js | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/node/global.rs b/ext/node/global.rs index a89a3f9830..92439773d6 100644 --- a/ext/node/global.rs +++ b/ext/node/global.rs @@ -54,6 +54,8 @@ const fn str_to_utf16(s: &str) -> [u16; N] { // - clearTimeout (both, but different implementation) // - global (node only) // - performance (both, but different implementation) +// - process (always available in Node, while the availability in Deno depends +// on project creation time in Deno Deploy) // - setImmediate (node only) // - setInterval (both, but different implementation) // - setTimeout (both, but different implementation) @@ -61,7 +63,7 @@ const fn str_to_utf16(s: &str) -> [u16; N] { // UTF-16 encodings of the managed globals. THIS LIST MUST BE SORTED. #[rustfmt::skip] -const MANAGED_GLOBALS: [&[u16]; 12] = [ +const MANAGED_GLOBALS: [&[u16]; 13] = [ &str_to_utf16::<6>("Buffer"), &str_to_utf16::<17>("WorkerGlobalScope"), &str_to_utf16::<14>("clearImmediate"), @@ -69,6 +71,7 @@ const MANAGED_GLOBALS: [&[u16]; 12] = [ &str_to_utf16::<12>("clearTimeout"), &str_to_utf16::<6>("global"), &str_to_utf16::<11>("performance"), + &str_to_utf16::<7>("process"), &str_to_utf16::<4>("self"), &str_to_utf16::<12>("setImmediate"), &str_to_utf16::<11>("setInterval"), diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js index 8f3201755f..3e90750cd3 100644 --- a/ext/node/polyfills/01_require.js +++ b/ext/node/polyfills/01_require.js @@ -946,7 +946,7 @@ Module.prototype.require = function (id) { // wrapper function we run the users code in. The only observable difference is // that in Deno `arguments.callee` is not null. Module.wrapper = [ - "(function (exports, require, module, __filename, __dirname, Buffer, clearImmediate, clearInterval, clearTimeout, global, setImmediate, setInterval, setTimeout, performance) { (function (exports, require, module, __filename, __dirname) {", + "(function (exports, require, module, __filename, __dirname, Buffer, clearImmediate, clearInterval, clearTimeout, global, process, setImmediate, setInterval, setTimeout, performance) { (function (exports, require, module, __filename, __dirname) {", "\n}).call(this, exports, require, module, __filename, __dirname); })", ]; Module.wrap = function (script) { @@ -1031,6 +1031,7 @@ Module.prototype._compile = function (content, filename, format) { clearInterval, clearTimeout, global, + process, setImmediate, setInterval, setTimeout, @@ -1049,6 +1050,7 @@ Module.prototype._compile = function (content, filename, format) { clearInterval, clearTimeout, global, + process, setImmediate, setInterval, setTimeout, From e233173653c19a0eb44418ed639df69b5f137acb Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Wed, 8 Jan 2025 20:07:47 +0530 Subject: [PATCH 077/107] fix(ext/websocket): Fix close code without reason (#27578) Fixes https://github.com/denoland/deno/issues/27566 The close code wasn't sent if reason was None, defaulting to 1005. This patch allows sending close code without reason. --- ext/websocket/lib.rs | 7 ++++++- tests/integration/run_tests.rs | 1 - tests/unit/websocket_test.ts | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index 5819906da6..b47dbef3e1 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -758,9 +758,14 @@ pub async fn op_ws_close( return Ok(()); }; + const EMPTY_PAYLOAD: &[u8] = &[]; + let frame = reason .map(|reason| Frame::close(code.unwrap_or(1005), reason.as_bytes())) - .unwrap_or_else(|| Frame::close_raw(vec![].into())); + .unwrap_or_else(|| match code { + Some(code) => Frame::close(code, EMPTY_PAYLOAD), + _ => Frame::close_raw(EMPTY_PAYLOAD.into()), + }); resource.closed.set(true); let lock = resource.reserve_lock(); diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index ebb372418e..5e4db3d3e4 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -2663,7 +2663,6 @@ async fn websocket_server_multi_field_connection_header() { let message = socket.read_frame().await.unwrap(); assert_eq!(message.opcode, fastwebsockets::OpCode::Close); - assert!(message.payload.is_empty()); socket .write_frame(fastwebsockets::Frame::close_raw(vec![].into())) .await diff --git a/tests/unit/websocket_test.ts b/tests/unit/websocket_test.ts index 1c8c4581bf..e5e4b1a7a7 100644 --- a/tests/unit/websocket_test.ts +++ b/tests/unit/websocket_test.ts @@ -262,7 +262,7 @@ Deno.test({ socket.onopen = () => socket.send("Hello"); socket.onmessage = () => { socket.send("Bye"); - socket.close(); + socket.close(1000); }; socket.onclose = () => ac.abort(); socket.onerror = () => fail(); @@ -288,7 +288,8 @@ Deno.test({ seenBye = true; } }; - ws.onclose = () => { + ws.onclose = (e) => { + assertEquals(e.code, 1000); deferred.resolve(); }; await Promise.all([deferred.promise, server.finished]); From fffa3804aa66eb7cee2485fdf701cad52bfd31d7 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Wed, 8 Jan 2025 22:09:55 +0530 Subject: [PATCH 078/107] fix(ext/node): Fix `os.cpus()` on Linux (#27592) Populate `speed` using current scaling frequency and fix times multiplier. Fixes https://github.com/denoland/deno/issues/27555
Node.js Deno
``` > os.cpus() [ { model: 'AMD Ryzen 5 7530U with Radeon Graphics', speed: 1396, times: { user: 1769930, nice: 20, sys: 525630, idle: 41325700, irq: 110060 } }, ``` ``` > os.cpus() [ { model: "AMD Ryzen 5 7530U with Radeon Graphics", speed: 1630, times: [Object: null prototype] { user: 1795620, nice: 20, sys: 537840, idle: 41589390, irq: 111230 } }, ```
--- ext/node/ops/os/cpus.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/ext/node/ops/os/cpus.rs b/ext/node/ops/os/cpus.rs index d9b28ce88c..4dd0e59a17 100644 --- a/ext/node/ops/os/cpus.rs +++ b/ext/node/ops/os/cpus.rs @@ -264,13 +264,16 @@ pub fn cpu_info() -> Option> { let nice = fields.next()?.parse::().ok()?; let sys = fields.next()?.parse::().ok()?; let idle = fields.next()?.parse::().ok()?; + let _iowait = fields.next()?.parse::().ok()?; let irq = fields.next()?.parse::().ok()?; - cpus[i].times.user = user; - cpus[i].times.nice = nice; - cpus[i].times.sys = sys; - cpus[i].times.idle = idle; - cpus[i].times.irq = irq; + // sysconf(_SC_CLK_TCK) is fixed at 100 Hz, therefore the + // multiplier is always 1000/100 = 10 + cpus[i].times.user = user * 10; + cpus[i].times.nice = nice * 10; + cpus[i].times.sys = sys * 10; + cpus[i].times.idle = idle * 10; + cpus[i].times.irq = irq * 10; } let fp = std::fs::File::open("/proc/cpuinfo").ok()?; @@ -287,6 +290,18 @@ pub fn cpu_info() -> Option> { let model = fields.next()?.trim(); cpus[j].model = model.to_string(); + + if let Ok(fp) = std::fs::File::open(format!( + "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq", + j + )) { + let mut reader = std::io::BufReader::new(fp); + let mut speed = String::new(); + reader.read_line(&mut speed).ok()?; + + cpus[j].speed = speed.trim().parse::().ok()? / 1000; + } + j += 1; } From fc2788bfd73d236e64688efda5686325c622081f Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 8 Jan 2025 14:46:15 -0500 Subject: [PATCH 079/107] fix(jsr): Wasm imports fail to load (#27594) * https://github.com/denoland/deno_graph/pull/562 Closes https://github.com/denoland/deno/issues/27593 --- Cargo.lock | 4 ++-- cli/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1798fd677..df587d0ad0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,9 +1750,9 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.86.8" +version = "0.86.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ceaff8e0b1ea21369c1aff28d75fed0c4194896195cf57c2fbee4f7db3e4c42" +checksum = "fa96b7d353c0d36b108ec504272b148792f48dccaf29f302b39c46b43457bb94" dependencies = [ "anyhow", "async-trait", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2adbbfc604..a2cbd746ca 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -74,7 +74,7 @@ deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] } deno_error.workspace = true -deno_graph = { version = "=0.86.8" } +deno_graph = { version = "=0.86.9" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true deno_npm.workspace = true From 814da49dff3b891eb5ede4f2f976f9a3674640e9 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Thu, 9 Jan 2025 05:48:23 +0800 Subject: [PATCH 080/107] fix(ext/net): update moka cache to avoid potential panic in `Deno.resolveDns` on some laptops with Ryzen CPU (#27572) --- Cargo.lock | 228 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 192 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df587d0ad0..e20bdad7e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3513,6 +3513,19 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +dependencies = [ + "cfg-if", + "libc", + "log", + "rustversion", + "windows 0.58.0", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -3588,8 +3601,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -4777,6 +4790,19 @@ dependencies = [ "serde", ] +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "lsp-types" version = "0.97.0" @@ -4831,6 +4857,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -4943,21 +4978,20 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.7" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e0d88686dc561d743b40de8269b26eaf0dc58781bde087b0984646602021d08" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "once_cell", + "loom", "parking_lot", - "quanta", + "portable-atomic", "rustc_version 0.4.0", "smallvec", "tagptr", "thiserror 1.0.64", - "triomphe", "uuid", ] @@ -5135,6 +5169,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -5392,6 +5436,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "p224" version = "0.13.2" @@ -5726,6 +5776,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + [[package]] name = "powerfmt" version = "0.2.0" @@ -5943,21 +5999,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "quanta" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" -dependencies = [ - "crossbeam-utils", - "libc", - "once_cell", - "raw-cpuid", - "wasi", - "web-sys", - "winapi", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -6127,15 +6168,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" -[[package]] -name = "raw-cpuid" -version = "11.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" -dependencies = [ - "bitflags 2.6.0", -] - [[package]] name = "raw-window-handle" version = "0.6.1" @@ -6210,8 +6242,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -6222,9 +6263,15 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.3", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.3" @@ -6858,6 +6905,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell-escape" version = "0.1.5" @@ -8189,6 +8245,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -8450,6 +8536,12 @@ dependencies = [ "wtf8", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "value-trait" version = "0.10.0" @@ -8778,7 +8870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b2b1bf557d947847a30eb73f79aa6cdb3eaf3ce02f5e9599438f77896a62b3c" dependencies = [ "thiserror 1.0.64", - "windows", + "windows 0.52.0", ] [[package]] @@ -8818,7 +8910,17 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", "windows-targets 0.52.6", ] @@ -8831,6 +8933,60 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" From ea30e188a82519cdb46ff1ed17b6a9f49e18c45f Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Wed, 8 Jan 2025 14:52:32 -0800 Subject: [PATCH 081/107] refactor: update deno_core for error refactor (#26867) Closes #26171 --------- Co-authored-by: David Sherret --- Cargo.lock | 157 +- Cargo.toml | 12 +- cli/Cargo.toml | 7 +- cli/args/lockfile.rs | 21 +- cli/args/mod.rs | 18 +- cli/build.rs | 18 +- cli/cache/mod.rs | 17 +- cli/emit.rs | 48 +- cli/errors.rs | 123 - cli/factory.rs | 5 +- cli/graph_util.rs | 78 +- cli/http_util.rs | 38 +- cli/lsp/analysis.rs | 13 +- cli/lsp/config.rs | 3 +- cli/lsp/diagnostics.rs | 6 +- cli/lsp/documents.rs | 4 +- cli/lsp/language_server.rs | 2 +- cli/lsp/text.rs | 6 +- cli/lsp/tsc.rs | 34 +- cli/main.rs | 13 +- cli/mainrt.rs | 10 +- cli/module_loader.rs | 125 +- cli/npm/managed/mod.rs | 49 +- cli/npm/managed/resolution.rs | 5 +- cli/npm/managed/resolvers/common.rs | 18 +- .../managed/resolvers/common/bin_entries.rs | 126 +- .../resolvers/common/lifecycle_scripts.rs | 58 +- cli/npm/managed/resolvers/global.rs | 23 +- cli/npm/managed/resolvers/local.rs | 130 +- cli/npm/managed/resolvers/mod.rs | 1 + cli/npm/mod.rs | 5 +- cli/npm/permission_checker.rs | 34 +- cli/ops/bench.rs | 12 +- cli/ops/jupyter.rs | 4 +- cli/ops/lint.rs | 23 +- cli/ops/testing.rs | 12 +- cli/resolver.rs | 27 +- cli/standalone/mod.rs | 128 +- cli/tools/bench/mod.rs | 54 +- cli/tools/check.rs | 27 +- cli/tools/compile.rs | 4 +- cli/tools/coverage/mod.rs | 9 +- cli/tools/doc.rs | 101 +- cli/tools/fmt.rs | 13 +- cli/tools/info.rs | 8 +- cli/tools/installer.rs | 22 +- cli/tools/jupyter/mod.rs | 6 +- cli/tools/lint/mod.rs | 13 +- cli/tools/lint/rules/no_sloppy_imports.rs | 6 +- cli/tools/registry/paths.rs | 4 +- cli/tools/repl/channel.rs | 11 +- cli/tools/repl/session.rs | 50 +- cli/tools/run/hmr.rs | 35 +- cli/tools/serve.rs | 4 +- cli/tools/test/channel.rs | 3 +- cli/tools/test/mod.rs | 99 +- cli/tools/test/reporters/compound.rs | 2 +- cli/tools/test/reporters/junit.rs | 2 + cli/tsc/diagnostics.rs | 3 +- cli/tsc/mod.rs | 119 +- cli/util/file_watcher.rs | 18 +- cli/util/fs.rs | 83 +- cli/util/result.rs | 27 + cli/worker.rs | 40 +- ext/broadcast_channel/Cargo.toml | 1 + ext/broadcast_channel/lib.rs | 32 +- ext/cache/Cargo.toml | 1 + ext/cache/lib.rs | 27 +- ext/cache/sqlite.rs | 21 +- ext/canvas/Cargo.toml | 1 + ext/canvas/lib.rs | 4 +- ext/cron/Cargo.toml | 1 + ext/cron/lib.rs | 20 +- ext/crypto/Cargo.toml | 1 + ext/crypto/decrypt.rs | 18 +- ext/crypto/ed25519.rs | 5 +- ext/crypto/encrypt.rs | 15 +- ext/crypto/export_key.rs | 11 +- ext/crypto/generate_key.rs | 10 +- ext/crypto/import_key.rs | 10 +- ext/crypto/lib.rs | 188 +- ext/crypto/shared.rs | 12 +- ext/crypto/x25519.rs | 4 +- ext/crypto/x448.rs | 4 +- ext/fetch/Cargo.toml | 1 + ext/fetch/fs_fetch_handler.rs | 3 +- ext/fetch/lib.rs | 92 +- ext/ffi/Cargo.toml | 1 + ext/ffi/call.rs | 15 +- ext/ffi/callback.rs | 36 +- ext/ffi/dlfcn.rs | 33 +- ext/ffi/ir.rs | 3 +- ext/ffi/repr.rs | 4 +- ext/ffi/static.rs | 13 +- ext/ffi/symbol.rs | 9 +- ext/fs/Cargo.toml | 1 + ext/fs/ops.rs | 36 +- ext/http/Cargo.toml | 1 + ext/http/http_next.rs | 66 +- ext/http/lib.rs | 63 +- ext/http/request_body.rs | 6 +- ext/http/request_properties.rs | 17 +- ext/http/response_body.rs | 10 +- ext/http/service.rs | 3 +- ext/http/websocket_upgrade.rs | 10 +- ext/io/Cargo.toml | 1 + ext/io/fs.rs | 55 +- ext/io/lib.rs | 9 +- ext/kv/Cargo.toml | 1 + ext/kv/dynamic.rs | 31 +- ext/kv/interface.rs | 4 +- ext/kv/lib.rs | 65 +- ext/kv/remote.rs | 57 +- ext/kv/sqlite.rs | 44 +- ext/napi/Cargo.toml | 1 + ext/napi/lib.rs | 44 +- ext/net/Cargo.toml | 1 + ext/net/io.rs | 13 +- ext/net/lib.rs | 5 +- ext/net/ops.rs | 49 +- ext/net/ops_tls.rs | 3 +- ext/net/quic.rs | 60 +- ext/net/raw.rs | 52 +- ext/node/Cargo.toml | 1 + ext/node/lib.rs | 6 +- ext/node/ops/blocklist.rs | 3 +- ext/node/ops/buffer.rs | 18 +- ext/node/ops/crypto/cipher.rs | 31 +- ext/node/ops/crypto/digest.rs | 3 +- ext/node/ops/crypto/keys.rs | 192 +- ext/node/ops/crypto/mod.rs | 73 +- ext/node/ops/crypto/sign.rs | 5 +- ext/node/ops/crypto/x509.rs | 12 +- ext/node/ops/fs.rs | 21 +- ext/node/ops/http.rs | 41 +- ext/node/ops/http2.rs | 67 +- ext/node/ops/idna.rs | 11 +- ext/node/ops/inspector.rs | 23 +- ext/node/ops/ipc.rs | 23 +- ext/node/ops/os/mod.rs | 31 +- ext/node/ops/os/priority.rs | 4 +- ext/node/ops/perf_hooks.rs | 3 +- ext/node/ops/process.rs | 2 +- ext/node/ops/require.rs | 84 +- ext/node/ops/util.rs | 2 +- ext/node/ops/v8.rs | 19 +- ext/node/ops/vm_internal.rs | 7 +- ext/node/ops/worker_threads.rs | 34 +- ext/node/ops/zlib/brotli.rs | 42 +- ext/node/ops/zlib/mod.rs | 41 +- ext/node/ops/zlib/mode.rs | 3 +- ext/telemetry/Cargo.toml | 2 + ext/telemetry/lib.rs | 111 +- ext/tls/Cargo.toml | 1 + ext/tls/lib.rs | 13 +- ext/url/Cargo.toml | 1 + ext/url/lib.rs | 8 +- ext/url/urlpattern.rs | 13 +- ext/web/Cargo.toml | 1 + ext/web/blob.rs | 6 +- ext/web/compression.rs | 6 +- ext/web/lib.rs | 9 +- ext/web/message_port.rs | 9 +- ext/web/stream_resource.rs | 9 +- ext/webgpu/Cargo.toml | 1 + ext/webgpu/binding.rs | 12 +- ext/webgpu/buffer.rs | 36 +- ext/webgpu/bundle.rs | 53 +- ext/webgpu/byow.rs | 15 +- ext/webgpu/command_encoder.rs | 32 +- ext/webgpu/compute_pass.rs | 18 +- ext/webgpu/lib.rs | 30 +- ext/webgpu/pipeline.rs | 10 +- ext/webgpu/queue.rs | 10 +- ext/webgpu/render_pass.rs | 59 +- ext/webgpu/sampler.rs | 2 +- ext/webgpu/shader.rs | 2 +- ext/webgpu/surface.rs | 34 +- ext/webgpu/texture.rs | 4 +- ext/websocket/Cargo.toml | 1 + ext/websocket/lib.rs | 46 +- ext/webstorage/Cargo.toml | 1 + ext/webstorage/lib.rs | 6 +- resolvers/deno/Cargo.toml | 1 + resolvers/deno/lib.rs | 15 +- resolvers/deno/npm/byonm.rs | 6 +- resolvers/deno/npm/mod.rs | 29 +- resolvers/node/Cargo.toml | 1 + resolvers/node/errors.rs | 136 +- resolvers/npm_cache/fs_util.rs | 111 +- resolvers/npm_cache/lib.rs | 95 +- resolvers/npm_cache/registry_info.rs | 134 +- resolvers/npm_cache/remote.rs | 22 +- resolvers/npm_cache/tarball.rs | 57 +- resolvers/npm_cache/tarball_extract.rs | 103 +- runtime/Cargo.toml | 1 + runtime/errors.rs | 1983 ----------------- runtime/lib.rs | 1 - runtime/ops/fs_events.rs | 43 +- runtime/ops/http.rs | 20 +- runtime/ops/os/mod.rs | 48 +- runtime/ops/permissions.rs | 7 +- runtime/ops/process.rs | 62 +- runtime/ops/signal.rs | 11 +- runtime/ops/tty.rs | 69 +- runtime/ops/web_worker/sync_fetch.rs | 33 +- runtime/ops/worker_host.rs | 7 +- runtime/permissions/Cargo.toml | 1 + runtime/permissions/lib.rs | 38 +- runtime/shared.rs | 21 +- runtime/web_worker.rs | 43 +- runtime/worker.rs | 52 +- .../npmrc_tarball_other_server/fail/main.out | 2 +- .../success/main.out | 2 +- 214 files changed, 3787 insertions(+), 4210 deletions(-) delete mode 100644 cli/errors.rs delete mode 100644 runtime/errors.rs diff --git a/Cargo.lock b/Cargo.lock index e20bdad7e1..d55c1a19aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1275,7 +1275,7 @@ dependencies = [ "deno_npm", "deno_npm_cache", "deno_package_json", - "deno_path_util 0.3.0", + "deno_path_util", "deno_resolver", "deno_runtime", "deno_semver", @@ -1434,6 +1434,7 @@ version = "0.178.0" dependencies = [ "async-trait", "deno_core", + "deno_error", "thiserror 2.0.3", "tokio", "uuid", @@ -1445,6 +1446,7 @@ version = "0.116.0" dependencies = [ "async-trait", "deno_core", + "deno_error", "rusqlite", "serde", "sha2", @@ -1467,7 +1469,7 @@ dependencies = [ "data-url", "deno_error", "deno_media_type", - "deno_path_util 0.3.0", + "deno_path_util", "http 1.1.0", "indexmap 2.3.0", "log", @@ -1486,6 +1488,7 @@ name = "deno_canvas" version = "0.53.0" dependencies = [ "deno_core", + "deno_error", "deno_webgpu", "image", "serde", @@ -1494,13 +1497,14 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.42.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45aaf31e58ca915d5c0746bf8e2d07b94635154ad9e5afe5ff265cae6187b19" +checksum = "6c4c11bd51ef6738cabfc3c53f16c209a0b8615cb1e4e5bf3b14e3b5deebfe21" dependencies = [ - "anyhow", + "boxed_error", + "deno_error", "deno_package_json", - "deno_path_util 0.3.0", + "deno_path_util", "deno_semver", "glob", "ignore", @@ -1513,7 +1517,7 @@ dependencies = [ "serde", "serde_json", "sys_traits", - "thiserror 1.0.64", + "thiserror 2.0.3", "url", ] @@ -1526,9 +1530,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.327.0" +version = "0.330.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf8dff204b9c2415deb47b9f30d4d38b0925d0d88f1f9074e8e76f59e6d7ded" +checksum = "fd38bbbd68ed873165ccb630322704b44140d3a8c8d50f898beac4d1a8a3358c" dependencies = [ "anyhow", "az", @@ -1539,6 +1543,7 @@ dependencies = [ "capacity_builder 0.1.3", "cooked-waker", "deno_core_icudata", + "deno_error", "deno_ops", "deno_unsync", "futures", @@ -1554,6 +1559,7 @@ dependencies = [ "smallvec", "sourcemap 8.0.1", "static_assertions", + "thiserror 2.0.3", "tokio", "url", "v8", @@ -1574,6 +1580,7 @@ dependencies = [ "async-trait", "chrono", "deno_core", + "deno_error", "saffron", "thiserror 2.0.3", "tokio", @@ -1592,6 +1599,7 @@ dependencies = [ "ctr", "curve25519-dalek", "deno_core", + "deno_error", "deno_web", "ed448-goldilocks", "elliptic-curve", @@ -1618,16 +1626,17 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.161.3" +version = "0.164.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353a39c70d248af04600928cefc8066a9e4535fb6e7d7c518411e5efc822819f" +checksum = "ad1edb02603c7e8a4003c84af2482a05e5eda3a14f1af275434fda89223f054d" dependencies = [ "anyhow", "cfg-if", "comrak", "deno_ast", "deno_graph", - "deno_path_util 0.2.2", + "deno_path_util", + "deno_terminal 0.2.0", "handlebars", "html-escape", "import_map", @@ -1647,22 +1656,23 @@ dependencies = [ [[package]] name = "deno_error" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c66ffd17ee1a948904d33f3d3f364573951c1f9fb3f859bfe7770bf33862a" +checksum = "c4da6a58de6932a96f84e133c072fd3b525966ee122a71f3efd48bbff2eed5ac" dependencies = [ "deno_error_macro", "libc", "serde", "serde_json", + "tokio", "url", ] [[package]] name = "deno_error_macro" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd99df6ae75443907e1f959fc42ec6dcea67a7bd083e76cf23a117102c9a2ce" +checksum = "46351dff93aed2039407c91e2ded2a5591e42d2795ab3d111288625bb710d3d2" dependencies = [ "proc-macro2", "quote", @@ -1677,7 +1687,8 @@ dependencies = [ "bytes", "data-url", "deno_core", - "deno_path_util 0.3.0", + "deno_error", + "deno_path_util", "deno_permissions", "deno_tls", "dyn-clone", @@ -1710,6 +1721,7 @@ name = "deno_ffi" version = "0.171.0" dependencies = [ "deno_core", + "deno_error", "deno_permissions", "dlopen2", "dynasmrt", @@ -1733,8 +1745,9 @@ dependencies = [ "base32", "boxed_error", "deno_core", + "deno_error", "deno_io", - "deno_path_util 0.3.0", + "deno_path_util", "deno_permissions", "filetime", "junction", @@ -1750,17 +1763,17 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.86.9" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa96b7d353c0d36b108ec504272b148792f48dccaf29f302b39c46b43457bb94" +checksum = "f56d4eb4b7c81ae920b6d18c45a1866924f93110caee80bbbc362dc28143f2bb" dependencies = [ - "anyhow", "async-trait", "capacity_builder 0.5.0", "data-url", "deno_ast", + "deno_error", "deno_media_type", - "deno_path_util 0.3.0", + "deno_path_util", "deno_semver", "deno_unsync", "encoding_rs", @@ -1794,6 +1807,7 @@ dependencies = [ "bytes", "cache_control", "deno_core", + "deno_error", "deno_net", "deno_websocket", "flate2", @@ -1827,6 +1841,7 @@ version = "0.94.0" dependencies = [ "async-trait", "deno_core", + "deno_error", "filetime", "fs3", "libc", @@ -1853,8 +1868,9 @@ dependencies = [ "bytes", "chrono", "deno_core", + "deno_error", "deno_fetch", - "deno_path_util 0.3.0", + "deno_path_util", "deno_permissions", "deno_tls", "denokv_proto", @@ -1920,6 +1936,7 @@ name = "deno_napi" version = "0.115.0" dependencies = [ "deno_core", + "deno_error", "deno_permissions", "libc", "libloading 0.7.4", @@ -1948,6 +1965,7 @@ name = "deno_net" version = "0.176.0" dependencies = [ "deno_core", + "deno_error", "deno_permissions", "deno_tls", "hickory-proto", @@ -1977,13 +1995,14 @@ dependencies = [ "const-oid", "data-encoding", "deno_core", + "deno_error", "deno_fetch", "deno_fs", "deno_io", "deno_media_type", "deno_net", "deno_package_json", - "deno_path_util 0.3.0", + "deno_path_util", "deno_permissions", "deno_whoami", "der", @@ -2085,7 +2104,7 @@ dependencies = [ "deno_core", "deno_error", "deno_npm", - "deno_path_util 0.3.0", + "deno_path_util", "deno_semver", "deno_unsync", "faster-hex", @@ -2107,10 +2126,11 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.203.0" +version = "0.206.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146ca74cac431843486ade58e2accc16c11315fb2c6934590a52a73c56b7ec3" +checksum = "4c25ffa9d088ea00748dbef870bba110ac22ebf8cf7b2e9eb288409c5d852af3" dependencies = [ + "indexmap 2.3.0", "proc-macro-rules", "proc-macro2", "quote", @@ -2118,7 +2138,7 @@ dependencies = [ "strum", "strum_macros", "syn 2.0.87", - "thiserror 1.0.64", + "thiserror 2.0.3", ] [[package]] @@ -2129,7 +2149,7 @@ checksum = "e1d3c0f699ba2040669204ce24ab73720499fc290af843e4ce0fc8a9b3d67735" dependencies = [ "boxed_error", "deno_error", - "deno_path_util 0.3.0", + "deno_path_util", "deno_semver", "indexmap 2.3.0", "serde", @@ -2139,18 +2159,6 @@ dependencies = [ "url", ] -[[package]] -name = "deno_path_util" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b02c7d341e1b2cf089daff0f4fb2b4be8f3b5511b1d96040b3f7ed63a66c737b" -dependencies = [ - "deno_error", - "percent-encoding", - "thiserror 2.0.3", - "url", -] - [[package]] name = "deno_path_util" version = "0.3.0" @@ -2170,7 +2178,8 @@ version = "0.43.0" dependencies = [ "capacity_builder 0.5.0", "deno_core", - "deno_path_util 0.3.0", + "deno_error", + "deno_path_util", "deno_terminal 0.2.0", "fqdn", "libc", @@ -2192,9 +2201,10 @@ dependencies = [ "boxed_error", "dashmap", "deno_config", + "deno_error", "deno_media_type", "deno_package_json", - "deno_path_util 0.3.0", + "deno_path_util", "deno_semver", "node_resolver", "sys_traits", @@ -2216,6 +2226,7 @@ dependencies = [ "deno_core", "deno_cron", "deno_crypto", + "deno_error", "deno_fetch", "deno_ffi", "deno_fs", @@ -2225,7 +2236,7 @@ dependencies = [ "deno_napi", "deno_net", "deno_node", - "deno_path_util 0.3.0", + "deno_path_util", "deno_permissions", "deno_telemetry", "deno_terminal 0.2.0", @@ -2314,6 +2325,7 @@ version = "0.6.0" dependencies = [ "async-trait", "deno_core", + "deno_error", "http-body-util", "hyper 1.4.1", "hyper-util", @@ -2326,6 +2338,7 @@ dependencies = [ "opentelemetry_sdk", "pin-project", "serde", + "thiserror 2.0.3", "tokio", ] @@ -2354,6 +2367,7 @@ name = "deno_tls" version = "0.171.0" dependencies = [ "deno_core", + "deno_error", "deno_native_certs", "rustls", "rustls-pemfile", @@ -2406,6 +2420,7 @@ dependencies = [ "deno_bench_util", "deno_console", "deno_core", + "deno_error", "deno_webidl", "thiserror 2.0.3", "urlpattern", @@ -2421,6 +2436,7 @@ dependencies = [ "deno_bench_util", "deno_console", "deno_core", + "deno_error", "deno_permissions", "deno_url", "deno_webidl", @@ -2438,6 +2454,7 @@ name = "deno_webgpu" version = "0.151.0" dependencies = [ "deno_core", + "deno_error", "raw-window-handle", "serde", "thiserror 2.0.3", @@ -2460,6 +2477,7 @@ version = "0.189.0" dependencies = [ "bytes", "deno_core", + "deno_error", "deno_net", "deno_permissions", "deno_tls", @@ -2481,6 +2499,7 @@ name = "deno_webstorage" version = "0.179.0" dependencies = [ "deno_core", + "deno_error", "deno_web", "rusqlite", "thiserror 2.0.3", @@ -2498,13 +2517,13 @@ dependencies = [ [[package]] name = "denokv_proto" -version = "0.8.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ba1f99ed11a9c11e868a8521b1f71a7e1aba785d7f42ea9ecbdc01146c89ec" +checksum = "d5b77de4d3b9215e14624d4f4eb16cb38c0810e3f5860ba3b3fc47d0537f9a4d" dependencies = [ - "anyhow", "async-trait", "chrono", + "deno_error", "futures", "num-bigint", "prost", @@ -2514,15 +2533,15 @@ dependencies = [ [[package]] name = "denokv_remote" -version = "0.8.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ed833073189e8f6d03155fe3b05a024e75e29d8a28a4c2e9ec3b5c925e727b" +checksum = "c6497c28eec268ed99f1e8664f0842935f02d1508529c67d94c57ca5d893d743" dependencies = [ - "anyhow", "async-stream", "async-trait", "bytes", "chrono", + "deno_error", "denokv_proto", "futures", "http 1.1.0", @@ -2531,6 +2550,7 @@ dependencies = [ "rand", "serde", "serde_json", + "thiserror 2.0.3", "tokio", "tokio-util", "url", @@ -2539,14 +2559,14 @@ dependencies = [ [[package]] name = "denokv_sqlite" -version = "0.8.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b790f01d1302d53a0c3cbd27de88a06b3abd64ec8ab8673924e490541c7c713" +checksum = "dc0f21a450a35eb85760761401fddf9bfff9840127be07a6ca5c31863127913d" dependencies = [ - "anyhow", "async-stream", "async-trait", "chrono", + "deno_error", "denokv_proto", "futures", "hex", @@ -2555,7 +2575,7 @@ dependencies = [ "rand", "rusqlite", "serde_json", - "thiserror 1.0.64", + "thiserror 2.0.3", "tokio", "tokio-stream", "uuid", @@ -4331,16 +4351,18 @@ dependencies = [ [[package]] name = "import_map" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a787decc56f38d65d16d32687265045d6d6a4531b4a0e1b649def3590354e" +checksum = "1215d4d92511fbbdaea50e750e91f2429598ef817f02b579158e92803b52c00a" dependencies = [ + "boxed_error", + "deno_error", "indexmap 2.3.0", "log", "percent-encoding", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 2.0.3", "url", ] @@ -5106,9 +5128,10 @@ dependencies = [ "anyhow", "async-trait", "boxed_error", + "deno_error", "deno_media_type", "deno_package_json", - "deno_path_util 0.3.0", + "deno_path_util", "futures", "lazy-regex", "once_cell", @@ -6846,14 +6869,15 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.236.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e23b3abce64010612f88f4ff689a959736f99eb3dc0dbf1c7903434b8bd8cda5" +checksum = "3caa6d882827148e5d9052d9d8d6d1c9d6ad426ed00cab46cafb8c07a0e7126a" dependencies = [ + "deno_error", "num-bigint", "serde", "smallvec", - "thiserror 1.0.64", + "thiserror 2.0.3", "v8", ] @@ -8506,9 +8530,9 @@ dependencies = [ [[package]] name = "v8" -version = "130.0.2" +version = "130.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee0be58935708fa4d7efb970c6cf9f2d9511d24ee24246481a65b6ee167348d" +checksum = "a511192602f7b435b0a241c1947aa743eb7717f20a9195f4b5e8ed1952e01db1" dependencies = [ "bindgen", "bitflags 2.6.0", @@ -8706,11 +8730,12 @@ dependencies = [ [[package]] name = "wasm_dep_analyzer" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f270206a91783fd90625c8bb0d8fbd459d0b1d1bf209b656f713f01ae7c04b8" +checksum = "2eeee3bdea6257cc36d756fa745a70f9d393571e47d69e0ed97581676a5369ca" dependencies = [ - "thiserror 1.0.64", + "deno_error", + "thiserror 2.0.3", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8e7d446b01..75572162c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,10 +48,10 @@ repository = "https://github.com/denoland/deno" [workspace.dependencies] deno_ast = { version = "=0.44.0", features = ["transpiling"] } -deno_core = { version = "0.327.0" } +deno_core = { version = "0.330.0" } deno_bench_util = { version = "0.178.0", path = "./bench_util" } -deno_config = { version = "=0.42.0", features = ["workspace", "sync"] } +deno_config = { version = "=0.43.0", features = ["workspace", "sync"] } deno_lockfile = "=0.24.0" deno_media_type = { version = "0.2.3", features = ["module_specifier"] } deno_npm = "=0.27.0" @@ -63,10 +63,10 @@ deno_terminal = "0.2.0" napi_sym = { version = "0.114.0", path = "./ext/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } -denokv_proto = "0.8.4" -denokv_remote = "0.8.4" +denokv_proto = "0.9.0" +denokv_remote = "0.9.0" # denokv_sqlite brings in bundled sqlite if we don't disable the default features -denokv_sqlite = { default-features = false, version = "0.8.4" } +denokv_sqlite = { default-features = false, version = "0.9.0" } # exts deno_broadcast_channel = { version = "0.178.0", path = "./ext/broadcast_channel" } @@ -119,7 +119,7 @@ dashmap = "5.5.3" data-encoding = "2.3.3" data-url = "=0.3.1" deno_cache_dir = "=0.16.0" -deno_error = "=0.5.2" +deno_error = "=0.5.3" deno_package_json = { version = "0.4.0", default-features = false } deno_unsync = "0.4.2" dlopen2 = "0.6.1" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a2cbd746ca..b77c904c40 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -62,6 +62,7 @@ serde_json.workspace = true zstd.workspace = true glibc_version = "0.1.2" flate2 = { workspace = true, features = ["default"] } +deno_error.workspace = true [target.'cfg(windows)'.build-dependencies] winapi.workspace = true @@ -72,9 +73,9 @@ deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposa deno_cache_dir.workspace = true deno_config.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } -deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] } +deno_doc = { version = "=0.164.0", features = ["rust", "comrak"] } deno_error.workspace = true -deno_graph = { version = "=0.86.9" } +deno_graph = { version = "=0.87.0" } deno_lint = { version = "=0.68.2", features = ["docs"] } deno_lockfile.workspace = true deno_npm.workspace = true @@ -124,7 +125,7 @@ http.workspace = true http-body.workspace = true http-body-util.workspace = true hyper-util.workspace = true -import_map = { version = "=0.20.1", features = ["ext"] } +import_map = { version = "=0.21.0", features = ["ext"] } indexmap.workspace = true jsonc-parser = { workspace = true, features = ["cst", "serde"] } jupyter_runtime = { package = "runtimelib", version = "=0.19.0", features = ["tokio-runtime"] } diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index bc4c92638a..976992aac8 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -10,6 +10,7 @@ use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::parking_lot::MutexGuard; use deno_core::serde_json; +use deno_error::JsErrorBox; use deno_lockfile::Lockfile; use deno_lockfile::WorkspaceMemberConfig; use deno_package_json::PackageJsonDepValue; @@ -59,6 +60,14 @@ impl<'a, T> std::ops::DerefMut for Guard<'a, T> { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[error("Failed writing lockfile")] +#[class(inherit)] +struct AtomicWriteFileWithRetriesError { + #[source] + source: std::io::Error, +} + impl CliLockfile { /// Get the inner deno_lockfile::Lockfile. pub fn lock(&self) -> Guard { @@ -78,7 +87,7 @@ impl CliLockfile { self.lockfile.lock().overwrite } - pub fn write_if_changed(&self) -> Result<(), AnyError> { + pub fn write_if_changed(&self) -> Result<(), JsErrorBox> { if self.skip_write { return Ok(()); } @@ -96,7 +105,9 @@ impl CliLockfile { &bytes, cache::CACHE_PERM, ) - .context("Failed writing lockfile.")?; + .map_err(|source| { + JsErrorBox::from_err(AtomicWriteFileWithRetriesError { source }) + })?; lockfile.has_content_changed = false; Ok(()) } @@ -255,7 +266,7 @@ impl CliLockfile { }) } - pub fn error_if_changed(&self) -> Result<(), AnyError> { + pub fn error_if_changed(&self) -> Result<(), JsErrorBox> { if !self.frozen { return Ok(()); } @@ -267,9 +278,7 @@ impl CliLockfile { let diff = crate::util::diff::diff(&contents, &new_contents); // has an extra newline at the end let diff = diff.trim_end(); - Err(deno_core::anyhow::anyhow!( - "The lockfile is out of date. Run `deno install --frozen=false`, or rerun with `--frozen=false` to update it.\nchanges:\n{diff}" - )) + Err(JsErrorBox::generic(format!("The lockfile is out of date. Run `deno install --frozen=false`, or rerun with `--frozen=false` to update it.\nchanges:\n{diff}"))) } else { Ok(()) } diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 35f79a9c3e..ebd321a20a 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -26,6 +26,7 @@ use deno_ast::SourceMapOption; use deno_cache_dir::file_fetcher::CacheSetting; pub use deno_config::deno_json::BenchConfig; pub use deno_config::deno_json::ConfigFile; +use deno_config::deno_json::ConfigFileError; use deno_config::deno_json::FmtConfig; pub use deno_config::deno_json::FmtOptionsConfig; use deno_config::deno_json::LintConfig; @@ -55,6 +56,7 @@ use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::url::Url; +use deno_error::JsErrorBox; use deno_graph::GraphKind; pub use deno_json::check_warn_tsconfig; use deno_lint::linter::LintConfig as DenoLintConfig; @@ -604,7 +606,8 @@ pub fn create_default_npmrc() -> Arc { }) } -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, deno_error::JsError)] +#[class(generic)] pub enum RootCertStoreLoadError { #[error( "Unknown certificate store \"{0}\" specified (allowed: \"system,mozilla\")" @@ -1104,7 +1107,7 @@ impl CliOptions { pkg_json_dep_resolution, specified_import_map: cli_arg_specified_import_map, }, - |path| Ok(std::fs::read_to_string(path)?), + |path| std::fs::read_to_string(path).map_err(JsErrorBox::from_err), )?) } @@ -1246,11 +1249,14 @@ impl CliOptions { pub fn node_modules_dir( &self, - ) -> Result, AnyError> { + ) -> Result< + Option, + deno_config::deno_json::NodeModulesDirParseError, + > { if let Some(flag) = self.flags.node_modules_dir { return Ok(Some(flag)); } - self.workspace().node_modules_dir().map_err(Into::into) + self.workspace().node_modules_dir() } pub fn vendor_dir_path(&self) -> Option<&PathBuf> { @@ -1260,7 +1266,7 @@ impl CliOptions { pub fn resolve_ts_config_for_emit( &self, config_type: TsConfigType, - ) -> Result { + ) -> Result { self.workspace().resolve_ts_config_for_emit(config_type) } @@ -1289,7 +1295,7 @@ impl CliOptions { pub fn to_compiler_option_types( &self, - ) -> Result, AnyError> { + ) -> Result, serde_json::Error> { self .workspace() .to_compiler_option_types() diff --git a/cli/build.rs b/cli/build.rs index 83290599e6..590fee795d 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -13,10 +13,9 @@ mod ts { use std::path::Path; use std::path::PathBuf; - use deno_core::error::custom_error; - use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; + use deno_error::JsErrorBox; use serde::Serialize; use super::*; @@ -53,7 +52,7 @@ mod ts { fn op_script_version( _state: &mut OpState, #[string] _arg: &str, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { Ok(Some("1".to_string())) } @@ -72,7 +71,7 @@ mod ts { fn op_load( state: &mut OpState, #[string] load_specifier: &str, - ) -> Result { + ) -> Result { let op_crate_libs = state.borrow::>(); let path_dts = state.borrow::(); let re_asset = lazy_regex::regex!(r"asset:/{3}lib\.(\S+)\.d\.ts"); @@ -93,12 +92,15 @@ mod ts { // if it comes from an op crate, we were supplied with the path to the // file. let path = if let Some(op_crate_lib) = op_crate_libs.get(lib) { - PathBuf::from(op_crate_lib).canonicalize()? + PathBuf::from(op_crate_lib) + .canonicalize() + .map_err(JsErrorBox::from_err)? // otherwise we will generate the path ourself } else { path_dts.join(format!("lib.{lib}.d.ts")) }; - let data = std::fs::read_to_string(path)?; + let data = + std::fs::read_to_string(path).map_err(JsErrorBox::from_err)?; Ok(LoadResponse { data, version: "1".to_string(), @@ -106,13 +108,13 @@ mod ts { script_kind: 3, }) } else { - Err(custom_error( + Err(JsErrorBox::new( "InvalidSpecifier", format!("An invalid specifier was requested: {}", load_specifier), )) } } else { - Err(custom_error( + Err(JsErrorBox::new( "InvalidSpecifier", format!("An invalid specifier was requested: {}", load_specifier), )) diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index fdd8fcf40c..ff9f07fc4e 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -8,7 +8,6 @@ use deno_ast::MediaType; use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::file_fetcher::FetchNoFollowErrorKind; use deno_cache_dir::file_fetcher::FileOrRedirect; -use deno_core::error::AnyError; use deno_core::futures; use deno_core::futures::FutureExt; use deno_core::ModuleSpecifier; @@ -62,6 +61,7 @@ pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache; pub type LocalHttpCache = deno_cache_dir::LocalHttpCache; pub type LocalLspHttpCache = deno_cache_dir::LocalLspHttpCache; pub use deno_cache_dir::HttpCache; +use deno_error::JsErrorBox; pub struct FetchCacherOptions { pub file_header_overrides: HashMap>, @@ -194,9 +194,9 @@ impl Loader for FetchCacher { LoaderCacheSetting::Use => None, LoaderCacheSetting::Reload => { if matches!(file_fetcher.cache_setting(), CacheSetting::Only) { - return Err(deno_core::anyhow::anyhow!( + return Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::generic( "Could not resolve version constraint using only cached data. Try running again without --cached-only" - )); + )))); } Some(CacheSetting::ReloadAll) } @@ -262,28 +262,27 @@ impl Loader for FetchCacher { FetchNoFollowErrorKind::CacheSave { .. } | FetchNoFollowErrorKind::UnsupportedScheme { .. } | FetchNoFollowErrorKind::RedirectHeaderParse { .. } | - FetchNoFollowErrorKind::InvalidHeader { .. } => Err(AnyError::from(err)), + FetchNoFollowErrorKind::InvalidHeader { .. } => Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::from_err(err)))), FetchNoFollowErrorKind::NotCached { .. } => { if options.cache_setting == LoaderCacheSetting::Only { Ok(None) } else { - Err(AnyError::from(err)) + Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::from_err(err)))) } }, FetchNoFollowErrorKind::ChecksumIntegrity(err) => { // convert to the equivalent deno_graph error so that it // enhances it if this is passed to deno_graph Err( - deno_graph::source::ChecksumIntegrityError { + deno_graph::source::LoadError::ChecksumIntegrity(deno_graph::source::ChecksumIntegrityError { actual: err.actual, expected: err.expected, - } - .into(), + }), ) } } }, - CliFetchNoFollowErrorKind::PermissionCheck(permission_check_error) => Err(AnyError::from(permission_check_error)), + CliFetchNoFollowErrorKind::PermissionCheck(permission_check_error) => Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::from_err(permission_check_error)))), } }) } diff --git a/cli/emit.rs b/cli/emit.rs index 32a636de36..e9b5a4e250 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -11,10 +11,12 @@ use deno_ast::SourceRangedForSpanned; use deno_ast::TranspileModuleOptions; use deno_ast::TranspileResult; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::FutureExt; use deno_core::futures::StreamExt; use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; use deno_graph::MediaType; use deno_graph::Module; use deno_graph::ModuleGraph; @@ -124,7 +126,7 @@ impl Emitter { let transpiled_source = deno_core::unsync::spawn_blocking({ let specifier = specifier.clone(); let source = source.clone(); - move || -> Result<_, AnyError> { + move || { EmitParsedSourceHelper::transpile( &parsed_source_cache, &specifier, @@ -155,7 +157,7 @@ impl Emitter { media_type: MediaType, module_kind: deno_ast::ModuleKind, source: &Arc, - ) -> Result { + ) -> Result { // Note: keep this in sync with the async version above let helper = EmitParsedSourceHelper(self); match helper.pre_emit_parsed_source(specifier, module_kind, source) { @@ -210,7 +212,7 @@ impl Emitter { pub async fn load_and_emit_for_hmr( &self, specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { let media_type = MediaType::from_specifier(specifier); let source_code = tokio::fs::read_to_string( ModuleSpecifier::to_file_path(specifier).unwrap(), @@ -225,17 +227,21 @@ impl Emitter { let source_arc: Arc = source_code.into(); let parsed_source = self .parsed_source_cache - .remove_or_parse_module(specifier, source_arc, media_type)?; + .remove_or_parse_module(specifier, source_arc, media_type) + .map_err(JsErrorBox::from_err)?; // HMR doesn't work with embedded source maps for some reason, so set // the option to not use them (though you should test this out because // this statement is probably wrong) let mut options = self.transpile_and_emit_options.1.clone(); options.source_map = SourceMapOption::None; - let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script( - specifier, - media_type, - parsed_source.compute_is_script(), - )?; + let is_cjs = self + .cjs_tracker + .is_cjs_with_known_is_script( + specifier, + media_type, + parsed_source.compute_is_script(), + ) + .map_err(JsErrorBox::from_err)?; let transpiled_source = parsed_source .transpile( &self.transpile_and_emit_options.0, @@ -243,7 +249,8 @@ impl Emitter { module_kind: Some(ModuleKind::from_is_cjs(is_cjs)), }, &options, - )? + ) + .map_err(JsErrorBox::from_err)? .into_source(); Ok(transpiled_source.text) } @@ -282,6 +289,19 @@ enum PreEmitResult { NotCached { source_hash: u64 }, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum EmitParsedSourceHelperError { + #[class(inherit)] + #[error(transparent)] + ParseDiagnostic(#[from] deno_ast::ParseDiagnostic), + #[class(inherit)] + #[error(transparent)] + Transpile(#[from] deno_ast::TranspileError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + /// Helper to share code between async and sync emit_parsed_source methods. struct EmitParsedSourceHelper<'a>(&'a Emitter); @@ -311,7 +331,7 @@ impl<'a> EmitParsedSourceHelper<'a> { source: Arc, transpile_options: &deno_ast::TranspileOptions, emit_options: &deno_ast::EmitOptions, - ) -> Result { + ) -> Result { // nothing else needs the parsed source at this point, so remove from // the cache in order to not transpile owned let parsed_source = parsed_source_cache @@ -351,7 +371,7 @@ impl<'a> EmitParsedSourceHelper<'a> { // todo(dsherret): this is a temporary measure until we have swc erroring for this fn ensure_no_import_assertion( parsed_source: &deno_ast::ParsedSource, -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { fn has_import_assertion(text: &str) -> bool { // good enough text.contains(" assert ") && !text.contains(" with ") @@ -360,7 +380,7 @@ fn ensure_no_import_assertion( fn create_err( parsed_source: &deno_ast::ParsedSource, range: SourceRange, - ) -> AnyError { + ) -> JsErrorBox { let text_info = parsed_source.text_info_lazy(); let loc = text_info.line_and_column_display(range.start); let mut msg = "Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.".to_string(); @@ -373,7 +393,7 @@ fn ensure_no_import_assertion( loc.line_number, loc.column_number, )); - deno_core::anyhow::anyhow!("{}", msg) + JsErrorBox::generic(msg) } let deno_ast::ProgramRef::Module(module) = parsed_source.program_ref() else { diff --git a/cli/errors.rs b/cli/errors.rs deleted file mode 100644 index 6500efec50..0000000000 --- a/cli/errors.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -//! There are many types of errors in Deno: -//! - AnyError: a generic wrapper that can encapsulate any type of error. -//! - JsError: a container for the error message and stack trace for exceptions -//! thrown in JavaScript code. We use this to pretty-print stack traces. -//! - Diagnostic: these are errors that originate in TypeScript's compiler. -//! They're similar to JsError, in that they have line numbers. But -//! Diagnostics are compile-time type errors, whereas JsErrors are runtime -//! exceptions. - -use deno_ast::ParseDiagnostic; -use deno_core::error::AnyError; -use deno_graph::source::ResolveError; -use deno_graph::ModuleError; -use deno_graph::ModuleGraphError; -use deno_graph::ModuleLoadError; -use deno_graph::ResolutionError; -use import_map::ImportMapError; - -fn get_import_map_error_class(_: &ImportMapError) -> &'static str { - "URIError" -} - -fn get_diagnostic_class(_: &ParseDiagnostic) -> &'static str { - "SyntaxError" -} - -pub fn get_module_graph_error_class(err: &ModuleGraphError) -> &'static str { - match err { - ModuleGraphError::ResolutionError(err) - | ModuleGraphError::TypesResolutionError(err) => { - get_resolution_error_class(err) - } - ModuleGraphError::ModuleError(err) => get_module_error_class(err), - } -} - -pub fn get_module_error_class(err: &ModuleError) -> &'static str { - use deno_graph::JsrLoadError; - use deno_graph::NpmLoadError; - - match err { - ModuleError::InvalidTypeAssertion { .. } => "SyntaxError", - ModuleError::ParseErr(_, diagnostic) => get_diagnostic_class(diagnostic), - ModuleError::WasmParseErr(..) => "SyntaxError", - ModuleError::UnsupportedMediaType { .. } - | ModuleError::UnsupportedImportAttributeType { .. } => "TypeError", - ModuleError::Missing(_, _) | ModuleError::MissingDynamic(_, _) => { - "NotFound" - } - ModuleError::LoadingErr(_, _, err) => match err { - ModuleLoadError::Loader(err) => get_error_class_name(err.as_ref()), - ModuleLoadError::HttpsChecksumIntegrity(_) - | ModuleLoadError::TooManyRedirects => "Error", - ModuleLoadError::NodeUnknownBuiltinModule(_) => "NotFound", - ModuleLoadError::Decode(_) => "TypeError", - ModuleLoadError::Npm(err) => match err { - NpmLoadError::NotSupportedEnvironment - | NpmLoadError::PackageReqResolution(_) - | NpmLoadError::RegistryInfo(_) => "Error", - NpmLoadError::PackageReqReferenceParse(_) => "TypeError", - }, - ModuleLoadError::Jsr(err) => match err { - JsrLoadError::UnsupportedManifestChecksum - | JsrLoadError::PackageFormat(_) => "TypeError", - JsrLoadError::ContentLoadExternalSpecifier - | JsrLoadError::ContentLoad(_) - | JsrLoadError::ContentChecksumIntegrity(_) - | JsrLoadError::PackageManifestLoad(_, _) - | JsrLoadError::PackageVersionManifestChecksumIntegrity(..) - | JsrLoadError::PackageVersionManifestLoad(_, _) - | JsrLoadError::RedirectInPackage(_) => "Error", - JsrLoadError::PackageNotFound(_) - | JsrLoadError::PackageReqNotFound(_) - | JsrLoadError::PackageVersionNotFound(_) - | JsrLoadError::UnknownExport { .. } => "NotFound", - }, - }, - } -} - -fn get_resolution_error_class(err: &ResolutionError) -> &'static str { - match err { - ResolutionError::ResolverError { error, .. } => { - use ResolveError::*; - match error.as_ref() { - Specifier(_) => "TypeError", - Other(e) => get_error_class_name(e), - } - } - _ => "TypeError", - } -} - -fn get_try_from_int_error_class(_: &std::num::TryFromIntError) -> &'static str { - "TypeError" -} - -pub fn get_error_class_name(e: &AnyError) -> &'static str { - deno_runtime::errors::get_error_class_name(e) - .or_else(|| { - e.downcast_ref::() - .map(get_import_map_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_diagnostic_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_module_graph_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_resolution_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_try_from_int_error_class) - }) - .unwrap_or("Error") -} diff --git a/cli/factory.rs b/cli/factory.rs index 4ae1d94ea8..86902dfc3b 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -10,6 +10,7 @@ use deno_config::workspace::WorkspaceResolver; use deno_core::error::AnyError; use deno_core::futures::FutureExt; use deno_core::FeatureChecker; +use deno_error::JsErrorBox; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; @@ -118,7 +119,7 @@ impl CliRootCertStoreProvider { } impl RootCertStoreProvider for CliRootCertStoreProvider { - fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> { + fn get_or_try_init(&self) -> Result<&RootCertStore, JsErrorBox> { self .cell .get_or_try_init(|| { @@ -128,7 +129,7 @@ impl RootCertStoreProvider for CliRootCertStoreProvider { self.maybe_ca_data.clone(), ) }) - .map_err(|e| e.into()) + .map_err(JsErrorBox::from_err) } } diff --git a/cli/graph_util.rs b/cli/graph_util.rs index ac9e75cff0..f32dae8a07 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -2,17 +2,18 @@ use std::collections::HashSet; use std::error::Error; -use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; +use deno_config::deno_json; use deno_config::deno_json::JsxImportSourceConfig; use deno_config::workspace::JsrPackageConfig; -use deno_core::anyhow::bail; -use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; +use deno_core::serde_json; use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; use deno_graph::source::Loader; use deno_graph::source::LoaderChecksum; use deno_graph::source::ResolutionKind; @@ -49,8 +50,6 @@ use crate::cache::GlobalHttpCache; use crate::cache::ModuleInfoCache; use crate::cache::ParsedSourceCache; use crate::colors; -use crate::errors::get_error_class_name; -use crate::errors::get_module_graph_error_class; use crate::file_fetcher::CliFileFetcher; use crate::npm::CliNpmResolver; use crate::resolver::CjsTracker; @@ -59,6 +58,7 @@ use crate::resolver::CliSloppyImportsResolver; use crate::resolver::SloppyImportsCachedFs; use crate::sys::CliSys; use crate::tools::check; +use crate::tools::check::CheckError; use crate::tools::check::TypeChecker; use crate::util::file_watcher::WatcherCommunicator; use crate::util::fs::canonicalize_path; @@ -85,7 +85,7 @@ pub fn graph_valid( sys: &CliSys, roots: &[ModuleSpecifier], options: GraphValidOptions, -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if options.exit_integrity_errors { graph_exit_integrity_errors(graph); } @@ -104,9 +104,9 @@ pub fn graph_valid( } else { // finally surface the npm resolution result if let Err(err) = &graph.npm_dep_graph_result { - return Err(custom_error( - get_error_class_name(err), - format_deno_graph_error(err.as_ref().deref()), + return Err(JsErrorBox::new( + err.get_class(), + format_deno_graph_error(err), )); } Ok(()) @@ -145,7 +145,7 @@ pub fn graph_walk_errors<'a>( sys: &'a CliSys, roots: &'a [ModuleSpecifier], options: GraphWalkErrorsOptions, -) -> impl Iterator + 'a { +) -> impl Iterator + 'a { graph .walk( roots.iter(), @@ -197,7 +197,7 @@ pub fn graph_walk_errors<'a>( return None; } - Some(custom_error(get_module_graph_error_class(&error), message)) + Some(JsErrorBox::new(error.get_class(), message)) }) } @@ -437,14 +437,14 @@ impl ModuleGraphCreator { } } - pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), AnyError> { + pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), JsErrorBox> { self.module_graph_builder.graph_valid(graph) } async fn type_check_graph( &self, graph: ModuleGraph, - ) -> Result, AnyError> { + ) -> Result, CheckError> { self .type_checker .check( @@ -467,6 +467,27 @@ pub struct BuildFastCheckGraphOptions<'a> { pub workspace_fast_check: deno_graph::WorkspaceFastCheckOption<'a>, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum BuildGraphWithNpmResolutionError { + #[class(inherit)] + #[error(transparent)] + SerdeJson(#[from] serde_json::Error), + #[class(inherit)] + #[error(transparent)] + ToMaybeJsxImportSourceConfig( + #[from] deno_json::ToMaybeJsxImportSourceConfigError, + ), + #[class(inherit)] + #[error(transparent)] + NodeModulesDirParse(#[from] deno_json::NodeModulesDirParseError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), + #[class(generic)] + #[error("Resolving npm specifier entrypoints this way is currently not supported with \"nodeModules\": \"manual\". In the meantime, try with --node-modules-dir=auto instead")] + UnsupportedNpmSpecifierEntrypointResolutionWay, +} + pub struct ModuleGraphBuilder { caches: Arc, cjs_tracker: Arc, @@ -524,7 +545,7 @@ impl ModuleGraphBuilder { &self, graph: &mut ModuleGraph, options: CreateGraphOptions<'a>, - ) -> Result<(), AnyError> { + ) -> Result<(), BuildGraphWithNpmResolutionError> { enum MutLoaderRef<'a> { Borrowed(&'a mut dyn Loader), Owned(cache::FetchCacher), @@ -652,7 +673,7 @@ impl ModuleGraphBuilder { loader: &'a mut dyn deno_graph::source::Loader, options: deno_graph::BuildOptions<'a>, npm_caching: NpmCachingStrategy, - ) -> Result<(), AnyError> { + ) -> Result<(), BuildGraphWithNpmResolutionError> { // ensure an "npm install" is done if the user has explicitly // opted into using a node_modules directory if self @@ -689,7 +710,7 @@ impl ModuleGraphBuilder { if roots.iter().any(|r| r.scheme() == "npm") && self.npm_resolver.as_byonm().is_some() { - bail!("Resolving npm specifier entrypoints this way is currently not supported with \"nodeModules\": \"manual\". In the meantime, try with --node-modules-dir=auto instead"); + return Err(BuildGraphWithNpmResolutionError::UnsupportedNpmSpecifierEntrypointResolutionWay); } graph.build(roots, loader, options).await; @@ -740,7 +761,7 @@ impl ModuleGraphBuilder { &self, graph: &mut ModuleGraph, options: BuildFastCheckGraphOptions, - ) -> Result<(), AnyError> { + ) -> Result<(), deno_json::ToMaybeJsxImportSourceConfigError> { if !graph.graph_kind().include_types() { return Ok(()); } @@ -804,7 +825,7 @@ impl ModuleGraphBuilder { /// Check if `roots` and their deps are available. Returns `Ok(())` if /// so. Returns `Err(_)` if there is a known module graph or resolution /// error statically reachable from `roots` and not a dynamic import. - pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), AnyError> { + pub fn graph_valid(&self, graph: &ModuleGraph) -> Result<(), JsErrorBox> { self.graph_roots_valid( graph, &graph.roots.iter().cloned().collect::>(), @@ -815,7 +836,7 @@ impl ModuleGraphBuilder { &self, graph: &ModuleGraph, roots: &[ModuleSpecifier], - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { graph_valid( graph, &self.sys, @@ -832,7 +853,10 @@ impl ModuleGraphBuilder { ) } - fn create_graph_resolver(&self) -> Result { + fn create_graph_resolver( + &self, + ) -> Result + { let jsx_import_source_config = self .cli_options .workspace() @@ -1001,8 +1025,13 @@ fn get_resolution_error_bare_specifier( Some(specifier.as_str()) } else if let ResolutionError::ResolverError { error, .. } = error { if let ResolveError::Other(error) = (*error).as_ref() { - if let Some(ImportMapError::UnmappedBareSpecifier(specifier, _)) = - error.downcast_ref::() + if let Some(import_map::ImportMapErrorKind::UnmappedBareSpecifier( + specifier, + _, + )) = error + .as_any() + .downcast_ref::() + .map(|e| &**e) { Some(specifier.as_str()) } else { @@ -1039,11 +1068,12 @@ fn get_import_prefix_missing_error(error: &ResolutionError) -> Option<&str> { ResolveError::Other(other_error) => { if let Some(SpecifierError::ImportPrefixMissing { specifier, .. - }) = other_error.downcast_ref::() + }) = other_error.as_any().downcast_ref::() { maybe_specifier = Some(specifier); } } + ResolveError::ImportMap(_) => {} } } } @@ -1294,7 +1324,7 @@ mod test { let specifier = ModuleSpecifier::parse("file:///file.ts").unwrap(); let err = import_map.resolve(input, &specifier).err().unwrap(); let err = ResolutionError::ResolverError { - error: Arc::new(ResolveError::Other(err.into())), + error: Arc::new(ResolveError::Other(JsErrorBox::from_err(err))), specifier: input.to_string(), range: Range { specifier, diff --git a/cli/http_util.rs b/cli/http_util.rs index af6709c5d0..19d9071833 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -6,13 +6,14 @@ use std::thread::ThreadId; use boxed_error::Boxed; use deno_cache_dir::file_fetcher::RedirectHeaderParseError; -use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::futures::StreamExt; use deno_core::parking_lot::Mutex; use deno_core::serde; use deno_core::serde_json; use deno_core::url::Url; +use deno_error::JsError; +use deno_error::JsErrorBox; use deno_runtime::deno_fetch; use deno_runtime::deno_fetch::create_http_client; use deno_runtime::deno_fetch::CreateHttpClientOptions; @@ -94,34 +95,49 @@ impl HttpClientProvider { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(type)] #[error("Bad response: {:?}{}", .status_code, .response_text.as_ref().map(|s| format!("\n\n{}", s)).unwrap_or_else(String::new))] pub struct BadResponseError { pub status_code: StatusCode, pub response_text: Option, } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct DownloadError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum DownloadErrorKind { + #[class(inherit)] #[error(transparent)] - Fetch(AnyError), + Fetch(deno_fetch::ClientSendError), + #[class(inherit)] #[error(transparent)] UrlParse(#[from] deno_core::url::ParseError), + #[class(generic)] #[error(transparent)] HttpParse(#[from] http::Error), + #[class(inherit)] #[error(transparent)] Json(#[from] serde_json::Error), + #[class(generic)] #[error(transparent)] ToStr(#[from] http::header::ToStrError), + #[class(inherit)] #[error(transparent)] RedirectHeaderParse(RedirectHeaderParseError), + #[class(type)] #[error("Too many redirects.")] TooManyRedirects, + #[class(inherit)] #[error(transparent)] BadResponse(#[from] BadResponseError), + #[class("Http")] + #[error("Not Found.")] + NotFound, + #[class(inherit)] + #[error(transparent)] + Other(JsErrorBox), } #[derive(Debug)] @@ -208,11 +224,11 @@ impl HttpClient { Ok(String::from_utf8(bytes)?) } - pub async fn download(&self, url: Url) -> Result, AnyError> { + pub async fn download(&self, url: Url) -> Result, DownloadError> { let maybe_bytes = self.download_inner(url, None, None).await?; match maybe_bytes { Some(bytes) => Ok(bytes), - None => Err(custom_error("Http", "Not found.")), + None => Err(DownloadErrorKind::NotFound.into_box()), } } @@ -276,7 +292,7 @@ impl HttpClient { get_response_body_with_progress(response, progress_guard) .await .map(|(_, body)| Some(body)) - .map_err(|err| DownloadErrorKind::Fetch(err).into_box()) + .map_err(|err| DownloadErrorKind::Other(err).into_box()) } async fn get_redirected_response( @@ -293,7 +309,7 @@ impl HttpClient { .clone() .send(req) .await - .map_err(|e| DownloadErrorKind::Fetch(e.into()).into_box())?; + .map_err(|e| DownloadErrorKind::Fetch(e).into_box())?; let status = response.status(); if status.is_redirection() { for _ in 0..5 { @@ -313,7 +329,7 @@ impl HttpClient { .clone() .send(req) .await - .map_err(|e| DownloadErrorKind::Fetch(e.into()).into_box())?; + .map_err(|e| DownloadErrorKind::Fetch(e).into_box())?; let status = new_response.status(); if status.is_redirection() { response = new_response; @@ -332,7 +348,7 @@ impl HttpClient { pub async fn get_response_body_with_progress( response: http::Response, progress_guard: Option<&UpdateGuard>, -) -> Result<(HeaderMap, Vec), AnyError> { +) -> Result<(HeaderMap, Vec), JsErrorBox> { use http_body::Body as _; if let Some(progress_guard) = progress_guard { let mut total_size = response.body().size_hint().exact(); diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 968875e48a..f7b487f055 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -10,13 +10,13 @@ use deno_ast::SourceRange; use deno_ast::SourceRangedForSpanned; use deno_ast::SourceTextInfo; use deno_config::workspace::MappedResolution; -use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; use deno_lint::diagnostic::LintDiagnosticRange; use deno_path_util::url_to_file_path; use deno_runtime::deno_node::PathClean; @@ -1070,10 +1070,13 @@ impl CodeActionCollection { // we wrap tsc, we can't handle the asynchronous response, so it is // actually easier to return errors if we ever encounter one of these, // which we really wouldn't expect from the Deno lsp. - return Err(custom_error( - "UnsupportedFix", - "The action returned from TypeScript is unsupported.", - )); + return Err( + JsErrorBox::new( + "UnsupportedFix", + "The action returned from TypeScript is unsupported.", + ) + .into(), + ); } let Some(action) = fix_ts_import_action(specifier, resolution_mode, action, language_server) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 2aaba928ca..0cd8468153 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -41,6 +41,7 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::url::Url; use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; use deno_lint::linter::LintConfig as DenoLintConfig; use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonCache; @@ -1575,7 +1576,7 @@ impl ConfigData { pkg_json_dep_resolution, specified_import_map, }, - |path| Ok(std::fs::read_to_string(path)?), + |path| std::fs::read_to_string(path).map_err(JsErrorBox::from_err), ) .inspect_err(|err| { lsp_warn!( diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index cc9cca7f23..0982ff5ceb 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -34,7 +34,7 @@ use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; use import_map::ImportMap; -use import_map::ImportMapError; +use import_map::ImportMapErrorKind; use log::error; use tokio::sync::mpsc; use tokio::sync::Mutex; @@ -1297,8 +1297,8 @@ impl DenoDiagnostic { let mut message; message = enhanced_resolution_error_message(err); if let deno_graph::ResolutionError::ResolverError {error, ..} = err{ - if let ResolveError::Other(resolve_error, ..) = (*error).as_ref() { - if let Some(ImportMapError::UnmappedBareSpecifier(specifier, _)) = resolve_error.downcast_ref::() { + if let ResolveError::ImportMap(importmap) = (*error).as_ref() { + if let ImportMapErrorKind::UnmappedBareSpecifier(specifier, _) = &**importmap { if specifier.chars().next().unwrap_or('\0') == '@'{ let hint = format!("\nHint: Use [deno add {}] to add the dependency.", specifier); message.push_str(hint.as_str()); diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 39408e2589..f31353d436 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -18,13 +18,13 @@ use deno_ast::swc::visit::VisitWith; use deno_ast::MediaType; use deno_ast::ParsedSource; use deno_ast::SourceTextInfo; -use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::futures::future; use deno_core::futures::future::Shared; use deno_core::futures::FutureExt; use deno_core::parking_lot::Mutex; use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; use deno_graph::Resolution; use deno_path_util::url_to_file_path; use deno_runtime::deno_node; @@ -1081,7 +1081,7 @@ impl Documents { .or_else(|| self.file_system_docs.remove_document(specifier)) .map(Ok) .unwrap_or_else(|| { - Err(custom_error( + Err(JsErrorBox::new( "NotFound", format!("The specifier \"{specifier}\" was not found."), )) diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 6861d9dd63..35f5374efe 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -122,7 +122,7 @@ use crate::util::sync::AsyncFlag; struct LspRootCertStoreProvider(RootCertStore); impl RootCertStoreProvider for LspRootCertStoreProvider { - fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> { + fn get_or_try_init(&self) -> Result<&RootCertStore, deno_error::JsErrorBox> { Ok(&self.0) } } diff --git a/cli/lsp/text.rs b/cli/lsp/text.rs index a9a5f0753a..efb9a072a4 100644 --- a/cli/lsp/text.rs +++ b/cli/lsp/text.rs @@ -2,8 +2,8 @@ use std::collections::HashMap; -use deno_core::error::custom_error; use deno_core::error::AnyError; +use deno_error::JsErrorBox; use dissimilar::diff; use dissimilar::Chunk; use text_size::TextRange; @@ -137,7 +137,7 @@ impl LineIndex { if let Some(line_offset) = self.utf8_offsets.get(position.line as usize) { Ok(line_offset + col) } else { - Err(custom_error("OutOfRange", "The position is out of range.")) + Err(JsErrorBox::new("OutOfRange", "The position is out of range.").into()) } } @@ -157,7 +157,7 @@ impl LineIndex { if let Some(line_offset) = self.utf16_offsets.get(position.line as usize) { Ok(line_offset + TextSize::from(position.character)) } else { - Err(custom_error("OutOfRange", "The position is out of range.")) + Err(JsErrorBox::new("OutOfRange", "The position is out of range.").into()) } } diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index d2b501a539..826021a288 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -20,7 +20,6 @@ use deno_core::anyhow::Context as _; use deno_core::convert::Smi; use deno_core::convert::ToV8; use deno_core::error::AnyError; -use deno_core::error::StdAnyError; use deno_core::futures::stream::FuturesOrdered; use deno_core::futures::FutureExt; use deno_core::futures::StreamExt; @@ -4331,7 +4330,7 @@ impl TscSpecifierMap { pub fn normalize>( &self, specifier: S, - ) -> Result { + ) -> Result { let original = specifier.as_ref(); if let Some(specifier) = self.normalized_specifiers.get(original) { return Ok(specifier.clone()); @@ -4339,7 +4338,7 @@ impl TscSpecifierMap { let specifier_str = original.replace(".d.ts.d.ts", ".d.ts"); let specifier = match ModuleSpecifier::parse(&specifier_str) { Ok(s) => s, - Err(err) => return Err(err.into()), + Err(err) => return Err(err), }; if specifier.as_str() != original { self @@ -4437,6 +4436,16 @@ fn op_is_node_file(state: &mut OpState, #[string] path: String) -> bool { r } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +enum LoadError { + #[error("{0}")] + #[class(inherit)] + UrlParse(#[from] deno_core::url::ParseError), + #[error("{0}")] + #[class(inherit)] + SerdeV8(#[from] serde_v8::Error), +} + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct LoadResponse { @@ -4451,7 +4460,7 @@ fn op_load<'s>( scope: &'s mut v8::HandleScope, state: &mut OpState, #[string] specifier: &str, -) -> Result, AnyError> { +) -> Result, LoadError> { let state = state.borrow_mut::(); let mark = state .performance @@ -4482,7 +4491,7 @@ fn op_load<'s>( fn op_release( state: &mut OpState, #[string] specifier: &str, -) -> Result<(), AnyError> { +) -> Result<(), deno_core::url::ParseError> { let state = state.borrow_mut::(); let mark = state .performance @@ -4499,7 +4508,7 @@ fn op_resolve( state: &mut OpState, #[string] base: String, #[serde] specifiers: Vec<(bool, String)>, -) -> Result>, AnyError> { +) -> Result>, deno_core::url::ParseError> { op_resolve_inner(state, ResolveArgs { base, specifiers }) } @@ -4511,7 +4520,7 @@ struct TscRequestArray { } impl<'a> ToV8<'a> for TscRequestArray { - type Error = StdAnyError; + type Error = serde_v8::Error; fn to_v8( self, @@ -4526,9 +4535,7 @@ impl<'a> ToV8<'a> for TscRequestArray { .unwrap() .into(); let args = args.unwrap_or_else(|| v8::Array::new(scope, 0).into()); - let scope_url = serde_v8::to_v8(scope, self.scope) - .map_err(AnyError::from) - .map_err(StdAnyError::from)?; + let scope_url = serde_v8::to_v8(scope, self.scope)?; let change = self.change.to_v8(scope).unwrap_infallible(); @@ -4586,7 +4593,7 @@ async fn op_poll_requests( fn op_resolve_inner( state: &mut OpState, args: ResolveArgs, -) -> Result>, AnyError> { +) -> Result>, deno_core::url::ParseError> { let state = state.borrow_mut::(); let mark = state.performance.mark_with_args("tsc.op.op_resolve", &args); let referrer = state.specifier_map.normalize(&args.base)?; @@ -4743,7 +4750,7 @@ fn op_script_names(state: &mut OpState) -> ScriptNames { fn op_script_version( state: &mut OpState, #[string] specifier: &str, -) -> Result, AnyError> { +) -> Result, deno_core::url::ParseError> { let state = state.borrow_mut::(); let mark = state.performance.mark("tsc.op.op_script_version"); let specifier = state.specifier_map.normalize(specifier)?; @@ -5398,7 +5405,8 @@ impl TscRequest { fn to_server_request<'s>( &self, scope: &mut v8::HandleScope<'s>, - ) -> Result<(&'static str, Option>), AnyError> { + ) -> Result<(&'static str, Option>), serde_v8::Error> + { let args = match self { TscRequest::GetDiagnostics(args) => { ("$getDiagnostics", Some(serde_v8::to_v8(scope, args)?)) diff --git a/cli/main.rs b/cli/main.rs index 7db471932d..6bbefcf956 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -4,7 +4,6 @@ mod args; mod cache; mod cdp; mod emit; -mod errors; mod factory; mod file_fetcher; mod graph_container; @@ -38,7 +37,7 @@ use std::sync::Arc; use args::TaskFlags; use deno_core::anyhow::Context; use deno_core::error::AnyError; -use deno_core::error::JsError; +use deno_core::error::CoreError; use deno_core::futures::FutureExt; use deno_core::unsync::JoinHandle; use deno_npm::resolution::SnapshotFromLockfileError; @@ -202,7 +201,7 @@ async fn run_subcommand(flags: Arc) -> Result { match result { Ok(v) => Ok(v), Err(script_err) => { - if let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = script_err.downcast_ref::() { + if let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = util::result::any_and_jserrorbox_downcast_ref::(&script_err) { if flags.node_modules_dir.is_none() { let mut flags = flags.deref().clone(); let watch = match &flags.subcommand { @@ -373,10 +372,14 @@ fn exit_for_error(error: AnyError) -> ! { let mut error_string = format!("{error:?}"); let mut error_code = 1; - if let Some(e) = error.downcast_ref::() { + if let Some(CoreError::Js(e)) = + util::result::any_and_jserrorbox_downcast_ref::(&error) + { error_string = format_js_error(e); } else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) = - error.downcast_ref::() + util::result::any_and_jserrorbox_downcast_ref::( + &error, + ) { error_string = e.to_string(); error_code = 10; diff --git a/cli/mainrt.rs b/cli/mainrt.rs index 1279554514..8eea3f85ed 100644 --- a/cli/mainrt.rs +++ b/cli/mainrt.rs @@ -10,7 +10,6 @@ mod standalone; mod args; mod cache; mod emit; -mod errors; mod file_fetcher; mod http_util; mod js; @@ -30,8 +29,8 @@ use std::env; use std::env::current_exe; use std::sync::Arc; -use deno_core::error::generic_error; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::error::JsError; use deno_runtime::fmt_errors::format_js_error; use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; @@ -41,6 +40,7 @@ use indexmap::IndexMap; use standalone::DenoCompileFileSystem; use crate::args::Flags; +use crate::util::result::any_and_jserrorbox_downcast_ref; pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) { log::error!( @@ -65,8 +65,10 @@ fn unwrap_or_exit(result: Result) -> T { Err(error) => { let mut error_string = format!("{:?}", error); - if let Some(e) = error.downcast_ref::() { - error_string = format_js_error(e); + if let Some(CoreError::Js(js_error)) = + any_and_jserrorbox_downcast_ref::(&error) + { + error_string = format_js_error(js_error); } exit_with_message(&error_string, 1); diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 446397cad1..ba53077a3c 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -14,11 +14,9 @@ use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleKind; use deno_core::anyhow::anyhow; -use deno_core::anyhow::bail; use deno_core::anyhow::Context; -use deno_core::error::custom_error; -use deno_core::error::generic_error; use deno_core::error::AnyError; +use deno_core::error::ModuleLoaderError; use deno_core::futures::future::FutureExt; use deno_core::futures::Future; use deno_core::parking_lot::Mutex; @@ -31,6 +29,8 @@ use deno_core::ModuleSpecifier; use deno_core::ModuleType; use deno_core::RequestedModuleType; use deno_core::SourceCodeCacheInfo; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; use deno_graph::GraphKind; use deno_graph::JsModule; use deno_graph::JsonModule; @@ -59,7 +59,6 @@ use crate::cache::CodeCache; use crate::cache::FastInsecureHasher; use crate::cache::ParsedSourceCache; use crate::emit::Emitter; -use crate::errors::get_module_error_class; use crate::graph_container::MainModuleGraphContainer; use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; @@ -79,6 +78,7 @@ use crate::resolver::NotSupportedKindInNpmError; use crate::resolver::NpmModuleLoader; use crate::sys::CliSys; use crate::tools::check; +use crate::tools::check::CheckError; use crate::tools::check::TypeChecker; use crate::util::progress_bar::ProgressBar; use crate::util::text_encoding::code_without_source_map; @@ -86,6 +86,21 @@ use crate::util::text_encoding::source_map_from_code; use crate::worker::CreateModuleLoaderResult; use crate::worker::ModuleLoaderFactory; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum PrepareModuleLoadError { + #[class(inherit)] + #[error(transparent)] + BuildGraphWithNpmResolution( + #[from] crate::graph_util::BuildGraphWithNpmResolutionError, + ), + #[class(inherit)] + #[error(transparent)] + Check(#[from] CheckError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + pub struct ModuleLoadPreparer { options: Arc, lockfile: Option>, @@ -125,7 +140,7 @@ impl ModuleLoadPreparer { lib: TsTypeLib, permissions: PermissionsContainer, ext_overwrite: Option<&String>, - ) -> Result<(), AnyError> { + ) -> Result<(), PrepareModuleLoadError> { log::debug!("Preparing module load."); let _pb_clear_guard = self.progress_bar.clear_guard(); @@ -206,7 +221,7 @@ impl ModuleLoadPreparer { &self, graph: &ModuleGraph, roots: &[ModuleSpecifier], - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { self.module_graph_builder.graph_roots_valid(graph, roots) } } @@ -423,7 +438,7 @@ impl specifier: &ModuleSpecifier, maybe_referrer: Option<&ModuleSpecifier>, requested_module_type: RequestedModuleType, - ) -> Result { + ) -> Result { let code_source = self.load_code_source(specifier, maybe_referrer).await?; let code = if self.shared.is_inspecting || code_source.media_type == MediaType::Wasm @@ -446,7 +461,7 @@ impl if module_type == ModuleType::Json && requested_module_type != RequestedModuleType::Json { - return Err(generic_error("Attempted to load JSON module without specifying \"type\": \"json\" attribute in the import statement.")); + return Err(JsErrorBox::generic("Attempted to load JSON module without specifying \"type\": \"json\" attribute in the import statement.").into()); } let code_cache = if module_type == ModuleType::JavaScript { @@ -507,7 +522,7 @@ impl fn resolve_referrer( &self, referrer: &str, - ) -> Result { + ) -> Result { let referrer = if referrer.is_empty() && self.shared.is_repl { // FIXME(bartlomieju): this is a hacky way to provide compatibility with REPL // and `Deno.core.evalContext` API. Ideally we should always have a referrer filled @@ -533,7 +548,7 @@ impl &self, raw_specifier: &str, referrer: &ModuleSpecifier, - ) -> Result { + ) -> Result { let graph = self.graph_container.graph(); let resolution = match graph.get(referrer) { Some(Module::Js(module)) => module @@ -547,19 +562,25 @@ impl let specifier = match resolution { Resolution::Ok(resolved) => Cow::Borrowed(&resolved.specifier), Resolution::Err(err) => { - return Err(custom_error( - "TypeError", - format!("{}\n", err.to_string_with_range()), - )); + return Err( + JsErrorBox::type_error(format!("{}\n", err.to_string_with_range())) + .into(), + ); } - Resolution::None => Cow::Owned(self.shared.resolver.resolve( - raw_specifier, - referrer, - deno_graph::Position::zeroed(), - // if we're here, that means it's resolving a dynamic import - ResolutionMode::Import, - NodeResolutionKind::Execution, - )?), + Resolution::None => Cow::Owned( + self + .shared + .resolver + .resolve( + raw_specifier, + referrer, + deno_graph::Position::zeroed(), + // if we're here, that means it's resolving a dynamic import + ResolutionMode::Import, + NodeResolutionKind::Execution, + ) + .map_err(JsErrorBox::from_err)?, + ), }; if self.shared.is_repl { @@ -574,7 +595,7 @@ impl ResolutionMode::Import, NodeResolutionKind::Execution, ) - .map_err(AnyError::from); + .map_err(|e| JsErrorBox::from_err(e).into()); } } @@ -585,7 +606,8 @@ impl .npm_resolver .as_managed() .unwrap() // byonm won't create a Module::Npm - .resolve_pkg_folder_from_deno_module(module.nv_reference.nv())?; + .resolve_pkg_folder_from_deno_module(module.nv_reference.nv()) + .map_err(JsErrorBox::from_err)?; self .shared .node_resolver @@ -701,7 +723,7 @@ impl &self, graph: &'graph ModuleGraph, specifier: &ModuleSpecifier, - ) -> Result>, AnyError> { + ) -> Result>, JsErrorBox> { if specifier.scheme() == "node" { // Node built-in modules should be handled internally. unreachable!("Deno bug. {} was misconfigured internally.", specifier); @@ -710,8 +732,8 @@ impl let maybe_module = match graph.try_get(specifier) { Ok(module) => module, Err(err) => { - return Err(custom_error( - get_module_error_class(err), + return Err(JsErrorBox::new( + err.get_class(), enhance_graph_error( &self.shared.sys, &ModuleGraphError::ModuleError(err.clone()), @@ -739,11 +761,12 @@ impl is_script, .. })) => { - if self.shared.cjs_tracker.is_cjs_with_known_is_script( - specifier, - *media_type, - *is_script, - )? { + if self + .shared + .cjs_tracker + .is_cjs_with_known_is_script(specifier, *media_type, *is_script) + .map_err(JsErrorBox::from_err)? + { return Ok(Some(CodeOrDeferredEmit::Cjs { specifier, media_type: *media_type, @@ -875,16 +898,16 @@ impl ModuleLoader specifier: &str, referrer: &str, _kind: deno_core::ResolutionKind, - ) -> Result { + ) -> Result { fn ensure_not_jsr_non_jsr_remote_import( specifier: &ModuleSpecifier, referrer: &ModuleSpecifier, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { if referrer.as_str().starts_with(jsr_url().as_str()) && !specifier.as_str().starts_with(jsr_url().as_str()) && matches!(specifier.scheme(), "http" | "https") { - bail!("Importing {} blocked. JSR packages cannot import non-JSR remote modules for security reasons.", specifier); + return Err(JsErrorBox::generic(format!("Importing {} blocked. JSR packages cannot import non-JSR remote modules for security reasons.", specifier))); } Ok(()) } @@ -937,7 +960,7 @@ impl ModuleLoader specifier: &ModuleSpecifier, _maybe_referrer: Option, is_dynamic: bool, - ) -> Pin>>> { + ) -> Pin>>> { self.0.shared.in_flight_loads_tracker.increase(); if self.0.shared.in_npm_pkg_checker.in_npm_package(specifier) { return Box::pin(deno_core::futures::future::ready(Ok(()))); @@ -986,7 +1009,8 @@ impl ModuleLoader permissions, None, ) - .await?; + .await + .map_err(JsErrorBox::from_err)?; update_permit.commit(); Ok(()) } @@ -1130,35 +1154,37 @@ impl NodeRequireLoader &self, permissions: &mut dyn deno_runtime::deno_node::NodePermissions, path: &'a Path, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { if let Ok(url) = deno_path_util::url_from_file_path(path) { // allow reading if it's in the module graph if self.graph_container.graph().get(&url).is_some() { - return Ok(std::borrow::Cow::Borrowed(path)); + return Ok(Cow::Borrowed(path)); } } self .npm_registry_permission_checker .ensure_read_permission(permissions, path) + .map_err(JsErrorBox::from_err) } fn load_text_file_lossy( &self, path: &Path, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { // todo(dsherret): use the preloaded module from the graph if available? let media_type = MediaType::from_path(path); - let text = self.sys.fs_read_to_string_lossy(path)?; + let text = self + .sys + .fs_read_to_string_lossy(path) + .map_err(JsErrorBox::from_err)?; if media_type.is_emittable() { - let specifier = deno_path_util::url_from_file_path(path)?; + let specifier = deno_path_util::url_from_file_path(path) + .map_err(JsErrorBox::from_err)?; if self.in_npm_pkg_checker.in_npm_package(&specifier) { - return Err( - NotSupportedKindInNpmError { - media_type, - specifier, - } - .into(), - ); + return Err(JsErrorBox::from_err(NotSupportedKindInNpmError { + media_type, + specifier, + })); } self .emitter @@ -1172,6 +1198,7 @@ impl NodeRequireLoader &text.into(), ) .map(Cow::Owned) + .map_err(JsErrorBox::from_err) } else { Ok(text) } diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 55421f41e8..831b0b0ba8 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -11,6 +11,7 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmRegistryApi; @@ -322,6 +323,28 @@ impl std::fmt::Debug for ManagedCliNpmResolver { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolvePkgFolderFromPkgIdError { + #[class(inherit)] + #[error("{0}")] + NpmPackageFsResolverPackageFolder( + #[from] resolvers::NpmPackageFsResolverPackageFolderError, + ), + #[class(inherit)] + #[error("{0}")] + Io(#[from] std::io::Error), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolvePkgFolderFromDenoModuleError { + #[class(inherit)] + #[error("{0}")] + PackageNvNotFound(#[from] deno_npm::resolution::PackageNvNotFoundError), + #[class(inherit)] + #[error("{0}")] + ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), +} + impl ManagedCliNpmResolver { #[allow(clippy::too_many_arguments)] pub fn new( @@ -356,7 +379,7 @@ impl ManagedCliNpmResolver { pub fn resolve_pkg_folder_from_pkg_id( &self, pkg_id: &NpmPackageId, - ) -> Result { + ) -> Result { let path = self.fs_resolver.package_folder(pkg_id)?; let path = canonicalize_path_maybe_not_exists(&self.sys, &path)?; log::debug!( @@ -423,7 +446,7 @@ impl ManagedCliNpmResolver { pub async fn add_and_cache_package_reqs( &self, packages: &[PackageReq], - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { self .add_package_reqs_raw( packages, @@ -436,7 +459,7 @@ impl ManagedCliNpmResolver { pub async fn add_package_reqs_no_cache( &self, packages: &[PackageReq], - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { self .add_package_reqs_raw(packages, None) .await @@ -447,7 +470,7 @@ impl ManagedCliNpmResolver { &self, packages: &[PackageReq], caching: PackageCaching<'_>, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { self .add_package_reqs_raw(packages, Some(caching)) .await @@ -517,7 +540,7 @@ impl ManagedCliNpmResolver { pub async fn inject_synthetic_types_node_package( &self, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { let reqs = &[PackageReq::from_str("@types/node").unwrap()]; // add and ensure this isn't added to the lockfile self @@ -530,16 +553,16 @@ impl ManagedCliNpmResolver { pub async fn cache_packages( &self, caching: PackageCaching<'_>, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { self.fs_resolver.cache_packages(caching).await } pub fn resolve_pkg_folder_from_deno_module( &self, nv: &PackageNv, - ) -> Result { + ) -> Result { let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?; - self.resolve_pkg_folder_from_pkg_id(&pkg_id) + Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?) } pub fn resolve_pkg_id_from_pkg_req( @@ -580,7 +603,7 @@ impl ManagedCliNpmResolver { /// return value of `false` means that new packages were added to the NPM resolution. pub async fn ensure_top_level_package_json_install( &self, - ) -> Result { + ) -> Result { if !self.top_level_install_flag.raise() { return Ok(true); // already did this } @@ -687,12 +710,12 @@ impl CliNpmReqResolver for ManagedCliNpmResolver { req: &PackageReq, _referrer: &ModuleSpecifier, ) -> Result { - let pkg_id = self - .resolve_pkg_id_from_pkg_req(req) - .map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(err.into()))?; + let pkg_id = self.resolve_pkg_id_from_pkg_req(req).map_err(|err| { + ResolvePkgFolderFromDenoReqError::Managed(Box::new(err)) + })?; self .resolve_pkg_folder_from_pkg_id(&pkg_id) - .map_err(ResolvePkgFolderFromDenoReqError::Managed) + .map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(Box::new(err))) } } diff --git a/cli/npm/managed/resolution.rs b/cli/npm/managed/resolution.rs index 12f7b17565..8259062c05 100644 --- a/cli/npm/managed/resolution.rs +++ b/cli/npm/managed/resolution.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use capacity_builder::StringBuilder; use deno_core::error::AnyError; +use deno_error::JsErrorBox; use deno_lockfile::NpmPackageDependencyLockfileInfo; use deno_lockfile::NpmPackageLockfileInfo; use deno_npm::registry::NpmRegistryApi; @@ -39,7 +40,7 @@ pub struct AddPkgReqsResult { /// package requirements. pub results: Vec>, /// The final result of resolving and caching all the package requirements. - pub dependencies_result: Result<(), AnyError>, + pub dependencies_result: Result<(), JsErrorBox>, } /// Handles updating and storing npm resolution in memory where the underlying @@ -106,7 +107,7 @@ impl NpmResolution { *snapshot_lock.write() = snapshot; Ok(()) } - Err(err) => Err(err.into()), + Err(err) => Err(JsErrorBox::from_err(err)), }, } } diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs index 66d991bd49..0d5fab10d3 100644 --- a/cli/npm/managed/resolvers/common.rs +++ b/cli/npm/managed/resolvers/common.rs @@ -8,13 +8,18 @@ use std::path::PathBuf; use async_trait::async_trait; use deno_ast::ModuleSpecifier; -use deno_core::error::AnyError; +use deno_error::JsErrorBox; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use node_resolver::errors::PackageFolderResolveError; use super::super::PackageCaching; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +#[error("Package folder not found for '{0}'")] +pub struct NpmPackageFsResolverPackageFolderError(deno_semver::StackString); + /// Part of the resolution that interacts with the file system. #[async_trait(?Send)] pub trait NpmPackageFsResolver: Send + Sync { @@ -26,12 +31,9 @@ pub trait NpmPackageFsResolver: Send + Sync { fn package_folder( &self, package_id: &NpmPackageId, - ) -> Result { + ) -> Result { self.maybe_package_folder(package_id).ok_or_else(|| { - deno_core::anyhow::anyhow!( - "Package folder not found for '{}'", - package_id.as_serialized() - ) + NpmPackageFsResolverPackageFolderError(package_id.as_serialized()) }) } @@ -44,10 +46,10 @@ pub trait NpmPackageFsResolver: Send + Sync { fn resolve_package_cache_folder_id_from_specifier( &self, specifier: &ModuleSpecifier, - ) -> Result, AnyError>; + ) -> Result, std::io::Error>; async fn cache_packages<'a>( &self, caching: PackageCaching<'a>, - ) -> Result<(), AnyError>; + ) -> Result<(), JsErrorBox>; } diff --git a/cli/npm/managed/resolvers/common/bin_entries.rs b/cli/npm/managed/resolvers/common/bin_entries.rs index 32ebd68746..bc69786b6c 100644 --- a/cli/npm/managed/resolvers/common/bin_entries.rs +++ b/cli/npm/managed/resolvers/common/bin_entries.rs @@ -6,8 +6,6 @@ use std::collections::VecDeque; use std::path::Path; use std::path::PathBuf; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmPackageId; @@ -50,6 +48,48 @@ pub fn warn_missing_entrypoint( ); } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum BinEntriesError { + #[class(inherit)] + #[error("Creating '{path}'")] + Creating { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[cfg(unix)] + #[class(inherit)] + #[error("Setting permissions on '{path}'")] + Permissions { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Can't set up '{name}' bin at {path}")] + SetUpBin { + name: String, + path: PathBuf, + #[source] + #[inherit] + source: Box, + }, + #[cfg(unix)] + #[class(inherit)] + #[error("Setting permissions on '{path}'")] + RemoveBinSymlink { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), +} + impl<'a> BinEntries<'a> { pub fn new() -> Self { Self::default() @@ -92,15 +132,15 @@ impl<'a> BinEntries<'a> { mut already_seen: impl FnMut( &Path, &str, // bin script - ) -> Result<(), AnyError>, + ) -> Result<(), BinEntriesError>, mut new: impl FnMut( &NpmResolutionPackage, &Path, &str, // bin name &str, // bin script - ) -> Result<(), AnyError>, + ) -> Result<(), BinEntriesError>, mut filter: impl FnMut(&NpmResolutionPackage) -> bool, - ) -> Result<(), AnyError> { + ) -> Result<(), BinEntriesError> { if !self.collisions.is_empty() && !self.sorted { // walking the dependency tree to find out the depth of each package // is sort of expensive, so we only do it if there's a collision @@ -168,11 +208,14 @@ impl<'a> BinEntries<'a> { bin_node_modules_dir_path: &Path, filter: impl FnMut(&NpmResolutionPackage) -> bool, mut handler: impl FnMut(&EntrySetupOutcome<'_>), - ) -> Result<(), AnyError> { + ) -> Result<(), BinEntriesError> { if !self.entries.is_empty() && !bin_node_modules_dir_path.exists() { - std::fs::create_dir_all(bin_node_modules_dir_path).with_context( - || format!("Creating '{}'", bin_node_modules_dir_path.display()), - )?; + std::fs::create_dir_all(bin_node_modules_dir_path).map_err(|source| { + BinEntriesError::Creating { + path: bin_node_modules_dir_path.to_path_buf(), + source, + } + })?; } self.for_each_entry( @@ -209,7 +252,7 @@ impl<'a> BinEntries<'a> { snapshot: &NpmResolutionSnapshot, bin_node_modules_dir_path: &Path, handler: impl FnMut(&EntrySetupOutcome<'_>), - ) -> Result<(), AnyError> { + ) -> Result<(), BinEntriesError> { self.set_up_entries_filtered( snapshot, bin_node_modules_dir_path, @@ -226,7 +269,7 @@ impl<'a> BinEntries<'a> { bin_node_modules_dir_path: &Path, handler: impl FnMut(&EntrySetupOutcome<'_>), only: &HashSet<&NpmPackageId>, - ) -> Result<(), AnyError> { + ) -> Result<(), BinEntriesError> { self.set_up_entries_filtered( snapshot, bin_node_modules_dir_path, @@ -301,7 +344,7 @@ pub fn set_up_bin_entry<'a>( #[allow(unused_variables)] bin_script: &str, #[allow(unused_variables)] package_path: &'a Path, bin_node_modules_dir_path: &Path, -) -> Result, AnyError> { +) -> Result, BinEntriesError> { #[cfg(windows)] { set_up_bin_shim(package, bin_name, bin_node_modules_dir_path)?; @@ -324,14 +367,16 @@ fn set_up_bin_shim( package: &NpmResolutionPackage, bin_name: &str, bin_node_modules_dir_path: &Path, -) -> Result<(), AnyError> { +) -> Result<(), BinEntriesError> { use std::fs; let mut cmd_shim = bin_node_modules_dir_path.join(bin_name); cmd_shim.set_extension("cmd"); let shim = format!("@deno run -A npm:{}/{bin_name} %*", package.id.nv); - fs::write(&cmd_shim, shim).with_context(|| { - format!("Can't set up '{}' bin at {}", bin_name, cmd_shim.display()) + fs::write(&cmd_shim, shim).map_err(|err| BinEntriesError::SetUpBin { + name: bin_name.to_string(), + path: cmd_shim.clone(), + source: Box::new(err.into()), })?; Ok(()) @@ -340,7 +385,7 @@ fn set_up_bin_shim( #[cfg(unix)] /// Make the file at `path` executable if it exists. /// Returns `true` if the file exists, `false` otherwise. -fn make_executable_if_exists(path: &Path) -> Result { +fn make_executable_if_exists(path: &Path) -> Result { use std::io; use std::os::unix::fs::PermissionsExt; let mut perms = match std::fs::metadata(path) { @@ -355,8 +400,11 @@ fn make_executable_if_exists(path: &Path) -> Result { if perms.mode() & 0o111 == 0 { // if the original file is not executable, make it executable perms.set_mode(perms.mode() | 0o111); - std::fs::set_permissions(path, perms).with_context(|| { - format!("Setting permissions on '{}'", path.display()) + std::fs::set_permissions(path, perms).map_err(|source| { + BinEntriesError::Permissions { + path: path.to_path_buf(), + source, + } })?; } @@ -395,14 +443,18 @@ fn symlink_bin_entry<'a>( bin_script: &str, package_path: &'a Path, bin_node_modules_dir_path: &Path, -) -> Result, AnyError> { +) -> Result, BinEntriesError> { use std::io; use std::os::unix::fs::symlink; let link = bin_node_modules_dir_path.join(bin_name); let original = package_path.join(bin_script); - let found = make_executable_if_exists(&original).with_context(|| { - format!("Can't set up '{}' bin at {}", bin_name, original.display()) + let found = make_executable_if_exists(&original).map_err(|source| { + BinEntriesError::SetUpBin { + name: bin_name.to_string(), + path: original.to_path_buf(), + source: Box::new(source), + } })?; if !found { return Ok(EntrySetupOutcome::MissingEntrypoint { @@ -420,27 +472,25 @@ fn symlink_bin_entry<'a>( if let Err(err) = symlink(&original_relative, &link) { if err.kind() == io::ErrorKind::AlreadyExists { // remove and retry - std::fs::remove_file(&link).with_context(|| { - format!( - "Failed to remove existing bin symlink at {}", - link.display() - ) + std::fs::remove_file(&link).map_err(|source| { + BinEntriesError::RemoveBinSymlink { + path: link.clone(), + source, + } })?; - symlink(&original_relative, &link).with_context(|| { - format!( - "Can't set up '{}' bin at {}", - bin_name, - original_relative.display() - ) + symlink(&original_relative, &link).map_err(|source| { + BinEntriesError::SetUpBin { + name: bin_name.to_string(), + path: original_relative.to_path_buf(), + source: Box::new(source.into()), + } })?; return Ok(EntrySetupOutcome::Success); } - return Err(err).with_context(|| { - format!( - "Can't set up '{}' bin at {}", - bin_name, - original_relative.display() - ) + return Err(BinEntriesError::SetUpBin { + name: bin_name.to_string(), + path: original_relative.to_path_buf(), + source: Box::new(err.into()), }); } diff --git a/cli/npm/managed/resolvers/common/lifecycle_scripts.rs b/cli/npm/managed/resolvers/common/lifecycle_scripts.rs index 738326ad22..a0d821cdfc 100644 --- a/cli/npm/managed/resolvers/common/lifecycle_scripts.rs +++ b/cli/npm/managed/resolvers/common/lifecycle_scripts.rs @@ -6,7 +6,6 @@ use std::path::Path; use std::path::PathBuf; use std::rc::Rc; -use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmResolutionPackage; @@ -29,7 +28,7 @@ pub trait LifecycleScriptsStrategy { fn warn_on_scripts_not_run( &self, packages: &[(&NpmResolutionPackage, PathBuf)], - ) -> Result<(), AnyError>; + ) -> Result<(), std::io::Error>; fn has_warned(&self, package: &NpmResolutionPackage) -> bool; @@ -38,7 +37,7 @@ pub trait LifecycleScriptsStrategy { fn did_run_scripts( &self, package: &NpmResolutionPackage, - ) -> Result<(), AnyError>; + ) -> Result<(), std::io::Error>; } pub struct LifecycleScripts<'a> { @@ -84,6 +83,27 @@ fn is_broken_default_install_script(script: &str, package_path: &Path) -> bool { script == "node-gyp rebuild" && !package_path.join("binding.gyp").exists() } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum LifecycleScriptsError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + BinEntries(#[from] super::bin_entries::BinEntriesError), + #[class(inherit)] + #[error( + "failed to create npm process state tempfile for running lifecycle scripts" + )] + CreateNpmProcessState(#[source] std::io::Error), + #[class(generic)] + #[error(transparent)] + Task(AnyError), + #[class(generic)] + #[error("failed to run scripts for packages: {}", .0.join(", "))] + RunScripts(Vec), +} + impl<'a> LifecycleScripts<'a> { pub fn can_run_scripts(&self, package_nv: &PackageNv) -> bool { if !self.strategy.can_run_scripts() { @@ -141,7 +161,7 @@ impl<'a> LifecycleScripts<'a> { } } - pub fn warn_not_run_scripts(&self) -> Result<(), AnyError> { + pub fn warn_not_run_scripts(&self) -> Result<(), std::io::Error> { if !self.packages_with_scripts_not_run.is_empty() { self .strategy @@ -156,7 +176,7 @@ impl<'a> LifecycleScripts<'a> { packages: &[NpmResolutionPackage], root_node_modules_dir_path: &Path, progress_bar: &ProgressBar, - ) -> Result<(), AnyError> { + ) -> Result<(), LifecycleScriptsError> { let kill_signal = KillSignal::default(); let _drop_signal = kill_signal.clone().drop_guard(); // we don't run with signals forwarded because once signals @@ -179,7 +199,7 @@ impl<'a> LifecycleScripts<'a> { root_node_modules_dir_path: &Path, progress_bar: &ProgressBar, kill_signal: KillSignal, - ) -> Result<(), AnyError> { + ) -> Result<(), LifecycleScriptsError> { self.warn_not_run_scripts()?; let get_package_path = |p: &NpmResolutionPackage| self.strategy.package_path(p); @@ -198,7 +218,7 @@ impl<'a> LifecycleScripts<'a> { snapshot, packages, get_package_path, - )?; + ); let init_cwd = &self.config.initial_cwd; let process_state = crate::npm::managed::npm_process_state( snapshot.as_valid_serialized(), @@ -222,7 +242,8 @@ impl<'a> LifecycleScripts<'a> { let temp_file_fd = deno_runtime::ops::process::npm_process_state_tempfile( process_state.as_bytes(), - ).context("failed to create npm process state tempfile for running lifecycle scripts")?; + ) + .map_err(LifecycleScriptsError::CreateNpmProcessState)?; // SAFETY: fd/handle is valid let _temp_file = unsafe { std::fs::File::from_raw_io_handle(temp_file_fd) }; // make sure the file gets closed @@ -240,7 +261,7 @@ impl<'a> LifecycleScripts<'a> { package, snapshot, get_package_path, - )?; + ); for script_name in ["preinstall", "install", "postinstall"] { if let Some(script) = package.scripts.get(script_name) { if script_name == "install" @@ -273,7 +294,8 @@ impl<'a> LifecycleScripts<'a> { kill_signal: kill_signal.clone(), }, ) - .await?; + .await + .map_err(LifecycleScriptsError::Task)?; let stdout = stdout.unwrap(); let stderr = stderr.unwrap(); if exit_code != 0 { @@ -322,14 +344,12 @@ impl<'a> LifecycleScripts<'a> { if failed_packages.is_empty() { Ok(()) } else { - Err(AnyError::msg(format!( - "failed to run scripts for packages: {}", + Err(LifecycleScriptsError::RunScripts( failed_packages .iter() .map(|p| p.to_string()) - .collect::>() - .join(", ") - ))) + .collect::>(), + )) } } } @@ -349,7 +369,7 @@ fn resolve_baseline_custom_commands<'a>( snapshot: &'a NpmResolutionSnapshot, packages: &'a [NpmResolutionPackage], get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf, -) -> Result { +) -> crate::task_runner::TaskCustomCommands { let mut custom_commands = crate::task_runner::TaskCustomCommands::new(); custom_commands .insert("npx".to_string(), Rc::new(crate::task_runner::NpxCommand)); @@ -390,7 +410,7 @@ fn resolve_custom_commands_from_packages< snapshot: &'a NpmResolutionSnapshot, packages: P, get_package_path: impl Fn(&'a NpmResolutionPackage) -> PathBuf, -) -> Result { +) -> crate::task_runner::TaskCustomCommands { for package in packages { let package_path = get_package_path(package); @@ -409,7 +429,7 @@ fn resolve_custom_commands_from_packages< ); } - Ok(commands) + commands } // resolves the custom commands from the dependencies of a package @@ -420,7 +440,7 @@ fn resolve_custom_commands_from_deps( package: &NpmResolutionPackage, snapshot: &NpmResolutionSnapshot, get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf, -) -> Result { +) -> crate::task_runner::TaskCustomCommands { let mut bin_entries = BinEntries::new(); resolve_custom_commands_from_packages( &mut bin_entries, diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs index 417345cefe..9af35b169d 100644 --- a/cli/npm/managed/resolvers/global.rs +++ b/cli/npm/managed/resolvers/global.rs @@ -9,9 +9,9 @@ use std::sync::Arc; use async_trait::async_trait; use deno_ast::ModuleSpecifier; -use deno_core::error::AnyError; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::StreamExt; +use deno_error::JsErrorBox; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; @@ -134,7 +134,7 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { fn resolve_package_cache_folder_id_from_specifier( &self, specifier: &ModuleSpecifier, - ) -> Result, AnyError> { + ) -> Result, std::io::Error> { Ok( self .cache @@ -145,7 +145,7 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { async fn cache_packages<'a>( &self, caching: PackageCaching<'a>, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { let package_partitions = match caching { PackageCaching::All => self .resolution @@ -155,13 +155,16 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { .subset(&reqs) .all_system_packages_partitioned(&self.system_info), }; - cache_packages(&package_partitions.packages, &self.tarball_cache).await?; + cache_packages(&package_partitions.packages, &self.tarball_cache) + .await + .map_err(JsErrorBox::from_err)?; // create the copy package folders for copy in package_partitions.copy_packages { self .cache - .ensure_copy_package(©.get_package_cache_folder_id())?; + .ensure_copy_package(©.get_package_cache_folder_id()) + .map_err(JsErrorBox::from_err)?; } let mut lifecycle_scripts = @@ -174,7 +177,9 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { lifecycle_scripts.add(package, Cow::Borrowed(&package_folder)); } - lifecycle_scripts.warn_not_run_scripts()?; + lifecycle_scripts + .warn_not_run_scripts() + .map_err(JsErrorBox::from_err)?; Ok(()) } @@ -183,7 +188,7 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { async fn cache_packages( packages: &[NpmResolutionPackage], tarball_cache: &Arc, -) -> Result<(), AnyError> { +) -> Result<(), deno_npm_cache::EnsurePackageError> { let mut futures_unordered = FuturesUnordered::new(); for package in packages { futures_unordered.push(async move { @@ -235,7 +240,7 @@ impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy fn warn_on_scripts_not_run( &self, packages: &[(&NpmResolutionPackage, PathBuf)], - ) -> std::result::Result<(), deno_core::anyhow::Error> { + ) -> std::result::Result<(), std::io::Error> { log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall")); for (package, _) in packages { log::warn!("┠─ {}", colors::gray(format!("npm:{}", package.id.nv))); @@ -261,7 +266,7 @@ impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy fn did_run_scripts( &self, _package: &NpmResolutionPackage, - ) -> std::result::Result<(), deno_core::anyhow::Error> { + ) -> Result<(), std::io::Error> { Ok(()) } diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 1a4ec57a69..e1ac3df43b 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -19,12 +19,11 @@ use std::sync::Arc; use async_trait::async_trait; use deno_ast::ModuleSpecifier; use deno_cache_dir::npm::mixed_case_package_name_decode; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::StreamExt; use deno_core::parking_lot::Mutex; use deno_core::url::Url; +use deno_error::JsErrorBox; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; @@ -140,7 +139,7 @@ impl LocalNpmPackageResolver { fn resolve_package_folder_from_specifier( &self, specifier: &ModuleSpecifier, - ) -> Result, AnyError> { + ) -> Result, std::io::Error> { let Some(local_path) = self.resolve_folder_for_specifier(specifier)? else { return Ok(None); }; @@ -225,7 +224,7 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { fn resolve_package_cache_folder_id_from_specifier( &self, specifier: &ModuleSpecifier, - ) -> Result, AnyError> { + ) -> Result, std::io::Error> { let Some(folder_path) = self.resolve_package_folder_from_specifier(specifier)? else { @@ -251,7 +250,7 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { async fn cache_packages<'a>( &self, caching: PackageCaching<'a>, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { let snapshot = match caching { PackageCaching::All => self.resolution.snapshot(), PackageCaching::Only(reqs) => self.resolution.subset(&reqs), @@ -267,6 +266,7 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { &self.lifecycle_scripts, ) .await + .map_err(JsErrorBox::from_err) } } @@ -285,6 +285,38 @@ fn local_node_modules_package_contents_path( .join(&package.id.nv.name) } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum SyncResolutionWithFsError { + #[class(inherit)] + #[error("Creating '{path}'")] + Creating { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + CopyDirRecursive(#[from] crate::util::fs::CopyDirRecursiveError), + #[class(inherit)] + #[error(transparent)] + SymlinkPackageDir(#[from] SymlinkPackageDirError), + #[class(inherit)] + #[error(transparent)] + BinEntries(#[from] bin_entries::BinEntriesError), + #[class(inherit)] + #[error(transparent)] + LifecycleScripts( + #[from] super::common::lifecycle_scripts::LifecycleScriptsError, + ), + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + /// Creates a pnpm style folder structure. #[allow(clippy::too_many_arguments)] async fn sync_resolution_with_fs( @@ -296,7 +328,7 @@ async fn sync_resolution_with_fs( root_node_modules_dir_path: &Path, system_info: &NpmSystemInfo, lifecycle_scripts: &LifecycleScriptsConfig, -) -> Result<(), AnyError> { +) -> Result<(), SyncResolutionWithFsError> { if snapshot.is_empty() && npm_install_deps_provider.workspace_pkgs().is_empty() { @@ -311,12 +343,18 @@ async fn sync_resolution_with_fs( let deno_local_registry_dir = root_node_modules_dir_path.join(".deno"); let deno_node_modules_dir = deno_local_registry_dir.join("node_modules"); - fs::create_dir_all(&deno_node_modules_dir).with_context(|| { - format!("Creating '{}'", deno_local_registry_dir.display()) + fs::create_dir_all(&deno_node_modules_dir).map_err(|source| { + SyncResolutionWithFsError::Creating { + path: deno_local_registry_dir.to_path_buf(), + source, + } })?; let bin_node_modules_dir_path = root_node_modules_dir_path.join(".bin"); - fs::create_dir_all(&bin_node_modules_dir_path).with_context(|| { - format!("Creating '{}'", bin_node_modules_dir_path.display()) + fs::create_dir_all(&bin_node_modules_dir_path).map_err(|source| { + SyncResolutionWithFsError::Creating { + path: deno_local_registry_dir.to_path_buf(), + source, + } })?; let single_process_lock = LaxSingleProcessFsFlag::lock( @@ -420,7 +458,8 @@ async fn sync_resolution_with_fs( cache_futures.push(async move { tarball_cache .ensure_package(&package.id.nv, &package.dist) - .await?; + .await + .map_err(JsErrorBox::from_err)?; let pb_guard = progress_bar.update_with_prompt( ProgressMessagePrompt::Initialize, &package.id.nv.to_string(), @@ -441,10 +480,12 @@ async fn sync_resolution_with_fs( // write out a file that indicates this folder has been initialized fs::write(initialized_file, tags)?; - Ok::<_, AnyError>(()) + Ok::<_, SyncResolutionWithFsError>(()) } }) - .await??; + .await + .map_err(JsErrorBox::from_err)? + .map_err(JsErrorBox::from_err)?; if package.bin.is_some() { bin_entries_to_setup.borrow_mut().add(package, package_path); @@ -458,7 +499,7 @@ async fn sync_resolution_with_fs( // finally stop showing the progress bar drop(pb_guard); // explicit for clarity - Ok::<_, AnyError>(()) + Ok::<_, JsErrorBox>(()) }); } else if matches!(package_state, PackageFolderState::TagsOutdated) { fs::write(initialized_file, tags)?; @@ -597,8 +638,11 @@ async fn sync_resolution_with_fs( // symlink the dep into the package's child node_modules folder let dest_node_modules = remote.base_dir.join("node_modules"); if !existing_child_node_modules_dirs.contains(&dest_node_modules) { - fs::create_dir_all(&dest_node_modules).with_context(|| { - format!("Creating '{}'", dest_node_modules.display()) + fs::create_dir_all(&dest_node_modules).map_err(|source| { + SyncResolutionWithFsError::Creating { + path: dest_node_modules.clone(), + source, + } })?; existing_child_node_modules_dirs.insert(dest_node_modules.clone()); } @@ -813,7 +857,7 @@ impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy fn did_run_scripts( &self, package: &NpmResolutionPackage, - ) -> std::result::Result<(), deno_core::anyhow::Error> { + ) -> std::result::Result<(), std::io::Error> { std::fs::write(self.ran_scripts_file(package), "")?; Ok(()) } @@ -821,7 +865,7 @@ impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy fn warn_on_scripts_not_run( &self, packages: &[(&NpmResolutionPackage, std::path::PathBuf)], - ) -> Result<(), AnyError> { + ) -> Result<(), std::io::Error> { if !packages.is_empty() { log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall")); @@ -1041,15 +1085,42 @@ fn get_package_folder_id_from_folder_name( }) } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum SymlinkPackageDirError { + #[class(inherit)] + #[error("Creating '{parent}'")] + Creating { + parent: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + Other(#[from] std::io::Error), + #[cfg(windows)] + #[class(inherit)] + #[error("Creating junction in node_modules folder")] + FailedCreatingJunction { + #[source] + #[inherit] + source: std::io::Error, + }, +} + fn symlink_package_dir( old_path: &Path, new_path: &Path, -) -> Result<(), AnyError> { +) -> Result<(), SymlinkPackageDirError> { let new_parent = new_path.parent().unwrap(); if new_parent.file_name().unwrap() != "node_modules" { // create the parent folder that will contain the symlink - fs::create_dir_all(new_parent) - .with_context(|| format!("Creating '{}'", new_parent.display()))?; + fs::create_dir_all(new_parent).map_err(|source| { + SymlinkPackageDirError::Creating { + parent: new_parent.to_path_buf(), + source, + } + })?; } // need to delete the previous symlink before creating a new one @@ -1075,7 +1146,7 @@ fn junction_or_symlink_dir( old_path_relative: &Path, old_path: &Path, new_path: &Path, -) -> Result<(), AnyError> { +) -> Result<(), SymlinkPackageDirError> { static USE_JUNCTIONS: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); @@ -1084,8 +1155,9 @@ fn junction_or_symlink_dir( // needing to elevate privileges on Windows. // Note: junctions don't support relative paths, so we need to use the // absolute path here. - return junction::create(old_path, new_path) - .context("Failed creating junction in node_modules folder"); + return junction::create(old_path, new_path).map_err(|source| { + SymlinkPackageDirError::FailedCreatingJunction { source } + }); } match symlink_dir(&crate::sys::CliSys::default(), old_path_relative, new_path) @@ -1095,8 +1167,9 @@ fn junction_or_symlink_dir( if symlink_err.kind() == std::io::ErrorKind::PermissionDenied => { USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); - junction::create(old_path, new_path) - .context("Failed creating junction in node_modules folder") + junction::create(old_path, new_path).map_err(|source| { + SymlinkPackageDirError::FailedCreatingJunction { source } + }) } Err(symlink_err) => { log::warn!( @@ -1104,8 +1177,9 @@ fn junction_or_symlink_dir( colors::yellow("Warning") ); USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); - junction::create(old_path, new_path) - .context("Failed creating junction in node_modules folder") + junction::create(old_path, new_path).map_err(|source| { + SymlinkPackageDirError::FailedCreatingJunction { source } + }) } } } diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs index cc4c735c7c..77d00a896e 100644 --- a/cli/npm/managed/resolvers/mod.rs +++ b/cli/npm/managed/resolvers/mod.rs @@ -10,6 +10,7 @@ use std::sync::Arc; use deno_npm::NpmSystemInfo; pub use self::common::NpmPackageFsResolver; +pub use self::common::NpmPackageFsResolverPackageFolderError; use self::global::GlobalNpmPackageResolver; use self::local::LocalNpmPackageResolver; use super::resolution::NpmResolution; diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 710c24f98d..d66b3e618f 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -33,6 +33,7 @@ pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::ManagedCliNpmResolver; pub use self::managed::PackageCaching; +pub use self::managed::ResolvePkgFolderFromDenoModuleError; pub use self::permission_checker::NpmRegistryReadPermissionChecker; pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode; use crate::file_fetcher::CliFileFetcher; @@ -90,7 +91,9 @@ impl deno_npm_cache::NpmCacheHttpClient for CliNpmCacheHttpClient { | Json { .. } | ToStr { .. } | RedirectHeaderParse { .. } - | TooManyRedirects => None, + | TooManyRedirects + | NotFound + | Other(_) => None, BadResponse(bad_response_error) => { Some(bad_response_error.status_code) } diff --git a/cli/npm/permission_checker.rs b/cli/npm/permission_checker.rs index 01fed08954..53031b5bd4 100644 --- a/cli/npm/permission_checker.rs +++ b/cli/npm/permission_checker.rs @@ -6,9 +6,8 @@ use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; +use deno_error::JsErrorBox; use deno_runtime::deno_node::NodePermissions; use sys_traits::FsCanonicalize; @@ -28,6 +27,16 @@ pub struct NpmRegistryReadPermissionChecker { mode: NpmRegistryReadPermissionCheckerMode, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(inherit)] +#[error("failed canonicalizing '{path}'")] +struct EnsureRegistryReadPermissionError { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, +} + impl NpmRegistryReadPermissionChecker { pub fn new(sys: CliSys, mode: NpmRegistryReadPermissionCheckerMode) -> Self { Self { @@ -42,7 +51,7 @@ impl NpmRegistryReadPermissionChecker { &self, permissions: &mut dyn NodePermissions, path: &'a Path, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { if permissions.query_read_all() { return Ok(Cow::Borrowed(path)); // skip permissions checks below } @@ -52,7 +61,9 @@ impl NpmRegistryReadPermissionChecker { if path.components().any(|c| c.as_os_str() == "node_modules") { Ok(Cow::Borrowed(path)) } else { - permissions.check_read_path(path).map_err(Into::into) + permissions + .check_read_path(path) + .map_err(JsErrorBox::from_err) } } NpmRegistryReadPermissionCheckerMode::Global(registry_path) @@ -66,7 +77,7 @@ impl NpmRegistryReadPermissionChecker { if is_path_in_node_modules { let mut cache = self.cache.lock(); let mut canonicalize = - |path: &Path| -> Result, AnyError> { + |path: &Path| -> Result, JsErrorBox> { match cache.get(path) { Some(canon) => Ok(Some(canon.clone())), None => match self.sys.fs_canonicalize(path) { @@ -78,9 +89,12 @@ impl NpmRegistryReadPermissionChecker { if e.kind() == ErrorKind::NotFound { return Ok(None); } - Err(AnyError::from(e)).with_context(|| { - format!("failed canonicalizing '{}'", path.display()) - }) + Err(JsErrorBox::from_err( + EnsureRegistryReadPermissionError { + path: path.to_path_buf(), + source: e, + }, + )) } }, } @@ -98,7 +112,9 @@ impl NpmRegistryReadPermissionChecker { } } - permissions.check_read_path(path).map_err(Into::into) + permissions + .check_read_path(path) + .map_err(JsErrorBox::from_err) } } } diff --git a/cli/ops/bench.rs b/cli/ops/bench.rs index c6eca9216f..a06182fbd0 100644 --- a/cli/ops/bench.rs +++ b/cli/ops/bench.rs @@ -3,13 +3,11 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; -use deno_core::error::generic_error; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::v8; use deno_core::ModuleSpecifier; use deno_core::OpState; +use deno_error::JsErrorBox; use deno_runtime::deno_permissions::ChildPermissionsArg; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_web::StartTime; @@ -78,7 +76,7 @@ pub fn op_pledge_test_permissions( pub fn op_restore_test_permissions( state: &mut OpState, #[serde] token: Uuid, -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if let Some(permissions_holder) = state.try_take::() { if token != permissions_holder.0 { panic!("restore test permissions token does not match the stored token"); @@ -88,7 +86,7 @@ pub fn op_restore_test_permissions( state.put::(permissions); Ok(()) } else { - Err(generic_error("no permissions to restore")) + Err(JsErrorBox::generic("no permissions to restore")) } } @@ -106,9 +104,9 @@ fn op_register_bench( only: bool, warmup: bool, #[buffer] ret_buf: &mut [u8], -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if ret_buf.len() != 4 { - return Err(type_error(format!( + return Err(JsErrorBox::type_error(format!( "Invalid ret_buf length: {}", ret_buf.len() ))); diff --git a/cli/ops/jupyter.rs b/cli/ops/jupyter.rs index 6a9252c35a..3160f991bf 100644 --- a/cli/ops/jupyter.rs +++ b/cli/ops/jupyter.rs @@ -94,10 +94,12 @@ pub fn op_jupyter_input( None } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum JupyterBroadcastError { + #[class(inherit)] #[error(transparent)] SerdeJson(serde_json::Error), + #[class(generic)] #[error(transparent)] ZeroMq(AnyError), } diff --git a/cli/ops/lint.rs b/cli/ops/lint.rs index 0a444e942c..c13cb21a53 100644 --- a/cli/ops/lint.rs +++ b/cli/ops/lint.rs @@ -2,25 +2,36 @@ use deno_ast::MediaType; use deno_ast::ModuleSpecifier; -use deno_core::error::generic_error; -use deno_core::error::AnyError; +use deno_ast::ParseDiagnostic; use deno_core::op2; use crate::tools::lint; deno_core::extension!(deno_lint, ops = [op_lint_create_serialized_ast,],); +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum LintError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + ParseDiagnostic(#[from] ParseDiagnostic), + #[class(type)] + #[error("Failed to parse path as URL: {0}")] + PathParse(std::path::PathBuf), +} + #[op2] #[buffer] fn op_lint_create_serialized_ast( #[string] file_name: &str, #[string] source: String, -) -> Result, AnyError> { +) -> Result, LintError> { let file_text = deno_ast::strip_bom(source); let path = std::env::current_dir()?.join(file_name); - let specifier = ModuleSpecifier::from_file_path(&path).map_err(|_| { - generic_error(format!("Failed to parse path as URL: {}", path.display())) - })?; + let specifier = ModuleSpecifier::from_file_path(&path) + .map_err(|_| LintError::PathParse(path))?; let media_type = MediaType::from_specifier(&specifier); let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { specifier, diff --git a/cli/ops/testing.rs b/cli/ops/testing.rs index 84e9aff83b..c00ab949be 100644 --- a/cli/ops/testing.rs +++ b/cli/ops/testing.rs @@ -3,13 +3,11 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; -use deno_core::error::generic_error; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::v8; use deno_core::ModuleSpecifier; use deno_core::OpState; +use deno_error::JsErrorBox; use deno_runtime::deno_permissions::ChildPermissionsArg; use deno_runtime::deno_permissions::PermissionsContainer; use uuid::Uuid; @@ -73,7 +71,7 @@ pub fn op_pledge_test_permissions( pub fn op_restore_test_permissions( state: &mut OpState, #[serde] token: Uuid, -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if let Some(permissions_holder) = state.try_take::() { if token != permissions_holder.0 { panic!("restore test permissions token does not match the stored token"); @@ -83,7 +81,7 @@ pub fn op_restore_test_permissions( state.put::(permissions); Ok(()) } else { - Err(generic_error("no permissions to restore")) + Err(JsErrorBox::generic("no permissions to restore")) } } @@ -103,9 +101,9 @@ fn op_register_test( #[smi] line_number: u32, #[smi] column_number: u32, #[buffer] ret_buf: &mut [u8], -) -> Result<(), AnyError> { +) -> Result<(), JsErrorBox> { if ret_buf.len() != 4 { - return Err(type_error(format!( + return Err(JsErrorBox::type_error(format!( "Invalid ret_buf length: {}", ret_buf.len() ))); diff --git a/cli/resolver.rs b/cli/resolver.rs index 661685c081..7873a9cce0 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -11,12 +11,12 @@ use dashmap::DashSet; use deno_ast::MediaType; use deno_config::workspace::MappedResolutionDiagnostic; use deno_config::workspace::MappedResolutionError; -use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::url::Url; use deno_core::ModuleSourceCode; use deno_core::ModuleSpecifier; +use deno_error::JsErrorBox; use deno_graph::source::ResolveError; use deno_graph::source::UnknownBuiltInNodeModuleError; use deno_graph::NpmLoadError; @@ -61,7 +61,8 @@ pub struct ModuleCodeStringSource { pub media_type: MediaType, } -#[derive(Debug, Error)] +#[derive(Debug, Error, deno_error::JsError)] +#[class(type)] #[error("{media_type} files are not supported in npm packages: {specifier}")] pub struct NotSupportedKindInNpmError { pub media_type: MediaType, @@ -225,10 +226,12 @@ impl CliResolver { ) => match mapped_resolution_error { MappedResolutionError::Specifier(e) => ResolveError::Specifier(e), // deno_graph checks specifically for an ImportMapError - MappedResolutionError::ImportMap(e) => ResolveError::Other(e.into()), - err => ResolveError::Other(err.into()), + MappedResolutionError::ImportMap(e) => ResolveError::ImportMap(e), + MappedResolutionError::Workspace(e) => { + ResolveError::Other(JsErrorBox::from_err(e)) + } }, - err => ResolveError::Other(err.into()), + err => ResolveError::Other(JsErrorBox::from_err(err)), })?; if resolution.found_package_json_dep { @@ -356,26 +359,28 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { .map(|r| { r.map_err(|err| match err { NpmResolutionError::Registry(e) => { - NpmLoadError::RegistryInfo(Arc::new(e.into())) + NpmLoadError::RegistryInfo(Arc::new(e)) } NpmResolutionError::Resolution(e) => { - NpmLoadError::PackageReqResolution(Arc::new(e.into())) + NpmLoadError::PackageReqResolution(Arc::new(e)) } NpmResolutionError::DependencyEntry(e) => { - NpmLoadError::PackageReqResolution(Arc::new(e.into())) + NpmLoadError::PackageReqResolution(Arc::new(e)) } }) }) .collect(), dep_graph_result: match top_level_result { - Ok(()) => result.dependencies_result.map_err(Arc::new), + Ok(()) => result + .dependencies_result + .map_err(|e| Arc::new(e) as Arc), Err(err) => Err(Arc::new(err)), }, } } None => { - let err = Arc::new(anyhow!( - "npm specifiers were requested; but --no-npm is specified" + let err = Arc::new(JsErrorBox::generic( + "npm specifiers were requested; but --no-npm is specified", )); NpmResolvePkgReqsResult { results: package_reqs diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 1e21e69eb9..30e939cb7a 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -21,9 +21,8 @@ use deno_config::workspace::MappedResolutionError; use deno_config::workspace::ResolverWorkspaceJsrPackage; use deno_config::workspace::WorkspaceResolver; use deno_core::anyhow::Context; -use deno_core::error::generic_error; -use deno_core::error::type_error; use deno_core::error::AnyError; +use deno_core::error::ModuleLoaderError; use deno_core::futures::future::LocalBoxFuture; use deno_core::futures::FutureExt; use deno_core::v8_set_flags; @@ -36,6 +35,7 @@ use deno_core::ModuleType; use deno_core::RequestedModuleType; use deno_core::ResolutionKind; use deno_core::SourceCodeCacheInfo; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonDepValue; use deno_resolver::cjs::IsCjsResolutionMode; @@ -182,25 +182,32 @@ impl ModuleLoader for EmbeddedModuleLoader { raw_specifier: &str, referrer: &str, kind: ResolutionKind, - ) -> Result { + ) -> Result { let referrer = if referrer == "." { if kind != ResolutionKind::MainModule { - return Err(generic_error(format!( - "Expected to resolve main module, got {:?} instead.", - kind - ))); + return Err( + JsErrorBox::generic(format!( + "Expected to resolve main module, got {:?} instead.", + kind + )) + .into(), + ); } let current_dir = std::env::current_dir().unwrap(); deno_core::resolve_path(".", ¤t_dir)? } else { ModuleSpecifier::parse(referrer).map_err(|err| { - type_error(format!("Referrer uses invalid specifier: {}", err)) + JsErrorBox::type_error(format!( + "Referrer uses invalid specifier: {}", + err + )) })? }; let referrer_kind = if self .shared .cjs_tracker - .is_maybe_cjs(&referrer, MediaType::from_specifier(&referrer))? + .is_maybe_cjs(&referrer, MediaType::from_specifier(&referrer)) + .map_err(JsErrorBox::from_err)? { ResolutionMode::Require } else { @@ -217,7 +224,8 @@ impl ModuleLoader for EmbeddedModuleLoader { &referrer, referrer_kind, NodeResolutionKind::Execution, - )? + ) + .map_err(JsErrorBox::from_err)? .into_url(), ); } @@ -245,14 +253,18 @@ impl ModuleLoader for EmbeddedModuleLoader { Some(&referrer), referrer_kind, NodeResolutionKind::Execution, - )?, + ) + .map_err(JsErrorBox::from_err)?, ), Ok(MappedResolution::PackageJson { dep_result, sub_path, alias, .. - }) => match dep_result.as_ref().map_err(|e| AnyError::from(e.clone()))? { + }) => match dep_result + .as_ref() + .map_err(|e| JsErrorBox::from_err(e.clone()))? + { PackageJsonDepValue::Req(req) => self .shared .npm_req_resolver @@ -263,7 +275,7 @@ impl ModuleLoader for EmbeddedModuleLoader { referrer_kind, NodeResolutionKind::Execution, ) - .map_err(AnyError::from), + .map_err(|e| JsErrorBox::from_err(e).into()), PackageJsonDepValue::Workspace(version_req) => { let pkg_folder = self .shared @@ -271,7 +283,8 @@ impl ModuleLoader for EmbeddedModuleLoader { .resolve_workspace_pkg_json_folder_for_pkg_json_dep( alias, version_req, - )?; + ) + .map_err(JsErrorBox::from_err)?; Ok( self .shared @@ -282,7 +295,8 @@ impl ModuleLoader for EmbeddedModuleLoader { Some(&referrer), referrer_kind, NodeResolutionKind::Execution, - )?, + ) + .map_err(JsErrorBox::from_err)?, ) } }, @@ -291,12 +305,18 @@ impl ModuleLoader for EmbeddedModuleLoader { if let Ok(reference) = NpmPackageReqReference::from_specifier(&specifier) { - return Ok(self.shared.npm_req_resolver.resolve_req_reference( - &reference, - &referrer, - referrer_kind, - NodeResolutionKind::Execution, - )?); + return Ok( + self + .shared + .npm_req_resolver + .resolve_req_reference( + &reference, + &referrer, + referrer_kind, + NodeResolutionKind::Execution, + ) + .map_err(JsErrorBox::from_err)?, + ); } if specifier.scheme() == "jsr" { @@ -318,18 +338,22 @@ impl ModuleLoader for EmbeddedModuleLoader { Err(err) if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" => { - let maybe_res = self.shared.npm_req_resolver.resolve_if_for_npm_pkg( - raw_specifier, - &referrer, - referrer_kind, - NodeResolutionKind::Execution, - )?; + let maybe_res = self + .shared + .npm_req_resolver + .resolve_if_for_npm_pkg( + raw_specifier, + &referrer, + referrer_kind, + NodeResolutionKind::Execution, + ) + .map_err(JsErrorBox::from_err)?; if let Some(res) = maybe_res { return Ok(res.into_url()); } - Err(err.into()) + Err(JsErrorBox::from_err(err).into()) } - Err(err) => Err(err.into()), + Err(err) => Err(JsErrorBox::from_err(err).into()), } } @@ -360,9 +384,9 @@ impl ModuleLoader for EmbeddedModuleLoader { { Ok(response) => response, Err(err) => { - return deno_core::ModuleLoadResponse::Sync(Err(type_error( - format!("{:#}", err), - ))); + return deno_core::ModuleLoadResponse::Sync(Err( + JsErrorBox::type_error(format!("{:#}", err)).into(), + )); } }; return deno_core::ModuleLoadResponse::Sync(Ok( @@ -420,9 +444,9 @@ impl ModuleLoader for EmbeddedModuleLoader { { Ok(is_maybe_cjs) => is_maybe_cjs, Err(err) => { - return deno_core::ModuleLoadResponse::Sync(Err(type_error( - format!("{:?}", err), - ))); + return deno_core::ModuleLoadResponse::Sync(Err( + JsErrorBox::type_error(format!("{:?}", err)).into(), + )); } }; if is_maybe_cjs { @@ -482,12 +506,16 @@ impl ModuleLoader for EmbeddedModuleLoader { )) } } - Ok(None) => deno_core::ModuleLoadResponse::Sync(Err(type_error( - format!("{MODULE_NOT_FOUND}: {}", original_specifier), - ))), - Err(err) => deno_core::ModuleLoadResponse::Sync(Err(type_error( - format!("{:?}", err), - ))), + Ok(None) => deno_core::ModuleLoadResponse::Sync(Err( + JsErrorBox::type_error(format!( + "{MODULE_NOT_FOUND}: {}", + original_specifier + )) + .into(), + )), + Err(err) => deno_core::ModuleLoadResponse::Sync(Err( + JsErrorBox::type_error(format!("{:?}", err)).into(), + )), } } @@ -553,7 +581,7 @@ impl NodeRequireLoader for EmbeddedModuleLoader { &self, permissions: &mut dyn deno_runtime::deno_node::NodePermissions, path: &'a std::path::Path, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { if self.shared.modules.has_file(path) { // allow reading if the file is in the snapshot return Ok(Cow::Borrowed(path)); @@ -563,17 +591,23 @@ impl NodeRequireLoader for EmbeddedModuleLoader { .shared .npm_registry_permission_checker .ensure_read_permission(permissions, path) + .map_err(JsErrorBox::from_err) } fn load_text_file_lossy( &self, path: &std::path::Path, - ) -> Result, AnyError> { - let file_entry = self.shared.vfs.file_entry(path)?; + ) -> Result, JsErrorBox> { + let file_entry = self + .shared + .vfs + .file_entry(path) + .map_err(JsErrorBox::from_err)?; let file_bytes = self .shared .vfs - .read_file_all(file_entry, VfsFileSubDataKind::ModuleGraph)?; + .read_file_all(file_entry, VfsFileSubDataKind::ModuleGraph) + .map_err(JsErrorBox::from_err)?; Ok(from_utf8_lossy_cow(file_bytes)) } @@ -626,10 +660,10 @@ struct StandaloneRootCertStoreProvider { } impl RootCertStoreProvider for StandaloneRootCertStoreProvider { - fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> { + fn get_or_try_init(&self) -> Result<&RootCertStore, JsErrorBox> { self.cell.get_or_try_init(|| { get_root_cert_store(None, self.ca_stores.clone(), self.ca_data.clone()) - .map_err(|err| err.into()) + .map_err(JsErrorBox::from_err) }) } } diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index 42895089ef..6a57c4ce6c 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -6,8 +6,9 @@ use std::sync::Arc; use std::time::Duration; use deno_config::glob::WalkEntry; -use deno_core::error::generic_error; +use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::error::JsError; use deno_core::futures::future; use deno_core::futures::stream; @@ -18,6 +19,7 @@ use deno_core::unsync::spawn_blocking; use deno_core::v8; use deno_core::ModuleSpecifier; use deno_core::PollEventLoopOptions; +use deno_error::JsErrorBox; use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::permissions::RuntimePermissionDescriptorParser; @@ -162,17 +164,14 @@ async fn bench_specifier( .await { Ok(()) => Ok(()), - Err(error) => { - if error.is::() { - sender.send(BenchEvent::UncaughtError( - specifier.to_string(), - Box::new(error.downcast::().unwrap()), - ))?; - Ok(()) - } else { - Err(error) - } + Err(CoreError::Js(error)) => { + sender.send(BenchEvent::UncaughtError( + specifier.to_string(), + Box::new(error), + ))?; + Ok(()) } + Err(e) => Err(e.into()), } } @@ -183,7 +182,7 @@ async fn bench_specifier_inner( specifier: ModuleSpecifier, sender: &UnboundedSender, filter: TestFilter, -) -> Result<(), AnyError> { +) -> Result<(), CoreError> { let mut worker = worker_factory .create_custom_worker( WorkerExecutionMode::Bench, @@ -230,14 +229,18 @@ async fn bench_specifier_inner( .partial_cmp(&groups.get_index_of(&d2.group).unwrap()) .unwrap() }); - sender.send(BenchEvent::Plan(BenchPlan { - origin: specifier.to_string(), - total: benchmarks.len(), - used_only, - names: benchmarks.iter().map(|(d, _)| d.name.clone()).collect(), - }))?; + sender + .send(BenchEvent::Plan(BenchPlan { + origin: specifier.to_string(), + total: benchmarks.len(), + used_only, + names: benchmarks.iter().map(|(d, _)| d.name.clone()).collect(), + })) + .map_err(JsErrorBox::from_err)?; for (desc, function) in benchmarks { - sender.send(BenchEvent::Wait(desc.id))?; + sender + .send(BenchEvent::Wait(desc.id)) + .map_err(JsErrorBox::from_err)?; let call = worker.js_runtime.call(&function); let result = worker .js_runtime @@ -245,8 +248,11 @@ async fn bench_specifier_inner( .await?; let scope = &mut worker.js_runtime.handle_scope(); let result = v8::Local::new(scope, result); - let result = serde_v8::from_v8::(scope, result)?; - sender.send(BenchEvent::Result(desc.id, result))?; + let result = serde_v8::from_v8::(scope, result) + .map_err(JsErrorBox::from_err)?; + sender + .send(BenchEvent::Result(desc.id, result)) + .map_err(JsErrorBox::from_err)?; } // Ignore `defaultPrevented` of the `beforeunload` event. We don't allow the @@ -356,13 +362,13 @@ async fn bench_specifiers( reporter.report_end(&report); if used_only { - return Err(generic_error( + return Err(anyhow!( "Bench failed because the \"only\" option was used", )); } if report.failed > 0 { - return Err(generic_error("Bench failed")); + return Err(anyhow!("Bench failed")); } Ok(()) @@ -440,7 +446,7 @@ pub async fn run_benchmarks( .collect::>(); if specifiers.is_empty() { - return Err(generic_error("No bench modules found")); + return Err(anyhow!("No bench modules found")); } let main_graph_container = factory.main_module_graph_container().await?; diff --git a/cli/tools/check.rs b/cli/tools/check.rs index 1ee3f1782b..53fd5c5db9 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -6,7 +6,9 @@ use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; +use deno_config::deno_json; use deno_core::error::AnyError; +use deno_error::JsErrorBox; use deno_graph::Module; use deno_graph::ModuleError; use deno_graph::ModuleGraph; @@ -112,6 +114,27 @@ pub struct TypeChecker { sys: CliSys, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum CheckError { + #[class(inherit)] + #[error(transparent)] + Diagnostics(#[from] Diagnostics), + #[class(inherit)] + #[error(transparent)] + ConfigFile(#[from] deno_json::ConfigFileError), + #[class(inherit)] + #[error(transparent)] + ToMaybeJsxImportSourceConfig( + #[from] deno_json::ToMaybeJsxImportSourceConfigError, + ), + #[class(inherit)] + #[error(transparent)] + TscExec(#[from] tsc::ExecError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + impl TypeChecker { pub fn new( caches: Arc, @@ -141,7 +164,7 @@ impl TypeChecker { &self, graph: ModuleGraph, options: CheckOptions, - ) -> Result, AnyError> { + ) -> Result, CheckError> { let (graph, mut diagnostics) = self.check_diagnostics(graph, options).await?; diagnostics.emit_warnings(); @@ -160,7 +183,7 @@ impl TypeChecker { &self, mut graph: ModuleGraph, options: CheckOptions, - ) -> Result<(Arc, Diagnostics), AnyError> { + ) -> Result<(Arc, Diagnostics), CheckError> { if !options.type_check_mode.is_true() || graph.roots.is_empty() { return Ok((graph.into(), Default::default())); } diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index b148c9a0b1..96dd6798f5 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -8,9 +8,9 @@ use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; +use deno_core::anyhow::anyhow; use deno_core::anyhow::bail; use deno_core::anyhow::Context; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_graph::GraphKind; @@ -330,7 +330,7 @@ async fn resolve_compile_executable_output_path( .map(PathBuf::from) } - output_path.ok_or_else(|| generic_error( + output_path.ok_or_else(|| anyhow!( "An executable name was not provided. One could not be inferred from the URL. Aborting.", )).map(|output_path| { get_os_specific_filepath(output_path, &compile_flags.target) diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 522d7d75be..06afa5fac2 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -17,7 +17,6 @@ use deno_config::glob::PathOrPattern; use deno_config::glob::PathOrPatternSet; use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::sourcemap::SourceMap; @@ -430,7 +429,7 @@ fn collect_coverages( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(&CliSys::default(), file_patterns)?; + .collect_file_patterns(&CliSys::default(), file_patterns); let coverage_patterns = FilePatterns { base: initial_cwd.to_path_buf(), @@ -505,7 +504,7 @@ pub fn cover_files( coverage_flags: CoverageFlags, ) -> Result<(), AnyError> { if coverage_flags.files.include.is_empty() { - return Err(generic_error("No matching coverage profiles found")); + return Err(anyhow!("No matching coverage profiles found")); } let factory = CliFactory::from_flags(flags); @@ -527,7 +526,7 @@ pub fn cover_files( cli_options.initial_cwd(), )?; if script_coverages.is_empty() { - return Err(generic_error("No coverage files found")); + return Err(anyhow!("No coverage files found")); } let script_coverages = filter_coverages( script_coverages, @@ -536,7 +535,7 @@ pub fn cover_files( in_npm_pkg_checker.as_ref(), ); if script_coverages.is_empty() { - return Err(generic_error("No covered files included in the report")); + return Err(anyhow!("No covered files included in the report")); } let proc_coverages: Vec<_> = script_coverages diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 2fa944b362..114c8f958a 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -62,10 +62,11 @@ async fn generate_doc_nodes_for_builtin_types( )], Vec::new(), ); + let roots = vec![source_file_specifier.clone()]; let mut graph = deno_graph::ModuleGraph::new(GraphKind::TypesOnly); graph .build( - vec![source_file_specifier.clone()], + roots.clone(), &loader, deno_graph::BuildOptions { imports: Vec::new(), @@ -85,14 +86,13 @@ async fn generate_doc_nodes_for_builtin_types( let doc_parser = doc::DocParser::new( &graph, parser, + &roots, doc::DocParserOptions { diagnostics: false, private: doc_flags.private, }, )?; - let nodes = doc_parser.parse_module(&source_file_specifier)?.definitions; - - Ok(IndexMap::from([(source_file_specifier, nodes)])) + Ok(doc_parser.parse()?) } pub async fn doc( @@ -158,19 +158,13 @@ pub async fn doc( let doc_parser = doc::DocParser::new( &graph, &capturing_parser, + &module_specifiers, doc::DocParserOptions { private: doc_flags.private, diagnostics: doc_flags.lint, }, )?; - - let mut doc_nodes_by_url = - IndexMap::with_capacity(module_specifiers.len()); - - for module_specifier in module_specifiers { - let nodes = doc_parser.parse_with_reexports(&module_specifier)?; - doc_nodes_by_url.insert(module_specifier, nodes); - } + let doc_nodes_by_url = doc_parser.parse()?; if doc_flags.lint { let diagnostics = doc_parser.take_diagnostics(); @@ -191,29 +185,9 @@ pub async fn doc( .await?; let (_, deno_ns) = deno_ns.into_iter().next().unwrap(); - let short_path = Rc::new(ShortPath::new( - ModuleSpecifier::parse("file:///lib.deno.d.ts").unwrap(), - None, - None, - None, - )); - - deno_doc::html::compute_namespaced_symbols( - &deno_ns - .into_iter() - .map(|node| deno_doc::html::DocNodeWithContext { - origin: short_path.clone(), - ns_qualifiers: Rc::new([]), - kind_with_drilldown: - deno_doc::html::DocNodeKindWithDrilldown::Other(node.kind()), - inner: Rc::new(node), - drilldown_name: None, - parent: None, - }) - .collect::>(), - ) + Some(deno_ns) } else { - Default::default() + None }; let mut main_entrypoint = None; @@ -393,7 +367,7 @@ impl UsageComposer for DocComposer { fn generate_docs_directory( doc_nodes_by_url: IndexMap>, html_options: &DocHtmlFlag, - deno_ns: std::collections::HashMap, Option>>, + built_in_types: Option>, rewrite_map: Option>, main_entrypoint: Option, ) -> Result<(), AnyError> { @@ -426,12 +400,12 @@ fn generate_docs_directory( None }; - let options = deno_doc::html::GenerateOptions { + let mut options = deno_doc::html::GenerateOptions { package_name: html_options.name.clone(), main_entrypoint, rewrite_map, href_resolver: Rc::new(DocResolver { - deno_ns, + deno_ns: Default::default(), strip_trailing_html: html_options.strip_trailing_html, }), usage_composer: Rc::new(DocComposer), @@ -451,7 +425,58 @@ fn generate_docs_directory( })), }; - let mut files = deno_doc::html::generate(options, doc_nodes_by_url) + if let Some(built_in_types) = built_in_types { + let ctx = deno_doc::html::GenerateCtx::create_basic( + deno_doc::html::GenerateOptions { + package_name: None, + main_entrypoint: Some( + ModuleSpecifier::parse("file:///lib.deno.d.ts").unwrap(), + ), + href_resolver: Rc::new(DocResolver { + deno_ns: Default::default(), + strip_trailing_html: false, + }), + usage_composer: Rc::new(DocComposer), + rewrite_map: Default::default(), + category_docs: Default::default(), + disable_search: Default::default(), + symbol_redirect_map: Default::default(), + default_symbol_map: Default::default(), + markdown_renderer: deno_doc::html::comrak::create_renderer( + None, None, None, + ), + markdown_stripper: Rc::new(deno_doc::html::comrak::strip), + head_inject: None, + }, + IndexMap::from([( + ModuleSpecifier::parse("file:///lib.deno.d.ts").unwrap(), + built_in_types, + )]), + )?; + + let deno_ns = deno_doc::html::compute_namespaced_symbols( + &ctx, + Box::new( + ctx + .doc_nodes + .values() + .next() + .unwrap() + .iter() + .map(std::borrow::Cow::Borrowed), + ), + ); + + options.href_resolver = Rc::new(DocResolver { + deno_ns, + strip_trailing_html: html_options.strip_trailing_html, + }); + } + + let ctx = + deno_doc::html::GenerateCtx::create_basic(options, doc_nodes_by_url)?; + + let mut files = deno_doc::html::generate(ctx) .context("Failed to generate HTML documentation")?; files.insert("prism.js".to_string(), PRISM_JS.to_string()); diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 3411d557bf..d0948fd4f7 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -26,7 +26,6 @@ use deno_config::glob::FilePatterns; use deno_core::anyhow::anyhow; use deno_core::anyhow::bail; use deno_core::anyhow::Context; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::futures; use deno_core::parking_lot::Mutex; @@ -167,7 +166,7 @@ fn resolve_paths_with_options_batches( Vec::with_capacity(members_fmt_options.len()); for (_ctx, member_fmt_options) in members_fmt_options { let files = - collect_fmt_files(cli_options, member_fmt_options.files.clone())?; + collect_fmt_files(cli_options, member_fmt_options.files.clone()); if !files.is_empty() { paths_with_options_batches.push(PathsWithOptions { base: member_fmt_options.files.base.clone(), @@ -177,7 +176,7 @@ fn resolve_paths_with_options_batches( } } if paths_with_options_batches.is_empty() { - return Err(generic_error("No target files found.")); + return Err(anyhow!("No target files found.")); } Ok(paths_with_options_batches) } @@ -224,7 +223,7 @@ async fn format_files( fn collect_fmt_files( cli_options: &CliOptions, files: FilePatterns, -) -> Result, AnyError> { +) -> Vec { FileCollector::new(|e| { is_supported_ext_fmt(e.path) || (e.path.extension().is_none() && cli_options.ext_flag().is_some()) @@ -484,7 +483,7 @@ pub fn format_html( } if let Some(error_msg) = inner(&error, file_path) { - AnyError::from(generic_error(error_msg)) + AnyError::msg(error_msg) } else { AnyError::from(error) } @@ -732,9 +731,9 @@ impl Formatter for CheckFormatter { Ok(()) } else { let not_formatted_files_str = files_str(not_formatted_files_count); - Err(generic_error(format!( + Err(anyhow!( "Found {not_formatted_files_count} not formatted {not_formatted_files_str} in {checked_files_str}", - ))) + )) } } } diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 74d6591124..81b0af0a66 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -11,6 +11,7 @@ use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::url; +use deno_error::JsErrorClass; use deno_graph::Dependency; use deno_graph::GraphKind; use deno_graph::Module; @@ -664,9 +665,10 @@ impl<'a> GraphDisplayContext<'a> { HttpsChecksumIntegrity(_) => "(checksum integrity error)", Decode(_) => "(loading decode error)", Loader(err) => { - match deno_runtime::errors::get_error_class_name(err) { - Some("NotCapable") => "(not capable, requires --allow-import)", - _ => "(loading error)", + if err.get_class() == "NotCapable" { + "(not capable, requires --allow-import)" + } else { + "(loading error)" } } Jsr(_) => "(loading error)", diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 6dc065171a..ff5744c07a 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -12,9 +12,9 @@ use std::path::PathBuf; use std::sync::Arc; use deno_cache_dir::file_fetcher::CacheSetting; +use deno_core::anyhow::anyhow; use deno_core::anyhow::bail; use deno_core::anyhow::Context; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_core::url::Url; @@ -54,9 +54,7 @@ fn validate_name(exec_name: &str) -> Result<(), AnyError> { if EXEC_NAME_RE.is_match(exec_name) { Ok(()) } else { - Err(generic_error(format!( - "Invalid executable name: {exec_name}" - ))) + Err(anyhow!("Invalid executable name: {exec_name}")) } } @@ -223,7 +221,7 @@ pub async fn uninstall( // ensure directory exists if let Ok(metadata) = fs::metadata(&installation_dir) { if !metadata.is_dir() { - return Err(generic_error("Installation path is not a directory")); + return Err(anyhow!("Installation path is not a directory")); } } @@ -247,10 +245,10 @@ pub async fn uninstall( } if !removed { - return Err(generic_error(format!( + return Err(anyhow!( "No installation found for {}", uninstall_flags.name - ))); + )); } // There might be some extra files to delete @@ -423,14 +421,14 @@ async fn create_install_shim( // ensure directory exists if let Ok(metadata) = fs::metadata(&shim_data.installation_dir) { if !metadata.is_dir() { - return Err(generic_error("Installation path is not a directory")); + return Err(anyhow!("Installation path is not a directory")); } } else { fs::create_dir_all(&shim_data.installation_dir)?; }; if shim_data.file_path.exists() && !install_flags_global.force { - return Err(generic_error( + return Err(anyhow!( "Existing installation found. Aborting (Use -f to overwrite).", )); }; @@ -492,7 +490,7 @@ async fn resolve_shim_data( let name = match name { Some(name) => name, - None => return Err(generic_error( + None => return Err(anyhow!( "An executable name was not provided. One could not be inferred from the URL. Aborting.", )), }; @@ -524,9 +522,7 @@ async fn resolve_shim_data( let log_level = match log_level { Level::Debug => "debug", Level::Info => "info", - _ => { - return Err(generic_error(format!("invalid log level {log_level}"))) - } + _ => return Err(anyhow!(format!("invalid log level {log_level}"))), }; executable_args.push(log_level.to_string()); } diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs index bb39528f3e..78b7675420 100644 --- a/cli/tools/jupyter/mod.rs +++ b/cli/tools/jupyter/mod.rs @@ -2,9 +2,9 @@ use std::sync::Arc; +use deno_core::anyhow::anyhow; use deno_core::anyhow::bail; use deno_core::anyhow::Context; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::futures::FutureExt; use deno_core::located_script_name; @@ -137,10 +137,10 @@ pub async fn kernel( } let cwd_url = Url::from_directory_path(cli_options.initial_cwd()).map_err(|_| { - generic_error(format!( + anyhow!( "Unable to construct URL from the path of cwd: {}", cli_options.initial_cwd().to_string_lossy(), - )) + ) })?; repl_session.set_test_reporter_factory(Box::new(move || { Box::new( diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index e7b16f0283..36ba85f613 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -18,7 +18,6 @@ use deno_config::glob::FileCollector; use deno_config::glob::FilePatterns; use deno_config::workspace::WorkspaceDirectory; use deno_core::anyhow::anyhow; -use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::futures::future::LocalBoxFuture; use deno_core::futures::FutureExt; @@ -77,9 +76,7 @@ pub async fn lint( ) -> Result<(), AnyError> { if lint_flags.watch.is_some() { if lint_flags.is_stdin() { - return Err(generic_error( - "Lint watch on standard input is not supported.", - )); + return Err(anyhow!("Lint watch on standard input is not supported.",)); } return lint_with_watch(flags, lint_flags).await; @@ -223,7 +220,7 @@ fn resolve_paths_with_options_batches( let mut paths_with_options_batches = Vec::with_capacity(members_lint_options.len()); for (dir, lint_options) in members_lint_options { - let files = collect_lint_files(cli_options, lint_options.files.clone())?; + let files = collect_lint_files(cli_options, lint_options.files.clone()); if !files.is_empty() { paths_with_options_batches.push(PathsWithOptions { dir, @@ -233,7 +230,7 @@ fn resolve_paths_with_options_batches( } } if paths_with_options_batches.is_empty() { - return Err(generic_error("No target files found.")); + return Err(anyhow!("No target files found.")); } Ok(paths_with_options_batches) } @@ -446,7 +443,7 @@ impl WorkspaceLinter { fn collect_lint_files( cli_options: &CliOptions, files: FilePatterns, -) -> Result, AnyError> { +) -> Vec { FileCollector::new(|e| { is_script_ext(e.path) || (e.path.extension().is_none() && cli_options.ext_flag().is_some()) @@ -534,7 +531,7 @@ fn lint_stdin( } let mut source_code = String::new(); if stdin().read_to_string(&mut source_code).is_err() { - return Err(generic_error("Failed to read from stdin")); + return Err(anyhow!("Failed to read from stdin")); } let linter = CliLinter::new(CliLinterOptions { diff --git a/cli/tools/lint/rules/no_sloppy_imports.rs b/cli/tools/lint/rules/no_sloppy_imports.rs index 33d3090fe3..825835f3b5 100644 --- a/cli/tools/lint/rules/no_sloppy_imports.rs +++ b/cli/tools/lint/rules/no_sloppy_imports.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use deno_ast::SourceRange; use deno_config::workspace::WorkspaceResolver; -use deno_core::anyhow::anyhow; +use deno_error::JsErrorBox; use deno_graph::source::ResolutionKind; use deno_graph::source::ResolveError; use deno_graph::Range; @@ -187,7 +187,7 @@ impl<'a> deno_graph::source::Resolver for SloppyImportCaptureResolver<'a> { let resolution = self .workspace_resolver .resolve(specifier_text, &referrer_range.specifier) - .map_err(|err| ResolveError::Other(err.into()))?; + .map_err(|err| ResolveError::Other(JsErrorBox::from_err(err)))?; match resolution { deno_config::workspace::MappedResolution::Normal { @@ -220,7 +220,7 @@ impl<'a> deno_graph::source::Resolver for SloppyImportCaptureResolver<'a> { } | deno_config::workspace::MappedResolution::PackageJson { .. } => { // this error is ignored - Err(ResolveError::Other(anyhow!(""))) + Err(ResolveError::Other(JsErrorBox::generic(""))) } } } diff --git a/cli/tools/registry/paths.rs b/cli/tools/registry/paths.rs index ef98838913..563b841280 100644 --- a/cli/tools/registry/paths.rs +++ b/cli/tools/registry/paths.rs @@ -233,7 +233,7 @@ pub fn collect_publish_paths( ) -> Result, AnyError> { let diagnostics_collector = opts.diagnostics_collector; let publish_paths = - collect_paths(opts.cli_options, diagnostics_collector, opts.file_patterns)?; + collect_paths(opts.cli_options, diagnostics_collector, opts.file_patterns); let publish_paths_set = publish_paths.iter().cloned().collect::>(); let capacity = publish_paths.len() + opts.force_include_paths.len(); let mut paths = HashSet::with_capacity(capacity); @@ -321,7 +321,7 @@ fn collect_paths( cli_options: &CliOptions, diagnostics_collector: &PublishDiagnosticsCollector, file_patterns: FilePatterns, -) -> Result, AnyError> { +) -> Vec { FileCollector::new(|e| { if !e.metadata.file_type().is_file() { if let Ok(specifier) = ModuleSpecifier::from_file_path(e.path) { diff --git a/cli/tools/repl/channel.rs b/cli/tools/repl/channel.rs index 4ac28b9d52..dbafacebc9 100644 --- a/cli/tools/repl/channel.rs +++ b/cli/tools/repl/channel.rs @@ -4,8 +4,10 @@ use std::cell::RefCell; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::serde_json; use deno_core::serde_json::Value; +use deno_error::JsErrorBox; use tokio::sync::mpsc::channel; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::Receiver; @@ -47,7 +49,7 @@ pub enum RustylineSyncMessage { } pub enum RustylineSyncResponse { - PostMessage(Result), + PostMessage(Result), LspCompletions(Vec), } @@ -61,7 +63,7 @@ impl RustylineSyncMessageSender { &self, method: &str, params: Option, - ) -> Result { + ) -> Result { if let Err(err) = self .message_tx @@ -69,10 +71,11 @@ impl RustylineSyncMessageSender { method: method.to_string(), params: params .map(|params| serde_json::to_value(params)) - .transpose()?, + .transpose() + .map_err(JsErrorBox::from_err)?, }) { - Err(anyhow!("{}", err)) + Err(JsErrorBox::from_err(err).into()) } else { match self.response_rx.borrow_mut().blocking_recv().unwrap() { RustylineSyncResponse::PostMessage(result) => result, diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs index 7b20717649..ed9dd61e2d 100644 --- a/cli/tools/repl/session.rs +++ b/cli/tools/repl/session.rs @@ -16,8 +16,9 @@ use deno_ast::ParsedSource; use deno_ast::SourcePos; use deno_ast::SourceRangedForSpanned; use deno_ast::SourceTextInfo; -use deno_core::error::generic_error; +use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::futures::channel::mpsc::UnboundedReceiver; use deno_core::futures::FutureExt; use deno_core::futures::StreamExt; @@ -27,6 +28,7 @@ use deno_core::unsync::spawn; use deno_core::url::Url; use deno_core::LocalInspectorSession; use deno_core::PollEventLoopOptions; +use deno_error::JsErrorBox; use deno_graph::Position; use deno_graph::PositionRange; use deno_graph::SpecifierWithRange; @@ -250,10 +252,10 @@ impl ReplSession { let cwd_url = Url::from_directory_path(cli_options.initial_cwd()).map_err(|_| { - generic_error(format!( + anyhow!( "Unable to construct URL from the path of cwd: {}", cli_options.initial_cwd().to_string_lossy(), - )) + ) })?; let ts_config_for_emit = cli_options .resolve_ts_config_for_emit(deno_config::deno_json::TsConfigType::Emit)?; @@ -322,7 +324,7 @@ impl ReplSession { &mut self, method: &str, params: Option, - ) -> Result { + ) -> Result { self .worker .js_runtime @@ -339,7 +341,7 @@ impl ReplSession { .await } - pub async fn run_event_loop(&mut self) -> Result<(), AnyError> { + pub async fn run_event_loop(&mut self) -> Result<(), CoreError> { self.worker.run_event_loop(true).await } @@ -400,21 +402,29 @@ impl ReplSession { } Err(err) => { // handle a parsing diagnostic - match err.downcast_ref::() { + match crate::util::result::any_and_jserrorbox_downcast_ref::< + deno_ast::ParseDiagnostic, + >(&err) + { Some(diagnostic) => { Ok(EvaluationOutput::Error(format_diagnostic(diagnostic))) } - None => match err.downcast_ref::() { - Some(diagnostics) => Ok(EvaluationOutput::Error( - diagnostics - .0 - .iter() - .map(format_diagnostic) - .collect::>() - .join("\n\n"), - )), - None => Err(err), - }, + None => { + match crate::util::result::any_and_jserrorbox_downcast_ref::< + ParseDiagnosticsError, + >(&err) + { + Some(diagnostics) => Ok(EvaluationOutput::Error( + diagnostics + .0 + .iter() + .map(format_diagnostic) + .collect::>() + .join("\n\n"), + )), + None => Err(err), + } + } } } } @@ -742,7 +752,7 @@ impl ReplSession { async fn evaluate_expression( &mut self, expression: &str, - ) -> Result { + ) -> Result { self .post_message_with_event_loop( "Runtime.evaluate", @@ -765,7 +775,9 @@ impl ReplSession { }), ) .await - .and_then(|res| serde_json::from_value(res).map_err(|e| e.into())) + .and_then(|res| { + serde_json::from_value(res).map_err(|e| JsErrorBox::from_err(e).into()) + }) } } diff --git a/cli/tools/run/hmr.rs b/cli/tools/run/hmr.rs index 9f19aea015..913e119689 100644 --- a/cli/tools/run/hmr.rs +++ b/cli/tools/run/hmr.rs @@ -4,13 +4,13 @@ use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; -use deno_core::error::generic_error; -use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::futures::StreamExt; use deno_core::serde_json::json; use deno_core::serde_json::{self}; use deno_core::url::Url; use deno_core::LocalInspectorSession; +use deno_error::JsErrorBox; use deno_terminal::colors; use tokio::select; @@ -66,19 +66,19 @@ pub struct HmrRunner { #[async_trait::async_trait(?Send)] impl crate::worker::HmrRunner for HmrRunner { // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs` - async fn start(&mut self) -> Result<(), AnyError> { + async fn start(&mut self) -> Result<(), CoreError> { self.enable_debugger().await } // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs` - async fn stop(&mut self) -> Result<(), AnyError> { + async fn stop(&mut self) -> Result<(), CoreError> { self .watcher_communicator .change_restart_mode(WatcherRestartMode::Automatic); self.disable_debugger().await } - async fn run(&mut self) -> Result<(), AnyError> { + async fn run(&mut self) -> Result<(), CoreError> { self .watcher_communicator .change_restart_mode(WatcherRestartMode::Manual); @@ -87,13 +87,13 @@ impl crate::worker::HmrRunner for HmrRunner { select! { biased; Some(notification) = session_rx.next() => { - let notification = serde_json::from_value::(notification)?; + let notification = serde_json::from_value::(notification).map_err(JsErrorBox::from_err)?; if notification.method == "Runtime.exceptionThrown" { - let exception_thrown = serde_json::from_value::(notification.params)?; + let exception_thrown = serde_json::from_value::(notification.params).map_err(JsErrorBox::from_err)?; let (message, description) = exception_thrown.exception_details.get_message_and_description(); - break Err(generic_error(format!("{} {}", message, description))); + break Err(JsErrorBox::generic(format!("{} {}", message, description)).into()); } else if notification.method == "Debugger.scriptParsed" { - let params = serde_json::from_value::(notification.params)?; + let params = serde_json::from_value::(notification.params).map_err(JsErrorBox::from_err)?; if params.url.starts_with("file://") { let file_url = Url::parse(¶ms.url).unwrap(); let file_path = file_url.to_file_path().unwrap(); @@ -105,7 +105,7 @@ impl crate::worker::HmrRunner for HmrRunner { } } changed_paths = self.watcher_communicator.watch_for_changed_paths() => { - let changed_paths = changed_paths?; + let changed_paths = changed_paths.map_err(JsErrorBox::from_err)?; let Some(changed_paths) = changed_paths else { let _ = self.watcher_communicator.force_restart(); @@ -187,7 +187,7 @@ impl HmrRunner { } // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs` - async fn enable_debugger(&mut self) -> Result<(), AnyError> { + async fn enable_debugger(&mut self) -> Result<(), CoreError> { self .session .post_message::<()>("Debugger.enable", None) @@ -200,7 +200,7 @@ impl HmrRunner { } // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs` - async fn disable_debugger(&mut self) -> Result<(), AnyError> { + async fn disable_debugger(&mut self) -> Result<(), CoreError> { self .session .post_message::<()>("Debugger.disable", None) @@ -216,7 +216,7 @@ impl HmrRunner { &mut self, script_id: &str, source: &str, - ) -> Result { + ) -> Result { let result = self .session .post_message( @@ -229,15 +229,16 @@ impl HmrRunner { ) .await?; - Ok(serde_json::from_value::( - result, - )?) + Ok( + serde_json::from_value::(result) + .map_err(JsErrorBox::from_err)?, + ) } async fn dispatch_hmr_event( &mut self, script_id: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let expr = format!( "dispatchEvent(new CustomEvent(\"hmr\", {{ detail: {{ path: \"{}\" }} }}));", script_id diff --git a/cli/tools/serve.rs b/cli/tools/serve.rs index 18eac1b3e1..c2c53c1b69 100644 --- a/cli/tools/serve.rs +++ b/cli/tools/serve.rs @@ -73,7 +73,7 @@ async fn do_serve( ) .await?; let worker_count = match worker_count { - None | Some(1) => return worker.run().await, + None | Some(1) => return worker.run().await.map_err(Into::into), Some(c) => c, }; @@ -133,7 +133,7 @@ async fn run_worker( worker.run_for_watcher().await?; Ok(0) } else { - worker.run().await + worker.run().await.map_err(Into::into) } } diff --git a/cli/tools/test/channel.rs b/cli/tools/test/channel.rs index 29f24d65f1..68633b17e6 100644 --- a/cli/tools/test/channel.rs +++ b/cli/tools/test/channel.rs @@ -37,7 +37,8 @@ const HALF_SYNC_MARKER: &[u8; 4] = &[226, 128, 139, 0]; const BUFFER_SIZE: usize = 4096; /// The test channel has been closed and cannot be used to send further messages. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, deno_error::JsError)] +#[class(generic)] pub struct ChannelClosedError; impl std::error::Error for ChannelClosedError {} diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 2bd9cd3d7c..21ee7e152d 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -25,10 +25,9 @@ use deno_cache_dir::file_fetcher::File; use deno_config::glob::FilePatterns; use deno_config::glob::WalkEntry; use deno_core::anyhow; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context as _; -use deno_core::error::generic_error; +use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::error::JsError; use deno_core::futures::future; use deno_core::futures::stream; @@ -49,6 +48,7 @@ use deno_core::v8; use deno_core::ModuleSpecifier; use deno_core::OpState; use deno_core::PollEventLoopOptions; +use deno_error::JsErrorBox; use deno_runtime::deno_io::Stdio; use deno_runtime::deno_io::StdioPipe; use deno_runtime::deno_permissions::Permissions; @@ -106,6 +106,8 @@ use reporters::PrettyTestReporter; use reporters::TapTestReporter; use reporters::TestReporter; +use crate::tools::test::channel::ChannelClosedError; + /// How many times we're allowed to spin the event loop before considering something a leak. const MAX_SANITIZER_LOOP_SPINS: usize = 16; @@ -612,7 +614,7 @@ async fn configure_main_worker( permissions_container: PermissionsContainer, worker_sender: TestEventWorkerSender, options: &TestSpecifierOptions, -) -> Result<(Option>, MainWorker), anyhow::Error> { +) -> Result<(Option>, MainWorker), CoreError> { let mut worker = worker_factory .create_custom_worker( WorkerExecutionMode::Test, @@ -640,21 +642,15 @@ async fn configure_main_worker( let mut worker = worker.into_main_worker(); match res { Ok(()) => Ok(()), - Err(error) => { - // TODO(mmastrac): It would be nice to avoid having this error pattern repeated - if error.is::() { - send_test_event( - &worker.js_runtime.op_state(), - TestEvent::UncaughtError( - specifier.to_string(), - Box::new(error.downcast::().unwrap()), - ), - )?; - Ok(()) - } else { - Err(error) - } + Err(CoreError::Js(err)) => { + send_test_event( + &worker.js_runtime.op_state(), + TestEvent::UncaughtError(specifier.to_string(), Box::new(err)), + ) + .map_err(JsErrorBox::from_err)?; + Ok(()) } + Err(err) => Err(err), }?; Ok((coverage_collector, worker)) } @@ -691,21 +687,14 @@ pub async fn test_specifier( .await { Ok(()) => Ok(()), - Err(error) => { - // TODO(mmastrac): It would be nice to avoid having this error pattern repeated - if error.is::() { - send_test_event( - &worker.js_runtime.op_state(), - TestEvent::UncaughtError( - specifier.to_string(), - Box::new(error.downcast::().unwrap()), - ), - )?; - Ok(()) - } else { - Err(error) - } + Err(CoreError::Js(err)) => { + send_test_event( + &worker.js_runtime.op_state(), + TestEvent::UncaughtError(specifier.to_string(), Box::new(err)), + )?; + Ok(()) } + Err(e) => Err(e.into()), } } @@ -718,7 +707,7 @@ async fn test_specifier_inner( specifier: ModuleSpecifier, fail_fast_tracker: FailFastTracker, options: TestSpecifierOptions, -) -> Result<(), AnyError> { +) -> Result<(), CoreError> { // Ensure that there are no pending exceptions before we start running tests worker.run_up_to_duration(Duration::from_millis(0)).await?; @@ -765,7 +754,7 @@ pub fn worker_has_tests(worker: &mut MainWorker) -> bool { /// Yields to tokio to allow async work to process, and then polls /// the event loop once. #[must_use = "The event loop result should be checked"] -pub async fn poll_event_loop(worker: &mut MainWorker) -> Result<(), AnyError> { +pub async fn poll_event_loop(worker: &mut MainWorker) -> Result<(), CoreError> { // Allow any ops that to do work in the tokio event loop to do so tokio::task::yield_now().await; // Spin the event loop once @@ -784,13 +773,11 @@ pub async fn poll_event_loop(worker: &mut MainWorker) -> Result<(), AnyError> { pub fn send_test_event( op_state: &RefCell, event: TestEvent, -) -> Result<(), AnyError> { - Ok( - op_state - .borrow_mut() - .borrow_mut::() - .send(event)?, - ) +) -> Result<(), ChannelClosedError> { + op_state + .borrow_mut() + .borrow_mut::() + .send(event) } pub async fn run_tests_for_worker( @@ -986,13 +973,10 @@ async fn run_tests_for_worker_inner( let result = match result { Ok(r) => r, Err(error) => { - if error.is::() { + if let CoreError::Js(js_error) = error { send_test_event( &state_rc, - TestEvent::UncaughtError( - specifier.to_string(), - Box::new(error.downcast::().unwrap()), - ), + TestEvent::UncaughtError(specifier.to_string(), Box::new(js_error)), )?; fail_fast_tracker.add_failure(); send_test_event( @@ -1002,7 +986,7 @@ async fn run_tests_for_worker_inner( had_uncaught_error = true; continue; } else { - return Err(error); + return Err(error.into()); } } }; @@ -1374,25 +1358,20 @@ pub async fn report_tests( reporter.report_summary(&elapsed, &tests, &test_steps); if let Err(err) = reporter.flush_report(&elapsed, &tests, &test_steps) { return ( - Err(generic_error(format!( - "Test reporter failed to flush: {}", - err - ))), + Err(anyhow!("Test reporter failed to flush: {}", err)), receiver, ); } if used_only { return ( - Err(generic_error( - "Test failed because the \"only\" option was used", - )), + Err(anyhow!("Test failed because the \"only\" option was used",)), receiver, ); } if failed { - return (Err(generic_error("Test failed")), receiver); + return (Err(anyhow!("Test failed")), receiver); } (Ok(()), receiver) @@ -1575,7 +1554,7 @@ pub async fn run_tests( if !workspace_test_options.permit_no_files && specifiers_with_mode.is_empty() { - return Err(generic_error("No test modules found")); + return Err(anyhow!("No test modules found")); } let doc_tests = get_doc_tests(&specifiers_with_mode, file_fetcher).await?; @@ -1611,10 +1590,10 @@ pub async fn run_tests( TestSpecifiersOptions { cwd: Url::from_directory_path(cli_options.initial_cwd()).map_err( |_| { - generic_error(format!( + anyhow!( "Unable to construct URL from the path of cwd: {}", cli_options.initial_cwd().to_string_lossy(), - )) + ) }, )?, concurrent_jobs: workspace_test_options.concurrent_jobs, @@ -1793,10 +1772,10 @@ pub async fn run_tests_with_watch( TestSpecifiersOptions { cwd: Url::from_directory_path(cli_options.initial_cwd()).map_err( |_| { - generic_error(format!( + anyhow!( "Unable to construct URL from the path of cwd: {}", cli_options.initial_cwd().to_string_lossy(), - )) + ) }, )?, concurrent_jobs: workspace_test_options.concurrent_jobs, diff --git a/cli/tools/test/reporters/compound.rs b/cli/tools/test/reporters/compound.rs index e381dd0977..3c4409ecaa 100644 --- a/cli/tools/test/reporters/compound.rs +++ b/cli/tools/test/reporters/compound.rs @@ -129,7 +129,7 @@ impl TestReporter for CompoundTestReporter { if errors.is_empty() { Ok(()) } else { - bail!( + anyhow::bail!( "error in one or more wrapped reporters:\n{}", errors .iter() diff --git a/cli/tools/test/reporters/junit.rs b/cli/tools/test/reporters/junit.rs index 42ced07600..9418ac9fb2 100644 --- a/cli/tools/test/reporters/junit.rs +++ b/cli/tools/test/reporters/junit.rs @@ -3,6 +3,8 @@ use std::collections::VecDeque; use std::path::PathBuf; +use deno_core::anyhow::Context; + use super::fmt::to_relative_path_or_remote_url; use super::*; diff --git a/cli/tsc/diagnostics.rs b/cli/tsc/diagnostics.rs index ac93c8575d..3780f65e77 100644 --- a/cli/tsc/diagnostics.rs +++ b/cli/tsc/diagnostics.rs @@ -320,7 +320,8 @@ impl fmt::Display for Diagnostic { } } -#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Eq, PartialEq, deno_error::JsError)] +#[class(generic)] pub struct Diagnostics(Vec); impl Diagnostics { diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 3176c50d5c..1473b8a8d9 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -3,12 +3,10 @@ use std::borrow::Cow; use std::collections::HashMap; use std::fmt; -use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use deno_ast::MediaType; -use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; use deno_core::ascii_str; use deno_core::error::AnyError; @@ -454,13 +452,6 @@ impl State { } } -fn normalize_specifier( - specifier: &str, - current_dir: &Path, -) -> Result { - resolve_url_or_path(specifier, current_dir).map_err(|err| err.into()) -} - #[op2] #[string] fn op_create_hash(s: &mut OpState, #[string] text: &str) -> String { @@ -531,6 +522,21 @@ pub fn as_ts_script_kind(media_type: MediaType) -> i32 { pub const MISSING_DEPENDENCY_SPECIFIER: &str = "internal:///missing_dependency.d.ts"; +#[derive(Debug, Error, deno_error::JsError)] +pub enum LoadError { + #[class(generic)] + #[error("Unable to load {path}: {error}")] + LoadFromNodeModule { path: String, error: std::io::Error }, + #[class(inherit)] + #[error( + "Error converting a string module specifier for \"op_resolve\": {0}" + )] + ModuleResolution(#[from] deno_core::ModuleResolutionError), + #[class(inherit)] + #[error("{0}")] + ClosestPkgJson(#[from] node_resolver::errors::ClosestPkgJsonError), +} + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct LoadResponse { @@ -545,24 +551,28 @@ struct LoadResponse { fn op_load( state: &mut OpState, #[string] load_specifier: &str, -) -> Result, AnyError> { +) -> Result, LoadError> { op_load_inner(state, load_specifier) } fn op_load_inner( state: &mut OpState, load_specifier: &str, -) -> Result, AnyError> { +) -> Result, LoadError> { fn load_from_node_modules( specifier: &ModuleSpecifier, npm_state: Option<&RequestNpmState>, media_type: &mut MediaType, is_cjs: &mut bool, - ) -> Result { + ) -> Result { *media_type = MediaType::from_specifier(specifier); let file_path = specifier.to_file_path().unwrap(); - let code = std::fs::read_to_string(&file_path) - .with_context(|| format!("Unable to load {}", file_path.display()))?; + let code = std::fs::read_to_string(&file_path).map_err(|err| { + LoadError::LoadFromNodeModule { + path: file_path.display().to_string(), + error: err, + } + })?; let code: Arc = code.into(); *is_cjs = npm_state .map(|npm_state| { @@ -575,8 +585,7 @@ fn op_load_inner( let state = state.borrow_mut::(); - let specifier = normalize_specifier(load_specifier, &state.current_dir) - .context("Error converting a string module specifier for \"op_load\".")?; + let specifier = resolve_url_or_path(load_specifier, &state.current_dir)?; let mut hash: Option = None; let mut media_type = MediaType::Unknown; @@ -688,6 +697,26 @@ fn op_load_inner( })) } +#[derive(Debug, Error, deno_error::JsError)] +pub enum ResolveError { + #[class(inherit)] + #[error( + "Error converting a string module specifier for \"op_resolve\": {0}" + )] + ModuleResolution(#[from] deno_core::ModuleResolutionError), + #[class(inherit)] + #[error("{0}")] + PackageSubpathResolve(PackageSubpathResolveError), + #[class(inherit)] + #[error("{0}")] + ResolvePkgFolderFromDenoModule( + #[from] crate::npm::ResolvePkgFolderFromDenoModuleError, + ), + #[class(inherit)] + #[error("{0}")] + ResolveNonGraphSpecifierTypes(#[from] ResolveNonGraphSpecifierTypesError), +} + #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ResolveArgs { @@ -717,7 +746,7 @@ fn op_resolve( state: &mut OpState, #[string] base: String, #[serde] specifiers: Vec<(bool, String)>, -) -> Result, AnyError> { +) -> Result, ResolveError> { op_resolve_inner(state, ResolveArgs { base, specifiers }) } @@ -725,7 +754,7 @@ fn op_resolve( fn op_resolve_inner( state: &mut OpState, args: ResolveArgs, -) -> Result, AnyError> { +) -> Result, ResolveError> { let state = state.borrow_mut::(); let mut resolved: Vec<(String, &'static str)> = Vec::with_capacity(args.specifiers.len()); @@ -734,9 +763,7 @@ fn op_resolve_inner( { remapped_specifier.clone() } else { - normalize_specifier(&args.base, &state.current_dir).context( - "Error converting a string module specifier for \"op_resolve\".", - )? + resolve_url_or_path(&args.base, &state.current_dir)? }; let referrer_module = state.graph.get(&referrer); for (is_cjs, specifier) in args.specifiers { @@ -852,7 +879,7 @@ fn resolve_graph_specifier_types( referrer: &ModuleSpecifier, resolution_mode: ResolutionMode, state: &State, -) -> Result, AnyError> { +) -> Result, ResolveError> { let graph = &state.graph; let maybe_module = match graph.try_get(specifier) { Ok(Some(module)) => Some(module), @@ -914,7 +941,7 @@ fn resolve_graph_specifier_types( Err(err) => match err.code() { NodeJsErrorCode::ERR_TYPES_NOT_FOUND | NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None, - _ => return Err(err.into()), + _ => return Err(ResolveError::PackageSubpathResolve(err)), }, }; Ok(Some(into_specifier_and_media_type(maybe_url))) @@ -936,10 +963,12 @@ fn resolve_graph_specifier_types( } } -#[derive(Debug, Error)] -enum ResolveNonGraphSpecifierTypesError { +#[derive(Debug, Error, deno_error::JsError)] +pub enum ResolveNonGraphSpecifierTypesError { + #[class(inherit)] #[error(transparent)] ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError), + #[class(inherit)] #[error(transparent)] PackageSubpathResolve(#[from] PackageSubpathResolveError), } @@ -1036,10 +1065,20 @@ fn op_respond_inner(state: &mut OpState, args: RespondArgs) { state.maybe_response = Some(args); } +#[derive(Debug, Error, deno_error::JsError)] +pub enum ExecError { + #[class(generic)] + #[error("The response for the exec request was not set.")] + ResponseNotSet, + #[class(inherit)] + #[error(transparent)] + Core(deno_core::error::CoreError), +} + /// Execute a request on the supplied snapshot, returning a response which /// contains information, like any emitted files, diagnostics, statistics and /// optionally an updated TypeScript build info. -pub fn exec(request: Request) -> Result { +pub fn exec(request: Request) -> Result { // tsc cannot handle root specifiers that don't have one of the "acceptable" // extensions. Therefore, we have to check the root modules against their // extensions and remap any that are unacceptable to tsc and add them to the @@ -1115,7 +1154,9 @@ pub fn exec(request: Request) -> Result { ..Default::default() }); - runtime.execute_script(located_script_name!(), exec_source)?; + runtime + .execute_script(located_script_name!(), exec_source) + .map_err(ExecError::Core)?; let op_state = runtime.op_state(); let mut op_state = op_state.borrow_mut(); @@ -1132,7 +1173,7 @@ pub fn exec(request: Request) -> Result { stats, }) } else { - Err(anyhow!("The response for the exec request was not set.")) + Err(ExecError::ResponseNotSet) } } @@ -1141,6 +1182,7 @@ mod tests { use deno_core::futures::future; use deno_core::serde_json; use deno_core::OpState; + use deno_error::JsErrorBox; use deno_graph::GraphKind; use deno_graph::ModuleGraph; use test_util::PathRef; @@ -1167,13 +1209,20 @@ mod tests { .replace("://", "_") .replace('/', "-"); let source_path = self.fixtures.join(specifier_text); - let response = source_path.read_to_bytes_if_exists().map(|c| { - Some(deno_graph::source::LoadResponse::Module { - specifier: specifier.clone(), - maybe_headers: None, - content: c.into(), + let response = source_path + .read_to_bytes_if_exists() + .map(|c| { + Some(deno_graph::source::LoadResponse::Module { + specifier: specifier.clone(), + maybe_headers: None, + content: c.into(), + }) }) - }); + .map_err(|e| { + deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::generic( + e.to_string(), + ))) + }); Box::pin(future::ready(response)) } } @@ -1210,7 +1259,7 @@ mod tests { async fn test_exec( specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { let hash_data = 123; // something random let fixtures = test_util::testdata_path().join("tsc2"); let loader = MockLoader { fixtures }; diff --git a/cli/util/file_watcher.rs b/cli/util/file_watcher.rs index ca769b70fe..65963214b9 100644 --- a/cli/util/file_watcher.rs +++ b/cli/util/file_watcher.rs @@ -10,7 +10,7 @@ use std::time::Duration; use deno_config::glob::PathOrPatternSet; use deno_core::error::AnyError; -use deno_core::error::JsError; +use deno_core::error::CoreError; use deno_core::futures::Future; use deno_core::futures::FutureExt; use deno_core::parking_lot::Mutex; @@ -23,6 +23,7 @@ use notify::RecommendedWatcher; use notify::RecursiveMode; use notify::Watcher; use tokio::select; +use tokio::sync::broadcast::error::RecvError; use tokio::sync::mpsc; use tokio::sync::mpsc::UnboundedReceiver; use tokio::time::sleep; @@ -80,10 +81,13 @@ where { let result = watch_future.await; if let Err(err) = result { - let error_string = match err.downcast_ref::() { - Some(e) => format_js_error(e), - None => format!("{err:?}"), - }; + let error_string = + match crate::util::result::any_and_jserrorbox_downcast_ref::( + &err, + ) { + Some(CoreError::Js(e)) => format_js_error(e), + _ => format!("{err:?}"), + }; log::error!( "{}: {}", colors::red_bold("error"), @@ -171,9 +175,9 @@ impl WatcherCommunicator { pub async fn watch_for_changed_paths( &self, - ) -> Result>, AnyError> { + ) -> Result>, RecvError> { let mut rx = self.changed_paths_rx.resubscribe(); - rx.recv().await.map_err(AnyError::from) + rx.recv().await } pub fn change_restart_mode(&self, restart_mode: WatcherRestartMode) { diff --git a/cli/util/fs.rs b/cli/util/fs.rs index 61f1786ee9..d9cebe10d5 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -13,7 +13,6 @@ use deno_config::glob::PathOrPattern; use deno_config::glob::PathOrPatternSet; use deno_config::glob::WalkEntry; use deno_core::anyhow::anyhow; -use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::unsync::spawn_blocking; use deno_core::ModuleSpecifier; @@ -129,7 +128,7 @@ pub fn collect_specifiers( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(vendor_folder) - .collect_file_patterns(&CliSys::default(), files)?; + .collect_file_patterns(&CliSys::default(), files); let mut collected_files_as_urls = collected_files .iter() .map(|f| specifier_from_file_path(f).unwrap()) @@ -169,7 +168,7 @@ pub fn clone_dir_recursive< sys: &TSys, from: &Path, to: &Path, -) -> Result<(), AnyError> { +) -> Result<(), CopyDirRecursiveError> { if cfg!(target_vendor = "apple") { if let Some(parent) = to.parent() { sys.fs_create_dir_all(parent)?; @@ -200,6 +199,47 @@ pub fn clone_dir_recursive< Ok(()) } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum CopyDirRecursiveError { + #[class(inherit)] + #[error("Creating {path}")] + Creating { + path: PathBuf, + #[source] + #[inherit] + source: Error, + }, + #[class(inherit)] + #[error("Creating {path}")] + Reading { + path: PathBuf, + #[source] + #[inherit] + source: Error, + }, + #[class(inherit)] + #[error("Dir {from} to {to}")] + Dir { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: Box, + }, + #[class(inherit)] + #[error("Copying {from} to {to}")] + Copying { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: Error, + }, + #[class(inherit)] + #[error(transparent)] + Other(#[from] Error), +} + /// Copies a directory to another directory. /// /// Note: Does not handle symlinks. @@ -213,13 +253,20 @@ pub fn copy_dir_recursive< sys: &TSys, from: &Path, to: &Path, -) -> Result<(), AnyError> { - sys - .fs_create_dir_all(to) - .with_context(|| format!("Creating {}", to.display()))?; - let read_dir = sys - .fs_read_dir(from) - .with_context(|| format!("Reading {}", from.display()))?; +) -> Result<(), CopyDirRecursiveError> { + sys.fs_create_dir_all(to).map_err(|source| { + CopyDirRecursiveError::Creating { + path: to.to_path_buf(), + source, + } + })?; + let read_dir = + sys + .fs_read_dir(from) + .map_err(|source| CopyDirRecursiveError::Reading { + path: from.to_path_buf(), + source, + })?; for entry in read_dir { let entry = entry?; @@ -228,12 +275,20 @@ pub fn copy_dir_recursive< let new_to = to.join(entry.file_name()); if file_type.is_dir() { - copy_dir_recursive(sys, &new_from, &new_to).with_context(|| { - format!("Dir {} to {}", new_from.display(), new_to.display()) + copy_dir_recursive(sys, &new_from, &new_to).map_err(|source| { + CopyDirRecursiveError::Dir { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: Box::new(source), + } })?; } else if file_type.is_file() { - sys.fs_copy(&new_from, &new_to).with_context(|| { - format!("Copying {} to {}", new_from.display(), new_to.display()) + sys.fs_copy(&new_from, &new_to).map_err(|source| { + CopyDirRecursiveError::Copying { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source, + } })?; } } diff --git a/cli/util/result.rs b/cli/util/result.rs index 6a67416f26..0c1a75b1ce 100644 --- a/cli/util/result.rs +++ b/cli/util/result.rs @@ -1,6 +1,13 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::convert::Infallible; +use std::fmt::Debug; +use std::fmt::Display; + +use deno_core::error::AnyError; +use deno_core::error::CoreError; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; pub trait InfallibleResultExt { fn unwrap_infallible(self) -> T; @@ -14,3 +21,23 @@ impl InfallibleResultExt for Result { } } } + +pub fn any_and_jserrorbox_downcast_ref< + E: Display + Debug + Send + Sync + 'static, +>( + err: &AnyError, +) -> Option<&E> { + err + .downcast_ref::() + .or_else(|| { + err + .downcast_ref::() + .and_then(|e| e.as_any().downcast_ref::()) + }) + .or_else(|| { + err.downcast_ref::().and_then(|e| match e { + CoreError::JsNative(e) => e.as_any().downcast_ref::(), + _ => None, + }) + }) +} diff --git a/cli/worker.rs b/cli/worker.rs index 6289d00cf8..eee89d663c 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -8,6 +8,7 @@ use std::sync::Arc; use deno_ast::ModuleSpecifier; use deno_core::anyhow::bail; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::futures::FutureExt; use deno_core::url::Url; use deno_core::v8; @@ -17,6 +18,7 @@ use deno_core::FeatureChecker; use deno_core::ModuleLoader; use deno_core::PollEventLoopOptions; use deno_core::SharedArrayBufferStore; +use deno_error::JsErrorBox; use deno_runtime::code_cache; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_fs; @@ -50,7 +52,6 @@ use crate::args::CliLockfile; use crate::args::DenoSubcommand; use crate::args::NpmCachingStrategy; use crate::args::StorageKeyResolver; -use crate::errors; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; use crate::npm::CliNpmResolver; @@ -80,9 +81,9 @@ pub trait ModuleLoaderFactory: Send + Sync { #[async_trait::async_trait(?Send)] pub trait HmrRunner: Send + Sync { - async fn start(&mut self) -> Result<(), AnyError>; - async fn stop(&mut self) -> Result<(), AnyError>; - async fn run(&mut self) -> Result<(), AnyError>; + async fn start(&mut self) -> Result<(), CoreError>; + async fn stop(&mut self) -> Result<(), CoreError>; + async fn run(&mut self) -> Result<(), CoreError>; } pub trait CliCodeCache: code_cache::CodeCache { @@ -195,7 +196,7 @@ impl CliMainWorker { Ok(()) } - pub async fn run(&mut self) -> Result { + pub async fn run(&mut self) -> Result { let mut maybe_coverage_collector = self.maybe_setup_coverage_collector().await?; let mut maybe_hmr_runner = self.maybe_setup_hmr_runner().await?; @@ -216,7 +217,7 @@ impl CliMainWorker { let result; select! { hmr_result = hmr_future => { - result = hmr_result; + result = hmr_result.map_err(Into::into); }, event_loop_result = event_loop_future => { result = event_loop_result; @@ -331,12 +332,12 @@ impl CliMainWorker { executor.execute().await } - pub async fn execute_main_module(&mut self) -> Result<(), AnyError> { + pub async fn execute_main_module(&mut self) -> Result<(), CoreError> { let id = self.worker.preload_main_module(&self.main_module).await?; self.worker.evaluate_module(id).await } - pub async fn execute_side_module(&mut self) -> Result<(), AnyError> { + pub async fn execute_side_module(&mut self) -> Result<(), CoreError> { let id = self.worker.preload_side_module(&self.main_module).await?; self.worker.evaluate_module(id).await } @@ -393,7 +394,7 @@ impl CliMainWorker { &mut self, name: &'static str, source_code: &'static str, - ) -> Result, AnyError> { + ) -> Result, CoreError> { self.worker.js_runtime.execute_script(name, source_code) } } @@ -465,7 +466,7 @@ impl CliMainWorkerFactory { &self, mode: WorkerExecutionMode, main_module: ModuleSpecifier, - ) -> Result { + ) -> Result { self .create_custom_worker( mode, @@ -484,7 +485,7 @@ impl CliMainWorkerFactory { permissions: PermissionsContainer, custom_extensions: Vec, stdio: deno_runtime::deno_io::Stdio, - ) -> Result { + ) -> Result { let shared = &self.shared; let CreateModuleLoaderResult { module_loader, @@ -513,16 +514,15 @@ impl CliMainWorkerFactory { } // use a fake referrer that can be used to discover the package.json if necessary - let referrer = - ModuleSpecifier::from_directory_path(self.shared.fs.cwd()?) - .unwrap() - .join("package.json")?; + let referrer = ModuleSpecifier::from_directory_path( + self.shared.fs.cwd().map_err(JsErrorBox::from_err)?, + ) + .unwrap() + .join("package.json")?; let package_folder = shared .npm_resolver - .resolve_pkg_folder_from_deno_module_req( - package_ref.req(), - &referrer, - )?; + .resolve_pkg_folder_from_deno_module_req(package_ref.req(), &referrer) + .map_err(JsErrorBox::from_err)?; let main_module = self .resolve_binary_entrypoint(&package_folder, package_ref.sub_path())?; @@ -633,7 +633,6 @@ impl CliMainWorkerFactory { should_break_on_first_statement: shared.options.inspect_brk, should_wait_for_inspector_session: shared.options.inspect_wait, strace_ops: shared.options.strace_ops.clone(), - get_error_class_fn: Some(&errors::get_error_class_name), cache_storage_dir, origin_storage_dir, stdio, @@ -834,7 +833,6 @@ fn create_web_worker_callback( create_web_worker_cb, format_js_error_fn: Some(Arc::new(format_js_error)), worker_type: args.worker_type, - get_error_class_fn: Some(&errors::get_error_class_name), stdio: stdio.clone(), cache_storage_dir, strace_ops: shared.options.strace_ops.clone(), diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index 48b4127b0e..27aa7df702 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] async-trait.workspace = true deno_core.workspace = true +deno_error.workspace = true thiserror.workspace = true tokio.workspace = true uuid.workspace = true diff --git a/ext/broadcast_channel/lib.rs b/ext/broadcast_channel/lib.rs index 4929153bfe..ae709c674c 100644 --- a/ext/broadcast_channel/lib.rs +++ b/ext/broadcast_channel/lib.rs @@ -12,6 +12,7 @@ use deno_core::JsBuffer; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; pub use in_memory_broadcast_channel::InMemoryBroadcastChannel; pub use in_memory_broadcast_channel::InMemoryBroadcastChannelResource; use tokio::sync::broadcast::error::SendError as BroadcastSendError; @@ -19,18 +20,26 @@ use tokio::sync::mpsc::error::SendError as MpscSendError; pub const UNSTABLE_FEATURE_NAME: &str = "broadcast-channel"; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BroadcastChannelError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(generic)] #[error(transparent)] MPSCSendError(MpscSendError>), + #[class(generic)] #[error(transparent)] BroadcastSendError( BroadcastSendError>, ), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[inherit] JsErrorBox), } impl From> @@ -100,10 +109,7 @@ pub fn op_broadcast_unsubscribe( where BC: BroadcastChannel + 'static, { - let resource = state - .resource_table - .get::(rid) - .map_err(BroadcastChannelError::Resource)?; + let resource = state.resource_table.get::(rid)?; let bc = state.borrow::(); bc.unsubscribe(&resource) } @@ -118,11 +124,7 @@ pub async fn op_broadcast_send( where BC: BroadcastChannel + 'static, { - let resource = state - .borrow() - .resource_table - .get::(rid) - .map_err(BroadcastChannelError::Resource)?; + let resource = state.borrow().resource_table.get::(rid)?; let bc = state.borrow().borrow::().clone(); bc.send(&resource, name, buf.to_vec()).await } @@ -136,11 +138,7 @@ pub async fn op_broadcast_recv( where BC: BroadcastChannel + 'static, { - let resource = state - .borrow() - .resource_table - .get::(rid) - .map_err(BroadcastChannelError::Resource)?; + let resource = state.borrow().resource_table.get::(rid)?; let bc = state.borrow().borrow::().clone(); bc.recv(&resource).await } diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index cb4eef87df..b77630832f 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] async-trait.workspace = true deno_core.workspace = true +deno_error.workspace = true rusqlite.workspace = true serde.workspace = true sha2.workspace = true diff --git a/ext/cache/lib.rs b/ext/cache/lib.rs index 6ee7380cf0..d3bfe23def 100644 --- a/ext/cache/lib.rs +++ b/ext/cache/lib.rs @@ -6,7 +6,6 @@ use std::rc::Rc; use std::sync::Arc; use async_trait::async_trait; -use deno_core::error::type_error; use deno_core::op2; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; @@ -14,22 +13,38 @@ use deno_core::ByteString; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; mod sqlite; pub use sqlite::SqliteBackedCache; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CacheError { + #[class(type)] + #[error("CacheStorage is not available in this context")] + ContextUnsupported, + #[class(generic)] #[error(transparent)] Sqlite(#[from] rusqlite::Error), + #[class(generic)] #[error(transparent)] JoinError(#[from] tokio::task::JoinError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(JsErrorBox), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), + #[class(generic)] + #[error("Failed to create cache storage directory {}", .dir.display())] + CacheStorageDirectory { + dir: PathBuf, + #[source] + source: std::io::Error, + }, } #[derive(Clone)] @@ -237,9 +252,7 @@ where state.put(cache); Ok(state.borrow::().clone()) } else { - Err(CacheError::Other(type_error( - "CacheStorage is not available in this context", - ))) + Err(CacheError::ContextUnsupported) } } diff --git a/ext/cache/sqlite.rs b/ext/cache/sqlite.rs index 8bd73b4799..6587a52bac 100644 --- a/ext/cache/sqlite.rs +++ b/ext/cache/sqlite.rs @@ -8,8 +8,6 @@ use std::time::SystemTime; use std::time::UNIX_EPOCH; use async_trait::async_trait; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_core::futures::future::poll_fn; use deno_core::parking_lot::Mutex; use deno_core::unsync::spawn_blocking; @@ -45,14 +43,12 @@ pub struct SqliteBackedCache { impl SqliteBackedCache { pub fn new(cache_storage_dir: PathBuf) -> Result { { - std::fs::create_dir_all(&cache_storage_dir) - .with_context(|| { - format!( - "Failed to create cache storage directory {}", - cache_storage_dir.display() - ) - }) - .map_err(CacheError::Other)?; + std::fs::create_dir_all(&cache_storage_dir).map_err(|source| { + CacheError::CacheStorageDirectory { + dir: cache_storage_dir.clone(), + source, + } + })?; let path = cache_storage_dir.join("cache_metadata.db"); let connection = rusqlite::Connection::open(&path).unwrap_or_else(|_| { panic!("failed to open cache db at {}", path.display()) @@ -385,7 +381,10 @@ impl CacheResponseResource { } } - async fn read(self: Rc, data: &mut [u8]) -> Result { + async fn read( + self: Rc, + data: &mut [u8], + ) -> Result { let resource = deno_core::RcRef::map(&self, |r| &r.file); let mut file = resource.borrow_mut().await; let nread = file.read(data).await?; diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index 0bc3dcb8fb..11c6feabb8 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_webgpu.workspace = true image = { version = "0.24.7", default-features = false, features = ["png"] } serde = { workspace = true, features = ["derive"] } diff --git a/ext/canvas/lib.rs b/ext/canvas/lib.rs index 533b8c3fb3..91b4e44afe 100644 --- a/ext/canvas/lib.rs +++ b/ext/canvas/lib.rs @@ -12,10 +12,12 @@ use image::RgbaImage; use serde::Deserialize; use serde::Serialize; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CanvasError { + #[class(type)] #[error("Color type '{0:?}' not supported")] UnsupportedColorType(ColorType), + #[class(generic)] #[error(transparent)] Image(#[from] image::ImageError), } diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index 821a2aa884..224508265a 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -18,6 +18,7 @@ anyhow.workspace = true async-trait.workspace = true chrono = { workspace = true, features = ["now"] } deno_core.workspace = true +deno_error.workspace = true saffron.workspace = true thiserror.workspace = true tokio.workspace = true diff --git a/ext/cron/lib.rs b/ext/cron/lib.rs index 2c8d534d54..b4f4938b5e 100644 --- a/ext/cron/lib.rs +++ b/ext/cron/lib.rs @@ -7,11 +7,12 @@ use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; -use deno_core::error::get_custom_error_class; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; pub use crate::interface::*; @@ -47,26 +48,35 @@ impl Resource for CronResource { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CronError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(type)] #[error("Cron name cannot exceed 64 characters: current length {0}")] NameExceeded(usize), + #[class(type)] #[error("Invalid cron name: only alphanumeric characters, whitespace, hyphens, and underscores are allowed")] NameInvalid, + #[class(type)] #[error("Cron with this name already exists")] AlreadyExists, + #[class(type)] #[error("Too many crons")] TooManyCrons, + #[class(type)] #[error("Invalid cron schedule")] InvalidCron, + #[class(type)] #[error("Invalid backoff schedule")] InvalidBackoff, + #[class(generic)] #[error(transparent)] AcquireError(#[from] tokio::sync::AcquireError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(JsErrorBox), } #[op2] @@ -119,7 +129,7 @@ where let resource = match state.resource_table.get::>(rid) { Ok(resource) => resource, Err(err) => { - if get_custom_error_class(&err) == Some("BadResource") { + if err.get_class() == "BadResource" { return Ok(false); } else { return Err(CronError::Resource(err)); diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 8f7fea28d7..96ddd1621f 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -23,6 +23,7 @@ const-oid = "0.9.0" ctr = "0.9.1" curve25519-dalek = "4.1.3" deno_core.workspace = true +deno_error.workspace = true deno_web.workspace = true ed448-goldilocks = { version = "0.8.3", features = ["zeroize"] } elliptic-curve = { version = "0.13.1", features = ["std", "pem"] } diff --git a/ext/crypto/decrypt.rs b/ext/crypto/decrypt.rs index 8a00ffd8cd..766f62d16f 100644 --- a/ext/crypto/decrypt.rs +++ b/ext/crypto/decrypt.rs @@ -70,26 +70,40 @@ pub enum DecryptAlgorithm { }, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum DecryptError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), + #[class(generic)] #[error(transparent)] Pkcs1(#[from] rsa::pkcs1::Error), + #[class("DOMExceptionOperationError")] #[error("Decryption failed")] Failed, + #[class(type)] #[error("invalid length")] InvalidLength, + #[class(type)] #[error("invalid counter length. Currently supported 32/64/128 bits")] InvalidCounterLength, + #[class(type)] #[error("tag length not equal to 128")] InvalidTagLength, + #[class("DOMExceptionOperationError")] #[error("invalid key or iv")] InvalidKeyOrIv, + #[class("DOMExceptionOperationError")] #[error("tried to decrypt too much data")] TooMuchData, + #[class(type)] #[error("iv length not equal to 12 or 16")] InvalidIvLength, + #[class("DOMExceptionOperationError")] #[error("{0}")] Rsa(rsa::Error), } diff --git a/ext/crypto/ed25519.rs b/ext/crypto/ed25519.rs index d64b6904dd..c56fdc7c62 100644 --- a/ext/crypto/ed25519.rs +++ b/ext/crypto/ed25519.rs @@ -13,12 +13,15 @@ use spki::der::asn1::BitString; use spki::der::Decode; use spki::der::Encode; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum Ed25519Error { + #[class("DOMExceptionOperationError")] #[error("Failed to export key")] FailedExport, + #[class(generic)] #[error(transparent)] Der(#[from] rsa::pkcs1::der::Error), + #[class(generic)] #[error(transparent)] KeyRejected(#[from] ring::error::KeyRejected), } diff --git a/ext/crypto/encrypt.rs b/ext/crypto/encrypt.rs index f3464b5032..d94eb97cfd 100644 --- a/ext/crypto/encrypt.rs +++ b/ext/crypto/encrypt.rs @@ -71,20 +71,31 @@ pub enum EncryptAlgorithm { }, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum EncryptError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), + #[class(type)] #[error("invalid length")] InvalidLength, + #[class("DOMExceptionOperationError")] #[error("invalid key or iv")] InvalidKeyOrIv, + #[class(type)] #[error("iv length not equal to 12 or 16")] InvalidIvLength, + #[class(type)] #[error("invalid counter length. Currently supported 32/64/128 bits")] InvalidCounterLength, + #[class("DOMExceptionOperationError")] #[error("tried to encrypt too much data")] TooMuchData, + #[class("DOMExceptionOperationError")] #[error("Encryption failed")] Failed, } diff --git a/ext/crypto/export_key.rs b/ext/crypto/export_key.rs index c4e41ef2da..c7d59e3cc5 100644 --- a/ext/crypto/export_key.rs +++ b/ext/crypto/export_key.rs @@ -20,12 +20,19 @@ use spki::AlgorithmIdentifierOwned; use crate::shared::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ExportKeyError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), + #[class(generic)] #[error(transparent)] Der(#[from] spki::der::Error), + #[class("DOMExceptionNotSupportedError")] #[error("Unsupported named curve")] UnsupportedNamedCurve, } diff --git a/ext/crypto/generate_key.rs b/ext/crypto/generate_key.rs index 953e2f1df1..211084af17 100644 --- a/ext/crypto/generate_key.rs +++ b/ext/crypto/generate_key.rs @@ -15,10 +15,16 @@ use serde::Deserialize; use crate::shared::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class("DOMExceptionOperationError")] pub enum GenerateKeyError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), #[error("Bad public exponent")] BadPublicExponent, #[error("Invalid HMAC key length")] diff --git a/ext/crypto/import_key.rs b/ext/crypto/import_key.rs index 4011f2536b..e9059bbdc6 100644 --- a/ext/crypto/import_key.rs +++ b/ext/crypto/import_key.rs @@ -14,10 +14,16 @@ use spki::der::Decode; use crate::shared::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class("DOMExceptionDataError")] pub enum ImportKeyError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), #[error("invalid modulus")] InvalidModulus, #[error("invalid public exponent")] diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index f468af5b07..0d6eecb911 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -8,12 +8,12 @@ use aes_kw::KekAes192; use aes_kw::KekAes256; use base64::prelude::BASE64_URL_SAFE_NO_PAD; use base64::Engine; -use deno_core::error::not_supported; use deno_core::op2; use deno_core::unsync::spawn_blocking; use deno_core::JsBuffer; use deno_core::OpState; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use p256::elliptic_curve::sec1::FromEncodedPoint; use p256::pkcs8::DecodePrivateKey; pub use rand; @@ -129,63 +129,99 @@ deno_core::extension!(deno_crypto, }, ); -#[derive(Debug, thiserror::Error)] -pub enum Error { +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum CryptoError { + #[class(inherit)] #[error(transparent)] - General(#[from] SharedError), + General( + #[from] + #[inherit] + SharedError, + ), + #[class(inherit)] #[error(transparent)] - JoinError(#[from] tokio::task::JoinError), + JoinError( + #[from] + #[inherit] + tokio::task::JoinError, + ), + #[class(generic)] #[error(transparent)] Der(#[from] rsa::pkcs1::der::Error), + #[class(type)] #[error("Missing argument hash")] MissingArgumentHash, + #[class(type)] #[error("Missing argument saltLength")] MissingArgumentSaltLength, + #[class(type)] #[error("unsupported algorithm")] UnsupportedAlgorithm, + #[class(generic)] #[error(transparent)] KeyRejected(#[from] ring::error::KeyRejected), + #[class(generic)] #[error(transparent)] RSA(#[from] rsa::Error), + #[class(generic)] #[error(transparent)] Pkcs1(#[from] rsa::pkcs1::Error), + #[class(generic)] #[error(transparent)] Unspecified(#[from] ring::error::Unspecified), + #[class(type)] #[error("Invalid key format")] InvalidKeyFormat, + #[class(generic)] #[error(transparent)] P256Ecdsa(#[from] p256::ecdsa::Error), + #[class(type)] #[error("Unexpected error decoding private key")] DecodePrivateKey, + #[class(type)] #[error("Missing argument publicKey")] MissingArgumentPublicKey, + #[class(type)] #[error("Missing argument namedCurve")] MissingArgumentNamedCurve, + #[class(type)] #[error("Missing argument info")] MissingArgumentInfo, + #[class("DOMExceptionOperationError")] #[error("The length provided for HKDF is too large")] HKDFLengthTooLarge, + #[class(generic)] #[error(transparent)] Base64Decode(#[from] base64::DecodeError), + #[class(type)] #[error("Data must be multiple of 8 bytes")] DataInvalidSize, + #[class(type)] #[error("Invalid key length")] InvalidKeyLength, + #[class("DOMExceptionOperationError")] #[error("encryption error")] EncryptionError, + #[class("DOMExceptionOperationError")] #[error("decryption error - integrity check failed")] DecryptionError, + #[class("DOMExceptionQuotaExceededError")] #[error("The ArrayBufferView's byte length ({0}) exceeds the number of bytes of entropy available via this API (65536)")] ArrayBufferViewLengthExceeded(usize), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other( + #[from] + #[inherit] + JsErrorBox, + ), } #[op2] #[serde] pub fn op_crypto_base64url_decode( #[string] data: String, -) -> Result { +) -> Result { let data: Vec = BASE64_URL_SAFE_NO_PAD.decode(data)?; Ok(data.into()) } @@ -201,9 +237,9 @@ pub fn op_crypto_base64url_encode(#[buffer] data: JsBuffer) -> String { pub fn op_crypto_get_random_values( state: &mut OpState, #[buffer] out: &mut [u8], -) -> Result<(), Error> { +) -> Result<(), CryptoError> { if out.len() > 65536 { - return Err(Error::ArrayBufferViewLengthExceeded(out.len())); + return Err(CryptoError::ArrayBufferViewLengthExceeded(out.len())); } let maybe_seeded_rng = state.try_borrow_mut::(); @@ -255,7 +291,7 @@ pub struct SignArg { pub async fn op_crypto_sign_key( #[serde] args: SignArg, #[buffer] zero_copy: JsBuffer, -) -> Result { +) -> Result { deno_core::unsync::spawn_blocking(move || { let data = &*zero_copy; let algorithm = args.algorithm; @@ -264,7 +300,7 @@ pub async fn op_crypto_sign_key( Algorithm::RsassaPkcs1v15 => { use rsa::pkcs1v15::SigningKey; let private_key = RsaPrivateKey::from_pkcs1_der(&args.key.data)?; - match args.hash.ok_or_else(|| Error::MissingArgumentHash)? { + match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let signing_key = SigningKey::::new(private_key); signing_key.sign(data) @@ -289,11 +325,11 @@ pub async fn op_crypto_sign_key( let salt_len = args .salt_length - .ok_or_else(|| Error::MissingArgumentSaltLength)? + .ok_or_else(|| CryptoError::MissingArgumentSaltLength)? as usize; let mut rng = OsRng; - match args.hash.ok_or_else(|| Error::MissingArgumentHash)? { + match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let signing_key = Pss::new_with_salt::(salt_len); let hashed = Sha1::digest(data); @@ -320,7 +356,7 @@ pub async fn op_crypto_sign_key( Algorithm::Ecdsa => { let curve: &EcdsaSigningAlgorithm = args .named_curve - .ok_or_else(|| Error::Other(not_supported()))? + .ok_or_else(JsErrorBox::not_supported)? .into(); let rng = RingRand::SystemRandom::new(); @@ -330,7 +366,7 @@ pub async fn op_crypto_sign_key( if let Some(hash) = args.hash { match hash { CryptoHash::Sha256 | CryptoHash::Sha384 => (), - _ => return Err(Error::UnsupportedAlgorithm), + _ => return Err(CryptoError::UnsupportedAlgorithm), } }; @@ -340,17 +376,15 @@ pub async fn op_crypto_sign_key( signature.as_ref().to_vec() } Algorithm::Hmac => { - let hash: HmacAlgorithm = args - .hash - .ok_or_else(|| Error::Other(not_supported()))? - .into(); + let hash: HmacAlgorithm = + args.hash.ok_or_else(JsErrorBox::not_supported)?.into(); let key = HmacKey::new(hash, &args.key.data); let signature = ring::hmac::sign(&key, data); signature.as_ref().to_vec() } - _ => return Err(Error::UnsupportedAlgorithm), + _ => return Err(CryptoError::UnsupportedAlgorithm), }; Ok(signature.into()) @@ -373,7 +407,7 @@ pub struct VerifyArg { pub async fn op_crypto_verify_key( #[serde] args: VerifyArg, #[buffer] zero_copy: JsBuffer, -) -> Result { +) -> Result { deno_core::unsync::spawn_blocking(move || { let data = &*zero_copy; let algorithm = args.algorithm; @@ -384,7 +418,7 @@ pub async fn op_crypto_verify_key( use rsa::pkcs1v15::VerifyingKey; let public_key = read_rsa_public_key(args.key)?; let signature: Signature = args.signature.as_ref().try_into()?; - match args.hash.ok_or_else(|| Error::MissingArgumentHash)? { + match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let verifying_key = VerifyingKey::::new(public_key); verifying_key.verify(data, &signature).is_ok() @@ -409,10 +443,10 @@ pub async fn op_crypto_verify_key( let salt_len = args .salt_length - .ok_or_else(|| Error::MissingArgumentSaltLength)? + .ok_or_else(|| CryptoError::MissingArgumentSaltLength)? as usize; - match args.hash.ok_or_else(|| Error::MissingArgumentHash)? { + match args.hash.ok_or_else(|| CryptoError::MissingArgumentHash)? { CryptoHash::Sha1 => { let pss = Pss::new_with_salt::(salt_len); let hashed = Sha1::digest(data); @@ -436,21 +470,19 @@ pub async fn op_crypto_verify_key( } } Algorithm::Hmac => { - let hash: HmacAlgorithm = args - .hash - .ok_or_else(|| Error::Other(not_supported()))? - .into(); + let hash: HmacAlgorithm = + args.hash.ok_or_else(JsErrorBox::not_supported)?.into(); let key = HmacKey::new(hash, &args.key.data); ring::hmac::verify(&key, data, &args.signature).is_ok() } Algorithm::Ecdsa => { let signing_alg: &EcdsaSigningAlgorithm = args .named_curve - .ok_or_else(|| Error::Other(not_supported()))? + .ok_or_else(JsErrorBox::not_supported)? .into(); let verify_alg: &EcdsaVerificationAlgorithm = args .named_curve - .ok_or_else(|| Error::Other(not_supported()))? + .ok_or_else(JsErrorBox::not_supported)? .into(); let private_key; @@ -464,7 +496,7 @@ pub async fn op_crypto_verify_key( private_key.public_key().as_ref() } KeyType::Public => &*args.key.data, - _ => return Err(Error::InvalidKeyFormat), + _ => return Err(CryptoError::InvalidKeyFormat), }; let public_key = @@ -472,7 +504,7 @@ pub async fn op_crypto_verify_key( public_key.verify(data, &args.signature).is_ok() } - _ => return Err(Error::UnsupportedAlgorithm), + _ => return Err(CryptoError::UnsupportedAlgorithm), }; Ok(verification) @@ -500,31 +532,27 @@ pub struct DeriveKeyArg { pub async fn op_crypto_derive_bits( #[serde] args: DeriveKeyArg, #[buffer] zero_copy: Option, -) -> Result { +) -> Result { deno_core::unsync::spawn_blocking(move || { let algorithm = args.algorithm; match algorithm { Algorithm::Pbkdf2 => { - let zero_copy = - zero_copy.ok_or_else(|| Error::Other(not_supported()))?; + let zero_copy = zero_copy.ok_or_else(JsErrorBox::not_supported)?; let salt = &*zero_copy; // The caller must validate these cases. assert!(args.length > 0); assert!(args.length % 8 == 0); - let algorithm = - match args.hash.ok_or_else(|| Error::Other(not_supported()))? { - CryptoHash::Sha1 => pbkdf2::PBKDF2_HMAC_SHA1, - CryptoHash::Sha256 => pbkdf2::PBKDF2_HMAC_SHA256, - CryptoHash::Sha384 => pbkdf2::PBKDF2_HMAC_SHA384, - CryptoHash::Sha512 => pbkdf2::PBKDF2_HMAC_SHA512, - }; + let algorithm = match args.hash.ok_or_else(JsErrorBox::not_supported)? { + CryptoHash::Sha1 => pbkdf2::PBKDF2_HMAC_SHA1, + CryptoHash::Sha256 => pbkdf2::PBKDF2_HMAC_SHA256, + CryptoHash::Sha384 => pbkdf2::PBKDF2_HMAC_SHA384, + CryptoHash::Sha512 => pbkdf2::PBKDF2_HMAC_SHA512, + }; // This will never panic. We have already checked length earlier. let iterations = NonZeroU32::new( - args - .iterations - .ok_or_else(|| Error::Other(not_supported()))?, + args.iterations.ok_or_else(JsErrorBox::not_supported)?, ) .unwrap(); let secret = args.key.data; @@ -535,33 +563,33 @@ pub async fn op_crypto_derive_bits( Algorithm::Ecdh => { let named_curve = args .named_curve - .ok_or_else(|| Error::MissingArgumentNamedCurve)?; + .ok_or_else(|| CryptoError::MissingArgumentNamedCurve)?; let public_key = args .public_key - .ok_or_else(|| Error::MissingArgumentPublicKey)?; + .ok_or_else(|| CryptoError::MissingArgumentPublicKey)?; match named_curve { CryptoNamedCurve::P256 => { let secret_key = p256::SecretKey::from_pkcs8_der(&args.key.data) - .map_err(|_| Error::DecodePrivateKey)?; + .map_err(|_| CryptoError::DecodePrivateKey)?; let public_key = match public_key.r#type { KeyType::Private => { p256::SecretKey::from_pkcs8_der(&public_key.data) - .map_err(|_| Error::DecodePrivateKey)? + .map_err(|_| CryptoError::DecodePrivateKey)? .public_key() } KeyType::Public => { let point = p256::EncodedPoint::from_bytes(public_key.data) - .map_err(|_| Error::DecodePrivateKey)?; + .map_err(|_| CryptoError::DecodePrivateKey)?; let pk = p256::PublicKey::from_encoded_point(&point); // pk is a constant time Option. if pk.is_some().into() { pk.unwrap() } else { - return Err(Error::DecodePrivateKey); + return Err(CryptoError::DecodePrivateKey); } } _ => unreachable!(), @@ -577,24 +605,24 @@ pub async fn op_crypto_derive_bits( } CryptoNamedCurve::P384 => { let secret_key = p384::SecretKey::from_pkcs8_der(&args.key.data) - .map_err(|_| Error::DecodePrivateKey)?; + .map_err(|_| CryptoError::DecodePrivateKey)?; let public_key = match public_key.r#type { KeyType::Private => { p384::SecretKey::from_pkcs8_der(&public_key.data) - .map_err(|_| Error::DecodePrivateKey)? + .map_err(|_| CryptoError::DecodePrivateKey)? .public_key() } KeyType::Public => { let point = p384::EncodedPoint::from_bytes(public_key.data) - .map_err(|_| Error::DecodePrivateKey)?; + .map_err(|_| CryptoError::DecodePrivateKey)?; let pk = p384::PublicKey::from_encoded_point(&point); // pk is a constant time Option. if pk.is_some().into() { pk.unwrap() } else { - return Err(Error::DecodePrivateKey); + return Err(CryptoError::DecodePrivateKey); } } _ => unreachable!(), @@ -611,18 +639,16 @@ pub async fn op_crypto_derive_bits( } } Algorithm::Hkdf => { - let zero_copy = - zero_copy.ok_or_else(|| Error::Other(not_supported()))?; + let zero_copy = zero_copy.ok_or_else(JsErrorBox::not_supported)?; let salt = &*zero_copy; - let algorithm = - match args.hash.ok_or_else(|| Error::Other(not_supported()))? { - CryptoHash::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY, - CryptoHash::Sha256 => hkdf::HKDF_SHA256, - CryptoHash::Sha384 => hkdf::HKDF_SHA384, - CryptoHash::Sha512 => hkdf::HKDF_SHA512, - }; + let algorithm = match args.hash.ok_or_else(JsErrorBox::not_supported)? { + CryptoHash::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY, + CryptoHash::Sha256 => hkdf::HKDF_SHA256, + CryptoHash::Sha384 => hkdf::HKDF_SHA384, + CryptoHash::Sha512 => hkdf::HKDF_SHA512, + }; - let info = args.info.ok_or_else(|| Error::MissingArgumentInfo)?; + let info = args.info.ok_or(CryptoError::MissingArgumentInfo)?; // IKM let secret = args.key.data; // L @@ -633,18 +659,18 @@ pub async fn op_crypto_derive_bits( let info = &[&*info]; let okm = prk .expand(info, HkdfOutput(length)) - .map_err(|_e| Error::HKDFLengthTooLarge)?; + .map_err(|_e| CryptoError::HKDFLengthTooLarge)?; let mut r = vec![0u8; length]; okm.fill(&mut r)?; Ok(r.into()) } - _ => Err(Error::UnsupportedAlgorithm), + _ => Err(CryptoError::UnsupportedAlgorithm), } }) .await? } -fn read_rsa_public_key(key_data: KeyData) -> Result { +fn read_rsa_public_key(key_data: KeyData) -> Result { let public_key = match key_data.r#type { KeyType::Private => { RsaPrivateKey::from_pkcs1_der(&key_data.data)?.to_public_key() @@ -657,7 +683,9 @@ fn read_rsa_public_key(key_data: KeyData) -> Result { #[op2] #[string] -pub fn op_crypto_random_uuid(state: &mut OpState) -> Result { +pub fn op_crypto_random_uuid( + state: &mut OpState, +) -> Result { let maybe_seeded_rng = state.try_borrow_mut::(); let uuid = if let Some(seeded_rng) = maybe_seeded_rng { let mut bytes = [0u8; 16]; @@ -678,7 +706,7 @@ pub fn op_crypto_random_uuid(state: &mut OpState) -> Result { pub async fn op_crypto_subtle_digest( #[serde] algorithm: CryptoHash, #[buffer] data: JsBuffer, -) -> Result { +) -> Result { let output = spawn_blocking(move || { digest::digest(algorithm.into(), &data) .as_ref() @@ -702,7 +730,7 @@ pub struct WrapUnwrapKeyArg { pub fn op_crypto_wrap_key( #[serde] args: WrapUnwrapKeyArg, #[buffer] data: JsBuffer, -) -> Result { +) -> Result { let algorithm = args.algorithm; match algorithm { @@ -710,20 +738,20 @@ pub fn op_crypto_wrap_key( let key = args.key.as_secret_key()?; if data.len() % 8 != 0 { - return Err(Error::DataInvalidSize); + return Err(CryptoError::DataInvalidSize); } let wrapped_key = match key.len() { 16 => KekAes128::new(key.into()).wrap_vec(&data), 24 => KekAes192::new(key.into()).wrap_vec(&data), 32 => KekAes256::new(key.into()).wrap_vec(&data), - _ => return Err(Error::InvalidKeyLength), + _ => return Err(CryptoError::InvalidKeyLength), } - .map_err(|_| Error::EncryptionError)?; + .map_err(|_| CryptoError::EncryptionError)?; Ok(wrapped_key.into()) } - _ => Err(Error::UnsupportedAlgorithm), + _ => Err(CryptoError::UnsupportedAlgorithm), } } @@ -732,27 +760,27 @@ pub fn op_crypto_wrap_key( pub fn op_crypto_unwrap_key( #[serde] args: WrapUnwrapKeyArg, #[buffer] data: JsBuffer, -) -> Result { +) -> Result { let algorithm = args.algorithm; match algorithm { Algorithm::AesKw => { let key = args.key.as_secret_key()?; if data.len() % 8 != 0 { - return Err(Error::DataInvalidSize); + return Err(CryptoError::DataInvalidSize); } let unwrapped_key = match key.len() { 16 => KekAes128::new(key.into()).unwrap_vec(&data), 24 => KekAes192::new(key.into()).unwrap_vec(&data), 32 => KekAes256::new(key.into()).unwrap_vec(&data), - _ => return Err(Error::InvalidKeyLength), + _ => return Err(CryptoError::InvalidKeyLength), } - .map_err(|_| Error::DecryptionError)?; + .map_err(|_| CryptoError::DecryptionError)?; Ok(unwrapped_key.into()) } - _ => Err(Error::UnsupportedAlgorithm), + _ => Err(CryptoError::UnsupportedAlgorithm), } } diff --git a/ext/crypto/shared.rs b/ext/crypto/shared.rs index 60ba560c98..1c28e0b87d 100644 --- a/ext/crypto/shared.rs +++ b/ext/crypto/shared.rs @@ -60,26 +60,36 @@ pub enum RustRawKeyData { Public(ToJsBuffer), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SharedError { + #[class(type)] #[error("expected valid private key")] ExpectedValidPrivateKey, + #[class(type)] #[error("expected valid public key")] ExpectedValidPublicKey, + #[class(type)] #[error("expected valid private EC key")] ExpectedValidPrivateECKey, + #[class(type)] #[error("expected valid public EC key")] ExpectedValidPublicECKey, + #[class(type)] #[error("expected private key")] ExpectedPrivateKey, + #[class(type)] #[error("expected public key")] ExpectedPublicKey, + #[class(type)] #[error("expected secret key")] ExpectedSecretKey, + #[class("DOMExceptionOperationError")] #[error("failed to decode private key")] FailedDecodePrivateKey, + #[class("DOMExceptionOperationError")] #[error("failed to decode public key")] FailedDecodePublicKey, + #[class("DOMExceptionNotSupportedError")] #[error("unsupported format")] UnsupportedFormat, } diff --git a/ext/crypto/x25519.rs b/ext/crypto/x25519.rs index 80537d435f..226ed89e40 100644 --- a/ext/crypto/x25519.rs +++ b/ext/crypto/x25519.rs @@ -11,10 +11,12 @@ use spki::der::asn1::BitString; use spki::der::Decode; use spki::der::Encode; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum X25519Error { + #[class("DOMExceptionOperationError")] #[error("Failed to export key")] FailedExport, + #[class(generic)] #[error(transparent)] Der(#[from] spki::der::Error), } diff --git a/ext/crypto/x448.rs b/ext/crypto/x448.rs index c582aa9661..2086a8f048 100644 --- a/ext/crypto/x448.rs +++ b/ext/crypto/x448.rs @@ -12,10 +12,12 @@ use spki::der::asn1::BitString; use spki::der::Decode; use spki::der::Encode; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum X448Error { + #[class("DOMExceptionOperationError")] #[error("Failed to export key")] FailedExport, + #[class(generic)] #[error(transparent)] Der(#[from] spki::der::Error), } diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index ec82281bf4..21f77153b9 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -18,6 +18,7 @@ base64.workspace = true bytes.workspace = true data-url.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true deno_tls.workspace = true diff --git a/ext/fetch/fs_fetch_handler.rs b/ext/fetch/fs_fetch_handler.rs index 33293d0a27..8761eb2d65 100644 --- a/ext/fetch/fs_fetch_handler.rs +++ b/ext/fetch/fs_fetch_handler.rs @@ -8,6 +8,7 @@ use deno_core::futures::TryStreamExt; use deno_core::url::Url; use deno_core::CancelFuture; use deno_core::OpState; +use deno_error::JsErrorBox; use http::StatusCode; use http_body_util::BodyExt; use tokio_util::io::ReaderStream; @@ -34,7 +35,7 @@ impl FetchHandler for FsFetchHandler { let file = tokio::fs::File::open(path).map_err(|_| ()).await?; let stream = ReaderStream::new(file) .map_ok(hyper::body::Frame::data) - .map_err(Into::into); + .map_err(JsErrorBox::from_err); let body = http_body_util::StreamBody::new(stream).boxed(); let response = http::Response::builder() .status(StatusCode::OK) diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index d1f3963743..5af68695ef 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -46,6 +46,7 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; use deno_path_util::url_from_file_path; use deno_path_util::PathToUrlError; use deno_permissions::PermissionCheckError; @@ -100,9 +101,8 @@ pub struct Options { /// For more info on what can be configured, see [`hyper_util::client::legacy::Builder`]. pub client_builder_hook: Option HyperClientBuilder>, #[allow(clippy::type_complexity)] - pub request_builder_hook: Option< - fn(&mut http::Request) -> Result<(), deno_core::error::AnyError>, - >, + pub request_builder_hook: + Option) -> Result<(), JsErrorBox>>, pub unsafely_ignore_certificate_errors: Option>, pub client_cert_chain_and_key: TlsKeys, pub file_fetch_handler: Rc, @@ -110,9 +110,7 @@ pub struct Options { } impl Options { - pub fn root_cert_store( - &self, - ) -> Result, deno_core::error::AnyError> { + pub fn root_cert_store(&self) -> Result, JsErrorBox> { Ok(match &self.root_cert_store_provider { Some(provider) => Some(provider.get_or_try_init()?.clone()), None => None, @@ -164,48 +162,71 @@ deno_core::extension!(deno_fetch, }, ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum FetchError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Permission(#[from] PermissionCheckError), + #[class(type)] #[error("NetworkError when attempting to fetch resource")] NetworkError, + #[class(type)] #[error("Fetching files only supports the GET method: received {0}")] FsNotGet(Method), + #[class(inherit)] #[error(transparent)] PathToUrl(#[from] PathToUrlError), + #[class(type)] #[error("Invalid URL {0}")] InvalidUrl(Url), + #[class(type)] #[error(transparent)] InvalidHeaderName(#[from] http::header::InvalidHeaderName), + #[class(type)] #[error(transparent)] InvalidHeaderValue(#[from] http::header::InvalidHeaderValue), + #[class(type)] #[error("{0:?}")] DataUrl(data_url::DataUrlError), + #[class(type)] #[error("{0:?}")] Base64(data_url::forgiving_base64::InvalidBase64), + #[class(type)] #[error("Blob for the given URL not found.")] BlobNotFound, + #[class(type)] #[error("Url scheme '{0}' not supported")] SchemeNotSupported(String), + #[class(type)] #[error("Request was cancelled")] RequestCanceled, + #[class(generic)] #[error(transparent)] Http(#[from] http::Error), + #[class(inherit)] #[error(transparent)] ClientCreate(#[from] HttpClientCreateError), + #[class(inherit)] #[error(transparent)] Url(#[from] url::ParseError), + #[class(type)] #[error(transparent)] Method(#[from] http::method::InvalidMethod), + #[class(inherit)] #[error(transparent)] ClientSend(#[from] ClientSendError), + #[class(inherit)] #[error(transparent)] - RequestBuilderHook(deno_core::error::AnyError), + RequestBuilderHook(JsErrorBox), + #[class(inherit)] #[error(transparent)] Io(#[from] std::io::Error), + #[class(generic)] + #[error(transparent)] + Dns(hickory_resolver::ResolveError), } pub type CancelableResponseFuture = @@ -294,9 +315,7 @@ pub fn create_client_from_options( #[allow(clippy::type_complexity)] pub struct ResourceToBodyAdapter( Rc, - Option< - Pin>>>, - >, + Option>>>>, ); impl ResourceToBodyAdapter { @@ -312,7 +331,7 @@ unsafe impl Send for ResourceToBodyAdapter {} unsafe impl Sync for ResourceToBodyAdapter {} impl Stream for ResourceToBodyAdapter { - type Item = Result; + type Item = Result; fn poll_next( self: Pin<&mut Self>, @@ -342,7 +361,7 @@ impl Stream for ResourceToBodyAdapter { impl hyper::body::Body for ResourceToBodyAdapter { type Data = Bytes; - type Error = deno_core::error::AnyError; + type Error = JsErrorBox; fn poll_frame( self: Pin<&mut Self>, @@ -417,10 +436,7 @@ where FP: FetchPermissions + 'static, { let (client, allow_host) = if let Some(rid) = client_rid { - let r = state - .resource_table - .get::(rid) - .map_err(FetchError::Resource)?; + let r = state.resource_table.get::(rid)?; (r.client.clone(), r.allow_host) } else { (get_or_create_client_from_state(state)?, false) @@ -479,10 +495,7 @@ where ReqBody::full(data.to_vec().into()) } (_, Some(resource)) => { - let resource = state - .resource_table - .take_any(resource) - .map_err(FetchError::Resource)?; + let resource = state.resource_table.take_any(resource)?; match resource.size_hint() { (body_size, Some(n)) if body_size == n && body_size > 0 => { con_len = Some(body_size); @@ -624,8 +637,7 @@ pub async fn op_fetch_send( let request = state .borrow_mut() .resource_table - .take::(rid) - .map_err(FetchError::Resource)?; + .take::(rid)?; let request = Rc::try_unwrap(request) .ok() @@ -804,9 +816,7 @@ impl Resource for FetchResponseResource { // safely call `await` on it without creating a race condition. Some(_) => match reader.as_mut().next().await.unwrap() { Ok(chunk) => assert!(chunk.is_empty()), - Err(err) => { - break Err(deno_core::error::type_error(err.to_string())) - } + Err(err) => break Err(JsErrorBox::type_error(err.to_string())), }, None => break Ok(BufView::empty()), } @@ -814,7 +824,10 @@ impl Resource for FetchResponseResource { }; let cancel_handle = RcRef::map(self, |r| &r.cancel); - fut.try_or_cancel(cancel_handle).await + fut + .try_or_cancel(cancel_handle) + .await + .map_err(JsErrorBox::from_err) }) } @@ -897,9 +910,7 @@ where ca_certs, proxy: args.proxy, dns_resolver: if args.use_hickory_resolver { - dns::Resolver::hickory() - .map_err(deno_core::error::AnyError::new) - .map_err(FetchError::Resource)? + dns::Resolver::hickory().map_err(FetchError::Dns)? } else { dns::Resolver::default() }, @@ -963,7 +974,8 @@ impl Default for CreateHttpClientOptions { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum HttpClientCreateError { #[error(transparent)] Tls(deno_tls::TlsError), @@ -973,8 +985,9 @@ pub enum HttpClientCreateError { InvalidProxyUrl, #[error("Cannot create Http Client: either `http1` or `http2` needs to be set to true")] HttpVersionSelectionInvalid, + #[class(inherit)] #[error(transparent)] - RootCertStore(deno_core::error::AnyError), + RootCertStore(JsErrorBox), } /// Create new instance of async Client. This client supports @@ -1097,7 +1110,8 @@ type Connector = proxy::ProxyConnector>; #[allow(clippy::declare_interior_mutable_const)] const STAR_STAR: HeaderValue = HeaderValue::from_static("*/*"); -#[derive(Debug)] +#[derive(Debug, deno_error::JsError)] +#[class(type)] pub struct ClientSendError { uri: Uri, pub source: hyper_util::client::legacy::Error, @@ -1172,7 +1186,7 @@ impl Client { .oneshot(req) .await .map_err(|e| ClientSendError { uri, source: e })?; - Ok(resp.map(|b| b.map_err(|e| deno_core::anyhow::anyhow!(e)).boxed())) + Ok(resp.map(|b| b.map_err(|e| JsErrorBox::generic(e.to_string())).boxed())) } } @@ -1180,10 +1194,10 @@ impl Client { pub enum ReqBody { Full(http_body_util::Full), Empty(http_body_util::Empty), - Streaming(BoxBody), + Streaming(BoxBody), } -pub type ResBody = BoxBody; +pub type ResBody = BoxBody; impl ReqBody { pub fn full(bytes: Bytes) -> Self { @@ -1196,7 +1210,7 @@ impl ReqBody { pub fn streaming(body: B) -> Self where - B: hyper::body::Body + B: hyper::body::Body + Send + Sync + 'static, @@ -1207,7 +1221,7 @@ impl ReqBody { impl hyper::body::Body for ReqBody { type Data = Bytes; - type Error = deno_core::error::AnyError; + type Error = JsErrorBox; fn poll_frame( mut self: Pin<&mut Self>, diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index 0af0f4a131..b78aa36d7c 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_permissions.workspace = true dlopen2.workspace = true dynasmrt = "1.2.3" diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs index 001b925af5..4f9a057d01 100644 --- a/ext/ffi/call.rs +++ b/ext/ffi/call.rs @@ -25,18 +25,24 @@ use crate::symbol::Symbol; use crate::FfiPermissions; use crate::ForeignFunction; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CallError { + #[class(type)] #[error(transparent)] IR(#[from] IRError), + #[class(generic)] #[error("Nonblocking FFI call failed: {0}")] NonblockingCallFailure(#[source] tokio::task::JoinError), + #[class(type)] #[error("Invalid FFI symbol name: '{0}'")] InvalidSymbol(String), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Callback(#[from] super::CallbackError), } @@ -346,10 +352,7 @@ pub fn op_ffi_call_nonblocking( ) -> Result>, CallError> { let symbol = { let state = state.borrow(); - let resource = state - .resource_table - .get::(rid) - .map_err(CallError::Resource)?; + let resource = state.resource_table.get::(rid)?; let symbols = &resource.symbols; *symbols .get(&symbol) diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs index c4b5e14842..a6c104ef42 100644 --- a/ext/ffi/callback.rs +++ b/ext/ffi/callback.rs @@ -35,14 +35,17 @@ thread_local! { static LOCAL_THREAD_ID: RefCell = const { RefCell::new(0) }; } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CallbackError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[from] deno_error::JsErrorBox), } #[derive(Clone)] @@ -63,13 +66,8 @@ impl PtrSymbol { .clone() .into_iter() .map(libffi::middle::Type::try_from) - .collect::, _>>() - .map_err(CallbackError::Other)?, - def - .result - .clone() - .try_into() - .map_err(CallbackError::Other)?, + .collect::, _>>()?, + def.result.clone().try_into()?, ); Ok(Self { cif, ptr }) @@ -540,10 +538,8 @@ pub fn op_ffi_unsafe_callback_ref( #[smi] rid: ResourceId, ) -> Result, CallbackError> { let state = state.borrow(); - let callback_resource = state - .resource_table - .get::(rid) - .map_err(CallbackError::Resource)?; + let callback_resource = + state.resource_table.get::(rid)?; Ok(async move { let info: &mut CallbackInfo = @@ -610,10 +606,8 @@ where .parameters .into_iter() .map(libffi::middle::Type::try_from) - .collect::, _>>() - .map_err(CallbackError::Other)?, - libffi::middle::Type::try_from(args.result) - .map_err(CallbackError::Other)?, + .collect::, _>>()?, + libffi::middle::Type::try_from(args.result)?, ); // SAFETY: CallbackInfo is leaked, is not null and stays valid as long as the callback exists. @@ -649,10 +643,8 @@ pub fn op_ffi_unsafe_callback_close( // It is up to the user to know that it is safe to call the `close()` on the // UnsafeCallback instance. unsafe { - let callback_resource = state - .resource_table - .take::(rid) - .map_err(CallbackError::Resource)?; + let callback_resource = + state.resource_table.take::(rid)?; let info = Box::from_raw(callback_resource.info); let _ = v8::Global::from_raw(scope, info.callback); let _ = v8::Global::from_raw(scope, info.context); diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs index 4eea2402a0..da5a85e7e3 100644 --- a/ext/ffi/dlfcn.rs +++ b/ext/ffi/dlfcn.rs @@ -11,6 +11,8 @@ use deno_core::v8; use deno_core::GarbageCollected; use deno_core::OpState; use deno_core::Resource; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; use dlopen2::raw::Library; use serde::Deserialize; use serde_value::ValueDeserializer; @@ -22,20 +24,34 @@ use crate::turbocall; use crate::turbocall::Turbocall; use crate::FfiPermissions; -#[derive(Debug, thiserror::Error)] +deno_error::js_error_wrapper!(dlopen2::Error, JsDlopen2Error, |err| { + match err { + dlopen2::Error::NullCharacter(_) => "InvalidData".into(), + dlopen2::Error::OpeningLibraryError(e) => e.get_class(), + dlopen2::Error::SymbolGettingError(e) => e.get_class(), + dlopen2::Error::AddrNotMatchingDll(e) => e.get_class(), + dlopen2::Error::NullSymbol => "NotFound".into(), + } +}); + +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum DlfcnError { + #[class(generic)] #[error("Failed to register symbol {symbol}: {error}")] RegisterSymbol { symbol: String, #[source] error: dlopen2::Error, }, + #[class(generic)] #[error(transparent)] Dlopen(#[from] dlopen2::Error), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[from] JsErrorBox), } pub struct DynamicLibraryResource { @@ -190,13 +206,8 @@ where .clone() .into_iter() .map(libffi::middle::Type::try_from) - .collect::, _>>() - .map_err(DlfcnError::Other)?, - foreign_fn - .result - .clone() - .try_into() - .map_err(DlfcnError::Other)?, + .collect::, _>>()?, + foreign_fn.result.clone().try_into()?, ); let func_key = v8::String::new(scope, &symbol_key).unwrap(); @@ -304,9 +315,7 @@ fn sync_fn_impl<'s>( unsafe { result.to_v8(scope, data.symbol.result_type.clone()) }; rv.set(result); } - Err(err) => { - deno_core::_ops::throw_type_error(scope, err.to_string()); - } + Err(err) => deno_core::error::throw_js_error_class(scope, &err), }; } diff --git a/ext/ffi/ir.rs b/ext/ffi/ir.rs index 7b2f167ce7..a1877b4a2b 100644 --- a/ext/ffi/ir.rs +++ b/ext/ffi/ir.rs @@ -8,7 +8,8 @@ use libffi::middle::Arg; use crate::symbol::NativeType; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum IRError { #[error("Invalid FFI u8 type, expected boolean")] InvalidU8ExpectedBoolean, diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs index 05bef40a89..bcd80cbf03 100644 --- a/ext/ffi/repr.rs +++ b/ext/ffi/repr.rs @@ -11,7 +11,8 @@ use deno_core::OpState; use crate::FfiPermissions; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum ReprError { #[error("Invalid pointer to offset, pointer is null")] InvalidOffset, @@ -47,6 +48,7 @@ pub enum ReprError { InvalidF64, #[error("Invalid pointer pointer, pointer is null")] InvalidPointer, + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), } diff --git a/ext/ffi/static.rs b/ext/ffi/static.rs index 6ad7fe6d37..6d999430e3 100644 --- a/ext/ffi/static.rs +++ b/ext/ffi/static.rs @@ -10,16 +10,20 @@ use deno_core::ResourceId; use crate::dlfcn::DynamicLibraryResource; use crate::symbol::NativeType; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum StaticError { + #[class(inherit)] #[error(transparent)] Dlfcn(super::DlfcnError), + #[class(type)] #[error("Invalid FFI static type 'void'")] InvalidTypeVoid, + #[class(type)] #[error("Invalid FFI static type 'struct'")] InvalidTypeStruct, + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), } #[op2] @@ -31,10 +35,7 @@ pub fn op_ffi_get_static<'scope>( #[serde] static_type: NativeType, optional: bool, ) -> Result, StaticError> { - let resource = state - .resource_table - .get::(rid) - .map_err(StaticError::Resource)?; + let resource = state.resource_table.get::(rid)?; let data_ptr = match resource.get_static(name) { Ok(data_ptr) => data_ptr, diff --git a/ext/ffi/symbol.rs b/ext/ffi/symbol.rs index c4a68cf753..5bca5be6d2 100644 --- a/ext/ffi/symbol.rs +++ b/ext/ffi/symbol.rs @@ -1,7 +1,6 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::error::type_error; -use deno_core::error::AnyError; +use deno_error::JsErrorBox; /// Defines the accepted types that can be used as /// parameters and return values in FFI. @@ -29,7 +28,7 @@ pub enum NativeType { } impl TryFrom for libffi::middle::Type { - type Error = AnyError; + type Error = JsErrorBox; fn try_from(native_type: NativeType) -> Result { Ok(match native_type { @@ -56,7 +55,9 @@ impl TryFrom for libffi::middle::Type { .map(|field| field.clone().try_into()) .collect::, _>>()?, false => { - return Err(type_error("Struct must have at least one field")) + return Err(JsErrorBox::type_error( + "Struct must have at least one field", + )) } }) } diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index b7d22b7f93..05141e46c9 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -21,6 +21,7 @@ async-trait.workspace = true base32.workspace = true boxed_error.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_io.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index 64be03ea61..9f5f3c6e90 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -12,6 +12,7 @@ use std::path::StripPrefixError; use std::rc::Rc; use boxed_error::Boxed; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::CancelFuture; use deno_core::CancelHandle; @@ -20,6 +21,7 @@ use deno_core::JsBuffer; use deno_core::OpState; use deno_core::ResourceId; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use deno_io::fs::FileResource; use deno_io::fs::FsError; use deno_io::fs::FsStat; @@ -36,34 +38,46 @@ use crate::interface::FsFileType; use crate::FsPermissions; use crate::OpenOptions; -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, deno_error::JsError)] pub struct FsOpsError(pub Box); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum FsOpsErrorKind { + #[class(inherit)] #[error("{0}")] Io(#[source] std::io::Error), + #[class(inherit)] #[error("{0}")] OperationError(#[source] OperationError), + #[class(inherit)] #[error(transparent)] Permission(#[from] PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] ResourceError), + #[class("InvalidData")] #[error("File name or path {0:?} is not valid UTF-8")] InvalidUtf8(std::ffi::OsString), + #[class(generic)] #[error("{0}")] StripPrefix(#[from] StripPrefixError), + #[class(inherit)] #[error("{0}")] Canceled(#[from] deno_core::Canceled), + #[class(type)] #[error("Invalid seek mode: {0}")] InvalidSeekMode(i32), + #[class(generic)] #[error("Invalid control character in prefix or suffix: {0:?}")] InvalidControlCharacter(String), + #[class(generic)] #[error("Invalid character in prefix or suffix: {0:?}")] InvalidCharacter(String), #[cfg(windows)] + #[class(generic)] #[error("Invalid trailing character in suffix")] InvalidTrailingCharacter, + #[class("NotCapable")] #[error("Requires {err} access to {path}, {}", print_not_capable_info(*.standalone, .err))] NotCapableAccess { // NotCapable @@ -71,21 +85,21 @@ pub enum FsOpsErrorKind { err: &'static str, path: String, }, + #[class("NotCapable")] #[error("permission denied: {0}")] - NotCapable(&'static str), // NotCapable + NotCapable(&'static str), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(JsErrorBox), } impl From for FsOpsError { fn from(err: FsError) -> Self { match err { FsError::Io(err) => FsOpsErrorKind::Io(err), - FsError::FileBusy => { - FsOpsErrorKind::Other(deno_core::error::resource_unavailable()) - } + FsError::FileBusy => FsOpsErrorKind::Resource(ResourceError::Unavailable), FsError::NotSupported => { - FsOpsErrorKind::Other(deno_core::error::not_supported()) + FsOpsErrorKind::Other(JsErrorBox::not_supported()) } FsError::NotCapable(err) => FsOpsErrorKind::NotCapable(err), } @@ -1666,10 +1680,12 @@ pub async fn op_fs_futime_async( Ok(()) } -#[derive(Debug)] +#[derive(Debug, deno_error::JsError)] +#[class(inherit)] pub struct OperationError { operation: &'static str, kind: OperationErrorKind, + #[inherit] pub err: FsError, } diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index 8f131b84e6..1ecb6f66c8 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -28,6 +28,7 @@ brotli.workspace = true bytes.workspace = true cache_control.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_net.workspace = true deno_websocket.workspace = true flate2.workspace = true diff --git a/ext/http/http_next.rs b/ext/http/http_next.rs index d9861a0a6b..82edf817bf 100644 --- a/ext/http/http_next.rs +++ b/ext/http/http_next.rs @@ -146,24 +146,44 @@ macro_rules! clone_external { }}; } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HttpNextError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error("{0}")] Io(#[from] io::Error), + #[class(inherit)] #[error(transparent)] WebSocketUpgrade(crate::websocket_upgrade::WebSocketUpgradeError), + #[class("Http")] #[error("{0}")] Hyper(#[from] hyper::Error), + #[class(inherit)] #[error(transparent)] - JoinError(#[from] tokio::task::JoinError), + JoinError( + #[from] + #[inherit] + tokio::task::JoinError, + ), + #[class(inherit)] #[error(transparent)] - Canceled(#[from] deno_core::Canceled), - #[error(transparent)] - HttpPropertyExtractor(deno_core::error::AnyError), + Canceled( + #[from] + #[inherit] + deno_core::Canceled, + ), + #[class(generic)] #[error(transparent)] UpgradeUnavailable(#[from] crate::service::UpgradeUnavailableError), + #[class(inherit)] + #[error("{0}")] + Other( + #[from] + #[inherit] + deno_error::JsErrorBox, + ), } #[op2(fast)] @@ -747,15 +767,9 @@ pub async fn op_http_set_response_body_resource( let resource = { let mut state = state.borrow_mut(); if auto_close { - state - .resource_table - .take_any(stream_rid) - .map_err(HttpNextError::Resource)? + state.resource_table.take_any(stream_rid)? } else { - state - .resource_table - .get_any(stream_rid) - .map_err(HttpNextError::Resource)? + state.resource_table.get_any(stream_rid)? } }; @@ -1063,8 +1077,7 @@ where HTTP: HttpPropertyExtractor, { let listener = - HTTP::get_listener_for_rid(&mut state.borrow_mut(), listener_rid) - .map_err(HttpNextError::Resource)?; + HTTP::get_listener_for_rid(&mut state.borrow_mut(), listener_rid)?; let listen_properties = HTTP::listen_properties_from_listener(&listener)?; @@ -1084,8 +1097,7 @@ where loop { let conn = HTTP::accept_connection_from_listener(&listener) .try_or_cancel(listen_cancel_clone.clone()) - .await - .map_err(HttpNextError::HttpPropertyExtractor)?; + .await?; serve_http_on::( conn, &listen_properties_clone, @@ -1120,8 +1132,7 @@ where HTTP: HttpPropertyExtractor, { let connection = - HTTP::get_connection_for_rid(&mut state.borrow_mut(), connection_rid) - .map_err(HttpNextError::Resource)?; + HTTP::get_connection_for_rid(&mut state.borrow_mut(), connection_rid)?; let listen_properties = HTTP::listen_properties_from_connection(&connection)?; @@ -1190,8 +1201,7 @@ pub async fn op_http_wait( let join_handle = state .borrow_mut() .resource_table - .get::(rid) - .map_err(HttpNextError::Resource)?; + .get::(rid)?; let cancel = join_handle.listen_cancel_handle(); let next = async { @@ -1236,7 +1246,7 @@ pub fn op_http_cancel( state: &mut OpState, #[smi] rid: ResourceId, graceful: bool, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), deno_core::error::ResourceError> { let join_handle = state.resource_table.get::(rid)?; if graceful { @@ -1260,8 +1270,7 @@ pub async fn op_http_close( let join_handle = state .borrow_mut() .resource_table - .take::(rid) - .map_err(HttpNextError::Resource)?; + .take::(rid)?; if graceful { http_general_trace!("graceful shutdown"); @@ -1390,11 +1399,8 @@ pub async fn op_raw_write_vectored( #[buffer] buf1: JsBuffer, #[buffer] buf2: JsBuffer, ) -> Result { - let resource: Rc = state - .borrow() - .resource_table - .get::(rid) - .map_err(HttpNextError::Resource)?; + let resource: Rc = + state.borrow().resource_table.get::(rid)?; let nwritten = resource.write_vectored(&buf1, &buf2).await?; Ok(nwritten) } diff --git a/ext/http/lib.rs b/ext/http/lib.rs index e68bf3787d..981ca9f0c0 100644 --- a/ext/http/lib.rs +++ b/ext/http/lib.rs @@ -51,6 +51,7 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::StringOrBuffer; +use deno_error::JsErrorBox; use deno_net::raw::NetworkStream; use deno_websocket::ws_create_server_stream; use flate2::write::GzEncoder; @@ -165,36 +166,50 @@ deno_core::extension!( } ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HttpError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), + #[class("Http")] #[error("{0}")] HyperV014(#[source] Arc), + #[class(generic)] #[error("{0}")] InvalidHeaderName(#[from] hyper_v014::header::InvalidHeaderName), + #[class(generic)] #[error("{0}")] InvalidHeaderValue(#[from] hyper_v014::header::InvalidHeaderValue), + #[class(generic)] #[error("{0}")] Http(#[from] hyper_v014::http::Error), + #[class("Http")] #[error("response headers already sent")] ResponseHeadersAlreadySent, + #[class("Http")] #[error("connection closed while sending response")] ConnectionClosedWhileSendingResponse, + #[class("Http")] #[error("already in use")] AlreadyInUse, + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), + #[class("Http")] #[error("no response headers")] NoResponseHeaders, + #[class("Http")] #[error("response already completed")] ResponseAlreadyCompleted, + #[class("Http")] #[error("cannot upgrade because request body was used")] UpgradeBodyUsed, + #[class("Http")] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[from] JsErrorBox), } pub enum HttpSocketAddr { @@ -486,7 +501,9 @@ impl Resource for HttpStreamReadResource { Some(_) => match body.as_mut().next().await.unwrap() { Ok(chunk) => assert!(chunk.is_empty()), Err(err) => { - break Err(HttpError::HyperV014(Arc::new(err)).into()) + break Err(JsErrorBox::from_err(HttpError::HyperV014( + Arc::new(err), + ))) } }, None => break Ok(BufView::empty()), @@ -610,11 +627,7 @@ async fn op_http_accept( state: Rc>, #[smi] rid: ResourceId, ) -> Result, HttpError> { - let conn = state - .borrow() - .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + let conn = state.borrow().resource_table.get::(rid)?; match conn.accept().await { Ok(Some((read_stream, write_stream, method, url))) => { @@ -729,8 +742,7 @@ async fn op_http_write_headers( let stream = state .borrow_mut() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; // Track supported encoding let encoding = stream.accept_encoding; @@ -795,10 +807,7 @@ fn op_http_headers( state: &mut OpState, #[smi] rid: u32, ) -> Result, HttpError> { - let stream = state - .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + let stream = state.resource_table.get::(rid)?; let rd = RcRef::map(&stream, |r| &r.rd) .try_borrow() .ok_or(HttpError::AlreadyInUse)?; @@ -954,14 +963,9 @@ async fn op_http_write_resource( let http_stream = state .borrow() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; let mut wr = RcRef::map(&http_stream, |r| &r.wr).borrow_mut().await; - let resource = state - .borrow() - .resource_table - .get_any(stream) - .map_err(HttpError::Resource)?; + let resource = state.borrow().resource_table.get_any(stream)?; loop { match *wr { HttpResponseWriter::Headers(_) => { @@ -973,11 +977,7 @@ async fn op_http_write_resource( _ => {} }; - let view = resource - .clone() - .read(64 * 1024) - .await - .map_err(HttpError::Other)?; // 64KB + let view = resource.clone().read(64 * 1024).await?; // 64KB if view.is_empty() { break; } @@ -1022,8 +1022,7 @@ async fn op_http_write( let stream = state .borrow() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; let mut wr = RcRef::map(&stream, |r| &r.wr).borrow_mut().await; match &mut *wr { @@ -1075,8 +1074,7 @@ async fn op_http_shutdown( let stream = state .borrow() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; let mut wr = RcRef::map(&stream, |r| &r.wr).borrow_mut().await; let wr = take(&mut *wr); match wr { @@ -1122,8 +1120,7 @@ async fn op_http_upgrade_websocket( let stream = state .borrow_mut() .resource_table - .get::(rid) - .map_err(HttpError::Resource)?; + .get::(rid)?; let mut rd = RcRef::map(&stream, |r| &r.rd).borrow_mut().await; let request = match &mut *rd { diff --git a/ext/http/request_body.rs b/ext/http/request_body.rs index e7c9a06e2c..50ca1635c3 100644 --- a/ext/http/request_body.rs +++ b/ext/http/request_body.rs @@ -15,6 +15,7 @@ use deno_core::AsyncResult; use deno_core::BufView; use deno_core::RcRef; use deno_core::Resource; +use deno_error::JsErrorBox; use hyper::body::Body; use hyper::body::Incoming; use hyper::body::SizeHint; @@ -83,7 +84,10 @@ impl Resource for HttpRequestBody { } fn read(self: Rc, limit: usize) -> AsyncResult { - Box::pin(HttpRequestBody::read(self, limit).map_err(Into::into)) + Box::pin( + HttpRequestBody::read(self, limit) + .map_err(|e| JsErrorBox::new("Http", e.to_string())), + ) } fn size_hint(&self) -> (u64, Option) { diff --git a/ext/http/request_properties.rs b/ext/http/request_properties.rs index 9e60a22baf..32ae33a34a 100644 --- a/ext/http/request_properties.rs +++ b/ext/http/request_properties.rs @@ -5,9 +5,9 @@ use std::net::SocketAddr; use std::net::SocketAddrV4; use std::rc::Rc; -use deno_core::error::AnyError; use deno_core::OpState; use deno_core::ResourceId; +use deno_error::JsErrorBox; use deno_net::raw::take_network_stream_listener_resource; use deno_net::raw::take_network_stream_resource; use deno_net::raw::NetworkStream; @@ -50,13 +50,13 @@ pub trait HttpPropertyExtractor { fn get_listener_for_rid( state: &mut OpState, listener_rid: ResourceId, - ) -> Result; + ) -> Result; /// Given a connection [`ResourceId`], returns the [`HttpPropertyExtractor::Connection`]. fn get_connection_for_rid( state: &mut OpState, connection_rid: ResourceId, - ) -> Result; + ) -> Result; /// Determines the listener properties. fn listen_properties_from_listener( @@ -71,7 +71,7 @@ pub trait HttpPropertyExtractor { /// Accept a new [`HttpPropertyExtractor::Connection`] from the given listener [`HttpPropertyExtractor::Listener`]. async fn accept_connection_from_listener( listener: &Self::Listener, - ) -> Result; + ) -> Result; /// Determines the connection properties. fn connection_properties( @@ -103,7 +103,7 @@ impl HttpPropertyExtractor for DefaultHttpPropertyExtractor { fn get_listener_for_rid( state: &mut OpState, listener_rid: ResourceId, - ) -> Result { + ) -> Result { take_network_stream_listener_resource( &mut state.resource_table, listener_rid, @@ -113,17 +113,18 @@ impl HttpPropertyExtractor for DefaultHttpPropertyExtractor { fn get_connection_for_rid( state: &mut OpState, stream_rid: ResourceId, - ) -> Result { + ) -> Result { take_network_stream_resource(&mut state.resource_table, stream_rid) + .map_err(JsErrorBox::from_err) } async fn accept_connection_from_listener( listener: &NetworkStreamListener, - ) -> Result { + ) -> Result { listener .accept() .await - .map_err(Into::into) + .map_err(JsErrorBox::from_err) .map(|(stm, _)| stm) } diff --git a/ext/http/response_body.rs b/ext/http/response_body.rs index ff264a8305..6960e7c0fb 100644 --- a/ext/http/response_body.rs +++ b/ext/http/response_body.rs @@ -9,12 +9,12 @@ use brotli::enc::encode::BrotliEncoderStateStruct; use brotli::writer::StandardAlloc; use bytes::Bytes; use bytes::BytesMut; -use deno_core::error::AnyError; use deno_core::futures::ready; use deno_core::futures::FutureExt; use deno_core::AsyncResult; use deno_core::BufView; use deno_core::Resource; +use deno_error::JsErrorBox; use flate2::write::GzEncoder; use hyper::body::Frame; use hyper::body::SizeHint; @@ -32,10 +32,10 @@ pub enum ResponseStreamResult { /// will only be returned from compression streams that require additional buffering. NoData, /// Stream failed. - Error(AnyError), + Error(JsErrorBox), } -impl From for Option, AnyError>> { +impl From for Option, JsErrorBox>> { fn from(value: ResponseStreamResult) -> Self { match value { ResponseStreamResult::EndOfStream => None, @@ -411,7 +411,9 @@ impl PollFrame for GZipResponseStream { }; let len = stm.total_out() - start_out; let res = match res { - Err(err) => ResponseStreamResult::Error(err.into()), + Err(err) => { + ResponseStreamResult::Error(JsErrorBox::generic(err.to_string())) + } Ok(flate2::Status::BufError) => { // This should not happen unreachable!("old={orig_state:?} new={state:?} buf_len={}", buf.len()); diff --git a/ext/http/service.rs b/ext/http/service.rs index 3b7db49fc4..f220f8d8a7 100644 --- a/ext/http/service.rs +++ b/ext/http/service.rs @@ -15,6 +15,7 @@ use deno_core::futures::ready; use deno_core::BufView; use deno_core::OpState; use deno_core::ResourceId; +use deno_error::JsErrorBox; use http::request::Parts; use hyper::body::Body; use hyper::body::Frame; @@ -529,7 +530,7 @@ pub struct HttpRecordResponse(ManuallyDrop>); impl Body for HttpRecordResponse { type Data = BufView; - type Error = deno_core::error::AnyError; + type Error = JsErrorBox; fn poll_frame( self: Pin<&mut Self>, diff --git a/ext/http/websocket_upgrade.rs b/ext/http/websocket_upgrade.rs index aae4a13883..e030f1c7ae 100644 --- a/ext/http/websocket_upgrade.rs +++ b/ext/http/websocket_upgrade.rs @@ -12,22 +12,30 @@ use memmem::Searcher; use memmem::TwoWaySearcher; use once_cell::sync::OnceCell; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WebSocketUpgradeError { + #[class("Http")] #[error("invalid headers")] InvalidHeaders, + #[class(generic)] #[error("{0}")] HttpParse(#[from] httparse::Error), + #[class(generic)] #[error("{0}")] Http(#[from] http::Error), + #[class(generic)] #[error("{0}")] Utf8(#[from] std::str::Utf8Error), + #[class(generic)] #[error("{0}")] InvalidHeaderName(#[from] http::header::InvalidHeaderName), + #[class(generic)] #[error("{0}")] InvalidHeaderValue(#[from] http::header::InvalidHeaderValue), + #[class("Http")] #[error("invalid HTTP status line")] InvalidHttpStatusLine, + #[class("Http")] #[error("attempted to write to completed upgrade buffer")] UpgradeBufferAlreadyCompleted, } diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 782bd64444..9d11e1b0f6 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] async-trait.workspace = true deno_core.workspace = true +deno_error.workspace = true filetime.workspace = true fs3.workspace = true log.workspace = true diff --git a/ext/io/fs.rs b/ext/io/fs.rs index ee0c7da5da..d8767aa116 100644 --- a/ext/io/fs.rs +++ b/ext/io/fs.rs @@ -7,18 +7,24 @@ use std::rc::Rc; use std::time::SystemTime; use std::time::UNIX_EPOCH; +use deno_core::error::ResourceError; use deno_core::BufMutView; use deno_core::BufView; use deno_core::OpState; use deno_core::ResourceHandleFd; use deno_core::ResourceId; +use deno_error::JsErrorBox; use tokio::task::JoinError; -#[derive(Debug)] +#[derive(Debug, deno_error::JsError)] pub enum FsError { + #[class(inherit)] Io(io::Error), + #[class("Busy")] FileBusy, + #[class(not_supported)] NotSupported, + #[class("NotCapable")] NotCapable(&'static str), } @@ -277,18 +283,21 @@ impl FileResource { state: &OpState, rid: ResourceId, f: F, - ) -> Result + ) -> Result where - F: FnOnce(Rc) -> Result, + F: FnOnce(Rc) -> Result, { - let resource = state.resource_table.get::(rid)?; + let resource = state + .resource_table + .get::(rid) + .map_err(JsErrorBox::from_err)?; f(resource) } pub fn get_file( state: &OpState, rid: ResourceId, - ) -> Result, deno_core::error::AnyError> { + ) -> Result, ResourceError> { let resource = state.resource_table.get::(rid)?; Ok(resource.file()) } @@ -297,9 +306,9 @@ impl FileResource { state: &OpState, rid: ResourceId, f: F, - ) -> Result + ) -> Result where - F: FnOnce(Rc) -> Result, + F: FnOnce(Rc) -> Result, { Self::with_resource(state, rid, |r| f(r.file.clone())) } @@ -321,7 +330,7 @@ impl deno_core::Resource for FileResource { .clone() .read(limit) .await - .map_err(|err| err.into()) + .map_err(JsErrorBox::from_err) }) } @@ -335,7 +344,7 @@ impl deno_core::Resource for FileResource { .clone() .read_byob(buf) .await - .map_err(|err| err.into()) + .map_err(JsErrorBox::from_err) }) } @@ -344,7 +353,12 @@ impl deno_core::Resource for FileResource { buf: BufView, ) -> deno_core::AsyncResult { Box::pin(async move { - self.file.clone().write(buf).await.map_err(|err| err.into()) + self + .file + .clone() + .write(buf) + .await + .map_err(JsErrorBox::from_err) }) } @@ -355,22 +369,27 @@ impl deno_core::Resource for FileResource { .clone() .write_all(buf) .await - .map_err(|err| err.into()) + .map_err(JsErrorBox::from_err) }) } fn read_byob_sync( self: Rc, data: &mut [u8], - ) -> Result { - self.file.clone().read_sync(data).map_err(|err| err.into()) + ) -> Result { + self + .file + .clone() + .read_sync(data) + .map_err(JsErrorBox::from_err) } - fn write_sync( - self: Rc, - data: &[u8], - ) -> Result { - self.file.clone().write_sync(data).map_err(|err| err.into()) + fn write_sync(self: Rc, data: &[u8]) -> Result { + self + .file + .clone() + .write_sync(data) + .map_err(JsErrorBox::from_err) } fn backing_fd(self: Rc) -> Option { diff --git a/ext/io/lib.rs b/ext/io/lib.rs index f78e5a58fc..1f92ae5c8b 100644 --- a/ext/io/lib.rs +++ b/ext/io/lib.rs @@ -33,6 +33,7 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceHandle; use deno_core::ResourceHandleFd; +use deno_error::JsErrorBox; use fs::FileResource; use fs::FsError; use fs::FsResult; @@ -414,7 +415,7 @@ impl Resource for ChildStdinResource { deno_core::impl_writable!(); fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown().map_err(|e| e.into())) + Box::pin(self.shutdown().map_err(JsErrorBox::from_err)) } } @@ -1007,9 +1008,11 @@ pub fn op_print( state: &mut OpState, #[string] msg: &str, is_err: bool, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), JsErrorBox> { let rid = if is_err { 2 } else { 1 }; FileResource::with_file(state, rid, move |file| { - Ok(file.write_all_sync(msg.as_bytes())?) + file + .write_all_sync(msg.as_bytes()) + .map_err(JsErrorBox::from_err) }) } diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index 451fa50cf9..1a1cd346fb 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -21,6 +21,7 @@ boxed_error.workspace = true bytes.workspace = true chrono = { workspace = true, features = ["now"] } deno_core.workspace = true +deno_error.workspace = true deno_fetch.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true diff --git a/ext/kv/dynamic.rs b/ext/kv/dynamic.rs index 33e3d4842f..923e3cd4d8 100644 --- a/ext/kv/dynamic.rs +++ b/ext/kv/dynamic.rs @@ -4,9 +4,8 @@ use std::cell::RefCell; use std::rc::Rc; use async_trait::async_trait; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::OpState; +use deno_error::JsErrorBox; use denokv_proto::CommitResult; use denokv_proto::ReadRangeOutput; use denokv_proto::WatchStream; @@ -63,7 +62,7 @@ impl DatabaseHandler for MultiBackendDbHandler { &self, state: Rc>, path: Option, - ) -> Result { + ) -> Result { for (prefixes, handler) in &self.backends { for &prefix in *prefixes { if prefix.is_empty() { @@ -77,7 +76,7 @@ impl DatabaseHandler for MultiBackendDbHandler { } } } - Err(type_error(format!( + Err(JsErrorBox::type_error(format!( "No backend supports the given path: {:?}", path ))) @@ -90,7 +89,7 @@ pub trait DynamicDbHandler { &self, state: Rc>, path: Option, - ) -> Result; + ) -> Result; } #[async_trait(?Send)] @@ -101,7 +100,7 @@ impl DatabaseHandler for Box { &self, state: Rc>, path: Option, - ) -> Result { + ) -> Result { (**self).dyn_open(state, path).await } } @@ -116,7 +115,7 @@ where &self, state: Rc>, path: Option, - ) -> Result { + ) -> Result { Ok(RcDynamicDb(Rc::new(self.open(state, path).await?))) } } @@ -127,16 +126,16 @@ pub trait DynamicDb { &self, requests: Vec, options: SnapshotReadOptions, - ) -> Result, AnyError>; + ) -> Result, JsErrorBox>; async fn dyn_atomic_write( &self, write: AtomicWrite, - ) -> Result, AnyError>; + ) -> Result, JsErrorBox>; async fn dyn_dequeue_next_message( &self, - ) -> Result>, AnyError>; + ) -> Result>, JsErrorBox>; fn dyn_watch(&self, keys: Vec>) -> WatchStream; @@ -154,20 +153,20 @@ impl Database for RcDynamicDb { &self, requests: Vec, options: SnapshotReadOptions, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { (*self.0).dyn_snapshot_read(requests, options).await } async fn atomic_write( &self, write: AtomicWrite, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { (*self.0).dyn_atomic_write(write).await } async fn dequeue_next_message( &self, - ) -> Result>, AnyError> { + ) -> Result>, JsErrorBox> { (*self.0).dyn_dequeue_next_message().await } @@ -190,20 +189,20 @@ where &self, requests: Vec, options: SnapshotReadOptions, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { Ok(self.snapshot_read(requests, options).await?) } async fn dyn_atomic_write( &self, write: AtomicWrite, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { Ok(self.atomic_write(write).await?) } async fn dyn_dequeue_next_message( &self, - ) -> Result>, AnyError> { + ) -> Result>, JsErrorBox> { Ok( self .dequeue_next_message() diff --git a/ext/kv/interface.rs b/ext/kv/interface.rs index fec0ef1afd..df106fde78 100644 --- a/ext/kv/interface.rs +++ b/ext/kv/interface.rs @@ -4,8 +4,8 @@ use std::cell::RefCell; use std::rc::Rc; use async_trait::async_trait; -use deno_core::error::AnyError; use deno_core::OpState; +use deno_error::JsErrorBox; use denokv_proto::Database; #[async_trait(?Send)] @@ -16,5 +16,5 @@ pub trait DatabaseHandler { &self, state: Rc>, path: Option, - ) -> Result; + ) -> Result; } diff --git a/ext/kv/lib.rs b/ext/kv/lib.rs index 82dda6d759..61458888a9 100644 --- a/ext/kv/lib.rs +++ b/ext/kv/lib.rs @@ -17,7 +17,6 @@ use base64::Engine; use boxed_error::Boxed; use chrono::DateTime; use chrono::Utc; -use deno_core::error::get_custom_error_class; use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::serde_v8::AnyValue; @@ -32,6 +31,8 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; use denokv_proto::decode_key; use denokv_proto::encode_key; use denokv_proto::AtomicWrite; @@ -115,65 +116,93 @@ impl Resource for DatabaseWatcherResource { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, deno_error::JsError)] pub struct KvError(pub Box); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum KvErrorKind { + #[class(inherit)] #[error(transparent)] - DatabaseHandler(deno_core::error::AnyError), + DatabaseHandler(JsErrorBox), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(type)] #[error("Too many ranges (max {0})")] TooManyRanges(usize), + #[class(type)] #[error("Too many entries (max {0})")] TooManyEntries(usize), + #[class(type)] #[error("Too many checks (max {0})")] TooManyChecks(usize), + #[class(type)] #[error("Too many mutations (max {0})")] TooManyMutations(usize), + #[class(type)] #[error("Too many keys (max {0})")] TooManyKeys(usize), + #[class(type)] #[error("limit must be greater than 0")] InvalidLimit, + #[class(type)] #[error("Invalid boundary key")] InvalidBoundaryKey, + #[class(type)] #[error("Key too large for read (max {0} bytes)")] KeyTooLargeToRead(usize), + #[class(type)] #[error("Key too large for write (max {0} bytes)")] KeyTooLargeToWrite(usize), + #[class(type)] #[error("Total mutation size too large (max {0} bytes)")] TotalMutationTooLarge(usize), + #[class(type)] #[error("Total key size too large (max {0} bytes)")] TotalKeyTooLarge(usize), + #[class(inherit)] #[error(transparent)] - Kv(deno_core::error::AnyError), + Kv(JsErrorBox), + #[class(inherit)] #[error(transparent)] Io(#[from] std::io::Error), + #[class(type)] #[error("Queue message not found")] QueueMessageNotFound, + #[class(type)] #[error("Start key is not in the keyspace defined by prefix")] StartKeyNotInKeyspace, + #[class(type)] #[error("End key is not in the keyspace defined by prefix")] EndKeyNotInKeyspace, + #[class(type)] #[error("Start key is greater than end key")] StartKeyGreaterThanEndKey, + #[class(inherit)] #[error("Invalid check")] InvalidCheck(#[source] KvCheckError), + #[class(inherit)] #[error("Invalid mutation")] InvalidMutation(#[source] KvMutationError), + #[class(inherit)] #[error("Invalid enqueue")] InvalidEnqueue(#[source] std::io::Error), + #[class(type)] #[error("key cannot be empty")] - EmptyKey, // TypeError + EmptyKey, + #[class(type)] #[error("Value too large (max {0} bytes)")] - ValueTooLarge(usize), // TypeError + ValueTooLarge(usize), + #[class(type)] #[error("enqueue payload too large (max {0} bytes)")] - EnqueuePayloadTooLarge(usize), // TypeError + EnqueuePayloadTooLarge(usize), + #[class(type)] #[error("invalid cursor")] InvalidCursor, + #[class(type)] #[error("cursor out of bounds")] CursorOutOfBounds, + #[class(type)] #[error("Invalid range")] InvalidRange, } @@ -418,7 +447,7 @@ where match state.resource_table.get::>(rid) { Ok(resource) => resource, Err(err) => { - if get_custom_error_class(&err) == Some("BadResource") { + if err.get_class() == "BadResource" { return Ok(None); } else { return Err(KvErrorKind::Resource(err).into_box()); @@ -568,10 +597,12 @@ where Ok(()) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum KvCheckError { + #[class(type)] #[error("invalid versionstamp")] InvalidVersionstamp, + #[class(inherit)] #[error(transparent)] Io(std::io::Error), } @@ -597,14 +628,22 @@ fn check_from_v8(value: V8KvCheck) -> Result { }) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum KvMutationError { + #[class(generic)] #[error(transparent)] BigInt(#[from] num_bigint::TryFromBigIntError), + #[class(inherit)] #[error(transparent)] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + std::io::Error, + ), + #[class(type)] #[error("Invalid mutation '{0}' with value")] InvalidMutationWithValue(String), + #[class(type)] #[error("Invalid mutation '{0}' without value")] InvalidMutationWithoutValue(String), } diff --git a/ext/kv/remote.rs b/ext/kv/remote.rs index cb408ef644..e5a07ad96c 100644 --- a/ext/kv/remote.rs +++ b/ext/kv/remote.rs @@ -8,10 +8,9 @@ use std::sync::Arc; use anyhow::Context; use async_trait::async_trait; use bytes::Bytes; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::futures::Stream; use deno_core::OpState; +use deno_error::JsErrorBox; use deno_fetch::create_http_client; use deno_fetch::CreateHttpClientOptions; use deno_permissions::PermissionCheckError; @@ -38,7 +37,7 @@ pub struct HttpOptions { } impl HttpOptions { - pub fn root_cert_store(&self) -> Result, AnyError> { + pub fn root_cert_store(&self) -> Result, JsErrorBox> { Ok(match &self.root_cert_store_provider { Some(provider) => Some(provider.get_or_try_init()?.clone()), None => None, @@ -102,12 +101,12 @@ impl Clone for PermissionChecker

{ impl denokv_remote::RemotePermissions for PermissionChecker

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

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

(); - permissions.check_env(ENV_VAR_NAME)?; - permissions.check_net_url(&parsed_url, "Deno.openKv")?; + permissions + .check_env(ENV_VAR_NAME) + .map_err(JsErrorBox::from_err)?; + permissions + .check_net_url(&parsed_url, "Deno.openKv") + .map_err(JsErrorBox::from_err)?; } let access_token = std::env::var(ENV_VAR_NAME) .map_err(anyhow::Error::from) .with_context(|| { "Missing DENO_KV_ACCESS_TOKEN environment variable. Please set it to your access token from https://dash.deno.com/account." - })?; + }).map_err(|e| JsErrorBox::generic(e.to_string()))?; let metadata_endpoint = MetadataEndpoint { url: parsed_url.clone(), @@ -211,7 +229,8 @@ impl DatabaseHandler http2: true, client_builder_hook: None, }, - )?; + ) + .map_err(JsErrorBox::from_err)?; let fetch_client = FetchClient(client); let permissions = PermissionChecker { diff --git a/ext/kv/sqlite.rs b/ext/kv/sqlite.rs index a88c61d723..8be042eef0 100644 --- a/ext/kv/sqlite.rs +++ b/ext/kv/sqlite.rs @@ -14,10 +14,9 @@ use std::sync::Mutex; use std::sync::OnceLock; use async_trait::async_trait; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::unsync::spawn_blocking; use deno_core::OpState; +use deno_error::JsErrorBox; use deno_path_util::normalize_path; use deno_permissions::PermissionCheckError; pub use denokv_sqlite::SqliteBackendError; @@ -85,6 +84,12 @@ impl SqliteDbHandler

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

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

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

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

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

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

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

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

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

{ notifier, config, ) + .map_err(|e| JsErrorBox::generic(e.to_string())) } } /// Same as Path::canonicalize, but also handles non-existing paths. -fn canonicalize_path(path: &Path) -> Result { +fn canonicalize_path(path: &Path) -> Result { let path = normalize_path(path); let mut path = path; let mut names_stack = Vec::new(); @@ -225,7 +239,7 @@ fn canonicalize_path(path: &Path) -> Result { path.clone_from(¤t_dir); } } - Err(err) => return Err(err.into()), + Err(err) => return Err(err), } } } diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index a17fd9e0f6..ac62cc5dc8 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_permissions.workspace = true libc.workspace = true libloading = { version = "0.7" } diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs index 3f495e05e6..1db20ef647 100644 --- a/ext/napi/lib.rs +++ b/ext/napi/lib.rs @@ -24,37 +24,23 @@ pub mod uv; use core::ptr::NonNull; use std::cell::RefCell; use std::collections::HashMap; +pub use std::ffi::CStr; +pub use std::os::raw::c_char; +pub use std::os::raw::c_void; use std::path::PathBuf; +pub use std::ptr; use std::rc::Rc; use std::thread_local; use deno_core::op2; use deno_core::parking_lot::RwLock; use deno_core::url::Url; -use deno_core::ExternalOpsTracker; -use deno_core::OpState; -use deno_core::V8CrossThreadTaskSpawner; - -#[derive(Debug, thiserror::Error)] -pub enum NApiError { - #[error("Invalid path")] - InvalidPath, - #[error(transparent)] - LibLoading(#[from] libloading::Error), - #[error("Unable to find register Node-API module at {}", .0.display())] - ModuleNotFound(PathBuf), - #[error(transparent)] - Permission(#[from] PermissionCheckError), -} - -pub use std::ffi::CStr; -pub use std::os::raw::c_char; -pub use std::os::raw::c_void; -pub use std::ptr; - // Expose common stuff for ease of use. // `use deno_napi::*` pub use deno_core::v8; +use deno_core::ExternalOpsTracker; +use deno_core::OpState; +use deno_core::V8CrossThreadTaskSpawner; use deno_permissions::PermissionCheckError; #[cfg(unix)] use libloading::os::unix::*; @@ -65,6 +51,22 @@ pub use value::napi_value; pub mod function; mod value; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum NApiError { + #[class(type)] + #[error("Invalid path")] + InvalidPath, + #[class(type)] + #[error(transparent)] + LibLoading(#[from] libloading::Error), + #[class(type)] + #[error("Unable to find register Node-API module at {}", .0.display())] + ModuleNotFound(PathBuf), + #[class(inherit)] + #[error(transparent)] + Permission(#[from] PermissionCheckError), +} + pub type napi_status = i32; pub type napi_env = *mut c_void; pub type napi_callback_info = *mut c_void; diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index a834d8c8f3..ad20badb10 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_permissions.workspace = true deno_tls.workspace = true hickory-proto = "0.25.0-alpha.4" diff --git a/ext/net/io.rs b/ext/net/io.rs index 3f12b92a9a..71ae577cd0 100644 --- a/ext/net/io.rs +++ b/ext/net/io.rs @@ -11,6 +11,7 @@ use deno_core::CancelHandle; use deno_core::CancelTryFuture; use deno_core::RcRef; use deno_core::Resource; +use deno_error::JsErrorBox; use socket2::SockRef; use tokio::io::AsyncRead; use tokio::io::AsyncReadExt; @@ -90,10 +91,12 @@ where } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum MapError { + #[class(inherit)] #[error("{0}")] Io(std::io::Error), + #[class(generic)] #[error("Unable to get resources")] NoResources, } @@ -110,7 +113,7 @@ impl Resource for TcpStreamResource { } fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown().map_err(Into::into)) + Box::pin(self.shutdown().map_err(JsErrorBox::from_err)) } fn close(self: Rc) { @@ -162,9 +165,7 @@ impl UnixStreamResource { unreachable!() } #[allow(clippy::unused_async)] - pub async fn shutdown( - self: Rc, - ) -> Result<(), deno_core::error::AnyError> { + pub async fn shutdown(self: Rc) -> Result<(), JsErrorBox> { unreachable!() } pub fn cancel_read_ops(&self) { @@ -181,7 +182,7 @@ impl Resource for UnixStreamResource { } fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown().map_err(Into::into)) + Box::pin(self.shutdown().map_err(JsErrorBox::from_err)) } fn close(self: Rc) { diff --git a/ext/net/lib.rs b/ext/net/lib.rs index 726c51b749..b21da19f30 100644 --- a/ext/net/lib.rs +++ b/ext/net/lib.rs @@ -15,7 +15,6 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use deno_core::error::AnyError; use deno_core::OpState; use deno_permissions::PermissionCheckError; use deno_tls::rustls::RootCertStore; @@ -107,7 +106,9 @@ pub struct DefaultTlsOptions { } impl DefaultTlsOptions { - pub fn root_cert_store(&self) -> Result, AnyError> { + pub fn root_cert_store( + &self, + ) -> Result, deno_error::JsErrorBox> { Ok(match &self.root_cert_store_provider { Some(provider) => Some(provider.get_or_try_init()?.clone()), None => None, diff --git a/ext/net/ops.rs b/ext/net/ops.rs index 1fb0f39280..768dd33135 100644 --- a/ext/net/ops.rs +++ b/ext/net/ops.rs @@ -67,60 +67,87 @@ impl From for IpAddr { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum NetError { + #[class("BadResource")] #[error("Listener has been closed")] ListenerClosed, + #[class("Busy")] #[error("Listener already in use")] ListenerBusy, + #[class("BadResource")] #[error("Socket has been closed")] SocketClosed, + #[class("NotConnected")] #[error("Socket has been closed")] SocketClosedNotConnected, + #[class("Busy")] #[error("Socket already in use")] SocketBusy, + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), + #[class("Busy")] #[error("Another accept task is ongoing")] AcceptTaskOngoing, + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error("{0}")] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(generic)] #[error("No resolved address found")] NoResolvedAddress, + #[class(generic)] #[error("{0}")] AddrParse(#[from] std::net::AddrParseError), + #[class(inherit)] #[error("{0}")] Map(crate::io::MapError), + #[class(inherit)] #[error("{0}")] Canceled(#[from] deno_core::Canceled), + #[class("NotFound")] #[error("{0}")] DnsNotFound(ResolveError), + #[class("NotConnected")] #[error("{0}")] DnsNotConnected(ResolveError), + #[class("TimedOut")] #[error("{0}")] DnsTimedOut(ResolveError), + #[class(generic)] #[error("{0}")] Dns(#[from] ResolveError), + #[class("NotSupported")] #[error("Provided record type is not supported")] UnsupportedRecordType, + #[class("InvalidData")] #[error("File name or path {0:?} is not valid UTF-8")] InvalidUtf8(std::ffi::OsString), + #[class(generic)] #[error("unexpected key type")] UnexpectedKeyType, + #[class(type)] #[error("Invalid hostname: '{0}'")] - InvalidHostname(String), // TypeError + InvalidHostname(String), + #[class("Busy")] #[error("TCP stream is currently in use")] TcpStreamBusy, + #[class(generic)] #[error("{0}")] Rustls(#[from] deno_tls::rustls::Error), + #[class(inherit)] #[error("{0}")] Tls(#[from] deno_tls::TlsError), + #[class("InvalidData")] #[error("Error creating TLS certificate: Deno.listenTls requires a key")] - ListenTlsRequiresKey, // InvalidData + ListenTlsRequiresKey, + #[class(inherit)] #[error("{0}")] - RootCertStore(deno_core::anyhow::Error), + RootCertStore(deno_error::JsErrorBox), + #[class(generic)] #[error("{0}")] Reunite(tokio::net::tcp::ReuniteError), } @@ -713,10 +740,8 @@ pub fn op_set_nodelay_inner( rid: ResourceId, nodelay: bool, ) -> Result<(), NetError> { - let resource: Rc = state - .resource_table - .get::(rid) - .map_err(NetError::Resource)?; + let resource: Rc = + state.resource_table.get::(rid)?; resource.set_nodelay(nodelay).map_err(NetError::Map) } @@ -735,10 +760,8 @@ pub fn op_set_keepalive_inner( rid: ResourceId, keepalive: bool, ) -> Result<(), NetError> { - let resource: Rc = state - .resource_table - .get::(rid) - .map_err(NetError::Resource)?; + let resource: Rc = + state.resource_table.get::(rid)?; resource.set_keepalive(keepalive).map_err(NetError::Map) } diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs index 5b8cd47751..12c6580136 100644 --- a/ext/net/ops_tls.rs +++ b/ext/net/ops_tls.rs @@ -23,6 +23,7 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::JsErrorBox; use deno_tls::create_client_config; use deno_tls::load_certs; use deno_tls::load_private_keys; @@ -163,7 +164,7 @@ impl Resource for TlsStreamResource { } fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown().map_err(Into::into)) + Box::pin(self.shutdown().map_err(JsErrorBox::from_err)) } fn close(self: Rc) { diff --git a/ext/net/quic.rs b/ext/net/quic.rs index 8b06d46d19..e075745495 100644 --- a/ext/net/quic.rs +++ b/ext/net/quic.rs @@ -17,6 +17,7 @@ use std::task::Context; use std::task::Poll; use std::time::Duration; +use deno_core::error::ResourceError; use deno_core::futures::task::noop_waker_ref; use deno_core::op2; use deno_core::AsyncRefCell; @@ -30,6 +31,8 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::WriteOutcome; +use deno_error::JsError; +use deno_error::JsErrorBox; use deno_permissions::PermissionCheckError; use deno_tls::create_client_config; use deno_tls::SocketUse; @@ -49,40 +52,59 @@ use crate::DefaultTlsOptions; use crate::NetPermissions; use crate::UnsafelyIgnoreCertificateErrors; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, JsError)] pub enum QuicError { + #[class(generic)] #[error("Endpoint created by 'connectQuic' cannot be used for listening")] CannotListen, + #[class(type)] #[error("key and cert are required")] MissingTlsKey, + #[class(type)] #[error("Duration is invalid")] InvalidDuration, + #[class(generic)] #[error("Unable to resolve hostname")] UnableToResolve, + #[class(inherit)] #[error("{0}")] StdIo(#[from] std::io::Error), + #[class(inherit)] #[error("{0}")] PermissionCheck(#[from] PermissionCheckError), + #[class(range)] #[error("{0}")] VarIntBoundsExceeded(#[from] quinn::VarIntBoundsExceeded), + #[class(generic)] #[error("{0}")] Rustls(#[from] quinn::rustls::Error), + #[class(inherit)] #[error("{0}")] Tls(#[from] TlsError), + #[class(generic)] #[error("{0}")] ConnectionError(#[from] quinn::ConnectionError), + #[class(generic)] #[error("{0}")] ConnectError(#[from] quinn::ConnectError), + #[class(generic)] #[error("{0}")] SendDatagramError(#[from] quinn::SendDatagramError), + #[class("BadResource")] #[error("{0}")] ClosedStream(#[from] quinn::ClosedStream), + #[class("BadResource")] #[error("Invalid {0} resource")] BadResource(&'static str), + #[class(range)] #[error("Connection has reached the maximum number of concurrent outgoing {0} streams")] MaxStreams(&'static str), + #[class(generic)] #[error("{0}")] Core(#[from] deno_core::error::AnyError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), } #[derive(Serialize, Deserialize)] @@ -658,8 +680,13 @@ impl Resource for SendStreamResource { Box::pin(async move { let mut stream = RcRef::map(self.clone(), |r| &r.stream).borrow_mut().await; - stream.set_priority(self.priority.load(Ordering::Relaxed))?; - let nwritten = stream.write(&view).await?; + stream + .set_priority(self.priority.load(Ordering::Relaxed)) + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))?; + let nwritten = stream + .write(&view) + .await + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))?; Ok(WriteOutcome::Partial { nwritten, view }) }) } @@ -690,7 +717,11 @@ impl Resource for RecvStreamResource { Box::pin(async move { let mut r = RcRef::map(self, |r| &r.stream).borrow_mut().await; let mut data = vec![0; limit]; - let nread = r.read(&mut data).await?.unwrap_or(0); + let nread = r + .read(&mut data) + .await + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))? + .unwrap_or(0); data.truncate(nread); Ok(BufView::from(data)) }) @@ -702,7 +733,11 @@ impl Resource for RecvStreamResource { ) -> AsyncResult<(usize, BufMutView)> { Box::pin(async move { let mut r = RcRef::map(self, |r| &r.stream).borrow_mut().await; - let nread = r.read(&mut buf).await?.unwrap_or(0); + let nread = r + .read(&mut buf) + .await + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))? + .unwrap_or(0); Ok((nread, buf)) }) } @@ -710,7 +745,8 @@ impl Resource for RecvStreamResource { fn shutdown(self: Rc) -> AsyncResult<()> { Box::pin(async move { let mut r = RcRef::map(self, |r| &r.stream).borrow_mut().await; - r.stop(quinn::VarInt::from(0u32))?; + r.stop(quinn::VarInt::from(0u32)) + .map_err(|e| JsErrorBox::from_err(std::io::Error::from(e)))?; Ok(()) }) } @@ -835,15 +871,15 @@ pub(crate) async fn op_quic_connection_read_datagram( #[op2(fast)] pub(crate) fn op_quic_connection_get_max_datagram_size( #[cppgc] connection: &ConnectionResource, -) -> Result { - Ok(connection.0.max_datagram_size().unwrap_or(0) as _) +) -> u32 { + connection.0.max_datagram_size().unwrap_or(0) as _ } #[op2(fast)] pub(crate) fn op_quic_send_stream_get_priority( state: Rc>, #[smi] rid: ResourceId, -) -> Result { +) -> Result { let resource = state .borrow() .resource_table @@ -856,7 +892,7 @@ pub(crate) fn op_quic_send_stream_set_priority( state: Rc>, #[smi] rid: ResourceId, priority: i32, -) -> Result<(), QuicError> { +) -> Result<(), ResourceError> { let resource = state .borrow() .resource_table @@ -870,7 +906,7 @@ pub(crate) fn op_quic_send_stream_set_priority( pub(crate) fn op_quic_send_stream_get_id( state: Rc>, #[smi] rid: ResourceId, -) -> Result { +) -> Result { let resource = state .borrow() .resource_table @@ -884,7 +920,7 @@ pub(crate) fn op_quic_send_stream_get_id( pub(crate) fn op_quic_recv_stream_get_id( state: Rc>, #[smi] rid: ResourceId, -) -> Result { +) -> Result { let resource = state .borrow() .resource_table diff --git a/ext/net/raw.rs b/ext/net/raw.rs index 0312d661a5..fc380635f6 100644 --- a/ext/net/raw.rs +++ b/ext/net/raw.rs @@ -1,15 +1,15 @@ // Copyright 2018-2025 the Deno authors. MIT license. + use std::borrow::Cow; use std::rc::Rc; -use deno_core::error::bad_resource_id; -use deno_core::error::custom_error; -use deno_core::error::AnyError; +use deno_core::error::ResourceError; use deno_core::AsyncRefCell; use deno_core::CancelHandle; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ResourceTable; +use deno_error::JsErrorBox; use crate::io::TcpStreamResource; use crate::ops_tls::TlsStreamResource; @@ -69,10 +69,10 @@ impl NetworkListenerResource { fn take( resource_table: &mut ResourceTable, listener_rid: ResourceId, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { if let Ok(resource_rc) = resource_table.take::(listener_rid) { let resource = Rc::try_unwrap(resource_rc) - .map_err(|_| custom_error("Busy", "Listener is currently in use"))?; + .map_err(|_| JsErrorBox::new("Busy", "Listener is currently in use"))?; return Ok(Some(resource.listener.into_inner().into())); } Ok(None) @@ -243,13 +243,13 @@ macro_rules! network_stream { /// Return a `NetworkStreamListener` if a resource exists for this `ResourceId` and it is currently /// not locked. - pub fn take_resource(resource_table: &mut ResourceTable, listener_rid: ResourceId) -> Result { + pub fn take_resource(resource_table: &mut ResourceTable, listener_rid: ResourceId) -> Result { $( if let Some(resource) = NetworkListenerResource::<$listener>::take(resource_table, listener_rid)? { return Ok(resource) } )* - Err(bad_resource_id()) + Err(JsErrorBox::from_err(ResourceError::BadResourceId)) } } }; @@ -322,12 +322,36 @@ impl From for NetworkStreamAddress { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum TakeNetworkStreamError { + #[class("Busy")] + #[error("TCP stream is currently in use")] + TcpBusy, + #[class("Busy")] + #[error("TLS stream is currently in use")] + TlsBusy, + #[cfg(unix)] + #[class("Busy")] + #[error("Unix socket is currently in use")] + UnixBusy, + #[class(generic)] + #[error(transparent)] + ReuniteTcp(#[from] tokio::net::tcp::ReuniteError), + #[cfg(unix)] + #[class(generic)] + #[error(transparent)] + ReuniteUnix(#[from] tokio::net::unix::ReuniteError), + #[class(inherit)] + #[error(transparent)] + Resource(deno_core::error::ResourceError), +} + /// In some cases it may be more efficient to extract the resource from the resource table and use it directly (for example, an HTTP server). /// This method will extract a stream from the resource table and return it, unwrapped. pub fn take_network_stream_resource( resource_table: &mut ResourceTable, stream_rid: ResourceId, -) -> Result { +) -> Result { // The stream we're attempting to unwrap may be in use somewhere else. If that's the case, we cannot proceed // with the process of unwrapping this connection, so we just return a bad resource error. // See also: https://github.com/denoland/deno/pull/16242 @@ -336,7 +360,7 @@ pub fn take_network_stream_resource( { // This TCP connection might be used somewhere else. let resource = Rc::try_unwrap(resource_rc) - .map_err(|_| custom_error("Busy", "TCP stream is currently in use"))?; + .map_err(|_| TakeNetworkStreamError::TcpBusy)?; let (read_half, write_half) = resource.into_inner(); let tcp_stream = read_half.reunite(write_half)?; return Ok(NetworkStream::Tcp(tcp_stream)); @@ -346,7 +370,7 @@ pub fn take_network_stream_resource( { // This TLS connection might be used somewhere else. let resource = Rc::try_unwrap(resource_rc) - .map_err(|_| custom_error("Busy", "TLS stream is currently in use"))?; + .map_err(|_| TakeNetworkStreamError::TlsBusy)?; let (read_half, write_half) = resource.into_inner(); let tls_stream = read_half.unsplit(write_half); return Ok(NetworkStream::Tls(tls_stream)); @@ -358,13 +382,15 @@ pub fn take_network_stream_resource( { // This UNIX socket might be used somewhere else. let resource = Rc::try_unwrap(resource_rc) - .map_err(|_| custom_error("Busy", "Unix socket is currently in use"))?; + .map_err(|_| TakeNetworkStreamError::UnixBusy)?; let (read_half, write_half) = resource.into_inner(); let unix_stream = read_half.reunite(write_half)?; return Ok(NetworkStream::Unix(unix_stream)); } - Err(bad_resource_id()) + Err(TakeNetworkStreamError::Resource( + ResourceError::BadResourceId, + )) } /// In some cases it may be more efficient to extract the resource from the resource table and use it directly (for example, an HTTP server). @@ -372,6 +398,6 @@ pub fn take_network_stream_resource( pub fn take_network_stream_listener_resource( resource_table: &mut ResourceTable, listener_rid: ResourceId, -) -> Result { +) -> Result { NetworkStreamListener::take_resource(resource_table, listener_rid) } diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 0f6ae1d7ab..19936b74f1 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -29,6 +29,7 @@ cbc.workspace = true const-oid = "0.9.5" data-encoding.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_fetch.workspace = true deno_fs.workspace = true deno_io.workspace = true diff --git a/ext/node/lib.rs b/ext/node/lib.rs index d7aa82430d..64b6c006a1 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -8,12 +8,12 @@ use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::url::Url; #[allow(unused_imports)] use deno_core::v8; use deno_core::v8::ExternalReference; +use deno_error::JsErrorBox; use node_resolver::errors::ClosestPkgJsonError; use node_resolver::IsBuiltInNodeModuleChecker; use node_resolver::NpmPackageFolderResolverRc; @@ -157,12 +157,12 @@ pub trait NodeRequireLoader { &self, permissions: &mut dyn NodePermissions, path: &'a Path, - ) -> Result, AnyError>; + ) -> Result, JsErrorBox>; fn load_text_file_lossy( &self, path: &Path, - ) -> Result, AnyError>; + ) -> Result, JsErrorBox>; /// Get if the module kind is maybe CJS and loading should determine /// if its CJS or ESM. diff --git a/ext/node/ops/blocklist.rs b/ext/node/ops/blocklist.rs index bcb36fc97b..16bda73fe0 100644 --- a/ext/node/ops/blocklist.rs +++ b/ext/node/ops/blocklist.rs @@ -23,7 +23,8 @@ impl deno_core::GarbageCollected for BlockListResource {} #[derive(Serialize)] struct SocketAddressSerialization(String, String); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] pub enum BlocklistError { #[error("{0}")] AddrParse(#[from] std::net::AddrParseError), diff --git a/ext/node/ops/buffer.rs b/ext/node/ops/buffer.rs index e3ae2b2391..0e8cff5cc0 100644 --- a/ext/node/ops/buffer.rs +++ b/ext/node/ops/buffer.rs @@ -1,8 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::anyhow::anyhow; -use deno_core::anyhow::Result; use deno_core::op2; +use deno_error::JsErrorBox; #[op2(fast)] pub fn op_is_ascii(#[buffer] buf: &[u8]) -> bool { @@ -20,7 +19,7 @@ pub fn op_transcode( #[buffer] source: &[u8], #[string] from_encoding: &str, #[string] to_encoding: &str, -) -> Result> { +) -> Result, JsErrorBox> { match (from_encoding, to_encoding) { ("utf8", "ascii") => Ok(utf8_to_ascii(source)), ("utf8", "latin1") => Ok(utf8_to_latin1(source)), @@ -29,7 +28,9 @@ pub fn op_transcode( ("latin1", "utf16le") | ("ascii", "utf16le") => { Ok(latin1_ascii_to_utf16le(source)) } - (from, to) => Err(anyhow!("Unable to transcode Buffer {from}->{to}")), + (from, to) => Err(JsErrorBox::generic(format!( + "Unable to transcode Buffer {from}->{to}" + ))), } } @@ -42,18 +43,19 @@ fn latin1_ascii_to_utf16le(source: &[u8]) -> Vec { result } -fn utf16le_to_utf8(source: &[u8]) -> Result> { +fn utf16le_to_utf8(source: &[u8]) -> Result, JsErrorBox> { let ucs2_vec: Vec = source .chunks(2) .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) .collect(); String::from_utf16(&ucs2_vec) .map(|utf8_string| utf8_string.into_bytes()) - .map_err(|e| anyhow!("Invalid UTF-16 sequence: {}", e)) + .map_err(|e| JsErrorBox::generic(format!("Invalid UTF-16 sequence: {}", e))) } -fn utf8_to_utf16le(source: &[u8]) -> Result> { - let utf8_string = std::str::from_utf8(source)?; +fn utf8_to_utf16le(source: &[u8]) -> Result, JsErrorBox> { + let utf8_string = + std::str::from_utf8(source).map_err(JsErrorBox::from_err)?; let ucs2_vec: Vec = utf8_string.encode_utf16().collect(); let bytes: Vec = ucs2_vec.iter().flat_map(|&x| x.to_le_bytes()).collect(); Ok(bytes) diff --git a/ext/node/ops/crypto/cipher.rs b/ext/node/ops/crypto/cipher.rs index 500d8d1b4b..a12f36e04a 100644 --- a/ext/node/ops/crypto/cipher.rs +++ b/ext/node/ops/crypto/cipher.rs @@ -47,12 +47,15 @@ pub struct DecipherContext { decipher: Rc>, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CipherContextError { + #[class(type)] #[error("Cipher context is already in use")] ContextInUse, + #[class(inherit)] #[error("{0}")] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Cipher(#[from] CipherError), } @@ -94,12 +97,15 @@ impl CipherContext { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum DecipherContextError { + #[class(type)] #[error("Decipher context is already in use")] ContextInUse, + #[class(inherit)] #[error("{0}")] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Decipher(#[from] DecipherError), } @@ -150,16 +156,21 @@ impl Resource for DecipherContext { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CipherError { + #[class(type)] #[error("IV length must be 12 bytes")] InvalidIvLength, + #[class(range)] #[error("Invalid key length")] InvalidKeyLength, + #[class(type)] #[error("Invalid initialization vector")] InvalidInitializationVector, + #[class(type)] #[error("Cannot pad the input data")] CannotPadInputData, + #[class(type)] #[error("Unknown cipher {0}")] UnknownCipher(String), } @@ -352,22 +363,30 @@ impl Cipher { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum DecipherError { + #[class(type)] #[error("IV length must be 12 bytes")] InvalidIvLength, + #[class(range)] #[error("Invalid key length")] InvalidKeyLength, + #[class(type)] #[error("Invalid initialization vector")] InvalidInitializationVector, + #[class(type)] #[error("Cannot unpad the input data")] CannotUnpadInputData, + #[class(type)] #[error("Failed to authenticate data")] DataAuthenticationFailed, + #[class(type)] #[error("setAutoPadding(false) not supported for Aes128Gcm yet")] SetAutoPaddingFalseAes128GcmUnsupported, + #[class(type)] #[error("setAutoPadding(false) not supported for Aes256Gcm yet")] SetAutoPaddingFalseAes256GcmUnsupported, + #[class(type)] #[error("Unknown cipher {0}")] UnknownCipher(String), } diff --git a/ext/node/ops/crypto/digest.rs b/ext/node/ops/crypto/digest.rs index f9810e8c67..5f15dace30 100644 --- a/ext/node/ops/crypto/digest.rs +++ b/ext/node/ops/crypto/digest.rs @@ -183,7 +183,8 @@ pub enum Hash { use Hash::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] pub enum HashError { #[error("Output length mismatch for non-extendable algorithm")] OutputLengthMismatch, diff --git a/ext/node/ops/crypto/keys.rs b/ext/node/ops/crypto/keys.rs index d982d96a8c..79b09faa26 100644 --- a/ext/node/ops/crypto/keys.rs +++ b/ext/node/ops/crypto/keys.rs @@ -4,12 +4,12 @@ use std::borrow::Cow; use std::cell::RefCell; use base64::Engine; -use deno_core::error::type_error; use deno_core::op2; use deno_core::serde_v8::BigInt as V8BigInt; use deno_core::unsync::spawn_blocking; use deno_core::GarbageCollected; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use ed25519_dalek::pkcs8::BitStringRef; use elliptic_curve::JwkEcKey; use num_bigint::BigInt; @@ -375,55 +375,72 @@ impl<'a> TryFrom> for RsaPssParameters<'a> { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum X509PublicKeyError { + #[class(generic)] #[error(transparent)] - X509(#[from] x509_parser::error::X509Error), + X509(#[from] X509Error), + #[class(generic)] #[error(transparent)] Rsa(#[from] rsa::Error), + #[class(generic)] #[error(transparent)] Asn1(#[from] x509_parser::der_parser::asn1_rs::Error), + #[class(generic)] #[error(transparent)] Ec(#[from] elliptic_curve::Error), + #[class(type)] #[error("unsupported ec named curve")] UnsupportedEcNamedCurve, + #[class(type)] #[error("missing ec parameters")] MissingEcParameters, + #[class(type)] #[error("malformed DSS public key")] MalformedDssPublicKey, + #[class(type)] #[error("unsupported x509 public key type")] UnsupportedX509KeyType, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum RsaJwkError { + #[class(generic)] #[error(transparent)] Base64(#[from] base64::DecodeError), + #[class(generic)] #[error(transparent)] Rsa(#[from] rsa::Error), + #[class(type)] #[error("missing RSA private component")] MissingRsaPrivateComponent, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum EcJwkError { + #[class(generic)] #[error(transparent)] Ec(#[from] elliptic_curve::Error), + #[class(type)] #[error("unsupported curve: {0}")] UnsupportedCurve(String), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum EdRawError { + #[class(generic)] #[error(transparent)] Ed25519Signature(#[from] ed25519_dalek::SignatureError), + #[class(type)] #[error("invalid Ed25519 key")] InvalidEd25519Key, + #[class(type)] #[error("unsupported curve")] UnsupportedCurve, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum AsymmetricPrivateKeyError { #[error("invalid PEM private key: not valid utf8 starting at byte {0}")] InvalidPemPrivateKeyInvalidUtf8(usize), @@ -439,8 +456,13 @@ pub enum AsymmetricPrivateKeyError { InvalidSec1PrivateKey, #[error("unsupported PEM label: {0}")] UnsupportedPemLabel(String), + #[class(inherit)] #[error(transparent)] - RsaPssParamsParse(#[from] RsaPssParamsParseError), + RsaPssParamsParse( + #[from] + #[inherit] + RsaPssParamsParseError, + ), #[error("invalid encrypted PKCS#8 private key")] InvalidEncryptedPkcs8PrivateKey, #[error("invalid PKCS#8 private key")] @@ -473,58 +495,96 @@ pub enum AsymmetricPrivateKeyError { UnsupportedPrivateKeyOid, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum AsymmetricPublicKeyError { + #[class(type)] #[error("invalid PEM private key: not valid utf8 starting at byte {0}")] InvalidPemPrivateKeyInvalidUtf8(usize), + #[class(type)] #[error("invalid PEM public key")] InvalidPemPublicKey, + #[class(type)] #[error("invalid PKCS#1 public key")] InvalidPkcs1PublicKey, + #[class(inherit)] #[error(transparent)] - AsymmetricPrivateKey(#[from] AsymmetricPrivateKeyError), + AsymmetricPrivateKey( + #[from] + #[inherit] + AsymmetricPrivateKeyError, + ), + #[class(type)] #[error("invalid x509 certificate")] InvalidX509Certificate, + #[class(generic)] #[error(transparent)] X509(#[from] x509_parser::nom::Err), + #[class(inherit)] #[error(transparent)] - X509PublicKey(#[from] X509PublicKeyError), + X509PublicKey( + #[from] + #[inherit] + X509PublicKeyError, + ), + #[class(type)] #[error("unsupported PEM label: {0}")] UnsupportedPemLabel(String), + #[class(type)] #[error("invalid SPKI public key")] InvalidSpkiPublicKey, + #[class(type)] #[error("unsupported key type: {0}")] UnsupportedKeyType(String), + #[class(type)] #[error("unsupported key format: {0}")] UnsupportedKeyFormat(String), + #[class(generic)] #[error(transparent)] Spki(#[from] spki::Error), + #[class(generic)] #[error(transparent)] Pkcs1(#[from] rsa::pkcs1::Error), + #[class(inherit)] #[error(transparent)] - RsaPssParamsParse(#[from] RsaPssParamsParseError), + RsaPssParamsParse( + #[from] + #[inherit] + RsaPssParamsParseError, + ), + #[class(type)] #[error("malformed DSS public key")] MalformedDssPublicKey, + #[class(type)] #[error("malformed or missing named curve in ec parameters")] MalformedOrMissingNamedCurveInEcParameters, + #[class(type)] #[error("malformed or missing public key in ec spki")] MalformedOrMissingPublicKeyInEcSpki, + #[class(generic)] #[error(transparent)] Ec(#[from] elliptic_curve::Error), + #[class(type)] #[error("unsupported ec named curve")] UnsupportedEcNamedCurve, + #[class(type)] #[error("malformed or missing public key in x25519 spki")] MalformedOrMissingPublicKeyInX25519Spki, + #[class(type)] #[error("x25519 public key is too short")] X25519PublicKeyIsTooShort, + #[class(type)] #[error("invalid Ed25519 public key")] InvalidEd25519PublicKey, + #[class(type)] #[error("missing dh parameters")] MissingDhParameters, + #[class(type)] #[error("malformed dh parameters")] MalformedDhParameters, + #[class(type)] #[error("malformed or missing public key in dh spki")] MalformedOrMissingPublicKeyInDhSpki, + #[class(type)] #[error("unsupported private key oid")] UnsupportedPrivateKeyOid, } @@ -1043,7 +1103,8 @@ impl KeyObjectHandle { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum RsaPssParamsParseError { #[error("malformed pss private key parameters")] MalformedPssPrivateKeyParameters, @@ -1118,7 +1179,8 @@ fn bytes_to_b64(bytes: &[u8]) -> String { BASE64_URL_SAFE_NO_PAD.encode(bytes) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum AsymmetricPrivateKeyJwkError { #[error("key is not an asymmetric private key")] KeyIsNotAsymmetricPrivateKey, @@ -1128,7 +1190,8 @@ pub enum AsymmetricPrivateKeyJwkError { JwkExportNotImplementedForKeyType, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum AsymmetricPublicKeyJwkError { #[error("key is not an asymmetric public key")] KeyIsNotAsymmetricPublicKey, @@ -1138,7 +1201,8 @@ pub enum AsymmetricPublicKeyJwkError { JwkExportNotImplementedForKeyType, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum AsymmetricPublicKeyDerError { #[error("key is not an asymmetric public key")] KeyIsNotAsymmetricPublicKey, @@ -1323,7 +1387,8 @@ impl AsymmetricPublicKey { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum AsymmetricPrivateKeyDerError { #[error("key is not an asymmetric private key")] KeyIsNotAsymmetricPrivateKey, @@ -1335,6 +1400,7 @@ pub enum AsymmetricPrivateKeyDerError { InvalidEcPrivateKey, #[error("exporting non-EC private key as SEC1 is not supported")] ExportingNonEcPrivateKeyAsSec1Unsupported, + #[class(type)] #[error("exporting RSA-PSS private key as PKCS#8 is not supported yet")] ExportingNonRsaPssPrivateKeyAsPkcs8Unsupported, #[error("invalid DSA private key")] @@ -1615,7 +1681,7 @@ pub fn op_node_create_secret_key( #[string] pub fn op_node_get_asymmetric_key_type( #[cppgc] handle: &KeyObjectHandle, -) -> Result<&'static str, deno_core::error::AnyError> { +) -> Result<&'static str, JsErrorBox> { match handle { KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Rsa(_)) | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Rsa(_)) => { @@ -1641,9 +1707,9 @@ pub fn op_node_get_asymmetric_key_type( } KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Dh(_)) | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Dh(_)) => Ok("dh"), - KeyObjectHandle::Secret(_) => { - Err(type_error("symmetric key is not an asymmetric key")) - } + KeyObjectHandle::Secret(_) => Err(JsErrorBox::type_error( + "symmetric key is not an asymmetric key", + )), } } @@ -1686,7 +1752,7 @@ pub enum AsymmetricKeyDetails { #[serde] pub fn op_node_get_asymmetric_key_details( #[cppgc] handle: &KeyObjectHandle, -) -> Result { +) -> Result { match handle { KeyObjectHandle::AsymmetricPrivate(private_key) => match private_key { AsymmetricPrivateKey::Rsa(key) => { @@ -1794,9 +1860,9 @@ pub fn op_node_get_asymmetric_key_details( AsymmetricPublicKey::Ed25519(_) => Ok(AsymmetricKeyDetails::Ed25519), AsymmetricPublicKey::Dh(_) => Ok(AsymmetricKeyDetails::Dh), }, - KeyObjectHandle::Secret(_) => { - Err(type_error("symmetric key is not an asymmetric key")) - } + KeyObjectHandle::Secret(_) => Err(JsErrorBox::type_error( + "symmetric key is not an asymmetric key", + )), } } @@ -1804,12 +1870,12 @@ pub fn op_node_get_asymmetric_key_details( #[smi] pub fn op_node_get_symmetric_key_size( #[cppgc] handle: &KeyObjectHandle, -) -> Result { +) -> Result { match handle { KeyObjectHandle::AsymmetricPrivate(_) - | KeyObjectHandle::AsymmetricPublic(_) => { - Err(type_error("asymmetric key is not a symmetric key")) - } + | KeyObjectHandle::AsymmetricPublic(_) => Err(JsErrorBox::type_error( + "asymmetric key is not a symmetric key", + )), KeyObjectHandle::Secret(key) => Ok(key.len() * 8), } } @@ -1912,7 +1978,8 @@ pub async fn op_node_generate_rsa_key_async( .unwrap() } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] #[error("digest not allowed for RSA-PSS keys{}", .0.as_ref().map(|digest| format!(": {digest}")).unwrap_or_default())] pub struct GenerateRsaPssError(Option); @@ -2016,7 +2083,7 @@ pub async fn op_node_generate_rsa_pss_key_async( fn dsa_generate( modulus_length: usize, divisor_length: usize, -) -> Result { +) -> Result { let mut rng = rand::thread_rng(); use dsa::Components; use dsa::KeySize; @@ -2029,7 +2096,7 @@ fn dsa_generate( (2048, 256) => KeySize::DSA_2048_256, (3072, 256) => KeySize::DSA_3072_256, _ => { - return Err(type_error( + return Err(JsErrorBox::type_error( "Invalid modulusLength+divisorLength combination", )) } @@ -2047,7 +2114,7 @@ fn dsa_generate( pub fn op_node_generate_dsa_key( #[smi] modulus_length: usize, #[smi] divisor_length: usize, -) -> Result { +) -> Result { dsa_generate(modulus_length, divisor_length) } @@ -2056,15 +2123,13 @@ pub fn op_node_generate_dsa_key( pub async fn op_node_generate_dsa_key_async( #[smi] modulus_length: usize, #[smi] divisor_length: usize, -) -> Result { +) -> Result { spawn_blocking(move || dsa_generate(modulus_length, divisor_length)) .await .unwrap() } -fn ec_generate( - named_curve: &str, -) -> Result { +fn ec_generate(named_curve: &str) -> Result { let mut rng = rand::thread_rng(); // TODO(@littledivy): Support public key point encoding. // Default is uncompressed. @@ -2082,7 +2147,7 @@ fn ec_generate( AsymmetricPrivateKey::Ec(EcPrivateKey::P384(key)) } _ => { - return Err(type_error(format!( + return Err(JsErrorBox::type_error(format!( "unsupported named curve: {}", named_curve ))) @@ -2096,7 +2161,7 @@ fn ec_generate( #[cppgc] pub fn op_node_generate_ec_key( #[string] named_curve: &str, -) -> Result { +) -> Result { ec_generate(named_curve) } @@ -2104,7 +2169,7 @@ pub fn op_node_generate_ec_key( #[cppgc] pub async fn op_node_generate_ec_key_async( #[string] named_curve: String, -) -> Result { +) -> Result { spawn_blocking(move || ec_generate(&named_curve)) .await .unwrap() @@ -2160,7 +2225,7 @@ fn u32_slice_to_u8_slice(slice: &[u32]) -> &[u8] { fn dh_group_generate( group_name: &str, -) -> Result { +) -> Result { let (dh, prime, generator) = match group_name { "modp5" => ( dh::DiffieHellman::group::(), @@ -2192,7 +2257,7 @@ fn dh_group_generate( dh::Modp8192::MODULUS, dh::Modp8192::GENERATOR, ), - _ => return Err(type_error("Unsupported group name")), + _ => return Err(JsErrorBox::type_error("Unsupported group name")), }; let params = DhParameter { prime: asn1::Int::new(u32_slice_to_u8_slice(prime)).unwrap(), @@ -2215,7 +2280,7 @@ fn dh_group_generate( #[cppgc] pub fn op_node_generate_dh_group_key( #[string] group_name: &str, -) -> Result { +) -> Result { dh_group_generate(group_name) } @@ -2223,7 +2288,7 @@ pub fn op_node_generate_dh_group_key( #[cppgc] pub async fn op_node_generate_dh_group_key_async( #[string] group_name: String, -) -> Result { +) -> Result { spawn_blocking(move || dh_group_generate(&group_name)) .await .unwrap() @@ -2297,10 +2362,10 @@ pub fn op_node_dh_keys_generate_and_export( #[buffer] pub fn op_node_export_secret_key( #[cppgc] handle: &KeyObjectHandle, -) -> Result, deno_core::error::AnyError> { +) -> Result, JsErrorBox> { let key = handle .as_secret_key() - .ok_or_else(|| type_error("key is not a secret key"))?; + .ok_or_else(|| JsErrorBox::type_error("key is not a secret key"))?; Ok(key.to_vec().into_boxed_slice()) } @@ -2308,10 +2373,10 @@ pub fn op_node_export_secret_key( #[string] pub fn op_node_export_secret_key_b64url( #[cppgc] handle: &KeyObjectHandle, -) -> Result { +) -> Result { let key = handle .as_secret_key() - .ok_or_else(|| type_error("key is not a secret key"))?; + .ok_or_else(|| JsErrorBox::type_error("key is not a secret key"))?; Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(key)) } @@ -2327,12 +2392,19 @@ pub fn op_node_export_public_key_jwk( public_key.export_jwk() } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ExportPublicKeyPemError { + #[class(inherit)] #[error(transparent)] - AsymmetricPublicKeyDer(#[from] AsymmetricPublicKeyDerError), + AsymmetricPublicKeyDer( + #[from] + #[inherit] + AsymmetricPublicKeyDerError, + ), + #[class(type)] #[error("very large data")] VeryLargeData, + #[class(generic)] #[error(transparent)] Der(#[from] der::Error), } @@ -2377,12 +2449,19 @@ pub fn op_node_export_public_key_der( public_key.export_der(typ) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ExportPrivateKeyPemError { + #[class(inherit)] #[error(transparent)] - AsymmetricPublicKeyDer(#[from] AsymmetricPrivateKeyDerError), + AsymmetricPublicKeyDer( + #[from] + #[inherit] + AsymmetricPrivateKeyDerError, + ), + #[class(type)] #[error("very large data")] VeryLargeData, + #[class(generic)] #[error(transparent)] Der(#[from] der::Error), } @@ -2416,12 +2495,15 @@ pub fn op_node_export_private_key_pem( Ok(String::from_utf8(out).expect("invalid pem is not possible")) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ExportPrivateKeyJwkError { + #[class(inherit)] #[error(transparent)] AsymmetricPublicKeyJwk(#[from] AsymmetricPrivateKeyJwkError), + #[class(type)] #[error("very large data")] VeryLargeData, + #[class(generic)] #[error(transparent)] Der(#[from] der::Error), } @@ -2464,9 +2546,9 @@ pub fn op_node_key_type(#[cppgc] handle: &KeyObjectHandle) -> &'static str { #[cppgc] pub fn op_node_derive_public_key_from_private_key( #[cppgc] handle: &KeyObjectHandle, -) -> Result { +) -> Result { let Some(private_key) = handle.as_private_key() else { - return Err(type_error("expected private key")); + return Err(JsErrorBox::type_error("expected private key")); }; Ok(KeyObjectHandle::AsymmetricPublic( diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index 19a0bbfc40..8c6b571316 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -2,14 +2,13 @@ use std::future::Future; use std::rc::Rc; -use deno_core::error::generic_error; -use deno_core::error::type_error; use deno_core::op2; use deno_core::unsync::spawn_blocking; use deno_core::JsBuffer; use deno_core::OpState; use deno_core::StringOrBuffer; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use elliptic_curve::sec1::ToEncodedPoint; use hkdf::Hkdf; use keys::AsymmetricPrivateKey; @@ -141,16 +140,21 @@ pub fn op_node_hash_clone( hasher.clone_inner(output_length.map(|l| l as usize)) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PrivateEncryptDecryptError { + #[class(generic)] #[error(transparent)] Pkcs8(#[from] pkcs8::Error), + #[class(generic)] #[error(transparent)] Spki(#[from] spki::Error), + #[class(generic)] #[error(transparent)] Utf8(#[from] std::str::Utf8Error), + #[class(generic)] #[error(transparent)] Rsa(#[from] rsa::Error), + #[class(type)] #[error("Unknown padding")] UnknownPadding, } @@ -269,10 +273,7 @@ pub fn op_node_cipheriv_final( #[buffer] input: &[u8], #[anybuffer] output: &mut [u8], ) -> Result>, cipher::CipherContextError> { - let context = state - .resource_table - .take::(rid) - .map_err(cipher::CipherContextError::Resource)?; + let context = state.resource_table.take::(rid)?; let context = Rc::try_unwrap(context) .map_err(|_| cipher::CipherContextError::ContextInUse)?; context.r#final(auto_pad, input, output).map_err(Into::into) @@ -284,10 +285,7 @@ pub fn op_node_cipheriv_take( state: &mut OpState, #[smi] rid: u32, ) -> Result>, cipher::CipherContextError> { - let context = state - .resource_table - .take::(rid) - .map_err(cipher::CipherContextError::Resource)?; + let context = state.resource_table.take::(rid)?; let context = Rc::try_unwrap(context) .map_err(|_| cipher::CipherContextError::ContextInUse)?; Ok(context.take_tag()) @@ -339,10 +337,7 @@ pub fn op_node_decipheriv_take( state: &mut OpState, #[smi] rid: u32, ) -> Result<(), cipher::DecipherContextError> { - let context = state - .resource_table - .take::(rid) - .map_err(cipher::DecipherContextError::Resource)?; + let context = state.resource_table.take::(rid)?; Rc::try_unwrap(context) .map_err(|_| cipher::DecipherContextError::ContextInUse)?; Ok(()) @@ -357,10 +352,7 @@ pub fn op_node_decipheriv_final( #[anybuffer] output: &mut [u8], #[buffer] auth_tag: &[u8], ) -> Result<(), cipher::DecipherContextError> { - let context = state - .resource_table - .take::(rid) - .map_err(cipher::DecipherContextError::Resource)?; + let context = state.resource_table.take::(rid)?; let context = Rc::try_unwrap(context) .map_err(|_| cipher::DecipherContextError::ContextInUse)?; context @@ -403,10 +395,12 @@ pub fn op_node_verify( ) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum Pbkdf2Error { + #[class(type)] #[error("unsupported digest: {0}")] UnsupportedDigest(String), + #[class(inherit)] #[error(transparent)] Join(#[from] tokio::task::JoinError), } @@ -475,14 +469,18 @@ pub async fn op_node_fill_random_async(#[smi] len: i32) -> ToJsBuffer { .unwrap() } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HkdfError { + #[class(type)] #[error("expected secret key")] ExpectedSecretKey, + #[class(type)] #[error("HKDF-Expand failed")] HkdfExpandFailed, + #[class(type)] #[error("Unsupported digest: {0}")] UnsupportedDigest(String), + #[class(inherit)] #[error(transparent)] Join(#[from] tokio::task::JoinError), } @@ -576,7 +574,7 @@ fn scrypt( parallelization: u32, _maxmem: u32, output_buffer: &mut [u8], -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), JsErrorBox> { // Construct Params let params = scrypt::Params::new( cost as u8, @@ -592,7 +590,7 @@ fn scrypt( Ok(()) } else { // TODO(lev): key derivation failed, so what? - Err(generic_error("scrypt key derivation failed")) + Err(JsErrorBox::generic("scrypt key derivation failed")) } } @@ -607,7 +605,7 @@ pub fn op_node_scrypt_sync( #[smi] parallelization: u32, #[smi] maxmem: u32, #[anybuffer] output_buffer: &mut [u8], -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), JsErrorBox> { scrypt( password, salt, @@ -620,12 +618,14 @@ pub fn op_node_scrypt_sync( ) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ScryptAsyncError { + #[class(inherit)] #[error(transparent)] Join(#[from] tokio::task::JoinError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(JsErrorBox), } #[op2(async)] @@ -658,12 +658,15 @@ pub async fn op_node_scrypt_async( .await? } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum EcdhEncodePubKey { + #[class(type)] #[error("Invalid public key")] InvalidPublicKey, + #[class(type)] #[error("Unsupported curve")] UnsupportedCurve, + #[class(generic)] #[error(transparent)] Sec1(#[from] sec1::Error), } @@ -743,7 +746,7 @@ pub fn op_node_ecdh_generate_keys( #[buffer] pubbuf: &mut [u8], #[buffer] privbuf: &mut [u8], #[string] format: &str, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), JsErrorBox> { let mut rng = rand::thread_rng(); let compress = format == "compressed"; match curve { @@ -780,7 +783,10 @@ pub fn op_node_ecdh_generate_keys( Ok(()) } - &_ => Err(type_error(format!("Unsupported curve: {}", curve))), + &_ => Err(JsErrorBox::type_error(format!( + "Unsupported curve: {}", + curve + ))), } } @@ -913,7 +919,8 @@ pub async fn op_node_gen_prime_async( spawn_blocking(move || gen_prime(size)).await } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum DiffieHellmanError { #[error("Expected private key")] ExpectedPrivateKey, @@ -1005,7 +1012,8 @@ pub fn op_node_diffie_hellman( Ok(res) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum SignEd25519Error { #[error("Expected private key")] ExpectedPrivateKey, @@ -1037,7 +1045,8 @@ pub fn op_node_sign_ed25519( Ok(()) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum VerifyEd25519Error { #[error("Expected public key")] ExpectedPublicKey, diff --git a/ext/node/ops/crypto/sign.rs b/ext/node/ops/crypto/sign.rs index e7c15d16b7..74ed50eb2b 100644 --- a/ext/node/ops/crypto/sign.rs +++ b/ext/node/ops/crypto/sign.rs @@ -39,7 +39,8 @@ where } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(type)] pub enum KeyObjectHandlePrehashedSignAndVerifyError { #[error("invalid DSA signature encoding")] InvalidDsaSignatureEncoding, @@ -47,10 +48,12 @@ pub enum KeyObjectHandlePrehashedSignAndVerifyError { KeyIsNotPrivate, #[error("digest not allowed for RSA signature: {0}")] DigestNotAllowedForRsaSignature(String), + #[class(generic)] #[error("failed to sign digest with RSA")] FailedToSignDigestWithRsa, #[error("digest not allowed for RSA-PSS signature: {0}")] DigestNotAllowedForRsaPssSignature(String), + #[class(generic)] #[error("failed to sign digest with RSA-PSS")] FailedToSignDigestWithRsaPss, #[error("failed to sign digest with DSA")] diff --git a/ext/node/ops/crypto/x509.rs b/ext/node/ops/crypto/x509.rs index 23b19720e3..ad931f01ff 100644 --- a/ext/node/ops/crypto/x509.rs +++ b/ext/node/ops/crypto/x509.rs @@ -59,11 +59,13 @@ impl<'a> Deref for CertificateView<'a> { } } +deno_error::js_error_wrapper!(X509Error, JsX509Error, "Error"); + #[op2] #[cppgc] pub fn op_node_x509_parse( #[buffer] buf: &[u8], -) -> Result { +) -> Result { let source = match pem::parse_x509_pem(buf) { Ok((_, pem)) => CertificateSources::Pem(pem), Err(_) => CertificateSources::Der(buf.to_vec().into_boxed_slice()), @@ -154,18 +156,18 @@ pub fn op_node_x509_fingerprint512( #[string] pub fn op_node_x509_get_issuer( #[cppgc] cert: &Certificate, -) -> Result { +) -> Result { let cert = cert.inner.get().deref(); - x509name_to_string(cert.issuer(), oid_registry()) + x509name_to_string(cert.issuer(), oid_registry()).map_err(Into::into) } #[op2] #[string] pub fn op_node_x509_get_subject( #[cppgc] cert: &Certificate, -) -> Result { +) -> Result { let cert = cert.inner.get().deref(); - x509name_to_string(cert.subject(), oid_registry()) + x509name_to_string(cert.subject(), oid_registry()).map_err(Into::into) } #[op2] diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs index 73db3ea2e5..0e9310375c 100644 --- a/ext/node/ops/fs.rs +++ b/ext/node/ops/fs.rs @@ -10,27 +10,40 @@ use serde::Serialize; use crate::NodePermissions; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum FsError { + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error("{0}")] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + std::io::Error, + ), #[cfg(windows)] + #[class(generic)] #[error("Path has no root.")] PathHasNoRoot, #[cfg(not(any(unix, windows)))] + #[class(generic)] #[error("Unsupported platform.")] UnsupportedPlatform, + #[class(inherit)] #[error(transparent)] - Fs(#[from] deno_io::fs::FsError), + Fs( + #[from] + #[inherit] + deno_io::fs::FsError, + ), } #[op2(fast, stack_trace)] pub fn op_node_fs_exists_sync

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

( _state: &mut OpState, _port: Option, #[string] _host: Option, -) -> Result<(), Error> +) -> Result<(), JsErrorBox> where P: NodePermissions + 'static, { @@ -87,6 +86,20 @@ struct JSInspectorSession { impl GarbageCollected for JSInspectorSession {} +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum InspectorConnectError { + #[class(inherit)] + #[error(transparent)] + Permission( + #[from] + #[inherit] + deno_permissions::PermissionCheckError, + ), + #[class(generic)] + #[error("connectToMainThread not supported")] + ConnectToMainThreadUnsupported, +} + #[op2(stack_trace)] #[cppgc] pub fn op_inspector_connect<'s, P>( @@ -95,7 +108,7 @@ pub fn op_inspector_connect<'s, P>( state: &mut OpState, connect_to_main_thread: bool, callback: v8::Local<'s, v8::Function>, -) -> Result +) -> Result where P: NodePermissions + 'static, { @@ -104,7 +117,7 @@ where .check_sys("inspector", "inspector.Session.connect")?; if connect_to_main_thread { - return Err(generic_error("connectToMainThread not supported")); + return Err(InspectorConnectError::ConnectToMainThreadUnsupported); } let context = scope.get_current_context(); diff --git a/ext/node/ops/ipc.rs b/ext/node/ops/ipc.rs index 0eb3ae6aae..cf5e1e97ef 100644 --- a/ext/node/ops/ipc.rs +++ b/ext/node/ops/ipc.rs @@ -30,6 +30,7 @@ mod impl_ { use deno_core::RcRef; use deno_core::ResourceId; use deno_core::ToV8; + use deno_error::JsErrorBox; use deno_io::BiPipe; use deno_io::BiPipeRead; use deno_io::BiPipeWrite; @@ -79,7 +80,7 @@ mod impl_ { } else if value.is_string_object() { let str = deno_core::serde_v8::to_utf8( value.to_string(scope).ok_or_else(|| { - S::Error::custom(deno_core::error::generic_error( + S::Error::custom(deno_error::JsErrorBox::generic( "toString on string object failed", )) })?, @@ -152,7 +153,7 @@ mod impl_ { map.end() } else { // TODO(nathanwhit): better error message - Err(S::Error::custom(deno_core::error::type_error(format!( + Err(S::Error::custom(JsErrorBox::type_error(format!( "Unsupported type: {}", value.type_repr() )))) @@ -177,14 +178,18 @@ mod impl_ { )) } - #[derive(Debug, thiserror::Error)] + #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum IpcError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] IpcJsonStream(#[from] IpcJsonStreamError), + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), + #[class(inherit)] #[error("failed to serialize json value: {0}")] SerdeJson(serde_json::Error), } @@ -210,8 +215,7 @@ mod impl_ { let stream = state .borrow() .resource_table - .get::(rid) - .map_err(IpcError::Resource)?; + .get::(rid)?; let old = stream .queued_bytes .fetch_add(serialized.len(), std::sync::atomic::Ordering::Relaxed); @@ -255,8 +259,7 @@ mod impl_ { let stream = state .borrow() .resource_table - .get::(rid) - .map_err(IpcError::Resource)?; + .get::(rid)?; let cancel = stream.cancel.clone(); let mut stream = RcRef::map(stream, |r| &r.read_half).borrow_mut().await; @@ -467,10 +470,12 @@ mod impl_ { } } - #[derive(Debug, thiserror::Error)] + #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum IpcJsonStreamError { + #[class(inherit)] #[error("{0}")] Io(#[source] std::io::Error), + #[class(generic)] #[error("{0}")] SimdJson(#[source] simd_json::Error), } diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs index 944f950607..ad0be8200e 100644 --- a/ext/node/ops/os/mod.rs +++ b/ext/node/ops/os/mod.rs @@ -4,6 +4,7 @@ use std::mem::MaybeUninit; use deno_core::op2; use deno_core::OpState; +use deno_permissions::PermissionCheckError; use sys_traits::EnvHomeDir; use crate::NodePermissions; @@ -11,16 +12,28 @@ use crate::NodePermissions; mod cpus; pub mod priority; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum OsError { + #[class(inherit)] #[error(transparent)] - Priority(priority::PriorityError), + Priority(#[inherit] priority::PriorityError), + #[class(inherit)] #[error(transparent)] - Permission(#[from] deno_permissions::PermissionCheckError), + Permission( + #[from] + #[inherit] + PermissionCheckError, + ), + #[class(type)] #[error("Failed to get cpu info")] FailedToGetCpuInfo, + #[class(inherit)] #[error("Failed to get user info")] - FailedToGetUserInfo(#[source] std::io::Error), + FailedToGetUserInfo( + #[source] + #[inherit] + std::io::Error, + ), } #[op2(fast, stack_trace)] @@ -215,9 +228,7 @@ where } #[op2(fast, stack_trace)] -pub fn op_geteuid

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

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

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

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

( state: &mut OpState, -) -> Result, deno_core::error::AnyError> +) -> Result, PermissionCheckError> where P: NodePermissions + 'static, { diff --git a/ext/node/ops/os/priority.rs b/ext/node/ops/os/priority.rs index f9e686ceba..10640e4942 100644 --- a/ext/node/ops/os/priority.rs +++ b/ext/node/ops/os/priority.rs @@ -2,11 +2,13 @@ pub use impl_::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PriorityError { + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), #[cfg(windows)] + #[class(type)] #[error("Invalid priority")] InvalidPriority, } diff --git a/ext/node/ops/perf_hooks.rs b/ext/node/ops/perf_hooks.rs index eca5fe2fa8..9c0fd01385 100644 --- a/ext/node/ops/perf_hooks.rs +++ b/ext/node/ops/perf_hooks.rs @@ -5,8 +5,9 @@ use std::cell::Cell; use deno_core::op2; use deno_core::GarbageCollected; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PerfHooksError { + #[class(generic)] #[error(transparent)] TokioEld(#[from] tokio_eld::Error), } diff --git a/ext/node/ops/process.rs b/ext/node/ops/process.rs index 0ef360af8c..f28e452437 100644 --- a/ext/node/ops/process.rs +++ b/ext/node/ops/process.rs @@ -50,7 +50,7 @@ pub fn op_node_process_kill( state: &mut OpState, #[smi] pid: i32, #[smi] sig: i32, -) -> Result { +) -> Result { state .borrow_mut::() .check_run_all("process.kill")?; diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs index 218079d4e1..3135ba1e86 100644 --- a/ext/node/ops/require.rs +++ b/ext/node/ops/require.rs @@ -7,13 +7,13 @@ use std::path::PathBuf; use std::rc::Rc; use boxed_error::Boxed; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::url::Url; use deno_core::v8; use deno_core::FastString; use deno_core::JsRuntimeInspector; use deno_core::OpState; +use deno_error::JsErrorBox; use deno_package_json::PackageJsonRc; use deno_path_util::normalize_path; use deno_path_util::url_from_file_path; @@ -37,7 +37,7 @@ use crate::PackageJsonResolverRc; fn ensure_read_permission<'a, P>( state: &mut OpState, file_path: &'a Path, -) -> Result, deno_core::error::AnyError> +) -> Result, JsErrorBox> where P: NodePermissions + 'static, { @@ -46,37 +46,67 @@ where loader.ensure_read_permission(permissions, file_path) } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, deno_error::JsError)] pub struct RequireError(pub Box); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum RequireErrorKind { + #[class(inherit)] #[error(transparent)] - UrlParse(#[from] url::ParseError), + UrlParse( + #[from] + #[inherit] + url::ParseError, + ), + #[class(inherit)] #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[inherit] JsErrorBox), + #[class(generic)] #[error(transparent)] PackageExportsResolve( #[from] node_resolver::errors::PackageExportsResolveError, ), + #[class(generic)] #[error(transparent)] PackageJsonLoad(#[from] node_resolver::errors::PackageJsonLoadError), + #[class(generic)] #[error(transparent)] - ClosestPkgJson(#[from] node_resolver::errors::ClosestPkgJsonError), + ClosestPkgJson(#[from] ClosestPkgJsonError), + #[class(generic)] #[error(transparent)] PackageImportsResolve( #[from] node_resolver::errors::PackageImportsResolveError, ), + #[class(generic)] #[error(transparent)] FilePathConversion(#[from] deno_path_util::UrlToFilePathError), + #[class(generic)] #[error(transparent)] UrlConversion(#[from] deno_path_util::PathToUrlError), + #[class(inherit)] #[error(transparent)] - Fs(#[from] std::io::Error), + Fs( + #[from] + #[inherit] + deno_io::fs::FsError, + ), + #[class(inherit)] #[error(transparent)] - ReadModule(deno_core::error::AnyError), + Io( + #[from] + #[inherit] + std::io::Error, + ), + #[class(inherit)] + #[error(transparent)] + ReadModule( + #[from] + #[inherit] + JsErrorBox, + ), + #[class(inherit)] #[error("Unable to get CWD: {0}")] - UnableToGetCwd(std::io::Error), + UnableToGetCwd(#[inherit] std::io::Error), } #[op2] @@ -230,8 +260,9 @@ pub fn op_require_resolve_deno_dir( state: &mut OpState, #[string] request: String, #[string] parent_filename: String, -) -> Result, AnyError> { +) -> Result, deno_path_util::PathToUrlError> { let resolver = state.borrow::(); + Ok( resolver .resolve_package_folder_from_package( @@ -309,7 +340,7 @@ pub fn op_require_stat< >( state: &mut OpState, #[string] path: String, -) -> Result { +) -> Result { let path = PathBuf::from(path); let path = ensure_read_permission::

(state, &path)?; let sys = state.borrow::(); @@ -337,8 +368,9 @@ pub fn op_require_real_path< let path = ensure_read_permission::

(state, &path) .map_err(RequireErrorKind::Permission)?; let sys = state.borrow::(); - let canonicalized_path = - deno_path_util::strip_unc_prefix(sys.fs_canonicalize(&path)?); + let canonicalized_path = deno_path_util::strip_unc_prefix( + sys.fs_canonicalize(&path).map_err(RequireErrorKind::Io)?, + ); Ok(canonicalized_path.to_string_lossy().into_owned()) } @@ -362,14 +394,12 @@ pub fn op_require_path_resolve(#[serde] parts: Vec) -> String { #[string] pub fn op_require_path_dirname( #[string] request: String, -) -> Result { +) -> Result { let p = PathBuf::from(request); if let Some(parent) = p.parent() { Ok(parent.to_string_lossy().into_owned()) } else { - Err(deno_core::error::generic_error( - "Path doesn't have a parent", - )) + Err(JsErrorBox::generic("Path doesn't have a parent")) } } @@ -377,14 +407,12 @@ pub fn op_require_path_dirname( #[string] pub fn op_require_path_basename( #[string] request: String, -) -> Result { +) -> Result { let p = PathBuf::from(request); if let Some(path) = p.file_name() { Ok(path.to_string_lossy().into_owned()) } else { - Err(deno_core::error::generic_error( - "Path doesn't have a file name", - )) + Err(JsErrorBox::generic("Path doesn't have a file name")) } } @@ -398,7 +426,7 @@ pub fn op_require_try_self_parent_path< has_parent: bool, #[string] maybe_parent_filename: Option, #[string] maybe_parent_id: Option, -) -> Result, deno_core::error::AnyError> { +) -> Result, JsErrorBox> { if !has_parent { return Ok(None); } @@ -583,17 +611,23 @@ pub fn op_require_resolve_exports< })) } +deno_error::js_error_wrapper!( + ClosestPkgJsonError, + JsClosestPkgJsonError, + "Error" +); + #[op2(fast)] pub fn op_require_is_maybe_cjs( state: &mut OpState, #[string] filename: String, -) -> Result { +) -> Result { let filename = PathBuf::from(filename); let Ok(url) = url_from_file_path(&filename) else { return Ok(false); }; let loader = state.borrow::(); - loader.is_maybe_cjs(&url) + loader.is_maybe_cjs(&url).map_err(Into::into) } #[op2(stack_trace)] diff --git a/ext/node/ops/util.rs b/ext/node/ops/util.rs index bc1d2c0588..1af4f7edbd 100644 --- a/ext/node/ops/util.rs +++ b/ext/node/ops/util.rs @@ -21,7 +21,7 @@ enum HandleType { pub fn op_node_guess_handle_type( state: &mut OpState, rid: u32, -) -> Result { +) -> Result { let handle = state.resource_table.get_handle(rid)?; let handle_type = match handle { diff --git a/ext/node/ops/v8.rs b/ext/node/ops/v8.rs index 8f4a70dccf..c268d41925 100644 --- a/ext/node/ops/v8.rs +++ b/ext/node/ops/v8.rs @@ -7,6 +7,7 @@ use deno_core::v8; use deno_core::FastString; use deno_core::GarbageCollected; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use v8::ValueDeserializerHelper; use v8::ValueSerializerHelper; @@ -274,13 +275,11 @@ pub fn op_v8_new_deserializer( scope: &mut v8::HandleScope, obj: v8::Local, buffer: v8::Local, -) -> Result, deno_core::error::AnyError> { +) -> Result, JsErrorBox> { let offset = buffer.byte_offset(); let len = buffer.byte_length(); let backing_store = buffer.get_backing_store().ok_or_else(|| { - deno_core::error::generic_error( - "deserialization buffer has no backing store", - ) + JsErrorBox::generic("deserialization buffer has no backing store") })?; let (buf_slice, buf_ptr) = if let Some(data) = backing_store.data() { // SAFETY: the offset is valid for the underlying buffer because we're getting it directly from v8 @@ -322,10 +321,10 @@ pub fn op_v8_transfer_array_buffer_de( #[op2(fast)] pub fn op_v8_read_double( #[cppgc] deser: &Deserializer, -) -> Result { +) -> Result { let mut double = 0f64; if !deser.inner.read_double(&mut double) { - return Err(deno_core::error::type_error("ReadDouble() failed")); + return Err(JsErrorBox::type_error("ReadDouble() failed")); } Ok(double) } @@ -360,10 +359,10 @@ pub fn op_v8_read_raw_bytes( #[op2(fast)] pub fn op_v8_read_uint32( #[cppgc] deser: &Deserializer, -) -> Result { +) -> Result { let mut value = 0; if !deser.inner.read_uint32(&mut value) { - return Err(deno_core::error::type_error("ReadUint32() failed")); + return Err(JsErrorBox::type_error("ReadUint32() failed")); } Ok(value) @@ -373,10 +372,10 @@ pub fn op_v8_read_uint32( #[serde] pub fn op_v8_read_uint64( #[cppgc] deser: &Deserializer, -) -> Result<(u32, u32), deno_core::error::AnyError> { +) -> Result<(u32, u32), JsErrorBox> { let mut val = 0; if !deser.inner.read_uint64(&mut val) { - return Err(deno_core::error::type_error("ReadUint64() failed")); + return Err(JsErrorBox::type_error("ReadUint64() failed")); } Ok(((val >> 32) as u32, val as u32)) diff --git a/ext/node/ops/vm_internal.rs b/ext/node/ops/vm_internal.rs index e8c1cc02f0..2219d05cd0 100644 --- a/ext/node/ops/vm_internal.rs +++ b/ext/node/ops/vm_internal.rs @@ -1,9 +1,8 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::v8; use deno_core::v8::MapFnTo; +use deno_error::JsErrorBox; use crate::create_host_defined_options; @@ -20,7 +19,7 @@ impl ContextifyScript { pub fn new( scope: &mut v8::HandleScope, source_str: v8::Local, - ) -> Result { + ) -> Result { let resource_name = v8::undefined(scope); let host_defined_options = create_host_defined_options(scope); let origin = v8::ScriptOrigin::new( @@ -45,7 +44,7 @@ impl ContextifyScript { v8::script_compiler::CompileOptions::NoCompileOptions, v8::script_compiler::NoCacheReason::NoReason, ) - .ok_or_else(|| type_error("Failed to compile script"))?; + .ok_or_else(|| JsErrorBox::type_error("Failed to compile script"))?; let script = v8::Global::new(scope, unbound_script); Ok(Self { script }) } diff --git a/ext/node/ops/worker_threads.rs b/ext/node/ops/worker_threads.rs index f7aa8c71cb..ae3c28ef35 100644 --- a/ext/node/ops/worker_threads.rs +++ b/ext/node/ops/worker_threads.rs @@ -7,6 +7,7 @@ use std::path::PathBuf; use deno_core::op2; use deno_core::url::Url; use deno_core::OpState; +use deno_error::JsErrorBox; use sys_traits::FsCanonicalize; use sys_traits::FsMetadata; @@ -18,7 +19,7 @@ use crate::NodeRequireLoaderRc; fn ensure_read_permission<'a, P>( state: &mut OpState, file_path: &'a Path, -) -> Result, deno_core::error::AnyError> +) -> Result, JsErrorBox> where P: NodePermissions + 'static, { @@ -27,24 +28,47 @@ where loader.ensure_read_permission(permissions, file_path) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WorkerThreadsFilenameError { + #[class(inherit)] #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(JsErrorBox), + #[class(inherit)] #[error("{0}")] - UrlParse(#[from] url::ParseError), + UrlParse( + #[from] + #[inherit] + url::ParseError, + ), + #[class(generic)] #[error("Relative path entries must start with '.' or '..'")] InvalidRelativeUrl, + #[class(generic)] #[error("URL from Path-String")] UrlFromPathString, + #[class(generic)] #[error("URL to Path-String")] UrlToPathString, + #[class(generic)] #[error("URL to Path")] UrlToPath, + #[class(generic)] #[error("File not found [{0:?}]")] FileNotFound(PathBuf), + #[class(inherit)] #[error(transparent)] - Fs(#[from] std::io::Error), + Fs( + #[from] + #[inherit] + deno_io::fs::FsError, + ), + #[class(inherit)] + #[error(transparent)] + Io( + #[from] + #[inherit] + std::io::Error, + ), } // todo(dsherret): we should remove this and do all this work inside op_create_worker diff --git a/ext/node/ops/zlib/brotli.rs b/ext/node/ops/zlib/brotli.rs index 5d15df559f..5e4c1d16e6 100644 --- a/ext/node/ops/zlib/brotli.rs +++ b/ext/node/ops/zlib/brotli.rs @@ -18,20 +18,34 @@ use deno_core::OpState; use deno_core::Resource; use deno_core::ToJsBuffer; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BrotliError { + #[class(type)] #[error("Invalid encoder mode")] InvalidEncoderMode, + #[class(type)] #[error("Failed to compress")] CompressFailed, + #[class(type)] #[error("Failed to decompress")] DecompressFailed, + #[class(inherit)] #[error(transparent)] - Join(#[from] tokio::task::JoinError), + Join( + #[from] + #[inherit] + tokio::task::JoinError, + ), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(inherit)] #[error("{0}")] - Io(std::io::Error), + Io(#[inherit] std::io::Error), } fn encoder_mode(mode: u32) -> Result { @@ -167,10 +181,7 @@ pub fn op_brotli_compress_stream( #[buffer] input: &[u8], #[buffer] output: &mut [u8], ) -> Result { - let ctx = state - .resource_table - .get::(rid) - .map_err(BrotliError::Resource)?; + let ctx = state.resource_table.get::(rid)?; let mut inst = ctx.inst.borrow_mut(); let mut output_offset = 0; @@ -199,10 +210,7 @@ pub fn op_brotli_compress_stream_end( #[smi] rid: u32, #[buffer] output: &mut [u8], ) -> Result { - let ctx = state - .resource_table - .get::(rid) - .map_err(BrotliError::Resource)?; + let ctx = state.resource_table.get::(rid)?; let mut inst = ctx.inst.borrow_mut(); let mut output_offset = 0; @@ -277,10 +285,7 @@ pub fn op_brotli_decompress_stream( #[buffer] input: &[u8], #[buffer] output: &mut [u8], ) -> Result { - let ctx = state - .resource_table - .get::(rid) - .map_err(BrotliError::Resource)?; + let ctx = state.resource_table.get::(rid)?; let mut inst = ctx.inst.borrow_mut(); let mut output_offset = 0; @@ -308,10 +313,7 @@ pub fn op_brotli_decompress_stream_end( #[smi] rid: u32, #[buffer] output: &mut [u8], ) -> Result { - let ctx = state - .resource_table - .get::(rid) - .map_err(BrotliError::Resource)?; + let ctx = state.resource_table.get::(rid)?; let mut inst = ctx.inst.borrow_mut(); let mut output_offset = 0; diff --git a/ext/node/ops/zlib/mod.rs b/ext/node/ops/zlib/mod.rs index b5277e7a34..892944bcea 100644 --- a/ext/node/ops/zlib/mod.rs +++ b/ext/node/ops/zlib/mod.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use std::cell::RefCell; use deno_core::op2; +use deno_error::JsErrorBox; use libc::c_ulong; use zlib::*; @@ -18,11 +19,11 @@ use mode::Mode; use self::stream::StreamWrapper; #[inline] -fn check(condition: bool, msg: &str) -> Result<(), deno_core::error::AnyError> { +fn check(condition: bool, msg: &str) -> Result<(), JsErrorBox> { if condition { Ok(()) } else { - Err(deno_core::error::type_error(msg.to_string())) + Err(JsErrorBox::type_error(msg.to_string())) } } @@ -57,7 +58,7 @@ impl ZlibInner { out_off: u32, out_len: u32, flush: Flush, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), JsErrorBox> { check(self.init_done, "write before init")?; check(!self.write_in_progress, "write already in progress")?; check(!self.pending_close, "close already in progress")?; @@ -66,11 +67,11 @@ impl ZlibInner { let next_in = input .get(in_off as usize..in_off as usize + in_len as usize) - .ok_or_else(|| deno_core::error::type_error("invalid input range"))? + .ok_or_else(|| JsErrorBox::type_error("invalid input range"))? .as_ptr() as *mut _; let next_out = out .get_mut(out_off as usize..out_off as usize + out_len as usize) - .ok_or_else(|| deno_core::error::type_error("invalid output range"))? + .ok_or_else(|| JsErrorBox::type_error("invalid output range"))? .as_mut_ptr(); self.strm.avail_in = in_len; @@ -82,10 +83,7 @@ impl ZlibInner { Ok(()) } - fn do_write( - &mut self, - flush: Flush, - ) -> Result<(), deno_core::error::AnyError> { + fn do_write(&mut self, flush: Flush) -> Result<(), JsErrorBox> { self.flush = flush; match self.mode { Mode::Deflate | Mode::Gzip | Mode::DeflateRaw => { @@ -131,7 +129,7 @@ impl ZlibInner { self.mode = Mode::Inflate; } } else if next_expected_header_byte.is_some() { - return Err(deno_core::error::type_error( + return Err(JsErrorBox::type_error( "invalid number of gzip magic number bytes read", )); } @@ -185,7 +183,7 @@ impl ZlibInner { Ok(()) } - fn init_stream(&mut self) -> Result<(), deno_core::error::AnyError> { + fn init_stream(&mut self) -> Result<(), JsErrorBox> { match self.mode { Mode::Gzip | Mode::Gunzip => self.window_bits += 16, Mode::Unzip => self.window_bits += 32, @@ -203,7 +201,7 @@ impl ZlibInner { Mode::Inflate | Mode::Gunzip | Mode::InflateRaw | Mode::Unzip => { self.strm.inflate_init(self.window_bits) } - Mode::None => return Err(deno_core::error::type_error("Unknown mode")), + Mode::None => return Err(JsErrorBox::type_error("Unknown mode")), }; self.write_in_progress = false; @@ -212,7 +210,7 @@ impl ZlibInner { Ok(()) } - fn close(&mut self) -> Result { + fn close(&mut self) -> Result { if self.write_in_progress { self.pending_close = true; return Ok(false); @@ -258,14 +256,25 @@ pub fn op_zlib_new(#[smi] mode: i32) -> Result { }) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ZlibError { + #[class(type)] #[error("zlib not initialized")] NotInitialized, + #[class(inherit)] #[error(transparent)] - Mode(#[from] mode::ModeError), + Mode( + #[from] + #[inherit] + mode::ModeError, + ), + #[class(inherit)] #[error(transparent)] - Other(#[from] deno_core::error::AnyError), + Other( + #[from] + #[inherit] + JsErrorBox, + ), } #[op2(fast)] diff --git a/ext/node/ops/zlib/mode.rs b/ext/node/ops/zlib/mode.rs index c0660a7c7a..5fa2e501dc 100644 --- a/ext/node/ops/zlib/mode.rs +++ b/ext/node/ops/zlib/mode.rs @@ -1,6 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] #[error("bad argument")] pub struct ModeError; diff --git a/ext/telemetry/Cargo.toml b/ext/telemetry/Cargo.toml index 4328b707d3..484a90eeb1 100644 --- a/ext/telemetry/Cargo.toml +++ b/ext/telemetry/Cargo.toml @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] async-trait.workspace = true deno_core.workspace = true +deno_error.workspace = true http-body-util.workspace = true hyper.workspace = true hyper-util.workspace = true @@ -28,4 +29,5 @@ opentelemetry-semantic-conventions.workspace = true opentelemetry_sdk.workspace = true pin-project.workspace = true serde.workspace = true +thiserror.workspace = true tokio.workspace = true diff --git a/ext/telemetry/lib.rs b/ext/telemetry/lib.rs index 261e93124d..ce3f34a0af 100644 --- a/ext/telemetry/lib.rs +++ b/ext/telemetry/lib.rs @@ -18,10 +18,6 @@ use std::thread; use std::time::Duration; use std::time::SystemTime; -use deno_core::anyhow; -use deno_core::anyhow::anyhow; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::futures::channel::mpsc; use deno_core::futures::channel::mpsc::UnboundedSender; use deno_core::futures::future::BoxFuture; @@ -31,8 +27,11 @@ use deno_core::futures::Stream; use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::v8; +use deno_core::v8::DataError; use deno_core::GarbageCollected; use deno_core::OpState; +use deno_error::JsError; +use deno_error::JsErrorBox; use once_cell::sync::Lazy; use once_cell::sync::OnceCell; use opentelemetry::logs::AnyValue; @@ -83,6 +82,7 @@ use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_NAME; use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_VERSION; use serde::Deserialize; use serde::Serialize; +use thiserror::Error; use tokio::sync::oneshot; use tokio::task::JoinSet; @@ -563,7 +563,7 @@ static OTEL_GLOBALS: OnceCell = OnceCell::new(); pub fn init( rt_config: OtelRuntimeConfig, config: &OtelConfig, -) -> anyhow::Result<()> { +) -> deno_core::anyhow::Result<()> { // Parse the `OTEL_EXPORTER_OTLP_PROTOCOL` variable. The opentelemetry_* // crates don't do this automatically. // TODO(piscisaureus): enable GRPC support. @@ -572,13 +572,13 @@ pub fn init( Ok("http/json") => Protocol::HttpJson, Ok("") | Err(env::VarError::NotPresent) => Protocol::HttpBinary, Ok(protocol) => { - return Err(anyhow!( + return Err(deno_core::anyhow::anyhow!( "Env var OTEL_EXPORTER_OTLP_PROTOCOL specifies an unsupported protocol: {}", protocol )); } Err(err) => { - return Err(anyhow!( + return Err(deno_core::anyhow::anyhow!( "Failed to read env var OTEL_EXPORTER_OTLP_PROTOCOL: {}", err )); @@ -645,7 +645,7 @@ pub fn init( Some("delta") => Temporality::Delta, Some("lowmemory") => Temporality::LowMemory, Some(other) => { - return Err(anyhow!( + return Err(deno_core::anyhow::anyhow!( "Invalid value for OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: {}", other )); @@ -688,7 +688,7 @@ pub fn init( meter_provider, builtin_instrumentation_scope, }) - .map_err(|_| anyhow!("failed to set otel globals"))?; + .map_err(|_| deno_core::anyhow::anyhow!("failed to set otel globals"))?; Ok(()) } @@ -1100,7 +1100,7 @@ impl OtelTracer { #[smi] span_kind: u8, start_time: Option, #[smi] attribute_count: usize, - ) -> Result { + ) -> Result { let OtelGlobals { id_generator, .. } = OTEL_GLOBALS.get().unwrap(); let span_context; let parent_span_id; @@ -1131,20 +1131,25 @@ impl OtelTracer { parent_span_id = SpanId::INVALID; } } - let name = owned_string(scope, name.try_cast()?); + let name = owned_string( + scope, + name + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); let span_kind = match span_kind { 0 => SpanKind::Internal, 1 => SpanKind::Server, 2 => SpanKind::Client, 3 => SpanKind::Producer, 4 => SpanKind::Consumer, - _ => return Err(anyhow!("invalid span kind")), + _ => return Err(JsErrorBox::generic("invalid span kind")), }; let start_time = start_time .map(|start_time| { SystemTime::UNIX_EPOCH .checked_add(std::time::Duration::from_secs_f64(start_time)) - .ok_or_else(|| anyhow!("invalid start time")) + .ok_or_else(|| JsErrorBox::generic("invalid start time")) }) .unwrap_or_else(|| Ok(SystemTime::now()))?; let span_data = SpanData { @@ -1176,14 +1181,14 @@ impl OtelTracer { #[smi] span_kind: u8, start_time: Option, #[smi] attribute_count: usize, - ) -> Result { + ) -> Result { let parent_trace_id = parse_trace_id(scope, parent_trace_id); if parent_trace_id == TraceId::INVALID { - return Err(anyhow!("invalid trace id")); + return Err(JsErrorBox::generic("invalid trace id")); }; let parent_span_id = parse_span_id(scope, parent_span_id); if parent_span_id == SpanId::INVALID { - return Err(anyhow!("invalid span id")); + return Err(JsErrorBox::generic("invalid span id")); }; let OtelGlobals { id_generator, .. } = OTEL_GLOBALS.get().unwrap(); let span_context = SpanContext::new( @@ -1193,20 +1198,25 @@ impl OtelTracer { false, TraceState::NONE, ); - let name = owned_string(scope, name.try_cast()?); + let name = owned_string( + scope, + name + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); let span_kind = match span_kind { 0 => SpanKind::Internal, 1 => SpanKind::Server, 2 => SpanKind::Client, 3 => SpanKind::Producer, 4 => SpanKind::Consumer, - _ => return Err(anyhow!("invalid span kind")), + _ => return Err(JsErrorBox::generic("invalid span kind")), }; let start_time = start_time .map(|start_time| { SystemTime::UNIX_EPOCH .checked_add(std::time::Duration::from_secs_f64(start_time)) - .ok_or_else(|| anyhow!("invalid start time")) + .ok_or_else(|| JsErrorBox::generic("invalid start time")) }) .unwrap_or_else(|| Ok(SystemTime::now()))?; let span_data = SpanData { @@ -1237,6 +1247,16 @@ struct JsSpanContext { trace_flags: u8, } +#[derive(Debug, Error, JsError)] +#[error("OtelSpan cannot be constructed.")] +#[class(type)] +struct OtelSpanCannotBeConstructedError; + +#[derive(Debug, Error, JsError)] +#[error("invalid span status code")] +#[class(type)] +struct InvalidSpanStatusCodeError; + // boxed because of https://github.com/denoland/rusty_v8/issues/1676 #[derive(Debug)] struct OtelSpan(RefCell>); @@ -1254,8 +1274,8 @@ impl deno_core::GarbageCollected for OtelSpan {} impl OtelSpan { #[constructor] #[cppgc] - fn new() -> Result { - Err(type_error("OtelSpan can not be constructed.")) + fn new() -> Result { + Err(OtelSpanCannotBeConstructedError) } #[serde] @@ -1277,7 +1297,7 @@ impl OtelSpan { &self, #[smi] status: u8, #[string] error_description: String, - ) -> Result<(), AnyError> { + ) -> Result<(), InvalidSpanStatusCodeError> { let mut state = self.0.borrow_mut(); let OtelSpanState::Recording(span) = &mut **state else { return Ok(()); @@ -1288,7 +1308,7 @@ impl OtelSpan { 2 => SpanStatus::Error { description: Cow::Owned(error_description), }, - _ => return Err(type_error("invalid span status code")), + _ => return Err(InvalidSpanStatusCodeError), }; Ok(()) } @@ -1464,7 +1484,7 @@ impl OtelMeter { name: v8::Local<'s, v8::Value>, description: v8::Local<'s, v8::Value>, unit: v8::Local<'s, v8::Value>, - ) -> Result { + ) -> Result { create_instrument( |name| self.0.f64_counter(name), |i| Instrument::Counter(i.build()), @@ -1473,6 +1493,7 @@ impl OtelMeter { description, unit, ) + .map_err(|e| JsErrorBox::generic(e.to_string())) } #[cppgc] @@ -1482,7 +1503,7 @@ impl OtelMeter { name: v8::Local<'s, v8::Value>, description: v8::Local<'s, v8::Value>, unit: v8::Local<'s, v8::Value>, - ) -> Result { + ) -> Result { create_instrument( |name| self.0.f64_up_down_counter(name), |i| Instrument::UpDownCounter(i.build()), @@ -1491,6 +1512,7 @@ impl OtelMeter { description, unit, ) + .map_err(|e| JsErrorBox::generic(e.to_string())) } #[cppgc] @@ -1500,7 +1522,7 @@ impl OtelMeter { name: v8::Local<'s, v8::Value>, description: v8::Local<'s, v8::Value>, unit: v8::Local<'s, v8::Value>, - ) -> Result { + ) -> Result { create_instrument( |name| self.0.f64_gauge(name), |i| Instrument::Gauge(i.build()), @@ -1509,6 +1531,7 @@ impl OtelMeter { description, unit, ) + .map_err(|e| JsErrorBox::generic(e.to_string())) } #[cppgc] @@ -1519,15 +1542,30 @@ impl OtelMeter { description: v8::Local<'s, v8::Value>, unit: v8::Local<'s, v8::Value>, #[serde] boundaries: Option>, - ) -> Result { - let name = owned_string(scope, name.try_cast()?); + ) -> Result { + let name = owned_string( + scope, + name + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); let mut builder = self.0.f64_histogram(name); if !description.is_null_or_undefined() { - let description = owned_string(scope, description.try_cast()?); + let description = owned_string( + scope, + description + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); builder = builder.with_description(description); }; if !unit.is_null_or_undefined() { - let unit = owned_string(scope, unit.try_cast()?); + let unit = owned_string( + scope, + unit + .try_cast() + .map_err(|e: DataError| JsErrorBox::generic(e.to_string()))?, + ); builder = builder.with_unit(unit); }; if let Some(boundaries) = boundaries { @@ -1544,7 +1582,7 @@ impl OtelMeter { name: v8::Local<'s, v8::Value>, description: v8::Local<'s, v8::Value>, unit: v8::Local<'s, v8::Value>, - ) -> Result { + ) -> Result { create_async_instrument( |name| self.0.f64_observable_counter(name), |i| { @@ -1555,6 +1593,7 @@ impl OtelMeter { description, unit, ) + .map_err(|e| JsErrorBox::generic(e.to_string())) } #[cppgc] @@ -1564,7 +1603,7 @@ impl OtelMeter { name: v8::Local<'s, v8::Value>, description: v8::Local<'s, v8::Value>, unit: v8::Local<'s, v8::Value>, - ) -> Result { + ) -> Result { create_async_instrument( |name| self.0.f64_observable_up_down_counter(name), |i| { @@ -1575,6 +1614,7 @@ impl OtelMeter { description, unit, ) + .map_err(|e| JsErrorBox::generic(e.to_string())) } #[cppgc] @@ -1584,7 +1624,7 @@ impl OtelMeter { name: v8::Local<'s, v8::Value>, description: v8::Local<'s, v8::Value>, unit: v8::Local<'s, v8::Value>, - ) -> Result { + ) -> Result { create_async_instrument( |name| self.0.f64_observable_gauge(name), |i| { @@ -1595,6 +1635,7 @@ impl OtelMeter { description, unit, ) + .map_err(|e| JsErrorBox::generic(e.to_string())) } } @@ -1615,7 +1656,7 @@ fn create_instrument<'a, 'b, T>( name: v8::Local<'a, v8::Value>, description: v8::Local<'a, v8::Value>, unit: v8::Local<'a, v8::Value>, -) -> Result { +) -> Result { let name = owned_string(scope, name.try_cast()?); let mut builder = cb(name); if !description.is_null_or_undefined() { @@ -1637,7 +1678,7 @@ fn create_async_instrument<'a, 'b, T>( name: v8::Local<'a, v8::Value>, description: v8::Local<'a, v8::Value>, unit: v8::Local<'a, v8::Value>, -) -> Result { +) -> Result { let name = owned_string(scope, name.try_cast()?); let mut builder = cb(name); if !description.is_null_or_undefined() { diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index 53d85ba83c..1e804bd538 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_native_certs = "0.3.0" rustls.workspace = true rustls-pemfile.workspace = true diff --git a/ext/tls/lib.rs b/ext/tls/lib.rs index 63e78e3f83..a3e386052e 100644 --- a/ext/tls/lib.rs +++ b/ext/tls/lib.rs @@ -5,6 +5,7 @@ use std::io::Cursor; use std::net::IpAddr; use std::sync::Arc; +use deno_error::JsErrorBox; pub use deno_native_certs; pub use rustls; use rustls::client::danger::HandshakeSignatureValid; @@ -30,18 +31,24 @@ pub use webpki_roots; mod tls_key; pub use tls_key::*; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum TlsError { + #[class(generic)] #[error(transparent)] Rustls(#[from] rustls::Error), + #[class(inherit)] #[error("Unable to add pem file to certificate store: {0}")] UnableAddPemFileToCert(std::io::Error), + #[class("InvalidData")] #[error("Unable to decode certificate")] CertInvalid, + #[class("InvalidData")] #[error("No certificates found in certificate data")] CertsNotFound, + #[class("InvalidData")] #[error("No keys found in key data")] KeysNotFound, + #[class("InvalidData")] #[error("Unable to decode key")] KeyDecode, } @@ -51,9 +58,7 @@ pub enum TlsError { /// This was done because the root cert store is not needed in all cases /// and takes a bit of time to initialize. pub trait RootCertStoreProvider: Send + Sync { - fn get_or_try_init( - &self, - ) -> Result<&RootCertStore, deno_core::error::AnyError>; + fn get_or_try_init(&self) -> Result<&RootCertStore, JsErrorBox>; } // This extension has no runtime apis, it only exports some shared native functions. diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index 4dcb6f42ca..1f7f7b36c6 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true thiserror.workspace = true urlpattern = "0.3.0" diff --git a/ext/url/lib.rs b/ext/url/lib.rs index 0e9ca5839a..dd74239d93 100644 --- a/ext/url/lib.rs +++ b/ext/url/lib.rs @@ -4,15 +4,13 @@ mod urlpattern; use std::path::PathBuf; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::url::form_urlencoded; use deno_core::url::quirks; use deno_core::url::Url; use deno_core::JsBuffer; use deno_core::OpState; -pub use urlpattern::UrlPatternError; +use deno_error::JsErrorBox; use crate::urlpattern::op_urlpattern_parse; use crate::urlpattern::op_urlpattern_process_match_input; @@ -220,7 +218,7 @@ pub fn op_url_reparse( pub fn op_url_parse_search_params( #[string] args: Option, #[buffer] zero_copy: Option, -) -> Result, AnyError> { +) -> Result, JsErrorBox> { let params = match (args, zero_copy) { (None, Some(zero_copy)) => form_urlencoded::parse(&zero_copy) .into_iter() @@ -230,7 +228,7 @@ pub fn op_url_parse_search_params( .into_iter() .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned())) .collect(), - _ => return Err(type_error("invalid parameters")), + _ => return Err(JsErrorBox::type_error("invalid parameters")), }; Ok(params) } diff --git a/ext/url/urlpattern.rs b/ext/url/urlpattern.rs index 88564625da..02034332cf 100644 --- a/ext/url/urlpattern.rs +++ b/ext/url/urlpattern.rs @@ -6,9 +6,7 @@ use urlpattern::quirks::MatchInput; use urlpattern::quirks::StringOrInit; use urlpattern::quirks::UrlPattern; -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct UrlPatternError(urlpattern::Error); +deno_error::js_error_wrapper!(urlpattern::Error, UrlPatternError, "TypeError"); #[op2] #[serde] @@ -18,11 +16,9 @@ pub fn op_urlpattern_parse( #[serde] options: urlpattern::UrlPatternOptions, ) -> Result { let init = - quirks::process_construct_pattern_input(input, base_url.as_deref()) - .map_err(UrlPatternError)?; + quirks::process_construct_pattern_input(input, base_url.as_deref())?; - let pattern = - quirks::parse_pattern(init, options).map_err(UrlPatternError)?; + let pattern = quirks::parse_pattern(init, options)?; Ok(pattern) } @@ -33,8 +29,7 @@ pub fn op_urlpattern_process_match_input( #[serde] input: StringOrInit, #[string] base_url: Option, ) -> Result, UrlPatternError> { - let res = quirks::process_match_input(input, base_url.as_deref()) - .map_err(UrlPatternError)?; + let res = quirks::process_match_input(input, base_url.as_deref())?; let (input, inputs) = match res { Some((input, inputs)) => (input, inputs), diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index dd521ad037..d5dbbdecca 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -18,6 +18,7 @@ async-trait.workspace = true base64-simd = "0.8" bytes.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_permissions.workspace = true encoding_rs.workspace = true flate2 = { workspace = true, features = ["default"] } diff --git a/ext/web/blob.rs b/ext/web/blob.rs index 8723337093..555e6da1cf 100644 --- a/ext/web/blob.rs +++ b/ext/web/blob.rs @@ -17,14 +17,18 @@ use serde::Deserialize; use serde::Serialize; use uuid::Uuid; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BlobError { + #[class(type)] #[error("Blob part not found")] BlobPartNotFound, + #[class(type)] #[error("start + len can not be larger than blob part size")] SizeLargerThanBlobPart, + #[class(type)] #[error("Blob URLs are not supported in this context")] BlobURLsNotSupported, + #[class(generic)] #[error(transparent)] Url(#[from] deno_core::url::ParseError), } diff --git a/ext/web/compression.rs b/ext/web/compression.rs index 650cc84085..66662de74a 100644 --- a/ext/web/compression.rs +++ b/ext/web/compression.rs @@ -12,14 +12,18 @@ use flate2::write::ZlibDecoder; use flate2::write::ZlibEncoder; use flate2::Compression; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CompressionError { + #[class(type)] #[error("Unsupported format")] UnsupportedFormat, + #[class(type)] #[error("resource is closed")] ResourceClosed, + #[class(type)] #[error(transparent)] IoTypeError(std::io::Error), + #[class(inherit)] #[error(transparent)] Io(std::io::Error), } diff --git a/ext/web/lib.rs b/ext/web/lib.rs index 07b00f0049..7d22fa3b2a 100644 --- a/ext/web/lib.rs +++ b/ext/web/lib.rs @@ -126,20 +126,27 @@ deno_core::extension!(deno_web, } ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WebError { + #[class("DOMExceptionInvalidCharacterError")] #[error("Failed to decode base64")] Base64Decode, + #[class(range)] #[error("The encoding label provided ('{0}') is invalid.")] InvalidEncodingLabel(String), + #[class(type)] #[error("buffer exceeds maximum length")] BufferTooLong, + #[class(range)] #[error("Value too large to decode")] ValueTooLarge, + #[class(range)] #[error("Provided buffer too small")] BufferTooSmall, + #[class(type)] #[error("The encoded data is not valid")] DataInvalid, + #[class(generic)] #[error(transparent)] DataError(#[from] v8::DataError), } diff --git a/ext/web/message_port.rs b/ext/web/message_port.rs index b2aad6776f..3d656fdea2 100644 --- a/ext/web/message_port.rs +++ b/ext/web/message_port.rs @@ -20,18 +20,23 @@ use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum MessagePortError { + #[class(type)] #[error("Invalid message port transfer")] InvalidTransfer, + #[class(type)] #[error("Message port is not ready for transfer")] NotReady, + #[class(type)] #[error("Can not transfer self message port")] TransferSelf, + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(deno_core::error::ResourceError), } pub enum Transferable { diff --git a/ext/web/stream_resource.rs b/ext/web/stream_resource.rs index 5613f57384..edc842ff4d 100644 --- a/ext/web/stream_resource.rs +++ b/ext/web/stream_resource.rs @@ -31,10 +31,12 @@ use deno_core::ResourceId; use futures::future::poll_fn; use futures::TryFutureExt; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum StreamResourceError { + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), + #[class(type)] #[error("{0}")] Js(String), } @@ -404,7 +406,10 @@ impl Resource for ReadableStreamResource { } fn read(self: Rc, limit: usize) -> AsyncResult { - Box::pin(ReadableStreamResource::read(self, limit).map_err(|e| e.into())) + Box::pin( + ReadableStreamResource::read(self, limit) + .map_err(deno_error::JsErrorBox::from_err), + ) } fn close(self: Rc) { diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 9aa1b2370c..4bb9fa9a41 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -21,6 +21,7 @@ vulkan-portability = [] # so the whole workspace can built as wasm. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] deno_core.workspace = true +deno_error.workspace = true serde = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["full"] } wgpu-types = { workspace = true, features = ["serde"] } diff --git a/ext/webgpu/binding.rs b/ext/webgpu/binding.rs index 2849cf9bfe..c8441c64af 100644 --- a/ext/webgpu/binding.rs +++ b/ext/webgpu/binding.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use std::rc::Rc; -use deno_core::error::AnyError; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; @@ -170,7 +170,7 @@ pub fn op_webgpu_create_bind_group_layout( #[smi] device_rid: ResourceId, #[string] label: Cow, #[serde] entries: Vec, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -209,7 +209,7 @@ pub fn op_webgpu_create_pipeline_layout( #[smi] device_rid: ResourceId, #[string] label: Cow, #[serde] bind_group_layouts: Vec, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -223,7 +223,7 @@ pub fn op_webgpu_create_pipeline_layout( state.resource_table.get::(rid)?; Ok(bind_group_layout.1) }) - .collect::, AnyError>>()?; + .collect::, ResourceError>>()?; let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor { label: Some(label), @@ -256,7 +256,7 @@ pub fn op_webgpu_create_bind_group( #[string] label: Cow, #[smi] layout: ResourceId, #[serde] entries: Vec, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -304,7 +304,7 @@ pub fn op_webgpu_create_bind_group( }, }) }) - .collect::, AnyError>>()?; + .collect::, ResourceError>>()?; let bind_group_layout = state.resource_table.get::(layout)?; diff --git a/ext/webgpu/buffer.rs b/ext/webgpu/buffer.rs index e8e33244c9..25a5606e12 100644 --- a/ext/webgpu/buffer.rs +++ b/ext/webgpu/buffer.rs @@ -14,12 +14,19 @@ use deno_core::ResourceId; use super::error::WebGpuResult; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BufferError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(type)] #[error("usage is not valid")] InvalidUsage, + #[class("DOMExceptionOperationError")] #[error(transparent)] Access(wgpu_core::resource::BufferAccessError), } @@ -58,8 +65,7 @@ pub fn op_webgpu_create_buffer( let instance = state.borrow::(); let device_resource = state .resource_table - .get::(device_rid) - .map_err(BufferError::Resource)?; + .get::(device_rid)?; let device = device_resource.1; let descriptor = wgpu_core::resource::BufferDescriptor { @@ -92,15 +98,12 @@ pub async fn op_webgpu_buffer_get_map_async( { let state_ = state.borrow(); let instance = state_.borrow::(); - let buffer_resource = state_ - .resource_table - .get::(buffer_rid) - .map_err(BufferError::Resource)?; + let buffer_resource = + state_.resource_table.get::(buffer_rid)?; let buffer = buffer_resource.1; let device_resource = state_ .resource_table - .get::(device_rid) - .map_err(BufferError::Resource)?; + .get::(device_rid)?; device = device_resource.1; let done_ = done.clone(); @@ -155,10 +158,7 @@ pub fn op_webgpu_buffer_get_mapped_range( #[buffer] buf: &mut [u8], ) -> Result { let instance = state.borrow::(); - let buffer_resource = state - .resource_table - .get::(buffer_rid) - .map_err(BufferError::Resource)?; + let buffer_resource = state.resource_table.get::(buffer_rid)?; let buffer = buffer_resource.1; let (slice_pointer, range_size) = @@ -192,13 +192,9 @@ pub fn op_webgpu_buffer_unmap( ) -> Result { let mapped_resource = state .resource_table - .take::(mapped_rid) - .map_err(BufferError::Resource)?; + .take::(mapped_rid)?; let instance = state.borrow::(); - let buffer_resource = state - .resource_table - .get::(buffer_rid) - .map_err(BufferError::Resource)?; + let buffer_resource = state.resource_table.get::(buffer_rid)?; let buffer = buffer_resource.1; if let Some(buf) = buf { diff --git a/ext/webgpu/bundle.rs b/ext/webgpu/bundle.rs index 5fc147a44a..73c3c9f221 100644 --- a/ext/webgpu/bundle.rs +++ b/ext/webgpu/bundle.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; @@ -12,10 +13,16 @@ use serde::Deserialize; use super::error::WebGpuResult; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BundleError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + ResourceError, + ), + #[class(type)] #[error("size must be larger than 0")] InvalidSize, } @@ -60,7 +67,7 @@ pub struct CreateRenderBundleEncoderArgs { pub fn op_webgpu_create_render_bundle_encoder( state: &mut OpState, #[serde] args: CreateRenderBundleEncoderArgs, -) -> Result { +) -> Result { let device_resource = state .resource_table .get::(args.device_rid)?; @@ -107,7 +114,7 @@ pub fn op_webgpu_render_bundle_encoder_finish( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, #[string] label: Cow, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -138,7 +145,7 @@ pub fn op_webgpu_render_bundle_encoder_set_bind_group( #[buffer] dynamic_offsets_data: &[u32], #[number] dynamic_offsets_data_start: usize, #[number] dynamic_offsets_data_length: usize, -) -> Result { +) -> Result { let bind_group_resource = state .resource_table @@ -178,7 +185,7 @@ pub fn op_webgpu_render_bundle_encoder_push_debug_group( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, #[string] group_label: &str, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -202,7 +209,7 @@ pub fn op_webgpu_render_bundle_encoder_push_debug_group( pub fn op_webgpu_render_bundle_encoder_pop_debug_group( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -221,7 +228,7 @@ pub fn op_webgpu_render_bundle_encoder_insert_debug_marker( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, #[string] marker_label: &str, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -246,7 +253,7 @@ pub fn op_webgpu_render_bundle_encoder_set_pipeline( state: &mut OpState, #[smi] render_bundle_encoder_rid: ResourceId, #[smi] pipeline: ResourceId, -) -> Result { +) -> Result { let render_pipeline_resource = state .resource_table @@ -276,12 +283,11 @@ pub fn op_webgpu_render_bundle_encoder_set_index_buffer( ) -> Result { let buffer_resource = state .resource_table - .get::(buffer) - .map_err(BundleError::Resource)?; - let render_bundle_encoder_resource = state - .resource_table - .get::(render_bundle_encoder_rid) - .map_err(BundleError::Resource)?; + .get::(buffer)?; + let render_bundle_encoder_resource = + state + .resource_table + .get::(render_bundle_encoder_rid)?; let size = Some(std::num::NonZeroU64::new(size).ok_or(BundleError::InvalidSize)?); @@ -305,12 +311,11 @@ pub fn op_webgpu_render_bundle_encoder_set_vertex_buffer( ) -> Result { let buffer_resource = state .resource_table - .get::(buffer) - .map_err(BundleError::Resource)?; - let render_bundle_encoder_resource = state - .resource_table - .get::(render_bundle_encoder_rid) - .map_err(BundleError::Resource)?; + .get::(buffer)?; + let render_bundle_encoder_resource = + state + .resource_table + .get::(render_bundle_encoder_rid)?; let size = if let Some(size) = size { Some(std::num::NonZeroU64::new(size).ok_or(BundleError::InvalidSize)?) } else { @@ -337,7 +342,7 @@ pub fn op_webgpu_render_bundle_encoder_draw( instance_count: u32, first_vertex: u32, first_instance: u32, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -364,7 +369,7 @@ pub fn op_webgpu_render_bundle_encoder_draw_indexed( first_index: u32, base_vertex: i32, first_instance: u32, -) -> Result { +) -> Result { let render_bundle_encoder_resource = state .resource_table @@ -389,7 +394,7 @@ pub fn op_webgpu_render_bundle_encoder_draw_indirect( #[smi] render_bundle_encoder_rid: ResourceId, #[smi] indirect_buffer: ResourceId, #[number] indirect_offset: u64, -) -> Result { +) -> Result { let buffer_resource = state .resource_table .get::(indirect_buffer)?; diff --git a/ext/webgpu/byow.rs b/ext/webgpu/byow.rs index 7c16c8a0d2..e911e1402b 100644 --- a/ext/webgpu/byow.rs +++ b/ext/webgpu/byow.rs @@ -15,18 +15,23 @@ use deno_core::ResourceId; use crate::surface::WebGpuSurface; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ByowError { + #[class(type)] #[error("Cannot create surface outside of WebGPU context. Did you forget to call `navigator.gpu.requestAdapter()`?")] WebGPUNotInitiated, + #[class(type)] #[error("Invalid parameters")] InvalidParameters, + #[class(generic)] #[error(transparent)] CreateSurface(wgpu_core::instance::CreateSurfaceError), #[cfg(target_os = "windows")] + #[class(type)] #[error("Invalid system on Windows")] InvalidSystem, #[cfg(target_os = "macos")] + #[class(type)] #[error("Invalid system on macOS")] InvalidSystem, #[cfg(any( @@ -34,6 +39,7 @@ pub enum ByowError { target_os = "freebsd", target_os = "openbsd" ))] + #[class(type)] #[error("Invalid system on Linux/BSD")] InvalidSystem, #[cfg(any( @@ -42,6 +48,7 @@ pub enum ByowError { target_os = "freebsd", target_os = "openbsd" ))] + #[class(type)] #[error("window is null")] NullWindow, #[cfg(any( @@ -49,9 +56,11 @@ pub enum ByowError { target_os = "freebsd", target_os = "openbsd" ))] + #[class(type)] #[error("display is null")] NullDisplay, #[cfg(target_os = "macos")] + #[class(type)] #[error("ns_view is null")] NSViewDisplay, } @@ -199,6 +208,6 @@ fn raw_window( _system: &str, _window: *const c_void, _display: *const c_void, -) -> Result { - Err(deno_core::error::type_error("Unsupported platform")) +) -> Result { + Err(deno_error::JsErrorBox::type_error("Unsupported platform")) } diff --git a/ext/webgpu/command_encoder.rs b/ext/webgpu/command_encoder.rs index 4b14345a2c..9b6bb44ae8 100644 --- a/ext/webgpu/command_encoder.rs +++ b/ext/webgpu/command_encoder.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; -use deno_core::error::AnyError; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; @@ -50,7 +50,7 @@ pub fn op_webgpu_create_command_encoder( state: &mut OpState, #[smi] device_rid: ResourceId, #[string] label: Cow, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -110,7 +110,7 @@ pub fn op_webgpu_command_encoder_begin_render_pass( >, #[smi] occlusion_query_set: Option, #[serde] timestamp_writes: Option, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .get::(command_encoder_rid)?; @@ -149,7 +149,7 @@ pub fn op_webgpu_command_encoder_begin_render_pass( }; Ok(rp_at) }) - .collect::, AnyError>>()?; + .collect::, ResourceError>>()?; let mut processed_depth_stencil_attachment = None; @@ -245,7 +245,7 @@ pub fn op_webgpu_command_encoder_begin_compute_pass( #[smi] command_encoder_rid: ResourceId, #[string] label: Cow, #[serde] timestamp_writes: Option, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .get::(command_encoder_rid)?; @@ -295,7 +295,7 @@ pub fn op_webgpu_command_encoder_copy_buffer_to_buffer( #[smi] destination: ResourceId, #[number] destination_offset: u64, #[number] size: u64, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -347,7 +347,7 @@ pub fn op_webgpu_command_encoder_copy_buffer_to_texture( #[serde] source: GpuImageCopyBuffer, #[serde] destination: GpuImageCopyTexture, #[serde] copy_size: wgpu_types::Extent3d, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -392,7 +392,7 @@ pub fn op_webgpu_command_encoder_copy_texture_to_buffer( #[serde] source: GpuImageCopyTexture, #[serde] destination: GpuImageCopyBuffer, #[serde] copy_size: wgpu_types::Extent3d, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -437,7 +437,7 @@ pub fn op_webgpu_command_encoder_copy_texture_to_texture( #[serde] source: GpuImageCopyTexture, #[serde] destination: GpuImageCopyTexture, #[serde] copy_size: wgpu_types::Extent3d, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -480,7 +480,7 @@ pub fn op_webgpu_command_encoder_clear_buffer( #[smi] buffer_rid: ResourceId, #[number] offset: u64, #[number] size: u64, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -504,7 +504,7 @@ pub fn op_webgpu_command_encoder_push_debug_group( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[string] group_label: &str, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -519,7 +519,7 @@ pub fn op_webgpu_command_encoder_push_debug_group( pub fn op_webgpu_command_encoder_pop_debug_group( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -535,7 +535,7 @@ pub fn op_webgpu_command_encoder_insert_debug_marker( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[string] marker_label: &str, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -555,7 +555,7 @@ pub fn op_webgpu_command_encoder_write_timestamp( #[smi] command_encoder_rid: ResourceId, #[smi] query_set: ResourceId, query_index: u32, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -582,7 +582,7 @@ pub fn op_webgpu_command_encoder_resolve_query_set( query_count: u32, #[smi] destination: ResourceId, #[number] destination_offset: u64, -) -> Result { +) -> Result { let instance = state.borrow::(); let command_encoder_resource = state .resource_table @@ -611,7 +611,7 @@ pub fn op_webgpu_command_encoder_finish( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[string] label: Cow, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .take::(command_encoder_rid)?; diff --git a/ext/webgpu/compute_pass.rs b/ext/webgpu/compute_pass.rs index 22cd522d8a..afa19b3fac 100644 --- a/ext/webgpu/compute_pass.rs +++ b/ext/webgpu/compute_pass.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use std::cell::RefCell; -use deno_core::error::AnyError; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; @@ -26,7 +26,7 @@ pub fn op_webgpu_compute_pass_set_pipeline( state: &mut OpState, #[smi] compute_pass_rid: ResourceId, #[smi] pipeline: ResourceId, -) -> Result { +) -> Result { let compute_pipeline_resource = state .resource_table @@ -51,7 +51,7 @@ pub fn op_webgpu_compute_pass_dispatch_workgroups( x: u32, y: u32, z: u32, -) -> Result { +) -> Result { let compute_pass_resource = state .resource_table .get::(compute_pass_rid)?; @@ -73,7 +73,7 @@ pub fn op_webgpu_compute_pass_dispatch_workgroups_indirect( #[smi] compute_pass_rid: ResourceId, #[smi] indirect_buffer: ResourceId, #[number] indirect_offset: u64, -) -> Result { +) -> Result { let buffer_resource = state .resource_table .get::(indirect_buffer)?; @@ -96,7 +96,7 @@ pub fn op_webgpu_compute_pass_end( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[smi] compute_pass_rid: ResourceId, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .get::( @@ -125,7 +125,7 @@ pub fn op_webgpu_compute_pass_set_bind_group( #[buffer] dynamic_offsets_data: &[u32], #[number] dynamic_offsets_data_start: usize, #[number] dynamic_offsets_data_length: usize, -) -> Result { +) -> Result { let bind_group_resource = state .resource_table @@ -159,7 +159,7 @@ pub fn op_webgpu_compute_pass_push_debug_group( state: &mut OpState, #[smi] compute_pass_rid: ResourceId, #[string] group_label: &str, -) -> Result { +) -> Result { let compute_pass_resource = state .resource_table .get::(compute_pass_rid)?; @@ -178,7 +178,7 @@ pub fn op_webgpu_compute_pass_push_debug_group( pub fn op_webgpu_compute_pass_pop_debug_group( state: &mut OpState, #[smi] compute_pass_rid: ResourceId, -) -> Result { +) -> Result { let compute_pass_resource = state .resource_table .get::(compute_pass_rid)?; @@ -196,7 +196,7 @@ pub fn op_webgpu_compute_pass_insert_debug_marker( state: &mut OpState, #[smi] compute_pass_rid: ResourceId, #[string] marker_label: &str, -) -> Result { +) -> Result { let compute_pass_resource = state .resource_table .get::(compute_pass_rid)?; diff --git a/ext/webgpu/lib.rs b/ext/webgpu/lib.rs index bdf0f39b63..afcd808f74 100644 --- a/ext/webgpu/lib.rs +++ b/ext/webgpu/lib.rs @@ -83,14 +83,22 @@ pub mod shader; pub mod surface; pub mod texture; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum InitError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(generic)] #[error(transparent)] InvalidAdapter(wgpu_core::instance::InvalidAdapter), + #[class("DOMExceptionOperationError")] #[error(transparent)] RequestDevice(wgpu_core::instance::RequestDeviceError), + #[class(generic)] #[error(transparent)] InvalidDevice(wgpu_core::device::InvalidDevice), } @@ -676,10 +684,8 @@ pub fn op_webgpu_request_device( #[serde] required_limits: Option, ) -> Result { let mut state = state.borrow_mut(); - let adapter_resource = state - .resource_table - .take::(adapter_rid) - .map_err(InitError::Resource)?; + let adapter_resource = + state.resource_table.take::(adapter_rid)?; let adapter = adapter_resource.1; let instance = state.borrow::(); @@ -738,10 +744,8 @@ pub fn op_webgpu_request_adapter_info( #[smi] adapter_rid: ResourceId, ) -> Result { let state = state.borrow_mut(); - let adapter_resource = state - .resource_table - .get::(adapter_rid) - .map_err(InitError::Resource)?; + let adapter_resource = + state.resource_table.get::(adapter_rid)?; let adapter = adapter_resource.1; let instance = state.borrow::(); @@ -788,10 +792,8 @@ pub fn op_webgpu_create_query_set( state: &mut OpState, #[serde] args: CreateQuerySetArgs, ) -> Result { - let device_resource = state - .resource_table - .get::(args.device_rid) - .map_err(InitError::Resource)?; + let device_resource = + state.resource_table.get::(args.device_rid)?; let device = device_resource.1; let instance = state.borrow::(); diff --git a/ext/webgpu/pipeline.rs b/ext/webgpu/pipeline.rs index 87b1610ad7..07452a1caf 100644 --- a/ext/webgpu/pipeline.rs +++ b/ext/webgpu/pipeline.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::rc::Rc; -use deno_core::error::AnyError; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; @@ -88,7 +88,7 @@ pub fn op_webgpu_create_compute_pipeline( #[string] label: Cow, #[serde] layout: GPUPipelineLayoutOrGPUAutoLayoutMode, #[serde] compute: GpuProgrammableStage, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -156,7 +156,7 @@ pub fn op_webgpu_compute_pipeline_get_bind_group_layout( state: &mut OpState, #[smi] compute_pipeline_rid: ResourceId, index: u32, -) -> Result { +) -> Result { let instance = state.borrow::(); let compute_pipeline_resource = state .resource_table @@ -335,7 +335,7 @@ pub struct CreateRenderPipelineArgs { pub fn op_webgpu_create_render_pipeline( state: &mut OpState, #[serde] args: CreateRenderPipelineArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -434,7 +434,7 @@ pub fn op_webgpu_render_pipeline_get_bind_group_layout( state: &mut OpState, #[smi] render_pipeline_rid: ResourceId, index: u32, -) -> Result { +) -> Result { let instance = state.borrow::(); let render_pipeline_resource = state .resource_table diff --git a/ext/webgpu/queue.rs b/ext/webgpu/queue.rs index 4f367f5469..51f4c4e009 100644 --- a/ext/webgpu/queue.rs +++ b/ext/webgpu/queue.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use std::rc::Rc; -use deno_core::error::AnyError; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; @@ -31,7 +31,7 @@ pub fn op_webgpu_queue_submit( state: &mut OpState, #[smi] queue_rid: ResourceId, #[serde] command_buffers: Vec, -) -> Result { +) -> Result { let instance = state.borrow::(); let queue_resource = state.resource_table.get::(queue_rid)?; let queue = queue_resource.1; @@ -44,7 +44,7 @@ pub fn op_webgpu_queue_submit( let mut id = buffer_resource.1.borrow_mut(); Ok(id.take().unwrap()) }) - .collect::, AnyError>>()?; + .collect::, ResourceError>>()?; let maybe_err = gfx_select!(queue => instance.queue_submit(queue, &ids)).err(); @@ -85,7 +85,7 @@ pub fn op_webgpu_write_buffer( #[number] data_offset: usize, #[number] size: Option, #[buffer] buf: &[u8], -) -> Result { +) -> Result { let instance = state.borrow::(); let buffer_resource = state .resource_table @@ -118,7 +118,7 @@ pub fn op_webgpu_write_texture( #[serde] data_layout: GpuImageDataLayout, #[serde] size: wgpu_types::Extent3d, #[buffer] buf: &[u8], -) -> Result { +) -> Result { let instance = state.borrow::(); let texture_resource = state .resource_table diff --git a/ext/webgpu/render_pass.rs b/ext/webgpu/render_pass.rs index 41d610c0f9..43c4cae846 100644 --- a/ext/webgpu/render_pass.rs +++ b/ext/webgpu/render_pass.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::cell::RefCell; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; @@ -11,10 +12,16 @@ use serde::Deserialize; use super::error::WebGpuResult; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum RenderPassError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + ResourceError, + ), + #[class(type)] #[error("size must be larger than 0")] InvalidSize, } @@ -45,7 +52,7 @@ pub struct RenderPassSetViewportArgs { pub fn op_webgpu_render_pass_set_viewport( state: &mut OpState, #[serde] args: RenderPassSetViewportArgs, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(args.render_pass_rid)?; @@ -72,7 +79,7 @@ pub fn op_webgpu_render_pass_set_scissor_rect( y: u32, width: u32, height: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -94,7 +101,7 @@ pub fn op_webgpu_render_pass_set_blend_constant( state: &mut OpState, #[smi] render_pass_rid: ResourceId, #[serde] color: wgpu_types::Color, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -113,7 +120,7 @@ pub fn op_webgpu_render_pass_set_stencil_reference( state: &mut OpState, #[smi] render_pass_rid: ResourceId, reference: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -132,7 +139,7 @@ pub fn op_webgpu_render_pass_begin_occlusion_query( state: &mut OpState, #[smi] render_pass_rid: ResourceId, query_index: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -150,7 +157,7 @@ pub fn op_webgpu_render_pass_begin_occlusion_query( pub fn op_webgpu_render_pass_end_occlusion_query( state: &mut OpState, #[smi] render_pass_rid: ResourceId, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -168,7 +175,7 @@ pub fn op_webgpu_render_pass_execute_bundles( state: &mut OpState, #[smi] render_pass_rid: ResourceId, #[serde] bundles: Vec, -) -> Result { +) -> Result { let bundles = bundles .iter() .map(|rid| { @@ -178,7 +185,7 @@ pub fn op_webgpu_render_pass_execute_bundles( .get::(*rid)?; Ok(render_bundle_resource.1) }) - .collect::, deno_core::error::AnyError>>()?; + .collect::, ResourceError>>()?; let render_pass_resource = state .resource_table @@ -198,7 +205,7 @@ pub fn op_webgpu_render_pass_end( state: &mut OpState, #[smi] command_encoder_rid: ResourceId, #[smi] render_pass_rid: ResourceId, -) -> Result { +) -> Result { let command_encoder_resource = state .resource_table .get::( @@ -224,7 +231,7 @@ pub fn op_webgpu_render_pass_set_bind_group( #[buffer] dynamic_offsets_data: &[u32], #[number] dynamic_offsets_data_start: usize, #[number] dynamic_offsets_data_length: usize, -) -> Result { +) -> Result { let bind_group_resource = state .resource_table @@ -258,7 +265,7 @@ pub fn op_webgpu_render_pass_push_debug_group( state: &mut OpState, #[smi] render_pass_rid: ResourceId, #[string] group_label: &str, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -277,7 +284,7 @@ pub fn op_webgpu_render_pass_push_debug_group( pub fn op_webgpu_render_pass_pop_debug_group( state: &mut OpState, #[smi] render_pass_rid: ResourceId, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -295,7 +302,7 @@ pub fn op_webgpu_render_pass_insert_debug_marker( state: &mut OpState, #[smi] render_pass_rid: ResourceId, #[string] marker_label: &str, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -315,7 +322,7 @@ pub fn op_webgpu_render_pass_set_pipeline( state: &mut OpState, #[smi] render_pass_rid: ResourceId, pipeline: u32, -) -> Result { +) -> Result { let render_pipeline_resource = state .resource_table @@ -344,12 +351,10 @@ pub fn op_webgpu_render_pass_set_index_buffer( ) -> Result { let buffer_resource = state .resource_table - .get::(buffer) - .map_err(RenderPassError::Resource)?; + .get::(buffer)?; let render_pass_resource = state .resource_table - .get::(render_pass_rid) - .map_err(RenderPassError::Resource)?; + .get::(render_pass_rid)?; let size = if let Some(size) = size { Some(std::num::NonZeroU64::new(size).ok_or(RenderPassError::InvalidSize)?) @@ -379,12 +384,10 @@ pub fn op_webgpu_render_pass_set_vertex_buffer( ) -> Result { let buffer_resource = state .resource_table - .get::(buffer) - .map_err(RenderPassError::Resource)?; + .get::(buffer)?; let render_pass_resource = state .resource_table - .get::(render_pass_rid) - .map_err(RenderPassError::Resource)?; + .get::(render_pass_rid)?; let size = if let Some(size) = size { Some(std::num::NonZeroU64::new(size).ok_or(RenderPassError::InvalidSize)?) @@ -412,7 +415,7 @@ pub fn op_webgpu_render_pass_draw( instance_count: u32, first_vertex: u32, first_instance: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -438,7 +441,7 @@ pub fn op_webgpu_render_pass_draw_indexed( first_index: u32, base_vertex: i32, first_instance: u32, -) -> Result { +) -> Result { let render_pass_resource = state .resource_table .get::(render_pass_rid)?; @@ -462,7 +465,7 @@ pub fn op_webgpu_render_pass_draw_indirect( #[smi] render_pass_rid: ResourceId, indirect_buffer: u32, #[number] indirect_offset: u64, -) -> Result { +) -> Result { let buffer_resource = state .resource_table .get::(indirect_buffer)?; @@ -486,7 +489,7 @@ pub fn op_webgpu_render_pass_draw_indexed_indirect( #[smi] render_pass_rid: ResourceId, indirect_buffer: u32, #[number] indirect_offset: u64, -) -> Result { +) -> Result { let buffer_resource = state .resource_table .get::(indirect_buffer)?; diff --git a/ext/webgpu/sampler.rs b/ext/webgpu/sampler.rs index e4f73e93ac..88c1947e63 100644 --- a/ext/webgpu/sampler.rs +++ b/ext/webgpu/sampler.rs @@ -47,7 +47,7 @@ pub struct CreateSamplerArgs { pub fn op_webgpu_create_sampler( state: &mut OpState, #[serde] args: CreateSamplerArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table diff --git a/ext/webgpu/shader.rs b/ext/webgpu/shader.rs index f57e24fa26..84615ea6f6 100644 --- a/ext/webgpu/shader.rs +++ b/ext/webgpu/shader.rs @@ -31,7 +31,7 @@ pub fn op_webgpu_create_shader_module( #[smi] device_rid: ResourceId, #[string] label: Cow, #[string] code: Cow, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table diff --git a/ext/webgpu/surface.rs b/ext/webgpu/surface.rs index e23c5f182b..23e617c7de 100644 --- a/ext/webgpu/surface.rs +++ b/ext/webgpu/surface.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::rc::Rc; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; @@ -10,14 +11,21 @@ use deno_core::ResourceId; use serde::Deserialize; use wgpu_types::SurfaceStatus; -use super::WebGpuResult; +use crate::error::WebGpuResult; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SurfaceError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + ResourceError, + ), + #[class(generic)] #[error("Invalid Surface Status")] InvalidStatus, + #[class(generic)] #[error(transparent)] Surface(wgpu_core::present::SurfaceError), } @@ -52,7 +60,7 @@ pub struct SurfaceConfigureArgs { pub fn op_webgpu_surface_configure( state: &mut OpState, #[serde] args: SurfaceConfigureArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -90,13 +98,10 @@ pub fn op_webgpu_surface_get_current_texture( let instance = state.borrow::(); let device_resource = state .resource_table - .get::(device_rid) - .map_err(SurfaceError::Resource)?; + .get::(device_rid)?; let device = device_resource.1; - let surface_resource = state - .resource_table - .get::(surface_rid) - .map_err(SurfaceError::Resource)?; + let surface_resource = + state.resource_table.get::(surface_rid)?; let surface = surface_resource.1; let output = @@ -126,13 +131,10 @@ pub fn op_webgpu_surface_present( let instance = state.borrow::(); let device_resource = state .resource_table - .get::(device_rid) - .map_err(SurfaceError::Resource)?; + .get::(device_rid)?; let device = device_resource.1; - let surface_resource = state - .resource_table - .get::(surface_rid) - .map_err(SurfaceError::Resource)?; + let surface_resource = + state.resource_table.get::(surface_rid)?; let surface = surface_resource.1; let _ = gfx_select!(device => instance.surface_present(surface)) diff --git a/ext/webgpu/texture.rs b/ext/webgpu/texture.rs index a354567de8..10c5d902ee 100644 --- a/ext/webgpu/texture.rs +++ b/ext/webgpu/texture.rs @@ -62,7 +62,7 @@ pub struct CreateTextureArgs { pub fn op_webgpu_create_texture( state: &mut OpState, #[serde] args: CreateTextureArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let device_resource = state .resource_table @@ -111,7 +111,7 @@ pub struct CreateTextureViewArgs { pub fn op_webgpu_create_texture_view( state: &mut OpState, #[serde] args: CreateTextureViewArgs, -) -> Result { +) -> Result { let instance = state.borrow::(); let texture_resource = state .resource_table diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index a1f1c98ba1..dc24d52e16 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] bytes.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_net.workspace = true deno_permissions.workspace = true deno_tls.workspace = true diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index b47dbef3e1..deb424c9be 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -24,6 +24,7 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use deno_net::raw::NetworkStream; use deno_permissions::PermissionCheckError; use deno_tls::create_client_config; @@ -72,22 +73,30 @@ static USE_WRITEV: Lazy = Lazy::new(|| { false }); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WebsocketError { + #[class(inherit)] #[error(transparent)] Url(url::ParseError), + #[class(inherit)] #[error(transparent)] Permission(#[from] PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(generic)] #[error(transparent)] Uri(#[from] http::uri::InvalidUri), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), + #[class(type)] #[error(transparent)] WebSocket(#[from] fastwebsockets::WebSocketError), + #[class("DOMExceptionNetworkError")] #[error("failed to connect to WebSocket: {0}")] ConnectionFailed(#[from] HandshakeError), + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), } @@ -96,9 +105,7 @@ pub enum WebsocketError { pub struct WsRootStoreProvider(Option>); impl WsRootStoreProvider { - pub fn get_or_try_init( - &self, - ) -> Result, deno_core::error::AnyError> { + pub fn get_or_try_init(&self) -> Result, JsErrorBox> { Ok(match &self.0 { Some(provider) => Some(provider.get_or_try_init()?.clone()), None => None, @@ -183,32 +190,45 @@ pub struct CreateResponse { extensions: String, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HandshakeError { + #[class(type)] #[error("Missing path in url")] MissingPath, + #[class(generic)] #[error("Invalid status code {0}")] InvalidStatusCode(StatusCode), + #[class(generic)] #[error(transparent)] Http(#[from] http::Error), + #[class(type)] #[error(transparent)] WebSocket(#[from] fastwebsockets::WebSocketError), + #[class(generic)] #[error("Didn't receive h2 alpn, aborting connection")] NoH2Alpn, + #[class(generic)] #[error(transparent)] Rustls(#[from] deno_tls::rustls::Error), + #[class(inherit)] #[error(transparent)] Io(#[from] std::io::Error), + #[class(generic)] #[error(transparent)] H2(#[from] h2::Error), + #[class(type)] #[error("Invalid hostname: '{0}'")] InvalidHostname(String), + #[class(inherit)] #[error(transparent)] - RootStoreError(deno_core::error::AnyError), + RootStoreError(JsErrorBox), + #[class(inherit)] #[error(transparent)] Tls(deno_tls::TlsError), + #[class(type)] #[error(transparent)] HeaderName(#[from] http::header::InvalidHeaderName), + #[class(type)] #[error(transparent)] HeaderValue(#[from] http::header::InvalidHeaderValue), } @@ -473,8 +493,7 @@ where let r = state .borrow_mut() .resource_table - .get::(cancel_rid) - .map_err(WebsocketError::Resource)?; + .get::(cancel_rid)?; Some(r.0.clone()) } else { None @@ -678,8 +697,7 @@ pub async fn op_ws_send_binary_async( let resource = state .borrow_mut() .resource_table - .get::(rid) - .map_err(WebsocketError::Resource)?; + .get::(rid)?; let data = data.to_vec(); let lock = resource.reserve_lock(); resource @@ -697,8 +715,7 @@ pub async fn op_ws_send_text_async( let resource = state .borrow_mut() .resource_table - .get::(rid) - .map_err(WebsocketError::Resource)?; + .get::(rid)?; let lock = resource.reserve_lock(); resource .write_frame( @@ -732,8 +749,7 @@ pub async fn op_ws_send_ping( let resource = state .borrow_mut() .resource_table - .get::(rid) - .map_err(WebsocketError::Resource)?; + .get::(rid)?; let lock = resource.reserve_lock(); resource .write_frame( diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index 2cae0d8e01..044aa41f6a 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -15,6 +15,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +deno_error.workspace = true deno_web.workspace = true rusqlite.workspace = true thiserror.workspace = true diff --git a/ext/webstorage/lib.rs b/ext/webstorage/lib.rs index ca6b43a827..4653e1b948 100644 --- a/ext/webstorage/lib.rs +++ b/ext/webstorage/lib.rs @@ -12,14 +12,18 @@ use rusqlite::params; use rusqlite::Connection; use rusqlite::OptionalExtension; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum WebStorageError { + #[class("DOMExceptionNotSupportedError")] #[error("LocalStorage is not supported in this context.")] ContextNotSupported, + #[class(generic)] #[error(transparent)] Sqlite(#[from] rusqlite::Error), + #[class(inherit)] #[error(transparent)] Io(std::io::Error), + #[class("DOMExceptionQuotaExceededError")] #[error("Exceeded maximum storage size")] StorageExceeded, } diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 71e75d5a1d..0eeec14e77 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -22,6 +22,7 @@ base32.workspace = true boxed_error.workspace = true dashmap = { workspace = true, optional = true } deno_config.workspace = true +deno_error.workspace = true deno_media_type.workspace = true deno_package_json.workspace = true deno_package_json.features = ["sync"] diff --git a/resolvers/deno/lib.rs b/resolvers/deno/lib.rs index ab01f397fb..49b2cf4d1b 100644 --- a/resolvers/deno/lib.rs +++ b/resolvers/deno/lib.rs @@ -11,6 +11,7 @@ use deno_config::workspace::MappedResolutionDiagnostic; use deno_config::workspace::MappedResolutionError; use deno_config::workspace::WorkspaceResolvePkgJsonFolderError; use deno_config::workspace::WorkspaceResolver; +use deno_error::JsError; use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValueParseError; use deno_semver::npm::NpmPackageReqReference; @@ -53,29 +54,39 @@ pub struct DenoResolution { pub found_package_json_dep: bool, } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct DenoResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum DenoResolveErrorKind { + #[class(type)] #[error("Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring.")] InvalidVendorFolderImport, + #[class(inherit)] #[error(transparent)] MappedResolution(#[from] MappedResolutionError), + #[class(inherit)] #[error(transparent)] MissingPackageNodeModulesFolder(#[from] MissingPackageNodeModulesFolderError), + #[class(inherit)] #[error(transparent)] Node(#[from] NodeResolveError), + #[class(inherit)] #[error(transparent)] NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError), + #[class(inherit)] #[error(transparent)] PackageJsonDepValueParse(#[from] PackageJsonDepValueParseError), + #[class(inherit)] #[error(transparent)] PackageJsonDepValueUrlParse(url::ParseError), + #[class(inherit)] #[error(transparent)] PackageSubpathResolve(#[from] PackageSubpathResolveError), + #[class(inherit)] #[error(transparent)] ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError), + #[class(inherit)] #[error(transparent)] WorkspaceResolvePkgJsonFolder(#[from] WorkspaceResolvePkgJsonFolderError), } diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index 3ceec368ad..e9aad66e3f 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -30,14 +30,18 @@ use super::local::normalize_pkg_name_for_node_modules_deno_folder; use super::CliNpmReqResolver; use super::ResolvePkgFolderFromDenoReqError; -#[derive(Debug, Error)] +#[derive(Debug, Error, deno_error::JsError)] pub enum ByonmResolvePkgFolderFromDenoReqError { + #[class(generic)] #[error("Could not find \"{}\" in a node_modules folder. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", .0)] MissingAlias(StackString), + #[class(inherit)] #[error(transparent)] PackageJson(#[from] PackageJsonLoadError), + #[class(generic)] #[error("Could not find a matching package for 'npm:{}' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `\"nodeModulesDir\": \"auto\"` in your deno.json file.", .0)] UnmatchedReq(PackageReq), + #[class(inherit)] #[error(transparent)] Io(#[from] std::io::Error), } diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 1501c05941..2e7c4c888b 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -9,6 +9,7 @@ pub use byonm::ByonmNpmResolver; pub use byonm::ByonmNpmResolverCreateOptions; pub use byonm::ByonmNpmResolverRc; pub use byonm::ByonmResolvePkgFolderFromDenoReqError; +use deno_error::JsError; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; pub use local::normalize_pkg_name_for_node_modules_deno_folder; @@ -35,49 +36,57 @@ use url::Url; mod byonm; mod local; -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error("Could not resolve \"{}\", but found it in a package.json. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", specifier)] pub struct NodeModulesOutOfDateError { pub specifier: String, } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error("Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", package_json_path.display())] pub struct MissingPackageNodeModulesFolderError { pub package_json_path: PathBuf, } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct ResolveIfForNpmPackageError( pub Box, ); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolveIfForNpmPackageErrorKind { + #[class(inherit)] #[error(transparent)] NodeResolve(#[from] NodeResolveError), + #[class(inherit)] #[error(transparent)] NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError), } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct ResolveReqWithSubPathError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolveReqWithSubPathErrorKind { + #[class(inherit)] #[error(transparent)] MissingPackageNodeModulesFolder(#[from] MissingPackageNodeModulesFolderError), + #[class(inherit)] #[error(transparent)] ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError), + #[class(inherit)] #[error(transparent)] PackageSubpathResolve(#[from] PackageSubpathResolveError), } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolvePkgFolderFromDenoReqError { - // todo(dsherret): don't use anyhow here - #[error(transparent)] - Managed(anyhow::Error), + #[class(inherit)] + #[error("{0}")] + Managed(Box), + #[class(inherit)] #[error(transparent)] Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError), } diff --git a/resolvers/node/Cargo.toml b/resolvers/node/Cargo.toml index bdb0ba2ab1..31bca50a31 100644 --- a/resolvers/node/Cargo.toml +++ b/resolvers/node/Cargo.toml @@ -20,6 +20,7 @@ sync = ["deno_package_json/sync"] anyhow.workspace = true async-trait.workspace = true boxed_error.workspace = true +deno_error.workspace = true deno_media_type.workspace = true deno_package_json.workspace = true deno_path_util.workspace = true diff --git a/resolvers/node/errors.rs b/resolvers/node/errors.rs index 4157bd4c85..1b4ce460d1 100644 --- a/resolvers/node/errors.rs +++ b/resolvers/node/errors.rs @@ -5,6 +5,7 @@ use std::fmt::Write; use std::path::PathBuf; use boxed_error::Boxed; +use deno_error::JsError; use thiserror::Error; use url::Url; @@ -55,8 +56,7 @@ pub trait NodeJsErrorCoded { fn code(&self) -> NodeJsErrorCode; } -// todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError -#[derive(Debug, Clone, Error)] +#[derive(Debug, Clone, Error, JsError)] #[error( "[{}] Invalid module '{}' {}{}", self.code(), @@ -64,6 +64,7 @@ pub trait NodeJsErrorCoded { reason, maybe_referrer.as_ref().map(|referrer| format!(" imported from '{}'", referrer)).unwrap_or_default() )] +#[class(type)] pub struct InvalidModuleSpecifierError { pub request: String, pub reason: Cow<'static, str>, @@ -76,13 +77,15 @@ impl NodeJsErrorCoded for InvalidModuleSpecifierError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct LegacyResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum LegacyResolveErrorKind { + #[class(inherit)] #[error(transparent)] TypesNotFound(#[from] TypesNotFoundError), + #[class(inherit)] #[error(transparent)] ModuleNotFound(#[from] ModuleNotFoundError), } @@ -96,13 +99,14 @@ impl NodeJsErrorCoded for LegacyResolveError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] #[error( "Could not find package '{}' from referrer '{}'{}.", package_name, referrer, referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default() )] +#[class(generic)] pub struct PackageNotFoundError { pub package_name: String, pub referrer: Url, @@ -116,12 +120,13 @@ impl NodeJsErrorCoded for PackageNotFoundError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] #[error( "Could not find referrer npm package '{}'{}.", referrer, referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default() )] +#[class(generic)] pub struct ReferrerNotFoundError { pub referrer: Url, /// Extra information about the referrer. @@ -134,12 +139,14 @@ impl NodeJsErrorCoded for ReferrerNotFoundError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(inherit)] #[error("Failed resolving '{package_name}' from referrer '{referrer}'.")] pub struct PackageFolderResolveIoError { pub package_name: String, pub referrer: Url, #[source] + #[inherit] pub source: std::io::Error, } @@ -159,15 +166,18 @@ impl NodeJsErrorCoded for PackageFolderResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageFolderResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageFolderResolveErrorKind { + #[class(inherit)] #[error(transparent)] PackageNotFound(#[from] PackageNotFoundError), + #[class(inherit)] #[error(transparent)] ReferrerNotFound(#[from] ReferrerNotFoundError), + #[class(inherit)] #[error(transparent)] Io(#[from] PackageFolderResolveIoError), } @@ -182,20 +192,24 @@ impl NodeJsErrorCoded for PackageSubpathResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageSubpathResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageSubpathResolveErrorKind { + #[class(inherit)] #[error(transparent)] PkgJsonLoad(#[from] PackageJsonLoadError), + #[class(inherit)] #[error(transparent)] Exports(PackageExportsResolveError), + #[class(inherit)] #[error(transparent)] LegacyResolve(LegacyResolveError), } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error( "Target '{}' not found from '{}'{}{}.", target, @@ -241,19 +255,24 @@ impl NodeJsErrorCoded for PackageTargetResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageTargetResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageTargetResolveErrorKind { + #[class(inherit)] #[error(transparent)] NotFound(#[from] PackageTargetNotFoundError), + #[class(inherit)] #[error(transparent)] InvalidPackageTarget(#[from] InvalidPackageTargetError), + #[class(inherit)] #[error(transparent)] InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError), + #[class(inherit)] #[error(transparent)] PackageResolve(#[from] PackageResolveError), + #[class(inherit)] #[error(transparent)] TypesNotFound(#[from] TypesNotFoundError), } @@ -267,24 +286,27 @@ impl NodeJsErrorCoded for PackageExportsResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageExportsResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageExportsResolveErrorKind { + #[class(inherit)] #[error(transparent)] PackagePathNotExported(#[from] PackagePathNotExportedError), + #[class(inherit)] #[error(transparent)] PackageTargetResolve(#[from] PackageTargetResolveError), } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] #[error( "[{}] Could not find types for '{}'{}", self.code(), self.0.code_specifier, self.0.maybe_referrer.as_ref().map(|r| format!(" imported from '{}'", r)).unwrap_or_default(), )] +#[class(generic)] pub struct TypesNotFoundError(pub Box); #[derive(Debug)] @@ -299,7 +321,7 @@ impl NodeJsErrorCoded for TypesNotFoundError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] #[error( "[{}] Invalid package config. {}", self.code(), @@ -325,17 +347,18 @@ impl NodeJsErrorCoded for ClosestPkgJsonError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct ClosestPkgJsonError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ClosestPkgJsonErrorKind { + #[class(inherit)] #[error(transparent)] Load(#[from] PackageJsonLoadError), } -// todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(type)] #[error( "[{}] Package import specifier \"{}\" is not defined{}{}", self.code(), @@ -355,17 +378,21 @@ impl NodeJsErrorCoded for PackageImportNotDefinedError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageImportsResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageImportsResolveErrorKind { + #[class(inherit)] #[error(transparent)] ClosestPkgJson(ClosestPkgJsonError), + #[class(inherit)] #[error(transparent)] InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError), + #[class(inherit)] #[error(transparent)] NotDefined(#[from] PackageImportNotDefinedError), + #[class(inherit)] #[error(transparent)] Target(#[from] PackageTargetResolveError), } @@ -393,24 +420,30 @@ impl NodeJsErrorCoded for PackageResolveError { } } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct PackageResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum PackageResolveErrorKind { + #[class(inherit)] #[error(transparent)] ClosestPkgJson(#[from] ClosestPkgJsonError), + #[class(inherit)] #[error(transparent)] InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError), + #[class(inherit)] #[error(transparent)] PackageFolderResolve(#[from] PackageFolderResolveError), + #[class(inherit)] #[error(transparent)] ExportsResolve(#[from] PackageExportsResolveError), + #[class(inherit)] #[error(transparent)] SubpathResolve(#[from] PackageSubpathResolveError), } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error("Failed joining '{path}' from '{base}'.")] pub struct NodeResolveRelativeJoinError { pub path: String, @@ -419,43 +452,54 @@ pub struct NodeResolveRelativeJoinError { pub source: url::ParseError, } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error("Failed resolving specifier from data url referrer.")] pub struct DataUrlReferrerError { #[source] pub source: url::ParseError, } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct NodeResolveError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum NodeResolveErrorKind { + #[class(inherit)] #[error(transparent)] RelativeJoin(#[from] NodeResolveRelativeJoinError), + #[class(inherit)] #[error(transparent)] PackageImportsResolve(#[from] PackageImportsResolveError), + #[class(inherit)] #[error(transparent)] UnsupportedEsmUrlScheme(#[from] UnsupportedEsmUrlSchemeError), + #[class(inherit)] #[error(transparent)] DataUrlReferrer(#[from] DataUrlReferrerError), + #[class(inherit)] #[error(transparent)] PackageResolve(#[from] PackageResolveError), + #[class(inherit)] #[error(transparent)] TypesNotFound(#[from] TypesNotFoundError), + #[class(inherit)] #[error(transparent)] FinalizeResolution(#[from] FinalizeResolutionError), } -#[derive(Debug, Boxed)] +#[derive(Debug, Boxed, JsError)] pub struct FinalizeResolutionError(pub Box); -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum FinalizeResolutionErrorKind { + #[class(inherit)] #[error(transparent)] InvalidModuleSpecifierError(#[from] InvalidModuleSpecifierError), + #[class(inherit)] #[error(transparent)] ModuleNotFound(#[from] ModuleNotFoundError), + #[class(inherit)] #[error(transparent)] UnsupportedDirImport(#[from] UnsupportedDirImportError), } @@ -470,7 +514,8 @@ impl NodeJsErrorCoded for FinalizeResolutionError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error( "[{}] Cannot find {} '{}'{}", self.code(), @@ -490,7 +535,8 @@ impl NodeJsErrorCoded for ModuleNotFoundError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] +#[class(generic)] #[error( "[{}] Directory import '{}' is not supported resolving ES modules{}", self.code(), @@ -508,7 +554,8 @@ impl NodeJsErrorCoded for UnsupportedDirImportError { } } -#[derive(Debug)] +#[derive(Debug, JsError)] +#[class(generic)] pub struct InvalidPackageTargetError { pub pkg_json_path: PathBuf, pub sub_path: String, @@ -564,7 +611,8 @@ impl NodeJsErrorCoded for InvalidPackageTargetError { } } -#[derive(Debug)] +#[derive(Debug, JsError)] +#[class(generic)] pub struct PackagePathNotExportedError { pub pkg_json_path: PathBuf, pub subpath: String, @@ -614,7 +662,8 @@ impl std::fmt::Display for PackagePathNotExportedError { } } -#[derive(Debug, Clone, Error)] +#[derive(Debug, Clone, Error, JsError)] +#[class(type)] #[error( "[{}] Only file and data URLs are supported by the default ESM loader.{} Received protocol '{}'", self.code(), @@ -631,20 +680,25 @@ impl NodeJsErrorCoded for UnsupportedEsmUrlSchemeError { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolvePkgJsonBinExportError { + #[class(inherit)] #[error(transparent)] PkgJsonLoad(#[from] PackageJsonLoadError), + #[class(generic)] #[error("Failed resolving binary export. '{}' did not exist", pkg_json_path.display())] MissingPkgJson { pkg_json_path: PathBuf }, + #[class(generic)] #[error("Failed resolving binary export. {message}")] InvalidBinProperty { message: String }, } -#[derive(Debug, Error)] +#[derive(Debug, Error, JsError)] pub enum ResolveBinaryCommandsError { + #[class(inherit)] #[error(transparent)] PkgJsonLoad(#[from] PackageJsonLoadError), + #[class(generic)] #[error("'{}' did not have a name", pkg_json_path.display())] MissingPkgJsonName { pkg_json_path: PathBuf }, } @@ -659,7 +713,7 @@ mod test { assert_eq!( PackagePathNotExportedError { pkg_json_path: PathBuf::from("test_path").join("package.json"), - subpath: "./jsx-runtime".to_string(), + subpath: "./jsx-runtime".to_string(), maybe_referrer: None, resolution_kind: NodeResolutionKind::Types }.to_string(), @@ -668,7 +722,7 @@ mod test { assert_eq!( PackagePathNotExportedError { pkg_json_path: PathBuf::from("test_path").join("package.json"), - subpath: ".".to_string(), + subpath: ".".to_string(), maybe_referrer: None, resolution_kind: NodeResolutionKind::Types }.to_string(), diff --git a/resolvers/npm_cache/fs_util.rs b/resolvers/npm_cache/fs_util.rs index 625d83e24d..77269ebe0b 100644 --- a/resolvers/npm_cache/fs_util.rs +++ b/resolvers/npm_cache/fs_util.rs @@ -2,10 +2,9 @@ use std::io::ErrorKind; use std::path::Path; +use std::path::PathBuf; use std::time::Duration; -use anyhow::Context; -use anyhow::Error as AnyError; use sys_traits::FsCreateDirAll; use sys_traits::FsDirEntry; use sys_traits::FsHardLink; @@ -13,6 +12,56 @@ use sys_traits::FsReadDir; use sys_traits::FsRemoveFile; use sys_traits::ThreadSleep; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum HardLinkDirRecursiveError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error("Creating {path}")] + Creating { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Creating {path}")] + Reading { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Dir {from} to {to}")] + Dir { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: Box, + }, + #[class(inherit)] + #[error("Removing file to hard link {from} to {to}")] + RemoveFileToHardLink { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Hard linking {from} to {to}")] + HardLinking { + from: PathBuf, + to: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, +} + /// Hardlinks the files in one directory to another directory. /// /// Note: Does not handle symlinks. @@ -22,13 +71,19 @@ pub fn hard_link_dir_recursive< sys: &TSys, from: &Path, to: &Path, -) -> Result<(), AnyError> { - sys - .fs_create_dir_all(to) - .with_context(|| format!("Creating {}", to.display()))?; - let read_dir = sys - .fs_read_dir(from) - .with_context(|| format!("Reading {}", from.display()))?; +) -> Result<(), HardLinkDirRecursiveError> { + sys.fs_create_dir_all(to).map_err(|source| { + HardLinkDirRecursiveError::Creating { + path: to.to_path_buf(), + source, + } + })?; + let read_dir = sys.fs_read_dir(from).map_err(|source| { + HardLinkDirRecursiveError::Reading { + path: from.to_path_buf(), + source, + } + })?; for entry in read_dir { let entry = entry?; @@ -37,8 +92,12 @@ pub fn hard_link_dir_recursive< let new_to = to.join(entry.file_name()); if file_type.is_dir() { - hard_link_dir_recursive(sys, &new_from, &new_to).with_context(|| { - format!("Dir {} to {}", new_from.display(), new_to.display()) + hard_link_dir_recursive(sys, &new_from, &new_to).map_err(|source| { + HardLinkDirRecursiveError::Dir { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: Box::new(source), + } })?; } else if file_type.is_file() { // note: chance for race conditions here between attempting to create, @@ -55,12 +114,10 @@ pub fn hard_link_dir_recursive< // faster to reduce contention. sys.thread_sleep(Duration::from_millis(10)); } else { - return Err(err).with_context(|| { - format!( - "Removing file to hard link {} to {}", - new_from.display(), - new_to.display() - ) + return Err(HardLinkDirRecursiveError::RemoveFileToHardLink { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: err, }); } } @@ -74,22 +131,18 @@ pub fn hard_link_dir_recursive< if err.kind() == ErrorKind::AlreadyExists { sys.thread_sleep(Duration::from_millis(10)); } else { - return Err(err).with_context(|| { - format!( - "Hard linking {} to {}", - new_from.display(), - new_to.display() - ) + return Err(HardLinkDirRecursiveError::HardLinking { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: err, }); } } } else { - return Err(err).with_context(|| { - format!( - "Hard linking {} to {}", - new_from.display(), - new_to.display() - ) + return Err(HardLinkDirRecursiveError::HardLinking { + from: new_from.to_path_buf(), + to: new_to.to_path_buf(), + source: err, }); } } diff --git a/resolvers/npm_cache/lib.rs b/resolvers/npm_cache/lib.rs index 012c277e22..90c07de5d5 100644 --- a/resolvers/npm_cache/lib.rs +++ b/resolvers/npm_cache/lib.rs @@ -6,11 +6,10 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use anyhow::bail; -use anyhow::Context; use anyhow::Error as AnyError; use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::npm::NpmCacheDir; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_npm::NpmPackageCacheFolderId; @@ -45,9 +44,11 @@ pub use fs_util::hard_link_dir_recursive; pub use registry_info::get_package_url; pub use registry_info::RegistryInfoProvider; pub use remote::maybe_auth_header_for_npm_registry; +pub use tarball::EnsurePackageError; pub use tarball::TarballCache; -#[derive(Debug)] +#[derive(Debug, deno_error::JsError)] +#[class(generic)] pub struct DownloadError { pub status_code: Option, pub error: AnyError, @@ -203,7 +204,7 @@ impl< pub fn ensure_copy_package( &self, folder_id: &NpmPackageCacheFolderId, - ) -> Result<(), AnyError> { + ) -> Result<(), WithFolderSyncLockError> { let registry_url = self.npmrc.get_registry_url(&folder_id.nv.name); assert_ne!(folder_id.copy_index, 0); let package_folder = self.cache_dir.package_folder_for_id( @@ -237,6 +238,7 @@ impl< &original_package_folder, &package_folder, ) + .map_err(JsErrorBox::from_err) })?; Ok(()) } @@ -295,15 +297,15 @@ impl< pub fn load_package_info( &self, name: &str, - ) -> Result, AnyError> { + ) -> Result, serde_json::Error> { let file_cache_path = self.get_registry_package_info_file_cache_path(name); let file_text = match std::fs::read_to_string(file_cache_path) { Ok(file_text) => file_text, Err(err) if err.kind() == ErrorKind::NotFound => return Ok(None), - Err(err) => return Err(err.into()), + Err(err) => return Err(serde_json::Error::io(err)), }; - Ok(serde_json::from_str(&file_text)?) + serde_json::from_str(&file_text) } pub fn save_package_info( @@ -330,18 +332,52 @@ impl< const NPM_PACKAGE_SYNC_LOCK_FILENAME: &str = ".deno_sync_lock"; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum WithFolderSyncLockError { + #[class(inherit)] + #[error("Error creating '{path}'")] + CreateDir { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error("Error creating package sync lock file at '{path}'. Maybe try manually deleting this folder.")] + CreateLockFile { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + Action(#[from] JsErrorBox), + #[class(generic)] + #[error("Failed setting up package cache directory for {package}, then failed cleaning it up.\n\nOriginal error:\n\n{error}\n\nRemove error:\n\n{remove_error}\n\nPlease manually delete this folder or you will run into issues using this package in the future:\n\n{output_folder}")] + SetUpPackageCacheDir { + package: Box, + error: Box, + remove_error: std::io::Error, + output_folder: PathBuf, + }, +} + // todo(dsherret): use `sys` here instead of `std::fs`. fn with_folder_sync_lock( package: &PackageNv, output_folder: &Path, - action: impl FnOnce() -> Result<(), AnyError>, -) -> Result<(), AnyError> { + action: impl FnOnce() -> Result<(), JsErrorBox>, +) -> Result<(), WithFolderSyncLockError> { fn inner( output_folder: &Path, - action: impl FnOnce() -> Result<(), AnyError>, - ) -> Result<(), AnyError> { - std::fs::create_dir_all(output_folder).with_context(|| { - format!("Error creating '{}'.", output_folder.display()) + action: impl FnOnce() -> Result<(), JsErrorBox>, + ) -> Result<(), WithFolderSyncLockError> { + std::fs::create_dir_all(output_folder).map_err(|source| { + WithFolderSyncLockError::CreateDir { + path: output_folder.to_path_buf(), + source, + } })?; // This sync lock file is a way to ensure that partially created @@ -365,16 +401,10 @@ fn with_folder_sync_lock( let _ignore = std::fs::remove_file(&sync_lock_path); Ok(()) } - Err(err) => { - bail!( - concat!( - "Error creating package sync lock file at '{}'. ", - "Maybe try manually deleting this folder.\n\n{:#}", - ), - output_folder.display(), - err - ); - } + Err(err) => Err(WithFolderSyncLockError::CreateLockFile { + path: output_folder.to_path_buf(), + source: err, + }), } } @@ -383,19 +413,12 @@ fn with_folder_sync_lock( Err(err) => { if let Err(remove_err) = std::fs::remove_dir_all(output_folder) { if remove_err.kind() != std::io::ErrorKind::NotFound { - bail!( - concat!( - "Failed setting up package cache directory for {}, then ", - "failed cleaning it up.\n\nOriginal error:\n\n{}\n\n", - "Remove error:\n\n{}\n\nPlease manually ", - "delete this folder or you will run into issues using this ", - "package in the future:\n\n{}" - ), - package, - err, - remove_err, - output_folder.display(), - ); + return Err(WithFolderSyncLockError::SetUpPackageCacheDir { + package: Box::new(package.clone()), + error: Box::new(err), + remove_error: remove_err, + output_folder: output_folder.to_path_buf(), + }); } } Err(err) diff --git a/resolvers/npm_cache/registry_info.rs b/resolvers/npm_cache/registry_info.rs index 0637d75c19..ece797abba 100644 --- a/resolvers/npm_cache/registry_info.rs +++ b/resolvers/npm_cache/registry_info.rs @@ -4,20 +4,19 @@ use std::collections::HashMap; use std::collections::HashSet; use std::sync::Arc; -use anyhow::anyhow; -use anyhow::bail; -use anyhow::Context; -use anyhow::Error as AnyError; use async_trait::async_trait; +use deno_core::futures::future::LocalBoxFuture; +use deno_core::futures::FutureExt; +use deno_core::parking_lot::Mutex; +use deno_core::serde_json; +use deno_core::url::Url; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmRegistryApi; use deno_npm::registry::NpmRegistryPackageInfoLoadError; use deno_unsync::sync::AtomicFlag; use deno_unsync::sync::MultiRuntimeAsyncValueCreator; -use futures::future::LocalBoxFuture; -use futures::FutureExt; -use parking_lot::Mutex; use sys_traits::FsCreateDirAll; use sys_traits::FsHardLink; use sys_traits::FsMetadata; @@ -27,42 +26,15 @@ use sys_traits::FsRemoveFile; use sys_traits::FsRename; use sys_traits::SystemRandom; use sys_traits::ThreadSleep; -use thiserror::Error; -use url::Url; use crate::remote::maybe_auth_header_for_npm_registry; use crate::NpmCache; use crate::NpmCacheHttpClient; use crate::NpmCacheSetting; -type LoadResult = Result>; +type LoadResult = Result>; type LoadFuture = LocalBoxFuture<'static, LoadResult>; -#[derive(Debug, Error)] -#[error(transparent)] -pub struct AnyhowJsError(pub AnyError); - -impl deno_error::JsErrorClass for AnyhowJsError { - fn get_class(&self) -> &'static str { - "generic" - } - - fn get_message(&self) -> std::borrow::Cow<'static, str> { - self.0.to_string().into() - } - - fn get_additional_properties( - &self, - ) -> Option< - Vec<( - std::borrow::Cow<'static, str>, - std::borrow::Cow<'static, str>, - )>, - > { - None - } -} - #[derive(Debug, Clone)] enum FutureResult { PackageNotExists, @@ -80,7 +52,7 @@ enum MemoryCacheItem { FsCached, /// An item is memory cached when it fails saving to the file system cache /// or the package does not exist. - MemoryCached(Result>, Arc>), + MemoryCached(Result>, Arc>), } #[derive(Debug, Default)] @@ -125,6 +97,39 @@ impl MemoryCache { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +pub enum LoadFileCachedPackageInfoError { + #[error("Previously saved '{name}' from the npm cache, but now it fails to load: {err}")] + LoadPackageInfo { + err: serde_json::Error, + name: String, + }, + #[error("The package '{0}' previously saved its registry information to the file system cache, but that file no longer exists.")] + FileMissing(String), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(inherit)] +#[error("Failed loading {url} for package \"{name}\"")] +pub struct LoadPackageInfoError { + url: Url, + name: String, + #[inherit] + #[source] + inner: LoadPackageInfoInnerError, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum LoadPackageInfoInnerError { + #[class(inherit)] + #[error("{0}")] + LoadFileCachedPackageInfo(LoadFileCachedPackageInfoError), + #[class(inherit)] + #[error("{0}")] + Other(Arc), +} + // todo(#27198): refactor to store this only in the http cache /// Downloads packuments from the npm registry. @@ -224,7 +229,7 @@ impl< package_name: name.to_string(), }), Err(err) => Err(NpmRegistryPackageInfoLoadError::LoadError(Arc::new( - AnyhowJsError(err), + JsErrorBox::from_err(err), ))), } } @@ -232,20 +237,20 @@ impl< pub async fn maybe_package_info( self: &Arc, name: &str, - ) -> Result>, AnyError> { - self.load_package_info_inner(name).await.with_context(|| { - format!( - "Failed loading {} for package \"{}\"", - get_package_url(&self.npmrc, name), - name - ) + ) -> Result>, LoadPackageInfoError> { + self.load_package_info_inner(name).await.map_err(|err| { + LoadPackageInfoError { + url: get_package_url(&self.npmrc, name), + name: name.to_string(), + inner: err, + } }) } async fn load_package_info_inner( self: &Arc, name: &str, - ) -> Result>, AnyError> { + ) -> Result>, LoadPackageInfoInnerError> { let (cache_item, clear_id) = { let mut mem_cache = self.memory_cache.lock(); let cache_item = if let Some(cache_item) = mem_cache.get(name) { @@ -270,9 +275,10 @@ impl< .load_file_cached_package_info(name) .await .map(|info| Some(Arc::new(info))) + .map_err(LoadPackageInfoInnerError::LoadFileCachedPackageInfo) } MemoryCacheItem::MemoryCached(maybe_info) => { - maybe_info.clone().map_err(|e| anyhow!("{}", e)) + maybe_info.clone().map_err(LoadPackageInfoInnerError::Other) } MemoryCacheItem::Pending(value_creator) => { match value_creator.get().await { @@ -304,13 +310,13 @@ impl< Ok(None) } Err(err) => { - let return_err = anyhow!("{:#}", err); + let return_err = err.clone(); self.memory_cache.lock().try_insert( clear_id, name, MemoryCacheItem::MemoryCached(Err(err)), ); - Err(return_err) + Err(LoadPackageInfoInnerError::Other(return_err)) } } } @@ -320,7 +326,7 @@ impl< async fn load_file_cached_package_info( &self, name: &str, - ) -> Result { + ) -> Result { // this scenario failing should be exceptionally rare so let's // deal with improving it only when anyone runs into an issue let maybe_package_info = deno_unsync::spawn_blocking({ @@ -330,17 +336,15 @@ impl< }) .await .unwrap() - .with_context(|| { - format!( - "Previously saved '{}' from the npm cache, but now it fails to load.", - name - ) + .map_err(|err| LoadFileCachedPackageInfoError::LoadPackageInfo { + err, + name: name.to_string(), })?; match maybe_package_info { Some(package_info) => Ok(package_info), - None => { - bail!("The package '{}' previously saved its registry information to the file system cache, but that file no longer exists.", name) - } + None => Err(LoadFileCachedPackageInfoError::FileMissing( + name.to_string(), + )), } } @@ -352,7 +356,8 @@ impl< match maybe_auth_header_for_npm_registry(registry_config) { Ok(maybe_auth_header) => maybe_auth_header, Err(err) => { - return std::future::ready(Err(Arc::new(err))).boxed_local() + return std::future::ready(Err(Arc::new(JsErrorBox::from_err(err)))) + .boxed_local() } }; let name = name.to_string(); @@ -363,14 +368,14 @@ impl< || downloader.previously_loaded_packages.lock().contains(&name) { // attempt to load from the file cache - if let Some(info) = downloader.cache.load_package_info(&name)? { + if let Some(info) = downloader.cache.load_package_info(&name).map_err(JsErrorBox::from_err)? { let result = Arc::new(info); return Ok(FutureResult::SavedFsCache(result)); } } if *downloader.cache.cache_setting() == NpmCacheSetting::Only { - return Err(deno_core::error::custom_error( + return Err(JsErrorBox::new( "NotCached", format!( "npm package not found in cache: \"{name}\", --cached-only is specified." @@ -386,12 +391,12 @@ impl< package_url, maybe_auth_header, ) - .await?; + .await.map_err(JsErrorBox::from_err)?; match maybe_bytes { Some(bytes) => { let future_result = deno_unsync::spawn_blocking( - move || -> Result { - let package_info = serde_json::from_slice(&bytes)?; + move || -> Result { + let package_info = serde_json::from_slice(&bytes).map_err(JsErrorBox::from_err)?; match downloader.cache.save_package_info(&name, &package_info) { Ok(()) => { Ok(FutureResult::SavedFsCache(Arc::new(package_info))) @@ -407,7 +412,8 @@ impl< } }, ) - .await??; + .await + .map_err(JsErrorBox::from_err)??; Ok(future_result) } None => Ok(FutureResult::PackageNotExists), diff --git a/resolvers/npm_cache/remote.rs b/resolvers/npm_cache/remote.rs index 16eb0f6cd9..0e04d05502 100644 --- a/resolvers/npm_cache/remote.rs +++ b/resolvers/npm_cache/remote.rs @@ -1,17 +1,27 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use anyhow::bail; -use anyhow::Context; -use anyhow::Error as AnyError; use base64::prelude::BASE64_STANDARD; use base64::Engine; use deno_npm::npm_rc::RegistryConfig; use http::header; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum AuthHeaderForNpmRegistryError { + #[class(type)] + #[error("Both the username and password must be provided for basic auth")] + Both, + #[class(type)] + #[error("The password in npmrc is an invalid base64 string: {0}")] + Base64(base64::DecodeError), +} + // TODO(bartlomieju): support more auth methods besides token and basic auth pub fn maybe_auth_header_for_npm_registry( registry_config: &RegistryConfig, -) -> Result, AnyError> { +) -> Result< + Option<(header::HeaderName, header::HeaderValue)>, + AuthHeaderForNpmRegistryError, +> { if let Some(token) = registry_config.auth_token.as_ref() { return Ok(Some(( header::AUTHORIZATION, @@ -33,7 +43,7 @@ pub fn maybe_auth_header_for_npm_registry( if (username.is_some() && password.is_none()) || (username.is_none() && password.is_some()) { - bail!("Both the username and password must be provided for basic auth") + return Err(AuthHeaderForNpmRegistryError::Both); } if username.is_some() && password.is_some() { @@ -42,7 +52,7 @@ pub fn maybe_auth_header_for_npm_registry( // https://github.com/npm/cli/blob/780afc50e3a345feb1871a28e33fa48235bc3bd5/workspaces/config/lib/index.js#L846-L851 let pw_base64 = BASE64_STANDARD .decode(password.unwrap()) - .with_context(|| "The password in npmrc is an invalid base64 string")?; + .map_err(AuthHeaderForNpmRegistryError::Base64)?; let bearer = BASE64_STANDARD.encode(format!( "{}:{}", username.unwrap(), diff --git a/resolvers/npm_cache/tarball.rs b/resolvers/npm_cache/tarball.rs index 49ca3bc7fd..d9575ba9cd 100644 --- a/resolvers/npm_cache/tarball.rs +++ b/resolvers/npm_cache/tarball.rs @@ -3,18 +3,16 @@ use std::collections::HashMap; use std::sync::Arc; -use anyhow::anyhow; -use anyhow::bail; -use anyhow::Context; -use anyhow::Error as AnyError; +use deno_core::futures::future::LocalBoxFuture; +use deno_core::futures::FutureExt; +use deno_core::parking_lot::Mutex; +use deno_core::url::Url; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageVersionDistInfo; use deno_semver::package::PackageNv; use deno_unsync::sync::MultiRuntimeAsyncValueCreator; -use futures::future::LocalBoxFuture; -use futures::FutureExt; use http::StatusCode; -use parking_lot::Mutex; use sys_traits::FsCreateDirAll; use sys_traits::FsHardLink; use sys_traits::FsMetadata; @@ -24,7 +22,6 @@ use sys_traits::FsRemoveFile; use sys_traits::FsRename; use sys_traits::SystemRandom; use sys_traits::ThreadSleep; -use url::Url; use crate::remote::maybe_auth_header_for_npm_registry; use crate::tarball_extract::verify_and_extract_tarball; @@ -33,7 +30,7 @@ use crate::NpmCache; use crate::NpmCacheHttpClient; use crate::NpmCacheSetting; -type LoadResult = Result<(), Arc>; +type LoadResult = Result<(), Arc>; type LoadFuture = LocalBoxFuture<'static, LoadResult>; #[derive(Debug, Clone)] @@ -41,7 +38,7 @@ enum MemoryCacheItem { /// The cache item hasn't finished yet. Pending(Arc>), /// The result errored. - Errored(Arc), + Errored(Arc), /// This package has already been cached. Cached, } @@ -73,6 +70,14 @@ pub struct TarballCache< memory_cache: Mutex>, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +#[error("Failed caching npm package '{package_nv}'")] +pub struct EnsurePackageError { + package_nv: Box, + #[source] + source: Arc, +} impl< THttpClient: NpmCacheHttpClient, TSys: FsCreateDirAll @@ -108,18 +113,21 @@ impl< self: &Arc, package_nv: &PackageNv, dist: &NpmPackageVersionDistInfo, - ) -> Result<(), AnyError> { + ) -> Result<(), EnsurePackageError> { self .ensure_package_inner(package_nv, dist) .await - .with_context(|| format!("Failed caching npm package '{}'.", package_nv)) + .map_err(|source| EnsurePackageError { + package_nv: Box::new(package_nv.clone()), + source, + }) } async fn ensure_package_inner( self: &Arc, package_nv: &PackageNv, dist: &NpmPackageVersionDistInfo, - ) -> Result<(), AnyError> { + ) -> Result<(), Arc> { let cache_item = { let mut mem_cache = self.memory_cache.lock(); if let Some(cache_item) = mem_cache.get(package_nv) { @@ -141,7 +149,7 @@ impl< match cache_item { MemoryCacheItem::Cached => Ok(()), - MemoryCacheItem::Errored(err) => Err(anyhow!("{:#}", err)), + MemoryCacheItem::Errored(err) => Err(err), MemoryCacheItem::Pending(creator) => { let result = creator.get().await; match result { @@ -151,10 +159,9 @@ impl< Ok(()) } Err(err) => { - let result_err = anyhow!("{:#}", err); *self.memory_cache.lock().get_mut(package_nv).unwrap() = - MemoryCacheItem::Errored(err); - Err(result_err) + MemoryCacheItem::Errored(err.clone()); + Err(err) } } } @@ -176,7 +183,7 @@ impl< if should_use_cache && package_folder_exists { return Ok(()); } else if tarball_cache.cache.cache_setting() == &NpmCacheSetting::Only { - return Err(deno_core::error::custom_error( + return Err(JsErrorBox::new( "NotCached", format!( "npm package not found in cache: \"{}\", --cached-only is specified.", @@ -187,12 +194,12 @@ impl< } if dist.tarball.is_empty() { - bail!("Tarball URL was empty."); + return Err(JsErrorBox::generic("Tarball URL was empty.")); } // IMPORTANT: npm registries may specify tarball URLs at different URLS than the // registry, so we MUST get the auth for the tarball URL and not the registry URL. - let tarball_uri = Url::parse(&dist.tarball)?; + let tarball_uri = Url::parse(&dist.tarball).map_err(JsErrorBox::from_err)?; let maybe_registry_config = tarball_cache.npmrc.tarball_config(&tarball_uri); let maybe_auth_header = maybe_registry_config.and_then(|c| maybe_auth_header_for_npm_registry(c).ok()?); @@ -207,7 +214,7 @@ impl< && maybe_registry_config.is_none() && tarball_cache.npmrc.get_registry_config(&package_nv.name).auth_token.is_some() { - bail!( + return Err(JsErrorBox::generic(format!( concat!( "No auth for tarball URI, but present for scoped registry.\n\n", "Tarball URI: {}\n", @@ -216,9 +223,9 @@ impl< ), dist.tarball, registry_url, - ) + ))); } - return Err(err.into()) + return Err(JsErrorBox::from_err(err)) }, }; match maybe_bytes { @@ -247,10 +254,10 @@ impl< extraction_mode, ) }) - .await? + .await.map_err(JsErrorBox::from_err)?.map_err(JsErrorBox::from_err) } None => { - bail!("Could not find npm package tarball at: {}", dist.tarball); + Err(JsErrorBox::generic(format!("Could not find npm package tarball at: {}", dist.tarball))) } } } diff --git a/resolvers/npm_cache/tarball_extract.rs b/resolvers/npm_cache/tarball_extract.rs index cf408ac632..e53e4544d2 100644 --- a/resolvers/npm_cache/tarball_extract.rs +++ b/resolvers/npm_cache/tarball_extract.rs @@ -7,9 +7,6 @@ use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; -use anyhow::bail; -use anyhow::Context; -use anyhow::Error as AnyError; use base64::prelude::BASE64_STANDARD; use base64::Engine; use deno_npm::registry::NpmPackageVersionDistInfo; @@ -31,23 +28,37 @@ pub enum TarballExtractionMode { SiblingTempDir, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum VerifyAndExtractTarballError { + #[class(inherit)] + #[error(transparent)] + TarballIntegrity(#[from] TarballIntegrityError), + #[class(inherit)] + #[error(transparent)] + ExtractTarball(#[from] ExtractTarballError), + #[class(inherit)] + #[error("Failed moving extracted tarball to final destination")] + MoveFailed(std::io::Error), +} + pub fn verify_and_extract_tarball( package_nv: &PackageNv, data: &[u8], dist_info: &NpmPackageVersionDistInfo, output_folder: &Path, extraction_mode: TarballExtractionMode, -) -> Result<(), AnyError> { +) -> Result<(), VerifyAndExtractTarballError> { verify_tarball_integrity(package_nv, data, &dist_info.integrity())?; match extraction_mode { - TarballExtractionMode::Overwrite => extract_tarball(data, output_folder), + TarballExtractionMode::Overwrite => { + extract_tarball(data, output_folder).map_err(Into::into) + } TarballExtractionMode::SiblingTempDir => { let temp_dir = get_atomic_dir_path(output_folder); extract_tarball(data, &temp_dir)?; rename_with_retries(&temp_dir, output_folder) - .map_err(AnyError::from) - .context("Failed moving extracted tarball to final destination.") + .map_err(VerifyAndExtractTarballError::MoveFailed) } } } @@ -89,11 +100,32 @@ fn rename_with_retries( } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +pub enum TarballIntegrityError { + #[error("Not implemented hash function for {package}: {hash_kind}")] + NotImplementedHashFunction { + package: Box, + hash_kind: String, + }, + #[error("Not implemented integrity kind for {package}: {integrity}")] + NotImplementedIntegrityKind { + package: Box, + integrity: String, + }, + #[error("Tarball checksum did not match what was provided by npm registry for {package}.\n\nExpected: {expected}\nActual: {actual}")] + MismatchedChecksum { + package: Box, + expected: String, + actual: String, + }, +} + fn verify_tarball_integrity( package: &PackageNv, data: &[u8], npm_integrity: &NpmPackageVersionDistInfoIntegrity, -) -> Result<(), AnyError> { +) -> Result<(), TarballIntegrityError> { use ring::digest::Context; let (tarball_checksum, expected_checksum) = match npm_integrity { NpmPackageVersionDistInfoIntegrity::Integrity { @@ -103,11 +135,12 @@ fn verify_tarball_integrity( let algo = match *algorithm { "sha512" => &ring::digest::SHA512, "sha1" => &ring::digest::SHA1_FOR_LEGACY_USE_ONLY, - hash_kind => bail!( - "Not implemented hash function for {}: {}", - package, - hash_kind - ), + hash_kind => { + return Err(TarballIntegrityError::NotImplementedHashFunction { + package: Box::new(package.clone()), + hash_kind: hash_kind.to_string(), + }); + } }; let mut hash_ctx = Context::new(algo); hash_ctx.update(data); @@ -123,26 +156,39 @@ fn verify_tarball_integrity( (tarball_checksum, hex) } NpmPackageVersionDistInfoIntegrity::UnknownIntegrity(integrity) => { - bail!( - "Not implemented integrity kind for {}: {}", - package, - integrity - ) + return Err(TarballIntegrityError::NotImplementedIntegrityKind { + package: Box::new(package.clone()), + integrity: integrity.to_string(), + }); } }; if tarball_checksum != *expected_checksum { - bail!( - "Tarball checksum did not match what was provided by npm registry for {}.\n\nExpected: {}\nActual: {}", - package, - expected_checksum, - tarball_checksum, - ) + return Err(TarballIntegrityError::MismatchedChecksum { + package: Box::new(package.clone()), + expected: expected_checksum.to_string(), + actual: tarball_checksum, + }); } Ok(()) } -fn extract_tarball(data: &[u8], output_folder: &Path) -> Result<(), AnyError> { +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ExtractTarballError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(generic)] + #[error( + "Extracted directory '{0}' of npm tarball was not in output directory." + )] + NotInOutputDirectory(PathBuf), +} + +fn extract_tarball( + data: &[u8], + output_folder: &Path, +) -> Result<(), ExtractTarballError> { fs::create_dir_all(output_folder)?; let output_folder = fs::canonicalize(output_folder)?; let tar = GzDecoder::new(data); @@ -174,10 +220,9 @@ fn extract_tarball(data: &[u8], output_folder: &Path) -> Result<(), AnyError> { fs::create_dir_all(dir_path)?; let canonicalized_dir = fs::canonicalize(dir_path)?; if !canonicalized_dir.starts_with(&output_folder) { - bail!( - "Extracted directory '{}' of npm tarball was not in output directory.", - canonicalized_dir.display() - ) + return Err(ExtractTarballError::NotInOutputDirectory( + canonicalized_dir.to_path_buf(), + )); } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 9826a7459d..72db8888f8 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -79,6 +79,7 @@ deno_console.workspace = true deno_core.workspace = true deno_cron.workspace = true deno_crypto.workspace = true +deno_error.workspace = true deno_fetch.workspace = true deno_ffi.workspace = true deno_fs = { workspace = true, features = ["sync_fs"] } diff --git a/runtime/errors.rs b/runtime/errors.rs deleted file mode 100644 index feb4942967..0000000000 --- a/runtime/errors.rs +++ /dev/null @@ -1,1983 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -//! There are many types of errors in Deno: -//! - AnyError: a generic wrapper that can encapsulate any type of error. -//! - JsError: a container for the error message and stack trace for exceptions -//! thrown in JavaScript code. We use this to pretty-print stack traces. -//! - Diagnostic: these are errors that originate in TypeScript's compiler. -//! They're similar to JsError, in that they have line numbers. But -//! Diagnostics are compile-time type errors, whereas JsErrors are runtime -//! exceptions. - -use std::env; -use std::error::Error; -use std::io; -use std::sync::Arc; - -use deno_broadcast_channel::BroadcastChannelError; -use deno_cache::CacheError; -use deno_canvas::CanvasError; -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::url; -use deno_core::ModuleResolutionError; -use deno_cron::CronError; -use deno_crypto::DecryptError; -use deno_crypto::EncryptError; -use deno_crypto::ExportKeyError; -use deno_crypto::GenerateKeyError; -use deno_crypto::ImportKeyError; -use deno_fetch::FetchError; -use deno_fetch::HttpClientCreateError; -use deno_ffi::CallError; -use deno_ffi::CallbackError; -use deno_ffi::DlfcnError; -use deno_ffi::IRError; -use deno_ffi::ReprError; -use deno_ffi::StaticError; -use deno_fs::FsOpsError; -use deno_fs::FsOpsErrorKind; -use deno_http::HttpError; -use deno_http::HttpNextError; -use deno_http::WebSocketUpgradeError; -use deno_io::fs::FsError; -use deno_kv::KvCheckError; -use deno_kv::KvError; -use deno_kv::KvErrorKind; -use deno_kv::KvMutationError; -use deno_napi::NApiError; -use deno_net::ops::NetError; -use deno_net::QuicError; -use deno_permissions::ChildPermissionError; -use deno_permissions::NetDescriptorFromUrlParseError; -use deno_permissions::PathResolveError; -use deno_permissions::PermissionCheckError; -use deno_permissions::RunDescriptorParseError; -use deno_permissions::SysDescriptorParseError; -use deno_tls::TlsError; -use deno_web::BlobError; -use deno_web::CompressionError; -use deno_web::MessagePortError; -use deno_web::StreamResourceError; -use deno_web::WebError; -use deno_websocket::HandshakeError; -use deno_websocket::WebsocketError; -use deno_webstorage::WebStorageError; -use rustyline::error::ReadlineError; - -use crate::ops::fs_events::FsEventsError; -use crate::ops::http::HttpStartError; -use crate::ops::os::OsError; -use crate::ops::permissions::PermissionError; -use crate::ops::process::CheckRunPermissionError; -use crate::ops::process::ProcessError; -use crate::ops::signal::SignalError; -use crate::ops::tty::TtyError; -use crate::ops::web_worker::SyncFetchError; -use crate::ops::worker_host::CreateWorkerError; - -fn get_run_descriptor_parse_error(e: &RunDescriptorParseError) -> &'static str { - match e { - RunDescriptorParseError::Which(_) => "Error", - RunDescriptorParseError::PathResolve(e) => get_path_resolve_error(e), - RunDescriptorParseError::EmptyRunQuery => "Error", - } -} - -fn get_sys_descriptor_parse_error(e: &SysDescriptorParseError) -> &'static str { - match e { - SysDescriptorParseError::InvalidKind(_) => "TypeError", - SysDescriptorParseError::Empty => "Error", - } -} - -fn get_path_resolve_error(e: &PathResolveError) -> &'static str { - match e { - PathResolveError::CwdResolve(e) => get_io_error_class(e), - PathResolveError::EmptyPath => "Error", - } -} - -fn get_permission_error_class(e: &PermissionError) -> &'static str { - match e { - PermissionError::InvalidPermissionName(_) => "ReferenceError", - PermissionError::PathResolve(e) => get_path_resolve_error(e), - PermissionError::NetDescriptorParse(_) => "URIError", - PermissionError::SysDescriptorParse(e) => get_sys_descriptor_parse_error(e), - PermissionError::RunDescriptorParse(e) => get_run_descriptor_parse_error(e), - } -} - -fn get_permission_check_error_class(e: &PermissionCheckError) -> &'static str { - match e { - PermissionCheckError::PermissionDenied(_) => "NotCapable", - PermissionCheckError::InvalidFilePath(_) => "URIError", - PermissionCheckError::NetDescriptorForUrlParse(e) => match e { - NetDescriptorFromUrlParseError::MissingHost(_) => "TypeError", - NetDescriptorFromUrlParseError::Host(_) => "URIError", - }, - PermissionCheckError::SysDescriptorParse(e) => { - get_sys_descriptor_parse_error(e) - } - PermissionCheckError::PathResolve(e) => get_path_resolve_error(e), - PermissionCheckError::HostParse(_) => "URIError", - } -} - -fn get_dlopen_error_class(error: &dlopen2::Error) -> &'static str { - use dlopen2::Error::*; - match error { - NullCharacter(_) => "InvalidData", - OpeningLibraryError(ref e) => get_io_error_class(e), - SymbolGettingError(ref e) => get_io_error_class(e), - AddrNotMatchingDll(ref e) => get_io_error_class(e), - NullSymbol => "NotFound", - } -} - -fn get_env_var_error_class(error: &env::VarError) -> &'static str { - use env::VarError::*; - match error { - NotPresent => "NotFound", - NotUnicode(..) => "InvalidData", - } -} - -fn get_io_error_class(error: &io::Error) -> &'static str { - use io::ErrorKind::*; - match error.kind() { - NotFound => "NotFound", - PermissionDenied => "PermissionDenied", - ConnectionRefused => "ConnectionRefused", - ConnectionReset => "ConnectionReset", - ConnectionAborted => "ConnectionAborted", - NotConnected => "NotConnected", - AddrInUse => "AddrInUse", - AddrNotAvailable => "AddrNotAvailable", - BrokenPipe => "BrokenPipe", - AlreadyExists => "AlreadyExists", - InvalidInput => "TypeError", - InvalidData => "InvalidData", - TimedOut => "TimedOut", - Interrupted => "Interrupted", - WriteZero => "WriteZero", - UnexpectedEof => "UnexpectedEof", - Other => "Error", - WouldBlock => "WouldBlock", - // Non-exhaustive enum - might add new variants - // in the future - kind => { - let kind_str = kind.to_string(); - match kind_str.as_str() { - "FilesystemLoop" => "FilesystemLoop", - "IsADirectory" => "IsADirectory", - "NetworkUnreachable" => "NetworkUnreachable", - "NotADirectory" => "NotADirectory", - _ => "Error", - } - } - } -} - -fn get_module_resolution_error_class( - _: &ModuleResolutionError, -) -> &'static str { - "URIError" -} - -fn get_notify_error_class(error: ¬ify::Error) -> &'static str { - use notify::ErrorKind::*; - match error.kind { - Generic(_) => "Error", - Io(ref e) => get_io_error_class(e), - PathNotFound => "NotFound", - WatchNotFound => "NotFound", - InvalidConfig(_) => "InvalidData", - MaxFilesWatch => "Error", - } -} - -fn get_regex_error_class(error: ®ex::Error) -> &'static str { - use regex::Error::*; - match error { - Syntax(_) => "SyntaxError", - CompiledTooBig(_) => "RangeError", - _ => "Error", - } -} - -fn get_serde_json_error_class( - error: &serde_json::error::Error, -) -> &'static str { - use deno_core::serde_json::error::*; - match error.classify() { - Category::Io => error - .source() - .and_then(|e| e.downcast_ref::()) - .map(get_io_error_class) - .unwrap(), - Category::Syntax => "SyntaxError", - Category::Data => "InvalidData", - Category::Eof => "UnexpectedEof", - } -} - -fn get_url_parse_error_class(_error: &url::ParseError) -> &'static str { - "URIError" -} - -fn get_hyper_error_class(_error: &hyper::Error) -> &'static str { - "Http" -} - -fn get_hyper_util_error_class( - _error: &hyper_util::client::legacy::Error, -) -> &'static str { - "Http" -} - -fn get_hyper_v014_error_class(_error: &hyper_v014::Error) -> &'static str { - "Http" -} - -#[cfg(unix)] -pub fn get_nix_error_class(error: &nix::Error) -> &'static str { - match error { - nix::Error::ECHILD => "NotFound", - nix::Error::EINVAL => "TypeError", - nix::Error::ENOENT => "NotFound", - nix::Error::ENOTTY => "BadResource", - nix::Error::EPERM => "PermissionDenied", - nix::Error::ESRCH => "NotFound", - nix::Error::ELOOP => "FilesystemLoop", - nix::Error::ENOTDIR => "NotADirectory", - nix::Error::ENETUNREACH => "NetworkUnreachable", - nix::Error::EISDIR => "IsADirectory", - nix::Error::UnknownErrno => "Error", - &nix::Error::ENOTSUP => unreachable!(), - _ => "Error", - } -} - -fn get_webgpu_error_class(e: &deno_webgpu::InitError) -> &'static str { - match e { - deno_webgpu::InitError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::InitError::InvalidAdapter(_) => "Error", - deno_webgpu::InitError::RequestDevice(_) => "DOMExceptionOperationError", - deno_webgpu::InitError::InvalidDevice(_) => "Error", - } -} - -fn get_webgpu_buffer_error_class( - e: &deno_webgpu::buffer::BufferError, -) -> &'static str { - match e { - deno_webgpu::buffer::BufferError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::buffer::BufferError::InvalidUsage => "TypeError", - deno_webgpu::buffer::BufferError::Access(_) => "DOMExceptionOperationError", - } -} - -fn get_webgpu_bundle_error_class( - e: &deno_webgpu::bundle::BundleError, -) -> &'static str { - match e { - deno_webgpu::bundle::BundleError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::bundle::BundleError::InvalidSize => "TypeError", - } -} - -fn get_webgpu_byow_error_class( - e: &deno_webgpu::byow::ByowError, -) -> &'static str { - match e { - deno_webgpu::byow::ByowError::WebGPUNotInitiated => "TypeError", - deno_webgpu::byow::ByowError::InvalidParameters => "TypeError", - deno_webgpu::byow::ByowError::CreateSurface(_) => "Error", - deno_webgpu::byow::ByowError::InvalidSystem => "TypeError", - #[cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "freebsd", - target_os = "openbsd" - ))] - deno_webgpu::byow::ByowError::NullWindow => "TypeError", - #[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "openbsd" - ))] - deno_webgpu::byow::ByowError::NullDisplay => "TypeError", - #[cfg(target_os = "macos")] - deno_webgpu::byow::ByowError::NSViewDisplay => "TypeError", - } -} - -fn get_webgpu_render_pass_error_class( - e: &deno_webgpu::render_pass::RenderPassError, -) -> &'static str { - match e { - deno_webgpu::render_pass::RenderPassError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::render_pass::RenderPassError::InvalidSize => "TypeError", - } -} - -fn get_webgpu_surface_error_class( - e: &deno_webgpu::surface::SurfaceError, -) -> &'static str { - match e { - deno_webgpu::surface::SurfaceError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - deno_webgpu::surface::SurfaceError::Surface(_) => "Error", - deno_webgpu::surface::SurfaceError::InvalidStatus => "Error", - } -} - -fn get_crypto_decrypt_error_class(e: &DecryptError) -> &'static str { - match e { - DecryptError::General(e) => get_crypto_shared_error_class(e), - DecryptError::Pkcs1(_) => "Error", - DecryptError::Failed => "DOMExceptionOperationError", - DecryptError::InvalidLength => "TypeError", - DecryptError::InvalidCounterLength => "TypeError", - DecryptError::InvalidTagLength => "TypeError", - DecryptError::InvalidKeyOrIv => "DOMExceptionOperationError", - DecryptError::TooMuchData => "DOMExceptionOperationError", - DecryptError::InvalidIvLength => "TypeError", - DecryptError::Rsa(_) => "DOMExceptionOperationError", - } -} - -fn get_crypto_encrypt_error_class(e: &EncryptError) -> &'static str { - match e { - EncryptError::General(e) => get_crypto_shared_error_class(e), - EncryptError::InvalidKeyOrIv => "DOMExceptionOperationError", - EncryptError::Failed => "DOMExceptionOperationError", - EncryptError::InvalidLength => "TypeError", - EncryptError::InvalidIvLength => "TypeError", - EncryptError::InvalidCounterLength => "TypeError", - EncryptError::TooMuchData => "DOMExceptionOperationError", - } -} - -fn get_crypto_shared_error_class(e: &deno_crypto::SharedError) -> &'static str { - match e { - deno_crypto::SharedError::ExpectedValidPrivateKey => "TypeError", - deno_crypto::SharedError::ExpectedValidPublicKey => "TypeError", - deno_crypto::SharedError::ExpectedValidPrivateECKey => "TypeError", - deno_crypto::SharedError::ExpectedValidPublicECKey => "TypeError", - deno_crypto::SharedError::ExpectedPrivateKey => "TypeError", - deno_crypto::SharedError::ExpectedPublicKey => "TypeError", - deno_crypto::SharedError::ExpectedSecretKey => "TypeError", - deno_crypto::SharedError::FailedDecodePrivateKey => { - "DOMExceptionOperationError" - } - deno_crypto::SharedError::FailedDecodePublicKey => { - "DOMExceptionOperationError" - } - deno_crypto::SharedError::UnsupportedFormat => { - "DOMExceptionNotSupportedError" - } - } -} - -fn get_crypto_ed25519_error_class( - e: &deno_crypto::Ed25519Error, -) -> &'static str { - match e { - deno_crypto::Ed25519Error::FailedExport => "DOMExceptionOperationError", - deno_crypto::Ed25519Error::Der(_) => "Error", - deno_crypto::Ed25519Error::KeyRejected(_) => "Error", - } -} - -fn get_crypto_export_key_error_class(e: &ExportKeyError) -> &'static str { - match e { - ExportKeyError::General(e) => get_crypto_shared_error_class(e), - ExportKeyError::Der(_) => "Error", - ExportKeyError::UnsupportedNamedCurve => "DOMExceptionNotSupportedError", - } -} - -fn get_crypto_generate_key_error_class(e: &GenerateKeyError) -> &'static str { - match e { - GenerateKeyError::General(e) => get_crypto_shared_error_class(e), - GenerateKeyError::BadPublicExponent => "DOMExceptionOperationError", - GenerateKeyError::InvalidHMACKeyLength => "DOMExceptionOperationError", - GenerateKeyError::FailedRSAKeySerialization => "DOMExceptionOperationError", - GenerateKeyError::InvalidAESKeyLength => "DOMExceptionOperationError", - GenerateKeyError::FailedRSAKeyGeneration => "DOMExceptionOperationError", - GenerateKeyError::FailedECKeyGeneration => "DOMExceptionOperationError", - GenerateKeyError::FailedKeyGeneration => "DOMExceptionOperationError", - } -} - -fn get_crypto_import_key_error_class(e: &ImportKeyError) -> &'static str { - match e { - ImportKeyError::General(e) => get_crypto_shared_error_class(e), - ImportKeyError::InvalidModulus => "DOMExceptionDataError", - ImportKeyError::InvalidPublicExponent => "DOMExceptionDataError", - ImportKeyError::InvalidPrivateExponent => "DOMExceptionDataError", - ImportKeyError::InvalidFirstPrimeFactor => "DOMExceptionDataError", - ImportKeyError::InvalidSecondPrimeFactor => "DOMExceptionDataError", - ImportKeyError::InvalidFirstCRTExponent => "DOMExceptionDataError", - ImportKeyError::InvalidSecondCRTExponent => "DOMExceptionDataError", - ImportKeyError::InvalidCRTCoefficient => "DOMExceptionDataError", - ImportKeyError::InvalidB64Coordinate => "DOMExceptionDataError", - ImportKeyError::InvalidRSAPublicKey => "DOMExceptionDataError", - ImportKeyError::InvalidRSAPrivateKey => "DOMExceptionDataError", - ImportKeyError::UnsupportedAlgorithm => "DOMExceptionDataError", - ImportKeyError::PublicKeyTooLong => "DOMExceptionDataError", - ImportKeyError::PrivateKeyTooLong => "DOMExceptionDataError", - ImportKeyError::InvalidP256ECPoint => "DOMExceptionDataError", - ImportKeyError::InvalidP384ECPoint => "DOMExceptionDataError", - ImportKeyError::InvalidP521ECPoint => "DOMExceptionDataError", - ImportKeyError::UnsupportedNamedCurve => "DOMExceptionDataError", - ImportKeyError::CurveMismatch => "DOMExceptionDataError", - ImportKeyError::InvalidKeyData => "DOMExceptionDataError", - ImportKeyError::InvalidJWKPrivateKey => "DOMExceptionDataError", - ImportKeyError::EllipticCurve(_) => "DOMExceptionDataError", - ImportKeyError::ExpectedValidPkcs8Data => "DOMExceptionDataError", - ImportKeyError::MalformedParameters => "DOMExceptionDataError", - ImportKeyError::Spki(_) => "DOMExceptionDataError", - ImportKeyError::InvalidP256ECSPKIData => "DOMExceptionDataError", - ImportKeyError::InvalidP384ECSPKIData => "DOMExceptionDataError", - ImportKeyError::InvalidP521ECSPKIData => "DOMExceptionDataError", - ImportKeyError::Der(_) => "DOMExceptionDataError", - } -} - -fn get_crypto_x448_error_class(e: &deno_crypto::X448Error) -> &'static str { - match e { - deno_crypto::X448Error::FailedExport => "DOMExceptionOperationError", - deno_crypto::X448Error::Der(_) => "Error", - } -} - -fn get_crypto_x25519_error_class(e: &deno_crypto::X25519Error) -> &'static str { - match e { - deno_crypto::X25519Error::FailedExport => "DOMExceptionOperationError", - deno_crypto::X25519Error::Der(_) => "Error", - } -} - -fn get_crypto_error_class(e: &deno_crypto::Error) -> &'static str { - match e { - deno_crypto::Error::Der(_) => "Error", - deno_crypto::Error::JoinError(_) => "Error", - deno_crypto::Error::MissingArgumentHash => "TypeError", - deno_crypto::Error::MissingArgumentSaltLength => "TypeError", - deno_crypto::Error::Other(e) => get_error_class_name(e).unwrap_or("Error"), - deno_crypto::Error::UnsupportedAlgorithm => "TypeError", - deno_crypto::Error::KeyRejected(_) => "Error", - deno_crypto::Error::RSA(_) => "Error", - deno_crypto::Error::Pkcs1(_) => "Error", - deno_crypto::Error::Unspecified(_) => "Error", - deno_crypto::Error::InvalidKeyFormat => "TypeError", - deno_crypto::Error::MissingArgumentPublicKey => "TypeError", - deno_crypto::Error::P256Ecdsa(_) => "Error", - deno_crypto::Error::DecodePrivateKey => "TypeError", - deno_crypto::Error::MissingArgumentNamedCurve => "TypeError", - deno_crypto::Error::MissingArgumentInfo => "TypeError", - deno_crypto::Error::HKDFLengthTooLarge => "DOMExceptionOperationError", - deno_crypto::Error::General(e) => get_crypto_shared_error_class(e), - deno_crypto::Error::Base64Decode(_) => "Error", - deno_crypto::Error::DataInvalidSize => "TypeError", - deno_crypto::Error::InvalidKeyLength => "TypeError", - deno_crypto::Error::EncryptionError => "DOMExceptionOperationError", - deno_crypto::Error::DecryptionError => "DOMExceptionOperationError", - deno_crypto::Error::ArrayBufferViewLengthExceeded(_) => { - "DOMExceptionQuotaExceededError" - } - } -} - -fn get_napi_error_class(e: &NApiError) -> &'static str { - match e { - NApiError::InvalidPath - | NApiError::LibLoading(_) - | NApiError::ModuleNotFound(_) => "TypeError", - NApiError::Permission(e) => get_permission_check_error_class(e), - } -} - -fn get_web_error_class(e: &WebError) -> &'static str { - match e { - WebError::Base64Decode => "DOMExceptionInvalidCharacterError", - WebError::InvalidEncodingLabel(_) => "RangeError", - WebError::BufferTooLong => "TypeError", - WebError::ValueTooLarge => "RangeError", - WebError::BufferTooSmall => "RangeError", - WebError::DataInvalid => "TypeError", - WebError::DataError(_) => "Error", - } -} - -fn get_web_compression_error_class(e: &CompressionError) -> &'static str { - match e { - CompressionError::UnsupportedFormat => "TypeError", - CompressionError::ResourceClosed => "TypeError", - CompressionError::IoTypeError(_) => "TypeError", - CompressionError::Io(e) => get_io_error_class(e), - } -} - -fn get_web_message_port_error_class(e: &MessagePortError) -> &'static str { - match e { - MessagePortError::InvalidTransfer => "TypeError", - MessagePortError::NotReady => "TypeError", - MessagePortError::TransferSelf => "TypeError", - MessagePortError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - MessagePortError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_web_stream_resource_error_class( - e: &StreamResourceError, -) -> &'static str { - match e { - StreamResourceError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - StreamResourceError::Js(_) => "TypeError", - } -} - -fn get_web_blob_error_class(e: &BlobError) -> &'static str { - match e { - BlobError::BlobPartNotFound => "TypeError", - BlobError::SizeLargerThanBlobPart => "TypeError", - BlobError::BlobURLsNotSupported => "TypeError", - BlobError::Url(_) => "Error", - } -} - -fn get_ffi_repr_error_class(e: &ReprError) -> &'static str { - match e { - ReprError::InvalidOffset => "TypeError", - ReprError::InvalidArrayBuffer => "TypeError", - ReprError::DestinationLengthTooShort => "RangeError", - ReprError::InvalidCString => "TypeError", - ReprError::CStringTooLong => "TypeError", - ReprError::InvalidBool => "TypeError", - ReprError::InvalidU8 => "TypeError", - ReprError::InvalidI8 => "TypeError", - ReprError::InvalidU16 => "TypeError", - ReprError::InvalidI16 => "TypeError", - ReprError::InvalidU32 => "TypeError", - ReprError::InvalidI32 => "TypeError", - ReprError::InvalidU64 => "TypeError", - ReprError::InvalidI64 => "TypeError", - ReprError::InvalidF32 => "TypeError", - ReprError::InvalidF64 => "TypeError", - ReprError::InvalidPointer => "TypeError", - ReprError::Permission(e) => get_permission_check_error_class(e), - } -} - -fn get_ffi_dlfcn_error_class(e: &DlfcnError) -> &'static str { - match e { - DlfcnError::RegisterSymbol { .. } => "Error", - DlfcnError::Dlopen(_) => "Error", - DlfcnError::Permission(e) => get_permission_check_error_class(e), - DlfcnError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_ffi_static_error_class(e: &StaticError) -> &'static str { - match e { - StaticError::Dlfcn(e) => get_ffi_dlfcn_error_class(e), - StaticError::InvalidTypeVoid => "TypeError", - StaticError::InvalidTypeStruct => "TypeError", - StaticError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_ffi_callback_error_class(e: &CallbackError) -> &'static str { - match e { - CallbackError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - CallbackError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - CallbackError::Permission(e) => get_permission_check_error_class(e), - } -} - -fn get_ffi_call_error_class(e: &CallError) -> &'static str { - match e { - CallError::IR(_) => "TypeError", - CallError::NonblockingCallFailure(_) => "Error", - CallError::InvalidSymbol(_) => "TypeError", - CallError::Permission(e) => get_permission_check_error_class(e), - CallError::Callback(e) => get_ffi_callback_error_class(e), - CallError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_webstorage_class_name(e: &WebStorageError) -> &'static str { - match e { - WebStorageError::ContextNotSupported => "DOMExceptionNotSupportedError", - WebStorageError::Sqlite(_) => "Error", - WebStorageError::Io(e) => get_io_error_class(e), - WebStorageError::StorageExceeded => "DOMExceptionQuotaExceededError", - } -} - -fn get_tls_error_class(e: &TlsError) -> &'static str { - match e { - TlsError::Rustls(_) => "Error", - TlsError::UnableAddPemFileToCert(e) => get_io_error_class(e), - TlsError::CertInvalid - | TlsError::CertsNotFound - | TlsError::KeysNotFound - | TlsError::KeyDecode => "InvalidData", - } -} - -pub fn get_cron_error_class(e: &CronError) -> &'static str { - match e { - CronError::Resource(e) => { - deno_core::error::get_custom_error_class(e).unwrap_or("Error") - } - CronError::NameExceeded(_) => "TypeError", - CronError::NameInvalid => "TypeError", - CronError::AlreadyExists => "TypeError", - CronError::TooManyCrons => "TypeError", - CronError::InvalidCron => "TypeError", - CronError::InvalidBackoff => "TypeError", - CronError::AcquireError(_) => "Error", - CronError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_canvas_error(e: &CanvasError) -> &'static str { - match e { - CanvasError::UnsupportedColorType(_) => "TypeError", - CanvasError::Image(_) => "Error", - } -} - -pub fn get_cache_error(error: &CacheError) -> &'static str { - match error { - CacheError::Sqlite(_) => "Error", - CacheError::JoinError(_) => "Error", - CacheError::Resource(err) => { - deno_core::error::get_custom_error_class(err).unwrap_or("Error") - } - CacheError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - CacheError::Io(err) => get_io_error_class(err), - } -} - -fn get_broadcast_channel_error(error: &BroadcastChannelError) -> &'static str { - match error { - BroadcastChannelError::Resource(err) => { - deno_core::error::get_custom_error_class(err).unwrap() - } - BroadcastChannelError::MPSCSendError(_) => "Error", - BroadcastChannelError::BroadcastSendError(_) => "Error", - BroadcastChannelError::Other(err) => { - get_error_class_name(err).unwrap_or("Error") - } - } -} - -fn get_fetch_error(error: &FetchError) -> &'static str { - match error { - FetchError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - FetchError::Permission(e) => get_permission_check_error_class(e), - FetchError::NetworkError => "TypeError", - FetchError::FsNotGet(_) => "TypeError", - FetchError::PathToUrl(_) => "TypeError", - FetchError::InvalidUrl(_) => "TypeError", - FetchError::InvalidHeaderName(_) => "TypeError", - FetchError::InvalidHeaderValue(_) => "TypeError", - FetchError::DataUrl(_) => "TypeError", - FetchError::Base64(_) => "TypeError", - FetchError::BlobNotFound => "TypeError", - FetchError::SchemeNotSupported(_) => "TypeError", - FetchError::RequestCanceled => "TypeError", - FetchError::Http(_) => "Error", - FetchError::ClientCreate(e) => get_http_client_create_error(e), - FetchError::Url(e) => get_url_parse_error_class(e), - FetchError::Method(_) => "TypeError", - FetchError::ClientSend(_) => "TypeError", - FetchError::RequestBuilderHook(_) => "TypeError", - FetchError::Io(e) => get_io_error_class(e), - } -} - -fn get_http_client_create_error(error: &HttpClientCreateError) -> &'static str { - match error { - HttpClientCreateError::Tls(_) => "TypeError", - HttpClientCreateError::InvalidUserAgent(_) => "TypeError", - HttpClientCreateError::InvalidProxyUrl => "TypeError", - HttpClientCreateError::HttpVersionSelectionInvalid => "TypeError", - HttpClientCreateError::RootCertStore(_) => "TypeError", - } -} - -fn get_websocket_error(error: &WebsocketError) -> &'static str { - match error { - WebsocketError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - WebsocketError::Permission(e) => get_permission_check_error_class(e), - WebsocketError::Url(e) => get_url_parse_error_class(e), - WebsocketError::Io(e) => get_io_error_class(e), - WebsocketError::WebSocket(_) => "TypeError", - WebsocketError::ConnectionFailed(_) => "DOMExceptionNetworkError", - WebsocketError::Uri(_) => "Error", - WebsocketError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - } -} - -fn get_websocket_handshake_error(error: &HandshakeError) -> &'static str { - match error { - HandshakeError::RootStoreError(e) => { - get_error_class_name(e).unwrap_or("Error") - } - HandshakeError::Tls(e) => get_tls_error_class(e), - HandshakeError::MissingPath => "TypeError", - HandshakeError::Http(_) => "Error", - HandshakeError::InvalidHostname(_) => "TypeError", - HandshakeError::Io(e) => get_io_error_class(e), - HandshakeError::Rustls(_) => "Error", - HandshakeError::H2(_) => "Error", - HandshakeError::NoH2Alpn => "Error", - HandshakeError::InvalidStatusCode(_) => "Error", - HandshakeError::WebSocket(_) => "TypeError", - HandshakeError::HeaderName(_) => "TypeError", - HandshakeError::HeaderValue(_) => "TypeError", - } -} - -fn get_fs_ops_error(error: &FsOpsError) -> &'static str { - use FsOpsErrorKind::*; - match error.as_kind() { - Io(e) => get_io_error_class(e), - OperationError(e) => get_fs_error(&e.err), - Permission(e) => get_permission_check_error_class(e), - Resource(e) | Other(e) => get_error_class_name(e).unwrap_or("Error"), - InvalidUtf8(_) => "InvalidData", - StripPrefix(_) => "Error", - Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - InvalidSeekMode(_) => "TypeError", - InvalidControlCharacter(_) => "Error", - InvalidCharacter(_) => "Error", - #[cfg(windows)] - InvalidTrailingCharacter => "Error", - NotCapableAccess { .. } => "NotCapable", - NotCapable(_) => "NotCapable", - } -} - -fn get_kv_error(error: &KvError) -> &'static str { - use KvErrorKind::*; - match error.as_kind() { - DatabaseHandler(e) | Resource(e) | Kv(e) => { - get_error_class_name(e).unwrap_or("Error") - } - TooManyRanges(_) => "TypeError", - TooManyEntries(_) => "TypeError", - TooManyChecks(_) => "TypeError", - TooManyMutations(_) => "TypeError", - TooManyKeys(_) => "TypeError", - InvalidLimit => "TypeError", - InvalidBoundaryKey => "TypeError", - KeyTooLargeToRead(_) => "TypeError", - KeyTooLargeToWrite(_) => "TypeError", - TotalMutationTooLarge(_) => "TypeError", - TotalKeyTooLarge(_) => "TypeError", - Io(e) => get_io_error_class(e), - QueueMessageNotFound => "TypeError", - StartKeyNotInKeyspace => "TypeError", - EndKeyNotInKeyspace => "TypeError", - StartKeyGreaterThanEndKey => "TypeError", - InvalidCheck(e) => match e { - KvCheckError::InvalidVersionstamp => "TypeError", - KvCheckError::Io(e) => get_io_error_class(e), - }, - InvalidMutation(e) => match e { - KvMutationError::BigInt(_) => "Error", - KvMutationError::Io(e) => get_io_error_class(e), - KvMutationError::InvalidMutationWithValue(_) => "TypeError", - KvMutationError::InvalidMutationWithoutValue(_) => "TypeError", - }, - InvalidEnqueue(e) => get_io_error_class(e), - EmptyKey => "TypeError", - ValueTooLarge(_) => "TypeError", - EnqueuePayloadTooLarge(_) => "TypeError", - InvalidCursor => "TypeError", - CursorOutOfBounds => "TypeError", - InvalidRange => "TypeError", - } -} - -fn get_net_error(error: &NetError) -> &'static str { - match error { - NetError::ListenerClosed => "BadResource", - NetError::ListenerBusy => "Busy", - NetError::SocketClosed => "BadResource", - NetError::SocketClosedNotConnected => "NotConnected", - NetError::SocketBusy => "Busy", - NetError::Io(e) => get_io_error_class(e), - NetError::AcceptTaskOngoing => "Busy", - NetError::RootCertStore(e) | NetError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - NetError::Permission(e) => get_permission_check_error_class(e), - NetError::NoResolvedAddress => "Error", - NetError::AddrParse(_) => "Error", - NetError::Map(e) => get_net_map_error(e), - NetError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - NetError::DnsNotFound(_) => "NotFound", - NetError::DnsNotConnected(_) => "NotConnected", - NetError::DnsTimedOut(_) => "TimedOut", - NetError::Dns(_) => "Error", - NetError::UnsupportedRecordType => "NotSupported", - NetError::InvalidUtf8(_) => "InvalidData", - NetError::UnexpectedKeyType => "Error", - NetError::InvalidHostname(_) => "TypeError", - NetError::TcpStreamBusy => "Busy", - NetError::Rustls(_) => "Error", - NetError::Tls(e) => get_tls_error_class(e), - NetError::ListenTlsRequiresKey => "InvalidData", - NetError::Reunite(_) => "Error", - } -} - -fn get_net_map_error(error: &deno_net::io::MapError) -> &'static str { - match error { - deno_net::io::MapError::Io(e) => get_io_error_class(e), - deno_net::io::MapError::NoResources => "Error", - } -} - -fn get_child_permission_error(e: &ChildPermissionError) -> &'static str { - match e { - ChildPermissionError::Escalation => "NotCapable", - ChildPermissionError::PathResolve(e) => get_path_resolve_error(e), - ChildPermissionError::NetDescriptorParse(_) => "URIError", - ChildPermissionError::EnvDescriptorParse(_) => "Error", - ChildPermissionError::SysDescriptorParse(e) => { - get_sys_descriptor_parse_error(e) - } - ChildPermissionError::RunDescriptorParse(e) => { - get_run_descriptor_parse_error(e) - } - } -} - -fn get_create_worker_error(error: &CreateWorkerError) -> &'static str { - match error { - CreateWorkerError::ClassicWorkers => "DOMExceptionNotSupportedError", - CreateWorkerError::Permission(e) => get_child_permission_error(e), - CreateWorkerError::ModuleResolution(e) => { - get_module_resolution_error_class(e) - } - CreateWorkerError::Io(e) => get_io_error_class(e), - CreateWorkerError::MessagePort(e) => get_web_message_port_error_class(e), - } -} - -fn get_tty_error(error: &TtyError) -> &'static str { - match error { - TtyError::Resource(e) | TtyError::Other(e) => { - get_error_class_name(e).unwrap_or("Error") - } - TtyError::Io(e) => get_io_error_class(e), - #[cfg(unix)] - TtyError::Nix(e) => get_nix_error_class(e), - } -} - -fn get_readline_error(error: &ReadlineError) -> &'static str { - match error { - ReadlineError::Io(e) => get_io_error_class(e), - ReadlineError::Eof => "Error", - ReadlineError::Interrupted => "Error", - #[cfg(unix)] - ReadlineError::Errno(e) => get_nix_error_class(e), - ReadlineError::WindowResized => "Error", - #[cfg(windows)] - ReadlineError::Decode(_) => "Error", - #[cfg(windows)] - ReadlineError::SystemError(_) => "Error", - _ => "Error", - } -} - -fn get_signal_error(error: &SignalError) -> &'static str { - match error { - SignalError::InvalidSignalStr(_) => "TypeError", - SignalError::InvalidSignalInt(_) => "TypeError", - SignalError::SignalNotAllowed(_) => "TypeError", - SignalError::Io(e) => get_io_error_class(e), - } -} - -fn get_fs_events_error(error: &FsEventsError) -> &'static str { - match error { - FsEventsError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - FsEventsError::Permission(e) => get_permission_check_error_class(e), - FsEventsError::Notify(e) => get_notify_error_class(e), - FsEventsError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - } -} - -fn get_http_start_error(error: &HttpStartError) -> &'static str { - match error { - HttpStartError::TcpStreamInUse => "Busy", - HttpStartError::TlsStreamInUse => "Busy", - HttpStartError::UnixSocketInUse => "Busy", - HttpStartError::ReuniteTcp(_) => "Error", - #[cfg(unix)] - HttpStartError::ReuniteUnix(_) => "Error", - HttpStartError::Io(e) => get_io_error_class(e), - HttpStartError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_process_error(error: &ProcessError) -> &'static str { - match error { - ProcessError::SpawnFailed { error, .. } => get_process_error(error), - ProcessError::FailedResolvingCwd(e) | ProcessError::Io(e) => { - get_io_error_class(e) - } - ProcessError::Permission(e) => get_permission_check_error_class(e), - ProcessError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - ProcessError::BorrowMut(_) => "Error", - ProcessError::Which(_) => "Error", - ProcessError::ChildProcessAlreadyTerminated => "TypeError", - ProcessError::Signal(e) => get_signal_error(e), - ProcessError::MissingCmd => "Error", - ProcessError::InvalidPid => "TypeError", - #[cfg(unix)] - ProcessError::Nix(e) => get_nix_error_class(e), - ProcessError::RunPermission(e) => match e { - CheckRunPermissionError::Permission(e) => { - get_permission_check_error_class(e) - } - CheckRunPermissionError::Other(e) => { - get_error_class_name(e).unwrap_or("Error") - } - }, - } -} - -fn get_http_error(error: &HttpError) -> &'static str { - match error { - HttpError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - HttpError::HyperV014(e) => get_hyper_v014_error_class(e), - HttpError::InvalidHeaderName(_) => "Error", - HttpError::InvalidHeaderValue(_) => "Error", - HttpError::Http(_) => "Error", - HttpError::ResponseHeadersAlreadySent => "Http", - HttpError::ConnectionClosedWhileSendingResponse => "Http", - HttpError::AlreadyInUse => "Http", - HttpError::Io(e) => get_io_error_class(e), - HttpError::NoResponseHeaders => "Http", - HttpError::ResponseAlreadyCompleted => "Http", - HttpError::UpgradeBodyUsed => "Http", - HttpError::Resource(e) | HttpError::Other(e) => { - get_error_class_name(e).unwrap_or("Error") - } - } -} - -fn get_http_next_error(error: &HttpNextError) -> &'static str { - match error { - HttpNextError::Io(e) => get_io_error_class(e), - HttpNextError::WebSocketUpgrade(e) => get_websocket_upgrade_error(e), - HttpNextError::Hyper(e) => get_hyper_error_class(e), - HttpNextError::JoinError(_) => "Error", - HttpNextError::Canceled(e) => { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - HttpNextError::UpgradeUnavailable(_) => "Error", - HttpNextError::HttpPropertyExtractor(e) | HttpNextError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - } -} - -fn get_websocket_upgrade_error(error: &WebSocketUpgradeError) -> &'static str { - match error { - WebSocketUpgradeError::InvalidHeaders => "Http", - WebSocketUpgradeError::HttpParse(_) => "Error", - WebSocketUpgradeError::Http(_) => "Error", - WebSocketUpgradeError::Utf8(_) => "Error", - WebSocketUpgradeError::InvalidHeaderName(_) => "Error", - WebSocketUpgradeError::InvalidHeaderValue(_) => "Error", - WebSocketUpgradeError::InvalidHttpStatusLine => "Http", - WebSocketUpgradeError::UpgradeBufferAlreadyCompleted => "Http", - } -} - -fn get_fs_error(e: &FsError) -> &'static str { - match &e { - FsError::Io(e) => get_io_error_class(e), - FsError::FileBusy => "Busy", - FsError::NotSupported => "NotSupported", - FsError::NotCapable(_) => "NotCapable", - } -} - -mod node { - pub use deno_node::ops::blocklist::BlocklistError; - pub use deno_node::ops::crypto::cipher::CipherContextError; - pub use deno_node::ops::crypto::cipher::CipherError; - pub use deno_node::ops::crypto::cipher::DecipherContextError; - pub use deno_node::ops::crypto::cipher::DecipherError; - pub use deno_node::ops::crypto::digest::HashError; - pub use deno_node::ops::crypto::keys::AsymmetricPrivateKeyDerError; - pub use deno_node::ops::crypto::keys::AsymmetricPrivateKeyError; - pub use deno_node::ops::crypto::keys::AsymmetricPublicKeyDerError; - pub use deno_node::ops::crypto::keys::AsymmetricPublicKeyError; - pub use deno_node::ops::crypto::keys::AsymmetricPublicKeyJwkError; - pub use deno_node::ops::crypto::keys::EcJwkError; - pub use deno_node::ops::crypto::keys::EdRawError; - pub use deno_node::ops::crypto::keys::ExportPrivateKeyPemError; - pub use deno_node::ops::crypto::keys::ExportPublicKeyPemError; - pub use deno_node::ops::crypto::keys::GenerateRsaPssError; - pub use deno_node::ops::crypto::keys::RsaJwkError; - pub use deno_node::ops::crypto::keys::RsaPssParamsParseError; - pub use deno_node::ops::crypto::keys::X509PublicKeyError; - pub use deno_node::ops::crypto::sign::KeyObjectHandlePrehashedSignAndVerifyError; - pub use deno_node::ops::crypto::x509::X509Error; - pub use deno_node::ops::crypto::DiffieHellmanError; - pub use deno_node::ops::crypto::EcdhEncodePubKey; - pub use deno_node::ops::crypto::HkdfError; - pub use deno_node::ops::crypto::Pbkdf2Error; - pub use deno_node::ops::crypto::PrivateEncryptDecryptError; - pub use deno_node::ops::crypto::ScryptAsyncError; - pub use deno_node::ops::crypto::SignEd25519Error; - pub use deno_node::ops::crypto::VerifyEd25519Error; - pub use deno_node::ops::fs::FsError; - pub use deno_node::ops::http::ConnError; - pub use deno_node::ops::http2::Http2Error; - pub use deno_node::ops::idna::IdnaError; - pub use deno_node::ops::ipc::IpcError; - pub use deno_node::ops::ipc::IpcJsonStreamError; - use deno_node::ops::os::priority::PriorityError; - pub use deno_node::ops::os::OsError; - pub use deno_node::ops::require::RequireError; - use deno_node::ops::require::RequireErrorKind; - pub use deno_node::ops::worker_threads::WorkerThreadsFilenameError; - pub use deno_node::ops::zlib::brotli::BrotliError; - pub use deno_node::ops::zlib::mode::ModeError; - pub use deno_node::ops::zlib::ZlibError; - - use super::get_error_class_name; - use super::get_io_error_class; - use super::get_permission_check_error_class; - use super::get_serde_json_error_class; - use super::get_url_parse_error_class; - - pub fn get_blocklist_error(error: &BlocklistError) -> &'static str { - match error { - BlocklistError::AddrParse(_) => "Error", - BlocklistError::IpNetwork(_) => "Error", - BlocklistError::InvalidAddress => "Error", - BlocklistError::IpVersionMismatch => "Error", - } - } - - pub fn get_fs_error(error: &FsError) -> &'static str { - match error { - FsError::Permission(e) => get_permission_check_error_class(e), - FsError::Io(e) => get_io_error_class(e), - #[cfg(windows)] - FsError::PathHasNoRoot => "Error", - #[cfg(not(any(unix, windows)))] - FsError::UnsupportedPlatform => "Error", - FsError::Fs(e) => super::get_fs_error(e), - } - } - - pub fn get_idna_error(error: &IdnaError) -> &'static str { - match error { - IdnaError::InvalidInput => "RangeError", - IdnaError::InputTooLong => "Error", - IdnaError::IllegalInput => "RangeError", - } - } - - pub fn get_ipc_json_stream_error(error: &IpcJsonStreamError) -> &'static str { - match error { - IpcJsonStreamError::Io(e) => get_io_error_class(e), - IpcJsonStreamError::SimdJson(_) => "Error", - } - } - - pub fn get_ipc_error(error: &IpcError) -> &'static str { - match error { - IpcError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - IpcError::IpcJsonStream(e) => get_ipc_json_stream_error(e), - IpcError::Canceled(e) => { - let io_err: std::io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - } - IpcError::SerdeJson(e) => get_serde_json_error_class(e), - } - } - - pub fn get_worker_threads_filename_error( - error: &WorkerThreadsFilenameError, - ) -> &'static str { - match error { - WorkerThreadsFilenameError::Permission(e) => { - get_error_class_name(e).unwrap_or("Error") - } - WorkerThreadsFilenameError::UrlParse(e) => get_url_parse_error_class(e), - WorkerThreadsFilenameError::InvalidRelativeUrl => "Error", - WorkerThreadsFilenameError::UrlFromPathString => "Error", - WorkerThreadsFilenameError::UrlToPathString => "Error", - WorkerThreadsFilenameError::UrlToPath => "Error", - WorkerThreadsFilenameError::FileNotFound(_) => "Error", - WorkerThreadsFilenameError::Fs(e) => super::get_io_error_class(e), - } - } - - pub fn get_require_error(error: &RequireError) -> &'static str { - use RequireErrorKind::*; - match error.as_kind() { - UrlParse(e) => get_url_parse_error_class(e), - Permission(e) => get_error_class_name(e).unwrap_or("Error"), - PackageExportsResolve(_) - | PackageJsonLoad(_) - | ClosestPkgJson(_) - | FilePathConversion(_) - | UrlConversion(_) - | ReadModule(_) - | PackageImportsResolve(_) => "Error", - Fs(e) | UnableToGetCwd(e) => super::get_io_error_class(e), - } - } - - pub fn get_http2_error(error: &Http2Error) -> &'static str { - match error { - Http2Error::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - Http2Error::UrlParse(e) => get_url_parse_error_class(e), - Http2Error::H2(_) => "Error", - } - } - - pub fn get_os_error(error: &OsError) -> &'static str { - match error { - OsError::Priority(e) => match e { - PriorityError::Io(e) => get_io_error_class(e), - #[cfg(windows)] - PriorityError::InvalidPriority => "TypeError", - }, - OsError::Permission(e) => get_permission_check_error_class(e), - OsError::FailedToGetCpuInfo => "TypeError", - OsError::FailedToGetUserInfo(e) => get_io_error_class(e), - } - } - - pub fn get_brotli_error(error: &BrotliError) -> &'static str { - match error { - BrotliError::InvalidEncoderMode => "TypeError", - BrotliError::CompressFailed => "TypeError", - BrotliError::DecompressFailed => "TypeError", - BrotliError::Join(_) => "Error", - BrotliError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - BrotliError::Io(e) => get_io_error_class(e), - } - } - - pub fn get_mode_error(_: &ModeError) -> &'static str { - "Error" - } - - pub fn get_zlib_error(e: &ZlibError) -> &'static str { - match e { - ZlibError::NotInitialized => "TypeError", - ZlibError::Mode(e) => get_mode_error(e), - ZlibError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } - } - - pub fn get_crypto_cipher_context_error( - e: &CipherContextError, - ) -> &'static str { - match e { - CipherContextError::ContextInUse => "TypeError", - CipherContextError::Cipher(e) => get_crypto_cipher_error(e), - CipherContextError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - } - } - - pub fn get_crypto_cipher_error(e: &CipherError) -> &'static str { - match e { - CipherError::InvalidIvLength => "TypeError", - CipherError::InvalidKeyLength => "RangeError", - CipherError::InvalidInitializationVector => "TypeError", - CipherError::CannotPadInputData => "TypeError", - CipherError::UnknownCipher(_) => "TypeError", - } - } - - pub fn get_crypto_decipher_context_error( - e: &DecipherContextError, - ) -> &'static str { - match e { - DecipherContextError::ContextInUse => "TypeError", - DecipherContextError::Decipher(e) => get_crypto_decipher_error(e), - DecipherContextError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } - } - } - - pub fn get_crypto_decipher_error(e: &DecipherError) -> &'static str { - match e { - DecipherError::InvalidIvLength => "TypeError", - DecipherError::InvalidKeyLength => "RangeError", - DecipherError::InvalidInitializationVector => "TypeError", - DecipherError::CannotUnpadInputData => "TypeError", - DecipherError::DataAuthenticationFailed => "TypeError", - DecipherError::SetAutoPaddingFalseAes128GcmUnsupported => "TypeError", - DecipherError::SetAutoPaddingFalseAes256GcmUnsupported => "TypeError", - DecipherError::UnknownCipher(_) => "TypeError", - } - } - - pub fn get_x509_error(_: &X509Error) -> &'static str { - "Error" - } - - pub fn get_crypto_key_object_handle_prehashed_sign_and_verify_error( - e: &KeyObjectHandlePrehashedSignAndVerifyError, - ) -> &'static str { - match e { - KeyObjectHandlePrehashedSignAndVerifyError::InvalidDsaSignatureEncoding => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::KeyIsNotPrivate => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaSignature(_) => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithRsa => "Error", - KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaPssSignature(_) => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithRsaPss => "Error", - KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithDsa => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::RsaPssHashAlgorithmUnsupported => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::PrivateKeyDisallowsUsage { .. } => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigest => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::X25519KeyCannotBeUsedForSigning => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::Ed25519KeyCannotBeUsedForPrehashedSigning => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::DhKeyCannotBeUsedForSigning => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::KeyIsNotPublicOrPrivate => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::InvalidDsaSignature => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::X25519KeyCannotBeUsedForVerification => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::Ed25519KeyCannotBeUsedForPrehashedVerification => "TypeError", - KeyObjectHandlePrehashedSignAndVerifyError::DhKeyCannotBeUsedForVerification => "TypeError", - } - } - - pub fn get_crypto_hash_error(_: &HashError) -> &'static str { - "Error" - } - - pub fn get_asymmetric_public_key_jwk_error( - e: &AsymmetricPublicKeyJwkError, - ) -> &'static str { - match e { - AsymmetricPublicKeyJwkError::UnsupportedJwkEcCurveP224 => "TypeError", - AsymmetricPublicKeyJwkError::JwkExportNotImplementedForKeyType => { - "TypeError" - } - AsymmetricPublicKeyJwkError::KeyIsNotAsymmetricPublicKey => "TypeError", - } - } - - pub fn get_generate_rsa_pss_error(_: &GenerateRsaPssError) -> &'static str { - "TypeError" - } - - pub fn get_asymmetric_private_key_der_error( - e: &AsymmetricPrivateKeyDerError, - ) -> &'static str { - match e { - AsymmetricPrivateKeyDerError::KeyIsNotAsymmetricPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::InvalidRsaPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::ExportingNonRsaPrivateKeyAsPkcs1Unsupported => "TypeError", - AsymmetricPrivateKeyDerError::InvalidEcPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::ExportingNonEcPrivateKeyAsSec1Unsupported => "TypeError", - AsymmetricPrivateKeyDerError::ExportingNonRsaPssPrivateKeyAsPkcs8Unsupported => "Error", - AsymmetricPrivateKeyDerError::InvalidDsaPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::InvalidX25519PrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::InvalidEd25519PrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::InvalidDhPrivateKey => "TypeError", - AsymmetricPrivateKeyDerError::UnsupportedKeyType(_) => "TypeError", - } - } - - pub fn get_asymmetric_public_key_der_error( - _: &AsymmetricPublicKeyDerError, - ) -> &'static str { - "TypeError" - } - - pub fn get_export_public_key_pem_error( - e: &ExportPublicKeyPemError, - ) -> &'static str { - match e { - ExportPublicKeyPemError::AsymmetricPublicKeyDer(e) => { - get_asymmetric_public_key_der_error(e) - } - ExportPublicKeyPemError::VeryLargeData => "TypeError", - ExportPublicKeyPemError::Der(_) => "Error", - } - } - - pub fn get_export_private_key_pem_error( - e: &ExportPrivateKeyPemError, - ) -> &'static str { - match e { - ExportPrivateKeyPemError::AsymmetricPublicKeyDer(e) => { - get_asymmetric_private_key_der_error(e) - } - ExportPrivateKeyPemError::VeryLargeData => "TypeError", - ExportPrivateKeyPemError::Der(_) => "Error", - } - } - - pub fn get_x509_public_key_error(e: &X509PublicKeyError) -> &'static str { - match e { - X509PublicKeyError::X509(_) => "Error", - X509PublicKeyError::Rsa(_) => "Error", - X509PublicKeyError::Asn1(_) => "Error", - X509PublicKeyError::Ec(_) => "Error", - X509PublicKeyError::UnsupportedEcNamedCurve => "TypeError", - X509PublicKeyError::MissingEcParameters => "TypeError", - X509PublicKeyError::MalformedDssPublicKey => "TypeError", - X509PublicKeyError::UnsupportedX509KeyType => "TypeError", - } - } - - pub fn get_rsa_jwk_error(e: &RsaJwkError) -> &'static str { - match e { - RsaJwkError::Base64(_) => "Error", - RsaJwkError::Rsa(_) => "Error", - RsaJwkError::MissingRsaPrivateComponent => "TypeError", - } - } - - pub fn get_ec_jwk_error(e: &EcJwkError) -> &'static str { - match e { - EcJwkError::Ec(_) => "Error", - EcJwkError::UnsupportedCurve(_) => "TypeError", - } - } - - pub fn get_ed_raw_error(e: &EdRawError) -> &'static str { - match e { - EdRawError::Ed25519Signature(_) => "Error", - EdRawError::InvalidEd25519Key => "TypeError", - EdRawError::UnsupportedCurve => "TypeError", - } - } - - pub fn get_pbkdf2_error(e: &Pbkdf2Error) -> &'static str { - match e { - Pbkdf2Error::UnsupportedDigest(_) => "TypeError", - Pbkdf2Error::Join(_) => "Error", - } - } - - pub fn get_scrypt_async_error(e: &ScryptAsyncError) -> &'static str { - match e { - ScryptAsyncError::Join(_) => "Error", - ScryptAsyncError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } - } - - pub fn get_hkdf_error_error(e: &HkdfError) -> &'static str { - match e { - HkdfError::ExpectedSecretKey => "TypeError", - HkdfError::HkdfExpandFailed => "TypeError", - HkdfError::UnsupportedDigest(_) => "TypeError", - HkdfError::Join(_) => "Error", - } - } - - pub fn get_rsa_pss_params_parse_error( - _: &RsaPssParamsParseError, - ) -> &'static str { - "TypeError" - } - - pub fn get_asymmetric_private_key_error( - e: &AsymmetricPrivateKeyError, - ) -> &'static str { - match e { - AsymmetricPrivateKeyError::InvalidPemPrivateKeyInvalidUtf8(_) => "TypeError", - AsymmetricPrivateKeyError::InvalidEncryptedPemPrivateKey => "TypeError", - AsymmetricPrivateKeyError::InvalidPemPrivateKey => "TypeError", - AsymmetricPrivateKeyError::EncryptedPrivateKeyRequiresPassphraseToDecrypt => "TypeError", - AsymmetricPrivateKeyError::InvalidPkcs1PrivateKey => "TypeError", - AsymmetricPrivateKeyError::InvalidSec1PrivateKey => "TypeError", - AsymmetricPrivateKeyError::UnsupportedPemLabel(_) => "TypeError", - AsymmetricPrivateKeyError::RsaPssParamsParse(e) => get_rsa_pss_params_parse_error(e), - AsymmetricPrivateKeyError::InvalidEncryptedPkcs8PrivateKey => "TypeError", - AsymmetricPrivateKeyError::InvalidPkcs8PrivateKey => "TypeError", - AsymmetricPrivateKeyError::Pkcs1PrivateKeyDoesNotSupportEncryptionWithPassphrase => "TypeError", - AsymmetricPrivateKeyError::Sec1PrivateKeyDoesNotSupportEncryptionWithPassphrase => "TypeError", - AsymmetricPrivateKeyError::UnsupportedEcNamedCurve => "TypeError", - AsymmetricPrivateKeyError::InvalidPrivateKey => "TypeError", - AsymmetricPrivateKeyError::InvalidDsaPrivateKey => "TypeError", - AsymmetricPrivateKeyError::MalformedOrMissingNamedCurveInEcParameters => "TypeError", - AsymmetricPrivateKeyError::UnsupportedKeyType(_) => "TypeError", - AsymmetricPrivateKeyError::UnsupportedKeyFormat(_) => "TypeError", - AsymmetricPrivateKeyError::InvalidX25519PrivateKey => "TypeError", - AsymmetricPrivateKeyError::X25519PrivateKeyIsWrongLength => "TypeError", - AsymmetricPrivateKeyError::InvalidEd25519PrivateKey => "TypeError", - AsymmetricPrivateKeyError::MissingDhParameters => "TypeError", - AsymmetricPrivateKeyError::UnsupportedPrivateKeyOid => "TypeError", - } - } - - pub fn get_asymmetric_public_key_error( - e: &AsymmetricPublicKeyError, - ) -> &'static str { - match e { - AsymmetricPublicKeyError::InvalidPemPrivateKeyInvalidUtf8(_) => { - "TypeError" - } - AsymmetricPublicKeyError::InvalidPemPublicKey => "TypeError", - AsymmetricPublicKeyError::InvalidPkcs1PublicKey => "TypeError", - AsymmetricPublicKeyError::AsymmetricPrivateKey(e) => { - get_asymmetric_private_key_error(e) - } - AsymmetricPublicKeyError::InvalidX509Certificate => "TypeError", - AsymmetricPublicKeyError::X509(_) => "Error", - AsymmetricPublicKeyError::X509PublicKey(e) => { - get_x509_public_key_error(e) - } - AsymmetricPublicKeyError::UnsupportedPemLabel(_) => "TypeError", - AsymmetricPublicKeyError::InvalidSpkiPublicKey => "TypeError", - AsymmetricPublicKeyError::UnsupportedKeyType(_) => "TypeError", - AsymmetricPublicKeyError::UnsupportedKeyFormat(_) => "TypeError", - AsymmetricPublicKeyError::Spki(_) => "Error", - AsymmetricPublicKeyError::Pkcs1(_) => "Error", - AsymmetricPublicKeyError::RsaPssParamsParse(_) => "TypeError", - AsymmetricPublicKeyError::MalformedDssPublicKey => "TypeError", - AsymmetricPublicKeyError::MalformedOrMissingNamedCurveInEcParameters => { - "TypeError" - } - AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInEcSpki => { - "TypeError" - } - AsymmetricPublicKeyError::Ec(_) => "Error", - AsymmetricPublicKeyError::UnsupportedEcNamedCurve => "TypeError", - AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInX25519Spki => { - "TypeError" - } - AsymmetricPublicKeyError::X25519PublicKeyIsTooShort => "TypeError", - AsymmetricPublicKeyError::InvalidEd25519PublicKey => "TypeError", - AsymmetricPublicKeyError::MissingDhParameters => "TypeError", - AsymmetricPublicKeyError::MalformedDhParameters => "TypeError", - AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInDhSpki => { - "TypeError" - } - AsymmetricPublicKeyError::UnsupportedPrivateKeyOid => "TypeError", - } - } - - pub fn get_private_encrypt_decrypt_error( - e: &PrivateEncryptDecryptError, - ) -> &'static str { - match e { - PrivateEncryptDecryptError::Pkcs8(_) => "Error", - PrivateEncryptDecryptError::Spki(_) => "Error", - PrivateEncryptDecryptError::Utf8(_) => "Error", - PrivateEncryptDecryptError::Rsa(_) => "Error", - PrivateEncryptDecryptError::UnknownPadding => "TypeError", - } - } - - pub fn get_ecdh_encode_pub_key_error(e: &EcdhEncodePubKey) -> &'static str { - match e { - EcdhEncodePubKey::InvalidPublicKey => "TypeError", - EcdhEncodePubKey::UnsupportedCurve => "TypeError", - EcdhEncodePubKey::Sec1(_) => "Error", - } - } - - pub fn get_diffie_hellman_error(_: &DiffieHellmanError) -> &'static str { - "TypeError" - } - - pub fn get_sign_ed25519_error(_: &SignEd25519Error) -> &'static str { - "TypeError" - } - - pub fn get_verify_ed25519_error(_: &VerifyEd25519Error) -> &'static str { - "TypeError" - } - - pub fn get_conn_error(e: &ConnError) -> &'static str { - match e { - ConnError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), - ConnError::Permission(e) => get_permission_check_error_class(e), - ConnError::InvalidUrl(_) => "TypeError", - ConnError::InvalidHeaderName(_) => "TypeError", - ConnError::InvalidHeaderValue(_) => "TypeError", - ConnError::Url(e) => get_url_parse_error_class(e), - ConnError::Method(_) => "TypeError", - ConnError::Io(e) => get_io_error_class(e), - ConnError::Hyper(e) => super::get_hyper_error_class(e), - ConnError::TlsStreamBusy => "Busy", - ConnError::TcpStreamBusy => "Busy", - ConnError::ReuniteTcp(_) => "Error", - ConnError::Canceled(_) => "Error", - } - } -} - -fn get_os_error(error: &OsError) -> &'static str { - match error { - OsError::Permission(e) => get_permission_check_error_class(e), - OsError::InvalidUtf8(_) => "InvalidData", - OsError::EnvEmptyKey => "TypeError", - OsError::EnvInvalidKey(_) => "TypeError", - OsError::EnvInvalidValue(_) => "TypeError", - OsError::Io(e) => get_io_error_class(e), - OsError::Var(e) => get_env_var_error_class(e), - } -} - -fn get_sync_fetch_error(error: &SyncFetchError) -> &'static str { - match error { - SyncFetchError::BlobUrlsNotSupportedInContext => "TypeError", - SyncFetchError::Io(e) => get_io_error_class(e), - SyncFetchError::InvalidScriptUrl => "TypeError", - SyncFetchError::InvalidStatusCode(_) => "TypeError", - SyncFetchError::ClassicScriptSchemeUnsupportedInWorkers(_) => "TypeError", - SyncFetchError::InvalidUri(_) => "Error", - SyncFetchError::InvalidMimeType(_) => "DOMExceptionNetworkError", - SyncFetchError::MissingMimeType => "DOMExceptionNetworkError", - SyncFetchError::Fetch(e) => get_fetch_error(e), - SyncFetchError::Join(_) => "Error", - SyncFetchError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -fn get_quic_error_class(error: &QuicError) -> &'static str { - match error { - QuicError::CannotListen => "Error", - QuicError::MissingTlsKey => "TypeError", - QuicError::InvalidDuration => "TypeError", - QuicError::UnableToResolve => "Error", - QuicError::StdIo(e) => get_io_error_class(e), - QuicError::PermissionCheck(e) => get_permission_check_error_class(e), - QuicError::VarIntBoundsExceeded(_) => "RangeError", - QuicError::Rustls(_) => "Error", - QuicError::Tls(e) => get_tls_error_class(e), - QuicError::ConnectionError(_) => "Error", - QuicError::ConnectError(_) => "Error", - QuicError::SendDatagramError(_) => "Error", - QuicError::ClosedStream(_) => "BadResource", - QuicError::BadResource(_) => "BadResource", - QuicError::MaxStreams(_) => "RangeError", - QuicError::Core(e) => get_error_class_name(e).unwrap_or("Error"), - } -} - -pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { - deno_core::error::get_custom_error_class(e) - .or_else(|| { - e.downcast_ref::() - .map(get_child_permission_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_permission_check_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_permission_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_fs_error)) - .or_else(|| { - e.downcast_ref::() - .map(node::get_blocklist_error) - }) - .or_else(|| e.downcast_ref::().map(node::get_fs_error)) - .or_else(|| { - e.downcast_ref::() - .map(node::get_idna_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_ipc_json_stream_error) - }) - .or_else(|| e.downcast_ref::().map(node::get_ipc_error)) - .or_else(|| { - e.downcast_ref::() - .map(node::get_worker_threads_filename_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_require_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_http2_error) - }) - .or_else(|| e.downcast_ref::().map(node::get_os_error)) - .or_else(|| { - e.downcast_ref::() - .map(node::get_brotli_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_mode_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_zlib_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_cipher_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_cipher_context_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_decipher_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_decipher_context_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_x509_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_key_object_handle_prehashed_sign_and_verify_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_crypto_hash_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_public_key_jwk_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_generate_rsa_pss_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_private_key_der_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_public_key_der_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_export_public_key_pem_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_export_private_key_pem_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_rsa_jwk_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_ec_jwk_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_ed_raw_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_pbkdf2_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_scrypt_async_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_hkdf_error_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_rsa_pss_params_parse_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_private_key_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_asymmetric_public_key_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_private_encrypt_decrypt_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_ecdh_encode_pub_key_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_diffie_hellman_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_sign_ed25519_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_verify_ed25519_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(node::get_conn_error) - }) - .or_else(|| e.downcast_ref::().map(get_napi_error_class)) - .or_else(|| e.downcast_ref::().map(get_web_error_class)) - .or_else(|| { - e.downcast_ref::() - .map(get_create_worker_error) - }) - .or_else(|| e.downcast_ref::().map(get_tty_error)) - .or_else(|| e.downcast_ref::().map(get_readline_error)) - .or_else(|| e.downcast_ref::().map(get_signal_error)) - .or_else(|| e.downcast_ref::().map(get_fs_events_error)) - .or_else(|| e.downcast_ref::().map(get_http_start_error)) - .or_else(|| e.downcast_ref::().map(get_process_error)) - .or_else(|| e.downcast_ref::().map(get_os_error)) - .or_else(|| e.downcast_ref::().map(get_sync_fetch_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_web_compression_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_web_message_port_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_web_stream_resource_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_web_blob_error_class)) - .or_else(|| e.downcast_ref::().map(|_| "TypeError")) - .or_else(|| e.downcast_ref::().map(get_ffi_repr_error_class)) - .or_else(|| e.downcast_ref::().map(get_http_error)) - .or_else(|| e.downcast_ref::().map(get_http_next_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_websocket_upgrade_error) - }) - .or_else(|| e.downcast_ref::().map(get_fs_ops_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_ffi_dlfcn_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_ffi_static_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_ffi_callback_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_ffi_call_error_class)) - .or_else(|| e.downcast_ref::().map(get_tls_error_class)) - .or_else(|| e.downcast_ref::().map(get_cron_error_class)) - .or_else(|| e.downcast_ref::().map(get_canvas_error)) - .or_else(|| e.downcast_ref::().map(get_cache_error)) - .or_else(|| e.downcast_ref::().map(get_websocket_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_websocket_handshake_error) - }) - .or_else(|| e.downcast_ref::().map(get_kv_error)) - .or_else(|| e.downcast_ref::().map(get_fetch_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_http_client_create_error) - }) - .or_else(|| e.downcast_ref::().map(get_net_error)) - .or_else(|| { - e.downcast_ref::() - .map(get_net_map_error) - }) - .or_else(|| e.downcast_ref::().map(get_quic_error_class)) - .or_else(|| { - e.downcast_ref::() - .map(get_broadcast_channel_error) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_buffer_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_bundle_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_byow_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_render_pass_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webgpu_surface_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_decrypt_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_encrypt_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_shared_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_ed25519_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_export_key_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_generate_key_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_import_key_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_x448_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_x25519_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_crypto_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_webstorage_class_name) - }) - .or_else(|| { - e.downcast_ref::() - .map(|_| "TypeError") - }) - .or_else(|| { - e.downcast_ref::() - .map(get_dlopen_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_hyper_error_class)) - .or_else(|| { - e.downcast_ref::() - .map(get_hyper_util_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_hyper_v014_error_class) - }) - .or_else(|| { - e.downcast_ref::>() - .map(|e| get_hyper_v014_error_class(e)) - }) - .or_else(|| { - e.downcast_ref::().map(|e| { - let io_err: io::Error = e.to_owned().into(); - get_io_error_class(&io_err) - }) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_env_var_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_io_error_class)) - .or_else(|| { - e.downcast_ref::() - .map(get_module_resolution_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_notify_error_class) - }) - .or_else(|| e.downcast_ref::().map(get_regex_error_class)) - .or_else(|| { - e.downcast_ref::() - .map(get_serde_json_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(get_url_parse_error_class) - }) - .or_else(|| { - e.downcast_ref::() - .map(|_| "TypeError") - }) - .or_else(|| { - #[cfg(unix)] - let maybe_get_nix_error_class = - || e.downcast_ref::().map(get_nix_error_class); - #[cfg(not(unix))] - let maybe_get_nix_error_class = || Option::<&'static str>::None; - (maybe_get_nix_error_class)() - }) -} diff --git a/runtime/lib.rs b/runtime/lib.rs index 3e48ec89c0..9afe91cd25 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -27,7 +27,6 @@ pub use deno_websocket; pub use deno_webstorage; pub mod code_cache; -pub mod errors; pub mod fmt_errors; pub mod fs_util; pub mod inspector_server; diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs index e8fb9f2cef..5336c232c9 100644 --- a/runtime/ops/fs_events.rs +++ b/runtime/ops/fs_events.rs @@ -17,6 +17,8 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; +use deno_error::builtin_classes::GENERIC_ERROR; +use deno_error::JsErrorClass; use deno_permissions::PermissionsContainer; use notify::event::Event as NotifyEvent; use notify::event::ModifyKind; @@ -116,14 +118,29 @@ fn is_file_removed(event_path: &PathBuf) -> bool { } } -#[derive(Debug, thiserror::Error)] +deno_error::js_error_wrapper!(NotifyError, JsNotifyError, |err| { + match &err.kind { + notify::ErrorKind::Generic(_) => GENERIC_ERROR.into(), + notify::ErrorKind::Io(e) => e.get_class(), + notify::ErrorKind::PathNotFound => "NotFound".into(), + notify::ErrorKind::WatchNotFound => "NotFound".into(), + notify::ErrorKind::InvalidConfig(_) => "InvalidData".into(), + notify::ErrorKind::MaxFilesWatch => GENERIC_ERROR.into(), + } +}); + +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum FsEventsError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(#[from] deno_core::error::ResourceError), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] - Notify(#[from] NotifyError), + Notify(JsNotifyError), + #[class(inherit)] #[error(transparent)] Canceled(#[from] deno_core::Canceled), } @@ -143,7 +160,9 @@ fn start_watcher( let sender_clone = senders.clone(); let watcher: RecommendedWatcher = Watcher::new( move |res: Result| { - let res2 = res.map(FsEvent::from).map_err(FsEventsError::Notify); + let res2 = res + .map(FsEvent::from) + .map_err(|e| FsEventsError::Notify(JsNotifyError(e))); for (paths, sender) in sender_clone.lock().iter() { // Ignore result, if send failed it means that watcher was already closed, // but not all messages have been flushed. @@ -169,7 +188,8 @@ fn start_watcher( } }, Default::default(), - )?; + ) + .map_err(|e| FsEventsError::Notify(JsNotifyError(e)))?; state.put::(WatcherState { watcher, senders }); @@ -198,7 +218,10 @@ fn op_fs_events_open( .check_read(path, "Deno.watchFs()")?; let watcher = state.borrow_mut::(); - watcher.watcher.watch(&path, recursive_mode)?; + watcher + .watcher + .watch(&path, recursive_mode) + .map_err(|e| FsEventsError::Notify(JsNotifyError(e)))?; } let resource = FsEventsResource { receiver: AsyncRefCell::new(receiver), @@ -214,17 +237,13 @@ async fn op_fs_events_poll( state: Rc>, #[smi] rid: ResourceId, ) -> Result, FsEventsError> { - let resource = state - .borrow() - .resource_table - .get::(rid) - .map_err(FsEventsError::Resource)?; + let resource = state.borrow().resource_table.get::(rid)?; let mut receiver = RcRef::map(&resource, |r| &r.receiver).borrow_mut().await; let cancel = RcRef::map(resource, |r| &r.cancel); let maybe_result = receiver.recv().or_cancel(cancel).await?; match maybe_result { Some(Ok(value)) => Ok(Some(value)), - Some(Err(err)) => Err(FsEventsError::Notify(err)), + Some(Err(err)) => Err(FsEventsError::Notify(JsNotifyError(err))), None => Ok(None), } } diff --git a/runtime/ops/http.rs b/runtime/ops/http.rs index c9dc16cafe..931b407779 100644 --- a/runtime/ops/http.rs +++ b/runtime/ops/http.rs @@ -2,6 +2,7 @@ use std::rc::Rc; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::OpState; use deno_core::ResourceId; @@ -13,23 +14,34 @@ pub const UNSTABLE_FEATURE_NAME: &str = "http"; deno_core::extension!(deno_http_runtime, ops = [op_http_start],); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum HttpStartError { + #[class("Busy")] #[error("TCP stream is currently in use")] TcpStreamInUse, + #[class("Busy")] #[error("TLS stream is currently in use")] TlsStreamInUse, + #[class("Busy")] #[error("Unix socket is currently in use")] UnixSocketInUse, + #[class(generic)] #[error(transparent)] ReuniteTcp(#[from] tokio::net::tcp::ReuniteError), #[cfg(unix)] + #[class(generic)] #[error(transparent)] ReuniteUnix(#[from] tokio::net::unix::ReuniteError), + #[class(inherit)] #[error("{0}")] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + std::io::Error, + ), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Resource(#[inherit] ResourceError), } #[op2(fast)] @@ -89,5 +101,5 @@ fn op_http_start( )); } - Err(HttpStartError::Other(deno_core::error::bad_resource_id())) + Err(HttpStartError::Resource(ResourceError::BadResourceId)) } diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs index a17b467cb7..ad3a292c3a 100644 --- a/runtime/ops/os/mod.rs +++ b/runtime/ops/os/mod.rs @@ -8,6 +8,7 @@ use deno_core::v8; use deno_core::OpState; use deno_node::NODE_ENV_VAR_ALLOWLIST; use deno_path_util::normalize_path; +use deno_permissions::PermissionCheckError; use deno_permissions::PermissionsContainer; use serde::Serialize; @@ -71,20 +72,27 @@ deno_core::extension!( }, ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum OsError { + #[class(inherit)] #[error(transparent)] - Permission(#[from] deno_permissions::PermissionCheckError), + Permission(#[from] PermissionCheckError), + #[class("InvalidData")] #[error("File name or path {0:?} is not valid UTF-8")] InvalidUtf8(std::ffi::OsString), + #[class(type)] #[error("Key is an empty string.")] EnvEmptyKey, + #[class(type)] #[error("Key contains invalid characters: {0:?}")] EnvInvalidKey(String), + #[class(type)] #[error("Value contains invalid characters: {0:?}")] EnvInvalidValue(String), + #[class(inherit)] #[error(transparent)] Var(#[from] env::VarError), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), } @@ -129,7 +137,7 @@ fn op_set_env( #[serde] fn op_env( state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +) -> Result, PermissionCheckError> { state.borrow_mut::().check_env_all()?; Ok(env::vars().collect()) } @@ -195,7 +203,7 @@ fn op_exit(state: &mut OpState) { #[serde] fn op_loadavg( state: &mut OpState, -) -> Result<(f64, f64, f64), deno_core::error::AnyError> { +) -> Result<(f64, f64, f64), PermissionCheckError> { state .borrow_mut::() .check_sys("loadavg", "Deno.loadavg()")?; @@ -204,9 +212,7 @@ fn op_loadavg( #[op2(stack_trace, stack_trace)] #[string] -fn op_hostname( - state: &mut OpState, -) -> Result { +fn op_hostname(state: &mut OpState) -> Result { state .borrow_mut::() .check_sys("hostname", "Deno.hostname()")?; @@ -215,9 +221,7 @@ fn op_hostname( #[op2(stack_trace)] #[string] -fn op_os_release( - state: &mut OpState, -) -> Result { +fn op_os_release(state: &mut OpState) -> Result { state .borrow_mut::() .check_sys("osRelease", "Deno.osRelease()")?; @@ -280,7 +284,7 @@ impl From for NetworkInterface { #[serde] fn op_system_memory_info( state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("systemMemoryInfo", "Deno.systemMemoryInfo()")?; @@ -290,9 +294,7 @@ fn op_system_memory_info( #[cfg(not(windows))] #[op2(stack_trace)] #[smi] -fn op_gid( - state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +fn op_gid(state: &mut OpState) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("gid", "Deno.gid()")?; @@ -306,9 +308,7 @@ fn op_gid( #[cfg(windows)] #[op2(stack_trace)] #[smi] -fn op_gid( - state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +fn op_gid(state: &mut OpState) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("gid", "Deno.gid()")?; @@ -318,9 +318,7 @@ fn op_gid( #[cfg(not(windows))] #[op2(stack_trace)] #[smi] -fn op_uid( - state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +fn op_uid(state: &mut OpState) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("uid", "Deno.uid()")?; @@ -334,9 +332,7 @@ fn op_uid( #[cfg(windows)] #[op2(stack_trace)] #[smi] -fn op_uid( - state: &mut OpState, -) -> Result, deno_core::error::AnyError> { +fn op_uid(state: &mut OpState) -> Result, PermissionCheckError> { state .borrow_mut::() .check_sys("uid", "Deno.uid()")?; @@ -517,7 +513,7 @@ fn rss() -> usize { } } -fn os_uptime(state: &mut OpState) -> Result { +fn os_uptime(state: &mut OpState) -> Result { state .borrow_mut::() .check_sys("osUptime", "Deno.osUptime()")?; @@ -526,8 +522,6 @@ fn os_uptime(state: &mut OpState) -> Result { #[op2(fast, stack_trace)] #[number] -fn op_os_uptime( - state: &mut OpState, -) -> Result { +fn op_os_uptime(state: &mut OpState) -> Result { os_uptime(state) } diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs index 216637787c..0ad14d433b 100644 --- a/runtime/ops/permissions.rs +++ b/runtime/ops/permissions.rs @@ -45,16 +45,21 @@ impl From for PermissionStatus { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PermissionError { + #[class(reference)] #[error("No such permission name: {0}")] InvalidPermissionName(String), + #[class(inherit)] #[error("{0}")] PathResolve(#[from] ::deno_permissions::PathResolveError), + #[class(uri)] #[error("{0}")] NetDescriptorParse(#[from] ::deno_permissions::NetDescriptorParseError), + #[class(inherit)] #[error("{0}")] SysDescriptorParse(#[from] ::deno_permissions::SysDescriptorParseError), + #[class(inherit)] #[error("{0}")] RunDescriptorParse(#[from] ::deno_permissions::RunDescriptorParseError), } diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index cda0c73111..4c737f1126 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -25,6 +25,7 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ToJsBuffer; +use deno_error::JsErrorBox; use deno_io::fs::FileResource; use deno_io::ChildStderrResource; use deno_io::ChildStdinResource; @@ -107,8 +108,9 @@ impl StdioOrRid { match &self { StdioOrRid::Stdio(val) => Ok(val.as_stdio()), StdioOrRid::Rid(rid) => { - FileResource::with_file(state, *rid, |file| Ok(file.as_stdio()?)) - .map_err(ProcessError::Resource) + Ok(FileResource::with_file(state, *rid, |file| { + file.as_stdio().map_err(deno_error::JsErrorBox::from_err) + })?) } } } @@ -190,37 +192,73 @@ pub struct SpawnArgs { needs_npm_process_state: bool, } -#[derive(Debug, thiserror::Error)] +#[cfg(unix)] +deno_error::js_error_wrapper!(nix::Error, JsNixError, |err| { + match err { + nix::Error::ECHILD => "NotFound", + nix::Error::EINVAL => "TypeError", + nix::Error::ENOENT => "NotFound", + nix::Error::ENOTTY => "BadResource", + nix::Error::EPERM => "PermissionDenied", + nix::Error::ESRCH => "NotFound", + nix::Error::ELOOP => "FilesystemLoop", + nix::Error::ENOTDIR => "NotADirectory", + nix::Error::ENETUNREACH => "NetworkUnreachable", + nix::Error::EISDIR => "IsADirectory", + nix::Error::UnknownErrno => "Error", + &nix::Error::ENOTSUP => unreachable!(), + _ => "Error", + } +}); + +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ProcessError { + #[class(inherit)] #[error("Failed to spawn '{command}': {error}")] SpawnFailed { command: String, #[source] + #[inherit] error: Box, }, + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), #[cfg(unix)] + #[class(inherit)] #[error(transparent)] - Nix(nix::Error), + Nix(JsNixError), + #[class(inherit)] #[error("failed resolving cwd: {0}")] FailedResolvingCwd(#[source] std::io::Error), + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error(transparent)] RunPermission(#[from] CheckRunPermissionError), + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource(deno_core::error::ResourceError), + #[class(generic)] #[error(transparent)] BorrowMut(std::cell::BorrowMutError), + #[class(generic)] #[error(transparent)] Which(which::Error), + #[class(type)] #[error("Child process has already terminated.")] ChildProcessAlreadyTerminated, + #[class(type)] #[error("Invalid pid")] InvalidPid, + #[class(inherit)] #[error(transparent)] Signal(#[from] SignalError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), + #[class(type)] #[error("Missing cmd")] MissingCmd, // only for Deno.run } @@ -733,12 +771,14 @@ fn resolve_path(path: &str, cwd: &Path) -> PathBuf { deno_path_util::normalize_path(cwd.join(path)) } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CheckRunPermissionError { + #[class(inherit)] #[error(transparent)] Permission(#[from] deno_permissions::PermissionCheckError), + #[class(inherit)] #[error("{0}")] - Other(deno_core::error::AnyError), + Other(JsErrorBox), } fn check_run_permission( @@ -755,7 +795,7 @@ fn check_run_permission( // we don't allow users to launch subprocesses with any LD_ or DYLD_* // env vars set because this allows executing code (ex. LD_PRELOAD) return Err(CheckRunPermissionError::Other( - deno_core::error::custom_error( + JsErrorBox::new( "NotCapable", format!( "Requires --allow-run permissions to spawn subprocess with {0} environment variable{1}. Alternatively, spawn with {2} environment variable{1} unset.", @@ -1079,8 +1119,10 @@ mod deprecated { use nix::sys::signal::kill as unix_kill; use nix::sys::signal::Signal; use nix::unistd::Pid; - let sig = Signal::try_from(signo).map_err(ProcessError::Nix)?; - unix_kill(Pid::from_raw(pid), Some(sig)).map_err(ProcessError::Nix) + let sig = + Signal::try_from(signo).map_err(|e| ProcessError::Nix(JsNixError(e)))?; + unix_kill(Pid::from_raw(pid), Some(sig)) + .map_err(|e| ProcessError::Nix(JsNixError(e))) } #[cfg(not(unix))] diff --git a/runtime/ops/signal.rs b/runtime/ops/signal.rs index dfb52463cd..beefa1cd78 100644 --- a/runtime/ops/signal.rs +++ b/runtime/ops/signal.rs @@ -9,6 +9,7 @@ use std::sync::atomic::AtomicBool; #[cfg(unix)] use std::sync::Arc; +use deno_core::error::ResourceError; use deno_core::op2; use deno_core::AsyncRefCell; use deno_core::CancelFuture; @@ -43,14 +44,18 @@ deno_core::extension!( } ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SignalError { + #[class(type)] #[error(transparent)] InvalidSignalStr(#[from] crate::signal::InvalidSignalStrError), + #[class(type)] #[error(transparent)] InvalidSignalInt(#[from] crate::signal::InvalidSignalIntError), + #[class(type)] #[error("Binding to signal '{0}' is not allowed")] SignalNotAllowed(String), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), } @@ -223,7 +228,7 @@ fn op_signal_bind( async fn op_signal_poll( state: Rc>, #[smi] rid: ResourceId, -) -> Result { +) -> Result { let resource = state .borrow_mut() .resource_table @@ -242,7 +247,7 @@ async fn op_signal_poll( pub fn op_signal_unbind( state: &mut OpState, #[smi] rid: ResourceId, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), ResourceError> { let resource = state.resource_table.take::(rid)?; #[cfg(unix)] diff --git a/runtime/ops/tty.rs b/runtime/ops/tty.rs index 4843acccf3..d9912839b8 100644 --- a/runtime/ops/tty.rs +++ b/runtime/ops/tty.rs @@ -14,6 +14,9 @@ use deno_core::parking_lot::Mutex; use deno_core::OpState; #[cfg(unix)] use deno_core::ResourceId; +use deno_error::builtin_classes::GENERIC_ERROR; +use deno_error::JsErrorBox; +use deno_error::JsErrorClass; #[cfg(windows)] use deno_io::WinTtyState; #[cfg(unix)] @@ -52,6 +55,9 @@ use winapi::shared::minwindef::DWORD; #[cfg(windows)] use winapi::um::wincon; +#[cfg(unix)] +use crate::ops::process::JsNixError; + deno_core::extension!( deno_tty, ops = [op_set_raw, op_console_size, op_read_line_prompt], @@ -61,17 +67,29 @@ deno_core::extension!( }, ); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum TtyError { + #[class(inherit)] #[error(transparent)] - Resource(deno_core::error::AnyError), + Resource( + #[from] + #[inherit] + deno_core::error::ResourceError, + ), + #[class(inherit)] #[error("{0}")] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + Error, + ), #[cfg(unix)] + #[class(inherit)] #[error(transparent)] - Nix(nix::Error), + Nix(#[inherit] JsNixError), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[inherit] JsErrorBox), } // ref: @@ -101,10 +119,7 @@ fn op_set_raw( is_raw: bool, cbreak: bool, ) -> Result<(), TtyError> { - let handle_or_fd = state - .resource_table - .get_fd(rid) - .map_err(TtyError::Resource)?; + let handle_or_fd = state.resource_table.get_fd(rid)?; // From https://github.com/kkawakam/rustyline/blob/master/src/tty/windows.rs // and https://github.com/kkawakam/rustyline/blob/master/src/tty/unix.rs @@ -113,13 +128,14 @@ fn op_set_raw( // Copyright (c) 2019 Timon. MIT license. #[cfg(windows)] { + use deno_error::JsErrorBox; use winapi::shared::minwindef::FALSE; use winapi::um::consoleapi; let handle = handle_or_fd; if cbreak { - return Err(TtyError::Other(deno_core::error::not_supported())); + return Err(TtyError::Other(JsErrorBox::not_supported())); } let mut original_mode: DWORD = 0; @@ -264,8 +280,8 @@ fn op_set_raw( Some(mode) => mode, None => { // Save original mode. - let original_mode = - termios::tcgetattr(raw_fd).map_err(TtyError::Nix)?; + let original_mode = termios::tcgetattr(raw_fd) + .map_err(|e| TtyError::Nix(JsNixError(e)))?; tty_mode_store.set(rid, original_mode.clone()); original_mode } @@ -288,12 +304,12 @@ fn op_set_raw( raw.control_chars[termios::SpecialCharacterIndices::VMIN as usize] = 1; raw.control_chars[termios::SpecialCharacterIndices::VTIME as usize] = 0; termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &raw) - .map_err(TtyError::Nix)?; + .map_err(|e| TtyError::Nix(JsNixError(e)))?; } else { // Try restore saved mode. if let Some(mode) = tty_mode_store.take(rid) { termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &mode) - .map_err(TtyError::Nix)?; + .map_err(|e| TtyError::Nix(JsNixError(e)))?; } } @@ -311,10 +327,7 @@ fn op_console_size( result: &mut [u32], rid: u32, ) -> Result<(), TtyError> { - let fd = state - .resource_table - .get_fd(rid) - .map_err(TtyError::Resource)?; + let fd = state.resource_table.get_fd(rid)?; let size = console_size_from_fd(fd)?; result[0] = size.cols; result[1] = size.rows; @@ -432,12 +445,28 @@ mod tests { } } +deno_error::js_error_wrapper!(ReadlineError, JsReadlineError, |err| { + match err { + ReadlineError::Io(e) => e.get_class(), + ReadlineError::Eof => GENERIC_ERROR.into(), + ReadlineError::Interrupted => GENERIC_ERROR.into(), + #[cfg(unix)] + ReadlineError::Errno(e) => JsNixError(*e).get_class(), + ReadlineError::WindowResized => GENERIC_ERROR.into(), + #[cfg(windows)] + ReadlineError::Decode(_) => GENERIC_ERROR.into(), + #[cfg(windows)] + ReadlineError::SystemError(_) => GENERIC_ERROR.into(), + _ => GENERIC_ERROR.into(), + } +}); + #[op2] #[string] pub fn op_read_line_prompt( #[string] prompt_text: &str, #[string] default_value: &str, -) -> Result, ReadlineError> { +) -> Result, JsReadlineError> { let mut editor = Editor::<(), rustyline::history::DefaultHistory>::new() .expect("Failed to create editor."); @@ -457,6 +486,6 @@ pub fn op_read_line_prompt( Ok(None) } Err(ReadlineError::Eof) => Ok(None), - Err(err) => Err(err), + Err(err) => Err(JsReadlineError(err)), } } diff --git a/runtime/ops/web_worker/sync_fetch.rs b/runtime/ops/web_worker/sync_fetch.rs index c9b622d31e..4c5da428b2 100644 --- a/runtime/ops/web_worker/sync_fetch.rs +++ b/runtime/ops/web_worker/sync_fetch.rs @@ -26,30 +26,53 @@ fn mime_type_essence(mime_type: &str) -> String { essence.trim().to_ascii_lowercase() } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SyncFetchError { + #[class(type)] #[error("Blob URLs are not supported in this context.")] BlobUrlsNotSupportedInContext, + #[class(inherit)] #[error("{0}")] - Io(#[from] std::io::Error), + Io( + #[from] + #[inherit] + std::io::Error, + ), + #[class(type)] #[error("Invalid script URL")] InvalidScriptUrl, + #[class(type)] #[error("http status error: {0}")] InvalidStatusCode(http::StatusCode), + #[class(type)] #[error("Classic scripts with scheme {0}: are not supported in workers")] ClassicScriptSchemeUnsupportedInWorkers(String), + #[class(generic)] #[error("{0}")] InvalidUri(#[from] http::uri::InvalidUri), + #[class("DOMExceptionNetworkError")] #[error("Invalid MIME type {0:?}.")] InvalidMimeType(String), + #[class("DOMExceptionNetworkError")] #[error("Missing MIME type.")] MissingMimeType, + #[class(inherit)] #[error(transparent)] - Fetch(#[from] FetchError), + Fetch( + #[from] + #[inherit] + FetchError, + ), + #[class(inherit)] #[error(transparent)] - Join(#[from] tokio::task::JoinError), + Join( + #[from] + #[inherit] + tokio::task::JoinError, + ), + #[class(inherit)] #[error(transparent)] - Other(deno_core::error::AnyError), + Other(#[inherit] deno_error::JsErrorBox), } #[derive(Serialize, Deserialize)] diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs index 45285943eb..c77b3af694 100644 --- a/runtime/ops/worker_host.rs +++ b/runtime/ops/worker_host.rs @@ -120,16 +120,21 @@ pub struct CreateWorkerArgs { close_on_idle: bool, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CreateWorkerError { + #[class("DOMExceptionNotSupportedError")] #[error("Classic workers are not supported.")] ClassicWorkers, + #[class(inherit)] #[error(transparent)] Permission(deno_permissions::ChildPermissionError), + #[class(inherit)] #[error(transparent)] ModuleResolution(#[from] deno_core::ModuleResolutionError), + #[class(inherit)] #[error(transparent)] MessagePort(#[from] MessagePortError), + #[class(inherit)] #[error("{0}")] Io(#[from] std::io::Error), } diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index b9259941aa..be397fe6d3 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] capacity_builder.workspace = true deno_core.workspace = true +deno_error.workspace = true deno_path_util.workspace = true deno_terminal.workspace = true fqdn = "0.3.4" diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 8ab4058e79..3a357d2d44 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -819,7 +819,8 @@ pub enum Host { Ip(IpAddr), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(uri)] pub enum HostParseError { #[error("invalid IPv6 address: '{0}'")] InvalidIpv6(String), @@ -954,10 +955,12 @@ pub enum NetDescriptorParseError { Host(#[from] HostParseError), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum NetDescriptorFromUrlParseError { + #[class(type)] #[error("Missing host in url: '{0}'")] MissingHost(Url), + #[class(inherit)] #[error("{0}")] Host(#[from] HostParseError), } @@ -1324,10 +1327,12 @@ pub enum RunQueryDescriptor { Name(String), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PathResolveError { + #[class(inherit)] #[error("failed resolving cwd: {0}")] CwdResolve(#[source] std::io::Error), + #[class(generic)] #[error("Empty path is not allowed")] EmptyPath, } @@ -1484,12 +1489,15 @@ pub enum AllowRunDescriptorParseResult { Descriptor(AllowRunDescriptor), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum RunDescriptorParseError { + #[class(generic)] #[error("{0}")] Which(#[from] which::Error), + #[class(inherit)] #[error("{0}")] PathResolve(#[from] PathResolveError), + #[class(generic)] #[error("Empty run query is not allowed")] EmptyRunQuery, } @@ -1573,10 +1581,12 @@ fn denies_run_name(name: &str, cmd_path: &Path) -> bool { suffix.is_empty() || suffix.starts_with('.') } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SysDescriptorParseError { + #[class(type)] #[error("unknown system info kind \"{0}\"")] - InvalidKind(String), // TypeError + InvalidKind(String), + #[class(generic)] #[error("Empty sys not allowed")] Empty, // Error } @@ -2301,34 +2311,46 @@ pub enum CheckSpecifierKind { Dynamic, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ChildPermissionError { + #[class("NotCapable")] #[error("Can't escalate parent thread permissions")] Escalation, + #[class(inherit)] #[error("{0}")] PathResolve(#[from] PathResolveError), + #[class(uri)] #[error("{0}")] NetDescriptorParse(#[from] NetDescriptorParseError), + #[class(generic)] #[error("{0}")] EnvDescriptorParse(#[from] EnvDescriptorParseError), + #[class(inherit)] #[error("{0}")] SysDescriptorParse(#[from] SysDescriptorParseError), + #[class(inherit)] #[error("{0}")] RunDescriptorParse(#[from] RunDescriptorParseError), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum PermissionCheckError { + #[class("NotCapable")] #[error(transparent)] PermissionDenied(#[from] PermissionDeniedError), + #[class(uri)] #[error("Invalid file path.\n Specifier: {0}")] InvalidFilePath(Url), + #[class(inherit)] #[error(transparent)] NetDescriptorForUrlParse(#[from] NetDescriptorFromUrlParseError), + #[class(inherit)] #[error(transparent)] SysDescriptorParse(#[from] SysDescriptorParseError), + #[class(inherit)] #[error(transparent)] PathResolve(#[from] PathResolveError), + #[class(uri)] #[error(transparent)] HostParse(#[from] HostParseError), } diff --git a/runtime/shared.rs b/runtime/shared.rs index 1a9f2e66be..f8588dd72c 100644 --- a/runtime/shared.rs +++ b/runtime/shared.rs @@ -6,12 +6,12 @@ use std::path::Path; use deno_ast::MediaType; use deno_ast::ParseParams; use deno_ast::SourceMapOption; -use deno_core::error::AnyError; use deno_core::extension; use deno_core::Extension; use deno_core::ModuleCodeString; use deno_core::ModuleName; use deno_core::SourceMapData; +use deno_error::JsErrorBox; extension!(runtime, deps = [ @@ -64,10 +64,21 @@ extension!(runtime, } ); +deno_error::js_error_wrapper!( + deno_ast::ParseDiagnostic, + JsParseDiagnostic, + "Error" +); +deno_error::js_error_wrapper!( + deno_ast::TranspileError, + JsTranspileError, + "Error" +); + pub fn maybe_transpile_source( name: ModuleName, source: ModuleCodeString, -) -> Result<(ModuleCodeString, Option), AnyError> { +) -> Result<(ModuleCodeString, Option), JsErrorBox> { // Always transpile `node:` built-in modules, since they might be TypeScript. let media_type = if name.starts_with("node:") { MediaType::TypeScript @@ -92,7 +103,8 @@ pub fn maybe_transpile_source( capture_tokens: false, scope_analysis: false, maybe_syntax: None, - })?; + }) + .map_err(|e| JsErrorBox::from_err(JsParseDiagnostic(e)))?; let transpiled_source = parsed .transpile( &deno_ast::TranspileOptions { @@ -108,7 +120,8 @@ pub fn maybe_transpile_source( }, ..Default::default() }, - )? + ) + .map_err(|e| JsErrorBox::from_err(JsTranspileError(e)))? .into_source(); let maybe_source_map: Option = transpiled_source diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 270fc1ab9f..64393bb64c 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -13,8 +13,7 @@ use std::task::Poll; use deno_broadcast_channel::InMemoryBroadcastChannel; use deno_cache::CreateCache; use deno_cache::SqliteBackedCache; -use deno_core::error::AnyError; -use deno_core::error::JsError; +use deno_core::error::CoreError; use deno_core::futures::channel::mpsc; use deno_core::futures::future::poll_fn; use deno_core::futures::stream::StreamExt; @@ -29,7 +28,6 @@ use deno_core::CompiledWasmModuleStore; use deno_core::DetachedBuffer; use deno_core::Extension; use deno_core::FeatureChecker; -use deno_core::GetErrorClassFn; use deno_core::JsRuntime; use deno_core::ModuleCodeString; use deno_core::ModuleId; @@ -105,8 +103,7 @@ pub enum WebWorkerType { /// Events that are sent to host from child /// worker. pub enum WorkerControlEvent { - Error(AnyError), - TerminalError(AnyError), + TerminalError(CoreError), Close, } @@ -119,15 +116,13 @@ impl Serialize for WorkerControlEvent { { let type_id = match &self { WorkerControlEvent::TerminalError(_) => 1_i32, - WorkerControlEvent::Error(_) => 2_i32, WorkerControlEvent::Close => 3_i32, }; match self { - WorkerControlEvent::TerminalError(error) - | WorkerControlEvent::Error(error) => { - let value = match error.downcast_ref::() { - Some(js_error) => { + WorkerControlEvent::TerminalError(error) => { + let value = match error { + CoreError::Js(js_error) => { let frame = js_error.frames.iter().find(|f| match &f.file_name { Some(s) => !s.trim_start_matches('[').starts_with("ext:"), None => false, @@ -139,7 +134,7 @@ impl Serialize for WorkerControlEvent { "columnNumber": frame.map(|f| f.column_number.as_ref()), }) } - None => json!({ + _ => json!({ "message": error.to_string(), }), }; @@ -368,7 +363,6 @@ pub struct WebWorkerOptions { pub create_web_worker_cb: Arc, pub format_js_error_fn: Option>, pub worker_type: WebWorkerType, - pub get_error_class_fn: Option, pub cache_storage_dir: Option, pub stdio: Stdio, pub strace_ops: Option>, @@ -569,7 +563,6 @@ impl WebWorker { module_loader: Some(services.module_loader), startup_snapshot: options.startup_snapshot, create_params: options.create_params, - get_error_class_fn: options.get_error_class_fn, shared_array_buffer_store: services.shared_array_buffer_store, compiled_wasm_module_store: services.compiled_wasm_module_store, extensions, @@ -737,7 +730,7 @@ impl WebWorker { &mut self, name: &'static str, source_code: ModuleCodeString, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { self.js_runtime.execute_script(name, source_code)?; Ok(()) } @@ -746,7 +739,7 @@ impl WebWorker { pub async fn preload_main_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { self.js_runtime.load_main_es_module(module_specifier).await } @@ -754,7 +747,7 @@ impl WebWorker { pub async fn preload_side_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { self.js_runtime.load_side_es_module(module_specifier).await } @@ -765,7 +758,7 @@ impl WebWorker { pub async fn execute_side_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let id = self.preload_side_module(module_specifier).await?; let mut receiver = self.js_runtime.mod_evaluate(id); tokio::select! { @@ -789,7 +782,7 @@ impl WebWorker { pub async fn execute_main_module( &mut self, id: ModuleId, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let mut receiver = self.js_runtime.mod_evaluate(id); let poll_options = PollEventLoopOptions::default(); @@ -815,7 +808,7 @@ impl WebWorker { &mut self, cx: &mut Context, poll_options: PollEventLoopOptions, - ) -> Poll> { + ) -> Poll> { // If awakened because we are terminating, just return Ok if self.internal_handle.terminate_if_needed() { return Poll::Ready(Ok(())); @@ -859,7 +852,7 @@ impl WebWorker { pub async fn run_event_loop( &mut self, poll_options: PollEventLoopOptions, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { poll_fn(|cx| self.poll_event_loop(cx, poll_options)).await } @@ -893,14 +886,14 @@ impl WebWorker { } fn print_worker_error( - error: &AnyError, + error: &CoreError, name: &str, format_js_error_fn: Option<&FormatJsErrorFn>, ) { let error_str = match format_js_error_fn { - Some(format_js_error_fn) => match error.downcast_ref::() { - Some(js_error) => format_js_error_fn(js_error), - None => error.to_string(), + Some(format_js_error_fn) => match error { + CoreError::Js(js_error) => format_js_error_fn(js_error), + _ => error.to_string(), }, None => error.to_string(), }; @@ -919,7 +912,7 @@ pub fn run_web_worker( specifier: ModuleSpecifier, mut maybe_source_code: Option, format_js_error_fn: Option>, -) -> Result<(), AnyError> { +) -> Result<(), CoreError> { let name = worker.name.to_string(); // TODO(bartlomieju): run following block using "select!" diff --git a/runtime/worker.rs b/runtime/worker.rs index de29b66291..a649c83d47 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -12,14 +12,13 @@ use std::time::Instant; use deno_broadcast_channel::InMemoryBroadcastChannel; use deno_cache::CreateCache; use deno_cache::SqliteBackedCache; -use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::error::JsError; use deno_core::merge_op_metrics; use deno_core::v8; use deno_core::CompiledWasmModuleStore; use deno_core::Extension; use deno_core::FeatureChecker; -use deno_core::GetErrorClassFn; use deno_core::InspectorSessionKind; use deno_core::InspectorSessionOptions; use deno_core::JsRuntime; @@ -59,10 +58,10 @@ use crate::BootstrapOptions; pub type FormatJsErrorFn = dyn Fn(&JsError) -> String + Sync + Send; pub fn import_meta_resolve_callback( - loader: &dyn deno_core::ModuleLoader, + loader: &dyn ModuleLoader, specifier: String, referrer: String, -) -> Result { +) -> Result { loader.resolve( &specifier, &referrer, @@ -202,9 +201,6 @@ pub struct WorkerOptions { /// If Some, print a low-level trace output for ops matching the given patterns. pub strace_ops: Option>, - /// Allows to map error type to a string "class" used to represent - /// error in JavaScript. - pub get_error_class_fn: Option, pub cache_storage_dir: Option, pub origin_storage_dir: Option, pub stdio: Stdio, @@ -225,7 +221,6 @@ impl Default for WorkerOptions { strace_ops: Default::default(), maybe_inspector_server: Default::default(), format_js_error_fn: Default::default(), - get_error_class_fn: Default::default(), origin_storage_dir: Default::default(), cache_storage_dir: Default::default(), extensions: Default::default(), @@ -489,7 +484,6 @@ impl MainWorker { startup_snapshot: options.startup_snapshot, create_params: options.create_params, skip_op_registration: options.skip_op_registration, - get_error_class_fn: options.get_error_class_fn, shared_array_buffer_store: services.shared_array_buffer_store.clone(), compiled_wasm_module_store: services.compiled_wasm_module_store.clone(), extensions, @@ -708,7 +702,7 @@ impl MainWorker { &mut self, script_name: &'static str, source_code: ModuleCodeString, - ) -> Result, AnyError> { + ) -> Result, CoreError> { self.js_runtime.execute_script(script_name, source_code) } @@ -716,7 +710,7 @@ impl MainWorker { pub async fn preload_main_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { self.js_runtime.load_main_es_module(module_specifier).await } @@ -724,7 +718,7 @@ impl MainWorker { pub async fn preload_side_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result { + ) -> Result { self.js_runtime.load_side_es_module(module_specifier).await } @@ -732,7 +726,7 @@ impl MainWorker { pub async fn evaluate_module( &mut self, id: ModuleId, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { self.wait_for_inspector_session(); let mut receiver = self.js_runtime.mod_evaluate(id); tokio::select! { @@ -757,7 +751,7 @@ impl MainWorker { pub async fn run_up_to_duration( &mut self, duration: Duration, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { match tokio::time::timeout( duration, self @@ -776,7 +770,7 @@ impl MainWorker { pub async fn execute_side_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let id = self.preload_side_module(module_specifier).await?; self.evaluate_module(id).await } @@ -787,7 +781,7 @@ impl MainWorker { pub async fn execute_main_module( &mut self, module_specifier: &ModuleSpecifier, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { let id = self.preload_main_module(module_specifier).await?; self.evaluate_module(id).await } @@ -818,10 +812,10 @@ impl MainWorker { pub async fn run_event_loop( &mut self, wait_for_inspector: bool, - ) -> Result<(), AnyError> { + ) -> Result<(), CoreError> { self .js_runtime - .run_event_loop(deno_core::PollEventLoopOptions { + .run_event_loop(PollEventLoopOptions { wait_for_inspector, ..Default::default() }) @@ -837,7 +831,7 @@ impl MainWorker { /// Dispatches "load" event to the JavaScript runtime. /// /// Does not poll event loop, and thus not await any of the "load" event handlers. - pub fn dispatch_load_event(&mut self) -> Result<(), AnyError> { + pub fn dispatch_load_event(&mut self) -> Result<(), JsError> { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_load_event_fn = @@ -846,7 +840,7 @@ impl MainWorker { dispatch_load_event_fn.call(tc_scope, undefined.into(), &[]); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } Ok(()) } @@ -854,7 +848,7 @@ impl MainWorker { /// Dispatches "unload" event to the JavaScript runtime. /// /// Does not poll event loop, and thus not await any of the "unload" event handlers. - pub fn dispatch_unload_event(&mut self) -> Result<(), AnyError> { + pub fn dispatch_unload_event(&mut self) -> Result<(), JsError> { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_unload_event_fn = @@ -863,13 +857,13 @@ impl MainWorker { dispatch_unload_event_fn.call(tc_scope, undefined.into(), &[]); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } Ok(()) } /// Dispatches process.emit("exit") event for node compat. - pub fn dispatch_process_exit_event(&mut self) -> Result<(), AnyError> { + pub fn dispatch_process_exit_event(&mut self) -> Result<(), JsError> { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_process_exit_event_fn = @@ -878,7 +872,7 @@ impl MainWorker { dispatch_process_exit_event_fn.call(tc_scope, undefined.into(), &[]); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } Ok(()) } @@ -886,7 +880,7 @@ impl MainWorker { /// Dispatches "beforeunload" event to the JavaScript runtime. Returns a boolean /// indicating if the event was prevented and thus event loop should continue /// running. - pub fn dispatch_beforeunload_event(&mut self) -> Result { + pub fn dispatch_beforeunload_event(&mut self) -> Result { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_beforeunload_event_fn = @@ -896,16 +890,14 @@ impl MainWorker { dispatch_beforeunload_event_fn.call(tc_scope, undefined.into(), &[]); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } let ret_val = ret_val.unwrap(); Ok(ret_val.is_false()) } /// Dispatches process.emit("beforeExit") event for node compat. - pub fn dispatch_process_beforeexit_event( - &mut self, - ) -> Result { + pub fn dispatch_process_beforeexit_event(&mut self) -> Result { let scope = &mut self.js_runtime.handle_scope(); let tc_scope = &mut v8::TryCatch::new(scope); let dispatch_process_beforeexit_event_fn = v8::Local::new( @@ -920,7 +912,7 @@ impl MainWorker { ); if let Some(exception) = tc_scope.exception() { let error = JsError::from_v8_exception(tc_scope, exception); - return Err(error.into()); + return Err(error); } let ret_val = ret_val.unwrap(); Ok(ret_val.is_true()) diff --git a/tests/specs/npm/npmrc_tarball_other_server/fail/main.out b/tests/specs/npm/npmrc_tarball_other_server/fail/main.out index 2c68dba54e..d49bc148ea 100644 --- a/tests/specs/npm/npmrc_tarball_other_server/fail/main.out +++ b/tests/specs/npm/npmrc_tarball_other_server/fail/main.out @@ -1,6 +1,6 @@ Download http://localhost:4261/@denotest%2ftarballs-privateserver2 Download http://localhost:4262/@denotest/tarballs-privateserver2/1.0.0.tgz -error: Failed caching npm package '@denotest/tarballs-privateserver2@1.0.0'. +error: Failed caching npm package '@denotest/tarballs-privateserver2@1.0.0' Caused by: No auth for tarball URI, but present for scoped registry. diff --git a/tests/specs/npm/npmrc_tarball_other_server/success/main.out b/tests/specs/npm/npmrc_tarball_other_server/success/main.out index 5322a1a17d..239f1d525b 100644 --- a/tests/specs/npm/npmrc_tarball_other_server/success/main.out +++ b/tests/specs/npm/npmrc_tarball_other_server/success/main.out @@ -4,7 +4,7 @@ Download http://localhost:4262/@denotest/tarballs-privateserver2/1.0.0.tgz [# to serve proper checksums for a package at another registry. That's fine] [# though because this shows us that we're making it to this step instead of] [# failing sooner on an auth issue.] -error: Failed caching npm package '@denotest/tarballs-privateserver2@1.0.0'. +error: Failed caching npm package '@denotest/tarballs-privateserver2@1.0.0' Caused by: Tarball checksum did not match [WILDCARD] From ce0968ef3ad32fd007fe592495788fa453f2bb0b Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 8 Jan 2025 18:46:37 -0500 Subject: [PATCH 082/107] refactor(npm): split some resolution from installation (#27595) This splits away some npm resolution code from installation. It will allow for more easily extracting out resolution code in the future. --- cli/npm/managed/installer.rs | 226 ++++ .../common/bin_entries.rs | 0 .../common/lifecycle_scripts.rs | 0 cli/npm/managed/installers/common/mod.rs | 18 + cli/npm/managed/installers/global.rs | 190 +++ cli/npm/managed/installers/local.rs | 1032 +++++++++++++++++ cli/npm/managed/installers/mod.rs | 55 + cli/npm/managed/mod.rs | 55 +- cli/npm/managed/resolution.rs | 214 +--- cli/npm/managed/resolvers/common.rs | 11 - cli/npm/managed/resolvers/global.rs | 167 +-- cli/npm/managed/resolvers/local.rs | 981 +--------------- cli/npm/managed/resolvers/mod.rs | 27 +- cli/util/sync/mod.rs | 2 - cli/util/sync/sync_read_async_write_lock.rs | 62 - 15 files changed, 1571 insertions(+), 1469 deletions(-) create mode 100644 cli/npm/managed/installer.rs rename cli/npm/managed/{resolvers => installers}/common/bin_entries.rs (100%) rename cli/npm/managed/{resolvers => installers}/common/lifecycle_scripts.rs (100%) create mode 100644 cli/npm/managed/installers/common/mod.rs create mode 100644 cli/npm/managed/installers/global.rs create mode 100644 cli/npm/managed/installers/local.rs create mode 100644 cli/npm/managed/installers/mod.rs delete mode 100644 cli/util/sync/sync_read_async_write_lock.rs diff --git a/cli/npm/managed/installer.rs b/cli/npm/managed/installer.rs new file mode 100644 index 0000000000..30ac807023 --- /dev/null +++ b/cli/npm/managed/installer.rs @@ -0,0 +1,226 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashSet; +use std::sync::Arc; + +use capacity_builder::StringBuilder; +use deno_core::error::AnyError; +use deno_error::JsErrorBox; +use deno_lockfile::NpmPackageDependencyLockfileInfo; +use deno_lockfile::NpmPackageLockfileInfo; +use deno_npm::registry::NpmRegistryApi; +use deno_npm::resolution::AddPkgReqsOptions; +use deno_npm::resolution::NpmResolutionError; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::NpmResolutionPackage; +use deno_semver::jsr::JsrDepPackageReq; +use deno_semver::package::PackageNv; +use deno_semver::package::PackageReq; +use deno_semver::SmallStackString; +use deno_semver::VersionReq; + +use super::resolution::NpmResolution; +use crate::args::CliLockfile; +use crate::npm::CliNpmRegistryInfoProvider; +use crate::util::sync::TaskQueue; + +pub struct AddPkgReqsResult { + /// Results from adding the individual packages. + /// + /// The indexes of the results correspond to the indexes of the provided + /// package requirements. + pub results: Vec>, + /// The final result of resolving and caching all the package requirements. + pub dependencies_result: Result<(), JsErrorBox>, +} + +/// Updates the npm resolution with the provided package requirements. +pub struct NpmResolutionInstaller { + registry_info_provider: Arc, + resolution: Arc, + maybe_lockfile: Option>, + update_queue: TaskQueue, +} + +impl NpmResolutionInstaller { + pub fn new( + registry_info_provider: Arc, + resolution: Arc, + maybe_lockfile: Option>, + ) -> Self { + Self { + registry_info_provider, + resolution, + maybe_lockfile, + update_queue: Default::default(), + } + } + + pub async fn add_package_reqs( + &self, + package_reqs: &[PackageReq], + ) -> AddPkgReqsResult { + // only allow one thread in here at a time + let _snapshot_lock = self.update_queue.acquire().await; + let result = add_package_reqs_to_snapshot( + &self.registry_info_provider, + package_reqs, + self.maybe_lockfile.clone(), + || self.resolution.snapshot(), + ) + .await; + + AddPkgReqsResult { + results: result.results, + dependencies_result: match result.dep_graph_result { + Ok(snapshot) => { + self.resolution.set_snapshot(snapshot); + Ok(()) + } + Err(err) => Err(JsErrorBox::from_err(err)), + }, + } + } + + pub async fn set_package_reqs( + &self, + package_reqs: &[PackageReq], + ) -> Result<(), AnyError> { + // only allow one thread in here at a time + let _snapshot_lock = self.update_queue.acquire().await; + + let reqs_set = package_reqs.iter().collect::>(); + let snapshot = add_package_reqs_to_snapshot( + &self.registry_info_provider, + package_reqs, + self.maybe_lockfile.clone(), + || { + let snapshot = self.resolution.snapshot(); + let has_removed_package = !snapshot + .package_reqs() + .keys() + .all(|req| reqs_set.contains(req)); + // if any packages were removed, we need to completely recreate the npm resolution snapshot + if has_removed_package { + snapshot.into_empty() + } else { + snapshot + } + }, + ) + .await + .into_result()?; + + self.resolution.set_snapshot(snapshot); + + Ok(()) + } +} + +async fn add_package_reqs_to_snapshot( + registry_info_provider: &Arc, + package_reqs: &[PackageReq], + maybe_lockfile: Option>, + get_new_snapshot: impl Fn() -> NpmResolutionSnapshot, +) -> deno_npm::resolution::AddPkgReqsResult { + let snapshot = get_new_snapshot(); + if package_reqs + .iter() + .all(|req| snapshot.package_reqs().contains_key(req)) + { + log::debug!("Snapshot already up to date. Skipping npm resolution."); + return deno_npm::resolution::AddPkgReqsResult { + results: package_reqs + .iter() + .map(|req| Ok(snapshot.package_reqs().get(req).unwrap().clone())) + .collect(), + dep_graph_result: Ok(snapshot), + }; + } + log::debug!( + /* this string is used in tests */ + "Running npm resolution." + ); + let npm_registry_api = registry_info_provider.as_npm_registry_api(); + let result = snapshot + .add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs)) + .await; + let result = match &result.dep_graph_result { + Err(NpmResolutionError::Resolution(err)) + if npm_registry_api.mark_force_reload() => + { + log::debug!("{err:#}"); + log::debug!("npm resolution failed. Trying again..."); + + // try again with forced reloading + let snapshot = get_new_snapshot(); + snapshot + .add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs)) + .await + } + _ => result, + }; + + registry_info_provider.clear_memory_cache(); + + if let Ok(snapshot) = &result.dep_graph_result { + if let Some(lockfile) = maybe_lockfile { + populate_lockfile_from_snapshot(&lockfile, snapshot); + } + } + + result +} + +fn get_add_pkg_reqs_options(package_reqs: &[PackageReq]) -> AddPkgReqsOptions { + AddPkgReqsOptions { + package_reqs, + // WARNING: When bumping this version, check if anything needs to be + // updated in the `setNodeOnlyGlobalNames` call in 99_main_compiler.js + types_node_version_req: Some( + VersionReq::parse_from_npm("22.0.0 - 22.5.4").unwrap(), + ), + } +} + +fn populate_lockfile_from_snapshot( + lockfile: &CliLockfile, + snapshot: &NpmResolutionSnapshot, +) { + fn npm_package_to_lockfile_info( + pkg: &NpmResolutionPackage, + ) -> NpmPackageLockfileInfo { + let dependencies = pkg + .dependencies + .iter() + .map(|(name, id)| NpmPackageDependencyLockfileInfo { + name: name.clone(), + id: id.as_serialized(), + }) + .collect(); + + NpmPackageLockfileInfo { + serialized_id: pkg.id.as_serialized(), + integrity: pkg.dist.integrity().for_lockfile(), + dependencies, + } + } + + let mut lockfile = lockfile.lock(); + for (package_req, nv) in snapshot.package_reqs() { + let id = &snapshot.resolve_package_from_deno_module(nv).unwrap().id; + lockfile.insert_package_specifier( + JsrDepPackageReq::npm(package_req.clone()), + { + StringBuilder::::build(|builder| { + builder.append(&id.nv.version); + builder.append(&id.peer_dependencies); + }) + .unwrap() + }, + ); + } + for package in snapshot.all_packages_for_every_system() { + lockfile.insert_npm_package(npm_package_to_lockfile_info(package)); + } +} diff --git a/cli/npm/managed/resolvers/common/bin_entries.rs b/cli/npm/managed/installers/common/bin_entries.rs similarity index 100% rename from cli/npm/managed/resolvers/common/bin_entries.rs rename to cli/npm/managed/installers/common/bin_entries.rs diff --git a/cli/npm/managed/resolvers/common/lifecycle_scripts.rs b/cli/npm/managed/installers/common/lifecycle_scripts.rs similarity index 100% rename from cli/npm/managed/resolvers/common/lifecycle_scripts.rs rename to cli/npm/managed/installers/common/lifecycle_scripts.rs diff --git a/cli/npm/managed/installers/common/mod.rs b/cli/npm/managed/installers/common/mod.rs new file mode 100644 index 0000000000..9659649a2e --- /dev/null +++ b/cli/npm/managed/installers/common/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use async_trait::async_trait; +use deno_error::JsErrorBox; + +use crate::npm::PackageCaching; + +pub mod bin_entries; +pub mod lifecycle_scripts; + +/// Part of the resolution that interacts with the file system. +#[async_trait(?Send)] +pub trait NpmPackageFsInstaller: Send + Sync { + async fn cache_packages<'a>( + &self, + caching: PackageCaching<'a>, + ) -> Result<(), JsErrorBox>; +} diff --git a/cli/npm/managed/installers/global.rs b/cli/npm/managed/installers/global.rs new file mode 100644 index 0000000000..d637c96122 --- /dev/null +++ b/cli/npm/managed/installers/global.rs @@ -0,0 +1,190 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use async_trait::async_trait; +use deno_core::futures::stream::FuturesUnordered; +use deno_core::futures::StreamExt; +use deno_error::JsErrorBox; +use deno_npm::NpmResolutionPackage; +use deno_npm::NpmSystemInfo; + +use super::super::resolution::NpmResolution; +use super::common::lifecycle_scripts::LifecycleScriptsStrategy; +use super::common::NpmPackageFsInstaller; +use crate::args::LifecycleScriptsConfig; +use crate::cache::FastInsecureHasher; +use crate::colors; +use crate::npm::managed::PackageCaching; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; + +/// Resolves packages from the global npm cache. +#[derive(Debug)] +pub struct GlobalNpmPackageInstaller { + cache: Arc, + tarball_cache: Arc, + resolution: Arc, + system_info: NpmSystemInfo, + lifecycle_scripts: LifecycleScriptsConfig, +} + +impl GlobalNpmPackageInstaller { + pub fn new( + cache: Arc, + tarball_cache: Arc, + resolution: Arc, + system_info: NpmSystemInfo, + lifecycle_scripts: LifecycleScriptsConfig, + ) -> Self { + Self { + cache, + tarball_cache, + resolution, + system_info, + lifecycle_scripts, + } + } +} + +#[async_trait(?Send)] +impl NpmPackageFsInstaller for GlobalNpmPackageInstaller { + async fn cache_packages<'a>( + &self, + caching: PackageCaching<'a>, + ) -> Result<(), JsErrorBox> { + let package_partitions = match caching { + PackageCaching::All => self + .resolution + .all_system_packages_partitioned(&self.system_info), + PackageCaching::Only(reqs) => self + .resolution + .subset(&reqs) + .all_system_packages_partitioned(&self.system_info), + }; + cache_packages(&package_partitions.packages, &self.tarball_cache) + .await + .map_err(JsErrorBox::from_err)?; + + // create the copy package folders + for copy in package_partitions.copy_packages { + self + .cache + .ensure_copy_package(©.get_package_cache_folder_id()) + .map_err(JsErrorBox::from_err)?; + } + + let mut lifecycle_scripts = + super::common::lifecycle_scripts::LifecycleScripts::new( + &self.lifecycle_scripts, + GlobalLifecycleScripts::new(self, &self.lifecycle_scripts.root_dir), + ); + for package in &package_partitions.packages { + let package_folder = self.cache.package_folder_for_nv(&package.id.nv); + lifecycle_scripts.add(package, Cow::Borrowed(&package_folder)); + } + + lifecycle_scripts + .warn_not_run_scripts() + .map_err(JsErrorBox::from_err)?; + + Ok(()) + } +} + +async fn cache_packages( + packages: &[NpmResolutionPackage], + tarball_cache: &Arc, +) -> Result<(), deno_npm_cache::EnsurePackageError> { + let mut futures_unordered = FuturesUnordered::new(); + for package in packages { + futures_unordered.push(async move { + tarball_cache + .ensure_package(&package.id.nv, &package.dist) + .await + }); + } + while let Some(result) = futures_unordered.next().await { + // surface the first error + result?; + } + Ok(()) +} + +struct GlobalLifecycleScripts<'a> { + installer: &'a GlobalNpmPackageInstaller, + path_hash: u64, +} + +impl<'a> GlobalLifecycleScripts<'a> { + fn new(installer: &'a GlobalNpmPackageInstaller, root_dir: &Path) -> Self { + let mut hasher = FastInsecureHasher::new_without_deno_version(); + hasher.write(root_dir.to_string_lossy().as_bytes()); + let path_hash = hasher.finish(); + Self { + installer, + path_hash, + } + } + + fn warned_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf { + self + .package_path(package) + .join(format!(".scripts-warned-{}", self.path_hash)) + } +} + +impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy + for GlobalLifecycleScripts<'a> +{ + fn can_run_scripts(&self) -> bool { + false + } + fn package_path(&self, package: &NpmResolutionPackage) -> PathBuf { + self.installer.cache.package_folder_for_nv(&package.id.nv) + } + + fn warn_on_scripts_not_run( + &self, + packages: &[(&NpmResolutionPackage, PathBuf)], + ) -> std::result::Result<(), std::io::Error> { + log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall")); + for (package, _) in packages { + log::warn!("┠─ {}", colors::gray(format!("npm:{}", package.id.nv))); + } + log::warn!("┃"); + log::warn!( + "┠─ {}", + colors::italic("This may cause the packages to not work correctly.") + ); + log::warn!("┠─ {}", colors::italic("Lifecycle scripts are only supported when using a `node_modules` directory.")); + log::warn!( + "┠─ {}", + colors::italic("Enable it in your deno config file:") + ); + log::warn!("┖─ {}", colors::bold("\"nodeModulesDir\": \"auto\"")); + + for (package, _) in packages { + std::fs::write(self.warned_scripts_file(package), "")?; + } + Ok(()) + } + + fn did_run_scripts( + &self, + _package: &NpmResolutionPackage, + ) -> Result<(), std::io::Error> { + Ok(()) + } + + fn has_warned(&self, package: &NpmResolutionPackage) -> bool { + self.warned_scripts_file(package).exists() + } + + fn has_run(&self, _package: &NpmResolutionPackage) -> bool { + false + } +} diff --git a/cli/npm/managed/installers/local.rs b/cli/npm/managed/installers/local.rs new file mode 100644 index 0000000000..e2c5653801 --- /dev/null +++ b/cli/npm/managed/installers/local.rs @@ -0,0 +1,1032 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +//! Code for local node_modules resolution. + +use std::cell::RefCell; +use std::cmp::Ordering; +use std::collections::hash_map::Entry; +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::collections::HashSet; +use std::fs; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + +use async_trait::async_trait; +use deno_core::futures::stream::FuturesUnordered; +use deno_core::futures::StreamExt; +use deno_core::parking_lot::Mutex; +use deno_error::JsErrorBox; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::NpmResolutionPackage; +use deno_npm::NpmSystemInfo; +use deno_path_util::fs::atomic_write_file_with_retries; +use deno_semver::package::PackageNv; +use deno_semver::StackString; +use serde::Deserialize; +use serde::Serialize; + +use super::super::resolution::NpmResolution; +use super::common::bin_entries; +use super::common::NpmPackageFsInstaller; +use crate::args::LifecycleScriptsConfig; +use crate::args::NpmInstallDepsProvider; +use crate::cache::CACHE_PERM; +use crate::colors; +use crate::npm::managed::resolvers::get_package_folder_id_folder_name; +use crate::npm::managed::PackageCaching; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; +use crate::util::fs::clone_dir_recursive; +use crate::util::fs::symlink_dir; +use crate::util::fs::LaxSingleProcessFsFlag; +use crate::util::progress_bar::ProgressBar; +use crate::util::progress_bar::ProgressMessagePrompt; + +/// Resolver that creates a local node_modules directory +/// and resolves packages from it. +#[derive(Debug)] +pub struct LocalNpmPackageInstaller { + cache: Arc, + npm_install_deps_provider: Arc, + progress_bar: ProgressBar, + resolution: Arc, + sys: CliSys, + tarball_cache: Arc, + root_node_modules_path: PathBuf, + system_info: NpmSystemInfo, + lifecycle_scripts: LifecycleScriptsConfig, +} + +impl LocalNpmPackageInstaller { + #[allow(clippy::too_many_arguments)] + pub fn new( + cache: Arc, + npm_install_deps_provider: Arc, + progress_bar: ProgressBar, + resolution: Arc, + sys: CliSys, + tarball_cache: Arc, + node_modules_folder: PathBuf, + system_info: NpmSystemInfo, + lifecycle_scripts: LifecycleScriptsConfig, + ) -> Self { + Self { + cache, + npm_install_deps_provider, + progress_bar, + resolution, + tarball_cache, + sys, + root_node_modules_path: node_modules_folder, + system_info, + lifecycle_scripts, + } + } +} + +#[async_trait(?Send)] +impl NpmPackageFsInstaller for LocalNpmPackageInstaller { + async fn cache_packages<'a>( + &self, + caching: PackageCaching<'a>, + ) -> Result<(), JsErrorBox> { + let snapshot = match caching { + PackageCaching::All => self.resolution.snapshot(), + PackageCaching::Only(reqs) => self.resolution.subset(&reqs), + }; + sync_resolution_with_fs( + &snapshot, + &self.cache, + &self.npm_install_deps_provider, + &self.progress_bar, + &self.tarball_cache, + &self.root_node_modules_path, + &self.sys, + &self.system_info, + &self.lifecycle_scripts, + ) + .await + .map_err(JsErrorBox::from_err) + } +} + +/// `node_modules/.deno//node_modules/` +/// +/// Where the actual package is stored. +fn local_node_modules_package_contents_path( + local_registry_dir: &Path, + package: &NpmResolutionPackage, +) -> PathBuf { + local_registry_dir + .join(get_package_folder_id_folder_name( + &package.get_package_cache_folder_id(), + )) + .join("node_modules") + .join(&package.id.nv.name) +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum SyncResolutionWithFsError { + #[class(inherit)] + #[error("Creating '{path}'")] + Creating { + path: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + CopyDirRecursive(#[from] crate::util::fs::CopyDirRecursiveError), + #[class(inherit)] + #[error(transparent)] + SymlinkPackageDir(#[from] SymlinkPackageDirError), + #[class(inherit)] + #[error(transparent)] + BinEntries(#[from] bin_entries::BinEntriesError), + #[class(inherit)] + #[error(transparent)] + LifecycleScripts( + #[from] super::common::lifecycle_scripts::LifecycleScriptsError, + ), + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + +/// Creates a pnpm style folder structure. +#[allow(clippy::too_many_arguments)] +async fn sync_resolution_with_fs( + snapshot: &NpmResolutionSnapshot, + cache: &Arc, + npm_install_deps_provider: &NpmInstallDepsProvider, + progress_bar: &ProgressBar, + tarball_cache: &Arc, + root_node_modules_dir_path: &Path, + sys: &CliSys, + system_info: &NpmSystemInfo, + lifecycle_scripts: &LifecycleScriptsConfig, +) -> Result<(), SyncResolutionWithFsError> { + if snapshot.is_empty() + && npm_install_deps_provider.workspace_pkgs().is_empty() + { + return Ok(()); // don't create the directory + } + + // don't set up node_modules (and more importantly try to acquire the file lock) + // if we're running as part of a lifecycle script + if super::common::lifecycle_scripts::is_running_lifecycle_script() { + return Ok(()); + } + + let deno_local_registry_dir = root_node_modules_dir_path.join(".deno"); + let deno_node_modules_dir = deno_local_registry_dir.join("node_modules"); + fs::create_dir_all(&deno_node_modules_dir).map_err(|source| { + SyncResolutionWithFsError::Creating { + path: deno_local_registry_dir.to_path_buf(), + source, + } + })?; + let bin_node_modules_dir_path = root_node_modules_dir_path.join(".bin"); + fs::create_dir_all(&bin_node_modules_dir_path).map_err(|source| { + SyncResolutionWithFsError::Creating { + path: deno_local_registry_dir.to_path_buf(), + source, + } + })?; + + let single_process_lock = LaxSingleProcessFsFlag::lock( + deno_local_registry_dir.join(".deno.lock"), + // similar message used by cargo build + "waiting for file lock on node_modules directory", + ) + .await; + + // load this after we get the directory lock + let mut setup_cache = + SetupCache::load(deno_local_registry_dir.join(".setup-cache.bin")); + + let pb_clear_guard = progress_bar.clear_guard(); // prevent flickering + + // 1. Write all the packages out the .deno directory. + // + // Copy (hardlink in future) // to + // node_modules/.deno//node_modules/ + let package_partitions = + snapshot.all_system_packages_partitioned(system_info); + let mut cache_futures = FuturesUnordered::new(); + let mut newest_packages_by_name: HashMap< + &StackString, + &NpmResolutionPackage, + > = HashMap::with_capacity(package_partitions.packages.len()); + let bin_entries = Rc::new(RefCell::new(bin_entries::BinEntries::new())); + let mut lifecycle_scripts = + super::common::lifecycle_scripts::LifecycleScripts::new( + lifecycle_scripts, + LocalLifecycleScripts { + deno_local_registry_dir: &deno_local_registry_dir, + }, + ); + let packages_with_deprecation_warnings = Arc::new(Mutex::new(Vec::new())); + + let mut package_tags: HashMap<&PackageNv, BTreeSet<&str>> = HashMap::new(); + for (package_req, package_nv) in snapshot.package_reqs() { + if let Some(tag) = package_req.version_req.tag() { + package_tags.entry(package_nv).or_default().insert(tag); + } + } + + for package in &package_partitions.packages { + if let Some(current_pkg) = + newest_packages_by_name.get_mut(&package.id.nv.name) + { + if current_pkg.id.nv.cmp(&package.id.nv) == Ordering::Less { + *current_pkg = package; + } + } else { + newest_packages_by_name.insert(&package.id.nv.name, package); + }; + + let package_folder_name = + get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); + let folder_path = deno_local_registry_dir.join(&package_folder_name); + let tags = package_tags + .get(&package.id.nv) + .map(|tags| { + capacity_builder::StringBuilder::::build(|builder| { + for (i, tag) in tags.iter().enumerate() { + if i > 0 { + builder.append(',') + } + builder.append(*tag); + } + }) + .unwrap() + }) + .unwrap_or_default(); + enum PackageFolderState { + UpToDate, + Uninitialized, + TagsOutdated, + } + let initialized_file = folder_path.join(".initialized"); + let package_state = std::fs::read_to_string(&initialized_file) + .map(|s| { + if s != tags { + PackageFolderState::TagsOutdated + } else { + PackageFolderState::UpToDate + } + }) + .unwrap_or(PackageFolderState::Uninitialized); + if !cache + .cache_setting() + .should_use_for_npm_package(&package.id.nv.name) + || matches!(package_state, PackageFolderState::Uninitialized) + { + // cache bust the dep from the dep setup cache so the symlinks + // are forced to be recreated + setup_cache.remove_dep(&package_folder_name); + + let folder_path = folder_path.clone(); + let bin_entries_to_setup = bin_entries.clone(); + let packages_with_deprecation_warnings = + packages_with_deprecation_warnings.clone(); + + cache_futures.push(async move { + tarball_cache + .ensure_package(&package.id.nv, &package.dist) + .await + .map_err(JsErrorBox::from_err)?; + let pb_guard = progress_bar.update_with_prompt( + ProgressMessagePrompt::Initialize, + &package.id.nv.to_string(), + ); + let sub_node_modules = folder_path.join("node_modules"); + let package_path = + join_package_name(&sub_node_modules, &package.id.nv.name); + let cache_folder = cache.package_folder_for_nv(&package.id.nv); + + deno_core::unsync::spawn_blocking({ + let package_path = package_path.clone(); + let sys = sys.clone(); + move || { + clone_dir_recursive(&sys, &cache_folder, &package_path)?; + // write out a file that indicates this folder has been initialized + fs::write(initialized_file, tags)?; + + Ok::<_, SyncResolutionWithFsError>(()) + } + }) + .await + .map_err(JsErrorBox::from_err)? + .map_err(JsErrorBox::from_err)?; + + if package.bin.is_some() { + bin_entries_to_setup.borrow_mut().add(package, package_path); + } + + if let Some(deprecated) = &package.deprecated { + packages_with_deprecation_warnings + .lock() + .push((package.id.clone(), deprecated.clone())); + } + + // finally stop showing the progress bar + drop(pb_guard); // explicit for clarity + Ok::<_, JsErrorBox>(()) + }); + } else if matches!(package_state, PackageFolderState::TagsOutdated) { + fs::write(initialized_file, tags)?; + } + + let sub_node_modules = folder_path.join("node_modules"); + let package_path = + join_package_name(&sub_node_modules, &package.id.nv.name); + lifecycle_scripts.add(package, package_path.into()); + } + + while let Some(result) = cache_futures.next().await { + result?; // surface the first error + } + + // 2. Create any "copy" packages, which are used for peer dependencies + for package in &package_partitions.copy_packages { + let package_cache_folder_id = package.get_package_cache_folder_id(); + let destination_path = deno_local_registry_dir + .join(get_package_folder_id_folder_name(&package_cache_folder_id)); + let initialized_file = destination_path.join(".initialized"); + if !initialized_file.exists() { + let sub_node_modules = destination_path.join("node_modules"); + let package_path = + join_package_name(&sub_node_modules, &package.id.nv.name); + + let source_path = join_package_name( + &deno_local_registry_dir + .join(get_package_folder_id_folder_name( + &package_cache_folder_id.with_no_count(), + )) + .join("node_modules"), + &package.id.nv.name, + ); + + clone_dir_recursive(sys, &source_path, &package_path)?; + // write out a file that indicates this folder has been initialized + fs::write(initialized_file, "")?; + } + } + + // 3. Symlink all the dependencies into the .deno directory. + // + // Symlink node_modules/.deno//node_modules/ to + // node_modules/.deno//node_modules/ + for package in package_partitions.iter_all() { + let package_folder_name = + get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); + let sub_node_modules = deno_local_registry_dir + .join(&package_folder_name) + .join("node_modules"); + let mut dep_setup_cache = setup_cache.with_dep(&package_folder_name); + for (name, dep_id) in &package.dependencies { + let dep = snapshot.package_from_id(dep_id).unwrap(); + if package.optional_dependencies.contains(name) + && !dep.system.matches_system(system_info) + { + continue; // this isn't a dependency for the current system + } + let dep_cache_folder_id = dep.get_package_cache_folder_id(); + let dep_folder_name = + get_package_folder_id_folder_name(&dep_cache_folder_id); + if dep_setup_cache.insert(name, &dep_folder_name) { + let dep_folder_path = join_package_name( + &deno_local_registry_dir + .join(dep_folder_name) + .join("node_modules"), + &dep_id.nv.name, + ); + symlink_package_dir( + &dep_folder_path, + &join_package_name(&sub_node_modules, name), + )?; + } + } + } + + let mut found_names: HashMap<&StackString, &PackageNv> = HashMap::new(); + + // set of node_modules in workspace packages that we've already ensured exist + let mut existing_child_node_modules_dirs: HashSet = HashSet::new(); + + // 4. Create symlinks for package json dependencies + { + for remote in npm_install_deps_provider.remote_pkgs() { + let remote_pkg = if let Ok(remote_pkg) = + snapshot.resolve_pkg_from_pkg_req(&remote.req) + { + remote_pkg + } else if remote.req.version_req.tag().is_some() { + // couldn't find a match, and `resolve_best_package_id` + // panics if you give it a tag + continue; + } else if let Some(remote_id) = snapshot + .resolve_best_package_id(&remote.req.name, &remote.req.version_req) + { + snapshot.package_from_id(&remote_id).unwrap() + } else { + continue; // skip, package not found + }; + let Some(remote_alias) = &remote.alias else { + continue; + }; + let alias_clashes = remote.req.name != *remote_alias + && newest_packages_by_name.contains_key(remote_alias); + let install_in_child = { + // we'll install in the child if the alias is taken by another package, or + // if there's already a package with the same name but different version + // linked into the root + match found_names.entry(remote_alias) { + Entry::Occupied(nv) => { + // alias to a different package (in case of duplicate aliases) + // or the version doesn't match the version in the root node_modules + alias_clashes || &remote_pkg.id.nv != *nv.get() + } + Entry::Vacant(entry) => { + entry.insert(&remote_pkg.id.nv); + alias_clashes + } + } + }; + let target_folder_name = get_package_folder_id_folder_name( + &remote_pkg.get_package_cache_folder_id(), + ); + let local_registry_package_path = join_package_name( + &deno_local_registry_dir + .join(&target_folder_name) + .join("node_modules"), + &remote_pkg.id.nv.name, + ); + if install_in_child { + // symlink the dep into the package's child node_modules folder + let dest_node_modules = remote.base_dir.join("node_modules"); + if !existing_child_node_modules_dirs.contains(&dest_node_modules) { + fs::create_dir_all(&dest_node_modules).map_err(|source| { + SyncResolutionWithFsError::Creating { + path: dest_node_modules.clone(), + source, + } + })?; + existing_child_node_modules_dirs.insert(dest_node_modules.clone()); + } + let mut dest_path = dest_node_modules; + dest_path.push(remote_alias); + + symlink_package_dir(&local_registry_package_path, &dest_path)?; + } else { + // symlink the package into `node_modules/` + if setup_cache + .insert_root_symlink(&remote_pkg.id.nv.name, &target_folder_name) + { + symlink_package_dir( + &local_registry_package_path, + &join_package_name(root_node_modules_dir_path, remote_alias), + )?; + } + } + } + } + + // 5. Create symlinks for the remaining top level packages in the node_modules folder. + // (These may be present if they are not in the package.json dependencies) + // Symlink node_modules/.deno//node_modules/ to + // node_modules/ + let mut ids = snapshot + .top_level_packages() + .filter(|f| !found_names.contains_key(&f.nv.name)) + .collect::>(); + ids.sort_by(|a, b| b.cmp(a)); // create determinism and only include the latest version + for id in ids { + match found_names.entry(&id.nv.name) { + Entry::Occupied(_) => { + continue; // skip, already handled + } + Entry::Vacant(entry) => { + entry.insert(&id.nv); + } + } + let package = snapshot.package_from_id(id).unwrap(); + let target_folder_name = + get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); + if setup_cache.insert_root_symlink(&id.nv.name, &target_folder_name) { + let local_registry_package_path = join_package_name( + &deno_local_registry_dir + .join(target_folder_name) + .join("node_modules"), + &id.nv.name, + ); + + symlink_package_dir( + &local_registry_package_path, + &join_package_name(root_node_modules_dir_path, &id.nv.name), + )?; + } + } + + // 6. Create a node_modules/.deno/node_modules/ directory with + // the remaining packages + for package in newest_packages_by_name.values() { + match found_names.entry(&package.id.nv.name) { + Entry::Occupied(_) => { + continue; // skip, already handled + } + Entry::Vacant(entry) => { + entry.insert(&package.id.nv); + } + } + + let target_folder_name = + get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); + if setup_cache.insert_deno_symlink(&package.id.nv.name, &target_folder_name) + { + let local_registry_package_path = join_package_name( + &deno_local_registry_dir + .join(target_folder_name) + .join("node_modules"), + &package.id.nv.name, + ); + + symlink_package_dir( + &local_registry_package_path, + &join_package_name(&deno_node_modules_dir, &package.id.nv.name), + )?; + } + } + + // 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, + |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 + { + // todo(dsherret): this is not exactly correct because it should + // install correctly for a workspace (potentially in sub directories), + // but this is good enough for a first pass + for workspace in npm_install_deps_provider.workspace_pkgs() { + let Some(workspace_alias) = &workspace.alias else { + continue; + }; + symlink_package_dir( + &workspace.target_dir, + &root_node_modules_dir_path.join(workspace_alias), + )?; + } + } + + { + let packages_with_deprecation_warnings = + packages_with_deprecation_warnings.lock(); + if !packages_with_deprecation_warnings.is_empty() { + log::warn!( + "{} The following packages are deprecated:", + colors::yellow("Warning") + ); + let len = packages_with_deprecation_warnings.len(); + for (idx, (package_id, msg)) in + packages_with_deprecation_warnings.iter().enumerate() + { + if idx != len - 1 { + log::warn!( + "┠─ {}", + colors::gray(format!("npm:{:?} ({})", package_id, msg)) + ); + } else { + log::warn!( + "┖─ {}", + colors::gray(format!("npm:{:?} ({})", package_id, msg)) + ); + } + } + } + } + + lifecycle_scripts + .finish( + snapshot, + &package_partitions.packages, + root_node_modules_dir_path, + progress_bar, + ) + .await?; + + setup_cache.save(); + drop(single_process_lock); + drop(pb_clear_guard); + + Ok(()) +} + +/// `node_modules/.deno//` +fn local_node_modules_package_folder( + local_registry_dir: &Path, + package: &NpmResolutionPackage, +) -> PathBuf { + local_registry_dir.join(get_package_folder_id_folder_name( + &package.get_package_cache_folder_id(), + )) +} + +struct LocalLifecycleScripts<'a> { + deno_local_registry_dir: &'a Path, +} + +impl<'a> LocalLifecycleScripts<'a> { + /// `node_modules/.deno//.scripts-run` + fn ran_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf { + local_node_modules_package_folder(self.deno_local_registry_dir, package) + .join(".scripts-run") + } + + /// `node_modules/.deno//.scripts-warned` + fn warned_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf { + local_node_modules_package_folder(self.deno_local_registry_dir, package) + .join(".scripts-warned") + } +} + +impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy + for LocalLifecycleScripts<'a> +{ + fn package_path(&self, package: &NpmResolutionPackage) -> PathBuf { + local_node_modules_package_contents_path( + self.deno_local_registry_dir, + package, + ) + } + + fn did_run_scripts( + &self, + package: &NpmResolutionPackage, + ) -> std::result::Result<(), std::io::Error> { + std::fs::write(self.ran_scripts_file(package), "")?; + Ok(()) + } + + fn warn_on_scripts_not_run( + &self, + packages: &[(&NpmResolutionPackage, std::path::PathBuf)], + ) -> Result<(), std::io::Error> { + if !packages.is_empty() { + log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall")); + + for (package, _) in packages { + log::warn!("┠─ {}", colors::gray(format!("npm:{}", package.id.nv))); + } + + log::warn!("┃"); + log::warn!( + "┠─ {}", + colors::italic("This may cause the packages to not work correctly.") + ); + log::warn!("┖─ {}", colors::italic("To run lifecycle scripts, use the `--allow-scripts` flag with `deno install`:")); + let packages_comma_separated = packages + .iter() + .map(|(p, _)| format!("npm:{}", p.id.nv)) + .collect::>() + .join(","); + log::warn!( + " {}", + colors::bold(format!( + "deno install --allow-scripts={}", + packages_comma_separated + )) + ); + + for (package, _) in packages { + let _ignore_err = fs::write(self.warned_scripts_file(package), ""); + } + } + Ok(()) + } + + fn has_warned(&self, package: &NpmResolutionPackage) -> bool { + self.warned_scripts_file(package).exists() + } + + fn has_run(&self, package: &NpmResolutionPackage) -> bool { + self.ran_scripts_file(package).exists() + } +} + +// Uses BTreeMap to preserve the ordering of the elements in memory, to ensure +// the file generated from this datastructure is deterministic. +// See: https://github.com/denoland/deno/issues/24479 +/// Represents a dependency at `node_modules/.deno//` +struct SetupCacheDep<'a> { + previous: Option<&'a BTreeMap>, + current: &'a mut BTreeMap, +} + +impl<'a> SetupCacheDep<'a> { + pub fn insert(&mut self, name: &str, target_folder_name: &str) -> bool { + self + .current + .insert(name.to_string(), target_folder_name.to_string()); + if let Some(previous_target) = self.previous.and_then(|p| p.get(name)) { + previous_target != target_folder_name + } else { + true + } + } +} + +// Uses BTreeMap to preserve the ordering of the elements in memory, to ensure +// the file generated from this datastructure is deterministic. +// See: https://github.com/denoland/deno/issues/24479 +#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)] +struct SetupCacheData { + root_symlinks: BTreeMap, + deno_symlinks: BTreeMap, + dep_symlinks: BTreeMap>, +} + +/// It is very slow to try to re-setup the symlinks each time, so this will +/// cache what we've setup on the last run and only update what is necessary. +/// Obviously this could lead to issues if the cache gets out of date with the +/// file system, such as if the user manually deletes a symlink. +struct SetupCache { + file_path: PathBuf, + previous: Option, + current: SetupCacheData, +} + +impl SetupCache { + pub fn load(file_path: PathBuf) -> Self { + let previous = std::fs::read(&file_path) + .ok() + .and_then(|data| bincode::deserialize(&data).ok()); + Self { + file_path, + previous, + current: Default::default(), + } + } + + pub fn save(&self) -> bool { + if let Some(previous) = &self.previous { + if previous == &self.current { + return false; // nothing to save + } + } + + bincode::serialize(&self.current).ok().and_then(|data| { + atomic_write_file_with_retries( + &CliSys::default(), + &self.file_path, + &data, + CACHE_PERM, + ) + .ok() + }); + true + } + + /// Inserts and checks for the existence of a root symlink + /// at `node_modules/` pointing to + /// `node_modules/.deno//` + pub fn insert_root_symlink( + &mut self, + name: &str, + target_folder_name: &str, + ) -> bool { + self + .current + .root_symlinks + .insert(name.to_string(), target_folder_name.to_string()); + if let Some(previous_target) = self + .previous + .as_ref() + .and_then(|p| p.root_symlinks.get(name)) + { + previous_target != target_folder_name + } else { + true + } + } + + /// Inserts and checks for the existence of a symlink at + /// `node_modules/.deno/node_modules/` pointing to + /// `node_modules/.deno//` + pub fn insert_deno_symlink( + &mut self, + name: &str, + target_folder_name: &str, + ) -> bool { + self + .current + .deno_symlinks + .insert(name.to_string(), target_folder_name.to_string()); + if let Some(previous_target) = self + .previous + .as_ref() + .and_then(|p| p.deno_symlinks.get(name)) + { + previous_target != target_folder_name + } else { + true + } + } + + pub fn remove_dep(&mut self, parent_name: &str) { + if let Some(previous) = &mut self.previous { + previous.dep_symlinks.remove(parent_name); + } + } + + pub fn with_dep(&mut self, parent_name: &str) -> SetupCacheDep<'_> { + SetupCacheDep { + previous: self + .previous + .as_ref() + .and_then(|p| p.dep_symlinks.get(parent_name)), + current: self + .current + .dep_symlinks + .entry(parent_name.to_string()) + .or_default(), + } + } +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum SymlinkPackageDirError { + #[class(inherit)] + #[error("Creating '{parent}'")] + Creating { + parent: PathBuf, + #[source] + #[inherit] + source: std::io::Error, + }, + #[class(inherit)] + #[error(transparent)] + Other(#[from] std::io::Error), + #[cfg(windows)] + #[class(inherit)] + #[error("Creating junction in node_modules folder")] + FailedCreatingJunction { + #[source] + #[inherit] + source: std::io::Error, + }, +} + +fn symlink_package_dir( + old_path: &Path, + new_path: &Path, +) -> Result<(), SymlinkPackageDirError> { + let new_parent = new_path.parent().unwrap(); + if new_parent.file_name().unwrap() != "node_modules" { + // create the parent folder that will contain the symlink + fs::create_dir_all(new_parent).map_err(|source| { + SymlinkPackageDirError::Creating { + parent: new_parent.to_path_buf(), + source, + } + })?; + } + + // need to delete the previous symlink before creating a new one + let _ignore = fs::remove_dir_all(new_path); + + let old_path_relative = + crate::util::path::relative_path(new_parent, old_path) + .unwrap_or_else(|| old_path.to_path_buf()); + + #[cfg(windows)] + { + junction_or_symlink_dir(&old_path_relative, old_path, new_path) + } + #[cfg(not(windows))] + { + symlink_dir(&crate::sys::CliSys::default(), &old_path_relative, new_path) + .map_err(Into::into) + } +} + +#[cfg(windows)] +fn junction_or_symlink_dir( + old_path_relative: &Path, + old_path: &Path, + new_path: &Path, +) -> Result<(), SymlinkPackageDirError> { + static USE_JUNCTIONS: std::sync::atomic::AtomicBool = + std::sync::atomic::AtomicBool::new(false); + + if USE_JUNCTIONS.load(std::sync::atomic::Ordering::Relaxed) { + // Use junctions because they're supported on ntfs file systems without + // needing to elevate privileges on Windows. + // Note: junctions don't support relative paths, so we need to use the + // absolute path here. + return junction::create(old_path, new_path).map_err(|source| { + SymlinkPackageDirError::FailedCreatingJunction { source } + }); + } + + match symlink_dir(&crate::sys::CliSys::default(), old_path_relative, new_path) + { + Ok(()) => Ok(()), + Err(symlink_err) + if symlink_err.kind() == std::io::ErrorKind::PermissionDenied => + { + USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); + junction::create(old_path, new_path).map_err(|source| { + SymlinkPackageDirError::FailedCreatingJunction { source } + }) + } + Err(symlink_err) => { + log::warn!( + "{} Unexpected error symlinking node_modules: {symlink_err}", + colors::yellow("Warning") + ); + USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); + junction::create(old_path, new_path).map_err(|source| { + SymlinkPackageDirError::FailedCreatingJunction { source } + }) + } + } +} + +fn join_package_name(path: &Path, package_name: &str) -> PathBuf { + let mut path = path.to_path_buf(); + // ensure backslashes are used on windows + for part in package_name.split('/') { + path = path.join(part); + } + path +} + +#[cfg(test)] +mod test { + use test_util::TempDir; + + use super::*; + + #[test] + fn test_setup_cache() { + let temp_dir = TempDir::new(); + let cache_bin_path = temp_dir.path().join("cache.bin").to_path_buf(); + let mut cache = SetupCache::load(cache_bin_path.clone()); + assert!(cache.insert_deno_symlink("package-a", "package-a@1.0.0")); + assert!(cache.insert_root_symlink("package-a", "package-a@1.0.0")); + assert!(cache + .with_dep("package-a") + .insert("package-b", "package-b@1.0.0")); + assert!(cache.save()); + + let mut cache = SetupCache::load(cache_bin_path.clone()); + assert!(!cache.insert_deno_symlink("package-a", "package-a@1.0.0")); + assert!(!cache.insert_root_symlink("package-a", "package-a@1.0.0")); + assert!(!cache + .with_dep("package-a") + .insert("package-b", "package-b@1.0.0")); + assert!(!cache.save()); + assert!(cache.insert_root_symlink("package-b", "package-b@0.2.0")); + assert!(cache.save()); + + let mut cache = SetupCache::load(cache_bin_path); + cache.remove_dep("package-a"); + assert!(cache + .with_dep("package-a") + .insert("package-b", "package-b@1.0.0")); + } +} diff --git a/cli/npm/managed/installers/mod.rs b/cli/npm/managed/installers/mod.rs new file mode 100644 index 0000000000..4514fa1ec3 --- /dev/null +++ b/cli/npm/managed/installers/mod.rs @@ -0,0 +1,55 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::path::PathBuf; +use std::sync::Arc; + +use deno_npm::NpmSystemInfo; + +pub use self::common::NpmPackageFsInstaller; +use self::global::GlobalNpmPackageInstaller; +use self::local::LocalNpmPackageInstaller; +use super::resolution::NpmResolution; +use crate::args::LifecycleScriptsConfig; +use crate::args::NpmInstallDepsProvider; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; +use crate::util::progress_bar::ProgressBar; + +mod common; +mod global; +mod local; + +#[allow(clippy::too_many_arguments)] +pub fn create_npm_fs_installer( + npm_cache: Arc, + npm_install_deps_provider: &Arc, + progress_bar: &ProgressBar, + resolution: Arc, + sys: CliSys, + tarball_cache: Arc, + maybe_node_modules_path: Option, + system_info: NpmSystemInfo, + lifecycle_scripts: LifecycleScriptsConfig, +) -> Arc { + match maybe_node_modules_path { + Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new( + npm_cache, + npm_install_deps_provider.clone(), + progress_bar.clone(), + resolution, + sys, + tarball_cache, + node_modules_folder, + system_info, + lifecycle_scripts, + )), + None => Arc::new(GlobalNpmPackageInstaller::new( + npm_cache, + tarball_cache, + resolution, + system_info, + lifecycle_scripts, + )), + } +} diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 831b0b0ba8..6729a56b1e 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -28,11 +28,14 @@ use deno_runtime::colors; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use installer::AddPkgReqsResult; +use installer::NpmResolutionInstaller; +use installers::create_npm_fs_installer; +use installers::NpmPackageFsInstaller; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; -use resolution::AddPkgReqsResult; use self::resolution::NpmResolution; use self::resolvers::create_npm_fs_resolver; @@ -55,6 +58,8 @@ use crate::sys::CliSys; use crate::util::progress_bar::ProgressBar; use crate::util::sync::AtomicFlag; +mod installer; +mod installers; mod resolution; mod resolvers; @@ -156,11 +161,7 @@ fn create_inner( snapshot: Option, lifecycle_scripts: LifecycleScriptsConfig, ) -> Arc { - let resolution = Arc::new(NpmResolution::from_serialized( - registry_info_provider.clone(), - snapshot, - maybe_lockfile.clone(), - )); + let resolution = Arc::new(NpmResolution::from_serialized(snapshot)); let tarball_cache = Arc::new(CliNpmTarballCache::new( npm_cache.clone(), http_client, @@ -168,18 +169,25 @@ fn create_inner( npm_rc.clone(), )); - let fs_resolver = create_npm_fs_resolver( + let fs_installer = create_npm_fs_installer( npm_cache.clone(), &npm_install_deps_provider, &text_only_progress_bar, resolution.clone(), sys.clone(), tarball_cache.clone(), - node_modules_dir_path, + node_modules_dir_path.clone(), npm_system_info.clone(), lifecycle_scripts.clone(), ); + let fs_resolver = create_npm_fs_resolver( + npm_cache.clone(), + resolution.clone(), + sys.clone(), + node_modules_dir_path, + ); Arc::new(ManagedCliNpmResolver::new( + fs_installer, fs_resolver, maybe_lockfile, registry_info_provider, @@ -301,6 +309,7 @@ pub enum PackageCaching<'a> { /// An npm resolver where the resolution is managed by Deno rather than /// the user bringing their own node_modules (BYONM) on the file system. pub struct ManagedCliNpmResolver { + fs_installer: Arc, fs_resolver: Arc, maybe_lockfile: Option>, registry_info_provider: Arc, @@ -308,6 +317,7 @@ pub struct ManagedCliNpmResolver { npm_install_deps_provider: Arc, sys: CliSys, resolution: Arc, + resolution_installer: NpmResolutionInstaller, tarball_cache: Arc, text_only_progress_bar: ProgressBar, npm_system_info: NpmSystemInfo, @@ -348,6 +358,7 @@ pub enum ResolvePkgFolderFromDenoModuleError { impl ManagedCliNpmResolver { #[allow(clippy::too_many_arguments)] pub fn new( + fs_installer: Arc, fs_resolver: Arc, maybe_lockfile: Option>, registry_info_provider: Arc, @@ -360,7 +371,13 @@ impl ManagedCliNpmResolver { npm_system_info: NpmSystemInfo, lifecycle_scripts: LifecycleScriptsConfig, ) -> Self { + let resolution_installer = NpmResolutionInstaller::new( + registry_info_provider.clone(), + resolution.clone(), + maybe_lockfile.clone(), + ); Self { + fs_installer, fs_resolver, maybe_lockfile, registry_info_provider, @@ -368,6 +385,7 @@ impl ManagedCliNpmResolver { npm_install_deps_provider, text_only_progress_bar, resolution, + resolution_installer, sys, tarball_cache, npm_system_info, @@ -489,7 +507,7 @@ impl ManagedCliNpmResolver { }; } - let mut result = self.resolution.add_package_reqs(packages).await; + let mut result = self.resolution_installer.add_package_reqs(packages).await; if result.dependencies_result.is_ok() { if let Some(lockfile) = self.maybe_lockfile.as_ref() { @@ -512,7 +530,7 @@ impl ManagedCliNpmResolver { &self, packages: &[PackageReq], ) -> Result<(), AnyError> { - self.resolution.set_package_reqs(packages).await + self.resolution_installer.set_package_reqs(packages).await } pub fn snapshot(&self) -> NpmResolutionSnapshot { @@ -554,7 +572,7 @@ impl ManagedCliNpmResolver { &self, caching: PackageCaching<'_>, ) -> Result<(), JsErrorBox> { - self.fs_resolver.cache_packages(caching).await + self.fs_installer.cache_packages(caching).await } pub fn resolve_pkg_folder_from_deno_module( @@ -738,14 +756,11 @@ impl CliNpmResolver for ManagedCliNpmResolver { fn clone_snapshotted(&self) -> Arc { // create a new snapshotted npm resolution and resolver - let npm_resolution = Arc::new(NpmResolution::new( - self.registry_info_provider.clone(), - self.resolution.snapshot(), - self.maybe_lockfile.clone(), - )); + let npm_resolution = + Arc::new(NpmResolution::new(self.resolution.snapshot())); Arc::new(ManagedCliNpmResolver::new( - create_npm_fs_resolver( + create_npm_fs_installer( self.npm_cache.clone(), &self.npm_install_deps_provider, &self.text_only_progress_bar, @@ -756,6 +771,12 @@ impl CliNpmResolver for ManagedCliNpmResolver { self.npm_system_info.clone(), self.lifecycle_scripts.clone(), ), + create_npm_fs_resolver( + self.npm_cache.clone(), + npm_resolution.clone(), + self.sys.clone(), + self.root_node_modules_path().map(ToOwned::to_owned), + ), self.maybe_lockfile.clone(), self.registry_info_provider.clone(), self.npm_cache.clone(), diff --git a/cli/npm/managed/resolution.rs b/cli/npm/managed/resolution.rs index 8259062c05..5178c20608 100644 --- a/cli/npm/managed/resolution.rs +++ b/cli/npm/managed/resolution.rs @@ -1,18 +1,9 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; -use std::collections::HashSet; -use std::sync::Arc; -use capacity_builder::StringBuilder; -use deno_core::error::AnyError; -use deno_error::JsErrorBox; -use deno_lockfile::NpmPackageDependencyLockfileInfo; -use deno_lockfile::NpmPackageLockfileInfo; -use deno_npm::registry::NpmRegistryApi; -use deno_npm::resolution::AddPkgReqsOptions; +use deno_core::parking_lot::RwLock; use deno_npm::resolution::NpmPackagesPartitioned; -use deno_npm::resolution::NpmResolutionError; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::resolution::PackageCacheFolderIdNotFoundError; use deno_npm::resolution::PackageNotFoundFromReferrerError; @@ -23,25 +14,8 @@ use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; -use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; -use deno_semver::SmallStackString; -use deno_semver::VersionReq; - -use crate::args::CliLockfile; -use crate::npm::CliNpmRegistryInfoProvider; -use crate::util::sync::SyncReadAsyncWriteLock; - -pub struct AddPkgReqsResult { - /// Results from adding the individual packages. - /// - /// The indexes of the results correspond to the indexes of the provided - /// package requirements. - pub results: Vec>, - /// The final result of resolving and caching all the package requirements. - pub dependencies_result: Result<(), JsErrorBox>, -} /// Handles updating and storing npm resolution in memory where the underlying /// snapshot can be updated concurrently. Additionally handles updating the lockfile @@ -49,9 +23,7 @@ pub struct AddPkgReqsResult { /// /// This does not interact with the file system. pub struct NpmResolution { - registry_info_provider: Arc, - snapshot: SyncReadAsyncWriteLock, - maybe_lockfile: Option>, + snapshot: RwLock, } impl std::fmt::Debug for NpmResolution { @@ -65,87 +37,19 @@ impl std::fmt::Debug for NpmResolution { impl NpmResolution { pub fn from_serialized( - registry_info_provider: Arc, initial_snapshot: Option, - maybe_lockfile: Option>, ) -> Self { let snapshot = NpmResolutionSnapshot::new(initial_snapshot.unwrap_or_default()); - Self::new(registry_info_provider, snapshot, maybe_lockfile) + Self::new(snapshot) } - pub fn new( - registry_info_provider: Arc, - initial_snapshot: NpmResolutionSnapshot, - maybe_lockfile: Option>, - ) -> Self { + pub fn new(initial_snapshot: NpmResolutionSnapshot) -> Self { Self { - registry_info_provider, - snapshot: SyncReadAsyncWriteLock::new(initial_snapshot), - maybe_lockfile, + snapshot: RwLock::new(initial_snapshot), } } - pub async fn add_package_reqs( - &self, - package_reqs: &[PackageReq], - ) -> AddPkgReqsResult { - // only allow one thread in here at a time - let snapshot_lock = self.snapshot.acquire().await; - let result = add_package_reqs_to_snapshot( - &self.registry_info_provider, - package_reqs, - self.maybe_lockfile.clone(), - || snapshot_lock.read().clone(), - ) - .await; - - AddPkgReqsResult { - results: result.results, - dependencies_result: match result.dep_graph_result { - Ok(snapshot) => { - *snapshot_lock.write() = snapshot; - Ok(()) - } - Err(err) => Err(JsErrorBox::from_err(err)), - }, - } - } - - pub async fn set_package_reqs( - &self, - package_reqs: &[PackageReq], - ) -> Result<(), AnyError> { - // only allow one thread in here at a time - let snapshot_lock = self.snapshot.acquire().await; - - let reqs_set = package_reqs.iter().collect::>(); - let snapshot = add_package_reqs_to_snapshot( - &self.registry_info_provider, - package_reqs, - self.maybe_lockfile.clone(), - || { - let snapshot = snapshot_lock.read().clone(); - let has_removed_package = !snapshot - .package_reqs() - .keys() - .all(|req| reqs_set.contains(req)); - // if any packages were removed, we need to completely recreate the npm resolution snapshot - if has_removed_package { - snapshot.into_empty() - } else { - snapshot - } - }, - ) - .await - .into_result()?; - - *snapshot_lock.write() = snapshot; - - Ok(()) - } - pub fn resolve_pkg_cache_folder_id_from_pkg_id( &self, id: &NpmPackageId, @@ -262,112 +166,8 @@ impl NpmResolution { pub fn subset(&self, package_reqs: &[PackageReq]) -> NpmResolutionSnapshot { self.snapshot.read().subset(package_reqs) } -} -async fn add_package_reqs_to_snapshot( - registry_info_provider: &Arc, - package_reqs: &[PackageReq], - maybe_lockfile: Option>, - get_new_snapshot: impl Fn() -> NpmResolutionSnapshot, -) -> deno_npm::resolution::AddPkgReqsResult { - let snapshot = get_new_snapshot(); - if package_reqs - .iter() - .all(|req| snapshot.package_reqs().contains_key(req)) - { - log::debug!("Snapshot already up to date. Skipping npm resolution."); - return deno_npm::resolution::AddPkgReqsResult { - results: package_reqs - .iter() - .map(|req| Ok(snapshot.package_reqs().get(req).unwrap().clone())) - .collect(), - dep_graph_result: Ok(snapshot), - }; - } - log::debug!( - /* this string is used in tests */ - "Running npm resolution." - ); - let npm_registry_api = registry_info_provider.as_npm_registry_api(); - let result = snapshot - .add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs)) - .await; - let result = match &result.dep_graph_result { - Err(NpmResolutionError::Resolution(err)) - if npm_registry_api.mark_force_reload() => - { - log::debug!("{err:#}"); - log::debug!("npm resolution failed. Trying again..."); - - // try again with forced reloading - let snapshot = get_new_snapshot(); - snapshot - .add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs)) - .await - } - _ => result, - }; - - registry_info_provider.clear_memory_cache(); - - if let Ok(snapshot) = &result.dep_graph_result { - if let Some(lockfile) = maybe_lockfile { - populate_lockfile_from_snapshot(&lockfile, snapshot); - } - } - - result -} - -fn get_add_pkg_reqs_options(package_reqs: &[PackageReq]) -> AddPkgReqsOptions { - AddPkgReqsOptions { - package_reqs, - // WARNING: When bumping this version, check if anything needs to be - // updated in the `setNodeOnlyGlobalNames` call in 99_main_compiler.js - types_node_version_req: Some( - VersionReq::parse_from_npm("22.0.0 - 22.5.4").unwrap(), - ), - } -} - -fn populate_lockfile_from_snapshot( - lockfile: &CliLockfile, - snapshot: &NpmResolutionSnapshot, -) { - let mut lockfile = lockfile.lock(); - for (package_req, nv) in snapshot.package_reqs() { - let id = &snapshot.resolve_package_from_deno_module(nv).unwrap().id; - lockfile.insert_package_specifier( - JsrDepPackageReq::npm(package_req.clone()), - { - StringBuilder::::build(|builder| { - builder.append(&id.nv.version); - builder.append(&id.peer_dependencies); - }) - .unwrap() - }, - ); - } - for package in snapshot.all_packages_for_every_system() { - lockfile.insert_npm_package(npm_package_to_lockfile_info(package)); - } -} - -fn npm_package_to_lockfile_info( - pkg: &NpmResolutionPackage, -) -> NpmPackageLockfileInfo { - let dependencies = pkg - .dependencies - .iter() - .map(|(name, id)| NpmPackageDependencyLockfileInfo { - name: name.clone(), - id: id.as_serialized(), - }) - .collect(); - - NpmPackageLockfileInfo { - serialized_id: pkg.id.as_serialized(), - integrity: pkg.dist.integrity().for_lockfile(), - dependencies, + pub fn set_snapshot(&self, snapshot: NpmResolutionSnapshot) { + *self.snapshot.write() = snapshot; } } diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs index 0d5fab10d3..be0b40fa2a 100644 --- a/cli/npm/managed/resolvers/common.rs +++ b/cli/npm/managed/resolvers/common.rs @@ -1,20 +1,14 @@ // Copyright 2018-2025 the Deno authors. MIT license. -pub mod bin_entries; -pub mod lifecycle_scripts; - use std::path::Path; use std::path::PathBuf; use async_trait::async_trait; use deno_ast::ModuleSpecifier; -use deno_error::JsErrorBox; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use node_resolver::errors::PackageFolderResolveError; -use super::super::PackageCaching; - #[derive(Debug, thiserror::Error, deno_error::JsError)] #[class(generic)] #[error("Package folder not found for '{0}'")] @@ -47,9 +41,4 @@ pub trait NpmPackageFsResolver: Send + Sync { &self, specifier: &ModuleSpecifier, ) -> Result, std::io::Error>; - - async fn cache_packages<'a>( - &self, - caching: PackageCaching<'a>, - ) -> Result<(), JsErrorBox>; } diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs index 9af35b169d..d63d3a7e45 100644 --- a/cli/npm/managed/resolvers/global.rs +++ b/cli/npm/managed/resolvers/global.rs @@ -2,59 +2,32 @@ //! Code for global npm cache resolution. -use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use async_trait::async_trait; use deno_ast::ModuleSpecifier; -use deno_core::futures::stream::FuturesUnordered; -use deno_core::futures::StreamExt; -use deno_error::JsErrorBox; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_npm::NpmSystemInfo; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::ReferrerNotFoundError; use super::super::resolution::NpmResolution; -use super::common::lifecycle_scripts::LifecycleScriptsStrategy; use super::common::NpmPackageFsResolver; -use crate::args::LifecycleScriptsConfig; -use crate::cache::FastInsecureHasher; -use crate::colors; -use crate::npm::managed::PackageCaching; use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; /// Resolves packages from the global npm cache. #[derive(Debug)] pub struct GlobalNpmPackageResolver { cache: Arc, - tarball_cache: Arc, resolution: Arc, - system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, } impl GlobalNpmPackageResolver { - pub fn new( - cache: Arc, - tarball_cache: Arc, - resolution: Arc, - system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, - ) -> Self { - Self { - cache, - tarball_cache, - resolution, - system_info, - lifecycle_scripts, - } + pub fn new(cache: Arc, resolution: Arc) -> Self { + Self { cache, resolution } } } @@ -141,140 +114,4 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { .resolve_package_folder_id_from_specifier(specifier), ) } - - async fn cache_packages<'a>( - &self, - caching: PackageCaching<'a>, - ) -> Result<(), JsErrorBox> { - let package_partitions = match caching { - PackageCaching::All => self - .resolution - .all_system_packages_partitioned(&self.system_info), - PackageCaching::Only(reqs) => self - .resolution - .subset(&reqs) - .all_system_packages_partitioned(&self.system_info), - }; - cache_packages(&package_partitions.packages, &self.tarball_cache) - .await - .map_err(JsErrorBox::from_err)?; - - // create the copy package folders - for copy in package_partitions.copy_packages { - self - .cache - .ensure_copy_package(©.get_package_cache_folder_id()) - .map_err(JsErrorBox::from_err)?; - } - - let mut lifecycle_scripts = - super::common::lifecycle_scripts::LifecycleScripts::new( - &self.lifecycle_scripts, - GlobalLifecycleScripts::new(self, &self.lifecycle_scripts.root_dir), - ); - for package in &package_partitions.packages { - let package_folder = self.cache.package_folder_for_nv(&package.id.nv); - lifecycle_scripts.add(package, Cow::Borrowed(&package_folder)); - } - - lifecycle_scripts - .warn_not_run_scripts() - .map_err(JsErrorBox::from_err)?; - - Ok(()) - } -} - -async fn cache_packages( - packages: &[NpmResolutionPackage], - tarball_cache: &Arc, -) -> Result<(), deno_npm_cache::EnsurePackageError> { - let mut futures_unordered = FuturesUnordered::new(); - for package in packages { - futures_unordered.push(async move { - tarball_cache - .ensure_package(&package.id.nv, &package.dist) - .await - }); - } - while let Some(result) = futures_unordered.next().await { - // surface the first error - result?; - } - Ok(()) -} - -struct GlobalLifecycleScripts<'a> { - resolver: &'a GlobalNpmPackageResolver, - path_hash: u64, -} - -impl<'a> GlobalLifecycleScripts<'a> { - fn new(resolver: &'a GlobalNpmPackageResolver, root_dir: &Path) -> Self { - let mut hasher = FastInsecureHasher::new_without_deno_version(); - hasher.write(root_dir.to_string_lossy().as_bytes()); - let path_hash = hasher.finish(); - Self { - resolver, - path_hash, - } - } - - fn warned_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf { - self - .package_path(package) - .join(format!(".scripts-warned-{}", self.path_hash)) - } -} - -impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy - for GlobalLifecycleScripts<'a> -{ - fn can_run_scripts(&self) -> bool { - false - } - fn package_path(&self, package: &NpmResolutionPackage) -> PathBuf { - self.resolver.cache.package_folder_for_nv(&package.id.nv) - } - - fn warn_on_scripts_not_run( - &self, - packages: &[(&NpmResolutionPackage, PathBuf)], - ) -> std::result::Result<(), std::io::Error> { - log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall")); - for (package, _) in packages { - log::warn!("┠─ {}", colors::gray(format!("npm:{}", package.id.nv))); - } - log::warn!("┃"); - log::warn!( - "┠─ {}", - colors::italic("This may cause the packages to not work correctly.") - ); - log::warn!("┠─ {}", colors::italic("Lifecycle scripts are only supported when using a `node_modules` directory.")); - log::warn!( - "┠─ {}", - colors::italic("Enable it in your deno config file:") - ); - log::warn!("┖─ {}", colors::bold("\"nodeModulesDir\": \"auto\"")); - - for (package, _) in packages { - std::fs::write(self.warned_scripts_file(package), "")?; - } - Ok(()) - } - - fn did_run_scripts( - &self, - _package: &NpmResolutionPackage, - ) -> Result<(), std::io::Error> { - Ok(()) - } - - fn has_warned(&self, package: &NpmResolutionPackage) -> bool { - self.warned_scripts_file(package).exists() - } - - fn has_run(&self, _package: &NpmResolutionPackage) -> bool { - false - } } diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index e1ac3df43b..52d5ac5a9e 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -3,33 +3,16 @@ //! Code for local node_modules resolution. use std::borrow::Cow; -use std::cell::RefCell; -use std::cmp::Ordering; -use std::collections::hash_map::Entry; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::collections::HashSet; -use std::fs; use std::path::Path; use std::path::PathBuf; -use std::rc::Rc; use std::sync::Arc; use async_trait::async_trait; use deno_ast::ModuleSpecifier; use deno_cache_dir::npm::mixed_case_package_name_decode; -use deno_core::futures::stream::FuturesUnordered; -use deno_core::futures::StreamExt; -use deno_core::parking_lot::Mutex; use deno_core::url::Url; -use deno_error::JsErrorBox; -use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_npm::NpmSystemInfo; -use deno_path_util::fs::atomic_write_file_with_retries; use deno_path_util::fs::canonicalize_path_maybe_not_exists; use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder; use deno_semver::package::PackageNv; @@ -38,68 +21,35 @@ use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::ReferrerNotFoundError; -use serde::Deserialize; -use serde::Serialize; use sys_traits::FsMetadata; use super::super::resolution::NpmResolution; -use super::common::bin_entries; use super::common::NpmPackageFsResolver; -use crate::args::LifecycleScriptsConfig; -use crate::args::NpmInstallDepsProvider; -use crate::cache::CACHE_PERM; -use crate::colors; -use crate::npm::managed::PackageCaching; -use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; use crate::sys::CliSys; -use crate::util::fs::clone_dir_recursive; -use crate::util::fs::symlink_dir; -use crate::util::fs::LaxSingleProcessFsFlag; -use crate::util::progress_bar::ProgressBar; -use crate::util::progress_bar::ProgressMessagePrompt; /// Resolver that creates a local node_modules directory /// and resolves packages from it. #[derive(Debug)] pub struct LocalNpmPackageResolver { - cache: Arc, - npm_install_deps_provider: Arc, - progress_bar: ProgressBar, resolution: Arc, sys: CliSys, - tarball_cache: Arc, root_node_modules_path: PathBuf, root_node_modules_url: Url, - system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, } impl LocalNpmPackageResolver { #[allow(clippy::too_many_arguments)] pub fn new( - cache: Arc, - npm_install_deps_provider: Arc, - progress_bar: ProgressBar, resolution: Arc, sys: CliSys, - tarball_cache: Arc, node_modules_folder: PathBuf, - system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, ) -> Self { Self { - cache, - npm_install_deps_provider, - progress_bar, resolution, - tarball_cache, sys, root_node_modules_url: Url::from_directory_path(&node_modules_folder) .unwrap(), root_node_modules_path: node_modules_folder, - system_info, - lifecycle_scripts, } } @@ -246,809 +196,9 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { &folder_name.to_string_lossy(), )) } - - async fn cache_packages<'a>( - &self, - caching: PackageCaching<'a>, - ) -> Result<(), JsErrorBox> { - let snapshot = match caching { - PackageCaching::All => self.resolution.snapshot(), - PackageCaching::Only(reqs) => self.resolution.subset(&reqs), - }; - sync_resolution_with_fs( - &snapshot, - &self.cache, - &self.npm_install_deps_provider, - &self.progress_bar, - &self.tarball_cache, - &self.root_node_modules_path, - &self.system_info, - &self.lifecycle_scripts, - ) - .await - .map_err(JsErrorBox::from_err) - } } -/// `node_modules/.deno//node_modules/` -/// -/// Where the actual package is stored. -fn local_node_modules_package_contents_path( - local_registry_dir: &Path, - package: &NpmResolutionPackage, -) -> PathBuf { - local_registry_dir - .join(get_package_folder_id_folder_name( - &package.get_package_cache_folder_id(), - )) - .join("node_modules") - .join(&package.id.nv.name) -} - -#[derive(Debug, thiserror::Error, deno_error::JsError)] -pub enum SyncResolutionWithFsError { - #[class(inherit)] - #[error("Creating '{path}'")] - Creating { - path: PathBuf, - #[source] - #[inherit] - source: std::io::Error, - }, - #[class(inherit)] - #[error(transparent)] - CopyDirRecursive(#[from] crate::util::fs::CopyDirRecursiveError), - #[class(inherit)] - #[error(transparent)] - SymlinkPackageDir(#[from] SymlinkPackageDirError), - #[class(inherit)] - #[error(transparent)] - BinEntries(#[from] bin_entries::BinEntriesError), - #[class(inherit)] - #[error(transparent)] - LifecycleScripts( - #[from] super::common::lifecycle_scripts::LifecycleScriptsError, - ), - #[class(inherit)] - #[error(transparent)] - Io(#[from] std::io::Error), - #[class(inherit)] - #[error(transparent)] - Other(#[from] JsErrorBox), -} - -/// Creates a pnpm style folder structure. -#[allow(clippy::too_many_arguments)] -async fn sync_resolution_with_fs( - snapshot: &NpmResolutionSnapshot, - cache: &Arc, - npm_install_deps_provider: &NpmInstallDepsProvider, - progress_bar: &ProgressBar, - tarball_cache: &Arc, - root_node_modules_dir_path: &Path, - system_info: &NpmSystemInfo, - lifecycle_scripts: &LifecycleScriptsConfig, -) -> Result<(), SyncResolutionWithFsError> { - if snapshot.is_empty() - && npm_install_deps_provider.workspace_pkgs().is_empty() - { - return Ok(()); // don't create the directory - } - - // don't set up node_modules (and more importantly try to acquire the file lock) - // if we're running as part of a lifecycle script - if super::common::lifecycle_scripts::is_running_lifecycle_script() { - return Ok(()); - } - - let deno_local_registry_dir = root_node_modules_dir_path.join(".deno"); - let deno_node_modules_dir = deno_local_registry_dir.join("node_modules"); - fs::create_dir_all(&deno_node_modules_dir).map_err(|source| { - SyncResolutionWithFsError::Creating { - path: deno_local_registry_dir.to_path_buf(), - source, - } - })?; - let bin_node_modules_dir_path = root_node_modules_dir_path.join(".bin"); - fs::create_dir_all(&bin_node_modules_dir_path).map_err(|source| { - SyncResolutionWithFsError::Creating { - path: deno_local_registry_dir.to_path_buf(), - source, - } - })?; - - let single_process_lock = LaxSingleProcessFsFlag::lock( - deno_local_registry_dir.join(".deno.lock"), - // similar message used by cargo build - "waiting for file lock on node_modules directory", - ) - .await; - - // load this after we get the directory lock - let mut setup_cache = - SetupCache::load(deno_local_registry_dir.join(".setup-cache.bin")); - - let pb_clear_guard = progress_bar.clear_guard(); // prevent flickering - - // 1. Write all the packages out the .deno directory. - // - // Copy (hardlink in future) // to - // node_modules/.deno//node_modules/ - let package_partitions = - snapshot.all_system_packages_partitioned(system_info); - let mut cache_futures = FuturesUnordered::new(); - let mut newest_packages_by_name: HashMap< - &StackString, - &NpmResolutionPackage, - > = HashMap::with_capacity(package_partitions.packages.len()); - let bin_entries = Rc::new(RefCell::new(bin_entries::BinEntries::new())); - let mut lifecycle_scripts = - super::common::lifecycle_scripts::LifecycleScripts::new( - lifecycle_scripts, - LocalLifecycleScripts { - deno_local_registry_dir: &deno_local_registry_dir, - }, - ); - let packages_with_deprecation_warnings = Arc::new(Mutex::new(Vec::new())); - - let mut package_tags: HashMap<&PackageNv, BTreeSet<&str>> = HashMap::new(); - for (package_req, package_nv) in snapshot.package_reqs() { - if let Some(tag) = package_req.version_req.tag() { - package_tags.entry(package_nv).or_default().insert(tag); - } - } - - for package in &package_partitions.packages { - if let Some(current_pkg) = - newest_packages_by_name.get_mut(&package.id.nv.name) - { - if current_pkg.id.nv.cmp(&package.id.nv) == Ordering::Less { - *current_pkg = package; - } - } else { - newest_packages_by_name.insert(&package.id.nv.name, package); - }; - - let package_folder_name = - get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); - let folder_path = deno_local_registry_dir.join(&package_folder_name); - let tags = package_tags - .get(&package.id.nv) - .map(|tags| { - capacity_builder::StringBuilder::::build(|builder| { - for (i, tag) in tags.iter().enumerate() { - if i > 0 { - builder.append(',') - } - builder.append(*tag); - } - }) - .unwrap() - }) - .unwrap_or_default(); - enum PackageFolderState { - UpToDate, - Uninitialized, - TagsOutdated, - } - let initialized_file = folder_path.join(".initialized"); - let package_state = std::fs::read_to_string(&initialized_file) - .map(|s| { - if s != tags { - PackageFolderState::TagsOutdated - } else { - PackageFolderState::UpToDate - } - }) - .unwrap_or(PackageFolderState::Uninitialized); - if !cache - .cache_setting() - .should_use_for_npm_package(&package.id.nv.name) - || matches!(package_state, PackageFolderState::Uninitialized) - { - // cache bust the dep from the dep setup cache so the symlinks - // are forced to be recreated - setup_cache.remove_dep(&package_folder_name); - - let folder_path = folder_path.clone(); - let bin_entries_to_setup = bin_entries.clone(); - let packages_with_deprecation_warnings = - packages_with_deprecation_warnings.clone(); - - cache_futures.push(async move { - tarball_cache - .ensure_package(&package.id.nv, &package.dist) - .await - .map_err(JsErrorBox::from_err)?; - let pb_guard = progress_bar.update_with_prompt( - ProgressMessagePrompt::Initialize, - &package.id.nv.to_string(), - ); - let sub_node_modules = folder_path.join("node_modules"); - let package_path = - join_package_name(&sub_node_modules, &package.id.nv.name); - let cache_folder = cache.package_folder_for_nv(&package.id.nv); - - deno_core::unsync::spawn_blocking({ - let package_path = package_path.clone(); - move || { - clone_dir_recursive( - &crate::sys::CliSys::default(), - &cache_folder, - &package_path, - )?; - // write out a file that indicates this folder has been initialized - fs::write(initialized_file, tags)?; - - Ok::<_, SyncResolutionWithFsError>(()) - } - }) - .await - .map_err(JsErrorBox::from_err)? - .map_err(JsErrorBox::from_err)?; - - if package.bin.is_some() { - bin_entries_to_setup.borrow_mut().add(package, package_path); - } - - if let Some(deprecated) = &package.deprecated { - packages_with_deprecation_warnings - .lock() - .push((package.id.clone(), deprecated.clone())); - } - - // finally stop showing the progress bar - drop(pb_guard); // explicit for clarity - Ok::<_, JsErrorBox>(()) - }); - } else if matches!(package_state, PackageFolderState::TagsOutdated) { - fs::write(initialized_file, tags)?; - } - - let sub_node_modules = folder_path.join("node_modules"); - let package_path = - join_package_name(&sub_node_modules, &package.id.nv.name); - lifecycle_scripts.add(package, package_path.into()); - } - - while let Some(result) = cache_futures.next().await { - result?; // surface the first error - } - - // 2. Create any "copy" packages, which are used for peer dependencies - for package in &package_partitions.copy_packages { - let package_cache_folder_id = package.get_package_cache_folder_id(); - let destination_path = deno_local_registry_dir - .join(get_package_folder_id_folder_name(&package_cache_folder_id)); - let initialized_file = destination_path.join(".initialized"); - if !initialized_file.exists() { - let sub_node_modules = destination_path.join("node_modules"); - let package_path = - join_package_name(&sub_node_modules, &package.id.nv.name); - - let source_path = join_package_name( - &deno_local_registry_dir - .join(get_package_folder_id_folder_name( - &package_cache_folder_id.with_no_count(), - )) - .join("node_modules"), - &package.id.nv.name, - ); - - clone_dir_recursive( - &crate::sys::CliSys::default(), - &source_path, - &package_path, - )?; - // write out a file that indicates this folder has been initialized - fs::write(initialized_file, "")?; - } - } - - // 3. Symlink all the dependencies into the .deno directory. - // - // Symlink node_modules/.deno//node_modules/ to - // node_modules/.deno//node_modules/ - for package in package_partitions.iter_all() { - let package_folder_name = - get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); - let sub_node_modules = deno_local_registry_dir - .join(&package_folder_name) - .join("node_modules"); - let mut dep_setup_cache = setup_cache.with_dep(&package_folder_name); - for (name, dep_id) in &package.dependencies { - let dep = snapshot.package_from_id(dep_id).unwrap(); - if package.optional_dependencies.contains(name) - && !dep.system.matches_system(system_info) - { - continue; // this isn't a dependency for the current system - } - let dep_cache_folder_id = dep.get_package_cache_folder_id(); - let dep_folder_name = - get_package_folder_id_folder_name(&dep_cache_folder_id); - if dep_setup_cache.insert(name, &dep_folder_name) { - let dep_folder_path = join_package_name( - &deno_local_registry_dir - .join(dep_folder_name) - .join("node_modules"), - &dep_id.nv.name, - ); - symlink_package_dir( - &dep_folder_path, - &join_package_name(&sub_node_modules, name), - )?; - } - } - } - - let mut found_names: HashMap<&StackString, &PackageNv> = HashMap::new(); - - // set of node_modules in workspace packages that we've already ensured exist - let mut existing_child_node_modules_dirs: HashSet = HashSet::new(); - - // 4. Create symlinks for package json dependencies - { - for remote in npm_install_deps_provider.remote_pkgs() { - let remote_pkg = if let Ok(remote_pkg) = - snapshot.resolve_pkg_from_pkg_req(&remote.req) - { - remote_pkg - } else if remote.req.version_req.tag().is_some() { - // couldn't find a match, and `resolve_best_package_id` - // panics if you give it a tag - continue; - } else if let Some(remote_id) = snapshot - .resolve_best_package_id(&remote.req.name, &remote.req.version_req) - { - snapshot.package_from_id(&remote_id).unwrap() - } else { - continue; // skip, package not found - }; - let Some(remote_alias) = &remote.alias else { - continue; - }; - let alias_clashes = remote.req.name != *remote_alias - && newest_packages_by_name.contains_key(remote_alias); - let install_in_child = { - // we'll install in the child if the alias is taken by another package, or - // if there's already a package with the same name but different version - // linked into the root - match found_names.entry(remote_alias) { - Entry::Occupied(nv) => { - // alias to a different package (in case of duplicate aliases) - // or the version doesn't match the version in the root node_modules - alias_clashes || &remote_pkg.id.nv != *nv.get() - } - Entry::Vacant(entry) => { - entry.insert(&remote_pkg.id.nv); - alias_clashes - } - } - }; - let target_folder_name = get_package_folder_id_folder_name( - &remote_pkg.get_package_cache_folder_id(), - ); - let local_registry_package_path = join_package_name( - &deno_local_registry_dir - .join(&target_folder_name) - .join("node_modules"), - &remote_pkg.id.nv.name, - ); - if install_in_child { - // symlink the dep into the package's child node_modules folder - let dest_node_modules = remote.base_dir.join("node_modules"); - if !existing_child_node_modules_dirs.contains(&dest_node_modules) { - fs::create_dir_all(&dest_node_modules).map_err(|source| { - SyncResolutionWithFsError::Creating { - path: dest_node_modules.clone(), - source, - } - })?; - existing_child_node_modules_dirs.insert(dest_node_modules.clone()); - } - let mut dest_path = dest_node_modules; - dest_path.push(remote_alias); - - symlink_package_dir(&local_registry_package_path, &dest_path)?; - } else { - // symlink the package into `node_modules/` - if setup_cache - .insert_root_symlink(&remote_pkg.id.nv.name, &target_folder_name) - { - symlink_package_dir( - &local_registry_package_path, - &join_package_name(root_node_modules_dir_path, remote_alias), - )?; - } - } - } - } - - // 5. Create symlinks for the remaining top level packages in the node_modules folder. - // (These may be present if they are not in the package.json dependencies) - // Symlink node_modules/.deno//node_modules/ to - // node_modules/ - let mut ids = snapshot - .top_level_packages() - .filter(|f| !found_names.contains_key(&f.nv.name)) - .collect::>(); - ids.sort_by(|a, b| b.cmp(a)); // create determinism and only include the latest version - for id in ids { - match found_names.entry(&id.nv.name) { - Entry::Occupied(_) => { - continue; // skip, already handled - } - Entry::Vacant(entry) => { - entry.insert(&id.nv); - } - } - let package = snapshot.package_from_id(id).unwrap(); - let target_folder_name = - get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); - if setup_cache.insert_root_symlink(&id.nv.name, &target_folder_name) { - let local_registry_package_path = join_package_name( - &deno_local_registry_dir - .join(target_folder_name) - .join("node_modules"), - &id.nv.name, - ); - - symlink_package_dir( - &local_registry_package_path, - &join_package_name(root_node_modules_dir_path, &id.nv.name), - )?; - } - } - - // 6. Create a node_modules/.deno/node_modules/ directory with - // the remaining packages - for package in newest_packages_by_name.values() { - match found_names.entry(&package.id.nv.name) { - Entry::Occupied(_) => { - continue; // skip, already handled - } - Entry::Vacant(entry) => { - entry.insert(&package.id.nv); - } - } - - let target_folder_name = - get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); - if setup_cache.insert_deno_symlink(&package.id.nv.name, &target_folder_name) - { - let local_registry_package_path = join_package_name( - &deno_local_registry_dir - .join(target_folder_name) - .join("node_modules"), - &package.id.nv.name, - ); - - symlink_package_dir( - &local_registry_package_path, - &join_package_name(&deno_node_modules_dir, &package.id.nv.name), - )?; - } - } - - // 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, - |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 - { - // todo(dsherret): this is not exactly correct because it should - // install correctly for a workspace (potentially in sub directories), - // but this is good enough for a first pass - for workspace in npm_install_deps_provider.workspace_pkgs() { - let Some(workspace_alias) = &workspace.alias else { - continue; - }; - symlink_package_dir( - &workspace.target_dir, - &root_node_modules_dir_path.join(workspace_alias), - )?; - } - } - - { - let packages_with_deprecation_warnings = - packages_with_deprecation_warnings.lock(); - if !packages_with_deprecation_warnings.is_empty() { - log::warn!( - "{} The following packages are deprecated:", - colors::yellow("Warning") - ); - let len = packages_with_deprecation_warnings.len(); - for (idx, (package_id, msg)) in - packages_with_deprecation_warnings.iter().enumerate() - { - if idx != len - 1 { - log::warn!( - "┠─ {}", - colors::gray(format!("npm:{:?} ({})", package_id, msg)) - ); - } else { - log::warn!( - "┖─ {}", - colors::gray(format!("npm:{:?} ({})", package_id, msg)) - ); - } - } - } - } - - lifecycle_scripts - .finish( - snapshot, - &package_partitions.packages, - root_node_modules_dir_path, - progress_bar, - ) - .await?; - - setup_cache.save(); - drop(single_process_lock); - drop(pb_clear_guard); - - Ok(()) -} - -/// `node_modules/.deno//` -fn local_node_modules_package_folder( - local_registry_dir: &Path, - package: &NpmResolutionPackage, -) -> PathBuf { - local_registry_dir.join(get_package_folder_id_folder_name( - &package.get_package_cache_folder_id(), - )) -} - -struct LocalLifecycleScripts<'a> { - deno_local_registry_dir: &'a Path, -} - -impl<'a> LocalLifecycleScripts<'a> { - /// `node_modules/.deno//.scripts-run` - fn ran_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf { - local_node_modules_package_folder(self.deno_local_registry_dir, package) - .join(".scripts-run") - } - - /// `node_modules/.deno//.scripts-warned` - fn warned_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf { - local_node_modules_package_folder(self.deno_local_registry_dir, package) - .join(".scripts-warned") - } -} - -impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy - for LocalLifecycleScripts<'a> -{ - fn package_path(&self, package: &NpmResolutionPackage) -> PathBuf { - local_node_modules_package_contents_path( - self.deno_local_registry_dir, - package, - ) - } - - fn did_run_scripts( - &self, - package: &NpmResolutionPackage, - ) -> std::result::Result<(), std::io::Error> { - std::fs::write(self.ran_scripts_file(package), "")?; - Ok(()) - } - - fn warn_on_scripts_not_run( - &self, - packages: &[(&NpmResolutionPackage, std::path::PathBuf)], - ) -> Result<(), std::io::Error> { - if !packages.is_empty() { - log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall")); - - for (package, _) in packages { - log::warn!("┠─ {}", colors::gray(format!("npm:{}", package.id.nv))); - } - - log::warn!("┃"); - log::warn!( - "┠─ {}", - colors::italic("This may cause the packages to not work correctly.") - ); - log::warn!("┖─ {}", colors::italic("To run lifecycle scripts, use the `--allow-scripts` flag with `deno install`:")); - let packages_comma_separated = packages - .iter() - .map(|(p, _)| format!("npm:{}", p.id.nv)) - .collect::>() - .join(","); - log::warn!( - " {}", - colors::bold(format!( - "deno install --allow-scripts={}", - packages_comma_separated - )) - ); - - for (package, _) in packages { - let _ignore_err = fs::write(self.warned_scripts_file(package), ""); - } - } - Ok(()) - } - - fn has_warned(&self, package: &NpmResolutionPackage) -> bool { - self.warned_scripts_file(package).exists() - } - - fn has_run(&self, package: &NpmResolutionPackage) -> bool { - self.ran_scripts_file(package).exists() - } -} - -// Uses BTreeMap to preserve the ordering of the elements in memory, to ensure -// the file generated from this datastructure is deterministic. -// See: https://github.com/denoland/deno/issues/24479 -/// Represents a dependency at `node_modules/.deno//` -struct SetupCacheDep<'a> { - previous: Option<&'a BTreeMap>, - current: &'a mut BTreeMap, -} - -impl<'a> SetupCacheDep<'a> { - pub fn insert(&mut self, name: &str, target_folder_name: &str) -> bool { - self - .current - .insert(name.to_string(), target_folder_name.to_string()); - if let Some(previous_target) = self.previous.and_then(|p| p.get(name)) { - previous_target != target_folder_name - } else { - true - } - } -} - -// Uses BTreeMap to preserve the ordering of the elements in memory, to ensure -// the file generated from this datastructure is deterministic. -// See: https://github.com/denoland/deno/issues/24479 -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)] -struct SetupCacheData { - root_symlinks: BTreeMap, - deno_symlinks: BTreeMap, - dep_symlinks: BTreeMap>, -} - -/// It is very slow to try to re-setup the symlinks each time, so this will -/// cache what we've setup on the last run and only update what is necessary. -/// Obviously this could lead to issues if the cache gets out of date with the -/// file system, such as if the user manually deletes a symlink. -struct SetupCache { - file_path: PathBuf, - previous: Option, - current: SetupCacheData, -} - -impl SetupCache { - pub fn load(file_path: PathBuf) -> Self { - let previous = std::fs::read(&file_path) - .ok() - .and_then(|data| bincode::deserialize(&data).ok()); - Self { - file_path, - previous, - current: Default::default(), - } - } - - pub fn save(&self) -> bool { - if let Some(previous) = &self.previous { - if previous == &self.current { - return false; // nothing to save - } - } - - bincode::serialize(&self.current).ok().and_then(|data| { - atomic_write_file_with_retries( - &CliSys::default(), - &self.file_path, - &data, - CACHE_PERM, - ) - .ok() - }); - true - } - - /// Inserts and checks for the existence of a root symlink - /// at `node_modules/` pointing to - /// `node_modules/.deno//` - pub fn insert_root_symlink( - &mut self, - name: &str, - target_folder_name: &str, - ) -> bool { - self - .current - .root_symlinks - .insert(name.to_string(), target_folder_name.to_string()); - if let Some(previous_target) = self - .previous - .as_ref() - .and_then(|p| p.root_symlinks.get(name)) - { - previous_target != target_folder_name - } else { - true - } - } - - /// Inserts and checks for the existence of a symlink at - /// `node_modules/.deno/node_modules/` pointing to - /// `node_modules/.deno//` - pub fn insert_deno_symlink( - &mut self, - name: &str, - target_folder_name: &str, - ) -> bool { - self - .current - .deno_symlinks - .insert(name.to_string(), target_folder_name.to_string()); - if let Some(previous_target) = self - .previous - .as_ref() - .and_then(|p| p.deno_symlinks.get(name)) - { - previous_target != target_folder_name - } else { - true - } - } - - pub fn remove_dep(&mut self, parent_name: &str) { - if let Some(previous) = &mut self.previous { - previous.dep_symlinks.remove(parent_name); - } - } - - pub fn with_dep(&mut self, parent_name: &str) -> SetupCacheDep<'_> { - SetupCacheDep { - previous: self - .previous - .as_ref() - .and_then(|p| p.dep_symlinks.get(parent_name)), - current: self - .current - .dep_symlinks - .entry(parent_name.to_string()) - .or_default(), - } - } -} - -fn get_package_folder_id_folder_name( +pub fn get_package_folder_id_folder_name( folder_id: &NpmPackageCacheFolderId, ) -> String { let copy_str = if folder_id.copy_index == 0 { @@ -1085,105 +235,6 @@ fn get_package_folder_id_from_folder_name( }) } -#[derive(Debug, thiserror::Error, deno_error::JsError)] -pub enum SymlinkPackageDirError { - #[class(inherit)] - #[error("Creating '{parent}'")] - Creating { - parent: PathBuf, - #[source] - #[inherit] - source: std::io::Error, - }, - #[class(inherit)] - #[error(transparent)] - Other(#[from] std::io::Error), - #[cfg(windows)] - #[class(inherit)] - #[error("Creating junction in node_modules folder")] - FailedCreatingJunction { - #[source] - #[inherit] - source: std::io::Error, - }, -} - -fn symlink_package_dir( - old_path: &Path, - new_path: &Path, -) -> Result<(), SymlinkPackageDirError> { - let new_parent = new_path.parent().unwrap(); - if new_parent.file_name().unwrap() != "node_modules" { - // create the parent folder that will contain the symlink - fs::create_dir_all(new_parent).map_err(|source| { - SymlinkPackageDirError::Creating { - parent: new_parent.to_path_buf(), - source, - } - })?; - } - - // need to delete the previous symlink before creating a new one - let _ignore = fs::remove_dir_all(new_path); - - let old_path_relative = - crate::util::path::relative_path(new_parent, old_path) - .unwrap_or_else(|| old_path.to_path_buf()); - - #[cfg(windows)] - { - junction_or_symlink_dir(&old_path_relative, old_path, new_path) - } - #[cfg(not(windows))] - { - symlink_dir(&crate::sys::CliSys::default(), &old_path_relative, new_path) - .map_err(Into::into) - } -} - -#[cfg(windows)] -fn junction_or_symlink_dir( - old_path_relative: &Path, - old_path: &Path, - new_path: &Path, -) -> Result<(), SymlinkPackageDirError> { - static USE_JUNCTIONS: std::sync::atomic::AtomicBool = - std::sync::atomic::AtomicBool::new(false); - - if USE_JUNCTIONS.load(std::sync::atomic::Ordering::Relaxed) { - // Use junctions because they're supported on ntfs file systems without - // needing to elevate privileges on Windows. - // Note: junctions don't support relative paths, so we need to use the - // absolute path here. - return junction::create(old_path, new_path).map_err(|source| { - SymlinkPackageDirError::FailedCreatingJunction { source } - }); - } - - match symlink_dir(&crate::sys::CliSys::default(), old_path_relative, new_path) - { - Ok(()) => Ok(()), - Err(symlink_err) - if symlink_err.kind() == std::io::ErrorKind::PermissionDenied => - { - USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); - junction::create(old_path, new_path).map_err(|source| { - SymlinkPackageDirError::FailedCreatingJunction { source } - }) - } - Err(symlink_err) => { - log::warn!( - "{} Unexpected error symlinking node_modules: {symlink_err}", - colors::yellow("Warning") - ); - USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); - junction::create(old_path, new_path).map_err(|source| { - SymlinkPackageDirError::FailedCreatingJunction { source } - }) - } - } -} - fn join_package_name(path: &Path, package_name: &str) -> PathBuf { let mut path = path.to_path_buf(); // ensure backslashes are used on windows @@ -1197,7 +248,6 @@ fn join_package_name(path: &Path, package_name: &str) -> PathBuf { mod test { use deno_npm::NpmPackageCacheFolderId; use deno_semver::package::PackageNv; - use test_util::TempDir; use super::*; @@ -1225,33 +275,4 @@ mod test { assert_eq!(folder_id, input); } } - - #[test] - fn test_setup_cache() { - let temp_dir = TempDir::new(); - let cache_bin_path = temp_dir.path().join("cache.bin").to_path_buf(); - let mut cache = SetupCache::load(cache_bin_path.clone()); - assert!(cache.insert_deno_symlink("package-a", "package-a@1.0.0")); - assert!(cache.insert_root_symlink("package-a", "package-a@1.0.0")); - assert!(cache - .with_dep("package-a") - .insert("package-b", "package-b@1.0.0")); - assert!(cache.save()); - - let mut cache = SetupCache::load(cache_bin_path.clone()); - assert!(!cache.insert_deno_symlink("package-a", "package-a@1.0.0")); - assert!(!cache.insert_root_symlink("package-a", "package-a@1.0.0")); - assert!(!cache - .with_dep("package-a") - .insert("package-b", "package-b@1.0.0")); - assert!(!cache.save()); - assert!(cache.insert_root_symlink("package-b", "package-b@0.2.0")); - assert!(cache.save()); - - let mut cache = SetupCache::load(cache_bin_path); - cache.remove_dep("package-a"); - assert!(cache - .with_dep("package-a") - .insert("package-b", "package-b@1.0.0")); - } } diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs index 77d00a896e..8a89208c77 100644 --- a/cli/npm/managed/resolvers/mod.rs +++ b/cli/npm/managed/resolvers/mod.rs @@ -7,50 +7,27 @@ mod local; use std::path::PathBuf; use std::sync::Arc; -use deno_npm::NpmSystemInfo; - pub use self::common::NpmPackageFsResolver; pub use self::common::NpmPackageFsResolverPackageFolderError; use self::global::GlobalNpmPackageResolver; +pub use self::local::get_package_folder_id_folder_name; use self::local::LocalNpmPackageResolver; use super::resolution::NpmResolution; -use crate::args::LifecycleScriptsConfig; -use crate::args::NpmInstallDepsProvider; use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; use crate::sys::CliSys; -use crate::util::progress_bar::ProgressBar; -#[allow(clippy::too_many_arguments)] pub fn create_npm_fs_resolver( npm_cache: Arc, - npm_install_deps_provider: &Arc, - progress_bar: &ProgressBar, resolution: Arc, sys: CliSys, - tarball_cache: Arc, maybe_node_modules_path: Option, - system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, ) -> Arc { match maybe_node_modules_path { Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new( - npm_cache, - npm_install_deps_provider.clone(), - progress_bar.clone(), resolution, sys, - tarball_cache, node_modules_folder, - system_info, - lifecycle_scripts, - )), - None => Arc::new(GlobalNpmPackageResolver::new( - npm_cache, - tarball_cache, - resolution, - system_info, - lifecycle_scripts, )), + None => Arc::new(GlobalNpmPackageResolver::new(npm_cache, resolution)), } } diff --git a/cli/util/sync/mod.rs b/cli/util/sync/mod.rs index 74ec469533..717dcd768a 100644 --- a/cli/util/sync/mod.rs +++ b/cli/util/sync/mod.rs @@ -1,11 +1,9 @@ // Copyright 2018-2025 the Deno authors. MIT license. mod async_flag; -mod sync_read_async_write_lock; mod task_queue; pub use async_flag::AsyncFlag; pub use deno_core::unsync::sync::AtomicFlag; -pub use sync_read_async_write_lock::SyncReadAsyncWriteLock; pub use task_queue::TaskQueue; pub use task_queue::TaskQueuePermit; diff --git a/cli/util/sync/sync_read_async_write_lock.rs b/cli/util/sync/sync_read_async_write_lock.rs deleted file mode 100644 index 48ad6bb863..0000000000 --- a/cli/util/sync/sync_read_async_write_lock.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -use deno_core::parking_lot::RwLock; -use deno_core::parking_lot::RwLockReadGuard; -use deno_core::parking_lot::RwLockWriteGuard; - -use super::TaskQueue; -use super::TaskQueuePermit; - -/// A lock that can be read synchronously at any time (including when -/// being written to), but must write asynchronously. -pub struct SyncReadAsyncWriteLockWriteGuard<'a, T: Send + Sync> { - _update_permit: TaskQueuePermit<'a>, - data: &'a RwLock, -} - -impl<'a, T: Send + Sync> SyncReadAsyncWriteLockWriteGuard<'a, T> { - pub fn read(&self) -> RwLockReadGuard<'_, T> { - self.data.read() - } - - /// Warning: Only `write()` with data you created within this - /// write this `SyncReadAsyncWriteLockWriteGuard`. - /// - /// ```rs - /// let mut data = lock.write().await; - /// - /// let mut data = data.read().clone(); - /// data.value = 2; - /// *data.write() = data; - /// ``` - pub fn write(&self) -> RwLockWriteGuard<'_, T> { - self.data.write() - } -} - -/// A lock that can only be -pub struct SyncReadAsyncWriteLock { - data: RwLock, - update_queue: TaskQueue, -} - -impl SyncReadAsyncWriteLock { - pub fn new(data: T) -> Self { - Self { - data: RwLock::new(data), - update_queue: TaskQueue::default(), - } - } - - pub fn read(&self) -> RwLockReadGuard<'_, T> { - self.data.read() - } - - pub async fn acquire(&self) -> SyncReadAsyncWriteLockWriteGuard<'_, T> { - let update_permit = self.update_queue.acquire().await; - SyncReadAsyncWriteLockWriteGuard { - _update_permit: update_permit, - data: &self.data, - } - } -} From 093f3ba56518aba6e7844c17f9ba1fd09dafc6d1 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 9 Jan 2025 12:10:07 -0500 Subject: [PATCH 083/107] refactor(npm): extract out some npm fs resolution code from the cli (#27607) Moves the npm fs resolvers into the deno_resolution crate. This does not entirely move things out, but is a step in that direction. --- Cargo.lock | 6 +- cli/http_util.rs | 5 +- cli/npm/managed/installer.rs | 2 +- cli/npm/managed/installers/global.rs | 2 +- cli/npm/managed/installers/local.rs | 4 +- cli/npm/managed/installers/mod.rs | 2 +- cli/npm/managed/mod.rs | 34 +++-- cli/npm/managed/resolvers/mod.rs | 33 ----- cli/npm/mod.rs | 3 +- resolvers/deno/Cargo.toml | 10 +- resolvers/deno/lib.rs | 8 ++ resolvers/deno/npm/local.rs | 85 ++++++++++++ .../deno/npm/managed}/common.rs | 10 +- .../deno/npm/managed}/global.rs | 75 +++++++--- .../deno/npm/managed}/local.rs | 130 +++++------------- resolvers/deno/npm/managed/mod.rs | 45 ++++++ .../deno}/npm/managed/resolution.rs | 15 +- resolvers/deno/npm/mod.rs | 15 +- resolvers/deno/sync.rs | 6 + resolvers/npm_cache/Cargo.toml | 5 - resolvers/npm_cache/lib.rs | 11 +- resolvers/npm_cache/registry_info.rs | 9 +- resolvers/npm_cache/tarball.rs | 8 +- resolvers/npm_cache/todo.md | 2 - 24 files changed, 312 insertions(+), 213 deletions(-) delete mode 100644 cli/npm/managed/resolvers/mod.rs rename {cli/npm/managed/resolvers => resolvers/deno/npm/managed}/common.rs (87%) rename {cli/npm/managed/resolvers => resolvers/deno/npm/managed}/global.rs (62%) rename {cli/npm/managed/resolvers => resolvers/deno/npm/managed}/local.rs (61%) create mode 100644 resolvers/deno/npm/managed/mod.rs rename {cli => resolvers/deno}/npm/managed/resolution.rs (91%) diff --git a/Cargo.lock b/Cargo.lock index d55c1a19aa..e94fd48410 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2096,12 +2096,10 @@ dependencies = [ name = "deno_npm_cache" version = "0.3.0" dependencies = [ - "anyhow", "async-trait", "base64 0.21.7", "boxed_error", "deno_cache_dir", - "deno_core", "deno_error", "deno_npm", "deno_path_util", @@ -2197,16 +2195,20 @@ name = "deno_resolver" version = "0.15.0" dependencies = [ "anyhow", + "async-trait", "base32", "boxed_error", "dashmap", + "deno_cache_dir", "deno_config", "deno_error", "deno_media_type", + "deno_npm", "deno_package_json", "deno_path_util", "deno_semver", "node_resolver", + "parking_lot", "sys_traits", "test_server", "thiserror 2.0.3", diff --git a/cli/http_util.rs b/cli/http_util.rs index 19d9071833..5e63ab0a4a 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -70,7 +70,7 @@ impl HttpClientProvider { } } - pub fn get_or_create(&self) -> Result { + pub fn get_or_create(&self) -> Result { use std::collections::hash_map::Entry; let thread_id = std::thread::current().id(); let mut clients = self.clients_by_thread_id.lock(); @@ -87,7 +87,8 @@ impl HttpClientProvider { }, ..self.options.clone() }, - )?; + ) + .map_err(JsErrorBox::from_err)?; entry.insert(client.clone()); Ok(HttpClient::new(client)) } diff --git a/cli/npm/managed/installer.rs b/cli/npm/managed/installer.rs index 30ac807023..9f1cc05968 100644 --- a/cli/npm/managed/installer.rs +++ b/cli/npm/managed/installer.rs @@ -13,13 +13,13 @@ use deno_npm::resolution::AddPkgReqsOptions; use deno_npm::resolution::NpmResolutionError; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmResolutionPackage; +use deno_resolver::npm::managed::NpmResolution; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use deno_semver::SmallStackString; use deno_semver::VersionReq; -use super::resolution::NpmResolution; use crate::args::CliLockfile; use crate::npm::CliNpmRegistryInfoProvider; use crate::util::sync::TaskQueue; diff --git a/cli/npm/managed/installers/global.rs b/cli/npm/managed/installers/global.rs index d637c96122..477d73dbfb 100644 --- a/cli/npm/managed/installers/global.rs +++ b/cli/npm/managed/installers/global.rs @@ -11,8 +11,8 @@ use deno_core::futures::StreamExt; use deno_error::JsErrorBox; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; +use deno_resolver::npm::managed::NpmResolution; -use super::super::resolution::NpmResolution; use super::common::lifecycle_scripts::LifecycleScriptsStrategy; use super::common::NpmPackageFsInstaller; use crate::args::LifecycleScriptsConfig; diff --git a/cli/npm/managed/installers/local.rs b/cli/npm/managed/installers/local.rs index e2c5653801..a8efaaeb00 100644 --- a/cli/npm/managed/installers/local.rs +++ b/cli/npm/managed/installers/local.rs @@ -24,19 +24,19 @@ use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; use deno_path_util::fs::atomic_write_file_with_retries; +use deno_resolver::npm::get_package_folder_id_folder_name; +use deno_resolver::npm::managed::NpmResolution; use deno_semver::package::PackageNv; use deno_semver::StackString; use serde::Deserialize; use serde::Serialize; -use super::super::resolution::NpmResolution; use super::common::bin_entries; use super::common::NpmPackageFsInstaller; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; use crate::cache::CACHE_PERM; use crate::colors; -use crate::npm::managed::resolvers::get_package_folder_id_folder_name; use crate::npm::managed::PackageCaching; use crate::npm::CliNpmCache; use crate::npm::CliNpmTarballCache; diff --git a/cli/npm/managed/installers/mod.rs b/cli/npm/managed/installers/mod.rs index 4514fa1ec3..39e0d6f77c 100644 --- a/cli/npm/managed/installers/mod.rs +++ b/cli/npm/managed/installers/mod.rs @@ -4,11 +4,11 @@ use std::path::PathBuf; use std::sync::Arc; use deno_npm::NpmSystemInfo; +use deno_resolver::npm::managed::NpmResolution; pub use self::common::NpmPackageFsInstaller; use self::global::GlobalNpmPackageInstaller; use self::local::LocalNpmPackageInstaller; -use super::resolution::NpmResolution; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; use crate::npm::CliNpmCache; diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 6729a56b1e..5950af948c 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -23,6 +23,10 @@ use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; use deno_npm_cache::NpmCacheSetting; use deno_path_util::fs::canonicalize_path_maybe_not_exists; +use deno_resolver::npm::managed::create_npm_fs_resolver; +use deno_resolver::npm::managed::NpmPackageFsResolver; +use deno_resolver::npm::managed::NpmPackageFsResolverPackageFolderError; +use deno_resolver::npm::managed::NpmResolution; use deno_resolver::npm::CliNpmReqResolver; use deno_runtime::colors; use deno_runtime::ops::process::NpmProcessStateProvider; @@ -37,9 +41,6 @@ use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; -use self::resolution::NpmResolution; -use self::resolvers::create_npm_fs_resolver; -use self::resolvers::NpmPackageFsResolver; use super::CliNpmCache; use super::CliNpmCacheHttpClient; use super::CliNpmRegistryInfoProvider; @@ -60,8 +61,6 @@ use crate::util::sync::AtomicFlag; mod installer; mod installers; -mod resolution; -mod resolvers; pub enum CliNpmResolverManagedSnapshotOption { ResolveFromLockfile(Arc), @@ -104,6 +103,7 @@ pub async fn create_managed_npm_resolver_for_lsp( create_inner( http_client, npm_cache, + options.npm_cache_dir, options.npm_install_deps_provider, npm_api, options.sys, @@ -133,6 +133,7 @@ pub async fn create_managed_npm_resolver( Ok(create_inner( http_client, npm_cache, + options.npm_cache_dir, options.npm_install_deps_provider, api, options.sys, @@ -150,6 +151,7 @@ pub async fn create_managed_npm_resolver( fn create_inner( http_client: Arc, npm_cache: Arc, + npm_cache_dir: Arc, npm_install_deps_provider: Arc, registry_info_provider: Arc, sys: CliSys, @@ -181,7 +183,8 @@ fn create_inner( lifecycle_scripts.clone(), ); let fs_resolver = create_npm_fs_resolver( - npm_cache.clone(), + &npm_cache_dir, + &npm_rc, resolution.clone(), sys.clone(), node_modules_dir_path, @@ -192,7 +195,9 @@ fn create_inner( maybe_lockfile, registry_info_provider, npm_cache, + npm_cache_dir, npm_install_deps_provider, + npm_rc, resolution, sys, tarball_cache, @@ -314,7 +319,9 @@ pub struct ManagedCliNpmResolver { maybe_lockfile: Option>, registry_info_provider: Arc, npm_cache: Arc, + npm_cache_dir: Arc, npm_install_deps_provider: Arc, + npm_rc: Arc, sys: CliSys, resolution: Arc, resolution_installer: NpmResolutionInstaller, @@ -338,7 +345,7 @@ pub enum ResolvePkgFolderFromPkgIdError { #[class(inherit)] #[error("{0}")] NpmPackageFsResolverPackageFolder( - #[from] resolvers::NpmPackageFsResolverPackageFolderError, + #[from] NpmPackageFsResolverPackageFolderError, ), #[class(inherit)] #[error("{0}")] @@ -363,7 +370,9 @@ impl ManagedCliNpmResolver { maybe_lockfile: Option>, registry_info_provider: Arc, npm_cache: Arc, + npm_cache_dir: Arc, npm_install_deps_provider: Arc, + npm_rc: Arc, resolution: Arc, sys: CliSys, tarball_cache: Arc, @@ -382,7 +391,9 @@ impl ManagedCliNpmResolver { maybe_lockfile, registry_info_provider, npm_cache, + npm_cache_dir, npm_install_deps_provider, + npm_rc, text_only_progress_bar, resolution, resolution_installer, @@ -671,11 +682,11 @@ impl ManagedCliNpmResolver { } pub fn global_cache_root_path(&self) -> &Path { - self.npm_cache.root_dir_path() + self.npm_cache_dir.root_dir() } pub fn global_cache_root_url(&self) -> &Url { - self.npm_cache.root_dir_url() + self.npm_cache_dir.root_dir_url() } } @@ -772,7 +783,8 @@ impl CliNpmResolver for ManagedCliNpmResolver { self.lifecycle_scripts.clone(), ), create_npm_fs_resolver( - self.npm_cache.clone(), + &self.npm_cache_dir, + &self.npm_rc, npm_resolution.clone(), self.sys.clone(), self.root_node_modules_path().map(ToOwned::to_owned), @@ -780,7 +792,9 @@ impl CliNpmResolver for ManagedCliNpmResolver { self.maybe_lockfile.clone(), self.registry_info_provider.clone(), self.npm_cache.clone(), + self.npm_cache_dir.clone(), self.npm_install_deps_provider.clone(), + self.npm_rc.clone(), npm_resolution, self.sys.clone(), self.tarball_cache.clone(), diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs deleted file mode 100644 index 8a89208c77..0000000000 --- a/cli/npm/managed/resolvers/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -mod common; -mod global; -mod local; - -use std::path::PathBuf; -use std::sync::Arc; - -pub use self::common::NpmPackageFsResolver; -pub use self::common::NpmPackageFsResolverPackageFolderError; -use self::global::GlobalNpmPackageResolver; -pub use self::local::get_package_folder_id_folder_name; -use self::local::LocalNpmPackageResolver; -use super::resolution::NpmResolution; -use crate::npm::CliNpmCache; -use crate::sys::CliSys; - -pub fn create_npm_fs_resolver( - npm_cache: Arc, - resolution: Arc, - sys: CliSys, - maybe_node_modules_path: Option, -) -> Arc { - match maybe_node_modules_path { - Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new( - resolution, - sys, - node_modules_folder, - )), - None => Arc::new(GlobalNpmPackageResolver::new(npm_cache, resolution)), - } -} diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index d66b3e618f..56032e1a0b 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -11,6 +11,7 @@ use dashmap::DashMap; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; +use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_resolver::npm::ByonmInNpmPackageChecker; @@ -100,7 +101,7 @@ impl deno_npm_cache::NpmCacheHttpClient for CliNpmCacheHttpClient { }; deno_npm_cache::DownloadError { status_code, - error: err.into(), + error: JsErrorBox::from_err(err), } }) } diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 0eeec14e77..059fdbc08e 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -18,18 +18,20 @@ sync = ["dashmap"] [dependencies] anyhow.workspace = true +async-trait.workspace = true base32.workspace = true boxed_error.workspace = true dashmap = { workspace = true, optional = true } +deno_cache_dir.workspace = true deno_config.workspace = true deno_error.workspace = true deno_media_type.workspace = true -deno_package_json.workspace = true -deno_package_json.features = ["sync"] +deno_npm.workspace = true +deno_package_json = { workspace = true, features = ["sync"] } deno_path_util.workspace = true deno_semver.workspace = true -node_resolver.workspace = true -node_resolver.features = ["sync"] +node_resolver = { workspace = true, features = ["sync"] } +parking_lot.workspace = true sys_traits.workspace = true thiserror.workspace = true url.workspace = true diff --git a/resolvers/deno/lib.rs b/resolvers/deno/lib.rs index 49b2cf4d1b..12d8effc46 100644 --- a/resolvers/deno/lib.rs +++ b/resolvers/deno/lib.rs @@ -6,12 +6,14 @@ use std::path::PathBuf; use boxed_error::Boxed; +use deno_cache_dir::npm::NpmCacheDir; use deno_config::workspace::MappedResolution; use deno_config::workspace::MappedResolutionDiagnostic; use deno_config::workspace::MappedResolutionError; use deno_config::workspace::WorkspaceResolvePkgJsonFolderError; use deno_config::workspace::WorkspaceResolver; use deno_error::JsError; +use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValueParseError; use deno_semver::npm::NpmPackageReqReference; @@ -47,6 +49,12 @@ mod sync; #[allow(clippy::disallowed_types)] pub type WorkspaceResolverRc = crate::sync::MaybeArc; +#[allow(clippy::disallowed_types)] +pub(crate) type ResolvedNpmRcRc = crate::sync::MaybeArc; + +#[allow(clippy::disallowed_types)] +pub(crate) type NpmCacheDirRc = crate::sync::MaybeArc; + #[derive(Debug, Clone)] pub struct DenoResolution { pub url: Url, diff --git a/resolvers/deno/npm/local.rs b/resolvers/deno/npm/local.rs index 6322a4b3f7..b05864b856 100644 --- a/resolvers/deno/npm/local.rs +++ b/resolvers/deno/npm/local.rs @@ -2,6 +2,58 @@ use std::borrow::Cow; +use deno_cache_dir::npm::mixed_case_package_name_decode; +use deno_npm::NpmPackageCacheFolderId; +use deno_semver::package::PackageNv; +use deno_semver::StackString; + +#[inline] +pub fn get_package_folder_id_folder_name( + folder_id: &NpmPackageCacheFolderId, +) -> String { + get_package_folder_id_folder_name_from_parts( + &folder_id.nv, + folder_id.copy_index, + ) +} + +pub fn get_package_folder_id_folder_name_from_parts( + nv: &PackageNv, + copy_index: u8, +) -> String { + let copy_str = if copy_index == 0 { + Cow::Borrowed("") + } else { + Cow::Owned(format!("_{}", copy_index)) + }; + let name = normalize_pkg_name_for_node_modules_deno_folder(&nv.name); + format!("{}@{}{}", name, nv.version, copy_str) +} + +pub fn get_package_folder_id_from_folder_name( + folder_name: &str, +) -> Option { + let folder_name = folder_name.replace('+', "/"); + let (name, ending) = folder_name.rsplit_once('@')?; + let name: StackString = if let Some(encoded_name) = name.strip_prefix('_') { + StackString::from_string(mixed_case_package_name_decode(encoded_name)?) + } else { + name.into() + }; + let (raw_version, copy_index) = match ending.split_once('_') { + Some((raw_version, copy_index)) => { + let copy_index = copy_index.parse::().ok()?; + (raw_version, copy_index) + } + None => (ending, 0), + }; + let version = deno_semver::Version::parse_from_npm(raw_version).ok()?; + Some(NpmPackageCacheFolderId { + nv: PackageNv { name, version }, + copy_index, + }) +} + /// Normalizes a package name for use at `node_modules/.deno/@[_]` pub fn normalize_pkg_name_for_node_modules_deno_folder(name: &str) -> Cow { let name = if name.to_lowercase() == name { @@ -25,3 +77,36 @@ fn mixed_case_package_name_encode(name: &str) -> String { ) .to_lowercase() } + +#[cfg(test)] +mod test { + use deno_npm::NpmPackageCacheFolderId; + use deno_semver::package::PackageNv; + + use super::*; + + #[test] + fn test_get_package_folder_id_folder_name() { + let cases = vec![ + ( + NpmPackageCacheFolderId { + nv: PackageNv::from_str("@types/foo@1.2.3").unwrap(), + copy_index: 1, + }, + "@types+foo@1.2.3_1".to_string(), + ), + ( + NpmPackageCacheFolderId { + nv: PackageNv::from_str("JSON@3.2.1").unwrap(), + copy_index: 0, + }, + "_jjju6tq@3.2.1".to_string(), + ), + ]; + for (input, output) in cases { + assert_eq!(get_package_folder_id_folder_name(&input), output); + let folder_id = get_package_folder_id_from_folder_name(&output).unwrap(); + assert_eq!(folder_id, input); + } + } +} diff --git a/cli/npm/managed/resolvers/common.rs b/resolvers/deno/npm/managed/common.rs similarity index 87% rename from cli/npm/managed/resolvers/common.rs rename to resolvers/deno/npm/managed/common.rs index be0b40fa2a..23ed63e2f0 100644 --- a/cli/npm/managed/resolvers/common.rs +++ b/resolvers/deno/npm/managed/common.rs @@ -4,10 +4,14 @@ use std::path::Path; use std::path::PathBuf; use async_trait::async_trait; -use deno_ast::ModuleSpecifier; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use node_resolver::errors::PackageFolderResolveError; +use url::Url; + +#[allow(clippy::disallowed_types)] +pub(super) type NpmPackageFsResolverRc = + crate::sync::MaybeArc; #[derive(Debug, thiserror::Error, deno_error::JsError)] #[class(generic)] @@ -34,11 +38,11 @@ pub trait NpmPackageFsResolver: Send + Sync { fn resolve_package_folder_from_package( &self, name: &str, - referrer: &ModuleSpecifier, + referrer: &Url, ) -> Result; fn resolve_package_cache_folder_id_from_specifier( &self, - specifier: &ModuleSpecifier, + specifier: &Url, ) -> Result, std::io::Error>; } diff --git a/cli/npm/managed/resolvers/global.rs b/resolvers/deno/npm/managed/global.rs similarity index 62% rename from cli/npm/managed/resolvers/global.rs rename to resolvers/deno/npm/managed/global.rs index d63d3a7e45..16a2bc7a70 100644 --- a/cli/npm/managed/resolvers/global.rs +++ b/resolvers/deno/npm/managed/global.rs @@ -4,30 +4,60 @@ use std::path::Path; use std::path::PathBuf; -use std::sync::Arc; use async_trait::async_trait; -use deno_ast::ModuleSpecifier; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; +use deno_semver::package::PackageNv; +use deno_semver::StackString; +use deno_semver::Version; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::ReferrerNotFoundError; +use url::Url; -use super::super::resolution::NpmResolution; -use super::common::NpmPackageFsResolver; -use crate::npm::CliNpmCache; +use super::resolution::NpmResolutionRc; +use super::NpmCacheDirRc; +use super::NpmPackageFsResolver; +use crate::ResolvedNpmRcRc; /// Resolves packages from the global npm cache. #[derive(Debug)] pub struct GlobalNpmPackageResolver { - cache: Arc, - resolution: Arc, + cache: NpmCacheDirRc, + npm_rc: ResolvedNpmRcRc, + resolution: NpmResolutionRc, } impl GlobalNpmPackageResolver { - pub fn new(cache: Arc, resolution: Arc) -> Self { - Self { cache, resolution } + pub fn new( + cache: NpmCacheDirRc, + npm_rc: ResolvedNpmRcRc, + resolution: NpmResolutionRc, + ) -> Self { + Self { + cache, + npm_rc, + resolution, + } + } + + fn resolve_package_cache_folder_id_from_specifier_inner( + &self, + specifier: &Url, + ) -> Option { + self + .cache + .resolve_package_folder_id_from_specifier(specifier) + .and_then(|cache_id| { + Some(NpmPackageCacheFolderId { + nv: PackageNv { + name: StackString::from_string(cache_id.name), + version: Version::parse_from_npm(&cache_id.version).ok()?, + }, + copy_index: cache_id.copy_index, + }) + }) } } @@ -38,21 +68,26 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { } fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { - let folder_id = self + let folder_copy_index = self .resolution - .resolve_pkg_cache_folder_id_from_pkg_id(id)?; - Some(self.cache.package_folder_for_id(&folder_id)) + .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; + let registry_url = self.npm_rc.get_registry_url(&id.nv.name); + Some(self.cache.package_folder_for_id( + &id.nv.name, + &id.nv.version.to_string(), + folder_copy_index, + registry_url, + )) } fn resolve_package_folder_from_package( &self, name: &str, - referrer: &ModuleSpecifier, + referrer: &Url, ) -> Result { use deno_npm::resolution::PackageNotFoundFromReferrerError; - let Some(referrer_cache_folder_id) = self - .cache - .resolve_package_folder_id_from_specifier(referrer) + let Some(referrer_cache_folder_id) = + self.resolve_package_cache_folder_id_from_specifier_inner(referrer) else { return Err( ReferrerNotFoundError { @@ -106,12 +141,8 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { fn resolve_package_cache_folder_id_from_specifier( &self, - specifier: &ModuleSpecifier, + specifier: &Url, ) -> Result, std::io::Error> { - Ok( - self - .cache - .resolve_package_folder_id_from_specifier(specifier), - ) + Ok(self.resolve_package_cache_folder_id_from_specifier_inner(specifier)) } } diff --git a/cli/npm/managed/resolvers/local.rs b/resolvers/deno/npm/managed/local.rs similarity index 61% rename from cli/npm/managed/resolvers/local.rs rename to resolvers/deno/npm/managed/local.rs index 52d5ac5a9e..e84de964ad 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/resolvers/deno/npm/managed/local.rs @@ -5,49 +5,50 @@ use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; -use std::sync::Arc; use async_trait::async_trait; -use deno_ast::ModuleSpecifier; -use deno_cache_dir::npm::mixed_case_package_name_decode; -use deno_core::url::Url; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_path_util::fs::canonicalize_path_maybe_not_exists; -use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder; -use deno_semver::package::PackageNv; -use deno_semver::StackString; +use deno_path_util::url_from_directory_path; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::ReferrerNotFoundError; +use sys_traits::FsCanonicalize; use sys_traits::FsMetadata; +use url::Url; -use super::super::resolution::NpmResolution; -use super::common::NpmPackageFsResolver; -use crate::sys::CliSys; +use super::resolution::NpmResolutionRc; +use super::NpmPackageFsResolver; +use crate::npm::local::get_package_folder_id_folder_name_from_parts; +use crate::npm::local::get_package_folder_id_from_folder_name; /// Resolver that creates a local node_modules directory /// and resolves packages from it. #[derive(Debug)] -pub struct LocalNpmPackageResolver { - resolution: Arc, - sys: CliSys, +pub struct LocalNpmPackageResolver< + TSys: FsCanonicalize + FsMetadata + Send + Sync, +> { + resolution: NpmResolutionRc, + sys: TSys, root_node_modules_path: PathBuf, root_node_modules_url: Url, } -impl LocalNpmPackageResolver { +impl + LocalNpmPackageResolver +{ #[allow(clippy::too_many_arguments)] pub fn new( - resolution: Arc, - sys: CliSys, + resolution: NpmResolutionRc, + sys: TSys, node_modules_folder: PathBuf, ) -> Self { Self { resolution, sys, - root_node_modules_url: Url::from_directory_path(&node_modules_folder) + root_node_modules_url: url_from_directory_path(&node_modules_folder) .unwrap(), root_node_modules_path: node_modules_folder, } @@ -67,7 +68,7 @@ impl LocalNpmPackageResolver { fn resolve_folder_for_specifier( &self, - specifier: &ModuleSpecifier, + specifier: &Url, ) -> Result, std::io::Error> { let Some(relative_url) = self.root_node_modules_url.make_relative(specifier) @@ -78,7 +79,7 @@ impl LocalNpmPackageResolver { return Ok(None); } // it's within the directory, so use it - let Some(path) = specifier.to_file_path().ok() else { + let Some(path) = deno_path_util::url_to_file_path(specifier).ok() else { return Ok(None); }; // Canonicalize the path so it's not pointing to the symlinked directory @@ -88,7 +89,7 @@ impl LocalNpmPackageResolver { fn resolve_package_folder_from_specifier( &self, - specifier: &ModuleSpecifier, + specifier: &Url, ) -> Result, std::io::Error> { let Some(local_path) = self.resolve_folder_for_specifier(specifier)? else { return Ok(None); @@ -99,31 +100,36 @@ impl LocalNpmPackageResolver { } #[async_trait(?Send)] -impl NpmPackageFsResolver for LocalNpmPackageResolver { +impl NpmPackageFsResolver + for LocalNpmPackageResolver +{ fn node_modules_path(&self) -> Option<&Path> { Some(self.root_node_modules_path.as_ref()) } fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { - let cache_folder_id = self + let folder_copy_index = self .resolution - .resolve_pkg_cache_folder_id_from_pkg_id(id)?; + .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; // package is stored at: // node_modules/.deno//node_modules/ Some( self .root_node_modules_path .join(".deno") - .join(get_package_folder_id_folder_name(&cache_folder_id)) + .join(get_package_folder_id_folder_name_from_parts( + &id.nv, + folder_copy_index, + )) .join("node_modules") - .join(&cache_folder_id.nv.name), + .join(&id.nv.name), ) } fn resolve_package_folder_from_package( &self, name: &str, - referrer: &ModuleSpecifier, + referrer: &Url, ) -> Result { let maybe_local_path = self .resolve_folder_for_specifier(referrer) @@ -173,7 +179,7 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { fn resolve_package_cache_folder_id_from_specifier( &self, - specifier: &ModuleSpecifier, + specifier: &Url, ) -> Result, std::io::Error> { let Some(folder_path) = self.resolve_package_folder_from_specifier(specifier)? @@ -198,43 +204,6 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { } } -pub fn get_package_folder_id_folder_name( - folder_id: &NpmPackageCacheFolderId, -) -> String { - let copy_str = if folder_id.copy_index == 0 { - Cow::Borrowed("") - } else { - Cow::Owned(format!("_{}", folder_id.copy_index)) - }; - let nv = &folder_id.nv; - let name = normalize_pkg_name_for_node_modules_deno_folder(&nv.name); - format!("{}@{}{}", name, nv.version, copy_str) -} - -fn get_package_folder_id_from_folder_name( - folder_name: &str, -) -> Option { - let folder_name = folder_name.replace('+', "/"); - let (name, ending) = folder_name.rsplit_once('@')?; - let name: StackString = if let Some(encoded_name) = name.strip_prefix('_') { - StackString::from_string(mixed_case_package_name_decode(encoded_name)?) - } else { - name.into() - }; - let (raw_version, copy_index) = match ending.split_once('_') { - Some((raw_version, copy_index)) => { - let copy_index = copy_index.parse::().ok()?; - (raw_version, copy_index) - } - None => (ending, 0), - }; - let version = deno_semver::Version::parse_from_npm(raw_version).ok()?; - Some(NpmPackageCacheFolderId { - nv: PackageNv { name, version }, - copy_index, - }) -} - fn join_package_name(path: &Path, package_name: &str) -> PathBuf { let mut path = path.to_path_buf(); // ensure backslashes are used on windows @@ -243,36 +212,3 @@ fn join_package_name(path: &Path, package_name: &str) -> PathBuf { } path } - -#[cfg(test)] -mod test { - use deno_npm::NpmPackageCacheFolderId; - use deno_semver::package::PackageNv; - - use super::*; - - #[test] - fn test_get_package_folder_id_folder_name() { - let cases = vec![ - ( - NpmPackageCacheFolderId { - nv: PackageNv::from_str("@types/foo@1.2.3").unwrap(), - copy_index: 1, - }, - "@types+foo@1.2.3_1".to_string(), - ), - ( - NpmPackageCacheFolderId { - nv: PackageNv::from_str("JSON@3.2.1").unwrap(), - copy_index: 0, - }, - "_jjju6tq@3.2.1".to_string(), - ), - ]; - for (input, output) in cases { - assert_eq!(get_package_folder_id_folder_name(&input), output); - let folder_id = get_package_folder_id_from_folder_name(&output).unwrap(); - assert_eq!(folder_id, input); - } - } -} diff --git a/resolvers/deno/npm/managed/mod.rs b/resolvers/deno/npm/managed/mod.rs new file mode 100644 index 0000000000..b460656eab --- /dev/null +++ b/resolvers/deno/npm/managed/mod.rs @@ -0,0 +1,45 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +mod common; +mod global; +mod local; +mod resolution; + +use std::path::PathBuf; + +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; + +pub use self::common::NpmPackageFsResolver; +pub use self::common::NpmPackageFsResolverPackageFolderError; +use self::common::NpmPackageFsResolverRc; +use self::global::GlobalNpmPackageResolver; +use self::local::LocalNpmPackageResolver; +pub use self::resolution::NpmResolution; +use self::resolution::NpmResolutionRc; +use crate::sync::new_rc; +use crate::NpmCacheDirRc; +use crate::ResolvedNpmRcRc; + +pub fn create_npm_fs_resolver< + TSys: FsCanonicalize + FsMetadata + Send + Sync + 'static, +>( + npm_cache_dir: &NpmCacheDirRc, + npm_rc: &ResolvedNpmRcRc, + resolution: NpmResolutionRc, + sys: TSys, + maybe_node_modules_path: Option, +) -> NpmPackageFsResolverRc { + match maybe_node_modules_path { + Some(node_modules_folder) => new_rc(LocalNpmPackageResolver::new( + resolution, + sys, + node_modules_folder, + )), + None => new_rc(GlobalNpmPackageResolver::new( + npm_cache_dir.clone(), + npm_rc.clone(), + resolution, + )), + } +} diff --git a/cli/npm/managed/resolution.rs b/resolvers/deno/npm/managed/resolution.rs similarity index 91% rename from cli/npm/managed/resolution.rs rename to resolvers/deno/npm/managed/resolution.rs index 5178c20608..40ab91202c 100644 --- a/cli/npm/managed/resolution.rs +++ b/resolvers/deno/npm/managed/resolution.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; -use deno_core::parking_lot::RwLock; use deno_npm::resolution::NpmPackagesPartitioned; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::resolution::PackageCacheFolderIdNotFoundError; @@ -16,10 +15,12 @@ use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use parking_lot::RwLock; -/// Handles updating and storing npm resolution in memory where the underlying -/// snapshot can be updated concurrently. Additionally handles updating the lockfile -/// based on changes to the resolution. +#[allow(clippy::disallowed_types)] +pub(super) type NpmResolutionRc = crate::sync::MaybeArc; + +/// Handles updating and storing npm resolution in memory. /// /// This does not interact with the file system. pub struct NpmResolution { @@ -50,15 +51,15 @@ impl NpmResolution { } } - pub fn resolve_pkg_cache_folder_id_from_pkg_id( + pub fn resolve_pkg_cache_folder_copy_index_from_pkg_id( &self, id: &NpmPackageId, - ) -> Option { + ) -> Option { self .snapshot .read() .package_from_id(id) - .map(|p| p.get_package_cache_folder_id()) + .map(|p| p.copy_index) } pub fn resolve_pkg_id_from_pkg_cache_folder_id( diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 2e7c4c888b..54282e677d 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -4,15 +4,9 @@ use std::fmt::Debug; use std::path::PathBuf; use boxed_error::Boxed; -pub use byonm::ByonmInNpmPackageChecker; -pub use byonm::ByonmNpmResolver; -pub use byonm::ByonmNpmResolverCreateOptions; -pub use byonm::ByonmNpmResolverRc; -pub use byonm::ByonmResolvePkgFolderFromDenoReqError; use deno_error::JsError; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; -pub use local::normalize_pkg_name_for_node_modules_deno_folder; use node_resolver::errors::NodeResolveError; use node_resolver::errors::NodeResolveErrorKind; use node_resolver::errors::PackageFolderResolveErrorKind; @@ -33,8 +27,17 @@ use sys_traits::FsReadDir; use thiserror::Error; use url::Url; +pub use self::byonm::ByonmInNpmPackageChecker; +pub use self::byonm::ByonmNpmResolver; +pub use self::byonm::ByonmNpmResolverCreateOptions; +pub use self::byonm::ByonmNpmResolverRc; +pub use self::byonm::ByonmResolvePkgFolderFromDenoReqError; +pub use self::local::get_package_folder_id_folder_name; +pub use self::local::normalize_pkg_name_for_node_modules_deno_folder; + mod byonm; mod local; +pub mod managed; #[derive(Debug, Error, JsError)] #[class(generic)] diff --git a/resolvers/deno/sync.rs b/resolvers/deno/sync.rs index 43635e62b7..10cdc5ec4d 100644 --- a/resolvers/deno/sync.rs +++ b/resolvers/deno/sync.rs @@ -46,3 +46,9 @@ mod inner { } } } + +#[allow(clippy::disallowed_types)] +#[inline] +pub fn new_rc(value: T) -> MaybeArc { + MaybeArc::new(value) +} diff --git a/resolvers/npm_cache/Cargo.toml b/resolvers/npm_cache/Cargo.toml index 328355d340..3cefc6538a 100644 --- a/resolvers/npm_cache/Cargo.toml +++ b/resolvers/npm_cache/Cargo.toml @@ -14,11 +14,6 @@ description = "Helpers for downloading and caching npm dependencies for Deno" path = "lib.rs" [dependencies] -# todo(dsherret): remove this dependency -anyhow.workspace = true -# todo(dsherret): remove this dependency -deno_core.workspace = true - async-trait.workspace = true base64.workspace = true boxed_error.workspace = true diff --git a/resolvers/npm_cache/lib.rs b/resolvers/npm_cache/lib.rs index 90c07de5d5..f0de201b75 100644 --- a/resolvers/npm_cache/lib.rs +++ b/resolvers/npm_cache/lib.rs @@ -6,7 +6,6 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use anyhow::Error as AnyError; use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::npm::NpmCacheDir; use deno_error::JsErrorBox; @@ -51,7 +50,7 @@ pub use tarball::TarballCache; #[class(generic)] pub struct DownloadError { pub status_code: Option, - pub error: AnyError, + pub error: JsErrorBox, } impl std::error::Error for DownloadError { @@ -312,15 +311,17 @@ impl< &self, name: &str, package_info: &NpmPackageInfo, - ) -> Result<(), AnyError> { + ) -> Result<(), JsErrorBox> { let file_cache_path = self.get_registry_package_info_file_cache_path(name); - let file_text = serde_json::to_string(&package_info)?; + let file_text = + serde_json::to_string(&package_info).map_err(JsErrorBox::from_err)?; atomic_write_file_with_retries( &self.sys, &file_cache_path, file_text.as_bytes(), 0o644, - )?; + ) + .map_err(JsErrorBox::from_err)?; Ok(()) } diff --git a/resolvers/npm_cache/registry_info.rs b/resolvers/npm_cache/registry_info.rs index ece797abba..673f2ff445 100644 --- a/resolvers/npm_cache/registry_info.rs +++ b/resolvers/npm_cache/registry_info.rs @@ -5,11 +5,6 @@ use std::collections::HashSet; use std::sync::Arc; use async_trait::async_trait; -use deno_core::futures::future::LocalBoxFuture; -use deno_core::futures::FutureExt; -use deno_core::parking_lot::Mutex; -use deno_core::serde_json; -use deno_core::url::Url; use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; @@ -17,6 +12,9 @@ use deno_npm::registry::NpmRegistryApi; use deno_npm::registry::NpmRegistryPackageInfoLoadError; use deno_unsync::sync::AtomicFlag; use deno_unsync::sync::MultiRuntimeAsyncValueCreator; +use futures::future::LocalBoxFuture; +use futures::FutureExt; +use parking_lot::Mutex; use sys_traits::FsCreateDirAll; use sys_traits::FsHardLink; use sys_traits::FsMetadata; @@ -26,6 +24,7 @@ use sys_traits::FsRemoveFile; use sys_traits::FsRename; use sys_traits::SystemRandom; use sys_traits::ThreadSleep; +use url::Url; use crate::remote::maybe_auth_header_for_npm_registry; use crate::NpmCache; diff --git a/resolvers/npm_cache/tarball.rs b/resolvers/npm_cache/tarball.rs index d9575ba9cd..7a9453e655 100644 --- a/resolvers/npm_cache/tarball.rs +++ b/resolvers/npm_cache/tarball.rs @@ -3,16 +3,15 @@ use std::collections::HashMap; use std::sync::Arc; -use deno_core::futures::future::LocalBoxFuture; -use deno_core::futures::FutureExt; -use deno_core::parking_lot::Mutex; -use deno_core::url::Url; use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageVersionDistInfo; use deno_semver::package::PackageNv; use deno_unsync::sync::MultiRuntimeAsyncValueCreator; +use futures::future::LocalBoxFuture; +use futures::FutureExt; use http::StatusCode; +use parking_lot::Mutex; use sys_traits::FsCreateDirAll; use sys_traits::FsHardLink; use sys_traits::FsMetadata; @@ -22,6 +21,7 @@ use sys_traits::FsRemoveFile; use sys_traits::FsRename; use sys_traits::SystemRandom; use sys_traits::ThreadSleep; +use url::Url; use crate::remote::maybe_auth_header_for_npm_registry; use crate::tarball_extract::verify_and_extract_tarball; diff --git a/resolvers/npm_cache/todo.md b/resolvers/npm_cache/todo.md index e10b1cfd89..e460fcae86 100644 --- a/resolvers/npm_cache/todo.md +++ b/resolvers/npm_cache/todo.md @@ -1,7 +1,5 @@ This crate is a work in progress: -1. Remove `deno_core` dependency. -1. Remove `anyhow` dependency. 1. Add a clippy.toml file that bans accessing the file system directory and instead does it through a trait. 1. Make this crate work in Wasm. From 318f524c5c72feaffe4a76e2e8cbc8fca27fb75f Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Thu, 9 Jan 2025 17:54:14 +0000 Subject: [PATCH 084/107] fix(lsp): use verbatim specifier for URL auto-imports (#27605) --- cli/lsp/tsc.rs | 5 ++ tests/integration/lsp_tests.rs | 68 +++++++++++++++++++ .../imports_declaration/imports_interface.ts | 3 + .../subdir/imports_declaration/interface.d.ts | 3 + 4 files changed, 79 insertions(+) create mode 100644 tests/testdata/subdir/imports_declaration/imports_interface.ts create mode 100644 tests/testdata/subdir/imports_declaration/interface.d.ts diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 826021a288..cd1a724f5e 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -3972,6 +3972,11 @@ impl CompletionEntry { if let Some(mut new_specifier) = import_mapper .check_specifier(&import_data.normalized, specifier) .or_else(|| relative_specifier(specifier, &import_data.normalized)) + .or_else(|| { + ModuleSpecifier::parse(&import_data.raw.module_specifier) + .is_ok() + .then(|| import_data.normalized.to_string()) + }) { if new_specifier.contains("/node_modules/") { return None; diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 9efb34f337..247851da9c 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -9937,6 +9937,74 @@ fn lsp_auto_imports_npm_auto() { client.shutdown(); } +// Regression test for https://github.com/denoland/deno/issues/23869. +#[test] +fn lsp_auto_imports_remote_dts() { + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); + let temp_dir = context.temp_dir(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": r#" + import "http://localhost:4545/subdir/imports_declaration/imports_interface.ts"; + const a: SomeInterface + "#, + }, + })); + client.write_request( + "workspace/executeCommand", + json!({ + "command": "deno.cache", + "arguments": [[], temp_dir.url().join("file.ts").unwrap()], + }), + ); + let list = client.get_completion_list( + temp_dir.url().join("file.ts").unwrap(), + (2, 21), + json!({ "triggerKind": 2 }), + ); + assert!(!list.is_incomplete); + let item = list + .items + .iter() + .find(|item| item.label == "SomeInterface") + .unwrap(); + let res = client.write_request("completionItem/resolve", json!(item)); + assert_eq!( + res, + json!({ + "label": "SomeInterface", + "labelDetails": { + "description": "http://localhost:4545/subdir/imports_declaration/interface.d.ts", + }, + "kind": 8, + "detail": "interface SomeInterface", + "documentation": { + "kind": "markdown", + "value": "", + }, + "sortText": "￿16_1", + "additionalTextEdits": [ + { + "range": { + "start": { "line": 2, "character": 0 }, + "end": { "line": 2, "character": 0 }, + }, + "newText": " import { SomeInterface } from \"http://localhost:4545/subdir/imports_declaration/interface.d.ts\";\n", + }, + ], + }), + ); + client.shutdown(); +} + #[test] fn lsp_npm_specifier_unopened_file() { let context = TestContextBuilder::new() diff --git a/tests/testdata/subdir/imports_declaration/imports_interface.ts b/tests/testdata/subdir/imports_declaration/imports_interface.ts new file mode 100644 index 0000000000..5eb2e64d51 --- /dev/null +++ b/tests/testdata/subdir/imports_declaration/imports_interface.ts @@ -0,0 +1,3 @@ +import type { SomeInterface } from "./interface.d.ts"; + +export const someObject: SomeInterface = { someField: "someValue" }; diff --git a/tests/testdata/subdir/imports_declaration/interface.d.ts b/tests/testdata/subdir/imports_declaration/interface.d.ts new file mode 100644 index 0000000000..e1531905b9 --- /dev/null +++ b/tests/testdata/subdir/imports_declaration/interface.d.ts @@ -0,0 +1,3 @@ +export interface SomeInterface { + someField: string; +} From 966370c9080afa5fcf3b94617135a3306c1a76f4 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 9 Jan 2025 14:04:52 -0500 Subject: [PATCH 085/107] refactor(npm): move `InNpmPackageChecker` code to deno_resolver (#27609) As title. Will allow consumers to create this struct and use our behaviour. Closes #27409 --- cli/factory.rs | 9 ++++---- cli/lsp/resolver.rs | 8 +++---- cli/npm/managed/mod.rs | 30 -------------------------- cli/npm/mod.rs | 20 ----------------- cli/standalone/mod.rs | 10 ++++----- resolvers/deno/Cargo.toml | 6 +++--- resolvers/deno/npm/managed/common.rs | 5 ++++- resolvers/deno/npm/managed/mod.rs | 32 ++++++++++++++++++++++++++++ resolvers/deno/npm/mod.rs | 23 +++++++++++++++++++- resolvers/deno/sync.rs | 7 ++++++ 10 files changed, 81 insertions(+), 69 deletions(-) diff --git a/cli/factory.rs b/cli/factory.rs index 86902dfc3b..09b8004366 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -12,6 +12,8 @@ use deno_core::futures::FutureExt; use deno_core::FeatureChecker; use deno_error::JsErrorBox; use deno_resolver::cjs::IsCjsResolutionMode; +use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; use deno_resolver::NodeAndNpmReqResolver; @@ -64,14 +66,11 @@ use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; use crate::npm::create_cli_npm_resolver; -use crate::npm::create_in_npm_pkg_checker; use crate::npm::CliByonmNpmResolverCreateOptions; -use crate::npm::CliManagedInNpmPkgCheckerCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; -use crate::npm::CreateInNpmPkgCheckerOptions; use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionCheckerMode; use crate::resolver::CjsTracker; @@ -387,7 +386,7 @@ impl CliFactory { CreateInNpmPkgCheckerOptions::Byonm } else { CreateInNpmPkgCheckerOptions::Managed( - CliManagedInNpmPkgCheckerCreateOptions { + ManagedInNpmPkgCheckerCreateOptions { root_cache_dir_url: self.npm_cache_dir()?.root_dir_url(), maybe_node_modules_path: cli_options .node_modules_dir_path() @@ -395,7 +394,7 @@ impl CliFactory { }, ) }; - Ok(create_in_npm_pkg_checker(options)) + Ok(deno_resolver::npm::create_in_npm_pkg_checker(options)) }) } diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 51820432f7..9ae28405bf 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -23,6 +23,8 @@ use deno_graph::Range; use deno_npm::NpmSystemInfo; use deno_path_util::url_to_file_path; use deno_resolver::cjs::IsCjsResolutionMode; +use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; use deno_resolver::NodeAndNpmReqResolver; @@ -53,12 +55,10 @@ use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; use crate::npm::create_cli_npm_resolver_for_lsp; use crate::npm::CliByonmNpmResolverCreateOptions; -use crate::npm::CliManagedInNpmPkgCheckerCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; -use crate::npm::CreateInNpmPkgCheckerOptions; use crate::npm::ManagedCliNpmResolver; use crate::resolver::CliDenoResolver; use crate::resolver::CliNpmReqResolver; @@ -736,14 +736,14 @@ impl<'a> ResolverFactory<'a> { pub fn in_npm_pkg_checker(&self) -> &Arc { self.services.in_npm_pkg_checker.get_or_init(|| { - crate::npm::create_in_npm_pkg_checker( + deno_resolver::npm::create_in_npm_pkg_checker( match self.services.npm_resolver.as_ref().map(|r| r.as_inner()) { Some(crate::npm::InnerCliNpmResolverRef::Byonm(_)) | None => { CreateInNpmPkgCheckerOptions::Byonm } Some(crate::npm::InnerCliNpmResolverRef::Managed(m)) => { CreateInNpmPkgCheckerOptions::Managed( - CliManagedInNpmPkgCheckerCreateOptions { + ManagedInNpmPkgCheckerCreateOptions { root_cache_dir_url: m.global_cache_root_url(), maybe_node_modules_path: m.maybe_node_modules_path(), }, diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 5950af948c..3f4390988a 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -38,7 +38,6 @@ use installers::create_npm_fs_installer; use installers::NpmPackageFsInstaller; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; -use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; use super::CliNpmCache; @@ -276,35 +275,6 @@ async fn snapshot_from_lockfile( Ok(snapshot) } -#[derive(Debug)] -struct ManagedInNpmPackageChecker { - root_dir: Url, -} - -impl InNpmPackageChecker for ManagedInNpmPackageChecker { - fn in_npm_package(&self, specifier: &Url) -> bool { - specifier.as_ref().starts_with(self.root_dir.as_str()) - } -} - -pub struct CliManagedInNpmPkgCheckerCreateOptions<'a> { - pub root_cache_dir_url: &'a Url, - pub maybe_node_modules_path: Option<&'a Path>, -} - -pub fn create_managed_in_npm_pkg_checker( - options: CliManagedInNpmPkgCheckerCreateOptions, -) -> Arc { - let root_dir = match options.maybe_node_modules_path { - Some(node_modules_folder) => { - deno_path_util::url_from_directory_path(node_modules_folder).unwrap() - } - None => options.root_cache_dir_url.clone(), - }; - debug_assert!(root_dir.as_str().ends_with('/')); - Arc::new(ManagedInNpmPackageChecker { root_dir }) -} - #[derive(Debug, Clone, PartialEq, Eq)] pub enum PackageCaching<'a> { Only(Cow<'a, [PackageReq]>), diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 56032e1a0b..9ac478732d 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -14,7 +14,6 @@ use deno_core::url::Url; use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; -use deno_resolver::npm::ByonmInNpmPackageChecker; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::CliNpmReqResolver; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; @@ -23,13 +22,10 @@ use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use http::HeaderName; use http::HeaderValue; -use managed::create_managed_in_npm_pkg_checker; -use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; pub use self::byonm::CliByonmNpmResolver; pub use self::byonm::CliByonmNpmResolverCreateOptions; -pub use self::managed::CliManagedInNpmPkgCheckerCreateOptions; pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::ManagedCliNpmResolver; @@ -134,22 +130,6 @@ pub async fn create_cli_npm_resolver( } } -pub enum CreateInNpmPkgCheckerOptions<'a> { - Managed(CliManagedInNpmPkgCheckerCreateOptions<'a>), - Byonm, -} - -pub fn create_in_npm_pkg_checker( - options: CreateInNpmPkgCheckerOptions, -) -> Arc { - match options { - CreateInNpmPkgCheckerOptions::Managed(options) => { - create_managed_in_npm_pkg_checker(options) - } - CreateInNpmPkgCheckerOptions::Byonm => Arc::new(ByonmInNpmPackageChecker), - } -} - pub enum InnerCliNpmResolverRef<'a> { Managed(&'a ManagedCliNpmResolver), #[allow(dead_code)] diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 30e939cb7a..ecf9805b2d 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -39,6 +39,9 @@ use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonDepValue; use deno_resolver::cjs::IsCjsResolutionMode; +use deno_resolver::npm::create_in_npm_pkg_checker; +use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; use deno_runtime::deno_fs; use deno_runtime::deno_fs::FileSystem; @@ -81,14 +84,11 @@ use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; use crate::npm::create_cli_npm_resolver; -use crate::npm::create_in_npm_pkg_checker; use crate::npm::CliByonmNpmResolverCreateOptions; -use crate::npm::CliManagedInNpmPkgCheckerCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; -use crate::npm::CreateInNpmPkgCheckerOptions; use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionCheckerMode; use crate::resolver::CjsTracker; @@ -738,7 +738,7 @@ pub async fn run( .map(|node_modules_dir| root_path.join(node_modules_dir)); let in_npm_pkg_checker = create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed( - CliManagedInNpmPkgCheckerCreateOptions { + ManagedInNpmPkgCheckerCreateOptions { root_cache_dir_url: npm_cache_dir.root_dir_url(), maybe_node_modules_path: maybe_node_modules_path.as_deref(), }, @@ -796,7 +796,7 @@ pub async fn run( )); let in_npm_pkg_checker = create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed( - CliManagedInNpmPkgCheckerCreateOptions { + ManagedInNpmPkgCheckerCreateOptions { root_cache_dir_url: npm_cache_dir.root_dir_url(), maybe_node_modules_path: None, }, diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 059fdbc08e..7a5d2833d0 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -14,7 +14,7 @@ description = "Deno resolution algorithm" path = "lib.rs" [features] -sync = ["dashmap"] +sync = ["dashmap", "deno_package_json/sync", "node_resolver/sync"] [dependencies] anyhow.workspace = true @@ -27,10 +27,10 @@ deno_config.workspace = true deno_error.workspace = true deno_media_type.workspace = true deno_npm.workspace = true -deno_package_json = { workspace = true, features = ["sync"] } +deno_package_json.workspace = true deno_path_util.workspace = true deno_semver.workspace = true -node_resolver = { workspace = true, features = ["sync"] } +node_resolver.workspace = true parking_lot.workspace = true sys_traits.workspace = true thiserror.workspace = true diff --git a/resolvers/deno/npm/managed/common.rs b/resolvers/deno/npm/managed/common.rs index 23ed63e2f0..5a4a517bf0 100644 --- a/resolvers/deno/npm/managed/common.rs +++ b/resolvers/deno/npm/managed/common.rs @@ -9,6 +9,9 @@ use deno_npm::NpmPackageId; use node_resolver::errors::PackageFolderResolveError; use url::Url; +use crate::sync::MaybeSend; +use crate::sync::MaybeSync; + #[allow(clippy::disallowed_types)] pub(super) type NpmPackageFsResolverRc = crate::sync::MaybeArc; @@ -20,7 +23,7 @@ pub struct NpmPackageFsResolverPackageFolderError(deno_semver::StackString); /// Part of the resolution that interacts with the file system. #[async_trait(?Send)] -pub trait NpmPackageFsResolver: Send + Sync { +pub trait NpmPackageFsResolver: MaybeSend + MaybeSync { /// The local node_modules folder if it is applicable to the implementation. fn node_modules_path(&self) -> Option<&Path>; diff --git a/resolvers/deno/npm/managed/mod.rs b/resolvers/deno/npm/managed/mod.rs index b460656eab..53b07f7d90 100644 --- a/resolvers/deno/npm/managed/mod.rs +++ b/resolvers/deno/npm/managed/mod.rs @@ -5,10 +5,13 @@ mod global; mod local; mod resolution; +use std::path::Path; use std::path::PathBuf; +use node_resolver::InNpmPackageChecker; use sys_traits::FsCanonicalize; use sys_traits::FsMetadata; +use url::Url; pub use self::common::NpmPackageFsResolver; pub use self::common::NpmPackageFsResolverPackageFolderError; @@ -43,3 +46,32 @@ pub fn create_npm_fs_resolver< )), } } + +#[derive(Debug)] +pub struct ManagedInNpmPackageChecker { + root_dir: Url, +} + +impl InNpmPackageChecker for ManagedInNpmPackageChecker { + fn in_npm_package(&self, specifier: &Url) -> bool { + specifier.as_ref().starts_with(self.root_dir.as_str()) + } +} + +pub struct ManagedInNpmPkgCheckerCreateOptions<'a> { + pub root_cache_dir_url: &'a Url, + pub maybe_node_modules_path: Option<&'a Path>, +} + +pub fn create_managed_in_npm_pkg_checker( + options: ManagedInNpmPkgCheckerCreateOptions, +) -> ManagedInNpmPackageChecker { + let root_dir = match options.maybe_node_modules_path { + Some(node_modules_folder) => { + deno_path_util::url_from_directory_path(node_modules_folder).unwrap() + } + None => options.root_cache_dir_url.clone(), + }; + debug_assert!(root_dir.as_str().ends_with('/')); + ManagedInNpmPackageChecker { root_dir } +} diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 54282e677d..4b102d6491 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -34,11 +34,32 @@ pub use self::byonm::ByonmNpmResolverRc; pub use self::byonm::ByonmResolvePkgFolderFromDenoReqError; pub use self::local::get_package_folder_id_folder_name; pub use self::local::normalize_pkg_name_for_node_modules_deno_folder; +use self::managed::create_managed_in_npm_pkg_checker; +use self::managed::ManagedInNpmPkgCheckerCreateOptions; +use crate::sync::new_rc; +use crate::sync::MaybeSend; +use crate::sync::MaybeSync; mod byonm; mod local; pub mod managed; +pub enum CreateInNpmPkgCheckerOptions<'a> { + Managed(ManagedInNpmPkgCheckerCreateOptions<'a>), + Byonm, +} + +pub fn create_in_npm_pkg_checker( + options: CreateInNpmPkgCheckerOptions, +) -> InNpmPackageCheckerRc { + match options { + CreateInNpmPkgCheckerOptions::Managed(options) => { + new_rc(create_managed_in_npm_pkg_checker(options)) + } + CreateInNpmPkgCheckerOptions::Byonm => new_rc(ByonmInNpmPackageChecker), + } +} + #[derive(Debug, Error, JsError)] #[class(generic)] #[error("Could not resolve \"{}\", but found it in a package.json. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", specifier)] @@ -99,7 +120,7 @@ pub type CliNpmReqResolverRc = crate::sync::MaybeArc; // todo(dsherret): a temporary trait until we extract // out the CLI npm resolver into here -pub trait CliNpmReqResolver: Debug + Send + Sync { +pub trait CliNpmReqResolver: Debug + MaybeSend + MaybeSync { fn resolve_pkg_folder_from_deno_module_req( &self, req: &PackageReq, diff --git a/resolvers/deno/sync.rs b/resolvers/deno/sync.rs index 10cdc5ec4d..1734e84231 100644 --- a/resolvers/deno/sync.rs +++ b/resolvers/deno/sync.rs @@ -6,6 +6,8 @@ pub use inner::*; mod inner { #![allow(clippy::disallowed_types)] + pub use core::marker::Send as MaybeSend; + pub use core::marker::Sync as MaybeSync; pub use std::sync::Arc as MaybeArc; pub use dashmap::DashMap as MaybeDashMap; @@ -21,6 +23,11 @@ mod inner { use std::hash::RandomState; pub use std::rc::Rc as MaybeArc; + pub trait MaybeSync {} + impl MaybeSync for T where T: ?Sized {} + pub trait MaybeSend {} + impl MaybeSend for T where T: ?Sized {} + // Wrapper struct that exposes a subset of `DashMap` API. #[derive(Debug)] pub struct MaybeDashMap(RefCell>); From 1d64670f9cb13cb9a61f5fafa3a763edae2fde80 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 9 Jan 2025 12:05:39 -0800 Subject: [PATCH 086/107] docs: added jsdoc for window.close() (#27608) --- cli/tsc/dts/lib.deno.window.d.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/cli/tsc/dts/lib.deno.window.d.ts b/cli/tsc/dts/lib.deno.window.d.ts index 8a516beaf6..251f338be6 100644 --- a/cli/tsc/dts/lib.deno.window.d.ts +++ b/cli/tsc/dts/lib.deno.window.d.ts @@ -83,8 +83,30 @@ declare var window: Window & typeof globalThis; declare var self: Window & typeof globalThis; /** @category Platform */ declare var closed: boolean; -/** @category Platform */ + +/** + * Exits the current Deno process. + * + * This function terminates the process by signaling the runtime to exit. + * Similar to exit(0) in posix. Its behavior is similar to the `window.close()` + * method in the browser, but specific to the Deno runtime. + * + * Note: Use this function cautiously, as it will stop the execution of the + * entire Deno program immediately. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/close + * + * @example + * ```ts + * console.log("About to close the Deno process."); + * close(); // The process will terminate here. + * console.log("This will not be logged."); // This line will never execute. + * ``` + * + * @category Platform + */ declare function close(): void; + /** @category Events */ declare var onerror: ((this: Window, ev: ErrorEvent) => any) | null; /** @category Events */ From 8bafb182ef6c9e096e3c2665ee5dfcaea447e637 Mon Sep 17 00:00:00 2001 From: denobot <33910674+denobot@users.noreply.github.com> Date: Thu, 9 Jan 2025 17:38:18 -0500 Subject: [PATCH 087/107] chore: forward v2.1.5 release commit to main (#27613) Co-authored-by: dsherret --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 8 ++-- Cargo.lock | 62 ++++++++++++------------- Cargo.toml | 60 ++++++++++++------------ Releases.md | 79 ++++++++++++++++++++++++++++++++ bench_util/Cargo.toml | 2 +- cli/Cargo.toml | 2 +- ext/broadcast_channel/Cargo.toml | 2 +- ext/cache/Cargo.toml | 2 +- ext/canvas/Cargo.toml | 2 +- ext/console/Cargo.toml | 2 +- ext/cron/Cargo.toml | 2 +- ext/crypto/Cargo.toml | 2 +- ext/fetch/Cargo.toml | 2 +- ext/ffi/Cargo.toml | 2 +- ext/fs/Cargo.toml | 2 +- ext/http/Cargo.toml | 2 +- ext/io/Cargo.toml | 2 +- ext/kv/Cargo.toml | 2 +- ext/napi/Cargo.toml | 2 +- ext/napi/sym/Cargo.toml | 2 +- ext/net/Cargo.toml | 2 +- ext/node/Cargo.toml | 2 +- ext/telemetry/Cargo.toml | 2 +- ext/tls/Cargo.toml | 2 +- ext/url/Cargo.toml | 2 +- ext/web/Cargo.toml | 2 +- ext/webgpu/Cargo.toml | 2 +- ext/webidl/Cargo.toml | 2 +- ext/websocket/Cargo.toml | 2 +- ext/webstorage/Cargo.toml | 2 +- resolvers/deno/Cargo.toml | 2 +- resolvers/node/Cargo.toml | 2 +- resolvers/npm_cache/Cargo.toml | 4 +- runtime/Cargo.toml | 2 +- runtime/permissions/Cargo.toml | 2 +- 36 files changed, 177 insertions(+), 98 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index c8980180a0..3214fad509 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -5,7 +5,7 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify"; // Bump this number when you want to purge the cache. // Note: the tools/release/01_bump_crate_versions.ts script will update this version // automatically via regex, so ensure that this line maintains this format. -const cacheVersion = 32; +const cacheVersion = 33; const ubuntuX86Runner = "ubuntu-24.04"; const ubuntuX86XlRunner = "ubuntu-24.04-xl"; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 524af7ac36..e560840d2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -184,8 +184,8 @@ jobs: ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db - key: '32-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '32-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' + key: '33-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '33-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' if: '!(matrix.skip)' - uses: dsherret/rust-toolchain-file@v1 if: '!(matrix.skip)' @@ -379,7 +379,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '32-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '33-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' - name: Apply and update mtime cache if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))' uses: ./.github/mtime_cache @@ -689,7 +689,7 @@ jobs: !./target/*/gn_root !./target/*/*.zip !./target/*/*.tar.gz - key: '32-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '33-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-24.04 diff --git a/Cargo.lock b/Cargo.lock index e94fd48410..d16aafe6f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1244,7 +1244,7 @@ dependencies = [ [[package]] name = "deno" -version = "2.1.4" +version = "2.1.5" dependencies = [ "anstream", "async-trait", @@ -1421,7 +1421,7 @@ dependencies = [ [[package]] name = "deno_bench_util" -version = "0.178.0" +version = "0.179.0" dependencies = [ "bencher", "deno_core", @@ -1430,7 +1430,7 @@ dependencies = [ [[package]] name = "deno_broadcast_channel" -version = "0.178.0" +version = "0.179.0" dependencies = [ "async-trait", "deno_core", @@ -1442,7 +1442,7 @@ dependencies = [ [[package]] name = "deno_cache" -version = "0.116.0" +version = "0.117.0" dependencies = [ "async-trait", "deno_core", @@ -1485,7 +1485,7 @@ dependencies = [ [[package]] name = "deno_canvas" -version = "0.53.0" +version = "0.54.0" dependencies = [ "deno_core", "deno_error", @@ -1523,7 +1523,7 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.184.0" +version = "0.185.0" dependencies = [ "deno_core", ] @@ -1574,7 +1574,7 @@ checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" [[package]] name = "deno_cron" -version = "0.64.0" +version = "0.65.0" dependencies = [ "anyhow", "async-trait", @@ -1588,7 +1588,7 @@ dependencies = [ [[package]] name = "deno_crypto" -version = "0.198.0" +version = "0.199.0" dependencies = [ "aes", "aes-gcm", @@ -1681,7 +1681,7 @@ dependencies = [ [[package]] name = "deno_fetch" -version = "0.208.0" +version = "0.209.0" dependencies = [ "base64 0.21.7", "bytes", @@ -1718,7 +1718,7 @@ dependencies = [ [[package]] name = "deno_ffi" -version = "0.171.0" +version = "0.172.0" dependencies = [ "deno_core", "deno_error", @@ -1739,7 +1739,7 @@ dependencies = [ [[package]] name = "deno_fs" -version = "0.94.0" +version = "0.95.0" dependencies = [ "async-trait", "base32", @@ -1797,7 +1797,7 @@ dependencies = [ [[package]] name = "deno_http" -version = "0.182.0" +version = "0.183.0" dependencies = [ "async-compression", "async-trait", @@ -1837,7 +1837,7 @@ dependencies = [ [[package]] name = "deno_io" -version = "0.94.0" +version = "0.95.0" dependencies = [ "async-trait", "deno_core", @@ -1859,7 +1859,7 @@ dependencies = [ [[package]] name = "deno_kv" -version = "0.92.0" +version = "0.93.0" dependencies = [ "anyhow", "async-trait", @@ -1933,7 +1933,7 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.115.0" +version = "0.116.0" dependencies = [ "deno_core", "deno_error", @@ -1962,7 +1962,7 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.176.0" +version = "0.177.0" dependencies = [ "deno_core", "deno_error", @@ -1981,7 +1981,7 @@ dependencies = [ [[package]] name = "deno_node" -version = "0.122.0" +version = "0.123.0" dependencies = [ "aead-gcm-stream", "aes", @@ -2094,7 +2094,7 @@ dependencies = [ [[package]] name = "deno_npm_cache" -version = "0.3.0" +version = "0.4.0" dependencies = [ "async-trait", "base64 0.21.7", @@ -2172,7 +2172,7 @@ dependencies = [ [[package]] name = "deno_permissions" -version = "0.43.0" +version = "0.44.0" dependencies = [ "capacity_builder 0.5.0", "deno_core", @@ -2192,7 +2192,7 @@ dependencies = [ [[package]] name = "deno_resolver" -version = "0.15.0" +version = "0.16.0" dependencies = [ "anyhow", "async-trait", @@ -2217,7 +2217,7 @@ dependencies = [ [[package]] name = "deno_runtime" -version = "0.192.0" +version = "0.193.0" dependencies = [ "color-print", "deno_ast", @@ -2323,7 +2323,7 @@ dependencies = [ [[package]] name = "deno_telemetry" -version = "0.6.0" +version = "0.7.0" dependencies = [ "async-trait", "deno_core", @@ -2366,7 +2366,7 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.171.0" +version = "0.172.0" dependencies = [ "deno_core", "deno_error", @@ -2417,7 +2417,7 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.184.0" +version = "0.185.0" dependencies = [ "deno_bench_util", "deno_console", @@ -2430,7 +2430,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.215.0" +version = "0.216.0" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -2453,7 +2453,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.151.0" +version = "0.152.0" dependencies = [ "deno_core", "deno_error", @@ -2467,7 +2467,7 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.184.0" +version = "0.185.0" dependencies = [ "deno_bench_util", "deno_core", @@ -2475,7 +2475,7 @@ dependencies = [ [[package]] name = "deno_websocket" -version = "0.189.0" +version = "0.190.0" dependencies = [ "bytes", "deno_core", @@ -2498,7 +2498,7 @@ dependencies = [ [[package]] name = "deno_webstorage" -version = "0.179.0" +version = "0.180.0" dependencies = [ "deno_core", "deno_error", @@ -5070,7 +5070,7 @@ dependencies = [ [[package]] name = "napi_sym" -version = "0.114.0" +version = "0.115.0" dependencies = [ "quote", "serde", @@ -5125,7 +5125,7 @@ dependencies = [ [[package]] name = "node_resolver" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 75572162c4..b7c7219fba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,17 +50,17 @@ repository = "https://github.com/denoland/deno" deno_ast = { version = "=0.44.0", features = ["transpiling"] } deno_core = { version = "0.330.0" } -deno_bench_util = { version = "0.178.0", path = "./bench_util" } +deno_bench_util = { version = "0.179.0", path = "./bench_util" } deno_config = { version = "=0.43.0", features = ["workspace", "sync"] } deno_lockfile = "=0.24.0" deno_media_type = { version = "0.2.3", features = ["module_specifier"] } deno_npm = "=0.27.0" deno_path_util = "=0.3.0" -deno_permissions = { version = "0.43.0", path = "./runtime/permissions" } -deno_runtime = { version = "0.192.0", path = "./runtime" } +deno_permissions = { version = "0.44.0", path = "./runtime/permissions" } +deno_runtime = { version = "0.193.0", path = "./runtime" } deno_semver = "=0.7.1" deno_terminal = "0.2.0" -napi_sym = { version = "0.114.0", path = "./ext/napi/sym" } +napi_sym = { version = "0.115.0", path = "./ext/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } denokv_proto = "0.9.0" @@ -69,34 +69,34 @@ denokv_remote = "0.9.0" denokv_sqlite = { default-features = false, version = "0.9.0" } # exts -deno_broadcast_channel = { version = "0.178.0", path = "./ext/broadcast_channel" } -deno_cache = { version = "0.116.0", path = "./ext/cache" } -deno_canvas = { version = "0.53.0", path = "./ext/canvas" } -deno_console = { version = "0.184.0", path = "./ext/console" } -deno_cron = { version = "0.64.0", path = "./ext/cron" } -deno_crypto = { version = "0.198.0", path = "./ext/crypto" } -deno_fetch = { version = "0.208.0", path = "./ext/fetch" } -deno_ffi = { version = "0.171.0", path = "./ext/ffi" } -deno_fs = { version = "0.94.0", path = "./ext/fs" } -deno_http = { version = "0.182.0", path = "./ext/http" } -deno_io = { version = "0.94.0", path = "./ext/io" } -deno_kv = { version = "0.92.0", path = "./ext/kv" } -deno_napi = { version = "0.115.0", path = "./ext/napi" } -deno_net = { version = "0.176.0", path = "./ext/net" } -deno_node = { version = "0.122.0", path = "./ext/node" } -deno_telemetry = { version = "0.6.0", path = "./ext/telemetry" } -deno_tls = { version = "0.171.0", path = "./ext/tls" } -deno_url = { version = "0.184.0", path = "./ext/url" } -deno_web = { version = "0.215.0", path = "./ext/web" } -deno_webgpu = { version = "0.151.0", path = "./ext/webgpu" } -deno_webidl = { version = "0.184.0", path = "./ext/webidl" } -deno_websocket = { version = "0.189.0", path = "./ext/websocket" } -deno_webstorage = { version = "0.179.0", path = "./ext/webstorage" } +deno_broadcast_channel = { version = "0.179.0", path = "./ext/broadcast_channel" } +deno_cache = { version = "0.117.0", path = "./ext/cache" } +deno_canvas = { version = "0.54.0", path = "./ext/canvas" } +deno_console = { version = "0.185.0", path = "./ext/console" } +deno_cron = { version = "0.65.0", path = "./ext/cron" } +deno_crypto = { version = "0.199.0", path = "./ext/crypto" } +deno_fetch = { version = "0.209.0", path = "./ext/fetch" } +deno_ffi = { version = "0.172.0", path = "./ext/ffi" } +deno_fs = { version = "0.95.0", path = "./ext/fs" } +deno_http = { version = "0.183.0", path = "./ext/http" } +deno_io = { version = "0.95.0", path = "./ext/io" } +deno_kv = { version = "0.93.0", path = "./ext/kv" } +deno_napi = { version = "0.116.0", path = "./ext/napi" } +deno_net = { version = "0.177.0", path = "./ext/net" } +deno_node = { version = "0.123.0", path = "./ext/node" } +deno_telemetry = { version = "0.7.0", path = "./ext/telemetry" } +deno_tls = { version = "0.172.0", path = "./ext/tls" } +deno_url = { version = "0.185.0", path = "./ext/url" } +deno_web = { version = "0.216.0", path = "./ext/web" } +deno_webgpu = { version = "0.152.0", path = "./ext/webgpu" } +deno_webidl = { version = "0.185.0", path = "./ext/webidl" } +deno_websocket = { version = "0.190.0", path = "./ext/websocket" } +deno_webstorage = { version = "0.180.0", path = "./ext/webstorage" } # resolvers -deno_npm_cache = { version = "0.3.0", path = "./resolvers/npm_cache" } -deno_resolver = { version = "0.15.0", path = "./resolvers/deno" } -node_resolver = { version = "0.22.0", path = "./resolvers/node" } +deno_npm_cache = { version = "0.4.0", path = "./resolvers/npm_cache" } +deno_resolver = { version = "0.16.0", path = "./resolvers/deno" } +node_resolver = { version = "0.23.0", path = "./resolvers/node" } aes = "=0.8.3" anyhow = "1.0.57" diff --git a/Releases.md b/Releases.md index aaae202a37..71cf524ca2 100644 --- a/Releases.md +++ b/Releases.md @@ -6,6 +6,85 @@ https://github.com/denoland/deno/releases We also have one-line install commands at: https://github.com/denoland/deno_install +### 2.1.5 / 2025.01.09 + +- feat(unstable): implement QUIC (#21942) +- feat(unstable): add JS linting plugin infrastructure (#27416) +- feat(unstable): add OTEL MeterProvider (#27240) +- feat(unstable): no config npm:@opentelemetry/api integration (#27541) +- feat(unstable): replace SpanExporter with TracerProvider (#27473) +- feat(unstable): support selectors in JS lint plugins (#27452) +- fix(check): line-break between diagnostic message chain entries (#27543) +- fix(check): move module not found errors to typescript diagnostics (#27533) +- fix(compile): analyze modules in directory specified in --include (#27296) +- fix(compile): be more deterministic when compiling the same code in different + directories (#27395) +- fix(compile): display embedded file sizes and total (#27360) +- fix(compile): output contents of embedded file system (#27302) +- fix(ext/fetch): better error message when body resource is unavailable + (#27429) +- fix(ext/fetch): retry some http/2 errors (#27417) +- fix(ext/fs): do not throw for bigint ctime/mtime/atime (#27453) +- fix(ext/http): improve error message when underlying resource of request body + unavailable (#27463) +- fix(ext/net): update moka cache to avoid potential panic in `Deno.resolveDns` + on some laptops with Ryzen CPU (#27572) +- fix(ext/node): fix `fs.access`/`fs.promises.access` with `X_OK` mode parameter + on Windows (#27407) +- fix(ext/node): fix `os.cpus()` on Linux (#27592) +- fix(ext/node): RangeError timingSafeEqual with different byteLength (#27470) +- fix(ext/node): add `truncate` method to the `FileHandle` class (#27389) +- fix(ext/node): add support of any length IV for aes-(128|256)-gcm ciphers + (#27476) +- fix(ext/node): convert brotli chunks with proper byte offset (#27455) +- fix(ext/node): do not exit worker thread when there is pending async op + (#27378) +- fix(ext/node): have `process` global available in Node context (#27562) +- fix(ext/node): make getCiphers return supported ciphers (#27466) +- fix(ext/node): sort list of built-in modules alphabetically (#27410) +- fix(ext/node): support createConnection option in node:http.request() (#25470) +- fix(ext/node): support private key export in JWK format (#27325) +- fix(ext/web): add `[[ErrorData]]` slot to `DOMException` (#27342) +- fix(ext/websocket): Fix close code without reason (#27578) +- fix(jsr): Wasm imports fail to load (#27594) +- fix(kv): improve backoff error message and inline documentation (#27537) +- fix(lint): fix single char selectors being ignored (#27576) +- fix(lockfile): include dependencies listed in external import map in lockfile + (#27337) +- fix(lsp): css preprocessor formatting (#27526) +- fix(lsp): don't skip dirs with enabled subdirs (#27580) +- fix(lsp): include "node:" prefix for node builtin auto-imports (#27404) +- fix(lsp): respect "typescript.suggestionActions.enabled" setting (#27373) +- fix(lsp): rewrite imports for 'Move to a new file' action (#27427) +- fix(lsp): sql and component file formatting (#27350) +- fix(lsp): use verbatim specifier for URL auto-imports (#27605) +- fix(no-slow-types): handle rest param with internal assignments (#27581) +- fix(node/fs): add a chmod method to the FileHandle class (#27522) +- fix(node): add missing `inspector/promises` (#27491) +- fix(node): handle cjs exports with escaped chars (#27438) +- fix(npm): deterministically output tags to initialized file (#27514) +- fix(npm): search node_modules folder for package matching npm specifier + (#27345) +- fix(outdated): ensure "Latest" version is greater than "Update" version + (#27390) +- fix(outdated): support updating dependencies in external import maps (#27339) +- fix(permissions): implicit `--allow-import` when using `--cached-only` + (#27530) +- fix(publish): infer literal types in const contexts (#27425) +- fix(task): properly handle task name wildcards with --recursive (#27396) +- fix(task): support tasks without commands (#27191) +- fix(unstable): don't error on non-existing attrs or type attr (#27456) +- fix: FastString v8_string() should error when cannot allocated (#27375) +- fix: deno_resolver crate without 'sync' feature (#27403) +- fix: incorrect memory info free/available bytes on mac (#27460) +- fix: upgrade deno_doc to 0.161.3 (#27377) +- perf(fs/windows): stat - only open file once (#27487) +- perf(node/fs/copy): reduce metadata lookups copying directory (#27495) +- perf: don't store duplicate info for ops in the snapshot (#27430) +- perf: remove now needless canonicalization getting closest package.json + (#27437) +- perf: upgrade to deno_semver 0.7 (#27426) + ### 2.1.4 / 2024.12.11 - feat(unstable): support caching npm dependencies only as they're needed diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index 3172426c27..30a88e931e 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_bench_util" -version = "0.178.0" +version = "0.179.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b77c904c40..8248d40701 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno" -version = "2.1.4" +version = "2.1.5" authors.workspace = true default-run = "deno" edition.workspace = true diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index 27aa7df702..0e1e0c3fbd 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_broadcast_channel" -version = "0.178.0" +version = "0.179.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index b77630832f..18fbe23a23 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cache" -version = "0.116.0" +version = "0.117.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index 11c6feabb8..e5c70b0054 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_canvas" -version = "0.53.0" +version = "0.54.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/console/Cargo.toml b/ext/console/Cargo.toml index b4811265ba..24cdb040a7 100644 --- a/ext/console/Cargo.toml +++ b/ext/console/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_console" -version = "0.184.0" +version = "0.185.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index 224508265a..7b41593841 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cron" -version = "0.64.0" +version = "0.65.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 96ddd1621f..c9876105e5 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_crypto" -version = "0.198.0" +version = "0.199.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index 21f77153b9..a0d29291b0 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fetch" -version = "0.208.0" +version = "0.209.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index b78aa36d7c..d71c04f9ff 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_ffi" -version = "0.171.0" +version = "0.172.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index 05141e46c9..df9d140ac3 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fs" -version = "0.94.0" +version = "0.95.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index 1ecb6f66c8..01198cb8e6 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_http" -version = "0.182.0" +version = "0.183.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 9d11e1b0f6..0de9dfca84 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_io" -version = "0.94.0" +version = "0.95.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index 1a1cd346fb..726dccdfba 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_kv" -version = "0.92.0" +version = "0.93.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index ac62cc5dc8..ff69d6a47e 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_napi" -version = "0.115.0" +version = "0.116.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/sym/Cargo.toml b/ext/napi/sym/Cargo.toml index c832a1fa30..f845b639ba 100644 --- a/ext/napi/sym/Cargo.toml +++ b/ext/napi/sym/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "napi_sym" -version = "0.114.0" +version = "0.115.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index ad20badb10..ba02fe8708 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_net" -version = "0.176.0" +version = "0.177.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 19936b74f1..6cd2b32866 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_node" -version = "0.122.0" +version = "0.123.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/telemetry/Cargo.toml b/ext/telemetry/Cargo.toml index 484a90eeb1..a83a08c3fe 100644 --- a/ext/telemetry/Cargo.toml +++ b/ext/telemetry/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_telemetry" -version = "0.6.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index 1e804bd538..39b3d17c86 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_tls" -version = "0.171.0" +version = "0.172.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index 1f7f7b36c6..2078571e39 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_url" -version = "0.184.0" +version = "0.185.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index d5dbbdecca..065f1a12b1 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_web" -version = "0.215.0" +version = "0.216.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 4bb9fa9a41..369fdb02e6 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.151.0" +version = "0.152.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" diff --git a/ext/webidl/Cargo.toml b/ext/webidl/Cargo.toml index ab285c7204..adb072ff45 100644 --- a/ext/webidl/Cargo.toml +++ b/ext/webidl/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webidl" -version = "0.184.0" +version = "0.185.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index dc24d52e16..a94da90484 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_websocket" -version = "0.189.0" +version = "0.190.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index 044aa41f6a..8a900f662b 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webstorage" -version = "0.179.0" +version = "0.180.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 7a5d2833d0..1c603ecaed 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_resolver" -version = "0.15.0" +version = "0.16.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/node/Cargo.toml b/resolvers/node/Cargo.toml index 31bca50a31..1c2a342a9f 100644 --- a/resolvers/node/Cargo.toml +++ b/resolvers/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "node_resolver" -version = "0.22.0" +version = "0.23.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/npm_cache/Cargo.toml b/resolvers/npm_cache/Cargo.toml index 3cefc6538a..d73cee9cd6 100644 --- a/resolvers/npm_cache/Cargo.toml +++ b/resolvers/npm_cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_npm_cache" -version = "0.3.0" +version = "0.4.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -18,7 +18,7 @@ async-trait.workspace = true base64.workspace = true boxed_error.workspace = true deno_cache_dir.workspace = true -deno_error.workspace = true +deno_error = { workspace = true, features = ["serde", "serde_json", "tokio"] } deno_npm.workspace = true deno_path_util.workspace = true deno_semver.workspace = true diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 72db8888f8..5ecd911e8a 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_runtime" -version = "0.192.0" +version = "0.193.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index be397fe6d3..52501ee197 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_permissions" -version = "0.43.0" +version = "0.44.0" authors.workspace = true edition.workspace = true license.workspace = true From 34beeb7703d1845e31dce169b90f012834689808 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 9 Jan 2025 18:30:48 -0500 Subject: [PATCH 088/107] refactor(npm): move `SloppyImportsCachedFs` to deno_resolver (#27610) --- cli/factory.rs | 2 +- cli/graph_util.rs | 2 +- cli/lsp/config.rs | 2 +- cli/lsp/diagnostics.rs | 2 +- cli/resolver.rs | 68 ++------------------------------ cli/tools/registry/unfurl.rs | 2 +- resolvers/deno/sloppy_imports.rs | 48 ++++++++++++++++++++++ 7 files changed, 57 insertions(+), 69 deletions(-) diff --git a/cli/factory.rs b/cli/factory.rs index 09b8004366..d545fd6ddf 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -15,6 +15,7 @@ use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::DenoResolverOptions; use deno_resolver::NodeAndNpmReqResolver; use deno_runtime::deno_fs; @@ -80,7 +81,6 @@ use crate::resolver::CliResolver; use crate::resolver::CliResolverOptions; use crate::resolver::CliSloppyImportsResolver; use crate::resolver::NpmModuleLoader; -use crate::resolver::SloppyImportsCachedFs; use crate::standalone::binary::DenoCompileBinaryWriter; use crate::sys::CliSys; use crate::tools::check::TypeChecker; diff --git a/cli/graph_util.rs b/cli/graph_util.rs index f32dae8a07..84beee027e 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -29,6 +29,7 @@ use deno_graph::ResolutionError; use deno_graph::SpecifierError; use deno_graph::WorkspaceFastCheckOption; use deno_path_util::url_to_file_path; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; use deno_runtime::deno_node; use deno_runtime::deno_permissions::PermissionsContainer; @@ -55,7 +56,6 @@ use crate::npm::CliNpmResolver; use crate::resolver::CjsTracker; use crate::resolver::CliResolver; use crate::resolver::CliSloppyImportsResolver; -use crate::resolver::SloppyImportsCachedFs; use crate::sys::CliSys; use crate::tools::check; use crate::tools::check::CheckError; diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 0cd8468153..7841ee0783 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -46,6 +46,7 @@ use deno_lint::linter::LintConfig as DenoLintConfig; use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonCache; use deno_path_util::url_to_file_path; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_runtime::deno_node::PackageJson; use indexmap::IndexSet; use lsp_types::ClientCapabilities; @@ -65,7 +66,6 @@ use crate::cache::FastInsecureHasher; use crate::file_fetcher::CliFileFetcher; use crate::lsp::logging::lsp_warn; use crate::resolver::CliSloppyImportsResolver; -use crate::resolver::SloppyImportsCachedFs; use crate::sys::CliSys; use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinterOptions; diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 0982ff5ceb..42a1a0c52a 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -26,6 +26,7 @@ use deno_graph::Resolution; use deno_graph::ResolutionError; use deno_graph::SpecifierError; use deno_lint::linter::LintConfig as DenoLintConfig; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::sloppy_imports::SloppyImportsResolution; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; use deno_runtime::deno_node; @@ -61,7 +62,6 @@ use crate::graph_util; use crate::graph_util::enhanced_resolution_error_message; use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams; use crate::resolver::CliSloppyImportsResolver; -use crate::resolver::SloppyImportsCachedFs; use crate::sys::CliSys; use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinterOptions; diff --git a/cli/resolver.rs b/cli/resolver.rs index 7873a9cce0..1d12d5f8b7 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -1,12 +1,9 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; use std::sync::Arc; use async_trait::async_trait; -use dashmap::DashMap; use dashmap::DashSet; use deno_ast::MediaType; use deno_config::workspace::MappedResolutionDiagnostic; @@ -22,6 +19,7 @@ use deno_graph::source::UnknownBuiltInNodeModuleError; use deno_graph::NpmLoadError; use deno_graph::NpmResolvePkgReqsResult; use deno_npm::resolution::NpmResolutionError; +use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::sloppy_imports::SloppyImportsResolver; use deno_runtime::colors; use deno_runtime::deno_fs; @@ -30,8 +28,6 @@ use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use deno_semver::package::PackageReq; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; -use sys_traits::FsMetadata; -use sys_traits::FsMetadataValue; use thiserror::Error; use crate::args::NpmCachingStrategy; @@ -45,11 +41,12 @@ use crate::util::text_encoding::from_utf8_lossy_cow; pub type CjsTracker = deno_resolver::cjs::CjsTracker; pub type IsCjsResolver = deno_resolver::cjs::IsCjsResolver; +pub type CliSloppyImportsCachedFs = SloppyImportsCachedFs; pub type CliSloppyImportsResolver = - SloppyImportsResolver; + SloppyImportsResolver; pub type CliDenoResolver = deno_resolver::DenoResolver< RealIsBuiltInNodeModuleChecker, - SloppyImportsCachedFs, + CliSloppyImportsCachedFs, CliSys, >; pub type CliNpmReqResolver = @@ -397,60 +394,3 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { self.bare_node_builtins_enabled } } - -#[derive(Debug)] -pub struct SloppyImportsCachedFs { - sys: CliSys, - cache: Option< - DashMap< - PathBuf, - Option, - >, - >, -} - -impl SloppyImportsCachedFs { - pub fn new(sys: CliSys) -> Self { - Self { - sys, - cache: Some(Default::default()), - } - } - - pub fn new_without_stat_cache(fs: CliSys) -> Self { - Self { - sys: fs, - cache: None, - } - } -} - -impl deno_resolver::sloppy_imports::SloppyImportResolverFs - for SloppyImportsCachedFs -{ - fn stat_sync( - &self, - path: &Path, - ) -> Option { - if let Some(cache) = &self.cache { - if let Some(entry) = cache.get(path) { - return *entry; - } - } - - let entry = self.sys.fs_metadata(path).ok().and_then(|stat| { - if stat.file_type().is_file() { - Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::File) - } else if stat.file_type().is_dir() { - Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::Dir) - } else { - None - } - }); - - if let Some(cache) = &self.cache { - cache.insert(path.to_owned(), entry); - } - entry - } -} diff --git a/cli/tools/registry/unfurl.rs b/cli/tools/registry/unfurl.rs index e3501c348b..e3fd4e715b 100644 --- a/cli/tools/registry/unfurl.rs +++ b/cli/tools/registry/unfurl.rs @@ -660,6 +660,7 @@ mod tests { use deno_config::workspace::ResolverWorkspaceJsrPackage; use deno_core::serde_json::json; use deno_core::url::Url; + use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_runtime::deno_node::PackageJson; use deno_semver::Version; use import_map::ImportMapWithDiagnostics; @@ -668,7 +669,6 @@ mod tests { use test_util::testdata_path; use super::*; - use crate::resolver::SloppyImportsCachedFs; use crate::sys::CliSys; fn parse_ast(specifier: &Url, source_code: &str) -> ParsedSource { diff --git a/resolvers/deno/sloppy_imports.rs b/resolvers/deno/sloppy_imports.rs index b6fbf487dd..486d2dab1e 100644 --- a/resolvers/deno/sloppy_imports.rs +++ b/resolvers/deno/sloppy_imports.rs @@ -7,8 +7,12 @@ use std::path::PathBuf; use deno_media_type::MediaType; use deno_path_util::url_from_file_path; use deno_path_util::url_to_file_path; +use sys_traits::FsMetadata; +use sys_traits::FsMetadataValue; use url::Url; +use crate::sync::MaybeDashMap; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SloppyImportsFsEntry { File, @@ -368,6 +372,50 @@ impl SloppyImportsResolver { } } +#[derive(Debug)] +pub struct SloppyImportsCachedFs { + sys: TSys, + cache: Option>>, +} + +impl SloppyImportsCachedFs { + pub fn new(sys: TSys) -> Self { + Self { + sys, + cache: Some(Default::default()), + } + } + + pub fn new_without_stat_cache(sys: TSys) -> Self { + Self { sys, cache: None } + } +} + +impl SloppyImportResolverFs for SloppyImportsCachedFs { + fn stat_sync(&self, path: &Path) -> Option { + if let Some(cache) = &self.cache { + if let Some(entry) = cache.get(path) { + return *entry; + } + } + + let entry = self.sys.fs_metadata(path).ok().and_then(|stat| { + if stat.file_type().is_file() { + Some(SloppyImportsFsEntry::File) + } else if stat.file_type().is_dir() { + Some(SloppyImportsFsEntry::Dir) + } else { + None + } + }); + + if let Some(cache) = &self.cache { + cache.insert(path.to_owned(), entry); + } + entry + } +} + #[cfg(test)] mod test { use test_util::TestContext; From 475793f94dadbe90495be69bc49173407fe101f7 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 9 Jan 2025 19:01:47 -0500 Subject: [PATCH 089/107] refactor: implement `NpmPackageFolderResolver` in deno_resolver (#27614) --- Cargo.lock | 4 +- Cargo.toml | 2 +- cli/npm/mod.rs | 2 +- resolvers/deno/npm/byonm.rs | 4 +- resolvers/deno/npm/managed/common.rs | 14 ++---- resolvers/deno/npm/managed/global.rs | 41 +++++++++--------- resolvers/deno/npm/managed/local.rs | 65 ++++++++++++++++------------ resolvers/deno/npm/managed/mod.rs | 2 +- 8 files changed, 69 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d16aafe6f0..71979d448a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7762,9 +7762,9 @@ dependencies = [ [[package]] name = "sys_traits" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c12873696bde6de3aea3cd27de8e52897177c5b368a6a30987fd4926e30f85" +checksum = "5b46ac05dfbe9fd3a9703eff20e17f5b31e7b6a54daf27a421dcd56c7a27ecdd" dependencies = [ "filetime", "getrandom", diff --git a/Cargo.toml b/Cargo.toml index b7c7219fba..48abe48305 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -193,7 +193,7 @@ slab = "0.4" smallvec = "1.8" socket2 = { version = "0.5.3", features = ["all"] } spki = "0.7.2" -sys_traits = "=0.1.6" +sys_traits = "=0.1.7" tar = "=0.4.40" tempfile = "3.4.0" termcolor = "1.1.3" diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 9ac478732d..837fe26cf9 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -136,7 +136,7 @@ pub enum InnerCliNpmResolverRef<'a> { Byonm(&'a CliByonmNpmResolver), } -pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver { +pub trait CliNpmResolver: CliNpmReqResolver { fn into_npm_pkg_folder_resolver( self: Arc, ) -> Arc; diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index e9aad66e3f..710608490a 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -400,14 +400,14 @@ impl< } impl< - Sys: FsCanonicalize + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir + Send + Sync + std::fmt::Debug, - > NpmPackageFolderResolver for ByonmNpmResolver + > NpmPackageFolderResolver for ByonmNpmResolver { fn resolve_package_folder_from_package( &self, diff --git a/resolvers/deno/npm/managed/common.rs b/resolvers/deno/npm/managed/common.rs index 5a4a517bf0..8a523f3ea4 100644 --- a/resolvers/deno/npm/managed/common.rs +++ b/resolvers/deno/npm/managed/common.rs @@ -3,10 +3,9 @@ use std::path::Path; use std::path::PathBuf; -use async_trait::async_trait; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; -use node_resolver::errors::PackageFolderResolveError; +use node_resolver::NpmPackageFolderResolver; use url::Url; use crate::sync::MaybeSend; @@ -22,8 +21,9 @@ pub(super) type NpmPackageFsResolverRc = pub struct NpmPackageFsResolverPackageFolderError(deno_semver::StackString); /// Part of the resolution that interacts with the file system. -#[async_trait(?Send)] -pub trait NpmPackageFsResolver: MaybeSend + MaybeSync { +pub trait NpmPackageFsResolver: + NpmPackageFolderResolver + MaybeSend + MaybeSync +{ /// The local node_modules folder if it is applicable to the implementation. fn node_modules_path(&self) -> Option<&Path>; @@ -38,12 +38,6 @@ pub trait NpmPackageFsResolver: MaybeSend + MaybeSync { }) } - fn resolve_package_folder_from_package( - &self, - name: &str, - referrer: &Url, - ) -> Result; - fn resolve_package_cache_folder_id_from_specifier( &self, specifier: &Url, diff --git a/resolvers/deno/npm/managed/global.rs b/resolvers/deno/npm/managed/global.rs index 16a2bc7a70..cffe68a30e 100644 --- a/resolvers/deno/npm/managed/global.rs +++ b/resolvers/deno/npm/managed/global.rs @@ -5,7 +5,6 @@ use std::path::Path; use std::path::PathBuf; -use async_trait::async_trait; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_semver::package::PackageNv; @@ -14,6 +13,7 @@ use deno_semver::Version; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::ReferrerNotFoundError; +use node_resolver::NpmPackageFolderResolver; use url::Url; use super::resolution::NpmResolutionRc; @@ -61,25 +61,7 @@ impl GlobalNpmPackageResolver { } } -#[async_trait(?Send)] -impl NpmPackageFsResolver for GlobalNpmPackageResolver { - fn node_modules_path(&self) -> Option<&Path> { - None - } - - fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { - let folder_copy_index = self - .resolution - .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; - let registry_url = self.npm_rc.get_registry_url(&id.nv.name); - Some(self.cache.package_folder_for_id( - &id.nv.name, - &id.nv.version.to_string(), - folder_copy_index, - registry_url, - )) - } - +impl NpmPackageFolderResolver for GlobalNpmPackageResolver { fn resolve_package_folder_from_package( &self, name: &str, @@ -138,6 +120,25 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver { }, } } +} + +impl NpmPackageFsResolver for GlobalNpmPackageResolver { + fn node_modules_path(&self) -> Option<&Path> { + None + } + + fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { + let folder_copy_index = self + .resolution + .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; + let registry_url = self.npm_rc.get_registry_url(&id.nv.name); + Some(self.cache.package_folder_for_id( + &id.nv.name, + &id.nv.version.to_string(), + folder_copy_index, + registry_url, + )) + } fn resolve_package_cache_folder_id_from_specifier( &self, diff --git a/resolvers/deno/npm/managed/local.rs b/resolvers/deno/npm/managed/local.rs index e84de964ad..f23f7bd591 100644 --- a/resolvers/deno/npm/managed/local.rs +++ b/resolvers/deno/npm/managed/local.rs @@ -6,7 +6,6 @@ use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; -use async_trait::async_trait; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_path_util::fs::canonicalize_path_maybe_not_exists; @@ -15,6 +14,7 @@ use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::ReferrerNotFoundError; +use node_resolver::NpmPackageFolderResolver; use sys_traits::FsCanonicalize; use sys_traits::FsMetadata; use url::Url; @@ -99,33 +99,9 @@ impl } } -#[async_trait(?Send)] -impl NpmPackageFsResolver - for LocalNpmPackageResolver +impl + NpmPackageFolderResolver for LocalNpmPackageResolver { - fn node_modules_path(&self) -> Option<&Path> { - Some(self.root_node_modules_path.as_ref()) - } - - fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { - let folder_copy_index = self - .resolution - .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; - // package is stored at: - // node_modules/.deno//node_modules/ - Some( - self - .root_node_modules_path - .join(".deno") - .join(get_package_folder_id_folder_name_from_parts( - &id.nv, - folder_copy_index, - )) - .join("node_modules") - .join(&id.nv.name), - ) - } - fn resolve_package_folder_from_package( &self, name: &str, @@ -159,7 +135,13 @@ impl NpmPackageFsResolver let sub_dir = join_package_name(&node_modules_folder, name); if self.sys.fs_is_dir_no_err(&sub_dir) { - return Ok(sub_dir); + return Ok(self.sys.fs_canonicalize(&sub_dir).map_err(|err| { + PackageFolderResolveIoError { + package_name: name.to_string(), + referrer: referrer.clone(), + source: err, + } + })?); } if current_folder == self.root_node_modules_path { @@ -176,6 +158,33 @@ impl NpmPackageFsResolver .into(), ) } +} + +impl + NpmPackageFsResolver for LocalNpmPackageResolver +{ + fn node_modules_path(&self) -> Option<&Path> { + Some(self.root_node_modules_path.as_ref()) + } + + fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { + let folder_copy_index = self + .resolution + .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; + // package is stored at: + // node_modules/.deno//node_modules/ + Some( + self + .root_node_modules_path + .join(".deno") + .join(get_package_folder_id_folder_name_from_parts( + &id.nv, + folder_copy_index, + )) + .join("node_modules") + .join(&id.nv.name), + ) + } fn resolve_package_cache_folder_id_from_specifier( &self, diff --git a/resolvers/deno/npm/managed/mod.rs b/resolvers/deno/npm/managed/mod.rs index 53b07f7d90..d08ee07d6a 100644 --- a/resolvers/deno/npm/managed/mod.rs +++ b/resolvers/deno/npm/managed/mod.rs @@ -25,7 +25,7 @@ use crate::NpmCacheDirRc; use crate::ResolvedNpmRcRc; pub fn create_npm_fs_resolver< - TSys: FsCanonicalize + FsMetadata + Send + Sync + 'static, + TSys: FsCanonicalize + FsMetadata + std::fmt::Debug + Send + Sync + 'static, >( npm_cache_dir: &NpmCacheDirRc, npm_rc: &ResolvedNpmRcRc, From 1dd5bd667cb61fc21bee6ef0a701cf46a4ed0991 Mon Sep 17 00:00:00 2001 From: Rajhans Jadhao Date: Fri, 10 Jan 2025 18:21:50 +0530 Subject: [PATCH 090/107] fix(ext/node): use primordials in `ext/node/polyfills/_fs_common.ts` (#27589) Related to #24236 --- ext/node/polyfills/_fs/_fs_common.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ext/node/polyfills/_fs/_fs_common.ts b/ext/node/polyfills/_fs/_fs_common.ts index 9ec474afa9..8358f0271c 100644 --- a/ext/node/polyfills/_fs/_fs_common.ts +++ b/ext/node/polyfills/_fs/_fs_common.ts @@ -1,8 +1,12 @@ // Copyright 2018-2025 the Deno authors. MIT license. -// TODO(petamoriken): enable prefer-primordials for node polyfills -// deno-lint-ignore-file prefer-primordials - +import { primordials } from "ext:core/mod.js"; +const { + StringPrototypeToLowerCase, + ArrayPrototypeIncludes, + ReflectApply, + Error, +} = primordials; import { O_APPEND, O_CREAT, @@ -85,8 +89,10 @@ export function getEncoding( export function checkEncoding(encoding: Encodings | null): Encodings | null { if (!encoding) return null; - encoding = encoding.toLowerCase() as Encodings; - if (["utf8", "hex", "base64", "ascii"].includes(encoding)) return encoding; + encoding = StringPrototypeToLowerCase(encoding) as Encodings; + if (ArrayPrototypeIncludes(["utf8", "hex", "base64", "ascii"], encoding)) { + return encoding; + } if (encoding === "utf-8") { return "utf8"; @@ -99,7 +105,7 @@ export function checkEncoding(encoding: Encodings | null): Encodings | null { const notImplementedEncodings = ["utf16le", "latin1", "ucs2"]; - if (notImplementedEncodings.includes(encoding as string)) { + if (ArrayPrototypeIncludes(notImplementedEncodings, encoding as string)) { notImplemented(`"${encoding}" encoding`); } @@ -241,5 +247,5 @@ export function makeCallback( ) { validateFunction(cb, "cb"); - return (...args: unknown[]) => Reflect.apply(cb!, this, args); + return (...args: unknown[]) => ReflectApply(cb!, this, args); } From c27248a8f36155d1677385998e36028a01fea02d Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 10 Jan 2025 14:48:43 -0500 Subject: [PATCH 091/107] refactor: remove `CliNpmReqResolver` trait in deno_resolver (#27616) --- Cargo.lock | 1 + cli/factory.rs | 3 +- cli/lsp/resolver.rs | 3 +- cli/npm/byonm.rs | 26 +++- cli/npm/managed/mod.rs | 176 +++++++++-------------- cli/npm/mod.rs | 19 ++- cli/standalone/mod.rs | 3 +- resolvers/deno/Cargo.toml | 1 + resolvers/deno/npm/byonm.rs | 30 +--- resolvers/deno/npm/managed/common.rs | 16 +-- resolvers/deno/npm/managed/local.rs | 16 ++- resolvers/deno/npm/managed/mod.rs | 176 ++++++++++++++++++++--- resolvers/deno/npm/managed/resolution.rs | 2 +- resolvers/deno/npm/mod.rs | 60 +++++--- resolvers/deno/sync.rs | 8 +- 15 files changed, 324 insertions(+), 216 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 71979d448a..1f13898fe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2207,6 +2207,7 @@ dependencies = [ "deno_package_json", "deno_path_util", "deno_semver", + "log", "node_resolver", "parking_lot", "sys_traits", diff --git a/cli/factory.rs b/cli/factory.rs index d545fd6ddf..5cc99830bc 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -720,11 +720,10 @@ impl CliFactory { .get_or_try_init_async(async { let npm_resolver = self.npm_resolver().await?; Ok(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { - byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), sys: self.sys(), in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(), node_resolver: self.node_resolver().await?.clone(), - npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), + npm_resolver: npm_resolver.clone().into_byonm_or_managed(), }))) }) .await diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 9ae28405bf..57ef2e6a3c 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -796,10 +796,9 @@ impl<'a> ResolverFactory<'a> { let node_resolver = self.node_resolver()?; let npm_resolver = self.npm_resolver()?; Some(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { - byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), in_npm_pkg_checker: self.in_npm_pkg_checker().clone(), node_resolver: node_resolver.clone(), - npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), + npm_resolver: npm_resolver.clone().into_byonm_or_managed(), sys: self.sys.clone(), }))) }) diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index 2c11a417f3..bd29a6ec72 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -1,13 +1,17 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::path::Path; +use std::path::PathBuf; use std::sync::Arc; use deno_core::serde_json; +use deno_core::url::Url; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmNpmResolverCreateOptions; -use deno_resolver::npm::CliNpmReqResolver; +use deno_resolver::npm::ByonmOrManagedNpmResolver; +use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_runtime::ops::process::NpmProcessStateProvider; +use deno_semver::package::PackageReq; use node_resolver::NpmPackageFolderResolver; use super::CliNpmResolver; @@ -44,18 +48,16 @@ impl CliNpmResolver for CliByonmNpmResolver { self } - fn into_npm_req_resolver(self: Arc) -> Arc { - self - } - fn into_process_state_provider( self: Arc, ) -> Arc { Arc::new(CliByonmWrapper(self)) } - fn into_maybe_byonm(self: Arc) -> Option> { - Some(self) + fn into_byonm_or_managed( + self: Arc, + ) -> ByonmOrManagedNpmResolver { + ByonmOrManagedNpmResolver::Byonm(self) } fn clone_snapshotted(&self) -> Arc { @@ -75,4 +77,14 @@ impl CliNpmResolver for CliByonmNpmResolver { // so we just return None to signify check caching is not supported None } + + fn resolve_pkg_folder_from_deno_module_req( + &self, + req: &PackageReq, + referrer: &Url, + ) -> Result { + self + .resolve_pkg_folder_from_deno_module_req(req, referrer) + .map_err(ResolvePkgFolderFromDenoReqError::Byonm) + } } diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 3f4390988a..8d2ede7e67 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -22,12 +22,11 @@ use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; use deno_npm_cache::NpmCacheSetting; -use deno_path_util::fs::canonicalize_path_maybe_not_exists; -use deno_resolver::npm::managed::create_npm_fs_resolver; -use deno_resolver::npm::managed::NpmPackageFsResolver; -use deno_resolver::npm::managed::NpmPackageFsResolverPackageFolderError; use deno_resolver::npm::managed::NpmResolution; -use deno_resolver::npm::CliNpmReqResolver; +use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError; +use deno_resolver::npm::ByonmOrManagedNpmResolver; +use deno_resolver::npm::ManagedNpmResolver; +use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_runtime::colors; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; @@ -36,8 +35,6 @@ use installer::AddPkgReqsResult; use installer::NpmResolutionInstaller; use installers::create_npm_fs_installer; use installers::NpmPackageFsInstaller; -use node_resolver::errors::PackageFolderResolveError; -use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::NpmPackageFolderResolver; use super::CliNpmCache; @@ -46,7 +43,6 @@ use super::CliNpmRegistryInfoProvider; use super::CliNpmResolver; use super::CliNpmTarballCache; use super::InnerCliNpmResolverRef; -use super::ResolvePkgFolderFromDenoReqError; use crate::args::CliLockfile; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; @@ -181,22 +177,23 @@ fn create_inner( npm_system_info.clone(), lifecycle_scripts.clone(), ); - let fs_resolver = create_npm_fs_resolver( - &npm_cache_dir, - &npm_rc, - resolution.clone(), - sys.clone(), - node_modules_dir_path, - ); + let managed_npm_resolver = + Arc::new(ManagedNpmResolver::::new::( + &npm_cache_dir, + &npm_rc, + resolution.clone(), + sys.clone(), + node_modules_dir_path, + )); Arc::new(ManagedCliNpmResolver::new( fs_installer, - fs_resolver, maybe_lockfile, - registry_info_provider, + managed_npm_resolver, npm_cache, npm_cache_dir, npm_install_deps_provider, npm_rc, + registry_info_provider, resolution, sys, tarball_cache, @@ -281,13 +278,23 @@ pub enum PackageCaching<'a> { All, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolvePkgFolderFromDenoModuleError { + #[class(inherit)] + #[error(transparent)] + PackageNvNotFound(#[from] deno_npm::resolution::PackageNvNotFoundError), + #[class(inherit)] + #[error(transparent)] + ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), +} + /// An npm resolver where the resolution is managed by Deno rather than /// the user bringing their own node_modules (BYONM) on the file system. pub struct ManagedCliNpmResolver { fs_installer: Arc, - fs_resolver: Arc, maybe_lockfile: Option>, registry_info_provider: Arc, + managed_npm_resolver: Arc>, npm_cache: Arc, npm_cache_dir: Arc, npm_install_deps_provider: Arc, @@ -304,45 +311,23 @@ pub struct ManagedCliNpmResolver { impl std::fmt::Debug for ManagedCliNpmResolver { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ManagedNpmResolver") + f.debug_struct("ManagedCliNpmResolver") .field("", &"") .finish() } } -#[derive(Debug, thiserror::Error, deno_error::JsError)] -pub enum ResolvePkgFolderFromPkgIdError { - #[class(inherit)] - #[error("{0}")] - NpmPackageFsResolverPackageFolder( - #[from] NpmPackageFsResolverPackageFolderError, - ), - #[class(inherit)] - #[error("{0}")] - Io(#[from] std::io::Error), -} - -#[derive(Debug, thiserror::Error, deno_error::JsError)] -pub enum ResolvePkgFolderFromDenoModuleError { - #[class(inherit)] - #[error("{0}")] - PackageNvNotFound(#[from] deno_npm::resolution::PackageNvNotFoundError), - #[class(inherit)] - #[error("{0}")] - ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), -} - impl ManagedCliNpmResolver { #[allow(clippy::too_many_arguments)] pub fn new( fs_installer: Arc, - fs_resolver: Arc, maybe_lockfile: Option>, - registry_info_provider: Arc, + managed_npm_resolver: Arc>, npm_cache: Arc, npm_cache_dir: Arc, npm_install_deps_provider: Arc, npm_rc: Arc, + registry_info_provider: Arc, resolution: Arc, sys: CliSys, tarball_cache: Arc, @@ -357,13 +342,13 @@ impl ManagedCliNpmResolver { ); Self { fs_installer, - fs_resolver, maybe_lockfile, - registry_info_provider, + managed_npm_resolver, npm_cache, npm_cache_dir, npm_install_deps_provider, npm_rc, + registry_info_provider, text_only_progress_bar, resolution, resolution_installer, @@ -379,14 +364,9 @@ impl ManagedCliNpmResolver { &self, pkg_id: &NpmPackageId, ) -> Result { - let path = self.fs_resolver.package_folder(pkg_id)?; - let path = canonicalize_path_maybe_not_exists(&self.sys, &path)?; - log::debug!( - "Resolved package folder of {} to {}", - pkg_id.as_serialized(), - path.display() - ); - Ok(path) + self + .managed_npm_resolver + .resolve_pkg_folder_from_pkg_id(pkg_id) } /// Resolves the package id from the provided specifier. @@ -395,7 +375,7 @@ impl ManagedCliNpmResolver { specifier: &ModuleSpecifier, ) -> Result, AnyError> { let Some(cache_folder_id) = self - .fs_resolver + .managed_npm_resolver .resolve_package_cache_folder_id_from_specifier(specifier)? else { return Ok(None); @@ -419,7 +399,9 @@ impl ManagedCliNpmResolver { &self, package_id: &NpmPackageId, ) -> Result { - let package_folder = self.fs_resolver.package_folder(package_id)?; + let package_folder = self + .managed_npm_resolver + .resolve_pkg_folder_from_pkg_id(package_id)?; Ok(crate::util::fs::dir_size(&package_folder)?) } @@ -435,7 +417,12 @@ impl ManagedCliNpmResolver { self .resolve_pkg_id_from_pkg_req(req) .ok() - .and_then(|id| self.fs_resolver.package_folder(&id).ok()) + .and_then(|id| { + self + .managed_npm_resolver + .resolve_pkg_folder_from_pkg_id(&id) + .ok() + }) .map(|folder| folder.exists()) .unwrap_or(false) } @@ -648,7 +635,7 @@ impl ManagedCliNpmResolver { } pub fn maybe_node_modules_path(&self) -> Option<&Path> { - self.fs_resolver.node_modules_path() + self.managed_npm_resolver.node_modules_path() } pub fn global_cache_root_path(&self) -> &Path { @@ -672,61 +659,20 @@ fn npm_process_state( .unwrap() } -impl NpmPackageFolderResolver for ManagedCliNpmResolver { - fn resolve_package_folder_from_package( - &self, - name: &str, - referrer: &ModuleSpecifier, - ) -> Result { - let path = self - .fs_resolver - .resolve_package_folder_from_package(name, referrer)?; - let path = - canonicalize_path_maybe_not_exists(&self.sys, &path).map_err(|err| { - PackageFolderResolveIoError { - package_name: name.to_string(), - referrer: referrer.clone(), - source: err, - } - })?; - log::debug!("Resolved {} from {} to {}", name, referrer, path.display()); - Ok(path) - } -} - impl NpmProcessStateProvider for ManagedCliNpmResolver { fn get_npm_process_state(&self) -> String { npm_process_state( self.resolution.serialized_valid_snapshot(), - self.fs_resolver.node_modules_path(), + self.managed_npm_resolver.node_modules_path(), ) } } -impl CliNpmReqResolver for ManagedCliNpmResolver { - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - _referrer: &ModuleSpecifier, - ) -> Result { - let pkg_id = self.resolve_pkg_id_from_pkg_req(req).map_err(|err| { - ResolvePkgFolderFromDenoReqError::Managed(Box::new(err)) - })?; - self - .resolve_pkg_folder_from_pkg_id(&pkg_id) - .map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(Box::new(err))) - } -} - impl CliNpmResolver for ManagedCliNpmResolver { fn into_npm_pkg_folder_resolver( self: Arc, ) -> Arc { - self - } - - fn into_npm_req_resolver(self: Arc) -> Arc { - self + self.managed_npm_resolver.clone() } fn into_process_state_provider( @@ -735,6 +681,12 @@ impl CliNpmResolver for ManagedCliNpmResolver { self } + fn into_byonm_or_managed( + self: Arc, + ) -> ByonmOrManagedNpmResolver { + ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone()) + } + fn clone_snapshotted(&self) -> Arc { // create a new snapshotted npm resolution and resolver let npm_resolution = @@ -752,19 +704,19 @@ impl CliNpmResolver for ManagedCliNpmResolver { self.npm_system_info.clone(), self.lifecycle_scripts.clone(), ), - create_npm_fs_resolver( + self.maybe_lockfile.clone(), + Arc::new(ManagedNpmResolver::::new::( &self.npm_cache_dir, &self.npm_rc, npm_resolution.clone(), self.sys.clone(), self.root_node_modules_path().map(ToOwned::to_owned), - ), - self.maybe_lockfile.clone(), - self.registry_info_provider.clone(), + )), self.npm_cache.clone(), self.npm_cache_dir.clone(), self.npm_install_deps_provider.clone(), self.npm_rc.clone(), + self.registry_info_provider.clone(), npm_resolution, self.sys.clone(), self.tarball_cache.clone(), @@ -779,7 +731,7 @@ impl CliNpmResolver for ManagedCliNpmResolver { } fn root_node_modules_path(&self) -> Option<&Path> { - self.fs_resolver.node_modules_path() + self.managed_npm_resolver.node_modules_path() } fn check_state_hash(&self) -> Option { @@ -794,11 +746,23 @@ impl CliNpmResolver for ManagedCliNpmResolver { let mut hasher = FastInsecureHasher::new_without_deno_version(); // ensure the cache gets busted when turning nodeModulesDir on or off // as this could cause changes in resolution - hasher.write_hashable(self.fs_resolver.node_modules_path().is_some()); + hasher + .write_hashable(self.managed_npm_resolver.node_modules_path().is_some()); for (pkg_req, pkg_nv) in package_reqs { hasher.write_hashable(&pkg_req); hasher.write_hashable(&pkg_nv); } Some(hasher.finish()) } + + fn resolve_pkg_folder_from_deno_module_req( + &self, + req: &PackageReq, + referrer: &Url, + ) -> Result { + self + .managed_npm_resolver + .resolve_pkg_folder_from_deno_module_req(req, referrer) + .map_err(ResolvePkgFolderFromDenoReqError::Managed) + } } diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 837fe26cf9..052a98e6cf 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -5,6 +5,7 @@ mod managed; mod permission_checker; use std::path::Path; +use std::path::PathBuf; use std::sync::Arc; use dashmap::DashMap; @@ -15,7 +16,7 @@ use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_resolver::npm::ByonmNpmResolver; -use deno_resolver::npm::CliNpmReqResolver; +use deno_resolver::npm::ByonmOrManagedNpmResolver; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; @@ -136,17 +137,17 @@ pub enum InnerCliNpmResolverRef<'a> { Byonm(&'a CliByonmNpmResolver), } -pub trait CliNpmResolver: CliNpmReqResolver { +// todo(dsherret): replace with an enum +pub trait CliNpmResolver: Send + Sync + std::fmt::Debug { fn into_npm_pkg_folder_resolver( self: Arc, ) -> Arc; - fn into_npm_req_resolver(self: Arc) -> Arc; fn into_process_state_provider( self: Arc, ) -> Arc; - fn into_maybe_byonm(self: Arc) -> Option> { - None - } + fn into_byonm_or_managed( + self: Arc, + ) -> ByonmOrManagedNpmResolver; fn clone_snapshotted(&self) -> Arc; @@ -166,6 +167,12 @@ pub trait CliNpmResolver: CliNpmReqResolver { } } + fn resolve_pkg_folder_from_deno_module_req( + &self, + req: &PackageReq, + referrer: &Url, + ) -> Result; + fn root_node_modules_path(&self) -> Option<&Path>; /// Returns a hash returning the state of the npm resolver diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index ecf9805b2d..961e8d6b51 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -849,11 +849,10 @@ pub async fn run( let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db()); let npm_req_resolver = Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { - byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), sys: sys.clone(), in_npm_pkg_checker: in_npm_pkg_checker.clone(), node_resolver: node_resolver.clone(), - npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), + npm_resolver: npm_resolver.clone().into_byonm_or_managed(), })); let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new( node_analysis_cache, diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 1c603ecaed..534a07ccf2 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -30,6 +30,7 @@ deno_npm.workspace = true deno_package_json.workspace = true deno_path_util.workspace = true deno_semver.workspace = true +log.workspace = true node_resolver.workspace = true parking_lot.workspace = true sys_traits.workspace = true diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index 710608490a..bf25ba5646 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -27,8 +27,8 @@ use thiserror::Error; use url::Url; use super::local::normalize_pkg_name_for_node_modules_deno_folder; -use super::CliNpmReqResolver; -use super::ResolvePkgFolderFromDenoReqError; +use crate::sync::MaybeSend; +use crate::sync::MaybeSync; #[derive(Debug, Error, deno_error::JsError)] pub enum ByonmResolvePkgFolderFromDenoReqError { @@ -377,35 +377,13 @@ impl } } -impl< - Sys: FsCanonicalize - + FsMetadata - + FsRead - + FsReadDir - + Send - + Sync - + std::fmt::Debug, - > CliNpmReqResolver for ByonmNpmResolver -{ - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - referrer: &Url, - ) -> Result { - ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req( - self, req, referrer, - ) - .map_err(ResolvePkgFolderFromDenoReqError::Byonm) - } -} - impl< TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir - + Send - + Sync + + MaybeSend + + MaybeSync + std::fmt::Debug, > NpmPackageFolderResolver for ByonmNpmResolver { diff --git a/resolvers/deno/npm/managed/common.rs b/resolvers/deno/npm/managed/common.rs index 8a523f3ea4..6569a4594f 100644 --- a/resolvers/deno/npm/managed/common.rs +++ b/resolvers/deno/npm/managed/common.rs @@ -12,14 +12,9 @@ use crate::sync::MaybeSend; use crate::sync::MaybeSync; #[allow(clippy::disallowed_types)] -pub(super) type NpmPackageFsResolverRc = +pub type NpmPackageFsResolverRc = crate::sync::MaybeArc; -#[derive(Debug, thiserror::Error, deno_error::JsError)] -#[class(generic)] -#[error("Package folder not found for '{0}'")] -pub struct NpmPackageFsResolverPackageFolderError(deno_semver::StackString); - /// Part of the resolution that interacts with the file system. pub trait NpmPackageFsResolver: NpmPackageFolderResolver + MaybeSend + MaybeSync @@ -29,15 +24,6 @@ pub trait NpmPackageFsResolver: fn maybe_package_folder(&self, package_id: &NpmPackageId) -> Option; - fn package_folder( - &self, - package_id: &NpmPackageId, - ) -> Result { - self.maybe_package_folder(package_id).ok_or_else(|| { - NpmPackageFsResolverPackageFolderError(package_id.as_serialized()) - }) - } - fn resolve_package_cache_folder_id_from_specifier( &self, specifier: &Url, diff --git a/resolvers/deno/npm/managed/local.rs b/resolvers/deno/npm/managed/local.rs index f23f7bd591..3aa1275d66 100644 --- a/resolvers/deno/npm/managed/local.rs +++ b/resolvers/deno/npm/managed/local.rs @@ -23,12 +23,14 @@ use super::resolution::NpmResolutionRc; use super::NpmPackageFsResolver; use crate::npm::local::get_package_folder_id_folder_name_from_parts; use crate::npm::local::get_package_folder_id_from_folder_name; +use crate::sync::MaybeSend; +use crate::sync::MaybeSync; /// Resolver that creates a local node_modules directory /// and resolves packages from it. #[derive(Debug)] pub struct LocalNpmPackageResolver< - TSys: FsCanonicalize + FsMetadata + Send + Sync, + TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync, > { resolution: NpmResolutionRc, sys: TSys, @@ -36,7 +38,7 @@ pub struct LocalNpmPackageResolver< root_node_modules_url: Url, } -impl +impl LocalNpmPackageResolver { #[allow(clippy::too_many_arguments)] @@ -99,8 +101,9 @@ impl } } -impl - NpmPackageFolderResolver for LocalNpmPackageResolver +impl< + TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync + std::fmt::Debug, + > NpmPackageFolderResolver for LocalNpmPackageResolver { fn resolve_package_folder_from_package( &self, @@ -160,8 +163,9 @@ impl } } -impl - NpmPackageFsResolver for LocalNpmPackageResolver +impl< + TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync + std::fmt::Debug, + > NpmPackageFsResolver for LocalNpmPackageResolver { fn node_modules_path(&self) -> Option<&Path> { Some(self.root_node_modules_path.as_ref()) diff --git a/resolvers/deno/npm/managed/mod.rs b/resolvers/deno/npm/managed/mod.rs index d08ee07d6a..a9775ee374 100644 --- a/resolvers/deno/npm/managed/mod.rs +++ b/resolvers/deno/npm/managed/mod.rs @@ -8,42 +8,180 @@ mod resolution; use std::path::Path; use std::path::PathBuf; +use deno_npm::resolution::PackageReqNotFoundError; +use deno_npm::NpmPackageCacheFolderId; +use deno_npm::NpmPackageId; +use deno_path_util::fs::canonicalize_path_maybe_not_exists; +use deno_semver::package::PackageReq; use node_resolver::InNpmPackageChecker; +use node_resolver::NpmPackageFolderResolver; use sys_traits::FsCanonicalize; use sys_traits::FsMetadata; use url::Url; -pub use self::common::NpmPackageFsResolver; -pub use self::common::NpmPackageFsResolverPackageFolderError; +use self::common::NpmPackageFsResolver; use self::common::NpmPackageFsResolverRc; use self::global::GlobalNpmPackageResolver; use self::local::LocalNpmPackageResolver; pub use self::resolution::NpmResolution; -use self::resolution::NpmResolutionRc; +pub use self::resolution::NpmResolutionRc; use crate::sync::new_rc; +use crate::sync::MaybeSend; +use crate::sync::MaybeSync; use crate::NpmCacheDirRc; use crate::ResolvedNpmRcRc; -pub fn create_npm_fs_resolver< - TSys: FsCanonicalize + FsMetadata + std::fmt::Debug + Send + Sync + 'static, ->( - npm_cache_dir: &NpmCacheDirRc, - npm_rc: &ResolvedNpmRcRc, +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[error(transparent)] +pub enum ResolvePkgFolderFromPkgIdError { + #[class(inherit)] + #[error(transparent)] + NotFound(#[from] NpmManagedPackageFolderNotFoundError), + #[class(inherit)] + #[error(transparent)] + FailedCanonicalizing(#[from] FailedCanonicalizingError), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +#[error("Package folder not found for '{0}'")] +pub struct NpmManagedPackageFolderNotFoundError(deno_semver::StackString); + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +#[error("Failed canonicalizing '{}'", path.display())] +pub struct FailedCanonicalizingError { + path: PathBuf, + #[source] + source: std::io::Error, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ManagedResolvePkgFolderFromDenoReqError { + #[class(inherit)] + #[error(transparent)] + PackageReqNotFound(#[from] PackageReqNotFoundError), + #[class(inherit)] + #[error(transparent)] + ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), +} + +#[allow(clippy::disallowed_types)] +pub type ManagedNpmResolverRc = + crate::sync::MaybeArc>; + +#[derive(Debug)] +pub struct ManagedNpmResolver { + fs_resolver: NpmPackageFsResolverRc, resolution: NpmResolutionRc, sys: TSys, - maybe_node_modules_path: Option, -) -> NpmPackageFsResolverRc { - match maybe_node_modules_path { - Some(node_modules_folder) => new_rc(LocalNpmPackageResolver::new( - resolution, +} + +impl ManagedNpmResolver { + pub fn new< + TCreateSys: FsCanonicalize + + FsMetadata + + std::fmt::Debug + + MaybeSend + + MaybeSync + + Clone + + 'static, + >( + npm_cache_dir: &NpmCacheDirRc, + npm_rc: &ResolvedNpmRcRc, + resolution: NpmResolutionRc, + sys: TCreateSys, + maybe_node_modules_path: Option, + ) -> ManagedNpmResolver { + let fs_resolver: NpmPackageFsResolverRc = match maybe_node_modules_path { + Some(node_modules_folder) => new_rc(LocalNpmPackageResolver::new( + resolution.clone(), + sys.clone(), + node_modules_folder, + )), + None => new_rc(GlobalNpmPackageResolver::new( + npm_cache_dir.clone(), + npm_rc.clone(), + resolution.clone(), + )), + }; + + ManagedNpmResolver { + fs_resolver, sys, - node_modules_folder, - )), - None => new_rc(GlobalNpmPackageResolver::new( - npm_cache_dir.clone(), - npm_rc.clone(), resolution, - )), + } + } + + #[inline] + pub fn node_modules_path(&self) -> Option<&Path> { + self.fs_resolver.node_modules_path() + } + + pub fn resolve_pkg_folder_from_pkg_id( + &self, + package_id: &NpmPackageId, + ) -> Result { + let path = self + .fs_resolver + .maybe_package_folder(package_id) + .ok_or_else(|| { + NpmManagedPackageFolderNotFoundError(package_id.as_serialized()) + })?; + // todo(dsherret): investigate if this canonicalization is always + // necessary. For example, maybe it's not necessary for the global cache + let path = canonicalize_path_maybe_not_exists(&self.sys, &path).map_err( + |source| FailedCanonicalizingError { + path: path.to_path_buf(), + source, + }, + )?; + log::debug!( + "Resolved package folder of {} to {}", + package_id.as_serialized(), + path.display() + ); + Ok(path) + } + + pub fn resolve_pkg_folder_from_deno_module_req( + &self, + req: &PackageReq, + _referrer: &Url, + ) -> Result { + let pkg_id = self.resolution.resolve_pkg_id_from_pkg_req(req)?; + Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?) + } + + #[inline] + pub fn resolve_package_cache_folder_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, std::io::Error> { + self + .fs_resolver + .resolve_package_cache_folder_id_from_specifier(specifier) + } +} + +impl + NpmPackageFolderResolver for ManagedNpmResolver +{ + fn resolve_package_folder_from_package( + &self, + specifier: &str, + referrer: &Url, + ) -> Result { + let path = self + .fs_resolver + .resolve_package_folder_from_package(specifier, referrer)?; + log::debug!( + "Resolved {} from {} to {}", + specifier, + referrer, + path.display() + ); + Ok(path) } } diff --git a/resolvers/deno/npm/managed/resolution.rs b/resolvers/deno/npm/managed/resolution.rs index 40ab91202c..8ddd1ac70f 100644 --- a/resolvers/deno/npm/managed/resolution.rs +++ b/resolvers/deno/npm/managed/resolution.rs @@ -18,7 +18,7 @@ use deno_semver::package::PackageReq; use parking_lot::RwLock; #[allow(clippy::disallowed_types)] -pub(super) type NpmResolutionRc = crate::sync::MaybeArc; +pub type NpmResolutionRc = crate::sync::MaybeArc; /// Handles updating and storing npm resolution in memory. /// diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 4b102d6491..26b156d29d 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -36,9 +36,9 @@ pub use self::local::get_package_folder_id_folder_name; pub use self::local::normalize_pkg_name_for_node_modules_deno_folder; use self::managed::create_managed_in_npm_pkg_checker; use self::managed::ManagedInNpmPkgCheckerCreateOptions; +pub use self::managed::ManagedNpmResolver; +pub use self::managed::ManagedNpmResolverRc; use crate::sync::new_rc; -use crate::sync::MaybeSend; -use crate::sync::MaybeSync; mod byonm; mod local; @@ -108,37 +108,50 @@ pub enum ResolveReqWithSubPathErrorKind { #[derive(Debug, Error, JsError)] pub enum ResolvePkgFolderFromDenoReqError { #[class(inherit)] - #[error("{0}")] - Managed(Box), + #[error(transparent)] + Managed(managed::ManagedResolvePkgFolderFromDenoReqError), #[class(inherit)] #[error(transparent)] - Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError), + Byonm(byonm::ByonmResolvePkgFolderFromDenoReqError), } -#[allow(clippy::disallowed_types)] -pub type CliNpmReqResolverRc = crate::sync::MaybeArc; +#[derive(Debug, Clone)] +pub enum ByonmOrManagedNpmResolver< + TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, +> { + /// The resolver when "bring your own node_modules" is enabled where Deno + /// does not setup the node_modules directories automatically, but instead + /// uses what already exists on the file system. + Byonm(ByonmNpmResolverRc), + Managed(ManagedNpmResolverRc), +} -// todo(dsherret): a temporary trait until we extract -// out the CLI npm resolver into here -pub trait CliNpmReqResolver: Debug + MaybeSend + MaybeSync { - fn resolve_pkg_folder_from_deno_module_req( +impl + ByonmOrManagedNpmResolver +{ + pub fn resolve_pkg_folder_from_deno_module_req( &self, req: &PackageReq, referrer: &Url, - ) -> Result; + ) -> Result { + match self { + ByonmOrManagedNpmResolver::Byonm(byonm_resolver) => byonm_resolver + .resolve_pkg_folder_from_deno_module_req(req, referrer) + .map_err(ResolvePkgFolderFromDenoReqError::Byonm), + ByonmOrManagedNpmResolver::Managed(managed_resolver) => managed_resolver + .resolve_pkg_folder_from_deno_module_req(req, referrer) + .map_err(ResolvePkgFolderFromDenoReqError::Managed), + } + } } pub struct NpmReqResolverOptions< TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - /// The resolver when "bring your own node_modules" is enabled where Deno - /// does not setup the node_modules directories automatically, but instead - /// uses what already exists on the file system. - pub byonm_resolver: Option>, pub in_npm_pkg_checker: InNpmPackageCheckerRc, pub node_resolver: NodeResolverRc, - pub npm_req_resolver: CliNpmReqResolverRc, + pub npm_resolver: ByonmOrManagedNpmResolver, pub sys: TSys, } @@ -151,11 +164,10 @@ pub struct NpmReqResolver< TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - byonm_resolver: Option>, sys: TSys, in_npm_pkg_checker: InNpmPackageCheckerRc, node_resolver: NodeResolverRc, - npm_resolver: CliNpmReqResolverRc, + npm_resolver: ByonmOrManagedNpmResolver, } impl< @@ -167,11 +179,10 @@ impl< options: NpmReqResolverOptions, ) -> Self { Self { - byonm_resolver: options.byonm_resolver, sys: options.sys, in_npm_pkg_checker: options.in_npm_pkg_checker, node_resolver: options.node_resolver, - npm_resolver: options.npm_req_resolver, + npm_resolver: options.npm_resolver, } } @@ -213,7 +224,7 @@ impl< match resolution_result { Ok(url) => Ok(url), Err(err) => { - if self.byonm_resolver.is_some() { + if matches!(self.npm_resolver, ByonmOrManagedNpmResolver::Byonm(_)) { let package_json_path = package_folder.join("package.json"); if !self.sys.fs_exists_no_err(&package_json_path) { return Err( @@ -281,7 +292,10 @@ impl< .into_box(), ); } - if let Some(byonm_npm_resolver) = &self.byonm_resolver { + if let ByonmOrManagedNpmResolver::Byonm( + byonm_npm_resolver, + ) = &self.npm_resolver + { if byonm_npm_resolver .find_ancestor_package_json_with_dep( package_name, diff --git a/resolvers/deno/sync.rs b/resolvers/deno/sync.rs index 1734e84231..af3e290bb2 100644 --- a/resolvers/deno/sync.rs +++ b/resolvers/deno/sync.rs @@ -43,7 +43,13 @@ mod inner { } impl MaybeDashMap { - pub fn get<'a>(&'a self, key: &K) -> Option> { + pub fn get<'a, Q: Eq + Hash + ?Sized>( + &'a self, + key: &Q, + ) -> Option> + where + K: std::borrow::Borrow, + { Ref::filter_map(self.0.borrow(), |map| map.get(key)).ok() } From f6dcc135379d2bc78c8489d483ed7da65174f953 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 10 Jan 2025 20:39:43 -0500 Subject: [PATCH 092/107] fix(regression): show bare-node-builtin hint when using an import map (#27632) --- cli/graph_util.rs | 12 ++++-------- tests/specs/run/node_prefix_missing/__test__.jsonc | 5 +++++ tests/specs/run/node_prefix_missing/config.json | 1 + tests/specs/run/node_prefix_missing/deno.json | 3 +++ tests/specs/run/node_prefix_missing/main.ts.out | 2 +- .../specs/run/node_prefix_missing/main_no_config.out | 3 +++ 6 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 tests/specs/run/node_prefix_missing/deno.json create mode 100644 tests/specs/run/node_prefix_missing/main_no_config.out diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 84beee027e..8da44c76ab 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -36,7 +36,6 @@ use deno_runtime::deno_permissions::PermissionsContainer; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; use deno_semver::SmallStackString; -use import_map::ImportMapError; use node_resolver::InNpmPackageChecker; use crate::args::config_to_deno_graph_workspace_member; @@ -1024,14 +1023,11 @@ fn get_resolution_error_bare_specifier( { Some(specifier.as_str()) } else if let ResolutionError::ResolverError { error, .. } = error { - if let ResolveError::Other(error) = (*error).as_ref() { - if let Some(import_map::ImportMapErrorKind::UnmappedBareSpecifier( + if let ResolveError::ImportMap(error) = (*error).as_ref() { + if let import_map::ImportMapErrorKind::UnmappedBareSpecifier( specifier, _, - )) = error - .as_any() - .downcast_ref::() - .map(|e| &**e) + ) = error.as_kind() { Some(specifier.as_str()) } else { @@ -1324,7 +1320,7 @@ mod test { let specifier = ModuleSpecifier::parse("file:///file.ts").unwrap(); let err = import_map.resolve(input, &specifier).err().unwrap(); let err = ResolutionError::ResolverError { - error: Arc::new(ResolveError::Other(JsErrorBox::from_err(err))), + error: Arc::new(ResolveError::ImportMap(err)), specifier: input.to_string(), range: Range { specifier, diff --git a/tests/specs/run/node_prefix_missing/__test__.jsonc b/tests/specs/run/node_prefix_missing/__test__.jsonc index 305020ed97..fa4504fdc2 100644 --- a/tests/specs/run/node_prefix_missing/__test__.jsonc +++ b/tests/specs/run/node_prefix_missing/__test__.jsonc @@ -5,6 +5,11 @@ "output": "main.ts.out", "exitCode": 1 }, + "basic_no_config": { + "args": "run --quiet --no-config main.ts", + "output": "main_no_config.out", + "exitCode": 1 + }, "unstable_bare_node_builtins_enabled": { "args": "run --unstable-bare-node-builtins main.ts", "output": "feature_enabled.out" diff --git a/tests/specs/run/node_prefix_missing/config.json b/tests/specs/run/node_prefix_missing/config.json index 72f40aaf36..52f5f4d4f4 100644 --- a/tests/specs/run/node_prefix_missing/config.json +++ b/tests/specs/run/node_prefix_missing/config.json @@ -1,3 +1,4 @@ { + "imports": {}, "unstable": ["bare-node-builtins"] } diff --git a/tests/specs/run/node_prefix_missing/deno.json b/tests/specs/run/node_prefix_missing/deno.json new file mode 100644 index 0000000000..f6ca8454c5 --- /dev/null +++ b/tests/specs/run/node_prefix_missing/deno.json @@ -0,0 +1,3 @@ +{ + "imports": {} +} diff --git a/tests/specs/run/node_prefix_missing/main.ts.out b/tests/specs/run/node_prefix_missing/main.ts.out index 48b4e37e27..c7067c6026 100644 --- a/tests/specs/run/node_prefix_missing/main.ts.out +++ b/tests/specs/run/node_prefix_missing/main.ts.out @@ -1,3 +1,3 @@ -error: Relative import path "fs" not prefixed with / or ./ or ../ +error: Relative import path "fs" not prefixed with / or ./ or ../ and not in import map from "[WILDLINE]/main.ts" hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs"). at file:///[WILDCARD]/main.ts:1:16 diff --git a/tests/specs/run/node_prefix_missing/main_no_config.out b/tests/specs/run/node_prefix_missing/main_no_config.out new file mode 100644 index 0000000000..48b4e37e27 --- /dev/null +++ b/tests/specs/run/node_prefix_missing/main_no_config.out @@ -0,0 +1,3 @@ +error: Relative import path "fs" not prefixed with / or ./ or ../ + hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs"). + at file:///[WILDCARD]/main.ts:1:16 From 70c822bfe2180a2c98f52f02ded6aa1f19bc5b89 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Fri, 10 Jan 2025 19:26:01 -0800 Subject: [PATCH 093/107] fix(lsp/check): don't resolve unknown media types to a `.js` extension (#27631) Fixes https://github.com/denoland/deno/issues/25762. Note that some of the things in that issue are not resolved (vite/client types not working properly which has other root causes), but the wildcard module augmentation specifically is fixed by this. We were telling TSC that files with unknown media types had an extension of `.js`, so the ambient module declarations weren't applying. Instead, just don't resolve them, so the ambient declaration applies. --- cli/lsp/tsc.rs | 14 ++++++++++---- cli/tsc/99_main_compiler.js | 8 ++++---- cli/tsc/mod.rs | 27 ++++++++++++++++---------- tests/integration/lsp_tests.rs | 35 ++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 18 deletions(-) diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index cd1a724f5e..8d9a5a46a1 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -4509,11 +4509,12 @@ fn op_release( #[op2] #[serde] +#[allow(clippy::type_complexity)] fn op_resolve( state: &mut OpState, #[string] base: String, #[serde] specifiers: Vec<(bool, String)>, -) -> Result>, deno_core::url::ParseError> { +) -> Result)>>, deno_core::url::ParseError> { op_resolve_inner(state, ResolveArgs { base, specifiers }) } @@ -4595,10 +4596,11 @@ async fn op_poll_requests( } #[inline] +#[allow(clippy::type_complexity)] fn op_resolve_inner( state: &mut OpState, args: ResolveArgs, -) -> Result>, deno_core::url::ParseError> { +) -> Result)>>, deno_core::url::ParseError> { let state = state.borrow_mut::(); let mark = state.performance.mark_with_args("tsc.op.op_resolve", &args); let referrer = state.specifier_map.normalize(&args.base)?; @@ -4611,7 +4613,11 @@ fn op_resolve_inner( o.map(|(s, mt)| { ( state.specifier_map.denormalize(&s), - mt.as_ts_extension().to_string(), + if matches!(mt, MediaType::Unknown) { + None + } else { + Some(mt.as_ts_extension().to_string()) + }, ) }) }) @@ -6461,7 +6467,7 @@ mod tests { resolved, vec![Some(( temp_dir.url().join("b.ts").unwrap().to_string(), - MediaType::TypeScript.as_ts_extension().to_string() + Some(MediaType::TypeScript.as_ts_extension().to_string()) ))] ); } diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 25813c3f9d..b3279f54ac 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -723,7 +723,7 @@ delete Object.prototype.__proto__; } : arg; if (fileReference.fileName.startsWith("npm:")) { - /** @type {[string, ts.Extension] | undefined} */ + /** @type {[string, ts.Extension | null] | undefined} */ const resolved = ops.op_resolve( containingFilePath, [ @@ -735,7 +735,7 @@ delete Object.prototype.__proto__; ], ], )?.[0]; - if (resolved) { + if (resolved && resolved[1]) { return { resolvedTypeReferenceDirective: { primary: true, @@ -785,7 +785,7 @@ delete Object.prototype.__proto__; debug(` base: ${base}`); debug(` specifiers: ${specifiers.map((s) => s[1]).join(", ")}`); } - /** @type {Array<[string, ts.Extension] | undefined>} */ + /** @type {Array<[string, ts.Extension | null] | undefined>} */ const resolved = ops.op_resolve( base, specifiers, @@ -793,7 +793,7 @@ delete Object.prototype.__proto__; if (resolved) { /** @type {Array} */ const result = resolved.map((item) => { - if (item) { + if (item && item[1]) { const [resolvedFileName, extension] = item; return { resolvedModule: { diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 1473b8a8d9..f645a5f6b8 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -746,7 +746,7 @@ fn op_resolve( state: &mut OpState, #[string] base: String, #[serde] specifiers: Vec<(bool, String)>, -) -> Result, ResolveError> { +) -> Result)>, ResolveError> { op_resolve_inner(state, ResolveArgs { base, specifiers }) } @@ -754,9 +754,9 @@ fn op_resolve( fn op_resolve_inner( state: &mut OpState, args: ResolveArgs, -) -> Result, ResolveError> { +) -> Result)>, ResolveError> { let state = state.borrow_mut::(); - let mut resolved: Vec<(String, &'static str)> = + let mut resolved: Vec<(String, Option<&'static str>)> = Vec::with_capacity(args.specifiers.len()); let referrer = if let Some(remapped_specifier) = state.maybe_remapped_specifier(&args.base) @@ -770,14 +770,14 @@ fn op_resolve_inner( if specifier.starts_with("node:") { resolved.push(( MISSING_DEPENDENCY_SPECIFIER.to_string(), - MediaType::Dts.as_ts_extension(), + Some(MediaType::Dts.as_ts_extension()), )); continue; } if specifier.starts_with("asset:///") { let ext = MediaType::from_str(&specifier).as_ts_extension(); - resolved.push((specifier, ext)); + resolved.push((specifier, Some(ext))); continue; } @@ -857,14 +857,15 @@ fn op_resolve_inner( ( specifier_str, match media_type { - MediaType::Css => ".js", // surface these as .js for typescript - media_type => media_type.as_ts_extension(), + MediaType::Css => Some(".js"), // surface these as .js for typescript + MediaType::Unknown => None, + media_type => Some(media_type.as_ts_extension()), }, ) } None => ( MISSING_DEPENDENCY_SPECIFIER.to_string(), - MediaType::Dts.as_ts_extension(), + Some(MediaType::Dts.as_ts_extension()), ), }; log::debug!("Resolved {} from {} to {:?}", specifier, referrer, result); @@ -1441,7 +1442,10 @@ mod tests { }, ) .expect("should have invoked op"); - assert_eq!(actual, vec![("https://deno.land/x/b.ts".into(), ".ts")]); + assert_eq!( + actual, + vec![("https://deno.land/x/b.ts".into(), Some(".ts"))] + ); } #[tokio::test] @@ -1460,7 +1464,10 @@ mod tests { }, ) .expect("should have not errored"); - assert_eq!(actual, vec![(MISSING_DEPENDENCY_SPECIFIER.into(), ".d.ts")]); + assert_eq!( + actual, + vec![(MISSING_DEPENDENCY_SPECIFIER.into(), Some(".d.ts"))] + ); } #[tokio::test] diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 247851da9c..a25710b2b1 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -17221,3 +17221,38 @@ fn lsp_wasm_module() { ); client.shutdown(); } + +#[test] +fn wildcard_augment() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); + let temp_dir = context.temp_dir().path(); + let source = source_file( + temp_dir.join("index.ts"), + r#" + import styles from "./hello_world.scss"; + + function bar(v: string): string { + return v; + } + + bar(styles); + "#, + ); + temp_dir.join("index.d.ts").write( + r#" + declare module '*.scss' { + const content: string; + export default content; + } + "#, + ); + temp_dir + .join("hello_world.scss") + .write("body { color: red; }"); + + client.initialize_default(); + + let diagnostics = client.did_open_file(&source); + assert_eq!(diagnostics.all().len(), 0); +} From 2091691164eadecd22af28e00abf15d5cd0ae377 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Mon, 13 Jan 2025 18:11:26 +0900 Subject: [PATCH 094/107] fix(ext/node): apply `@npmcli/agent` workaround to `npm-check-updates` (#27639) See the comment https://github.com/denoland/deno/pull/25470#issuecomment-2435077722 for the reason why we do this workaround to make `make-fetch-happen` work in Deno This PR applies the same workaround to `npm-check-updates` package. `npm-check-updates` internally uses [`npm-registry-fetch`](https://www.npmjs.com/package/npm-registry-fetch) which uses [`make-fetch-happen`](https://www.npmjs.com/package/make-fetch-happen) (the problematic package) for making http request to npm registry. The detection of `make-fetch-happen` doesn't work for `npm-check-updates` because we use call stack at `net.Socket` constructor to check if it's called from `make-fetch-happen`, but `npm-check-updates` bundles its dependency and the check doesn't work. This PR adds the check of `npm-check-updates` string in call stack in net.Socket constructor to trigger the workaroud. closes #27629 --- ext/node/polyfills/net.ts | 11 +- .../npm-check-updates-17.1.13.tgz | Bin 0 -> 1495496 bytes .../npm/npm-check-updates/registry.json | 201 ++++++++++++++++++ .../npm/npm_check_updates/__test__.jsonc | 7 + tests/specs/npm/npm_check_updates/output.out | 2 + .../specs/npm/npm_check_updates/package.json | 5 + 6 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 tests/registry/npm/npm-check-updates/npm-check-updates-17.1.13.tgz create mode 100644 tests/registry/npm/npm-check-updates/registry.json create mode 100644 tests/specs/npm/npm_check_updates/__test__.jsonc create mode 100644 tests/specs/npm/npm_check_updates/output.out create mode 100644 tests/specs/npm/npm_check_updates/package.json diff --git a/ext/node/polyfills/net.ts b/ext/node/polyfills/net.ts index 3f7603079e..bebdd8dade 100644 --- a/ext/node/polyfills/net.ts +++ b/ext/node/polyfills/net.ts @@ -1224,15 +1224,20 @@ export class Socket extends Duplex { super(options); - // Note: If the socket is created from @npmcli/agent, the 'socket' event - // on ClientRequest object happens after 'connect' event on Socket object. + // Note: If the socket is created from one of: + // - @npmcli/agent + // - npm-check-updates (bundles @npmcli/agent as a dependency) + // the 'socket' event on ClientRequest object happens after 'connect' event on Socket object. // That swaps the sequence of op_node_http_request_with_conn() call and // initial socket read. That causes op_node_http_request_with_conn() not // working. // To avoid the above situation, we detect the socket created from // @npmcli/agent and pause the socket (and also skips the startTls call // if it's TLSSocket) - this._isNpmAgent = new Error().stack?.includes("@npmcli/agent") || false; + // TODO(kt3k): Remove this workaround + const errorStack = new Error().stack; + this._isNpmAgent = errorStack?.includes("@npmcli/agent") || + errorStack?.includes("npm-check-updates") || false; if (this._isNpmAgent) { this.pause(); } diff --git a/tests/registry/npm/npm-check-updates/npm-check-updates-17.1.13.tgz b/tests/registry/npm/npm-check-updates/npm-check-updates-17.1.13.tgz new file mode 100644 index 0000000000000000000000000000000000000000..4a95f26159d8f6bab1f2940eb2e662216bb19517 GIT binary patch literal 1495496 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

OEfUU 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

uAF8*}6a&YD0nuKc#u4%Yt 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 z

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�L= zZs07!MH~GHKW)lIYNQ_MSm&K7s>3v@q)`ouS7<5VHQ?Pv{ej~L zI!9dh@OnglNXCGwTe2cL`gDJ=;c-3SW{-O>-q?IS@w+0Ffyn#9u8C+0#u6Gj?B;OF zW3!Ll2Y&8k6f^Hb!y;`=+S_z7$&V?Sviaftj&C|YR{WZCFy&;+Ng#3?QE#SEA&uUU zzd>mYB_A{sv@Oi%Fb~0*!C8e%7d|%TZB)-VYvVD&vx@O|9_`P+e!G@6YRsr}rjAeD zCJnB1sM67(KIH?t_Qj|vjy3U_V9mkio5#=8&!z8f`aVEz1i1$^RqzUM973SL z`v9L8#5pXKu=>VMg55jzsyKM!z{hEf&KkEG9+r4f@TK5ujs6sa2`LlG|UdPJQ! zjcr;~$gR=ZpiM;An4UG(D!k6~Zp+t+C>}&f7nQ!yTwzp2btXn-G5+e_fBj~S#v?Jy zi?9aj6Z9r@Lzu0=A453Bd=={rT%0hOlGL^KTrt^t^D^R{&p}a?O)*%Bp)E#{m{&!l zr%4S;WvCdSZ=ipLumjN!Yg=ra*xTSBhxQxo07qk7jB(jRFT${k(UD3XRUA^c)Y{VM zO^!v*ki03aXSD6owMVxpJ&F{ZIbY%4hleZPba946ZHZd`f%34Y@z&<9)se+u?O)4^JgsWaM{JzfEq_yAL)^&=ZO3Zm%d%EfBn{% z{4M(rVaB4-PooBm8}M@B?O}CC(KhuzX;e?+I^>4njk!>S<`$Z3=*8gF;W&WT1U^F? zjB#|q=>=z7yshzm#X!f{r2LEOGqNglsnR`9_dPuqtWMdsdHvySi63Wv>m1&MauKDP zD78ehD7*&k0=hbBqC+tUl`>3KIGy13!NCC65k7|awlLXKu}a+&?Up=S2xTsE4`Ca^ zOSqa#{X_b>^1Li6CYTZKY;<;b$#L@yiwTx%Xvb)rs4%8ZlX?{jTHNyapz&Qz0|V+` zMfb0-z67lujLtAtAn9Q>ht&!;PuMwOZ;gXDPR2O7;bDQ78D57N?rG}K%ppf3r$_ez z=NDYebLGbRnDrNr`n;`XUdZ<%KVy-*l6|N3fYvVxI-Gk!rvkkW%pX`iVPk;<4F?9! z59r!>EaKh4_l4>vHP_UdQ`aUdAbU=u2~7<0T=F&Y$6Wbv=fsO1uit!Y@@L2KStwUg zJBYzj3~pjnN#i2aFW_xqr-47fd5zj-vSRW!v~6*D$@YP7TYgnJIiNMgQ8)73XGipN~Vn>!KNnRYN#c zPy;wkvF+mMK=&gQ4p1+^Jit;PO9QOD(c05)z{eIJEl@O2=Ab>GbAfdTpBY4FEZ(s@ z!G0C{PaH1LGx1Zz@JdyS`g7X0xfrlE;JU@F5b_V0p5dgxX${c^Yjc{p^n7rA#GSdQ zR7B+gWgVvvI{bbvef1^2?x9iy_k;O}2VGD`nU{dpf%6+n5voHp+StCL<>04E{TUZ^ z4rXF76~j;ZnSxmWs}HLUyvEor;Z4KWn`#}}B&^zOxqMr(-w?T%sCc3`7yY@ImZ9Q9 z`wCVW;s9GKj8%HxIh=B2ipB#98k{Y>#`yYB$EL}L&JkT!ya{-hhuH+&7<>!O7ilfB zbQ;YuId8G%v(aT!gKv=C)Ol#gjr0J}+*15JB8QrPq1d40beplV`C0;l_q{b$+fniA1p_N`@$pM71HR zeNh_<+ZOAmIGd^8r0$5EG40-TXwpd||H$nk2Llf7{PBg-z`ld4gqj7Yym53CpAnQw zkj(IE@$|~`G0$I^zjOJ)jV}xL*H>TSdkN|mGy>?~Vf}>jh4MNjGtLdUSmw%spG#5J z#AYg9SIFJdPZ4S}EPT-4!)2AmDkv-Ht>89*V2U{h7ahEQ@KvO8OuY{E?lhRwdPp0M zUKM(MC^~ZG&ch9dBVlX8y@{v=S_O=fSRUBjrhWmZEey+P{D9K}CtbX%`0%M=)65f< zu`olP7Rg)D`U1%={aDaiK_`aA5nKlgXVltgSU7s3(`EG`28wVm!W)CK2eS>`B8Xr!_d@pTLH4mJ+xe&E;zzlp&qTNk*yfl?LfXIO1fnPJJmV;L`Jyq#%3 z;X;j#4!_DGe-?{Nwk~43g7FGlP3#n*RD(tXMpItbur$T_Gh3IaXValYkBd--Fe{^1 z_w%<5@;cbEUnAANr zo;15t^kV(MFI&udpsrw?$8kcMO6?_IHypb{{n8YFef1^L-$H2z^#hvKG}=I?h z4j&c!XT091*=6&^W0!A=$Th@hBF0!9QcuC5T zqAn$8VKs%-5`%`Awn4jcRD?-@^wT%9WLZ9kF2zxO6*1h3O%I$dsR}=K zV!TO{4&3e#x>)gXVB>8^`5o1YR12sc(`Z9ulO6?nEXZH7`r>&jTkCvV2u&076C9ef zx$|5RvmETIX)*(|1FINr5msY-1Y`tUEOD{IwS_S1Vs=j7YbX^#ZNlWMFa7m9%N8y0 zmd8(xloI6{ls{3qMfD9eH*D%WS@3)=6jkIGqP7(78cIiqdRUlY$;07|y8{k%Q9g-@ zkp{K&-GXKa%PuV6;6!kJKLRmZ@>kB@N&lNlx(%8kirP{X0- zklHgEw#gaMO(%cCg*odkH+S4hcu?cPl*e~Iy!f&ewY``rV(ws}iK-<^FW3yRU&q%A z{g@hwIOHLTurQ>39oj_*`*@k)t&R6B75a29@gU%JRop*NwV+jneF@iZ%I&E8Z6EmS zH^1bX{8zcYY0vQjxGcr%rCKS<6({i z2j^|{2UK#Xb*8&2@>6O+2V0eA1gtmr@DLzen zefJsu`sz!hemH*@rH9yUXg)3v0g=FAMD~ z9A@D(3v-!&{-wCT6ekNBSbmmqnvrN;~r}zm!hq-+p!PnSalM zdKRi#sAc{m^PgE{XVEzGUzzWJ#||?;$o%);O5<;(nfag05C2y5zZE0%qs)*0R@A>0 zE%TEsQ2th`e=BAdntp0iSxJ8`aK{Sma=g6GbyQM z!6pl~S+M)N-u=5a`nxv%yEgf|Ui-T?|7U=#_#bB|4WML0X#%A&l;%)cLeHc2hHkZA zR_-VM^UJFLvTDC9^OsfqWf{LL{gMn{pjFE7v{z{(t}6 z&|lr-ukQI*_w=jV_|={K>TZ8^=f8T3U%ln8p7X1>`qjPs>Rx|!Z&}#*X)OBg?8!gr z`A^3lm^m1?mqq<78f4M%f0*U_|E=-KWx>+_yBYR< z;s3k%dxY#?gMVrN(*ITYm+^a!@n_BNSAPG`RN{aBi8m=}Fp%=0Ur2+33{$?#(l>a& zp_oX?ytISL>?a$P1KpN9qx=9PsH>^3$+MELroNu~KTrC~{3nsArol7~Cem|z&!yF= z7NiPp7NqJ>U&3fAfx%T*rj(r9kHp@DLtDNPN=Rz)1G8M8n-!TAlB6TI#aeBhHv!BV;(1)1@chXMn&npCi?ip(Tg zs~V${ZS9nW|9`>bvy537FUVP6|a8&ls?%}D*h?_J2> zx7$knt`vd%zSI!|GY#tUobqubmiy^Q((_}bpI-V|$}r7mkbWle#kW+1vIb@SCtnpn zi9?n&Gxzn=Ph$HfnYj8LwsI^dQN;#+J;^>H@y`=R=I~*OP5PmWxfPZdC zkl~V(y7)Pkh_^hDYSbP{bH3C6VKCR!+5D*f7wTLk^kf;5C?6hWc)~u?)RxrM^d`Y8 zH>S;dZPrf(F1Mv>itexjdvt07a`rHT~7ODdGszqdDkZnw;umrW?2WpGMug4&Yd2Gs(62~|~Q zyQ?!W&(iyCXfmnOs7h0%(SSw{8U{3q@^s%YqaM^y$8kJ=A1Y{4av(zRcP z;0eL~_Xqjj?yxQ$%VZ!GBejNVRnmoO1GOb;Hfntd81^<&`t9wd32$E|Au`f%6v>3d z=z^oiSLT%H@!7)5SiYiN26&lDh4|9IOBXM5yx4d#@lul~K^a~d(;$}0_vJ=6pTZ}_ zx?};AoGcHJzGzEYqq*eAJvL;Px3ZK@VCD9c5wA-9Po4gBa$H%9d9dTT!t<*1Y@I~( z=c#{9-|I9orKm5BvR)cZ((s-}b{cil@SH}CG-}Ew5{Y2@KWGfAdSXpq^8kOY9u8?27^iu35AtkpgK~4P&X2bR@b2Kq>G|&LEp6ZmogIMTHu=Cu4Izi)xmY(FowhECpuvNz%m9L>u}z~MS)8W&WX(ETpMt0 z!nFg}D!g{!SAWpyiiEX4F9?eeP9a)Bw1DUZQSg)BszTC(#DqjDWRUofY-NLm+LT(U z-b3Az!Ex;=YdN%o6oIr5wOC^Q+D+ov+DV#w+7b0G>TSum>1A1D()maZqIZ$48{?@A z$&KsMVjVYSEOl(j(D_)Cy)Wb0KjWwJc1wX|+>_akQ(ZO|TqcsKzviU2chw{ha<%cY zl^r41hLpsvRlKb6YT>o@6I(Cf<%*XWFLx;$T%LF}@!G*_AFp}JJ*8Qli)0IewvZ_< zZAL~>)<4uN>g=hrqs~jZox?oYCy69@IZ5xjiL6PueJJWmNjTq@oizEr6eRLP&R=Dm zRB!z=uKJ^x>IUnktTypllJYyh_)?Zc>UAs)+sB!oH%W|sbdGI~&z$5rDRWZdqzJFM z$T#Kdcz!G6LHXqm-%*zF!TwbOQ2mSOZ$*Ef`feInl2Z9*8s3xiy_5YY(J76`X>6v^ zP39Y-eHzKbrg?$pQ@*vghR|F{OR!}?Gm`BW_8TSz z7{8?EH{M98a_q{^l5q&DkxcM76>wB=iZTS_#^8P=3giah9%ZqLSC+oLXG+%CYlEkQ zdjj_cUJJYu9Hz2)<*<}K=W!?-M~)-uLmq8758i~G1iq}OdH3Kwf_E3*7JQx%x)6F0ZXi}5&Owww6hbnFWClqD z$wMZ1lsYOMR8FWoq(Z4|QR$*GmyEx%L1l-EjjDxeR|e74SR(maf`%r8aYhk!ndy;f zPNOAl5Pc{8AESeYAsY+ywX`7gP!^mR1DQ26Oi45tL#cxreW{ciJv7Rw@6o9J1l>GI zy!PHQIni(b*jZ)l=VjWmuSkWa-^ITE&y4QRx4U1#ktst(WU%tw#hlx*Bf3VX*XR%DOz85c#L_0=6I-<6zcz9nst{6K2q>VZ^W%HEns+D7JJjG6>MjGh#|Y8$GpW%jYQ zqnbmVNXB%m0a-&ycG&r!wCi2kxzp&M%ur4S;5?N!Z`qi#d($nZs6%m;Vneby`8Ves zY5C?avhpXtlU+*ry9^rUPZF`L#4={2f8F4L6zTOyCVuols$BYBmP_d;N$u%N*3YcR z(lnOx+Xd9I6sJE1CpAtwvdAR=kf2KbBJ$54*Rd(OFBuq>;WI@-=>3lv{9aA|??1O& z4DwQB(|;hBsw``fHcjJG0 zv^LQ4pp}4~$l%DtfXPfUjFW{7-%O4_I&@c7LpgPDYEmrpM&R`&e&sE}Tf@2lo+B-B zZz$`~);Y-Qq__uFlp(Ac99qj8s|8rA#{m{XFNm=$4f zA??ThK~mLyPsTXbTk?u65atTLLOWIS}*#K|3} zL!3H46-RZPPGkYV5MZxzrHSK^WTI>QNW(X zofZ#T*5tmuG-;jECL_$=HlcI!&CcY+@(D{67AVY0O!yVk+o5;L+TU;Yf8T#@)~c-8 ztkqb%vC)>Aoza&>t&#mUmB(xh-5Ayq{mhL&-aysL5B^H6pOXBoSU>f zXfH{uXnj#?w3?JZG!3*SXe)8Qw6OqzS`W1DCk>>grtLx7eA2h@6pnq2fVfCEQLk1I-8;FF_w07b(0p9?)z;V-1bH zB(j?&xdt>g|63RTepG8Lpt+R9P$Pk6MJ}ao7CarireF`A0e(rYi}Mc5yK;hC)nL_y zl`F-O)c{rv`9gi=!)huJ#cB>K3sxoR@SVGGIKd%+!wn9hAYq5AB;pUTly1*8$ts?! z5bPmHAb3KsfnWu}Ng$svBS*IA0P$YBxsR=610PFxo#9=FckSC8{rkS?m;IE`Yv#w) zwM44Rtfd*IXHoa0IH=olMbRy(o9Jz+c$xuPMKn{i9l5+&N3>gL>uBe{wp2+Hh*E_~ z(AuE2MQeq&jkYC+lvWAbJ@EiKIccr+zFjN1^VG3tVK4U$^u_2l(QOHAtt74VYag$n9Q1A$-pY8r z<1L5RIo_INl;y%^%!K?gic%aY-l=pKo5#G%8Q+Y_j3icE*^A3kIY>;evJoGs)fBj* z3iT~lQM6mNu+r1L5WB2CZ#z_ zGhZ65`=up&qK0UR7?Z|XB74S3Zn?%@45w_KjhKx1`?*NWM$N?ysj%DK<3uyv6Vm~|8N#MY#8YOUBd{x^32eKGGf zxnJh~Mk4C{ndENzmZ*(=m&Yd_M^d?%=sZq%d>7hnRpph#D~nex={~MHylU{O&8rHp zYNEcKAAG(`dipZqYs8NoKkNK#^0UJ4vs7}mym%p+#_5C8P-%wbo3tFKv2=!vu`-6r zm`GGJ_YIX7x)SOsbObyo^uDF%$v0p){(F#Lx}-Nyy`h9^dtKEVss2;gxy4RxR-#?* z=4y9QyVvLbhRg#p$v0eFkWOC4lJIG!Br0m=1{p^J1(_>kLdZmrseo32%n>qs$hkt% z6iyPUE*-(Fi_~Iv1?aM>k|nWvV5K58T5qTrICx2ozO{kYLQ+Mo5wyn8GN4tKRA8$O ztqQbSQZ8xDpczB+04*1qJIPQq188NS)d9CC+M-*M#-5*(mZzT=VZxgU{^#o=oOzMh zAv6E$7TknTT9;tGfb|B}+V7|IyG2SAaXo=`U(!A6vQ&qU1t~5ZvvAbmkisz!r;_Bv zPX!^<&MusX-==R~Six`rVH?5;gf$4)5H=y~K{$hODKc&}5hNhKNs2#?1j>EHQsRGH z;qivo6HoL}P(^^7kOW z7jq+r`V;l5H2C!w8eKG6Xym1lqsM6U#aAo#u{e-;g5wKHYG0&p>P#eQZb!ms$4OpLrAbqg;FFb{>nCHJ=+ZVjJbpi=-(^w~8OI%-w%>y0 z3!?y|9^Nj(^+gs&GmM@XJuuE;RKUo@+fjPtw*y8ajP7`gC2bL1#YKqf829mZ6_+d8 z3Xd0UFgi=tAX;IR$0&=p4Msb>r5G*9xKp-7*|GpC#__8}Bw}S*t{_iVo2&*|J#q@d zxmC;Jl2-E)kg3`d5^3E29_068-iWB_QgcGBIkgs&b!wf&6_5yFOPVX4GIeahRtHTQ z)W6h0MDCm17r8ePo7@MDw=_0r>~;dN?0U2t(e6N( zx+Lf?ExP8vnVmC*2MX^LzUZn^c%m?naD8}`{Cjwz@Jz3S-lMP3W>M-$1735*Jj>)I zL6&Vx$v(Rk10Z|i?2L;#7q5~7D&7Q&a2)A7I$gG7b|&HwI|(~mcIwh`aT?hlQGqJx@D?531mXi5RHsXs6H16MckmdfB2YJyP`)BSSc~FuP|70Yd_hieHx(Eal zTQJ2%Lu}O5hF3#gO?frv)mapZmB*_AuO@sJq)_sGmmOcWd|B~jBjCz)DSpZA&9?_X z2mJJ;;G)?|>kB=oO_k<}z@b$IRMtjPJJ2dZ>1b`G4V2dWdywCYxv^5lUOdHIM-?1Z zxT?ZJItqoQD(qEZ{mt=hRPQP=^Ik0dvtB6jVlPs?o%E0f8#P!} z{rc7H=HjL46TzJHoY*#cO~hZlEw+l@mvC<345bT{LJ4t~)?ki=E-(jR_raM%>nSF8 zdnvV#b{X132~D?kNs+WS&~8Gz0OEEV&<=##YqXnBXN(0;QKJWe^Xb#FQfkX-Me8O3DdPsg>*5R@ z8*r>jd*fun=?qs3u2l#_xE3Hv;GTmxkxI?o5GnRqhGzzzIy|!yDopn9Uc-AMNhPBu zoPd52*~{=T>tc2)xJb4oSz6cB&Q!Wwm5Olhl{HpM zg7Q}EzX$ofm{;mpnMz}(GL{BmWiRm``zB4$%0@C)wYpUHD|d;{w-&;>x2D2zJ+!2~l28bPb@LrL-w|F3sdjUHFq+OC|*qU@w8Y!GH%d9#nZSlKq5fhNn7D&cfm@ z+EP(k`@C)m0kwMZ`uE-AKfimol(g1^FA$aIRVXE!)s5F}5tS~wln5^`z64TT4KjRr za-i`g6k+jl<4ekym~Umi<@lE2$dM_4$mFQcQBB%5(SW18Y&bk)e!j$p*AC)gYiB8; zY8R#5L=7>nDqB*Zr8C>B()rrJ%_uBf)D+O@w2 z`MsF88xj_7JHqt#9U*OciFD2Tc?mQ2E~=jufMk$UgRC0lg{U7Kzuj?5`8k1S{GysJ z)pVn#M>Som=|xTVYH<*0<7o)#^sI?8tM8>WqhF-{XyjxiK;MaIr9VMGf>Dr0NGXy~ zw&{Vn0CNWBM9P=eQTjuj6?7WVSwP1TV7pU*PEAO#&I~$7VHG-K$vJlN(3wN0B;|Ft z0ppR7NaL#eNt2wxmrLO`QMD^#1PM(yEz9!fnr+IKSfTgbQ7SmHA2FPaPsZu?g_}JiMiH}e^tht^Dh`E_4yt$8g)+8%?QQXISX;uNu|-! z31~2)fhRT7;Z`Wn;fRKRE8xF-w_wu44GniRbZI!J;fjV6@(wg{XmSz&W6~9ccTy6B zZ&IU4gT|?t>+_Tr8LsYVkqA<|>Pyjc)ufe2s}XHH+W54Oq;9+Sg+|*iMLxa+bh*;4 zA>$jj8r?GDQC$a=94J{+vZ3Tc?=`(wLih^_))lNy&&oDP?j$wq+?;a<&ISMLcmDHt z@6O!?Im<0%L!eaPy3VzTs|i=n0`1qn;qF>V%GtF6S6^JcN`;~}<=UESd#>e0q3F31 z{T|%eZ3sQ$7Nk}+c=O=Nt}e`yn`gHy?$Oi}5M|mE@p0Ofp7(Uj)3O)|(>gEQZ`gk; zdH;3rCqk`HGA^-x38S*{CE~rw$&$c&B0c8yPFiT|C$HYTUh#Rtmoi^UQm_sRvUL+= z`EudF_}a!fsa!`MM=Md2qky9aM;nf294+{9kzt2diJxzN{SKi2{O;ZS_V|4f6(ds? zDLkXAOjBtu0Vgt55jZjx8NSHa67d$=AtRNqY)2%p+(xw8LP-@P8A2(>s+h<$P4TTt z1649*qM=k12f9>JC0mwWig#&dm-?#R6>q8E{AwJwv{wfuH7Kh=Bz5h<{Yyu;)i6}U zoARs5uPMKw{Ib}nz9kajH2iyz-;4P)QPa1yvKD8x2-S8hqYw|NL916oXgy;I#zD*# zqb(Ilqc0ezQIVkx;|zua#u{=HCmXGv(yBc%{A`W&E8Mx@?WtgaIL)U>$ z1l=}tJ?IvpTZPU|^m?}q;RDBsaBj0X%qB3K!F&VrwRG#Z3)rq< z+ktHxwq4jxzaHo43-*uUIEUjxs+PwI9A|L!gq%N5;Fgtzjr%~lME4flJ8*BpvjNXG zyvmT|AvsCqG50qj|99`src&O@MItWerqajC^}jvQtJLD_1!?)$no^V$Y1qTs18X&` zEwLJ6ZI895jJ>qS-|2?7R7LwMk&G8EE>=RtEHgqAEgLfQu^8YY!o?F8Tk)`$Rq2&3 z_PDfgu@DeGsWi- zUwM3;@fG9ig0HFc&R<)69r0bn*9MlMsU|l;=Z7+*J?HYBLVs3R!>XxY6khZZbHDzg-2#GWt3r#WcXtJb<=!`M| zwQy)rk@m*wK&vxtHi8Uozw-r$v20TuOtGd89Xbrf5lG(kQI>c^6ULS>2{Ll z`YoZ`n{GZuWx9oeTi?zyZE>53HhsI&EvDO^qPp}HZ@=L2Kfil7B_~RKO2@)8WJXes z)CU5mRp)4x>kou{#Q1Jf1H`aC`IX2Bba zHzR?fH@2(~Z5q6p{XNL<#eB2j&0K0Cn=Wr^yy^31$>$AUY`$0=G^FPp)cLvk-9npx7~T4vPds5U3HU8sFV?R6D|D!8d2P>+{-JylYMY*vUG;|a!9 z7SgghWQ($YmCZrnH(2z~@7@jNf{4iF-!xVk%o8{l=pLZ^gzjDNhHff-+3s3;V?76Y z9YJ}!H{mn8Czxn3@nA9%rg!2>Ep@Vn$wEZZT?w{NK}NS%85G(Th0j0kgc3Y%;XH%$ z9BxCnHQ_#iy9f8N6da#@czW<0!gKm9ninBC!#k3qeC{S)uG|50LvhXX`rm{6e!FF2 z>kB#L-m))|uZr89cVs~;-xoPJ-xP+tc);3PK!Vy`W;*H@*%PQ&r9EGt3!PPOVSO#! zZ2cf@(fStaeXQ@W-oyF?>w$e5t%a7U|D8yF_imZ@scSNT)Agyl z7Wu3@6X3Hu7GJ#Im&W#BMk9wt1sYj2stJ=YHO2p)JZM^`$(yFFuhw6cwXW5R)|#w= zts+`Ig%jHx#8x?+>6jHJ=a`q0>oG$IpN^JPItcB%=56??HZlcc*LC7Mvf*K0tmbYgG9@=exrEG!t(2Wuc_VP&OCdmUyd+N3uEbw#{3cH#dQww*x*~e0KP}6Y@M5 zeF5wl2RoTw2__s&MNJMi9E|z8=bOj3DaTums~itGo^V{@c+IiL@tETgKOX!V@@pV0 z!E4R01-~}@a``p-{gi(9Zhp;$(#afDW+gI8CQ{jxcre+e%AN&7&6mCmfT7HsDnA9R zH8-lf6J6NMsB)mnF9E{Mf-+C4e3AW-_F1`EA!OWwa!YFH3%5Bk)u^KUvGTXdchw|N zv$AlWv$~pf)pDqoRdJw~1GTKFWlJ`=mOZtJgreUbWiex)Q~RviM>5x7>*|y+eyk&lg-y;5-?xJf-Mkc z{w)S!p(yfDL%cw2m@+F=e|>igzjgM8F5pc)mC39|6YFmQeg=7ICHC()$ja7GKNh29 z731oG>mIJRxb6zayUyZzA}ICR#B~8zR~fci7jfUieT1hRraHdQ`2M>a{@uG%LptapSmkcS@rIB zU~nWo@1{jfo4O%&W9mL=u%yA9Mm~*3H1cRPr|F8OeOVx!E@`n4=V|TGI!|kh)~4*0 z9eZ^8iVnva9S3wA3nhMBP|%{FP4VBK7=QQfxJGf2;sM1|iboU=zf4Q@J4JG#^hD{I z(gSNN5vlWQ&aXspDHgfZ6?CLkmcDPRD64C&wQL2nO2RX@HryKV$l#I3qmh(`M>dbg zJlpW>BCg?V%d;^rHoREycEQ^RZ@1Fg+-YKXY?tDPZQo+b?sO@&Z}+?nczfgRi_cp= z@A=Z;Amn?N<1@!0-*t}f9AEkMsRO%{p!mC)Acdz0&MPt0Zx8W+Z!z4r-yM$x3neV*GQL_^eAh)T z-!&~qMmrl_Z1~tXiEz`{VdE_Hg-k^1*fzfGdRtb)Hci>~+)Siryd8+mwtnNLgPSUD zeB8S-VRY;ZDwcLJJ!9&9Ti^psXP9m=J>px#bcg8})3qqk=>gM|_}K-I3J1Y>3Ujel z3Og!Pk$s{1S_;c7*c!?fbE{A zIHLGMcZ1?B#R$>wc)^YXEDzbUhHJHW!K?dk9Py!m3imO3c)VRyDFbE zd`kHoaPZ(Dku}I5mg$MxN;svv$@e1P%baAt2%OGIj$f&u6q!Vd?|Md{1zl6wyUI^h zek5*G&yQtx z-YPN0>FKwie~?koX%}`eBttAru^342zj49F8=HBtPa02bL^6~&aM4}J=)`6xiz{|=}+l<;7YL{30irPDBZ>wWj9ZTw%sAEwb zHFd42>rh>N_4L)NrCvM824Y}OM>2Xgoxt%bTRqR6*kgqV3pXrWWpt=nkvZAH5Vv#O z4rM!T+xRNco~)v2eVK{VTHn>RJQeN#O#;S$5AyrxwjWeaWR$ou70s@lr=BM;l2@Qv zPgV!lTj7>ZZ<&ZW#iD1OB06i|G2uPk?{r_&eM9dY-2-~x=>6RvD|ETD7D>9Z5$?A$ z6BE4?3A5_w*tZ2o@!mZ5dG7JN&GV7y7kgJ|{C$b{ecn4V=DE-FzQ)DR+Nv^96d8FwjevwXG=B=&o%`| z3Qqh!&^@91>+eB+|J)Cq5=->xLZ3?uK^Z%D?$%@uuAAqsDVrGGGW$*TTi+qX2QOpZ zpJWVYzu^6m_a5)J99B5A`BCCWh95b8=ra0OFjcjys)nj0!n3(2L5AjanP6S4)M~3% zC$);y>Z(o+b+Xm{t{zwQT&dSlC7w#ADjCZzVm5|B1_l}obQzdEi>oa=!-cm@jx=3t zw{Wn=?Ng>5w+~q+$yihz2#?Wd3y9uZlh>egl^zv(SoD5zx5M4NY@zrQGPioGZAG^7_BD0dtJ7TO1#Y2=baji>W2l}-_3Ej_RmnmY zSF$h2K4DOlIoLr7_9;Zoza_mwip`nq74OF~Et2Vyqfuifc3tm4y}u73fA{W1Kxd!M z9%tTy*cCjUUwD4x<%^8xo_ne%FBqWI&aBX^x9V2 zy4v++Q0sJ7r?)yiRn(AW=2xhaok}+9-GqS!QTuO{vbmL2>cI|o)h}~crt^%>Q##M- z(V$109!)MxxEsr=r+?-{S?N`!S5$4PYQCz?)NrN7M>Th40)0JD>yC_&Y@2FZQ|G3v z7oOWHnyZH=D{zAvOxM`lQD#KFv+%KAhmxwS1p7Dk@3Jy|Xz<}zoc#Ci-hF6uDdKP- z0}Hi{8tSrXIm{|Auf``4=5`ac@2az>qWgEI@}=&HdKFX>spKL|;I1uuhUXof4>D%B z)c7%0g^a3QRPCh3XEh(GZCmZ8>S zjE4Y^7M_|^Oh57Y6Td#Pl(6uic@96%J`^pmT(B*0C*V$D)`8g(4jDLP;Z&DZ z`Ev;80OBk>G&P+ovF z0n3wtN~a9f0u075@W4HQy8$4enq%jPodddWbnocK z=ssm^+)dGap&Q_-i|0I^>v(qYT)}f0&rLl4Vmbf*-FnUzWzJL@Q8}dYRgB^0hI%Qj zuawk8ku1HqtZ^yfQp!D>=WnI=l-^N#SLp+#H&u6{x--@FRd@PH+Mn0+lT1EI>60`* z$?Wrbf0E@VsecmhlVmN6yajR zr39BcT*`3C!=(Y27DPV8Wr!VknD8jU!-7W<9yYvk@ESrALvn+(4rvY2Dx`ZzSCAec z4ImvuI)iiy=>yUuq!FY~)HFy#EIzPoVA;a5gXIdAn^-Q$xO(Y{r3aQySk7R%h@}Wi z36?Hc3b7es=Y*XQI{|hs*ty~`hr_?WyZ`wss`qf%!l8x3A`XW*v~k$Sp^3*To_cud z<2lCj63<(_WbtC+rGn=(o{xB5i`{$PeAvcoRClGiN7dbb;^6b{ zev;Sco%$(B;n^>`nE)Q=iT`vo6o!dN$xVKl*FHR_49T=@8;({ z`n>C(xBhvzKFRiz93Ve}{0s7LC|;p>fnorqJQUARet}g6%LMBMP92;k)C*8cp`L+y z4eC17i%{D_?E$qn)aFpzK`n&Z0_rx@j!+vz&4+pyYE!5!p*Dj-1cN&ak1)Kza1X;R z49_q;!SDtn4Te`3K4BQZFofan0Q&FWt!KD`VFEJ?<~dkgVex=v1(pshF0iyg}g4*B9)ZeW~3sEbh)mt&iFWwH;~`)NItMs2xyipjJc8Lal_Fi^V1u6D;qs z9HLpn@*B$=EZ@*9WBGvP0Lw=#Ke4>V@(If^HWO^Gu>F(;)XodLS?t!aTa*phu7O<> zyEb-nGI-vt$sFb2hHe>0IUMeAosADk1ONf^;UWRzlFc@K=VR*v8#-M21<=()*+vRN9c)CetLdOJ<2om&`huO)~prT4dJ9nUOOlXHU+8oC!HT zIXiNO)EH7@Ma`628a3b4wy52u{*eX&4bC(;(cns6Ox}yUJ9$s?9)Az=`^@P@G}mdC z(lSTunAT6)KIwd=AYtvwdWH1^>r>Wa)~~GZx#)A*XH(mv3C2aPP#uBlq^)JMg*9X`NG((*~zDr)^GKoOYCc zQF^HKqtXLWY`b^WO;q=#x)0SeKB@glbDy;Fd0#$h>64nDH1kQDpVa-Nz0dpkc?X|# z@JS1wwEcNMKB@M3-#_UNN?9nCz|_Fh!Aih>fztt}4Ngy1yXz0AkD&0*AsQ46La%xsu9U^#~6NQl#w2Fo)nhp>FWGJ)j_mN!_=V0nRM z1j_*|*RTv=d4;79%Qq}TSnlDlg0lrd6@o4V76cs#Ob7;l5Au8O4n`34At*smfS?RP z8-fM|O$aIw6d}k%;6UI(5JPl_cnt9b;wi*F#4~uD;4y>81Reu;OyMzyR{>rJNZRnu zqZXld$4nD57tD+>lfz5{GX>0)Ff+i6g&9rO1v4IMGt6W$)5XjJGY2f`STfM8pk4z3R)JLF`75DG_)FMHPL*a)k5=$<_TLFY#G?zU^^8qy*tM41-n=5 zy4YP{cY@sjyIWZg?ha)}yL-p(0o@9^RrE6GUC=Ay=7@ z4!tdUGxR(hUD2}z+V`UGxa$GEDSA4N?&vw_EzvvS=z`u0y*zq*^eh}5&|9E4K<|NG z3B59U26`LxOx&&Tpo^w|xP9kvyLj~R|z*UXkb{utA=3#Lm$H(hD8h~7?v=s$~=PZk^UzA zO!`R{F!Yf0E9q+zO1VU(GL?2zI#DU0(t*q}nKLq1WNyjy$lQ>*AahDiPJrQxPL(26 zast^pH*&7zgjCs*lTxKX&YLO*Rr2J-R57Ws`>ufV>tL6+PixyE^m+kd}X53S#{t&Ur(>bM!MwbkQ28BfmGn91cU8A>6?>B1& z)*RL}(nOTE9&B#eY_l1%xsw?p^TOtpEsM=3n>RL3Z2s;2{w>*>Gd6uT zA8ZC}?ztCnFXY~fdjXF#Ja%}zfY$=A4x0@U3_aJ{o^$hA0)QhNVsMk@?pgu(1 zK)ofqJ$eWA0_ra64iY~r{g4= z|3H6^zK^>d?vA+IR5chl5bvzXDxWMBttM=djx{WJ5PVqRw;}TDEygu+c#p?{O zHwj|$fyn1-k@z%xgh}RX~ zOuWAFrr|A%VHfYk})MCN_i+d$zM|_1LblHD|lcR?2pUtp!^yTa&*B z`MsFiI@=Cg1GYA771?^RZL`&8YsQw(R>ankt+kl+c8+a}?FL&TwsvfrY%SR?a<9R? zI`=2sUva<9eVh9=?vJ=%;C_eudG4Fs?{h!L{W6H zijc7(lYz_tGG)k2Kx;uJ2N@4CYshRNGlk3yGI_{okU2mmfJ_H6H7Hb}w1v_hN;@bW zWC_!3fVBt5h1L~XH)y_~Wkah9%@CR!Xw{%Kf#yx7lA9-J)uEO9dywBJ^QHyO3$*6Y za-eyKRuP(eXg;B(LCXZU3~m9uC-_b9Cg9z`b7i^O*TCC@mw;acF9zQPF95FsegV7# zcvJ8$;LX9?fj0)<0q+HV6}%<*8SpLe&fu+OKFGJh&%)e=wGZnqtXr^N!g>U&7*}G2v-o=5H28`L-_kc?7!cw2f`DCbqJ>r zdJrBUenT9>;|z}w9(Qe8q8ylM7BNIF4{q$MFlt zTO40-vcZXl<1>!mI2quijS~|mK2AzFnc+Cb@e?OCoLD$1<2b?H4R-cLOdy?^> ztW8El*$x?@%&!?InGP@l%2vsEk&#k1PuT`#H8Nr{Zj?1Ct5epY(wj<8D!r(bk~Ji& zLso&T8d+W0Rkn&`resyf8j(|^%8e>9RjyRcQZ*y1fYkz3HL5(Q@}f#gRg0=7RYIy3 zsWqe4lsb+KgLnS^-~PId)G?`JQKv?oD)lSWFVUbu?w#BtxqEWY zB=<_=B8}@bF45Sgahb-JOnZ%UG_KNoM)M`j*EHYId`j~z&BwHO((*yen3gwMMzpEW zrb(MB?XI*h&@QCioOUPLO=vfxT|m1o?KZR<((X*VE$#NSJJRk#yO?%;+D+-wq)V4B zZMsY;yi%A@7*lwo@Ilv{%;<${x@PEFpzuv$O4l-lPYPF*OzGXCw@dFKy$9b}RFhJh z(ki7jN(+=$C@rz}_e=5r{=~2mYXi;{IAd_8D67udIcIyE9dLHfS&y?j&W<=c=4^|z zOU@2CJKGEXBlO9h7GNU({@npvnizf}9IK0q#+2&=Bmn~kMc(vr!j#nc--TCz7 zv&-ippA){!`I_+c$=5sIUL5IsyYnsOD8tV#KS%r=^3&yKho3$_$4Z;YY>`$|nyWNR zRt=4#GB(OMDI-+IMHzuIn#$NJW2cO@%C%Llt8#snYp7gH6?Uo+s=`?n0#%4rVWtXq zRrvdJ_1A4wg{>+?s<2XpgK9lgJFnVB)qAPlUiG%BcUHZl>ZfYBQGQlU=W5XbeE@n3 z^d{&d&`r>%pc|lDpgW)sA+rMA1-%Zs4tfD{9u%5j)xoNPGXrM{?Hb6O$Q;^xXhqP= zL3;x22DFRNu0Z<=?F_W1(5^#!2CW3z7PJ@8N}=6?_88g@xEAqL`*j<^@5wfu zzXX2@{tCP+jEmsU!FRzQfFFRLf`0@50RA2P82ml>JMf<{AH(JdnDsFe!*rAn;mRgusOggf=w1SW!Q9JW5C9Q%^KEESSPTtVB^B30-FYGhH%`#(T1Z5 z$0i(Wa4f^ofs+NN6NC|54Y+RMnt`hcVG7|Dt~IziaBV>tK=^=b9l`{{GhBOc)gipY zwFF@d;S0hGgg3ZqaLvIz3-=5pU3f3xJ%{%ejS}j2G%PeGXgFwOG5fjSOZD%yuz5#B3Ff6th{(npi5M(?sWt&Iv0HR_0h)VdaPw z1M5Sq1Xw9!#mCAGD=n;CuyV!P7%Lt+Z>$Wl;>zH5rHhpYR&=Zsuwr4w#7Yq>C#-a^ z(!=HjTT5(hur(#5=GeBdJ;e41+Xw7au~Ywhkl%ZE?}fb`_MX_+uy@AZ9eW$> zUC_D(-BS=I1O=H!&x4uO`NWA z*238wrv=>IareYsin|vcnt15op@)YS9uhpB@OZ&vfTt5iFTCgRUihxymhe%;_$5Qf z_Z;3Q7;iC7@t(o^5bpuTF2(`gO}uM(uVMVe*ueOJ_g@O@zrTC;eT8=y?_-S57_adD z|7m)YrB|6POYmJ{9J9)bj7hyi&C_xc5&xa!OQ=DhUVs|ZqK008dVv5T4gMQ;EzRm7 zD$Y5+n^EZz%Fg}1J3}ty=F6MCUB0&OwSnvmSqP5|9y@rPAR9vFz{7*=0htY13fUO4 z3^E-u7an(bB#_Nf+Mr~jbi{gr(gCG2N)}2EN;{M;D6LWQQOZ$TV5@_zIkubFZed%- zb_3fT?CRL*)j6tL zRAW@nsM@GnsIE{AeoGaSHiI{sFF9Y<(*Xw1>rqT!;kLSv1FjfR2qG0t^d%yA{- zO2L)%TiR>Yz*QSpeOz^L)y1`m>j<|S+}gNxaJ$59jN3DA_qaXa_JrF2w?1x7-0sl1 zp>sv&g3by38v1SY>*#mUmvBGB(8bWkxQ$VPv4nB+w=8qq$GC#=6ypY7c6c%I;^SqD z_bJ|wc(?FA#QOyA9^UtOAK*R4`x5UV-gUf>@E+h@!}}WVeSE&~`Nr}qN#=k2<8Gsi zuN}TN_}b#zpsGgI0aaD1zo}j#0MM#S!Eh1V3v@mGl(?X+d zleQDuj%k;ZWza68-Gi(O?OwD?X!oRDO1qMF7qY8lwa6WlJ0;g3Z$;jmyafe)3VP&c z6wD~7P+(9nr9h&fL4i&|lLC!`HU(V@26SH2rAn6yU2?j-=yIY5|d`rDIBGlr||fC~Z)2q#~u_iz4}tFTR_Kj0%^E0~HPxcPcz8d@7RP z>b}L9iinCE6#-XbuH3kCWJzWzVoBoqo9j=Onq1GgUa(YUslt-NQiG*BH@4icxv~DO zq1xDRv&zjjHyv))xjE;i%gq5dyWFgBQ)lJPO36)`n-VuYZq~TDi25q_z<{l|ZNXB|mJSP@}c>~CV9h_EO2nb@o1 zI1$IGIL^drElyW)Ka0C1?rm{*#Qh@fk+_@U?u)x8?mLm5L^cpvUu12O^+eVYMMD%_ zQOKg`h(Zx%MZAvU6+=CRMhhBkkV0rQKuSQmfOLR{4AK!A1CRn}RG_)xh7U~(nhrE= z7&KsThCv9b3aSoj3)Bs$->1P}d^f09(CVPBK^=oS1vLXT{Vi>+TA+48bwNFW>VsAV z^$zM0)HbMd&?=xhpn9NoVBmqe0QCUs7SukBk1)}}7{XkJc^Bqmn5!`Fz`PCf8O%q& z1?QI=a9nWKU_Zd=gR=wY4E7tG1~_}LGjM9)7~ssn$-!xY(*pYrP6YN9>=GOYtkZAd zU;74508Ry*BRCQ`2{==5s^Hwf>4IH=(*ZjLX9>;#91ZLXI6Bxza0;-W5X%rZAg)7P zgSZKC6%LF4&+oypfa6!-@qhgBeP*7)F@@s;@&yzVD4LKDpr}B;hI|P52=Wc&XUH=s zWGHmVODL+4o4+*)^Az$E6g|jYCjR!XN*w|vDhK(sU>e%RDip=Gd!XuZ_JHY7%N~)H~mUT8eg$k2G; zR7ImeBS9lZZVPmt=+@9F(aF%)(4YQY)Llp4K!1$>4*l<5 z`r^CMU!bqz-opJ3_a=rR#xBM-#xce>j021>7&{nWF+O8_!g!1E1LFi^>$e2@<%X9d zCQVFaOe&c4@czc6f%hDfKHdvV>Ue)*(#2$iiG=qO6CKMK%MeQsOCQS!UoO5Je0lg@ z;Co4xEj4UvY^ae^V@ZvG8VhRdsBwYimKr8C5^8wVIDP~9PBJ4=V@u5ywOZ7Ys8yj> zjam&Fo=JU^HX$t`EhBwEdXF@RG>xV^BkfzeILraMkDJ{FS$Z0X5#hq3?T0Cgkr)7(lRazvp?9sA8 z)`YAMSv|7)WSL|w$eNSYB}*l@LGFUQHF*wsUpDZM?_9p#mb?vlJMxy~xfCR{mnnEt zuqFRZK}!1??IjBCw6D{?O2LhSoK8(ha2%rDsYHl&X{llv8Z`y0rY@2*N*ZE>Y!NoA?W(tst6r3p(TmO3o;xpCmeky}k}>D)YV zGv}7e%`3MYZkgP!bMwHhHn(JM#oUUxHQ?5UTOPNL+)TK&<<^OtXKszSWpPX8)`eSr zZVkEB<<^9o7oMznvgC=$lLgOpo)3Ah@!a4=pBFhVN?ts8ap%>QS1GR^yt?paz?&X# z`n-$SO4!QSJ+gaY_r&g<_c8B7ju(90^EKwH$9aW|Qq)~hABnml>b|I7MSUUadr@DB zx-IHMQIACZB|w?zFb{o^0se77DP7ymz?D}Gx zh+R$Wnqpgs-AL>P!g>iO6yZ{Y2N60V9E)%-!np`z5r!hXif|{wjR+Saj6`UPa3#W- z2z?Q*MVyM`KY)B4vm7mPwnS1BNk!aCk;vkni=-;ShZqOJ(BZtNU8V*PWH1^QgLSq8W0Gb{&Lr_D|dY~;pTY+{1EdlKU+7+}C zv^!`n=q_k|&}*QXpe;e`fHnke1DXoj8`L*w5ojT3GteB+QW#%hvVr*)<{Ox=VcCGC z1j`+`XK)knzRt}5_~yIa4S0QUC*YpIjlg|^tARTMw-2rdZV%i5+!we`jO`)z>e%aHPs3gpwH<0#)Xu2g zQM;h#p?1Jg1xGa;)p1v0=S;f^8%?6q^TpiG?;OdC0 z7n*h4U2wO@-3@m!?tI+7a2Mjv#GQk?4elb`?QmzKJ4LsRZU@~7x?^-l=zjU`Kfd{H z*Fd+4u8yvXeuzOE{S*2}^nLVQ-21o>aUWrLz(W%cOFXpkpy5Hq!vqgKJaqA}z(XGo z4LnRmTf)N(4>lfZcq#Dm#>*2gIVK(^DW)|{EKK&8Rxzz$+Q4LmX&+MwQyJ3^rd>>q zm}HntFnMCK#>B=dirg)^YjREU0`fxg zeACvV~ogNB3WO_6x zebJ*r4~d>CJvDmv>Dgn+Vrj|JhNT@#YnE0l%~`Tp+Oo9ZM$U~3H!^Nqx$SV<=2pV( z1-Ei;Z@4|;_MY3lZy;a3yX|s&&h3y}8Mh5?y}501=fLegw`<%Ub9=|FlG{ygZ@Jy( zw#@A=w`bh;xb@<;!tH?DD{gnV8S~ua`IZ-NUQKyb@+#-mi_JQl6*gpri0yqWMW=G}qq8rv1N6?RwbF4?QIo3UH4o3mGAw`5P@{h33HLyw~uM{kZd9B(;Z z^YzTv3t#V?d!qgpQe8+VQGW8sLEWYn||MAUt&*oxQ5!0)fmSP%-=}Sx>V!9X8TrB5e7mD3V?9O7h z5j#ulj$#*yog;QzVZBB87U4~Vr3h0ICL*ef@FAj_2s05?MWl$RA)M#1P3^ zB)Uj;A{mNgERu~#mLl1TWG)g@WP!+5BHM^;iRo1o3sIP&*ov|xUKfx%AlIRhfqVw} z1o95#Z?E*tcgqWqhaew8jzM07tb@DI;k6`n4XFL){Nmf`iq zEd24!cfWe@YQd`u;WdFWfii+u4azZ;7btZoRVWR3jo>weas|%{lv^l6 zC`%|kc&Shh;njgMfbtEmF_adRDU^FC3zRP?A5mVQd_sAH@*L#=u^ zDDP2TqU@o3#r722bL>sAr(@5=-Wq!w?5(i3z}^;nbL`E&fqY+7-=VIc-a@^Nx{P`i zwG#CP>P^(1sAV{6;z+^q3#SfFO`Ohfy29xarxs4W>y1;agsfVe7=@uV#Omj?KOcPAcn4T~_ zV7kUK!B>Q@6TV`6$M_EMy~lTiZx`P_Rc)$zBwHkZ@!fxX^WCyZ@|ffu$up7-YEGy% zpw^IDI<-`4^{J)N$fi+2qXCU7Gzw_cr%{VWJsPcP)TWU~qa_Us8ih2>Y1E`qmxebQ z?P++S(U^uQ4MQ4UY51g(PQyJ70~$?e_@a?dqZN$~G^)|Cq>)L(ghpo?xis`>=+p2- z!y^qd8ck`mq0xy(9U9GObfnqt8_1XMUQK9a(aNS(O6x7HuC$ucYDB9At#n##X*H*n zMk|w6F|GErQfbwv)tXi-S{-Q>(JH6afmTaeIkXzks-V@KRy$gGv&C#Q8=P-O#6aDgZ3{9 zD-<>;oKje)eNOw5!Wo@jbnek*Kv##ZYr4+p>eF>j*A-noN;Z@%DcMr8pvQ(D7Cjt# z*z`EiV@!_)Jtp+f=rN;*OOLO3-9NtgZhDO9q0?hZk0Cvl^f2f#r)NY@o1Rm8&gr?K zr$x^RJq@m2xq9O2k*gk8Lza##g)9Xu?OD38Nxxuo`a+~EU%QcoImMh#*xl`wk z#GMLvX53D=GvLmdJ8SMtxf5{5;LeIWUG5yYQ{#@$oi=xx-05@2tyqdGwX0y*` zlh-P*2fQ(Pv*L}*n}9byZ#KMH@W$iah3zI=IolfBBeo}O8*Go+Te8<6o;j8|u5;{heB=1cvCDD7@txxn$3Dk4#}Qwj ze9ie<^7YNvly3^(DtyiOTJWvOw+7!N&g)zZ`8nXHDx{^5hC(ugG#8R46jO9>Vz3pn zlbCJ9?C{k~-`cgatC+cBW{FuWW*0Ft#Vi%erC2V6)fBr@SS_)8i(M|Pn%F&sC5y-q z(M&`O5p6{@646jZs)!~c@?jkvfQdg5H8*9fk`SUST+c(G|vT7(ZdMgGCeOFIZGy(S}6_76X`< zFfU-9!=ejI3;Y`dE(9$IJn%0N_~5@FSV0g&kU+42Km*^0U=Dr({u_cS1bYZ<2vi6L z5R4!=LJ&Z(0sjes3Bd${F$6sb`Vhzvk0BmGJb{A;hdmrZI5y#^z)6Br9ZoX5Vt9Ap zWx-p4*Aw1-csHRm;Vr?dgqI2LHoSbSNbqvtb%0k6uLxdycn{$9hSyhk_8;GT_iG35 z7Q8}u_uzGe*9BfDR2rz%P=2HQKxKkT1(hl)9aKiBv{6w|8K7LEGDZ1`ij2w}l?Ap} z*fy~jV9&*#hrI}U4)#Lq?NNK9K1Y3n`UrI$^)c#O9CdI~!RZmFF;34oEpd9q=@X}S zoL+Ex!g+xU7tJ}ED>S!gF40_|xkJ-JbByM<@BZeyO#`hJS^-)PT6?riv@En1Xl>Ej zp%tUGMr(tvje&$g69WbJ3GN@bzvKRl`xK8R9xXh2cwFIej7JBLb3FQZ%#{d>HuX;$w}E4L*kW=;LFFk1;;x_}Jm2gO33| zCiv*#V}uU}A2ybCEDJ23SmyXTvoRNGdIVAZ+azyfteYK^Hip_W1835^>x4r$z@@sP$_8h`K1zxZw%S7!VbJm9X!U6Z>_?yk8jb9cbK zoVyS1KDoQ#uEkxQyH)PV+>N>0=WdTX9qz8UlXKVM?vOhfcT(=&xLf0{&7C)Q1MX(r zy>oZZ-4S=YU%mA2-}wML+<9@g#oY>b3hqvL@?@jOlNV1ip5#19c=G1SohK#F173`H z+2N(mOO?$Tn+BUBUY~gV;LV*kN8Vi6w%J~@?XexOYqED>FJjMO&t~t$p2gmty?{NB z_Zjcsyub0j;4tAZ=P=_i<><X@~PeiVWjv_jUxDdy)I9^1OiYyY@Uc6r7Re(|l`3~|E6c3aQC=Mt#C_X4A4397j zU}(WGgrN;XA0}0pRA7>TF^7c?`yT8ku&=`2fqe`1YuGPfzlFUCdjHB{7 zehm9M?0wkVu-71X!`_Ac9QF;^cVVwXynsUtrxv`s@Cx94gZBvD7QC16j^XXW+l99Y z?+d(F@ZQ1OM`er385JLu3o14$CscOWyJGKvy(8)d>KD{Q)K%2?sK=p-Xr9sZaeYGTidKns4XpyL zCt4S@8)#){z0j7>4bk;6SYoiiV1mIEgAoRE+`llaU|7Xd15X1yUhq`G;~S3!p89ya z;i-eiE1qh2YT>Dlrz##Fcr5WG3Gylvv+fsZ|AEzByI zDfkF5>*C{tj{+YFW)ePfEE`z9v3y}!;`@g03soYL3sNLfx}>y8X^^6j{3Ioy)`D7d zYAtC}r%8n-1Dbd=8PjA#lMYQR8W%LNX)>qD_u1ojF5huV<0nm8G}+QPr}2Zv22J`j znb1V0NslHonyhG2rOA#aHJUVO(x7oh`9W@O&U ze3E&fb&J+@TGwdZB{L=SNNb7KZ8C3UCS<8 zp!12&7doHme5A{mE+e{TbU#wor>sZWkTR9BE@cXpbt)@dYjE|&(u3tW%R81AEYDb8 zv%F<_#qx&xTkhAnf8?Icy$Sae?mf9T<=&Ee3HMsuQ@N*cZ^ONq`vdNExwq$D#yx|3 zWA5qP8*uN;y_9yEeeTWJP}r!j(PE>{MuR6Q&ksC5@jT+?hL>wz zPIk9j@f^_jO--b%b}^R~s?25;+ZJ8Xw+M{Mue zzOsE``^a|8_L=PyyHj>8cDL-;*vr|wv6r%!vG-svVXt7XWbeh^ll?mHUmU(TEIF!j z_~f|BhdCd7KA3!1@WJK7mJbdeY(B_**zsY*ha=yve6RA|<6FYF1K*B(JM-@(Px5N;uqVV}c3fqe?00{a{6 zV~9;Sw&2vkih`9cR(e>eVx@tV5mx%}eq*Hq?*c0>yr1w+vC_t76_qzC88+*vl-T>S zfq(hF->pAzSV8@c!z$`8)DzST9M*6&!qE_COPtv_o8e5w*$`(2&gM9GabDsg!gUSJ z9IZFnU9^X2kI-(Rt)i`?t)UyE8)2}<(-Kbso@RL3;AxGg7*8uaxp?yNw8N8wrxRvN z%v8)Km|2)jG23Cb#!SO(gPDQZ5MOtEUGbgb`$5$^DH&uCMn^^Mj(tykm>$?1?YAg4=? zOpZ#9Mox>OJw+Zx7DZQzP88iKRwz1B^rYxT(U~HLqLd<+qJ*LYMK(nRMI}W!oojTy z(`81NDP0V@zUU^=twy&BWlPF-lx-+8D4S4bQsz+hbi!&i$183+}JD-{QW<{XX~i+&8)J za(~SICiiFDUvt05{WJG9?q9fXbKmFwjr$h&9X50}hHPj&zw*N1#ayH{UaWaB<;9AZ z7B6?abl8m8Jh17r8M0}!x&H?8wYtq6n+~rHUe9(O_FL@t*q7MX*l)AnU|(Th=CH!ylA|U^5=RY=0*>w+w>j=|+~K3bN12axKD_x@ z<-?s14?Yh0knmCBL&k@a?@PW9`99!#o9_$0clbWz`-JaZz8iek`99+Nn(tG-&pGdN z-sNJ=&niDF{G9O9;b+LtJwF3}`doIo91H0tq`PQOL?;#fo0x}U9*emv=AM}E#XJ($ zTv#h%ErhL$s1(slL~oJSMfPO_|MJCmi`N^J0!9jqR2cPO)PhkLMtzt_V3=U6U|~Y& zLAZgi3!w(#48jhCYY3MRst|?{4j}A7XhOV!!wF7Ztjw_DVP%7r306X^Y_YP#iiMQ` zD+X3gTn^uDGuv6n&51M>lUutxR!8z zMtgP03BjODI+;Zc^N)xI*!e;t9ngiaQj4 z{eSt(w{~r;QCy=~r`Vu#oz5wp6S~alCeux!TZ@v2l079cB_U-oWe>`(lGHkgd&u`2-|u`s@jc+A!S^fQ3r=c$Kk_}} z`^@v&NVJ7Ts-+1b6MfC%4Lts36}0cvnHmU}V5(0;4gEhG48=;lsj$g$>~q!V`ph2z>}M2+t56AUtB_iIqE6 zj#x>ta>L3Qt2L}Vu=2)Aj+G3n6|59kxni}7%^@~N*!HnkV(*E1j`I-bdz`;;5#zdx z>prdrxbETlg0_RUg|>&bkG73=@D1cUx$6ak0|pTWF@`CgTX=5bxs7K9&kejDFe@;7 zVP3_&g0CFkZ>nabEJ&GAvqLSPS^-UWWV~rwCF4cY1x+htBs7(1+M;QLrd={h+RSL9 z(nh0=L7O3MCgfP;xa4N!r4(-{-c!7y*r(W{*rC{?IHu%4xlDPJa*4_rl@lrrD#uh# zsho4I$BjBSsw}50-&ww}d}X!6>W~Kos~c7atd4l_Vs*@_%Bs)mjMW|w?yOdLkg}?< z+Glmbs?LL))fNvPthRaZ718&XFTR@xPgc7;NLbZamDw=a*s!tY<$;$+HZwL0UYERn z@pi-89d9k(uGy-xRbxkGr_aueoh~~ac82U<*uSxVWu8u6*c$(oZHCpIT5PIOMDoT!|9 z8}l!|o0APEBTkl_>^U(w*>W=HWXFlmiOGq@$(xgalL;p-Cm!cB&KI1|xp26!x$wAf z`FY~!fy)J#8!k;QS6t5cr3on)QYP9{(Kf`QF6O0}zeL;=u_DrrNC%=ggHpn10Z|RY zCq#7!9}rcrTF0t{)izd}SZ!gofmIosD{RiOIm6xyM+VOJxK`1Q(LSSn#PbNxL%d!v zZ)2`t-o(6vxr})a%O1W8s+>q!(R51FJxyJju4!t~bV<`0O+A_(Xu758hNb~cO`3jP z8v4t3F5gW+n-y&qwAs+crp=NzA#K~_#N-6z9LNdD%PGz%eo)+?IH$Ovn@YD4B}dBp zly|8#sa#OG;)cu(i5m@;GgjZM7OW<$2CSB>K3P4nnz6cPHDxtot-|V=4TlYz4U3m| zUgm7RdA;SW%~p#onXL*tQ+6ioXY7|8$Q(!<)HtZJpML}S8gBp1VUMFRM+QezjwT%C z9P4}v`LyEGl1~wzEI#>s3ixF6Y0F8?%)Whl+s}rmmSkIz0>qc(}<=QnkKY~XyenyrOlo;4moFX-{ii?D=2=^L83#A4l*4Y6u;>Ih}BN=XA{Jit~hv z6F=|#yz=wHWsS>_Ut@mlgj9<5Ojy3KJmFkK+!N`PHb+qopjm}x9hwr10vI_knZP21 zNP|d+XaL7HRu@>EV{?c78uqK$ujA~5>k*zkJiC~WsTI*Qr&)!j8BL!wJ=3(H>5Dce z+FWRJpv{$>gq#~WSMpw8z4Xm@AG&nt)1gC$CY>8}n^8Wc(x%d4`N`UjwJ~ce)(qB6 zUQ4`g@TS7Sgo7DJCP!=Qf{veD3gh%;y=OyL|3*T5y_jdgRpNH01Qb zsm*D^>6X(2r!J=#PEVZfIE^^HaeC#{=QQS`#)Znog^MdcWqvOC`QT^D&xFf5mxFI0 zU&CFVxjb=s;PNOIL$T%egbQsZLOu0$BcUOOC^9F94j@!8_D$7h$%J3gCy-teQw>5Cr4NlroDn+{Vt%qVv#w<)(+JFphBc4Y0$+Lc!= z-d@;Q^V#R~fzL5NI{fJIL*>VSA38rY&bM6L`Pt%9;qt*{!DY^6CYB~vT|6IY^Q7cL zxywqOwHs>>))Ln49BlY}SNWxYf7^{ZCD4ew{AAruAPF_t*IG@mk)md-`i~{dkx2uj}sP zT^m0S<6q0<*Gl?zR(_o;AG`hjSX1NU_4JRMcRpTS|8+J#`=0h|HTY%Be_a|M?;QQQ zDZlpiule@lMiw@mj}weO+rau*5BX!2TbT94;wv`xU;q2R{P*7zFc}Mb_3;nc&-BL2 z+s8W|#c}c3-c7N$e@>lGke@`Ti9;%`PgosjGW-Ni1GO#ctDoEZXL}!hthxDdE8Cw% z^j-Jg=fuac%%9;M!zaItkKNdQe7pCtl>KM3Z-1z9@OsZ{>ypa0uG|I=q9b$=?2&!(<@Huc_5MgFNMpH02< z*}nasO7pX+w|^?FpGy0u()p=$KO3q4Q|bLw`hTtbS^Zn@&&pr*KU;sD|JnS{?w`ZI zX@Bbf@B95{`~Q*tD}Q}Gb^oiV{Mr6%_0R3!`hRZzxAxcJpTS=jfBJt7|BU~p{u%ve z@6YSs^gl2E8UC67ZS-gMx5?k8f17`|_3xwbx8i$QB|+7m~2%x%mg%yTRhECyJt@hRgorB+Uz0rigL$#i(paYv;~ zrAOuAyOrOW2{gPuG3|jPW7rZ+?;qd(+ix=_cX9XNhDo*Oclp2l-7VibR8Kw|?;mgf z+yD8yw6^9#`E~7a+Tus|*Xi{M(&Xb+`uu2r_R{sIQT=}Z-~S#Il+UU8!qMy>g}*EL zWosNnydUsuhOYAQwdk`=+ntX-OtH1KfuUz?+zke0suV?yG>;8I`KeMK& z)J3iNQ!9V!!=Jh%WLek^p)7>*6wSu3yd_$hXse>@{WKKO4}Oj{(Lenh55=G+RO{zB z{yDz`)0NA)A*hnUlBYXFOkH^ z`A5<3>GIi}YoE<+{BfjEOw`ZrD*yDN&!$(UO#ax(;}Z;>U)ABquf1@Ue|D$GPyhC7 zJ^uKO)yMTNA3NQ{aP`^TpP$X^_Ssy=pKbjHdXKW#XOmApn^WhrDeceh5`6sDy-JQoxJ_{ard)Xp8xTS-wWo~rt{gz!O!>q&)S>y*pX!Efq#V+7-Tb0 z(b;4&$t0P>X-KUWpl*#MsMVUDE+iU}!C;b=tYCr>?5b22fxGYfzF*&$yYKq~XrPDI zy!8A9x|^HZQ++>S;1QRZ%&Zwv=2?SjM@2T5?789r7W;H9mG}Il0`qZAgNQJl3 z8)NA`WIKCwJGKLm_7O5Kf1>|KIf;ULY8E~5+ zgwY6kjWaSEYp{j1IH;A=r_Zj%B0qt!ib4p5&?;A}62ElE5Xq84#x+!Sm z476Dp+Bm`rEs~AdQ6DKR55?YhhA#BAmkkuwnnA5MAk2Hz@3Q?}+uI?78kGFV_DzgN zk;I$OJX;o+fm^S@EgW*@;MQxSScf!k8>KL~ae-9&5E@hP-Y~dD3fyu5-n{_t4?znB z!Kd@!mP<&>6oi>X9Odq+gLgH+d!1}E(x`!3qz{d}kxR9bgBFd0+fBhO*O2`?qvi#- zn}H9wAV*7RMYfRC3V3b>$v=k{-VmWo!kSU4K(1|RZ$!G9$+^r7 zFtr9N>h91`+gx;Lqzg&Z={7^9H61VMHVY-(GO~x6^vkE$5~{?bw##FCTu@3GC~}&_ z>ozahsvD{>vU{98sk4lq^*nXQNlb#^-m&C_R?E;-k?mC4teY#*+z*InV=nsUrpR_0NloZ1 zN`wp%Q{ZPavdWNEne06fY8k6=Sk2tHH$hA;EvanjUWh}{8t>mHUM~9&w!yOzCLvR&KWC+&QbsT>RO zY}bJ{TY^vRP501@2t+%qR?e=cCB|!3y={hPq_%w@?{v6bEI^Xic4QCL7dJ<4DANg< zuAy}DP#|*s^a^a(x2h^#R;^``su5!rGP7QY;R1w4!Aw-l-0V>9`Gf1r{qqnz z6}!D8!-X-)K+LDfbisBb>Mxr45+rX2{6aK`b31DNn%;%cESS|flwrX1 zZ=h91P?LKz-!;S>v%y3~6u3am!n@d|NV>4JB*^=2l>71(Sxd zutFUxlCq+*6)PF)f>aLb>&8vW(7aYsu##&l6|~Y7LyuZn7n$TqiD$c}(JESnfYI7o z#l2O|8tsu)$rv5Msuhf`$7)Cx=Q6q_i;q}>&*+Yf?$GG2Eh%itUK*}hvScYuOYs}M zjinZhe%;b#OP^T!&}w;%e#dH+jsD1LwXN3FYUix>%<8$VzT4`ztl`uewTyApns}^9 zz?vx5%x#SO)=aWyLu-+@7A0$;TdM#x)n%>L)>^RE6}H`5Yt35g)<&{66JzRpGP$jt z+uH4nY0H>SX<$x9Wn&hhu8uKFQ+JBGbJX3Z?mhK{!Np`V={AZ(aO)et*N5i^^|LOL z#tzClOMN@YP0lDMEJc9U*GYFm^)O_Shn_7{U8edLveKcgHEj)O+e6!48cajU#LZxY zwkxz7jzt^;~*U?bU2~oF&$6oIB_8D zW;wl7&0HPI(nqItDC+{v#OW+dXKm`6(%G2KGmzXxC@!7OC1aAM^C|e|lFnsl@h)A| zq4;Z9ceLK%B)*V=Y%Y&Q>~mNS(wl#&8*E<&klR!z1& zDl|Q0`+~NwLWBW?>dYGMp=3&sb_*-3g3mU53=ko9UGu(!q7&Q1lGM z>CW6Q!DqL2RWQ0&z5@a>>qUPhz zVslAS=IwIF;tOUeY4d)Y4?rz)5NAuMMakwHwoo(6Ra=TPdh|nYMMPofX7g(yV2nb{5pXu{$xl z(}bKFK+el{XK#07b~kRfbV&Nj?xpNr)o%5{SDR)jZ}UleRE0XtL7gShS93;9hC0pK z;{w!K3F>TXPcrtz3tmdu<0^I6>`5BZIBcv__VniB%twctNM<@Tmj}En2T6$--3_$B z2C6Sc+p4|l+N+tp_OfWs-jwZ47g}6~mde;WnZ>+lMVjFBl)W39QVW{efR@`roUO@v z0WmDt`z2bg9JI;?TFwn2CL4+i%DTv0b7;8@)YgutRCpR5f^SU zWU&s5x1qIpX#FAG3X`M*8%V4l^B zq#J=AY_ockrDWnwLG;WadNu%)Ky1IP5oV1hYYbUq$C@tI%&;a0Io)B+9!=Gtr^e1d zSDRDlsXA17Wwz6dn=o$7_$cEQqmzK@pBmFNxY7WU|Hg6i(T!^t^QPKG_l751toUF99hW zWdjfE@7W**eW|f|4*G76Rv?2`ppBN7N6ROo6>PHUj?KJi`6g_Uu!PWoz#G%^+y}v5 zMJtd+E5w19)@X&sY_delvqUR0VXG8d<=LviR)RU1uvLq#`jFxhTf2=eXQ;9l@)VM?rv5o3bpa_Jf~2Quq5-X60WYqo(u5}Ope<%BuCwGAn(kxK4okRMG73%Z zK*}s>sA#O)v_7J`mwHo>(ql-;1@-U1H~Z8hf){oW3uR*+1TXj@r~2TV3!^50udcy! zOQXtzuTQ{>X~^5CQ4_&eU8L?T@8So?Fti|@^C z)Z9fNm(xVozy;dil2Jn`gNsGLrE*4UYHoCJQ5Uq;7+iR2w=3X6F>_Tq(0X$@jyFzx zadRh_8!sd+0dW;Ew;tl^5qF1p+~z)J?q%XG5$}fh%fw$L{yOnDtmO=1ArEn}gt*#U z(-C;V2XPf90gVK`;EJ5-7_c%TxJ(9ICT&kw&{{QcwFC*5Nkjt2k4cng@t#>;(LfAR zA_i{2K~g&4y^^J-!7b`Ey)@Phnkmy%j;0GVQ8Jy8)7n7t#7Qz`6nr$4gyapA!USC0 zPqJ}v{Q#-eS-8%s6BbuUvrZauQr{b;9TB6XRfeVqz*kq`>wWOG6wx|FS+Gcw=sVI* z5-kH+4uDt3;CUWWByUuw*2GWKUZU}kS2w1lNB0gF9lD|Qi{Kp37LI0O;A39u^PBRB zr9#luHA`%b21f=BGHQ@zgDgYfl>&Hq1F_vB>p1Bq!Ap+%^BFZac!4vjb@0`lQO(-E z7BoEwezt;`9zjzyvWbyR0=%SxZ?uWt247o~uFo#zpcc0_&spvr1fGLXYLk7{SS=tF z`Va~`=$;Ui?br&sz`1td+yMxssud_(fs*B(LKsycOoEWAIq3fQfzX>no#`Q@vJ9cy zGBh1RM}U}4K^I#PmJ2I3M=M>mGBYbSw32B^s$(-$t%zf&EE4l&As7t7F7d#mcCh9L&VMw>GODaZvMw2q@Tli;fx$oVSdluX4QM4uNt z*MbW z5E~n#SBBUSASQP-n=*O@tC56MYFWa};u=QZXSH?)FG6hQA!UM=Hn#MP)ec)7KX@r) z<J}mHT@XhVmf%=)1g+nK*1oYnJUaZ3Boc<^YCuzKEUj8I+1f-Q zsU2&oLfr0+S<+hgAh|X~+gghV;e*yhqP&Ot){tTqNZ}dv4rw?=BS9L;L2_DLlZk%` zsU?9cwrP3=(OKY>n~?MfB&i5_&l{V7u^K{ZH=zwgh?}Y9QH}MOW@9w9VTmA3&CN^` z%DiRf)3o37%(3Gk}a8rkx7`R9o;>?*VdPbp0@@*qOFe)u@!5p|j zoVEp`mmyTAkXwDots2qd;KEfZ4v5x*+z-(9&I~T;uug|vGn9th8$(`gAg>fUjzdzH z;2Q%vO3>kkj(S#WMCUcS2-10$ZWB<#{0%Q}{N14VYf!r(Ix9k6dCYJNN_PrQDp<_| zIClbKF-e*}D5(sTtyg)&Z?*_Q2_n<MHV25QWdfHBBq0Cgf zl!6+nl2*X<@4@pf(rJSic+!@@izU0lp|5Np&XP9Qg_;tG<~h)Lb8><2nOnat1Z`n& zi;6AFwj76C@!E0@YQqn;;Vgfr(E4R)y&5!iX{@?-Ml`Eavzj!TBU{_rS`B(SM7D7j zYD0?ntYVJ(bn5ki=lyKjwMR{RtV5mGh*F_VFHr|(yk}H%_M&C4R7iucy&2fsh#8$& z!;-aILx}OFP_?#8YZV3GtgyarRHl%mKJ%=Zr%3WcQwp%anx%Zu^r-1}R_q&=QlSm% z&<1NeE8AL|70RrjvO7O=$|P<)Qxefz%tJkz_Bp@t#y9aHR^kejD6qL2@3d z`oK3=biM;$^+3{7;Q5T1>#>yEs70W)U67P6Q3l|JJ$S(jHRB^K-lz%?r&XgeVBsdw zm(W^n@Vvm{6T{j1Yfa2n!B8T^J29rZF^z#w)Tn=IM%U0nC2Ns{?v>1J+nN@kE|b)) zTBE)(E#luQP~*TkXdmV>x#(_iRxqFo+<6@ts9!N`PO$4 zE<>6jqgnu;&QR|TQe1$f4OP<>%Lwgq2X*-0m-3#vN{ zzF8$on6y0L8)KsLL?42e{E&hhqt+#=vmD$8Z|(+ACK?%gAY~I!mvKmmt);~vg$v+gKI&1Z&-sSbjhSqX zo9J^gBt1!&GEv)P(l_Q~vaLbV>)@MBI+N^_av=2Piz(xW*j_=5xxj^Tq`m_u=E0T9 z#$E=$YoHaGQI{9Gze=V9nn==d7MkuXYdLctqI;D_<206n+MdwZg2tARt1}vR(|E{O zIhM8(b9ZQP3P~K%ByR_2#yUc4F=L&!MgpmBX{t<94Vr2iYr$9#jP=A=uV^-FY!b#c zOYgh)xW6&K-k(^aQ+ujyJ^ zb>4LQjbfEHN~XJIy8C3LWn}HE+ZS3&U4Ekt0N!wL3m^2qW?FzKp zq1`ndWayx3hB-65qQf;E)yznYjuvJlN5@@zm!)18xN(xslXRY=^Aw$L>8?k26B=on ziK6Y|Y?n+sOWW16T|?WQGv+bUm{1RAd*-%RHD`}{*YHPZ!E$mj#ZcXfA+8*cZ z@yMRk%tnduO?&P&n_Y9=vllB;ERlN7UJgldYOe++H@DXbdo9>&$zFHu^~m1M?Y-OH zN9=u!xyDTOnL?L|91CSlslh@T3zt~9%_2h<>9NSors<;_uNW2;S+vcfV-}sT=$6I3 zEa78`t||LXIn7pkmMW5%o2BY3)n%!jDYsZU$I?rd>#~AiDk>{ZSZQpkven>BO=5MI zsZCj9#F`#b&Y5k=@@h<2nw>HehlKYrDZ!*N2~3z&W73?-9wyhBESTK^Q~hSI&a@5d zBx%dbx;pE*S-;NuL)M>|!!8?ehGPfld)PG02CD-lH^#N~j+9eumSwXoo4eS2!?H7b zH)G2+TXooK$W~jn4zO&Etp&Covh{+tLu~5<=gEPK%)uo%ORBJ}mna=@*&^XxEbBHJ zOK?SJ=E#F9&%jq!q+X={F}St|Tssb~w2JZ=h_Y{nB5WHs(-kSyQ>x1{LNnElsUGPy6e0&N%HnV$f z@cB9TQW<>H3%(gNmka7~3~^oX^36fjM+cV_Uh9H)jG==T_~8Kjr~`f-vZ7w_>pb{P zo(4)5pNHV~TdHnULlD9n2+1OZ#TL{D0p%D*mwxdTO2%|aVz6N0uf-uQIn9U)~@(`STqU{92J_gY>hUiux z#+ne54YC_r<&ag`L)DiM^D4yRn14 zEfs3n`3@H#F47QJ&fd)qf~R7NDYLbJeP~4ux*9g>0)(K~?lz!x6HF~Y;xdrL zH6$%hLt#j57r5bq$TFmr3~3htS6|xe3S?J@M3SVor~V=2NEvdf3~?KQ)KW=d134Xl zobEyCW>_$1q{onRd#I%mL8=86SA5X180AR3{jMxbH*BWz4<{N?{MBGKEm9va}4j$y{`%N8VQ!pa3}mV`3e8l{pg&!IHRQ1~8{R2mB3pur{-F-W@}=#GZc(oiIY`Fv2M zCDr|GK7gXGn5IC{x<1~29MGS?9=!lzF1E2KMK-Z2dP}Ms0fJSuH9L=G6`p}d%RCNngi$Lu}p-vS@{uS+d zsk1TQ3R}A=TG6JZIh)%hk`SOQf-JGOxEZV0q51pJTp|=@3O?C|80tfFhsewiZ7_i5 z@se7eaUwK1OlFQzDr6OQmYOHY45XF}apyJ$X{$Da=1D>GPN4aE;CDPU|IJa3M+YYs zEf9tl=tB!tpv3~vVtr_dh*69{bIe|qmMN~RqykMYP%gy6d1%VQOpc(bs+nv;Q#EK^ z=c^<~)CDxhq!5L+_e1w~Xg3YrmxAuw81p%Fe*;oGV((Klk|dQV^sM8vsY5RepcfX< zYa{4l8oC^YF1MhoUg(+zy_0}Gtw5hu>8c5Ry@0+E&5^|NL9`-S8W7Nm?#)@6xiV-K zqvoszZd9{(J96;TeRSj5V((TEG6Q?(7!b2&E=-quYg@2dLv!6SvJ$jT2dYpvYAbu? z0^giMb4k#;9caBWRBZ>P(l_R9sO3CFXMQbBA{MyP5NXc>4h>%nTax87k1JGwBbGRiXfs`6_8-~0t5qS!6>ayzt zb5R4gZ-JYJAy!tlugInids3pMH5>YAIYAps7Wb3X$o7Zq=$w>f+c&aPCEK$l#TJzX zQudNm0oyBtHT-bb}ie$T|ht9|E_WK^WdR z%{@AJzHlpnt=e>2Aw@@_POV6t)w?8#FbjNFX-vPIXSZ)DoK?Jw&zR&aY`fJf0 z0`FX}FVaxQQ`X+oev)-sbhu-k9vyn33Ue*OFQ zGOM`R`(6HL*Y4uXpN6(7T5)G{U$oj0QcANxg63CN#1AgHq46Xg4j>PBh~w=c>)DMp zNGj86f%WRndj_fk~vocyRC=^T6}9SGnbe;ud|-sIp{UL!ZwOEPT0)?EjrFIBg$vd=Uw(BMxz3a5A9Bu#ym7Tw8w2o zt+ie1likV|I5RUply&0@&U0%h3;+ALhfdS<9fVxUeaz( z4+szLU7zkw+T%Hmby$sOy@63I(8$Of1nDr%{F3$b#=(Y6i_rQWdtZUJSiVoWc8c|u!A}EJ z3Xs@{l^i4Z+H}kBQ5+$p&D|mR<;^K&59CdSjw@8_+HS>mi*y>NIX}&gY$a!hDpoCK zJJVq%V7sd{JFtCTT9a(Y53d=c`7D%m+HUA}Ghqiqb|Yr%VSCiIyIxvr)9k{|R*cfr zQbzVnIaK`AIqG36I!$ZVY-u;$kX&h+RgHO-`e&9$vH~?@oTh%ka=-7Z^G~jSyE%l` z>C$k@^6F-HM?#!2C_x+tA+L*2%LT}}A$ZvbKDvW0d7;c}P(58Tc0&ntp=n8oOv6TWPd}DJxy?5qt=v~TnbaL;m^K!GBb#`OGf0yQtP98bW z$<5P0xPF?8nYq}TE5EtoNWen^1rlhJ=#s=1B(9Uhj7SmEbCG%V-DXUdd$OL8ZHjDp zD>t!<94%yMd1mjXtTOwr+W#Feyj#P&Gdy*}(=c3Kb6q!Fe!~?oTtUOT`Q2vyyUq4p zZELvQM<<`$yKdi&S=?}!k52ACJUKEvyx|cHk7#&q{)}~YV@x(Y^3ln|yC+Bgm^wPS ze{%GX?td&CoqT$Yabq3*$Li6^Cl5}J{-5+e&W=t#xqou>E~vf>EZzl{MlcD^8#9*` zaGnSWdCbhs;f1rqDdOFL1VU^#gnAgeXAjRD9s0Yy@VmYAyG`(S8;=nhzVj8{`FO)q zH9WOCBIv#?^dmMtIoSs@7-$l zZnb&0+Pzx`-mN3=*0Fc%_`7xT-Ma8@U3|B0yjzQhs;_m|fA{`F>L05Ako$+}f6M+~ zi~kn=?~#8vTtW5d+4*(TZnXWg!wD%aA7s%gxMCCH-f!n)W@Ji}eG-@&WoI|T15K4! z+y`E*+R+}wafosotM0R+O-Rxhn$iLn;h^-!;A4Js*M^Yo5?2VyM2Fh)K`gl-wT2LK zS>n>c#dl;UflIi+#e>$m3NFuOP-f~BbRm%`#;Hw%9*3FDS z;$0G-K&!M#d`IGY@Pz{SQkFIqlE^}9RY`&)i8e{}XhMZt9~s*+P1H!zO_F|V7&ez- z(%g|`*=RZEj*=wFLEN_?%tVr$fg8?ADofoA}It&K_?|Yc(Dk{wKDS=NU5n+ z8H4vsNNH|W;}9bet0GyIE-A-JIZ4GKsSa5qNRm5JbCGJFRJ+i+icu25hm)ipfE19e zVUK#Er0y{)Lny-?X)54T1;Tm3r*n|vBHb7Ld}TpaUJ^}_?#wELNOuEOOPDU7$=AV!e59YZ{2X{~0$GiLE6AijCcU~jNs)mc zeAhr-GV+tLPR4yQi-Mmo!7t`yk|L85nFheGBr;l&dC`t; z46X}Fazk*3A^693BxrCmqwgV$p4Ezw<&-Q}kTM%^<19%i=4wvXDU#5@*N0>gChG~b z%*{gV*}%f7j&a9DY7j=NDs+Y zWQ{G9Wsht}EbfA)&B#tAvzak%LJp_EZ(C5k3kYS61qpccvQv`f_nR#s-|wV^9k zYGvp}D<8Ku&gQEINfWJt-^zEbd=sL3XBDQ9WS7~QTg9~LErI8BtH@bJk?flgQ!cAi zf*71b3@)s)4`M)q7?7=U6x?!Zl~u?=#VU`j@&x>13o$f=R-8Z#h9JkXwCRT+&Y2=v zH4mgj4U)ziowlW}AT~W#y z^I*3T(|t?jEiDSI-L&MfP{du zhloS8>?Tl26G6RC?s(RrJJ^f6+5I@!zIMo#?1O5 z&bE*O5+o@GNpc-1Je*t`hmERijN9NQBi8dm3@#yL(sY-!xH*)GV=LsL%-hziU`=b* za${|LkmNlij~~i%WIMhK+X)EQ6Ffvpedhz7C{x1(K#h(gYeW8GvAt{a@*qSEi zG?lj`#~oLpDQAz!YpjbjU4tNWX_|w!8qsu+CMBAl*@-1hcZ}MArY9^pw6qS*cud(@ zb;J&|9sC{qj^E!BS}#c)GvvSvUTDy)ac{L=HoOU zp?Q_&dysY>!;zgymKG8;U8jWxElg<1LrWc6PSbLdmix3kgOZ%mO3DuQjphQ1dQ-|!|f?w2VtwC!Ft-GvDA99GJ^%BHLhc^1qWC@z=C_*=F zPAC@!AD=)%Wt3DJ$d!)$#<8}Up%||<%`S5-(7wC z&H3Y_zkB-S+2zs6)#;Zn&mX_f|3l{AeEs(1{Nn7@)AN7v^y%a8j{d8B^!U*yCr9sG zyQ9Z8u@y&leeR=u#7vDX5{pwebAKdxu-h-2?SC1dudwA#J+b`apoLzoj z_0jvcqmy4y}O@0K62`LaC8Vhyz`TYyN^DII=lQyrnAcz zm#^+W{Jp&AZ_dsxz7Ks*|AX(Ne)Xf!hws;qKak4HXV-Lm{Rg9eeD><%?DXpPfByFP z?c0;zI9ZRr`ts|elNXn-UcA2l@DTdy%exQn+`IG7D)#%#zmNEDzW#k7z5B_-FMiZh z?>wEO$DiJP@aWOow(@UNaQXG++5HE1?%sX$@U!n@e~fd@y1O6L`t_^V=cgAx1pkQ=eu#VY)41=$ z?iq|IPVPN;@aX=@y$7G&y>|^A z%!haHI`Dym`RFqz{?Wq+C-*+P zI5>~)K5*bC4^BS4bLY+{4!rxwf%iUj;HP)4;XMc5zkl-Sox7iWdJV7NKXu+e`OLxp z(T4*cvo{lM==74|gfd9n-|Iq=Cy8Png^^-fFzy4p(KYn?B@zvGy&tL!i=f~eU1zkKj|HbPs zK7U{C<*gSNw=RzV{g=NyKfC()o7bKB(^Jh}MA2Z#fzT%h%FkY(pI)8ce*Htr$#-Y3E-tTLzd5^l_4@I5e}DS&&H3Ytw{MS6 zKFI#@-(9_L+Yc>&b#i+0=9+W|<<+xWuYUIA=<>QXNAFqj=Jv(;uW$YN>({Sd-#+>` z4h6TahyROzi*Z{A#ed~|&L`PJ)huNidq z6?D*u(?c=v+r&njsZ@)bL?#=C& zjuQJ3LC;>kxqSWsF~{G%uj%xIn$9nt-ah{P^rMeX|A}f&k568GU(K82?_S-0^V4e1 zUcG#Ies=YvdT#3Y#TW0T;kv+A7q^ej9X1`EygEMlKI-Y|)#=d>BjwHUcc(XXzd3&U z0owV+(+{wmmR!7f`SRrI{k(g6{2u-753+UnKa#EYy>!Ei??Xp#uAV(S`l+D3*Y5vp zdAoGv?eY(nx1U{o{NnOvNZo#Q{Lx20yZ!3ci;K&v(~C1l4*&Ya#nr=q@%r`Yx5xhj zIlLY{zbA)ZUYGhmOAb$d#Jzu7Dlh+MOXW{1{ikH~@(+^HqoaSOjQ;$|n?FE2Prnz+ zlkcwk#NJpFTQ8r<&*2CUVQxY{MqT7msfxG{Pgu- zU%YUd^5p1u`|Z(>lm6<(#aA!S|K+z==TDCQ)JeNp?EJvn(~~zRXUE?;y#4v@)8GE~ z=;-)bPrQ6`@$pyZR|o2EJ4h$bpS(PX*y-`f(+uJ#_#7cIQve-w*X) zJU@B*#qs0kZ?7BnPgZ`Iu1}wQKV5(H(d~WS$G86D`Lpxa zx2|5@dVY2F&E?}i`IE0+Ts?pD<;Q2Qe)T7(m(Q=>oIU^g#l@ez_`di*`SpvhU;N2m zU!MQ*O($Lc@vonsU;Od=F?-}_r{8Pulb_u=J~@4I{;~5ld;13lpB#Pp;^Or6x6UyB zfY%3s`h$4=r_X=>^oFPJ1>?_u^WyUA@&n%94An!1>k9tt)zkAcr@Wutxfy_m)SunG z))H5*{`~#4`d&^>F0R}DLHrGOPcAX zRvAZ+-?#6nvu1mKeD;f{Up#sKi>F_Fe)fIg|L*ix=Z{9 z%iX)Sw{0s6quS^V$ZvcNs+=Z0X>&tq5 z>4)R-^S5v6byw7)exH&BNg&aU(bDr!ECG;v@f!dblg|UN<8lyStu3CveSPf*%s17!yz~FZ1%Vw`3rzM&XRJ zYZNl{u_DW97-KZkryq=R8!T?&tbtl+=9vTI8=5V^j+-VkqpZ!XZ1QW_o8;y6uHa`N(k9D5MXvoo6{UQHQi{f(?5a}4EC$&Z|v93 z8L35*``Q3O!T-I0|9j6nfYVNttVO^<=DGVGE|KqNip${4%kLKR@><>`f7dt3-)0BD zo0}vsHwVAXI1fl@hKZ?4T!uI|AL4|cdgo^^8$UT7N^#<*);Gfp&P3kg5=eNsgR#u5 z*CnB51ZPaD&54 zT`Xh=%K})%E|v(cCNM+~Epv?*)sI@fDb}NHXL6OeaEA%PLXKEYP}vLgSD^w*z1+H{ zGfjp$et`~)i@EFh8I9&PE&dRPS(FSe8kfVidokYgcAF7$j!xe;e){Q$e>HaZ9lP*d zl(_8xf@WTy?I{5Ggt)8F1e0g{h zoZsX)>L8Y4+Bt|xR$DAML0Bv>>;<*Z6*1rq*B&B#`Y^vvW7m0}jvs1Kk`bDOF>^e0 znCLtxyqps#;&{CAq=QFs$e4WrbqS()m5b$yzwFI3}~SW$cE6g%_+ zE7OnI9}&6Rgi2gz5%I4dGV*pZVI-3uJ$`fWf{##H(r*r+-GCetf&1?)VuOchh*#I? z_#sHqIAYn7UXyXKvyb?$4FEa0xb^`iaUy<8WMYQw&fwyn+IPwjKNHhXTjJ19fD^;m z>0GBY+*{<2TV^C^Tae#0T!K`IKZe7^t~U>OzpL^UoO>SCrAn$wM0*Ll8~hd%s;i~ zUpCNy2H<$k$bI&O8M48;-a4hcr5A#9Z7hyrldo=Eg6v81yj&FFV%3e+r1*k9P3I9K zJ|!?gd}hub7DVtgR~!jT2KR}Wl5qS`o1`?badPE(2bCS*ofazYhAmx|3aQrk=D>nh zZq{UJd`nzOV8S>~M{Y|%QYMwUEpJ;Zro@3-UTJEjw)Ib6FlDY6=p^WJr%3s}8d}jC z#hT33>%^8mE+^~t&bG{t6mBM^@<4TUg*Hs)a^i}!NGInWr+ptO$f+!%TCY=ON-Rq6KlO=OG3ZNNg+^ zJ5};9PbM<_%pJufqzXfchnVO2g1kV#g1&5&sXB?)Ym@vEkR=Z&7MS#tFqsmWt}y>1 zUB+X6uMmRqnpj;mIBZKqY?9LQ@x`i+FXnAO6nR5mbuw;gI`?vxg;_MJjp8&Rpsb1} za5l-4&)c|9jlzTv4~k1_Jjn>HMH$?vfG9a;3lY5;?Y&4neG#E>XgyDt$vC7BJJ^VF z)R~fCxht6Zk;)%>&6QaUR$NBF$(K`d57AP!q+EvU$6)a>{VRoP{8!>3qIm_(P@40* zj|52!TVa;mXEYqafdL0Ika0k>Dr&Qx5-G&GXE|`zG#7CgB?qG!oSqpLml(;UI^kjw zN23t1*1T`r-Q6|dCf!)lSlnvH<$9cXZ4)j=Td#S?n~dyw{qOU!>vc8fS{S1}@z|R9 z_SJkDXVD_0S(6V0v=0Z@s^spfP}BADt1^enyV;I6H7!$;ouyeAgDjuHjEEZoXMSmp ziCSM&&QJ>wN4+-Xvfmd(Pd3QD*ki;)B+n1zIvkTpln|-Nj|g*qlxFq@L1}8E5Nj0b-Ie*)L>Bj!F2R-6mRQMjCuE1!wXC6kxr9WH>7)6< zeIBRR>z7eXyxpds5tg}|RlIE_2SUc7F?kkFhaP%$gWU`f;QNe8#$r)8H8+myvl*!^ z!qH7QCABN1)3~b5mn^GYOExu*$ru=iC2)*0QY&3=YNmN}u-EIP>b@!DFj$J}5Idk; zzSvYG*d2U07(Vivkx%Z)2>3eLWQsF+F>ay#*5jwPd`;4#9agWWqPd^vBn$n$wrTcz zXXv^9Ufa9aSB(SzSgtd;V8gvOC@C8t(Y=*5?Ll1Zi$XH3(Dq}~HUiySvDvB``(M-J z6T4Xuh%eHG>+#o%LFN-8ddE zrA;n94=omMhNK)7CH6E4iU0c+PC9Z@SqC=#g4CbS6P0ASVwLVICcO?d&yfj zWGJm9{9!F7(=e{RK0E8z#5KGI+wTglGTn_XrVG`@zt=Q3c!9PF)BBa+k}QSmXys-C zEokON6PFgtV2p{nw9*_+GA;U?hYPvYQYrGR%@;+fC%#Riajj*<43lhFvf1HRcvQHm z&KHXB2Hu^9cie13R=Xh&%%wKytOm!vXA)9KpkPwRFCXH7<3e`1hjX}H>WFw2|1LL!ynx6e13L8oPNT$iP|e|k2fT58f!aIbFnd_=X2rsX zB!*iD?B_(8q%tr``>Z`RsMxe_I2YXQH_WB3 zTCa<`HESMoPN@oF*hx0b`C=?6wr>()(&*ff2gl3#M%Y$_XF6WKvA?%Bxu@!o1g>uE zD@smDCAX(*O47v~BTWF^@^LyQFVguU%%ba<$TIp8Tcfxr*{>x0fFoFyaQo$Wc3!6} zbM(eZES$S^kPI>W8MKBTqFQRiTa^9xND`4Gv%!FI&ld-uCl(;9RR?>*KLyc?82|5b z^5K;UGbF0BntAKBOXW&6`(39D*f5}lh0HupsH;^Iygj>>0i+BK8TrgrG%Z|3r)1++ zh$MB{NM{vg#RlF;o`q~9O>BjYWI*gjqFC%3$#SV~y~jQ*I2VKMRjU<8d?V_%aAw)q>C4+}lISqI-}GWwHI-%X2wqRvfcER#c6a>eRuog6?|KzVpu(PSqXb z-3|V8gjT0#owM`P%NK9Eho`}(n;gHpL9~niY@x7&-?z|;3#@~WH)vBF8~kyTAcK$f`qK?eu>>k$Xp}Ai83AZs_%D>vFp3Ec$|^PL%bfLE49?@*swPuQ zM|C$Es`9+llExJ!mNW)sHZLKdtSBThc$xHOYyLC*xYZK6b1U`dC#@E*Ny#{(WVET; zg|c$!&sr8BXPb&;(=252vJ#OM1!YbyR4@geVbpOy-U!Np_~a?2j+#HAKjgpdnY5DO zThF4cibm;;_)c$-8M8<--+GdT)=$pUS1>MW3pvsh@UI)Z>})+kmxUt~M!IoSiUF6C z)0>SXYcSP~oK*;JHHymVf^cj*9)G*R&RkkTHMr)@LGN#{^VDzoEeHL1gPl%Ni{^`% z%t?}QH4RX9@uJfgy1FQ-0Ym5i4GJ{S;wjrI`E-Rj=+_(Uyg52^(C;_ciKFW#C&&KZ z@D{p{R}SznK^C*dB)ZR*lrSf7{3fI}RZ269S(A}$x$xP{LA-{Og$oC!MB4JDa%AoM&^q+%t#|;tTCFA(T%7xiSEnQYK=y%0q}_WqCZtcv|DTr zG+7T$P>~4jq!fZ0i>8C`Wf#t)N6kn6>TiZde>0RZH2<5Szu7&?*nRGNpQ8+rGXmvy zI3*1boCF{~;fQlM{0)`s1UoI{BoCuTNT*IPa2gGAKZ=(;cy<;p8h|D?OEcDd^vD_J zh(g=HB#Zedj+z3X#wblD(bNf?4?l<%pbOZeasub6|M-ayKInc~Cev`yT++B9%)}cD zAzBZd4=w+(58l^8&`d+y8HKkz6!Mc zYdMPgc%}A6;C%SeZ}9=^?E-qVv8w*WL6Gq&j;`h4_MIFheZ1O)K1ZyN<`^`TCV(mn=n7ktZVpd92R>6oak?AZI%8KMyCTt`XML3|fFL$vJU z6;y$7{N(Td=(lR?bq&rK{|AnSQ6D?6qU`nZx^|R|dCmucKuMRs_54a$MBwQ9zKB31eP=?`7)CD4gwuwlqP1;Etc1D#Abn$ z(Is(mG=njOa}xY^qYJ;kL1V&3G+F@73jVx7Af8Vc34Xmn^Ds(+-*1q}X94N+$eIlj-3F;kuNxtVHr0ZifQGuP-~+|O}$ zfnu9+T=c6L$7vrG)X2fSpX0cL9{PCJ_a!D#w~w!iE3mn{dg$jhw`#pcEDT4iv}C<2 zbVe=i^y8AStY8^CUw%nhHaMe0la3DiT=cE&3Qa+~@|LTN*2(O+ToqKMD&*!gr_q$( z$#Tv;&lLAOG2C<9*>0~}OYnTjb^7G47K35RlEz-ZyPQ!nG3oFn3}xbpD!o@h?d`4iaSttz`HATwlnO4L18va$LlrGznoWxXJzYXxK+WJA#1(n zKVKhqx(=}NF6HJRJ6Lk``cnXLK#srMwbpA6CJf0c2DY=+mqRuAcTqO0y*fN=LPs?L zTuoZ%%)(n?oTCNFA^toRZSfsc!O~vz!5oCrD1=2>suM z-Txe2!qid>9#i?l-m@YlARAdKC3r0Cec@RZW1^5qs8y0h4x(uJup`@}~1OZS$PrR*q-hRP`D(Zr5-|j1lsVs4| z=WD8>l*p*MtWP>pz4H}!V@|_uTQp!rWU^c^wj7-lhmI0OK8Za*PBRlhDLbPPS!D16 zAOWGAPC(A1<|D^LJIxQA`Dh2q>_qx-7^U$Rs`oaDAGoU9J@sm;EUSf-)Z_y|Zv4p& zNClSa^-15d%@c;(n$Zy7{+O_grVpRDV|f8IHIDWDSzL?>*urw0F>xMRBv(mIZP4_& zPHa1NSef!uYW}!kPXF!c*}8vz2Jfq+jzF$XukEy)fD{i@QaVuQ)}aa3(0mfqp(HHx z9K`k<{?wgM**PP$(Sa>uO`Da7x_z*SR(r8t-zm&>L%kgaMy4?7CI>mwPtgL6$pjc? zOUl*Iiomb#6AH-L8F(NMvpdWbj)e= zOw;JGMWZK*Mo%|s^n%mqxlN-FIgRdC(dg?EjlM0@=u4YMcQ$GC^IYsr$qxgkxq|jV z%F0AFFxdoX;5O3+HLT9b1Nc&T`BmUJXqGZK2jL(8%{vy~@DnYjCxQA5wcXFk3rE^3+vpSs$t6K3FyL`3+L^|Fc=r@BK87noF_dZ6D9JizR0T zv7|&@SwdJPt=uL})U3`|i5V4VvnhRq+thVm-lo)5iqp#CZAxn(Jauk^Uz3-IXD?n~ zcAmdIIXmn+@<9g}N(yNb89Ru#<&VMz$PQ1laCCD<;lU_ZVJlBHsvmIx1NO}sXT7EX za|e-2yp_cJT_0cfB}o7-Zkh7?`eh%~Ih{TZTPS1rx0ZE-pH27;{^*$@LxCR*A~C@S81z*sGH+oTj*ViZ~I(%yLl1^yZs!$OVRrX(+)aZ;JZHR zMVLQ^}PgNI|XC>v2ia(5dAGucp@%E3ijzj(skiTkD!vdD6w!4a}9>K((yc(|LW?RB7 zC7KBL5^_Iyg&q$V!mt+Tcukh5g^Cs7-lWuJb5%a_1Le{wS1SQ*H%EiweRYUa4~Z9< zC*Uh%rRd2B-d=k8HFjaCP?UIDqQeS{jxQg+cv8IEcbUTF!Z5HL!BSS>rux7DQHGFG zIqgnAxa`B*aXwCDA&l`JMw}IrV~??GpjqIr!rK8rfo2oKGqMuT$O=4T(Kfr72Q6{8 zU682$7oQp|ynNe4$F7>gnHWdxdT8u=IU0-aiKyXvh*zg%p5BtfTaskGh-D-p)D^_k z0XG?iM`u$nM-v=^UoquHWm`p#)~P_N^(1iOr^yJsX{4hYk~xS&G{xoTfr;K93CZqK zdZQl%hM9O2#@~t}N6vut%1?~dJoC#vG_l`pEI0}%VE_yztc+M>`j(A@g}LgoHUTx_ z7+Ftu7gc+6ApudHtA}r*{}NK=e9dvxbr*#zQ7`f%cA_qR3#k|dk29J^cko=;v-eW* zmP+dTp9|c>&UubI*nX`gX>AAbF%>MKg+GI6;u=jVNKocsvIJR>H8D#Xsr66P!@jY+ z%y4OvdhsV(0ZIng>evsQRi>OaWonxj`iFfiX6LKB*S#Xs|2!RY#RQz_-~ffY1+bS= zl5T^Cn_F$bq;Ud|gq83U&^;G72|XX^xguKUFoJV0-_oL#71isyIWZGTb&r)q#YV(D zypQI~xfv)Wq43yL+$r<$e%tt>NnvU2VEm-@L{X)B{UwlH^>W(u3^!Z2rV5Zu|9lGg z`^swqDq)z7aLnD!NFO@LStj~&yA3Gg9e`sKt`Xv$@G^Cud;D6VHqJTLrwkPMwdI;u z>qttI6za4wh(_z)zPvj~u@BNBBO`lKcMqoS9$e%s5TsaLCPfFvLx<+r(cg-{5jzf@ zVLg(Azx5Sq2jesSQLnq_8U@no)KOOpxzW*PiBX!~L?B^Cg*nB1PS@+r#QpVpBki#V zvoD4U3ljtiT*+{*61lzroeSxMG`*?1&6{h+T`HSCii0xE$r_Oes)+S`5jLc5$+;+3E^sTUHT~x5*<7*Ovc+biI`~-$)Q-!~6Iqyn5n7I})p4P?HY0oC zt_RGThnM7L32>#*dVQ@WPo+ikxb?JRAwBh0BP@BrL)3-&*FMoC&vuJf+_h+s$%|Q- zWXykmdHUD!i_4e2^V8R85+xCvz5IMB&JbPm=RcpKg#^{PZ}2a3SOPE^^It8KPtn4? z^$rYt_A+^npLL4M|T@;P`-l*2q%(B~WQU&9fryt-a0R6VC@ z7DwPaO{@!fmUZAL=oGf^VQHgXZfrCG&ZPy;<;JSmSTDJUUi96Gc=XS&;N1}SqJsi+ z#C6Dupot%k)sH9Y$B*jAPf%H2r}3{X_>vcA{4Nz=f?vZY349t)3iv(m*vJU*;X=}a zlRgG3p#~+>h-LIanPS}cv9te^Ff=*qOScRSxGIjh*U_2~PI12q@RE@qpxoR@HUwVZ zn1kN-xdD}fo^RT69D%vXH(3~MF_`+YNt9)Nwaq$8n0e?9Msd_I=2*gJ%dRkq?rV+4 zmyES6Td!}nzRJzVQ1u!uxA8eAOl})c~F>~C<&e7=$2feXPt|%FY z;7?TBLcuF-t|Q?#MNhcR(mcG8);x_FR}FK6BWdtdvQBzzTPF!%jB(V4brRysR6ed1 zHe=3*{m0TQ3pA^ZIJHc{a`g9qY%uoJL8EX4oGJA6F9Ao%Y|D|(3L8-({H+2qoNPi&t1yX!wb_WKNlFP{A1oVOTwGia8+ddfwNDQbz?dryI-6sjp3%dVOtbbzHKZ zdb4R@^|vs+dTorO&Wk2dulhMky7*n66a8@qujb+XDVH3uVE?gV$^|c~c4OeEEv-`g zWHJAn`6-MT%V zBwcPTl>fqiN_6H;SQ5YNu2glEzGsrSvZLmj1Q;!tH0It_E)SAVeQhwMHY0Fip>)Xo zdke4L9G-Q8Pkodwv&AwCR=vYN4tuA;zyZ!^98!pyDvw=9T00_#^I6Ui{dV}*X|Pfy z1FMd>6|0(}VxSuke41>Anv~_cJY*(>UZjfB=-6Vyez~>0I{tO>Ic{WCIe+ zZ)!meNj4!y_DwAaEXW3O6sJ>b7LqiJ!01=c0r(_$K=3h19pu~+dJURI#MdeyC5mG> zN3v)xl%0!k$@3`zSCM+oM7x1*zc^peNg%s|o&wpQ>@G0tr%8uPLRJ^^1} zUCflMD<|B^!L9;oT_kRl@Bd_M zlQEhFDhu8p@@!6?XlA+uZNjS99kebe5>(B+1Isz@76@e(e#g$dyyyv_!Uq^Jp8C0q zxk;43N#EALz&OFB;H^NbMrh85VTXqJ8dzXSZ=3}hU4Yi`i&#TYC=f+AF<8bQ?@uGp zvwc2q0a?|6zHIDeRoJ6AGuWeG_^Gg`e}SQj3s$UDV~>=u_f?RHahLW4NYM$R2ev;q zt+E2w5ZpfoLlmd_F1@7}j!;Ap6}t!}pu`vLzG4_|66D@NAN$x@(%3YzW= zwdEB90}8O34w@I&ZnBuO_N6xv{z+Pme+^dJA2-#{01Gsnz&b<8rv#Z=u|6?_Bs7VK$@?vk@Q4GG;p(Tnp?^R9ir@Q>KM%qA`B4}8{j;iHw;c!rw)G@ic)>;U z0zApErnBp}zZ?4S+ZjIkTp_-#_!;;IAG3E+Hgg>< zoGr_cw{#+=@lsjRcFvC8a#a5Q@Ab+E+!%#y`U_p29-dvkc>DIZqeJMA^uDP-sS_%7 zlHq5rGJ?t4N{BC-Xmfic^U$!ZpBeIJi*2qzfrIl~0g)QN3P`xILe}=-zkUzdR<9v9 zAyIO#MF+3W{^}oIo*ce9{1X-x{{q$8$G-U8@0`5`2+dXx{^%oXV+McqQE`I=zx5F; z*x-F1ouBkB-*nDiygob${_Laf;bFgWB?P|~GtHa@zxUC};k%QgGXV4V^TX4#fb^`S z)BfAz)59R^<;I9c_epzZNQ)R*gN=ID0Fz#h7acT8u$$qQho89_wvH0z%r8@Z4KtMe z{8QU`1k`{CBdzO!~`2m_VzrwhqFO4+`DpjSM!c=6OnsY`ap$jg`md%kn7e?$n@}j z;Zoo-d)QrRJHD*2h)E8QO<*PS4Y7y|8-hR8&bg;sRUc!VX#!}Kn8sJ`2W}Fmto)kI zqMf0?_lwttB-Ir7gt0)m+TGt>k@b3%fJaRT@(6GXo4$V&XlD8O0i_#%V^vWYglEP+KVl#(J!iG@;+=W5%{O&~K1$ZiJh0MExKTJN_zzex*d_5duZ9it3lKo z0?{O}$2T56@pn%|@7c_k2tj%uL!01^*+YXx%v<6<4GOLZ?Uz($fC;8;@p#`16s^pR zm=itCadyDO93;6{0xH!RWvI%yF&yk5=@nqLAmWD=!-*sgzEcXUWP_L%FdODLLrZMM zSTm;5VE`J#P+NAnD;nPQ;bcq1G0Eh(mN2)QdEDbE8ufr6E_(yvy>bXUhfK|Mi_v60 zD6n|gk@0e*d1)Q1vjA8cjC$bq&t>2Ogm@_KaWf`qy=cu~vYcNN>a<62Qwhi({P)@N zn#o8JlGJ(XKbmo96;RS8+eSl_H{_v7PdUXSQ}>}$+_@z8JRs^^PT9k&kY$8M>?Rl` zh_F$(AcC?6(++SIBEHcHaQ&}nx?BYFa`IU?4W=;?ih^?*zn0YR4XhZ@1@}yz?Uz*L zr&fMnZgrLS&@p)`Ewsv?6n3Lb?>;2qJQ~?~v8wYjx+h~hXQpyw^DH3KO?ZNdVgYKb z3&;6tv&f>C<8cp7CfIrO=#hhN7bWMka^Heu`*s#AdTxd$6B8wdb$!DLY3gv5EasQH ztH~t4Dij4SNEez+Jd}0|Cwy~o!SJv-JrL)wh`)#(2Z3WXIEoLz`ylsJnRy3ZvWIUM zu5u5`5EZUyJa3P3pLCe`amcbGNYqk-J-Z5M?5Dlb`&h-@q+M8_MUPXN*UqFTqh}RP zxl{1G9S|?b_)?wsu*7+AyO0yqOQ*q9Ps}LA?=R6xsY8Q@UXHsV>h|!sgH|B)d^yVT z?=3ZV!C^1Q^B%hGVP}hK4YV3OhT?JBfV&8&=o;rIN2qbV%tQ+4nZY&za@Z(ca4R9D zY`gAB`(@40qa}?30z#&M3ZVtq)&&VyO9Tv_ZL=WdCu_=|)OLHoNu@c`Z#c!P5KxOr z5hJ_`DdpZb;&c?o4X!qV0xw6tDD66q)f6|cu$uzs>&k)4(^jkPhFv#9VDu`Srt%?n zmf7q#^1%J(Wa$k__%9d%?+9du z0!5D#JlP9997H{8tHMIkCj%`!^hE4c>UjZ`{*vx`__}B0O1wSe^t+beZkQ22N$*^* zLHUL%Ov1g?pTdrwFEYlXjseP_?YFOLGPU;I?uv;gEa7GY)|5BI96AsHENoxZT%HQS zdvcFgAK}BEeCd6_gLciL%xS+I`LGip00FEUbhW!0<+a^alF!-hD$RFS%lyg_%3Vgb90(e>c7si1-IOQ6@Jw3h(a7&DMSQZkdYTy z?WEb_b1l3*>zC?Ql{!yuz|DQJr0rO=j6tSeb2rn3aP#IGu^)iaHD0!2Pm!Kl<^m2TE$9L;#4_lW==Y!i0esKpMn!~-e zH)syMw))t0JoKUmh7{?TT+T&Hpi>KPA&{XTjZ$Z5w*AYV36DG&A`0Gax#QzvG#@G) zBs#z+DSrajqy|MC55=i)W+p4j_mZElx=h5os&T3Q2K!Ojj&WYAN);NBd?LQ@BB5p%wMn;$OxrU!EIP2l+{sH~wEghuJOQ1kL@DkdBOl#RfC z0&0tJfjbyy)s?~CGmMg2OhO)3Hl@OI8jruc2uFnRiT9Pu+Cqrp4Ik2>?j+oEz)&Kr zUB}_*CXTqxH+1a4HL=t4ql6o4Ft^kScL>3o$W#+Bn3`B}vI(T7;@RW^Z=6@MF#D~lr;besj5(228v z=yy`oSGju6OIN@q2T>8PtfGiWM>I#fJ^ZqFa2u*Pt@!uU`cg0Q@Lk7F&C>po#`wst z2=x=K1sEgVg0o(i8b`MsoK4%!i;MN3Icx?^_=~PP zxCv*;G|C2hL$3`=ui;+MjL_R2Zhjbi_+_~F%iu!*+9`kVAsFrj{vN~wrBmD-_(QiH zWYbUUETq2p2k+v|>GHdHX(nW^=x{s}={y7;ePrDLV1W!fQNR7q1}$(A?EC_PZ9xF`J;SH`;&^)y z{KQTGcK2DUZ6Z`KxcbSlTob}X8Bc+4qC$<-g&+5-pZP( zw_ek!`YoD){#=Svhz6&wv3vUv&+(Dt1&19s^Zbh41a&gFOhc&zXXvg2&jLXru3q;& z@A1U}OJU#-*Z(_c4%dU`#l>(f)u6+`YX;@~Lav$hyN=uTzW*Z#W)L6C_xtHhdB;(2WeV{qQ%&o6~spo>3aXXh9^AMtw#9(9dY9~LuIM$);RBRf4Mmexa9B}WUk{$3`@9>+pBJ4t7 zdjLNB{VQV@B44&29v%PD=^b_Zofp4#UL78H-W)nMQ2m}DQ?~TI1X31GKLZW{aL&T1 zupiaVw!*|dM1iY;(BkETS|%|Ia9RTVtn*3$O%2e!uK@aC@i~Cu>@q9>aK!w=0t_I? zH}uNk?R*Zr{`pA{6o>`*-hpywWxNOoxiylWWW}<^nf@sD2&@30{9 zZSjwz64GG@pNRZJB&f#S?`1czj$==R%r7ifk6^4#4fOEF9hvnWuhA4^1F! zkU9D{(gzr?tLT~t{Uqv5ql`z9#F_r7GYkO|x#n9JIbB|lBPz1RZy9Sg1=b#C@<;xT zS>d@PAlNoYQj%GIuJ8v;r0R5$bVY7#g)PqxFf1^i*~qOpa9hYaV4IHT1>5GzXp$Xx zr}00JCyu~feoz>dy2xY8Vo8iDgL|$9P00hS1nhv5M0vka$tLJ*VdmHC;(W{RO)@Ec z0u@^)=91LTBq967@$?Gb7!yd2szy|d?ywSm!t^|-02y-nQI?y7K=R@g1R!{V2B^6)Ix5ku0XC>#f=M)aV6}&@nq0VSx#kZVs)uD|P}|uU(?ASED<4mWpKDK*hi5v%qURz=Jx0 zBi_rUphS>o7$yhm@}oFRSFx;{%{T#XDR4YOOH(bS1Ky zyQ@!;z7`QLZ(f15j4Eb1O;PSFtZ7l)18+)rN#P!*J?N;q63 zJ~OVzHpeky{7gn1B>_Pxjiz8_%6kmJ1Wfp-OIZe%yzn20Bv%md*Xat-*qp#+e1NTA z6FCwAP4F9{1{{AKUSbc0IIhHV&OP~DG*|oRNq*s9_-*&1x!(2U9ERRk0my4B82`%v z@;DS#8`Ss&gQuYt1rk$0AKKG;-Cg3P)zw9F&*jpA;G*d@BjoJvJJ8_hOJzI+8UI_r z*V1ddhF!v9@Vl#}v?*}+!N6yPEmYk1z^RJLf8u#w@Cn|icCJ|(FEiqXpq<@l(Q07? zQGvG=0Uv^Za0I20#jn{gKrYG*u{f3-xI&urmc<`Is&f_t7pwa;EQxDhE%!@T%YAOo zVqoA=HM?XIF5~QkNF8J-j(>jC%)s0GGIN9v5~~*viL>1|=SX1)Z?<0|&W#@lAOhUy zqr6NAC1=Hb;QaXXyx->z#QG<1&)&Xx+bi4&{^r^SZv1Wbi8#Uxju6+^&`er;Drt#P zY$cdzVf!@7E_gOh%pIG?FO=Tq#5-Vj z5x`}6-&~DCMxe#O3DnO$@z$vL&FlmU+I%}PAw|qgF zra;ib=S0(FK@g1KrETNx%0iK{L@Y}Al{-Y~*6<3n;Q$MmhS)7pYta|xXJbbARAklH zGdaSio_BCfNqBR>F=L}yp;+Mv9B5>u2#Z-}J1BMMTTit$q2rv@ezL zQotyP;8U>KGO`|+hcJ#pR^cc$5w3iAHre!mD%f^)6W^?^>Tx*%cNqop0sVx2OcqSH z!q1VdEpSFc$VQ}wNFO;cEHRVFNChys7FJ#e`QU7F%5547TahOiG@f&MQ)tdy;WR#l z>h0ij4HLabmtAoQb_v>PL8LsDz&T`*$|uEBa4Rv&f$~h0=q$lv^Ndxseuu|LR0!?& zTqzU*XhjoQICabd;@%}X*k~ivOPC&>XXsrIUv@+!gu4Y-NX)uA9IZ`+?iO(67m~p% zvorJJRupDgiPrEP7;*2X9z^)$h+vYIt|*y;8H_L;f?q5L4#-eMp~7BYG`(HH(-bT2 z?CY9bJe;FX#%^%HL+}QdP~r?se6EIV$9y?Kp@hyf>B@%pYFMx9vdWSd%BupV3V1>V z+GSU{ArB&{?Wk-AAph9gj%ew)`EFsE`wE1E7G7W;{n0b#ax2bG6REy33;UuQUt;o0$||}kKZ01pDB_=v}9IHV5cME2y)YGIX3Q9qQkxA=9qA* zl5G-{^OGacIZLkw+KqG7BEvIoDz#oqY6(ZbXSqroPuwj_4B6INB88ie1=Kf73ychS zDpD)Na`TJS3W6%?x9$5ENx#6nJ;;FnNG%-*t0sRUxv~Q4%U?o2vZIwOWBdm<7zeMO z^*UUj#`XF~I7r5tpXqemvD}wqAS{OHuL}5^Xgo|g{>GM*QiZbuawe#vrk6`6ny}20 z;_eJmBvZ}K3qqABhZ;6ES-SMR0ad#z}ByzS*lZk1P6<1qodI)t7O9k#ogB#~MoOi*v5T{zy z4qs9xEoq`U5r0a&@CU??xm)^mw zULOOmsK-4sgC2W%&X3!_dbra;pIC)GSM9eRj$7zGm}5ng#ylNIlZcEPEK0ZsgJ{x7 zzyyt7Ld5qnsVZd^5G=zG{aH8!p+J-5o*UluWTT>AFLtsalKXo8nP`@k}>mk{WixQW-^XgAm{Fw1H9Yl#o%cX@NH$F?}1WZQXTh>>sWPJsf7}YNL*JPQnB$H{K@i-Cz zwUjHaZ5it|*!_vjH{3}M<@VV;0iL3mj^GOyi$d6EZlL&U5C7Sd%l>y^tjHhUK{(UL zttaSl>nVEN`X_qQYN02see|UD6MEWuik`N9Ku`Bu=;{7b^h0ZZ=pk}kUE9=rjGnZf zpeL;#KhsKPwemx&g??x~MnANkpdVUKAy)MF8(y zUTcbWv!!(N;+IE4;f*Rcg4|US4HGsYw4s95@p8+>l!jyB(LFlm0SOBp9eGuiR;an9 zL0wpo2x!=-cy0?SJ-q`nzW3yU*ISTyZ4$K1H7Yr_93^D!>{vYAlV-IZ^rXu!CU!@w zTrzM<38wE+%HmO*AUC~oL~vy!FkmPh;zX6iP4w71AOj{_7~<4K_ugp*scP8>CY`mj z3(^+Ha%G?)X2(V-H5eY)8m=g*Wrl8L13EmA=76B**lD$CK=&N%>?NKs)t2v{v{kr? zOU=)U^jJ9OF9$b6D>e%$83zgCo9i`MQqYBr0*0dbe3^lZ*EdlDZ!gZ$UUUoZ&xFzc zi;zy!7a&{+(IuRRg06u7?Ui5+mQo9E<-*}+92D-*_1Zkhki51IEGAO4f3HJk!p@7c@=`t42H1t(l)ovExe+_%Bgct2Vnk{Y3D}A{!~$OxDj+6{ zacWAX!Q-cop8T^Vh_)y$ZNMG*(m)0J5--Go&S-EA#Z<4=b=GKv7ZXFS2jby@3c~3O z)v%N+SMeV@BW)F2LmsrEAlS(8XASb3-};Q*K9Beza+0)T(;B=(s@)D~U_Wk*!@mvb zLtEDi3>yc<#n{Ma#unD=&Vmx@H87IOeSsIqN?Eog&sFMYlQ^)N8luX4(_VFfB$W?_ zwq}b{u{s-MQ4LDC@CK;*aB~Gzs?_ZgXjQ^e0Mx|v0yfm;zGYRA&_tB5t@#TYPUoSA zwy!o7WvHnTp@TNfift$+b`x=Ur{(2*Ln``ip8e#2J=kD6b7x0HvzP0|MzQX~__Y;r z56h|2+if|S`$3i_7?-n<9o{eEXcT3KfF-Hx0BdY{_E=jQo|A2vcd#KX(iwQnXv+Ev z8r_B&1UK9)Sh?*~atIEu*=feL${mY27;`Y)ZL_WFt2G$jH^8OhP^GKx6((dk)|iL) zg@O`(a6~v-uYd1tqQ&#_ZHTnvmwjuW6+mweGylxFAk0kGYeT@wM~(;$uyNw#EX*P{ zi3k_wr_1a_ZC)4@QODf&o;CCFc{-;r0QN>%ugklPly}*BO)ca|Y=%?fde3M9Yw#5% zjR_^-Gf)=H^x5uuKkt(#kBX%vu2~kseyrDbT-hd1fmU`tPu+lxFVx^1jD}ezJ~Kc! z_|JKG-K(3>A{D_$Toka_QW`DQ9r>rCw}{^H-9dsv)&p5FCB;#oZXxEZ(( zvs$7WCWFF5jS`X34a2dg!SeFYZr;7ovtdZ7~#UrQL)h%?2&l(x5;)#_V!4?HoeSKfWs3Fg919g9Hol~xi?>@ zi{}ro(}lI~6|Aeg&?PWO9z7l;!}Z#wgJg)gKa~S1M^=|JTl;HLOSg!sK`|Iq;;ArUcNqBtWIZsR-64;SpwKGziUKkC7t=V>KKTeqRTNFWeLaUg^8 z%M|04PBF=^b-2ocrQzogTb_Ba7crYy)3Rrr9e(^&(}^udgWNM}+nZ`G(L*yoA#|Po z$Waa6>Uw+M@9#gUHEIr>jDCFlm z*Bl)dXpPM_yyiZ6qAfo6Xl^y3C<|Q0%YcSc;^hkGhCUbutVCSwb7|`#uJ)>9IS4DA z6Owe567|ih;(*^ELN$w6DIknU+VJ`yEwM>xMfDBJ=~bYtR3b0e*5~ZD%BDckoVPsq zcI7oQXsP`r)?p>3mRwNjE&Y~7stT)RbIj$Je#DN$V>f}IpFh)fE8tsZ9^ShIxk-ca zl~x?Ro^SAcp{32v@TUId4(`}{hW>HLv3+|u{d`rGk0x3Z*Ijh3?uK@emqNw@hl0odl zm>f+6$68(I%g|fAR<7E72ViTymV8^=6=Syhka2}AF%T()Du$x2w7Hl6R<=lqDkMq7&&nbs;;Uy6WdRcL zN3$(bBLW-Vj%ppv=i8-6VBfVVCg1@ZX9Xz{Pa2Ji;vnMBi(;RW&}WOJhxp;9PzO5r zI4ekRc+zN;XQ`YZHM^_ZU>{`_M;?NxF9jr`kxD6|5X6u{B?b{wElVGZfQw*5P{Pp> zSSUiOJ?SxW8EDiXRiBH$!k`i@2*w|j@q#!C0t+p9POWRA#g2aUZSjjN7%2pn(~PFA zuG`smb82}?Bw-GFulQy|v}9=twLFVf=@o~FRv$`PI2D(`QfqiKiH9WKNX;Q$W@Woz zT3eOwAv$nE5pSBKg30@@R%+_c4bA7Uc->QsjriIT;w4UjzJQF4VHh)|&AbZMKUJ*% zyNhO1Kgc~`n&sa_DBI5aZz5H>D*3{B3`)7m5$ZWDXW*|m%3O;sUQ1 z(ii3Y8>eEuo*cVWE#{T#6^v}=V*SI<1@9~nuebhvWrVO*+u0WyZ$XJPRR?DmD(Ok< zX&VX#Mo20)MtP2l`TPNP^^U4cwyokttDt?t7#EujwzOF+wXN^3?4-I_@Ws_p(d|YV zyR(hVU}wwlcD`!0mnUd@NAUO%quiJz$Wl0uj%{}-;j#8=$jP5)nMfW8_b?P* z7%(h|8@xYIFsB$k71#$3ybo#}gg>VW1Z+b1>%?b`)Mx8;;(u(U{zv$cHc~(Jes1v%pPGo*28Nl2LG<5}azp|4h9ji1 zQ=?>!_q8dLuC`MUcv$aYS5ZJw8W2B19|PilL}06AfI02hGY#v_SSmP=oFIc|Z;YhcT|M6Li zn@o(4vDx*AmB<4#lYo@iN>IV$Q~RNf>c9@D5AtoPzWgAH__cGJWUn#SIYh;u4x6&U~@82arFAD?+ z7WC&R&Ro%lsiY6xuc0^(pP@J#Eq)utnf^x!<8Ky=)8(9jgfYj34N*Z)3`vfKF&c$D43!nyrwDghJ$ z9-8pJ(mm`noINHMQK(4B6GK9l7(Q(hGQzEc(a%dYo{<_)jF*Ga(54-+CLHy;8`_j3 z2FjtI55c@d_d$Z#asodX`WS>jI?x1O%cjb)Rpfp`i!fy;wQ;>5BHI(G=Ny+CeL^KYcyXN5iZvP8X`U!vc) zirOAZYJ2}R^n3R+^n0VlZ=>Ib{}KJZFVb&V2zyQw{MpH6VT#^vQgfa6wo{~}V4%=C z0H2>4?C+iL!S_G+w0_``y*)(t_6|0mLYy5NZ{U_QHVnKbN^}LxJ+@Pv1-utYeOeGg zCNbY+4;)1dC`yJFu%G8=_p7M?TNz>Ne;#ApYLmg<9=8#r!vK=^u!EdE_&nI4|9kuP z3GjSd-v|Fe-v_d9yVC>J2i~Yt(~NELet7ZjmNG1r?n#%hNIehzjivB#Y>Wr$`*g9 zb<^cE4q5HR4CKA(Vn%4~T@R$k2229-O>#11PR{vRwkdH%?nYq|Hkn$TzNBD}}TCJ+WNn zNfDFrm8U6>5Qj^_vHj}4UWf28`9W1Xtk96GaU;$BB?v}CA(fu8t}_HbAH`>XKCu_P+q3enVCPVk~d6+dx$LUj|W^tOBw31rSx`DiG(2A>9U}F7Ra_P0=bS zkE@~d1(2JNl{FN&YEgZ@3ahNcwp3E0PF%S-zEFu(*V@*Xl5yb5S^h#LR$XgbU;560 zt7q#=)!3GLjPnj}jchAR_GUHMv_V+{SJ2la9_I&y|_OJr5*8Z4eF( z+83vfhL6Ax%B#&XI%4l$6AxEUpQ4|B%02g@J-E1kyx+LE|M7762rgUb9q-$u?(H_w zUC2XTwUzb4RU3lg2y=zD^AqwLdMkA~-(DtJw zBU2h>5AB$`Rp|y@p7kV;+%L!|BjZy*sZ|Y7j8s2u^}~-CfYBBmOw)9HN+a$)sQ@|F z<;_5YQXQ+w!}%h6P;*<+$RZlubX2k#o9C70&T<$rj;4fV5~H$DJ&HlG+R#Ery2zw6 z-@DMpV!8k_&Ac3FwRv*FnN$aGr5=@im$fDOzk?WtkgaDxmm#_gk!5ZP6-EZC2-QWX z7x2aJ%dT@Cn0x~W2-&47r=SDkb#&SMjtZmnf~6-wzK0d(Xs<;b;e|p#)#ZYli4sq> zt6IyUua%8uX-);}R2x346)BsBW$h-2dHC6twDdBI9v)6Kc2|jUPmkV% z$9aDOC9y7s65ZA&><}cnS}y9{drKk<7iMhawF$FINmhPxKBvG=GtheT|Z=mc{VnVj?WHXogAJ0W$>|y z2iZ1C%0x$hem!>Nmbb8OmE{D&99?p;en)Vjl%W%F#iip?sxHuNV^GIm4!EK~@@_S!eGMb_(`eJ%T}EZa`NT1^kn zl1HhP1kXk7iLGXcL~C+wygpXRa`vQ*k)JxE$h%{B73w&_##sU9gwCI??y zvYHuF1#H}5p)Ja5y{q2SDF^R;^QIFL@_r!4nA1!&$guVKinm3o=OT{ZRv%3lY_DlT%Zq!$N*`*X z0VW5R+Jj3=gM)AeI{P5uiY6ES;Q>7A16whECAYPHNZ`Zrkg7?daC=T+o&>g|$pa># znJbA5qd=)ZeET!h9x4YFVZur1%4GlrF2m0@0N?=+^P1e_kH-eMkH_VZSa+dI&CRw* z-Uytn1&#;_rl-Q^VXi5k?7ACfA^yYa`;X(IpXq{#X1!Jt7Y-8H1c~a!0MXC%VhC|T zHsYq9x~FE|#_bU;)zaaq8($b!+WoaReu$6tl1|wKV@Q*Jf_#qb3QZFQ? z7m`BCDZdZmzbv%;RYJ>X>K6T<_GvngnArrGzlavZrQms-hv^n~5V-OdzXd~cAX2S+ zMTnoRQXJ;Gf{ODEnn;g(Wfbwea}GDZxbfNi%>w&3y^>&uD>9F`vz3A>S@8dh%mA(>Ewq5wt6gr>_ttuUZ?AVgw^cSf z)JR$(xWw0N3#B>}yIguib^qVn8S2PQfYQ)vN+T((q%=v1(xe_rir*q`(1A!bqKPGb zwhHkw*X0$VVGzXzB_%?mM_f^<`Mq1x6(}LY$0_PzMOV`{1w|gw>?fbi zqDkgf5T}uKu!V52Ey004#Fyd3Xk;I3qtd|^V=Mb09mH_3!9RYm!4Gk;83zlgIoPDz z?b@%$Kuo6u-fO~hV-MYR@xYmne{hg#?QFDr&ckzDxdVm@*Ar z>Pf?tyEFwZO@T|U7*o7@hM)at2VeU5E!}a z;`A7Gy7lsd|{_t2ga5 z4l?fD(bPf^M~f21g3MPxa}xzaX_eJVg-E@y-fV|Vn8NFh9KGlkyH{@o<%32W)PJi3 z5xVTh3ZJDbl4SHDAV@011B#SifgnM0tHL2HCFW2X>)@ZlT7xpqFNq{k7r*Fo%4Q1< zrT0zom{DAcx&=jiu?ckAl@0Jtl?YN)ukuF9i@}72aC~z&Y}!?Zev)q~b!^wj;ILo2 zcGhkO*Xx~u@bM}9kPXf9RzAwOGc`FD`H1>QfZTEdV7%SgzO#g90wwV;nZ2?K5W0g-5z)Y$$z*dzfz-;}y4=LJx8N3Mr2?#2V7=aGz!;TO*SGU4 zsvEPJ>gsmBc4?<%oJLnmezYCsY~*{k=1MFflLusD!*^!QUWe8+gG>fNp1N*Sg~xJE;AYXBpVY- zg#-+-AMq(gyIuS@MJHVxhrWy9%YBg z*zwRulLKnA3`~$9{b5;5kCLg#n57#PIFIY0uBkGLQwHvZ0Fgj$ztDYGr$L|^$3usv z)FfU)SXXq`EhfE~Et4C^L%(OHD`ghvp=$%N2%iXj-`cIFccSFPfVv2m0-zDI00P$9 z@zAO1=bDRhdcx7xvyk*9RMF7&99xj;OcFJdw$ z2@hn3!a^g_M*V1e)#Z=eE=n0}tk?YKBuXF9grn|J~miQ=|>YKPMBK}hD-YfD(D zw_9d=U?_cx5ZM?zRx7*;+^EIsc!paj=H2c{`Cr7u4>$Abb*rKuu|Fbmr($^Bbhdb% zPqe|(NHJ$)&Y4*TT+*L-nv7`}CD`S`wnl8|DoGVjcM7Gve3|fDs%#$`^&wE+E$Kxp zgDCS`>Tx%Le0#(Bl9Obt+E6VT(0r08r$D>|UO3OutW39WI;$c)jE*+Ik@seCU>GQr zS&eESufU-=0>sBg1DJurv|yRQ;AysXSKsJU684E2NWY`UvfEvK#{O<_yBsPn`h!WT ze{-1u7z2neK|$2#pq~vf0b?9PKtLm-Gr4)z;P-6E&A0;n#p@_Kxn65qEuM+7Bvg)M z!F2-?)Vqkcu(LzNZZ}6rHl(K)%hB>pdjRQSE^VR^_b{$YO z+H_uB1)y;eTHbuQ3Y$_VcO4CVaGY~YlzVf4*xTCgXvTwso0ITBwGE-etoAWc(?o0w zMu90QQ&Sf)x%TV=Ak3lonTAoAuu;#GVi~d60Ck5J<_b3Qm~g}!%#={ym#DW2$R_ye zS3lP4ogL8T(jE_YtrcLE@U;E9ZDkHKzkt2;Jham~ zP%?2i4W(el;mUuCD3G@=+e5DoJk);*&aXbXk(X<#uHtYq;7PRed+Cp%5@Yvq_ zI~zM*1RyRT2JF=SAM)O{J#A!J8~*&PUjZ@~TMBJ#oP%dX+AM67I5WmD#))SJ6Gw&y zLxhAS;lqI5-~L`zeNan)lbJPZ@3o&NA7atxLv?p`^<7nD-S}&J2$zy*>ylaNmi;G; zjro_e#*jeYh_!{M9`og4K)t0Sx zwOZ=5)-!W-tzij}yi6kIQUGYQC^fcLFKySd`*_?lnRa-K4Bg#c>Q*i~7Q{S52IDMy z!$$|o;ylCCuxCnqxhfYPs1ODSSW5K+wFhM@x2c3;>f{1%HtP)&MPlNX%F;(BQ?J-fK+VPRf1WVgz;&nyP9OqYdv)o68q_!Gi4HURHxe)o# z;{yffycd#EmrFCt>Twoum!#H!xOT1%UQMl5u@oC)!R}$D_Ud?|90(tXQfDK!E5=K& zY~Knmx{D{PI({-GoC@Yij6){&e$He0wK@|y^n0<`L>6hLwG;J9l*{<|cN$SI#sXhb zm=$w+O^LPE@i3|4x3%K8X`0gIb7HD8@mv(+s55IZ@T+LInHPO=+%uDLq8kP*V5-N&Uby)lFj>}w##J2+elLg>vi+KZX zcN0GFf9~=tNL`om`P6UX@tpjsF;Ke+UTFBlrP3oKdb`g-eb5nxT!wVL*yG(V37*kpyj>Nik-ng z-A48ORqQ5QS41kg$f-!_pXmD8e2yjQ@VZl!lRA}okGaB&Ix*7+drebY(N0x+zMpUm zcjI2o)YV(m^A6m#>#i$12%=h19cO7p0k^)owrKMc2n-d>piE|2_WEGr@hg}TEoSzL zyKyh(o7*BUZ;Ns<-Hp}fX(ZQGYS~eZ#Tl!%KCNuI$js{jiRdU5oC<@>YHBf~P*Xc8i@vLD;eQIl1P-9h1%MMaXjq7UCh>g*c=$Iw3|{K zQ0utIy&_<==z`i#j`>in8}IZs<5QjBL@}v%f;Cj7M$A{5F&dGH!vVwq!P;8H^(t7$ z`D99zXX;{IIWqFw#VGkAOUoD6mdn1`e+CuE+gDo8jFZcn7KJg+D&v+BG(SMe&SCK_ zPF(=Nb5uo8syOl$Q>l-GJZIK3l1L~ENeGgaxzm`WL?TmJziBsy$)?S}WByms+s(L_ z-n{W9gC_|B!oIuLU~FimYk?KVy1&qL{y@|t-{(`LhPVB1ku%>$0KDM3+@QZKtJ|w4 zLJJe!=Q#WlOm{Pu>t_N%l&%g%x7Y~}AOE>m)8qo2DYc|@E-b_-qHG_9!Is49uSi^K z8T`~52@~eb%CrRAsXEOoV`9$#<$_VH$kY|ePG6cB(YtM-NSY7dVjU_A&Rsxdi6K3W z5K9&4O%kFPP3$6X(3%X`!%V3N3Cu$7z>2&6e!`qCJoXsbW&%VKbB(uIPT4{)dKJsY zpJx^W-(vuIxd-n=b`U4BClFOVpKCXk7T5VxA^uTYsw)R%MdXk4=A^g+O#%-co?9d| zT`MNe=3)}a+q329+Y|B31@EX!co!7=99uD&4}BGX2r7U0*I!0fpmCK)4TVq7XRX$)Gn_y%aKj&ZIk|;^4}p#ejOLSZ;%T zI@~9R$ph}SeB}O+4MU0V*KBCwKVg=AI2-JzADg5{=?&r&Mi*EjI0_=-QA`O^SQ+E; z>-s0|BbjJTTn=aZmVi4wCN(WJW2Qq{slp-nRYp&qWGsUEPYi6O4hzL=)z z|CQ5}vV(!m`wyKj{ie@(UBm`#nzwS>3B!xqz@Lw9>2#kPFRIfdfoMSc?}2`{6 zAzx=}AYa#6$X9(M8E>**ViugUj7 zzK-|l-2fBS6zExn|?3Y>+CtO*O`3SVu23!f?pfKUJ$to_L2pNrXFnsdvzpy zh6Nn#b#|;u(7|5U8o=w5_I;xb!h`lb`jcR~EQDCpLzX9ykSqJt)V*8iCUT2%ZUS|^Qb@nZ=*U?(A z*O8>wqi4Zh*MAP|)%?E(>~%Em)t=UZz0Nj+y&nD|*z4wR0ehWo0DB#!08Q6_3ea?~ z0h*5ftza))`}vg>|EIxTd=C*|ug%d+U3HG}AKCsvICa~A!ZvuNYH-No9tp|Ix(_pXm z{|MOYIt%tXSp|D>wj{v~xEk2Y{eKSZKd&F zdyT#a?DYx2UUd!Zb(4U-J|*Cz<~PA!*9q9`=5GRfJv;~Yy7`M>ucichHNOw+m0cJQ zI@s&s+hDJgXTe?%I@l{c-v3=-uhE|bd)5CQuvh&*4(wHzV6TV&POz6^Bu^5s*QW&R z_31gVSC%%fHL%yU0DE1hz+Rmduvh2*DzMktzXj}d_OFAz&i-9suYb8<6f6Flz+TON zBiQTszYFYj{r?Ww>-v8a*sJ;XfV~?3cCc6TyTD$}4A`srEwIE8?X>g?0{J~_gd`FH#|vRF*LNFHeoh&DGMdZq!qW#t@#y<)a2r^F8_Q|2`Ee-mu>dQ%Ujs zyi6w)a-q&#t4cNtceO$m^w!CM-ULYO9l!dRaEs%&dNBG6h&Yl)%5>(4lu7RHd~1Re z7fXt}^ zq`?440rJ9F7L}+QD@CrUr#J|buUt0gj@v@zHbn6GX zw@&UhU_)w5ov|DF_Jn!iQ}oXf`~(Yv#=%d_3v(CFlj{d>cbo&v95R->jiQ;ovojh; zxAPlFx^wGK*-gM;pmuHoXMXFT|q(7V;hWmq!=XqE1Ox75VNaAYk^ z&C)D7rA>^V256p6ILyxaLgnHcn{i9~Kmrk>A^E1~>5qk;aup4B_;0yQZQ={?fTgU* z2Ac7DNR_z2^o0L);uD9=NxvFCjKTI{UtJS9ALlZ!MEiudw)_=*wy3-P)LQhNkmYu5 z|1<3$I5iKwSswWVR*y`lRI-Y1UcY|%+TN{Vx{6F^Zb)doefxt%a_^PBOP(um?}uIM;NS;)H%{zuPHDd+KZ`F(e_OKFQ_?@!<$?#{g6XzW^{M}& zQhBpmDVe@#;ft5AUhe(4`)2?3Uh&o5kFS1wv%mM_YvNP??c2)hrGId6P}wJ#@#TKy zwU+VKo5~L__bNZ8@`azNPeK~LeOr0C3?cK6@bks%(#s#-yxRM5ukzy?J$>GE4rt%1 zT0Y${hM%9CiOpay3uG!XrHi)um4@3PlCPJK(U zw162oEqLFe1`OUcLTt zU(54y@6DT6uU@~b?5)f5hQOcQl`+$uGpA2Q7dIH{aK}!}#-k`o6B5zCP~L^L=u$PxY-C*0%J84#hq0o60MfY3z!BMoCV- zsd?b2ys<3t)V=*f$~BAGbfVjG;CKUnn#oa;g&H#N9;xhFF=_478%7TI>BT;|tZR)V z`qNQspG&zSVzL@58QX?!7*^?U9|Za_8LU(D$bHzSM&Cj6qXzl9PmRit<=t|{Anko> zymP{cGB-ao8Fx;NGPBip`Daj4S;n!`jc|IUHV1T!sk>`iexKBVZ z7cno|b_N45G7QpPZtApbkkfr?xZ|50d?PDQLW7*`)2?wd_I3<{F-DIx_vs=6^U_A- z%nTBG&P;sv{d*QTBPMXsH!e128lE$>2M;r3MMM?vsQ9z1gW8{7C?*)8vt!2-S;%($6FdX0mi+;`sd#GZqBx0gkXgv9gWGQJ_ejfw)?&}9FHK?YcU)$flg_LZM-V) zy@U!Ppz_a^H=@ix_UQr_nen;uL%FhF-pwtSxdXblTYilR?x510oeia404I!7I}UR* zCy2(5>ptbCj^~WnAb0bWTbHq%YYfIwn43CJxf_;)%>%A*5%bChc^G>G{~R zgk#@Jnm?Q0xa08FHlkq8jF^1dr^fqnbTYrmeen8agZu{V^`83MPB1d;uCcw%9?{%8 ztVn0J4a+>d^`mg-#S5bslRxTo0RwG+*jKfE-4~R+EkX=?Ar^*h>=^`iN!xgB#ALw2 zelSKgN4vdGrq0;2PxlEDxP7)ycx-4l_esEJemIW&;K`osldtUQ!4C$ZeX>vBcmZ4e zANR?C&6qb}UVqF&`#t==Kh}dT_X+bxV~>UQANxcOYOB++@Ae75KG~o4ah8AE=l9mT zI{kTHb4vs#r#Pm%k%EBjfeRCP(7j52ys~=YS@ZXO`bWK+8JJ#K3=H{opWfBUr#hUD zKY=rc>L5{a`$cQOFZ*eCPCrT8?kwf^Px5M?>ii`Atq!Uzf#!6!TrU5*Zw9)9K+B4g zT4mFY#{vDVuKAOyCnKPWkH{Cf_uUs3zwgIiEYt+(ICPNJ>0(W4MgZWQfzySDz?s1# zjuPa1;ItkXmPCk~XrWjcZ;Z@}x*@e#zNU8ZIvCbsod{npQ7&-aH}*ghfg7ZNMgZ?p+6(;p(q9~M)UF% z&YuyBtuNJidFG(ICpAd@68(Z1AR_)^kvWPCSCY^JOa*aq%5=Lgq1*#__-F0FpRphs z3wNx9?+N1h#pvK!)euh?-h9f?UjXb-5>065cM0E&=OS)5CZY?noc^E5JNAaY%s5YF zTuH1IREY@-0?)U>yJ;dboR={)CLo${t%dka-T3*EV5T z1@x2ybGZt>#9g6P2OEe1XZ9p4&!e$h7HhEFpNEk@HLMs%kTua7^RUthGY z&st~jX%ML`Ay0mm8;k=6B|YUH#?fug^Sx~q1ORfqv_|9C4;}F5a^VE1Fyg|S=~Iq9 zj>9NiN&VsMQ~lH+#s`nP1q*|I&JXZ0IGgs}hj*>(_J?*OX(DX$IVT4(IbiOxSwx?p zdmFlz>I?7Q#;$c*M?a&hnsn_U+@K<4v{rKb);#@i_A}Jq`FMEz;kgB zDLe!MG$WkfBuxUd7|b1JOa6HF^I7ZiEZZ0_H}}4J{)5N)J~-i0IU653d?3Ij5BknX zbyty88Pen2d^o$K#J7;}1zQ?0ToZNG8jU8L&??P1JHR*e>+R z8XB}6J!zWG?71GtsmAW7!q(G;?dpld6x+8dkk6hye>?RD_EVDpRlOU#%sy@s7vBHvU+ZN0bubR> zpXvlwHheak_zCoD|8Yz(-CaloN!#@qnS1#4OP$QYYgB08H)Hyqk;Pyf;9(v)WE|FS zLf@T7%)UD&!1vjojtO#~_6^j4P#ZgNNXTaPLz6`QnKNbK%;~cM(6jy9aSROZ0%-r^ z`RRp^q1VlrK1QV8q>BLy*pLOx>*EoR&w(wk0b3LN=6=>}e)tf)_`_x_-;KZ#Rv0*mRJ_gTkgnz-aC>$R3CO-1Tng?S{r{sjH31xV^fxmt*9*w=)b<>P2JBl+MI8q$| zIb^o~$~Z#V3HtD4XN;Yfdx^~id>_1Y0uO!cenjt>(JjlZmL#muz_#~tZ~JB(p;QTW zCfL*h*7v7VxL^(7M3K4L0qc)}X%wJf?p@m>6+4VYTi(f$gj+|*g{2*Q zu+k>K&^H&7A2r|%bi4r`g3^Ea4Z{HpLFzz&owAwY{*3<25RB!AF~_TU<#Bi{H+Iu1 z7V}{`M5{Y&CUt-5Z$A4Aizr^h0H716FVD1T)QpAy^lYOxw`u-to3i!Gc71=Frp?%j z*NszWhWFnMQ}d;-9ky6+FF?>-c<=l~#$DR)`kpa7h zKV#2_7kVuee-7AvQiP(y#0gkg+YM_WJ-7?akSuOsS3qx$mL1ZAU+bB`Y6bxbss>#& zWC5$V;0gR(W9L@pCgua};C|w47-uoWPR#cl z2Hso$4#Ey3VF%wJY&jyKFb|RY72R;cNLZ)@6qyfAOZqoG^nI5(p61n5tVSh``t=wI z4gTtm`q(`So}}Afuj_+GYPd2x1>!u(u8!*n+ahio^sWR zao#5~+SW-g(-JL8=`&zcOfR)4??U_6x+T0=`q`}t{0|FaSFq%!Jf(=YE|WIrnOwc_tC8rIQ@tP$LtA~$TMw%=dsfO_=QvJ#w!&Q)s! z=cfrSh%=&r=Xy4S<2utqt=>hdS;tMGalD3ct+kqF1b!?TXX~b}32)yJuu?&*nRzxo zY@#V4Udx9jcY<@q|3HJm7eb6SJ6e@ ze64C;luG@?E6SGowVB>l)fuB+?Q{27L)n4b8VSl%?(0{T&|XhmVaF7tb*jdBJ$;=v zH_YBoNK|5D?ntDc>}aNxaOJ?|85=MIRglHOQ5w?SYWP+Hu&QCHlx({o&nFK31mo;m zWDZ|&PLhS<#QJe^s{l|}U{(3=^QPG+JS5JxOl?yOt`EX<{BbNc^E+_fyj+@+LtqF` zCV9yM8NtTmpNrF$%Opq&in0Z3tijzevQcKjq9Ag*GkflqlHnM< z=Q)P(+Z@AqXqt);nPsiv%tIKYq&e=hmjtMkUkjjh-^;Kxn>ak#>1?J~tlIyWMe3YK z!S|?1lXm38&q)&m93PwX=Q?@cq~j*JY|=@S{L!TE^>-lSfL7j=_shHB@U=;~5gkrD z+N0G+*lzfvR@cyY1cMm*e#@WlBNo612QC<^yP!b~#X1WIywY5vwUL3AG%7C$%RFT^XWqLxboREv zF)}&bPkpyn&FE2zMxhW0WJ}<{K8Vl}wQ2m*u&j*Urb^EmaM)XxCDaR8gY&!m9a98q z%JRJR;|UF9>y@mo!KHISLkZ=^@aZ}cjEgtXMS0-_d-rny9rpD;!Vss{)6YTK>4W$P znEd>%m=OKm3!7R#x#q+%@jE&_0s7kmWht=fX7NjCK3XpGY`ul#FB~QF1;s62V8vfUmis2In}C0dWQ5oOqUAG(STW>9{3$I0OIRgBxsln|B`$K&K3dqb5_{>n$QDU z@ENirq758a&9+q|q>P%60G20KV(b-*=4!)2Y#1jSS-zht-$B3t>=c>Kf)v~It!g+i z=b+6nr{jj{5N~3_%bM6-`V=u#22N8yDFBCd+<-X+R>QJLKYPfTeQHdUGMU_6k`lN? z-JBDL^er$%mb7k~-5r$xJGR6I?o6oP5nJYbLO(T0e?p@PaVK;K*; zJ)aObKQGvHc03O3{zMA=JAHq~2DUpPp!13E1Gf{2lN?Uy#RFM5GXTb5++ZS}431`4 z0tU`hY4&IW2ZYnXmvUa*biy}CWn24cg0{8CW7tfNC-i+p8WVb1C-)QDamdXC@GTFM zwICQo1d)Ym>-8k2vk@r_>FerYR6g^gcmCYtc5}b&#~ye(;mQ6?xTDW8FAAP=LqEVf zT5br%fk>OL>5NZ$*ttXyWR~_tzp^J9#4)s}?@wn=Kce+X_FbVqk!&*>n3t#Dk%Pof zq`2tG1r-NJ9v?uIL+T3#c)L;^9?;$D_V!SgFrt2U*n>4cDl5dG&}S_c^IjPrXPt;O zprh7Jz?`pDGaJ5KW&+F+nnc(I!ge(Mp5-#{a5FJ8Q170Rk!8WfN@>K6q`YWOzx?A% z$-xCXROjM|{?uummAT%=c=%)v<+1gpusC)h`~N8{M)8-};`pI2|M+5&zBnlorejDO z6LUP47$e|1;I%Yi88Y(5qZy8 zsUJDF=D>=FV-LWDBFKdx*`IO%@;Fdxlhf9KTj`L1A;I_%BE^}QI{ zIbbe}7(JRK65XST;?%IPd>||byqml3lPpOP0vb^WlE*#Sp=9NFo0rUCRCa}?I_-Co z7W6v`AeUowG(zAasx3TC%tc}rW9@54#(Z~XLs<+MssPxY6d7#Dhqf>{Z)qB34(#7P z`Z&64+%+9Q_~V0tkTMK4SvL;?01%&hU+rZMl^Yr3f((`0k;Lc$-+MzfWr2)49Kho&>3l1WNHx@_@*zK8|8RoOez`RP$|WSU}lFUg8=a1(I{EnxSr*O zF*(}5@FjL%SUgN%16KI~AwrKFZG&>sW9N3Xf9+NKC*ONwaZYF4+8Hxv8 zjGx&Pb+xU>?P!g=g+0y)Z=Jo@Zw!?M`6hboCYg@ib(t!LHf^`*_u=8B+z*%&G0UIx-98Dh zB(?@mT~>xa{7`0Plr*bjn3d(H_6~kN)P`a-!Ii^h1e_FCo^6-QR1lz?5~h%Q7u;8z<$@<`E5(QbTAi8IqB;045vv z@-NWZ!s0fz3yT5JM*M{=@IGV@2u>yJoj&B$Cr)fs;w zL%pmKi>S7q9bSYY8#tCAO}gQ*C!~r1ubUkd}W7o!0A5rwNUPE=18-t<1sdnwsz{F zZh=QwVy}fO{K)~1Ix~u2_ljEtJYUU>|5kQOEvSM^PjWbwp&mv#PJz;$Y_R2Lw&#i#~vPI6MaQR?Z%7@OM%*LC#2!b*!E>pIH7=b~A z-9rp3gu!VnM=$F8!)$y+{?rfPMgJfdm|@dXQc7vuid#Omn9uxBEP zg3WrX8gQyqog@q*l!rh_D9{30ejZ@OG5*qAF-a;;k09^j4D(5A%h=Vpdm8BWPTz&S zln>1`H70i+8CO+_xDX-`2J=94aS+tj*_5(ZEfhmPcuV!c<`aYMBkPRs#n>YaJdm%X zEXBT?gChXKgiVZ;6Ti30g1zoQzy{M=rtr`p%XmvsFzczkfwxFAZUsi8rJY=BAuNqB zZeBz)%4_3q`GX6GF^JoTvNsO9K4DcYA0G)jWwI>n)%e{(tQy=lchh#R09z?VFzcL< z0!~BlWvxl#y0xSL0G4G3pBl9l4`#{01Co@7N@H9&ma$xSse(5z z!hxSQ$&2fdcvQEIRok`2!b;i#iz^_z%8(ux+ErCWKu#b(;DY;@o7Ks?x<&fEtt`99YCMI32v8a@SyOHb|S{qE!X zu@7hTl;1=-8P{ID{K4j7Jk5otpUiL_Odrh1)C)bICdo-Gn310i3Fw9`DUhKA{VUu-YX434 zTt4l)8}{D~C%ha7HL2e`@8y7rVSzCOlYS+zK@T#mEX>?g$&3g7NXXk_*?)m^cu=M& zAGr;1VLC4XRIIvbNtS^$M*rX?egJ}AVyg2SoBFNXIfb{Q#7N*eZ!?MasdcWHWTR5f zI;6xk<2B5*62D=l1^aWpBKU-aO2x1w1C;bkOaTN4_b1wRu#1J6Spw?L`{<}CziY+f zdm-<$8`vMY3W*T(sxd=MPK=)$ImTr5N>2+HUav&u^l6<*W?yf0vfIg-^%`Pu;cGLC zBCYU?0X>>0ZYh%8GUFEqdh?jFJ)g7>`O2g%S;DDGp8%`bDwZ1fWWM~FDez3`Bj{5g zjv%p*Urh8_`NmZI$8eJ3NIXn9msy%~QHILbZ8#RN9*nN#V^;Px`QRsR#+x4eHf&Zx zuo`eI&Ue>YO>2I*WCI#y66a^6b3aKv%^ z$t#}&TgRU)2c2pv0IH*{2?SnZ^*q_ElL^6ehXS&f8#@4_`7oiMJTe^tYUV5AWkwxZ zcdDO4Wdo)yb4)w*&8tL$kbPQe>+iQ*%7;P&FQx%mgk00Ij}HMp0aHqj(u2f@75SIxEEOe((PH`~pNIHD#m_Fg5#K99`)Uo87jC?+p zFQQ|oEI~-7529-BQ;xgx*2>RQI+v~;I6*fjB8sR8fnsd|7PMy=)obrm?}^{knco3 zJ;-dfKD@NY+JFF>ke+IYh0{q!1w*veqX%Or0UzT1N-yZv36HqVB6_y+5_~408bCf$ zt0rHmu0Uc_BzJauc$8SD5yVZfDQ@+!S(y`n#}S zC9r`26*Nj-V${+~e9j7Nvt{4Lu!!eWVC7tTkT+f3v@ur0_r$q^&WP=7>Ogg z51Jw>)oRh&i&HZLF4ADlht2@aS>}-Pa_kX|)-V7-0?vzx$jIlWJfb)Uo@qmpyO~G1 zkssx__MsO>%o%*q)jCB%Gy=7J7&i*KNCHTCSU>K3_`P9}AZg@eb0a^<9Wr;%g7I+N z7Z7Y`?6QG?ZboR_3XEE5tq|3HIJ;=PKmTy?n?Ztd7>uz|XxE6503VPW9hxlvHP6R6 zMw@jF(i-5Km`Cdl3F%=(e0uK^hc;GWae$s7$J$<;Z1*=rzUSTZjsH5P$D~eue3!(0 zNFViL@gCg@3OY$(b@ej7)~fe*B0}*ysYoVE-f*!x84N!p0{D<7dbQp_YefD~H#?99 z-XcOs6!1|L;Fwc#sMVP2x9Dx6u?G0H*&I53NIsHdP~n1}d`v(Ehe;@)E>6gqjp~*7pLk(X>e1(vd9Md z2JqN}^yy;|MbgLQ##$W1h9U>lC>C$bX@-<6f@u;Pq z%u8&<$onRNG6glR(B1K%k<1^Qn%ujfk^g|K{6u4?-~)= z@?_uQgHg*aw0ph_ShlvLw1$8IMmo&y%OhSqxzV&E?pIHv8$NRP-&&k0j*#>E=9JuP z1PvP@NzT&y%mH(sJz(zF9x(jUG0OI4mg>#=#x!^n8kxe98JcNE-Lxx-qy302^Hf++ zJDJea#Mt{&+7LQ9UTYZLX@HNdk0@90P~MQJ+PvqgHVM;n$&od%N*OH~@%3t=0hSj<1pG${ciKHL`t0`UZG zLTU);IVcY~l7giQ**%(Tut1@tJMU?C2TfzabIMAK%Ir(M4z^A|v%FAYa5VQVp{EIXszq0-q;wyZ}QzNl?+ZNQ|Z>OR=n=^Wgu)ehs6jWP42-7 zb8I?`o{ZJY)>-Sgeo;3_#^h3<@V-{OuFJ@p0W3Yx;mcb8H9jiPS(-pf{E{ck6lWC( z&I%lLf!Lm*qX_+a{5x(x`bG`C|9P_3{>HD9^=2#XJKSG+qE4X{+j$O8uEeY*tQ$~; zVI$m)!Q& zNNBoFgL~;*%}KO*6#V#r9t_YA-gCJ))G(6)t<8aUa%d$Uft5r_+tFOUqus`_v>+hm z%8#?>1hQI7h<-REw$g=So5wHFiM$xM9*tx{Pz6?@!A>$naoU7X2Sdf&$_~v_UDEH5 zKCjSOncw?PT7GgjG0$fi^+>i+J<@Z8()#&K>C6S2l_UdANix%pn-#k*A1C~MI;2|R ziQn!9y6ZxlDh(fD;vP}WSuqQjxx)!noc9wmk}JX^x~hLAw$Mlr*Nc>&mL^rk&Cr*& zY*T3dXO#X8$pNG#h`tg9a$CwV2(ZOU=38&W7rJ$(hg#qb&Rgq7?tb~-Ag^O5-yyF< zu1N^$?0@8PmyQ>)0ove>|B=h(kZV`=6P}gZt#2Vp@1hfWoOLDe&;hyJbq54<^$v|r zNN@ri>q5I)+2fNKGssOJ5=g<$2~DPl6rA+k z?WhCBQCy%ZGC!g3w8y@8YPU`KM?Pnx#^VeC9<(oP#}KwQ0fPh_eu-wdX+f5#9AgxV z3xQzk?)C`t1~w@8foWws0ji=}Ai41ePbv_CN|C}ff>IzlbOA0GvjW_ z0?Nwx5~Eo-3u-}kx5uA--*v#x*oe`BHY}PQ1_QlbUwXvEciGSV+qK#R5y zgxo)%yTzglJ`LfX35wNH`K(V|ZkoXZJc_Rnp4D5-lb=_uTT1&i`8s@SrK=^krHWIi$!Q~t#7+kPrnueQa;;C#?`JtE-=2;Zk(-+Zj2!M=(N>o ztc`DB_M=v_SzjA}i1F>l`G?l=x&+CDwsiGDe0coe!HTQwT9ih$bch}4t`2v#lhr-Z z4QoqX*Q=|mD?^x@czV;IV^RZK!vil= z;6jB#wLC0VVJ(%8sIWs7R@N0pKXioOKV`A5YZ$#McL4}4z3F)esv|~h5N=IK=?N!e zMhv-OppkK~kz0n$H41KP%kj59UnH8U!Q^P4aV&_xr7#TT)G8z#&t)+V0h0rwsn>Ox+0N=p>~M9KEGl z7p9rW13mJCukV@N9`V-9qbFxP;uDTLO~Bx_9w$v=4?p-O4?xf)(#Lpc?pzfLqRp6% z8!W@~i|$sPw{qI5;Q4gH=QS+BSa?VqFd!wr>-4Gt-x}xQu^*E5DU8b1pmbHfD&vP) zv(>j%OKOIqU@zSH+;z79sBiz?gJkmi`CPZOzg)UxKzV@LO@Tzgunr8;ZLyeFCSJt2 z@DpfV2_j9>1X55I8;I z?YO4Ha_CQ)IXkhcBWd{vU4zqE^~S7&;fQ*P9`mLiLbF}#ka8I+3UQCbes9(-atN#W z>Izo#AvKI-h-D5)bj{29NE_RSl5EFA^CO)2Xbut?*m@8+Pv*x`Med}#hE=V<-K`x` zW7{ZAN{51#saWpnPT0$)jp6?@m;V{W2FdUC2&S9eI&0D;3Bvw(EE7EL?rvXQJ?;+;lCNO& zjq7+{ll;Gj@zqtNGWWeGaFf`hep2pWJQ_zyl-{OM;LL8dGOsZ)!AohpfGCo$+8CS7B6broD)c~@8WS64%oa6X%*>YXQLfG5b!1CAAH-G9{j zU*CS`Y!7$0f5cIZ$-R$W0LuZJxlW&%JKgS8@9Jv1w=*Kf)m5QrSmgdBn`XOv)my}- z^`cZOldG%Gh2_>&sAq8-JPY4l42#^HAi^|(Cj^v(!;z(}gCHG{dtGD2M5O^Xbm8qO zcM~{$kW+#6ShS;;m|^Pn(7q}#8vvfdwwpJWOZ@n3Sj8gvwg^fUYvF?I%`cUp9nsjEGqAdf>3T-7=oE|j zzO}feeyPHB2_f2nJX5uoLyG>!V!9%QdWY*aNcWiBko#WGA4(w|<7Riv#t9l=jdWpO z1MsA?md@P z<{Z#zCT+SIeyIexZ{PaKwE>0)6l;3INsMIM^Pyx9o*zp@Y$y68&~u&f+4+2RRVynV z4!DzIG4Ccc=p{@ErF5@`zlBHgiiH9Tai{3spO{lnC?Z&QMLJ&{0_~Jcw`P3ip2XdT z=~*SCXONMK0yn@sD#R#RX3}BVi<)5?rD4ff8l_Rmuncw0uMO=HnG@0RICEOu@B`>P z+}ZCY1d zv-|mK=jy6wUEx+$s2FgiRtZeAmcP2P%FEBznzV&I!_o@7!UR1_7L;o1!^wuuhR){B zhEBQOxk&f(`h-5Xq%f!FA*r7r>(*hB*){S8F=_@eN(M1rfD>n7nJa3@#+W zhG~^wEaFnF+xuL&y2=|CIsfy=#XZpE=bfHGh7Ec@Dj6?o1{v_rQq2&L@=<_Z_NrT~ zx)oKoK#!%mHHK|}%dc*Q)h)NW)yKejb<3-6&EPxpb}wYu_?!xbfzrBA7%Oi@U>KCY zLT~xuL@07Rvn)qAZVyF%!;^$PRqxe3h|z;8WC&FuCD?~V`|w7{=z%B~?DKPW zV`*5pN2PKEXNDlZb=`g3=X^F8+|2R8e2;9|-5Bf-66w!S_XRo-;f zc`<`<;#awb9C9L(##}BSt#b8b>~bDS!;+_0mFs~q)WP5Kd3LYLSp@D{A}DW#rAjsL zVg6zf98D+!L0=Qeg!|}I8fII&yZCh7-9-)oMxgU{V9VHJG;;l$T)lHqs~J|+N3hoi z&xAU`DRmrBPc&BD%uxFl$+}!FGmX9B2IZN>T@=DCS8G13oQbAAX}IWTrIGJvMdjT zCbA@aa?>ahR4$W*z0|rHhaF&P&|97=0I*;ZG}`S+)J)g_SM@m^o?o^4HmW)XkNxYn%cdNl$k);|W-cURW zwAl?fBW8NpDfjfAsQP1BVJwDawJ&m9(qplygIGwad&zX9x4tBKwzKQ@>hvZFgT=Nh zu~)yZrirH?q9RQ|}dveY5}$nCDEXQmQ8IU)BOI zY;!u|7s-_-Pv@qs$bzda7|`S53i;xFobvbzobTD@BrldJSDS_UY`FQd1woPXmvvaq6OI24&z|cP)~sN6dG* zTpo~Hi?~%7g@hPz!SRZL{}NVGpm^G4;=u&^%w{guU^k$y%6<*j>jBzRpg5pR#m zD?P~E5U85RhR7I*pkTQ?CbKwU*KsSmg#|79s<%F|#sI9tZjYw8M%f!(9`jCInN=1` zS@@pOqZ2ZvL9sZ)G_KrZb}ehUbWLBb~c0=CT@ zN~w=$9*D#pgur}(30U~Cg@2vcdQtnr34E>-xIS&X$eAVgB>k2UaLTYsgNAN(roTG& zmSvRIu&T85hBDiMQU#vnRh>5>c;cd?^^~Isq zov#eN|12SH4K&AGnH!dVi~NquEhN=1w@w)0<(6-K0(S*g$-c3vX*-xhyjZyv4nS}I zIl8u%%X~i21R9b7zZXI*(ADF&{d|S^z#sr4r7l!+x?7zm9Ns*+QS}ohZ!Z16^Fm70 zEbxu6Z`FX~LmKF(*qFXz2Ip{DQ4p4zU!ojuWQW3bXF5;U(7^RzvARdiU2=V*NsBF$ z5#FSMweU+6G|!#o@|Mg?bg*3^XjyNV6s^jBVOR^VSQO+1O&K(7zeI-(60B`Z&AR$Cwu-YvYduj2ujJ~?kB(GZik0)sZ0US6rXT{>2m9MYIQvhxrb5oYfrAm3GmfTsf zcvepOSv#|p^O3W7)7V5ChlWInKs_p}4;=F{DDNS5g$fJ{{{70%Aql<{HXdh=H$b2B z*oKfsV)fCe{Qk6c*tw{m)2JM>8RS*vrKTaE%e`6Js z@2f&9Pb&cmxcI(d*jzx9Vp5g2K;@(;V+!=3o!kWq#bUmloz0m1@#l+a%js^5>5hrV zRA?^@^0 z>+9t*YbD7xW_~z+G>FUS0?j4a3-Fr@XM0RAP6X^2aa`rwq=zRgH?CB3SsA||#&%g5 z^S}?HZCYY?<)LcRl@{@VwSuwEy?9|*schQ*EI-BW_f9+%@+ZPanp`2Su6CB6E&M1i zDr7IVOn$Mk$`+*kc_l5peDb)jxR>*tyiE_xUA;Ay*(PVC0K2g`K)$9Lt45w*Sj1Qy`2{u!1T3E|mtx+}@@lr1%e=_MS-P@u_7cMaMCz zcP>_{u6Hi7m6FR;wJogpGO1XJv}6NS?e)$@S&Nkoj)~+mY#0$SIEwi&?}%tES(9od z4JU$B8o~4@Y2+~2cMzQ#lsn5JoRLWt!Gp9SG#)3FPz%>m^Lb58zTh%+5`dLlz{M&T zot><|*n(|9F;l;!mVm}%$xo>Z#?nMpdcNTKZc*!tx%zjL+VYxQaYEA%QBP?L@~XE(o*3s^z8|+Ca2eNZA=+9ESyUAe)#|#-OV#r| zHgY0X6@LU*Tt&6}s`LxroV(F@=CY3KgtsiL!kg{bajS4*bXbIc+i*x%xiGdm4lj9a zRe8e6>d$X=xZ@te zJ|-&+nL>_Nl2`2Hfw!O@ctaLx@Q*fx7c`_#TZ65ua@4!}fZfbT@CH?|7BO$BVu^Jk zB5kTS8J}vNzXJ4N_O^0I5W{X@BhY2ewYpHgXz~$^-FxtYG6nwO7bu04IY<2f~T%@*K z<_{S^rdy~WQ!Hu$FMAegGojnmvv>bI{fqWylw@ir4CMv+@; zoiKpSjE81hl>x=-87rGJ$|uwornpF_xL{yYMG4{8jQ^>XEa6jml(*fPId?B<<7qj9?3t)9Lubnhhg zxq4bES@64iik*1@j|Zj<0J*R5>Id~r$A6$Ti&J*bT)Ryxae5DuH}wO$2T~1;8;*l8 zYTr5`YX=~c6#XCtIpTfa8?44#cKd)k6}+D_E`Uc4~K9l+i5Kx$R~HgHPsc-nUNv{aEJShY?| zrRr&((z{}@jaqwsxLKTT-zkmic5KCKy0y|KvrXH|J@6elO}O0_z3bj38m@BmPt`QG z#8>Uk#z2(3wTi`-62rBm2n9bNsd*>#$L}l~zJUimgKs8 z_q10%B6r&XOWzDvF^V3uL6Oc;_&tniE9B;ZS0sb0QK7IO76 zAziznZ?vfVWC6{mka+rcA zCc9>_*m~PmP{r}kEO2h1-EH-(OFo30xNeLZ1lo|mBB!MiX_ZP!OZpBrSB;N=q#&!u zbt?-}S@mopCC`@Bu4Tivq-s-DiJIgULvls~KZ2#+ZS}Ix4HnO!s1ElH-n|5Y3?t;YKxqk3gykD&^|V-d1ya8K>@Vii4D50O^Wt z-7Qm{%tRhs8W*W*P*=@Ui$h=V5N^|ao7|O3)w`@Pq#$R^U71kNe z4;c`kswxoL+AU#~4Ncwb4M|~K5=a(cn9nW{L7H|u-jrODyW)W@ zWt-uW?!v~pOD-5qk}Ci{CKE=$Fgq7Trc@^|EO}T+S0zIY(&c6Jcy*uXCHb8?tb|4U4tt2u8`5*Xc#(6W zTy)F8)t+Ht*N|nw9r;q@gT*&8T&J)V!h*W&rNvTZZM#z{C3rl2f`U0lX258n`zbpB zU;d$9H}OpWLJH&)X_Mc{K*|M^?L<)6C%D4!&)?xzlY&i)9;#Bg(-tfvZ;(&qf?>&A zcV(d1t^r%==YXyBGBpJQKgtbQgjNajhHSU+h8wSKO!$B@XmBLXMj*f=Ima91B*zMl z+)E{?m6JE`QUW*g%Rded4sxFGOha-)lv`6{W|2>nQWVC(4*#pRQA~8*81q;9L@AxH z)G85}J}lfonj*himTmq@Cqg{+lwMZS#zrOw$^V*Y7y-M8Z4!*Hl0ESW;Bj&S@2c_y zRmpNZ#dd|$9qzEz6*%HUGz2kBN^B`E;rK?aMObSjG-4;R_!$dK<1^13RSB=io@E8W z`y>y~>P=q8$~c1~c;P(~r2YIJ$=|eYEUR`(+dzV~Yx~~TqWTzRwk5KjCmPxVn}6Nf zu7x)MOV3qbo9m-}C~{5g3(LGqWf^I0nA$TvssT)oc}kDvIJ9V(elRYi$26nFAmtm$ zVWs0X&e_mi1Ac1}1L#k|-cDnSv|{<{sCXg%ej)(X;xR>c6{3Zw`6PIaL-~0YI-nIK z-%?Wf(?mc()Rc(wYVX7dKiWUTL<<4CnuxA3#yb$d8n|~k zYOfSeIb7-7y{|OBW?0r~*%=JXrv!AnW>0uRIj@~LbZW_`Jz-_g#YhZmwpn@T2Mwox zYnphBsg{4DZPtA%ReI!i{DO<)1TOVSam(fJH2oV-EAfi-Nj)ePi=R^YYNBLBsQ^C3 zDBYC|z^~Md(kB~Yd-Tr!l$0eJKz}c#vH-F=T{Q4ID`~H;c2o^U=@XIFYX>uUx`DCa z_c@rWOkX8FdB?C~4XZ3*((v5SQ!K|prPf7^KQSyI55pi}eys)iVx`MctK_hfQ809t z8=e=a@!729jZ&**Smjc|u$22t^{kla*R%+{9-Q`+qKNcmnI`ynbs5AdnO_P-zTpyF zD@a1zF@z$f_MWl>)+2V%L_|qo7YY`Lvp^D1P^Qv$N;_81PXFfD41Ac@A32llKxfL} zc-HrQEA?!;s*sB>XmT6iTesT8cYwvu>gwL5kArMKIe#pzLK+%D$bIy5jZC$=>0QCV zq6~7H?80gz+nk3*25Ir#=8~RvPc^YK?0%O@8sadjBKNBKOE|m1I-bL}w=Kx(uwGS5 z3^8w8$_5!rWGDC_2C4B1%IGQ7B;Xr|sC;(8(zfo}jCL=3S=O)3fN^UpR`0GnbjLG( z8?NPd?Yy=e;Ia!_9?DT-SFN&zM4j#c>*? z2vBrn5>lc17^Wfwgxwb(sW^pncaqvH)Ae|d95mqONAUl}NrK7m2h54s;n)LjvG8EP z`^$Du{WDx}{x*xjH9qr7SIK=C}bzoTO$>Uuqj#aX1K4Oz${ z-eRT6@XV-Li;Ywyme2|jaYhc*Iqh^w3K>UPY>~s0tY!tM0cmO#+>~upEJoTWEB;Mb zwOz!xvFEv-y}a;)U5s30q5TZm%wkdWWES|54}20R`e?IV25Tm0QM*{YI58Q~9eT7d z0IWl7(r}5M(Qt{%06yyU=Rxp9#V?u)J$8bJv6l{O1D-e?bO|7-bP(*dG9;BFl~$%Y zx*fZNfO+Wp1p>~wXD|LcrTOu-LnmW$(FZ?JA5Q3FM85iT8j-Ij^zg)-MiyzG(62r? zKy$(6Ciu86gFA(LMjq$jacb58uTpKFE?n4^A%XqXFRLHuLU}L_Y+ed{${?9m`-xHR z*!!x-$=^h8h21_O!o@~t^SbTFIj>jM`8ZG22?fb8_*MmqK>Utl(nenbdC;|n%dF4; zem$wReH%>3w|%s3qScRq(&a~pvFaCq>f)H3c0dsV{?)*&7RnE;tE*aJXG~7Bwxq6h zIQ&pE#&y~O6I>8P&Q9n~_v-3ur?*3n1G-DjI_>CiXiq(groEss`q z$;S?@?2%DGE3e6oPyfGBKn{nr@`j8W^yMBo_34{eq!rN0-VfwrPWN8FA#R_(uI!O% zgP!`!R-7uCA=wNymm?$K!wj*zn1;YQF>+#3AtG{^{8JYFf3H zhoNBY)#*npIA-@~UZ&RMB^lMs(s|Y-+g%3GOgNK$370oQ$uk`%?q`n4rh6ASTC29j$q) zj^l6Capr?xla#`}tm8OUhd`KK%oHa4Yo~mA{t;Jf3K=dD;ftyBXz#s#1FnLyEBs<6 z%6s4WW_gTO-k`gAz2FXaeh#hHw7EQ znaCjC8e^pAA{Ay-XX7m&W9Q>RHqMW?LW~=&#)a{gi*do4_PFs@A0uzvuP}(&_n7+vay|b>nN!|8+#OQ-K1;Jrnrf>8r7{rb!$}J zdaQ1}t8P71x18$MadqpWx^<12Ieh_9CNf5O0cD;ruVIyka~DqolN+p<>FIM?dMGH) z7~iAE!lVZ{tq?+IAam(3Z>vH5T5ArLjm~^i%E`AJ8Wfu+DP*tz|Fie5ZEfRP!|?a} zR|L14$8OX#ftG2V5xF>|z#a&rpzZAYp6h;} z4=s)?OH0zy($YF|p%v^5oT?u=_p!&H#yFRt^P=|{&bE-9*{tYJE28WK(nf@H z{-783j2MBeOvDF-;*aNc-WsJ&5s-Ohrk*`72fusnR@;7Vw9VUT@_XO2=eS+?I;|=9 zv0sS%rG%wNFv77YvQG^NNd{)0GD03ou^v1|5IK>Ff!)pF@tn-HEGBwekRk8cXFKIG z68cy?`wYOa$X^tSQYb3oBsgi&VHr`V#6Zgt+MeyR^9C?<#gSwBL0t~fVxGjeQ-~w% zg>JhjdG7DCQwUHgk$AX{|L^VyaWGgWNa&0kyAVTV#Aa8#ZkgrAp6VJAG}I79zzi-{ z)%JxpT|P`#1o|x>whtr+73qOwHl_+nQO7=r7{K=iN3?M>ZW5uNf!?eM)E^j^amiQE+1&KKAC@;ib(}dmhU{mS@8O-MWis+T^ z&GRGZ+%%=&VJHT(8wG?O&bDwtR-UlL_m*}%$+7F3|gTL0^A;{M6D!o zm+e0`+Pf8_z5ABY-f?4BzEm98nt_Wq4~!nh)P(dL;uKJ!^~Nh;9)AnW*v;p+SHg56 zb$X(^>sPOOubwZ`;@TBOX<^JNKbat_RB5~rd$Vb;K|~r-UPv{^rSV0@G#x~rqxdc= zaRbl1^>G4vKBC&P1L`?D!7b8zBaauI%x&=6X|zCS{io%S|oay@q3aaAQv?OLpcGE znirE#fe90)BsM71_LKE>_4`TZl@Ljsxe)9(#*EESzbV7Plc6SR4$NU)l$b6tL&m&uClx+HDxq+~a}tVh zWkNh8rBQZ?hyqEowQo3QERex=oL)=tyex!RWYX^o95Kb9o$y=8vlE-8=M=b56uN-z z&mOQCs?kTyDC>sD;Q2E~`0S(1rVY&5?nsNrYixjSqC!cT!FeGvZv@o{_%lyLPxO|jiL-vguW!%Jz-Sx(U%4|4II!;0$RkB3tm|jTwlEp}$tCX&iRkn?qX6RR62SsrUZ7tfAtLZeet?!JF=da-|WChh?JkNvX` zJW?-o?uttNlM7M2WAja>#%hkVcs8l)qG#MQ%Z-cJaCAma`1k6X1*1%s_##KwY#mB~QVUuc=2cL(%W!T(A$ zI~uXOXz516Sjh-2v^8?90q=m~n%npf81u&-2gfClMut#wZ8&iUWW(QKpLw0Xcb-1dN8j2J%)EQpNJb-bKIa zyRYu@*+7P5T~PU+n;tfkP6O! zS1WlaGlfV>`ALNM?~32toTE+ibTjj?2yaI$Ml#r@3T~6qIqDuDxk6nU;w+LLVP9Pi z9ryQ~a9GiSICGo#>3zd6^V#cDr1AE?MfixhhT^m2=WO1B;wEW zRXP;sshrGqcg6j;t8+d8X$nCOvk6>{;G&p<&KTJ-^0=dV?65D6?3nE}a~QbIqMRzW zAP2U<<6rICIf_K5A0Zh%d;HyAH7!oz6wf|%6~D_^?)rt~-sZ#ZD(+>~>O6AaFj3JI zGq3@CaCDOSflUHCnPBl21t0SVs-gSH2&$dp?FgsLgig&^N@r{}3l1PBHOu74Nlp<_ zlT5hL9RwB?YnSr1D{*s$0FuibBU`$Wt-Ok9I`O~Gx|kEHWm6!!4*!u^jA~~wQnMJX zn8hdFUL__A+Bxf2uc_l?U2=-NSt{#c+~7a>-_xjInQ(sb!p}NaC)@sqkQL1dZ zDWr(wQd}pY=GZB)gwNjNh}nWlaXF%r=txruNg zX!D$8&_>0xY>#T-rRC4h8P}?xc~b5Fa|YoJX)NQe&|u+{!|uZxOJ)hc*>GN1Xets` zUynLRt&=_aiBwW*x=UTDi3}>mb$KYFLR3R5C)&R7c?d@4-a#DNrl>C0{P@r=x;=<<8ExTS>Pkl zTPuSrO$Kpka8I=rf+iu7^Lyh05LcYznG%4`dd)>&W2j81mBT(@A|OU%jGU%{GCk<41?! z(?AAi#}_e-0K$4ZGS5e%QPZwwWnh?zQs&rEGfHh5Rp6jAUIbcBVF^GJInU*l$@s}A z?ZG+ZoE1@?b4~GX^ixv8N0goM4Qul?|FS(?U-x;qlvJ9>)+d#oBUTX64WjDkjwov~v`gZ{ixlM>*PwxV?%PV=mDDH}S8ioQl$W1~XHUmkd z83;?c$E1Qh+yYCxp~CrhV{nUzU%r&FJ}|kAc@nlYZ@KiitpiAvaMHVm%oVR7xy`lf z30`97CXK^9Bev%_4s#wgZn4h@HQM0v>xwQmGVhT3uoK-3sar}e1a=zHZ%dClFDbD4nf7++Nf(uiTDX`b`I#;_Z}+;4g=Uy!yK_ zkBhxEA@rZqB!j-Jh}9OOG)+lSS)9&yFHXy7vAR@^$Urlu232-}ksI~&4P>Z4|9V3y z>1Q7pNJ((+3lag+x8#5<~DKE(TmEgFfCoWy&Ns|o{>Xl8(;&GK zzH0mY_ivi)xM2P!%_5dO7n}oP;5aRwyZ0$RR ziLNj;C44QRp+7MtMUH6^3_27k9ZgvDvzl^O@&|~ zlo6z?l{NJT|42lp`>#=yEk-E~_n`@XA5iR-l~bUJTrP$0k4@upIl=*ml-H6eDeuH$ zUR4+K)wir=r?i$dl^^&fJ+hRmRt0hf*3bF<|HPb^CXuN#uf!z&p+W&%jThg!5z6yb z>YB$n`Quy!MZx8~j9P-lGta&EO3_musD3wg>)r;mORM+CxqCIHVPh^H$W(msK&Ijg zuf@Y1nvqgI9pW%ThdfXR5B7#TXizTU6TUNENIfwZK|GswpT*s0Y4=&!edc$c<=tnI zG(%A;c4qqwME&p(4!&AlCXH~`5FtFnX_{H=02>^Fx$*)Pl+skFNjkTBc9nzTgjX2NX6imEmOte;?DR25r z+W98Z&UY&9d=og$b-&xe$)3~~TvCe#!p~?U`O?jNfl^;}scosDIRT(&LE6)E)FCmJN zy~^@%kD2VUJ#0!vr%3B0am5v{swJ#4??Vq!NVBZPNm*CUDHi{r3GpJ+tP%sI9J(Q9 z{mCYKwy?Yc$T*C0)aeB zZKo(S&SQu1FcBELq0+n=6!NX#2S(=L5MOxcm&3<%bxYi-@?G-A zR?WK|PNV$Z63pkJv_k5N2N5``>+xli5Au!7aXOpDbCGIS@<|JxN7q%HD2ifopSdYg zQJmcvNuWIZ%b_r=6JW@Ml%jW8PzJP8v+_bt3^$TtVBmSY67Yz}3 z>8Zqg%pVX!X|BaiCEoiqx`n9hl8@vl=Wt_dEk9B$pTQnlup+S(*$De3YFBtD_Kyvw zsBLpm94>b<2JN-aq1NF;;7_`o~%YVoN4 z%ko>ND5i8*8_TJS!%&2;!C33nHH~Ye?3$KKt7V}W-loN?YYksK>B=E$gQ>H}gT|-M zaw(o;r!McR`d$o5|31;akDQu(tK#B&^zgtaw;5r(GA8<_TXOZOokajEtpTO(+w%J8 z-zIg#C=|v0Wuht62Q+xFNIem zsEO`*3#491QNl%<#R(}1PIzQPP8DelObS1qtKb}T=U#<2uWGBBjJ0IlSDl_|6>0=% z2y1)P!67kr9%QwT6QZ}I>>xDE=FGHeIdZybkI}eNi>_7^>;)qvm1kznA#ADYL|%n_ z;(kA86DOvsODQP}$SbIzo%A-`wzIUOUdL^NZ+3YG_$5!lLr|<@+^2539y^6_IS}&* zVcxaR-IH{%kG`xk2I3$eE;@ zM((q;*lOiDX1My9d`Y>{9HA3h>3ZlCtWy2juy;d|MS$S#(oE6?k^A^(u^Q*&#l%3o zE)%k(ba{S1@22_99sLA;|9mde&VyqTFrnQb&bl?+k%)&uIS@V^_61&BBm9x4gNZX* z76{)76Cj-QAfg5k1eZ~Qg0p-4v`w|{1avsj;%;$M#7lzGq2w$sQxp$rP`5=*r!Acx zq$pM=ecWPq1eD{rj_HR1OVYgt`9o+{8fO8rccD=5L?Qt7Nn)fyH_Khmk?JG}Nt|+R zQQ*WlU{0wj-aTRDC`D^+w8nB)QvFL&(DOqDuu+;BV6=P`qKVW{F_zo*aomz}5yvp2 z3?(GsDyEIi2l7MPqTzasCXo6HO_WfqH1~?8!^|r%pR!Jc%30(qc*T|uurKp8`8=MW z&-uIR4C+J5QJ_twka@wkw|y6!;_d8(+j&t$f(iCUOKn z6(+2O%cULg5e3qGer@E`AV!YLHDx}PZzuG3}0e4=>jkjl^h;VL-WC0c4DF zcJZX0`_m6HFL`AatULM zd{&c{@{>}Oig`{8qj_CLU<(RP!`^|ZcTJK@J5k~~jvhK5K4lOD|efQ9i?uI!g$A#AmMU5X6FwR zy=p#JExc10L*?zlBC=3w$v3uI7`K@7@rrv;6s^7oMN#X3agfBDVg8E}heZ4uWYf^R z_56Ma83}Is_|!vpeVk3v&xe?1r@-4nVW7#bfC%06`Nhq?%!Y_Ku9-DGb4s#vSTSBk+fo&dwEI%INiOQILX)}s7i)hLkQ7&y|6p;?)SmVtvikUFb~+( z^8=gQs+v$8;F9ekGELG%XvHW87)fo|TErCJ5Y4PczY3~)2%5>=ysBW}sIHVUTF8!C zOg15L@ks|eHey_8O5_{*(DmVir$|INRzoZIqUIB40s&$K!v zDGRGhG1HdZJ3~{5wUOpCJzg;zL$Ag0*9e89u$xAj<2F{>E9LbAj*CKz=z!5m9G$VQ zM3ACVM?!1H8tTa)7c4T53u;a706`ac4-Oc4n@w~EKZ=8CqKCwbq%xT?`rL4eeyRIk zD5JirdkI>LWRwXk!WV(BrDkfyk6hXbSX}i)Gs3`;n1HYow*vb5dfclq{t_n-Yc$qf zUp%V6eEcFvlz~ z9*XJ@wDt8!Xqs5%4OScC!t@ottWbakDKV<@yC1iKPbbvH=A0Nl2VNAZeEV6*3)Kh zlfoMz&vA3zS208rbDE*zgjS@Y7|#|}Y1>A9@uaPzs{glaAEUefnJuJNkpjb3@_%5+D|-rFHh+c%f^6KzdMf@z>&R@$-u_1XV=@F+9Xq-{{!g zSHG|(#7ko{jv4R_rW=V7pdO+&v5N#^<%ZueES|XO@`F--XQX#dwJUK6%wA$wL4{e&cq6w-y7s_a!=-5N9(A2PQpQfiL7EAgGp1s%et zv}9vQ1$ff4LiaD!7SsTPdXYH=my~|pSH^&J@4j*>x<4N&ABCncg|j~$ zu8{9~511%=9&N&u-~NkwTKtn{qutGMoT0x{W5uH!M-lqRe~-x`87${4y2dvF0}*fDl0%COI9Y3;@aI! zsf$b(R#ixi?^v38aG*&~;yz8Q-8C+bwen;trj|nMiupj<)I#&jpza=ErWi+QBO$-% z(}VSw8lD;hLd3%rVD$kKdmT$;{pH!nNCx=F2fqfwE0S!y-S*DQ*sQPkCE z1O~-902=j*+oX%hfLOFiYm^pT1ExDG8*^mZ2G&3=_^-;ikyuTzj~7%ONvw2DB$ej# zItdh(^tvl8`||W#XOkYD{`ru&_8T?Zwv#~?LBwpu0#R3$;a-Rrl6oD z1(JD7=|&LS2Uh1_hnl6j4`Kv#>7jf`h44Y{6WfL>D&Dps3@%fNf%_mBR65AN?C53E zG~YqKgDx+J=yEu8x~BHnV|JyiRT&8>zYLi*|Kuwy`85llzbm_ZA5AKD`9BD|J-q;gErWN8gO9s^WBY(heFMr@toO{$<4-PQ%Vw?@&Vw?=9F%v{-z}t(mp%U0j z;LFSi4@Of6X5hX8xRnjGV*r1MKS0n zw<$g-3Wow5W6xU+FihU1x<^$ z4d0%lSvk!_LPrR1N<>V47C!AW{gcUKpQtmEGfDE(;3=Q#eT^Rbz@(Y-yYUmhvz@}& zuCUMeHs)*Pr6B6DL$rXQxIU!^)6!NG6v6yv%)c4YZz%CFD@@RWb>{krH^D1Yf@4+= z&;m-`0EwONP7rOMwT%>ga6`n~6{A5WJ#l@c2JPxIfHbP%jwMubh^Rzq+!4|y!!@P` z!F0Q9){)o`ZX~w`V*A=9oNOSm(_NxAy_gL_gW*r%*n8(9q8pu*>7c-S=jf`BZwQJ8 zEV>QL!Ex2+VW=R{5eqPJ)n`^Aw21D`U(bcH?Q~#A13Pn&vV$4m1A^iKKKBs#J?+I& z2KO>=P~iSK+uQ@}54t5}_Xo7eQYK}Ol}g_(xXP!=w8!&orh`^8vty`4%N@vt;pa&T z$uMZJy2R(l(puYru+D52l9?M03IKF6z`vfO(E#se==~VqhA0@otjq^EA9SC4Epk7P zlPtwQoom0SUhoS5zCc00lu7u1ItNHKTe8oTj-h@#wG(h81Qsg$a-OOs9wHU(oj7w zUpSV77U?vUSM4-TGRD+!DD}{dOjeZO?Qj4o<5sMV;}n1vUj|qW13e$(1%GxIMZwW` z_Bog)?*0IQ;u*wqZhwH1nR_-yARct@$0$qk$cJ5cJ3v%xM62Df<^I<>;N{%_{~Vz! zy7aRYmVPIMML(V6gADBq1a@y{+b(H9Hx2awYF zV6M{eG7DuYt>5MQ0XiF?`vE!~prZl$JV4I}Lz-Es>pUmPY??wo#MBZR_= z82{e?{e#!~lv*?={0#CUiwiI~bqQ;Th^dsl%(IZsl;?z7iO{r*+ymDyIWLF!bk4&k zf^0e?<7AqBF=Hc7qekd3tBLS(GHhkBa!{|eXyB4E1`>$^3;7OvX2EhpM`C-7|8*|t z`2IA3F$tsXtAV`}GP)Wg2r>bo|5zl|wGN{Cs}eayIbJXhjfZzYSkG2;!u(xKp7Bm9Z&8 zPgxEMaCj+SydI(vEW#*>XNTn8o`6dy^@#!LWd3@7R zv${bG>>JfK)bq81Jb|Wa1(7~;YPK9f3mL-E9)Ppz%F5tk*FQSB=pURNUHobZk+Pb+ zZ1v4Q`(AvgIn41ju3vhyFJU91U8!5HmYP_i72^}PxwB?-7;o7+b8 zn2|q0F<>0W9#AJ5gJfr5gV~&NTbzA zxW2v?_TYgj4t&RDrQqBg_H6h8!H#H+E2>kTrLCLX|4dkD)C|CKc|HHJiqaE6P#m3m zh=DwA@4{-vFr3tRcw?kxM4+EimYX$yJFp3ZMW^`@3zQ@+zW)V`dZeEZu$$!mNyRBJ z1Nml3l8jdWOupzJp~Z}39-L5a)Dn*@Nqt0jAEF8EqZa(3Xsxe(v&pwuOt)BE-C{MX zm$Xa_Yc`o@0NY)0QK6t20|$&dz{cu_1Y&+z&BQ@BaFMuvz5sTr6~%Y91G;9#P`99$vpm8d zD~$C=dId7?JN<#Z8bpvU`7w|Z*UGceTdOdXUI2pKB<%8XJirPFbTNjVtzxKOklB2H znz$!}0?)^2rw@0L-8sG?-HUOHUTW}%3cw!*LJG`@eKwnaOvq?@4@8Wh2=C6#b>bv; zr_b87qte2~cM(ZkYZ^^RGR>?)Ij;aaTs&Xw&f%6{JYOVW!BSj{@v;^n0t@81uQXF! zjH&&_#cYm0X&2Z}gVLoqk)h^+^FJBb#PbBMWDDr))hZ0`9P&c#!u1|tVJ=MUQZFvWQ*OpOvD z8OtS;tGynR3=AUl->sW3`_nN=Gfd=TsRnP62r*UNTfLc5NvS>re8GhNsd=NfWBe}F zokk<-q!x|h(iF`3aTV3vF_3aP*&g8$88CZf@c%=bdz z5{W16wSi1nA6Pb~HCv*cP66(%v(2hto=tq+*hu6^=a_GwIvm0Bie$ zZHd7E?|9T|{$PN)c_TLq9t;-34PC~AGA#kYLPHn`t1Q2yMZlFgMIwB(FgfK02MKug zRzre4Bg!m@$S|E5_h{}Vw$Nqr?4iVQT-tSuw^qctg!=mcFT6aPz-?@lcw=Tt(m^Tl5`O}uGmoY|HGl3D z=w~YJk<==XtB zTr(hl9pIle+Iik*N2#vln??=dtWt|4LJdbQlOZxyBYqr>rsG`vRtH(8xQ3r3v8Ilf z%Vjl^LcP`4$B=-<6c~>;yolM2z=2JY$jpcdp$h}sJJpQ&!q^FPZ&PE!uayKt(+d!@wK{MVRd zA4QGRk)1gXI`7k~6Lklqr~khO_@@E-`#I(3#eD80Yyq_q-^4L+UCd+oGQk=5+ zhh@|7DpHu*Y@(=c!$DjhwbfQOQ~l7^8h47i#vNA3_GKO=WCQ_cL|?1~TuLIKDW&ET zGvRl+nN)m@An37y=DZON(UI^K0@9TsY98H^C~vPIQ?d$;igmNH;@%A~z&DPLX5Cd) zWh)O)cv3szFfBE+?5v=THJejXaN8tfPH7IygaXdTIHWgm+Zuq3piqLSxlD&l60X|XrXi)o zey$i;tBQd3W6FFcjcnpY4fPq1oX#w|E{Dj&?XLG@)Z(M)dd;R2^AnL8{L^Lx?SZIt zrV)kYqt;@1y?f+)h{+x&8X?P(udJ+If~`*st7CksEfIL3GBk}^aw2;lz=M(fw4G+h z?VuR5vN(sB!!Puid_mvL<2yTQu|ew8FS76yGy*e0S^xo5LM7-ecgtD zYK#+h&{_(yWqAfnx&&;+g)0;6B`r=FoL_V%W2X>bu=SFA))RX+$0^9ZeVkn8!!G=% zAe^u&0oHb%9YJ7HW)i9qK;SKyIf9pr^BKLjcN{dvqt-m0Lx@ZUk!*YJECR+d+H)4-^%HLwwxw=JW6iMGhn@Ql)7i3A0A+CcJ)vMqvKmx`6=F2{b|wt2T~>I^ZI(G#Ofc^Bdfua`l$IpZFRt3vPFsDEOcP}|0bLO}(AJMQ zNY^jRDTAo1i>Nx4o=S#VQdHF(O4{B8o(KrdUf<4XW=a~ZfezL|{}@zQZ+g8)rKv)k zN(OEYl-F|#-gk=ho@)9)5y!54xs+W&eDGLg9%py5=>bj-xMb$|Y(QhLxY0p@KTXm5 zJ`USx!i-3w1A8&^?%g+_B}@;ny%>43Y52v>a2rjgQI@&{zwftp_V>^BPEXz+9Uw0Z z-2^=8Zb@?EWz(4(Nt59$bW{B1#a5e=`7n+@=W{oP2G}!LUfT=au6S>IAXUnh=M03v zp!*AjIKONUd%XqZWS5``z!)eR9MlqsAy26JKZ*~-!VL=);L@FcF^SU*Omtid$fTgx zn~6__Yk^c3j_|~%vb8{xM&Sw_k^Ago>O=ld%2~8pt&vm2wudIj9>LGCgSSUL-yS(G zv<$Mf0FBXt&KTrvwZ(^H(C=}3-G7|p1(Rz{e6YhkW&7Jr4+>!%ae)7Ej=Tdyaz-T^ zM^R>HEjIFA&uUw!!){5f&xFi(!s#vP^}sj+FJ!@ol=!Ke^g3jdhi!wS`aA~pW9nut z^`qD8yrIW9=-)&IILl|={riLvQIzyH-fW4sQnxKC05G3Jn6C{`s zcQFk@u>Ir(y&e_2MTBk>st-=*ag>76JWISN9g)Os(}pEOLoLMOgO-{>iROgW3?lH? zlet)_HDMzX4WOt%WRFfUjnjxNOPYjnIw~TXWDYycZ8!z-c68Yuz68RrqPIsV9a8(S z6w`CCmja@Oq2;C}U+NwNCs(!|F2`)cZ{JQa*|WjJjWn6I$%Pyxu2eCR0_Cu+Q&3ou zeiY2(X_UDcjGMrABzu*YYinvzI0lI=*P?$>82eth69frKp-}$`M!i!%J5NJ5>#fmv zfdK0u0zq(`P(9LG2l<{0arS)ArHJp7Bx42eK#M+;FSG>y@&bV#MBy+hA+Pp|7U&2} zGz6|7FMX+(lu?;h&PCN1?tp{~3Pr)ZwU_b{9EFW=qS9$rKsFAt}G5V5tr14MVc z_?;gV=sbj5%hB90-9(@B((e&^-q&pL57_PKxy(oYdDMO0$4ms0VIa^1XD_zeD8U5& zL^ye|@fr-qUi^S!9KG1wdeho?)BQZsm?soCivR~81Emd(gy%WUc0a(dk>!DYmr$cR!wD~zLY&$lOzo5M#H{{Gerfw2cKbFUG_(My)3lXc#R zlZMw|9+s?|7W~3ivqbwRj-$zz0X&$q3Fez_;-EEK4x`g6!S&+fUu1x=^- zTdN0u8!znHGz_OH@#82+d&?PMApBX%B367HJ|;K^yaM~BerV<5No#_zCit513hbBqp_Pj# zt%*!Pq0m8gDT_pj*4j)pH`ChdN;4}K4ox>p$<>X9Uevss6tF^Uq-`Aaw42}LRpc~b zwu5b)m@g8i2{RdNV`9D_P7~%Q*v6UpB6FHB>PZuTEi^ZvMW?FIrqS1AkI~mNNnbyXx@SONWfdB4DPsQWaPMvLRiw{;OcI~q5Q?WM zXb`c7Kod_NWBC~197U~3KJy|lKLoN)TS~Je{(>h+xBVi@ay+1x&!GXi2 zs*k9ZexA$5Y@s0Cx)C^kw z#7p+#fb3-U*g>NuZcAxZ%a22lo=JQdIe!y{yuD!&wNe5$(tA*h69r8qp+RFNyPKn# zdff-@_dZjw_QkZBM$9+3k8be^Jqm0gZt)1+ zv_MIMZ_xu;wPi@LXi+g;iO{HH>T{LO|eqB}4D z^|zt(YK-gw`ozPU4d8_Q+uwv;sP#9iyCbbA!A>$x#W(v7vTs1l4Ed%ufUo>^}9qP^q z#;D^p&VP?xdk)VE@N|9Mo*M;-8OhKd;7W*Q4oXSp?&Iyn81ENK0}s}W)6A4yurj;v zpuQqcsU4t^GS}wH1=(d&tmkOk%B`N|TJFecs;lQ17J#P$kH=`N8jfMJM6J}jB`}}* zFmO;$2Ple;q-nP3I?$qi1wav{5&Lf`8a7`!XpYB%8UT@?#Nx+!chqb;>2GxO@!tm02B4@Z{Ml zi4u(s!M~;fLmWg)Sm&&S{u$wI>!sBkIU?IQ(n51^Okjrk`av8}8WdaxrR5}#D*?8){r*CS$*WA>#Q#+6%uRyzZLfB4* z1@;CqUhqL~INI{IetYp-@A)ePgVxVtt!@39OPTpzz${9Z%RuN=+(7sBRXVOaFNtjH zh&RCVPc*LS5i4kf?OdChE*g0%iW_jHAW0*L30Ty8$sin4t8tVy?xtbbxB)HkEWRZG zU?ZE528eYUGw)NJfW>S|H`j=FcHL48fpzY5~XLNC%(X^?7ybxO

;(u9SCC+@TK*v-EOz{_AJDn-oIy0o13C+b5oRUZHcn2Em8LFohW3^mUt51!pw*#@vVRbW<`7pqhn71zjzX50%mw(W$)h$ znBhs4v3}u+^$Sm|UwC5uzJD)Zh9~i@fEk{|w*qE(65k4#;YoZeV1_4ACSZoA%}r~l zv*k)F!OmzFGwVkyx7OF$b4$kZw9Nu48i(^JAyGgQl32@Ssm9e+boVKBdTVVVZH&Cz z=@`PdurB0JpghUWm_%7^p($wKA<=9~av-U@U^!x+>cCiU z;#W&N^g}YEU9Ni5O0zE^dF*}b7jpA?n(fTz#7ltENvq77>EyIgO{XB1qbYbrjpZbY za&qqzD!nT=(aPYIaAaqH>f8b}qxfD<>iW7=12Fce^}GWrfNi|l`OiQAmjQA|XI4T{ zXm>cDaz_wy4Krp}9$r1`Z$7QHVqx;j_UmdlTG70d9AF^R6eadauT4gtr%? zHFm{YU$-aCCZaOYzxftrnkQ5TEPD%}H>aRBK3HH=z_3p<|5+~CS+B=rNnzfpXJbz? z2%vvD%E-*NXdB>s>#CRRnAYD5tKdSvWw0B$&%7qF`h0A*Ex+X)r(cdS%uvrF(XBO= zDz)`>+sA?`k?6Ly-`^b^ILJ5odd+^B>LAV;3=&+402stIGOf%)U7p>%P~HsPy!A=R zx_zVllHk}E{+ywp$kyc`BKLFZUj~&w!uF&5?GTVC6s&Dg6!zyFea=O%j-&lxqNb8) z|9FHhG~RKd@s1b3J6-_qcsyd^t;DQ($@o_3R1;>lErasDw49!0xtTJU>h!FamTSFy z38~@?V8j`w7QPBp+sTqkiVWo9R% z?rC3UB*<`-Ac9ZtP=aBQaF@RYKU_X0y1UXY4JZ1NZavFcF14R2|IK6zBQH#eQ4)c5 zj^*@H+;(kIs@E_ZVDV|S0x!G+;Wdq9oZ^mytoNs9d;P1u)8o@KK;MX3hH8bdscAF4 z6nEUpD+VHk2P7ogTg{TK*Kwr@jwI?vWvbiVbhK?B+scZ_r;V(2+(k%3bNUbnXV*)o zJ_txAUJ5!|e;aSWb7yM?&zpt5)4*LH7B1Bd>u8o_IW?WFnK}8O(=1OWpKsB^rqRO2 zn=SQfgQj2xzuNWnR9PC!u0@Q^bh&IZ%dm_7*#{auHJ{yx3pdkBRfCy}NUBOQkU=IU z!p<`$<)G<>1)4I!AqXs-ZQN&I z#o5l!N9Pu@_Kwc>j{AE@7P59ee*C%r>*<+=tb?PAX9QmQ4r3G;eYm&OSK34foZ*Q$Haj zTK@Y!Bgt&}@7t`?Zo{9AjrQ{2$^C7%q+c(+D4j0-FXOvuw4_nQzbbP*JCuTnlMx07G7+CZD=HF|?OLlt47F~V>dc1P>$_Nc|? zeYqTgOzoD0+jxVT+lw|f+GwMLHnz~l4`_1(ZEm8?w`lVndfi5^JLvTWdcBEWzeca$ zpf_9S%@1g618r@ht+#0F9s1!l`r!@wVGI561Nz}D`r#dV+eU9Y=-mc-w~5}pMep9B zPGk6< zg*sb5pw8A?)Y*E6IzO~g=Z6mJ{IG#KKWw7T4{uTDhj*y+_BHCfeS%mD<>YC$n(SY5kNA6viO zcXlr~t)@Nu@ojJBT5r2=x>j@MpnZJZXgpu^i*9H3d@(K>v}SvD-5pVdTND296XW{7 zu6dB1>;JlL?mLJZ@b;V5?6Lb|)8Y;D(DV)3yk|SjF;yMIPxE%7(&A_nH<<A9xXgmz)t}(~$?)*ooO`3ylS+99HzMKz-m{iN2a>)zyOJ2~5 z)g^CK3QAl-acoKnXt)wu#~z{bCytJ0phg8EVNbGWDT6)gK!gjF&MJHp%xMIHoT@2O1M z_ets})452~)3w*q<7DI7VVXL-#<{n zUlPn?zfzDKrR8NIdW=W`n)iI7MKu%Q03vRyDShOc__|?Fqj{dWjpvJ`aIR5W6k`7N z4>Uub12Z&FBf_>_jhE2N+^jW8$jC87T9YiBr>^Dnz8tu-1K`nUWC#{Yl!ZZ*@dsn% ziTHy)p~>^85xIq8hZpfcgr384xM@L(sILZ?W=}}OJa8aiYQgcMe^BS-_q8NWny!s? zN-)MFM&jm=k~lCf>Dp^^jl|7+Yk^MU<{F8cS4rIbX(TSV96dndCTNW2I_+3JFf_Rw z4>4^H{1m={!cpVoRd75);ew(BB{*9N3KxJu)FT9rTMmQObCtkNy38Ds5x7Z-z>R>w zO`Op;5V#2%IqU17Ab=<7iwM8LHU*^$YIgXRp@xAz49D7&00|60itaZgIH0N}lZM7P zvUeeN9F8a@&9T?+ra+!E6%3apo3JhVJ!CvOiXh8<4_9-BR^B`u%K@| z*fzD8aYqG~Ht{8wx{CT1L zWGobXb1=cI1?lBWG}d$h>+Af=R_ekj#w0uOKol%U^Aqs1SANGc&{VPB>E&$KXmo)0 z$XP9LVGTsoDFsZys5DP2P;XzuWL`&vrj3Kq;z-$DDj@fxQi0)O`*{_z=SI-u1qCnPNvZj-l!_jyFEnu#gU zzQ~eW-~ZU&2Om%N>7Mk1(@Y#|`xFUAtH5yAytSa}L4OiA7=AVGyp%^!Q6kW5F@Mf= zL)jZkBY!~020uhTf=8Tj$ptvHm%ScO)l{W+z1Ocd-M4M0NK5VUMDmyZkcS5;wTT0| zB^sj5Dgfe*=)Hn+PESDL(g)XKV8|wGVUU9e98f^BQ!OkU`^aO=+wK3m@qX=%_vWv6 z<4ym{H{RRwjrT_0c+uTK8E`p~I_$c~zki;0Rg6 z@m(F@bdU0UJM`)AzJtyW@ZEvE>p5ue0G}u5{Q*Am&`}>k3{F!>CVYM%*2ZD?z?am~ z@})OsarWVg9q*MwIKDrq;R9);KE??q{6wZr;cAB39!)scZw=0lZQ{5@t9?kk09?6EwKZg0#dX)ev?zMN+~LmOG+Ku>#MjjPr15;grB;yIzz%h}6C_REdTl`* zPuYrMcr3UX)Rr^sRxu8&p9q=1q|cUX`C$wuL0Ta55JYRbg2I;(A12s`46YN1>zGO` z2+_n5DMthkREM~1HMMNcGzU80r*&9LsZ@m0E|7^cGA0p?ZW4_fU|L6l#^J@q#|G;{ z_&I~1kjuHK))^>zIQ6FjSSXA0Y@TQPUgp6-)Gz6sy}oWkmmfujY879h!L&nu$9h4NY>!y0LGP>gpDo$wkKgkdVW@tzzUcuK(FEX2lW4QmeFcu$F4pM%&H;z1bMFeot80O7280MD}R7g7@_Qv;RD^@%{m3<9)~k)pX!zT*zxO9QOzK zj4>C-Ij|DrTr}am2OlkNDmmKfiF>a z0?y8kn@Lf%7XYgYqUw?{EVtY}o~9Yl4xU&i2iaQX4U=yBb?@qX$ZclAi-wv#r{p@Wbwh(5!=9NyZxh3% zY~n1q58%W>Q4@wCqCvibDlrlfDPDG*1@t@dZV3%6!GIO zFC=TVz}6?W#Re#Z{71Mfw$VsG)4bH5PNn(UHSL_rhP1G%Yjb%=P-aW!OrON!6p#SI z`PraYSvS8zBZcR}a;{;4?4&un>K&6y+P}pV zbLZ&BLDVCUCbEOw;ezxw%TsWccl(I_xFGkLyN@_}xixmt_zp~ijv@CYmRbq#H??287>^;B<>@f}~T_tz; zFv1>_JACfL6UZHo4sgC_=WPdlrcb~me5A66;{%XAoE+fyA-ao!ZTS2k+rUK-epv+Z z@rfb`5!i77R|+0Zt$2cT;`rbzq!V{>jdUXYUn-v1IsSO^Q83IdW0+rcS{-XhQ(W{u z_MafRsNMl<1P%01PuQUA5Hk{aPZSr~#m=ysYO zeZ1YW`7-scFP|^ib1}Sj*%Muo9A68zc!6qGjv1Ole+aRJNdPZF@iUnO7} ztq`!pt;0CY03>}#*C<{gc!~cE!ON&Dc!?b+r$G!y1sDp&Uba{=U>5WDYYuSUAxHV$xIbzNMc)P-g&w%99 z^vaB7=Hes}VvW3MNCHikWw+6hcBYP6A-|wftvJtP>uk5B4fjMoQKkk$C_{ql2jwC) zT!F6%Mn2HXJlv4e95Ml@+vmsrpDHqWPK71m9{fL{|DQsxJ9IdOX6w0~!|7^nhhxni z_S@)VgcnCAKkpnL?O*jz_D=VYP7Yj4T)RXrK6&=E8a(k3m=sS(Ut}CbSrfFsQ*L6@ zIEe$&`jm3(!-nuSYFSA1dH!MN>|%HD{(b-K>ipj0`q0uH2u{;bF`)n0d^)1{!9jc3+vxEMHoU#iq z0X;t->@;oMkS}o*P&B=P=zcUGyMeU&5&<2lHZDefPB#IptN}I%%+dy=fQ%E?-}oE* zJL~W4U+wmPJw4gK+C3ed?4P^V4vSXK&LJa%$QzTO-0Cl9M;HAEo27^4Q`;@N zhtpR7?CkXH9Dj_oM3e{oy16#mYjH+nn)W%N07K%kQOU=sRi;HwaX4l9lf{SllV%ef z?CIA+WeZ%59-BGq>%Ra*(4Z(iV)O$C8d;z3v75Cr!qXaLzYp04efd-3RtD1DBF6y2h)fDI9PG|pN=0p{Z9x!oTB4Ce%*ezg?>N4%#@#7!|skv znS(Oj!^)>bR$PI(lFcVt!XG=4 zD7hx&$Z$D>F&G5^m@I(nmKPjF*;@?~wT+m!E~U$Mg=mN`Q3w2c_^-~^ZvUH`f{)RL z(c1C%w03N^wz=^gu))XZwb9z~_q2AbwZ_Q()@uX26SA5XGsUR-Oyo~{TL0=h^tKGb z@wb6+{EZ+Sa}X#UXJ}YXH_T}KgDuLh0TmG!R2M9V_lic@%dcux0?BavJKz*}4vk7E zSG4*Sfa=Yvvg7ZC^|%h!uSoLOA+41+ndRzzV@pr>| zY{IJ37Omljw|41ExwoG3g9Lb_-@u0 z`x%5m9fvUZZWdus%OecFpGg?haS4O(W)lXre8S-S8HK?LPGRuv$MfLZd4<6$X5r5S z=PQoI!3ut1@a>26;M+Nd!77&F&jjbIXUF6kXqK;UJxck<>g1yZ$KTVMI7cbW61Z>O zh8los0amyA)VVA*tICeQ7uMrCSdYIOR@E#_^nRS*gydTnU3{%L4OVjeUF{h>g97kd zCsPMf&E^B`K6N_F4QqwR-wp3^ExgCy53jlBx$O>Zlo=anhJPa6trV(j3L%;xStar& zy4d+;S%eQRERbwD!VlIGDUW#Y_=+e$aio)jk`8l|>~dojH9|AeY?TsjnI?4sW^@Pr zGaMlyg;J9RmcEKsm$hHg%JORBML|g9cWZ6>DZ`Z7*6Kg&u+P+jom{{M)1~Y+(?yuk zU!qUC438(KP>r;k{-{?>S;`+Z(U<9v25VjF=7wq=XMoCjDB$?1rTc1jQ^!w@pB2_M$71*rpB-N9v>Pzg0uIcTG zt0Y#fu(=Ion6H|hOI&S9_5G?BS5Q8YMzasDX|#Gx)t;$QuRLML$gg0>2;QyQQvD|# zLEKo;$%QRKqSbALsJam%V_cdV!yrPIVB3LZFTGfk~h+=vBW92z_=L6oLwuK@jD%f1-|@Ma?l( z(Yw=@g)tLHol^U-hYAsEur(L#sMN1C zay0G(# z`}~V`@<3tj+$dW5ol}&m6T*F?~bYe z>p#hAuZn+?)pnKtB(3w)y`RqCT%DX=?dR(X9!gKmM_CKe4;S@slK|E7g_C`8*%M$S7 z0>?sC0&ZdQ*Xzm`d+}`MMFITg=H34!t^NM{ox$1_U?cg4PI?*`ya7&P6)%oe4fgM+@a32}f*EZR z{z?xRsYZ4IbCpiwJPfF7Pb58wVUp7ORDpU*>b|4{$bNlWv_p>xR`$pVsj(=ct^ zk%T;HEEm%mfiy_^ATm}~>o3E7q6l1qm~T=}8s1HOOB!tmZr+%s&SS7oNy^x9aK&gq zgIOcHV?j1)Ur<9jKuM1!z&4-b#zS4~Zx+mqAj5=L``7GPz zwXgZdY)oF5#>RBPXLJlL3!RX0oJ}dJ(^Jv4w2}sorNLwEf`4k@TIbvqlm4{ON6qhZ z8b^RO!71QPCE5*OPXKYUU=rC?nfIwprg8~lk!|RaCbkB63rxKEoJ5Yf5U&q`2u-}t zL=bS-Iuns!Bx#{F8xv7g#eYHU6?d8aL=#l1A#wO#H`s_0UO^jL37DhEk7v_pteQ|z z@umb!Q(je{rw73jZSR6J(&I47_NnvIN|4_iMbm8Rg-@QwH;pO#qINnuTYA|ZRvx+&74CEx<@gdnT31DZ!(f?I_MsdUA-%~O{qHOB3%>kU*W4#Ze z&ry6AeN)#t`*uaQig{7*-ct`@jtXxY=1cBBTmtw#|N6FlaKo8j%O`2#X}U-xa%eT3`&i3LnL>6@86vdc{P+2hU- z%K&@`UI1r`H-^}P2Ua9mRJ=LFmi%QEj(q~j63a~ZNgpzwu+T~$YWhA%yeK09e%Bsi zi~nFfv8{pM0aWk#Mu2jG4EP;DCVrXkjvyC1ez&PrphW=Y2OUgxd-$LM`l$xclbBvR z08IACdOSEk-A~Kk6QgPbFl!6wVuq21NN(BD?Os6(s)6Y{0!$l9gu2| zXltpw@a5&3=G_b)Vv|~rXz{0Pi=ps4H|WS)5;3|BY z1S&*`*isPjE=F3ttOl&Qnj%_(s{lEM{@71Iy;q3?LXrNwQjqT!oQ~jE&UR_ms~vI7 zUO5GSJRc4xlzBKLuN<{E4wMxAuqVU+dKLLCW4YU$meG{cGA|4_wf!&UH8{P3%_DXE zl*q93d81BGi*=gM$0BUyEjat>Sgc&na|S~7ZqTJY9CW>2k96S=hJ+piA~9fk z#b)s}^R)7fg(SS%F_%|6=JLuZ*#5cedB7X6kJnCTe&a*|HsYZMSL(xe<_nq?d~5mN zP6Fivg7%1@IsP|Y&0#O#c`^!+-S|TCRt9hyw6o!ep^Z1&4^R}+3CR7tM>bkAK0%KuP-}tf#heoRFBJ4>dD#%U-VM7 zTg+E`t5Ua^uk^ab{Bdv9EgHQB`^o;(K%Wc$H|6#ZMjPkg;Pb)f!P3B`aQ}c!@bCWi z3I5&RKEc2H^61_`AndE}BM|Hy*3|Yi49+LPf0I1#co@6%IKtn+!erKIzmMXp)oa=l!;+v?>4S`Vm7W^d_1?a(M5 z9n~rbx4W%eu19WZR-~1s_ppGfew;V-Ej!GXONX^X+@Yj%dTLZtAO!)BVYKLpa@R5; zm+L`!Jo8A0J`aN2b@2R_)`NiDvQJ%~dAtrfIWek;4@VDZ)+ip94=ct|sf=(yi5>)} zr>Es2%IP!j6xu|&4t;(dKw+LE#~B1l>R04D<75RNGsb%%FCrjTG>5PRtH8} zL2}`u!Ih50*dUV)O&L(Bc2p@>E7htp9)M>YnN3)?_Q(ocru>Ka2Yxmir4_8%48m^_ zqP?~Ip6k2AAj3tTG{`jXW)s(AX2`B$(QHjgAA6)+MCO*}_Slui?^`pvUU!Z?QmP>{ zoj!r|BZSrDixx{O@o+BFG!{4&Zz~-$9FR zhx>`_c6#+iArM9Iq980-{kXAPXpH9*Lxh1Li<4p_ZP@9yvN0x~0x2=JF$6$~S=H-awd=%?)x?5xsS^H43XW%|% zlio@Y`!*p*jYYTB%a*ED6IQv!TpiuglOkF!Br3EJAzGeUOMpX1CU9Z?-ovPEBIch% z#E%5CPjhW8Vo6Ot*APS*}8G8k>n|Sl7}I%z$a@krs0E*kYHH)6KgsH13{Y2 zx`jWQ?f1aSUb;N^A+yYkk>`aT?}dfHZf<{c@s`{71g9b{W()u!i#p148&}22)ki>E zY1Ito9vmICyvA|ep(86`Ar7U<xchGJ3@{4Y}hZ4<;9||#kh~YADTNKdSWC1K$ z4Qgkz`hv7^CkF_OdDc-f<4C4glIazhUP`8yWV(^+AVK*ZnwsO_gcKX%W_$7Cxh{7s z$~~lL$d}|+S3{Oi^*n*eYsY6~o1c+w)akZ!i=LcFQEZVa(IdrrCptfK3*6aog1+Y6 zNg~uHi+cM+tZy#I+Q}GCt02F?MYOT)mnws>cqTVPE80bE#)}#!CoRONt$LwY z97qZs(FFhGq%EfmcW9k&e}#phf`_1})9uh)pXLn7O28g%t!^narpu&kK+(xbTQ=0E z>y5BU?Di>~H&~fXXj>UH?nuHP51*gP%eau(JsSrJo2f0%r~oPjq$m$`adbx$7{rvZ z1+yk+u-*aIMl99oX{)YWGuk<$eoNz(y@lc3(tMkrXb~0c!;AzJ^UR(?;aYW0zB&?( zm8u0_Rz%7q<=dO4<<~UE6>$rg6&gQf0*WGL0=+Z%ZcC+b0XwR-QZ#6o()LY-mXawFNAxs3rK( zT+5Wx%p2nqx^X7Isr!qI>TY};VPNz4uYi(vA>jB8%R+OlLQv1=191p;1BT#`9{?*D z(32KJkASkwT)PnTNV{N7^&)OTY21N3Y!SC%ZD26P7&5_d?!iD)%ufe*6u@6?xC3y2 z+XOiN((?`jlOGxjPRcluE7sd59k$znIxYFu=m3CinExJL;L`IpE@A`K^+mUx>lA`s zPL#+Qxb!?Ol>_FZ!!a45Q_?A1d;(%GYUNo17N0;$3=}RnN2V{j3r5LvTRKPVzEZ@C z^=12)6cI_5oDH3YfY4j)5JBSZPnD@*_V%<8zyhIVrx9I8Kn@{gO+qR=yT5exc61?V zN8++Jun?RB>r?n0(^lZO76l&*fddei2ffv8u~DLW5S$hpdaE$$Ko~0EIRcPm;D9EQ z+KexfAcz7HK)mPcfb?l00Kr9{cCVp*7~ISQR)7uM^~lV79bHou1$xGj+UqYdN7ZsGmArqT-Q1L2RhD8?=& zs19>*avjuBpLXFlv{jVs5}n6aviX>_yZQXS*CS*m-ilnjHI-we-QD+k&*yfI{Q|6f z>7nQUvKZ3v+#Jj~+s5-TZeu#fMSL9~bTS{K?c-L)RcFnd<-}99_i2x)Bu_f2*{jb^1-%NyWk}d3d6lK)j%7%4dX54(v5fac=r?r_HCO^FF`!Z z!nAZGwSu-g$^!F#*wta7fU7coO&W)cW=ATx>+=^katFV>Qw6>^_Sp3WziF1b#n`hR(sE_olsqNoet zwB^+{IEezr73=ZExm)aU61vD5C|RIG7TIzn$042PdMOXZs_1zKWsNG1(@IsISgOXU z`XXDaR?Cnfejxr=mRLOR2;S@yl0R@q2%icUpYk0uub>Z2pbwws>5ME2wsU)7kL*C6 zq|@1ih#x}CExyJwnTfjD@A;IGy#>&1qYCUaFA{*vaiPiy-fi3}3%O5XYR>8x*e>pD z#=8zX<#y~K(Z=R&w7&%qaa^onpj!8Xr=X5>#80T~-#pM4{fvlKvu0qHzKw87ulTI$0Heg!>r#KZ16o#EC zHQG#z(1gV``EN?2b*FbTeE~UqJEszmh{10!1`H` z0c4t7!v%sNJh|2lHA*qvBzIJA7)goa2lbE!`Ez_?6Jn^s!EUja7e`}VDI?{8c_WOR z&Tpx{samEODukeGaEWfYl$RnFCfOAqE76c5WnGizN%cj6KWFR~dnYHQ%IRsTqOfI8 zsbthZRvB0Nq_B8tOQkpON>bK+z!D}d2e5wQ81iDyStrn-{np7whwYqX|?~musH|DE<0oV6PAqqqZ zx5%~LW|R{MXnf_=G)^#~frzt^+VOfoBM455bp#T-cCHWmPrN=w2_=RI0 z4K0fKO0=Oik-Js2^}lg~8fnx|t`c!A8-YC4Ru%Q64Y@o}QFYVnqxQ!6L$=ggK35 zPXg)(FYG{Hg~V!oiI7y|^9EV767mVDQ*FHTR(h>4{g zdKbL752();n4T6JEntYaWCV0vCVWd3v0^^dVpc`U&3YK00#Yv*gAfaxBjL7`9W5e_ z-4WOD6`5Ez*4wq$)pm>C>1iA7MIvRX9XUO1bGb8R648}Z21IHER9`Swt9^15>+}}< z0BCc%f0S=43O(paZ^!DqBSoFZ^YY|~-vZlQo7YyAc?;0x32kv}Me;CbFh&yX6CZM6 zD6AODWZ zs<$JBLoB10qSLje7O8fqfCU&+wA{H3!zACkT4YbAGw0c{BDMAAr<4%^*RsZ6CPV++9Cz--6M@nHZHc-ajlL%A9o%Dw1x*|oDy~vS!esS-%dtW;7qA9GO6))>%HLZk1s%jfj=T$U? zTDb?;`$$;}1_a9fa8#Au+JV1q!?YRM#cgHIoHgP-h8O*e*TLtiLBam6l4GhA9q%ma zt3`+Ve@}~>`!OwU?tfE@n|q2DH@88Hds>uA+?EL{-B^1YDRp;faR>W5bQgoc26{2u zQG>IR=^CRQH59R4y4rMqs|X;H9VUeU_d7aLR4^STKTh;@@wA#!?yRa~k~MLAch7Bv zBLrQmD`BOUn0r|#s`AEN#8v((uT}9% zsJ=E+UJ0E51D6z-#8vqYNT{g15?*X*iD|Q-rLEemyZ!G`XI<_;S)X;`Y@lE-_i-X! zDzw&;nMSLfO6G*^GL^~++nqw#-tQ+YCsmmd7p8!u9``9T8az=fZ)v@KVjMME#4y_@ z75Gs$+b2e)(rA&2**-a}L0Z*pks1bJNuMfeAw^px(k~oH7avStR2RyrSPIplqzpzw zt-JwmlOkJ0W~SXWdSG{wRGPI;4L-D)M-;Ogt|DV~@XSLX9wlHNb4!^SCKn(Jec8;z zEID85@gF^e!QQ$Q3iBamoQ3B$m;>z%i3KHsLk6NGWFsWdfKux*ijc&g~VemN$CQyM!DxfB-bos_*G@J8Pgi zn%i)H!(d{{?Q`;^L($2al*#oF{+9*gpW8i2T)@H-#>B1gIX}^%rv{i2i75;ye(ABv z;@>Yl%iZty55rzEYpH!tnd_HquIDxrwuG!hHfP!Fo|qFRWm^Ye ztx=rMxA`z5!JEOy8O8#7q2dhG+s2Fiv~~JMYxY8a;(}2&vm%5di#UHFGnv)8LT1`P z)ukO_y+hi#O zfIbt1v=a$nC)Y`6K=jR^FcS-+lj($xRt0g0hURitQIy_%fYM^JJXBYTgT9SH*;1fvsU=zh%G(5#og|dE2`FzF0gG?9 z0Of6>TX<_BY%q{zr^kqo62l9kZmx{qB2t*rdVXgu5!DySdCj* zBA`jAVXISwLVL&zWMgZo_wSnOD;R-0fZQ}Gl2=~Juto#V>NBYkFDnt{q=-5+79Gf- z^-&Cm_$bYt^(36;WDn`Lu?q>C}ZOg8^^d*#-(FiHgNe6R}5S!{Wn3vA;|felYsEucGs?JTz~5tBD;v00fq!LOs~qB5wTx@9CTvm4xORAm zYe!{VJ2voP5&j+HL*p19mJab@xr`6X$M~>P#)nk{A65_XVa>pYwPSn;Yk72t504M= zQSlfb83sNo!M`#-DjnjZ5-jvFKB|`SQS}fXRgdvet&EQj5Ao5FfsX(Sjt=p0@em&y z20k{9@p0)GAD0jDamB#Ll|y`7E#u?bAwE7d@bTdxK0Yer<6{FKA0PJchkeFWmTKtW zD(GItZeMya!~7;D*tnfCnr{nJh&Ho>NHxI;Dpxf`=}g-IH9@-xH99@9@%E%Ne$NP5 z(-Fmz$^5MaF%s>?a?%!Wj@gps40vx5mC9yYCm;6fsP!DmFr}clowwm?3Oer=0*we| zHf616&=SaaBC@b&NIt4vnA(#G_1e}Ah!+%A>fIVECFuw!M7X_)(@0%3_KdoGc)_IC zZF@8ZtF-!pEON%_seu=#%DD6eiN0dHiP$KlSnt!5MIDS3&@zaO%=D|EyVa3T++Z)d zlmybk2@F4&-ke=RHW6D!r{nYvillRFYA7rq&E-zon+me|zoq0>v8d8Z!fs$gf9eXY zfz(lG|DF+s648>jc+$9pd`g*=u~)TZK-cW?c*bvQ>D)wujA+`5jO!Z6RWc*e0FbGZ zv`~Zdg$(zThI7(9gxsbwB0&JT+23fJCjDBuemkx$yv%y z&vr;Vm1NzK_sJyi2YC}Dl3b7cl1R7%?>B4J-njvpCQ}_ZAp7Y-3dIyT-;lxfJh@Be zqzDHY2Wc!jcjrz(eos~tRyyQ2E7=cklEHr3M3j*{ zGTV%6WVD|)2ek*3+$U3c2&w_~$i=4OXU?D)UT<+XMaw5p5Y4_agh0M?lFU>kc)U0!HG4lqw|C>JupD1Jx?6OC|YB3 z4sMc;)Ng374y%Jv!ZGSVrSj_lM?X4BFJoT8{Q%x_qX$e53JW~c_41b*_EVYGI9cd}TT6e4qHHIw> z>b}Y!imjaDs6%B|aZ7=zRIM>^cwK<0BcHwEWQjK6A`Dh4x9E1B$?-6+EmF$RVZ3J4 zn)ua@(%L=Js;|MWJk*U8zzKb-cyMXw4Az;b$NxyMcnJ>8f+Q1h@-vb9HJSUhC!a`l zx{D2M_d<7^ILYmReB8&0L|8ZSN?9_%u!LK*F}v|vWAMvU4+_Fb99a(N(3(#I)5R}X z@NPPyp6TKa?L#CYwgt>zr(uYA=$bi2A<{?ZoFko{lhL_s3x5FTkx56eX@_%^EFCwv z<{sb7gYFlsX zV)g0Roq$7bo~$=ay=&x;d!Gl-(dYfuXI<}pLA@OMj8H@WoI?%0xca&KWxt0SdLEJn zkk&9i7uZkK(7QMJk^L2X>CSKOe(RwF{Egn52VeBAm4CSIvHyM^^q!*w8*;9}LA~!o z|AkFO;0(S~3UygOpL+)w*fkhnF5f!{6|b23Ji-rOxAZx}mN%MDsS~JbWk;XmepHar z=*d0vu!p;Ikk~tYE71F^Rkx=a@cXqWodZjR~$GU!Hm;?$gizej6&?YIqqIsWXKYz%O3hJa-Sxl zsZR?mZa_B6Gome%${j>;XWU`trC73TRv(Z79Coyz?4(pxG24T^$Z$QIwf%S2JAHtZ zIRpRDa^AFf-ss(oJ9^IW%9)#akQp*h`_C>lJV2=KFYKT{))@>p)Jt`*-;x1{2(d_6 z_AQ@kH<~#h%t0nxQU};5Di0q@hzGl^v!Z<{%5&IF@m%l*da`<=&OkHSUwge9wb)n4 z{-yK&U4f&jJ-pWk+?HorSlEML+=wcR1Y{6t6-vw;bUFAwD0I(N8@T^qjY0p&czdjK_PHI$h<_8Xh)+iG@4qE)6-LR zO^*^4{CG|E0m4a$2IA!mWEOE^SOc|@Kxt%e?}wYYgl2?0+4Wa(`~)2!5d`)K=M5MY z6APSMKIlq*tlRlIYQmJMMQ)+Upui~7`{57wbocn`7`4Rm{Pv_8g}`Ov4?YbdJ~b`C zNu8hpjbV-q%&qic?{>ACG6@B0=-1@7q2)AF%WD|M9`%O`9%YBR&ZQYx(-1PR;as|a zS+NG!aA|17KM5gPQEO;L&5RS}izNe@T1LY+L~QH4ew`a2dEhl=ayEN&T5McE-_#29 z8)Rx49_GljU`_0i)40wJa!vC>bakC;<_2an2Te)Ff5=t;62bb+sXljiTUXrS&e=)k zcIUlF-_o7;BB5lT%<%+|Nwnt;LSxJk&%Mbvu?2`#i>w9)3~cP(Vk$P)|*wEj0OXhRF3Nj)#AgkbMFM!9X}qG z!;wes$pmhO?Uf98g&t%iPnP*c@3nulDVSC=zo!9;Lk4Q7E|6oXGSz@uSh6_g1gv9#A2Qj>GyZkbEkg;u z@61WJ(!*@in3Ha`huQWrC*2xY)d;#}PP&IZ7Fg4qbdP!=3ZFK2bBA*&$pLAo-Y0zp zohAx8g~~1?<1i?Q#vuG=;Z}Y)c!SVJido_sA|b8@*Dd(>!Qxjg=Zef}Han+<>uXK{ zUf2%Azh15M1Yu~XuD^MVS%(nesyv(3A!ada(U$}T;;Szl=>YwL`hCxyNvGv?ea~O5 zbZtZf&2}<=V-FyZ?$J%3MZ*Tk+-e2Stel}entSlgRNX#VQpQ$7in&+BBN~|N&iHB9 z>xCi0n?14zECOt3K4D-u=sG>ZnnT!Erp%`Shxmfudw0$nT5bbG6W3-9r^dyd#M3YZ zCR(yapbDWO!uRJfNcI$VM<=kn)k?iP!|f)U4U}1qjNt0xE*&yq;9g7unN#8ZYwjG=nfB=_g~ zm4k5GgJ0h~gddvZcOPH7WO|MrpM0={PyK1ii@p#?jXZZg^GRDqD!@v}1<@%SBI|(| zGz}{u7>IUIE+tY}Xos>eAHA)Bft|36iNaen;}5;fqb5X8YNJ3Dtd#u1YOD!D>oSx_ zdp%SEKT-GtX)ks&kQjppG)QtX+JOda%-R?t#%rxUj^D!b_WnHBU#6;uf2JEek&o}= zq*FQin7BSP6VVCuC|W76m4Tog1vt+LjYhCx7GOl-mH-`uZd&X(NvA0E11qrm8QW*E zbQ#uo_o`HE%Azpr6~n$2^vBCsMJbzz!mZGPKbzQrt^wHKgbDOja8+%qkulIy<^ZBF zWyFA>H7Q&P6R}1TM*oRH7N*urr{pwRTFUOMg|DvdF!wqQcZGM6f5W35j}V-pq=}=BQ)SKNoB#4|DvvV0kkOH9i;i57=Ay z(@m!UN!D)O^(S-RzNO02uY;%=F)BZ;7iJj;wtLsq8GP>~7*CpnO$~1YPA#j}{O5+R z_7GbL(IkmcYOPTxnGs`^B4Oa$l+90Ek*ZuOC2MoUSsG>$;J>hZTGES{qA-!d;s+(4 z&^nfP{|EKl57t1JvLz@M3i`&*6N;8}wbFrSL=oe$9^vYVt>2GH0(vz+N$evl%0F^8lJx~r8+l|m8ze^7zbA~nd=1jMvLmHWmAn?Oo-yoHQq zeCc|hRN}c#N$MNIgQZv%apvRH=zl)(XO@#;d@Iq$?k)B5tkVqpZ~nF-e$=$nXa30n zls+vywbG$d>1RyEau;z)S*0ksgeyuiLSd*f<5M2e0oxAS`Fclx$ zZ?3OiU$(A4yl-EUC{6tO zqjUaV{1PF``avR$nf!#nsBt0oTU1>ZAu`nRGt9l-1)JwOutX+UpgL*DJJNe^@VPt(R-Q ze%0PkapcGoX zYu>o-?8fSUIgohgRr zk<;6A-Ai1OH!hx~fHvPcX=ZZP4kRaaz_o@BYB=W3sa$8nQj-W%c+00SOd;Kz$K$+V zv5EWtmDzu<%zpBpC$k@4{RhkJ`)Qf|(6zW`|c~e5LmT zgzx01HyWC$nVKdM=6zr9 z4*R-~u+@uA4*IOim>|dEY7P+Zde~Qs;P-vqV@t6OVFn5TpIvd9pa^ms8Uqdc#FIZ= zn5sRR&F*>NZIxRXm?m6yvsoocLs<8+**&^iK}oO*iArlMe5sI2-X{eXx>;NrxDukJ z0jsWI2SZr1tu=IB+!W;jOTZn2|CAcf1|k2g@jmJ@;=4tRx^)6?xvg9d$<#S9>hqHz zZ`J3y9C}LY=ElC}ILOcQ7V9xybC)gD+HRkTc+&qpxxV-0n*QgVTz8ZI;FD{VKDnlk zom}n7_LHkUkpiw-@__lPN$w_0+x&G1zd+ke1Na4pPK%{|@DRRs$Tth0!J&p<+Mtbo z2tXVCSCBZC-p$3N?FH)wGiSa}J$~3QQykFA#s1?G2fzU{)Yd-_mgNu~aIXTwU^f6K z%OvKuDc1H4ykCmNuu902iv(v)l05p-z#-xm>p8Ta=GWPAlcy~3e9X4*mwrZgb3?`N zYeR8kcg3+&9$?+xW^H5is@TuvkbgqE#a=_6H(kGnO#c*o=w!2wk{BOM!YaM1u_vhg zKzx350q%^E03^+sF$1KMFO>X69r2 z&0|1wXgv~ngK-+VL)6Pv5nt~IWBSP?rE{^=JU!8^?9Z4vwD51 zTeygs@K2uT#E(riqGbrDcHeV-cNkoHmcwrkDto_nr27SXc3aC&?0|aKq~Op%9F|E9 zc+x;BuSI(NHHCdb4ZW_nmv<_DF8MlzV_P9h!A4UF?C|Hv;M|4gqa)cTUfzl9F;9x@ zEyDd3Q&!)FDGoAZE=7wg#K?fr~nVWlfV(Dq9zJXe?ez&kk z&Ot?3%+98}=s4eq~j+(S&a*&xQ5Aw-4J7vSmwiXr_-C}e?cKK^8a0?~k)hDeO#qF2E zqhwxZPIlQ{(jIKm`kaf3om>vj_xMldjMF(avRR+Auw9yWFombb01*lkd0RorJ9v8@ zym69t!Q29KPEO}tqnDp|i#>3K41B97eOio%ta|DsdR^KBQY7U8*{lvp@tk^M3X{ghbuN?jVvb&7jYTYl&lSxAU&SQS8*^7` zsdOQc$5~|SZ<@P_Eyp)68)C(FY8#E&O~#SSYfaujaQEhR?O%tgT{~MsA<9Thd4*pY z)q7oKjq}}5P2~rRb@wFEfDkRzUG53s;7gGi2js*}I9WEi72MwLYqDZ{PyI~aa()gn zW9yb?CS1dud0;B$f`(%bIx!V^NAC>zFRl_ccXj0niP!bwO+%6m#U|aDswfwkN5^jx zRC`6SvtW%=_ z@3A#VksLe1X=y?dswYVje@5|zWqpV$mO9@tzB75Fey+&uDV2!ihZu8qb1u!}luBHofFke^QM0p;papY*Xz14YvdIRK@|nUG?Aa?-C)KyWPdenkz{mD2NAxTegxf&3&{T!*}v zetx?)374*$nOe?$#x%tgaQ7B`g?({4TFinc$zrI&W{bazzkYS3GdgKktr>1I@GoXEY3a%xd^eQGLSg#I}FEMJ#S25e3er#aKcY!G$bp&oD zZRWkklS__K&W&<%MbD7dA&@rK$2APs`ncOtii-dkfJmpnV32_zkl$K13`jDtHD!9> zBf(+{$`YyZ56u0@zG=16eZp!vIX_3`SrgjKo-w$j9v!%8xRUphA}*IeACR~-92N0# z4T0muP2vZTSZQoo)%Oq%*)**idMuZ4tpeEpXoYC%cL1^p82G|&a!9obJ}LrJwtk}# zS-`H!P^(52;lBD^L3pBkA0a$eK99lM#yWsjWTjt2IV*As0s-$zNx6+!+vUuoL;G&S z1;vCjD}D@6iJt&e@DWTVGjXk$j>G}npV-t1DrUri;Nip?`KE>Yu0!8hfjP&KfX!Tb z!ff5_7dxiv8-*m0dcH3uP{4`v@gSn7om>1rExWE$mQ z2E3=@i&4XsBlP$bCFciW2%504?oBV+7GdBD=$KlUOBX7UDtg@!58m%~F2!!)2Gx zf`LpL2JS}(94yd*+EXIhx7s$NvA;KyPo$!3nykr`9&4>r*+x=1vR*DJM^B45t2HL% zWYo{uRHDf8vAh4i#nG_acb^2=-0?bF-U0pAZ244Qr{_-u2q<%&fxzm8pR*`pSI&QS3)Eh8^ece zFC|LeRzlm4PN5wy!d?bCY&XL+;hjD>O7+D~eY{c$?C)#5k~G-g1#x85AC5gb-1vI0 z0eW_mk~ERwR2boO`Zx$?=D|VtncsWnA8b-OfVu#lEF>gj7#smxzR!K8DlZ3>WR1P{ z;zTc5Mx<#6KS|9zD|%Z=7Gnkkd>^g92aIXSZ3Sc|5$9q%wFG#L)R2EZu`)1yM0;lJ z`_D@Iz9#QT&&vD0b_z)+2Nrw6#v#R?FbZNW#+9~_`FP`xwI`na>9NT{g0ok`nzV+h z8Fxa|ps-k#o|j(Tr56RdbsOhCn*|8TomO+yCLjB3xz@oVh@3U zQW`_XB1hGlUF#{tZw)djjZxFqFJMeK<44Ab351V~<*|#D18Gy)+(Z6ppz-U1Sofy*>DpldXnXU;Y=TckV5D+P*uSgkb~89XGLP8)_IJub*wTS^pj zHe#$FdrHb?p9Zy`6nh|bA30n1+9O~)De*xJ7Kq9*7z`-iaE8YzPY2vc0q&8mBFpPV z!o1Z=_efXM8|2GMgErF!n6o#si<;x)H@t50*ey`DRA6(Mm0a^~!<7q&~NVtM~%xHLm z$6z!(RE&m)jlGF#G(1#{hKK7$!^5W;4Ntn`9Y(`_aB!Dr?(5lYg?-L$E0^wTNQ6&juVe+Z(U4#;m5-(Hcg9{x5XuSfXTCVAnimi!1qJMB)|Ku5)bvJJ`i` z=D!!M#M;}`&t{qIsbKqD1{Gz1=X3d6YC1{jAHqVL`fudhLXH-)C;2Xr@<)BJ0q8%8 z`Zwq7p-uR2!ujwC#CV}#8Ei{nRP!E}$GI&Ra{I0CjzT{%fat zeg3g?`Tp(o*~iP)b?5S|eYsj~s&TrxM)Tdp-=Bzc*4x=U7`Gwb7^Y&iRVY0A*PQyn z_1JO-6B<9mSgoWOVOQ}zqRGkA{z-U6!WM}And|uU+K13>(USbTdw=qTCCmTs&sZ2g z8d@ZrC`7>&g2LGKgE+*6k^yWtQ9>>76c(78|2!dJw_c6=xK8z z;})TcvRfO|J8jLk_L{@f`0a-4O|3x8RLsP8_-o_K$##b?mpdMPUQP;a+Nbs{m6rm+ zwX-*Q=U2%$d8AT2t_fSV8(v;ai8YzH{f~}s4Z+TeOAK8e(oy*Tbv=7zJJy5+s-$2V zo8kOGN~OSSh1P6F9rl_D&Tr9+?eibW!(2Wc4FWoy1^nF$ub#0oj_c$(s1Z8|`H4Xb z$FxfKIT|r5%3!5U&obc!kmuDOnUu&*bG^OX+Wg=Rg=pl1ALR4y>|Sk2aRr*rQr9A} zFu6#17tSdCCZ?L6s^xc2vPZ2!=7!$8&L9(vY32qjW&8}2*;6p*u|~||EubDaydPMB zRY+M=i!n;|GWXf|?*m0HRkAYVIt44aLt21J|LA zrhdlbuOM?8{VW7y>gYH47KPUkSYjqZYz^>`2h|d>{$OlBBHc>!F(i?c6mNK>15XoQ zfN|aX5q3e$VUb%8&&lNk_sJ_~I|S)|o+vcxuK^efjwa;wNRaSxhTa7f_JUV{Xw*S- zY6k&LI)xvir9Yp6MTEdmaijH=ht8u<`9n(42-}2FG_7iIC;8(_$0M?6lP03=kJTcj&`n( zdTj-C5ew6NbYYq>h9(lY`(lzgIo5kDTeAyIDkqK9t@1Q|+K$QzBJDr_;AaAPo)nmMD#ZV{k8NhC%$ z2>voQl?!Phz9#oJoeVPL!rCrb3K^0a%S)Ls&b+}YJhwQ7r)D!m^(JYqR@Vid5^kGw z{*pJYhG)9+h0Mffypm5_!Vsm<@Z-gYm!37HK#}+gjM&EW3!SOu1^kGV=kkbYrRc}k zPGHntH1#6FO45-&EDWJ7?yq3ADuO0l+#iN)^8~cz`uu)}8hWCxpjlNY02VFN^}$zX9==g zRE)E4aii>eLu(wUM0-3bYOx+69uGb4&I4U3-i%LunVaOOq;z<-atjyDmuDZ}TwY(k ze%pNi@e&Yr?s#+MiZ$SgI>JNlVTMy*GBnjMpqWlVDk$O%!AA69nzY0$q7B6a zx1rA3ree|Albp14QC$OMib|;~`xK5*oO;ek6!sFgCK&KMd6P;`P=QWCI1_1FOkK^C z_$ZAU`OFC(MMtSc^l5M%^k>)L?0aI}@5SZ`j}(K$R1^{&`!smxUO=|Kx3{adgkc6R z>7BuQr%xr@m*O^Ot^WbA56p*O$C(tX(~llf#<07M}=nvXv;p7$L!RZOBM*UK>gmpO_o_**Hy) zE*@9k@a206^2VrcTWEOXBJz@=2f@p%myN@TKLK1&@IY+kK37U_>n2(3?{=CTB}&gi zb=xdFxs$EC_xn}@o*+(bXB5}pydrXAZ7m95ZX~#}q>zCQv;-`X1#eHdlL;14>tT zlowj-VGCCjCg$aP8R#8ka|28u1n}H@Jz|-5O}(%IXNR~LEpS!qaz=faI3vJ?`>rEF zuogqIVK>Zu>b)MUR+@%4UGx(-(@N^5>Za_!)YcH_`lQXj5e*wfOC$G(kCjjkm<2G^rU6QC%Vd0fAKcmDqE>vylNKb)Qa3LY3= z|Iy@>!LLYClt4xXh^}7mQTUY8D2YGotmzR`-VM7(FWx{A1cg_g(v_k=;A0E#T=>(g z>pPCWO}9_W_ZO}=05{+K1x=#Wk3RK2SkV?t*4<(87q#}JY6uV2Bc_`A9z`;M*zw$q zdCowIax>x0li6K9y`SWe-??Y~J_Vx+z%me9tG%U> zx)ICJ{ObAG^{FGPZ7PmavH@`bwz(-L$jvu(B{BWUoyiL6?qquMcBypxq%E-vj3+g) zHVcvIV-lzzTtVR#GFray@dyx#OONzK ziUY5}hmA*=26MNRGCheenZH72{K$_tCEBKU>Jq+-MG6C(aIyocOMEou#<8Ta44;-qvT;ZyV= zyRJ9PaJ(wS82pN}S|tS<)$bE%B$(SYNL;`Hl8i`@QB4Ul(sGPpx6DJ7%hNB*Wy6U`Yzr2N%JX;SW_70gwySUmn{^vDgAYWYql9#ZV|Uk)8ZDc_nxMtH6i|C zVPN#CX5$n`yEU5TVYkx57vHxPL?e0=?Vj|Dr%d|Ak5BrAob;+P=~LAptGR80cTW?e zoIi?*wf{g&e2Tf-^igxSZFBi#29i;dh;PI{CaD)Hy@vjuB&bB@kEi_p1&b-c3wGID0qyKwG|M!gk?-~6crPYIX$Nt(| zv29kUDZ5h`P2v&YI7IsR9Q_l1+#Kv<7h7m)sqW4!;&MN3^GE%-K@9V{zBgC=xXl}T zzUs$quK01AulsSEKh2LDgbLa2$IZbOo~s_xwhs(DU8_e}a|u6g^lpRKGzclZlzz}$ z@Ppal%PamH%=P&x?oG;GH|Hk_M-dNxJ|9P>#JLmtw zvA$oXV}0*OvA%KpDh+rvkt*zp{}cuOMqn-lL}n0f+Yb9Xd`Rq1I89`H;n`^IarE64 znVjRxEAraH@2^PvN`GM?{Njpq&h<$V;n(cfFMWi+U6H#0zn#M)tM^xOageStik)ZN zOx}VXG!**>`M~^x@K}6%2KGobat_O%qX9dDeI*VUlhc92@zu&G9u@2U!iIN?VkP?S zB#xqNvM9=E*|&Vk!Ki=(YY0h1D%^CouH;#oxhcIG)v2!KL5=m)>R3&W7ASIp0GtT7 z#{BavBO1y`@S|XM5IDdd+H|m&dF2LXW`8Lqp3zVd1Vfdc6FEQN@T1&kkBLn$j2s~_ zbui3kgt{p-ak0iV#u0`LXx|;skL}mz?sVolluf^E24Quh8tmD<)F|(X*D_l>k_C5n z71`0Ob+w$h?zj2Oq~gu28-{>G=jVjKoR{zBFRuy=sn^4k^EdNe9A8O1+@i5Rpayd$ zb5-Cmj7^G{yfYt*-rbOVxY8YAA>)tT`DE~hWf3=ey>NF=ggJwFKSsQU{Ml%*C%Ouo zBRzt7jbs8Z8Cw3m)6d8kPjUFfrP?{*fB;nV1CT-|%=4SaCF5&8Io`RMKOyJO3}3Io zp2n1P9Ivd9czmw7sn}djLZ7_2D)8S>#9{ma^K_AXd=9VD$?3AMTX-2_=?h@i80j-) z4)rN}ViY?;h~|3zakeF%{qO~w$DWiLb2Fk7b085RUVmgAO-Z96w3^8WN#^=$$dVx* zlOZ`>QpGrXp!^8uOL_!J*hT&#+O+1%x4Gp1B&Hh;d7k#X%@$@a{@w zS=S$tPuM*T`svL3@G@pNpG@w>gY3bUJ~f2h=s}x4C@AkCT^JBN_YA@a@8_~kb5j73($|%iX$Q(k=Iwz zBfe#!PX zNAzH(cn;?_z>8^rG9OT%hcxnl{{ki~cK^#r-n*+5$z!`YN$(`J`%}86iq4wPf9@}7 zG}iF*pV9I;BNl?3p$;zv3le_*^G_rrISeS#ny=q|I(zf_;zRrWZ@+6Q(W7A~+?c$* zdJ=E~2U8G0uN;8&zR%ykd)GX_eEt5Nc!w(hV6qe%hP?Z)PwW2mU_vtj?ZG8ce@qA9 zxN9c_uP@#-clF)c**B1H9?C_NQiccutqu=IoE-U+1%8hg!jlV;`4b{skw3T1SF~W5 zkI+ei-fnh2ynok8fzQ{@lUI4BRgZ`B@u|aMvB|-2*Y^(xru-@JU$)QQbzU~xPlKP` z^v=mr>;SemKM1sELowf7;pBr%cg~;8m~XEzAV(tY>lKzumPl`1VLA3h`p2tKkoZ;* z%wp1DzPbuYXM$O>LNd&&tB{;e@G?b8%>DC_>`(CWP5a|H;Hdd<6_T$u-Z8Sju0rzi z9DlmvQGu@X<tKIC7=6-hmW&s=!{ zrVa#X_I2@Wvp}8O*ll5e!IxkUTo+OTF8?4J;6RK%kYDPBM(G0<{Snxpp+Q&-=JU~wD2P#-f(|f>uXhVM64ja34bQ+G3-y9#U+9`>_0p)_)4>KY8_^?D|h`J?R~C z8Op1KAlbg@iED%tEt?OFiyIQ}0bJ~Hvg``7{1cUzo}U8ZVMprae)6JKmK6LaTV9EN za*6+-JBaxf;HN^$rQ}auV>$OGruYcWWf=bCOW%?rRp4Hv42Y@ocpD#wjQBVa@%cDp5`9MovJ{h1 zPkQ3^xuj2o@aZJDZ=IqB1YR*Aq)Qw=ftkJO9$qT2CGIlja%fHMi5w&?@Xs)2qCXDd zMxcX$6}$Y#`O`#)se+4~Z&EC8N?vrCazYVv?(D|(f^*jiJa;mo9$O_xa=I1?9@N{0 zgN27Gd*oOX(=YHJp>Pk#d|$+*_v^f{`GD4AHo}Bod~s0-S`H8m%{i5x7l3$z5RU=L z{q>1!@H|9y5$ni8jIVZ?&oNir!^ADN;IVkf5Tw?EHgxGV%R&OsC<9yaLg8+@wR_^q z+-MNF5Ilk31h7Pj$i0c}I?*C;MhGtoJ$6e^C?ubG0vJ-u| zzMejgmb+^eRLDX8>oc_U^LpH9ek@({Iz)FH^uhes6^bf)sP4eR|4QlXY+~OE6Vq?< z6DL34`n2zZb*18AC0Rm=vaybI5kmDtGRG6*blrx=@2;^Y8Z8nDz(=p~M-wnURZdgn z263avjL?Dl?&MZEeZOmBCzy3AZ>vfN9RJ4=`j5NCUWj@jo=XZE6zsu-X0wjEpVTaN zabG%-I6~~8G$XrZ@4$`N39Uh7SR0F)Kvb4MGN%DBh=L&G^95LH5^Dfrk~4V}9H4>r~wOhqhz;V=l5-rkp$}G8-`yo7i_vyGf@} zTVRd+$FdW>kXrUo-dv(9$NYg`Io6n(eu}A?q?wv=f`|F7$#|Gwn&e*{Jm?ea0YC5~ zZ2q8JQiczVhh(~gNa7ox)()J*eh3^4C-hY4ZzM-t-*8oM$@n2=OqIq$prUYk_8)W~ zMdD>VClh|`TR15OGdHrV{M>(V*||TD&ix0Mo%{1R1U*1D=tD5~xCk7-a|rF!4;eEz zcwj6kXYz&*nEx2`Fl!j>^-=6;P~b*%>v8E3>Qp>7QZzM>`O=Z z!Hs^(k}@Hb*pQh$5N&FGqxm5X`s4hV`R&p0=z(ZPrBE!CG(3GE+WVR9*scTONSuMD zY-fZs9u2&EkXW$F86K8d!}kwFo7gu8EPzkptFPgU2hxQwLI)aVIa&`lAIK77Oq*I@ z%``l7{eXWuvuVbgJDG_MZBZZonm_Zu8SVM=gXe|iS0DGk`Z)0M^sA4*`gb_+??UZo z%nCg}})dm9q*pk1&`JfE1-GT_w)TR?SIp-?^rVdXEL9;JJ!q% ziO{g?^{E!(TNo**akP;gXc*XF_?6>0c5pDVgYo{^R8 zArjHB#tLd!n^MckjBIZL5>Bx|;ui(E=KKauxw+S;%mw$zvx9rBhZhe-du0c$`Az1v z(=TZF`hjS#JLeicdmu~g|2ea1e_|hSg`cT?DKk?iKud<0+85)vP%Icio{(p$d7n-# zc<$jtjY`3Q{Hg8Opa?RxFNYkhG?~N)%geIXm7K}kXE{cpSg5KwGkZoSwnKSlsZaq| zDI+(qt-$q6?MtyxE0nopqo1F+z$kpFisr@HooDC^86`dj-}h{2uEffPxT8)1N0~fV z;j6950xAw@rdBDG$_y&E{|y=~b09VFY%bj4^Hxf1`oKZ>!14z9TmHlt@{S8-n5Y1d z?~73rP@O-JC3vKRw4;J?SSV&ztIR1W6$@2X;N=5Z_T7o=nFb!(6Y2wg`oY0-wiPvo z)A>FGRJBG_1MR$IYidQTIrsg%<&8A6tL5|bjtQ>;?X6iJwt+u(1OMRpbFCNReVCtv zgDn$qIkx=_^q;YPa32b<*CzLoBlw&9%%+NkOmkoder9UjgZ31l1PhAb|1;+~X<;U*n@y>hrgm9gE z2dBq7@BtFr_e^BFRD?e#RbL{hUKIZJ@Mjo;>zBG1&_-{ z`R?9)py290vbsig-(Uku0qRJqSAly6Q80&0QK^;blWkX+7V4=8VP2Fqp_dW~2;n4K zB4M5F0|DGJjlM&9|$uF*GhyeB#|)U zkc2C12r+iP93AfO9q%0NoE&{KT1geI&=GZ60gIQz9#QFf12j;eZu7ERwf~1 zO7 zvEw1W?}HA(1jBlTFgOL`MsmG^Ov{oY?9Aobdam5Zq{=s>8rktE>Vx9lkv0%0WtNsK zoXZaHfNTe9z2GFOwadU}MRIo$;lc|&hMbCZE68X(m0C1cbS4lHvqt6wgi!y11XU~= zT;d`10^5}rAXl4drz3#Vp(&akSxinIEG!j8=piDu^r$N@+Wdx(e7T5ui8$L)MpCQo z562wxay#$ycP=+{7wq)(N%Pk&q4Hy)=|BM_XERelC(MoBn9IPPu{E47v5XJEiitCC zEi0WNF{R3!DRcgc{QAc6Nn*4*1qmWVlo zEUnA4X0t}ErY8EZVk#N$${C@~um0DhCv=HX9E4OT!S!Y}Rswc^HY;{vZPsgbyk2{waS^#*Gme5Q z)aBWT17>nT1ZS=95W+suxyu}& zXpE-ZZ7aOVtxJ9Y`FCNQ4-GXYHP?E^ua4$;!2H&&WEGV=d97S3^!{)(v`2-x?T`#N zl(cb@(-LGUF4zbA)C&X1MZ%`f1`GX#!6g9b${^L_jHWb>wHy@Z3RSR3k4o0+s zA6BN@W_GI$S%2{jcFD0{og{HJ;yiHvq$~U=?`IH1xJ&r^XgPoH=K1?*fxjPCLF{X~ zP*ezUO`&Cl7jHPKCVn&=^<#6We=KesN}ExpzDvdwjJL5&qVVP&rXMZ16wgWvJ^37+ zF!CcB#!s&c;8H&y-trj-(!lJdo_k=iv@bMwS|2;+*Ys|6%h&Youe3D|n7Ek3hw&FG zN?b$#LLHz6xr=mQ&=^y&cYG+`*C2$&%@t|amTDF*Ed5TJJGS{7cYZLBUUexF=$+SM zq$Rw@$CIl(b>tuBTsuVYay`*cwgU%xK;?n1 zUfsPP4VZB85eVj2Y=%)E#&jG2_RUoii4YN!ooj)>_|kYV0u^1r%LGh&2PZopj`mKz z8Ksg=go&HvKRGUJYW*G@?i?`j(z2l-ITjZ#5=A>MUQOyU3i4RnL)g)R@ThDJ0WOUB9xlwZyYm-o4?zbEIl_C|U=Ft>nF)cyZNW<1mcZ>GslMQOBwsjB=jpVZt(hkL z$FSRDEmej#Sdgr0Zhn=YXzUs2G5RYgF?tRf^q(j-Q0;PaKO;a+?kzkxZ+?zhr%!CRGq-rL!wNs{_qIGyOd8Ed*gj#h? zDoA4-GL@-E4J2f!wzAckA1ig)%k_+_3}Jo|Jmk$d&qRoKipPPO<1!LdDjf&r6TAqN z%GK*8J@R;+Zqi#Az)HbThOQTehduI~@!_E=Hg`9uOD#c7P+53?Ybv-nER`H-F1(@( zXllM|a*obO zCb9u8*Pq^#dnwmH2x4Y8C$Yv<53hD%veqv zg&zpCE)R039ZBAuL^`2_7oSo$I?|8~n0l!v5vplsStDAX%~sERHY@tAP;Im1t1-oX zOP~+S7>*zo;lkk3=1E7p?DIR4&6M3nVuW8NIqHBl0=xg|o}6^V?(c%;Nj&K3W2xi$ zeve1dkyHqu-!m=GSsxTRe=rr!=!Sf{$A2>Lpi@mx0RV7*Z)(gx%!D@tDlFaTn5o%f|Cvrl_Aj0@&J7}#M9MaatNp<9f?spJkL}yh zI2jkzDC7%5JI-}Fwr`irdcS?H_oyDw;CQ-Bvjn#z7~IdvOtPgB8ke&^)3`)nfzY0NE zum>TfE&%$-FM!<x_72|T0v*Le z8bTBFmMIBgq$5~H>Q)QKl;(y24=>?c_?>VHQgsRq48WeshhsFMAv;ll9~ErhEqJ5x zC}waMEzS#eW=*>r1VnPEQxDphPdoVmgn)u;mwGg&rom09M)wlF7fU!na6O_1LfF&< zXzR6gFtVAm1t^sS^W4yw=Zs-yx-(IQ3(}Kh^FkG45s9d z&)A-laEd3+f}vP21vMS#Y~FQD-GPriHGMFRZ%!QP=_X?JVZ%c*VcRS0OJX1|n8^fx z78>K2+o!8pgSnP4UzeOlG@HdZA?lr3kkfBuls9C_k5t)#g-oicK7DPBsw-{ekO+X} zr_KAQ$~|~RWa4OF)ogvqXsTz3r6ztZRjfLB*U6{y4It5!J4Tz5EEj)dtGCx7A8GaT z&hE+OyQ6kzht%=Ffuy6o4-h5|Ul39^@R(Ka9DY7LAg}N{@wLC- zCY$&RYv$zWbcejgpV`N5`}6S*d4mtw*TL!M&*UxMW3`S?j`j{dka`X8i_HCZJ4d8m z$M1Ze-#$7a_1E}khkqV^A@w)dzmc8P$@)5u)MtaNZ{R?Eu9Njw*uGJNsFRJiILW>n zzm1&l1+j zn{|8}W)rWIx3BTwCQE3Lw{P%ROIRmw-(qK)C2Wv-t%f}<iABpv`Ol<27Vtw z%31sK=?J2=Y!fuVHq`r>trsOUDG9a59FYC3&M&^>^EYVzS$uPv87dS2b!;BHb zz|u)0N>u~jmNWo<4qDppN2(8f8Bc?IRV44Y)j^f{MiLIWgqI*Dk6Dr=VyT}c<`)@3 zN6!!e-$a$fA2DI2-pp_`7#=5K7z|(pdJfo2=-=b{s!?n^*QDFSTD0Bw;)O57`iU+> z3Z~OA@|vDJ@-HJm6uv`c23XKtf|Ywj9vaqJVm1(7hrjCxZ@}LT`OqogQ>xU{p*E39Fk$fx$raLaFo0SkpnNs6+%j1xnk}f2#QitMBapl#tGl9b zx!fc1!oc4peUkTlh=d7`3^mcz96JaXQ!oRb7;1i}!mU~>t5%`q(DF%Gp_ZPAu)oFK zDb`j_tL$(8ja9<6RqZ{#epO(R%!LxG>aZnRIdbB&6=vZr^6-$6L78`qm@u5l<(TZ0 zic=ZSpGqm56$gQkcRm+ek+>W7bW~6-W5};J){=t2ZO>)bYq{)UK6_mZK-@5f@zfc6~OZ+l`Hl+&(P1#iVPp)05rGn*wZ_}O$)p)D99B&P7G`yuR z^O7ze!{BdMjcho5wBE++74`a$*L(YBMLqZNdg{7cs5p6|qPz$fDh{5ixN1emPgUGp zQSt7niegbE$7bZO)A7yU1eWoQu#Q(L&U8M-TS&Rffmn8b1CHP12=p~vz1wd$*#~Tq zGEo&%C_m$ZFPC{|j2^c&=g(R@xV|YI?>@=RY7w;UZSrb!y#c?rUcFx5*br|H<}uy3 zWI6A&&^ES_v~pX@7B~){^43Wj;Em|K{`XZ){H=?>4e@tf{M`_LUx~k);_qwGj`*(E zL<5_m;SJHi#w#m#V?B>#op%SW-+&@c1dkuWxXL4XGrEz%K*Ti#bWI{Dsic{^w6t>S zDqkiu3M&=y#$oxo`|OOv`sx@@WJiA&-$eOVU*}p4{%)&5uGLz7DOn+}{4X6`tj*o@ z>wmLjTkrU7em2S9olTOT&6`}S&fjg-$*=sYT&v#SZPoiLEC0EJi}ksc-}svy4<7@h z{@AUPXK@ z-U20hFTunV!~_?)Q&DQ!dqgJwrtFUv%}BBykStyWDM>gV5fob#ltTGT;Ku;Nkucz& z!5~0Yz&DwF=9+k9_a!t&0vO*%3>XO^d(LbIfA>55?-%j+v-o?^LD^Esv9XV?T#p{1 zdMGEVodD=SG{80wX{wBtw!}+YeMq%kCNhH4yaIwx=~G|ZkNYGyg6qFMf^JVAfz)Zh z2z2eoG|B5aa-9zxCA1uKlkodQ42Qwl!imn#DA&L1jeVZXH#YqnjXkS39a=v1GiX8C zDbQy}>5`#AZC51sn61Nt_DPq}V(td}Ha_Cn<7tZM0_fs;IUQBnUCj;U0%sutTZ2HQ*}aK3ceQ z*i^*U*}0LnA&a_l>X)m#{t~ebdiu^U6mS%l^xAy@(%oMB)#Bs zf1jd)ET5|`Hzw4ohrD|F+r-o&j|h*UEn)xe@*~W2c)2Hr44!ha8~O7j_Kf6)%><>t z-7Rwyo4s%SEfTI_csf@n`ho=Gi^+8eynyl}Bz++ZhW9sTHsg*;**L*Vr^qK+G$l9% zY5H4#akAQY5<;B*F$`6zzt=J@w73E{krX zz69aL4dgUAg`B@9=O2~JX_~p&9nJ5^s434z9&)*{96VNo%HSL)KQB260Y>F=Q@#EX z<#=R?I>#;v*-qFr;c{{8*zAUkkmNcj;fZ!`XhBQ-H9>r6Q6|>O7j;O`5^PVBT1hh~ zmH5^Z);z-c32;!FhLT+8R;dN?Ek?8WT!>8tCE9YQyfJo!mj<;!l<4+MRA+pPB4-o{ z`=&WMV*zVqG4PCz?HAzvMS+nx1-OL^EJ_UT7mSuUaZ(uhlO#4!{`vE7o>&ISlJvdc z*T^5FR5@pnD6EF5z%JQbn|XAVflYQPDhw3nJLcCbggRIPUq17bu4VLtAcl-cZXS#e zN&g~1jm*&>*~9zyH?kO8BH= z4H&(KcVcA4A`H!82H!V%-!8>^nl@u)X*$SE@doA%zRZGA6xz%L)&)D)%M`yOee;I9 z8#*QJYDfoQGk-Umx$1ZG-E3y+nWS9>i}q4!$K=0o8_i~Vzc1F-Sndz3PgR1{Ptq2~(z6-2rasZ!@Z){rn>VY{ePCVpO1ZI$>QgNY$QfWy)xkdvgrg23* zhiz8}_anw0-!Jt@WI(ooJ#`0vVuY$!{ct~;G<~W+F25$*pYllRnz;cWKIN~YKM8Y@ zz1hrU$+966v4iYWbpt2>*d;nxzy`Fd(}ae1*?`0*h;)sXO*q4+YFOe&Q#WS(gw&gY zYqI_Dse9ei^g_GUh}xku{9=dpn7;+S#!v0D9voQR=F1K1kwB)3%h zNrYdSQps$Ch!XrL!}Z#(PD09c?F&ghPpQd8x#bTO3ZIm<`!$Ko8|mM@U1fi<=Io`d zdw79^VSN(r&EK1Cgw<<0t1Z2n+w3QHXc$}jRrmuZ9{wsb`g_gaOIv#n3X;WrSY4b%(r<+qc0+a5szSm znU6@T&3#3-#9!-yJIRDjnLo-S{)ziGFmHP5-7ZT4C)&c}e=c`_|0SH z#Q>U4e0==bYFE{7vTiLu=qf>L-Rq+J_$$G4ZI1pK46J}ty6gVAF}8N z)(P{LZ0*CNmdI+S+FdVCO2Fc$jVF1Q_*!_r;lTBQKXCk@yAc)fBK~Ag&i@qeSGDF$ z_0kvUnLot?zYi+5B*193T5aPXo>+gTC=~@#WL>ML7W0uo04=bExJ9YD@ZfyG#S6~m zR2pDRGRbbj3EYJ0mS^{(XsS)N+pM^W^hO~?c;g`Bter8`#BJ?PeV5&-?G&GqK%kmS z{0>N3z}QHi^*hn;PqO`!dpXh&=Ly+9%_GheggDAPMbaJ7&xJxi5yGe1;3p&J`bgf` zJK5-W6g_Z5YR3x(}yLtQEdL2B78X|eu!^w$mO&;494*A*W~ruQx?TAW3=Lo3`0vph3`l`oGln5 zTo?r4Y*;ps{!klubJL17i2g6LMPAG<)@BzMXbs=oAS;$(JU(_{tnDcu)oF)3h`B(Z zHGDA0eG7r(@pL|hs;)ZYOqfq^Whi+On|sRrgXXG=nliWu^`{9CR_b1n`Z%T8Z*x$o8LD5fC32$f< z-HoW{7O2vAQZ`=USz>oeI#U4zFQ#O7ijNdX7EP0@4yq&W>5VB7xL%{aRzsyyyj?>} z!PHP-3V~^zBn+wV-2H%;(O0x!7r4rT^Y?Z3fH;{LSpZLH?8TH}`H#)td8>_to=Twn+_nKe_E;f%2;)z>Nmu{Z$EsvmVU3pm8!v7!J3XB4z0m5SxY#okH{>M7DKDf8h2iFca+Y%nVfv zOwU8M3m4qr&eX;awiAzTXa`;qTM+bt#rX+S45nOQf0)nU!%BHbpg&<$mJmRR_*q#5 zAP8>9VCZ&AjwB3S-H_Xt$JtVb;#cH1~p;ip7i(J`_2HSg{UtEbp#hM1CA37#w(Gryu0>9IG<6ag*h=HO2*Q&48Fua|Ic;knmOG5~I!W=Lt z;Jms4e=}JfaDvqZcWON*A+*VM*T%kiGQ~Egs0ZE|Se0C}tvYn2L!&;YNVXK1hf`*9 zm~)0?>vZmi4pNZ?*xD*TM|{)vrsSR6w67g3qjOuBk3QMw%uN8o z@tyBj5(?l$6EXYD?FQ0kx?+wHm|2@2QXa{rQ)C9Q2UC*G5%15_BwUoznSq#P>%*I#^%+#IPI6gAFUIdL#O>`Jid3(yWs7doA1;`^GQD>S?P z9(Iw{Pv^8wu4rT^;_d-3C)^p8!8&sjEJZS~sy?^!i^JBs2cC&++tsdV)JA&hwlGDx{ z|MbNHp5%0j1UYf&%^QG#S5!{$Uc!|S5t^yGFE2rML_$4f5(YOT=BDD;jp@rE=n>f# zF*TGlJj#UW(vto15-za8;OOMLmXjlN{Z5#GMB2#92x6E zV}0Ea=MlQX7L~`O?q#@N*v;@Itp?yui1`7_yL~*Sv5FHP734^@RY%I-teQY8n@WhR zuTxV5BZ2waIDz>FC}fc=ioPDy!ggKLMwpKqN&-bH&K%rdImH+gr!sTaLoYXR9aQkI z8*^dA@)%Qu@OcLUdPw30cALKz=e7c|Y_m&yO&`<)fI+kf1uUF2Tb5i8pnWDErY40z z+mVvnBmPW4`W(<6izqiPs%%T@aAZzSB&t%G>k0W`McGt7Ou4NnW4AJQdGiq83d#2l zgqlTGNN6|i;ZV?B=N%TAl(#Y$R*6L|f*Xr@+T%*|8g4L!!sR6-0?l{mWzG^>;F`dS ztNe7O8o~HShj*b8LfC8nk-Y|2eSGI6p3SBW`<E#Xiqhmg#_<3;l5BGOwGe68^N!S2!n1NEv<4YdcV;W_RFzHq37`#~MfOB{1-+T!vL^V~#*8c5J z#+ST>%e7I;E)A6n{9lfUO!2@Au56E2$QP8uKAXbeXbgSxuEB$JM8o+?e`TJml@w)g zF%w5HVoMQ|o^Gh4-f zqf1a8R}9qjyMrFl2GQ;J(#(_QC4eIiNLbC*XEuv+U%vK*Z4#79=Y1eC5)xMR&47CO zZ$I~q9g?uRj9U5Pa4LeauM&DN;~GNPYJtXvu%2G2mC+l*H8Tv8mu3B0L-BQ$5gs10?8VdoSs!wf&|LmwY5Jf4$RRJnOauZj_BVRbBG%0GyCWKeqxe7jsTU8N^wN-@( zx>A7T&t^BV7DEV#I*m}6n?XyUBqo#r8m&riCij^SPY_9yW(mh(6WT-plB_MaWdV3XQmIbv0iuxG7KC=K<%v{30g zYi%Km*HO1AyF>X5!34ME1EE)}$vg3UDrrZv3tmEu+04L3sbpYd3;rhtHn!n^0?M~P zm~1*6!6h37V+xCQK4o-aSOuXc2*Vk7hA?eF*|=0Pi#KewNA8jB4=591f3AVs8T`)E z<$mnOV0OrgP|X>h-1u&l1-=C-o99(->SfTjHVacyuhwv3-tq-9YSlNm@N(^?fo-zH zG$tY6`#G&Ai^_@}l2_LGnX+y+`@u9}EDAIC6CJI+c(`=3FukyiYcC$;k(BA+?AA%L z0MS_$o-w%|R7S30{ZJ}-CS1cj2&3g!`-$i63zyg}+X_HXq?}i#m>YnDmCG_`_Yg)E zM8RK2@z6BZ4Ae@jA1@w~bnORfLfk)(8BhaEm&uQ{7Y`6qRLSjuiS?D$hJ%692JIE# zYZj_jADhI3-}_uY?p22&?L)BGhY%P-1A-?N%$z)WO-xqN(*2K|D|UdCsBG6%q|^x3_SbyB2{GE-;ifvj9dd8)skiu=Z8IE+V0 zg`iG58?_AsF3a4k`K9B^+V4C8)qL&rqJS|#j+o+~g9U$=501?QwuvB&l6hm{`P)&u z$0?jW{ILU(VC9{OJz`Ygznz2kg$@L3-rei8Pj*@%Z z<7BlCueklLOX zO>BqGWdcZoEx~+pFyT~vJ~^hh`q16*E==^!#eKk5MoVV42>EY12!q6TWytef-q?;O zwsUO{sK=t#XL*V5YM+Bq9NUA%73L)BLP8YZdHN_ueWo+<^^x+mS{#);i7^C?UrhfM zgx73!nHW#cW+;Ase*sHqpB^39&fQhwyWYqjEM&mV=+cSa05qt72Dxg&NJLK=k78I$ zIFS-9i^HbRX8EdXsdJFRs8R3&tfw(Ulf@y3bblc^7%*eFED5l}Tt^|jN=BZ`mPoH1 z**Ei_OUvXB1*FhhI|rhW#{B{3AP16R{pXXyKwr0L7>@kwU!gtG6GsDoFpBxvT}+Q{ zearZfS+dhSBZ~Av#iMZ^PK3`AJ%!!qeh`CxOCNm#58Slx#nU5F^g$@_j(EFsSRwJb z7EO9E#LVJPniVdl-U-vfB{^edA-FVGSs2dcE~I+FAR61ZS%Qw!Wg53qWiH3(6IoOS zpTVWdh=NJrdHs;Pp}Na*=6eVGfduZHq*O}aqV3yW1SYTSqh{a$vXvj21kGkOdENd2 zeyycTZ~0zE2*I4psHf#vemBV^1Ql*so);_t{jYYa1<{6|TS>a244{sq*|5KOFuVUw zdu5caVbjHZO_;+a5ePqYlO9;|hjaP{{cYVtci6MM)Y7stNh1&%L(|hA$;n<-WHzLah(4fH_Z~sT8I7U_wqKGZyL=_7H~a4JlEq^KzcN^2R{V|uWMaA~MlW*kW&g5sc(A+o;qvr&=ZOCV+gG6m5O&JNy3zvt z956v@itxt)Ie@3Yii2}(T^h@DirjpjuR|==zY8jIWpa}}oiSd3#Rj*MON}agiAw=w zJgpyvlOYXh!3h%21;d<47+iUD9A&W;=BlIx@lpL@83qKsk6Xr`AH~#m3n6vr2rj0A zQI0L+NPHIPcoN^KV6jyLh&3gS6(N-6?DxDpFQzy#t^{O6ZP$>f&gk(_h_H{$3;M^w zJfX{(6nS^T+fKpUWQjN$c|c9D=brEcis`-r$sI*mDfcP<@H)jB!Guxd3ByGK@h}vLmAe;Ll zm@$G^&t2NXHt7UrfLdx!R=@-__M(vpyHZXkgV1)V`^9#ylQH#Uh^jG$E5yZxt0XS? z0sAb7<_f>;s1PL+77v4kzj*OL(`sSgzNUpJ3E|P99T#lRE4Z4NqX5x7c(c`l;#3np zSlB?gTw2wx-J8u;MVJIWpRtw{8xZ%S65E5wupYWm6+TbwLB_21EIXBCy7k~Qs=-Lv z6=pqj?VgpSiYBl#THqmt10YtALpOdIS&bDwwNxFfpUqO!{MEMQq}OoLOrUEK-boJBcWCFfnbO#B`sfl8y{Q$Wk7PyEm{K!3Xm$)y?k_Qi9!RwlzUSgcI2K%fhWtuEU){s4GDhriXcC@9tz z3Sp1@In^*CLx2xUs1+y0b7(Vo1Wnq2Cb?#=nHkx+#9zETnnRs_;{Ghy>+MYTp4~qR z6n#x2)9UuDu91BkSSP|*VO^)tHN;o0oYn2INV6>fLd(S-$_Y4q{;;i7Dwm@zBEFP? z6o5{`K?esiYNsoKZ75AZeYk$Y27ZWWII_LbJq<0htDp{GuBR@S2knBexW#o!e8tk` zAFZ-R7$9LsHk2;H__dcs0D52&#x!$bv08c80{wW^8M}rBzkt_duqE`it*%k2@SkiQ z*(O!FtgqGmQK|5sgwgBlXZnMcGb$CXYJhiMIY%ap!~(-u{1C-1pb0w_U@{6>FrZM2 zF>cRO>Z1l|npjvo-r=g_rSg?||MOR&nQ=jU!^B8^ZKM>lcfbA7z`(|U#s)TchX&Yq z6JbSa`q?h9z+TK_&}`;2b&@?(g17X$w&7bG$;I!6Q=}avfNnL>? zbMuPSfW2Lls)NrqAbe>rZ!7I^H44Jm+^?ak9h;Rpn$6gSnBvpH@{nm=<7PyE8Tbb% z!~O-V!4GWA>pX!E6##h+ws>IU6H*x6A$MT}ynhxRwdL3l-dHf^R+kX~@}TxOEou}D zuv38NS6{q^Wz3$J2##2yTa6+11w#z2FQX#D)eJC1Wy^Rz=&&J&kc&`8vq^Ui50HJO z;?hYxBsHVQ1|h+!^&o`%I;A2pL+e2(Eo6cm#^NH1Mu8w_1}f~QWZQ8_92;U!%aw&g zn}O>xbR%$$kruqDz;^)Rbc#V61%-5>%#RdRbskWY{e@V#ureRIe$R?BHkpTbhnSHH zl2e3Iy#LhU$eoSsde#pI*Z{Svx!hK@8!q7@x6r4lsniYxl&?SZYWRAW-}SnW`9G>mjuc?2I-O1aX)rxJM9gYph{GjT6Cfs)HF^r-K}@vo zn_{_GLmt_-yB_$wVDEsKH!p6_;vr>wqgH+BnF8fWn^|VQwe5B5J&x+kt_Q@8C$U?a z;(hOb6bJvTna@PS%tew!!w)674T8Z76} z<^+yUjoTsH%9vvTY?g>1!K@b3GueDUr1YMe%N`NlnGeibISwu_Io_5Qo7d{6up=Ec z)Op;u@p4(*J{)6 z?Ryx6aJx%;v*HH0!X)q@dQ^Ajwkj2ixkS zRFs@O=H16<*+VFG|H=C}ukLtvdT_aSaD3AK{CPIhtOrd+lJoYv2JkUP1?cjtk#F=6 zM0^MO9a+*1{n;|QLRkgVl&p9}%Y9L>r>y*THI3@p$am>&<(Db&AVEWWmG_~7so!jB$VM%_s9V|0v9D2NofJRD#spY4CC{zi*<;TTG3q5QPnC_Sb~>0v zx|B4BnUq;P5djugJ_Y?im9EqDm7X*Afbbzr8K3&&AEzv=J&dZ5Ntp$sYJ+14KV2fm zD+J`{z*O2*aJQ;}j5Qo+RdA3Z9L*n9n6f-LO34aBfoJjglZ?@yFnPzqITfruXDbFq z#vs8$YVyD^cRISYUXDiR1Y4k#9+sVQDm?&=3J~I-d{ABSxk=3mefI04w{R~+4RtSI|9Rq~ zg$0}w7#WgrdF)ciT`S9P6%g93lo0uPG;DE`xc@yw5;a!S6~WnGL?rf^L?p=>Mz2v?GW~t2WJYE3`^YaCzn8W2^6y6BcZ8!dfdC$|2AKJk>)`b&OLGD*8T%|18Mx>J z6VEate>9oUnD_AGAOHAK&SxMTlnoGHA}i1#mCa20Y8Sqs#XDOV88N=K_doC4g55co z3-BC1Rq4nFx*_7hsu1Q99vX~AnOWfozV)bs_yzCqmKNALvokb_AW&{UzCTy zVK~U-9jaV}wW4}ogywQ2cFElN`*}}5YzGZyYQqQbqZI$qClvqDXAt*iA9pl}o1Afo z>nMo(v)}9h#POggfQ_2k7BK|}5RM!KJpjQW{Ns{+aC_eO0`T;()?JOG7b`Nuzgl(_@1 z`5~|Y=-smp%TMfsCP<`Jo5|Ik;eHqaH@fizh)30azI> zWH{)-?us37P>&=jVot#{B2HR(*u-XxnBJGYyf5*9%B8o5Kv)t&Ds|1&FE)Qa6|@gG z`v}^FtJH#0DvT zFD(ForWZP3)}C2G9Ri7+`_zju*e5psJDU|%z{w0?iErAu1bL~3YOB?%KepwN4;!iq zi;Y(v!I%N((j8aFm_(Sm?99h5exaTDpux}l3w7pWxA|h>%mZp@YNQ5(aUQrcb0QIR z?d2zi6I1fWYbJ|@hS)_9Lk5e4IPkYr8gh7qZ}usN~{6D1=@h0ecSV{Z0DLKL(`V5)9qz-x;<5=+so>7dua-Eqh`-;dg#I9 zgKPnT1V6eSn@3Mj5eSR)s%%JRAS;k%yB@4Xj|8faO!2^Lj%kw`PKX^2nCxFtc};sO zY1+UqsvLc593{R8a(F9GY;$hT3=|Ya8mTT=J+*%tG%&KNIq#Z}N@~i^B4^C=-^d}3 z^p~I&v%y_)zG!tchF8TSc-B0hMQ=qGLRq;|$xKM18qew@oYjCpBiabGmYO0EOLjVq zV`oUmwpfUQT<1bGlSG2(AZ(B%*@JM>X;X9_1tD(h~&o)Yu;R5x6c5{EC)W<-*@# z)eCI*lQ^65H>kM9C7mAze|IsG?eQ|)x#7rX4=?00sBy~WsuuQS7Qcd6aFl)_N}QpX z$0#cJLClQWgD|G98Y4SnD@Vd|pF4Y}n9? z?A+=_WRtlfFtvSqLg1PEe9KE(LfA`j40mA6@4(Q-N7@}|i#za0-GQOoJj(7scKN9s zB6bPhM1mru8iWJfsMQ))6o&?W0K9D}GF6d4mVyDzFNUB;TkKr}*z)l4ja+=IjV}@7 zyH(>Gxy{?<;~PCTK03|Dr>N)Wju37+ghzaYzKc(^5jtXoCu)Sg+dNr5LjREwj_rvW zp(N^`J-`R%pV#LDj9h%E4bT$Z03sOn0mD>#pc>-+ z1TKE3^)nRxyi@%IZu8yTLg*4FRyq=b?aciOr(4v0F53d(ZK(vsjsG%cGx**BK_q_$ z>4Cp7_WMBpefs&cfpj@h<~uAW8uwu|n)5Z5t7fr!(^$vza4$Fount$-7h*-bTRv74 zxb@)Je&CP5_IrV^F#gL(u?H_cA^iN3eAau4lgVUd5w-#;cI+|rRu$o`uvdFavW+yw zIwxQmYDh+kaKK@~cJZDD3w;3#dkPk8x4HLMVWIvAEU3L_QEJ3~pXk4jPInB%0p*n} z%pdrQ61%yN_UGfBJgnqF;_;4_gOa##7R9_=3?4DBcwK%`72fd&z&#~owjTjJcrERahun9 z`Sud#%~6Bcc(X;EQpwpOuU^k)o3G^8>)LGg_O<+~*BbiPF0KAb=OmVQpsTAKHg0Dr ztb0nLhKHnvU5KgF&xKhm*5R{Hm{`#40{~Tw*^K=t8_fL-ak}O5um_%O*fVz4->_UK zglK41v;rl0!7GEi4;_}v&Eb~U9G1%{>#7g5?Imxk&!5MaO6*77$cE;En^Y-?6Zw}$ z%9xP5jP#n5zEnBq?0(spbP5qfqn!Sj3Z|9UT~n@erp%nk1oeMQgft7sNov7eVZ zKC&}uf`f;o&NZ=~lBYoFB1lr0WWdX$pqJcx0LZC9Y>$~kUe!V<3OV%qh5 zT(4`DdLgn~U+BLKp&bDwM=9I9-OpM<4`VfxrU|f!i7a zPjm!+HS+CHyRow=IoqVxGHO>w*~w()4P+UWg*3IvDGr=ir$QE*3vyN8u;%8|wB}su zjK(V(%&Ne);32)WA1luLzs>bk-voWYDcnlj9|jEPi6YtP<}V!4JGgPlWR_})yA5H` zDAuU^1{;9`R`zn#-FgiK_>R%bO{QDl#Kw@`8jOEP_(kTp_=~Qn6&KkTb&)x4^UKnU z4D-M^M|7}rJ28!an=PwbDfe3N7lO9KKbO6i$ZS~`7ghPivguBb73IO*QER3Ol$U0+ zuZxSS*|J7{f3^qzAMBzTPx5Vlc3>aO4%!C@y)aO7*%et-cie1N40#ZA2hC_|-h*3l9-ZZ#q2M7yQ5=c2Ds`)ljdI0M$dTc%Z8)x`j@blCtB(A>(E{IW z^_H+5`Eq!?m$MzQRvDF8ya^v%w;b39mcTl@USLbS!>QP{e``xj*P1$J#~z>b5HQ^+ zIj%*K&?V3pfgdx*I*DIhkk9Tv!n2)02+L zhKodOjWU$QefayThX?R?vxk%J>mFx9CIo&fNBHZgIT5$t)_N07%0N6fC!{=4G-LNI zJP_MqqpS7sdr~%&ZoS@{&1wjbiMjJ~vsQkOUT)G2422ctJ|>khLimcT7_wYpgz&gj zx`NQa5Pj9W0@wTO7pjaAzFJ{hnfGOrs-v=6NCEHOqSzi!o{3~K1-Jzx*ANXDr1$yZ zbzS^ZJG}43;r*!&uj@8{u9h@qA=E=yxUDT?HVfw@e(qypTJV{%TJn-2?DBC=T>McR z=Z+ZXM>Wog+x)nIWPY^Yb>8pne)zce$EVNx2Zvw&JUTu(J^On8?Yn*DxU@eQj(%Qy zV?UTo!zfN}e%;>P*XoV+jaQqm-@Glak$+r-Kbm=)UuKZ8Ox(bZ9;rdVA1QWl3FR@J zAP(Qwu<3AyHX*JerfkN>wt<>MxM(KZn;VFAl5Ewo_PsLMlnd(p^9?XdX&!-OBd{~_ zu6q$)_@u{)JRs||f#E)>;Q^>vVT1{(HIuDjGbxu*^3M&wE`j8Ab(rI%bRwhd$fezSvuCR_!*uav6F5T<0stFlw3fyUzA@ z{Y`0ey@%CjBlo$feZfyK{5GAftv9F)uUj+f$o=O!9`e=tn=M;Vy`fdF<;rhpUt3#R zTbmoAZe5#IBUgGOS30Yvn^EZa?+G%^&XH-^4rSup$z$SN#%QiYE1`xaeT^0iG!z-7 z2v0cb8MydVLp?VF^_(iyGjN-y3#eydf9B95^H^mtfBJj9Jw#Q|s7ZBca{* zB#w!#HNVW4F5qc}wQx~Vb(TWU`t?e1CuXc5)=uLgxcR|ICN%B44E zitvDM`PjuDv@O39TmC_9`PglKczVkhH#B@)PseS>nI*9Zd&FkOkyB>o9grHfiTrK` zTXr*$JDiYVH|SxpPs!5Foy|;V;kAiqtw=~+6Qz3Hphvn6NIDbtr`KDuMgH<>b$kDj zX^AN<40*}el#C-)8?_oXCiW*BzdS1QSj&50E#JFjyqs|bt`Y-v+QXSH9jHJI_!z?p#lIvs`<{0>)fr{+fMcq5=~L32 zF31I$kzz>Txd-+M3=&6AqVj;$u}vB{A?w&78`uLTnKZCV)^VS#buVsfwaUe9?ajq) z?cK#~t#ff(+o@dK)^{&%U+-2fZr^NN+`ieYT-?6hy-0RRuNJIN{rk4Jh9_jLi!Ztty^Dv7 zw6})GWDVq?#aXd9D}uw}i%PFtL@(Axct>V+|DDwU?)IO+*V>hS@1c>-`0x9R%5Tk! zwTqVZuM)W^U%bGV7uDi_UA+AL!n{CGyN6!BSQ}}Khk5s0+Tczj(yp{>cR--jW;6D8 zJu7oFSLQ7*vaYEjFIN>2tu1M_Jy&I8gO_<*7k}49ore0MX*S8^{nK{{8ZnxjEkDVT7CPvWnA3WUW+ftxiUo0n_?we#{VIcYONpt z_#u8n*&C5jYqX4uzAUxTGA=@yQ-==f@A&6>qh%Dt&yCja#_#Os#_N``CPn}aUGU$p z-?WVX^?!==S1sdz{XZgovt{sez`x(r#P?g~r%26vU!V6D*?jeC{Z&gqBKZI72=$|R zhpc7E=3NEqYyzQ;%CBBE-fm)!|!{bG-qoH1WLXUb`0?uQq`{nNRU3up4x% z@oKZ_(Q+BxcRku8C#FYHt8Ue>PqT#VeC?Tc_)Nt69Ql(ZCU@M<9AYD|SA%Ejr*56j z4Hn6n_%WM_M}vX?)ee1lVt1DP)+_QL4&+Sy$PC-z6*@U%f3ieo_~_FR%8Q?RWAYvV zbG?XY16wrO<1Z0}S>Ljf!sJhml07!NlRO=NBOu<^keVEvPxap{a*Cd?$6un=Hqj{PB-<$G1$0y+EZb2Wj19kC|AI zi90qV%wL^qxs|(Y@ai4Qclp+ogywh74pZ|Cf7fu!t%Mv91oGi(0mgXdT?PZClJ>z7 zHrq#DlWT9T$phG-X4=U!{2jZLFT*(=P~4}@GwG5?J+-+t&#>VpDhd?Xv;LDAwDQ>L zqZ{zH;8Kl}6_xUZ3Optg7}?rR?A;XtpDMd+1L1SH>4ff1N_xxa#Tplnx|C_}&q;1r zBhRaVj{&M#aI+w;w6HM30VPV6ehxBIPGKoar1+%$Vf8LH^{r@#S)8;# zJhBt0!-Fo{}m;u_botT2wfe8-!e5u=%ZQUI3Hr3uh|G9cSV6L(9ZeV^#Vz`d&!@B_{(k=+LC~C6( z6akCfLKGySBaP`{i}XXy;9ci&zq3`U;Wj1ba#8zq3&ucUSqGGycl)%b;21NAGtasM z+9TiLU+hx$QPd4~0;*<*vx(s&G1o(izp`!5KZrR;Vw-t#MxHZJ;3GIk_z*_?RU7dk z-TF$Khit@iEp`uSPwpE8&|=s#wC{L)fZQt#c;+y2X#p0-P!G7zbsrt?MT2CC$fH!^ znM)*H%`CY5d~++CTG9+YCqkQZj__HgS33iTIG6`jS)~9$3yVFVe6{6j4(PJwn}?zs{Pon`R9Lo@d4Okp$)J!KQ(TUoEQvZQj^VrlCwQ%}T?x5HWi z;wYB7@ z{axpOYplI1iLcmZKRIdaQnrOUIp1UhYBZ6aTrNLnLhK;29hLI5@4}jt#h_$>khQt> z9Z0V`_Go?Nj9AnamchteM$>AdgA90eNcgTu`ema#L%3Kp&F{c0znab1-%V61!H;Fd z%k{@H8-Ov+%48j{>I|yZX0uE4J3`vUb4NM9z|K!AyLy4AKilSzB-4}=18}c!R;lal zTq?-sP@TWUFuTC;E`T8jDe)DY8Qw1L0C04kf`&`80}n#kza0ja;_n;&ccUS`*ZJRi zV}t+Llo@ZuUy;84TI7rGx0|z>$r3~h8?O+%SRNf%xeh}E;`e*0)CLI8>uLCW)vHM? zXAK(60bq9>V%n4x>&=ch>}*vCdG&_<{LACuur5u$_>%FstMu`vDy%7&>Pt?F*@^EkozxK(<$)B@Z>_KLlb<@w@ME#g*l9guo1CEsSe+Vv3Q)jV zUp(>?%Ezq@x-V}~a+JSI4{09Fe5Fl3^Rx}lrhpLVundlv*n#22D@PQq*hGYH7^IrH zJlLeplc-AKr@4Lw;6Si&301o^gQu$)ie(4#FxKQ4jI4wm`To=~1EjeA_W3ZJyKh}e z&b8?_pX?5CP-g>U{enu*yo2FtD_X3fc^~AAgF^VGb73%~~%IElY7M>1tNAm6OGzp-nd}?i(E2n{)kF+nC zt|wa0VCFrYhnXDD*``lHPaCe%^^Ly?KHo`h@0w3{h%!{!rbO$S9iH#=K$Po^KM`Wp z-Zr>0d0Cz1W5`3^6)?LfgObb(0+R}tO84D1?V;=@^(iFe#-r*0$~a{WLYyYf-}Ncr znOgIFyVg9fRM2w<1?{}!?T}h)K#4h^TU#5b+^1Hanb$1(bKHD3-hJSHa>L~pi-0}u z%Y;UbJ)s4qwMEUj4Nb)%Yg2K`7>q4FWpg$E!>7y*lB*|AS$+WNUpQvVo7emW>c$4{QsF@yX%`bx>$ zb=(2_lQsq0GYN>SK)taJCF+e=P=pCS9@07E5`M_}Se0m4rGRit{_c@BWj#Mhu8(PU zv^ikZ@Y$-W3)A{amF7Rs4}udKMhEcma7uuor%p>L&UMQJg{;~Dsm<>#*8awZ5RoWw z062ia2~5HTqzntq*oV;Kh2Qb-hTts6qbQm2g{O4)sKV!Iq;Yyb^btSdyqk~bSHwYC&@tRxVc zyHt^gyUzdt*44)PY{q}QLhyL$ymd}QM$5H^{3LQ#mivHlhMbK8XU~fD1wNr z;Bp2Y(e!C+etiCtX7xc@K*gMuztRfd!d`rrbU*1$ut< z;uX{{mH7T~eY)DrhfE5Tm+#;wVXSEuGl{5>)wFp>%SA)a88TQ$g2E%max&hpoo7&i z)MHC|M9bxj(`3wXen1Z(sd8H@geL>qs+W(bbwrT`1-(*fKy$Jd79@dlq0ALEZLtk$ z0mf%2{Hb=4bx3*X`l`}L;6)QzM;w^v1_1H8td$j4W@#f}Sf$1xk|UJwI(46ikS2Q0 zMlDk4b+p9z@kJiRiR$lF}DnO*7H7@qC}n~K!S}oStE`VwXU|>X{71$5{ z&1N?8!GMZs)fD|9m3v@F-7MJuA8+rroi@`fieB&kDX^9&kwSxk9KJ-jti~y*Tr2_G1C%{V5UOzKPr;RSkm}uwh;otJTTJnM5aT4_0ho~x`68eVK(5VevBs( z6Pafu?&t@BDHozdBj^2%-DmN*GZnJA8@Q1l#B4a{`=ne6qSC*M2{`iw)E>hMuPS*) zQJ*ohq|pN+IAq*G-f{K_ZuI>rq`j4hVI=%MQk+&UF@)_|tLlpJ%;aLZK4-AlO~sLksb--~yy@9Kz@fGkS} z9H)h4p`<3i+_upq?%A`17ZdhMGe^9CV2Ngg*Qe2_0W~ z_RKK{f^Hi-JI0^A3Uv*VJUd2dZTQFH)=;pL`1`U5x?7dAK$_Az=IEiZ*v6=T-`Rhl zgn)coZZw&}3>X$ak3%_7_Lei3B}>2#!ZZo#7v@Zu^a)W}SdOLGU4QD%`1C@~ln~_K z#^$)P1(Mvb6IO~~+vIK-C~kZPqtEJQX@t*fJ$an>M?fq62JUlZnBP)Y}YBC-Ny!ynGr*2 z*w`_|Lu1E?3**vcI=ypfwycu%`$kF+RN<6@`-)lz$qpjBh-|V%6*o#vnG4a1J*FQJ z#aqFzuv-WCxdw1DK)OF*zqbPdfwh=~`hfjI;0nF#8_3TUNP`i@2|KqE?)_BpA7I|LKZ|Auec z-0BCFD_rAv&Q`02MNHnA)d~j>!}K{6Al6082^p;eYg*3~_z}!Dw-PZR2b%`9*aW1U z4|qXBW?8Eho<@eua$thp%n>U9VM&5N_I@VCJ8PK*wzF=lpB$@Iz%9S z*p9|7_#Almk}l%8mt|k`uMuAk{j->1o<%}w7;rmqKx+R}{e$A* ztbg3H#&+{&podGoSMVy@Liv^qtIvV;B;i?tFvCnC;NGya?;OC^hUW-zN&&_$aH*DSHBJbl>E$>8 zswlGlb1(`ctOPW7kUik;*ovj#{$uTXk=@3S`813!?}25(kbY6Ejn%}_H%ye!h?V2l zM4pdr-;mHPKLSr|T)B#s0h(AMyzD8Zs?1te`HdKSRj{kl#sXNHoqZ#NO=gS(Bc}EW zu2qRs_ae87XyF$aB^!LQ6|SF+ zDAtZ~IYjN|zudO^Q%Du8_N~XL+iLY$lXBfhEP>^zF+XJ$Y@_rC6Z_t#JJ`MO(GyKAB@Q8U?%?;r_)bEnaFAcExOn~-o)loFH};3JbMjbdi){E?{%nIG zJ zp34X_fws0oX|ap&_%8#sArDy|1*)}{?eeo+L6E;CGYpGI2_?2|poQtrPa;N@bNTaAoI1B@<@KQHT_)-V+el zb;(-r1Yv%6FTKO=K~$_`KEM0eJ(eHLCdQ>YwIf}J9)Aqltb2ZrPZIU=0F{?7ihsR^ z8uUI6*2~lQv@pH^j}9QuqC^DrPTr8jPM&|Y;t3kSiC2qY){Io|K4LO&k}D2-@ThCP zh=5&g@p;QoXtdONIE$^sEP`Q0OE;{b1=7;bhIoYdxM;tnz)`YX2C53>;I6PuJJ?k2 zBXzYK3lG4$?g1#uT97mxMo;Uc&bjoG@TS7qZRswA^&M)+52@Jd1Ew;_3NP8g7BAU4 z_?9=in)TGzgzct4CDa{hn1k%8EI@%$j=i1lmf`@O5Ik3%C`s#Vh14Qv6-Rj8o^a*| z%&=5kpxOfSa~)2n^aCT}j(&wp^zE+<-~_m?N0Akwcj_wiPGwN4KNfliWA1p5$I|+v zcsz*k#HFvvf)*eeTHzU)(q~07rq7;1T%PA-NT0nRBl_$wGN;d8kr{pVnpEiXA~~YZ zpOYSa{({`n7q7@QeOVxf^ktE}p)a42PxR$;@|C_WkSj1`eNSJ%BtOA!6+(3siq9cF z2biy(fZ^&11kpA+oxDL#+JE7t3zx6(vIm#1@iKzT05*8yA? zd5a9LpFQu=C!LP*1e)aaJ01Q4f8z{qUh+>|V<(Ewc*Rq3$18p?a9WOO+qgG~(KLv0 z&qBE3e=C;;F(9kBagKk^3}V0q{`zo%f6k%S5E8bY^S{T(Mwi^_VY_*-bX#0naHEA; z-_=5T*UU1hie&Dw;W61|q|C@2s)ee_Xj#iOF1QfQ5hg6*Rb9oCPBev_s8u*-`!iM= zqY$+V587kaC1ob5cD23Bx)#}GO0vT38uY}V>tNf}#HxDHvdA5y<;^H0aWckT)+KjL zx#ixmM8Z!}Mx)v?qZ<#}W$0GO;C)(VD)W>kCVVr?9Cn%(txC(ki0n_w`xL@sh79sa z0Q`zd%TN}9aPZ**A3oO~J};mGwmTW4N&=~=Kdt%kb)i3Pi#jh zUId&q&fgu2e}*LR5bP%tvP_)vI1gXUR#lqvDw3tQv_L-6VoCRbFN=6MBF0Tw#kdJ% zI$2}`c@k~+&Mc6!C|j+HA_cDJqrbM`7|Y3fEt~7N$^68_I0o>Y_%5yjhb^bf;?6;wZV`wf1onO@@IY!)!2OqT@aUg2p*4p25V2yEXeEC z>Q!D2^nwjT93fo9?|D9)+PKksf57l@d>dDSFc-|jIQ~Cy6~wo3C2(Pwcq3REw4Au3 z^ojAr`Dnitpz?!0`v?vX&>`(EZc9YK4XD_b%W;XeKRa=;{qIK`k`n*p^=1CRI8SmS zC$ex);$ecM?>xi_;zeLNbQBjS1AkW-CytW82w5`ycNIlXj5YtoN&epqzT(E zvywH2BFX)Mb>#$4)zQb>0TNdh#0<(OjFy>YCyU7#Y&_u^zu1-JHJ#oC9`fd2HxO8x zWoU?YSsA*kDAtT69M^G7{1K#cPYg+}!lzX$;THX1laPA=8nu+SYa>IpKVhX%YR|?G zDz-xgX-JgTscOX$8CBF$-NU)8f`)+`?3BCrx(VmyxC%r+DW}ureOf5KT&)T(3-TV~ zg}q3p!9N$aYIXH&wR-W_v**ucJ)p+V_){M;Z@qZ-QZ>oLsU+Mq<4K4TsDg(KU1tnb0@h*RWX!0_h4%t3^@$y;Wbvj+m z6!yz%;hB@o7Wfi9pcs2Dou0s(e?5Et%yP_-WwAZ$X(3;Ho|&*LR>~k&**a74K`|9{ z?@Pyfn%a|{s&eH)nTPuNP7CCe7Rfn%Mn2Q$#AWnFNf(aQ;hxUa@uu$>IYrVX&gbsY z9jVD}d0aZ-Vkwz<@b})MjF|wrJZLA*@Uk3B$tX5eSF2qftmqCL9PgL{ROyr*;8J?& z+_4NT+IJ9J55$&JMxm)Y2Ik4kJY|+j!pKVZ9=-R{=_4p7;|RJA-92To{mH-erBfig z3+o;|s2Y&By2)shtyXs|JM*%I16*b8nU^U*v{#^tGmqY}_%!IB^i{MC-+8osY50C0 zrPDi2=0H~6R*#yk~nqS=D2YVnJSXKJ;`^Snj0`|J!X z{D9Q1RvP1X0J<1h`&wDNrtWawC1J#E@swF>k@4q>nN^UQWVd0x-NCIkWRl>i(&-&O zEj(9H>bor$I_CdLoyxR70GT-Rz!S(h@Z^bMA+WIKxvaS|GGDPDq7zo;9vpP4WCluR ztovEkJ=k8iZdS=IG7<6(mx))NpnBJ=S}pu@`HxE5EjM5}I7+lpH zq@>KUV|GwQ)|l?HgE4b9j1&6uOpWS2IWwzd?P)cEnw5Z1Svzfw+c-0g}Zi2_Jww;b9z>T|xUl^Gto6s*-silU6`lSKt& zZFm)K435}}R~c#$BV#7zWuy%$(5fS<<5p>vaBy@Cv>W$Mqitj+EMs)Fc*Y}bpA`x6 z5H>Rj8>LVK*hmD9VY7>J;x5AG4q>yq37flL!Dbh6Duzv_09(&a9{=OPl!DP+0!EpF zfRa`fUKCb2P24-M%J+TZwioP6QDB%&@o%BjJ6KW zA^SC+C6tbwCE(yC$bTh(JlhK7F{%Y(j2OcS3P~9-HXS9zBQ%e=gT`=TPH~FvHmB%x z>l9(HEQsESgSwMg3;51q$NP5xf`ROc16_eDUgZI%BEtk6fc+>I%MYGvAB6dy;{ z7W`gv{9bDla`=1velRE6gpm7)G*LSmtH&q>4=7OM`T?ZAQaKH!Rw=Hio%+c7@#FYS ztt`Qk$Wm?<0KO`z9#3J;Paosf5pm?sqEsxUGTXmnr7|K;6UY5vc{KIqaf%yg8I&6l zAj;_`qKu<$h=QO}h>|Jb%<*-Z$3T)12evv`lXr##8>v{64{?fsU}64t&B722$FTV? zAW%~+THZurkgAvC2)cM5S%3TyTYSV$8TvPXMl_lA2!UAY_;|5NAU4k}FtrY>Qm33$ zBH9}|C?Yqi@z#wB&IodDl%>Tb(W_$nxY0=6cbnJm zVqf$en#ALHn|NK1>3S^Ys%u!N(TuB^Km_Au8$N6Z_p*(*fZXbDCYCsuiSeEa(>G~M z7N|Jb!rtu;tW&rU@&qsq5p2R@=kte($MbEf6cDAAwplb2f}OQlhMLHgsGB@L)h;tC zp~kmDATtwK_$8ELP@kma&i#B+G_CSRfOastHT6)kTx9i%Y413aL?qUPE71cec9YTj zh`R(6^B1J1i9$0M8hQNj>Or0}2^cc32^x+K-Fo{z>RNJ#c^a_cwUN1xln*NO4PM*X z_8t1rVW`lGslC(9I4bs=B)QoMAAUzmi6ik)$Ol%&Bv!rP%u?IumWz?`G6tB# z1#ITua#OgkVHTdHp661ZLMH2lP|ph^<_-vi0qBpp72S_i=nfggOAy>lPP4%J8s$(i zjfv&HM!8FFzTg-6?eXEs>E+>Z<@&PxuJZm6Gl4@nq)(7#BT-g9zfkpNqU@-0eE8|~ zN*1F%*4K!edGQ~43pQZt3JP%o`0T(fBoC?F0qp~WLA3As8li_nNS8cEr;RDxFm<{O z$;ipfo!fo5fVF00x^hg@XJdx9kVQ6Rc_S(pvWeodQ0V&_^Nleon#Gw$2yJ&kjQ|C6U9dJ6KzI97DQMKgTn5P$(xJ^}%G= z!)CXR+2V#Evq@wc;wT}DK~0F4j4{DXPl*N-xl>td;Xtw87<(i!ATe?h$RU~fjAWwN zq|35$;e0xYxUxPmZ(eTh&rLa*8gxyM1P^*JoG!w#_H~YYdV`XnMrLdo%cx$Xv>k8s zPq$f@L>Avvj_R})lFP@Iz^AGC$O`y0{V%M5%U@ys`qT7L+`UZTu3quC^M`2YaWv$n zZO9I<3@8KSvz5<8Q~uf&ie~ zy2-3HR8!y9&waF-y}Fhu=tmu}Ku$V8p;%Fk2oS6*6CzQF6(sek#e0L z*3HX0F+MZ*+j|!%;&Gjp>!fx|kLzT&PHVU6G<>jjOUgyMTPGKF`h7%Nx3pX&cXisj zb&@1NS~}n6LKa~WiPrW-og5eGU7cLj>Ch#O+vJpy^SVAzHL}RGmg~*ko^fQm0$|0) zr4S-tu@a426Ji-?7{W@NE@1{$$@}OPZvDw1l_volFp~O+p4Z8nI<1e$3G_Gy_BS2a z8;5d?s!UlWak?v+I&oB+x3x)(_%UfAQD$N?GybIb@CkvVHu3n`BaaE3!ihT1e^rOD z_A^=W#cwMT)_yK)zx)7LK&QX6+Jv=V$XEXQ2d@y;e%a;bDgk{{H?@fZQ}ih3&gav6 zGa&J4(@EX*EXT8hHMcIE(rVCkKO!Qr|;|W_ui>q3G@jbL7#|| zMrLiGkSr&`7A#6UB*7L$dRT)5fJdGWn%U>qOLOktk8|;F7 zDj5UfQaM1ffJc2Q*9)1_Pj%DJx&*@a@&s3AKQpx;Q@U5mWFU#UHEu7ul(iRK?tYTV zg!^vEG6$}WoXMzsL}MJ%ZjrvNlh50R^z)B~ zlr4}&Jgz?6Pvdco>1P<%0>(8E;~K-bhJ0M%{1t-8)n=ucy(WZDUY1n&x=??8v8d-`-g61Xh zU9kRbp%(c+x;f*HeWi~0&Y+;H=rMw5HOC3mF`(qDU%8Iy^_UX zMZvNvCE#;ar2QtDHfd`_#!XrskztcwX>JmOrr`M_efyan>Q3V!Z8f2sR8p{=cBkhLp5zU))&?F-q5$nN-W-#yMzDd7@t9+hZG-=@_xogrRHv%2w56ysB>u#Q)B48`8?9sa>sD0=~Gi!R;0Ih)o z>XGn(`%h*v?11hnv{oDxbO^{(97bF`0sP0$>rIR?2X zh_RZ(Imi}NXWCwun!!QAG14gmEkU4&Fv@aY{fTD=2So?$S-2%gq|PheH_6TNUwcne z$^PABSmvgidb$TfFMRD?lYZ53_fvDbu%u*F&Z{@+rzZK_1i|!YQx^n58t3ZZ&rOq) z2A+~HPED!&&Qg)NL?tyO_l!iSI+W(VH|ftNsW<7{CW(6Vdy_PqAjH<1H0o^^U!xw- z0jaz(ex3WWAo5+-n zlNICp3Ey3vysdT?+(pu9t|ub_iNJb#r%;N)fv}Bgl+`o^dTkpAtJpoC1{u?H3U;<& zl|0AKX6;PI-w!eW0({Ev5}!nhCi>!I*#drSQlo+PF|VfUC3Yy~T?%W|(`xxODf7q< zAOKb+wO;)yL$DfYzL;EVdVL z)vrnUW>}-+8X45+P-|pf(-+^Cxsfz9NT&z9<-Bzux;U!Qd5z3!G^&yN8XeWhQH>Tz zrACY7wnkr(UJYpLj~X~fU)R97`LIR{#aHCAMvE_Al5&l{C=|(Yjkeo{8{8Y@u+}AQ zqc`ye202pKHFj^13f}e?0o1#VZ!RXIab%DlmJFwpxk1W!J$Hj>;!X|nL%$W(udys( zZZI;)Wv$zdVX|9Gte;uUJgyP7w3L`_T<44o2$v<62JsShY3iB>5 zuGHvmjoj7fS(9AV=({F4uhDNs@}@>Fv}rl1ZHV+4L)mMe!?UZJ>5(@z^R8xk76~8^ zCe-{;q$f4LJJ3-H$^axAS|7nh$ zNzV>?7GdeHHQ*_s@T|6aUlWldX0#3c2+4>H$eav$Kf0tsgXI1=Q$*rh4cVm+HM2ry zkR=_-{n(!l80^KTP!98ofI2PGPa3@6*0hZ;W+Jr~_(oTxZ)@apjlRz3|0=wGU3~HU zujl#KuM6a7jqZIh9Xr2j7qYLroxxK}_c8yTTxOipi9bY{<9x1>ogFYj03T9sG3&9w zSgn8?3h!qB`P z{ec^fuQjkX_0s>dR8Q1i&ksIZ_$q<;9=T+Ok4C78MlDU4O>Xs1}&gz8e3od^@QA@zQ2kj+r_HZ)wpxWf^Q&c%uY%%yB0Yse6|V9u()UF& zZ_(z6j9P#$d$YI^Xr3cGs@7OqcX=w*4%fDi&&A zG2r*S!?P9%6O$*U6f%{U^6(++3uC>czWH_wz(dE2r+3~m$Al}I6|_Ty_Kz0rwa7I> z`w*dhiL0^P(zW5ot(Yz3EFnvmTg0b_Nf^5tjD^DX0idT)KBk_$kCmK#P;w>)cDZF> zuH)NLDU+FUMd-9kz4laSI{PqNgZ30?Q#@ap%t9$qu3P4igmNJa6f|6}+ilU~7P&zB zb%*rJ?;(YHoYIop0WFX#pj+oH`kX9!^c6Ywk!m21U9p_jfLAf+#Y4!PJR;fe<7+kum}&s!Y*78H(S;Ozw;K%YiX0DoxT zxoDYvvf#%-_JTz1=TPeROn6S*o@K)$33+JjMVBnnX%m`x(IxPQ_MJP~%uCVCOV!K^ z+04r>ELRvGHUMy-3yXMZ0FD_!+#dYHS#TJ-{icQG1a2&gL=ezN#+Aey)_&cE@;qGT zp*w{x4GG_QzqRN|i+pI&y-uhL`K?XGsbKY4Hk(d=XbBzzt=11CIGBtDG0tS1wdiS! zyl>I77Wvu&7ws^kec)Y7mz97SmZpF2;Yp?t+YWwh5yKEPzeV4*$lDhE)FRU!oUDFs z(O^o(J$mMzk)JIQ-6wwsQGDF&u$$T4n~?8Np6|8u1%6-0d%vB}{>2NLEs6)#Ctjpk zy0oO?QLth$&y`T5eTI6nWQ<5Sg$-EoisJPgO3mlPMEDO?YFc(wis-awGRpy0-o~6z z@?~K0xNNSzjakehI|Hx1MY_e;-gh}WYDR+9s;&y1X3LCJP#_XGMf;Uy|BRoHdv=B+ zN2^6^Ez)SwR*MXKw6K>a)fO%M_4==uub&m3J%9DG_^j~aFIIf5@iHG<%0^?et9h=A zYd{SGg~Q%z1;5#0PeE%x4_?Z#3g>CHwbt5+pdIWTYtWA^5;Z7mXaavjW6~R3d&vGH zZVkoXz+u_IW}=-*g9Z)aH9%VyHt5$n@f-9{0+}~xk@N=i75Om$9`JeqGSvuVDl+SBId&U0Hn~Ab zl~{U?Y0Y=~$_O+{M0?w2d@^WQVt@A>FFxJ?G?h}-^>1o3pks+YX2v5@K2v2M22uQj|M?i{0I)W4R1swt1!#}E@eNABjqk*T{ zDGX<4kxsL;*&D2W3_vD8i10g$?6B2}Q9AD-?>ncVm>Xe2#VG9!u*Q#pGp9a(y0C1M zQF_gvnDgH1dH~|sj)adhrwcX7mOZC#BPp?tq=Y+kyoNn)D5Y}4oSAbnvSKN9)}W&X zxo^-}gB&&JD^0mJlME!IfrK>^TGD&?{!wFtBis5D8y$EFd6~@;Y@WAH7EocLTWL@> z6Sg_G4Y19rG%WJ2NReTn2eH<5ud~}(n(Z%kwwtj`$2M)t$(rpi>+Vy_aysk15i!-O zu;>9un2?>waY40fk*{-l+c3qT=>ZU-Am`0zhl^-@<$q(IwdCFKD3)Ka(Ikw5`%4zw zvS77}+BbjiF4@`+#Dg2eKF?ZCS(FN?7x(nbIEv<>WADM;YZ-0!6e{2Vs13`d#;RiCnh(bru)O4M5(XW z*geb1@gT8&aR0&O>Ut$C^rQx!7`b~l@PdBsel|6%HE%1L(NCetI}_0ZF>j3W4?wSg zvdc!iAUF1BY%mE-Yok5T4RA`ysMl_pL!0z7lS^AIK+$?i)fgA zWnh>Emxf#F1c^Ee`hJe4$smID)s1VF0GXzAnaky>%LG7nr5H^hMa zMi3<2BKXciE3rtp+0-Ir!NZn<-?yau3qUpn+3b6;ryeAloOmg3J_ARCnHz0sc$#Q< zy4FOyUPEI*VZ;0o__ImKOj8gKNhy|V5>jc&Xj-K(=X<6xa3j|sJ|{9hnv{dX%ETM` zMmoLY=b(Z(9ozd-pM;ZW&l2RPjr$r+)-lY#o!nFFZ!`DQGWM+1s@*MlZMRF8B5u09 zAj80)*{;YC5PuW{h#2W~V8#vsym1S3%e=!3wB;rYjG4;;n=|m3>Q7i`duy#0Hia6! z931R0SZ7GqOlvLI=69b;!ZDw^5n7j0YW$dZ&x(d)zy#&`vw>mbrLjp;0^F7WuCPTA zfUu;?A#LY!IVPx!(>C~p(Y4!Mf=}iF8&2+=_Dwd+FX`@5macEGo{`NC*gP83yaCr= zLyQ%g&3b-z9{8hxg`okBC7oO%%ZcoE)a{aG7!CYIWczDD7bb!E-6tXL)g`4L+!)o5 zhLjdWZzQ1;c%`DZspp((%CE8H*VNE4(6-UFa@-;pX3&)H@a-Tz0TXZob|%dPVi{#N zg*+LSW9%)$U=LTgVLv{BAdAU%cC-ybf}S}n7X_1hw} zr_*!;uhThlBe&Dx4c_n`3p3pYMG%@1U;&GlTp$1u%uECF6Br~S%QXRV7v=gt2F|ko zV_@Uu+Fq^^FV;Zk`D?vKe5{|UGSkcH*iKB&I+#n)MY}R7!D{8^0``3|3E04qg=-*@ zEjW!l;5|il4bYVyMEr*EZ$>k)bRmZg+Vafzu0<~K>dduB8Lv*K7CGimrxw}ePa#1t zUVWWfL7 zLTUwDlfC5RZCm%e`D}Y9v)SQbuv}bUjn&K8q!#37%=ve3HyLt(mcRiUSFdT%=I;oCn}>)1v7d10oSqpqd^7} zNFO}14-!<0c63@Jp%gL2HB1>+WV7&JSizzdJIL2|O7y@<)d0+!OpgZ|BZaqk| z`_qYqEJNTla{(Ke%(5Ae`64z&E9RE(CXrcq;9t)-UM6?R$t<~yf$Iy1^tkvHhe-n6 zFUXT=2(kY+?{KU_)xfB{t3%Y5{$bEmc;uM+R9i8liRYDSKxMiXqf6A3#ZgBX?_ z*%8ue89%|w&D^=^(SvUdl=D1oE)mD0?swP5 z4e)~t9eo?Mmm(NenFQ61&^&Dsf86~B-c zjED=`9cxWa#pa08KQPVwy(8ok)Gg385E139AgMN=WV2fJlEg$7Dql5#gHiliTg_=u4TSzjiR zz4sSEqT>*%ViB@nT| z9IxT8XiiQYe-9@@h=8z7dup3jeClZfv{OE=+YKp3lqB|TUbt-+hQ-eTBDA7R%<~Dy zO-Y>x(8RDyHy%q0oiaEb+aQ0Njafvo*|6boZH3UVEzGtd76fqSNuWJ|;@Vo1h1PRU zL;RF~+&i-e9NQGboTl}uX+a-SR0J;Tgp9Lrce z!QUq>gQjWH#pHs9o%?R!8ju{>^v0 z94*>2n2t)9&VL1U?4Snn8L5C7z$Vax1xQf1^!@K+(fYdH6rre06?7je?*^ zsi^>EWFpf*!pW3*5g3B=_CQ!hMlYeP1tHkdgJdVTCFAhHm=Ra9k)Iv7u*+texlzEF zm+Se{foa&55hqLo>=}c>cRYI#&SMPW$<<^gz;&~`6z!SDfBx_P%dpldKCN>%3~wZ6 ze$Ja96E1=P2Iv!xyKG=jN#sZF)P8h?>y6L-3pA11+&e~j?1#)VY#6z*PL5rE2Pa;T zLN5-<vK{^h^na>W=Vx;3q5~<;jWPLns|fB?PRcUr*bYro^%YRpnvar z=FD1465tLY1yJBmrwBT?{|(wf28G>CLWogkRe+3DitpJeZ~gBPE{t+m<9aMgZC`*Q)tT=AqN`hjS*k~&OFZ+0;U@)7P$!f?UD+>M3AVn{}~<+g`p)b)oV=3S0?Q7~a} zvV@2?8Jh7fxkcPkoZ)KqC_A|Q5wd@6!yOFZ_(E|#;v^z;Z<)~rHRcec)Y1Cp&$eZY zcrimh4)~0_~i)B2*{so@i2yD??o?g2>*&Ho*?>tIMtVjWv*4?r@y<)C?`ha z#B7vd8)YB?a0%1dhy4;+-@15&-s5AnFBl1B0$`ILY;oNpI`THSsa1<3SSOS?K@xEYqoEn7*i~2^!C@_V4?nvT+;t(LyX+-&5Oy~XH?5}v zIbA_Ln!CA(-9@G*3{E_-YjefPrVI-WmT!pM0bQ(CJD!6v;dmn-`ruW?;6W(%5a=&Q zzd#w0eCV4yiIJh>*Mqe`V4e@#1^kL77+?2|uI-Wd*mFa5xVCHoc>ywU5-|TZ0*H(L zGNoV$cuGFFhf^Ng<8G<%@E~@IfA2!D-r9b`bQglhI19xYi#&5d#_lfU%7V{h=FvMy zDdKW`>qydYKG?a1ONHwQRVMA<&ey>^d=uuMMngG!|4*c0= zQuF4=IpHBI;G~I)AQY4E4e-22>~_*;G)Ug(z&{ggt3=hZOfIig z-zeLBnnWL%^*6c@r3k2`wGaF;&vqf0gd-xYW?bHA~}Ug%F35msGz zlRF6fCh(50*v}2>1>|Rg_89qGq@NqgP{p)jQx)Cld8tvVgk*=g^X*3*phx2R)5uA!#>IzKVoco%Bb8sNgv>2%C?&goiv zq}8B%s?E&b{6%X|3-8i+d#7#q4wvZ^RsAw*PIuA7PT*6kJVRd4Pxx= z?G}uS?5+rpG{teY7C@Otwfo_!Gh( zAO5&BTP(3SHmzugVM9+1FnWiaR#e1z-)ncexs27_6YfB5*0okEZ-++>m+!R=ycVe$ zyAT`R+XugN5dUPbaieR15jgP<7@q0#WQXp7w_2I5UJvrbGw?W3H7v_ot-_6uE)ySJ z8)#myHM7Q6Lh-d(H^(WZqkCs(fLlM{zmfT5_cdZ18)JohR zsa|Yz2c15zGu6J)2fvZ2=%mT$G*du9R5q+3v50S!$;4}xO#UhMKS1)m_ETrM#yvfO z&c7aJ@L-r83^S0PATolC9Hs}u4C7&j|7@6^PB}^N$FviwRpWrLDyUUZRbos!9bjDD zKDf>86mWG;sulqfy(VFmdR5|ALEAEieFuCsfTJgFm3qN;e(P`mtO8k7Dd%GPRl3*i zEIaLP?rFQT?sU7mdt_Rrd*+u_`%99kt~duTah zhXZRrk8`CW|C@^{IRuqgU*rx(qY{>(hXad13MSVm4g3_NKES;6V+=Z%5Uy9Nsd`0= zET@dFs>A@6VNJfHoM!4c9pxtBi5D?Y0Wf-?xv2;@Q|AnS<7!})xXqhIT4eJylMmkF z1<*qX-W27A6K_C;unpnN1}wxfFt+ogTLDNuId%sp58>%#Hi@XPe=d;Fq6e6mtgu?` zM2YTg6a<#DQ?O+R0xKJLWlvms)&Y1U3%>0PL|^z2%@a?WO@>tT5|$xoJ-u`~l>PKp zD|K!7a|VjnxS&5p1rXHd9{|Pa4orW@J#TU8XT%LrwIFHE8hYhn3vU9q&tQ@mwhVCY zK@nNoIM5h0K`^x^0uXgHPMnjtKjX!D-CuCy zG9TK1fgv9x`3)&#K5+eaqAa?z+Yil|L9@G+4j;oQFWbB~Gm&K{ZZugX^0NUOvAZlT zgsotjP5Ob4mPUKY+K&S2#eWr09KkC+I;fJb9!+@KRv_j9ZVKB|AVmHJbnyauXs6Us zIDBR}2|zn90-V6P2xgy@!4{;Jg0Dv&F+d@vu-oEh#}$a4@bC3OoQ6NriJY+K_UJNT zQ}?dL0v|XIu+R$DC4mc`;J5H8&~u(XlasOdJ-Sg3x?AG=oEVK>8!P}-aX9va2+~#n zMNp-EcOESQ1}7M*4B}S8d1-{XPg%yGknG3LxS0h&>MOj}AG3`}vu0mKvXC{}{VwRA z7Sl*LuByE8nCxJz0vO;uw2cLBFEppwG0aAR)7u&JWo2Yo>`g8@?%bHW%h}qxv3b7{ zB+ksfF&&#fSaXY!OTe+!>Jp}z4C!(<@$87q+&h~Q*PfD|Jzhf`fi6LdyUEam&@aQ> zB>dnG25dkFWC&e7WuAl--pd0Yi%j?PvbW>?yrYIPw(LS_PH%hWjKt%!$dGsznbKj- zr7EV`_yD4AcFl`xR)AtUKyC07~A( z@Y>=0{YZ==6Q?!B(W`d>n%dhI(dL*ARWCFh_Pi=X2ypDb9N@i?HF)aAHUwR`9GHwm z5Q5gy7tI*OMDdIR7Rjvo+8)t#Dp7aNVD&>CK>__U7MUc8h z#JZ36;ZQu>r>tc8Id?FCtux5z?_tG|UiVA}Nu8~1p%m>ue97Z4>D8pt4y`sJFes1N zcvQ8BA9s4DASU7tOG>qzbCsSQkv>FY2Upd>WHgCFJHHmh<(`iiasepp%vKBf&Z52*)&c%M=_b^HwN+kM!A^2TZf61ef&fYM6Ugs5`1t0d5r3mStA z4PwH=5zgpzz?=-C=yVN>sJ{EM@2={6xQ+*Z+lw7)I6)J0h#~oiOCz#S1L@OzsUP!Y zeaV+iY1r;}$(YWnrdXulQOU2S7A&}dU~_f?wY1aeMLG?>C0uVx=~!M{%S8@*t*r`K z!V?jxJIsj3Zy=upS%}0mR5hGA?3toEkl<;i09;=*@fK{Ycg7sh+p0N5P*`Lv+ZklW zdP`&3k}tk3+9V4)jw2B&@QOu+Auue#J_^HPWN7&@Cp=j4@7aqrbxRQ2dZ*U|4>Q53 zkcE{_i*E)Q3^0}cWrZ!BxlgQ4ts8Lm>czSkFO#tZ2i=Y^_;TCpcD2)1)#6b#8I$;c zIR%>?L=X>1OWN4$ZN#*N;Hfi)!Yfz01R4>+Wgj;xgiggZ#L+pJl`&o$Ah|OarTMsx z>tq{IHu?|T1Lpn@vjFi?v<%=qVHnfq3R%PG#=$~^)ruVyoSWtDl4Z=mF^G`~Aau!k zjfn)~h>g|c5O3Px?z+2V$a@wrb$J#Xw%EFRqv1y{W?lEbWF0e~u{r~(`_g)Os}rdm zrtmh7YsDv0?Q~4+n%Nj|a>op5pm8faD6`1s;%%gHF^rA$EumxN9m6*A#zyKEc(BZq z;u;?2jgsij4=2;< zFSzZbP!d=EH>Hw4I9TD#_Kzw(n~-La_Nt`Z16RsuGY@E_C(hp43~NBHtMC>mm^f`$ zDi~W#CM2OBV9O7r9hICrDdwPjL+U=f^g!E`i7axA?QsJ!ioV)6a5{l{OD6QVSwkq{ zWYSo$romk+c|=rV?Mw!hXLgySH;CWqBn2zl63)q71@2I`AI%QuE%^JO(D%dmsI zB8OO^IlvbvvS728?ME!f(Drbhg-&&E)}&QTqT=RqmUtN`Nhyyc zcw-tAvyEd85o|iJZ2dMlpib`syjLZ+nKMaP9 z5r5et)s4}}6A5e(Mc=%T)Eh+;!oMlTpxs@Q;~0C<73i^dlT!YT3dI*jYz~gV(c0LM z=Wq1S?8?=TGDfVM3#lNLz@Sk@Hj(jsK=ndUeLpDmc^($;p9~wGqk@{|;k*3Cr+Mj7 z9v=9ypdW#ZAJg%vyoL}cv1G3H@xUTN*D$8X19M@mR>uRg&wnu4*w|5TEmTe~73)^+ z`Zh*&7w1ZcoSX2t=7o%?ms4pcSF7tPc)=kMg!%=ewbFuICAFe?3=G!5BFDgk4lHm# z^Q7n8QmuB7m=wi;ydq>qd-P!5?(OFtLsbJ$OIgbh)_n>9Jqs;q6>j&5;?BBa47*BZdxh!farNnSdN+GO>uOH{(DnWsvMW z%Wf6T3XdzXL0as}np|wbM8LMXk74U$31+o|GB~ATs!YPOS`^b!l2Pll*)WInU>iAt z!T4gQM*AQrMe`xzLNR+#yrBMp3xUm~ItT*mkZFu`aQ~Zm?~^InDafvlVO|DuUKWmj zKwaD4r*6p_3nNX_N6U1dE*w7_*nTD;gGa&MDw^XBQ2Wxye{F%P=)O;r!B>`C)yUba z@*ocE+Ky12w1Z`<75u8hR}JD?iXuJCtBR1#W4#o}QcZ~+a7Vgw4`RY3>*YaT!6y-Y zfY))LL0vtBhgrBAz!6hG12_N!+P7L2NF9ROIRb$YXPjNZ{gcqLFZlH%lmco5I|3X# z*hS~KYT}z}EP=@X`mspw`{E4bu1Y@^$u69PkgMt+oPzBBmkvFAaTMk9f^pQ9mlXVu3s%0Cdk*q0BAh%VuHmky^o)I6p%ma42 z@eua+g`-+n8Gm$ElTLf`m{2gbV}Q6go|9)cWI*q#uyc1(Ll%B)jNQcSb59 zpxOzXNdW7~DCYRvDM$?Sgg!W;v2}R_0*7X74$)ra2B!yj-ibC!1E-?w(M`tV)P(c^ z{{)BiG+81wzx$g_M z03ZwA1vnR<7mD3?AX-S!Cw?-Uqkw)D*9xVhMJo<95PS|T=Eb$$SCn~QgsZl<|<$qvu0ue zRDVhZ+K@2~IhKqcV99tBOU4RI##CODDIIIb#$lf?4fd0BS{g{z^N@-s?E#NFy+M67 zvlG!1YYCf6A)TJ`VH<`;;>%b!oNSD530u%;O8VC3TS&~)>7g1i-+Pu*_4ZT(TI>T_ z3q!R5K%NLC>cym0U-`i9a5DR?N>8ffLzOPyA2zN}D`#)7-r0qG@%hu2&q?{+;l*_x zw08XRwQal>m!Cd-s9cl}FDuuFws9y5PtQNch3*Gr0$Tm8|xa{-c#nH7G?4Bro zhj;%&-ZekGtDIfGKRNq!394poCJxWuo>s2kTpX5bl`GrW*PoR?U7R-YS;aP%`jg9# zpAIjy*V^*o$ytT>@aA7Pyy)!w`preSe+v~)m&3<>q)62^H6EUwPS#*Nm;B#d>k)?bPDRFys_{KK=54i@; zjw%j*pGCV2Vzw zzz*rl_O7>y;k$zk{64GdsQJDcW8&B9Ba84|^|3Yhsrm>;ysbV4^v~7DQRS-li}^V3 zZCNKjt6L`Idvz;z)vLdOZ?n3lAF8C5;E1*-=Gba-qUD8<^`z<)LvgF{A`!Q`fXnfH(ThEAsv|(*)fNd(q?tFg5P?T zs{!G9cVI15Ti8>PP1R(%Z;Z_=Ah%Auku$_}wIguvO)>QxisW4(JQzzuEMgF+WPgNdYKrv3d)ki>=Xu+kv;He&~OExC{!PkF^$^R~X z&#Ka}XhRTl~8F2mQ zGxD}-Ee8T)Dv}6rrk4N*3f6DP$byD|3tjd?GOOZ3oLOZH=tQtCYD1{7# zn`JKmdYW~63`&yNIJEUGYGg9Rg$QaWm~QVc?})XWaTE~iVb-2$b6k=AmjfA+1+A!l zWV3PG3R(b%ZNPdAoa=#~;`@!l#e@h9Fc7XBBBt;WJy;&`ven8wO6o4tY4b?xKS6DL zv>}~}=VgKJP+_Abxfvg6ptr;k>1<sB!3O1_ryh^H7`mIVnR%xqB#=%3y7FP^DIA>MnOtDo} zDx~8;c{P<1J+!B#nwu7(Q@_mTe1;G>Ix6N&KA!bAq z?^~o%72_KRa#$Mt|ELYYEmy^8l#7+;v)MelkHHei;D>Lx*)`ac{4wzOWN4j>(BTe3 z4GTMqXAWeTm9xX0YGTlEc&SnHzM^O1M>-D3Cl_pvL~5gt5Y?a+fag)CvuBW#9=&OQ z*Ha^H)Q$|PY#dQPn{bQ9Mk2|`PKF5$yMdz@YKGV5D?$h zeejWd1i@VuOg<8nR#icLO3u$J*C4Lho~UiJV^TRgx;{KBzdOIM7c+8nesw8y8~qtM zJ4Y!t{3xz2ZC_rBO0Ie&3Kp^>;@$p7a(Q)lVNdz*mHbw8ye8w1crX_nZ*&!GI{0kz zUY_)L8#lYllOBRY`=B-@reljYaRNmT^y!hqfHIt0QQvx`5%r4vSYjTxSG{~tcU!Xw zAn|GELry^HCeg_oeX?$nqg|8XN4j*U-eP7OZuaM4ww2GmHi$c&k6l?}5I5{kCLrhB z<@4E2=kBj#gY>4Z_sup^olazsz83{ktwP~g)Ixh@+rUOgfjb{#rD6eFsTc%8fd}{@ z$aFCABL19zrIIqp9FB9LFuT(+{>Scrq@S2pr}H#Ja(i}ZpB_M5kxuu^_04j%{+|X! zk?%9!t4=5Abi7XIwgYOo#e5DQ!r}bzg8^Q;*`N9B>uZDDiGChtkKODLdds)B=5(tv z_QUz*N#pvW^0v~rzC8bQQLfm-kA&aH8Xpm=(bu)(*E-I4g>=rkWJwWI8v`VoxN^mPrS2Hznz7o3tH=2>tUBJD#x}6xng!c zTW06kQ!DOEvi{rC^EZd5_MZ8m{rHWG%KP(rMIWu{*qwInse?Yqoh}A{p*iu952uIa z%KOUM)up{Oo<4nQppMvh`gD(9s~dH{mMZV(ucXHN#a|B<-Y;J~)OWxB>!G^)b^f8Y zyHG4XqG)@be@xN#ys)9`eqP*Ebw7KhG~H5h4`9+1z^{wjHQR+}kLk5vKiAdTFJAqB z?7jb1+eou0`uEvS0bU$FDRhi~fWh`OY}v9hOnyN^CNo1ABJ2h`*peg33B+iv`vCX6 z!+nS1bP;x= zxPvC|5 zfNYfhJ;~=rg}F))GOA#-YtmSIc+v`-IOjM(d2+f0OR?pV+}J$74S35aW>`qn{Z%B{ zjrs56Hr^md+jBnS2eZ6s`fP=1`V35chi-_rB{+Pj!l&B`GrB9Eq+#u;*a7^WWXnB055-iDD*@;WH;lM3V{m;e8O@N- z%40W-juMA2&pH?x9;4Ha^Gc@B5AJPv)GyWJS(?Oxj zuqWCPe#~cy$xtTFU73RLj`=u^cixvFZ8)Z~F;otAPOoRtbQYh&s{!eauD9z1M#T*BiEJTBm2!K1?~>>c~WPPvMQSCXRk z3Zt>0s{!ixOQcZa1MabJ__MDY`hwRc=F#;j;~oFRGO1fzV^dze9slID&G{$p2`M9e zj@Qf)#^(HzpO!B*TDo3gl$Ak_*KlFd2JvA=25U>GQ5F*&dUUq7+Aq<;0?rXtB6TDy z8pB@cb*nl%Icc~pYligOLD~YoN+sSYcsE7wEuaR#SAZ$JS#%u(4MF?>ll*IHKx~l5X zEM);X!M7gql?i02xa{8Mrg4FCZV4X5w!eaEatY*-?FFc87iL#YOOiV?8E~bV^Y2)Dkk>vj=hq3Eg6G{r?0*c}0@Bd7sx7Z% zUd{Ozewu6mNI1i=ZH^#FPoloK1q&dXf}68fm&PX(H0p{# z->Td$Kqt$0X!VdP>v>`ld&4X3o1c`Pt$hQ=umpf-{Q2SYt!it?PPl(|bIyE`+(UiF zPRzEdcXQLYAxmJq6L4s_XE)~wLxn0?_&^TVj?9+!<_q@n67$0V)bO@AX2EU7WX8=p z>YaVUAQK%uz@U9$vgiB+iOS~Y^Q~&)c0di`Ch73=t*Y6U2ZrxC? zw1j;3^fJ`X__nN;yU%Xr1k9AZ(JhH_znt*iiA1nZec(#_0kZd+Z1?AWky!WgWT~|F z>&Z%C>%~b{?CCi%|I}yJiGICOU6qy^EVMB%y2F&ofcHE>)fLfQVYPGujxG!29n+^J zGd}-Fk&n|DwSfxul-v@Q_bB>-evXH$60)9hS-VgjvoKWsPog5j{WXmJD&o@GQw$b~ zS~mI+`D^GeYi&G&C|zUC= z#Y?X%b_S05Zmed?DpBEPZ8GOEHsR#SMDQ?^~FYQd?lS4*^i zy{rn%&Z)UBrIPMTR;6`8DmyEy&+5c{%-(S!_yJsPn#Cl1+?aR38wa>)j1DIdC$Vfk zIW#}APdvbHrBbWMPUXv&)&NPS_b-)79F)IV~kA-HslaYL?o*8u@;j2b!}G)njPH%8qg z7uIImd}f&J8wtg>wwMsl>S!hMGIBex$6ZqrNDAIC8jBG2kdK zvrpi+tmP$3kwJY#r_tIt3c6!>;>;5o+cEMy5g2c*hYRnBoR2~hPDZTL;aGuwSmrR+Z~K?2E|Jz zyAaZbObEHVy;3AkRS6_}8f#gVQL;omuz#hZu*PKJ;~1o6H2MuQX0}`guyS7kL{aav zrT5arUa}2zjg!uXRB;ire#~FWWCRaR8~)NR-6o$#UVl6rh|nmJ`1_c{&SEm-c%Ac? z_xH~Y^ZtJO&kyF-hI9>e0%4LjH;uH1{G+)6O0;9RN?K6!6RdfIL&?p}?LR-1LDjxp zK2Pf0Fx4Xe%*PXBLtZJqa>HoqM#B(8nW@>!Ha!0vW7FnGg;FUE@WT?W$Lw(lEe{`_ zs9QBnuVRzkY4{)yTS@1nL&f!N*x1p+cGpv>D)}*X_<;1uUFYCo`xg2IAO*H zJ7%BR3-+45H~GN}rT>6UW4*#Q%+?E{(1*u|$6GJ9jMs->l;^S6iI0s^$!xtx5`DTN zNAme|^7#dYd^sE}NqVBS`w}a8**ZSlXnh1mc_aRqTG=IM>)dJk@D+cRk7!deTe+_g z8GC~tY{=H-euE5oskNc*AxaJHwIq9-QZF-L9Cf{rM*EAl|S0qy4E+$`Rx{I z%u*!O&3A8g17Ag|AfC|HHU&6=b%ZR2D! ztPu7s4fIatwg81C+R&)Y8B!aED!~P$CAJ`<^;`i}Ri!H?p*;JXYo)@DP4*)9@p!9d zve*3bdTrzg0fbBwu%qgTCdsC~tud-*OdfWi`li#U$|MO1@P*W2O!)Qu?!GvSjZD64_<_FX# zwg3(hIc%+)%Z8FNG*knz8XrEGrQGhfIO4OCT(+Js*Suu3*G~*nTYKlp`c5)RsrdJT z2R45K57gOC@s~{JVeS<5DVgnb$nxXL`8!`WnhZm%YD!jyW^J;RMk*L5z0I$aETz>v zpnX5yoB?3|LO<@Zuv>jHa9ntF*cKqCE5H3LU0O96ILKiSYvbh-WwT7`utm2ek^Y z)9cwzFQA<|k>WhSP?D*j92;eAp3L@!#IEEZTl(-}UShZW7=Fd21ikoYEspJLxQg_` zOi}N3avpyzF*8dU7qRJB(td@5Y%DjFMa;M_ne$|C0+uf?en`*lTU+cqHe)7FwHy34 zS?hV!JKo$pIy*`~Bmy8j{~gl|AuEU-)tb-TdrZc?QPh!b+8CVNZSz!!s3c)kAQ$+P zCsII`%KShOm`oVBYnOQHaKC7qCW%wNhu1|tEGyO{m6AERo0J+KS0>(2c4Uwem^^fq zj0%@XBHsb>tx7qT?kEy?1n3Q~szsddB2 zu(k$lRhoEQE^5hR8^~^x=oBh=9U00(_%_={hE-7a$QAP580zqLi-$MJ^Al{E{h-Js z)X;r7Nj(z8y()hS*S=IM;Dl*7x*m#PZ4|Br;`3}2hyeyV#~?3jnYE-bmon8>hJG!U;`mKoGSq%l(qS-xnMYwu{%lW2Hc9y z*m@-W+ImF4@)${`=tT&rO*|<11LgZN!?05E9MJOjvz^SuDPCBgIzsx)WYW%if3HZv zL1k&X#S^M$xhk5*fB#N!rB#Ey{c-_dsZ`vdm3!AZiq6bAb!|hP*%waS3EBO6qDgl) zNI(Njbt=8t&;r&zatqDQRH9OWi9xF{WK>{V$f_Zv#h6auGx~^(WaC@;IyEu_Wz=ns zr4s@~zwN@Q+5QuPJszr+B!pZ>)RR0Kr^JF3L$b=Ged4=vT}aO`ART7MoOA{3Gv8Ps zJ{uY0!(Q-XWFRul-rpY=D5+?8iGp@>(^&ruij}j+ip_-{g!x6`+xt5pKz~)n4cd zp6Xb4@C4zC%}rNYe34g}wg9b&tfmgyR0C^ab(7fdthE2Rp`mj!JB5f0ijI^MQ=eC( zbEZY54BSZ74k=Hw{z7(U#p%;f9Q{_J*h|)T!tB#EZS@*|WJROXhP0JP4dk5_66M@) zm|1~ccAV)kVWCfd9R@;0Z`dwrb1uXgh9H(sK}+`=7`@j{g-M_Y!<9 zhWX=`qcRLwMV4O1Inr#-UnR${@{_|7GqNnF&b7$%2&g+%nNM5lCxY$OUt^8%ppmd( zdY^9dwHgaY%*7t+U+|=t=>~jB^W5LvsggB# zh#%04_))!>m$G7=SZh=683o5P#W&`0+thU{6Zh^?Tx4lTLP9aiO|8FejmiEMmY54>r2c|6 zWyVa>&DllHQoG2$ZWlRLc9Fxxo5&z%5_v_okJnk-$7!53-t1%>A_P}wI6R0h|&Wl$anJGwFN$I#+O3{F-m>#1%&^+`Ej_)A=r zZM!JjOlIq4vL-p#CR-N}(M5jNs*nLvH|7__Bg|s+kgCbH`ea-0-||w$eSWh2U}3$G z3f3mq+bQ4v(?Pco?-ZDgUXl>*9k+~5Qu$5CVNM=`EO77cq04JMK4Xx3IB4! z-c9-26E>WHo&LrN*zCVK!JDLy4Z${zR9qK|NKhEpNnJH2JNPSpFpZ2W845s-=1PV& zm(Emyk|sADruzZG#b?*TK zcp26*4YcHYPtikHY8X#_2<1&#pARQla^`aHa2hF>T5qINttkNHM!rJ0n01Uk;{xLp ziS|H@MI_eLD_x-lbJ_K(j16$@`GH8`ONMClwT0nfeD5V(3s5g_FbXcd#qBZcqA@pyY#=8TKg~xW)pN!0nnCMYZIjcOPa_hC44DgI#ZWF4VK}4PmLCHpRo`$N ztrBZ6U@qt8_mn{jYH;hD^sNtL>vi3wz)^$}u7#|@SC)`vY- zcP7+Qjv$g{hu8)hgkrzf_ zZtq{oOF)N0Ti!_OXq9wNlAZ!>gf02-W z--4}oQzI(0Oob}puD+_#IU$#RI(bTII`dM(9%RA?MOg#ol=g)S4kg_DH|bDf2K$sx z4kc7|U?A(pAKS14<$X%b_(yUmF++zE3A@xs8bqOdjG5+3(NiV+a*;t$IXSO4YMkH{ zBR|yjsA#Wv@S;?~&LBOFYy6=$9hErs`j|J-HB`(@FwRuJ$vQbqL=w}qnDk2-CW><- zNHxp#$M!eTRz^=8pL3 z341mLw&hde`T{q2gxGe8xsev1F&7Le8!Aag1eUHdAws$QmBe9(DCH2injg$5jQTJU zsszA=sXa&k4gOsclaC>Cc3R0VK&`7o4@WpvBWxL7nHZ7u z8w;~}Of!zjU5x>8%G5XyDV%%@{)FeIa$-8<2kRafNf|?vG!YPEM)$icP1Z-_hrOCJ+W4(v$Dond%M>sFg_Vp)7Bkf;v%!En(O z+-wOg){HB+2<}G7ZxN`~>L6K@`L#j+?>G_qWRP;xugR;s`MTU%K})rEH3)7^oW9_n?ifJIFAr)lh9_CMEeA1 zwC4z)#PU58FZGes_2a@dRJ;TmI!Qd%D4FK$-k1h46iF0ytv5!*JUoDC7lc!9z2x!W z5+fphx=iaL&O1si289WZbQfJSMTuV~J+G>zl7wnVrd)%#>LNy^OHu5(xXZzC{n<2e zQU=V#>g@@_UR{j>&}+!hwu&3{*5nCKVG>-$z%S(+aTj7ZIWQ=Lz=rKi*f!4$&-`;9 zo%!cU1V`*KKwRsr3RI_2T|JJRj@YJ*o0w9@(1d7_6l@kI(&Y!f;hGE31kXQiR2E-E zaQDO8UiYZth1RQa(hXe5}aF@&M-s3ChM@JiCPxjT~Vbf|kV53tW9i~doU z>U9U{Ig`}w5A*Vq4{>ra3mtX)4mO`1Cj6&<`O7tW2gz@_7 zjc^Ae?^>3@O1uFU#r}e`leZw7Jrb}B>aW)#f-|E%3PeBhgFC8RR`f=Ufnz)Mk1;Aw zCQ~|~@bxSTM+4EF1*>u$d8;zXV9_eG{QOGBlh!|v!dzzhn9Hh7%BjC9t*|ot&T}WD ze&*ZT$c@rZL>*M51C&!9o$#w)*wuu;K4GrI(TVZR30+A$KH=MEPY%!T|6Jc@FHZRO zhqJRk&(5EmZ?jh?e489}|GEBT>+s*tO)%Xt&Yyg^Kl}6k{K*G%o4thMl&TWVZT9Je zZ?pRk;kvd z*VheEhSStr;Sm6(L+OgiZ0yWztDca(taoR$>pi?WhU)1vjk+8cPF@i z6ir!Q8wX}f9JWTu6*17*wJF#%e_BoXCUvOPa>gYlw&;FFP?+0GeB8kHrAX5!IR?*+ zlioZ#(LBsPACqK6Z9uIkUWJ^`PSR1m*Xk*)m$tGc^CH4<^7=#g8IcziKu9URdK`lCNqRjr?7!YcN?lFW_}&k$p#BJdYySgNZi%dY6wt zrZ&GCkEWUjE|k8-MZ3dut-F6@hn}o>m*zs>X#UWO4V_FewNb&1rY%{tc=H-+DD+!q zNp~`m%EbSzgS2Dj7>d-b+B1LCy7)_WWHL;!L5!(TEZUWWSNhM+sm~AX+P`QGTseC8 zuO=D;lkhG~hE0K=dQdvzOtb@I5E%vt$@qbCMex*1tb~L@$$%dO1rE% z=9DIhBi^W{YeaZsJ%|e!s}p@e(LSeU@@k?fu@DTBKO1NR7eZtTB~t5wL-Oi)3A-nR zNNb65wa9}#OhV$iAl7-vNG}~g=?nsrWasA-^o$)ZXG;SiC=Sy&jde2;f`1Cw)Zrr` z%ExXPo(=;Kd2pSVt(pGo02fUkF2CEeIx=;2!vlaK~dq9n?b= zD+s03;!MBs;w`9UJ#%x@D}(7Qmj)+vYp3O<-4D431Oc~v5OiX8JQd*CqwSSRB=FK7 z2<7?Q$b1C3q*sRP9Piyo1TWp-M$4G}-q?8pQ9YEr-sMxr@Jx-FGRXO)l$EjpPez`| zOn(}n+%%%&*|n1b=WKY+ymUSE4U6*N*@o_d2MPdN`lbA8GmTbl^=6Y z+(xIP{*95i{*JRy9-8;}#*l;5woS6!q3r8cRijCUIe+?ea8LnJIl#Jam>S!1ZA{I1 z@-{Kd`BWm|i%}Q}PXtD3B7$pCd*|T{d4m$0m~1hZkSYG=WD+uc4cU-_Y50i zs|g6nuJYJuVKpsZ1i~Faxb(LuW)|L{HyK5d7^w68m0`B0W#3!YK}gAT3O^(rnIgs+ zE}IJ=%+;6%1-H%&GBqQab)`N1NddM!-P$s*jp5n!+-zq?GB(UPf>(_OW24)FJMu~< z3&Dkf@CJsNhDZ%X^wPamAPS8NI0SYF13qIec6Em1DX}Cx!O)x!W$pP9n=$MO%6@%T zcBj+voq#Z+y3=ih-m+xRbTM>w)kPK`|(Ub3m#m z1#%P@%Le`!FP9DcF)m|`*`uH<1P^thIU`!G_Ig+7EJ~Eb2@IqNye|^e^MKdLV7zD2 z^iej75y-&I;%AWmrYVTb`u8~L>`d<)CePDOh_pqj+%#j@2~JU*S`4Doa5;!hL-6>F z5#sPCNv{;ZQlgjL?iK>l#d_hsK7etyB%V%Vt<|v*OS(OdmUnv`$zXk)ehGI1zozOa zgFlDx_bP;^2m;TCA1*AAEItf{;J0BrNgN7wxBf~9{(js}%3mcZ10nb~Ot}jAAo)BM zg4lo~?vHGEKvROc>x+<^4O^PrWSE@g&=Gv7k9nHGVJf{HyeQ7;2Nj#A9B z6$7V)0q-vHoq6y}C|e`}s)mU}lIc4K`EWEMNHvHr12rvqVT&oOy4I(MjyZTF5_lYY zi1Hp6^MZDoqg zWDzf3j?C|;>3mM~V|8u}x#a;unk5ToqMd~9vZtqPo%uCsL}pIotL0xN&r;o3vB2+i zxzSYLNmF}|^n^~w;JG?DWV@UpEx^<(HQ?IFTMG_@v+$fIz%yI$!q2>O7QkUPeuz$e z(F9YC%n$UQ9l49Hb_ z#3VEzsCP13a>*4)q9=r!0#UvS%VV%VF(PJoER<>|PX@^{=g%$?Yt+dJ*i_^; z?k1Q1c+`I}@@~Lah?FyYy;y8hVl+oC@C~OB{nh7me4B+J~7>i`+WM?!MZ|}lLOss)BjRdGL zoTC@Li?_eOJ?*``ux#u2wD%^<_y|6U+Qs?iI2A2HQ^Mp6{!S)IrNVEfeN;bVO{`@k z1PkpQrG~w*?bZVl#==4-moFyIb~P`(Pm`(rl@3Cn^R z>;ner%0Qx1zVN5$m4^$I@Dv~`r+n8U`V|^7GQ|q=qj??G74l1vyA=;JUr{Ja<&k}L z`}y3+Y+{zxc)z;V)n(Duby`&4L&f1?5XWtMQkEB6_}8U}m8)R+L|z36VsiN#{=OAa zH1e*)?8Ce?6SFI_b|HkSCanMm{8q2z_o)$ORJ*!icsjJhs0HGc^aC)D z+fUZlP;B*dGU^9@7{B&hvi{5seCztsP9yYVk-Vr znw1XeXKNTjCZ1@pKPUu&{%*XU~^Ul3e^<&V(^13Q5XP3#|T2w{xP)>xNu z&L9pY8=CXrtbfjj%y{|~=zZn>er#@Tx^OP_4#UGR5j-WkU*DWZXZ`c^16HHz11_c& z6_jFQbKXzihim|a#teR7As6`=INq&Sk(GJoE@x$)xyuR2Gq-&7_TufkDk$3%k$kDLufvc%B-Y5oY>yza?mA$`z*FUSK?~7)| z?>wPwqAtbI{W9`{^4jT8to@^~wSTOAaf1iS+!!aTOo>4tCaf1;nfN|g`f!)_F4$ua}K^Ztb@N`fX?RTtq=$NRES5y z_q7oB_Z`Vcy@#n}Vz_)l#c=tAilN*{;WXz(PT)^KNRw^_u*cSU71yZGTR73YNcaCm3x~iwjJ8B8+6uNrTvXeOrQ`O! z<^A$K-0ugYfTus9L6~7Mp$ygcEO1^4!LI^#>2OB~_EHG`%w=C4{@!I54*%w+qxzKL zYSxLl4>vbo3h{{5YR)Z37FV3kTlFDUJ^Q5ZZ%KYSzsn(kb;MVFSTOu0hx&fzP_M28 zhA8Qn@dIN%K$L6E7_8sc43{y8M~1BnXJKNDh{+rRW$*I)TLBw?a30!#J@a>ygahZv`DPv zddms2yMpk!Y18hKCEykjzIR0Uo)N|CBE@sE6z_d@xuhiT*`r9_;ReahTW_SLt?I{n zoy=`GIUAuAPf1i&K<^UyaU$eaq|75y=4qtNDMVIpH7TPD`%9<-SpFB0xK!Te=j3f6 zHe;~?wPLy_a{|KTD>CP&>8YWz%_ z*7WOtLhbOqXQFPug5d!PD}Vp{!piV_rIleSFuV%+uir1OELu;i zJ5f}EeS_sErm|969xH3XEdMHLrvi{p5%6y7XCV?nqcL9uZ)@2+v8zy~g=}^Etw)S6 zen1}EOXab@FFm_>qve?EjZ%54M;GSg!qi@~#N+cN_dWJp2=039k16j>GPV3sAeH^& z5mMRDiSYHetW-9WKvG$)Yk%k)`fqu`>~FeY_IXAyn;Y$KnR-^qX8*{`X8*{@W--3; zgo7P9xIAyoV~xc9LiSRSMEZ|zhoJ?+~^nq7fkSoQ723m z{8U{u3k25_C@WqAtv4ER^zYUtGgRuVT_a@*)}o>7tyN1VZj~MfM*>lV7CtmS`a>QT z@MoXT!C;^@VFR$=Z1n;by3s5^{~5i2p_O54NPp3zhSq#5V8P6TxH7FVBXd&lBQzL` z)|5#%bOUxRq7&$N>xxO+_g0Ug;|^%xW;7nRF7c1$4bX_R^@X9eAVe=$hmUsF`cxX0 zrqeY9vu?79gy5rT!a!O(13)YuvJS7bt+r`#3+I+~(om+IIAckiEp>Jd#X4kOVW~)& zs+xi-uE`EsbxQ9ZMP8-4D{bp1vUZX{ZChJgrn|MpY(qZ`PPJjv<%7NyccTk)ZNwA9 zVjWrp?>L%`7-st&-o*!Gj@Y}h#9nT*chdR0#onP6Ja}K^o%{P?agboKsTjp@)oG~_~h2OJ|x>G2oqmlK3COtWRc(4@tEq?%{%%3uB9Mhwi1!Ss(A z$Et3JhsHbVcF4jX({0^l5@2xTtl##-(A-v&2AV;=Lfv^<`Nu!z|2y0b`zV zOT*V*V3_mtHHzTk1@6~0Q6%ty_hhMDd*oj36Rz&*h4x(gx zf7~XaZ+NIHQb;l!g={FbfXQ&L%=v)i6<1~q!V(%fZcgCC03AsIWkbVHNu?9xGECt! zmZb1L3keIvfR9EzU$!~G1!vRoMx6watl z45U7@7np3IUp9F}d+v{GZ%jlzEW;*eBu-C?+cxKLZGsm6kV}ZMWj!_LGDnJ~=6q<- zEfTF`ar|XcmaGv!b=v70T!Dg0c@UABU<4MzNiw?#mJ6vaR#+Td1dDS1i$GoJIxafp zT!3Q5noh?fF>uf@m_9jf1hgZ%I--pkGAB7m^o`U0{s>KbM?EOWrd_b2jNV{r zHn>Iw)JX?%=8gf%-cbkN5@cf?#BII%WKYgQaC5z`xJ}A1M3zUon}_XFVy;dyyYN;X*q^j7bG0zhph z@9)=tn`QPEBinTb9?6WN6;NR-@FKUW)KsQDA_P%VgT-zwwwU#P4Hm;dWZOX!O=WY_ zpl|%Q*`fj@+Sifbzm4UtGl|4s++UFn z;YMXYH@s-qS2+~lE0$j@L~mKTs(*wUx}ss1zE`pot3mS zR`OyAE2;R%a=D!Pn#l2wUsL`O^?>;moZ%Ry3(>-P`%a#U={qNt%8`!(9ztgmz#(Z7 zFOh6&vQjPZ0ETeU>p*x@T+_5O>E58T5OPzG7c*hUw7WAoHsnF(!_=b8gyeQ}k*f&N zfxo&Cvyfi=$W0(W|4= z-({UCo~^D@!$%U*lgv3?2CIwf>qRfG3Aa|4Pc!P}e~@v36Y0hU3&apnawA%;$(QQ- zm8KL|3zj}=1Iui7=NuR-~%JHB~K zET4J336(7{W7YB3IuqZCa>fN%NRN4Ns#8A}n2MO`FzM)39z9s)?E_U3(2w*= z-}hG-FZ_}>Uif9fc;WL3^Zd{5LuB@b$n0^1Mo_RG7wlDI;Hrd_Oq^ljKz#iu~ zdW=1e#u%+y#TxtcxJ9?BTDjDm*?5fn4v>E%6}jI;=>@mE9Q5k|rCZTCUHH($-hm7H{TZc z`ILz-S_Bz*N-=n@O`Lb-n?qTE9D0Upw!mr)?=-lOajj_Wpi`m6&X%>j?V`TO_QI?J z0u3ENb1`rux5Q@P9EoC`Py8^J8QKKt7vITe#WELU^9Ry`r>*l7`8_kQfk$5NqM?_A#2!35k=DH6e`tOn8 zjuJsU*9hD18eyA|HvrvFjl8?PT>`99evK2_ps+`}b@Tbbx?fgE1R($w#>j#Po-R0( z>bQA&Pf%giS>+t>8K#wOZh8l1@oIJCgNFA^M7ax0S; zqF(AkPeLwUQamj3=7+=22V&s^}tKs&vvFT2_Qp>1e+in)BjE$KhXL1+aEyjmNH*7^IB{X;jxs z8KhtS9&wVs6(H0IGQBU|X_j84n9%iBF4pB-0}CH28$9IGe`(e8fgff4{QEh==p%i| z5{#4tQ^%GsajBhXC*&o;nVC2GJQGUGF$xnGq4KDf&>P-7RvQ@0W0h4H1F`_xd=}4*p4*`j6qbP%yx)4}f0ti>Zf&?^2fKzWo z-wy`hq125RVI;D|46zU+CBnK}tVIdUC|@XG7gIJti%?!dCsn?1b8}q-6nrvdHH&ky zqArzrl&?_(ef+4&Ok7;#T5)B8#i&0Dd4X$I)fNld)YNI~Fd3Y4p<-ek@=kcr}qLs(9Fm#9Bis&A>WSWt}u0n~3_E;kBiDHvNh zu$EKO1*Rd&*D{mAtw&$Lb|anAR(_7P^2Ycj~7e zxbdFQuT~+3mGk6~1ZV{)nz+68qkI z^I6w4HC>99P9JnsfSA||+72y|LROewc z`T(`plK}O4cY&t8wl$2p3;v?^8uWRSKqRInn&ukQt^>^}_yZbjsi!N}U(1-;nvrRO zrJCe6YY-8pXYWJ->@(2iw%z1T<9b#d?V9)ZQXl6gk)pgs4RUHPtBI4PqBqN!XSK-E z1eOG+kJ2F*sgSc!UZOHKS<>e;{~g!oKJP=~6?VFMp39_5RreDKjA{ zRU)lgN+SM=Tg7Y5x?U~sAL5Y9?m5=G7pE7Ga=*nq{*q3Ew}uy#`_oxyC}}aPLWs0g z)#bc&E%(f&e0TkAFI|Lb*}JuFfw`8q%jINwz`xINp^Ow z1$@v1G!Msn&@!i>*5atHtRT&&mCXugAE+RkIa>>giytnJ*;1%|zq_}rgz$l)nY{lR zmvO2M68NvKi}Iw?`6m#PpG;9MrP47&@Y1J>o4!f82nRWt$H--rL|rxt5x1R z#E_%1QrT_Y^-e*xDMrmqHbj>2B()Gv+vqq+SVMH39H(IpDPEeVIWZ|}Xlc!NE8;KC z7>*_XoohaPd?UqpY>`og!?J+*?ZTKr4fGrD^Z0tajb&w1g>9-GqMC|QxSM+HnQ0L` z(BDs5|1jS-P}kq$acOAAGrf};4|2;YQC(5Bq&>yz3VS+^!IwCc09OF?3rK+Z*GBLj zBfW%wdkk7Vj~>O>kyo$bDDW4KA~p>H@w7z+U$yw~mBnzC2`yFj*BXAJRg#YMpipNn zW)nu3YTn6VIr7dx0n^(fK(kP_sW&9e#XNpMi+1E)Cy{nBIQzQ(9b@W(5vu0t1H39r zZy!o)nfO(7*Qn^4;Q?`i(&m~E{=V0c?4X$&<_Jr4_v39KxKPKGBZr5P@iq`9yK(rO zc9+&!>B*CAh)r@a!BbGU{p3mMJZ9ZMaH7{yV)zE?9r7`BiOMOs(E#8rO}**lDY)AR zr*2=A@7&3_Bm>uD=(s@A?`L!hLCv&arCD_KbiX7I?sM&guA~Dmr{%FvcX;xfhZBf2 zY9n@UKE1iN-Nri-Wcm8ZGoQnSF>Tb!i648#@-JNl3o8_x42LAsut!VyI2oFpr= zN0DNB<>}2e{=C1R=!RP1c+?lh)0zoEb$YRBp0g`HP(zr=BBhcRJ3NKWI_K+^!?S_1 z0-W#zE%~q<`V(RF_(5O(v{j9q^B#2MYBUyj3!3v3TH2zqP1a+g5L`g3&s%2$c6A=- zaDF4m#6FM88aChGXQzUJ=kWg5!Qszc1AmyUQhB?C!K*UGDD_ZvGssVAEB6i}Xbn^x zKy`nwebVlNYhR@-WS#R$o9>sKk?m~=h4Dr*)TBl?=3W7C{t_x(@dFKmu1wQBZ~Iuz zXXSEva85x=rG)UzXJ_Z>iJ2t9-Tia8VsTu(^gqC2X-KM)I>6I=8AXsxuRDj<)8GcL*(u*;CB z4cjq-WI6a4h8qwQ3?A56?!!KC!DC!ZxVU^aMz$iZGw%)J< zfo-w$hd5Rfkofooho$}DXgmmnr~gJ8(-(&J1&y~}Um0VQRZTXGW79ZxsOC3;;Kxql z^zb$ieEaOftlHSGe3;cLmC956W5KiLz;oC6FsoN8mGf>%(nl_rq<+`f%H= zJpFKMH$L3j`%gdII*kvv&i>O6vr4U7#b*OwY<%fe@TIqd&n`ZDA7+*M9wyZHcJSH2 z=N>*SO0e&K707ds^YVbFU~GL_uyq`zlu++au)_y*{xUcWw(LP zCOjMTX&>6(wXtKnJ!odvp%@TIW>DUC)CpHO9E56d^~IzFNOMvtBid}7uf zW_7SD4X2Aw__SB8;Ijq^d)UXl9UEUPsAR7LW7z9r!9DEO0D3@$zn-&$PfT{;%RbIx zWxu+MPyAHd!RJ0acPM!mUmEn%z+4u+bTD(LK`#LNW@QInn)N0=@zZVvpEwcC2K1{* zV{h(b%Kk3Bbnyv8X*MyTiF4Dm_VI~BX?8HBQ^RM2K4Dwbd>5eBw6Q81zt|3D>S4PM zmUiejWU?yQxCMw@u`1BGg`l;n*tms|uxi-2RmYTi4WD&Doi zg-%&}I9C?pu0>QIA{w8ZFR8S4wc8DTR3#f#!NkWVpR^L zc4&m1N*A9n!VZqGLnG|q2s<>w4vw&cBkbS^J9R3K!|LFKc5qakUHXlE@8Gm{_VBrn z#Ex26m9>LUM4*m^SuLs%$KSz`cWInm z9A~$J>0KOXmsUv^2im29c5ysi98Z@}v%3p5cNk>>a4e z-o>S6BTcet9&GHlP1MjvI%re(?IvbxQVLQByHlr^4nDEwE-q}l3z>TrT%|q4i5}ua z4{@SLgrT>CxsY1+_Az;X2cLWR+{dSdPpG`t#18kGnA~jA3-qyPQA;@8JtSZ~3v=OY z_pA;+k-qhCG4yaT^bpuR?0S#7-s@r)XaIH>KiSZRQ-N|0b=}$7!zcE^X#mh2+9@2E z<|^%P)k@8RXWfD4E`8eF#ivCtHar37)k*^s8V!8*FnOq>xiM%I&34=dX-XOF;(mP*jgQkdbLi2 z#;vwmcPN_!o!UV}t2SUBs*Q$&FMzz&#y+J0eW*65f<_NtdI;i%14G=S>Dq(UQ{BVW zQ{Afqy!UF9QpcAadfCMng3%r}xpIU zE^-pp?ha(@5@L0MwW@ZhgIz?dZm)?iz@SyTG#^OQs$Cp(*MZL3RI}ZI%6jzTAgVZo zDozz@c4$n_4yNo-3L?8h;Bt2N@Y%#?2cMLT_~Kyy9O|C~j7il&{-Ek~y7+{VJA~0r z7t_0x-o|tu^quk54L5qhtqPVDqX~>s@+*7OObrwdxM0!2VFH zB7)bdFjqC2-&(f@Dcw4~?BWY$>jJY~>-IYIf+?^w)w&M!m1ePK*LLx_hfnB|O?BA~ z`~;kB&EBU^u;bKhi(V{x*{2t%-tJIJ2U9wf(!rE2rF1c+ODSDUu_?vI6q{0DW3BaS zbo>A?)GGk&dZkvyms%5^b$S7YtzN0`(@PhgJCp+5tyh{hysl zshui-vtFgDtNZwZXaP^Rk83zpeI5boGOZFguPG##L27VS3H4s1Qvj!iE>8LeGP zX#iof8dZ9!I`D+8$!gR(@Z6yUI9*tcJqKT!P@YJpg~teMuL?xU+QagDwHm$vc36A+ z9eja#vG#CYti8@2Jn=|m?N_S!tkcUbK4pr9&n~{yYVf4)?C;g#xsUDacksob7iSNj z9ehGRo478lCZLzqM3l0cz+PC*2EFV71e*Ih_yU{1)$GDJnmtMf^tGD32EAa4Lv=bZ zD59Shvh$Wj9kh08_}rlv=#53KS#Y$ptUU{#J^TbT)3ShZvMiWF%j#gJP7{-X2eB;J zHZ7}*_1Xwi%fd@K2=~VadY2(wuPXJ+ujuTF&j+t?_IUQJSPN&|3=S~HmReaX*xr5IJX6;~t zjg27B>)5ypI5w~nj*X1BV*}^p*bb#&|9jNs9&D#hZx3th0eyBHqy~-ye6-{2(915q zG%y?P5Dx7Sj)PS@cz$*qz%<9PDaFPVn^NrV`Td8Py{o+c8hf(AS_Z1S_}s^*Mc~9w^*wy< zYoH~dVx_df+1uO(DBaFF*n9u1)!S`v|8x%`t$MR;X=bH*AEqO(kt?$?hUH*}!|* zwvAYAcT;@m0mBZU!xz}t63~Ds&&C6#-NVz2jVDUGhmsS!hp5Z z4(y_~Q$f_O;&P$J9H3dYgEY%_>X=eTVB%~!bt;Ys;NYrtb}`c~EwF}y^{x)z>ZjQZDMndhNYDb z;bIM+P(u#~)Whw=?(NqJ>h#jo@Y30rE$n4hroJX?xU_)4+g+4-*g4& za7|HnYy`Z$(~vFfYb`iUHL|AOZljk%)!wO}-=AR~nCAT|4rRXvl@do{?>F}GY0(R2 z!_%g{k4U?}w?i+@bG>cHk!{<0+qE8jt?ERvr+Qzt^sh*oNNQsDfa?ZwO-tL>rVpoA zE$tmdy#^sDoH%V~kI-#j_NUr8(+VS2NW5%PsV3If!%sblqcy#|PET%MHJ#A*6fLNA zwXb#+(Q;2_>BuZjWvneu`G~yl(F@Wb+NSL)lBH^cUb-}5jqukc zlJ>OS7-4SjAVQ;j28bX&YqU6bi69Y4#}(G46eNEnp90%8e0FIK<2+J79Hb#monCf& z+CGk`?+~aRgswxB(Lws>SoG3GGS=Op7ddHl9XW{(sl^G42(ut=DY{dkZCh?6G@r_|fU{0+?C!2Au&-@tk9AzJnt1Z-UCJzD8KTvxrlrnVdJ_4JJgo7yLG*~Av{ zOlYI15+;~lFq=i$IugnaomBPghPGA}MQP|1MNalkE=y0Jt19wbwQQC>eT(l|)pYwy zvsjr`Uy)z+x_edoe4D-gXKC&#|4PkWonH37TaDMG7ZfP(cCZKzau?Q7Wfx`3yRcw*L#>)p0ydhY1k5P@=mk}Kq^oKGZ&hjZij;n+&O=?IQuj$kAEgv1_drUM zs`5IISLN;7D6Zc}UB!NnbaBAISDFn{yrCX5(dE^Dpe_&P_DuwL69L`qVnIBrCR)C| zWx77>N{?#HDEq3U?5qD1HGg$%sg84u5{8O}$_8r(GwpOJ0abRp)g`(@0iBNfolK?JPC?}|N z5TqRhX@@}C*&%&o1D|`#^psdHfwzPBtCW_t{}9z>hhC7pbcm965Y;=Ry6@l&kcP8^ z2;U*lcM$YMyDMFU_FvS1;tN8xi_(`0o)RnF9m=*tD$-qiHVV|G_=0fl5?s3oSK|CC zT?FY5(4pe&BePQJ5}>*Ws4fAj+a)C{_S0_Y3Rg@ELKerLOFR}O^f|NX#I7St=&Z}ahIC3v6~1CI!;z>RE63E1{=X( z69AmLuJN^WEiVkIw@WX$Xa4I|zSwG~^K_-Q7e!Z<9wGvruPQyexkL|K-Nz>+lj^DB z)bIuAx>HB}aGk#NmZ^+s>vIqU4jz{)4i43!p`z-k;&dCDN*UPQM48;Q|0`*OQCw23 z?>hhCs^J}7HN2y%hU=?U!*F`2?(NkzHL;yn6Yl{tQr#o9;a(5sdmlv{)%^;j>{m%M zTx~AXDL^L=%q8%eB%(TWS>>UwW9UhdSg67|H!_skQ!o zO*NwNzgSak_b3I}<&|n{=X=!FsN-&0|C^|=vubP9XQM>MYV6sX4jVYMzooXW?Jeq~ z>7|Q0Z0hJfkUndlbhJgvX;LxQY)v^08%16@O)sbeZXye2AyG|q)EP~5y`gKOQAxZ@ zFF;-vG|^8VP%JyTV!81zQY>Q%&?Kt^f^4hfPy*1|f3n8;KZCBB`qzVVt<$T~OATM( z`0DiPsD#G-%0ZdtD)lt33l(&0bT3J)*#mRiAe? zZ6zvz9GoKurFb^7+lhL8Pgk;&K9p2psJm6#uf3fj^*d?Y6?tlEXrQUw)qL8S1%XSY zL;82r+9k?(TbmEma7$(Uu2k(dwfX4m>FC|esOj-Z<3d9Rr!H~5P=ViT>I(d)>CBS? zO*WoU+Bd&bX^#Y~OAA-(?lYj%75OBxv%A)wriJY^9;M>P1SAYLk~iFNVWE)raZg(> zyUqU+h5(W>Hkx^>fMh{ttbZB{CIWJ6#CiG)_5zf@M;ZTEJcT)nrj?~+3kaiz| zXg6%#4v7$NA7Az<+dgjUq#?I0n-tPCYdimKneZQDnSjq;W|`F+x(xzievii5YmllQ z2eH3H%6X!Dj;^Cu742u}UW2F~Dbx|csvTO%Rea*CpiM)!Msr@XmKshZJBUvIaPP^} zvHv!y_^)VE0be^ic-B(b(R0qX+0B5@--3IAi_|muZ6Mf@eE}iPqSh(BbbT)xc{9;^ z9f+6>B0e95SS~3Bm-3Y-2)eo+rS3mAH*W^zTssujD>7p?3XcZZT6gF^RF4rI@RD2F z+>CzWrEjJCdw93x`ycQQ4%s){@YgmXeiR79WG@17_B=W_%=XtIcpTV>$}p65&P&3-(tMB5 z_yRrn_2H;u%uA)#?|#fm7;NNsKTbU!jTSr}MP~v%YIy518mz3HbE^t?3)P}?f@wF+gVgH@M7AX#u# z4eqIhH=Bq+z4d+XYIHqQ@39$+aVXYw55*&I%}cw6-9gX8enEG_<=qKYcl___PU5|` z4~|CAC5RaV{9X!nBiJXO-HzBhJt}yh!fn13yd*tPmDr8or4sw(^Ytpb9r0?rZO-2b zempXiD7Hw`C-}>oS zlSMJr_rl?G<#x-QhhInFr1>5A9d<)e`X8kh{{8Sp{kW1p*5u#+_y18TAmnrC<>SA7 zlplj6wN|O^>o3hx3x*{}(#C{s`o>@;`u+smCYSUKYlJ^7HOY`S059gSTES1u07JB8 zcaC?$1WJ`OrBLLBLyJFVvZ;dcKhZignT?~-i;*X~1wG4j9o$2WMM2h>oV1K01M-?N z%h&mxU9iGP1f%e#5J614`Dzr^4vb4QE0gPD-IFMWS3 zT(1O9O)2};Vs>{NF6OVw{7+@iw+K4G7d!=a<95WKRu!^dds$R2ms&T1YtZbEChiz1 zlMGjv&0opp|0V)Io1rRuQImh`^6!rPyDR@TX*Hy2e^9UfQEGkixdiJ!O8<~s2EvrMpk=Fm zG!R}ix*CZ9JZdBB*duqDxLdqK4rHcWI;nDPT#afM@G6GcmWUcjJl&A+x+t|S1L5AZ zsb%=6@ueS!_UUC1{4qlEkD zUnrV6O5`^B?n9^*S_z`EIt&W%EPc2v6&|fY(p7k%|M+ho>3fhb5R?kf^rinHzkkUW_)F zLzm&g1n526!LWX9@$~81L8YC>d0W!s|DU;c+ioLQ5(S^TzJkodW+hCBB6U}?AZ}i5 zm36UY$yF|jCQ~FxrcEO0%%min%(M2q&L_;v%pc4@oIjbKADI;aATJcyU?2bTpEI6I5{1zb9vOGlm0Y?Zr}^hqEmRW7}A2IQGDGY)5{>d-9s zONVA~ewAhEbIBlaE_vzbrfC|gfK)e*;j;ww-5zrZ0#*r*X{{$Hh&ac3TTds0kq`!e zgqF|=cb?GYtv4Thzxm)VHXok_C(p{i49p-t2^Q@nf=gXbL{%ob<_PUSC*%0&$3V8$YMj^J4LoNaR5DP9Gvr`1y7>K&a`&EP`5kp@UCnd}!o##5jESD($ z9!*`>!%_ogWTO8JMQLUU{L)*A8e>zZ0TXeqatS;TnTA2Zn1+W5r(qLwMq3i&P1fqn zx_XswY68*tZWaR<*+t4gr^%vta~8p(XhaSLfh(xNlkB!o6M;JB%A z+$2`>DUPd6jp6n5x)dT*+qi09j=-aAQwk^OX-n<9r0{`qNLC+V8vz^?%ebakL#_sdLMUiD(js~VeidJ$dLlvMe%6a}F}txm05DA2nC ztAW^o$9-y3EjRs%{r;@eE0nv0Ebj^o*mnu`_^M0@L)z@rx|j+-;v`^Sa(w_`>#X*@ z)j`#*mxg+BX{hU^A+8$$SDVIrHeIch;Q5(<_01Oe#~LmM+%$W%M%YQK)9d~MJ+Ahw zTC`Z)vAf7s6f7xMeB64RoA=Zh8-zAH_gzBo3wMS4m}o{oG|tHMBd#|iC#be8i+R`p0IE6b#F#4B$8!cuT#&V0dV~z-shPLQ*FoTBY4XC3gz( zjIk2AXT~yAf-+JcL`#P+G z){JLfvrr(fqB5ieR;t{`eZ7ylOpi@e_sn|&bk~%1 z_9EJ=_AI$XNK?lL?4e8{}0Jp0zJ-D%VYpe#AHl?(JFsy8b`gBH(xQwBpf_*rGaNcI8 zv!XWu_kY@GS>olj%bbaM#|eEufM2tK(BC``Kd-=T=%+UPd4X)Yo(2(|bF{FJ{!+8n?jqN_omZ7W;W2yZQrrVEhR{tJMZ5sSou$3^8%=Fr+Ty#RS$cJ`tuid+I;1>J~>1pAG>EE1(PZ#RIpd zPu!LYQWfY(al%f1zcbH`y$3pA890-GeZ~)2p@#Gd1w71(hUI|Jr&C ztf>n5^{y3Nsjf+&{{^rKr zXs~UTT<*DCb}F`Jy*k^1*+qZii~q=_t%} z-0m6>5Ea9*kZ=TYH6c%E^S_Eb5}FXHky0(ccB8&{+;^w<+zik;t@3D_NcZ_XQ4OMx zf43gFu&&imNQWs;)cID`dFh+|GZ&4fGQZmQw$y-SWY;r*ODhBXt549!vu1ONk<-4O zaqNP~-?O4QvjmHa&P+^?2Few*>7k4S03iF#c>D<%$@XbhAJ%%s8TWmA%vrx-3t-kz z^eN0gVZHD>DiQW+@__igWuE&n@|Cl@``C5iN86V8Um%H9T?8+vf%=>6lVWM zd=~D5$Vhs~EywZps9ZG+%kYgb?sk$^`-CovV_2`pdZ&~DN8nVqgi|y0kbF zt<+O$dt=|4Q!Pw{sQ3>aG+ekrgGP!I+rNAedK;855_-@#B|)C?(9lPYG=a_jEAV;F zBO$DpxeIH(Jt;D>FQBEF4>nbmKUMa9-uJ`Wx2h%V(F z(Iu5gMi-2+t#YU;e8;6(R8aJtZ*YTw;}4C3GRn3>noogj#{~7GqZL2pwA^RjgUP8+ zXx1mIRTjt0^hsQo#;b1p+@Al%#yh3%54ZnHn4(keQp7vJ@jM`i*7{;J;vNa`OT$AKHV( z_AfTs5nV{_i0-B)vfgU7;Q)UrSg9iCmm}n01P}U(EF3)*3L>A+>NuE=t5sI!=4vd0 zjt8{pb)SqwCcT}CIje|tj9^{>GZAP+DwLOVTy8-R^ugmHyKv}hkF(qZ{lnwzgU8L+ z9xw7ojypxE8~NSiObQ19J5kHI9}L|^U~#>Q{{hSN*a-~UF{b{O$7gN;#wJ@^fj#yO z8gvQukQ~nKc|ymFWXg+y=gdqk*!O?x>n%;kr*4-Cm0fkb{pEb z-QK3KIf&A3Ki)2H!yLv|yrRzo)24oG!lo5v7enR;=I@}uIh)CjD&ELWmYnfKYhx#v zEbc5>r9_0i2qv)A0ggLxro5JeYu#Gqi#1VaK@8{ig%AF7+%(#g_+c)d3sC zBGAPU)GU70kDvf77Yu;FGA2PV_bnsb-p(yWRTPYW7?xp#xuqL!=azOg0lEYl7BTBd z1HM18wztPl@Q?48EK1>27n7|&(>5-bAn7g?g$$%oVCFEG#HV$ZLd%HzS;j|j*PoXu1UY72zkOW$)3g+_#=q7T}=YH{xIs<4D^ z*=8r%nQhtx6t#ZGPnf{0)#O%3zzkd>>kR43_P-D^(Gkx#?aQY9sOgIt3)qQl9c>9f zdlv!Vvmpmv|IO8D!=0(yDnr}!Nb%0I#bA<{jvswKyXT&yChEqZOA`eI-TfZuHv3dE zT^!H_J6R-ZzQ;NV^=V`+5!GNrk34EeJ$mbLb}=)JnIl|K&2mSE=RlnXp@Fkb2WA=^ zLOqAGmu>1ee@|mR_P7mCb679i(eE?dxLnNWR3`oEaf%RHZ#_;0e}G8OfelwMm{{E5 zRN*slepv4=91f$23Uo9?^?!4MiHU5)+TJ$EYV|b1#}w*2PZog;XRu8kJAUAOl~0V| zQyUHkZLudu*p%Zu;=nmLA;o|{2Bwk&a3`!N;U$l@+$N8<+mdIz2em{0r|SvN_;Wr0 z8>eB$5A47hWHbO$mLIxY>Z4IXhCAK}M6iBRtO7I(YUAUPkh%_MH@@jRoKX8ah=A&6W?LL`GF>I*fq>gtHt>9gtL(Pm~d!Qbve> zfil9!t&Y}3h@>t;YN5GC!aZ{b(II|~){(@D#bOP1^mVO(QqsV(Q!h?kG45JXN~rnJ zG9N`^|0t>bW@B=3G)Ey%@EH-Y+21T`u%H3qA8&(1aaNGkN zJ@^D+s9%(UgaG68NeW2svE6DiHX9G=G7)XztcwmJ%~Tm&$>|P}ouhKdP}tMGaFvFl z8Xrm-U=imVq-w}ahUMx`RU;w6a-;6kMM|mRMr3x+PVQ5jPb%F(KA3UyYtM8Esbm-V zybb0BsSW+}y~Ju?W1AYS8e3cGs7$&=I3bL?QOw-nKGR2X8_wp!$PIUJ&Jj~uwOwG z`o*t&I&~kwpuzwfRRc6@m}D3_hER7yKL!)m8E{|q0j6MYwdy^!tElMi0cL4^?Yyr> zsx=Etf3%@ama$SkUoY}|?tL|H*h#h*Q{)XHsN!=YxmM}EGLnmDWEV5JWi2db-eU$# z@iVrE8roh$-4z!5&|4RDfcbn@ZM9qzBcg^b+>E~%Ofo~q<3N00Eu^x+!!RbUDZ7GV zFw+U0$bh6ZLWjxVd%4sRI+C#-hxtP2I2qKLD!NoYKh5W1yE;|SgXK*cj}uaz#EI5~ zR3-FESuNM~uNgZdYs@Tg>fE`(B(t@p;%qU@Ko^RcrZdcZbr-NT7{grzH-i?a8CyVx z1-xie{plU&Rz}M(a_?3gxU+4~b_91lf8q$fE#|YW!GsS!Z!P9SSc$hC-(PTl`*8pG zf8bB`P|?=m&hGxv;m+}4pIFgE19=7J-H`LS?2)n7Bh`gOe(cBA78EI}zc5Jv_Nimb z{Z6{T&o5qVC(i?aHUNZY`5^$5Tot|xn5-p(MHOVpvc(^3sy3beomOho=?^rZG}Vf2 zmD3F^ke{ULM`f-XOt_bk@n|xn4kcuJ53cta&6lgHT4*PV^uODeD4Xg?Tujyt1Dem9 z8ybOCEYAJR6i}@oS*$ex^Ev^f;p3jWhyYpH{Idlp<30D@8A`1_YVu+BKp?#_P@T|` za-fon5WtG7ehNaxV}Dj4K3TQ?#ZYCg79s;@R++7>37^h0$p#IqWXJ>Cnfk?_YxEP{*0q6^ZUVr0wYxt_7cd<-R{ z6^q3dR6FFtH6#z5gThh34=t=#mqR|cI)=3C@{Mj>SkLRHGrUw?%7}|BCeXvV#t~) zK;$}Tjc95?w8m79gf*9yIwIisyvRK+5(~*wV=$H~#>y^K#pwxTwIbj~&Le^~avm+Y zvpy4@78pcys~XvFFnP5)6GbtfrDKWdk_h;isMcn|YGo#-XOWAc5{Q)$Dg%MkOK zLBdM2IIss3t~c@HeXw7bba}^q@NlhJ|GWba70r55s56lj1Tvn@27tqP!M>ly(c%o~F?)so=QZSBx zeViX2lojD7?YmDR>C$f&4Y~rnX ze9s-V9=rbRW+!Xz>jT%&LPUIvLZ3)`!+K=AGf;(J)g0@S+#OYF$q^W(x)HV z81Zq1QKU~l%~(ITtUKYdOn1WE@*ECX#}0>VSxg{(I*Y;|+F@?_X$FyBlbMz!T9&0r zpEjcunX?7q&c`CQ+3nMN2-^UHHf>!rd$o(ktII3vd`6p> z*YA2*()u)`7ni-}<+U|-xcd3dnnk}=wy$xbifKmRsPZj<=DK!e-K!thy2TG|`mXiH zx&wvQgTrz5yl$_7-tXuEmq0rYvbKJ1={z8!5{)N@%&c0fBo43wCOLY#QxJz^ukw>; zDv65d#!`d4IqSP8c{f#?!k457+2s_v`Y9m$$+7FN^%l?y7j;6~1H9T(v6xwgz4fiO z^+##z*r4`wKCyL-L2ZBFIF`KEvUPj=c-S`R-P9JJEhB?>b_OEwril#Yw#qex+cB1b zm5x1oKEX`jJnHuGuw~Gx`@lVH$e?r25rIsXt+X-*#e3ioxs|;&{zvZL^FN#9_Vz`A z7Pqa+FYKiHkIwCF_hYZWT!sJBpeRgAsBUk)+govadylt{7V|kYgxz^?y&?Xzx4xCO zj(Y}uRB*~SuK`+V+wM=;^1SwY@2qw4^7@rkF752SIM}0aF5jL0-n(kOZ2jJQcX@Nw zXjwgnQyEdOHD1e6-M+mwtJ%(rLiJ;A8Ja87x$Snz_E=@SxV-4Sd(*3NM1y|cDI zc0T?i+qvx)X%0#)LzAp-Z@0UJZB=G1e`o7Op;}DTzA+zybk%BG+ve?Um6+!3Z^XRa zeL-Sa()nMWUDj)7*0%Yi{-_S4E7@DKYTb5tJ8T zon8JG#T`G%_lI>{TF>`~Ro>ZAk*)T1?@jHhb#eWw^{(~K+Ecl&PG7!SODW5g+Pmx4 z)#>cReH}$ht@9oXy zb<5gS8Cq{|YG>Ag>Ot%G##!yWc71wz(L>(3t6FbgUY+;atya@2??f>P==T(QUtZPT zw;}*l!>=I6t{RkAAKtubUG&aRFK*shd;1Aw*DhY3wR-idTH|%=+BzsDk{UNxXCE-B zWtDe!6DjZB-qfz*Qpc(zXQvk}1+S{-;<8u2YSmu%-o3$Ebp1dM0PL>n0j!kPo@(Ol zP3v8ZjDy`I1+0O)lT1s)yPr&22Uyklu62H@2J%?NoMHoNxO5}}zpEO%uGKA-aC6aY zUA=2uUbU=a)!zHo)%9tkc818X$~$Vr|7cxZ_I^8UUcc&HTqaR+u)lM7O!;E~#+Gmi z%a83>mqymGvV=Y+_u+;2#K=AkhVpxZg-J7$SQ7w3e{Y&g{p0b zP+RE}7^cz%RQ}-E^UFmrUj({px=7i24B%eAD+1dQKAP@+H0X3aE@xEa-I#uEIS6ql z=7uDUIZYu5Ak`c)%8&#S>GPjJ$^|VldJnXoeP%tPs3-{?pMQ2EA=1kXV%r5;WL8I%i;6uZ9_wX%Vt}L((9)G@Z8#% z(*>RiRqn%gR?RDi1B;~g18OlCV}G=zpPW}NU0~!A_>3g!tfB|7mzW0=1}QJ6?j2Bb zwY*)j$|R(Z4v+3EMt2C6vL=WIoBGTv;;khlven8f`U6{ByDcHfj=JE&23{MX3;ig{ zfCY5Qw#szEN^}U$ZNLl37(R3O%;3|4PZK_O@VSRi4?Z>c)LDt%&`CeT`&(O zR*A~a03T9Bc8+{?dcp+#hCc_%Cj_R2GNFQFf_cL}(Nt=oFeT~&-SihxA3h7Ydbgd;<~(NmfEC zXgczp#)NvtJ*r#R3H7DgR5e5Tr(xUz2C<}}bNGL@I;bY+fPH&?Ym3(LD48sgD%NU+dQ>^QZb=;{Fb~scEoIaP9+HJ{@2^F!8$^ z$>BJceYwG(_)5@qf*~Qaf0btZhYjfY^I{F4*maD&PGaQsUzmW9_X=tpN2L0h2b-tB zkB}}9j$F|blPnrpvb<;4(Tblf(aGAphvIYF<04UdBN_cLX+Y_}MjGEIhPi4Otlyp= zt58iKE!E8Qx*?hJl(PXnh1a&)@W_qSr=PMxB=b!`V9~(|p~EdUArx#2=Bmo!NvUc= zJyDqEuL?eQ>#+pOUX~cc38I7l~)uM_nt$Y1T=j-94a9%NRKXc)0s2__n`>~0_ zNr~DI<|ndR#Z3B9=e7YmY3Ugz(;n$Wk* z?R@T17tAN+D7>U^;ZlRFH=j2@Gqg%V)Wz zyH{YC1(zB^&CgjpNOC0wk$iq3JMDsBK=u~7Ng z1iIa^V<}yM^;trdNLU9iSw_McJ{F@*9eq>-u+aO*N2wRSb~t-<^g96`1|XjP>Y=os zP#3=*Y|<28Mw;TM3gCK_wpoFDis=bm>hJB4eHhTs33)c&VZ#laxh;lQa9=U?d|qS; zo5+AiaFmluAg?pT*Nx$fMcs8bwhR(>`*71h@yL>M0hD z#2p&y`WpWJx#O!7TDYoVz+DX^vxW9Kc+w{$0yi^*7Bf=^&c>(lTa|vFzM5^oMiASR zCf*=PJPXu%obv%#s@NHwuM)7|+i08}MA(31D2^KiWqJ$*b*2<5(c^p3qkFFhG?9Z? zujTVWezea37U7?W1Wd_?8~dmDrl;};n8_{ukY@Pffb*fBnc0udY%$B&VA{xsiCiMK z*>joFi`}`tK_das#a+7P!6tsr#SlyYz)}_tkQWSGFwWsQB;1dq8jXDz6l3+7S5zBm zxS#xCP)unO2ZW~1Bq!-3$>G`=-Y7nO94C+K=K5G}mOfc)t)D5Xv%kCaN!h(T3GAhh z$!Z}R&^l{!cG5R<%S9-4nqWzpTQ+%d=myof1sVkMqDuPohItrg7}kt_Qh__`9!K$- zd7xdG$OW?w_qKbL2zU9sdCj1a>kxDi5xuAtKDxIHH(X^T%C_n^i8gpky>6k!xCI+i zT);|n=)wpNT`4c%edWcB4-?~Sz_}VOe}+LpmI+{Cg9m{0ss)=+x!Czu34e0a2|O5Ee`zm*sg;A{BC|pUSQ$3R_LC-bsIJ>YpT} zEeW4|50f)_x3=sZDX}cL}5mQ&Z_5nBO|z5+a8q1ox1fB&E=ZXt2ww)_mUOFj(|7pN~fZZa~F-j$+6%0@2jJRx8y7 zQi$kN#l(&42kM&~mV|qDsx=4LGHddX_Bc!%$`g9C3tSJU9nQPJeuj>Gv7=0}Yhck? zxzP3DT`3S?&(#llu|6h>7vz|kSbI%^bJD4a#OBuO|E^DaLxL3 zoxr-sVIr>@4So6rmtAC8m}{zVE+q7u?EeJ`>18120!&~I^P-5yN`)kt1i_>i_<&zL zD(VDH3hX&{t&$NxIDGgkvPw=w#YW6h`<`R4!VqfavW#7?R*xoGt+qejl0r^->3#`b zNAh_y0V3;@930Z+z}#&^)i1E>3kF5=`R$LlML56TE_S0zFQR)_Z`iAuc|lPEq9fZ6 zz$VoPw)e!@5L%{pmY7qUbkKG*(_Ts*`7uUL@MR z#!9ZOT=l~0gRFcJ#t-Q=qfPeOw!6wXNZfIl-6GD%nR<>1NstW@#J8 zpk$=9m2EXJB;rl4<#odgrc@PDTFc4>=R)FD{ivkt%3hFV8{gYC5{v^mtfs&)p$5`y zYx^VM2wy(H$%s^hXEOfk~Z~lEpUJx=B5Kna_Rm6nV zE-)z~Xdw>u$jcvq85XO!7%txJEP5=7)(b#PXD#^Akk4Lt#W|e*s_E5MPP!u0t0Iw2 zCyqmc?{tvwoXSdY8YL1fc%x^jk*cdo9+5(M`ev<_63r3%Wi2vS*dg?cT{^t;1s~); zgC$b-=S|@ZFsH?000WSjbrn@6A8$V;^1R&CiENxiW+=(8W90L%QLSiL#%k5pUYnJy z*Tr798P3f%u;jLkMqGRk=WE3j_SJ-LYdJDi2Np3t8YEuq+tlUeasx3SEl5#cb`wb9 zXX9tGY_qjZeESCgZ$Oa0>uWPrLsW6Hg=)zvQ?K}$^Z8pH30?UC!qrpVG{|zZG@zG_ z?-V8mQn40Aq~goNd!>V*9?zw0>6x9m>zf7$BQL_=B*f&-Py z!2G73N{nHAt{<^vBP)zqXIN-Buh?`+SKqxTF5C6d^m=b}AZF282N>@3(5`e*Xaz7_` zL{I2$i86#FI(bkfIgs4{CWazG%lyfZ8m3my;A$3aRJ0r*l`A?;hE!Tu5nS&|fZbhe zm>gkZ6&AcBc$ybQ@M{Ke@uzbkH3#K3#vq1V(T8q?f4xbt)zHCOoOmStjJ=5ZDR7U7& zfKX@CP(PtNy*b5!fWAayA#+?569Yvm7mB>1J*{zjyM*Jj9Q+E6h`!jH#LHD@@N(4` z64TyUv}Zq4q?u-oPsu5#n7<}MDtPQHD@__JgDk%#?&74i5%K=LP@sQd9j0n?d%@b# zOkXFw9Gnw&5+{P)NF9XRz?>W)6a*2@1OQTxwBGHDO`vScg&|%8hA!~`SgWX2V`X3$ z_>)CyK^iLoK{XhS7kD{Rbb89%P0O@YK&?YK+5}-y%N?=KEDdceonmej3>6@gn8B>$ zS8J?ribPr}VBZN&u4Zetv|&l(ejzOjoyPMKcj~8W>b5`S8k2CNNCSQRZdQ${s>D>* zJWa{9CM;D6eYu*`O29Tz)|R?dsG!G9ZHe4Yw~-PRlu|L;a=bS6)~#JJQ;mZrp_665 zSY4$&$dT152Wh`x+An4&%et%Ehr$V2)5^R_$nSrGyEkhHK_$PoRfxfsBLRDbq*jF_ zhemaNt^WJXW3xH)-KNpW#v<6W17XG^v0AO64V2ul$%6sXM&@nu#Hb@>6B1A35?QuV zb{G*{g&%-xoj{}o`X>5F3_KjbduR|10#6dfviKU(hT)Uy%%`Gha_A%trUp|kgsYXF znJ1-Z+ub~%$t=T7!}yQCh_1M5FtdMNEoT^;0s8J@l0|J6SxV4ydjI@n_ZxA+y2BH? zmJ6va0U>HsI4_Q48!-CT zY_CaJO%58_%EqDD{&;J4K34TiYH~G=Bp~Z#95V}fY@@1-nwL^g8iII70V-RXV^W7t z->{1(yyqgtd*~OI!{t2H>S~qSaDpUz7oDHN)!;X9?$IfAK?~j~b)|_>il50&ewW&m zWR*Ubx_ELdb@Aj>>XIc6p)S_Xpe~*qLBZ_fVSuEua5QVM+SCZK48R*0D?ITN7)byF`k?Y3=)?nV{onU^Y@P96iJ$ zk&gZ@9j6-9XLJ|wDL^b+WWOj_zOxH7wP0O{j@zPn9m(AC+O`gceY&Z0i5?ZA-MhNQ zB2_A@97$`<(%p=u^(Qd>Z4@BAYY|qDc9VC?-T27^W0eE-dgZ?pOK@iz?0a&{5$u;o zVQ%>w?3Po=c^(~4=%er%{3JdjI>UwbjdNoI?WPd+V>GPq@MBM+UM05v>|YJ{-#K)C zhEz9W{D&rDbW;C4W^^5{@nf>&JlX;kUsMfhrsd3}sG{}+c=o6Z-4vqVh&j&xo=Al9 zek6LO;A%gq*L^@{FePw=|Axs@UY2TjCa(jz+yCP3ssnBw7sp&%!}|2oBR& zJK9a)gZ~Sl2!9BQrIFUJgo!rQs_0EEft;kYx9Ojlc z2-yHUZz)5`9u!_Gce*#qo$d=mc{lFomh({R-S5J_bfa5Lrn>8?FN*7h@4`*lwl`(7 zRkm3wYm_rHFWj3R(b0)2P%?sA?xlEOhoma2Nw@l9I|<+Or8dL-V*5`ECkyF^hSend z#I`@0#TQj_TQqMU3M8it4sPU*U4GwGH<2 z7Y+yG`SbWq@h1*2o?Li`cSHjwAJzs{jI>c)&!eU2iFz!0#7@S6Il|Ytquz|z$uj1j zV_=`rt%Ermn^)MNvO?4bqsGw&*sjQU&)urtTEu? zvkKB|84N@z`brG!fLDfm%HjD_6_oc`!E!ZQu1L?iW8r!{My)gjld(-LgCtgdRNw*(4iVj$&eji)-$4L=WmVmvN#XfIFOo;3AHJwOH@Gx z6c0LqA<-Grao7)QK~Z|NY@J8l1e!#3MvX+xMlZB!cQ-}dnY8!$I39dYk5D;SHD^Ut z5g3#Wt1~NGCe{F6uQe0(ogyNh)+ghkzK!)HtB{FLYQSiy0Lzp})Eju&b1H|IEeM^4 z6@^SSpARriFIXpmClpCpJ;h?FH%I1D^1fZ#b@2reFQF zz4rQqdInHFVgS_(xVfLt+f~~Ph!sT78Zc?EWDb~4q6-GM`30foLLFwidYP2s78?B^ zh16|dpPRbzy8^gME*MTOl}TaKC)R=~9p!)!8uvB3P&BvDogRcZ7;!tX>j0i{gdc#N zyE{^MZmK*nnUSZ-Iq_>df(w`+)Tl`)s&5=%2{XWCbYP(6xOFaCjb7T6(alLJ`PY$QA9L_%2v0DqCi=_;G zw+%#Ru9sQH)MKy780ESV@~H(|* z5+A_L)Qtm1a!jh_))i zd%G3R1UvC0#}NcAy5d())k7-Vom-!F3)?Dp?;DK;!Z$Tc;Ttx`2MI@El{y z^|tWQ!>u8oIx`2@#MJSFq7nN$iC?G<6g^WBk#lQMw8hXwjIk3H3aUs`aIVLza52;d z8Sut6IgufdDX<7l7xbW@hXqBdphy;UldtB!wv*59X8CHxqvxQj@(E%q*!;#eB(J%p zf#-~QceRqfel$AOqre&UCb;qd1|D!ayi3;oy}X1KE8$bSI9_eV1NA_Xc4!+E<&Th5xPs8Q5%%4bgOKEL@B(_zBVeIkBffop}itrKX_1bWJpIeAu~id&cyS*gvix47umggmX)!tCCJiUFKJOSjhci;0E@T zxsvJOn-)F_Ci-2ojmx>3-^#y$@&Yte(y$iivF2>t@{{72AyGQSK8R$sMd7=0OIYJ!i)i^p{d2W4m_Z$pNQwii zm9b^e3AcxqUxYua)mq6=*6(pKY*?I&%NU$EcT9pqh)k(M;aD}OCtGulu z!5=1QPFehOwTftlEJKYTd{#jRp{k~5KQO}xy@V6oz!`jIQE5$8Lfz94OsrPr=$5Zo z^NuR0U}XaCQ_W0*;yW^LhK14TM8f&lBX}q7b!U5u&zZJXIDf(nQN2HpurSLd#P2 z8p!m~)CF(UFUqA7p;J1cLpq9vXDc2uI)<%exm2o_tkDZIZfHAdeX9&g-WV8jv|Z-A zptI0(Irh#Kq7N<2O>OwxG(e zBIG5Cjt$;8{4NPYkBM+Y4dw93?qHU7^6}a>IpyoS0$is&@G1U=bFYa{tZ*@KxUb>V z8cvb!v*u}1ucQ5%NS8UF>(GvAQXT};(b{EUJ};1`_|k#;%rrW{FuEDNu58OCBonjk zB}Q0+B3HC_O5jB#5;h1`pmG6w6@*rKpp?E&UD~nfqMLl~Rc@U&VnV)i>x0G`XRY4r z$YTZNB$ak-cvzJV8Iak8+R?stSd0fMpI1j0laAf(42$c--i4Q7*WGf0=hb*0`hMMe z%QAf8VVrp2!5!SVM_Pxtf^Xva@*P3_t~<)C;}2;&@z8OF<30?oXoE|aq@Qt^njh3V zPpm8*IHS&V3HC1FIU$2$IH*lppdtiLfbti3or$(5f2M8hN7}~QJ}U5U`;iL&wl7QG z_GxD92ASJ_ZmFvcZ~Oh^Zia8%$82tA;4VbE9a(ZiJAAzzuPD3!7rLcAxXFS))<4}$ z)I3kPe#`{XT#lYrh=-uM{c(Fdv$jFAeCxl+k?rv;I$VmDV?lKj`kFr{MDvL00}~#d zJh1IszhDQ&7vO8bT;2M_EWRMOq`FOCI~z|j$rVm2$_>|3$XQo?_E{Fbwy&^8wB`Fy+Kihzs2-@f9$ zyYL3w-{w<32s~F5Z68dRVH^ABh19uy&z{d!Ie4ol``Y1Bedv=g<)T2nu+ zHX{hWR=o%-lLw085xnvqKg{k&zyEMJHU1~up$8wxD0BkeG=KdSwA|o_$KmXEhf^-( z;d=t>KxLWY3wu2F@LXYw!z{X$K8q970HlXcMc{H32+jsm+xIj4F#xAuQg0y#$yE5Lq~CKGc`0iQzFHmw#f%@g^GktXTLNxp z$^&LVs9K-3E_#_>Ok%D2M$P%T4IX}CG0pMUv}{?^yuekZ==ksK5kD0HA0uK{D>!&8 zZ^My|cf!i#2NtZTTk-M%w1!n?z-e5jj9xbvb5Paa_D{tKHqu{{ON$rY_A_R@#3U@S zqBq6qfCU@IO1=gQXNuA?0N)wc+-)4WT9~Fyf#z&b6#OwTVH-IDq>Ks$Ixh}g!O3z0 z`koj!s^}VWB{k0GK}D)BYjn>OQsInRQhZH(M0U z6SlVK*wkw07Fmj_ImMUN%@!e+sh;|5MLBV&Lc-`$@tl<^^IyQKVGIv7x}i-UM*?Dp`|f%1WYGm4)7S%@cHbXdq%giwh6EWS0}swwa(!A zv(Dh@+Fl7Z!_RC*@FcwQclQu((s$qDFK;Du@#KzT5$`D05j!x4S{xp+-(55447-GO z#&q6gqt&W2q!81kGsd5~D{X0UWR_sMGizp!;qVQ#Or0jZ>#|nLx5YmUQtcQ+#}BUU zu|ZqW`w3mE)1tcaF1?4*KsS|D*v*(@FpzUb=Dn5z7ge>7q04*a$@5+sWA-BF;4gKT z>Z}IGS7QfS^(~v;uv*oS?xiyeZ|WP?`be%;=*W_OV)ah5t8D$2+RD!rT+0&Y?BpC& zKXp=h55^%N84S(-Pd6fV$19Ns@2fULL%GS$EpNg;DfEAO7iCi&QQ4Jy@qXbt;anqjg9K+@YtY#wT-3=gNpg;orK0(=&@FoTlP$ z>&cj{Rx{h26AI^MUoDtl;A^zH6B61hDzI6$md}s!`Ce7WR6qC3ah!oziKHr8sYzCJ ztr2_9uB+yCeCTxzs!z3S!;@X|?FuP;ZnySIrB!RMw6|(qTwb>?Z!Vgvz0zLh!VNNQ zP%5tWN~KKQ9%ioimj(BOZ70%O;6e*V-{-L$mf6ChBPg2Nc0kYyi<#PV3I~|7#~~gZ ze$9Zb;A|fFnUU>G`LLL2Iq13itGn`D!j7MDggpRFs&hBx zGSyxH%L<%2HCBM%AjP+EE_mIs(amrHlh{1Hy1`(Xvw)vVUka&ce0 zXx#L!Tko#l<@4L-75C?^@cByc09-?pOyJD8y9l-&DKMXdDS#_{cgl-guF60C)GtW- zHrhNGOr&I?@te7p6%;ezpk4QJYXmtnie>w-fbW({rBcS+EoFQT?;!Z3n7Q`8W_&Ka zenY>uwiGzZ2YAMGL}unpr;aWIHM#fP^Q6;e2RwNfP#bt1K4B6O5d!bkjK!CXB|)LI z7a2*)S>p@vB|S(BSFlh-p)k^ssKqv}-i8&3#t~B&*3DnJIi=>1jtDy$>Xuilp+s_Z z*>O9gZr2(rZ@aP1V4)sI7fA+l$f2x*Qx#lMV=1jpLvg0rFE zbo9XHg440A-O1@yG*ZRVnc&RH!D}{`4?@e*^s9`WtMIAc5{nu4;Pjq01!toNGoiWF z=-2RuCLz_)gOzkGrt*vxz%#+|+zpQ1;V%p)Av&Zj!C6yqI(%S!O78_{Ey3xX z;Ow(c=MQZ7P#r#ie#{jEJ9szuxIF|=mBn|AyoVI>?l81k-K)RZtT}(k=fPC2Gk@q3 z#AHu!_8>T|!ER4*`c81x6Fja{L{=uT3V*9i?U)pRT0F4rV(&mO^U;>9(Q7+zmwBti%50mBh(GSnaI($ z;H;4ZHYX7PSzs3|mzctnf;3mJ^&l3eG-^>G&Z!z=|us7RnGb zwjN{S2Q%TiJN7nYaX5EO(2$-|f<_eg^W}iF^MhMO&W*H&3xLbsk-Zp$`hMra`S|g0nY*)7b<2bxbb=XJ>-bbHUl~ zWBS{WJ?_v?lD@w_u*)6#3K&DeG&0&B(i_3q5q&8*+ufnR3C_wpNA#QEY-j%^Qg~e56KgoiMK$c2Ee~P4qOwSq!3$M6;1UVG{x~Y*8S6M>?=P`d1e03xE>ZRbTZvs4iX9u0VZgfL_&^hkH zLw+{1JHHKO;V!7cre95)4m!JCVu9JVesryjZMdXrcNSffST>>%{K@Of7%}Yw;L`-% zPuR3mLR8xM{4`nRCjpL5NaPl7KD9+)PKexcnk1roxkvV@yff5LB2`a`1#8m~@HS$Y zD)}uWj^djlVJPYJz2NMN;Pivw?7iUhHOzlJbNfSZ_Bzsv{4RH%e~9E!*{_o5oRPWl z&?l)=21ZoT252fb_9=PnqE#hYYmn;|M~)bp3wGkd)-BReE@DGTJ2KBe&+&G5wE{&0 z{8A1%VCxyuCA{{=)=n(A)JvKjpSp0)V=f<@>2P3sL(8XH*XF}53_=B)Oih?KCtIK` zRcX>;;2)E%KZQ6O#{<;en;uCo%@dFtyr z6?F>JW!#Zw>^diIqr?+atG)sLCydO%T}+24#NL5m>-l_`xdX8f^a5Lpz?u5R%=HAs zW*^*2-r0B4uNgaYw-^^QZ>HS#IT+7L^^or`c*YJglOUM;*7mmC?SbjQwr4wnyPiLB z1m6Z(bui(B&s*|P+%NXyt3IzvCg?I)!-vCXn`PHJ@4|(iIMVdvfzRg`pxWX4NH(Aw z4~o7WIQ|HR)~5nq*hUqTe9x#FSn@1NW}ODgN-!Y+a(&eyVJ`{V`1QScB>>yQfSS_r zWj4Erd-SW4HrhSvGL_*?4}Q=jb;;Xl*hx#@Rl`s$?*1;g*|FKlH(}b;T+jjd!-JhL z2TYx;(Xt&+qXq2@=rnO~IbpmrkWLDQC=-Ce56nnl&I5A_Xc>ZnVoKnAf%uQw zLF%-IRHEFPs2?hR6oQLRnA8!1qB<69bqsVJ({vq^kPc%Ujo8dK7trg39$l+lgeA-& zs9hF>koCR09xs^GgVkz5C#%&oIkiV234dhBp2dRhe1wh&3yDDs8Pkul zm$rh=pi9$n?~v77o?}8HiGbHdrn<@X6jktZ$&9j)kdW%KGw8;p)Rr1_y~s)hMehDh zn7;0n(%fof0?NUB&6(NLbsjD{(=H7bTScJ@Fd)rAkr8uU1K- zur~=P3p@tL-1+>*+$YiVn%WNJ<;=9<`V<~tK>1g~d#M`2?()RIcTxc+LV0nFjMOEFRn9|gx4Qrw=hzx03;ct!fRgy84_d2XOOg?lUlHoMZx%oQSds8uJ(?XF^FY!)Yx?Kj3K}^fZRaSiEC%}nybVG zTfd+LqWo9e6Gj#PWaQ{%6wHRTDgPYKnX33Fvo+4x!{O$9cMGWYZXqB`W@~I%hAN^a zicwJ$s9*R{9DKEfX^6oBl;RmD@cDF<8Q21LR|*^rot|CA%+*4m@_ppOOD?F2cca|) zbh=)`%KQ{J)`y$CnSQ~RTKF)sx*AL!xb}$Z?K9v{jIo{cS!Z-g@#q9#zbq~A?gwyB zxza%4|KbGEFLml`c|S;8vnvaCda#1pFOb_)8fw zC$1wK8Dq7=AOGwB(!XvO?N+-Dp%Z5O8yJTE!i-x_C;vbH$3IotfBf^GD)m49`M<}d zcS<`)snD?w{l`E5@95|M_J8W1|L;Hl2^V`d!u{*J%ZopU{omA&{!e|~uD4bt7}s4Z zvuW3RuF6ye-|Nu#D*qnL-+}NHkI6<3_uk=!p67a<)1dU!Fx1o_{Gr0O3V&1K|MgEi zu)lCc%^x_$Tg*MrrHz zv3YR&*gV?0eQY1xKDLjxZWpDUMj5{c7-(XkRl-1P55Eokwr&@t-9t>+J>0|Z0e%nh zTayV*_#R=x5mvu@RKsrrzb%ZamGQfaf%ZOr4w!Z^4@9&l|uz!HxWB4A( zs3S~oB1ro!XlB1H1MQvLMd@G%0|$GMa&XYXZyUacSpJ~7i(hE}pe4Tt_{FR(%-Tj! z4%!X;Leycogx?)VINZUgy(R`~P|0B(`f%95f` zRt*WYCVN{uYT_4xs~saivb#0xZcTQ#hTW}UcWZTQw=Te#os>{}iCR+oLNW8do7w>tK%zAKAkhw3<@b?i@lU;aj@>o~6UL;N0LIoY>5 z&a%3kUv(VH`tgno?Bf^fJ;q$eSnqKQzX)gztE%nc7b&P-!>lz~A@;nEeQwBpHn5+K z5~eq>qYXJF8rac>>}Uf!*N~lSVCNdxxdxJU1Lt$&0E=Va8nQnPq|AowPXqhYko{@k zST*EWHE?_ya(o&HWdp~j(S+VLaC{m#K8?2g0?M1R4^8YtQ}&^WeQ3%)GiEUF8aS_;4anRo;q+`FEwqppT8OTe zoc*mm%!Mqmb%e=Bd-y%X?-71$_=U<_$H){~$C!M4ECT>zt0r5*VQwLpYSl0oj&iG3 z$1gIm7LtDp$-jl@Z6W9_33{u6Ajl3h8yMAuHrgd9*Os8S_YU!kFtiT<&270Hv|(V& zC1ii)(oPM&yKVUH%c%W*{MKZk312{ZxpaUD2M73VVe(-Kzxxl>U?0CnG5`SXG%&p((-F*_hD>i_n@tSt0!@~8ktWN#uvV0J%Q9sjda!$ht?dE> zFYn4u@4{MB-b30d9{}ph2M28o0KJwEj${h3g7SeZbI`&-3$c0t47+?NN9YiyNcj+_ zNcpe~C_dbgDZ3chlYxBg4snbQC7_2uPvyhrA$}p(k*w?pmZ@AXt?f66?sG$_?yiqYdyiWo6AelyAvE8)={|X`o$(%G$CU z?LAD{lPO5WZHb}w{vm#k@mt5QOh&3`BS>wDvvz$CzXvkVz%O*RElIWA!1RVpZ(zEd zQf&~X%I&61X<{=inS$MHw`B!Lo$a>F22#~d30AY65)kK338&9a30A0`a%l&@hxi5j z?Uc*A_}$0vv5acM7lfys^3DN%kMJu??8xLc24Df&DepF909q{LDDRZ_Fa=osP8rE~ zrwp@uM-J&uV+T?iyE3qk0hz4uMfg z6v(AJ%_A8FOUX{NCIdAYIFbRVzFC(kbxf(tlscv~WJ&{58ZxDUDNUKu#FVB?fi-fc z1rqFT2`PWK1jybk?UXUFa}3{I835U9x3qgC0}c4@$rJ#0w{+Z;0j#YiQ-ICxN;bP& z0^xbLEZf0R*)7Ye%fPdD%h=!DavjQ)+cJP<_7H`8O<<#Yav~ff?bYNmQrm+yrMB0U zfp+tDQQMa(2f)5+2W1&3x8VzGPVHc)4&ObQ0DFVl!C@N%uzc1eW31sOpmtb>%}MPL z%OCFSU;wD2c6d~m0jS^*iMMumybE7h!69(5+9B?=YDc9qes^VHAHRn(P{VHn13NqL zm9QNhLTyJ!*yvFm18o^-AL17}a*WfXb_`TjJ4VW?9fSB#J3jdT*n79_wvi-JbY7>v z0>bu@028E0$yd^_ni46ur>n~4vfVW`6iWh$Bnb-yH~=VFC33IxwBOI#e>h)qRz&0# z1SPrK-7`IV*Xovuyk}%&WJF|S1bsXQK)ihM1V7+#@4h^N=@3cmBIn)hQlH%?2l)31 zeLx3YYNiV!U$^@lM%#TsNnLCPIG%17mZ;l3#zMz0F&mhNZWqqGZubP+?IFy%JCu_Zb#F#*7|zf=uXX4{7;n4^H~i9&*B` zJvh@&d#99x##r=}}JaWVnB)D&nWVnI@w7_t8Wg(#P|!R!h9359FPn!WrItiqm@v zo1pm=+32Uh`8A)O+@r^6ksbrJ1VX9t>?J8R3`GWgpsIpI@MrxO__s=lf$9m=jR^Gx zS6om|g|Y&Owo*+TSF0vo^ic|Vaf(_2s`>KSA^s)Z0qS7x(oIkfinrfNQGtTWO9VQy zg3XsFSP?hBRAD^drpUmd^r=Oz$!L)#;ZBm`@hz}iK18xj5HF@p8m zYE*E1NM@VI2*qQB;xR$-_z7uQp5fo;+q5s(E+ zF=-!<5$L41IY#V0CTJfcv`+}yCkX9tq1V9=1n~);WX%(V@yQda^Mn*XPx0@wO(hT( zLKvSAj8728CkWjWgzh&`41h*c-tw(U<3Mj_|srl(4D!>jYrN2!lMu*@jg5dN8uI4FD^^~THB6#!k3 z14&hC%U<^WPf~Y1?a{~Se}?YsiLU#4qU*j6@78@iKfu3-njWlI)`L9A_krw<^um?!9OV}Ms zt9Gnw)nMI8YHW2u_-u7izHW72U>!o7?#q{&@~x@q+Xy9(QEPSl3?XrhAUvi)9iJX* z3OCqotrJ?06Wky|`$h^k8tzH&0RKLzZz$RN_(415zon9`Nq<4}g!=Y_Cdi-%P0)UB zSHTehIcWCqqmQNfs4#27PWgva(S5yI4iWe7td{HbDF>vrJ9TuYU!$W#h2G2VKZw50 z)X|~x?m6nDp22o1DePX-2k>IwPGNWOd{YxgAFxNeFK7xcfDCqDkSfbm%aQU8peNOG za9otta`b^pvX{tib&<46?cCurT|0-0v#0d&^yL=q-1B2iM|X$f?NnF1J^QALw=VuY z!N1Vw@hN4TzWkeOlSw)BYhU>F}I7?LRraTYZOH;uQJK zk}mJ?^jmB6*3O?p*Y*A!3dJ6o?rBDN|29@ecvoPAcU5hK2gZ83FY&RgHRh&IV{ZC1 z=B85ceagQB{ynkPgQ;Xo8_%^e`PP2c^se;Q-m}~Gp6%_~d+j}Y&$G|Y>^A)IMs53S zWP6jgeKxVZqoyS$$G(d41o8NOZRCjr{2m+c2GLvr20^e3m(XHLE{|SBTKq z^QPP34`;9Z@t{_l<9mdWa|NzGeBU2SQ`gyR^h?{TVQlaY20n&oRQ;NN<-r?;`4Q!{ zXRmp|F}NN=yTT*;pKb7!=z4u&?fLu#1y5ef=x|`L+Q+{=&DjAyrN9w#uS+|v4m?Jk z2IHI6DsIfvPz5JgtsIpyrg#ORzte2YEmJ+FMj3;MbGyj1VC*PXa=j;?{D-`VbN>2>ZUJ%=+|iYCq__pQd? zLhyGG!V95=2DhGtt)i!lI9S+j=hE*Pqj=-J7p?X9TWaRtX2ErP+{l-syvUqOuUEe$ zckAu<9{N*jkA&-FcinlTzj94^luQ4O>uHmshrSf4j7jTYnwj@zz1H`B7eE6edE@u$ zeS9f%32{_AE>go*d=1VOv@o`SDfnpB(*E=V?QiXV_f=U$`Joxd>ot@@pnF*Dd1aZy_wTwbeuU_}Wyuc-z zg!5<3Z^UH@*lz@|;lZf@^cx9i@lp!l{l@j|OLjw!^WG4+RsJ@h%ff?u3dee3g8Ca* zRYBm&?q^_4ykSf2ym9&9&(0JA+Dlt(#$LVUUHYevtK$}Ty5aQv_Zs}#=guYXkTw&q zVL({}@<#z=#}y#faT(m|IhU@hSNH5|pw1Yn$9BCd$Ms;Jz&6s#BTb-1>df!P|t>!wt*ZyL)x*VizxZ@)k2S&nw zQ*U`)@Pysd$s1t5+IN23fW3b%eT9g3y<>DVh=jpy->$tk@RsB~a1jKgkB+3%pB=aJ z(V-=Pq(0MtSG}wH$Z4#>WuI~|BRn9?c6Z;y1nPCK>)0Q&0Dv^57*g;+k-I;2UGEZl zptjoBmOMytfZm8ZiC{Fa1@k{SZfDe3qzlLO7OHDpm>*LMPhRq2W=+z}x@0$&T}zpa zl>%L;u&B=efS}+7LcnZl_&ax{Op2G_+ zaBRqrSo^4yhqM_EKpXq{ndd=QycZdF6KY;XSMUT%kGttCm@l&o*1}srh`a^US(5kQ z<+9ps>m^j-&RQ4Xo>w@U0*ChFG?_%xrThjT{`~jabzSc-LK@F@A$($#ESXyUwWQ*g zovy=oB_WWzhW|_Sk4^L+3>7{Lbh(snKWOjiIHM2w{kpC~&y-51mg*Yh35t*a0#n~CFZrOV^b0i>W z0_&?zz!^9~W5UbgZ}lUmxf-{pIkn>zp<4SIs?g*q~M)9*NIK;9-$>|6`ie zK@R)+F^EHt;UjB0mw;`2Lp&mIPl#;AbGFM#*=p5?m`bW!r_YYEcH-*2^JAbTIIZ#Ea!3$50-6YnSgq!^MKZwa68sLc z$M4}7S+5l<`0yDrG2A|{B$0h%d0Z4q?rKzH_}=S;?UZjQMcZt93qw=Ii-O-eDA5|a z;^%?-d8mFq84~^wWom%`=#yEHounai)2vLXIc*OR0SJu;Nv`T^a# ztod&`W#;x$?yzbOd!v>~_P?0ylmQx@jsw}d6lnfNqyVr`fPU>SfP?~RPud57 z{mo9Fy-Hc94}6~*O-7iqT6G;82p7x8!Gc*iI2KKil|~e*J#^9PDFZD7F56lge*)vj z)8Uo<1m03QoZCzU?znt|9m*NHQP( z1kuF#(~fd*gW8p7;sA>POXHeB*|(khf2ut9ZQJ{k>w14u+)UT$9RZ6P21VdmX@ai1 zG=ZkAGAZgZ*QTtM$tXq7md9)C&0NiKeJM1%GxKlxxZ1O|>%|&xEv97f!ZBY!a3lqq zO$u88&8%H-R_0(uZYkZ4FrK{adK+1i>0PZVvP8Yz-C0SZ(fTT7>@;2Uej&VYAVj#w zhzWvbqQ_*=^ijLJ6VN>E@TMnR_r#g`Tk_#I=}ZhAR;a=c5>oUG=&g2 zW*O87(9JL57Y@q{*K6nB;bP<8&5Mm+Ly#j!+Vw{Ih_tbzYq4Q|4LhT2zlHr3bgiNL zt6jSoSVDUJDM=)H$%YoqL-U$zLw(!3owA}`QWd8nGNo{vEg8A9{IMu|mOpmRK0J2M z^2cY+zN>|2xhu8>@04Vv6kJ@b*F;X3f7^r`k*4jvfPY7}_Y(dsY_EkGiS4yo@Z-w% zS_klBVtcJa_>tM(OH4UCv)9T8qVoM&@(rAzg*aT7eQ8b3zC54Q&%V5zoGqKpAOwqL zG9rG~#7ET430TOC&;V!j6pb3jXpjII;FkbD4)BAn|C`SyXUhqjz~9LPk|vYMWVo;S zlyFiH+~tj>=S<@+;;Fq_?NE}cGjsYLSFVrzpm9!)UykH2?%N!La|34}FlkilOMd7I z?tRYvWzFfWR!!II`pr)F)p@5|tGQP-UsUbLs#mps&FLLoe0Kq%+!ZSkLhJ{0XY%jM zv|vbqM28cHf-`dL;i5+0b$!3TyW1Z$hdaRTiQ=f6?_&rZy#X;~epS`%j9!WRpU$XO zqwN9?t1yE4CJ)tz@Q8Yg@(eGPCgy3Qc!%KC*U!w6+X)&0JUBSIdeu630k>9Au?@FZ zXJ<>h=Gfqu+^$`z-2?mszvrkwaob6QePLtj@8~P)@vbE6tJVTWd#k|3m&6l#yPQ^pc!Yi3Dqsr%~iz?DsGZ((}_uYKTcy0At#{3p+Mch3R$ z&g-oq_mxS_*tP!PeAtFUFAsPc+|5G5NS5hAr%+JyZV3ClTDnzcl_+LeC4 zI;BG^<*~c??x716(r3ZlC0=48G8rMHQrEp5`St)j!%OPa>|DJPK7Be@0yWS5+(`#l zLm-*8mQV*>xo+oNQ^z>xzPQWj)%bW*93&h z(ILVaGVnK7qhF7cf%e59qf#Z`=V z+?b?UKN!!PE-40jb^pK60sTZ8V7K4`zze(X3|*7c zzwxOq2KYW#0Iyc%^kV}0W(Gn2$VvT_Y%|_X;Q9#Y_(tOpWgWV06zSU!@5Gw9?fVch z-G+#%x_eR+b7^K%$GinzXddc+4b^fhGS4}0Q#G;F$Ff?PM0)8SEkP4NLu)VBZLJm` z&hGAVM+C8SmMDjgg>39B`R4V<5KW8!-pI87*@kT7-?Uu%27Q{hB(_7#r0<;;5kiG^G838h>e zcCw^I&Z>cNDs?21Ui(e?+Xw?6CC>l+pCZR=HQicEclIdj?6lgOy(oRq=J2=%uL*6! zn#rS2TR4^Ww5gQ|zHF8WzWC3Zu7|Oiy(R{n@_r3b%}}80!#|<2;aXER1YFs0EtL&_ z3OmZLOL^6gsIxLxxK;QKM~o1Ng5 zXXx1L>yRHblqm|+25o3`?N-n_wSwN=0i^6%f{C~#i7MiObIEC9E8yMb^ zV~O>F#e7)nb=u%S7$9V_;2O(qB=YjxAP6BX8v05jMKOw+fTYqYZL_>EB$0&$Qf(Fm z8zNjdu`)YoW>ro?gDWRD=w8e9oQ<|tDh-%nxd<&P<~C?(UA04Q89eh7r!p3A&vb$RK*M-XjP2#+gAuZ%}uN z#w)HNkU)Gz#Wm-HaOiL5^DqKRoeMV4!qxpC91bSK*ZA*rcrw7(KhJ9Iyb&<(LjUKF8uY^yY`StFUBCrRuAxTDr;nc2)QV__ zkDk^Pin7dEc%=91N$2HY0DMIzzlc5D?04>QTbcE+(^qqgO!iCl7V=l3&2=P9SkLdurSW-g}>mz%i)6C^-8g?7p`e5 z{g$FBT8p5V;bMChwFTi7(f-2{S8GaWTj*Bm+REc?dVkR4bbBx#?)ZxiUc1(6ulShZ!^ zav9lKaP8S`@X_up2KD-1Gq~1WgIiY&3eO@>jQXzw4yT&iM_|-djLVf^cFd|CK~s)i z)6*}!IU#D9z)#d&mD`z1LOTbJx-BX7v>+fN zrRR*4vv#7-zotuYTEB;nqWVYnGRawCw{7_A)@-y*-Lq>8c(_p;VR)yV+?fk1-*aqu zmJuXYk|uSbdf3$C&QZrNj~d0Rhsdb|PA-JmopvD2`IdjRSgrcIyK@z{WuXS(Ss282 zVvWEkH>d8k(mTGtsI{~KN92Ql9))4d`0%+YB1eIp=32W2P?Hp;H2mLb%7A~`m1@p3 zpX7+xI71BaywIc1K>A$SHydXstqX+)v$8ZTtS}uf$qc-Mm(YacbAFw$(;_9Sy6sYh z_$A>&Dp2v7aOx}uqoLRHfMM_Xy#agx8N^0Y zUel}SuikRK*~7{WtKkXp)BPnuYh+aQt{}+E6-H9K!ej2vCuIvGrVcbjXl>vQ@q?)| z_ZGY_PS@03SDz^AXJFdsF_|}lXzitG!r*Ex9A3TUb_(Zs2gk^D?DTaMnw83HELu7C zM))3}a%kR8%lqVdAHhiElQ<%FcjZqd>3sA*Vif40aAdCQdY|}-1aR6pmr&5|88R4j~In*6BWraJ4OvRq+WNT|cjRvj@D{gYC^8 zfGPZW$rk6_`{)`dHaM>4vdU$2Udq_$N9B@II^Nwq*JtJ3HLJE9=tD{a43G&$DBv#% z2kKK!SL7>3%NQ>pZxvStSm_^g?Dmh>^5U=SBk%Ko4LjE^Qm<%d@T=DWMHwrjn9FY# zOWUkVO*UzhRZZgfF21s2d632AQ|$b;eG=Qatqc-Z&)6)Y=k+(8?KUJlqR!gRu^*So zR|^PF7Z>Xr^t8L{5V8pS(XS$dU4c0D*8P-FzCe?k25S;2)i%8YDdvz-3?DNs`mY?* zIh9;{I!}VEoyL_Y9Sl^>S>Y@^@jTF|I4xBpK1QCdbDhoTg8{NU7CU{eA10oWUqjCQ z`2eI@X*n@B`Bh(tbt1hLG4Y8`{d;kmAn=92;-|2ZH?@EjT z9{ca)&d7CvY>a>>1vvM-E6p6mz@IC3$2U_(uKR#m$a+WtSs2g@kdax_m2TG5BZAJE z?Q_s~!Pp1qfV<~I*W(-;H4=wIIwVxJirLrpVO(ipD2V%+F)4&9%?R~em9A0r!>ICHP(@i zEhWiC6zi`Xka6!shI0;Yl3*UerSOfFv43Ai8G~0qMQzWP0UE_yJBHE^ukkY#{)hqT zax2aDgCw-bRJvyr$t@yWAPW5o6Y$E%Ak zxXK#3a-lW!tYywX!JDx>$Jc64(OjMdp-s?39QdFcdpA{|AHbK*hq5mlWles5P8Kc@ z&yr>5=Z=l=vAxaV<5dObu3hi07UVrbRV(HN$2~2Hi;=Cv5e4pPhv-99D|k!8>Bl{d z@JpDgR`g4n`-TD$;;Y)gJ-c?-&hKE+|GR0?pXYqLszs(a>ju`Voq<^#{WF1Sc`mo3 ziTwiSVeHv@eKqFWE6te*Pnk0eXAKdjjR?~Pyh}1t7^O@LCn>|jgT!= z1aXQqk3nt&2r;|F(3eJk_R$hgk~RddA1sHf)$Lk_We)IRMu6e3*Di)u$H1bj0puF> z!6FFiYBk`|ms1fQHuZl{E|$ylf5z`3!&JrRTZPY_;}mDdmDx(s;Velmrt_tJ`5 ziE#~E7=gwGtH@(rgU=5UIv8$6c<|tIvWV-tyPH7BPAB)_tp7B3>_K}JWJ9aXgS76q zF&?YcUa)KoEqS*Tpf&NXqp+B@4mN-lX`=|TWYc7$G$_s zVRZOXpDzYnrF=6?1Oau81-} zQ~PqKz*z4p^fv~)Ev?*yR?f?CC(!!9P-u7@T{f4=+_+U$WSn(e^l}q}+)K4kyakWq z%m^qy0x8;Qc^j>AJ!DnPXfJ?`m5Qwgk>}wBff&V>nCeSD20-8dqUM?}ip1LHF5gv- zGZ=d@EGopb-|>AgF`ebiSwdLsqY>ik-s&hTZ3|D1jz*k(ZZn z8H_!X{KxK*i7m0hjiaNujgjb+ZIDm5L%s^uw?HXEztl*kHR4U6+++|BH4FP-5D(>` zc6WD-Z3CGi&x4BEDo?xt#6uUW44=ccIsXOJID)_Ji@?et%_?R=VdZRo#WE{QS&s4s z*cNB->@jRxk8YvDx?z1pULPyTf^c?N*HJO!O{lSg0RT@!?RU<`Mef0S)$zSU*;%lUDrSsJAyIAx);!9K z;1d0-Ji48(Js1U$IoW!E0#=TXGHLD+%|x^uGnx+wQ5-D%=xAKfmH;ZEpADgF-1yLr znE1=123m~Wt~(o8FwwApFIqmB4#5~{r?g})QceXz2MT%%S6lh1H&}Rcx$;xo-xrT= z7i;U$?R?FL%}>nj6d+?ko+j-O#04nN574oH@ttM;$N&2O_W$F5{qMH*fyHcGSb>1b zca}w7{j87abQ%NDpg@!+-&s~5Q-e7E&a(Omp5>n?BKAf31Xu{-q8On+1y1H=8>hG6 zZIL+Wi@{1qAIN<2*bDq(kPbN?vhKUkFgs)I211}Pnc z3S!fo`P4Zll!6%KpiibUDerH*aQHI78Zd8?X7iwECk^`HEiR`Zn6|UkitDq)>7u_- zB|Z~^hI>mug2A#;WYJvafK4M9&9y~37>Jdyn7J~XI4GO0K@0`oDPkx`jVKw%%aG-c zJ_yzz8SgjCv+&GaRki6xtpu<-bVnh4N)dwe{OqW~k}&@zDrOF8>BeCE%Z~Uml|>4-QOsC^ zljfR6$8PL{rOuc7c`uIs?exp1#^>CIBay#(a+up@ox~T~0;lJ$xq#V20kiNfl<6W3 zbrQww(QS5g0W43pzG&MPP-ZJH!YHxV4N`^yZxTdt-u~{kC2qbAOBqyu$5p zaGsD%XK}8r1XnDS>nXq_NKPD_hHVNoesUxV9bqoi0!|5wm)YyD;h=NDL4(00(B__Z zWw0Y7_vp&tphr3fJ!-={a<6b*E?VKasfhx)SHS-M7m#p3iSPQaN19mF8@5Mfu_!K; zMhgm27C%JES*c_Z&6y|&zj?WigcV<*C2=F+Pi47ebETGA3QxND?ve0@QZx$Gvnzwz zql-tkB6ZLlHj4DqV!^VL0QG4qk6UlFTtK^1R*YvXwAXjx%y=W^;ycb(g?H6U^*u~D z8~V7S(L9KcNNyzvUg?qtxbX3EF!4JAD7Fc%YqyhZ~HG=!)@jQ(i8El3|C`a6m5L-Fe6unLSh4Uz(2D1j(bEohprw zv)(=bbr1A1oSRv<3-6X|VA{DSu%bQnNcmzuBNk6hVvt^ncHrSub3WG|Tjap#l({z# z5jKazIGQu`szcc5O6o}S332YJw**R}_uNx#+U{=r)G53kTJ0HRquknrw{}-sGu{>_ zWD7wy4jks?-V`?O+$Z>VEY)`i=qRG9P8pCjgISza=C3e;n|?I|7^eSd?z<1?mu(8u8M8Tu8q8N|Khu!mmsYE+8EyiiF5JjwzqzCd$qRe%L{i+>;z8H0&>9* zE12VICDgwNJ9~XWN6xv_jnWGCz4=<7{M#S@rGN7A{HLGa{qWPr54*dgaU)6q$d0C> z-(JC`qks7bJi&@$Y2w(+WELc0%)*~hy$df%y@HEgY0QXLgQgv-HrIms%(<4mXiVdD z6o5Uf-ua*3egCn0{>OJe^j9lA{ip87Kk6dM+1a1d7NhyAVFiI9Wj(WPgr3t`&FxJRtERL;3mI5QBs?r^i zrPiTgfl={1xUmw3zTK^ijlg;p+8iVOLoT?Y(IT}_JHsXu*wnUXBU0F|x53SNr`Jk| zcIg(Te7}O0a79qS9X4DKXE(O{$8(2BzgbjRjVcR^a$`G40OryUPdUFM?h}f}_&Pi~ zVsN&z^?svq(H5g0Ux!XfH}P+&Gh(#&GtXWC%EjsUvT+m4!8`g9CINL>%suPAceh;P}R_cPp_TKE6|!L_b&|Emeg#EcVQFv!^hOx-QCI5J(T&C zAE8M%3qZ=sQkat!Mj3;*-JG9Bj<|R>3NRStgZu9jKNSIL( z1u@+43kh)PzrJ1m1rnfVj4_C>gPYu5ty1u|fW5o7RV8+JWGr}=nT|)X>xKTTCRc3{2L*6)_>Lt? z4iD@jyD^@J=o7@7_!(@$)oP@;i`cVq%&?AA@cIc7EAPfvdkXBQwL>(|13g z^v_S;egE!fP&%M+0;Oh%T|f_xZh=uDyB>T1IWEqDSv=R51Z+I8E?n5);!VXO_ELDI z&E?+3rh(DoNZqI|R;wvkP$79tS1W7*WSe~5xY*Ph^o(29LFOew%6pW-6||T888Ssn z|Me0WZU!#|!DB~JLYiU|X*$FN`t;9gm9AEqBo%5$3XVakA!!5!UV=;izA+23kI|f^ zOHd1V%e6WIP>2w_HZv8+;3Y$n;h2|McIZ-vOC6t1om-6~Ymenjg}SF*fLex@uv0YV zESs_u{My~EWNmkMozR~}0NqfC(bhy=uZ7;04mWP`+sWFyU0hCkK@qg4QWjaP`CR_E za3;D!JN5<>Jlb&g!~qaYN83r5>3Rzz@C!tP*|**HV%VluPp;u{vbS)z+zoqz(?J1@7)HX_mgzwFq>dwx}0r9eB(OsLwfiec&2P6S5?=kBEo0T@Ke9 z6yXBUt$0ECQudcQ*^u0hX6fk+|OAdki^j`$3_3km@!bxa9Myi1gGAk+h9G8GE+_fbRM_>2b5-~H2noBue$+}96*Z{0_;*?jioiB*-}Y_<-b9zL=D>G0{ZW~=q= zS@VhYPt8{A>C;2&pPGLU04(z&$o{F>{05V3Hk+3G_aEfH+wUxkTq(CLds9*8K{l5F zp+xI>qt$2~QhI>5`jGrFod>z~FUurJ6P8%73ixjQVLA_@7>LVjssQ@EB*(703pj`B z2aP6Wp{Mw0v@Fsrx7*g>JIk^x`{w|(B|n4o%q(YC6pOsQzdwzN*>VK!?q^US&GH$% zcf^(~%YHi#=F8uI4=y9CJ6c*Ff;gC4uOgYMkLx?j8sY##mXEV&QSgbLMo9?FJyF@Ct+UB3>yW{Ca7uy!D3K8i{@D{vGCj4-38U1l`RuWXmgeyGOnV6 zQK}fIQ3Tq8B6XOem1pC9-se6RtMg)FM6$ZD^8T8kbbsuA|Lec?fA%4WU&X*;@Oay{ z>~WUnd7UL!mL{0`EmVbP%cKsmtlI1>m`0ehIM8?e!TyvY0ph}_!bzVnjIE`sk@WIsU z#1Ge(2`H26VU$A{f zo{ndMB{GeQ&^oTUEJEctF{#f_{lNF6C!1{wo3rjf+;Wft?4+CAsO{>Hcfn^cFh zbj}vR6g&9prijRa@TW%0hxXNN?MHdOWEvzKM|BLGhb5Yg!^V?RiCLN#EUZtXq8=ra6w4hpnj0-ei5PQB ztq0HhoY(PMg7TY5NSFvw?OK+hYk zGH{SIPzTM55lI%|;Gpq*qjfyG_{~s#0#+4r5uTTd(yRyt>wGs<&S?>iQ}k{lAzX&y zB1@<69*)LU)fe6zi2NrTlY+-}1rDZO1k+qD$3dwaNr0IByz#X0qB6TqZ@OqYt&QpA zQ9|6~Z>{GQv!!ibPg05&fd!v6n&rOmkgC&JxKID^ox}nDvW)dc4cfVzMaRLM#pu+U z`qj3ex%Z6ZjZFsvz=2W1*lkaZR^v-derj|kcHbdK!oDil@(2TO6vy&(!rJ{kNBTpJq9v-h9wcw-rJ={~!wm8f>=4sUi_X zKIp@(#Xjd@IwmMK8&A~!FiUZ)d)9cS7RM~X7m+XiGu7QB$P2uv=X>x_wSZZ3HczA9 zIbmo#)pDaGtS|9Ada6_6$)X-4VO=9-vPu$4RfPr97nwECkLvsL2#d|@ob@`AeSj%X|jTxy4)#?`aD=jV%AiW<9(FZ zB)JA9H1+`?f~r)e!g>yn-#TaMkcP_!v$5C}aTSE$W?uyLSOQ`G;Z4h2#r!g|$t zWLQZ<6f)(D;5w<3C#y{;0EvZNl}BSA*ldpHeRaHsRM-9(g2|y zF4%rPp0Rl#$o5m!{4yz`ID~iL8yH!2=skyHM$=t{!FU{{o68CEH6N9Bu%oO$TEZ1d zoy+i<*w+VUWi{yoec>^SBS|QE(gU@|zyUz1&&9pDPS*TfaY*#r(tNg~uZd=blRVz~AKBXSAMaD=)VGf$I;XWoxrlkojbH)t{ zcshXOyY+YfNcw+W7Kba2znA|1>66xz=dH5-|KQ;1lmDy#|GVh_m3IHovq`&X3vCVG zZMoeZ*o}s%CX}rDejJVVk?*29cNYw@DZ2c5NLk?N9#W33y27SG*#YZ@ccq3&vRqRGL1IOr@Q0oOoz5ZdA{z8Eas|4jy0G<~ zE$2Z}2lqrEep=d%4_xh(TG@wxv`{gQn4O2i@kFaG!Oybt<$`67NbOMK{yxla!Ls3bVOGlNgTmrvxWjYXl*Cs=n+v>GjTfXpv=Ik}(>9Pm25$GdolyO7aJ~8f|fUkvG zSLu;p8eOr(La0CijUY65V;{l!8yUL*I-rXKsHx)!A86wkFefQS{1qawcHO+`r< zje~;G`vV-qtJhYLfgf&c6UNc7ep(jRU|3Ga=Kw*%y~+i#8~`8a7CeWl+qQr<+o8no zGKNC~o(>p^>xe`;3H1$vx>{L#dpho21z8k?(b)1y*3k1%YthZ78L=MUERV%C-g8dPiV)m|GmE^AC1)+>PkuSFT&he(Gl zmo3GNW%fDWj=1jp$)ab0|5sITl(FElWA;7J`W^^`7;f)d2lrpmxd?#xmrrs_(`y0r zXkt045;tPCUR#H7^QmR(1*Jw2Mv;`_-n=vY5OdCx$J*$4`s$t2oDeCew ze~u#dKh`$)Ah**KJI&ZzP~4G&HT-AlMA+=}4_rtyS~@46*NE>9xf`|?;NC4==CT8T zs7OR$ZEmO4$|^^t5v^!IG%c? z)J1&53#vU`q`*RkQND36(!fqtDP%Wc4XUv5ZPviNx@1T$d>B8a< zBU@UNoegxk8>r)DUZiu2%#1TUJ2;mO?DEO?Z#T|F;B<;qp*6jKss`pMH*sBCgULg| z6=c(&@LtN9lmMjB{>g(U3KkkN!dkN9Cx3mUYo3-JzHazLI=& zTkZ>=+3k7Op1W>bc&Y()M6C6(8g$nfnnC7tvSrSGv)N1%TS0LiiXcD0!$^r5;*6>! z)OpoXBgS%HzM*3-bM=Y$l|vFl?kk7%j#t}0B>l9n7?UBWeGD#P^|j*+Xo466Jj6wj z*^(kNbaNGeJ2@mOKya-k2s1DZYEx`4IRR*#ayAb}C=Ny=1>=(8S)xLELDxU?p1au; z+#Mm>)6Drh$zaajk-?k~9X0p?Tf?oTu>n3UmUTq@g4MS6xHO@onF8by@OV~D@1SPw zy~c`z&5R-Nx`yZ^#S>pjDw?=dH95<#oUE%A&bPKIM3+q^+F)QZr|DeHA3ZV`B4B5oG z{)tEFH5QlZ4k;g@FbCkj}nTx zt0+fjg1H(O^yrz==%pY95?b;O3CQ0;CD6W)AqGudVSs z+@Q@ob1*bn13pHcn>*uqC_zozw^RbOaPzPsCnTI0VnzS8wF9~5vnUK%B2GvCte3?L zfuYpM`IHrZRH>5VfRkA-y}N7eXy3AVbzYYSN$~=-Du#Td9sMOdxuZpNV&(*%H8MFK z)P?<_$hqZjTRMjT%ts4?(9$BIZZH@Dr~VDFGRFSE_}3ZnWR1Zn%|Qg%GsjttoxD{` z7=;72!Gx;uh!B2l1zXZb8_30#>4wG;d*pkE!_B~}DJ$Sj*Pqx8@RTVRLv+yY?_0n8 z`r+*_zrMi%rx|P%Lx4iz$@(yZLjlhKbU=Y!NARE_npj-r-mrd5t$e}8Q4o`i0ptjZ zCuqUW*(?QDP9Jk{#M`j;BZzY+oEKgCR4p2FImkTDxf$)86~v5k?Vdv z84^zlzK#S@t#3hr2p6^xAb}`b>5rnqKsm%*1ohF-XNSYxBtiT8*6}im!GuenSEEdz z-U&%or%#l)L7sS7L7mBh-70 zmf&D@7Ue~nK{ac{W&wyCnRHn@PLnH^M1rq{^EGC5Ata;qQ!vjZUh~($7`B0SjIYg@ z5E|x!69+RnKI?KBMUnItzfY5?*&yKkJ?%je)f~!>`uG5_s&zv;Gm%1(drELS0Q4l4 zzGxDBeJd%c#ozy1PQ`e?rb}Hw#ZwW(nO0BK(}AO_UaFy~ zW0enK^GFqA!xNQX>*$BYo#z}c^Yzq^zr2=2(D^YfEYeKC7Q~ap{l-Mi;2#arrt~Bm ze5Nb)mP4u8=O<=~9UTarCTxGX%IF*;88e_9Cv(AS0en%Iqe5DNsx**G)fcZ!<@8&H zYWl3#EYWF&Lfa{}#509u#>5Gy8;yq9@m(C=CMudOejO$nbsvt1n${8`&E8nE;0kX0 zHZSnL2LFVNxCH}6wuycF`_>0IgUAWjj@6Y4YYH@^k14lCY?AVjQfdysO-fd*p^iXE9rIc*k-szOBvczg-RgGvM3lo<;G_L+k zUR#3ft>A!Q?y!Ig8h9}pnCku+OC*mEL9ul3l>k-=kthjE;Ha8m#eK(?7AtX?laNXT zMx{LGCcqg#2=zd%#LYz<<>n#dgxwtq$l^!>> zp*tBe|FF!#ajFFf!AWBUtOP{K6)x)U{MXx+dy?(v>Ss%`_Z-ZQj{kI%R})Em%fwNT zvwYATf);ND%DQqoYX3_01byY#jz`v>xLwVMd!hyaEk~|C2PF+EyGZm&le)$X%a##1 z&=w2>KiW#Taqq?$9zyOegb{t3XMc#3Au9pc}3-e}swj1+w6H z9`cJi$$WIm{JLZ^Ojcdh!(X*(#>8YSOX4R~1V&3T*Z)bfYQ>QBTJi>#^E{=}mAvyt zlQkVumVgwZ$$dw)E;$H2thn3zrWm6ML1n!8+Di;g+|Y)9-%QmpY~{=OJjiYyE>~F2 zX%NRFG&4>&UeK^Eqv5Ml1O7E1m$Fyzp1Tp_f61yK3HSu!{Kro8uEthWx35(iGw z7W-0U!P{h#3aynQK%6&MCOq1vmDS36%>8`~J1pb$!ff$}$a$Mcs=wd^2wcePfVOh81Yq!_tg@G6|;&qzF475oo(&A;6 zH3y9hH<1H1{~SZ$p>KCMm{{g%jlo?4;sO|x?BgZ2(J;rJAV-((Bg%w3vN}9Xh6gdp zm4z2;=t)Ug<(2Lhpbze3QCH3IsY@%B)oLS8T8Qg;0%lQuOapaXw5cU0mQ?^n%UV9G zJ@aBg8=Ijo--=?Ebs>z06@qLtd|a){2vSi&$B^whbbMLCa<+Z#LH%^^`fp$!H3}Ad z2HF8cON+35171)g0FdhG%1+$%#r93WlB+)l*;e!~cM7hw)e=zU%H~x?X@%X~RF7R~ zt0`A-Z8?u_Refz&yBAvwhc_E}plaa(RQj*fFx;(R(5dx?o?#2U79f`ThyPr9T``Pr zPO&v@>jP-^)~V}Mq_WAo=}2Xjdq#fGKG2p}^o=*(7x!*~4Zu{X`1OreRTLtUmO5Ww zY&D}Tm8@pOD|*?s7ZaAzbEcA9W)|AT{ubP!6{6C?M*auzLl_ikrivgt!HR3fxSFQ)1dYphGM+b1 z_ie%E4wSBbazI$M4Vk$6`(+$|WM9B$0ph8#Yz`plC!wQ&RjU{@#2C?W@1nDTrW{z; zS+FpUf6=94$j#Gf-=zW5#Byo}w87^U+ft@NRK6ROYWQCXND;)<1fyzh3lNpz#HL|NiG|P& zAq@Dcxo=mGYh|`V{yykGbOpX@6-=tEtp>rSJn8CHc?fy2tUUsYrIyM#fYhp$J2gkU zr`H=+36^qyhzqp}a$C7W%nLYTvFG7e?~jTLeeaG!W$J$S4$;Q{m0Lv2uCzzA2DfYy zAp%l++VQ%@{5puo%UJN8ivXO60B?Z7g&}Dke2M1E`QP_~rjn;D-N(p_6ah=VRu8#{ z^s4JT;HGU$&3o^Xx754m3d+0FuHly~SZID1xGR?;^9+?B7 z>qZ``5$aE(4ESnfu;i;gtCwPQAmA|onWH3MPA1V9t$MRGrBITP3n4u$XC5UdX}p{# z#G-kimg7T$_>e)eLQVMoZOZ?n!=XMfjL6|NppCoZoch|yx5o#onUV4Y>owJHglFjlevqPBCTsob;9&d$yz@TY)3 zS7&F-W;1A>oh@6>Ucf&G&1SPs{|5Na!36(#3jZ87n^0!LCX>C6#^M$btx&EEE+r`O zl2GCTU%?z+u;nbs#xwCY0Y*-42dqiT-@ig344r1ohB_ej9XuDXYwKq=?SEM~7Xz3~ zG8{a*4hKd~!-kc!k z&XiD1xe#`95NJYfAGcv1PWrN63(*TlXq*A6*sBt{l;)wg9Tt9YqX;Yf(-7s_5}|mZdm{53u4;p0MwD&nSi% z0#}+Bu+;gZTeF^&diVE%ppS_*;Tkx3YN1<2POic<*~5v(!6IjZH16*Moq_v-C8+EG z009_1#<4YlD2fRO4U!}F1#SnZ)N%ook|-Yz(pcM!P1zR{S;bzwI6F&LjoX&@=vH6a zy46~*ox!79n(=zLdgQJKzp3xqQ7Km`%J!yv2BnucfD`w%)#aN3hljUuQUzKz=hN^< z+T~$;W$u>Fg=mw#1?0umz`F~?7tzb!UPs;rWLHdIrwRtyiGb^|j!X;_7JyKzQDo7) zuRXIPPD&f9#>kY;MB|k(zR>jXq2@Poi^N~j+~*YOg34W#ogrPIW#0uo@72^j7k3|j zSnIFyG=weO;Yb7X;o+#XJbbVZqAd^Q@uiS-sId2HkKVd;xhIJ^yr~^A@<9@Tw>mIG z)~<-7oL<3AB5AgbeaIWQ%{~UDKHveEDE)xu_xCONq8$T#MBHxo6}cg4G#alhumg#? zyS;fdovE{lw%sg=AB9s^D|#Ih&b8@0Bh#{#HJ<)V=7I0{pgeTlIUqfYFc^?u0lLxq`@|Fmf6sU%Shiv`$G{x(a^C{sT#v_Mk=2zhu*ue#y**=4L$n%d#3l zDBeq(%NnOsSjOuNqWHz>bc(8N6UQ@UxlLRJJ(47fw1 z&z)Q7rfuzE;YRVL*t6E|77R);p7~nLoO=3mrKhqo3R#xuu%fa=Cb)D=*!5d^_Ei+t zIS{fHt0+YB7UB7xX?v&iX|SYWI!59Ru2O+ivmn2*=24E^At2E;LvArlDXJzIU%_&Y zf>C?}4(c%Cue6ji27hY}Duy;BfBe!l#Jcj(=b__+JddV{V`Q#Y*6rHUx^ln97avQm zqAX3gd%7$_A6iAKJsZ zF`ykWIu;i;L!!2nQ1v*c>;;SOl;qIfL1LULu2I>|3;miFJ!38FC8 zG<_f%bnh?mfLJxZZ|wi@ml=civn6p+Nl~zw?h%=)XT}*PP(F*sFx2UTCOHo-`91It zqOzk)cF>SHc|CV$M}9pN41a`|*JA#6Jql6N0*4_FpAnQs3nTP1QZ)l9lBeFq@e8skkULGs z<}0an+qzxz(=#YN+c2l`PLpCd@#4ijp;PdrXIV=rCL~w4-An)1fk8dlFhQ|nYP7+X zk*R-{DKy+_7qB4C(aizGH%Rdi2hAx9a)@jVxASRsNz!|`RBnGfshUC0P%;k77x0Xb zkjGcxq8ZE|<*A1C3u)6~@p7`_WJa6hd`skD80hGVDJ>3si0-k2IJ#ukMZJ#EtN5h_ zsyrz{sD<+x`WX=lZiDG22*4b3D~ZO8uKcY$MaK0pYNcQw1Qkl#n>VuFSoRZXe2nWa6<}EF;aR<<)!F$gN4vun!%x&Y8HE&!AjDS z=@}h;9LCD!5FA)P1Pk+o6wtq?Sh&=EDnXGCtdK>d10&&}rpiuDd)q=-TRKxW2h#v8 zmUwk$(nR@IyzC3C+++z1U)N;Gj;eBua?YHsyJR<;^R67qEXW-wy$J^m+Mt%eQd>r~ zK?!>iiHgLbjZ=s)A}pwcn?l@nX+ar_G$-d@coM*F*Ea6a*Ql@W@k(OSM8slj%Zo_mq0<>il%4~!C`POW=nFc<*SA+NToH-|jg^Oo!8LY;A z6MvoNSL4i7A|Z@L>1B;8cTq_w>v@yr!?Yuj+^%vDZfrL^K%G*nKIfj@$|K|ioF@WF zCuI*#_>zr+Q5`uU(02ecauVbfZo*CbsjqetuGpNs5EggJM5TdOc+OsZPkB5bo2WE1 zEH70RtA?3_=CDzupB4+2!MO1~JEV(HDI5}Rrv!@+(4od9;WeXRlPtW*nBdt`;2y&B zYp*`{>h*c_g@ybg_w}9zr)^W;ay>~?0_C205eX^Al_N*vG>fJYa0^!~yRqUl&6!XU z5IYbhMGz(1o`iGfN7o*2k`7!RoD?&;cGg-ZLW5^r)bnf4s@HLZb%5)IHOQm!u%SG= z!)$(ni%fZqPK&ueuvXr%wofYn8%~x~CaW#vefEDF3|T6OzrSZ!EXijD zi<0w@H^rL}7Q^BOX&%IaR0VM~CH1PP;Aswd_Wer z%;>uOxZStjFQf6L#R3TK24VF)0ru+@47tXfCgQ@Qfp9R&oj@RR-5Lp>D%NWqhHkZG zv8d>&dL20wF-7_4Y>}}^^hM1!$LF$0vPNnjhPs~tu%UwZbRmlwtvKleNOMY7Rn!=N zp=UWjcOuKTnjI^$Uip?7W5K0}9}4fbds?t5ZjakA*kMw_iW+cYNIx^uGJnM{h01X|{U&_M*2c|$yN zM}yD!QF_FO)tYy(v>Po;{SAPbCQdjLC7K>8*o8QEJG?B}>T_s%lIW zg;8N?M7XF}>J=2@y1x%`50jkuU{wO36A9A(S^TJg*TGO_9NYoO3Rv(YkB|OfC>pF; zt!gI&Ya@b4I1mdy90)8KmRW?;C<(1`4CgG@ssK@-_|asfl>^^MR^YHDfdb{*IVU(i zD>6VJ90y4lLHPAt=dFzQv}LBk2$1VQ+n&}82Ou6(Diyt~$S*;>0Ps!S0+Vksa<{Am zif{=&()l2@b}(=XD6xT|bc8UejYZHl?lD7Hd#kgE7&gNiYEQ8Z6p+{ z5~n#VO^2#R$XD!@Z?(jN3Af+C%gCny)^rKCjJav~9jeh9CwU2ZN~XdbNz~zm@D}G+ zd2D9oT%Wrm4@)r+*V^ap>z4Vd&TA8X<=4|GESft3Eib#Noq z=y=r2y{?5oG#5J5SDF6P@I%65SmoAStpE_aZG+~`kynL{Whv1$vAIO0Qp}E^9Am?M zz{XPZ5v^A2n~Qq=(XC7p&Pn*L(H2r;_RES}FNm_DEj+4i70G~3iEputm{#1_C}B7i=6jOOP_jgqU^1F$?g3`A+AI# zrGUkE8O)0+Xo6oV$Rp1i<=%0%m@e;>s>wi5M#a)Hm+F|ghAF>@*(1ZX>qu~~fj_T( zkKz6RZieoT|62-gEyI93=JBxffJj_pCr#C4pd)E;t5s8)Yok`W>6Kqx)AAQt%Q)6E z&Qaa)DY;D2>qH8w8&<+ptdDPMOkL;yuVy1%Za@sdV>kNB)mA9--yl@$$(Pi{TbHJU}` z6m^A$wP-!EX}Sj(3a^|P8EJ|OXB`@36#Ns>l#SKXA_Q-SjXqBeOEc?sy)E|xS~2l5 zn&USQjEg)35-ZGM6{(C*(9M6sAnZlN@@nKk2KkTNny6wL>%FTz~ zOE>qxIGvB8gn>S(D?-);u~1ZNyH1Qow}}(ZCXz=^2&!Syo+%Wm#3FDJ$MB(bp3`UTj+qR8*{O zsoN<3`0-b~^)Qm@St5AgS2mDZ5B)5AP!BN1iXI^Ot{w=OxGFSvk6?iy*qHO$e7AO9 z`S?Z0B!2F#aD!Sa&9hn=rHt5I=5F~u`2EJBm_}%N9Wr#AYyCjZEV1cmqDTD#6=@jd zu%(PgaOXi7zU9%+{%AxiG<$@vaTLS&!G3NW6 z`;wKgiiT=fvdB^w0J#e}8Vn*h3!;Rpgr!}=;^wV@_y;}!$LPVdQ`ka^j2e}NI$JTn zR9EG7wqsF69sbd|4{s`~ZX0On%q`W`b2|@YXi80NEBWvPT#&U=!*|qq2rpi0t*TV# zVTXAcX4M5d_nx(7z-^Ry=s8@`P*urJ$pZHd8{(}2Y4uxU&AfQZ1h3bH|B=h}+$mX5 zWGqLo!N*{VlG$)5sd3=5< zdek)2kEK(zG-i!Bor+!sg3!A#^;Az;UOj#YWTkPQ5~T2v*CveFIEC1sa0;@5EFr%J zzv*H43!Y+w!DL zf0P3h03`dm1S~dzT!^5w9%&U|J6yaHcBZc{py!K$ureKz*<~EyE&mOFf6J^3p$KR+ z8W-i)k=*oIlX?b(9`6=K{x0i(VPg>LhB!R^92Lq92c6@L$=w_bNMAk26rbHKFY5+S z1UkH@n~Ih;*d9TOLGz~bKITTok#Y{nY*|#QiZkJ8>*ZNhaPllM72|auzyu!gR@CTd zjz<@G@`@HVpZrlX@WziIw+HvDmwf0er5KZ!Ee^E=30-P#$$KiE%_uG&qMh@7D>~I22tG*5Jp%Et*8?| z%7X%VTV~DlWi&& zDHZ{i)|%f?NMWO;)YVEUnohRrsgRI*_#}89<-&ptg+}S5TvIL^K2CENha+hZr5U`H z(4&e))ST6-P&b0JQ0djO1{89Q=oy3qWv)w)D^kr-oCc(5%hF{5tOeLRBC>Hs{4xoO zA{ft9_(YR0_(1v?F0GNo1ocU`YRSqWIiHmJF|T{oaFCZ&chd};`fXfPGLPDv+p z*E6~iAOH~4NFc-GO+u^)qX^boC~1l~38_NE2ZHXFCm!fZvSR8~sWfNvAu>RKih1gh zzt-c|L*y@*mIjosh35uL%IB|f=59TN%Mf)0!mjuWD)&yU1NZBgX1M>f_BT8(s9783 zjCW_|2z@hxf$^-aO2EHQM=-8X2;3_Me$@QYaQCd!l;K+*(tDCxi4>HT*b(uEH{?)7 zJW*SaSq_Ex7S)iuGcijGIf3`9c?lsjy=R3d2m=BdGU9_BaEiw*lk&Dvmy4w`mtNIF zgnBNKjTrO|YSn@z-X1 z0DvVKBFIGTNvK)i&H!@CWdR?pQWk!T<)~6UIvYJzx;(f`$tf2?<$sL>sQ|3z7WgJo zaiD)KyUe*|e(jN~OB#Hs1Sups-0vpbsv}#fn%+kE(z~<@f7R;Y%kMDMWFUM9XZnZO zk%FY*E>QR*pv4vjLKa+dli_hgYuPm%h>&qgT^?~jcTfV15~*y{L_5HEVkh~_^NS6? z@7rYmzfxqD)vKlCl1PmbJ09r&KYQ=K-ZZlGjoymL$1}EP9EZR#tO+Z_C(FVy5JCun zVM5kKv6V!`kvx)}10nzJ3)q*quVg=U=zvZ_Z4+uDimP-QUD86-SSz&TpRmo@N{B3X{4+@>v*|Kuk$y7@{&KlJ5~Q%; z{vyimyAXvEh!n=t*B@I1^91bNrbg)nr^NDd4D_DF=J~rvcT`JLZSN>;dsb>Y+peca zZD~YrBT`EoJ0f|wp*~iky}8--n$}P{766ty3!KQMQWK)6bbcIZM@o}KX<<3;);-o% zJ{20TIGSS6pVw58oMM)WR+)?9C0Vf2J8c6%q8^@&e)IO?^n4-e{tg0#?%T$jG_8Yu z3NX)`8sl7r<9a`>m}ffZgs=}?jCSR_O7p^HO#H#WV-bZ!IsXv3JrU?#w`vuc+FMeE zi1^Odtne7-D5B-nmPr#Yp}=$#Km1T?wh9Ewl?FwYO@tT3WFL`a8;oM4p}-sWuS*?~ zuy@m#n^Uv2-35eA9V@8F7+O`@AbH7>r406SK0dOsojD0d3FZeOE#vbzN6p9@Oh&Zu zB-E}kx;EUIr3G3Y2ZFZ}s?=woc0k(zx>x!|WW9n9ie}RcpihVBUKWi;gfMxP&Gfzk zht@g90h_gv+az)~;VIQ@V846Av(dgZKi@zB5mcbEjv6tCD}{*f=B6S1&GjC%?$xw%1eH&p zJ@XWWQm6Y3*MtqPU!!aVfB9_6u4t^@q-|%WtwmhN(I{;^j!GPe4aYQY`!!aW$kn*j z07Qk3@s(8j%&emN%9d5bNErIPQ>Q1XLROWAc(i6{Qm}#j`L-YD9rRUNwoqWR zlG#=(TDhOEHdAQbSSzwz)gb%1XuEzDK}MuuJg~S}>!6++sMdNmUl84tS%g96w-^=3 z4S!!|5)0Q9AJO(I$0}2o4Jd^cD{$aGit4yFx{TqO52>}l6I;wQ45S9y#$;>)xJGMa z-bGYsL%~}7GWmKPMg?QTQ}4?Ec}Notj)=-Q)aw?;WMrvaFBReiq};UwHv1jI_EC`v z#Z@|NwCqfOv<2ckm)ZA9Fh~T>2KCLnqsgzBL>DRTkfN>J8#qb}o9>8aHSfD4VZkI= zltLPKdx8@1u0=*%O-CvfELMW)f~Cux=hAFbcoHf@ zcU?CY43%MzXcWP47tUmo$7l-cSR^5rK=;sEl$wg3&*Ptln>G!v%SE0wFHJx6}v!yGn<-1tYxw|?$`j*Z%=vvXM#`!lB zxt4Ns;M_=PXUl-Dlt%TrUWNU(JmjW)#8)N)3%Nr%wfLlRin8~(&54XN!psbbnS4I3}6{HmNa1KZLDlv4T` zE%ecxl3ZuqO|?k3Zi{s8oI7Zi2xrHl#kzL@nboTXf8*W1h90=6&ckhTMd88DuiM_W zmYLQ-9_iR8K0U+H-c)cTSH8TNlOjW8@j|p;or$mG9`y<^0c{4l7-cf#TwKsCCa9&C z)#OLB8+REe0be3?~Xyw-g%yC5XvAAazRBExEq3bY`*w_6pFujkan1TIn$ zY>!1oWLZNcP-8;?cj->v51z@I^2M4bzwK$g3tg0gI2f7SmyP?^Tow7&O@q_;5X#XH zKX?bom{_#1-rh70Zj`^7OnZrB)wq8xzy78H-gsU#205JdkB3)c5cW)}!Ia(bB;pnw zzu7zG)>O+Zfta&r!D#y`h_K7eOY$ zm(P+dRr)OO;3JFgzDkmXfoz0|)ND!V#8w;O(jlnFy6Q=fqXX32>Jd*(lOyST9I?5h^HdID4+@x?3|U(4PEKv z10I^4SKG0d{%Gg2jqM~lOxP)p5m5bS`f;X-iLuG11YqH#gnx^ws&GbD_ z#^Ys3&vxwmK>jOY{*fRV4m(A3>lGkmVtbz z;N(%C8LNbFC6)SJ7!NL=-04p%f$~7ZN#DQL@P4A>u#F30vj*A@6%+`U(3n8_>O^|u z1=zTMeIAW(8o(8}t&X{wme|-F9@ZF+6T?1j(=hi=*^pg+Z9X_m&*y*h-~4JaF6vK4 zQwlJ0Q<6qlHywAJ5J)av;T(Lku!GKkq4J!V9hG$N@JcA?fO%^_R2BzX)h-H)3w7nY zhvzY3ZFtFXrfsjR-gE!t~D1S#O*HS4;(H^RW z(-t-#M3jERN=;VjJBBdWvd!WF3}iT! zlm0e74lgIRSZthjY#x-y$MK~!ta>;|7I;SSQS~N1Lm2Vn_!4cVxY?hUGW?!-&%Cp} z2m9U{H1yQ9Wq$i7*Ugy~u~AOkTDsxP_3vi_K}Es=lEgvVI}k*1KsMD<+>uUTrVk?U ziIX39Ku)yNlX65A_oQs3!wmDE6?Rg$6`B6FXlqz8WGl!K+7Nn>OwcL2m_)<4TOhMn@A8L4b}2rUm!;<$*ty~#vgPLqlE&-ebTUHTH}5gs9CU_Ly79Ep${sloEq^KlkUTT+&Xr%r&`_p;tpN4qIG zPk4y)UaW|CBd=6fN$~FqfrCG?xY^AQMx$af`&?d>zZ{QwU?LC^09~2_ZzJ>Ct`9A)UXQNfp!m;kN)xCfD(5TJgLeW z4$ZIs+q!_{HDn^C3+2&hh9EW6u zg`#-}bBm{5IfqK80>46YTXL~Xm&MmYPtJEC((q&wM*uu=MUZdbu=`CXe;1gc24B7@ zKBJOO>H+NU-7|F;r4-zVD$+^K72<*nvoj@imN#`CQRWRuZ;a;^Oyrpt83w>@DKtp9 zGY+RS@MyOSP(Ms(rH(q@yIG!Uk6Km`M-r=vZ!by;LK*&|96}o<;~UIw9#?OTOv#Am zPzo&t$hU8w(oC_Lwo-vnYveWctmxDQr@$kaM@+?X66-rLK$GR!*>{;Brxs-~0oV>J z_L3xSmM~Vx9<>su=PSc;vI2Ig=#gqdh68O+>pN#;kvcvC+t*J zXuyjyfIj3;r!~-4NNYQA_*Uh@cwZTr)BB1R?62sU^wqN#XmK{eujViivp)0GdZk&p z^QQ~=F*sC#H2QdPGUx062j@i&Q%=#!ioW)p$2kI47aryvZ<9LB`9_n|#G)md;=1Hl z2D7Z>;)`g|XQNL2TGl&l@d6P*X>9`~$2~`c7T-}x2D2g-$UqEeON-^9Due(9%F2+P zWE3yQM@R8+TC8El4wU~LVK$ZVP|kK!r#R2W>fEIp@uVg)(DPZ3#OxkozRyzYLp?6HFc zPzMP?aB&o)dLHKm)Ogm3A#rnVd5Cybjd~4LvQZA00b?ab20(&$rEnTrpx4DPPNO`@ z3O=Wibc0s|qBQcIleuKn^@vE%B*!*R?D5dY84-cE{OH*;lf2HpG_K99x=l$$Kl)+A3L5=slsx6eIvj{bNm0jM`4KPk9oj*K)MD4+*VS@vwr}hvq_to6ndja971ef+1+7 ziIRa1U{%*&ux34O0FgG<9M5h$NjVIBZ*^aI)Tlw&U-eKr-5lm|bo$ipOK%#{-^z;j zzo*4=a-3#)yyhLo!$=^o_!CUv6s1}>#P{HwDHpzUZ9XzM5Gd3Gve#VEsZB|ye*5wr0c@jZk?BCQEXFe&Dw zLG(&H4IgKhftq%!h}nU*>ohZBL1E;A1IhUgI$jUY^1=&wmlF%)m=`q4@^v2!@3DaCoKg`w=!o7Zyf3 zNc9M$cA$*y%iaO;%Y!vVoNZdgy$AaT%4RN-1>u+un9=A0L}8qvCZ9pXJA9opOl7N% zrlNPvK@ZmM(lJB_#Fz&!PDiX?)hWgSq(m>7Xd;1J5C}Ck5+guHb+G1r2C2{<@*>F2 zxK>R<+9EQNR77?(AsEKBv?M)d?8aq-j8|F|v$NRa`N9KbGEwP0VTLd|3sR;ujWzvKYF~4GW{(y6Qkd%%__h_b6m?!oB|Z!#e|HHDsAIpv|qGW1wLKHB* zH}|gX=X)^n+Q$EzdHO=^x`x%3FU8-5(Z0zMINerJqF^Yb%YQ3hrN#Pw+%+TsJ#wm8 z)?npYud;|Pt^~r@l(}aKW{bZ+DnVDZL~7}AG)spkprL(@z%U%q2*d^ge5!ElSXoLK zGzEBu!>wns9p3$>Sm$pI5bQsjiL?QLdO4J3x!7k(SOysGdBv0$Fz;WBfRyb2K>u)B7kH zA$*mFQD6u(zRbp#(>N{Muw^}qMt_Bak1tUt*VJP+1JY_2dqnT@E02JfW7C%lsfMT@ zv}UGf!or9zrxFUo^X!Q^xPPr?;s!7wrZ)$zl4Y7_lsnNQw(F|bh%w|Ca^<(V7x>rN zl(eAeaSpk*5rZ9c%pGqx1#x10rQMgb+qP$KP@?H!ls2Ybo*m8#aN>m+TG1rO_~A67 zF3`dYbo_KSDSTPS$tk~}R;5=@V1Q?2L7};`5`)`g?(BIRKaKr z^NHkMr?{zw=&EVgZKtf;cPG%|T~cj5J?V^+9JxZdt5(}qiR%{Y@meHbjfl-5R|U$S zn2ngPO{LJ>;6H~zZ~p&zq4a~2ZY zti&Lpj}e`Nna*rjM@e0|1hPOKxv)0&{=o+yksEPjoGqjHBsMmY)%9@ICI^@EZ5ORYB%bwgQ+;j7<=e-kb)dE&GI){K5e?@xt7AoiY(DqLFj_b{3IEV z-f%VS&$<}xJ=iBP6mBxbbd+RkUW3!SHY`BDlHH=d1A9IeY`->--R<*yms$P}bG+#M zLIcPJ7MOq=2~u$nTIif6aK_ofF#~{jXHk-p02G~P$;cbW(W!S4q4#Uyjb<2pI7{I= z6c-9W4)8?40YOd&%lFA~8jT5Ye`nC&-FdqTqCEZikpFxazTM2eoxjeJ)&!AQ&3& zMPwgjC6nEX9)DavMp%>KIKzPK<`6lV4c~%17XN$p%v(hP41X!Gr{t)9Tsp09#M5dW!!^j_4#4+N zgL_wV8ViElLI*3uk=rYWkmUBG+Y$2J3Qe56mi@dn7c{Kp&Oe4&!Tgs%{A(=O8dP<@ z{kn`68@}EOU%#t@hP4iV16E@_wjn(a*zJha#*0Vd-JK;B^aDM%9TUsT!|TgcWyvvy@5nRSzmr}baXH#orPvBn7p%1y%?Wihb@ zybXU!DQd1!AqzDWem%sBp(n^;OgaF42WaH!QIE zRznHCYFIMDR&+};G7YYnC43WB(PxfamT81*cRDE#RgPjS_UJ6@W+i&CZR{`2yCWO@ z4Z;_AF%JFvK@jdV9K1mXY_@9cY1!4og9p z+G9vC!WAgpP(XDafH~3}9Qu&~0)$^%IV^x#Zphx#6hf)4))p!tmF`SEa`P(LptrYm z-n6=LWj9oH>MrGln==TMp+KwEH#PD6jr)4#cHZsz6CQ z&p2nSvbVagkyT4UutbA3Ip9$3p+|E| zM-?fXY=i~fa z*6(WBW(zKZ7qJ5MV=)_^4C#IWz~JZ-2p8LvJWI1#0g0m1X@V{gHTfi(?2+`)?SCeWR{_H>@6p{#S*Lqxwy9=fencG9pXPahI zC+>4OuV}qV7rLWVWL(ih*NOkgIYH+|lM`#w%r&d!*>7K|%ACdDW2rC+u30Zi6|Cl! zJrzLFwyp+dB?5tzz~ytMHc(bgekX@ip5W20pByCs252H^A?CFvRj(Z}Yg$kz84M~O zX=Q}XaIKeh*@R6zuU2@uY#YCvGQeEhjkXP~+*n+u!bpN@JWR8Ulr8c)3)-fx^pv?* zLV9g|eGege-DE?&j%Bq5y{a%qF@c#TJL=O!Hy!8eRqjRC9^|MMsMGJfO|;j$xk&$4 zZ6rrCrFWE6=q$XuZb^zgKmE#WL=EdzUg^72heaQpQd4LFpWbR8T#Op@{me*&2`2KI z#+Osd7n~GMx?2EundJ}*Ku(zI8}J|ic?E&8HTPa9x_rJC&Az{pK(D4#I_)+cwVQC; zCX>jy5{%eNT4YLqH zUvFor&>RX#1|2Mv6LvTpP!izJ+{WB0i!PI^2rqw7Tjplu-6kS*={xFhqe|JIpjg!Jx`S+Yf1-e2<-lwI* zcsh;qPfPTv4Bw)7lI^`rOjr%a0<{J1RAIQWCqXDg%WBz~1*~gV1ZGvz%{n?79%@Kp zpLf8&*VrYQHCP8FNx&jNiBnMdnN687k>W{Qm7{Q*tFgx2ZW6Pu4{R3$Bb$<97jCG^ zw&pSkYX+L-dv~3)z5r`FcH%R1=`o92WokvfOYWxC;?dumcyWJtT7A=% z%j+I18VS?-E|v8hGqGDA*!oUX_`T-RN^Zs{e@O>rHk6b$8b3B#u5yx^t~=pwKjRud zHY$f*>0g1Z*i)^X9o8zwR-7gi<-~$h-}Ye6=!QWWQx!r0N6jQ2kLkEMJ_c9HEOk>~ z!-R_t=MUV>bJgKm%QCfWG|xxmHac2>lFBJ8ZyDyTokHx9xMg7Alf0FcDli z6i&shjpOJ@26-)N@1G~k3QhHgXnYD=_#}yQ@X@@2-3!>mD5@r@CAgQz%KIGIfpNe1 zW)wl7I=w{N*&y9A>*OQ`wG5hE&g8Tcw-?pA16Hf+IJuo$wy8+iHf^~GnmntVX1o1r zD~zKuyc502toJg50b*!t>tVulKc8v@&4??_EAZ4U(G1DO_k=)W#*)o99tDvH^Go);zzf0v!m))Lr|5Uj(QB8ICs>*NcDuvyG-S%^--b_ zO5BZCbG4})ScRkE(7lrDgv!+|)HoP@|E>7%<@?n;_aRhdn9Qagx=)d0F^Y5W@_;Q3gmN^?@d{tXU%!d?9k2ynVf{68_sTaA zI*CX+4YF7fHfb&|A7$D4VU$aW7r-8n~2xX70}`1jUPv$5W2 zJ-yR`sBp8DBX~8X17SAH)a;pJ5q&DGpkAz_fIMFEPsI{M43T zzt(2yYgyV&#|~I>?$DkjR)NZ-3e|?g($tAQt0eSF$)lx5Q75~*(z_*Nm7$pCcrP)- zThw!@;yn45@3ZUN^#d1hdXh`|@16JCHbaRbD|Um~2_(+AOr}$|R##E_b=A$!`S8lm z{i+!H*PBs zv@`DY%~J>eGfrJo0xhe!5rdG?glkpet$PoidWrW>sQ1)Me)_3GVmL(;gKTmy*{`)V zC{JO|xF$CWHl&#fX9uE*L=3lQ_8V`ll+v(lco1&EWx6V-j=MWioyGZaJYw1^C5iq{ zWM<`1D`h4tOlmS3M^W*WwK`-DGBf6Dx+9DV=z;L4@)ck8(E>67${BX+0VVQ=-HMbz5FT_R%}OpXaMMlY{U+~#1H zDaz>^uP}ylBIE2pHXE+ab75KH%)Je9%bPS&%LobA zsdwhMi=E%mEbVFId>beUC<}q@)APo@u|HQRQ*JT*+wgF;w}uK8ca^fBUUdc6+i7&F z>8w=lG>#|Ud7NK)1thRUs3TUPv7b3qFRE&nbP{2DUZs{0gtKQ+)&AX-UW<$!fG&p- z!be3@kBL_;Ejt{VRK?%&J@o8Izj1|7-d_}MnLYW-%2J&H-M#nOS&0GO~rfGj^r7XdvTH7 z*D6K8JT3D~H_BG%Squ}kulVHl$E#ZZ4PNE^HM1J;F}tc+u?~^}q?Kch)U9l&u8Ve+ zvb_euvLbdkt8}Sj+4(fSDw_7xSbBHu*2OOs@!YGNBT>j{MGeMj=DK21YV&xz1}oE6 z+kIt!(Vi)}Un;7p)Oq7@l2W*5nR6)cmIGi$nAnH?1?S%nG~juSlc$TBWcdd(woVCp zMg?Q(b4fzW5bEWD+*OWPe~seJniS8RVKi#z;eb|8P5|MWNIj4{8SL~c?V2^yG1u68 z+q5v}Zs>CZov>~iW_ozUcbuHcK`k&vm43sN>jHy$T}R`;J9+aQSQnf^O;NRTiD;!N z3(&p-&2OAjW2Zd@=k zhJ(xzB>tw4udHAJS=9<2PF0+VYu% zOshH>I7_T{t$HXjdx7)~?P+x|>v2qqD1+xVhu#nM2H{K{6+5QniB#SyrkPu|3-9yw zg|4wr%PFf!(FGUestG-)Qj!3N7vrN_n-L$%^L=1W22qbshl83~FCR4bubq`p^9ZSY zQs9h-vCvnRi%ZQ%)z-47A(9w3?{YZ<4>J$)$6WUF_)UmAXoD1(`5{aLodjG7kZvp`YGB8rv0f`&UEXzqK_ z_TQ3h(hhEw73;V_Xx9;Zz}DF_&hYweV6>J_slfCSs_Oc>x9UCf8V$$+Q8cWQM_Fzn z#syvwoGH(|12pC~Y-EVW0kmT`Q(k+80&JE5vJ#5UZaQA5h6q^!=+7Hn{{D4wgAjzr z+0?s#E$5U!`KEJ6+HuzD_VZ!!;7&Kf+)1uVZ#j>d!Jd4uzaZaVn7y<4M(tPV(P7pU zdtAvL6?5dTAO)a&Zvuv@!R8yv%h!EondAlVo_}U4SpiBu`Q{1h8iXS+4DlK4vqtpvXk_l=%c| zQg|Iqm`v6J0bgvkmq}NO`I6dRG0kgj^Vm`CThaM`37ZdnHNPqoz5oYk%}sKTZkTFj zxH6<5$lP?^%t^RtvzX>>nwPem6ze=GrnwtqVR>zETb7wt4P_k$!*7mQD53ws(rYcd z;`MTE)~#Pqg}n&MgkT>PmSc|MhQyDA@a%ka)fWiYo^`v`nrj)=Rs_zSSruD0>NO;g zVi)0t_mk5HakCOC&n>F-@vk=KWNKcuhsy(`0_e1%lc&6JIn>dY~FqQ zX#W>DwR-b53AOT7VK7QhB$-qRiQ8#xK4o*5fL;*=#&bCQ77;i;lOIs?!F2Ua6a; z@Ib6e36deEL+}{F>`>Ecf~jk#8QZzTM8h8>q~O2SRMVK|O*VfGh>lu#QWy&GS#6xS zvj1l?P9TXFYny-kkuK%}v6g7ydtBygukyE|BHnYn(fW75HIP6f=l2 z8jaA(rKxNj0X2+9v6aeHroxlhlQ0S+ZCm%I+eccJb=0i6mFZ5PEZRxbJ}oJLBC1Jw z)(=TUqByF;OQr?cDwk|;1BLn%RNeTVIiUqeo?`8s%J%FIJ4YQ)pRR$elhGb%m_{`? zgO2x{=J-V27xxXN6>$pk9%xFtb?0-Iq|L@x%gN4}I9>RFb&}{!j*|#EJm8O;2JjCd zy<0b5%=#c<-bF$2aW{>F`jp^$kriJe_jNhfi!n;7KA%tiJ6Im5M)tV(I<~jBKacUn z%=%)>3$>5@rSbU3_|j}JdTPiC3ueLTyJNf3HU15;_3#$_rZ>{;o^nZ ztdqO@W zGdYExc@6fkEA~Ngwr4w|RC0Kz!_R8#%*k8zj3&;hSLShEqXu7h#>$90h+K}YfRc|M z)iaduK!eCk?30WW2jdmo?u=F0mxoct^=NPEiL`%!Vhx@~y<}tNRM4Cd-w7 zMF*Ht$5hO6@6SKgPkREJDBLIMG|ov2LJ`M@<7DVX^r+lXvNxr@L=v1wCX#a|Kbg=@1nc%twsk%B_-wXWYUl7wG}#GLPhc4+<1GO zlR?Gjc*__J>0MJ%2(7pQi2(BF5=Bf$ah|L6&an2ybd=3XX*qc6t=WWu4$`A6Kf_Tr z?Y%DQ zl)5&*;ueLraK%cM2!9AoBF+aT@u26eyCfafF~s<>b5=vn&bs@c95kcSe5`w#;_lt6 zno{le47XoY^@*kB3RF_8B!y>ChD9Sw&W|LLfv%31|6Q8{WuzT9D<-LWdF|)b<}x+O$T$QIwtr%_72gKx7&6r=yu(>Co-QXuQRI=yC`h9`b2*K2mNoHUHS|kxW|KTdH$asTyc{{_(s^Ch zvT|Fc^_DJ8_TF)^jc=;=jT7Tvxj&s`li!lQtI9H|XokSW596aOkNq*a{5RTah%L)J zAd5QXtkmHS`&Zvtb1FzSU!HS^wcE{>i#MG@b*_a>3vQG5MDU~?4$_?+9mRPkDd>+< zKb78nu9Br;s95Bz6I#kh8Nnchsz+Tl%6Oj@yr}BKT@!g_LTFcmkBeMcp8a+JT0o`0 zKx8u$Z;eP$3BqueiR4ben}%8)oucfcOtbUjqLr-`c~X)J>uTiMSBXtR21F2;Ek0^{ zVta2?W8hkfnRiG%d|So&va`8*Hyg{(z~+c)xxEHxsIavLEr%XxeFNn5N{dHht4`;p zEx63wTjbqWe2Z0BtIy|HfJ<^?y`592!**tq2_!40Pk{l~PqO6wY|DjR&fnK?)Hzt9 z0;5-`LDtKUo;?#BpK=K?1clOkIyiUb7{rQh5W3A>u^I&EilFE~lW(O5L8r>RjoIH% zG^yQc+lKO1-UNn=LnjPtzn3R)0lW5s_oP?Rmn)B&tJ<9^-yG_$k|RtIaTv#`npgI_ zEpL}fAc?}eZYl?CN)CTx5_P%37dGO*k;N5H6*OJ*5s*f16bm#a!_VcE&dWI{zDt*B zi{iB=S=mL6-iX{(t`I`Xcr#8j@MqTQ>Updaooo_H7(GVC4|up!3ktDW&sK>kSNq1q z)8SoWWWI@Cc^=EkR&0!;)3J!&^3}hUrt5@$h2ZhuChY&aB>a;(vb_rlQ5KPv>?wd8 zB<_boa(GZ-Z8cRK3u{SVpm$Z}P2!$)lc;S+Y8k{%6$4)_B{*i!w>6cTje_jPV+Cyv znC-Z}=TR~yyM9%HWE>6SlWaUvzGv0I6vDUWgrurQ)69FTQ5P`^wnDZmMPnMv zGy}V<86fi55;w4^PB~%&L*R=>17u+m_0aqn9UTEo5Zd)^7lP6xZG zcG^}DskvDKnlbe?olhybPovHl`0M1?uz>o~jD+6D*l#&6WnOtyX=lqsDV+7+g;^U2`y*gl9+^L>NDMJ3|q+ zPvT;d4)YkJ1MwnB&bYXwNec8qS)2>6sneZ`kZi;XoJ1GMjDbffRRs_}by=BO!XhX$ z9yG9~fWidEBA-%F$oqzFgqzkRMg*o$2>ns7vr#;t@5u5uSw5At1~J+(T}oL%H`x}} zxPZ63%=56fzIT!p)3tq=r~T6F<-`2+wj%IY7J(8bJ4Ch11DzMS8_>s1Hd>X5CTs>y z3gN@qu@zOTpKfFbquW@0_@wio^PscZa7)0%L5W`(5{gqZr`%u{aVR7fUVC zKiQ)yNu_g#pwO>BeH#6ApP{d*$fNyIQcU|3*H<4t`dI>`n(0<8%sH#d%^@};P?*yu zFLJrf?>mWWO_+mQ=5o%c*W`5EGUs!@t2~iESNc`TK7RN?RKrWmgGPCK9o)Ybm2TGg zCk*6f?fx}lKn>DqQ#aKDTyP!6V=EWbUd59g2Nqw#lolQ1d&z%2{gf`Xm*}{=HqbOT z-nw!yX*N1565E#a0FS~8>HvaIMGf?1hiPu9137=W2o(~ zD2%rsr!22XMQK)=aCtLd$~S;ewPX@kQ-+jEW@Z_9XZ<&;j&jSeYiT;H)W~uZb9H!j zdwKzR7}X705J44ML3OZ)my@iB33VMY59vUhM@;IS{xE$ys5fGW>QC_&mvbFg^=U<4 z8ivy$BGE!6HLX_?S-BRCcA>h!aC*W}v(#Qn&QRLXLKTS~jm`=J`2%&)^sb;E$b{1A zO!Dj;qRe0|K=*{>LD3=H+3cthvP6n(gNYKaP-;KhSSEU^eh|A#vr}X)U12?(P(H% zX2Wc9Mdm!OIh?7>5V}9iNHG?rL%0J{AvX^!TOVPyu)4VXXatc9Z#YAshhmz~K<iT)Fbms^IPA8f2q3C8d(kv*d(BKU3wY4XT5~IHMiZ#h_Kpmb zfu4rrERFHT*TjEminA6LoZsXQOrO)_PCQ+tNi%2Vw)N5@RJejc#CLoRonNhfz}(8- zgZ;K@ac_0MB2hG|DY#1|%Lr1#Ax!dYnt9JW{MtA8V~u`LSws1HI=p5?lV#m_0k;ph zut4#PT9)$ZAyWp;DU>i2TZgGGO1aqT+^x5ch4zwtCp>%xA06$T?pJ7N;3Y~(6!h1? ztkW~{&UK`E&x;=Jo$dqcnB^78*`3Xb&C(=b>J~HqRm*t{bY?fuYE^d5?LcXq{ECs_ zylVf3_ep$#Jf`|vG#}G^s9Ho<6^>H7V5KzpP!OL@IJnQROLp9>vfOttLqtx#6GKFY_B7su(^Qb1-8G1oweC!B)6LFQPmc^92T#nwg2 z=@G6vpvizfPFoWHO6BOOBLD0=kbm~yN&Z)4*p8YcL`xB$dzxXx_~UUMZ9KJvcIFLX4->Ix#pRl1l6$SHA?hC z8Fjc2_q66+cCI>K^otu)hH3@S!T_)=&ZF@f<*i;>@op?Tm|orW(lC){-01tgD%s&V z`r!x9dDMaQfCag5Oiqs#IHGey77L8qzG7X89kfI!kpe)19E5RH#NI3)lL;6c{s?*K zvw`wtmo@|undOL=tVjUx)H8fTqqX8sbeTmd#MZ3$;Q; zs~W)v@73q53m=|7Ze|8N1F($0X5jLkdClL}TZH<`8SNDGT*PO2oNy8sNVq(OZSXpJ zO}Xhg6i24H2bZ^OrXAJO5&LH+2w|-95Cfo-_clt8b$C94`KHY9UW(W0(0dpA`Yt1&g>2x|3Z)rtt z4%Fx9_xQ(EQ@9Bp#rYahx`t;;3d*ZYiCgt!9m{gCOPtJ{1^2I!^>jwbS+jA?V^cHR zXx*sym{g_N@cy6w>;LxfvB(+Yd;$VFqE9_8K*L7&P|b~+jcfW?)0++FF{j-mb=oy8 zU&U}B-_kfVPsNY(I8Nu3zWJ2Couzu3hF1}k=HrHAKN>BJy`qPoQZc0+U^6+$Iq2+o z+e~@SZN`NtI&MWtK6h!bt-VU}Jr zoQrohjyn)X(QJIkX5$gWd3xjIG^P|>82o@?T^RofKcA_7-CH0@82Scc&?%H0lwt;H zT9&;|r=#YZR^>t4EJmyCmEK#kVPG;vvl>@`m(6x3CZ}JHUOl|>0O|nIvQF4vT@LoI zS&Znt;2~T$?{0P^y!@)$otsO`(}jMs83XsUzAjUzIGL*|O5vzW#hRwvL7FY=Y1Kh&>$@ z!@71;YQog4neIy|H*un&K>3yMSzW8Wx|V8zzaoXxU_`A7x`nWmsmv&_yqspsmN+1w z6t5<5i(wcOr;8gs>2;~0tMoV4G#=O)q9DIjlpo(5RH}1q9ia@#Z9Z_$fe5kbWT+M7 zR>pfRCi??XH}&$NO6m?Ecx}q&omzl}a3IMmRxnQ3=tuTcu?;bok=}f213*r2V=Ta< z&y+b>cDGK;TzoNFMflgUZb>-B(nNG5;Co|_2C`47G6C0|@Dq>YO9C`UWf5LaF#wIO z*G&)>F)w66q;VK~35Mv6S{=2gI)rL!@H#!!nJS^~1e(@#Uhe$ZJB-KK1-CS0kgV9= zD^&hnoS&^#u`$WG@Xi{H6R^=V>#{~YlT|c-g=AjHUV(gnY}mq??1PVDwPjto4Crh$ z@|Mq0E){K93n-7r}rjTwct9NVdCt-uCQ75UxxolZl$|*n@fAr*CQ7bJ#1h zjM}N3^O?_T+0Cu9hLV9hWd|R#StF0Vah9DXX~T=AUNY@?Ac=lj0(}nE^ZrQ<{`P5! zGy_IC!#y3iGX#W0wtwJRozg6*bHWlFJ++1>$!HX(u#^U_e0ll(%CgNE0p@@M6hy27 zt)D9CLj#Rg=Oh_1_MqB6BF%+Wwv^k#Vv$n)q6$>-K2K|d_>+L~h_v96RVh@YYdn+e}Kwa_^*8hxCAn3aS26_dWW`d8h+B*P3knF{K)ngXQ? zAK<)+A>vf(7p)}prsi`T76r{7KpGH{ywDL3L@l~NElWcr-mgSR^q&U+@BVsmXb+I&h$h?Xm`q)cMkDpmjy)`Qfh_!)G zhybXfqvR6!oOJ-06-Z;C+lY~3Iq48$E>VSoFl95m<}2LvbU4e0wn?-WcS(JE9>%;1PjuUefyWydW! zsibz5*KAAu&BeATutaQuLip^d=kxFE(u6V0#sq`_0t?_SbJ{Zf`%N@GX|t89t>mlc zC{Lcn`Eg8mpr{%bL)6spJc9(Z*bB&>sI)^}x(ehWPxe@C8PogN}%7Klv=hQ1m7!h7V)4N8xl#j-1cN3}oI|GNrkc%XnDY?kXm7uz?j_Vy6OZ_WQbI{EMCJYvagu5cT>15@R}P(TwD7su zO9p@iaUcX>k!ZQtOE5W0We@7*oqAH0S|rvPl26hirZ_?%K?9GFq$2AGiV5ajfwrV8 zw1`B+(U9egK)TR8Qu+y>9l}wl^9!)Hse-9IXnPM?)r8B{78C-%_w~Sc;<%D$LjQ2s zM~B%t9tKb6b56w8D>YMQr=^c*l|S&!T+Ye^CprVr7LiL#4N;vGh#^L;O8J!NTU^C0 z!BuF!pnu-folZwnJX{8)o347!*UXkm{P3Iizz6X!>=emUTQB@2RsNbR182vV8h7zFKN8WqPz$25zpr zrQYIoDc*tGnEv%DOJV$U=9zjCBkBe?D4<$yEQUEei!N~~)S!lEBmQ7f50ksXdR$KP z2%v?b;7OJYWA#v7)p2msI6p%`d+td6olN61FqouM`D7;sfShTTtA}(^98BXgZVR5x z#$&+q(yw17#S~v(PSIuzQ>RvkzouuczFIP_Nb}4;d)hfx84_hy>%?yu^up_NlX-9+gKj61~+juGkeJ@3zpEQhX8)c`|;&&|?f{u93H z4agY!W9@#4rh5sA-$0+EWGa3_)1PtLX=SbC#`rm$(HJL7B||Zd>-X?l*E);~*~wXw za*N3nzI9|?(P38V`A?WGgp3Q%63Fm{TPzs^O9y#GvO6dW$wTC`hcGo(sYf`+rOH3c zACKjaU*wM`^2a~qk6#U^hQ$KhHu#!smJeg|oSagaxB%Ci_)dg=fp9);<>6&KgguHp zBp$*;=GjCk1);r|bPP0+0Q<$n&c95uZ>(=r9{3Y9GJ@+1ejTU4;nwb&A`SrTWA9FKNC}0k8sI1k zj`~Pw8>?}EP_H^PTUueQEwz_sMV#~O8}v_D%xIPlPvBbfnxqzLGNcnJNk?#>1U?WI zklly>VlqN-r*4ws;WbM~R4e9xRZ<{s!O9Xu8z~!~$D|9E7E zG&J)7uX>7Yzz;nLToZ`mve1U1NG3Ghkdkn%k zo#Y*Y*RizAWfYw;Tq-uFG1=6w4U%y*5StpdAy=dUzR|){3DKl~iz8FQGntE71>h1a z#L;MoVh#L(U8S$35wzC|8d$h(!G$NfRXvkcePLvxi6& zEF59fh@sMwVH8P(Z>Cz$ZDvM1vAwi`)XkrhQ#{N-rAyzcol{Y$1V3avRJ5Bbj!nDR z;W`;tQd%x{po5Huk7rCLl4FY%h^iwR$LG0?rP#( zr?m;nNLxl)BafKIrIXhC|V6Thtk7^)Y-BtAL1^pz;`>>fRhB znRxcb9wn-YU_OR3&{~{DlSz^u7fWkPPcJt8zTY1D{x9A3OW)sY2maRQp#9PJKl<&^ z_jd#McN72JX$O9g2JKDX-}Kvo?{Bq3KRm?W?bELBpK=>7eg9?qgYSQ6hko#DK)+u? zdHf3+Z@=_|CtdTV2Sxv&-#fnl0e_!#b%kMj(+?(H7{NySrT-Ggd=~U!7;!uBU!L@^ z-JTg}mj)L4e!u;57rxL8`bqF@qaOIduRMs*4~uRH&HL@x_xm{FFWrEi{MGgSpSnT2 zA54Q4J+*#FZ+x8FqKkur8sX=lJ@9=$X!qgeOZuqYL+n*7LDUZX?I+##mjA{#bK^s; zxZU@IabTtpeybV8-&^K)1m(AIM)CJW*Y_VlvpiUwh^W2ghwpJlU&7#V1Oa^gAq@5< z%*IA_f1AF)?Nddw2%+!8e8IYJ`oZ(>(#EzQg!8Khhd^r^?Z6NF`pss03&^C;OU%zy zHz5pd6WX5i>Km`9sK*yhVV<^o@Ma5U>=kslrD!TRYzO{k50)W9657JK-G!2mVHx~^ z4`Ya^!qE4RyR-tEG%)zI0LBpF7~vKE`?4MS{kObZw{Twtw0baOfxp}1$|3JLq^MxE z3v@(e5n!7ZT_9;+c44jFwgZ2s*WUI0UHsSqRR4o&!zAI1zY`^)I~>n8lpIk>ATAmM zO!5~=r0^+N{^bVEK;Q?Taa#;%2Fy0y@%};SLAo?C49}lT&FbQFc z94iL4gPf@ke6%N7+VghO4JI#c;c4&Mq5pw-TBLaztZwj5OaxBzE-=Tezxdb2p;utf`&Z9 zr(L9bAdZlDtgiDB2<2C5`k2NATOLo3@K+va()EKkvIy1>ne7k!@Prx&X)=+$4t;+Z z=!ZzDurk3po)ZHgMYXqL-;YCmhWLH=%ZIRl;qScDHvQne{*$?g?w6S=zsI5C`%Rh{ zVrpr?JSFt~56|_H5aO6|ARmYqsegjcJu}i5dUeJ$Mr=6gzR}dYjjOW>GyTEL^p;|; z;jgqh(OaqkvwPk}*02r#4o#g8*b)%*BoOn_)9-e;4LD!7>FG;ipTRg3yX*sQsB&Rs zBB39A4w++V>OlUPc40I7v7w7yc${~A)2OC};E5?69P)Gk^DzV4E+xlP3uOmivjeO#NKV1<#Q-dGDmco+>~F~)%}ND(-|U$m6i%w^YQ*|2HP zxp%QZkLEq{H2#I>(MwPU? z+A{^i*YHGe7+-xx$on?JSv=G=f)R>xIM3Vq))yR6DcCaav{;sl zQaeCFpIM%QtHsYQoEq+}JJ&Sw0a7%ReI0Clv0LMMNZsuQ0NG%IF) z7<|#(Hh4vY0eS7Bd(w4AlUk#J=H`qA1(K;4%Z?li%pDMiIyTf;CZ)0bqwD+s=)QJ_ z^2Hhov5ngX^Z4%%<^c+_?qG6jFto4c3}%cX@TC~dX;%&hCR|7G~GbK_1e|^0ee+^f=n^gC; zdgSk5e|R3B^vv{~ktAb}W|L2Y35x+|cm^1TGVMwsH3E?kSF7lPBx%ZhvC3`JcGj%n zBcEcI0rf(c`c1vcmx675R42{P-7z$}+8rFy;(hdkcM*NUOFw*zhy9@;Gh}f?I!ct- zy6Y3|89~UoV8GJDSsZSU^wvwd?Hzw`sYI<^#p3bL*ZNPooo-6{9Af2;6?F=G@BS9`vG^#O0rUD*n- z=tE1GqiPe?7ZO$oM+qDea^9i7Y|6~;!0(^*bWz2&>8yJv@6*sFT}<@ye;mVh_XY_&Dk7F&z0~VJ>)xFJR*3-4xhYp$1cDDg)9pvn4i zprlXa@q-N<1g?9WcI(dR0qSVr>J$8e1o7&RsuMYQ%qQm-a|!h-0ImoBq5$2`n0C|$ zUDAch`5-?)O$J>PaYKC+IzXP#K^~g>XEfr;9vIIegDPa=dQRIf;h+5h0+&%@cobU1uxYt)%k+{lb&^fBXyzvrn7F|<%^pm z%#p^BKdk>v-$jdp2LmlE@J{tjkGn6b$qX-8K7AGP=3~VqLO*y951_^H1#J+c9hi3O zq;>cVP#Oh~g9p}kSPL0^=?>{J$uD|qkz_)|MeLR+Ov_WjdG}TI`h3)ih%KdEa@Xf^ z@0HnBf>F>xfXo<}%!+#{m~@3sdSH}8bcb+x@~XN#lUVP*K2hgCU5DJ)zpC0Khg+{f zodNP8YF39tY_l$3i?nW>l*oxzD};}YVkdafe%STHK|s0+%@Xmud&EnW!e-LL7G7up zb@Qdzy6ON>>IsxohOhJhK$?K+qzBkwAiSsBT-VKS+C9&PTpcHN%r&TPgng?^x%~#e$PAwX@~>yRS%zq3sC+YC*^|+Q0zv zCuoQIet>F$T|BC`UuaFzW}gfUeO7Lw5(n>iLHGg>MPjK&=upI zvBPbRLoG-ADy%`Lp|Ulx!g?Gkt6V6pa#+saBoSJvLu3KJ{N8GSk3W3iE1M6@iBYEa zQ4F)yz<$zwu1l{5X5h?;c8wk8rII~R$9>69*0od@ql zN!VP+)E`NpAI&h0B9Wv%wMtI{)j%xlXn)}m{D{PQ5-V_Eq{bR>8-3v6?GHfFS`g+l z0~z8O5k1^>ls;pd?-5(#SWqT8x@j9l_N1qV_K`%)OD5}p?=rMPI1O(cyWwCVyCDf^ ze}zPceQtXam^n6<2A`Y`*5-7Q;WFaJwTvTP&m{~vB6b#37TZ437W z^C;V#o^*ws-7#5_W>YDuaXo?&&hcN!e0O!r1EO|qV-gaSvQq7$V9?J)y`Nu;jX<{6 zzWzw2}@;O%D8E z-eXkrsE6mId)h{;!=s)uj|9`M7C(b`rnzHk&M2W!8#C~=+;QGpv~b{uD|%A|cC~F~ z5+m*P*3T$*D`k4Q1ElG`IfU6iN52BKYM{5;b7?1jOdA}p1=Pb6+8Dp!S@7{mPhSLJ z<2n0Ecl1k7-6j0KvTOC92z%pBY1pPl2q*3nvIQS^k%{85`H>YbeLtAe0rP85>jXdk zgauE#DXkECJP6g!;^M_XEjy(_$Suv^6sUiykxE8Ft` zm@w&JLVAayOwVXqcH7&2ND6@9Iho#e70U>Sm!VCjCzLvDHr)mzSwL&(`)pL+@s+I$ z+6;$dmO!_BzmM`{@VPtTXCFc8{3~pK48H76rKE;ekFIe6iB=u^{^lyvqird|wcH6T ztAjtd2wAw4_dI{_CDc{6?J9k~_B;sH8I31@|2?t_EtdCj2_A`NV5**UABKXjhI-fV z&;IY&ijnU7eyN>@A+^-A8wvx{CJbos2R7v|(iE0_+;xkdn0ebi?vXEAfRlw=n^gfI zzC58uk9*>i;6k!OLwgvC=eYcVsv9cJe&~nqkQl>9UGq^tcP)b^-NFxhs+?@qDGZA8 zUR8Tm9j9EWJ-cwg5^(5;yHEHfOw`Zf@5gtTio2E{fHOsJp-ECiUsJBX=qIbcLgZcj z4ddMsDoWDU3b|3VHyts$U+AWX=Kjr(%FF;tAANEl%?*9cDLz<-Mws^a{=2CiQTedu z<7`(C^-&;oZ->N)KS-C9F9F`8pOa;z#H!Id1xFeA+Lz^JXa=}z@paNMt@O-wV^`nv z-;=`SJ+*21bM*aL7tfw)m(=1Pa36-sPbWALO8H&7d$0xu7H(5r_&@xPY!G!Rn?rSa z2Ir_uQwJ;zE$wO*{`OFO2Jl@jjFJvZk$M*D;GA~U?V`eGmhA{V`HY>7K3}~hp0ph-oXKf zG939nUv=NL`@y9DrOVoS*bZOtl$dqFtUC=JpvV#9ed0)|_4~Y@r z!}oBshIyaFcvRMG2xZMxpQZ{=;d}!IL24k9>H5V6xm)4;6BIOkR;58elkpW|8fOInkUL~r`iv1yzqMX6G? z@@O8QR+u&;J&9R~1>zDuI790J8bR#4%uC}4TB^i7y)->3m5LsW>$?35y0N0it5i<) zjkd?Fk#yO-rFCI!phcTx-6v}|*6{rHY3ZwJ`HQaObQHrt65RFMzx4D5ClmUUp1##T z3Rs1*K*3{D9uEBA$St|;i{Gpa0;yh9@fWM{k@ZahU8G+Ag;zOZWt(}Z%_5JBT6V=#6&w49Og_Wq6k{ z29Ubo37#&SS6yvn9~}2dltVEB41FXiK4GS=wYGn#DiONb{W17?1JCc14dXR@yrC^P zldgKf1Umo@k!+Co@=>ecXe~c)Xdf*W63Or1NVTe_js}1~gFH|*i4Un#hNpfBCV0AT zvLh#4ej`1~$Ab4I-9usGG#>MKNFDNT+G|P(4OhFz8+07L^n>>&+H3t|kLOGsjAOP! zLdCzZ%67hC8!&e?M?t?)rJ&Z;l8}~Ft%%ljg9Zf|0d)CGPqTqXLZ5qzf1er_4Y6U) z{w5}b;|I@H(&zZPxhJ*F71W%gRR4>2X+W|D@d2eICI1T61}Nn<`6&wB{xRtGdA)~H zoeigO&t(R{B8?grcKa3ekacwzpL@g_`AD)g~mC~>4+m0f=P1vhi z;f#NgfcdcJ2Y;bX;2d>aJ~brv=kNaf{jd$wRzdo^zQ1kCpgUryxFgZBe>*KdBz_p; z(aj$oku&rkb}Sy8|C$a_Gqv-cO9V=*3V%Wj3K^qKb#l4>WkaoDz!vSx4H1t3eZG?| zhDt9;)X*oL^anUK_(}wP^PV9+4GfNspN*0^HA2>o)(QiG@mUcJ1gXMUzUaFc&=_i`HrU}v(?!;` z+zO562qX}?X7-_!&pre@s8g1d8N6*j41E7#po&Pp`T(_XA?QGYaFreJcm3d<@A&ks zc8%K{Mo&(=rCxfz|FGwmo%iW}7!GI~8jt;##;mlW7XviLFgXu5$RwB!G(|pUwF6Mc z*?)d^!f>uumR((*YU3 z!7bx6`FIY^i8jaH<{X!Pz!It3$ts0&<=$?0<09T#qUTfk_LX?zBz51E6hiBKSD2# z9daL0D->#z?Ji9rk(scFPK2DO?4W#4z@z#jF&_wz5;`!X0{8^uiLlaj4v6+~&-age zWCf?w0tDG}4$KqAK<%2z0o}%^NC&j(V{2)$Ycr|%Z8KVD1ueoy3 z7%KB0DeN|^YJs*_hJODf%r=a=Xu6^OBT@U2bjc+3QGf0`a<0bj|M+=>WODM@hZodX zX0z%o;4gFr<{M6ccZNMCy)@xiRh@bZ0se@&K&-dFVO?tw@2Eu0!m&Iu5l^fSN0oh5v!0*nTn) zd+Zndfj-yj2qP<|)=-mko30dCaYx%%18o8PqeNQ-h9D%4G0_ojrUFCYSdxDsqGS6Q z#-g_$)!CubYsdF@PhaQ=6mdr!XpJKZtK{m{4cA}%CuEcxD;wH5 za>rr4TPnINcp_p(w@amzRPrmGkw0x{{-t74$(`_Ius|c{fe9HiSIywiWoROpMHu0P zs*ml%#tQ4uER?6W3i1t=iw2`38pYh^bci$i`VkX8>rMx5 z5cdwpWexcDpeec-uu5BKLqVUkaY??Lr(GGgw8frSDjq397QI70)gcDq#=fkuZB%G< z>0}fCsfj-zc>qkh6Ed#N27F=|Cnt7$L&FJf*8WQGxj{R)9Eeb+9pKzViks@i2Y*1c z^l;$Zte(8k3XeQ^@PZspWScv%bGSKkl+xR6dj1kc-reWgt1ejW^~x>54b2@GP_k|$ zpquX?4>suAw*27LrJ;v+Y8c>6FItFo;FbAu(4)R)d&dCG_hJoJaffX_pm2P1B(3(u z^#gYZsMg5!xvxc)u`oXtU3S?2A}J2$^12o?A?bsHh!ufe1qV_@^ zg1h=c=njTz=KbxT`Zc&Q2bLj{r9OYO@g9g_zWp-cV8 za~U`S1Kia99@3|zr~MRy%_oBehE2!grXOsIj~wZfE0tjoP|t)VpppNAWnMGwu-M0V zG1xr`^lH;RQM99a0Nje|ssVcnph+aA3+S)6bo0gThT>W9gFh(yW{`HVmB1&M2p?Ms zeC!dgis+yjso3=-(BdC{_9|-%*ivSz0#~FeQ79{8U40rD6-MD!pUUbKFy!bu&$&7q zIqmAk`&tLP2MWQ41s$%=COW(NBVGMNy{5H(@XFe%^@CzOn>`^{Kf)6=R7Y^|N-KUb z{#)JDw>V}uJal=$gTVJ61VSXt;2PT_5G(xnS}m^e4pR^qN3KMA(er)WvEgcHSd{23 zVk>xvR-KZR^ixlBs6}+ftXlciv|5`2Vu^ z?tN`zOQPs}6m4ct107>yo*~)kK9VdO1IBQ4s`eKnK@_n%r9-U-nDAgs;X6wCOM(l6ML1MA2;`ks*&nNC|X*p^;{GbO*_%$ zH`oS}@h0n_-6AvkCcjr_F!xK%G`|4m?%x6C?$Vv$i8qmk*;u!;p1>Y|B7= z7eb7aw!iih1vok1EwCoL)5H0ln~p7}cOjjHpvSf6c8L}fGlwJ1k{Avq{mhv?uPVk9 z%#gd#HqB7D(Xi~!nv8wvL)uK!b;++uQx(l^OUB)Ku(QTGZF*X8CYE#(f3%22+|3*$ z-OK-6A7fW3_AWR~%!SkfHZYc%&}6FnYs#bG+;;P4_Sr+ZDN#u=L2A&Q87#yF=`&k2 zqN$rN9+Q8M+5EC6-9W*@M_G?cBGRUR+S1jdrQ8&xUy%Oq852!);-4J(u6R6kxTf}E z0WeI+FdM-&m1|=cQj=||rKjl`4jpOz-aA5P$40C0UckZt!TCU$TeRry;7J=3MI$kA zL%5ep*I?j-fFLui750#Aj&>kh$#pf*k)^@P0Uz%awz0!68^lk6ikR*fWG(CLzRxC7 z4RR?HY=ra#Iam6*+L;~>PsRFMw>8d1JoitOmm7tpAT6j#g4!m7! z1^E)v5RtwBt9AIL7JuWYur=2P@4NOdWCMTZU_&3~7*-JlISp#r-Y*NN@vN)kRqd$1-+CA0y@^kj;= zFu>=~dbFo{*xD0-bk?nHENG9BS6j1o9O=z;tdx{dU9b^OrFOeddic7svTkLpa}kxI zx*$Mg>fP2HARDF=IRzcPW*46Ay@IwK@w(ec9P`gvjy-QVpy?}Igqldei-4Ip$1*6$ zx%N+oO|mIwMon36lcI>ckC=eGa-@MW5pGLjDw&pdOlhMIQ+2iltGX}LPv{p$762W3 zQxfs(b`??f#GPVSExy1T%*~#STH@VF*mMX>62S5==-Gk>b~YhifKqb3PUt_z2!b85 z^tQo@|K#|K_6VlTkwWJVW-bCRCg%HHryJ(!NPC?KwqfOnYTZIdW!oWzo?_Oi+%QEhiV*Q@!-tJ7_5)gaz@$`Wu3pc^`< z-MbbWj5UNYB(j{xZ7J%WF=v9^?+ML4uvzxg9zlej+}a|_CW;dFaCgv3nGo4G)(}TC zkEAcDZQk#Z19m#t)@xfhqYF!OsSI&8b7m|;Nm+;!!dkg0mO|^V(&hWuB0wF&Zi88a+ldq+^m@ zwd=}83bYs#>#0Qx=}6A^SaLeG)!g*p#>vlGn{ZGtVL}a_6Mbt7IPpvx0X-0P9c6D* zRqFSYt2mm{+_$7X$~hTfkUisiOORrRV|K4mC}~q@OW0LG{SAhC4#y@^4pJ@E6S==K zQ{f2oIukJ_3I3f#sUcZ7qDW_*X`M{GB$6FV9P25?+@o+>U?*Gq6b07_&i>d`X50g7 zYnMRMWJl=^M;C>b_sF8%vhxUEAJpu@(QKZMrK7k@4+?eddDCXEG*+y!SF{%m}) z%5iG8{X{+zn=9?`FE!wB4yi~)_Ohjv?ZP0?5ed^m)?>cQ(jx1AYAZX zso(r|YhCId&ugfnU$dq(0KURsY%0sus8+N~Sp%}f(SUuNCG2Jfq-&03LO1Qrc`Mtf zO9r^rmj*|O?Fyn!b6dZK;l?I+S&eVu$2Z@~40a=}Y1`<8dT1*8Pu};!677qOq|fB$ z+*B-tc%~PUK-DIIo%ymrhU)>9<;cphA0T~MHH~+2Rq*MQ*WtFB>cmxBDL z#(8qIHy5@=RF0Y*<$SDacp?T4DTuc1jFJ8qt6-|mj;^T)@nHauGTDyE3E7(J-`4n< zgBo91Ha(&=T4~eHUW(?D;Yno8vG@F9nT^9)oT;?kX$YS)nx@!7k@!G3K>a}RBMI4~ zCHbO?Rnzc9a4=O%f5K7adU^nd9Kg=lK}&_7N!dlGG($4Z%N^?ZMevwgi!J}0gk&W& zm@MGHiEcA-6l&N?&78%2+uMrO)XI^eL@=Gn+wsW!AqTHs{oohvL6?F;Do&gz8$!|9 zb!N@|LTX&~V63UfzcXz-*anpNsMTGq5PFjHk#n>Qrg$d9VswNZfSn3U;qrrQgIy@H zC!6CbYF$Fui<+i2=ADr?$x<;^OSh5+)g9pgW~C$pC$KOYP99Hokal|Mvj>rJn$k^o z=4ooeD}-6pZR3fJztAf9P^z!8+ zX5tz70<`3_VLu(Xh(ab^C|-!Pz#p{8uUd?5;K#>!I)&|4_VKVK-wt7PGtxZ{^$+1b5mU;#^ zkMCq=5dhxoct35O+IS+$Pm`k|?x0U^J$*~48XfjJBa^1r)@qZBMXMgONTCiFD>@^q3{*Fc=0-fwgcNbT}8K=Lw+R@A)=ZOYWxHQ3>*PpKI*0h z2}g*1*&*0P!a?woTnRk232XjCD|wUd_i&MhL_Jdu)9 zEubSE-R0bl=FY8UIv1G+0~JoYsoq)9(ZnViJGC}jDlETrIs=6#Zzvu|e$V>wqw}z@ z4vqTQ)aRJrZv@_ld^9Lh=51zhz&R<*g>WGqP!NX9sp$ zJJu??Os-dszXCU90X#*bOU@EagDv^*5#kiFX1Vaz~`ijqC@B>a@B1r*+bh3zawb9aG|1cHzLL6Gf zP6~l%V%b_`8sy_L{N>I;8m3ii>p($lTbA0_@*vcaLU^ILCfnoFT6dt!fzduy_XNwze|nZRVw7)&3%Ky6#+iO^WaXU6^JP-Ubbtr@N49HrV*HOHY|krO-E ze3g^eS4INW9VE=O){t~N$G3+n{bfx!Yw1x7#S?`omH&5^=50m~;-f>QCcHhAp~jz( z4D=|^qi|OGO`euLJ+SYjt;ZvQ?L4IW;|s{PHs420MI&StwvWMkd`;N#(|bpi6(b~F z+^e9$d?!3`Y+6#!-D^2+(BOYZ*?6`@o+LGvFH{%&w&KuOr2f#x;u}qjMb8_lv2M{z ze%@G9+cY25Sc#)2W#h5Nha6kOMvFjAnKC(47C`na?MYyLIn-Wi(d}waK0>bBk9#V- zhSW{jH}+Fq*NXrZBO9@WFYHfnX8M+^w*unt$I zMXRyXA>xdkDzrB(F13aJ+95**r*6|z95TXog@UTyGiVpGj7 zUu^8jv+Q8Fp_-=?kIg+nc*s=OJ`~;!TBZ(ATW(^;pYx1S<^!qWZ;lALP_{gxH}Qu< zEyEc(+|W7()`7Gl;xO@AdiC{o7WtGj5rs{!sdug0BRv{ajteE7o62^80+*$%Vf65) zDxte#2G?uKIMbJp{LYOvI+Xd>dQy&{8yS@iaHxk?YoQjREgT&*^gc$Da4R2by1PewB(27`_08mqwv9S{Won%b3^B2V zmtRxFj;Bp+!cBX}bKkJcT1&Hc>F6m)q>`S_&;f;q$k+jQ@-XEGhfvacxkxtmAM5va`N7mve$U@<0g4^3j z%R1W{BSPdZRGeo+=CWXG3e9;>#=h7`Q(8uLihS0LS_E9UEWhAyage34R!_w|iYWh; zg*C-HgwGa?q~=lKkYH~k^{Vxoin4dC2iZF-*vC`R&3R4JVYR6n%|0!M5{d


+iR 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®A3sjg1YI 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

f`PqWK|Fh<8QMQv8Z_Cufq$8de1vML;sl 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
pO< zEt7xQ+j-Xnvv7F-{NY*3+ZXEWw^Rc=^3gHph!T`|WB7(Iu$C*%I=X`~w5I}Snijho zR^)HMcAI}uzB>{71LjLpZv1eyxC>#s_Id;?u)59kDGEle+YEThYa!kEP30SP*qs&j=$fSY$D(qE%36COV7 zk2Mf3wePZr$-K_b<{Kvx^Yg(B3~iU%3NeJf^~chwyjdMY5_v`P6Q9TiMw+)g#-DBC z7Imnwr+H3CAoFE~`NKl9SGQs;rfg8Q*8v=@K#E$U^@tl?QnaL^^t?FBdNlAn1 z9zSmr8%m4LJZu!7&^3go9|DdAM_fY^4deOV09-RqsgRG?U`+h)AS2m(H*c0&AVJO1 zH+DqDh@Bk%a?qEZ8W%1z?LPQzQ~zkh{di>?VNz{;NCV?o#pzn`Yz`T|4;SVoVut{1 zO~5!BC8C|*K=8vEQIy}neuFmgvv}OfWbiue7sD)Dxkx<%>0A2Y92R$WonRS++2M1a z&U~SmjfrC{vsx4a9uHCko`@WP=IMQO0*@tBmm%dl>AL{N$*>fOwkSVD6HwP{G@&wxQw`Z#&LG8wOcjE3a9V@c%|cVQ zdtO?OlL5egM1vpfA@w?FK36f5KXK8}uwPhmBRz-?>(qJDV0Q^!W+d|( zrV6U%s4HMNqnQAX-_5hz>JMQbklD>qwp2xyjc$1=J^~}5;&%VW5GT^v)Iz8X2j?${ z;@Dy^`{5hq7{Sy=s9ag}4g5Lo5zMl`h_yRv=cgJt@Ie*fe5pHv;eqTl~$a3RK=WJ}n3 z(CQ;UeVB`CGz1P%+uJ|*+s?cFH%G^Z|M+K3X&~-gpydn&*@-#owZNl!T5W>b)q&6O zR8)1>rhw-^&Y&E69+|U$cyPM^e(&(ZsX7HNhQmMkDeMme`e5K;5Gh^7)Ei8A@wKY( z@4tDsw<_eqGiN2}`89%H^)&7BGy%1BO$;mNQ_FNNdMAQio^6wlm;qfOH|B0Arc6F+ zTTFB+T4|9mM`BDx^)wk<5qP|Ja&&lbVnd>)HBg=l=k{%Jx5}gVH1@(|MB_(Bu^9G? zqZse4_r~ez@y@}?+r8t*!s!%^OfEk}#AU0HPvRM5=P>xn+`0JBwkhz1Z7CZFn2m#T z0r|HyohirMLb3bQz)qqYa`b*lS;wTPr+Kn!JuSwgtmnJq54*s4^e#a>Zw}zG+O+s+ z()zkBlz0;N_XK*2{&+-O?C$LTw#SoFiBCJ9$ke;-k@YrNC>@xS;nDHo>EZ6-yA$5& zgr-iR_>%kF-of84{<`;1weuOn=!XkX*eKwSX{w1|OlA-eFL4vT&I|{gxoPZAoqF9J z`2i%Xd>ut_d8jLMLGe}$`1vyte~WZi2Aa_5pmfiSz$bJ(r6F=$)fg;5b8A56pu$9| zRY*_HA(~Rh2_yB&#D)(eW$-XkhcqUpenJZGS$A_%`Dw|C@k;@mr#{&x?JeSyUr27K zE#g0Wri_QdUJ5qOd=}i5bv*OWZJwSA1nO-;Lq3>85%)S}iESzgOBzSSx-ORQTEvtt z7AQiwYuQ=7ngI7z#^_vyTZ-2``)i;!{e|th9LYkjmJN}06b#wyE59K(q`n(XiU%c; zGKP4{5y&uBDtc&Kw4px1A2H63V~)b4(%+R~ zkmn{PVukn`j@@Jbyao%sHgx73#6*$Yf--?!x0PmN;c|b+=ZJrE(VP8{b!_+pp15kgh2`w5`X`?NaZ4Ey8?8}bH093GEkx#%5d)mVVxW=i2 zeJ44x#INea_(bWPJBkd>ue)G~aUsrE3A$sdnN}-%h&QmF(kP14#Ye>n0Y}uPA@!Ub zr9BF$W1W6fz+nT4&ZoW~uZ? zP;k#r$ix@PFtCT@7nG02J&>c3$i)GGAxvmzVICBMc9k41&L=sG1`(*w{3RO18}PN; zxr}1ynZ{AT>OH88Mi{QL1?5}ORCjO&9VZsER|6#=EC}n5LoetNZ{V^&<$@xuK>lpU z$s0TjZ;iyYYg<&FW@le&t#=4QO9vB&ISrVfvpj*siipDR)CwvTsLvL8_-P!g zpI7({u9K{MsL#yCD;QPqb27eD*$#JDXJ%6F#^D0JIO031Yz*8UcK5jI1hWFqMflU#hW=JeeDQwvk9nd(`3R+nX~-_XWx?N zc-FW`C<4SpQ>rkLTc2`u}R-8WC<6=WBLJM^Q|b;n-3$Yc4o47e! z@y)~!p!#YO0k?vteyTE1Wwbmib#-Qwq#in2zk%q?FuKVoF%iby?8|PR8>;4B^B9tX zfa;9OcS?_(@d5*xi<(Zm=dxF}pOFs1Bjkom=Yt83PqcAJ8bw!lxFqw-H1-Bqeig`6 z83UbJvXiBlArqAj`Cm=qWXK{-hQYe28!)_@Br=^GrM|)&VGr(%b%_m~@+5=GydVBb z>1-$PuX!zpEAf+upvvFx6FLyXWW5ifH<0DY=xb~WPd~ki&NThjBaR-R8@;6=Z?cJU zE((#@2;7+kJvHWru`5kQHIn%`!O!zd(}6~Q?lwS{j-?J%Ybemy73GNF^V|zfBB|oG zD3FaC<`LBswt9{u1v|?2(dH+e85F?zId468<=G;IPqw?{_+ra}45KfeCZii1*&ha~ z$GF8qC*NMU;;4?vy62N3k(frF3bIJu|=V+cn&=9&7f@n4ME(0tk&Q@(qGjxqSKR_cjP11zS!_*JtTxJ+n0B1S_MZc5? z*cDLsY0J(|Ll8RL+X1szu#1|Ao7gXnMhEd~vo^Err&n&6MgnMVh( zOZd_B;1}bSkj$)B#tcM72L)CxRjVf1Or~Z4G2g6_ANt9pX5$SRGqGG@#3&ZlVKW71 zXi_yj>h+~Tie{A;N%Sd*hPFw7XP7WOI|f3E`IQC7DK}#qI8Jrz_HqC%PT$KAo!F3U zGppYN$#Pn%^(EnOGs?4I)xfKPgu3lkSa>EFT-3gw7Rww4RxrXpqI!N6eLRbdRhmHH zgxWwTpp+(zMHy}Y_>g_Hk{7_3rmD3XBo|LHC+IrzNWsD<#YTqg7^Lfo#}el9%Mw31 z!x{$Fc_i_tGjPd@=a4EI@*3hHBTP-~xHE&sgu&g=Wi7R)-`>5A;t$wL3Is*gzN};3 z3?QQqR3m|D4rnNfx}N&=M$Y!TM6YWzMKnZlGF5i&726pb!sw}CwoHesBDp|=t4$)L zH>ygMX+v#R5TcH$D=)SX=m)_v&#*2p_c1&kp z>RtMQpQ5k>ehaFVt05jHxu*kWb^MfHY@8ooCXaQ?=FOiELGugydZ0W+R@pO|sG{)2 z2AK{(!KU#>j;z06y5xm4j|x8Z-Vc3@_|Q|Cwr&02Gdbt0HO#tfTJ11 z>gS2t{OyO6y*FA1^zV%62%_ylh{xR1x=zT$^N78L<0 z1OgT-#YN>h>kD3rZ@^8lAvANKq~zj0iUuB2hWQEk`_4bf>ER(cd3X4CajbwgQ(Kd4 zKDuXdgS}8)cKIXI36(!K{U`+o`72bXC*W?0*PCHP4h~NVQZI?Z9&fdGaCrLm@Wa6y z;*4nuDdPNa6d`WrF?GqFpH3+7ouSN*+<>G9(P#v;SQo>n4&Hb}HC@W>7*M*3>8ZUT zsr@{`NmvIsH_7YtpGSKcb#W=HDZ_Mlk5-FJp-7x3Q&126m=55w$#1NZ4#79Sfjtm& zn8BLHDdaatp8%S`eT%sq5Dzd7x5or~jG`cjZlFs{*#{DkxG^ySQ<|Vq+0F-YzXR|056v0NQF}OYl6S;#Q13y^s)`|o9X+nch z1NHav^6nC{lctPIf%ZYW*mEOTld12$ROpPnK^!Fsna+dM2YHFZmo&H%VHPtF6yk+y z=vG25>0QJV*^m4lCW{0+yS@_w3EiNKlEK&^0(sm^y^|+LTR&43Fj91nKWeWA) zs>&65OOOEc8!iK?5c7eJ6MWc{Jp8bD;EE>r62NKXnIL=Mp-HKv`ZHS&&C>apuE>yv|9jwfks z5HC&Xbe0~>r+H7onEsoKa7e^%?w*D4)-A?2u0xR42<*?o+SNX6+3<-g_aWp=f{P?) zQy{vox7%h$`Ws5dQA#{`mu+T&Usy@JZ)8#UUPxGktXh2rxuW3N+R+9jL>o$C=cHYo52e6!%(eSQeVy14c zDO$-uL+6bl2OF$Gwm@hmg5=lXBcv`-F_w{C@!c9ROyylKarbX>?2n^15SvTs+)a)Q~Q{<5ib-jQ%9rI za}DNKBR~A0Zo((>Gx~GqOoRM93%ATzE9T`=IPJ@An4N-PV9p9ixj;_tT`zA-gMCF8KHG#d(;kdg%L zjv>1mc$d+&>Y#w$TD1g)d`9&R;2gh0YAkjh!=*;OVE~)B(R>^{z(|vOghj*yd7f{i zG`_R#h&vYPw5`p+$Gp`8pg^7bkmbnB@Gh+(?mNBr6b{kezEFfc?$z77tX)W)Qs`_mSS8HXx7KNb* z${kVDn;i?uSkmI6igv8J!cDY$8_bge#T&fII&c|AZOR{IZJ4%PzMsdzs%o?XJsPSa zkD|aI+!npaTS98ZT8Txb?6CYfw5vf1CT9|@+xJZ&AM<62V+)& zpD-GYr8@x1FPw*R)(RPzQ!0~+alHiOA;6)+D5X6#U$aa)Nkoz~LId}mIWTs>!9jTq z`XV%;XI!|JK$hM+-d^C~uX}q(7sq@5--o@E(~IBscHZnMT0&O2t~r=M@*(xcKOd6!l!AQ&8GrrH)j%FZOpDEyS=+7aH zpl?$OF*64G#bX2i;xSM!e>C(`@h43^(VyPV{Fw55BDKIv(&jW8;j^TR9gC**rY z?F;^##ol-}q>&4kSC?mbK(K(pWi1gQf?62`szgWsztUUME^Ee66Fms&B zl2aogfckzwLofED2Dcl*rsEe5Jn_BZ&}T`vvqN8i!h8ZpF-<0!V}zE04}^6X=El1d zw1Wb*90L<<0~-z^?y;vFu2k`5!&#%){9{e`oF7E9I|LQUwnX+RV0!8*aLU013h^C} ztk;t<@WAv|zn}crfd6a#|LnbadmA^hFZ@+tPfpmhDN>8$NvOygS(Y80v1MOLPR?9O zW4FjA)fU;!bT_p`*8S}7Qwx9s8coU0a?gFAlRq4>u>%F5P|L5(%Oh}Nh)Qd6iu)WE zzw^Lw{9JD9he-1o=duUhAs3+R1*cqD?{t9B8;-d^56yK0l(J>$)1T(QysJ7u1y{$9 zG>~mxPfr0zO)IR=0F#nf6Q<8;d<~q)B>oLt=@Jn6Bi%*iG*F%N2U&GZvU=b;b$Tp?I4nB$$yXi*%;Wr!tG71tfr%oeFzXFV{#?+r|- zCzu3(J8Ae;r;1Gv>cljkmj*AFd}ko`Ifa`UrbBqR18h*8vy(`9+{^Jp4R3)j0e?x z1`M-M;GolDk^$e3${lbH0)uTLGk?0ehM>{0WurOi^Jkd`pTd48Vwv=NSf5_)nxw*p zE+afha_F)q<7n(e*3K0S1fGtk$r+es5T)~;@HT_3{zz|u6BiuHrVeiw2j2)nEy~2$C2&G+B65!1 zcX4&rsS`s-MOz*9yk@hE=M4^hOX%`wdi62VS6lMf2$p`np!D-_X)RO3#=9)xG4oF+ z((|rM%_t!h{YL|pxS|&O<{0x$h+#bdp$0X8JkMHVb2=Z7(eS}}Nn4yI$E(%Zs-6mk zA_O>l+1^`UlJ}_ou0KHuZxAszgkV!sjcDdtYA!(xLC(d9T%HUOiXL4aV*TPX>hSPP z*t+iO-h&7A$@6D`wM+{J;UV$(SlPFWy>X?NEbbh`k^(necJ%8B?-W!Z*d4nNVnIlU zz&-=^bgM`}a@68y0Ln9HYT-mc+av8?lI2RMjq&Y`T>#dcZ@4U}F7x8Nt0%x7Vn!c= zRge))1yjyI z-?#Mx+PNSQIWb1@xW}+l`Fx}g!D4`)iyu2ZL4nmkYBdJXbKTR6ug?YEY#8|G5W`>PQ7`ejs-eUIzCQPyw za|jUJcf;HBSCNnIi*Sv6e|`Q80?5#mJJ(dhwAw4#v6Qm%bp)YnIX&=Ar|!KbJc&Lk z61@b2OTz4OZ9xPZtxrVKsnw1|+4GL%%#c9(4&JMiPMtLw_-$D`VBICW=Tva_-BS@P z?8>@{owehl3kC#%luf_Dv_wtIuLxYhoUm|)&8PTkvw#~UPmHwHivzM@> zFM!h2+VV<=#Nyv~?2S&Ru~cu4^5PV2tMeirCmj{fW(i%L_VUIKb2}_Tv>XX=ZS$0~be=iJwoV67uK&B+21oIsU<2aod=tp0aP!rd% zd5LteR7-?nc97h3TOMPzi zrE)5A_K>$>+<4*^-$|Y!!hF-|K;4Sdw@LK2XS1oD4q58t>;%Ab(6-OM&b9oVltl?z z^FR(}VYJq$BxO-Y7~sc+C3DXre91A&4{qTOOh^d6_Mzs+k?Zm zFWwqS0x=aR%w{P9_!`FQGA5L#)48-mlb#2ldvoe-G+t$|j2@m5OL?*6W2VU&<;)R7 z#oXgWYK%D9hQrS#HDFgCPj6Roki~!e}73Shp{VSy)4{)u(~cka~79pns3wU1X~6uQ_9{I zW=F9j1CYccgp@J#h=*vs3ZAOY&>z3?QG zjb%Tj0XJyf(SZBtz7M9L{8>w&kTr%nJNk)eU$#4vnbqbG);wGUfzL=b%HY^Pc~eAtY85^MBfY&6}eV8?K;=s zQyxZ9It&{wZ;bx0?ED%#)b8?Wf)%c_VQd*?Y)Wp71c;xrx}PlmiQ4j7yDuXcX7a{~ zX7akH*DumB2oYTrw*~i;r*;7$=`@+<=(^_=zgcCZw4 z{(BfUH#+mRqAa{QX?jy2_x^vMCv}d{Mc49|3&!u3i3g?Liv+NL3jcN=RtMPL1$Fxb z^942En<@Nye(>vA!LQ*0o6LoPJA8k*?Qw_JnQ#G5H7oLqbci=I8=+5feRVV7*1+%< zq*z8FgHaEXpbsT@W=O(V$6ojeVwgxI7W7v>aIp<&?2!&XqdYtcj8+@<<1%s=wphk#g*bhheoI32Pnr9O*3QQ}u zNOzoZ^NJ}u)xuoK*oKOFBFzaFy6z+Rpy9SObdJ83f*>fxOq>k?vs2n z3@wqt$838XKkLHQmY;OAMJJLSnHM+$3woT7hqd z9h!FKS;;rs+}CWv7uQVL!sho2ChDF>?Gnrd2p8O2?mJ)g8g|&E>Gw^YuFZJVhN|;k zyhf6vD|5kbMp6uz8b}M*infDNZ;H}FJH`=Mcql7m%%sH;bc$GTD?|BcIA?>spmPgf zMIbUiS3dR%9Jjm{-+rs-^v|cJh4W+$*#qp#MI`X?WINgM}4PB^BJ(C?yJ}g7qhpTU< zKC=j4xsM&LY@s?}p(da~%20<*YBm6}G!_I@YT#Tn5g{|IHJHKyOS3T2&PTwG@W`E@Jkd{{$ zbHWdtM0AKOgW&ve;q}D1VkOli&cTOI)S@TLu+?4|3EK|Efq>Y{34wKZcXVa}lAz~E zX2mXjDfj__8G#8f2#WE_X0|@ed7X+yWFtW_Hk4IM%#&gPm|% zN<(3361-6tuR)DrRBt?zTqh^g;|WfGKB+pcS{IGE(^9p-9%U&V(X)Km!d@b%4I^|e zG@kYfJC;#8HkV5THGf-ouNg03-2X0yW`~W}=s0k-A@-Tv^fn8I7r1j@F7`58xO1i{ zft5I>e)^GHdxb3^jt&G3A_^Y4yB1(Z^*Uxb%d*TpTP6l!(LVYcY~(c^1=iS9>|j|*RdOq!3$Xq}zgJVtJF!j!`t##tv8&R4v*8A49kaygZfkAHS2RZrdB;T^xTndZF}5T0 zE@l)R8oy2pw}LixKC*(NUuOQD!PZU_22DkrbiclW#Eph&SpiGS(MJdN7Y_o8F^Glx1j(iuh1-vY|Ap&F7sc>lpaJQl z4s(C!#qR#j_F;=}W)}@P1dL(uecJc!tS+ISzi`O|NsR95vxO28eKx@4jUBg;HmD5) zSU3HN==hF~Pvfg}I-gn+EfTs5<7z>F0Z}qI$#1s)tD&=w`{M20ySn4x6SqpkRrxqd zSP`3B2!#h(OpriP!w?bfk^`cJ=m#U<7#z5CxoZXL(wFW*52@OT{9@A$%`~B{STeMQ zbnohbH9B`k_5O{45jOk1E(G^7DGwVSD$ zq=N+fVX)w%M@S=&)Iaz$|J=@J*F`!$t5h^-tJUR|<)zi-l~wg7JqIYV{d}x3Tpp>{ zR8qFKHN*RaQ#$Hr!dFnM-DQyFC+heiryW0RuX8=u`5YlEfPW;FUs|dWymON)0Aqub z8!ZV_bfY)SAlKC8YW~I5+_Wq^*xx4p5DmT?5Ffm53E#TCrsK@K71CE}jyJ8$PwHiWW zwQ(I|P`9B0@eH6TN`lgW{X9vhX%*83stu7BtY?_ZebPS0^Xe=w(%)#0r6ZEtLG?PG zM5!Vpk{ltjly)f9If*CB3lmMEY!ZI3q^hfjg+5Q>oG=NE z$o}Xin?x;V%B;RWoOzJv=V{_|_OQqjWSaKZ+VVB!4@m+U+o+c zSc>spRN9K~G5o_n@VPluNS|8|v|6kIkrG zL;EKDfi^e*(i}k?D_}hh4Yp-a1HQ^#V(iO0J01iemhDJUqZ?c4&Zk^W3MAe(){(D4 z8nkJJ>1U0Z`gaPJfd_aScX=&bS+_^f;VyJAh+wg+KatOcj(&)n+}m~*fywVq#AkwBj*u0TuDWUd z8$>H^AG^b36wfC(?-BnE0R|QSE#ej4cF_BJmJGFw->b`gZ!STR$|$es-(wXlwk*SMr$H>K&A z@S62=bnT&`HpB^1%V!}af71AwUJh$TxUW@EwH)RWwQdLxI}dx0#*_Rso~Y+@K>7rR z9Bcy-ha2fD&UDNWM*l+na#FAf(QyVwblW)L>sa#{@ z>wj6kT3M&O`@64xvPqEF|FV3wy6)Bc&z(cBUjDLtwN|SalH7Xf%ktF|&b$5g_1it8 z4~M+|m*uPPOx`zdw!FOVm*uOcocD15{f=_-`d^l>{?2({ZoNL(aq{|Kmao3&ynF9o zzjo{Ozbs$h-F+MLSAt)tceV3lwr*wofSbeRKwF1C&Q}9uu2(LoeXOt12yr~ZQ_Z@@Pslv zaWZ@-GJHoFzH>4>6&apVhNncR?*RVUrP80bUcc9ejs1K1>hF47|J?bP*#Y+N<*UE1 zI(5E*sF_sa?eqWK+2%9J{=Izly{;0ZH2>l%?cd8+-*c7QXK@h^Dn@+4;9gKD^x7dEw+9HRRsg`Q`QQo~@a3XAQZxxAymUw_fdh*xxyPzrUw8Fn7_A`*7>I zs8`or1#(Lw`SASx%a=R*9}af^H+1Xi6ZJ@~EU!MnY&tj%!qp8Tu##e4kS~9_DZ7}g zHl3fGyN_%zCD5mJsEan# zvQ3|7DTj@6Ti4%{auCl5gB0_hrQ@@Mc~RtJ8fb7tga9X#IAQ8HCHe{Yy&LN2#QBD8 z;E`cJpI%o<`JqUr947+CgSI>99R^ZH%%N=z9=i7UYZprj6(fk7(iQ zG@oa~m|*)vb7}UW1eOnk;<`a<=8noOZ6wp#2Ovn&dK^DoJ$~5dC0uQH)bYbL`(X_~ zJh301wAJmp79kc1`siZRTL`|QyPk}|(npU#p)pKMXizNVVCT~uDZz9t*MzaOvTpTI z2Z$mGD+Xq#M2&+yyGRO{tq3uGPyupDjv)mJAIs~kimzBZ+&<1|n{=!EJVUovvwt{* z7VaK6GtQH1m>LeN1HYneqGU9}hAa@$d%2_Ftp@2`k_fJ=RQ;$X>neTx*gPk>M$UPZ z5?#@Yben~I8ykxIpbv%fM?%lRyjLZtvqex@nDWW z6o{)0J|b+0p!MSn|L*3QevjDcAcIeIy@7%`IP4-kJ6KNTRnzSF+kFlXai4`r1U+_6zyO9 z=nz39`q?%!M*}6)Gin(cbqVGy);6)mUnOwBJ=+M}KSJee&)mlw^;6O=YYR4Wi&bZL z9;dgpBb~AxH57dO7{tslq{az&J|Fq(jZT9_1zwCpv3D5g>~ucTd%}~xU@10D!*jCb z00?p}uzd^W(HOr&^`ojq(uKei5U)pN$vCdkiv;T#CW9hLN>&27aXxF^RZHB%$53|N z8biCF0;VK1z(v-5L{+a$V_WNWFi;pI=9i=73Y=A5M1GH{#Irw=2s?1>H8l8z1PT)Y z+S+>m8(VOQZXs{+cmoITP&7Gk)X%MUO+**UIaU2lM_@uHxd zi>?Z@nEL44Uu@uu#V-czn`{m^zn-i_{hp-;DWya%{*)& z)6gvvudHI|S0ThTP{*yVJm@0z>u&z(c4;VcG)|-1_7F;yDbo_9=HX@XD;Fy-2 z+C1OE3~uJ-t2K=OmBAT%^#qPtD7dopL(LV)G#g?>vUa#1Yhu8B6;RtesAZ7w>tD-1 zIKN`&*RWlS8E|=)R!KRF2T84w5c(nZ&#-QSU0Yue^T%|#ek9?75ov7UlO+e`(y#Np zO2P*fdWbn@1ntDcdp7J+EWH*y3tF(+)P~1%sw7XyeERr4V{hrBza(5rz{7H}o`o z<}oYge)vR>xKHZG;G1$?eG>PB+&$(IcNBgco-}+{OX0`sWA*98F4?D(^@dQ^SlHtY zqkDF#?VkamCvD@?TI^x9f!zHkwWL;j9<6Z=a5vG8f3~4kR=?*RuS;b)ztj;G51-UP zfe3bR0ow)R$w?}Vo^44 zk~9hNMVwC1}>q-)fYUd%+1$P3;I%P zyE(7lk-%Xjm?<}&dRR_d=R5wWci9&*XqvI5>)xroR%32nUEDpGsMoku@_E-$EXq)i zooQ@aG)zM|Hri~38Nv9G8#X{?ns zXkT9E-Dj)b?kA0PA8tMWiu%6O^;z8yJ|}|}t*tk`=>F&(xMpG;){>qyCwd7RJ|t#9CkXcS z){h3UKCp3|yBFIO{hPo2sG{Yo7cXDFgo~+7Jm8PtJzZN}?tJ(6wI^%O)RLOym!|Rn zCl+QkP(~P<#(IZ4+!>cAH}x-4cnUVmdjxr<7Ea0cfhl?JP04qwKRoSx_jGOL2bdD4 z_Ivc1st$G7<8AM=q&0^4k6MWO0({XDC?CxAYkCzXg*ubnc-+=`(cg1qs>U-@8yE#n zKbC_z>*SwxLZNb*BHT>Y_R)RI1P{AD9*Lo(6--(QC9Pu8YA9(9lh#5>PcZ38DCs*) z`Yx386qBBYlKzfKe-9;nk4fK$l77IX9|B3M2_|&|Nl&SVNg(M7CGCVNJjJBPp`>Bo zk*EeAD0kizb&9QvNlk1*=NsGJ_7~OVKcDn{ld!59bCWJ{Ke z7>#NGsr6kS=%RfYi3Y>3*!#SMxPwbecTC%zybkIch%qT7b73~8(yngX|PZsuT(9o|xalUxP%Y}5f*3hpfe_p?y{DG8+6?O<5qDG5<&KOYd z_?256?V{V#Fp+1TtVo6@RY7@-wqL7^$_W=|z zZLk;VqdcR1PmN3&GDDj|OglMTIYhKo#VpYcw%r$2zUW=t7m$%tL;*48ueV-9`7G29 zFj3lQ*w$^VU1l+=b2ps+yoG- z0eF?mS@Y-Yj~XxbFFayOYQB#en61X%+|`jcZ?>8`0!iu~WM>xT@#FPH-BzAVu)fdg z=bM$0+_8?}r>kQ>yFGL-A?Yz(%U|2@e-1_5)FQ<%E_Ts|YY zsssbbfT|lh?iD7lTLbj8+>7@j%2k8(SS(}~X6#sP7Xh21-qG^X4=0a%ZMCtXs5fBz zLrqxQhqija$r3yHHN**G85-a{q~jD^+A$U>fJF<58mi zGrouq21Ponj0ZdoUl_U9OeA<>e9H13VZd>9cvdYZRw2q>MMUPM8!Ec$sB4WF!DjzgZC=st$~3c+R-uhnsr+Gd};tV79xhV`q0u_V`IxU#W8QHVGdS>Cw(&KFX4Pl~y55r#xg~Z&j z2%&!)gOjF}67YFq1Y&xHj;4PqY*PXcjRwfkzxV)qd z6`>r)eSHD=AZ;#)-vw6@hQ5Y*g!}wjM_Dm^mfnk?3n#g)-!HRSou8jSGbo*(u?95f zV*2x8(9}CbNwy>KJIkd_XAPHTdf7M(a|$^>1H+({@G$IR=*<#|TMESJ_z^j!cybwE zmpVN8a83*Y#r=uecsX_DnZmb~?jRqen>yqQV7KC*B3QYTQyjT2x8%lAg(J$l*P_JU zxNUckbgG0TMGKF@1?N~78j57*jtZPfu;zR&ksOeB9!jV~FAux~0#T~?Zm@a0@ICf0 z^DG7Mrl8r5uolI z?r~&9QxKp}MX@iyWe-ru z@+p)BSKsF08^NV^if9w1+U@0U;;a^Zqv<38fz5Z68n?brj~^d9-Zzl<+i%r_^8|tp z;5`x`u>oAWpO7>IBntq{yo7xx{piz)M~j1?8%Lu@#RKF%=^7YNoY#QSx_@s(gRZzZ z?%AB3_6Il_{9E+r@#AJ;l1Sty9@vO2TBR|-mgHsZd!F7R>R7Bb8Z@B6pFNbS>(SZS>$!F9k7D)vKX3Lw+ zN(O{jfC$~NIFdLRvIU?t$fs4l@#LCQqJui+*I5(TKH%JG$*5FVqTLS5QR~d?;q{D# zqjn)$+YmAWkbKC?5*S``5Y~dc!uaoIvw78OyL(e|TD+drt2%c*B5-msnZTP{0iLnQgowCP8;(ijNfWY zEsW{(Q&r8dZ(H-~?Cs&ZdqhS=4nRP%%E(KQv}C}zWvOveK@)C%K>uG11r0E?gfYED zZ&{mH2nU@?L1MgAVuH<_&0?7=n@yKy@!&ijCrfcLw)uW_@Tz~EC&jfsX%8UT0hHqaiyq3_*Zz-sY7$PSup}a=KuKMLHu6}u?-tWIA#H$jhA7j>nxMMV*Od$S2 znNKbNzRV0~xBMAZ1{-Q_keo$qSEj`W4`dvOIgy_ZtomV5jHWP!7Ri_qh>fyFe*%o@ zw4-{DR0=4Pr}K#pzzU3~delQn)dxmOc5%N2j;t;MV?bp6l?#fKZFq(Y&9oHSv- z&f^J1T7AF2Yhwpb^86fJ)(JCz3=vPRbubUa99IY`H%TtytWxD5pCtrehtsc8dJO9| zNX-hE<$yWBNrsoFZb1v_%x?>={Z@WAmPER0u|5qoZP-BMB!PseDZui~QrPd5%cd~! zdfSZ#==989p|(>>@mV`B;8FhZn;Sn@w<^+U)b19^Yy#?(-tlp7^P3(x+?trUJzH#E zq_q_6~e(b*bEuBT?#e%DyXO`7& zxC#1do=wv8WO9wl@3Xi-bPLpD0vfz|L=~u08vo{T@PH2KG|J@Jou#uR8vhn}FF0f* z2BibJc%cy{nb;2VIE%r{43jX$ZD+q$bQsZ zy~GUtwQb`4;9K;WD+B+P6c)|TVj5hxOyXj2_AV~sY1u84ilm2By7pXk?g_u!+zv$tZ^Ljb0l&v*2hc-m7%-Dta?07vWf`{ z|2BH=crwn5v^txXI@x^a$oft6Ao(1k`$F6WyJhOLz*J3uDL$*+vGr-b*vTkjII<&Ugv-?iIc725CYk8HS&|m zH;u0j^1*ph>0I`M&67;?^tVK3pkE>t=)KSXH=ncoU6EhuA@DDgIumIaRSuKQNM=sv zBB=_U8UNOLYWmUl>^qAz(SyN7-ltC@Jj*Bs3kpBa^-KIL@=enW_@aDX)$+bcvx<^r z{`jgfe|+WVzcqi+>-pf|H5W6#O^(W`mVd!?tt}%+`|`4}=s;cuY`|bUIWKB!9^$PP z>@E@Ss^jy`qNB_z+6@Q%o#fI@KiW+;19>%rOKw&*TMqC`Z`Q>(U_|ExS#NkCg(&HQ z`+ETO`!WYl;tAYmUd=p|z;py3)hJFUr9`kogev%|!)nGA{+t(%*RXib>i?j+4Gs=$t(csnFo$P-i-&>*rL8=e*UMLJA|7?#k^HP!%M39uTT1-UlC`RVZR zUA+m47+%c+kXi(J-Pj)!ri|X)e0m$BB<$vGlit~_`?kH%1~p($!YRy0b@^4ytP<`G zzJS7U%(_;b#D4|y0DrCTCg7}vpuv1o;-%z7GH|B_)15tAB-B9EZs;K-c1!(AG7V~( zT)iv&R-p!=G~8hnl=7?PAifC1NIzs$V58!*=UrIl0f+x7)_@KI@oFD>i?GRdDDHQ?bjQA;5*U1%G zxrtmdl(TGrh!Qh|WIph{OEM(=Fos%=>~|sj_{S#5KK_BoWUaogrFG&2A?K>KeHqr# zBqKVqS?<-0AMl(~?v_9;kY%~YhD~v!he)o_F}>t+4&S_~TA-v^N=Hk`A`V+RtY(0T z-c+qoUJMe-8qAsLj9KpGYVoLAf&S|FXf;8M+|E{V!;iAKEYoqOSp^X87fZ-vx42L6 zWy>(#x4eOMJ!p95KP;hbP-oR2`yefcR4YPo3WTHvEO~<6fqJMgJnXsg0Q$qhDlbX@dPc7K9c1gTxlP5|M3mMuDfp6yKczo4fXMP5*JCK zzPYi5Nct&+7?=eA+@rdVJ&XlqXpN44>wZnb~phWv6t9kbt6fnavNc`+q{=uwV8{R0JeixGb35-#5TB

$IOoOmvrYyIG{nN9QksrCA?50?jT;_wi?_GoL8fAbv!N3eAWth-ty@6_c zbXM@&iKan5;B+xW+5+KbxySZLS5v9eGXxW|b?HuYErD~rylT!$>PB4h^JXTTDB_uz zCP0Y@-)$?>bi)vr&i+Up9b8XO^GTO1Q1jx%mYgKb6N(wwZm>S7*RT@;^MDIwYSigs zQg7wntDT{^4cD8GY}%&c)V|IKRlbCj;vCY$9x>%b{_W#i~<+*_qt9 zUR0;WVxHM}_M@z$Rn+)cjEiyE&=ooa#D+Jnl`IOPF^sZE*Tdc5tw1k+>p944LV_9^ zg^q6zS0h z39dcE>w}OVC0976c~+$pIuFg0_qxdds!^cW&Z5CNe#DLH8jyXx@F^baH~97Q4>Eunifc>Rj1 z&9K=O#q9(5_+BOe-cDFKai-?8r7B-Sl?QPbphIzn)OE?jmm%4ep*KsC)onXScH<;YGYL`FXb27jeqwYQ>WxAyZH_ zh#_`s$(PNHC`IcDiQXah0u2G=VLR++Z%*)pK6Ee@Ov}NU690^-JZEUHe_7)n^*#d+ zs`f|N^HX)2gXEQM_bA$k?nt6L_Dswc2VOUFcUhEontxDLI2ilj`6i{x!F zObX-C3n?Upg_`Qe$Arm}=riJcUCHrqehfVdaeqJ%K^8wC!MW~v1Le<;W z(khw4aE`j&t{(ph8Ry&eJ2Etdr6WOi9p%&8iRj)oI;cBcOVa8L;=(3H3R-SZf5_h2 zo}p6gb$BwInN^!=McK*M%?n1NPHQ-9f0(w#h-ldij*!eO^TCL!x_*PPDz#jiFl+A{ z(&0FqxOnSuoKig6=!|GO-;jD29dNO0ZVSKhY~$_f$W%t9NV=ibN=Mg@iR!#cCnA^K zKk@{{Zw`Y$DgnI?x~yxBMg*lIJM%h}X`*wP6YqMKyoqPCG#i^uIMgzazG|hVk$|*t zuZ_}4g5O`J6A;MWx?!?$$$|NgP(*Xvc11zR+lVH;hnzfU#EC?*ARXlh>j61E+K~2H z7(~J!f#rZ$AQ|eZ zruv$2T=sw|QxBB}4nIB!&ck_V*k;YuM18yCz^dB)^ zS$$1e#>XyH1FO@;!EO8z>)O1CfffI=))fzi0~ix5E}a1Ilj{*KN3@_URkd5Qd0oF` zMaBDJ;et9Z2BS5s&OX%mSov<%=X%r8{X5QPb2@JgV1fM;HO8+CPYAj}P^mWP03qNE_`h*KHLFH$eaPJW7e|>lBc22lQiuEv{Po07SkMf;os^a zWEx;PSUJe8m1+1 z?Mom?T0$K(D4=uXM5`rZQwoKA=W>?OEUE}+WGT&-CV4(vJZ*MmqZ@lYbQE4VhF)%6 zU57h`K;m~Q1e{{^$#&@4G~2NqZigxqJ7Z%*890wc{itoGVOk*RczffHqwfE?P5Z}h zSDcEk-Lrq0_p&9~G=0#F~5etQLPSh}&70Cbv_@i_@zFwNmhai^is@)2u``~DB z3UQrJv)Pn<4?6-P5(Ijp4sw(!3H<65J=7}lORG?>KTJ*`Ru1^7UF7M|6onpL#s!2w zEkOdGp+$JV*BhtR+5EIS$fvz*HtjimgnTq$5~sd|#CMcdLtjMeH+sU99c>|7l&<&s zhYy&GZQ@Fap~dNJUWfmyr_ZUSu-$g51|fqO zp5s`7NRQy|;`_#h8)k4uf0W6svMDxr{uApZxR_UKFBz?VkgJP^_u#7-HwA zp9HFy>tds=<4!-BW_4|#iGb?2iZSb*O;7MC2KUpE1xq>6G03|~hW7lCgi;t~7JqB2 z7;_mtY^d8c6jthyuI9s(j<iqT~mw$GklSo(IJVBT*Zz#1!^mx*&)$z+x3n4Db9wIL_4E6fDG0Gm^S!q zK4d~iu^@!Upi#i0um$jJKGc0Y(r_zi0fA=021JxH-`F`KW8sSTtsp9@uufA2MoF%U z>sQ(bZzjNYAhpSrPrxeK_I06JNJcbIX5-g}Dg}S$2Pk8oX_sBq* z$3%>jHUqb|2qU?f1)snT+kEI9b`9r6knG*#qIOI)a3CFLmfQZ4aHBrK=z}FL3dfq8 zR`&>JCN2tdb6=5PN!6Wa@u8|4-GucVmsOBcqDG~{Z#mE#3Wn@cp@IL3%G)p-t z%~Jb$RPtA_aO{sto02;zxky1dph!d6p6G^P`Y2bo>$1TDa%T^$%5eraT%v~6yCH8w z>x%pqOlIaYQlWAa4wNSHAW;CW%0N5acJu}l$THLg29|NUL3Bz^wl^kTB|Wk}B^CCTI@>nW zs^A4yn2LYLoI$m86cV7%qetphQW1)3HkzMLH}! zB(WSucg#`*TBlA@?+Xxu{o+;!Hpa46Kr4u^Pm}#59`0soBojqXh`s}Tk%X^%<9~zx z$ZvkN;AiR9@At}07X6wP`6tmUt;+zxn(m=EBESO`_q0j79P)x7S6k%v;foxXtR1k5 zAi8|VI4S9HSs8%q*R?=K>Q&eSetSdWdiDdKh+B;b~rKOwA!S|I$*$|I%>7lDAuiq zCNNx6t*!u_`SmroEl>cQ1u@^-9VreOO!*yjMKL^Lm_KBpERw=_K*a zY=`hh;w@2KRUY_Sh34QR@P>?sV8nuo&rwIU=GEx?mW#rc6otd>L(Gza4k6Uq*?YUQ zcNlm}wOZlFld(qfAADRc%~Dn@R9977OvbB((5?9JK$n+y`#Udp|Eqp^x!0_XSt##J zDq9ns)pB@#ZaTwk5`*Eul&0Y_&DK^Oz?VV)_!#;~|8|qBL@g~nV?eGAo9edi?Gx(C zRWh&#h%=qNp6boZqK5zdOB3T05$QaCJ@1ERP7FaaD?}~&c_Fa+u5i`o(zuF?V?Y})WZm2uAy8B|* zVFP#5veqq~x9zRMKF?gmGluUYI82jl49ZltufdH5+O@b!O9b%D3!E}zjmh|2bXPMT zte#(ZU@uy9rOfuE7rBjLcY2ML;XHO_70}5Y?4vFU#`okJkdL_gfWtIQv|Ekt6>7ayigNgDnL-P*D-RLeAjh2;H5q2(hS`atMoLT zz=|W&bm7ossWkC8G#Ola>cTtSuc&q(O6i*JeqNM)6-7}Vq6ic+@|o9GpV7LYeS`t2 zS)7ahcz1C_xi$0d_~qPNK9G|yY(AtA24a)ShmLt$K{s$RQ`kAL=Gvb2tFP@O(THF8 zfz!x|gB-G8j>T&5POx7fUH;q@xuYD~LkkR}9bs;K5Kof{$Rn8-$KQjv6psN0UDO(h zxU81=Q$YV9RSyliiflI3*ii(=$vTMHbV(y15jK8MRpDwWVD%0by{K*o?P@>H#`f7m zneZJYx?9fT%S`8hzn5mXBW(_JZyu(jYtChUi=LhfZArJ=&IUGJ?x~*l2+4ID`b+q{EcsEoAy^dy$^t{-yvBy zHdD{@g^zbXFSAljB`7&i?f^_eno2>UEaP2kozS^&E8~4u3Kizgf4zIXyS;n(VSj7y z)y{{v?+%1{(@|>)lw_Yj!@u8t3;+Iz|7^g&&+wm()=ArE1tbHD7A_RsI=KR<^3?F+ zs=)Ip0^aTa02BA`HeKFqB315i0XoMv2y?G5C(7Ezu{xe}+T+i{@M+aMeC?L`l<@?I zP(^(j&_arVsKFs0uNN}R4skzn$l_67#$+PYQOQ+2FeCSe84oJ^0McBU8K{m1t?Pw| z;}7f|(8U&Lu=;mKs_V&yU1bnEKZjg{_l zNBMyPAYr-teMdd&J!;wNAtXtlcD^*Fjfufntc~1>=V6==!jU5GfiMVk^AQpBF!~RH zjg-A_Zf56W{XV!w{sX?T)3QJG+Rg7XfLSt~~{kx@RUS& zyR_1%JuzA6n8B&I7|#h1N7qJnQSbL(cf}l%;T62Q%M>9PP!7Xnl@g%CC>_rssxH7e zz@s=;RS~BXXs?{a<(a9D1&7vPgebLxbtYFa;AUg=A}FeDi0@h;`EPk zF!8q?oosT!v$KE$L=IHc} zsgZY~IcHnP!RG8(OX5CKz6^#Xl+Om=Q=X)C{c-JvL11j?`&p5UI!@0T>~w)?*uZ~* zUz)%TQnd_JbPxS6%0ZFNDq0sj53lj?1BQVR^Kj=dzhX{Z&+}~%(L?h9C+eV?J}0iE<^eKC`{%tE`-O!*i=SIGAT!1PGdrU z`^<)mHbCAEKNS*>ASyVL)2%IzQeX7pjC7Pb>(ilb;kYgRC_Wrn5qYw1nhhrNVG{HY zFesfb(<@vvndo;}_^8bBa*DHS-#fuEO7qIMWvF)U9DjMNR%)IL^(et%p9CM!-fPt{ zDYv#5edvAIAOn3Pcb+UiX_;^u4STF^gki7Dyew0od6bwUQ$u2Ilx)vuGZIgnUeguh zhGEmT%OOUbxk8%14s1&&GJxzUNy`9X+A8Z@%a}a~+cQajqtg@!s|13IGkHY;1M1%7aJohO#b7{tgJp!2DP1vxCuFtdovTATp$I-2!eVkB3WC zRR_5qwBhhfr3aI=cRI;Wdry`JE2qmNlp4`+dZL2vr&7jldO#g(d%?efC$&g@%<*-Hc6`ps}`1V z#;XyXwI4jVDmh@H%mWSrl;PtyH=F~}#iI7@H#b=2?Z>uv4iec?&y4F9GZ)kNs?;%y zS~gyh4gk?2u#Kg%oV5D-*TKQ-mKT>3u+4ZUU&KWUcPgE6Xla+?WlK|1`r)*YKN#dm zF@O^hW5PlmxJSVJtRXd@v7LV?wZdp1wUpwzm|qPKDKPNGbBb0+nY)nw?)3 z@hp&*jN*d(p7H+C}R`43mr^LHH{Hm2)8+9v$J)hj!Z=-{TeCW+6d9%`n0v2 z2o4$So`~3PR}y7Pk;xf(t#_;Z0J{q)G9(LZO9>tJ2|b`MA*MS8MI*m+FwR<{*PU=H zCKO^J@4ro+szWWxx(UZwyR4-d;39k!lIh820* z(6&13LR>LY#Z+fj#*N@p9b>czN^Q)U<&-fkMJUmcX>^(tV{VjvurN{yc$eBv0CW@_ z|Bek54u%)U{R50tQ-gjPBD`r9pt6&nDFR#{i9{3;NUwhf|Vf*)1=nv zYy2!ffH=~1r!g>XUE~~TWHmvg##6tC(Mz;ST6c%$t`wo*nc?|$o&ZCQ1yM9Vjjutw z$a)2U#lJ`jh+JRMIubg75tgSQ1Varw?ck-QVUo=B4$T~h39(zFwsajpE)ZkBvPK)o zHU_Uwl6in|oKWo%R|P{V*L(mt=FbZdmQsi$vVW3!SV(CN_Zv*@vE+Z2dG_P}GmZ;n z88gx~+$avyXpWe5{UQhVar0H(w)<+|9yB1E@N}CT9T>*XM+t`_YV@e}A>K7b*|2{& znP~fT{P3F_=yI3)dwcw_`;9mxFirGaZK{vp`+%W;Sl2u&^3w@~XE0nO*D1=~>fv`k zLZH!>J(fVrjBB&0TDw_Ufw;6t21!aX5A|5DU0=2K`MXM{v+5chMIjV^U)?jMB-3$wt2ri63@%9_h}-{=?ba!Ufx=^+F;S z)9)DMxpVCpSrRUaJB{@WzDI?%!*i!@(^c;YgFGIdYtX6VkdaC|tl<=6>ZqfZmY=jv zI?B1cy4|kZmrnh=kpuf`QJdE|N*Y5D;C_u2oAQ%8s!S&+=q2?kdqFVjOxvQaNiUqz zLy4T7kB#B>hJ)qemy@MzP(WcXI!&_!G!woCc3f24@uuF8JF3*W6b*lnL3EfEX&u(b_o2 zv7mK>b~d6P!T~t96lhF~x51hZrBmi$GWQ>-%20$GSMvhRLvVBJUu%6`}z^w&^@xvjvNc;h33soM{U4K+m%nn zny2(ljlvc~HPKUOxCXuCqGP!?At70RKz zC}-)y*xAc2)J%^Ms>c0hh`k@M84{RTzTIClv#Hd_2BLyz1d?G#oz6k1h(Ar|FF~L+ z2b*zzjIiZ&SG|ePK`L7$dfQCujA(1{NYMt3%fc@uaU@ zQ*>+NVARg=GRa8M(-NsLCbu0uygsG{D)gHI3732eCX@Wq%L}dB3V_-8QZjY8i`tsr zh=x93JW4a?zmPGqa1YzC3*w0_{WzWSscy=20%H?aD&21M6{Ka^BftZfXMk9brZEZ3 zsM~gJ!EW2;=7r1ajg`9@y_=5^h6q0d6qXDsGMfUFGbxyh(5yHHru!~c#r}lE~ zAFYkXS^S3fV7fHaXhHzcSx_m<;NzC)Qg~W4)+*I-PQE1Y_9#pAKtS=rBp<6ua*<4+ zsf(mI%|RIGiU5P1Kvm-qh-?VaTgen&v;%FaeyFIW3D0AQy{%VKD!#RvM&Up;iej!am7=?9)A8T zQKJ{Mwf)NrZ%DAKLPNn|U#M*E z+y3Rn*G(BhzKZGM!?bCtfGLK+zyyCPZPVl7kTlbX`k`El^Tji~{mYBy+1>u-#r0uk_HWrgM3-B$4|Dj~u>#2WKFOiCFX-NUmjn9l{ z9E$SjUSrxm>81Qg>;lNWQ1vl3PU) zV~_>@jNYdgMi7qDfe@Ue`=2WoY(_^jVh?Ig&ledaNyIXg-^e_r16jt%eb1hCEdFP6 zZh?}(iYHIw6qaNy)H&S+0?{NW4XK+tEG{onJT#W?);!(wMRylygv1Zzcb&F%?19Xw zca}_MNm2I7YJMtZmp)eS!QuPoAAa81KiGY{ci?G%!ZKB<@NTZj*;UOl@vs=y)vakK zLU&;-E}76=&;cuH$WJl*V_L9$zOO>W5%7U z3S^-}EgRRj5?M)XTHn_elx|VLEZ;9yr){d}sN3!8NukeZY#1PDcuG=E7XZXyT5Nb- zthFa0LF$}_CueCCtN>rpCu2QfIP3J}wsT)`Oq1Zn6@O^AcpGG?31w04CC|izMbhY6 zzCggMDe4|tV{>&03fJH8MDK$2N94fF6D*RHItnRtM}D`2;eCxB1YnDSlv?(8i>^o3 zVgp6t@PUSoPTI8^!#IG^QHwv^yE^g1{^cRBpy6L1{fKMyK-B1%nJjdnwrnP>^ta!t z$B*?9{^N#nQtLiROfo_Kijm46ArX$mxNt z7xHOKOV+tV&5{C7q->BFaq)BxabFTeDo)}k;Y#t8p5fM@FGBPh&eSotSD6$kTFe!I zdO}(wY0ro}DO>R#ouDrnm?Y&Rb((<1);P(MA{}&^@fvIkfUtSw_+yEk=xiZx@#<%c zVF377@c3GMc%9&}MBp{$j|(-w&pbh{l!k&KP=dImAZJW7;knbpC7WvQP?V5x{OX1z3XkkUBzEgHvSZlL-zB=@u!%q{rGVqU}a zzj3nU2&YDg%HC3XiNG*a>qo?CBuMV7@5~cAF3~nVf=$<_&4ol}c;;K@`Gc5+B|d3T zS+KN(5GRUa+m+}HmY}5#TP~J(>im*sLo^Q^s@G{Yzv{g`_^;Y?2)ZDDA7uu%apUIp z#7RQj&|0EvAnLsqu*lwwu2&kaL3`6)@tQ|}t66=4Drt<%${KECKZN(_<~KQsyNt8z zXF7-A_3^WWgPRUTl&Mf460pw%V7S3_*aBNjh@B?cHn>W+3e_922+H2v47Xk_Eh3ecsFYY92?NYWh5YvKGxgXyqiM%xK2QpZ^ciO)zidX}gVgho&J5G>z3U=${1xGC;t!JKjrPZo#4a ze)84fYrwz1-evO|BVLang-MpBkMB(7gTWll&*}Kl&<$S=8}j$%wcrzBF-;z|7zMba zTH~aG|IxTwCvEGwonB$!cOpc|#JVi3)^MYi9V}&%uKodGycjN2cs}VCQt|w785pHn zXd|Q`2GAUl@IDIR`q-?)$FACrF+eAr6g3z)yMlM8_d}% zgu+#LF=P0o#V~U)pPTGO*4j=cleo}Q$xYR=p`7@2iLPpJjmHQ#(HEQQf?4zx_5atOrseM7^{Agzj(qg~4Q`Ro_Po z^a(X>&1T>ra(fIRv}V(f)~8S$2hQ28@nwNAJWI!CiLk;WuTXe?jpJg793co@<1rAy zY<@aP%QF<@EE3l?y9_j*{lPPwgCvs8YSouu+zyzn&n5@nC3&L_e6tYpPT8Y_9Br{` z!Iu-E>JTHx07}^xe=P_~sUf$N6p#cTjf-sS8z0ll;u&36R3dfR5H6+jioS8N5j1tV z5i-m$zy$0zfR5AfPHE=uA-$CvT@Wn#kULg zKx0G{4%lMYo4aDRG;D#3kOF~eXsB)+VZ4F#9psZqqMd*1dm^d)Ko53HgrxhbhGA{V z*bA8AA+W{F9=AYA)9T>yBV45ZAuu!H?)G}sI%#wrF)xl7S<;)^4r)AsuHI&N=dvRq z+W|Ua9kR0|k2ZF==vW7~z5+Qg)vGz{6Eah3DF_1$lD3;gG@*^^kv0tAwo%Uh6@;O? za3>|~aK@%H(NXbS+(&ow<*q^w4IyH_d6_`4iq;?3e>RHMAHcts3D3 zUR`(4j3cj5>ZmnAsX7R7XVuxpa_b~O5mD|x>u(HJ*gfnh%-jCPyuG}nCL$1`^7{GK zlvg%Q){z{(S$+(+DB$LY?j2$MfK8P4dI+tXI#mCB4DZR4rx0y-@hyS+SUT^@IP+KDo z3;`DobL1q=_q1?-9f!^h{t*oNqATc&Id$&@naNNXcd^-xw~x>-p4?v#vg|86{NGae z+^#(kr02(G*XhG%ccFicdF`jxc7`Uw>Iw3!iqlLO$OsitJRGVR0z#(Kcw*lx#@5NW z5Bh%Qb_`WKmd-dWdzplTg{2$oQuAu=4Q z7H>KW_3gJ!G5Xq0#+up4jpt?5cL<>CFAIYdX_9a~)gGJ}qebCFgqcK^fa#)K?%fd( zG=RCpYA`RWd}>U#IstVb2#*l=2qlp%u`ND<#uv@`G93UBJ%wak4=EfH^6VlhDmF|o#dQ;J4W6BdWQk#T zP*F)>k~&nbjxSW|s6UrQo{hdyhjl(uk|`Jtyj&u}f)Tqo#*E#LMUMq1JIjTka{E39E`1sr!y5LSyfygt(>$8i!h!k$whl4iH2#1PE@Nw{}Ne;E)@Osp5~?1A=u-ISWNj=itKDy^Ps3 z8SMP8+X>XC|Je$E-c|sy2%1$j1g`68v@-%B$bmCYd@b0$nX#AmbL zWsG*Dt$IXv{0u1iGOw_3t`)FpvP5I{K;D2|bgfuDPfI(rV3InWb6AUzMfdZ0_TSl} zTib?Oz~QnQqGytZ8g`DWFEH0`8SUVoVXpn9_Sw>$8f{-&r}M1!g%;U$v`LFVZ(}Wk z&yS`ES4$^Npqmx+ci2LcjKS5$YW0n$4ELbMpTFPxu)B9~xb^xqCINB~hn$WgX&20M zbJUtmQKgTz!KZ1~I%&7NL@#Zv3vx`JH8&AGpJ(kawVwEk%ouL;IuMD^wz4u6meF)H zM2O5dTZ*$XZCPo+={!@jshX5`8~p2iugo+~%KsmjL?qdTk69;3Oep_)J@q0jF=jCa z?L)YSjBywf9aYf^6pbrg*Qw(t9lu%v$){N|8i7R?geT8bnNKl9OgiD5h=Bb!QS*|4v$l2Zz5THL_BEP@ue1V?hriE= zgb9z(?8U_8BlXcx<3|G1wzAqAWBp**0L`5I0+!WK64jdVWoFs-S84Ur{8ViXXoHDy zcR-v=EthI*?_ihK6pZUfc~Q+Ffc)eda~TE)VAi28Q1eGUNgt`!l&Ur8;9UZThk%8s z5JcOKN=TJ)EfwrW!81+R=*}#3#Ag#n3T+)9XRT&K93dCRnmSma@L*&i6FNFlcLz|q>W34LlUK!+El#kIPTOdocK z^-f4w)@D)v^o?2RJ3npl$He}LVi4k7P`T3)&npLCofE3T8Sy2^)`HWX=COL(p(ezh zKA&fjJ=I>IRCE-V)e?WQ934E!-_LZGG#f6>XCk%KDP@mA6_`66iFs>oT0>G^tvKVCd@FJh~ zoiJFwKFjF9*elBB1^dCsNT;-^~#l8uZz^nXHnLGFg;@XF}8pbhTwd4LqoouNj2|+If){Xip^=>3Tg4Ww&i~ z?G1tA>DMoyJcZ+7y&})^FOb7dhz%+gHFU{FmvX)RYNkfg(kB2F_8wihknk;9rwtPJ z%}qo5w?3qpAN!DSx4MBqMmBg>pze03wt($m=XE$FzYl&yGz0Aa1&^J-;Hd+4vn7RO z15XKpwVaeD?)N0AF#0FwMdn&t;{m^|&L9Y0#;9%By%gMtLBUU>Ni$We?u^df`})1I!TPu*d{o*28xVmh#SA8# z`TF3N{3x!X;47($qvaFFB%19Fw9&JW)j77$9xrGulnoV5uz4eu_@#(vV5=q=1S5;0 zSbe34)mIFVEC4qJEzsEFD|DczBjD`GzEPg(AQ{~YoHK}HJx;7CmhDm7mCvIrqwY1; zD9NU+VC*bx8~3!2gJ~y9Xugl6s5oqAtu`8l4uUOhVc2f)qFh{9=V}Ly;ue>k1WS_6 za$!Nt(hk~fEiOoT+98$X;)0l^9aP{fE=YOmm>)>D|5qS8fOh^E2`_)$>^p34g9Lap zBm#D2yKF-9<$xWbL-lK9z%A<@@|BX?W^wTy(qhr%iR}Czh|kf4(4imQuDrJsd}Yg* zc<=?S?)Ov%BE+|HYtE682UTkwavx`gTvIUFIs;WwIY&7Wb8|e1h_RhGk%+=D7cF$> zjbtJUtk!wdPCIPpb^m5aZbj&Vri}*++Ng4(osy!IYsM6~4VSn~2E%peFY8bMyRv)o6f4s{afTY}(fQ56^Cqyp>6d0{$u5f>^2{;i`3NxB34g3k2M0I; zN>e{l1^TBmF0ShFhSq&GLD&Upd@mVe6w&{k6nR8O)gGbi2=W^n02U6EO)L9m-c+wHB?ITzG3(DdLS>+h_ZR|+ zCS|hnhk8z09c6RUhfwQsf~cqBL%jg)8c{QSSjS_n*Hi!6dh?ngbOw{SEW0Se@ArCT z6%WnuX@ck2z#vEsh&PIYerC?Oa)gZxBzJqV2cp&NT z5~G+B`B|JBQv_btB{>?lqIpIz6!fj#5?lL$EisLYb92;il_IB`5yQ?I3Z~9H-T;xM zI(`UYPXpQ2W7Rr-s9KV~ZOI5q`K_QN{4wm6$Ewv2<#jm}=B#uH7;E{$wMMK(elYM$XM zLP)JkND-8^?q+96kup>S7CFuUS3s!0oTULhWtF4M)!d!~d~NY8nRFOTLs2Y96}=S(FE zf0?_RnVXy2V_l&Fe941oXnz?6*FOfs^F1a0yaz4o`7lY+s@1J%iItjYh-JpZ6a=DS z02FWRE;Sh`VBoeQ`K(~g)&=X$25Jf0DrwA#7A}`VG)((42-M^O0uBH)XTvW!!#A*@ zXL#F{G|SUfn>8ZEI%3s2^5~dshPyVF;X})#>CdP@(?g|M(vjI3!>wd8->I*{bj^ zv9%!Nlr*Gl?W#IL>EZI@V1Qj{(Qy=9+XO|JEM*u7M1yriL&|@0Pykg6gzg{^2=$0i zzX}}OM0%%@jGoG7G3vVviB#jKC7yD$tTq0|%j+l&swSwKD0~v@;>D{q?MqD8EL966 z_E-vT==(~dW~Jt>v5vIh?Fiq>-{M<+Nk_cKx_C0Rqs?=0KAP5%tg$Xua zr;~1UI`M6%liqeZ?bd~+e2q`2r4Vh=wTiYO@asC=>lCZV`{s~)8gFbhP;)%VE~yQ!@pa8*5XCA;sCoYqRL2r4*Lc1n98uHH zYiUj(+k)dbo4{);Rgpb42EtHrgcZ-F4aO|=IlHT6Ys=WhEh2tlZOVRPGwM+kSGh1+ z#EQCEW4TByvLt6}@-Sc=f89eq4dXHy-%EOyH*`op7I(tA~i(S-))!k=wf#@)*oAC z8T4|^NkQOY!}huZ2iDx2;Q%ZdTsYV+*R36@nc$oiaf=cYn(btHikBd|U+7D{MsH`? z`Er*`yIc1zZnti)s4Ks$(P^ag4)iUC_v$%1J&y|$?*R7mkR5AHi01d zPN$D05s5eJ1qy#)a#@;5E=vzfE=zYLmv6(8Z>N`1r5Km<;_q8^2<%&?x5DeL7Mx0M zAi$)pn~Hr6(OLm{6M^c-fBvxD+d9}kIzIfkb$oExJNR%68Gh8$MCDTy56CF9g4uZG z-_rU;{?d0k`5T|?<9>K>Emw7dV#K%eyk2)rv>OO>;lPB0#mVHowJB~-W&M#J!k4OSfmW1kB3%U331 zf+MrmxGSRMT4v}3(gJM<{mZHzu8Ol_0>&s9pWwHyr*Z%Q)wlt%d?J_lN#&?ShAy;% z`KzJ|`NuRZtsV_b+o~92rc6d>Qn_9WK4sO;GZRY>2kTdcizJjCoXZxV>1}Y`Iwep6 zMTf1Xs(h5%Ljppo_n1BpGgE`vqQEB_VoAxltOyZW}bWKaM%QXx@N!pln60YIJ zQ#k4`w@1TIy2TzLXAtWZepNM{7)radtgpA|vos3z9?&yi#{(vFony=I-!qP3am{ z3*3m_OX{ zjK&ih2;6!u(v<;HtyOf7FR-ktX8p@>6p-<}@BGs5vQ&`_raZh_nganWdMBWxIK0}G zucT6@(RW5JgL!%!r{U))nPkvqU8rB!8Ds5~&(PFF-Ub-K0K#vNAM*reRue=3w+()r z(uhdJZG~SEJS&5nS^{epYbqtfOVtGpgL}_rbW@T10!c3{!*KP#BJO>~`+@o`-O(NE%jkWUALO&BaL%AjhhWkj>4(pqZh z+LT#*(EzRr?~Lkt&q^wYugrza3nSZf^=EN4O#L-)cX~4OEv~6=uktKW##$Xse9FB1 z41&|qv3Ww$LATD6>Qv}G5&Pg5o9Y+7ip^%TTj%QzAH>&M#kwBqr79la1qQl+p9(}W z&qeJ>OJ&ONUrS<{sPgwwoNpqH;$+lcu-J~1xWUS-i<-mZhe*0Zxs`DGN$C5)8^!!Tl0WS zBF>!9zNgBNAsiELfgi&e`87~1Lrn!GS7crOUm26wd@Q-jeyMbiCnL(Cguplv-E;JZ z+gmWXReZ$eMJm_AwbNLCup7IaM>dzz;$YXnz^@A`?0rRzU9kpNt+%hIw^oDuTf^eM z560~F2*PU#fc3JmOXcG-6As#yDVoz`GHfyTE9-()=ns$&CCN%2T&#kZM>9z;>MQN` z%4(LU6`4E$1iDpQ5vjEstJAd_TA3&|=r`mDSi?JM?N^#d_=`IaONaUmGdb!0sxuh{ zV`nBx{rjyG`SR782naWM5D$WMfUKvIiJc%CX2E%=m?>HSNz==#tE;Ahv<1qE)^c+N z|7c$3qv3baVC7|d~J(#2HnNe-1{&{mF2swo1$Fur=Di-qJrB?hS@3>$r=y(n1*@V>wzC zKYlE9kCp#GJAQU6)fJzjyliU1;1%G@=F`6C)5pENO08DVy=#=zleBM1Ql$Es#Oa8AWq__57Aw` zYUav+RMvO^kzsNbrhN<{=C6;lTGRC>srBvt!_hduStWgjw?2U{ z<&8SIs~e3jggsX?sysL~y(J&{`3OqshVM#IE};NnI#hA&`E>YkyHYD7zGqIK2%FQx zj_=#%-qH3<`tvLcKNYN5FzAZ&@fX6(JhZGv^^vm_Ig8e@WNfXPVE-qQ_GN$F%Dspm z!YAC3_~n38>7UoJSO705ase+@>xFWd-iF$pRfQNc$bCvjxLME!deQcQ@vj>IQY$3{i46nD6L0IiOemT|l^^-Ak$TA`Nm$r57wRb$U;Dn)!GnPz#t)0~xRSwPX}5rkkZD<`u{6CRL+$h3gR8Mq!{VK9{i`hgZscrQ_({Ajhp( zsnfqN<-g0%xu!*damJNHx~a9!OG|6!jRZez!WB}*g}U}F)bjJ+!b&X|=7z-5zYJ3N zwV4w#)oTBuHA{SJ_VPEfWQv#m>z0fs#UgZSE5|y~HqrhtzA?{^mZ56|#-cy%ovyi@ zrC0HCxOo=0WMOA4s$5WYkd!Xy7iWR^%F4y_F0NUnBU%Iu2sog6;7P{T1|AvI%4nu< zdSzU@WSMMUJ4KB49BpuD(QeTy*75KFtJk`H3}Gd0AcCMQ*rp+>8>*>|D|_fdW4~+Q zreVF4Hh=7!d05$%Y6{rK@|q6@a`9QN@e_!RY8`U2r8?*2nzq-b9)r5!$#T~mPRdOm zU|Xc{pu2HiSBeza@oCH&)>`P^YU(z`MY`PGfVU~s*B~ePbgE!<-09a=esUB z_a$;R?^bL>5m{OMPKq0C;8hFK#Mtp_SN-1W!WDse?49mgatWs1w|E`vzQqfQ_pP%i z4pOPMu2LRV)EYPtbz)XiIq!f*YB(QnAp@jB3h%r&d)!g4J?2aEKNaC6-Xvakws zgh|d(lTW$yzA51&MB`R`esnki!I|P)i>`0;PC%j-m874W3f4lOh~;)2$2AqKg*p+- z&+DRc8D3XJS6Sx$vv0!MvV3RnVDq^5Ve|NS`*2@U?O}MaeLb$qQ+B2KEPPsrfADmT zh?dYJyYiRn#_CC9sk;IHwi~ay@Sk3Hu~yw!?Q}YwruvN)!tE|r#+pmD4f(R)YKi6w z4AERsmn?Yd42EbRT*ovD^k9ts+fayu-?9;wX7nG$=hOx(V8iOhY80Q}?!y1?@6=f3 zm?CM)0je9TaS-3`2m81CoBOre_fvF?JHrIbB<^~>MX{u~9ZgK`Nxd1ZL8~SHIEnh7 z#3g$8QyMqSy{7l3Aos|2AJXJ3ID?SMBqB}51273r!;A2GEFP0lH!LtEB2}uq6L#qa z$Gxr*ne2#*gMA~sC|~od2xE=9ekJHhz;ubL`o#fPV$H6n^`gAq z{^q`0PpdtxdcnSZX)_y8x{I(Umh5(~nE)QmGn3u@ow;VRYx*mhi9Z(6H_xOo?^GJ| z&ZRMJGSlT(he#@ocC+m`wxKr=N!gZYq%Wd>IFdmDz&e$HSOGzcNYK4~aI(GE-NS$NsFmM67Vz!?*VKo- z?s)mY6_|SUFg2`;hxyE|9Yvlv@56}40t4dD-rM@($;y*u3V+pViT#7)?N#xIqXr_N zNn%AtdUA}0^fKwAYY8sVLu!y;% zoJ%{GN=+N~avRceyAphWFSQ-PcgNCFxFnYr5*oXPCmsaZf=C=xc$TSzkMj)nks*(>e~?e zPpxA7J(hssgEr|V4A8%^(y01tx@}gvVwA+>5pFkvn~A3xGDb;=ts`31C8E^j*4On`S`XNR za0m-T>D<-+)XjoJkGWDGsE|Wb7HrLU^5;_7PV=|gPFXT57=Yb{u~?_rbCq|bR3M*f zj67Ij!t#ic5-X-*I05HUg38v#5)B7Q&T$S@(|lj)QgAjYhEs7y(Vb9E#<%oAaKqX> z={g~{;N#OSC@EVlID!;07>mU4O2DkD@So<#w5$pRfy$Gk;YQBDLn}OcjB6Mqc7+%0T&t(AI zO2_%tU0@76&q@ikI7{o|xpRo@o`WVK4MhO|AKyULObo+w9d2J-1sUcPos4sO=}`JC z+3WAbOHx|3XV^{`HX5C7<=byxx^B7tQv!!dHe& zuo_h7^=t9GCT=YPpv844fFs?z$!LswE`xx}-G@$LdwwULqNKo%VpC2J@}J z)cG?FxdqGe%NfA5=K^M>Hs5F~(?@&ibYn>+aD07?;`}A~=2Hi?>2ivs*-{~;5RO!n ziXrT}O?pzGnL&F+{tU^~Zs7V9hD1|9;!nlq(bn!RdM?zpU~j1-uXZ+v)@`0RDkN>eXI^jE~95T)6@2;NKI5dBpJ z@0d#1T8xBMClS>m>DsTx03MLeKNVBVVJFropG+1x?0mVTGZ_IcQn3P0u=|sCm)813 z{_2YEy~ySc8?Udu|Lbl0%pLotU9iQdJ24#SJi9Z;!A+gYaxYrN8PR+(LXvQJra%u$BMh4MF9f2zirMCiY<6thO@TY$ z02HQKxp9v)&d2O;nHVJ)A`CkVuk$pJal#A~K`!Hmp_vY*ElARvrl?B$AZ;6j=0>%9 z8Qb)e^zJ58=2fRW<_1%)} z*^-o^-)-*CR;lgQ!T#~~za4{@#LvCmeVvP{PDtlTKiKbmINaSo-h8{a z%~l;=1g_id2K@IW{O2wFXAAzb4Ih_xI-TnmJMibrr|_TW@aLSv$IYA>~wEW zI-SY8m)Ozd-P@g=Zgpb;zir8HP^)$erbB;mtKR;lR;b!qE5eV8%HH1Y{&w%};r8ZF zM+*Lz&pMsSN_*uc{qw5OT5jOG-L1{zgTo^>`+BEy(&=3m=Bbvn(3|LAlUE1gcY)2UHc-P$6-VV#gYOjGN) z#?7H%S`)6es<(ZxSBTcpC*9_Cd$Y9&FQK5_h78^M`@7bn*gAyo9tBwM=O1^Ew|hq) zHn+CRyM~Y-$H7^l?-d9Wax{j-qh;M6^$rfF!);nlEg zwH8GkBBNM?sN!dDTZ`hPycj^}mKMK&)?dntq4ig>*eZYzwzF=S8_cKHXNvIoZp#IO&MF~u!zpACc+1ZOCr7#73eIA9D3j_1n|BBkxQo}l}>y)I$a z560o(#}IP6>6O7ZXSJp;TZ`hFBF(FgR-{*{Rqt7AQB3T5Pd)XXLA{T5y_aj2BUanH zeecLg)Y`&_-nH_DA{UcwT&^Q|FVkQY-GOC3FxqODqBXeSfKtITt|C?qKI@7a5p=~D zB^6g%i{gw|eCef*2XC+<2xII>Jvjoka!q|JJB8n>|eZum5u&chRP{`WC&G#}$chY186gy6$ zICuC9(kDo0E@jGt9*BNR21}{-S>l0KOKitz6Z7$Sr?LE8WRo;aF2G6OBzIzB<*8A98dD~tF1IAvS;IHL2fX5xmJdhFS)hh85 z>Y%2c9hXG=8sy2kzZSgUwV%KWV8yLATIlN27fvY7c(%|dnJd#)o;|OjxutY>uHzN_ zJjikp^!s6!6+3y4Z}CnSof!2fsPL1I>f*IndG?&=9xQ-z5@7~1A8U*NLBOZDz2-K3 zAB?9q^re!>!R`|+PuwJBG8q&Z6yd)-`TTh_3^5fL&wr#odb1`;4T7C9Ne0B3qaZMbemD2{^J~PyaK(zWWr8HoWB%Bl!%gtpm zNcxneLv|9$aPs;)1p$f^lfB?~u%9YgE2)u?0DW%Yr zs9Tm!3ggGel%3d&BR|@^V762$M~+cfv(7iC5I)?Dl&h%YDk++$m5fZz2b1b%YQwF^ zSk^F=%uGadOK#|@OTjHHk>%a4Pz-FT0Zgz7BLlzvQO6*q@l z)a9SUG*iawlskDXU9vFjKd|-=}1i{$4?BpgC)vYCP}l@1!LlkD;kx z*a_h1wA769#~>aI!NGQ2B?E(1V)1+~u>z>b->Eig07MXNZcV$3CK6#O+IR|Q0J z^I}0Wk#tqeG%7rcUl~k*5eABXIQ-t9Q3OD9-X*6EVRwnr#W;tAAVEJLqG7d8VN)DW zc~-Tg7v)k-JNGTszHgCl6##V7?z#!wZ3nAqAV$Ms140XgESpYpZXOt>EC}ufV6TdmR&*r}HuN{gvQA=JNxU^8lxpWC-;3kTAEsbDX6pB+~^JuR}z3K2@eX= zn%V_xa(<5BeJdqAqQko`<^n7?*@slF#NiD@0viTtI1t(79G1jn=9G|Aa!xZ9KadNK z{+bqymicZACby<4y$0{PVtIOFM$_9o2;pM-@o;x58I6-Tj5TaJEnpv6S~X158Ovy3 zgcQcXkYHcM^y5yaHcz14I-zCkQp!rA4La%yxkQ6;K1su)@B$V<0f3&Pb%>u;r)XGz z$gBi=JuSqDo-z;VX;baG=xK8Zc6g_`cnw|ZsrR&rNKvjb5ZJuRg+|-lX^_Z@q^HeD zdUoDY1G{*wYVnUtHs2O(UaT_~$yGP4x#4uHys1>J+1hK;uz>0bR3fbUC zQL}9Wz1Tz7#&7_^xuOBZjnJJ{IGA5u<3MA!?0umlij#{ln-##rZy1bB@-0ZcHBO_L z&OBq=Uv|P717?F!Mr6}Sj1S@O$^8HnAtd7S+PgPZFTeDLb+pEzJVlaO|NLBXJYz>*sI88oB1K^J2 zmL*MHqD*iJ!47Y0pmj`|Bq$}eRcbOoa=M3tg_WMN&&KLgXt(xR*DZLD6Kg{p`x{n9 z1~_U6{G~D0**;a3*}#7FB1o1esX`Dii&ERtQyI;ijuF1&-~#{MM_)t?2#0@C$jl(k zLAa`$KaYrI;qMQLkyV%}or6qh;u4Bvl7kM5PR(JUt(gQIrhvO-yT;Son8icrwjQ@T$LEcrK$K>2B3dUDPM5xetfN$jv zWOh(7W3oJ0e1h=tqQM5f9`MP;=nK1YGjhRI_|@(Pi%JQ@x$ zBo#QXK>BuF750%*Q>JuVV8?ZJ8RXy{2JKU2;Du&X8HPo=494RSdL+H)?|-lQ zJ`OHw+Q;cG%Q|a44aWKxQOEo@6{T;#gT^RyEBVL0;u^sS4FRo?n#7o7LZT9Hb;}IW zMr_T-YOVm~rzlfcTDK%=BrHq&0ok-}qoD}VV zGJ|j!=AlnC!iF9jhhB-#Um6AjtuUgkWdgF!)qv_eeq&>oAsb_A#oPwAF_NXhRjn}l zi5wJlet&VsQVtkTZEmZbW&X(B+~zBq%`1iV$(GDng(l)gk(l_do2QlfgQ z@V((U((k8ed=!p@6yaKVo!zszMFUIFs zes@joE?o6w>pT-(S}IPpsBh=1EEC^I1Cb}KMe&`D zbc3Xj*5{6c19CcR&G4IL-r*F*KYM4Pj4UthtTjcR!IBr<4M(VrK{risoFob_s0@S5 zy4top1spIPu`%ww*nSp{gzs?~(P}I?6H0Oe%g+dHLFFAOO#HQnN=wx9aO7d=W&ZqI z@r95W@>g5uaW2gAcT$fpxrkzNK8;|3o{Bl=rmb?%I(2iE}4}f~y~_h)O|fz-DizG?IF}G#rOPUafRGaYc>}!4%#1 zVM|W>0D77u3_+lusgi-D(K^OljN%<+`xrbZvh?TUH0*~Mq|02v;Edp3lx7~aO3uga zc)*i9eh6deY%7U#7+uSd%$7j@vU}?pwqm}4v4ut!_)&LcQeEgcfqtWa=;D~NDAEEz z(G2~Po3j_sRJ*+?2NToBQ_A6_T%1x4rnw8{4YXic^};-BY?rj6Vv#VpoW)MUubi5}`d`zrU;Zo*wG*wPOp>-Y1Q2=E z7J$BS@#kOuZ{7e~fMT+!?_%haqcxMAd#a5YQ`J4*70&7$g3IH_Q$?+s=jHGYOKcz7 z3gQBwUKzhP6LPL@}GHdlB$x*$n-!`K~7L(rm4>PbbhTuC(`_)bp&h2WboUy~2W7S#5k~1D43b zWfj^$vuI_KA6tP2!ChPP$wPB+(6TLe7`m0Lb&C37%5cCT-45@5kyLq0@4K+Jm85*3 zcg}A&d8~Da^|Y0(bAs`w_o?zc$i+udd@&6FI7#wwP$nK*N=3iCYZJ&fX&o1Ap=zc~ zbJvsIt0TMWx+jM$%_*&7i1wq z(qGMmc$SKO9&F(9{M-Oblcw3=7J1{lWO8;F-Z-xU06lnl%+<^A`p~|c=Q4i#1KiUM zOJ#15|9kM%;)EZ9sg`1JF&vWr^1~fSePCt}95LELK_+L7X z_pn577|K~B4R@5$c+Dy~RK(J@RtSwa)>zXw;LE9hHX4rM=zX_WM^~oVSm70LF6+)@ zT(GO=2L-cTl5^xuis$q!V^@|zY)%Ls^KPuyKu{2#H5r9R%0FtRj^?CiC(7{4B|2nJ*=R3t zw6EUL*nO;M4r$TQw8sgK2}OrWUJ8dg|J@Y@r-NA=P1=!yWnh2^ zPrrdU3-hZmjK%pdxdL}c~c&(P`JSyo7?d@YUXFdyI{4v<= z4R1s^8s|5iM|DWiI}TG7?qLO!m|+hunorAT)On=YJn(nG8B|H*J2V*lpC^-eAS(4r z=TX6~M_Cgydu7di#JY)Cp*SOMC+CUYDC)&0!C&bG%8r{}3bCX1pQ2K_BZ>Nz>7Mr! z%oqhW>uzX=CTlYNT?J0cGRXk2E6H#debxz107)&Cap_KDhUNilt_eJ+*0p~DORG8? zYneGC2GSFPWpU?w6xEnwA;$fT!S01I53patB3#ebIJSI}CtIh$13F1bNso(oh_N1y zN~>Qh*gjJtfikElz{7)pG{Xj);9&k6wxi;|;1Ks8K90=&O?d1s1?#)aBI4dR5a+xs zxqQ4GCjC$M)ta6sL(AH#e+dDBSoRbvysb$;91RY`VBkC+vh2h;PENnH+QoTzoy#H` zgpstWgJ(b9omChiT@>W+lT_9Z2gTkWRHN%yE^%9(fh+y{gduIyYjSB$F93dQMB_o2rf)LwVhfF97!f`N%t#!QsORW!&i3FI@Nd#RUL z6HzXtC-qqP zing?>ZTk&U+8%@$iPP`h;B^};I5&K=6N?MK&o|+X1^*Y%Nmy$CHQUw1oRxf9w(%8r zG~8w)2eOZ*>l#+N@?Xqtv(*wG!W81K&|OV((57wLpcwl4y3M<%zA1Ul&mEZqB55vh zF=3Vc@UlEVFmNIjvWnSb$&~g{Gi;J4X2cgyCGfz}n;bC!?+AFK2($^aU~PsI2tAwldxIB1x;dQe3GDyrmN_DejddA8<9?i zlwDMY9WwrT8N&@Jxwu$B%a87{u)6r>cy$@&p~#RDO*9ix9YG3gg+CLdF+0sc$v{wZW z_NE7c?b%LW2s_2yoHJoSJE|o}a5Kp$WjJ%*Y=B;_A7W@QiqV=uU|FWGu_nO!FN#6) z(cM)@Aq|2d991`XV2LalX=;>PJDm`PLxyacN1%udhMWYPX$~TYda2MSYtFK6oqN$e z;ti5ljsQub{VQxkFc?q_HW5!oXJHCR!~s_7TaM=nP#4TrOZ*guWAJ#j`jCVJn|c{y z;9XPN^y;M_l5^`7C|oreiBA(gIj+fAv$JjKEWxB)pkmL&-y%HR#}b9v3(t{g%jgGG zDCub$0_P1XvyhZ7Ze;-xStN(CU(g(%=>_Rq?a5~NtG~M4 z?agiGl)g-hj8SN|HZ_Rf85ashz|rK*6=PsTvBxdf5rmDgok!*16@MPUyJ}(f>xy@1 zjp7!EDm9V8t(c`fZ7jP%0E(h}smq6@z(pMVCpde|e(br^n~X}%}Y=~AK@;yMyk1u1rgFRC}b$63#{&Wjq;)F%py9e!1K1fTi@ z?kF*xhwiD*+#X+zIs7j0_><2hKJ)^9UpNVir<96RF%J5re#D!tq;#`MCZ{)=TQxHKr%S9s)=Oh)Lrd7s@d?_VLJRS>C6Oxd}T|89k2hA&!|Pl z`Qhl~*g!wP>pDGoYyRsnUd3^3i|6?bdb;V6W9BI;u4hAcCV!76kG}+p{&`u7qfW z#{tGg&V#eb5F_Z}ogT^0ZwKy@_Bwm2LI$a^c+Eq;DszrYzF^(kKYpJKay-6 ze#S&}btF(tl}&q)f*pm#(-IeQj>Xfw$O*m6^%*8OvnyvC;iq0mkZa4?E)wpC5uC(2 zU$cPJ2%*aWn^v1cd0t1E7$kAn6hCNXc0q$(8a%+g>u__XFZaE~u;3VTL|nVS4V1%3 zW(K8r2zQi)DKUyl2{<4PzN5hB!TK1V`4rwX=n%@3%0-__K97O1K}tPK@=J3#zzG?- zXKmIjimUh3rdUd`a(*)X@SXqooq7O!53f5ZyznF@6&2?LwL8h!Q%#f0XmEzvY}pHT z@&4$%^*^XF?H%~|U3fSbD(BN2-dA_}^|zovv>4PsLw`tQ4R>TN=(So}t#bt7uE9Yy z$ih`Igk-HsX2^`|2Ifk^r(h+4M{$YNSxyqavJhd?1#Djf*)t}K28RSl^Pi|?RSP*= z$!b3-Dp0(7)#KnohYT-`dCD9E{}iTYK^~0?%DpJt4@RWI7nE+0f7v(rCm%3m zg9=?ZLt_WqP*B&+PCgjOp`??s$+?=cK_rYtI$IoHK> zf4i9}3-wrY-MQnyNn0oly5+$~8Ld63!Tfh=A3+*VazBZ%fNf<$VM$ItnZMA1IK1u; zgAs8-NSd3K=2&|WdzyPwiy=(4lXj5EBfXu_e{FV(LjQ8$i&7L1;9yA7k}Pl5ldGB$ z(1bU~V*#WRfH2?tbbO*5imF4-Q(0C=v&5dHj&@sLPRD7`74tdzU@2kWTu745is#oH zxja1-%e%HxN$R>?18Z?m#&6c~wDNXbS*YBlld@ild5=oFE#HwztkaS*W<+U84Ir4A zv~nj)v`rLI?;tfkm~^KCTi^3Nb`AkA7P9ff2zhe|GV`39>f@A{xAzXnSrS^t1kIna zdPwlrvf&jEpL-R13UYYhmo~?4+#<^*PG5rVEF{guSjgKq1yL!n-^1gp3bgLU*Z;<) zQe1-FxF;%6+k1%)<(WMlLk@{AQ%~zjtR3D45-4K5^tXrYI`?_kcm&%~ESkAhS<~pk zqrId&B1(S=>yPHz*SXYL9w$d(_{mXV8hXOdWTp(SteI_e#r&InB_{5YDJA{ZQ6A(I z*wW=XXkV6B(K_f20b3Vs`n|4xH)Ms^^ugFI?T1&WCoZTbE`zMh0~!ME?P6+E{^gG{ zGobZxFUoT3gRzx1ud`ae22(G-#(I!n?Mk!mY^`kzuNB6n!^K zT!SwK(+PiEc8hxKTdae|KBu;#SLXWq+&#AE>hbm5{WX@SB}_%p-D+SZu90U@iEi=s zwp@gC?V#_rVcspXo?eT1SU}@7_gVig`#fc*zhSSZ?DoC)d)kik3w=RxP_~ZtTXqT3 zcpGcDO1DVw>g}y{@~q9oNoX;X$yiLn2}I*zNCo?v(*8K(7xo61=;%u8`y%kOBc!MR zYIvSGnfGxy^-kF-$6dQx>XgJFDLr z%4HR8I~t(<-fLUtQ=<4l+JUMh=IB|>U#^GY=WwX3F^TIn*$9_;K3;9LuCA_{N+s5U zq{^-3<_iANyv#?#@1nuVi&w8+ynO1F>dP3Kn2U*(XmzJ-_B?bOa7n%q zL6$`qF>UpDh^lE~pv{7X(a#}u02wKvI%fP7cmaXb)JZym?CcSl?t;sJF-p|~WfqL! zXhS(KWdwj>^ojgPRz>I0!t0GU;-uZ}JSsC!l9wFK%e1cq0tKH{sHRy_l!HDvT!YmC zu`}!dAVVPFjjZ%Tc~r-}AvFOToa+iKABj{{^#zp@+w7hkoinn)3d%450~P!0xX;|QK%UB$;9#SZ=R~*MpP`dkT#Ul@ zBksL6{0M$8iOL(lGj}N`MN-p@)a>9lu+kz-Ue=0_U3zrnq<7=8dcnL*%%P)u^E1B% zG8rIaBlMvegcOy>J9}+DXW40ZgYnP9HTlfeikh!!{PAAg@#{1hYkrkzf0$%pK_{v; z;r>gD|JDK}Mu;p)ctpt&3QtQaYl1&QiIoh%{xXPCkWN2`mU*21x|v>NTyOnO6=dbk zwlwzVFwN-Y$kFbi=(T4Ovug|K@FM8ntXb;h_rcijbzIOcPpkPrsf&Jum!MGh#H!j=G+`ex_Oz#3 zkNzB_F;m%UAyKCYGQ>vGmCY0i0+xBMoS&1IzMr(2dU`%SPa!Jo;6r!q1WidFpp%ta zY2@XiGxU?Y$HHNkO6we&Y8quuD=-fR!%yxw>KH~9tqBU-gr>M2uc$&CpnKtiE612k zP!^Z_2m8IvqpjWD-rny1cJJ-s_U2F0B(zBAmIPNMc6;Gm$^eBKj$}pR18ry&=V3Zd zL-f4ORA4`!A&3GX^9MtkVs3MvtYEIhGn05Ex`JJN(>{XA5%x|_SYLUxpczI+uQgL3 zu>kViPebjZxjV*#(TvRaKYI|~6XacihY+$m!Z0y+gsh5dS{KQfKYZdxu`Z7C6fg*X z(m=AFSl55u-co`n^Wq7cB# zd)apg!L<$X1FlHnm4G*o(A~2&Qx|SCl%a;Yh9`~dgL4R$*{*O3RTZ5mJB1iKvff&` zg&bT;&u;S2)#76`o$5<%+r7=}BZGzhC<_Ym)dR$QjKOn*_n1LD+<47T<(tEy z!WE433OV;&N(Z!+Wd-e6a<1FaOHP7U=L##=`~Zr#nX)~8_g`<0J*>h%V&n zZ6fA*s*sH${I%AJAJrdKAf7oetbR@$#=B@)*q~T(4O*o|^g+f1ITXFX`ro=p!(S#* z8dfXK7F9sNp}w^czWSjm)J@)P?`(eDJMJBA{kZ*p)6*pDUxuTgH3-jx$uO740ot7B z37zzMAgLo8r%9gBla#C>$8zq4z7}>3J=WHbn}@x(2k%AUf9jlEV-=4#-})=IU#*GO zqBsuDFps5ffI{@n_V&9%gH3FJVgjqbwRw2ByZOU*?{NG0MK|sQ=xN+e%w6# z;DzW3*8k3~{$cxYYkU9LQ~eoM|D9cZ^ZkeI!=ugp!g{Ve$LfzAV0QO^@B{w>t5=-f zkB+wwcaMH50z09QTg?_;K?PZk(H2KW!gV^=EIf`iWiraQBBF3-x80YM66*>fUXCINaXaJl=lSdwX!Ox4pT4 z)I0j~{_*C&!9xNFK9!qFT~z)I|G5b>{9FFBeFXm`;7L(Ap#Sby@ENUwzxFGr(ZN6I zzX#ah{2c4be|B~%ow(}-X!B_MdE0=~`pZdUsk?E~ZoKMsm#Q19tMbnp{OEL=^mDPc z@qKHp;Ib19Lo`)?AB?LcWz-?|h?Sb0-fur)%oCJ=m1JEfGbs#$NLC6=bR7j_X2>R` zNUd*hQ&fMlA`rtb=s=0WDBSx5d#HF9nLbyMev~Y)Z-Y1J`Y+WcBl6?z+A4L+CPS)N zEvH-6f4Rf0Y`@}YrIeuzZa`D+%ivgA%y1hSX&u9Wf~Joj3$MN(#W|cCTIYf>9{#d( zVA0eIbx-g|*Hxg<&LJT9LUJ<$3#o_rjS!*nai!m8~)sb1n-nl<~r4ThRc zA}1qg$SNllDyVcp18U#B)^y{@WFp1J?}PlZISQ_;%XLxJJy9*{X+^%-Ve>fIOw-^- zJ5=ZvRsl6M-Yx|8r)azze-4JxzyaG7cpHK-`W{+86UbG$J5jcu!2VwtxOLS^7xHH| zmc^>@7BaBpF`*X3Jw4cx*BQ*{2Swl-(+;Yeq&WtjFKmx2@J+Y+L1hjA8yUy0R{7kFF#GD#r#7w~->PF7Iq z(zydqq8nZUS@jVW!;&QJlOITC55fw((52yc81%zbt)i_*JgKO11)Z3i!rHqqAD8Fy$QGWV!fRg>?wEA&D%2==1M*q3jMh z9GJ`_nP&pBhf#=X!T4}fZ0pd~nW%O;F`BWP)ORZRTa&Rnsp>h%Y`WaP@3(><;f&vc>8AJvh#C>s~X zf>&Who`=ds#5~`pHhNmu2y=BP74@e%AIu~9mpdCowxfZ^%^R zB~G3^>58mD2{2bz*G1N#DB8CogHZOIz_XuqknL*U-lM+EQmXZtU^{5O9dEv6V;*9g z3?Ws?NsaUgA&JjIF-{}Q4GAJ*GRZ}l^#d7s5;B9P$q402vD{n%&lhl>Q}2WR#sheHxdH#0 zR!h7K)95p}7XdW!Spx3bj|- zAd~koR3Ay_?RLBUVq2&FyuB4ws=B#ctorT+i1GME8gl|D_U~7PzP8(K&#*f?JMUOu zO83rN1JKKDPwVA2UMMW=d)gIoMhgenXBJW7Bvo5^(bYvzt#39`G=y%`Z@{y0BS{Bg z+5kfy+4^}`)xp+h4d7Lcb8yPKg_0mmqu?TJ(5lM=vOk<)SZGBYRp0=ax-$)f3^=WH z!GiPvVFqb?(Ar&q3t2|5Ns#vke&#jol+Vq6f?8BxJ?DYLS4F>>@V#D!U4%72r@2NJoFkC97+}^7c`Ja@#XtVf|6x~y&)CPb z|LHy=0I1kU6wlQKIz%=gR_`~00bP`Xak30*z(VesiWs`tuWHJBl*hsYskE`#;S zrcc_uDb!U9E~>?iXo!FO{eQ0hNlrUgMhGcvZ;OaA=^>72CLoL*3%B-?U z183Gn3f9}Qnbh@PC;G3h{_BhW>+3)M{+~Wtz_)B)fqE$Rp8bGm{sPT8=Hwdla*X-7 z#ax^M7xq>kO8D1f{nw)YYeD~YtN&Wpf4$LvRrFt-MfVdgV}QPf~_u+9<(41!E|% zb{{5(sT}UevxV35tPgMe3wnqG`R@!umQ0>Cc_|flrwVb4LS3gSVW7OHoQU+FbxIYl91PE zN10<(InWi`@OL5EJ%<^bo0#s71WO);#vC?LrY!j^H77IZ zAwKqZI`N`taiQ0)q#RJNCj~Y1b#(maz3twIy-kRB$lloKyL@_s*EQ?=-tO`C;pU!L z7f-D3I|qC3z*KKtJhi^dHwdvVo~`-TD}swVMTeoMA=Gt`lPrtih7O@(Ql++n_&sTO z9;Og;S}_KQ0imCOK>aJ$A?b}6Mx!XF@(*d)fIUWKY_hQ%Xy`zEzWyO)1JirS0@5S9|>pq&EhA z@f+3x)=Vn%pv!O|s_@BFSXh@`8cuVWBw0wItndNPJS3p8E@+Am;^9qQ;`E0w#RtK< z=@oY!aBXvR7F|q|Ne0T)x?oRL7(hooDcSc#tp_%DKaxD#`U6Pb@-C>~XyE&0kZHFp zD^#E?+)0L*qen(=q;lXh7$Ha<1<|?GN*<>{{}awgTOl~##k#eYI=Z0(GD;>EM2%&- z6C{&3X97L$TR%y`3vPI$JHW$e-6P6-4uf{B#-ns#GG6C&^t7_LI=L)xhc9cx@!8L8 z8jt?8g(11(L`CI7>TQ~047mh&4QACv4RM}`bTSMDC|_vOn6ns1Z?XtP5h8(z+~a7C zFyzg;V%K6rM4;kd5vw9vTCzOPNrT{7i^vW>v0&XZ;5ujd080kTIhz|`Lg*c&f9*ZN zKI%$;-T}V+Vho%$rx(J1%@R`0fMTP-C$ss(YU0ljne=@KGgigb88fNf@3PObY;#QZ^jz!_JOC9bC z-`s(1_HnUpDrtzAiQZ>{5}}|3k||em5e#^O?5wF_%n~)85=1~-G!D}S;JwObFZrZJ z!Ju|=7M>pzWSWo}-(&(S6G5Dyw2V)&7`%}#WWQig(ly;d*t}>m*-BJ^VN}HDjJ}>F z$uJD)0k&x_9vU$hxP=S+E=Va#JT5-!GV#ynH_W9QWO_dPn+;nOUbTcs3sJTQ7oipE z(tm^K2V@sQYX;Fp2GK>7H|mMoKA`vn$O0+a?699HhzEs6+BpI#a2t`pvU3d1p71UrwG)HsYX&>-L$-D;xjQ+VUPWJyom$p}JERdsJwCn_r!uHA^Xme22em{{pN0@FUQ zwWw4&k8Cc4v_idcGejrccDeV0wyaW`2D;9W9jxEOz|tr9#D=>}u3AxX@{qC15H4a> zR6CCx^D=mp==+)af!1S^(u zADpFla-5U*3o4bQVOmQWwbmgz@nmF5@MM9vv;2n5$hpO@6Gvl8)_wclE>zyZQ8Ead z+$4#nU`)y1zzTqwBK=YerYk5CwX6V1+KMn02UBTs>$w@Wf>L{F(R_9lVd}v7wO1US zUELKL1#(#HMOLOhxe}#wURZfZ@gBqE$W;%=lztF}1|)G5TiD!DSz=dPmDWXF9uzgj zGkyI^;i%Yg=~7=$U)8kwcDv`$MXwb0?dPiK48Ym+S7)_eew@aBQ%&BMb3 z(w768#)R5*FzMj(fJw&~88VjkEzdXIv2@>O-Ie<8L?f*d4Dw3p8qUt`o1n$$4VwFv|V9sDYpeVv}x0CrVCF{=){gUL}dx&!7BE@Au8~r($F!1l*NRz(o3?J zB@L`XjOtNH%ZKX-Xut&ICO7vg5|+izEp|3_slBATw>L63DZ}sKxiF=R+9wg z^#m_p+O=&sgp9%=T`HsiFy%FflrpHc#P{Zu8=KUf$D+FIkKT>+!gyH971KnbU8{BL ztBdbHeUDO5*q>1RP+HLp6ygvBDage4pO%)t*9HIh`~NNbl9y+t?x8p(C=s25h!xnX zNpj1;ViEvhzOtvY@FI%wvILn8;sH>umZ7o`fgA;6)YgCh>3emhKL+z47!-5*WuXWI z%n>|^KLOmaLXH~D*%U_`7Udyw7&hVdu0q38{WVL40hq97$j1-? zRjw)QSz~ORu;?6ACLmCQDldg)muN1H#MlChn=r34rJueIcRF#CzgN9aQW*~`G_U$6 zZ7+AypM_y}Bi7#R7~JJm5In?RT^)?I*LaX-GT&ZTs~28}eSN{Sb`JED5iBaUI9cx2 zm3p2&tSIN?kv%1Jv0RBDcOQO0^SQ(J>M$QN0t5Y$D_=a3GIHYQ(OTFnt zk8Fp9ZyTIdo$lV#YKb4xFdW?2$|%Y3qW&joWRt`fp`s&*|CGw@8AUnKa2{b;aJXQB zYW6CESV>`?!xaHe20T+QlQh@eB%gUNMw|7%z=JZeNI~Zp%}Y+5?0*8Vhe4N+BBEMM zwy=OE_8DOU?gikhyXD$Cg50GfmL8#^%*QcFWrjf1RfNrc0K`PVGq#3?L7JqnE-^F# zHlMAFH6^5wOhV{RE=hP=Ne$dt2(lINlg3K3NH5?ygAq4DYX>KMe>lnD?7+U7#K8NL zG?kB3;By9-$naWLY_dRv7&pFMH;w40bi6aq?nMtsJk34_X%xhHwkpuwggP|fFRP@;bgq3JKt2RNh%WM9rgyyRz#Li zuhwF?OYAZsxqZZi@L;LAUWr(hjt%ojF$yBeA{?o;S@qA!@mW;GZ!Cm{|AlB|RmD!; zP%RLN@%Jjif`~OhXDF=A=H~cms_2(ui4I>naWV=%8^!~3+W{o|6pf`k9rUFl>MG!p zNyl7o6)J_8<6?Qu=VV}@EZ~Wy&Xi~vQ^r*uY4R;8&h^@2;0mWJ-0&PNm!k|lhS14f z48!vr*_6g6q@XaO$0v{xFO#9ZE8sN<%#UdVOfG|dW=Y&IhJ{TQw8RQ6O4u5vAfS?5 zDn0Ge5KshoZj|ZgWc4~nFsl~9=O7w__}S#lt;r%OFpNQSL<>j0_*_KoEr$j*br-Oa z0{hZNd~P_}vMy_-XOszwig*Ao9Gbn0za~)(#+K?7+r}GohQV?nKVgI30*zIbi5ybA zYZrplD6UmvQ6#$ve_3-wMt-Zo#60E_dUUZ~2v>Ci9oQbI3#L9jwwFHOn@y53ThuLD zXqY+FJPq(QyTl9H)HV>^-a3(7YUQ4=C638>=Y&WIZl*l>Btrp9*%tSFF6cd-lv*bh zvC@5CJO1hVCt4-Xaycd#?&4*P-RhP;&tesLo5CQlZB$L|3KYkOx~p^b$aSqwO=_M9 z$3Mg3()%MRdqR4c%<2L-I!20(;w%pX3|y^rOR5%oRmW}OVc6yN;LtkOI{stl9aS-1 z<+SV|v$iEn*%$z(GlZ>b#XJWrKUAes(`W)rx?VxiUIujMVL3KHMkI+{(@Kxig?jq) zr59jH-+C(HoAp>&Ie~$7f1=W@`0?MHD$*i=I>uQE{+kkhqm%%gQ&R)QoTvZtECdfr z7&IgiN7;x)I$4l-1iag&8TP2lh+ul?h_XyAj1V3_b|br*bOKPpj<;S0Mx{}il49Aa zC@Mav9*(Y6Pmn0^q+3A&ha$MB9JLe?U!od$zBEfuJ|}1OtbNt{RV zgiPs==*9?^VL_CNvk+h4IRpI^-jt_wMB>*<_T~1<)55xMu&uI-7)37mwvcRSoiej8b_NZ$^2JfZOS6!LlC9q{0>cP0l6TE9q1C!v;!3t@;OFoRn9>T6@irz zc}GQA9`)^fG&(>Y=66Lw!dTh9qsL@5j>DmlsX|e;=Ek3t;)iDlPsRnX&7Wj>GQx4R zkw@uhcCUr8onme@tkpMBCa%&%&8YN+TtkjI_|8D7s|6+J3Kk}HdfgXtu&%>b zqcf{X6t9zCZ^(JrF-BK?VFYJgcnTTf=MP$_)3tlFC={wNLi5^wcyNwZocL{=hG+~F z)-0`pOh3+AU?e&f%@xMKSsJ{GAzi#Uo2-ECnU4AY}6z3Twf?Z5u8hTc}0^X zmSwP$T-PBQ1B@}$R3YVLI79tC4=#@Kl)=jViiA*&3dZ)YDI0vz2A_#Q3{aZIL5dOO zC4tz)On2gLMX?FH6x(&pom+>w-nFjF`ZJr5HMCi1s{>(#4izC=8{gTSS66^seRdx# z-~vn7dKVF$^==#-q$defTVd`Ef{9}4^2TFKFc3_ydSE*m_=!D}u9KEhDOeWM@VtlV zt*z>w*Z9J^!^jpG3wMG;%mudXu>aM`JmSSR=lVkE4nhQ1(J*?lbIw})f0k~^^kVc& zYxd!%=i`+P)HXc!Z-e%UVvhzh4*ePWeV30s9H8)|ITKv#GU4Aw$reg?v3Q^LY@E3c z*{LwdeMWJAoAm5y&v+rF{NFS7-zNjie0iPK=WhhPWnE?e5FE7KcqJC?md~-}Wq9qh2n-v zvw?ZW@%wZ5{+zxCjdK@-7YIU-^`odUBtKRKJofl(Q}+1pc8@PMWsiSzd!!iDS4ldM z@zFh#61Rm>btqMx-Cm&`wjKswW0kWig}Sd z*hL#d4NAl$=jqSdOP7h@)--uQdj9}glZh1S35H!SKX*XV9biqY8)@V5i^DsSk?gJ50a(W&@0xkR%20?+VhJ8kOpKI8`Zx&d@8ZV4eV7W$iP4u|DUHu3eq zM^}Y`hzyj(=QOb{=w-_kstS^p@KvQjUEp0h#}{K2-*S?LDTcs97yCZu-W7dmVTG$z z03(-aEs@~PqBuxzR!K2Yz}prTSIF+|e3%6Jswk_A#j@%s7_aIGC&j9xU@R+wsIyvL z5sGC^e8evw9gAhvEX=2ko`nib@K(<9U^HI!R@B9^I@p6v0Tzm7%_yDu)8*ir+r`G{K8;Z*ABVskiDy}+L(9ldi1*Xam~p%)-w?BY@V5gdHS zX%J_S9DtK-mZxDbs??gg)H?h=Kx~UtIH{yjUa3-GHe>uib!(n;5Kb9+7H*;Yy8uZ8 zqG1e!Oh%(I#)(YKD3PtGP(v(V&M%{^F1|>q4`KkW(kt%Qn$Atz8-=4Jy%8!}0euWd zQ7%=Kd+4OUt|#0ql&K`ZHF>1ctefLB$rJfXDkWoLWmN;Fu2b_hZ1yOHxB|gQ78p68 zF8XJ3X?;$b6vJ9G9hV05>rT ziXNz@ZcTmkDX;dzlpDFh&t{fPQp|y-8fmj)1O)1M6uNdtsABPMqODB9q{yQvuU6#S zJtjS@)M2bz`JG3l7(J_6_54>t90c!!ur9Ttx}<+r#eCl-nA7a#j6%)!V^Ep8P?Z;ou z#<>O`vc}d|E)1WQ%3CdKdqL)8%ppCY9=aS@o0E!l$*{hOKho+#dY@F^0Q}sM)qGGE zc^58-O&B2e;9B0+Wtck%@}Mqy4#wggQSPSUChAIx%p{Ye++Y_pUByZ`1F+Z#RJCFZ zF|xHOP~KD3J+u@-*km=9$P=+3^&eTl4aQjDR4)_}*&a|7$%{F})g`=}DJvbLV`VC5t3n1{1c{HR?;~))2;>+S! zxpkZIjj=gi6u(yhK@DZYb&3^EWeu4!SA%5KoN$v6m7uaJmhq}AD?n(nWEeJw$wjr- zjNqvr!gnfXL^w*Bj&c#+MAqLRvB6Jn5vJRcBE?#E54;a_tIKige;B-4aUhfcs&a9062hw4Zcu^s%s1Zf5nFO zz$IH5xYQQdfT^!~wasW8eEHRYFDWS$XK?q`;n69^a&j(CvoL=b?q$zcgJ){E>A zof${d9*jezC$|G8d_taEz*M?c(RSg`*t*hg>l3HZ-W)|kWaLrN0t*N_SVPDzs zY;Usz#R2%<%C8LRG^r5xF_dD^jd;Np=`Wn)bne~n$p-%I53($}h{?5WQH;rO7-Bxj z5Zlz2*`-dy4Fd41z*8K7GIkYFd>N(?SUF8b?p~{QhwKOds$zq(9oOrjLlLTDMoRQ;~)jK{~)loyj?Nq_P7>6izFOKPR6k115%>!lVwTad>qK{w8UdWr!dt=(JBk zzCDE>r@?qUyg8kl_W>}7;23f}o%G>%o`|b77|-Tp3csQDn5mAM?kP{y2ZL4NKmK=u zD);aN#eN`8DTwzey^x>`1;8cgCu9Qh2Ebs?n&K!lnS3ExpYke>`#fcy^yYmSNK|FyQ=~uM^B(urC&xgSUv0!mZ z%lva1j8DBdV2RNu651|yM>Dc!V`S!sJdpsqq|7X*K&gZ_Qywmmun_1@PV=#|t(xj2 zwz1<5I@dN-#tjOfOYOkZ=dn^%qID4b3-&ySn(6~nn#n$ZU~tq`CFFB6rJ%7-@Vc4_r#!cB*#kJCadk(k5n*1t_kNomGzq=&fR8nyFwhFt=|+eFCU}s%i=6KvdZXt(?Bn>3?iNYA9QhI=f@~)HW0;6xpLa z7DsK`a>8l}tYr4md2Wih;RqYhiBX;cRjjdk%jNatS!To!NBAgjP!K~LA!Lr0jyU38 zo!D7umZlWVv9iMOX?h_FfDLDZM%xUMy~i@+J7qIG9VUYyyF8U0uuC#g%KHFp!c+z! z|5*VUrNN&IPl_m$CpMf*k-9Czl_7#uQwTpOsWj5n>4pquO_!KmKBIup<23mk4R}p> z=uRMlHRj2YgTg&TRhU6vF5VYM@Y0Ed-9szmG#rDCG~Vf9rB#pKe&pUFa>;pt6GtDl6a0#WwSCF0RI`X$$4MHb-x`)rY zse;sPn+EYksB3B5uQJ#gHXM-9y6%LTUDrmwddMZ-H4EvKeT>yst%FaQTrGJt%Q)Xl zOJ+{5*+iVE$tSv2mrap4@*8CEe{a0n0o~CoM(-dI!G{^71GDGFP zEpZ6$&QUoJNP||IgmLt+kP438SwPc6&-GqXXD=6on}Y<0?;M z@POUbJxGNTDS>QDnX1f`F$LO}`vLa8*f;;v{ZF#Lb%=-+k(ol+u9}&D#up|P=fjE> zE7lRO>%hi8J4h7>>`2%*i!xA`!CPukE8w9vK2E@6>sOSe44?HE=4G-3g7tjB__PcI z@Qfnk9J0nHh%m&+tDV1}?i?NM{Nr?g@8G95KdUYE<&*Ee{O;>-o_zOJ4O?&${(h~O zT1z3xu7O#Eq{u;rrf3b0H6ln3&HY+&5Cx03T8#u?fxaXgP;TXZ`1|!Q%*vAL8>Dk~ z$~?H!Q?+GOIZS3qI>wlJGLSjrIK$KpnRp%pGl?6sHu9W{!Q+c4s%owX-$lCs@R;Xs zfh_qxj;=%tCg@0#&$CDk#cYQr#*&chS=a&OCrUfAO~Jai6$Ar+v!-fC3h1`v(pSqs z-2!*0Dh-Xqby%a9~{|p_k-q6`gia~%T6pBK7F1=0mEXWL4bz(rI*N9Z6?08%;DW(Nnq20HfQQAMuE z%$A){oq=ab4UeV@kAS9HOIh)uB*RqiPPz;29_ffUONfZks#533ytjb5yaboHJ1Ukr7(2~~?fILcr;FWK9F)q<^7q9pZvcr_Kyapsk zcP5j8+9SfpxMAg^?b54dHUUl0Az)fcxB6K~8Eg#GA3V?vJ9G=sikQFd5F!`}1kCIy&j0H!S1{eN9pWZw=J~r z+=?eyeCuQ|sIOe4cQBviP~FGj?3(x_G`<$K??o~%vBepaQJs$CBZ9^U4hJ<#bi7wI zT@Huq5WpZ7!e6mwWKciSx4XnGSa``3k!NTTi=ZeyY$0+CYH=XMFE8^<4b!EsuA$Rw z74JrqG@)yean5i>{Z5Ud07CRPzzDRrif$(s9P6qjFyZuFC>0oa{f%`JKs^efzF0}Y zt&-yS4)z%|U6z=mUg_dBT~8pfma>VAqO`U!hGcY6ngw=S%jA3YG(uBbq6d_C_GH{*NJm?O8YE#Y(&o z?Q)$bOSUT>S)g8#(1_$a^qfRj#=y;y$P&!^(#a$u*e5YMl(7cWqc8zW5bOb$<^olE zy)Lq-$l~aIWF_lt_N5+!3hy+$Hr=XO263}NgNA4#iU>U~(kZ;nQNSOu{);;=Gdk^v z>JnmlYqcmc4^oFZig(N2*Cs-WDQ#78hNpiKc0-uHr-?3aS+)0#QfppIEu8NejhA+9 ztmY^$0*!xYRP+Xj8bUA6wVs1sY8L#V05AL$MB6h=pGyRw|O`LIUgK*Jy&c>PmSUm=aG@HYK;_-Pij>b#t##Exynu8|VrMa+g zUigGC)A}z7=K04jKw-lV;y2S*G9D7L$M5|x$Ksb!IEKG*5`(x9J|~*_PkfML!5*6et*4bEILm!9N`%*7X96sO!VhpBkW}q<^>HHejsYck9qP=H#JJ9 z)7alG3y-5T0Tyr0-#|BW`aCIqq;|5*Ntk7RQJ40&UYiO4IC&ot#-T6WG^o(>O6XezzTZXHwmtC1$B8^TKBv=g_E*lEhBJ%+ z#XpMCrSqw2Q{5!^T8MwguJEolA9}oW^6b~)Iey0<2=tTaLcnTLT!KP#OAS*#1Mruo zm@??4**MBL={*4|{6#$_ZU%nuy^k_e2!iDy_9n#!@o6>{f9vg-#Upbb;hVafP57+M z%p%xtn%8VjPzrx{oKL$8oS+v7e-`p?K@}16VMdrmA+3$Zs`|(8iJ+-j+YFmt~R^I67-e+lx|*nJrS9~K0C;OC+!IO#H*<6p+> zx}nq@IQ}H~g&#DNB7Hpxj%?Xr3CIC%n!0LDR!ey(;r0{!RZi*}vIb zTu0rOp#?1lt_qi_}% z7;XoDMFcXVNjRO+N1E8X3Vvt|Zhq*gVuJa>_u!k9@3whnZUr7AXT_lJhZjmXy3~Ko zsfv>PqRH9GJiUU>paZ}VDQ7{mDn$naY3j5g8#=QxeN^mUWN>RhOA@Dd%9nc zPS5EZedp5Wk;YYY1DTr(>>K)^iygx8R534fX!HAmMv^!WlVX>LqQ59!y-q=+tbkEF6@8`L*%{tB*1@o76b z2oLlJa5MOuPD7j@;KiYvJHQty<=7)F`C#gy?$!`$+Hh~=I zCwLngJLosg*((MWfFInDejdGx&cZW&;EG0q)XRt=MMH%-nbF)a-U{OK>!a|z$Qp?TQM!tov*=vg>E zF2YPx1vxCBcmZZ%GES#-&jJg@-5YjxmB5e5G^hBAX{Klif5aq7Q|=rrmcX zB22!P6VTw{Ma!9%$?-vMu!;D|7d9pcjKgiY$y^rZYdU}*1}Eml{LCC)`0)Z%k4$UD z{LI21{IYNbzushXYIA;8Wc-%N=TrV;HxjOn37=wAk)bQyp2i{-U|gi&2a3MjlUStt z08^A@Pf?M22SIy2qwlvUF+rLko|>kzc*+m~p9Um~Yz}WRy3!MUSrCaZk6w=Hs-I~n zXEFg245VwmAKu9jT_U$P@(-vZa9X3GW^{~>qu*8(9s_GdFpagSWmU%=C1JMLfUn|Y zT}k~m53?vpNQ(2=igDmaLV8aN$KR4>k8Y{c;AEqvp0pI(!avqpQhIb@Qex#2kCu`; zbRdJ%PBz-Eu3mlF4)#f&JeK;2 zqK$CW3`x1#-U>RCVn$InB8k51q-~BKZn>nC0+kgxtV)36R;5a1h1CoiLLAqq@Y3Rl z4&QqLv0XuKS>$KRdn$~EV+i!Eo?Tv{yg2c62OO1KM-b$f^i&BZT1mG;ayRi5c<(2( zvx;q=r!PpArL=}jxuni9r6&~kNeF zeI5A%OFr0A4dcBLG$0-Xrk~i+F&4TkzHrmksF$#?coprz%f=H*! z@uBtL0T%h<3uT&ygp>6_y^A`tlx6iuiZY6B%d7zL>{^&vpu7jgWq66@vk>Jw(|9t8 z^JtVN($MYa!#Ah9hi?zws4eyF`Z{k1q#nQCIRJI)uUGJG_j%0i4$>>NrJhsFhO1Tl zT{I>W;@>Oq>qTnxWEOAUVcM-f34{!sUtLBC$@oZAr-o)F&><3foj{@rjZ^f3kbePZ zDL?6y0{01mF0AgM0;h|7g<2mKSmbbC_~#H0G)4|FxeG53tF?-kX_-Ys){r*8S<#|LQ<^&US~ zFV8)v8fVcc1=zm?zmst%>Y%h+yB1TEv>*x@`zFT#Q5L5ZhAf^&Epj;nkqNSRrs5)3 zXIT`!17R!Z9RQorNGL<=&eNraArA!B>LSd}!iz}gQK%V2M8m`Rw{NxcuE*-+Z{Mmo z&*zbPvi{BbAkaRB&gK_+XLOmR(|A5bCtkDe`)|8gH1CpkneBMI1+|(qh$`1J|25n~ z<_4k+CJ^9MX{FSb>h}9L{eIH#cl-Uvs^33x zlsJOUTC?9DsDA%(S36RQjzMfIT4-&nFI3lo=)Dj49L~+FinoRW4xk`i>x-VH<7++6 zJ9bX1t_zh>{w!G)E;Bav##E*6F4l+@Km~H9Wj0!#_%m?U)uAe=_%{7L*%+`Za(5je zTHncFnI5=k{!?~-m(qFXgY91Z=6BY3>-+s~bGzS9w%tvBndDI>_ISt(IwXX{BT#Ho zU!*fcIsV>%%AQZtaeRJVv*jA(po;z`IEk`i6{`qr9n@HOqf2Bowp0d{mhP^~gj55a z(G6y=o)h1InZib4gDx3sxQ;-AYqM`MrQ^m=Oc78LW({NYOl_!b)%Zd+R1d=-tW}hk z&4|$g8`QS*1lnm)JF4LTJ1>=vGU>ovOEm!1bRR1h&Kh@xbx5W2`M~-A94uFhTBv_vwfgKaKl08ZWhhW5<$D^h5{g;!SzmZ-F}4IL=8&L ztaYlPVM{ZXOXSZ%D>>icF8n@@z~5*d_MhPl+__0gRp@qLe{g14tm*#o-oW(?!FtGB z?PZR^X*BN1aI15{h8`;b6m3uggQ8>b-Zi3RK8_Hc9kEzr`AV5rN#S#(}Qi>JmY5>BfB-}Z*(uT|j(KJ8Ir>!Cby`z_X+~|U z4b_9#)$R#!N?;T1w(60=40O(C6{;z(1yu)o&oJwZ%qB>?tJYNXaTfa}nlR zqT+cL=WOso%8AT89d2!HeSX(g75h+fXZ5j&;}1CTv}ml_q)$B8GUKYa+U%Iys_vG% z|BdDvJ!|0kducdgFll4OR zfUAA+#S-vbZ>nd@aG5hc3n7L;_W)r)p1DJsR|rfrAp?q5(^lJPXd_7#s!+8MKGI6E&D^MNw20SLidwUa4hb z=y{c}8=LS5i53?%!BZH^S$>BB!IlVG7si>RilW2YHcmSr?16l%;Q=IGVB@Y}=Ei_D zI$d;Y215u}P=I>k7=XG$nbMI_>B68Y9@Jm8rNwXr2d)&|lOBjn%NqBER$jGIz9klG zIih(5H?%ZtuTClde*97Ge+S{$M3Z8XEEF)hW+K3guLW?2e3)BXK{{+XHPX?~>YP_I zSgdD%c|x{7@N8D_a~&5)&a;dlOK-XM*3+^B_s=PEgLR1Lz|IeS^f_`{3rV)wdI5JC zQB*5U*zq*$51KbE8K6`vSNdngWXMBOp_`TadRcB)<2q;B~F-(yC_$E9$-0@knGH6Qrc3GhJzUZ@Lv`5bBxVZhYQlwnJ*Z%gcKtF-Xi>9xE0Rgee;wNZ!Tf>6VGS;3lz+uXF)Tdt{zSr@*UIVh-C zb0HiFO5Ec_OYq1ShceQwI;+*ZBhVqA>YCsf7M=N6{qT*G8Mj2>{-4SGQIb^=Dl|YN3|HC%g}{IGuyohzwPY`bh2uOgP?ov3L6N;LYAo zdq-+ZeYd{;&Bk}%J^AX(Z@ygr?z@c=5yo~k^8|bqp>Hj;du2I}RCs-vzl}-P_19p# zw|;xF(f)4G?~flhA9d^OysWq@gcjp5J$Bm|H3~1kPZqoRHy1Jfw*0lAy9`ZhHTNFf zv(c#RQ0^_qbDC(~Wh+M9YUV#W;u#EEOvZz3@MZPz3=dR)7$#_G%X`WZ36=SwgfRl@ zYD+a};lPwk-!7B~hGzNo^emk;fP0|#zf(lHRwryWO?z06_6Jz1P=kK`i`sy0)@_Or zOD(Yj6JlOcvb)pQt#J%mC1K)J^9K17La4=w385Az&ha>k&?}Kve*y1(X@)c_Qk)SB z0pe(^$wRQ(%7}=LNKmE1!Z##B(sw}%I12lx<<&VtG*S^H_d>ONuH|u zcqw-ZzcN~eO43))TUSUfBE}pZ#e84YBJR{E7ExnPG_!1fsfxwK3mFzCfgTc#-RnkJ;!7EeYBK#CCm2~ z$q_M>@uc#ew@2mm{?6{+&xiZZ@v3T^p3IPP z;{k~pp37GeD3)GUvHLvx7#OeOm)#wki}n5Q8|km^UFpI*!|Kb!z;^_er0jalMV)c)~^qb%}?p3 zB;%J-tQt%erR3U{3d>Ae2O`9KCDXL{FDrtr7opaON$c25odoA*Ym#t=W!-^9D~*4gC+aMyNv~y7ISkXCDt76P&n%7eALC?PE@%*;SMsmX zbxmQ797Tm)6EW9T2z-orTa77Y?A;fWcvkM!AV^b^FFrd`m!E1cK@;Z!c zS;P=c+~!^OCE#>t~@(+-DxZ0EoVJ8 zE7lrVB3$}==k)2-cf=4S(!FLu4O!|+SL{Z4$U`C)8|kJ}=Hh~@%NZ}D67YM1v(weQ zg+hkcC7m=9A2TM|)*Y%Ieu;k16Yp?}2GDoXfGzy2o0WIL*-tCJcs!CucKX{TO?%iXHZwWRdQ&bEMnOE&p`=@jJvMt&r395 zbSeYPfw{(>5;nU)X9W*D293_Ljy6hVntCwX>SUn5dn;XjHP>$$=hxo3pZ(%2OYZg6 zX{eT}FwXu3T5)kA?*0hmzfJJlQz9m-=Jz6QKTqhs|1`wDYh}NmAFuzJ44luwGbu_u z0^^iVvrEWmSG?4T6A+E{W76C-H)B>iW=WX|Qb zwq~dCy%@QF+nG`WF(;AgB3^FX&ZB&NnyMr>u$SoCn%S8_C3S7lZ{0KKlQ1#^?4K=u zxEI~);_p9FmiS*w2YGcPss9Z64X+@g6YC!M3+V*6juQxP4aq{)3u?pNU}}(e_S_A= zXnk|fc_aDG4Ly|q2GK_jtl&Jxs5H?CJZKu5Fw{gEo_2I7w$qM|fyRC`bQmb|fQ!L5 zO-RNpx(KuJB+8M)EFx{9W`hZ1s{4SjLeMM+)a+1@CYBN_VHU|q1A=|NL&cEGh}s0% zHreDCm(eviCZ|B4*fS`MOySgrprJ7XzC%*z>Qi1<@Q7@n`seftm=i5^1?Il*bQDw- zeSmy3fA-12Pnqsip(;vzeV zu57kZ%@wsIA3U8-(}W^c>q_6NRdir=qDCT3p@fRcuZPXCIS0TE67FUAU_>DBA}^e6GUxVLSn*j_E+G zhEH6YUT|ffh=BJ`G(FPog1s8K2-j>P6_U23gH~PGy{)Hc*-E?e`g5kZ%h#|G8XNjl zgR0gD;Xywg7_Dg6fdxt}tU^7`_NksB{}QyG+IEBsR;<7(n%v!&1Mn#;<5pl(UwTb> zYzpqKruK;#$9lohs2Sy`mOFFfz?C2VMLC^U46x_0BOHP%=WwM`d2YR{T%c$rY%%XZi zX^_jc1mamTI`Zg$j**??TqS8?-n?d**y4AlXLoGhi39&#{elw<1Fmcerme|h?1aNN z*oN5Zn6EwOa&py=8KqUfPs{~d=ZV?GeZRVlizuIkBaGS$KC$HsAcI(uIv+hU=dNBl zwt99AqzJ}t(iEN!-UK~@BaD(K6!7S)k~C@C0g&q>JqRLeSMqIWTrgWVh5T7>vv8-Y zmWlz274j(>nC~=#2xg=x>bSs^ntzm0>0?N{2}8|ryY8eN&h=mfI|UU5UGW?FYSkKb z)iB`=(Y{)}4DUhd(KA#Yu$VJ4MNcR6d-^GR`ICK zlhwOa<&_6!5>$COr8MbyC%NcgK-XZsRSO_%YNIKwWG(Mc3gD zq*Qp@fn=fDf2b{Pq&r+r6~&IxYQZl?sTE9yk1(`3zz{L(s>{JcDhz7Q!m(l%`pf7; zIF3f~G@R&duClKAF&zJl5Z(Rtq`fv+|8TP2{w{2v57r)a#n;^}ybQIL(Vxz%mj|bR+u47+=hXQsYCnDQWmUD8$vFf_ympG~#HtF7r=2H_ssTB7 zi7xF&nv*t6(#~j!vvF(YNM|+hJd<#ZgPjAvJfQ%;JR0e7cr7^`3Ze(@y_{HD+iq^) zle(JbG~IEetZhYK5&)|FPyX(Nkhmw z3>pJS!!2p87?{$xxAurTW?uT9kY|(f_|ACJY}rn?)f3fI-e zgJ}>GL;$C_C|;+29u)ub;Kj>>mv8<7JXgT;e7k_Xowcsq=zR0tH(!7EbmQrl-+uk% z>Bd*zL~Bpizg+=Y;jxQ84rgsV`BMp=&NS^ ztLm?a&A7CW%^tYV%B81uy?jtTEa^U0u>k{ajuA^+C%du8;tQ0SX>&^GybYdxH@q+h zSn#og*^=Odsg@VF{W7c%jgq=<8fRFFrLYvV|a zB47>Qn5e%L|EkyRUR_;vqR}*Lvk-}F7cdSMCO`T5N%x!1diO7RG-?+q525*I(GVYX z9OXJypGRJX@`0=@we&o$S62Dxv#1-WIE>|^s0;fJA&o_ucUpzX83fPMT6tLbUs#jF zv)Mwx=g(~&bq^|T>(sVrt;dPYJ5C1={A0u2k-V051wy(~jpqdcc*d?jk(#oL43)4ijk!=uwT zdw++ZqK&-|z{Hxx^aYe zM{@}{hs^MvTpMw)!>j+#VA{s9)YXP+8yUAwdt%f68eN;@FDaRX;4HYczDS4UG{Y4M z>8jt#s+_p-wMe8UIOi!3z>%*j_4SudvaGW4gnJ-CG9^CwihD2uXE1f7B=C$n{F7|= z&Z06v{EuI-Pf@W0qym4Rx>@bF$8T5&_bj4JDyV~0=FT|i_0v%!s)%ao<(?J z1m3OSd83;6x^%+pmljOx*F;rR!?H6h2T$KF_u=Kp2(2b}g*ymhB;fyBqJY}Qw4O!7 zfu-5P?79j5wKu5&3S!28ON~%mE?HUAmSt^j>xIo-BE3l7-d4=MN>Hp-+6+`#^~6ip zsoSNo;=kV_ije7bwuL&By43Z&m)Ej;T7PQqAp0pB)<+O4t8-Y6Zx>o;uq0grnoj<1;Qf%GrdqECuQI=JS+?2 zqB*Ue&wy^uDGdWW&)?rop|;C(d|-5nZk4akQq~J`o>um$QG=r-npdb5!V zfvo4p0lx@{_@`W2LLd_%uDf0`&`l{oqMR2-Q^V@1+Fn30lH6rPQDU?=T0mkO2ShbP zEzq4dupyvx8V%`IT1`>wkgJL~(~7IYwHRkHWK(;rT9VJt&*M?-#1KbfmFf77<9CTw zgh(xQ9G!>r36aFZQw&}`O_OvI7nku!oz3IP7;jgjN;_v3edy4?u=mEOqvBar%^Mu2 zQ4X4SIV*AWAF=`RLuia*fu`qWrPX@|FD~=@UdWfwism=8l1r%UEwMezC~mCWRyQP- zTBwtYd6?9N88H6o$)>7<9 z^RZ=CpK!*2*n*2qr;(BI1HmvDgl8ieZ<+Fgjb`wq89a4kEpN2cla_iqD3@-jdzNmg zyGny59}gDU2m-A6JtxL`3hf0?>YH?)MdC6*qvUD2fQEqdu!|8kbf7>EtZBE(5 z>gtk_xhawA+Nr2E3yVumhEJQ3&%!GXklw)W_6#m3F`ei`oEN&yPNzGG&$^T*rVPwa7KKVstTMZYt$C6D0>Y1Ra-&kmTJAm zO(lSE63YC^MEN=S}+bF1fXnx2eaw%D4j78p3J5@ldJGL-V>IC^)O>`iBIf$4-M+Gui+F&nDyN*6Ye%yeTyPh$7veg7nTri~s&`LF6^lg&ZLNFy-WQHeNYY8HMWmKoHnjlCt z7AY+{L)o(yXvEnK$^j6@x{ihnTpy#3Aj@=Q|K@?(?V>7k#vrP4gi%@%G^yp;p?MdW zilIUbCkA1j&!qT$HY0Cj=9 z*X;tN%-&seM(MOmYcZ#A)R>cvtPcryQo;&&0&fq}pFQ#??p87CYnDPKDw!FYSHbTh8hfq zgPz=ldU$OaJ<2F~ZzcE(*VC$?4Z*UZpDg@iZvnl0<~yI~%$vim=Pk~9u*wPUkwM5U z;{kD1KGVo{>A}zp%Ntd7D3&j{+mgHLdbNAJxYF6O2$T>aS|xLXbQDfhht5RjdOATa zhB=8HdX)2PoER0OVvdqA^xRL}4QNTUp^(%hI@CVM_Yl*QC_IoJOe&auF_=U zL^Q)UG$5yOoE=}|i<@kJp&(#6C=da=EKg|!aRH`!h6@oV*n{z!%ve6mp)nZ`;&lk% zyPu}-UZmMSa0YlilEgUgX_~J^fQuvQso)gWP=g5~^w;#5;5*@D@C+WxaOGl^RzGR> zyfV&ZZ7&4wxpOf|&%#MlpBBOFhLQxr<2Z{(MVej1Gyr(Rt1xyce%)S6kG|R5?l|=f zkJ(AB>zGqv{+)lOVErU(wNyjE0U$vA@Oyc#SblB~INtAZ?s&%fOu0deL5^vn<+)GV#-3$(?vy9 zJ&EeUsjZ5kZgV?)(g8yJakNxpUYxhTMSb!{qiRs5X)(h3mh~`~HcY#XgXqau%NFuD zI+HXU%YioGXxL&^Z9%tpGzCRUEs^Y30B|k!49p(7zdD_c0be_XA1N5QJ;pVF7iC>gveeG(P)Yiu? zP-)1PFmHasV%^=WT{z(7izl5~26G{_6!GOnq5L{2!Vk2(P^`$Vt$4O{9<9+daHQ>Olu$E7 zefU4m2=y3A-T<4Q%l)evX`|my&@;@~871A_K;2ElqSv@{cowFs2d1A1x|nz?Aeo4a zCiSn!hX;!IvTzrZ?hfjp(?H_)k%~~~6I`H#@noJwmefSilL{wb&~|aj9;$Mc$J2O% z%J4|q9=v`<)@%Qor%BKhZo48CBDYBowzwq0ASyqO(qxoHMbyS1OyFdtg|u&yUR4mg z$j4Vo9nOo{yr9R=5Z8AS=Y?vwf#E!~`e`}&XqQq2Gsb;b50(TQD%Da0H`Z`3j&hBU z4dd>aT0C=1OhHP-bpvQMoxG2L90Q=S+zdmME^m7ajfZ6g95!5nhNa%Yq@XEObMh$b zq)E^K#!{oD#-dw9E=Yu7Iox_=K#A=5A_32K1PM@}rJ~M7NA(~6EzaYjl5G$r-Elg~ zyQ4|m#XW${8>geWwG$4{zz~wTFw<$6j63~@^36Z)aCELM)qrl1Zh0{7pa2}1YC-o` z975N9{CB%Nn^`z|2dc`XN}G%|b5u*F5m3yO&9stq98J^Y2as`o*?))(AG9!yQ4-Go zXai1KYWD8p0NHx8cNhGeMHdLpK>H7aAGSM>n?D4d$Ibr3E_iQ2-=4NXx%nsD=)hiO z>=0YCcWq~m_imk{kg<&dKT@ks!xVsH!83{;1)rT~{1Da8@>GLP5U@c06-br%|<0df8kC*$-gFQF}PV<&%~ zKx_Q`*t%H^oV;8JJ!~d3Zy1+(FLbN|)MP+n(F?==2JcGz)J6yq9rvVY2ha@`$V+0A zUX{4TE$^1}%9LL8&dch2KAEsbz7s=U1!9=|!Uy-b4fDN!}@}M4d2UN2t3fo2AL_O3C z2(}B_)j6-iK(bA4R9=h$aWBk65O+HmO~);>ajgoFX*pogbO;q{(~5m`m{g@tXPfyP zBP;%<9R(Zo5FF1TD*_L#f6#cvo%7^!iUyT=)I;S_tLBOE&|YJ`Wh@1A41v{H!M~!O zeLG98-O*UoD=o-BcdL4Ottz+79H5qJjN_~^uo47>*C(BI^GpBCNNixo;s{>OJ6Td^PYO~^6xz& zC`WVEzA!pdS84VRU3fj}2YfEs)icg!@FVRJCFODs zJ$3hk&P>gFBbqf3ZE7A+zC^h|Z3!(Q+f%DTn){3w5xVNgacbQH(kFl?OcSO8Y6E&l z2EB>~VKx#?cDrhxOaV_sQRY(R=)zRew0V)uN5veSt&$WJdPkAldD_uWD{$!uzl~;! z&M{cn?gJx1vuHx^;}>)$A4lqf!4)dH(sw3pIGD=W8JScPiq2J)bwAvFrH02K# zM)IlRb5HL)&}oPgU9e@K8ctf1>!A@amsqKGi-D{ptT2LurqJQhGOWCK6T;c6oGkF8 zI$WmdRlOj+@M39QUC6IIxx4D5vf?yO(5HKSJuKl@OQNg0`lBL##Tn8h%j#hXzgiwm zNnN169u;Zcb$gVL6}uAFxm;Mv;sAEo33&{&R=fw|?ZuBw&^LXYhRAV4p zUH(R*$R~GB#Csd5ZAIY9R}e5F!y+`gb9$-O;#oj7pbut4^eBWDN){Wc(Nv2jvT{fm zfwyPbBByqRKyPu?g=)9Y<7hI@8~-7TW(iAw?fkKP{5twA(VAK^^JXDcFspDylw2SW zp7^AsnkFRue+I(PL%0(|>~TSZ7|2Gkj2s;ORmrHVrce8|$OAQOeNvRAeH}X@_l|x4 zahIzxnnLxQ_}nZuNY&B|oQWnNx;cxAtBBo017!pR$$WN^h45-xVPh2j#}?|GE{nP( z0vb}F<{tkZN25td8hM~yiUvd(jI1de(F4-0y!8gSVl!sRNRq!Z9>*N}xJqS&Ew(c4 z^qZm^pihhIyojcvk_(DEicJcrh4cC3Jf2L*VWO6DvuC<0(Bwtj!F}50ZCVLP-L>vX z#cs^^ma$x51%S3vdIPyt171!idIR}L!cnu&%ri||fvj$hu(~?`=rWwVGdl11OLNlI zYwQgoEdV<2kRy3K6U;OMU(|`(-G2$uA=o>u71;sPHJp-cqE_*Y-PJgIZf>|qN=l(~ z5|u1^LUCBy@L>tI?L&Dw&ZCo&)-R0s>N1^Z85wukF-@T*Udp!>dCdfyK6RbWGnHN? zG!k}Ss|V(4;4cJNB@nHm%rzWM;yST2&2-xiJK!;b{tJJ$h*xGrLAoL4v3!V&fV5*> zGQoO$ppDQCR~Q~x)~($*34Y5BjP7Sc@|unk=>C+;i_D5TFF_z!R|hhM`H`53fpM8= zKz%GSR$IM1r!bCvlVoLL|<^2aT2u*o)$LYC{)1*FjOu%A}|h>9`uGQXdPuhiP(?u z-hvNTpeUNEK8xjDa*yGCj@2K1FWukPumT44?NrWk6dMdd&Q1g|<`q0a=cpZ|hH%p# zMHhP?WzdyKH47wMn zDA(^l`l8VUD0_1mChuTKVD)zRKFZD~>D2@8NzLyY9~{Li2i4-mlS`R!Sk61`-Nkbq zZcAQztRocWYF?awtBUAD;YgV$V}}iJI2pT)Kg}lT(Iac&JhGxIr}9B`wMu2ZS1?o( zG>)CQRgHEWkv;)z*D(;Gd*|oT2!+Jyl++9=lu7vU!2pV$j}Yh5T2(!$dKCsf&V#D9 zKm{4!G{}%FR8^deCUdx$-$z*%k3oT$)L)Zp`x19nkY$>r189oGiD)9s)m1c^bUs2%6RFc5bM==+;7H3kONvfB!y9TJ7;XvD{NvkxoGwu=8t=5$qF323!>Oi4L!Pg&4*)4Z@oFpPH!!P;jQ{tx2q!}(a_7= zbgj>)n2J(T!B*F4cFxmmFC1M4c$>NEj5Pb`!wfWXa13hLe@Lcc{fB^8^2a^;gUjF` zEr#BcaWos?S{RA(J(>;r4{vXD51mq<&HHwmRcxUhh}|Ue0?P}pzeK1K+8S+;Ji3f0 zV^o_k^LTWFiE0~?dWwH8hM)u2Q;o%=8@&TK)F7#2 zN5lT`%*Gi+`w!jj06-8t5L_N_|8Vka|KZ?q|6$ir?Hrct^%U$}!i;x!e6Ts9escw@ zQZw#qnyGvVtp0kx+aCurpZFTj?_@$@D?iQ?TZ~tyJ*DNzyJz#nD}P}9FI1E#j|{T> z>ttz#S-A#End@t?Ch31lI`jVgkNx?2|`9KEj7dMG*f5u z#Ixy!zI|n3))B7ct>S^Td`jZ2I;uuhilE45U8azRMJV* znWPs1aYWC+`pPO^Iasy@`b4M>YXyJJvbLq14_o*c)%}vj((SEbtd6e6fmU6LlJ`AF z_ep5DwFHIp=J5GpPyKRvO`4gzFV$HVh3{}{wX)iFl?s(RGmJ3bKpcjORl6^HsD29(5}BxhLyG5i!gAs)%;c4GOK3g%jUAMg5PDe`4+HhjzSz`RUDVKKv$b3 z4z|sGC{ugp)BfAw|B$Aa)mSpHZpqi--XzN9qJx{^# zy`HOm7Z2Mqrjt~)+cSY^)66Dls?gD{7G-a5{Z&Q=oR29YBV%Hnf7u4i*y#}EpP()s zPA2J<_JG%Jk8#c&^^bH;mH`~F988Wt4Veq{AEJ@I^-w_dU6BEp#j@iDKJe~yS8oFv z_;>VyT_V)6*2Xit-aSbZtS%1_bcB`()ZCcDGEz3!s1pOY8`7v?|CgItHeUfo#?nId zAHvG@AF5!UU{w21jplg)iW#t?TuM4$Fsern%tIcx@(q+nrDr?!4!k$mrehmJf!zge z2V@6ghQ$Lgent#DjoDR0sVKjz`wz7($wSMgJtr}w!|Ezhh|T8UX9cha#(^D2NfDo4)9r@lyk8?cdaHkwb+`y=Y7!miOIHBV_wHi_qmIhcjxA**bj^nk2tsU>I&X)uGY z=oA>zGq9!zXY&M1tKY$KK)nztcm^5`GS%7Kid%p?1cq-#l*lq?I2Vjy3GQMGRwE)o zoXU%6rp}^CdZo{~?6hajUmKF?^r%}lgM%de1-Bj*KnIVIq{*2gox4yt2w})LpMovS=Z} z-7GVX^z#w}F(cacHYgFSLk;pFo6$;(8X3(C-(TD+A8fs-<+0K{i2pK{47{Kwd{0 zWm!{EXqR0vHWwBP^|{%VopvLco8E}Cs(Sp;F?xpI9 zcPE|@o(>z?9JRioy~aQ=lpVJOt6>u9kDw);HO{50f}2X7kttAthUB^pkLC#m2q1iP z7M+9h3L+fiPl6-g1TL7*CG52rFa#Rt?oG(fjTOP4wadwOdqnQ3z4{>W=R^3wsAN$ zcH$`O)LJM|J0;GQ+gH-``myRji&1bPu_Xz##4O<>9J)UjH{tG(z+9NFy}k_r6TUR)rJaTytehE5oZ zl+Sbv-;R`z;eWXRYyrM8nG1!^$z6p8m4F|p2|m|Vnn6$j;0#nL+8Eb3)G3?h&>NuH zvq??ILX>f3l(07?v)wC`vkK>0}%hF^vcOr^51^$!JPE*G{VSB70a2Z$)}L zk;@XlZ1ok1n7?8W0JNG?bVhM!;|7aXATQ8LFq;`P{`dd-e;dt(dSfv4&{SiyZ7Ei^ z3`a|%YK0~5!E7iFtFfdh+T*co1hz_oynxJ z@E*`wiw3~R`YG_%2w+`nL-VCnc_GI~lbX$nxxHjA9su%LhMo!K z4D4>9f)LOXu0)|_FMYn71i_{(8GtVrqyt=5vSG+H<)cNn+4RX@rzMx7Fm;xZ&p?n& zy^3JBIGSg9{61n#4!7bc(t@m}_o~u&C|VMS*1U-*Fp8%W66YJw6VsPK;)Qwf+Nk66 z#GM6hy9b#C0(-Vg>9sD9w?Q9xCF?Uf zx(*YkUF8km(E@5NG`+|hn9?1A5h1_@5-{`8H?Q1OXFv0faSA>M9Uo#C6SrK_5qkTW zFcrF$eDi@uWk>B%s5sP=p^+7-0`EWIAxQ`Q(~Y6#fu2B;PQyt$&(S5c*8EFJ4$x8b z0Aye3I4vSzA~gC-BI^tyi;C>J1AemQGls5LfQL9Q&^TG^N2MHU7sC$#B zh^iF-*<3OQj{3){gm&)G#Im<9vvb{R5tT2On%)CF#fB)r<2XsA8SDUN#8*_56yV0t z+)~Z@qbG;~Ks6JoR_z~9muaB1+crZ<*XtlyNr1Y&b!*+p`@dHWpUF>*{*y*H9ybPl zMo(E*X^Wk_0ZMKRnk~2N(CXh=!*^L^;QYD=(^XdUSR_s9Hxl+qa3e5~$qYzfVzjp8 zdkcB&!2L=n5CI)}Gd=6+3kvFX!g-N`J6PmlI{kjUiuA@e>$WAWv1z2D+;h`qGI{Xn zv$mO0#dSY{!@cx=Sw=K9UOOxAlSS^G5;>hYo;sa1_NnIbeNu5-3NA#MwK!C&nucaj zR*!BxQkXSL)&Ks#{~smb+y_JX@OLc{Ot1+}w-~GnLae;djO-As$dLVkk0b%q%=8wl zAcxeUnb(*pR8;W}rFLahIdAVQ+S1yR-pzL(@KwhZ*9lAv*{!xDvpy%6%XNv1!Ub4H zK^WA@I|PUzVE^y`_5XbYfb{#xuz9ygC2IN$q~8HZ{9dH#xN4Q|L$6o1?x;KvCPZIS z_#y{#W}G4}ywtR~#gnwhE(WWBft4@2`(AacUvwVwTH(1QPD!)MfpEoR1({U%HW2%! zkNT<)3j$EKAD7-Yh~b`V6DRSA6DR=qc>1B|ik}Rh=s0{&<3Wmaa$Q1Ij?&OauGOOY zwT0ThygY7yW=ZUdR6lxZtcqD_OlsoC$@uL|YvaesxILe(8piVmO~s66G`Pa<ikdR zu?6W0vM^FJcYM)?V}h*n+%Ys_^h4UjWy$x1f&ua1b(A0N#CPT)T0gRdWeaMy`?hKr z@eMNJ&|4AwfP2~RZ!xUlR+YS`)DbU3NeDRXkAqY_x)F=(JbapP`}g}S##P~XtU|B2 zb{C+9ZARg>#hR7Zm|e>!X*j2?;dBmc2B)H{v$A{;ZEbe(o%Z7uc z-GMky{~PkeVjY*2h1!l0ouvDCWDhLO$F7efN28ph!5JqI$YYk~d3+X6;)10eHEP_=vL_Oa zstlG?TlEgKMN7iLO+{x8bzwWJDCPIP9DTJkGOg<$0joBs%r9Y#+&$_n{uo%yjh!>zD0-;+JCr&ifF_ks-*uk?F z_B~VUV9{V&wLuvz!z0m$lP?jBXKL7>IbPh}q7c1>bDdp{*=-x*_B1X0P1Oz(ALx5- zo356gvXKJc&H}k{a>I4?nvG}C)ROJD_Yw3&h;jlUAFVh5)dO&j`uu2Asvnouh5#me zk&+U%M!Z$i=8;&nTxYft-#w_h_Jz;ys<+8FO40_h-QY`8(e}p(<2djb+*h~9f?3g| zs;q6)^j&~|!aOVX^jq+FNcs_~x4}qbEF8GYxYE$=^ zlb-7@z_i&?I!!AuZ;|5R`lfId!g)blMAO_4b%2RLaLh+h5LmC2EzCh{$df?_f<%Qy zIlBb`JT`Gf$|K<~ITSN=8VtmRjmGgPEF#Yiog+PS3^ST-mv>-iQ8yfUt8OBvuHpkwgl-rZdN?ol}G>8HPD&O^Gg~@xtWEtOH|oi zXbA@9d-IP5zBu1Y(kLl*({V&kz$%a{l?sp(6^_%fo<3{f|El6w~;1n#UjXBe(#GvAB}B3)M9{&`v)M;8ztPm}pT6GTdGq4%=oLI}Y;+e0vSZ`Bn0otrJI`Mp{B-*K@XhJ* z-cPUg4&I#Z{=9Rvv-@W6==gN^@a@4HwWXeH0x{d^!h5>|zHlr*4m9|ZG-=Zk@mm`d z6*UT=fGY}@yr+WZo<#& z!QD-3N5r%UuMdx3{{8gr!QS!i&g;GBr$6l<{&;%)k5@k)?jN5X?Sbt>07Kbs1}8Xv z@O5^(7~KB);P&Lf+7E+Zd#m3Mnzv9W*nV*GAZR{b+x|h9Yc{(VX2tb3t=b(sR`dI! zCiNz~L48g{!U!r@ztit4eDd}C+x>o%mP(6#ClOfeP2*9P=IMFCkssUhygN>dBr3Xj z5hmj>8+YTh3w(h#G~A}>qWNFOY2Ky|F+F$I^ZDWN>GQq6z1-b9eZBMMXP(oqef}4m zbhZc0?xqVv@cPRqVGbPI=))+Q!Iq_YM2_jupWR)%{~&0t{cy8bah@Dhsf#B9rx>T* zB%H>{MLTT6@4xUbL@9}hMAr4U^2eRszaH=J9RIwh_fG)A+7G;g?v6c=^7tYl$v#J} zny|Ha9)m^;Ze-q5!*QHJb1F`M1l5T${wnv!osSW0(1nfq`?V%V&=TgduDTSrRr#Lp zDUT}HEcwM0#e*3U6%}v~$>yZpiRiVb%qRXqtfWNzwDK6u@K+t-9rxZH<8x@J()!&& zj#Y2XG77J!YH38d6ib?@MUv$$HYF|OnpG>`Zh%a?k2V@rrK|6VD)hUYyj_x8G5zuy z-7$F!J2l998G*iRV;;61b{{{$D=*I5(T4*30IBaQw9o#TCi&=c5-0DXtmxXp&x}NO zH=SK)Xk!tKn(E2=#+U6U>luU@@;qm%VT+E1qV zJn8T}fy*R#4m5%wFEpnC&?!4(&I|U!q@6L}qHkzIppDW+3{s;_!FhZiW@LYgiC35K zFb&ARqoDwso!j8#hwTSzj|csJ(0SbN1KrmFs~;SbWjMjOw3Tbqgk{g>`q7UAgZ~+C zW7$rOe6KZQ2g^MC=4RO~h#vomtHuqw$OLwF0AEe`I)Sf2W5K2A*-PPNERcAhkr&y?`hY;-K?w%Tek?Qq#17*T zQb3xi*^%uhCK<&C$G^?PtTuOdo=vXnGGB*TlvHE_)qM<9x0lE8NaOn+0f%8jLL5vt zX5J-C`LU!K%0TFto}0RAO>J=fZ45lx1%kP=)*N)v-dgn1T)p|yg27R3Z$SJ0s81SB zDvbwE_CU_3veoZt3tcL$yjQS(Sn%*XXbc)HuC-aRJFo2ISw$yM(BC|o;q9%ztItY9 z@<_Y#T6+BF5|}s>LTa%zr$2X$i#iw($D$qtZfZebAwi&g&YX2vloz3D7**T(r!BA@ z$7fn!fm!O;H>g@)=g9XSY?aQtT2pYFc{QK%&NNFq;~+U_0h>#9M%4Nb-5EQMPv04P zi`;WhaAj|Ov}c>E!gun@au(K$X%Vb0!4!5^puJ zRLwz37(TpJ(Zo{%1Fbn3av=U1Ja9p(YKa%s3V5+2(Onplu7Xx3;xUUR;fHAahf_Vf%QKS^&bXN%bu)1@q5+yH~j1mSF{3lp2o`{)x1I&^u;r3(L*bvIAR>1RrcU+GMD-w7m z$oY64XDn=GT6|Iaqtt z)K#>wPm4NE`uzv}{$mt0-S+!{T>AZ>-)~|gTJ|AA6;bS*8-l&=suVLSwN=0MNofsU>WcRF3kDShlg;Pc1g!lZ!nR$ zNP-*X4MK~zxrI&P6EdDDmO7D`E(eptut>9#hOE~nz`lkUB9aW6!xByPz3{MNnLU~d zX@I0f5ae)pzzRr<)5Oe{i$>uM z0)nDM*?aIi1j?G&w)Q&&0e3TF|K@kfjW)uDU2T#RP+>3|67g*b;4xrb>aLl8m^F$3 zgstyagJPSgBZ_T_iIzsd;t1k4vB|qt!IIIn#D?(!YpDkCm6sqXtJL%|DI!$XRLOkm zxX@?3kI8^jlKJ!uLbZ5k`v8+|OF=T9HZ8?b)sntJzW4&tSQD5om%E#%1nfn0Q+eY_ z;~y2O1Oq#g2Mh8=2?H}sK%+Srlm7@P-ohfNeh5E1@E?`HY?F?$9AIu({1Z9{8FnpC*X+iLyO$`ZmErynm}gL*{cbaw-;OK8b)_vrEQ1y5g@Cw!Vk@ghP20g zy%A7MwUz!AC(Wkn>Yp&^W>Y=eqCuB-W{;rUE($Ia%Cm_yw0a!lsDpYyuMcjBo_dEZvfLcH&Bt%W6%eZaM|+4eJu%q$cJp{Sn`EcNgfg1S7+EX=a-8m||y<)vGyG&j<& z=Sk)I?y_3<;b_!SgaDV!78v$sY1ZRII*-63i9ZRPoxH^k*6s zDk8rPS#}azHpLE%v=is}H;BNBY#Td-?-maTLheRv%1(-%aK}&{tu9pDT&jw%)B^v< zwsG!qQLV;)4JF8KD+}WyWX}76ympVb*}!ZElvDV~W7N4fF{UTmiQmaoBWw>AV2jM4(PLB`-@r zzRUOyoC)3D$`Li?LgBV>E1A4Hmrer{6m#P`yLo+EpP1B%s{%Sc3 z_`r*-Af79aKJs~CM>rxT$@5XXU#e1rPVQHNdLdZxmZEDCq zddj%@gGrso8N>(4F6O8YNimT_B?js+YNsKIo%l`ypZ775C-~ZPA%kNEz(~ssU-?M< z^l7lOz?yk<+=(FSo)x64gPh%ekFqq^{y44UWxhbg2|+F)Z8z3oH#@{dv_}Jr*_P?* zDpxG=!eL~@vEI_F=QTwW!JG6l%|O|B?3Fd?w&*CF6~wod1v%9gyUN0uR|b+~JEP0! zuMyncuIz%xj1LdGjY;zud>H4J&ZZ#Z7on4Eu>%c6aCyTb>WJ~_&rO*> z4L|H7vCj}}QnfPy9}9&Jo15dBzXF7&&LB%|E3@fmaD!BoKJeYwuW(Oyi(C-meyP^wjjQWrxHs6~zU$$?)MZN)TFtHAc zNKBU-YuS3PsKN((LrHtvYr23_Ad?KJI?gcoBs|b30f?2r3_-h(ShKS=;_GMP*p|A6 z6H?hZO=cP1(Hpwy(4)2~LyZdc>e6E3GWyb1ys?tW^#RWXs6h;0P*JndZnW6tzHR09 zRvtWP>NT%axhZS~+iTp}61w5d=^kD>rtLD!tcSRN$%un1}-3`=r7higy}5b zwBaZDMUb#xYI<0JXB&!6r43sgNior@3$_Cr*5#$Mp_CV^n`1{@EbY*sS8Gp!&9-o; zRk{|KjhGV`)T4%O^pJas+rcI6DKK6g#}{#t_tZMy$l}KTJ)V(J;P-eYM__@LDpQGB zG?o`B1&Gx~8;yn~6VW{o-D${xs$iT^yDrlVik#pmo*lyUwaC+$1G_*Qjo~NR83kE_hl9DkoyM)*pay7B^3dlIo6z z1I)Elf$iU@ve;*VW@&8`r-^!$jdql|0E^2>v?-jb zkr&>1ehK?=dX^TKrxeOu(`22F0BAWjjB(8>J$WE!qPdDK?Re99yIRQx%@}wHRBW^C zO*hTtpfu4hql&l#`h&*f@-L+iBui%edpy(HPh}bau3dP&iG4(wlg4_LA}1G{fdmSN zc@mz)gO)lwi3bCR`U$hF1a(X$$P$UTq1px|^B&&jej~i92(<5_ z>lQd<@tVuB4w$?LC<}WAaCY(z-%7wm3@6QsR$Yp95+!bv%sCX?MK(fQZ>ctxUW02I zbf2QI2q2}V7s4VjskIoAQ})Gqs;zV}^)K|YpQTs9#=5Rl?!ZASbb*RpKG6tdy`_oZ zMgyoEri(1um(GX?f#MR!YnILpK3B6st#RpX)o3*kTGJU^_>JabF)T6L8;yz)dlU@u z6-812U5(aqUnFf8>Um`GMvo!jjG+TIS<*P8wQ z*ea5j7uH~Zlu1=m8l_v-Uo|Vv62$O0K0ntyK;YXz1I$ut>${$0WR6{{Ot5|* z2tF@9Rjy)m$`8F+HDl08T<>|DJSM3*d!tuk^g*ZFs7}J~4WMPtnS~_ll7L`47 z&;hHqJrf1&gNE+_7FqY)<7xP8dLDlR8T;Q@RnD|4JF_#HM~)a3kZ(}V6#`O9W!Y${ zYa<3^CEAkKs+NW7Dw@b8x5rX-LhJesAKhRkA3I{hv9s7jD%=@8s*Vd$+%6h{gIAS(JTlZGO(fwd z>fiJB(_eS9l*z=hYK;rc!tpUI7Nu_60Rt0F_2dL#$9z?6dCFYTk}J-|&dSnzO!gMA zNMC7@OlcK8x7G2h1q4xW%_Kl?XZn`8&u#o^!IXjx=COIt(oZw?}$9& zZ56=yxCrVwR4&DF^xHg2P|Yqt1A+>;$8;;JA&w>!=@6t?eUG#`(&Lg)U#F>3bt?n8 zS9QTrf#;4TX0+HwQ+M0FMx|s=Z~&#WNhF&WlHNyuz@CAjBb4%rN~XTUA@kSJYf}zw z*bW}u(0mt=0V}p5!Wpo zp&D)w3hDiKft*>TB&$jfP*4k6AB7$P`XbBkP)o^|$?3VR8&m_(VWAy$i;|j#ekKv_ z>40-{c%*<^-5|7g7rj8vnnLQ96=eZURV!!C)dlYwth{K$M>^w2Oj%)Pr8~?ef{1VE z+zhO8A$PJ$(QxH6X(oa#ZM$HA{X1Z-SYA**P1PG%HDPagkdD-Dkjq-P!nf>^DH$}h zk|hz`T8$^~VZd%6Wjk>KMrDO*u|#T73kGiQ+#A>S^N1PKMl?+tNOm^CiEYv4uOP3c zVkpOCmxHD*A2)_2%22~S6RZ`w=|6<+jb@AfLr?cX%=5fN2!~{DR7yW{y;6Ppwz=ydEe7F?nR^`waTmX=HU|B>19y#HNm0bkn**jAPA`b|ea>Oq7iM5|PyBf=wwm5wX~gx6J4=8HNTHX{in8 zw$~ap`0fbA=x{qI)r?1^*)iC083i0}b=wX?G_1p*+i98<+o-v=d%WHx6f(~#J#MM} zVRe7255J>v2knvfS%FsVyA<9S`iG&7J&hKs!q)-zAI&_iFpESHe`d$4Ig%MFXkHl-fh4|$e1M-)4{MI^5Pphc^%M9(s!k2l3SKFukp>3>FmPlo zER9Id71T*b5`xwir(zpU1!!3TA^QE6VM150tyIp(MRtWSj*mI{;W(f@ z2U3JQIq20EuR71J0z+8Ou_o zpdjM1uOmu$imRRVfH_=M6e94hEVze6)f168L?5|O3Z3ja_ulZ)O_Yo)imY%+pi;F} zR>c>b#Ih2->&#C=OjdC3>3fy5lRxii7l+e7m{L#j&pCdp9HfsrMODXV+0h}kmSM9> z2B!ar(4-6|5B!-wsHlommXi*L?h{U!$F5^gYiNN@qr!*y#A$K^R@ofK9<{Nkuo)nJ zw6-0;qt$g<=X%ZmNX8tq5eGyVseEn16ecRa$#*y(ZwenfSjTJy}| zao=e;N!E{LEN%UcYlVy|(?qYSUB^)Ro^5%pdB)x4#`MB&tDvG?c*`_`#tO}QHsbeb zzqwNTVDI0kG4(_xF2Ag1GzE4D!v9218P?GsU|4FrYiO4QLb{@LW-fAboY-hxSe$Ok zvYLQFT*=I&mUA1l+#8>%0vTmUYT^+E!?fxH$4`QoEA%1=S~TXC6(Nndb(l=7IZaN~ zyJ7rX*0+Gb$nci!{6E(G78`ObZ%Y`Q(dwroaVbs|h}?&0V)!fpvBB`uSFPl??(PNwd<$L(~TAw3WfEu53_ zRRcAj+&#{d#?7KJSbbEV?W&fSD;1Nj&+L_L1p)8|u>jfhf>vtTi4-@{8HvX19*0+k z`(Y9R{gQRmTksZGJ{(1=;sjzFLUyJ&bQ5ZAQN>23#(g~n;%Tr~5Eolz;(}YMgQ=CMkQwXy!>OZ8ZN{bkF@ zLD5tVeaJ%WwbOM}E^evrW3`S-sGR;?5A^IY{X14p#p*kg*=2Z!kRDP{MJoUH_3OQ( z-JRpT)197b>`=`e+2a1;FQP<9B|=%kw(;*h)%bTR@$c1bK#AX}#P4Q&RA%qRo72}j zM|%fve%?FYJI04O77S!lM=yW+*)P|ma!t3vo#QuqM=y{6+Jm4qSm3c-k%Kg8hrBBB z$X}P8SFiVuj&}~8_f+Exs{Mr=#?G6bYWzSMKgbLd1gxiY^nmVT)$jXd-n`s}kqs$- zD6@BWj*edL{Iqv^wD;!i(E&D;>2@-w4JsAB%9{fXD zXz$hRP2E)1|j86U+nEY$0a(~OLXp) z`DN$m`KjKME|u!aGCxz+UnuJ@PS!tO|Gal_`s(Gu+v6VD@pm7qwgcP%xH|_w?eG2n z?7jP18&|S8`YMg%9Z6V7U?-U!2w|{IGCn)D8DJ(eKfqQaHPAtHYr0#u8Lac2cQ|i& zp5%O0wJx=8-7Soh%CBgsr)y zYPU|c!$1FJ_keeJOf`>GfxF{TZ+G>h;^F2Yb8!^Y(C`hCDf$udnZ}%gXqJ zH-NiLkn~?v=f8}vKjA(+qECRdRDH15LhqzISl= z?DYXOe@j)~@(O?5J2-mrZ1*QTSCrpjosYcE|Jgfu{r2Y<&yW7}_SI|WV1L;A;fIbm zSNI!S)&~=8C9yF)^dW0-r5)#}8|w+{yZEpM4|-4_ASE9@I&!g%{cyB+GuU$xxSQ^^ zbO6$YMbFK3FRc$?U0CHP)Pp=ZsJbv}-uZqb@SJ1|^Yq8^l&#aB#YLG|Cy!;!nmnvR z6_Ok>;$HE&2zWz`DXY3TlJx`nLpk)vkL13@b@Mz}n6|<<+)8$|cAr7A)dU1Y`m2)p z88++R8*w-Eq7HAeD*hmh1z+Gch!$PpKO%Zq^!kePy~bP_G95P5ypBICdLw5O;u0l0 zxYA9*YxN^>Do7q%rblHJBe!)Nmz6dhkUUsiR)$BvRRm1VRf5~Djlm>W9P!0?jS{4} z#SE+K60Li{qf4%H>5GPB8^U1}tww)Jrn7`{Dk!2(h>R!iVqwyc;77g-!VG~2l$DlO zJZNw@h^`6-pvxzDWdxqkUwOb>J8c&aEZ`Z#AHdffgm|t;+rA*9eqjKMpsoV}t2i~N zVv?dktZICr!xvEnI$F{$^J}=!-KoHg(ti`G<{#j6#axNV@S zMo>1|4r3f+Tz|9Af`0I^97AqlN~?n6tJa8!G0tpjkO6SsNYdNNz*b7bpnnN(Vo2_Y zRPw`)om<7Wkkd>{A_eY387W9p)Y1#B`fx0QwZXRsxZkfJidz&cndj%CgRMmF>mi?|4xXAAIOa3XT)VWv;TF2*XqoSAjh|Fmcv;_A1-$BD;+2nM3`QsNcGA9Ma5^+p zq<4q5yHhIg?{QqLgrH5&GZ$y`{#D9eiweV3v6%K1=O#_|dt#QDLo=;9B~gGnlrifW zqVtXu;)KwPXfPNs*f2w2O1{Zh!S_0-j0yaSlFMr2__7LHx1h@BE?UKDdgiqS*KR#l zW$}SkRi?C7O)6&gR?6yfAsnMnr z4{zbF3@5&eDg0(?Zi|FBN*ybIRk>D{`%>P+)XYmHKgy)2aTe_!9tH0dUqjuUqD5L@ zVVZOxK=om5`HvIfiGJ46GT*qk^9n)G)yI;^BJ(YeDs)=a1_xT3ck#luO{oBBJ1J?2b( z50EEgGu8o|17>oAn}Cccv=EGmJs8x&5cbmqNAEA$lOtZn5p)Hw-ynL5d@c4gF9AnZ zd_bVziv&bH2_8dr8CTIwK97ndChGEdmJ}FMBg3rZ6;6Vc^?KMhJjbYpsA7;xPRi2r(!g;R0y2`cx=fH*F&)aVi*9-z zyxqNi6i1io#pU#dy-;;r-zPm;_xnx}@ej;+v3C1-eSI)^vcA3;&Wapv)tX=j*KRik zgN=1y3-x8DM%~Bjn1$%$epBO}$f7YUEI_q3BNHRHcqc&`ck+OmojeGK*{2${ zQk+?g)|m=@r)y;fC5$8R3qsx}nFmEG>VOg}^y)BKF? zoFzPX{`%qWCPRRbw7V`Sl-dj-cz^Crc z_Mm5V4dusghNrrC@743SyRV-8>Ggq1U}+4p`1lYb|BCUCb`Ors`fonkEm-~Mua6G( zwm!UNo3psx=s#XOywwv}=ytDOqlyYgSTURR>{^Ga8lT+)TZzHlMtV}t0JrP?4Rc=Z zkZyC&NOYKbDaUUH_Ew#mTT7dS?G~#$BL?96uMc1R4UhIvS_QBvcH{ryd~cfn$eLMo1dMHD}GM1N&dbJ-?^V(yn6on z=R|HHQ}FV?7${QY~~kTPjb^h3Tld*a6tSEoRhUjn_fuo?rfTa!Np?RwCRO) zzK>J`ubkOaujOz?cWSQw3((m9u%?5C32-Rb6!*e81r89K0@GN_9$BzT)!bcn=raHd zeC)vr_`m~D1ULtKKkohQ?cwXc96Z~TP!&(J`8BwguKja&?f{2#*ydg%} zqmR@4eNxc0s9;v4kepG}mUG&R#8)8s#MiZzlLucs{I4%hPCDHl6s$h#4~Cmt-)(=t z^T%T-IDPYWbo+7fe^c9XMus-DAoyPa|I6Ti@8EyuRBt|;;gr-iz0ZpY{f^iExxV)8 zTPpq_SGv3QL%enl>kX=TYTc3}&uMCJp7w?pecj;I>sN0N_xGN?*!_vn&So@xbNuFyD^PdPe+Z3l7v0`R ze78>@4Ov^e-SOJ$qn!aCD_9#Rda(DyW;E;~#n9_^PfmXBbx%&7toF<)nhk&a>Ge}U zgu|{ooZ0SR@8#>i?ipLs-Pt@jIUcO;9KS($I5|1}NZYhM5TlA+J`gHJz~Chb&Vf{W z7f(TpsZuvrc|=h?P#BD2i}V_0xY5Dh{!hEl_FnG20z@e2+Rp0gYKOe0Rl!gcS!KEy zsoy$x^{@Wh>#hFvAMWU^{>|47zUtrpp}|%C+w}%d^^M1m+j**gwcg56{i}_7KlQI3 ze}bF(lmGU6sekj8-$|W&MSuI)I5C>4X05S3wu_um3yff&SH3UWd=k=&Ns< zT+E+*6?QQHW}}UN`I9HL?&TYgTlkiL^#|9r{KtdczwYUc`Nnm1CFk=eUpM)lfAw`c z*Yj`I+jyRDJbu#B^ZeV5TF3K^^)_DT8{e!q`kO!bL&)9yt0#@#=HEPN>1_VZ<5s@r z8;`$k>udh)lUi5vufJ{bG=K7K(9QhGw@n`A-+tZbV7~GAt7RO_zxn#{ALOvw zyBvwiz5PuT77WT{*0)_XNxvBF{p|>zMS39(zs=-B1uqx`_b$7L;GA6QQZwFJ>1=9~ zQ4Efsbo#j*Ba!Gu=(6x6|Pb zAMu8>>AVcKdt|g*gleGKo@?8#&M7_g8> zk~KNRP0mkCaoFNhBurw!Xhx#n!E72=l%f!%bNE%uTy8xSW(}nkBHkK^x<+4lK|c>Z zL#jsE^<`Wldxb|uvBsc(&!ml2dJ_h3-SA%a zi6?rv(cv9dIm!YLr*V0ily!Zw*b<$gwf#kMoxe+-#^bBf?&#Lkb6cG{a1vd|kR4W= zTZ*Qlczatqp^A9CSWe)t1+;Qn<$Q*J{FLWcXUSy>N=@mheye@c%LGk>CEUMD3gDqs z5$lS}Q^B zuh04#)&~|6wQlQU7>Qcl#`r=Ga|S$9-?zpv|HNm%(-8ECYAqV#D^aap6ZaHp&Z&W{ zlF6I*=?ur*6+&4RO_*1faZd0Un4eAfvH~~D05w&91^yA-Gz3Z@nLVK- z$F-Th8|?>a`6Ec`s9!1Vp$8+|2$8}SJRuQ9LJ#jCPj_U<{{1^ zq8&*F7)O_JHaVY9qx@XhDkbo1R^+p!sM4hDN7M8wiAFS`Q6H7{m!qhQjuc6ZS$(oR zTZ19{u-+6b@6)m>dwtUS!xzyY9#`{tDreT~MGO?0#}iq7U0 zSVCc^qOd#a;qqiRd&D0$_tqWt!e$k-YlG29$2k&eKC6n^^#JyqtWwY7Vz@f^rBo(R zw-Ifd@D8STZZk3~_oK5ISu#=D>ErQ%tQ0;+i~F^}_ZSazh(Pcw1>hMbQEgJgbFG={%CG!;N=<)v>dY1p)G1Qt_2#xi;o<&{K?8{Mi%Kb=0IV{n;Q zSCA4-nt+z%C5!%rdGEI)7K=BFp)ty$O^_I~ z1BMVLZDR~GbBy3 z9Kt|(LS0D?c2BFj6#pt$1-!eSrSrJ$9@TvmLYJB<2Gd_t&lZ4KCuXgIp)eq-y1iC0 z=vt#FPV~x}ti-GwzK!lBuvclqlSsp9U=N4#b%PX`12&@0yAmLGAVTg)h}00G4fx-A z0m5iL1R_hmWzG25hmj4OZ2j<)LjJxMjc8Y1Q7iJ2iP>}|gQ;^wNY?sVZBqELzvH{J z)T?8lAHD~_|EbqUN)6xQyF@AceXtQrM^)w03{E$Rl6xdjYiY_~jiM5-J-rDvg#2>P zj261kzOdhX@z|_IC^I%uj3!ev&ziTo;jEU}>ASyofje3Ea@^^)Yk2G0a5{H+l2t>5 znW+N^F^-DUtGnEC1JziY?pYuHyFy-;#?PVo8_bP(at2EUi2ZRnEB@BX1p|+WSz#5m zS8E%+I#`@%WNhI9$MdKQo6wV6Av0)NmPb^{4Em-vfng)}8j1Oa`i0NFxyu+_!o-0? zZlAfW_mycyA^(OR`}%lgZIXW&zpj$1&u?Gfx}wHM7(x_?_ssky+HlFZ+JC9Gv3G~; zAg<#N&y6G^BJP|GC4i1OBm{*S3*Ng@8POkGKKKY|s6x}0S`WN?TW;dY$p?|Tasmng zDUVT^D<>a}+?90FnFy6kxM7F={V3X?+|fm~WyvAnc2e(Padc{ZmQlI6eNfhqQZp_V zTdAAwQdm1-6jVZ_D;|LywZ3of>NA730w@toQW{LRGkvt%Q5woGLls2CYz+wC8JbrIihM_I zbijg2dnVcjF2rt$%f|vM22aK~4!2#a!|@E~Ju0=2)DyECrN2y5v{=PLtV&N13#=6m zp$ODdt-Y19f2lKzX{PL`b(7yi)@j8h$T%M zHHIFfm>-5YjAfF8HI|O%*DhDyuaPEC1}fs$P(&_KK~4-S3SrN43m*T{sAs|8{4=N3 zV=3i)`Q%`u;M3Bn_BNx3x6>qB7zy^$>-lvQPrz!*pXG@~5;R-r3|f$I?rNF1WJ2@45k{ zL=+tTwgA09!63=Pv!;(%Sz6)eMKl9n3_aHVPrFCx7b$~6sJ;fa^AMY%;rnGYGJL1hxPe$E`w*n{@x}fL^g6Y=ycw-+a4uYDgPG(Kf8aTY?deOq zu&kV4Clkar-0Ytf5O2f^K-5qH<8J1!5gfQ#D1+MT`H)G4{*A(9_WK_qLDf2w9>9{! zehK3lY`%DwxQK(8K*Lj`CqT#Z7BIU%IupmlQ`sFFr7HXBsh#HZEH9mWq^1|;5v(Kq zCr~Mu!9=9o43T&SE%k?$n3k`?98oYA!Zs0>yTP)U@@^hQ*{Rn$kt+GqpgwxUx;Ujb ziFHyW5Va1LWi-=*Og6e<8z~?Xn(2IA{V1D*(e*kvm!wW41lyTGj3K~N^4f!&l#h*R zl`mTh4;ab+$=@r&@p8v3-V{IiEX3@(>`TvwBT4gP*@KcL>m1VvY$(21=slQyL}vx% zoK*Cflp&oiMx4W;~>I)PrC1GK$~j=|tmWD2>bLdOp5{e`k4FVw_~r z6BzA3_OwS@4oZ>_RT0anVE|j$l4Lxu(szmIge8!MT&J@s1gC@)9AXve^{mL>C1u0| z)WXPRdOe#$xKDxMqN#RV9e9wQNUB`rS5{7CD{+l{A37cbDQRp+a3!#}NT&pMlIJ49 zj8AZ9%yN-IY%5@UMKc_zN1O?7yYdwouv~*?=d*N{c*YcIC~aAoszj_Dh?4N)o8{CP zUBBf9(BERlwn>}DbKAyGrq;k67o|ev7s|hcC*e=sv<*QE>`#2?Vk2E6 zJbUij9<~Xx#j)KK#d~R%C53na49sqfsy@oI6bTH(0|VcCTkSJfc~|+B2d#?T71@EhEX^H%@)$cSB?bn^cZ@Ag`%rO` zBKf@u8Eo*FHYHok{pJF*r!3{ArjzieQNw#Hw)i~tCfmGqx1&;Gi_T_AruBC6(drU7 zmI}YPNdUDBz_=2EiBj=I7oHwiB3`lk($P#v7!(O02hrWA%V6uZyr-%%H(6wq;h0Pt zLD>%Y7lHAL7JD=~|h)L|u}QXpcn5GmN6;MJz(y^b%^ zaXgh-LAs+sRgQWf8cINNnk6L=ggVe4)VQt6epQ0-ECS!b^BhELQG5;&#et531w&^u znbA5G#DpCDMMk0Z$Dogn-X~F(KtO$wR0aDG^8q3SM8YXhmH0tyu1Eux7wJWsY4=of zoWprqfzVwA%iD}b!^uh-fkgx-yEvJ@;&yT$9J9jMOJ1zQ<*R8!s0r-GIrDmnI}8Jur-L zNOvO7M&$0o6Ym?c-F~E|rVRCa8hy5b-CI4-!UE%70~}j#Do9j46i>bRwM|&G$EhwN zRqTX?>s>zKhggI$GDQGNQl)kk5e986P>R$Jwq#TnX1|T3U04xYN$os>{GDzMUMOhi4Nw||=opGHEn-hWr z*7HusF(*jMv1K_RL!y-9=n+jF9w@|oLi`B3ki>-Fl-8R+SC;OdVz5XV&ANX)Ch+{k z;Y5cXWApM27GhzEBB1A;=sMbrMx^b#Rxjeh zwy3JLq-tT#<%>ciuMx(n9bWW})r69|1)4vC_SaV0PJJyT5u*e%Kwe2TCs0}4Ygy89 zx3yQxU&(oG3-lmzys_vsp53sxOzjPC9$pm?6{&_K+SO5b#B79Q*U7l&^t=d2UlXyG zc~+%Upb?5B9$!XDHd)Kh*XZFiaI%oWtsu3g7Ao0X*T>n+@Z7=JmFw%4#G*0ztaLTh zBAA9$(hgpg(nj>&A&j15BG$N{S# zyLIbGsG~2xq#lpgPc@VB*d%RK7gkM2uA;=^rah7}$QB&|0`Xbeg||NBHPHfL`fg7M zJE{qIDqFVYO7e%PmG3vbdGMybIFH^ZodWPmnNC#BSABD%@iZ>WzUo!Av``Rdal|49 z-b-(-Q_47*f#4}A1eiPyN{{`eZwwY!Tc~1c6@&a_tu~!2jRJb$vU##^6?<^Yrk6|( z^tfX?ZeSlYhoFnO-OFaD=kqaFG56TKc;G!4eaqKsRwTaFh*)^uCW3`_+Q0-JMUMmH zh-JF=bH~u%>6YsbiA_9CkA@5u=KqL#cN`i85J`1H$nVh?!^23`eU_K<(Uw9KozEI( zLkg)4&s?L3HQrY8EoTmk_I{&FIm~y>yP+MCS0sdkWS>=9>sa2(ol^_qlo=n zjbUA%3bo!{uI#eIvg^(LMA!hNWOOIv9aM%gv_>*T*QNkkh{|tWUNz*#}^KH;`wx(vf(U`rt*vPZ?ooHPJ zpEDrpDoJL4)-|0+zuoJ*SZ1O7QWDs_%r0MLSSTM70JS~`YuH}LdRw^)LVG$;crrBS ze#+?=XA}Llip);7+X3|h7;^S;(OzoF5a?q{$r`=bmPeK?yfe&|h;Op!k%jcR{y^*Q zP@CSCQm;81TX!NRp_C1P86nWQ9x5cQYYTjb|CobTT+(4p0C?Q-QCV4jCY_y`I(db$ z4vfCBUmH&^D`r0Sc0kn%tOUXYusZGZdLbw9rl-iE@7Z0HTiNL~AHJyj30L!Ui316# zapf$`=h6s?-fM{jH2|Ai*Pf4U#RI8@-Wy;rd~;j_7@u?H>?#{DWa2wGpucL;ILM+X z!xPN{sYo@l3mE~u^M=eX)Q1|~p}HEwS`uwpx`2r>(2hrXGAJMbECq6lbevYxn@D(! zOjx_PL_56qNmO3u^XWtgG(=ojFF)&C95Ji`P{cWY{3wVaGb4c7vR-THka)LL7%rE_ z(wk@7y0tQV;n(*rdz)m9TPx%uK!d7YYP?Wd?Xz=nZIc?YWg%l7_j7~13^xOo0BL|# za_16a@?Ihd4!%liRcCp1>4ENT!yx_Pt(e8)BStkBwYU<{Yba!1kTwtZQ_6DN@Lu0) znJB#piBBwQyKs^h9#Z8g(3R3W&jelbez%`WD0ITGXZYjcAYe;8h(xH zbhmh%1xiW|_!_2ek;Z0G%eQvnN~X_*y&erGQeInhXW(^COkxR(mVRZI6cQWS(oU~q z%?{@PR`+Evum#v47b+%GDNv6!K8TQnyW;VwA#>5r-XJ(F3LXkMN+DP+_ z(JChd;-WZCPhAU0H~L{sAQ87K>6kBJ`6^(B{G+{u0hUNL*$> ze=EsCI%|F+KH40F%|3r`5#ROpr`pbundC40yAl$gv{~CTfbqt~^=(r-1r*%7WjTCq z-8Lab7+)QeWj(9R(2<2j_&s%m)*7-ucA9xevIDOHtyW?HQlK?$=a8>rIF$Ja&XS8X zL*E%;d4|%Jw2V$342CBU`pQFxjMrjvJrjKh|AO(n05KTZ-t^J7ZIWjl@a~yl1?tN~ zRO&wH-jZ4W1^5U+1_@=w&+Ye-_T#G4%Y?iU06RQ=eaO8A(9Z1S!OF=4oYl#L!O4TD zo8^_5QqN(z^iwp+iLJJspqSr?BVyv;wj;~)$iZ^Torr}e^58(@Ti`imK46r{xJ+P; zP)ttdu*YQTf<4?t+(k|R4jp5-tu8MqZWde({31-#!@_fTj@ZH0CqT!h0Xwrma-PWc z_{DNlKtd$oi}1*e9a6`BN1PgPkWZ6r#@x6^hM=;iWx;y#E7GdbQ%umXG}X&?xIOJ_ zb_{3Rw%;}|_~&_2qD?E9$TdRaj?ee!$0DE4gjC0;x$1U|$7e;h&&;33XjBix2ibjHmH+3@D)q8LR|E_|3Q3eVojzje-Re(1d@j4Kl{PSGk2f zENxv-=!I9-;RzCJQ$r?yK@GZhwq@F%&KnW%qdV{UQW#XEM_w9%cEfK#PleedM5H9t zHFt(jL&G(X9e#dIo(BwGkIF%ZQ>jtqX zeZC0f3y?uBEW8}rKk3o0 zV<~)E#h?Jr^ryW^(5Lf7M`rM;ZOmv*(DjO(IneEJvY=4^^2gZX1=66HQ ztzt*m`=_HvI6K=IKf)I#!tO-{if}#<6KJ!tw2w&fsQSxChq&*p9S+ePbH>ApM5E#A z>TqP`zd*+~r7V#FY=*0=K7NQM?hS-#nJz`nGobENYw47U^#k7Nj)2n_Z=>lQxFg>4?S>9+qS2g3{CffG`Z638@5C>FE{;jIZOi-)&{ z+#hUa%z}rvNjBleGa-2SM2HB1Spa6DM=V#%R#=v1C_6Uc?CR@%J zvsI()#VhFwxI9WOt7nQh`^YE`*iKm4N1J%VI|=D}N~NSljP+Y%vF%Gvv`#r5+D^|0 zOI;h##@AYy^5Dl|Wvf-o@(M6iAg%0Ljrht-oQ0Hc$X2r!1Wn65)##W1l-v>#2eFr%zS@}%c{%2lly=hNyl;u@h!_E^}g># z_3pf=Zl^IkP~fDJ&%30>otJbQI;X^1k~L+?(bQ@;4SA`HdNtE$k}22m%~|r+CRP}& zGP0?w9pzx61AZ6D**u+2cto%iyiKFa7z2rEscBqAads2U@-(Yh;x|*W>d!u9S{AWX zPW(yE&kbwPP=u8`#w&JXX_fQybIYULKM+;)prQI)HZq5Xj(mk{R`FU;H zZxK)y4{s$DFT_4^nH6;~2=u^b+@_z^LBmwA-5vTJw(HlHmGPd%J*XMPH-=}f)nQ=6KVjIrp3v&>ex}lX;7M{G&!^R% z&ua+3S>8j)p=hQ(gc4Rkrdwk*u&8bB#HBMarX}TlBWVzza`*?LDG@PQPE%9HF`Sm>6_R1(GXi17lW-t^YSD z>@WfMDK&ajUB*>k_$iAVNULflaTd{D3K#bY`CY}+D9tdD1^hxL?3g7GEcNcuzjVj|gmwXoTo_6uk}tktgNh_97aI z<&Vs)hd+q(-Cjfo@_-z$6#DlE}^kcUx_CUmW|PAU=i3Cr_SeUM94S z=$UB>&M|)ghPQ##n1XNlo}HG-{TYKQIg9cFj&ELy_1WZ`AqtRCk)Oi^Jlao5P|G9s zJXnO^eW6#~z_~^apDimZsGhV-J);$hmcy1#)F9Ml`i3ti^ zIqnzL62Xl{C)lPRNz5X|uVN+=H<(&EE9o1Rb4q@E{_$vib1=ogG* z5rsdD4d0O~-}I*qqag(V*m3QB3#F>zg+^vYYpDhz{49$xXRG(J?oK{`Q%lAZ+xz{#Kph;57YTP-`^WhmP%!hA4$aDN=c#4eVQ2yC_ zIPB@c6+)4I1=sZBIKr+#Epwjd@DKDFoibAkITXSGquQ*D)ABS96^#xvH3>3G2fmO; z$q1O>FkV`kqzKT-B@C;!7t!7)>(yrkrDNp=2E>BnZll#()WFwIb~QMX)FiW4lLQt>S~ zB{9$oh)F@HAfIlZbY>A9%N>gjlO&4Y#px7toT>8e7ZRcZx5NP!G?0)Jp`!&b7`3=g zngEBx)hBDJ*ER9zewVTk4a@r|*o}=1g8dPSG=x`@R*MsWO^c;v0(POT6FwD=Ee24p zoKmfc>A9FTh$cmgk@s)|lD7t0Z(b(n^C=io%#xylI6xrI6%P{;R7ljvrHT@2Y+xJm zP)gh7iaqudu7pn1?J2RAOw$LlxtReOazAyy20}D;yG3q02SFZ!I>CjhGJz8iP$kRL z48L4}fV#X_Lk6LGcv7mxxU*8kbQFYG-gr`?5Qklsb+YW#*K7CeY^%7#~$! z$3?_g^cTXi_S7kkTXHtPxX`Fnf{ftcTfl}AFVj=XbEhdOsN(8~xouRJn9Xbki)$LV zMgCF(cE~@7o?T|bIcZk(hFcX;Q0!pchaD#0R_g5$1{sEi3$#4o(}g^w)TxMjp!6s@ z1dG5F9ve?J!W*>tq2G(%VD+KL>`2o1OkR>xBx`DCk@1!w&$szXs$V0|!&=T$Mmpmf zOc2wRm>93!{SZQO#`e42;ct@hUNax&<3ceV#TlH;A6 z41Otl4~O+Hg1GTO#M_usqf%Pgh@;0(MSmP^T*o&^@tcUx#CBo>CQ7FO++`9^NRZ$j zzZT*`>_#xg5XO{fCoS$Uu?J+hEnD*F{idB5(n% zN;=Kauf0q!l4VM1Q-YwzAugEThelz+mB7g6(~Q#3JEP?l5(Pq_-A@#mHYrH~lZwst z8FMnandm6fKoeFP%5W@*Sm~8XnIz4!Rqg|TlVDzvrd76w@-ak@D@J>(SC#g*VBM_U}d+Fm= z+tKXp2{5rNB)_aDUEN;DmS8BJgXEF6H%;CPK_Kt89Gp+%3v3{5meK5TC+egk|F3}G zXpC^sO^U)6tmBYm-iKEeiBQMQC@Cz1=i$RQLwPq$aNbv7r!CjLERdIe)pm61Z=?4; ze2yU6t}$tdKV(qY1o<2pu zf9BbF@Ss{PWWRu`KaMLU+7+tL6xGT4GZgd~hnHWc=ok)8OG(w$3RB2W=>3pZ$#v=d zf`m=_&PdgaV<^iS$+&255$J3XPbP-bq!sea#;~$K(+I@))Ma+smZ`Uq83dAmMBnM_ zCVaf2^wA^LI~jVp2B!p`YQvAy)1EC7RV!a;lQY?+Vu@wv1Cvz-O3wE|$mrM>i4Q$} z0GJVhxk}1&1L#BuFmn|Uz_O`yp|WWuXVk^xr)lm)E26p5LTpA>i5Y`SxCAgqeKRk* zSPZvDx=t$u64|c_CO*$}Y(lns!B!Chd40#ON?HWzBY0*YzrAgPQM3~s4+aB-@zZD% zf53>Rrd5J?*pSaVPp4H<;P%5US;1vo#wC{YdM;<6vY+ed?q;A{lZ!~F%68Nav|t6P z9=*!d^v?5nM$*EQ2M=%6Du2n-tc&j6i<1W}TzMZOi_UuF%k3EW`dtll{9{?At&_mbqR9prhdps4;#;JB4;2a(B}N*7y!K32y7< zj1d;3qF>Sw;hsrYMXQc}Lt_L6rJutZVF^qOQ|EY?Q6WBjJhgcdV9VaFu1<0k-wGrO z*W;~uKlCYnD)eW0bq7ujn;mPgJ%fM7gE4Sy2>T|xX4LS4w1iIL(RLkvwUJ3{1=*j4 zH>2PemvIK#zvwc5uV@!YIXX)Uu)M@fqwQEY0~kzLSY36c*kvp1ong3u5MO86^42qV zNcPayYyOK-r6HzGWl3z}%NXsZ*{ymmi+DMVGvhLI3pTE%dgVm*CONwkVyF}`>@YYu z4}<<)krc9~q#cJ0-~#ZIMI|a45vX^nd)jFC$B=}0AS0z{byXnIVS1fPo2e>~jwQxA z?S3Kt>P2Vs%CL?BBL;<=K=fWo>=YJjzl)2O7)j37evn(?CDphS$HTL;;dqR%@tKK` zLho^)^~7v_w}*Kd{PpAS5@*#u*a85|cN^`{G=$)`v_e-3Vy$;PU~?w4XkeDvT8B1b zIy9=ki7X}vVFzgjbY_LLfMzh6J}PA8W?ELrTUO@R32};^HHrn=W^ZgTu{;S7+rA}| zBNmevQ&>88RhHb~Zuwr?lD;RJop2 znhnm965tGm8bmM7(Wohd*jK0_KTl%to*=_Exar3*)EZ6W^!QY&{O^So{-5Ng>{8cl zD834I12xBS4Ea_%{U`?OKcME~F(l>`ZZ!@nHAeHG;9J>Iz;;g~HT=Uc)xe{{gRAn0 z#8$r|%k!DBY=fmQ%2H_UT8DE-qEMX?pjkYo8i_v=GtwbKc#h|1zqIx3)@e1lMF=>0 z$@OW&1*rwVi81<&Mjc!UsOtIV%PCSMYqtzS?JB{i3c3K3?b$E*r|E)G zHd0%F*-hf7jM9wb4dQ4txl||T_|z+o@5~*zIc#?fX1+rGCLDmY{;eN%R>WtfiVG4_ zW`~X4wDr3Xo5=_(u>1Egizo%rSE!82JXZlo2n^{&QXygf_rV{vRGKYP69k$Iz_inC zkOU_f_1Gf8u$EHD z9YPaAefznQ1y1HJ*n%2rY@zRY3(P=`YjMpzb<>Dz>bv}nkkHu$=s|9s{CH1Z^57mzxIu&tJ1%F_v^wp6f&aQ*#>;|=>3;}P zCUBYlJ;YyVB_|QC-~J=PrFakISBizP^yc3+`USpwzfI_ir2=2Dqq8GlgqTVaIUk>H zwdWy0jdpyhIXu-0NXqh*9 z&McbYN@8`fSfr|MJp8|e~j6nE#3`5q#>}<_OV+mQr|RjPrKQTmtX@t0Q6dd)EX`dAM3Q#Iwn z_%T`;o}AncR!>eALls@itGhoIA5ORInE0`_ZN2RGfdPU^dFo&&`xUj*dniVphG$%D6Vlkz2Y zd-4D#DiVn4xrw?Wg~IdsY?_YaDv{9{w~$Xk_8uu zthyi5`s5edvZv~N@mAWO|2s2T43hvx|Lc3!=NsCfE_K z6WGc_%8?_#U&f`U^dhMs6yEc+NXAuO+!zCkK@O5BP^3rsKIFoye0B9kl&|L%B}#t;_VtzUg7JJ!ORK5z&1&cZ!BO6#O5F(0Z?gD09s53kH#z@8 z?AcZzF~-RQC^&hbJwt(rqSG*{q2UN-EUb8K!?#b@X7`m<{_&^RPa*peWW8Hm?Wpp- z!)Lqudv6c_{_^STpWq{~R{Ha^*Dqhbe)Sewi#msI5B4Bq%kk>Y>Bl!KLsj))@5jBr zVGfwxXGePnZ=e0Ndw6IyIypH$esg@fdTKZO@!<7e_N~g8wE6hW%Ic$?Kc4nZPP)f$ zx~HorCm)aBe1v~{Lt}Ms_x^UY_v-M)>sR(nsR!KwYxwkF_u1aty}#}6ik5r(y9c{R zuMcG7{>PJ(!O6)0niv5<$ulPT$2`f#WGnDKS8`S8JQ>dcgeb39g7_9JreqinQddJG zQ2{aXC^^=bDD|Bpd;u{qnPaFUWZoddv7E_e5>1kKDbXaC!H)m(>RF@$ zmJa2XJb(R{r$6mcE-ZBtD{mmC^6lgPVtBE2k|`G<3+A1-PM9E@Z}5tJiTsbQ#`*5e zvP#S;HFR#yi4GP1Ta9)}BN9(3R}$qW$s!(y17olxK-@_>O9f=sa&$1^p&Y=}1=Isq z13Kz~9?$A~9A_Y3#W=|mS_GF6P@^Am5a7fRnx${>?IZLyUxh&6(cal#bKv3P zi73vCo1SmslD>3jZN^5H2cirh4HVk*?U-$1Km+=5t4Y4XfK7-s)WXK80m8U(o^oRn zauYV=#RM_$r;c`JQclL8mvLzlR!fd!TX%3w<2>(n5yv&*iP+&p5 zwljskJN=#+@uc?kCII9^idRLQUWnXt4p9u}P?x&mj=uyK=T!J|C$$V#9PC@MqGbvvoi*hMv+&Cr1x7)F=qdPaJ%$!uqvs?*J-=2bd zP)-@2f`@z28eRX^l*&uro|H8iKWF4AwI*aUTT4vHYU)nN!^n9`w_NSCNiTsM9SEP< zY?@-y2w)*8SRV0_`uS!NO$8w&bxy~f!H|qAFxt#;RcL)1dDE`jvLAH@qHHk4zfOCC zP&T%F}cG&C~l`oVM0qlpP`tW$edD0(MB2gdN5BHTea1P+7ldUrTAYomXy zLkmbXmbo7F=M+Akut24`l7;bFf=UatJQ#F(oW?c1fZ(fx92;FX=K@`+Zn=x z9~eI#R(dEq)HpmVgpsW|I)_yk*qCuS7j3$a? zG9M@1?l_-L6YNykp7o5Jld7q%DRqiInX`fZk<9LczR^1DR?_I&D(-HyVt1IQd95~r zYasfN!FxVSO8FDw8tp-F9yF!nW;dq1;V>^M(@FL0CK7>*AYJXH5Py{biW4|J8~39#1cX?IT3UN^+#HAUzjKP+d8gxKIt0$^MSAOyJf0O7CWW|%Dios6I zD~p4z^#vG`{j3rWxTa~F3$Ly+Hq^qL zgi=P*A0F19YcYMMD}kp7EC-G29GXwhDh%%I&rQYdBaL>#Q4XW7vAKWm!u=}`e*GX}aJjOpSt7=wm4u=EuE|d6e13t#3Fcgnshs*i=;(PesZ z`Ey()#ml(33if5yioW?OT8%c=AAhC#6e@gxf{6l625em&BN)vNCEUalS-tC87h?R` zJ`lfj*aROs1a0&kd^2OwMh4UgQ$s*gG)$@C>^MDTuY~ca;yS27w`ZUqz?ST=P55_K zf@bgp#g2EJGa!LFos=dL#cD7D6iZZh0z@#m=(lLd`R(^*nJ%ja38=P&Q-qi91#;FY@wGiBRHlUZA1-F$D+jC=ts z?Y0MX1kQEgOac*sSTh?-ea|zv%w1y|R9n0>5W73cAS@?g#lK{Dp|c4nrl4`Jv0>t^ z47n+L7k2XEdYD&U8j{D^;o4YuqJN;=^)RVPl8o7_nuEKwD-JmV7J{_ z!A!b*`%r?AO|+Dc?U@YeI3>TUd}{BVI{b2eow%^=iRf)!M;nRa>_ioa+ii8Ix79~N$be!-jskV}_D3q?MQAv3@;DBgp-X-D}XdJ&cS{)Ky^XYUSud60`G z&=6;a|MJslHH?WV{X1`9QQ8(MRr$Q&D54fxR?{*Oj)YOeycAuegx9Po+Ay-L@$iYR z^{5y@{%MYZQ}PeN`gVGiD-nNvcF%`Wu7VZN)sqD?$0l8voXel*hBHG^WI6jyqHG6M z$YPBt216_(RAINqe^p~S25*dIW@9xK_*XSPFRhm6rP&NCoR?t(`@v9~*jrly|L}%l zmaFODKEe3fDYK|Ak8kE4ya-&q51Pdo+<79vB>AM zqx>i?uYx`{Sf`{)hHD3uOR4eGn~;-=$D(OhSt7OeBXNY(&TN2KCD&IA=|RbBwAB5;v+bqxbqd(b=*&nv}}NsED`T>>B6F|bkhO8#k)F|{{<>wMY8 z!D;~=ZC<1oX%;I#0;%inul840`|)sfF+!$c3Vs0OpnEojSYgP_NuM0D4nUq-d3B)5 zNsR?uA8LA42P&X2bH|eErf!)en2dD|4d>IcoP&MC7f-(Vx*vV<bBZ6K{9+mm@ zoyfB*hCzK3>C#Pa2@w<-CQ4qh%2`;X4CafDL45#9m2#TDpXBc|HE7BfY*`P)(KH5f zv`i|)m>@m}CnPq&o9#P!1M)rpNR0&(tBJDYDV{9T}5ZfIr^(X)CuS} zlzciV63RYp4PhAGaBzZhwB;SgXv`ELA1^Xi<4vxI7=B_QfC=li9eX@ba%v;}XfqnE zt`1e=vBjuYXF8FV(rhB0mbuV^g7=*~8>v1Pv72w`{LYd@ZMTXRQAFgvPpW2-Un4&c zU!+DozIkmLl*0m1JK+QI1wrDBB7>d9rS?5jHoR%V(ueN*%(p48n=sz)jt0X~PX_Lg zMOfyw5Jq#X%Q%}rW^@^vW_mNwdVItAPTm~dzAEB(Nm0gA^1jn9m9bF`CUgM?Fq0&i zi6G;{IztP3M6VWO44eL%XW7P3SD9Vlo59vmlR#qJ#@ z_fHCGe|Y+9X#H-(URYFc_Q~V;7bb_n10&t%U{J@(bnDja0}O3cW;k$?LE;s@uxKQT zA%5l+SDsRe4&;cYK0y2Q$3@8x!Xxmqcp=2Zg;BAYkVIKo3RzMxV zS!NnZ70G#0sNst|ui)N}dV}Z%DRhxI=&LAiqQhvMzr%KzOtADKct|LpW_1T@AbWN$ zK^_$P>^FdX-N^`)Z>Rfg4-z$tX)&&pWkFXBmRI$3{#{*uTwGAc9p2IRr_MX7j)64s zs@zN;1`GRKNruJ8lg_kEL3WNWtl53YG0$R>WM_>&TZSy7_PeolZSb76V;2HP+S%*x zVxg|B`hENFciY%9mGRg(H8?)v)i*vVSGI2QQYwjQ_9 z2LF0dR^8{YRi}wr+E5%~);x)Zdaa3Agyjdl4MIru3^BayiMG%;4X$=s)JhD42+a7} zur=Pp~)I~U^%ZF_8@Ndq9>X3=;>M50ljXSAiufcD>LKLQW z;?>x|2e_@5TAl;JQ>Rm~Xw|wY$>*JygDJ?CM3XN5K+SJI!oO|61uUW~p^2bjfv2v2 zJe~d!r_*_nJQJgLunZR7vC7JzxXD;qXLQgo?m;^-Hl=vIwDNac$ur1JTx=Nn+S<$#S8b zw?it_&a)6%Kkcr_1V?=RHPq0{pf8J}La+hVn~lN_Z?Z8qHPS%b1G*4T81`iH4pnjE z5@??Jd8fBL7%u_BE?MQyevGp6ZsM2+k}=U8@r-A;tO=2c5e};-6ucy#S+k1aet$@( z$sscMyOVfUB|Mbh4==!5^yO*MH2zS@yr!Ai_4}q;R`pwtw3IZBj>GOgTx;W6Jg|A?Q~yf+ECXm_gx# zCx0qnb}gAoYG~N_6#2V!0#^dR@{pOewVldh)VCY9{f_uxVq`W+-BRu+$C!+m7try# zkd0p#&lG9puvjd%a7{xpZIh#!;r?(#28<%QNGjPww^utKejf~ST0m=U*LD4}g_@{W zQa$wQ8{4NSAJ3F6;;z)kQadBtI>K5Fx5Zg_VORcXiAL3krCR!HANu8Vo2~*Gh2w0D zsf!$-@?ip}3-4PA#@KFu?)aK=AFi%M=xDHpMiC&}9-wEYaM&C~KO{w7meKB6KEJw* zC+RQAW%QkN5k1F`gS@yHLQpu0?EgLL5zr*0$gk7V?`bI@9tVFZ$F_hR7Pn(n8Jwlr zMBbP8=$uP&4iweViFV|NO`-Kbfz5Cj6$zlbB0;;2iJV_Q64n|y>WI@Sn#TW3r>K(I zjK*g`HFrf_OuH@r-i84E@Yg_e+3iW$(4e|ZvhMgy{zMWQC}=e5O{$~xI?3l%muT`^ zP3nRWa~*mSr&G)`h$tLSr^)o5j>b8QVh(QeI0T#(-7x!Pv^%Z^FrA*^FUvZMx~MGa zN8_`ek)>fig~w!;WE0F|1+q|;7_bx>9d3?GU*NxDEy) z$1#`5Qr~vRw+p*t%qGtIwY-HUprnzyJE+=)*xGbRxI%;jOn388$8CR=wQ)zg{262}j$}tNHa=Qcymb7g<%m4gF|i zUDLZSU%a|&R~uGW;>7o!zqRMLvF@v6jm#>&IDGqZ_vqQ5_MX3e`giIr+O}5xeQ3P8 zUf%A$5(7K^!D)w9@a31*K7GF(S_q3+^5QTW!POX{quMI~*k8(MV|}o(PNqx9>?*UR zrS6VzhdzF+U-GXc1Ox48R{c=q*I?ppvTrD*uq$|k6%Y-M#NnkYst~d0;2)b^Ps^v% ze0=qsg60!>Zj};+NL{B9O_-wjtF%MMgoebXOa{P3E!f}afyz^sTh zNT&^xJIu#dNyVB_K|>=`?4g>nVowJPORL}m5bt3#mZh$`Pi*JHpSS$8--h$DQNCN} z3H5m=_e-8;`*C%7DAEIZl_+*440G4lA(gzj7vSX^=LNtz*?@{f6&O;DZ1Q#543V7> zqmD@fwc)5Ajh=0Sb!lFx+!pX*xHH6ZpFvuzBWLRQ`FQ0xdK8^``D6H^+KO6>+AXt- zeie27B;{%aQ34Bxn-A%wO6#)^3=c`nqCxSGKxRPR2_f|7GO=PdHR^Jvlh?!l$64hF z&jv&FiPvv(&XT=eU+1<<9~D|YTqwDjXj{P=!v#|{g~$Y6dA5QU@2~}4?m;w6ra`zS zdTj2bSuuQv$@xq?JTtmQvsKlp1loSo6_th6!UVBz=`#c274K>Y!uwdp-a>e{W);}P zN856mWN<}4aLQe4HuR})7Q)N4DLx^UVtofZwkhD*K_`4uc^CQsX1eyFLUG(!z}5)Y zX{O61Em_1CEx)(YhF)|)J4^BZRP@5x6pWqrpio8{bUgBKwAob3jJm~aDnJYV60-}K z#mKt#Rxu3_Ci=LmrAdQDgK;VaB5pcIJ>I^ zHUvjEpJWk5L;LNb%HM%u6fX8&?Vp;quPq4jLsO=|=K;D|?6XB}x1E-TT+6Vi-J{m> z@v+_*nCtKKg^^egr${&uBc6M})d+#m;<0G+N_gk%zuxjcq7k_fB0CKDbf}JvOn+)_ zAU}(w`4$=gmek#%+M5)(P3E(IqYY?$|9M_<`$ez$Ojm8Q>89~J7FfnsctcGpwsu45 zN|%ByE(IIB#J;t`L<_rc;lm5z=REmWf6J5QA9DS6|F1Xa{Po$}-z(etS(#OlRK6Ws z=y!FidCKpXE47R}1dnFf#){6+NTiuRwG2N(Y~QVqQ}jF;zeje^Vg_p|b_^xB46|ri zEW4J=Wyo|{lkYz4d(R}QgSP{g%J)hCyJpEXGg{P{TI2HWmjkT3vzCu0P^xY2n01vJ zC|q`p_CFViYeFF-WHkWp*HN@?Ux8~MywrLOAD*})LAVGR{QC*NdSdTR&}NV+|1qfl z6odM{A>+kQ;&1bdYRG>7#~A;&G{$#=6Y9`}cOpN3ug3l5^49%j$c+Az?fZQwziEEH zHhoHAS0*!O2#G0c`@~AWy|Vz@SpINf^n>^E#ZyMe*2CbQ+h!03toQ4@lvV97^|Rk> zvq3&~`G1#F1UpA%Yd(%Mf%e_fe-qaTTFigEBU*SzsB;&8dG_)5<0fH%F8lTG=`3L; z&N6ted6}NyMBQil#TFBL7Z=eb2}fW+R)I{i;X7m%ge0>`Jh2S4FA|w6Nl}J7@U6qihNt4cmpeN1$h6n(g~v{mC9mT^L$KT$h`>R8)o zTl7d@=x>TbQmOM--6_`sW}nYH`!3oD+D3QuYIqZj(0P_f16T+=m_-{MZn2KQ9FH#( zz4cvCTdP}vz5Yo6rI?{AKO$J*Q zUjWYpQm<@AS)R`%n-^De2y{3xN-?v+wKmmX;)6)Ixhek;#pr%vtn4bCPB-!YtY9~rJg1%E zzhob<-<$N0dr4u#?b|QTtZN%4$kX(p3`$MXWjf^PIpq+Um!4_BIT83tIM=qv~WKWMCS(sA=cf!xUSy;kjcMW;$7HNYqd ztA_h=;e?yxZ^|+~37Aw5fRj+p9?FF}dW0z3|8EQ`iom|ir<0^GaIjSr31E>JQhYYJCjc)07DI;?^Woxo%WjKp~^8yaDOPo;M$<9olt00u+mf!q>kf4L^Atxsp~Ri)5Y@n_NS-^?qH- zr%UsLI#X+&&v4FWF$Qv!OF>zu#D8+mSjaG+oFcijnUIHN8owGzxSrz~YxtzjVjtV?!66?*yBQM1k<5=YWn~QcvJ+MZRE#xOeH+Y4_Uj{>qb;6{+akFMZwkF`k+)Ria z0-FbF2A--GS1W0}P?!MA3aGFRU#QzIPg?Bem$vK$D4L0JEg~cGfC_u?Q&M#>qv{o< z&k&(e#+QJnt6bFs&jhSN82W5}24b}NOa`x?myqOSUa1CIUa@>Q^Gu|hIKb7UxtPIB zL{B|AL1$&7UllTrMf~_d#acwgoZU1ev$+E=zb{= zE4bpBeOU2zW!QBGX3u|`YcQ7IrJPZ=5?zdbZq6KdZIM}zs@pTLch)K~jSqu|1#?QI zHqmx3*E37VdIQ7043CE=CnqPT!yW<%bNjY!*Xlr|=IfnY;n5deB%1Y|`dM9qs{eS- zeAw&D)!wdXgs zN#?81CY>5TgM}N!q3gjG_pstJsa{-PCzBM?7JHXUy#Swhl%t-os}YTJK#&(@-*U@X zxF$~Ko|uchzzB_M&Rs9rW3SBYVnW4ThbMB{xjrej^}!J)FvGhF&*T*TzL6s&<|!`f z_udMN&w-6gGrN}IVxEzoy<@lL0#}{^XjR2c$Bhu8d&7mb>TR5KJ4iFu_LohT=q;Os z`!b-6xo%L>BF6y)x9&xiK=$<8$f|F6I`FMVO|)v-SDrlnOWwlMm^vq}l=jx^ZoH^5 zO(o3NO>shoqRqj{SZTl(Yu!MFuwB>c2B+8-OV!TQeP;E4M+aC`wR6`P$b2fN*tE|) z@&xV(!btsq%<s?zKA%p7%Y=6Q^QDtp3A&?bZ>EN*2$W8Ydj(oItjTlCN)n}j z!Q`K_G>yhVgn|VQ2+*Z@#s9PSFz{F}5X0 z9{!zfGpt8+B=>9pcodA+ZFeyWqW&_Uhz;-C6hJ)Ll$>_7upOR>p4jxW;ubqDfWD5$ z^WGB0GcMgmHq+lI0UoNcDLvggixG$qz-BT4QGBw z(F7MY#FI(aBbe~>S7KJYFUBDQj|&Vk?;gRj?1m_E>Q+P;=Z(y0WFr5Eqp#Pf+yu=YexS+47s24JtVC0zlSOJ zE!!it3^GW;C;Zo9$Lk4rhkpOv@#0Gp@4?sAc>b2*8V^mT@wbkcvJ^G;E5t*Qgh*4= z4p}cCmzE~m%v!g$JMQ`^bMfrIK74WX;;(yeU%dL^#j6)be|HAZNTsQNj(%+Fp6tjq z?riGIK(qeNxG7XaXS!;jvtJjJ*`BxFX=7e`vw zFvPDFB?dNtvmL*7oEV?bjZMdh8q=dE_!C0MEHPT$C}JXkXfgt(;2(RZ?J%nM9Z5zI z29hZAQbjd^Xr(2yXs;S{BC!AZ>y#qb`&_BSA^OL^XPBLTnYez9VWOK% zPHWhaRvgE@#)f-a7allB$1thTod}D%LF>thALdicsDe>gm36rfP>~vx&J(uL;`>Qo z*`3>?fHw>YTtrmBSqzHotk*hhG)0IO&AfqR{oh)Kk(G@bBgtB;{tWSC#g-ZJFjsUY z9Uy33Nl|3^AT9T@JjtqO`NR~tefedyVo!WNn-xh}V!iVBVE)`vBe`_Q(4Xn)_E(1| z;7$q&+7j`Ud{&i&@nq3*2FA-}i=uFMSSV-9YV;W`Q9wd-@V-oE(LPZ|le%5)W(3kF`;W zG6i#M9qY)Rz8&kxm^Z>lH|^D;a9eLHA6FAR6j-#lc|eCkMj`d4nv)YiqZ0{HtKds( zH>ZuOHw*SioVo#33@23Q(`mhlsz2_(k2W!Pz_VA}Vbt|54r}gNw|y@W*fLj>e8I_1 zRbI^<i+Nj^{kVv7_U8hX3=cx^S`pYBE|rVKy<$^ zdt?K;i9_$>W9ldZdWqKW@7*9ZU1_7V!kjx%ETW2B7I~J> zOE6kMS@htOp+dZ2rjv`Pz7?q2?M&h-?(`!@lE|E-jBW_F*B&rrJY@sQ9k3mO^k+tP zUMYl65b+Y8&VVFBLjf~iczd0Wi~Jgzz4pML&|-_0AgGXk-%Wq&j0Uxx!sTq4&eQ2s zYQgPy+V~c*>=7%{#yxV0J-;_tMT5GzD%O9ut*WZr5k97t!_apft~n`Y-Z9^DH3HIT>=eB`h|9<;=f-;05JTTRbKnRk3k%79qzUKHGMe<^#xLJ znY>HD;-8Qh%A-P@j6p{jU_lTl1an)V)P@e`vGB`5GN_WWqGzp!NeV5tM$WdHj@Xhivt8gQUnv=1uig_{TFU-Eh4Et$Ob&%UEtuuMePVivfzL!rnzHNQ^t8B(7#{j?~Rl#nMXgbz*$~V|3*WnjBipwLUX#vQjds!eZ=Pid zc;dzd1hbh{mtgKTo=;;!ei$e&t)fYulvxKc7P!~*j)t}Bf3Ok8m-t0?o@Qxv^Qqe= zHAX)HJ>_5Y_uG+Y76HvuO+VpG!+2k%&&$5aH@J*}bb=uha5tKtZRemyr~`f3p`42R zT{{!+DAAhPSqRQN7uRQR(QIr3#9D0z@i3%2_mU-GmKU?dq+q?T;_Cz-E;RlSjR(Xr zY9I(t#ksU>Dl_#I)tIDcsY5ls+|6zR9W;=8nnwD)2!HpO)&K93Y*WpIwjODdm+{O7 zJWbcNqUK~JB+Qy$e%)saS)qsYYaT&X?>8n>4$T0-VUkx07?vO<1qq@zrS2_j)h28x zb3f0&!VdE+{l^@cb`{BJKAWcFxJpc8g-@f1K)?aniw&z39V!VvE3d$M%auFPVNY*% z&&qr{uab7;$DN5Z=G>^Zf{Czs^e(0?LrR3}QQw2X{k8s5NtR@G|vQCfrKI@miA| z`$0Wq*Q0h93u=pivTCwQv?o5*Q z(Pmf@bRBZ0UQ1<%1`6`Eih$9s#a{YFY*ft^R;{cnd~4VV92GYZ&AQ5kkuaufS^d>6j<4bJ__dbcTB%!HRrWIFzLiFENut`EI?+GsoE7xmdnRLH%NbLA)YE?q(0qcJFt^ zmK+svR-Wg@^=CR&)_>q=Eo!-v4V<6|c&PSg-X&PnfG;ETv^X^_;n61?$I`&u<)q%^Q$f7gAefk&)%D_w~Zr0ZON_{5pj@Y5uErptYT8Fx2(d_oHN{!$Z&yx`+oEHLq7{6=+c@ zyOOCwSHQyNRF*~B-C9W;ic(1ol7y@Xo#G|20(u)44IqPM3;~iS--{nh$T(D#<2^_d zKrQc36T746tsodH#Hu+r{ItTA7^U=km6^<+L1soNWv273aj5Z?Z=!0xF!Vc+Uv_78 zJ$SloIm-t5Q?HhmR0?05D45(i6YehUV?E0BVd|%?@vzjP9XbQCPD7u{<@pzZJ#hwE zwLU~!Hw)Vf!SP2r*90p{XM)%x$0GvFughtuep~^P|`8E!j?%@De^IuTt1|)Aa*6+6@MSRs8E?fA-Em)W6Dh0Yz zC|E7n=U2R9=B#2F{b%A>D#?~znf8mXY1p4^H=@k;!sYuekSX^DhH^Lv_ZZM3IoYnn zZh2BWk~Ag?>dBZL0G}_iKhE<*mVYZ~ivwfM&v#I0xhcAcvS})Pi%S+;-tuO!R-xaz zOpXUlywv^eZ!z;^&$gR-zmrK9`paMvTV!o9+vH{`j9kxVyW9d7+F7sJX-C2-a3nauO zF1~ZPpHkqz6Jz^EcV&N1jw!y$h1sLbec1vh*>)6z#%?JT&@XUJk-6#maK{t{7eF6$ z8$nSI=DW36e|b$h!71=Ek>f@n$3B}2QC_EgbWeW~@S?oyZ8Fo(xf;6B+l}jlU+62L z&=0qCyHnsrVSf$JDs-N(e}7-uzk&aR`+HB&=jR5G2wVCk_y#iMwl0moKrsB)8?yWC z$9{J^u`k$zWdx{ys~y*`I$313rZqO+zj${xe*VHbJNloach<`@!!+K!v)*Riq+aT$ zjhEUr7hg~GIgxCy=~eD?Kdem_%kW&xo#@)1qLl#S?;lR!*Thc_cxr{0yWv>hL&wed zcDElmA)NMKIN}OTbpjDx+nfz6xQ?PlIH=dJ+~|6F0TPZXRp_HdY2bX=b~2!LCX(ozl48y(QI^fUNjqBsH84C zJ9_i}Z1np5t9Nu&6wV57%G|rMv_7ti=Ju#&s+k7+ww;-~4xlHh=85%_Si0s;j`ZQJ-)uO=5K zGDYa?SZ)cxx$}pS)z=$csk4?lAG>+R{Tm9*J7*~WHu~$h46+XzbU%$(#E1R=%O;$AIWX1W5a=7L7i-GyrFD@$|5tis!0uC zzR9vWeA}3o=X-k1p;i6T^GY*;e2d2WDELh5Fp{>7=nX@-9!W&b4ctWg1=J3I*lp}q zYDF{l_!POi&D#Zv|Kj%o(og0fyclqDj{D#Y>mtc7M?BcpT4=L0RjIOkxlz>K@L~bU zdLYHWv_1RE2{LXvFgC0MNVxH7%zugp1qw|IRkK1m^ozZskL@r5Pb+28GG{kpb{Hx* zbe?;QWyG+$gLilYo_)gq1V7^fQ%91Sy_D30hBU5p`p2%&YW8ea#*$Oc;8kxkmosRb zSEeG^O?thax2u5n55h^{E}|VLoVi|<6XS;A(h2LG1{Ok*J5x7=Q;i*%#5*(BbA;#b z1kTi%%&_G`{n7qi zTJBA3&-1C`t(^x#(LN}pfIH?xhj8q6We3xl6Nb|9OtL#i_J_v$>?~%!oJHGsf8xM<{Twt|Q_3pNcMG zYgEBr2j@<3kq2MBUrdnleG$`V$ST~Urb z3RH;+*HC+p^sI!_sLJ?c`&!ug5(I2;8yC;Z4Twl*6bG5^rqSVq6MKP1gtBAz?|-@z z*EK3N1)6ScQZ?Af$ANSUll!yD)ugZ4)^+62-qy_x5kpD)Kh~*vbt<_Ml<=(q71VWH*Y&Ft$fbeZf-p1WjMs;yV+2QE~}J~L#K`g<(axld!oohcVQgoTyiA)EFj zRy8h&w?Hwz@~Fp!zv-8~q%~NABLdT!>xO@|XYTY!0<(D%JMe>NxzTDVmg3;Ps2d*P z4^DD=#9AOKpLXSOE>9mS8h-a{9fPqFUK?urbxH8Un zghJY1F&ibpv=8OjcE?ijv-|8qNi@#YE{* zEfWS?HVyz3gQ5&vtZ(82Q}oV|ZDCcEmeJ)-k7``S20}4EQY?HB0{(Cuw6q2^SM%nBk9b+iD(LexMS1K@K-yC5T^*z{~xS zFQ!i91ar`5eY$q>_CA*$`P_k;S}rew%A|*J!fK>uRwXG&r0i4{_}iViU~q7$aR^eh zgPDcrpj{rg%N05e^BI0heJMo&08>f~%P<1IoP9j=@T5Gy3zA?E1WZSibS-W2X{v-3 z;y+fZKR&I-Ea8-?zg`!pqJ|1VX%t;M!6!FFU#J~R^|UHx{uR3X9f%UOw8YOd zC00mpK3om7HQ_+;v$EVCx=u*_hdYJ;ef0WO4VhP&A>iE{{(0ghr6;_M&JI9{O%>J@ zHV48Xbu!DPB*y@tQmInOD_a~012v9rh~IL$+KM>LHf4~BI;_dbLVZASB>{?ggSxVm zrTF426bb*%mG&*zC`ioRFa*&B89P^2`z7?fn%GHr;S)tBl1FJuX@{Z|!llkqNrJ7& zNi}IW(N*HDzZBuZnSjt>i<8Gm_LIj+b6f-r^p>1+7b?v6nA9(p?rh55wW#Z%9{VHwR25Dgc%aUJ|jOu2q? za&l6?s>0P+y8T&+G3eB;YT{UuSlzCxO{l_@K2308&t_>hIF?VLX*ju(TJ&-r`l_nX zk~HNpCJNxeU$+@SL&%)9ip=Ea=9K>RJo3qa1hbLhbzSMme; zz4&lN;m@D*9vP^GGk4;YcUqO>#%b1lj(6$DyhdJXuF#Lun5ceAj~$WK?}Y%spiG}S zMO1=A%4(}c60eO;MV17EsQ|@DE|t{jAKx{IoIn%7bND+v8z~+9;}Vq47B58gy1HzH zn&f(1)VpdSa!z%Q4?doIXS(IwE5yHiqM{J_F{96*@30p7bEljz z!+0*nd4#eaF?d4pDi`I~TeA6SiAjw)$VdsgK7TF(PA;4r9Aup=D65sWaY{Fg)0Avbo;nN1!&u)FJD;jz=M(Ra zcR*~ce1lz)+}7x{s_K@YJCeZxW0aQAFaV#ahBbeeW6Wk;Nmx$rYwocN zk@q)qw{Z!7Q8o$4oiy<$DTjRg6S!WIYO!uMy6?TLc|<`Ci}MZ0^o9m%YHq*+uc&-NyI3Vsa-ARF8kR9LK+ zGLltt?0VCTkVWPJBvVR>=_hn~?x_=Gj;53JAPr6+61VUs$s+J^TcZ~XW$fVO0ht?S zgR)3oInfY(swn%%7=_}IhFn0HL0g|^z%YBd!Ts+fZEkw&O#4c@U{*70dd z%F)Fq!u#>3{ADkWrp0a`3Yl{(ysuSfrL_k6TwGoUNojVNP(DqQ)q5LE=63B=UHcd7 z`pgSFouR}kLVY>I)09$Tz6oCLPP~tHAm|p9V#-(|4LKgv06@yC8y$pHFn{LsCSFV# z8c2~3r6y!!E)fW5Cc-$0&`g-LTbJ=N3F<9US(X^=B!WErA{zkhQ*4qaStu|G(Gusv z&*#tLZiR_TuJ|YJ5%!Z@HG2WlkK{0!C{%dt$Iy$w$$)EdF}&VsKn<>5QlA%hI5~df zqc;RiAG=j?4^|S-gi>HdMS&bgXci54?F>#s=r=Y_KNOHCsZ{pW5(0V+jK`hA`!ZQR z=}d)|lc6R!YS6rvgLqDnElf^EGbpUcl!X>bkBqGA44zp7R;W{-+Np$i=aP^yGk5m8 z@3{H3$+bJ129B2#g}e3%z7DBHqa*Csm*shduew3O@*Zbg*NV2YPU}3q zpPOg!0A$1k+!)5m$<1&N{=lyh6mw&C;7<>Jj^XDRel8oWQIkF$3YrwO8Wgm4=`*5_ z1)p}865HKf`gG{irH{dhCVj^E=@CqiDsA@+`i$sfQLNFVPn&|{J^FMpxZ7*ehsxhW zY&7=TO$zRH=+nnfhhux#rahAyyk}vxdt(lcTkz?$DA?J>)K15u50>BQQb`@NO&@Gi z$Kp?iK9s|v9Aj!=XFQ@0#&(+x`m`{yORewjniMp!yzUUY*d0*`U21N3yh|T~8DnIR zXrs|;b)g&_9)olV25B04h|{Lu){$dw|-+6gZ*}&J`}sxpbrgWzk}`V zGmiQ_O6~1&a6}*MYQIm3eHz@p(W4J_wLhfPVT(Q;j-|@ENh3sa-=tzqiZsUrYEe_h zRL+<~2x>H_K?csFhS9(V88jkBlNw|Y&Wsi{$Y@h)yG5TiefH?np%3LC3>kZU%D6{$ z?qOq%E{(TAQ^jD^7(J6d)Oe%cqz^ZRaAPoT48n~;xG{#*lp&WyC^85|hDo3nf2i0o zf(#i8!^Vg{h=n0xVaQk*5*CJxg&|>KNLUyW7KUvuh&nzb(il>|hkG2No(+jYhF$vf zr~t;okhs8*r~i;BWY}+UaF0GzOP^r-R7>BY5B0&IVvSw;5OEF-%3*Nngs355XvD}E z5i&*%0vZuEMm$SLgpCnnV?-SuQHMu72}XNZ&!|ILsly|h3M206h^E4bdpe??j<}~I z8t)O0_lQP##3MYShL323M<(`sL?b++5gv{CgTrKU-%RS8$$c}aZzlK6Y#a2!V$EGF z*4(36ZxS~(c@$0Rl*z2!B%W?^=gd9<_c@iAm^o~7a7Z7jXGBcL9AO@-L7c-PIMf>>C>f8k3I%{ur8}low51^)9-T-TW=X$H4%?R9N01lmT1Q^ zhV&sWZP7%uXd+rPOcr&_;*MD(Y8Us<98s)^b&eZYz?eHW-tE$d8aD3Wc#V0D93#y% zSqNx0S_XdFWBlxK?A{)I3=W$3!SQM~I+WPy(8nT}Zi7C16zmaf&%%$v<&G%ps7oIz z*5t%7HD!#QYqoZK^f5Syz%6cJ3rVTj+9M!tXp0)!>M&4`QhS`*qtrg9BHo*=VT*%% z^yzUBTizNG(1?Mk1FaDQnbZoCf^D1&%{I-2W*ZmLX1mF$IHQ~G9#!AQ1+3X-)R0)x zY>ydujBVW|N^W*=5SyLOn1VP}nw=h}A{T3RxTuarL5oJFGe#_QdBnRof16#Jzs+tF z2ddlR)HVfoIk-nbMrfCs(d9Ak(wKL-M_nY{X4mY}2Vr|$a1Z6UW{+WeIMJHDUYCN% ztC~HWAkE&8fe0skZh60nL(*pw=ywoB{VoS_Eo}C=f<8{NW}l1dkAU4Z4KB)P;=mc) zIAag<8aPdxyrea0VQLO{Ik<}@4ts418V&kb^uZAs4vEx;D9JTPT-~UJC5=cfYmRmi zc*ImYLYcNX;+BtyYDZR|f+!?6M?9Lul$#?$&1j7MFu5Lchy`0591{VKnE=O4tY^%f z9`91>E~nB28#8vtdtLhU=`*Ac!w?ycso`U8_!vdL=9t8+=6E=w4_pb-O& z2#9&W80F#S*yL1`sIL7*7SL^gDZTi*=W(HOCKD7 zRE|M;yd9G>L#(&0R{xMX}YSIn`?7 zaJQILTB8=Gj@lgDqaXv1aO2b(Swjv|Dz4D2(HOhI#LzNZd-Unj2U}`#xn_rAaXZp7 zdmM{vZp$<{XmGH{L9ERja_W#$hnza3)DfqSD0RfCBT6+n)udFDQ&IeASuNg!BEH%U z9Hn-n)udpnkDoRNQEF~C+C2`A@UzRQ*pqgnZ*q`IHaHcRnl>*r?FMe9+D)#vi9^tC zaz)J^1qmtb<`DBX#~h@*yM**z6IYR4o`L-_vSNc*TVod&Ut`zgAToJlk5fCynT<}9 zgUvC1aD_HHts#DPIT5#$MyETbU>{536~Lf9qtR_5cQv|HQn%HjAX1#s?F}i2!)kPC zSdH$miyzt_8@)!8K5Y)}(MP5l^chjG)xr-qve#|nr$_bnh7=rgaNMQOkUrRhK21rZ zkHl;AiCm373TsBc!@(|Ms^8nCAc{^#e}vuaTMUGA#pqid4pQovD;Q(1c|kB}pJo`` za$~nepIr`O8x5}BK-Gj{bPf7g6pKrMVW6OD7)S_)F(lAopI}g|GYpg}3}ZyKm^2WE zNr~KkgJ{QKbQ#F)j3G$`#<0<$PoIO>onezxo0Lkd-Wawx*yA7pGjfM0m>9!78pI*W zcgC>GsTMYp`IFg2!PaDIF}pa;&F*MOK^)96uh?S)m*}z4<)8^Ocs%4(>cW^eiQ{3j zOCOUyV~WLr8S~b0JZw`Qn)Tx$&ie7NZQ*CPL7ygl+Vt6_Pls|0Dbb`#X`?bWY1JE> zD3py&+Cq&@6jaCNm{X~D7I()&iDPVasbmY+pYfR3)p(3Mq49W^gL@S05HPKZV_p@< zW2$IODw^>aDS13LIn|_8lT*#n>1V)puh9U-o+-y>)ELsoq#%yEIigWFN7x{9geWs< zyJ?QvV@lkm;2xK>N5LKkkzbl7%?{JVj++*zTDZ2Fmcc>X3!7tZ)EJqMIc`y-#;6W6 zSUOk&Kn;aCvgEk5=(9_oJ*{I+3br_ivWdw{y=e}q+en8d369Wf{4{wan}mzz9tXSh z=~J*xsoYYlJIW)95eG+*Ww zdq*9P9tV+)n{- zMl=R z7EROYwRyZa*w?6k+>;e{Q+z^a5G|VCxQ>}4((;-kT$GY|-5_FXFk#ZP=0=({Wae&1 zR@l=j9QWg{_w|-^EOjKz-S+9{V**5i={2dNy%tu$BA(gn^yp)7kbp@~VfJWx^t!to z?4Rnb8INUaOuaQNixZkUYq#Q#H4Qz1xG8J*Obho-uof}DF*O1pDv9~cE>U)eX921z z%yE}#xhEUh93E?>5RpmJ?{n@xm2FY1B_~2l@5tDai+D?4I;@yawMJTk*(Ad6O1Pnf z8>dE+=2Ta%AR}#x5i1$><$@yVp`Dtjdo&04It({b*B|QR$RG}D&^$G0sWSRJmnjD+ z%1zQYo1-C#C_@%eXn`M%_L#JJ1?zHBrj;NXbZ?`W5Ua@^bc3}CJqvk92N1V>~Wv&aTZIC51dX4j&1s6|Y!Mf{Uw zBdgWdIp8h_DU}d!wTTh$63i~8?NZt*-|Fxv5eKrE16ee+t!`hFS-O_K=u+K1=KFnW1L+P-((D7RaFBov z1|G`M>FE5`GCLaYh&f+J=X{cIb~A96KH5!^aa(D)t}YZ=MpKmr6gVTrj~X&TugGee zsztzveb;|0qkzV5Y824sVE4f`0(}nBdCuODvJxWpaAr65=x}Eb#hu39XdB~#CL0&f z0ex(1(CM?SLCU&-gJe3uXWJc=Y#SY`z_5Uf1;~0KwkVLM1e}_0JbL3f=s0X5L# z;#1}XO*SWJe>>}fHdWmwdLgY>!=M91W0yd8hnz^Z0DH|fb_qN%MwjaBahGUdenm3{ z8X$vakTL3VkWzVsj8V#%fnsS)3?37MtZj@jcaLTRE8QAH8k=9w=7IV_Dyznj#$ia~ zFywIbaLCKUkS5rW&6b8Vd~9hkqv zVH?u0@tV~b(KvmfIR*u3EJmb0Y>4s}$_BwZ}mcDt-?}D%6nSaA&i53aM)w7LgY3!5fy@Ut_$|?9m5d_=vhO zZc&hyuyLCVTiP6HtuuFFnP5z~8k1h8F(!PB86R{+-58HLnoSIDS`uTJzWE;;mXr+( zsrj1iz47nTq-9q(Y1!3HTH0HfwBWX}+3mJ90~a&Lz@>{aUbD+K3SA3{r$_p@X0L&% zy(U|BDhbv#dd$&>ZpLj%ez} zhBo<@HQdjU*`m)bTh{b7TN)0IhyTX5G-SZi*rkgDsG(VJV8e{C)i5b&5v)ZPFb%Y$ z{+F}DdAQvT&6?lZ?#8q@6?Z!u&2YvKFvB6!oxbr;Zsy9yU;@hZuHoYq`=UItz=rJW@v+}W7g1ygJieTC+XLqg(5MyNtx8N zJGw~?S-0$Q5Sd|)NzKkSmN8@9GN$tzS;kN*a#CZ6M@+_W%!#;sd{eWT->tn2H{3!! z$=GUfuth;sg^aB>8Pm|pJ*FdqO{O+9DaW*H$XMRA$N%C6IOf>gY=>i6_cF&Zem!#> zdB|?`|6`r=K-I1qv7cAnRSOwsk=OO zLnFtqXEc_D_O;=mV>pw~Z%x8bV)oP4%zhf0ozI95mq&Cy&Gl_({v(f6t*>nbWbtXW zG#=<{qec|<>)8m6G=m8`8y?djk4dj>(w-?XBDH2b2MF3i+Sqy}G9Djo4*ylhX(R;m9mTP5PXBW5MiW+{bg-Gqtt*XGFRYNjH?&OHpA zD3LgqN$i=V5}2iI`q0&8>t6pq#!xDM@APwQqY~DTGR^GdnMz4{frk0vmQ$R~V(@=X z3o2E}>C3Td)7oTq*tCg|Zgx!FaFnUKN5LKg_ecn03n0@l*|dg-efK{$TmNh}TlBF~ zGrir>O|xjSS&R>>!{$lU#ojKPBQe_^>t;!DnY|R-?lAXeyCI_9=8$` zF#)xVwWemtlBx4=T6MGR_dkYc|1m`SGD9>>81B-pJEk{lj23ESqqg+i2|XE$o|00| za0Y4bI8^|Wo^A%Q&l?#n&D{p7?Hf7f;p!rHmb^jlhFE;!d#60fd#P1AW71uEVsl5=_Py&W>;6p36Y;}=DA|}c5#Jw((mzTS%{3~6 z7z)VWvmjW{6O508XDKk*Pl;~5gRw*NbYJBF{GPfCAPohi|1gZmc=F6N(t#<`EJmV4 z%vN|b-77b`FZ$}ojh+5!y_gdJGU}I-&?_A~;PcsdicG5#f`xL;*KjdHVR4o6T__O8Q<3I#YXY=n5&Mp4}r+x9n#u`+|breyKoSO*V^!(_4bb`32 z@!L)8(+;5IdqzoH1$NyI(8(TVgI1$oGYN~n02}gMHt>OaAe!+5P|yV>9lMcoZU^xn zoZFmO@&u}T!Xrj>k#zFvXHuLfU!ybr zD!b@UQyDqDq?f}F1i>By=&AB%7bQPBz~^vahH(cuTiSb~`XamjFbBP8o_P!p{3VxR zy0}P*!WG|B+3an1U%1bnWpX9WC*qV3lBFNeQ=g<#np)4L{$ZHM$@p{6nyT+WBfR@Tb6y zVD=5rx$hc4$6L;wU@Opx?_IiA%gw;6Je;reC?%MWGbE;jE?kcgu!em8Ll*y9WKqB= zY5Z#7hB?I)i9p!3EiUrTjVcLTEH5vefCE#1c`{;l@72dt_cnRmngMs4r_ccO;N7WaO^WlruTiM|7SpM3) z*1llKvdpq(#pS0q6X_sbTUi}>Tv-7&Goz$H2#I-gu+jzv(4veA6ic$!@xl*J^Ok2? zS!(d$$jF~MZ+zF2kB!zQGQx=9t=vkbpjLR=N~x9I?Po^`niI?DDQN+Ow`b3^x42W{ z8}kb+M5hOWe0Crva#7WCWI*G3R$9xnLP505DKRziCX5Q|ay-h(7FT+aE^CE{pIm&C zFVzn_bO19^X^@Tlm=}4OjTCus%SOX!7K+OC3l9oxQ<;F$Rx%*C=4jg={>&u7qe)Y2-B@}!b8#n<2@ zxA=#$y8CP_VrG|QTEp%bJGoVrGy7j2rhCkar}rj>`EnMyFI>+V-HRq3Ky>iz-OSmd zs@$>EkPfPvGF6yCTDn8u6njP(Ie{B~_`;dBp|cs}XzeUUg>2ndN6FVCl9W*NTi|co z8Is^RL)rIQ$G+(r^7V23<*Pse^^$5~draPCF>{>7Nvd575YCpc7;= zOV@Lw+im9j+zH%CsVbgxo}B6M>YlUk)qU!HWxvX)^5k&_=_ij%Cy!GN(G?Qfd^~xa zX+>6{kvsY5(B@YW1>LFRMee2R1n_bZo(Wpv#9pLV720s4B7d3XD_<;3@I!TYN`i-L z)CdhmE^N!3h3!dpMx144MBvyT)K>7~p2J2T@s3M5JdsVcjeaP zrZZVQI}pnPflD8@thlA8-$O#!gh)l4YK)2!%mQZg?BIT~F+4SQZ;?&!I?>U+=8m%C z)=$3kcHCR`tY8+Tcu>;xQk6^IFk$}{=jsDV>>;yT8{L1Z364_S_S5=5GTDD*vacbN zA=y3sjYYFBlX&nI(J!Al&+a*OwjDz!OGnn1H^I7D_})EKmp9o~{Ot=`aqZmL+>+84 zt&kZ?Y-Gk)pE~~;>3fPiXiN61OFEfxe6^fkI6;|(o}xij7oi-X=L@@#gUxYZlReU} zviVYbbq_-Q*CL#+h4rsRW3uvp+X49|SeBi;2bP~@vApy3uza*e!}$$KUp8lh-kl>_UjG|DP!+-dby<%E9sV# zRj900lReHm3Q{!Dxq6Ml2W)oJyHY4fGW+M%=& zVy)%=236TtH54d7n>vHwZS&vw(0YyC6WA(D91&eRvCUn~Ew62FI&(td_?NnQR3Qr4 zAnIPH{VH?DBmS)Z@4~tCgV>t+D=Yc$r>n|WRel5AzH(Z%ZLO=XWbN>)oCbd3nV)uU zk(^t7nQXA#;C`80U|&W~u!fWsW8Xok$gFwO2!2@;_38H49J8A(=B(Dwzuwbyr<$F! z4<+WWfeO5-l-CDkuui}3U~P-x!lvC5y{EqkdL3_?YWC)(%8g1PFHKW%ahtnzWvzK9 zYi{7gs}{8J-iuksRki0#p*D%RTy)fNI)yzglnyfzKvH zIgBe*Zc9yse<{M$tpP|_-cJg(PVl(Y_(r3Xrwxt*w%x)dT>t$z8!g?5<#K|E$;Ja#6M<4y82IrqHL7f$LXFjGW)S@ch`zMoNBQ(BuNISPSIO1h%m@;Br>Yj^Emw;g#Ussj)HQIvr%QW&h8gZJtG zM%~LdB>LYYb?XaH))z>QmSoeUxX;RwQDvpa{kLbBg>&aWl=HPHCqePHbaVQyvV1)^%dxc64)>9$@K&>u$Bxsb==5P!@1}x_=LY zeYJ2_U%};6Z8I*faSn7avMEaqVi zk{)(M8Oy#`o51^8+@dhsm~>rfAHbWA61f54tryuh`Aq;h$-fk?w^&BPj>MBYIIvp{ zp4`z%-RitR5xuU^$+ZoZ+)e=fy7Ys&E$tyzrNT;bdu+WPE3@Zo_2*$D&8MR32(K0C z8Rly?oQst0*Hy>4s#-;;(LBL5DwQ;ye)SP8BID#Hr*>kB{@->E#NPhaqn)T@l@@MC zV49(xb|UM0p+9qKpX|VkE6nM&>#;#<2-M^GD4V-eVU1oIN0Y!^MEh8lJ9Pp&Aa=d0 zb71>=Ly~gStkg>xmaK?IM+M zDHASJ37$;wQVD@f2vP}=Oo&nmA7#SFR6;{0G*StCfJuoI$O80LfW8D+X$M~_mX9a7 z4i-}&d5#Cg_u#iL=UC?&RtU6Q22X@Qr!YNn4%Q-{odP#I`6t^aCpRVWOq5HF27KeP zDz$KVLG)LSBUxOitao#mOo3!(iSpbDL;K1}5ug*m;TEi5N6Ivmu-sxN2x>6j$b1_d zWs(bWnMQLkPK~{iD{XUvfLjNC$f;lM<*n+AJc*PJWO-16stCb7U#jUDKjY}X*_)D( zrJdI;SVVc@hLI92%O5Yji4uKfFVY44FFRXJ$Uz-TDn)TmtsBpvBW{LKw+rT`4dlYsuaITMveQc3#rPu zrPT_G-Oxv&G%mA8K(dIO`F{EgO6l20&4quiEj$zfTl2JQr=&+Y{tdL!J)9sV?Y)C| zKP+4?jBIZL@XSp%!qJS=f!4L1S<%PJCihg(8_e@a6+K}etd2+xCfm*HO9ZWm?^9>q zd4ouMV}#$|+I=$m$3>dY^gVZ8@GTSK8Ck;16vYfTuPDZtUR2J^V0dMeGehcrrNHIP_hm=Y2xBglM3i{NC^sa(EZpYjw>*#fsTKrMif=ZG%n3p# zaM5#ltO}wmkD#KnC0DtVgLi@=*p{ z$JElAL@gtz&Puc1?GIj zCO7UV*X}CU?kv~tF4yib*X}ac?ljl#Hn-YwZrpWd$A>o`)8!=IlcXfrdp|mp=!59p z4mW+)mVXsv?Om#(yRPwO0`|n3-+x88!_I*d{`^;O_;tD53Qrn&HD=7WxufElXr5LI zUo%mfLi_4QqqLt7nlvk&vf4s2UZ+mTX-o=yEtWRcou`FRiR}Tph(i3`UoY|Sz7^0> z&SM=&kEl&M;EnF*&+-uRgL4~}iC`Vcpx9e@ z!*=~4D7|avEG|fSL;4z3fR_p!{_za9Aejh4f%z6Dw zte}AP%1YyTmfiE@OV05%3B7+DHZZf zE*Yl1C~bb{NYA48C6B8R#)%#%-?m#A==P=S*|YRD6E2d^PC-V@QZ;6THTQ0{`5ai$ z5xP#8Gi108E=FUL7l@LZ_lxg(Z9(G?f*!MWW?Be+-m_gqrfEsYP{q-?QL#!qGzte4vIv0>#rOjkHy zjZ2~O=Ig95$ETZDm@O6&eG$6vLUNp4NKSvvg=Bqew_8k(@4uLwe&fZ2w(~^JtR|ND zLkpv->bc6jysfJ5sL$LBaX{HH&be9mL6o=;a4+^xyvNnYCy$qCx)ivR=;ZM}Wj;G| z=KyXp^noZxV9o-@h}UjdOQqx6+A%{b&;4oIss`d9_uQY>&d!|hr9WNHP*nyA192x# z9&^(`Uj&iR75JZT<4N5KtK#HwQr^kqs<@L+djgXtYfFZ$1(Pvclsph0eRqnF2G;B1 z<@0yq|NVbNtI_NV`yWd+?(hCAvOV!zIW9vDo)gYsl{+J9 z*iZof+JPK9ltU}=m(ch2Hk5%dTCS;cvAja9zJ$S;lgAaPaKfd z{sI$*x7JORQ2NQ^+T@oIhywEc_t6Lofq|^X0Grc&FM)XdDi7w?p3fxA{}?Y{V-`&^B&bIN5+ zK1~O}$tF%1)+V3mHh0{#(3v?CMl;OmEZ(cakVQ(=2k95F!? zvl@FDVO#C0fao`Nbd5DV70)lRC71pZbKk}psROo0?XMy5A7QPTi#wV`E3x=+1y;^l zS}-9>p*9o#R^r%SMBym`Qj&TPpkzc2(gdd0tJ9OYNV9-8rEYw@mY=~oO$E&3WSavNH`}wmtuRXcr`d7i^ zd|xdiO*68TO#J81VgtEpR@#>wu=Wem0fH zpCaxbX2zv9w-@EI&I(lKv4L|aWe+yz%d{+~TXIogVZaW-sb^Ua-L0xQ+@VrM;X?9l z1w-*oj70dF(nKxmM(0tex*SrPo{p78Yc7e@`B7ENjYmH}HYNaHK%l=T=7Mb8nGt5_ zN5b{e90kKMTOlq|rO24Ka5ToQGE7B94C6yXL%R3rPHTP5pE0YnX`m#AFd1~ztSa@& z;bZp~Nfvn`kUa%;kcbq>@Y=nMG%3Aa7jyeoTsR2qhDbX#SOMKg)V^nB`47P)b6bUZ z{KSrkv*Bih(UoG8&!F&(vIXQm$>(a$jT=Vc(XBTr>m^t6HoU0u$eP?FHwv-m$bn8C zm!0_{y5%vi45|Wl06q+zGnHeR?j1)H_Qat&{;>X@iuCkKcTVR<3|lGKyw9YF!f^X} zK!Fp|-h_ZsXp&|GISSQBlrwiDoP*08J#^MlnLghK+_-Aq22{>Dy}haW^2ahyA;Kuy zLM4*MYWDbcJH(>;>t6=1t34{x5uag3c<^e-M zeY7Ksi$Mhddf|}*UtCn)sShO|=keatb*j(1!ZU0fjkV=g^YbPHl{7l%;Aa~XO;u$U zlD;L+zW$r6UwNAqTCo#NZ!Emsr1c}DCWuEja6*6f(J9}6Bnm9MQp3#U6kO;mDyd3T z!UR-sdl7y<7GMSRovqRPMDl=qMD$54pJLzasJ!>XwxMZ zbu%&$Pwr$Ht9Wx51-CgDM}03MRhu^TFM)U>a7~peD#Vfv{L5E zlAT(L6ec!W#5k~L)TontO;;oWC)4-hPR9&4gUR@&GVW0MDup-@=Of#LO)XSYh}4r4 z4;urKS^=x`{k$^M>2e=Z0Pc5RBH*#xz%t|E`m*tY2L6kTR2DTUzOnUe z;>naI16^r$4>Ev^o5`SF7c=`Gx1{aCERiqV`6BQ?I%44jco`0SaeTUVJk;?lLupn? zi~}@nnga8eaEG*S1*BH53lgTWI%txZ*dFX2;P9s^LSNXkPxfsHRb?m?impLx^QaP7 zf9wq7L9`jS>7SQ2>-9f~&|d}<=cT=XJC5()zBov$l54+&ps;vceN4J);fDg4wJ7;v z>BQqmSiCqIekKWbyoDAkPEMDvf=*^GtAt+Ki#SKER-eq=9iLKEc&P?fAVD`2)?a5~ z-%bjl<0Pd&fqxa)a~g66ud9*h%C!FUseoA&qQjY*H(3S~O~r>>eLKAMCdmoTl@q<1 zEZ|&w;R2%{h(lD2V=3i3F>^jTvw>)WUPP~n zjv0BMW`BAsw`xSf-^X z@!n5%$a;HJc`TesL=0Y9#Bi$)65JiuWGuj;nX8VN@4gd{G7F+Q&&ra%#rY_iSOTtx zs|Br;Qw7)4k|n0JgBx~^u!_4nAv+G7$UK81pYMfp6Tq=l;LQDx!2X|vlWS*g;{gcB zJ`hLu!Bu)@sDU`EidwA}*q^u)D;-N*BQ8n`HOteA__Ql7K%$dX;RIQu2BdC=q|$UI z%h4phJt}Lk2^}CBz}bZzTrKG`!nH%@EwKK(Q=pLxCxSctxZ((2m6EK&=t3J^==1Z6w80-g9Ht)<6yaCKUd{nm?<<{auBJn0Mo+Y{g5#Mur8#O3q%R0NOi zle_#P0hdj2C`u(TItfdwgviu2`n*!rtGEJ1T&hSm_YkFugs$jqb-hc!TzZehq?9he zRwAAyIa$vC$6umo1ZU(|j!MLsA6Ms3ZgdS31Dkg-TRP>^&W=7!O4)gFIkTh4i91Ut zEW-67UrhY@9MAt;Po<2=2MGmqQVWV!Uj^VAN74UXZiuWk>=V3=W|dgnYrLTGo*c{EET(vxm~K9f0y=*EgsR_Gcjg_dZJXz<7zqM zrSDh8g&lCA&tq5PRG>{e9tTn_a{8>CwSURLJPsg9zvLJOA1LdmQhJ?kgctM9x zvZ^5uQkGt3Nu=6+Tzymb39hlh3}|Lh%M~~a|9voHG5sXsMu*YVUq*=n zN0wDCUAa-IDvnFDa0m9*kpugqot?u&>0R?NV zMS(jn%QHTDz9=R)%5Z#bhu0v7UA=WKoxt%X&M7;?O0q?zMAE3&@zT#i_?IMgyWZ5D zpozVIDPTXr#Z0NWp)Wp>^4kCC1OaTAqiZKFYY{jfU4I!0*bLo*&CTRmEZ_o72tM(k zg2=uCA@v&NWYBfc9&bK(BaDyBIuD%7unx7>KlW>#TI1)t)w%fp{;&UAoKyH*?RjKh zNuxTY^YM+?`p;0>$qANsa#F8X;f_O+d~)*h$;sWx$?6HE#0UcTS_o^5&Bl_@$jW|p zA^;>?mOidN1})^+i6+-h80#G8PGnE*$R<7Pk7yK**+>T&YoLSBp+q1+sf!LmXNT;_ zcb35{QScJ6{pd#TV0>6Ljd1`@^rvW#Nf9J9$DuR-sB5klf$K%8W_jjb)Mx(H73g4M zxTmoB`RD|o@OvM*GaZ~0c5p5$W&h|zf2E|X>N-h#s9v12zW}9ZHiMd)lZ!Q^McF7h zC*RS+nH1)N#8|!lqZ5TZSQ2wEozRFGho9*9%88CJ@~;v`a-_0{3RyPIgNN>CaDfF? zu1bZ~SZ5sT+Y%dX+Fipj4xU95Jq=)QjRZD@VTJjK8^?)1tC4?F&AB8Vi!JOBz)^jY zqBb-KR$3#iNTM>rWbRiQg-yM(cya-S$*zRl@I2#ZlD^d`W z!o+zPUeg_#KW@?T#HzT4$DsK|%8NN$;z$;&XcK|8%jt}3il?3W#`H&GLaM{7_=r|D zxTiQT_)Y%|jg!0S*F0V=rA}Zpm;`}+~ zscJb;@#p!pvV+*c3Mx30Rb~|^tV$o9;KC0bdH+-TmF5|+QV*P1t|3!S;EX)0r1pqP zK;v;`38`JWH*Z2`IrW*DRg%{Xd}Kesa|EqeHNrqFo$zrUs5ax*+vju?j2G%|#3y$) z19h(JMNY5?oG4*-Gv5cx)`PIF>KralB}tF3ewZwPDwn%0h;dA+yAlNkOjE5f>YulRT2eR1#Gw zM-9lQ=_*_07)QjJst2iy0Mxy4qXzL$Ies7U$VMF}H+&N~<&PC*Ss=_MNK4mvoQ_c4 z=xh4kxD$GjhpKp0TQ05wdz!!KRrQ>(3P^mcrJnnf%T?g~ixQ0NS$06gn&@ zsxpwG*Y}!;{eyBYrcnb)k^a^BEK&c`_3&1^z94$jA31$^i$f5Lg)vp)DvIlk($A07!{jKJ!Q(KFZX(+0@2Y1h znp*b|dd8e8HM6Xs(chdquhC(-gwZfg8t?+8qzo`dmnw3u7_%AdZb%lf12_P*C&@UY z$sH(Xf-7_@0AwM;YkxVL3eSN6cP}o4rcJ(J5ZJEg_(6E>dJaelJCkc?@?mGW08@jo zzF5v?^=7-%OZ9}QF_)R1>9gysczT_!;ittkD4%Zt&78y}ih!Q;$w8wbIPC=f0&K%Z zOA$^|JWb+5Xl^L%+06e0jA`!PfY|CS=BVBw9EoKxlc(uR+BL!nGb=^NBFBxc{beL3 z_7ZIdY)_m#e(%NCdpQ? z4dbb*EZh(krJ@{4f+b&e=m;XL;gieZPi}Nw#@*?lUN4c|YBnvzL~aG%GfWn}Kz5i- zH)u_>Su2s4hxh?DFuCMN_p29VyQN%u%oNEt8s+sSdz!*@JUi1%nzEYdS$;@~HVZN< z?@m%dnQ*GD7Qx5*T>PRd%J=BrC=q1@>_#C*HejM}!M!0uQT9 z@qr?Dk_;CPc5K>-ccoy@nntVoa8SHQWuA!lsma!#`=Y-dF3WgHyaKim%r?qd%FrlQ zYJEZOlEMo<)S~qvhdz-XrriSY%UC=?)f8Xao~kr$@S%r8qT%IocInQD`|=DXgBRfR zw0FPPrch~QvN-8YjF+b^Bz=jDfymA@B&+l&=z5Jx{u(DU9V@Lpg&fM_m#hzW;yEe^ zGCUEZ!9kYFLEli3FKy4hg3ICQOnQ1sVScgnViW9(rI$**(p31hI)_tGu(V1DZ!u4$ zF4W{jHa*nQEFr^!6$A6!+CkE62$XW{kZ@4F#hi@13^c_nXtgIGJxZd%V3IloE51ZWw$5Y;yI-hSLc_1}&8; zS$SVOG3sN@=w#-q=S~0zn3HhH8OklHPSD|&6KO{9AkpI1AKc_?K9**#Da%o>Xh>h@ zQuHs|lL?8Av{_W{6xM1rNYoOCo!rm3f&L%1X#4O9Z6 zD7+t~#LoPy8R=@%k6%4fp!mox=`$FVgJpX){V>u}xz2J2=vGD%@B8TeqANg(^pJl8 ziRBxxWmNVbc;A8m6aggile=^hnR$w1K1x%ouf}mIiqy&zgVh3nM+#S!$O76@UDJW%T?iRs2 zt-`(ld1~q`0%u|)*G?03l9o`k!XX)JEOgun;AvI0z?Et)QdSa@z8^y-AiWRHt$!(J z&S^z?sSo%ZurhnbuRHlk9i&cMI^tp(#m0H?zHOi%ki!8HRD|M)zQNH(LQj&?i?-RM z2@xOLWpI>tB`yne=?9h#)*cDk=-v{lJ5?u@>A`S)@1?xDu6k-K=$(2u{SXX1Q(a8$ zu-#B;Y)!lm9kH_`6%?Rcz}ZyOiX8~ND+EnoQ;fJPtMf#4rqM24hP!n@5ns-Btx^>w zAktVLSIM0L@DQE?x60m|l&d0McUlCFTzV$gzB_S<))k6Qj|zk7BSJC>r@*B#`k8WV zO+u)4Xvfc>ttb6Vjxgi&Fn*AWN*sveHwB38WZsQ~@!{Dd%fas}LpjmO|T$+eH0BC!rTU*qrR7NUSDIcPeiEC1B`^ zZu8PhQ^;262J_*`iFf2Ug9jX0yJ5I=!g_bF{|EdD`##5ucDk+hUa#BgcT1XGXnJ<2 zE*qf9J3CXyS;PlQ>OiQVir@h7_rv3P3XhDq{HE0Si(J(c_RO`(0#eUVdiu1kI_)c+ zukH5E4*MoRft#yS_j|b*D3;e1e5j>tV0NW^L~LkUt&~=h-Mf8;@|5k@XqqvbePOHE zCqyhkHn(iw62&yFSt#%z!(s0j_)?0js8mD{pZwqhy;@wn|}v+mmYeL9M}#r&>f z(y!84lZu{$Dv$47@Bu4WH^&AsNRMBk=W{p2btXPrCl?!F$-mSRV6(E0gRk4;X`66a z;60bgpP)%GdzOc0Is2{HY@mt@`j(pXXo0PE%fnzjKxfy$aZI8ExqWgCjTIX4CDb}V&TFT zcHlOO>C$H15($NE09H=nIYz6+M+Iy5iNJ>Fags zMn9u#)VQ9aEF7b!yNu+J(MCfVc~fWVcz#J2h3I`m-QCgDS;UI&Lp;1#fafhboCr&& zx%KJm;<}ZxfrV1+AO)2vnuOBoi5Ok&ZW+p1! zQBz3NN^^=b!cQ(kY5_SocRhD*&yu!sL`p?|NY|i61}^3&5bv?R?V^1RYqeH75;#cyxWWjfnn>zk7S}db%SJk2vqL!7VN#IU4V<1SWobH@H zFRQt@y>xGsqXJcb$7Z$YnrJZ>1S`E7#dm>u&c{}4)I3RjZ?p<%*?^b4V^suHChtA{ld^K+|*MS~Y%DVr}^wOkW3)FmWrwJrkM+PEy#e`F>bi!fH@uqx4 zK%5iLEr^H0Yk7+~tBhA<{iQMQ8e5*ob%2896jY*dKreWXLL8*YDqI}oZ60-zNNh{W zvgcYJAl^H1{K2`ERv@Qw_G4XFDdeG~`aCZlkCImJQHGGAfYSl18FeyFdP#Ol?2Zj= zvd}cF?nILkO*)w5uw+UEgt#9natp-5@4ic~)p&Y?!>OOg z6remlpMfixYC*d0j%^hR3dwh(r5G3JmS$U6Q=NF|&Ws}9{ni6r|16CoUOK@QaPm(+ zseMDbJMDY;)D-(-oG!sd8>}iRi<>#*Mh#S0wu7L_PUOHxSkU4?MYH)=ZeN1&!I2Y{ zkAZ_hZkTw-rxiTLg@_hMoK`A{IS1O69$oV;0qPg!_y`=Rhe_nA*xlf+g!QS-^_XG%Cw@;1{h zEB#EFDtIdm9zTY0r$~#iHT|GOS4YZf zBR9Q8>5Af?D#mEEY);5aE0Pbffzq_1;&^$?ozm{B^AHVN@}`|S#<9bF& zQ$!L)4aiGc{maN|eErRhgdOgP8F%T@$~_-nD~6 zE}WC8%Xac+#B8Rxj08Hz6h9CSbOH1E6eo9N)CJdDEF-n8pmaD8Q*Krgpm?RYU#}y$6$H~n@t7%H zG^(8sx9vS%0NbkbusH$6%i6BX<8D9L-1CeFpgX`AUXIW8UnHCS)ALtnM=y+{Kfyz> zC;YFTcsPReSFdA`^W&eN+{JlTr|0`Br+wpx?hQJj2Fb~}VTLHnGGD(td-438_11VH zYpW$Sy?OEeD4qN)N&d@A4!-=HYlC0bma9b>umHV<1HkW4-aaURMGfQ?$7np zB-(mqo*A!3e|imh&dY~`CwE#UtItm==SltVjJNM}_@6(gb13lU>vu;9TA$o0$XVSr ztF6_OyQBfDB;TvoXXfj7RKp`ZTO|#36oaX)TVXD)Jew(LRHlNvMzyK8D_dnYdrnO> zm`PWTf3Brbb*iJ5kc9bGPY;ze>kW)tU8U(auaBPp4F~&3>OYiTpj@dE8%Aj4g)v%l zFh-J4k^z4u=%{j4++z$?zuhqt}{$Ado~;m;+(Ef))HQjDfg zelVr5z4MR8PXCOeZ)8u6ow2=h3H_Dhm=zcx!%O3DXD_T*Kfe3ZKr|bzUGY?Oc8OzV zMzgADAg-1(TfJ<7+6Mw28lyim)z_g5<- ztFxmw*66wMg4obN)PFwy`HznPwpM)tneSHR%4b~RPoLG5yy<+$J5Qe-*3`NRp|PQF zt?@wA%P3n^%H@-jpDN{(llIe!-b6j)k1t*ific$0+3NIsZ>^WF|7vL{DIX3_PL69& z508IFdN?^b{Y+>&sBx`~<+tP-0XaIK<$@>4k2V_5F6b5t%Bp0h9>0VTxpR8o_^tKk zg)y>TTCadH@;<8c^y$+QDEsBf2&nxzDixPR;~pt#nh9ivV-6TOYYfD0uPW+KMS~wE z!RY=#>~%4^&x`^y8!+uJ9K3rFkmof7fB)u<^>$<&S!c#TboMZ#p_cdJ^-l>vpGvk> zfTV=K4@AF#0DsRf;qL>{Y_$;LA93Rd#u~pndt(#q~=u?$HYhbTmz}xS8)XIUL^zx-K5WAGCswUymw%fsknx61}_`*8-%lp^w ztbu4#)|1C7i1nBEFb6w?^aCyFZ=)B+OXJ=1*RRedlH&WAyssrc$3{u&C5Vy+L3nFZYlWKh z&KM3vpV1j<-0hWVy*(PeerpXxpW1(>rvKG?`|kP3c!A>weWYpgS%vw#_4f7IPtVPF ze>!{hS{uX8UaKpsU5>5sIAK#L0SfU{jPUeOSbuv5OCYU=l*FLUO$4je2-sK|nOXX- zW30?8Ju1Paw+K&KN}h9NqxWHd;dlesu@6X$FIB~4=7-KeoL4INeo7G5KumhX>hK07MRRf=DW z+t(J~^k5W>L^2GYT(8|q4+BP`N)Gj0hpFA+K;2XC%R7_ONiaWp<=G$IEAYAubrz_b z?sDY9HS$rB$hAQfgKPMTpY2*5E4RH!dfApQ%ex^dvcYMUJ!6lDc&LLX$;q9ok9-I( zovEXBOvV5o3YSG++aYd>5b5FoZ~^7#G`a={@Z~TH)a-7OJINeIf&LQGci)NJ`qHI2 z;ly4zn68&5-E9@)vPwIdC(V*tVC18&?`UR+*G{;u%w#MYUb2h^7rGhPTI9cgo!!U|>FE>o)!;ZI)QXb>O?fi$(D>56 za&)J(ogKU9wOqb_mT9-FKb5YwV?TRPD81sZH0;v3@X(%o2-Cx)96@`U9`!*kSa1dXx6B;!c5TN!yMf;9leP zWm(U(A3GPC-taRCu>(8In*c!Q7{I^}qSF2tKHFBkp`4fM(JQOSndJc?RXYES80U@NR$*de|t z4mcy9Q+I;-ioEilLRIMSeVpi;pF@!FhoXK=I*~eHfVgeq>=h>*d_gm)1b z%!rIzikz&i(;RLDMI>(s6bJ&~RzK7iX^c{y&A7|#nX9TvPqKH8x4(LN47Fx6Tuq=- z+hN$HR&xqVZ&<6jRo2$+KdTbzQQ`Kos+3h017p8~zBYz{m#g9M8Q=Faf$1 z1JP6^^h9I-m!&)T0QUD28;{5FWQf&pp1oNTxqb4s&l66d^ro~s`ClH|Af6oKRyGR?rA zg5E7>%RsWC`lG#Y#bS9eb0?{DA;5(qBKY}k_51~%N@%|F_EnR2xe=0rVZ59viyl<< z&bOqb00w<{*dh11bh{<2DR7*m77|O($EQj(BOw*b6;PxUKTzsc1T}~wkN*6amjnZQ zaeA1GVq$4mi*-Xdb_0TP6 zpVFxPaKO~34!q-hh{0e_1v`b2KXnY)J3y{ya>KwP?DV7U;V70>M*`3VUWfEOX{yX; z&#+V~Q8t(#n4|i>7s^ALv?+##N>&{2d1(x8qJ8N~hh7FRUepv5Jbk=rSxUvp$yCq+ znUt+GE$UQM3-uHgYPGCX2d}Odr>xWt>IEnaVe3W^{XKi8bb|5k_+hycui^BHYpUlJ ztvC0;_ye$x=5wh*&qa`iqz4R&hLii%q78?NIlX$Y7s^Ga29j!2!Y`%V6o=DjiW#LU zVdViTTq%COS1ATIkeM|l)vR2^q)-DGb?gsc0du&a`L{xh-v7+jBbRb_c5)gl7aH=I zl4@$bA=MVk@Vcz5SMiR9 zlkj&c>As7#Y*~W9xU-oa(qm_^(54s*OJxTKr~nH7fA-#ly^SMD5dAAjjn)7t5EpOI z6dMQO@r)0RBzM1E0yc_3kt_>9p{oj_*d+J2?-xg&l?8&d+|&Esll`$!m3L%hWMpI< z8E)DZwD|L>Xq{{ZLX(G6jKIkzTNJXUi#Ys}<N;L?P*U~!^l5xo6$lSf{XEP4jzjc z$F>dF5{UY^Skuond-W4@^$bYAe7`GLlk0Yl7wvXW>~))*bY8N{eT`$pi|Cx3%=~P9 zb&6Zi7fTnz;GuK%`o*8ZCoO%{!dwua)0vzchM%D#gy41< zWM-8odGGxW&^_bBC#u?%?AN;e4w_M>EwXb3i1nFrR(#i&3l<&WUSSpmtDRgWkVC-gq}oBVYUCtKH^-(P)S@b)iY1aUMw;Vm zrB)Z(O%oyA@(ARpG%6? zn%{XPFt()qymqT%=4x7N*T4vTzKZdIVW zuCjGKi?)csqpB|+u%ym*|ya(LV zT74^}|FTXkW&_nMtxnRDMV;$?$BwYrc(Bmk?NEy)Rf%E$iDNOlQ#|+X3&Gm$PsO4&D@PXA$p!8y(W25*W|teRzHTWoeQJ9p&>Tv+_9i{UG>8+jbQ5HNzQU1_PXx%P}wb*I)cyugnbrBdp;eb$zzRB&KV%TaF_z{F$tE@_QW#q}w*j^jiu z!4^cf_U?zyN3^Dtt|e4mHEo839pO4mdS@AvS!FF_GP{k6HCmoM*XM`F>xa+FhvVlX z=g`A)>EUtY;dA9-x(o3*_Ap#~*ghPHAfGxAwFY~w<@dD2TqBKf`Sm?d_#>Io<=nyG zTuftd!fLp1X$#A(My;}aCiU8_9xP%g%BV%PAX?D?jj2U5yr1H}o16Uav@N(;N^vs# zWsu&OMrv~Gh)l5LVO;KkkH>Bv^b*wDe|-->AGNMNF933;3Z!4T3?z@$d6vKwEtNP@ zpQfui10U%1hm)s{*XD^DrMqph(=<=ma1+jfgknh>a2^;uRc#2hnuD{)lCs{2Fg68aql#l}0vcrf1OEZX%FHM5TJyyckU3p(Ig z)+QB39S@y1boyvca_XB(r_lPEX2Il>{wUAfAo__@^+|F!wpr#!7)SIE;6QE^E2`}> z^D2gP9?%kf7>TXb%BqHZaA3Vd7;ckw6v4d7tmchajJ%qMl*!dZqzc}=F{&8Resf|v z3!Deh0)}N3(?Uw4bxZG&F%pVN-WE2w#R?Rv8>T8GRU$DMPGIJ7&T0Y-Hs>DNr0j%j zjeA*|t$5_wPQGhT!q(?DznZ<^-j$7%ijPLpt&XGXNm{Ba3_;&iXI{S3FmxYETPVY% zPc6FWD3!?~5kBp5wzHBclgeK?@If?p*RxvacjwX)I`=s4-ra?aK5Lf454NPJ9dw zQQ;sUb>z`TSbPL`+O+<`BQ)@dt0foDG=;dlgEb8K;BA$P;By>JH9d|fIM^J*KtZJR zI8CF=8I#OyRLF06$|Fk$U&1e063DKh*IWPzL|=_Qhg^w1=b$IZQSJzD8#4L_KU4}Q&VoE+t9HIx4L@U#A4+^xkmZ@+Aa(^g! zl%J#`9pM#IZ|Phyi$59FVAxl3$ZvheLZ!+{fnF8$u%tJ!LlS1p2fH?K z6BQxf&Lx>mCnp6Rdvj`LUaJA)fw>Pn1`uS43%4kW?xMvaOn3*dD7)3FN|bX+J+1Ht z1L>6$8-u05)R=ZyMfQa1&sIP?6K-Ym6)3&}ctyO)_1c;c&!{B1bq5Lgn5R)j6uD~+ zIYiEw#h*Onm7)Ce5;-sy&1f&+mVv%j^sLot*2#~f7gizTG~%EZO|Xtypy#uaq4!C9 zZ$a;C#C~di316)o-qmqp_ES%WdwwocXd0dhsB9T7^xd4?yvl@@siHFoQabW8bo4pj zS{i(qQtnG1JvFo?)>kF;r7;;x5fm#R8`0IMMd99Ed1ti;?npfBlx1X1fI!bB;!Pf8 z6$Y7Mev*W#ZGJ*k=q1AbV2niq#kf-+GIo-I1jZq#pkIPZoR*z@HR5TxDLNWgy<}^R z*zMV;&yxaGA+yXDE9k&7CY6<)q_x5XU?mAsB7!Au4~ERSnhIR`L@~v;B)X0k+62{Z=ff(>K(MYn?zP3ZzE$3? zO)D_Ws(w>Ls*3UUHhkTgUS3yfe<}Z-R>oGXRttTU+@t5dn^26ef;0o`)O0LknNC;e zkQ+Q)$CtBk&XH`{-5-q-LpC7b7Xk2*$kDa|0q^2IuXT6HG%V2CyQ}cvW9V;AKpFb}Q0+iinH?}B1 zNvsr9kzf#Dfy1kpf4$h=8a-Up%jjJ4{&DzfRN)1Qr|*iT7vML>^bl-1xv%pDByZkC zd0rJ^I-$!Jz@al3lA$!HCbdN(y0WySuqXy)+x|QKQl#`VAOn8%Bc*5>BP|v zknUa5iFiX@hYboTAB!OY3SmI>P2Fsp+m&;_PyS@2tft{_&k%h6!REL^t1S6IbJ_M{ zNY!jeag~Ow_oWqY|KTaX&GG5sTStjUMx!MsQU9G9L+EN>+Z23Iu z$Yq4iZtK@oYczR{lC{O(K>4|~pv+|WdfH~p35(#JXkAh&snlxr%WC= zCY9l7#{&)Lw5cuL?a9juL`JG0iFma~A?^)Jc%AEWPtA`7QrM8kvRY)(40wbDLz9P# z=}vsLLmQ_>lUWwGh1{ANs66iG?U+?0b4l)2jYgy4NG`mZ)v~Y9TWLS`$O(C;UuXH~QXC9M+bEK8#_1q{}9jmPM! z3?o(9&^!-AcF$(P64Yjx7#J>;<1AccQ82?R&+yA~7PF&c4Q2L%hMY1i*;@D|oLG$f ziFs(zn_a`15~}E%Z>YwL#z-GhS~hE=s&I1VKqp3IK^xRL{v*gzxKh8r61QO~0lJbxP;f4R}uhtP)YoTH!7mNKgO0{!K8U^(W3|gm~ zQ1NO`Tz<(ufnJnc;|#2W`cP;_?$**SReW~j%738IFVl%2w0`0%wM;_aXxOixw+J7h zb7i0dZ$zu09Uf+0NA3dZ8=d4$9YCEoswgb zTpBqTr&RTb@5I+)X^c_zh_ClvvSgpztg*aJTM(mxt&$R5Tb=PXD%~ z^ZPvU-Gn#FQ(V+1n6lX8d6aeKK^8nO0 z#`HAbX!YJ6tniHeOf-~(ISZpch2iqQbk5wSzuE5l_?&^hlaj!vhgmj9SuB(<5&&&H z4^Fd{lC!s(R}k8ofn1QG%68Y}Uvfb7>mwSvu;mh5KY+4k6$kC*mMnq3I9-1~cH)s` zvjv7d5>At!-Z(_xyg$%)czCP6DW|?>Zrr;Q?u5E*0Llm+)OAxu(z;EN@5A3$a62yf zu%{64qU7VU7V)JtXg02uw~V8M!QZ&Mji(mV0Cr=f+6b+fU;vkfQ5c*$aESv^=y|HY9=?fo;%jevaMf45SA7)eys=Hy3LV zcICSJ{^EYM)zuio=)ZsBmV7DCa< z@!^4)Av-{U0$F8a=@9Q*%GFk$$1+qSP6|Yl>l9$j<@*xYhKnab%W@ImK~*hlEKN3o zb8g|fsx=Iy=Qk0ZDt8SJ$sjZrjXdWEiF|_PVcY}pnabfIqDnGJH}P!V&T=7iQ7 zvmZ)NpCL&fd<|-CBnF@hw5PyXH%C#g%a{X~;dQh?8yeO;L+MJCis7qTefX-XEOkhE zEg%y#u7m9iCaVO*V5ECfMcuY(yeNT{&lEFIT^^=V*Fo!+)cdc&RsfuLfMbns$)vhs=VLy@+ zF_~$n!m&WgQ8{H+WVj&m&_FA#U^_Kz!6+JX8p0UiGZ~Ttj7il6Yj}yci;Mwse2itj z`g}?8(}Js-UxaB}T6iWeCw6e}6QE*S(cREM%59OG(-+5F0U413n}j!c{~+s*-@@bu za>VB;*^CXiH zV&j?52I#vaUM*Rw7kZrF^Dc_@*N>BZLRx9t%tm7*>V-Gpj|o^Ltlx8;kiQwUam`{r%M zr@roRhdN}MUpoA3k>T@%qsl_sauex*nr*J+wX36Eyk@~!bFNp0Y101ECAY5X=PtMN zFOyj?4`9VTwbr%@B;-#omOSmZR}P-rZA4=;Ru^TgWu;;Qdrpk&{3<_sMF}21t1h!) zv+q=({9;w1ceB-#^zF@yVg4)v#bJ$~O`poeB(|bF)seth34qtE8M7N2uT3W%&s*WD zd*z~wn*6$EV@l70Ct(PV%eqv2yH>Bv9Mek|yje#vJT^MWD&yU!Fz+va8s^z!!wA>w z3@XUB?`44dBj@6cK(&=qb9cb_x}-hw@eR=nb#-jr(gIIuYgA2@LaASuRw|AbYm64D zmi{Tw2a6v+bu3!b$|_u~U2^OqrF5=6t>1pxC}=bE>ka~I?3$mDI#Tj=fy;gAOTwYc zMevh`leW>`i7$RgZV7{~a{1!dvr&OLpS4|ZH2V#^XSitl=3!RPcO`DE+{)1j^#)673XV6P;?%gpmMMK&rn@M!8WyXZq;^ z3F^$HXa@+sTM^qS5>&k88Qwjlr!&`D4f>&>71;DH0r!<5Bsa?}XuNgSK8rZO5hQy+e%a+uQZA6G8&*%G6zi z^b=Fx-u4|Q$=LSP>p*C4DO=h}D*LQ^i)!Cm)e1Xe!w6?$HLX3nob z=$37sG|bO@Y+m?q7d=99v?G&E~IQhLj@$(tKjMz19$XcwH$~HpYdg<}a_SPz0F& z#z~jk!Kr8IvaSuf&cb|6+BVRg5Skf2ye5-EYl{AF#FO#sdmB|AU*B6|bx=vMKfJyV z7gJL=r3hdCAVw6SSpm+7H%7RVlVVw#b(heFxqsO;o~1S1Egg$znxwy(h2y zOmDQMu3uTIGh7X7ExO&<#MjhUJ({`Rjn;C0*xutwF2s_>*t@YNj2p_@_F3s<&0Q)k z$-IEebBE;AOpaaOSf2}26Py5^Pn`{4B&4gT8yhWRTn`%O*#69puDg3K0;eazxeYD2 z>|U$DY5Ev0zRPrSIZF4|705D*-R(x{Jf(Y$L7K}ERWRtYY(dO z;z5;5PkP@en5pp39n#~Ahjd>wPoXm;r_1t0v-Wn=l9%R5uRiqIY|1>iy9|GGnH7Uq z8N1xonX=!|ezlYEauv;{ro*iWj5)@-`l>JQ*#EAAN4 zkjyPPzOs}piyWA|&@FJ@RaLsWx^fJ*{S8q@4;fk?4n~Hd(2k{W%*yR+5?-%nL1K9I z;tcV89xq7iOnNh;a1|fPliEV0(e)y_iYCDVmBPSvtynF=@z^A!xEi3_brYoGbC}$L z2}3pu#WcP}>s+iWnlFP%MpgrG?<$quu5@qluA!!)TcE=+!wuG&2p9hn!6jrf|60K% zo)MzstAu?h47>1^Vk~@E6JFto_z9Z?pjaEwfZOy%H9)f1FDRfwDddW66p|ws9KiL# zZ}{$Kgnue#0v;E>N~naV;!mu<<;lD9`e((fU`#)y$eP;s_ico6kIVp(0=-EBhuO|K zAsfHGM?`sieJ_#uk>O;@qo{&fK@Db)H>qd4_C!~(+zaZhZ&t4>2QzEtb+D!pUmNm} znLGWvW&J%wVW8~fQzD-;tCt%7U0#)I z+h>*GuvP{&z{+cOeg$jDxi8vcWd!AZ+O0EtCO*S-LOn7=qeAo5ge zLj*z$K|Aapd@%w8%5Y;w4*axf?7(M7B8*asdq$!hpw;jIPea7X;YVQKV$4QYSsjeH z5T*D8fnCKDZ7vl7p+lGm8NrX`rNE#jVh;E8He)P4-Vj4e5D%xiWEOnFD%ruXvV9Fw z5oIas{s+nR3XXGCtOVF*swkzcnQcP2ib(Yk+tqDyudxo*elSa8)d`M61dU^bA($4a z)jJdrCG*!AwL84E6wT$f#qn@p$|@MXF;Ai6*h64Qg)H zM>@Z51GlNZVKCzh#n{to-!`g@(jU1(U8b~M+bVAd6Ez2&+h?|?NN-8{*J}Zg-GNH`FHv##ytG79rbU( z^LzRz-pdKfp)FS#EF-a8UCyG38zj(}6NI(`c9p0719O+GuW1Gkv=sdXh=SEyftRaKDt?rDj|uokPYsBILKbWkruda59iD5&KvR3I%&&8_QQj?HxJsbC;D0T8o z;|f^zEu&fZKD05FO~u+q&Ux@Dm#r#KMYxlm_`<7U4{Ca{r{OHjLg%yG)1&nJ>qVTP zkr)MT(s-awHxXuWRt0_hIWLx|a#9;bNmUUq5OwtH`!GeG3nfhw)y$6Qg=I8fHCnV+ zSbhrUYJth6N%_@Y>(BvpzL$%sN>ykx46|2;H}Wch)tH`p3Ioq*@@TLA1V@hWqFn6k zmn*?CRUxSf%S*W@k{;OCXr{nNLbfPsuduU~sW%nh#gN*PJUZ@FtfqG@lX57UVFCP1 zj=b#;r$tRN0=RzOeFLSe#RC;~MrWuN@%vK-WA|3Wa^0*D70*q|2HnWT6@xCAD@3-% z>!Kz}Mb4eHbi1u+y)AwH2pVl;oS!kVPdXs*7?F4^LujSoI%NvszA)->5G{z|&}+Lg zHn_`Jz@oyuxyZs_i4V&!+hV3nvQJLo(p4PSFY$)E{wqjsE`F_#@N`(0KP#{672UWZ zDAEvMPA?Y%tAcx!t2l-S)G9`XZiH+KalqizoG8n3Ha;l69C~UqVH5)UQBILJ?y_dT zZ2lNKjO_a2DlTTkY(QCrcP)JcTKM>Nqd8MSx#N2yJ6{RO#Ub0_Mgccwjdxp9W98W# z1Dz@)%fN5wbPZu}#wYMBeh2UJxJ>(_K7C7M*Bu8ys$?C%N%`2OmMY?me=%VdZQ-(D z^8#Y8D&4i z5tfqsVM&CYYkp03fTKcbkQRj$+-4al)uqELo37+IjU3UX-2qMD4b2%uNz83a3{K8 zmU>kFH~LYj*|dzt+ruYFsK-`&# zgh&LDEL0Ac-aF!$?d@Q3x4o^LbKp*n9U05>{Fw0_1ug6dZ7Rk@SI1u!`HYh+I$4ul zc+Ng;2}i2pqI6l?CKL=xqr3oQqe`$2kD$em!ctNKXZw#@+~q}NyM!EXv2X=y0nsMN z3FOY~6SzEz3kfmtAq++EIf!N$(o20W|C>aqf<=%+7F1Y}9igcOG#GENS~PzRldEUe zUM-q8%%@B7hQ{T0D60)D00DgX@(b~mB+NVm0JK1A3SbplTi}o4v2(uDBd5q>+`T*@ zEu?~6V(gvV0%6({tlTOMuU0cqY*>a#25xUaLd*LO@k_|F2dQ$3X#sE(?oSHP<%m7x zFfVlDycH#>l2PeE+_tnZL&8(nYf(fC+8cy&%Mr*Aa6^#CsWgNcV62kgqXnj1gUmVo zs)P`3C5VQvlN)=m4GWgGYIWFudDqR+Ipc`AE$;RG`c|!8B3U_WR?s(9l}tnZ(6-o> z@38B%`5?oe$o9Ep6=NeZ$x~ZjKZRND`*+%YT75g0X4Rg&&@CrHsS+Rw+w36qojdiD zU{U*et8jxg#J5yFg&>U>7Dr&*{g8A~(>XKFA>RIAz(qXxk7d8q%?q8@v7!rFN|jOh z-|$yO%){&^o@zYLfrBdD1XP@=cA7CuHjosJ#rKOWTukBiDNaDQYKA(3F$Cko_?POU z2X%x}aTl*BpbBt?oa)d#P^qMi=DKUQknuRW?llF2v3Au%~_rg9D+@5t$Vz*j>1HR3wVmD|eb8h8yQ z2xv)6t=;bHFha7&?!H|z_atk})7K(c_BkxSQ&fxJPT#Va?rdu~tX0Zqr?m=+>4wAF z?`h?Aef^HWD;}75mr`m{N+TPa;0QaVVb50?DcutJLjn3GRQ1{8A}q2AIAg04 zKAYs%9j2Ga8u!e$fIziFr(knEJShwK#1z@yno+p zJFRZUvv1CT@vxBna<+7}dLcM~b+25=9%&uX@RlWkX0>VUY8G5$0jXzyjjNm9wfrHV{w&g>^l93|!Y>tE|~ZP@7iPNAJvI`0O;{i2RB^1s zDr~sOz)NW}trleEoo2=3Lc=&tgX3dkd$cRy;8s$ zp){%Ppww}-NGCA_lF~H`K)4!Wy~o_)7g<2d@2zg+?q@1_u)UzC;+ft2u&$G)Z>}wQ zhO=UpiEq9!Gub0poAt@@*OG8K3npQ?J{;ET*HuyatyHmrQdb9D<5rEYUzbVzIhty< z)zr9pb%2vI2vO?SX*>_haZzBo%i4uKu^tBAg#2eDly4_@9B}|!BHSbvBw3u_EkHGE%Yg^@a zX2T={1WEkKw!iuFd7+W@jk+@OB}e-&$7p328-M%p$kre(FuGbpFwl0M~DlEF)><>XsQP%Bq<=4C4@LTZFkr^T5l z*J`yOxh6TnmR*Q4Rdg0AT`B8*z*(+_Lu5ug`K3~I$ICgs!kCR%TIQS;0D)5#EX+;K zTpydjM%LwmNDo^QEgFA?7d4$L)v4S;VDp5z3?`p~>+r{55nL--I2^M$VQ0=MFFC>e zEL}RdIXS`1`_Ae}zKES}oNAN!Itk`Tab=mh`LSfHily=FbC@#EhE)n4qC6q8VojW| z1OW(G0>TKOQ`ihLIFcYaAOun+S)LrNvalllNRWFLAeWUqEk123MkytE|L8gMwqHIp;ZT=6!UIplT3#c z?NMeu+ifB?-x@3WEl#*|OXQYQ{bhSvB`@N1#nb?^R>Ywx?h06@E&j|#v~o2no=A)4 zB+Q^{Rh#4)EAC}e8{`*H&AolNYm1V21MER<9;PWEz{3N)dWgOYmqL1)Ttn zW*6}^+`+zuY05{qhmWR3CtNAW{62^T4bL8oge=k99!j@#-6$yodR(pMO{I}M|Nbs6^kBFds*7X1TG z!dA68^Si5gHkJ$-95K~Rr-+HBjk7olW`@Q-Q=qo`AjFOsL$uAYin$4_Hkc1jDf*_) zMwJPC2=Xn4VytNL33W$gL0#fDnr1g+5@to|au(c$Q-L21`4^5)Tn34pU8y<`?dZDF zED3}8*mSf1Zj769Q{%R6bibAb&wQ!bj(}p>7YPKmWKwQnO6VN@KydG$@kNQeCG1iPv zr9sWJK~>6Eb5r4%lo@U!^yCAJOQ|r(59nwU{Up|CsTrz4B)%+b?Fj9JP;D5W1Ma4- z`XqM^{b+xx$pZL1$qN{DQJ=yEbWORG*itSNujZNQ0hPs)Z1>380Mn8gdq{ay+%o+x z>K=NkqnZCa-!HDBMU>qcP-L=d z0#bf@c+e!aT#fgTb~TlpeEA)$*K$i;7S{#s;avrqafi?sDz!F#8Q?()Fw8;daz%GTO}N2q(I`~S zZ%>V7Zm5gu`qc4C`;<{x)Dp8|1yF^&iR*Bo7Up9YDwal>UzzMjRGZereG-!yZBaiN z?pk4Ul}0OOxl+nJ@l_PgJZ39@2ze8^`1MkzdeDRHM5=q?Z!({s5T((F*)#syc0BHE zfPr}MHumLOlz#m9KOJ|0u(d8?ARB!&aENbtramKcJ<9@&e{d^yT$*;GwMt@k=U1iz z-}*>v#gQPM_^$k1qPd7~&CL(`lmJI4jMXFR?iMApXb;BtBluFrl&WY{s-jsjAi#W? zEORxB0YSJ* z|D%*IlTch|M8YIqEvA5kVa1p+z_b-lm^RnOD{fH+{r!K0NnFAGM$W08Dplaal0p%x zh0&;*9DHpO&sOtADt2IYjsB5Sn^MV+P8D61otiP?7BT!%b(QWCZllF+t5o3ypX-nF zVTZR|WBE%qRjqB!yjGp!2Glsx+V8O(3Np`XOJx8r*49Wp*x9URU6%o$ zU^*ps8e}MJDum6kq==vpU6q|C-YS;M=%D;*#8GMj#v7f0wsh-PAPS)USp<`870m3d zomwY4lL^9C(gVxcdU9&8OO%2>Qniaz-2ugk~Loo+o({dJ( zMX?kW1|-xFxCaBfsoIqaI1pU~yhmyBN9UW`@Rb+2_g93~)}GbB*i8t+_wCYAtWF|o`>SwTZ! zySrGF0s^(W)IAB56mlZm8!XuJl;l&EKHKsGV`G>fC<+OxQykigy0fF z#6^xlqLiJp3Ts?9yLAXEw`XVD^b1Z&3XCMeeH0%P5AxfIXe z(k3#=q`=%;l|y6dbp}#q4w{3&=gE&8fGn%dd8$DUhYQ)|!sSB#8cV~W3_Fb4uRARL zFF5IeH)%=K{1sYsui|l1$UO#XVAgM}#m%J@YWSzN*v39;!}M8pSKi4r!?>-P{HJ$~ z$ATkP5!`u4cvXFsyLoa9ovf=eEUi^13i_RouXvt1!*GF7qIyv>0eixJ z0Hh0m_Fy<5O=WUhx{e~xomW@kVj3;3xz@I2BJjAT7U@JvIu2F3r8gnm14vKlYHO8u z<#f3s?MY{GyaZaX3`uK|^!C8B4|wuO0=&Hij(9fXFhm(xj)1RpF}a(}LbW2kzmlKC zZ2+N@Rtw%D)fk;tl-7E_lToV7vR&;d4TE=Km_UrXq;K?FUYOHgR-JlVl{H&h)@^&v zlzq+LSK$hl>&1mpYBVxpq+t#jL*0y##=&vIFWG-V7TMIr=wbvFj&i}a0H^>5tg(`} z2LXZ)tBlpmK81H_Zp;@@$wgx{0`=+p$BM@nxODo*6L!q4H1y)#5JkPj7S_#7seaT;O{)j5+8 zUE(Di$QI4p+0q4^i$_9=-s&V?6NW_Uo^D0ks#+Ezv_-2SBy9m+NZuyom2MbB;R4jO zY$zq+bTtXfggoV7GFiKwcV(|{{9!CvS#q!g&F%Gj#O_wmn z#(w*$I5vG@6>^F5$mcSgO5-F~CB3L6q}u(hZ^&=U_m7oIv108SD#1EehGnrLg{-kZ zRhmB01PvKpBB|g3_$Ctmi#m6ev zn6||)7bDQT#^P1+i{+7Z5#FkR0QZkz$E5SoiZFNjgjp@9SIv^D;sU+{lMrA2PQUXD zOJ#QLiJkDwhSZ7p!gF?YCw7HHddZ@(zYmb(C$y+ zT}3Lb`R1W}_wDW72bL^bM8ronW0((ufq&f z`xfBsQGf;peL*YOb8s01C(~53S&*##Lclv6zlWJ&v&horoUbA%?6d!)lwmejK8uD! za>zIwj+HO>;gH=94~OHbxQ;XU^7`It^Km!?&oNLL%4oC4T;~WjTdqFRv-*a0IDKP< zGhg2@`vzUpnlUgVe-M6lWt#)iF1N1NjAf{xA-kvi1w#F10oy~Kp#x-Nnm7~nU+dR% z(MCT(TfwN^(#@m?ZBP84yj>~Q8$%0?EFGvCq0%pB$n=8C=qWLCfU0n zyOI8X>ce6BCdg{r<+Jv1Sbi7|(@MFvT^SB5XLTc@oKb@O9>M=0XL%@}wNZ+=2xh|} zcn7$xkG3Cb+mQGht?d*-OPsYYgUfKXvy5kVaHaCFjNtY#g}4*7?aCPjCPeQ^?cs3x z2LDtZK*RELSkYa%iRWQIN{leUX$`(jBZ^vDQk1$Ry)Eh-tasw0IKkx8S}J3^cmY3+ zS$4++<ufM?4{fFO>#IW-7YnxSvJ40L}~Tmq|}6P>MI7w2$nOX7vu zA>9l+*H$hIYQb_@@5SIgHB0MY!3VCS(t60X)PghsYWMK){5qHk{4H26xf)rAZ{$*^ zB{y_V-%{&>Y~v{y0vC8Jm`|LtEQ#jj3Xw)N@!%*rH|wVH8zG&qr&y`PGZz!8qCBsP zWfReQuCgtg@KL?;!m5hW|1MQ3l{LeL#@s>TJ+2bguA@*y)i^MML96Kdj^Wym&n4hA z=0iq`h$=UJ-NE%`kvRPRJ<}suDy8S!{CWi1Cu-Zl^>u>&0(a2hb2zM{p@2n;kRo2A zdI7aJ3({0{WJxk1lk6nD-K!lVSxvGyDP!%anm{~_+m0zXhcU;#opS7t6SJV(ciIfq zPDoW!&~a-*DFI}HwOWl{Mu>!D_3C0F+rnz664)0XHeya60_keDBn{G0Lb+8VPN-FP zy)OPeSWIUjXv{^@G?#Lw9Oj2FhLm^XU@@&z0Q5AvoZX2ygMe!DFbk#us5?6SwrRXp zdB&!sp--hsO|M%aPEkz~6kWt%INaS+o+4RE!5kn6KC1r7!=2f5METgnWvSd-RmuE?3;+ZB+;T^*f@4LX=4NV6K zqWEyfOUPm!x5dSWyZL22t3_Fu1X-Mn$_inPvl`>+6E1hO8TG>m(M_06D_WLEI?=&i7a-e8(vN_x(b&zDvYSL|LHz?_Pk;S z87c%Jwj6sFCpGw1)^t%bM$jX(gF1`x@y(kPdrOr~)TS}t&&$r`KqZ-ZoddnM+wiiE z!aCcQxst#6hG$z|L2h|U0d&yz7m(VfM~c%qF$-vQ0J{>T0!GXaqlA8WG{;puE%sV-*Ha}FsJI1<-tO#Ji4GnC z{IpfG!<*3@8dI2?D{IkNr#@>golU)X+896Z=G0T@iLrS`mz!>%Fgvt*u3;TZ!A{J>m-j948g~62udf? zpW0t`P*Dwj;);e^a+Bciaz9G5j{)>~{Fm2Kb#E;D1zbF4=Btrhr5{)wCFMXc;GY+9 z=JqB6Uy7hg3E~j@V!v?0jgE;^8>kOXhaVhdwVX$RasVq$?aYIv)>hg~t7>Cs!!Pnu z^<9`1s#oc>e}gkJHH7jFJVRHAh=l;n(dz|-Dcx)A$ulex z2Muz$vs~VZo8U96jL+pI_ZUQ(W3J1HTSOc)yPOw7w#F+`dg!#=^a(cm@#wZ$J$aYJ z8S?5m?@7iQu~&zqAS$WpsZCuCoV=KZ%TdKF#0rp7K?){MR`c*6F3{VOQ+{NYCmKU5 z+I03ntO@u)VB(W6q?kbF0qv@6VWpMUwqDB%*AK8%&$0(fWva+5ehjW*sAfONL^l0f zl--m|e<>+V&@*vS;v!%$YLy@_@0F3v+m@|-V0{NU_>pwB&15K;+=Nrh4a4LnOwK^% zjE6(@n3`Ew##S;rQ(9*7V_u(DIo_fQs(B4p>BB(nScF||n{_e!FE{kQ*1A45cXR-5Pu1e&TP&hn?_Z+`x)-_VfO|7&hmITT>P@o=S_yj@YU;k z`uR9~C02OIdwrjuw8wEpyfL-Ip}r3Vr&na-+G1>%*a$d$4N?^)r@iu)A8E+(HlA6_ zVePsm{sNMV3WrfLKyfdSSy4fPwFhBmAsY_-guS9Yt{b`cWos9xn)Jg6wqtm8CW2VC zA_&N%>M{wFa25iGi{4|yTkr#Je7wAqjGR>-!={R9e7lfmeDv`Zep$|<2{=m+u0f{1 zA{Tc;#j3DeMS|NSyoNlpzP8uWdJO*t=aRDHSdd9-a@RKe^ZEkgf(^AH9;?-)QXCLFSU(yVy{<>76!2IRo!Lyh zc8pUjM6e7_2vwvBZYr(<2!B}<=Mmmg-^Hs$kY-hgBD+PXE~)_Qs~kC!)RsvvHkg1@ zg9t9_$XtDu-GFKGBp@e$Nr>@tf*Bn>*`sw;uz*o+u+un{Q8j_D9#=iKF^^9082058 zfdRI-!N(;yK4tM%G!Nm8pn*I0BFt{%Rdx28SO80_7wOB*UxkVZGSiS7dJ(S(jd$xH|K;n zjB4T|^{58xbw^Is&K2a8G!2Q~Zllp?i1Kbjdf2Pf#K+_g?4^}?Y7U5H$3r4oSc6S< z5=|*2X{}UsJ6g$)3a{I(j1_CX^@|F!SHu;1m`s>PKcUex;_|hr!`H4SRjPGf$)8<5 z)H0*7Qu##A_8dP3%d&Bvtgbn9RdIt=*9oTMIDNk>2|f7Q3O$o+s?JPRMWjO`_;;#@2Pc8_r_^D{ zT67l%a4(|KsBM?^QJFUGGBVbIp;qo0o!{KU5;{ra4)TY9(X>gz&Npx=9NNbuoWuL| zF$Qe0+{-1==YT!JO3QrIx_YGGE-2flUs()3a-*xenlCqt06)r@El3C`!nY%LlO<#v zDzj6jSt@>h2e)k;W`^oDJRry`=AEVj_DuSWq0CZZXD zxh}P^vB(A2K!k;sr!(GS0OBOY3zdzjFRh=x>34dL2yT!ri@CqeLs3X zg5U-+o-}RIfPcTCHXxUK3iq}dlQbfs&s(s-cG2ikMzmIl1ed&HAkl>L=vgFK0D{TZ zrInJ4YtZ-abuYL8vFucZ{S)k zSJ5onft{CJ{`t^=Kn8n7ti`>`8-rb^oGYcs2#v(Ii<~jpXYvj6^X)>atC5|SY^i1^ zs#dzP!-N~Y{z^T<@6+?eYEC!aPUXHVCw`NE1C?gcrx5l!A4VByO~Eli zSv;0?aA}l>kb?$aO&86oXjR4T$SNRxF~~`ctVQ82*A03j&T@;ZtsR|izTR!TMBkdy zwycC+j6x$s;g%24{AHBOlpL{$e1wA3%^dnb9}TilKu97l4Z&?WpD_vU49c;=`b(Ed z7);SIoa`k3I1^G!F73sONsxU|Sw&ml;5@w&193HjgC@nV$YV8}0BMK+C+{{!x2pL? zWjoy%=)8XXBNcj1LLp6Vg94)wM6|7P4(uRH!6C^X7jBkOrErI`x~I`j@hcAIsqY3p z85KCWtu&2X8*mZ~UyiiE0#V##l8_XJ;e5%tWNhS@aJpPY&Fdy?8ZNUNa#$67L6`qY z2o8aupO|JBQ>b&H%u!D*5R53sph68c2nKy#(y;_3nu02$^aGB21i``Z7j$2;;5R{r z>OYocGU9jY<>D`h9X!1lQJ*4;bs@P#to8|Z9?l zfAnzOoZ`?b{;H0!5PdINT+c#MqY%?DT>fFku*0!`pHH=3vO;})ALm0(?V?o`7rRw) zu{Ro3#s0|V+L)I!GvuO?noHhT&i2|Br|KdDyfQoMSxqz-I`a{eCJ5Y7ao>*xS1tww z%jN9O-Phoonsv?n?M)vDrOp%X5+FC`tRlx?-fvv?Y$H)F2A;F`!X_{ZRj;V9Tb zwhn20V&EZtX9wFN3?7L8SgKPd!6F4Oz^rbG?mIW&hNO<w4SR@3Y zpYn0(Y?2pv&2$Pi@XYNrX;UG0iKw5(Lr~pR)z%7nrp88II@Qp0f{XmRY!3f9CAT$! z)ENMZ=ZY%zx2juUtW^;NI)QORE%=F0$mOv|jk%EltR2A&gv5?wGl+xtp%D35E z$$f_BMiz6(lKialrD66l&Kn`u>c1H&&hE82LAjFq+&`0?{E-C=9aan2K2{mdn57JMx@2Yv1rqH^bzrF9g`>prO`}hCzx4{pc@5HG%;sIgG zyUzQM-*Bm@$t{rNsxI|_DTXU8ao zG#VB^ciY}V%y4RyZ7XN3Yu_{5hOiF)=Z;0neHDx!;)C8YG5O{jb9FO;!(b1RqFDNm z;K5muA?Shy$@0a>jxgl<{**H#>p0^tZDBCun=lA|NV>3Qf|VPfb)7#Y`ls_7gBNF# zLNa7rv7<hHAd~#vp9?>7p^x@>{dXi{=QLD%+&tAxB{zJe1|ZB^&uBwy=%8k>u09 z`G#6O##Wn_K7%>KdUpBOg$X@5qEU@_7tT|qx>@F4aDvx0TO2I(#(^7xx?-zn$==9m z8kHSYEY5&Sm})4%!9wo%N&Z+EVwmgnqA_w%z*tizi>yazN??6jR&or>S+K}BA6d(m zJuBLjS~aS+BVZOXsROOKP=mdvN)!V7&9&a%Iqu$7PVDS~nPNj|!Md6jjWIzc%osVJ2V{sXg zBW*r~Qa7fmP!-fLlTd`g1mw-6koK8&X=sa8(V`TE(HLHXIz(z6h<+-PZumKhSLw4P zLc3`DWJ`p8$s#Le`hBfdE6W}JLH@WHRe*1RnYQK2sF^`B=FQmz;=3SZfyFC`zocO( z4A#akLsB!B{(TiBVVSk1whf=C5E7b1Nsp=+F7N5|0~Qje+)nx8w^}d-moWHUN}yE6 zG6x7tD8AK6I9c%BS|>@8wZXrPd!fPQ)%W4jl9(%J;*tK z5~3og(^^#_*=jbcN&7IgA?LP2H!9wjR^B8)# zT!ukHRl;30WriSJsKvZc7rb~%limx+6yucXM%fr zT^g>Y@n5CI2k0ZrfwZ-Xsj>NHD#oA3NS~uGIYVu>B{e@$aciSsYm!iaF28MFVr>t3xCl%mv7v z=~u8_Z;G9_0)L=RJ6Za0ANh`+(Bw|L$Eb?SRfd}xs>ol7DanSUJtt-YiM6q;YK9f4 z?7td07BB+f*-_9Xg&u=R8J7i{vPry}O&L$g$P~lJnJX7Onny_KmPvdWT%u6-a&=u3 z@6h520uGW}uQXkSKn`!RY?-#}but-*=!U|oPZ3Of`HEmbBP6&vy< zB$ipkxA(>#HV;09B3&iuDG{}%um~X-=P{NG z%62)((aiEK;ZQN@;$ee&bQ~gC6_7o&iOX(+h1nK};Lx%MVj~$Ab5^{GdR_eV^T+eH z_#14b!&l!9U*Xxy2zQv>=xg|C1QbFU_TiEozWVlX_=;#SL?=`?we#TjI8pr!Y>LfA zu_XA*BX~&g>!27E49ajdEa)5uGr&|Gs>Pmz$0M@wodgiA1Hpjdos#G}g23A3DM!<& zBw*$}u)KM|fe`2f_)b-bR5KWWvXtRl=qN={HlE3^YJCL@MNnZ@Z)4Bor0kjVlA@Ug ziAg7@K2Yz_yKD|`4G>DDHlYc&LO@SB1X2z3EXgMy$OFGcAD=wVwyxTgx|Q{I1)ih1 zrCFA&>05=?XF)6uYuhxrBNDPB`#=f+b@=ikUqb&1!Q5WLc9C^7bFh+=GC$g`d|Y1B z^5)|v>Y~;Rs4~`93+fm;!(i>;gU*0Vt+~buIY2aQyn?W|+!8{DytjC`C*%7&+*GYp zKI5UY6PwA>pPXWK++{Vsfq+H>xhvn!9rO2EdY^^Z=t;PW4MHfU}BBMt3nUl zaNdOoX1s$F4tvSTn?!Kfuz;MJs3EpV|0|nz)u8rfR~@kwRHNj2`GQiAXU4{?$^~ad zZPW6-nLHHObz2AsawZ-Th=FXxr*aq>Cra@*~x0^T{rOG)&?j+5_aJplb zwjDUh3brnqBX3m8HI4JR&1u^iXmQhAJlC=Xl;NvOkoMisQ6!k9F?#7BjZk!L!Xox8 z79dr;%Wm+DgC^4`8Vu4KFvf={7lxl0-@vq!p(3!7izP}V<4v=M>o8MQfXSkQZN82R zPWymt=t%n2vcwA?gX_wsld4b2R>eie`Rm5+pH*{VJUwzbwVr`fl6Tyy_R5C%q(^Pm zS~O@7kee$QV;w3=XF*p}e5>`x+#NMb(y=X+rfU)i6xH;U!NMqzI<3ruOpX?s)axl) zXp`;d4ySE0)K%1`b_rt6c}~azX4C7A*O?C9O}Ug+)uLsTrvtRAd=&8$5e>Xo78<;X(|p4ee+g`zVb6X}+zs=hm= z+v2H>$Mw77Z7)}>ut8{2CV66H8&pgf?!N>bj#w`We~l_$7T+(i@tp1KEB|Tu_f;@c z5(0WPMF@EEPr=r`s>P&Lp(VSHJSQjgss0Pee*Q^*n%Bguwd6c&@;%#!^OM3J%d?8g zV|~?`J$R#vc+&99jb6*~itziZ?-ww*zee#4k8#RF9hpPnXOQmj287{K@uVG2nP^U* z5IZ2%lc(9=xPR)q!ck^;#FneTs$lg#8XV%n-zqp z0WY>vUrW1t2SGBs^U{(gDaHAl)?rCPJYWWk0ER$$zdNIPaw@3nGuRrqrZBt0vL5Ll z**7vJ*`8sgef$pJy zUg8MXs`uL*(9rX$;r>$=u}n+VC5=x5#SOVGE2!ZaPO%o{3rMMA zpI}l(6@z6FUVvkXkz*&OFFl0VGVTRsy%j5m-gU~wE$B{Ftou{ricyjp$>N$yQS0WX zqLoQDEuv9C7M9+;-BRvcu9;?ey)DO~rgwun84V>+i<(gEg@cisjTP|_@0Zr)$6U@G zx9iGL4zcPVpiMD0>l{~AOn9DxVwL-9vR1gwaRGKW*AFph7i^(wr@Jso-bVD%l5|l$ zZf5ojj@cLCt=rwR^`vxU?tD^VG7!5v*=jjMHQtq%RAo0|lsIzFRBb|}n+#iAZvWim z7R5|f@xAk^I}Cis&HAcn8lJtQxz`f3^GL_+PfWM91^K2?IW4qfDtLD!8JgJC#c=M8 zEWd!h_NsUg)lMZO#ZDZzu<}5!g9*@zklofK*_%#{n6g_M&|j5T^85fzQjjk~;-j`l zDv+zgG@6lks~S!$zlX&+t@g@xsfytOxKiXJ5qh$wxiy{<*D@@=J2@}5*qGtRp0x~) z+9&*Lzx+AJqsVB=X8Z|RHSPp5VID1_c|dWiT?Gdv&_{GAL=?55=SM^89R9JmPqR z;rjdmKRrvzYM~;U(${HhyFQXR-nMR@yzvtxO#J=-fIN~{ri z6i(TCSxD8XUInyT=1x}YCaWrB1gf@S4X4`#A7|Daa4aKjl<|Jy%v) z&$Ox3(aVXqcOKbwSYxDqwnkej!UNmOK23N1&c)VVC*%>&zEr@t0#$naM+y>4{P_V* za`;6l8X02sS#(*S#n;zxdHAPa3Q)8-Db%K5a;6mjdoVeg&BEzBr3Iu#_qjx~4~E44 z@oU9hEQGu(me1!K;_A*IPRUogOAwfGLpmEDtaF2*0-{=O9t;&U8L z`PhJSt|Q<<5CbmXhIl$M2wg?xC1lCP%(L?r)chUCVTj}`DFuLBTd+@CQKB46%(oo( z-io{<0~StZQB_QC!JiHi9{E%zLsWCsi7r%Sqo*ndoRd$jpnR%DA?)zR>Y~Xh=PIgJ;{242Dl$z5} zMx8P3W88UnX3>vTI=`L$iOi*^Y8zuX5sFV`YoO%0$Un3anEuvC`-fH z6nGmnEZHSvB$NO2W|x)~pr)Cp-eb4#>^C-johmGSF)J+F@tanD>ib{~zPMvf6= zbVaeIen6MAQ3?)N!!J=rYW!eF`;Rb*DUX=M)6iWolUZa&zcng@DQiL68cJXZuvC1j zFHu%*=2&N~-{WJR6$^#T@^DeFE6dVgmBrWL0xZ0wjbE59lV~1@ERJWOdJ~3iEaEXP z4U*_CsB?k(Bu;D#B_dp~wQ zOp<7siTzr$&WYmlL2b9z5I@C>PCALA?=dpjRTL&4Kvk~|g~9#EFN;W=3ftL8-u*}| zO8<5fW#NYRPK?z#RFlA7bvR{w_an(v*Z%Y=!{Kto%e=0&6 zNK=UOEbM3;7p8OyQ~HJ7{UKU}AH!rGEy!xq?5>?=;(7e3Dp2buN?CC^$w;>s2OXv2 zHi=10Wvz-`xhi%G#{I+?UNY&Mj5|M7`Sf+u^&-$aw8?JYNi1mmwWrzNJWH%&EUy2h z^^|pnz{v0qUj`h^5iQbR7E!H$z*3CFFO|yEgMPPW%Ysd4uURlFoB+ z%r61fyQg7Q0AX<|>KDV|mriFm{LkRBaQ?BnBz5|600-J=dYdh|G_ubn16cIkDn zPme=<-8*X1gDT!{VLSV~O?urwpvN&j4*2U4W$IH;_s_A}{Q~;K!o|b^55ePY?Rq+M~x2KK3}zKD~At^q?*rb+CWOjXiul-aV!V z&e!pNgB~&`LtlY!D9pmZ20 z9Rf;+fYRwwSGrsi!KOp7>GUbJpx9rL7934jUKPZ9zmnW(CE?ldp!OgjlW0Z z@6mGV5vl1NP(?KS9uK%jYoNyi?$LmIJm4P9caP`0M>E{x8SYX4do;tnJ_4XeGu)#Y z?hW{Xv(@Jz_GyTH9%7${*ykbkcRTdJYWsUwZJ&DIr{4FOX!mJB_jxe=;{$qdDp9F^ zcb8wg^q_isM05H*EOXu84_?#BrIW4?%nwE1K*f|gEyhr`w z@%4N3wU2EM8d$-A2R7I{qz83uaDekO;7xFVIMQqoC2uxb9enH#@UhQd_xI`1;nzMs zaK4(214=wNpvO7oIc(5lpI(nB@6kCvI$Up$iuMlaLDlv-aX?)e;NY6Ay(4;b_!Tp^ zxQ8v=d7G_$%ETRQQAb+`oau;Ck2v*+Qja+m;ofX@Tl~6Dk0XA?p0|3GsmGaU0IeQp z>QgWJ^ty|<-`pkKZ|>qQ+1zb%>OPKd_lTO`#ht3T%b?lC{kFMBsM|ciS!*5~4Coct zM)Tl^Q;}LV54h~XIlZ3KR2<;8)I8)VKEwsuJfsEMJZ$339JV-hmtObyb)Q}tf``_zjKyL#NjDvo)Q zj}LIdjt}`2cemy-*LaM}sCnEYm>d&Qb-1cd6Q`!bz3J>@*$ytmCht{E+HsoQJ$~K8 z8oNik^op!Yv)ehR2Tnn^OUTqkW~|xc=6WrxsYk3;v$uztdyJPo=7(_1MGO8EADr(rgMHB5Kavkrv^=|c)+6^>{03-r_!1kFgOSMhx9n6N0%O) zhY)Q*JsmKN2i-k-9Pn$89te>EFN#5rGW9r9k23MH9v~0f9P~N0PtBflDuH4!;Hqd9 z3$lawy}H$GwCHh24}^HD+1#baK0S{4YabuTu(q1519}|M zgG;tJ&wyTW7j89od;E%hXcES>ntPOr`&z3>tESaNy3=CZX!Tl{+S}#VeR}20Jv`;K zdgon!rBvKxTfG4egOQ=tZ|&3LkRI66KG)knps#p@X!VcyD{iB$eurN>{CdQ%*jm5K zsa;C#a%z`Sdz{*%)E=kyD7DY2eM;?fDsm95b3AhFHfX`^HgJ}98?7e2wvO?!%dg1z z?lyLh__c?RJx;}u>^6@3{7N-XsEOOsZj*qr+w5Z5 z=73+R>>dGqua6tU9U zKK3{fkAR(n!vVb_f6-wY-=T9}=dg+A(#|2(bl7UqD`H&d@TkkLSk)n|l+NMtE{j2oO9$4 z2Iq%V^EqxFg8|W+!2nMpgTWrZ?$hf5Wu`4{z+2j2Kn)E@CNdZx`V9tsPVG}_pHut2 zQT>ItKkww_&yzy7cmG5gTjRe+##xCna;7>X*gg)@F#^ zy2WC*qjNgD9t}vi#Z??1?9zh;VI+inO76y~By;%_#Bg-RJ*Md*?!Ix{qZ)|3X(8R= zI$0fc@SLlc!n!64>vsQnvb$Z9+3ga((5}_!&~d%9M_Ko}oJf+k{pK2}AJ13kklN%K z>ClY)i9$e{nGUU>PVbOkDV3+D({n_H^pz%~!;{e=;Wh7)jV?{bpC?JAj?)g*=n~+& z1o$okzPrZ)#shjBt`j~|D?ANdS~f}++4@%zNp|^_=wFvNf-WtkE{pxUG+!*g?9z(r z@;r5Eo_ah_J({PlE99hCnwlOl2aO)hO>d9O?y)FzpB@KZ!6;>=x#{uT^k{B+G$}or zl&>l_CDiH>wdyg9dj#Vi!?@RD;VSjNe_)GRDG#lX9z(vzkat9{{MdboAeLSU(0vAU zp8(xwKub9-VSk@t+9#Oy8K!j5%!_ZSZv^76Ak6LesN+3abp1YcoPfut%|@Rjp?!wW zVAmD{cWl8g4(@!PUkP#k739Fwk#2YAX$df$!5im-Ej8Y=rN+A(NR1C$^w_mz$bF9t`4CyS<{=9S56=->jv8GO8aFWYsL3+q z=J8s&^CkjkE_>V}k@7CT?$Rp}$qmKP+|V(yL(OBx+GAXqhIG1XOQ#WwSYpxapbVth zAxTiPb3|nsAv?#%mMFVn$*vjkyCmf89uUa71oSTVwL93ggx9$0n?0V*9xYBK%Vyy< zkEGXc(PMANC$2uHS6-d}-xOCj_<`bCx>{>BP!`f^;6228i8fQZR-;d^=al!HM9vLd zr~j`Ld%s-JO{?U;ThQG<=Ttn=KPmSfyg=?vvhw53|MgOESME(h@k5f{9^jJBiNTNg z6&c|_MGW3L^h&n*6^TaYhzEFt^D7CNkBQCg(0-u> z-n$33z?)>x`~13pyhh-C*tO)|Pe`!`wiNr|Pm^MI=+UDG_M$uB#KG}DTg3e@E8FId zpQ8|DaNgoq1nz)TS_bF4B>1KcdvLxt*gz~!i*Z25ft-|lckt&5%cTX!`;UVk+h~;0 zuGepGZ)@RX9_*m|x&@kB2eVq7T-TSY%UMLhbW;cm6Q*?#weNiRSU-mnt%Fu=IlZ#Y zD2({QYzsi9dxmQ7zlUG?5CqCq>h-JIuGi)rJf+QhRgx;TMh||TWZoX+J8)Ram+ps? z4NEJPbAZok5vG%18U7e7rPqqma9Ap}=~1N*uS#HIHGC!Chr=P|7!LWm{QjBWKl6JX-|O&ZkNgUPFJ)xs8ZDNqtSaXJhrM^*Zrey2 zMPH?@5RRo~W%3r1vqAYQaQWM?!MWE%n2 zIo59?kMRdN#&4ZHSKSnRyD99?*o(rJErBtHwX-KrsE!NiE~sMRF`VWYgT=JB z!^L}*Am&iqcIMd*mk7O^Xo~5LOMv!lT?pJ-OtTvZCF3G5BoVrOjdJ0~#;)Cy9o70< ztKzKWGS|_S=gY=`z!UIKk;{SFp2CPKU9E3Qn1#B5-cy+7nrGS=ft282o__nPrCPSn zE|^Ol@O&qMCnJkTvh{c4;t@L&z;D0nxQV&*0?6<_d}6FJ-E`R$JC+a&klQ)l|Hgvb z8Us-@k_cn*lu8+L=`bAA2jQ`n{WpNa)tQk~L*6&nYLI^&#u52jx&%sTmMq$GU|H2xB z&;3fW!*o!e@yzC-27_`LsasD{lv1$aicNGI(xnw8gfgHw)uyd7yV7ZB9o6ozs|%ZB zKOM>M*B$kNq5j$!=su>L@jtY4JfZUFrVqYXdM)Q02i&mqRm<5swm!Co*$gboTOV6j zdfEEeO5#%IQPTR@Dm1tREwjA!v6bo@7_6uZLu`TdGzMvh>=be0W9tSiTEPvy%RG^12Yy2iGOa(*)es(1ev*)afTYF)sOo^MIpjCdP-nlUqlSF zn08DIV#hjFcCGUyn#)iarUPcp1t$0U6E0#Gm!h##w!R?z42&5g`Rw@VyxIh`#p1Fk zbE?Zh<2t@ehn)@};BW8&0k%&+y&{Q+y1nTazLPKLA32QI4n3vt^AJ};X0NPuZx|&> z8{Knau}*Cern=>}XTWvn1g_JTR_u=Ii{hj`D<(cUK{S^~gxJk3PuoYbWN-lgpem$< z*vD#z=wfMduarS%?w<(fLr1Qu|00NH(PMuQ?Tx>kuYEjU`*LFfUQ!x!wcz{t8dBcp zYmM39`}rEyb-w1;_Jl?dhESq80TOsewcdisy&i%MJq;TUUwpFb6F3iCU5QRd;1wCC zqE+?t=a!J15A3LuWm-nJw2?oT!sk(Al|8@kz>sI5y(%|1V4(0?m z7tVWE*|ScB#StWuZRA7CcMEB1w79OT#9>*Dl!PsF0Ly!tI{PD<+RgK5d0$KGfg=={ zS7pEU>b?qA0Zd>RT!&y-q6?x8`1Mtao|Bd?!$EXV(@L8txUaqvoqdAKZeL&duLmvX z9Qu9`IKWP|KVkh-e^IcO|JKW!EFZO7a2VArgl&@!wTCuUu4)QM>Qzm__lGw%Uey%d zT2{41E?ieNChImt$a-^yj&P{+=30xH{#!5qPsB<OUVc1A=vCq$&kK;wF;un|s$jAdxS*{|RQs}=gbUM^N`Iu)(a!L0`L7r{} znG7ou2k&~_?u6u;$>|g8-hDz`VNK&T@zPdo-8v*tEk`|Zi&GP?@?U{VimP}oTpl^j zWCb}q0HZt@d(<*0JNJ#WcKeC@d?&~n;5Jnu4B2C@rAF^*%J2ai+o{=LoM>2a$b$Nb9{1E6t_crxt@2vz#n&A4Vk|+bliQk%s*|3uu zj*cf!9yo?71WJrhf8U4hgd;(|PU~$tOfm?0S{aPl3oFS1^}D_@zUq z-g=}i^)O;icrncu$*6U$?MEHuNvv~6pbmUdm-~<)gNU2)_|Cu!Xd|zCXY0=@{vpxn zpH=+i$qIoLbFn{uwEd1RfU6ktlj^p;+QCk)3-SVIwi?VJT!{Ri;?nk+J5F46J)4)yL5BIPk7)W77lSlQ2bjYb!aNAMgd~AP zI&rCITZYiWE69DNKKYjMP*S`0HgZSADD|?D$V0?sZ`yLY8aA|#lxVk|<`L|%3GT57 z0%Oo(wCee{fp_~=*s3oE@u+bSO%u-i>)w1(OxsvV$ETJdZQHD&WpGuBXY(rg%tie^ z(R3VSBo$HPY>|#|=U(Y0azMjQpkN$hcXR)6ltzXUEP+@n5MWn}u?(gYN(HD}zoJMU zdcyLbn*Mm}bQg}98iV!qw%o^$-uoHmLQF%uRd{^Ob)oZXJsN=`rVr!8VSw=3>H5oFNKEvQ+UT1yOZeHcd}W4PrfA=da^yop+eq!WgmIbpwk* zIVlpsQP9tJxM*Q7sJcfhZg(ogjxIc@`Nz1I${*#~T<7J|-q*N0A3_Eoiv+O;-SDGM z7c-rMgUmiQSjjNV(s4Xltjcne$7SQ^y9a@D6=mECh+&qNak|j5 zL>-2_3kx=2*LryM&-Bl@a@P_CwifrbmLOnuP%S-hcJT+C(*pgoV|*?e9^{Q@fOi7? z2ORI%*+(mn^UuORJP9k14>KqIc_buIf6UwNu3|BH)%lkN?h0%}oJ>p`J4a+F0=&jgl8`L$_I`t~V0@eCz z*}rMoF3MbJ2yAB0H&8pEtG??rFOgRjPj*q}orb+ZIeKw%5FIdrxSxedF!p0KQb-ZR zL&Y*AwXQ&t^5j7iV|8n#l7iSbZf`t-nV3PSoA$=v)}OaGKb~!^e}8edx&Hmd<4*hh zyoZlZI?vns?8*AYbMyM;MqJ?&83)Nx0`pYn@sJ`wL5L_|J6tZUqTYQoaFC_zsGs-G znudo4a}?T|`nIpuzxn>V?XSQ6rnmj|_uqZ{jefHI{nPKhwprlc-|#G)V|a>bT-2y2 ztOpXYC!Rd)9=@Gd(}5`IhU7~t52AyX2w8_?wZi4Q+G<}tpjsobmS-_y9B2Rr|0GVw zAi-IW({a!|;evLjp%0*0zlB|ZH{*r-gJ|WxrI5n*zx~Zy5nWhvzKMpSOTQJZg`A;I z4IghME|fq7fqvsCQ9}rd2?uwgBh;0KAR&FV+1uKF`t>(|s#%oHmKR_39_Fs6Y=Ep# zRiN?@s5xzB-+ue;`I`8iB_sJAKYQdL<5mm-$Q{ajMT0|<8#sFlDKX(XrutUaaO>Cb zTg%6wPS?RUk1aqoKJsZ=N$(N_a?3V%;<+$QQGL^uJ<*_TK7-3|Fn&i(!YFPhTZx^$qer53M-;2e(Y3ujQ94E zsuFgkXTxcJ;VXJzM;`51DH&&s;D*8D;|4pqwQ@1P5 z+s~m1?iWctOqA#?7fR_X* z;l*@YnALKpZK&z0WXiIs8YFI6$qC z6%Jc;;m^k5O$DYQQ#ZPmYc-B;z=K7vw2hovxb8U6_a?xMk-8s z0vm2+t*XVu-i3zVp>mI3B>b#eq?S_?jlEGzNP1VO$;1_QA5JpB1GYVxI z2`pv4G-ah-ObFP7For0s(z$naeE)ud)Plq16j=nN|5%woUJ$0MG;LL;uiJqn7TMYW+BU7 z^F7aQJk54%!YeUofU=DJy2M&0tr>AWV5>m3ij};SEEh9zNP7MgqR|M~xT7FXj}%!J zX)%q*WiOh~lV#g|=&EWtVf|8&ssuI`IP;+ItgvIV!#Pw5KHh7Db!k0N@D!t4ulpTv zZEUEQ@vVBv6Y@nG%{2Iuxy-Yh0{qI9)2LLVOp?BUPTQ~cJ2r}Ye`5p346Yr*0Jo%S7!9ZB z2g(zdB8>{$X)&3q2nBV~aHxwS%aNUs>pAFnk2tkw3fE5=j7&_4(K^I&--S|ONR`;Q zBOI8qoN@rfx;^_h**V!}t84M12}PgMjn>?(Ap3Wmot>9`&#&x@Y|GTfzRR|@_?>-| zZEf>A`#Rft%J1wu<$LqpxJ$cZ$cI_WMx#r@!|WF1@Fq2rq;|~YSt#oHIk=)Cn`t%I z**wvPv4XfB50zIfxY5j)Gz(=0L`xBkHMNM@G2l5zltDLxw_D@$^V@I7@c8{0gGEOp z3{%2^LjJT#L4h~N=$84CoIqcGL+);e>HN1|3g>Rdkx`?oBp@#y$&VfK#)I(oSRKye z6f{Kuut$1`Dq!j+Ld}6b!vlMq0fPX@I@o|*QhhMW+ z6s<1a0Q&01?&&TcV5WA=05FU=i%<4Tts;v;XamEx#Jp{&q@!4`<8fFH@bPs7y3&vU zJFhAl+F@Y&m*rQ_61i{ z&HF`Ba=Ircpd`2&bfGCv6?UHMaeOQ8tOHJYUPYS}`7UKfdt!miKqEk$qM#iTl(>DvU8F z&4Q5&wp<)&LYg&>Ai`N?3W%q^dHdaFqezIldLOIO){%#f(9Foa;?IiocE$cQYb_BR?n{B+@ICmvhd_Xj zK6myH+CC2kGy_Uoo3&5|^uT7FDq+o{Ne+G>1$?%nz7yJjTTcV?b`*2&sI9GLxbVnX zmU2FjsUP(7wic#7ZKE^*=0nuoVYaucv;B~c{}KKE+-^VH!G7CStfC*#9|m-#@+h6? zdRY4UTa@#e11DbJVm+B3T2)C5Vh*4@iXU@eCq>Cnv_oHJUFKpHbb?bojQ^B=K$oHur z8%UmNb{oW0y63!UXye{$Hy?10J)l~z4$oKTjgE7;rjGPHi_?13<2r@-dMK)OZb9dj zpjGXGwc_B*0;6|?MK)EpOh0aboQO3T!x>W(auKlU5-#&-2yV7r{b*O$iwm-^qt+zk zQVBRO&o1m)R8i=l>s0-*$$G%xB_OT|6~Y#VTMY(AIuR7v0bS^ zELr1M(fxBg09#)5Rh@P8y;0<9PmE256~+2IiAoSLs8mm)5)=%; zU=Pi|lSI8QlujoOQHwgz_N1KZC8(gFR!bL^*>0vw1>U$w6kEpWQeA>t=s2h$PRUU> zdB)bGL>EP}d|tyuY^{4dlqQwFN7gX;B^Yq(n#D6(6<~l%7Bp5yZ2T0rcT}+$UKMrR z%#cE@+QZjKCH$4Y%(6sBX$|DuTht!+HHC$;k6*~{ls-!S{8FBH#+BsA9(|98F^QeN%nPjZ8WT$SXLB z?D*|+!C1cu@sjj{gc8I@)etWNWr4A}N8}3GWN?{T-I0gj$JLEhtOp-*eXW`NN8aRD zSFwWqt8GdVAhu5%92LCkGyF)qYef#)T(!pr6+mg`;kX^1g%9bvWI)w7{~{{2PcWid z#Ud@^1SE&U2$0e!V+D+d_n}v2uTBmP>gY7xfIshO{k@0l9|s~8LZ@1)uE1|VL=f}HEJ@|F$Iy%>Vu#SD^bvpRzoDokX5Krwg3er99Iz%Y~iTE zM~Yy2)R2DuHLtP4>b=~k{jREfkzz+iYHpOYK$m58uZ{9^5KIk}#ivVkgUY>8x&(_6 zP!2B^!>M7%Jg?trRyU-XB=Y$l^(QZ72M7K4Wao4E!TI@A4%MtJxCh0(GuqA0B$_K! zXr9mEhF+>I$&Y%-%!DpAYxX1v{Ax79ZC43#y@|?cZx*NR|M@@t99;z>W$@p_d=S-S zAvj z7*|n7r8weEGav54W^4+#!S9CW6i6UsbVpyp<$TY?7d`DE+D#s7de zQBvA)Ron-<0(o=;%4IgJYL!zFkJ-HksNzPKT~fH_txIWvcbw8}gawdj+2F9yU+<1@Ps{-PHi+2mV*4S})a^lawgM984WK+_kG!L;J5&OJ}1!$+Gs1sqF zsSgyfS=84x20ef>x?TY0uCZ|=&fuN85ED_c-^7lwjjwk%uy+ZsRqmNBZrU{Fa`qTx z(@qn7xo%@_Y^c^&Z@c%j1r9>aT3fAdYrEBLJ#AeGz>V?=fGxxXZ^IwQF+|2CLdvZU zZ|RiNznepvvqi@oZtCeHn`3ZJ(xyg+Fe?vTt>mOs7Kv3~$TKuVG(+#5hhz1D$A~u?BG}P#Pz8RRV^3WGLKKZZa5n z&{0~OKGRe&1%H-YCup?86?sP?W_2etIE!Kx58Cdg&T@)cPONgQ?yR|_wr7)Io#p!I6svpO}S zv1^5+`t-Fi+9{B?wwFotM|2Wbz1qc3SY-|SQ;LsnV6Omsl1vB5Dw;tFoP0z^4PXP& zMHg)nbI?RulKcT{!GI}RWs8!1J1i4T-WxK~otuLnlWb9#s;#_<`}Kj& zcU7?%kK@}rn-~AJ;LrIQ9Qtdj509owbh3PYo&%?c{KsIOe?!NudcO5H^|!x4#m1W} z{-ZW*6F7xxo!qIyGy2!g#$;bACIWLqz`At>Ys*3AIU}Z+Nu)y=a3vAWi((Na2@1AB z;3Y!xm3jzR2e0Eeg%(KSV!+eyz@^ujw zm_Q2a91lNq*7@E7V7c>fyaO)ktm_;syTkYTPMt%}KW_N6F&h8z4qb8f8Gw3bwgO^8 zK_Qua;|KJ7FmiQM5+YaMV{G%k!tQs~^f^6TsW zrnTStPg#4r-1kkth>&fekiQf;L*sw@q4*D1X`qBg2r>hSslD--fH zD8O4hW-ltN7Gu@h99kpJ3htqs7_L^%sSHGf8^q(mFiHoPdVuO*7{J-69U=+8NDEzB zE|aIp>MOF^tD9{>BR@xaXyL5H=s92eYqnrj&GR*|Q8CSdEd@THJq7%NMB{voBEn)` zczQyz6ZHhWzy!qpBg-xh_1CR$I$$>Obuk6k)FS+@UF+3Wp@!|&HSzU;mlWm8idz)m zV+zslC=mP-W0-xXh$0=&vPs&OsvocrI6k+t2I_ZSo*i<);)|HQ;dk-+n~kaGMQt>& z=K3E|{ZF=<8wl*sXtslhc#R>q>V!2x-nzx~s;|*mwPtsIP!XplX7<_w93%GC$RPMa zm+cm^Hp{GMmG@g+)k4Cc)iFCS%Y#y9Sgh4;m07FXaz!s>+3G<8Wu-|iRKg5xo^s~5 zR@lOpyjje_B|Qfl<0AolG7-RnybC9@;p+FRu>}cRs0(p@C8$_5q+RSQ(5@z`*2<7qeD=0ZE1tA%XX5mK+&b zw3Qy6U0BhP^U{stBSR5c&)YldjDfF-xhvX7UAvmDB1fYu>ZxIad{Q+Rb=q4iwu2Z> zvq-_hXzaJ{qAQ+lUWgZKChEB=cf1-;@?(9&g+L@@a zMXYR4Np5F3Z6;(wm;<9DJl$o*07{3USO0m-xDuBDbzqx*g;$do4Hy->9knn z8boJ0*R7&5_Ud|_1;KhA-G|7Gfw}AogtTyigRXk_;kHk{C;EGDkB|2cPVYc23#oVS z>Z>aBylVrI!Wl~-5+(4(E7Iz>e6I=ftk_<`{tQRiXR=iAaEh$0}1AT z^dx;gR3Ja778GhDe6N_0Iu2pIh-AZTM)C*B-v6823KXS|MqUr>)ZKd|3q%LHb8a%d zRsKDD>J#Trd0p~nuuds8oK>ft%Y_|=t#Og^yk3y!O;HU}ZGgEggt@MYJ2g%9neX+x z4_>Crt!CDWz#(WRkJ@f}1CfFThQDmXC}XA`Z67QT99u8$V)BQ_nEENmEv)_}32QDq*(!FiVF~ z8C27$)X&9S^<4auYNy1SX=$-cqu)~P2>l+K1S{d_!0a%i50ga1ihIbbhECNiZOxI| z@Z_zrRQ5>qEJ8;Ow)Z61!aOcC1+kA=#toXw?v29?LCn?eAm#oZNG2Z zpV7DN@YZj4Oc0sErgv3!?TOzLk5Q|IH9s&Uio;;L2QREAl8BXJATWUN64h{uHEL5- zvwAY{RZJ2H0b~?#YndGx+o2GwBnGtx#s*bcM8tEbrY$pp;QO7!q919e7d92m}YUP{IxHda&Xq_NJ`yyXTi>`InX)n3qv zY4{wek0{*4bd_>4YQJfi)2R3l9HyYc9y&zd^`!)&*&LJ{fhWW#w~0)Vr5tBwI0fg) zY7|4vbpW>leFD@&8wd`(ut`$xAf(|0%Ze*+1K-q%pN+JlBG2?Bg*^YvI%LbqL&da$^KR=F)><%D zx8-c7YP{L_gp=$3qn*%UNBt0BX|XGDVdY%vx5HyxWf==!T+})^m zro!{J-o`LX$MK|x#m?6{@6;7Aosc<-ey4&tGY!=&C@R^z?sgTr^RYcCP!f@!>5H=# zHHXd)Y%Q~{8au(&V_hV13Za(Qi)A|OZ$4(F^qJ|3+JWiesD&?2dby59FXKd?EYo3o z+*PebIsUHI=>)14{=Chy^hz(+=b$j73lX3b=Pf1;r<*uQ%yL5bQ}+^$AoW@ zAh>U?uf1duBet(vOnk@+{cx|o6BM$S^vSijRHJwVH)9+W`9-zy(kOr{$71O1YWad9I5Bc5DhnH)%TEM7ic7QHik`=VhT5 z^CXK#$Z~_rVmw6ijtxQ%7&%3c`+2~m1HB`movC>}}dlb67r`JW6uRT$G|G{;|{_GttDmY!pN1kRYYa76n zkO-@(%rX>kvG3_?;9FrBX;+7%0xAd2ibE$nVcfx?696eji`pwqLeSx%uk$l(EFFN$zh^w824T|BC|_=zewokr zQD1%4{CV4a27=od9)xxam=Rr>&7vfcGqWXXo|J8Vn#Cy(io5WrtLi^(cOID&Lmk0h zE6A9eVEIjyMi6La9P0#Jqm`M8)CYA2=gCFiF2S9eM{zEf0JrUtU&)SY&#xvgFp#d; zD=_8GrQsva((`*@S-_+YL5*NHx~3N>%&0AFTxI*59+RE;Pnv{!>|k2S zBaxt6OcZQE-Lp34{{_OBnL6YC?0mSpU^6H>5oBFmHPY8Fyo9;dxt|iHqYbd`Vih_0 z(KvaHHHI^Hjq@EI8kgzD%4URGc9mp88+BiDZM{MNm+{zflxMRToB({_i8zR6+Ro^m z8fHmC+di~~pXhRrW>yZS7DCYa%km`p7Gpy)AH)O@BY}T*YLHS}jNR${gGdK%hV=_v z=!BFW3a&&%cy>jW=wJbYyF=6+IPGzVf@&(4>#8!k(&RfJy8(u}0lU*2NN_91u>9t*Ltu4mttPL!Y$iPmS3UPjGd=B#pL3H)dF9bv4BCJ5vMZI<}}wM z2?YI`-ddwbLC)5dMfItgb@37dpzET?Xq@euaS`HllN=BxVT0s}>_{nZRS&&?UA z1|I*t6*c8EeWn|_An5zx+}bJk3o55=wQ8)dCF)CGL3li!=RKz8F{Edncd7=ob<5%r zyv8p@16q{y?-BG(7sZc}p~~i*@~@zPf3Zh?Jth15DBp=L1ERr@k(DooiuBHE4ja@< z;XY!vsKez4M{kgaRm5eMFV$rv+}9XvTt@jsmzAS@?l!ejyGHR{-Ap0q5SqaPS!k#M z+&qXToY=}Dm;+I=c;kd5@qJewUO2RsLtVrE=NL?9G6tb2Oh%uDR}dWS?t0UCdsUdKN}bbYtlV54?aTKE#hYk(S<@IM z|HmFG4eKFKmxe5^8o@VN#Wh*bbTcY^p)tan#}R4FVHqQPpnY1P`l)1-&Olr)PBsBG zllWSv_V^+Arc)3|V{tLJMPQ&bX4t)ANAzilJyO%|S20U(EcjXuis7C-k{j&fN7rb3 zRW)6?FD+BzC%&EWtQ9OG0)8sNw;C-p>3t$|ri>v>i##8RFTtC%dNTUkqzLy360?k- z>!@%fD7kZ^#tgN0*Y0^Lf;l{L?k~*B*lDd{eyo~FwIh&AySd)yJx7#yeW-`j%k=-7QnLl!Zpo3215EQ=^uvTh3YxZam^0UBjDDH;nvY$0gJOkdbw7jCF% z)Jbf5g(2rrJP*i$ToeQZFR0zj&7Bgr$uznKCx_bIK){1VS0VTEa_M3r#c4c?5@lIT z=Dx)X*K-T*t!v@maO>8*%hz}EA!IOw+cG}f;O*VT9`*o~SP`CJ1he;zLnT98)aY0B z-8Hb|#v(weu%NkuSWW^MmqyL?#8&+%nVR7_w%6r0kS7Po(@gcvFM{%NBQh15tmM7l zWyhGLHAr4!_`()mjn}=)vQmaxwA~d%Rc)_TTP#+E1*L4ki@v|Aa8ULQ&UJnDg4Sn! z(VF+k!wis(gPeefN1Z4M+t%M&xG@HfCY}7^=o(2xra|@EHmZqJ#)B z+lu)#`!z17(8#eSNj1?pjzTa*gH13MbgfOmzl&UfRrBeTd1bafflR5)P*53-OCDyc zz?yM6A6fvw`+RU2RQRy-40T@HXM#m#ulfKj9``^7C-C*7UEv^D(*%|vPuT7sZQ8&E z87l!;8cl4VUQojukiCGX4B0KR=p&Xc*M)-Kt`NjJEKC}dsJGDbBRg4n?zI#oKUZx* zgv(Ess!`9ME3ZsnzKU76s2oz}R|4Dbr9+zOk9*0EfJC^TG_b$`?73V=1@7pEUWXto zWKJOsMRXL2P>XXCr#pnsv6`g8RzGD;F~l<{sFWtvosV2bmi zjM7nGefhz!?5?LyL1&7J+ae%#B4IfzE;wcf1Qez;NiUHvfpk7w#~ac*Y4(A>W`c!M zW5S-#fa&3%rb}fG+Bk{JWl!ys>8w?dRkOH20dAb~2#&5McW7A~m!b`FNkjpN+0_z_ zM;zohDv~YZG#Mt(yKq|j0bF=%b>|B?CC$px)H)~%!!ps<>(sk?a=cdT$Km+s3XC9D zpFPieZhN6LSEBv;eeP=aAggX~<1I-;RBQNa50`M}9zZ}~H5Zl5tJI@*2>FDZG3=Is zxAucua6Rn=Vrg%zHG6yY?sf-j`$M>=<<;t)16PJzD0%lT1fcssNObrv(8m9FT=Vh} zdx@JI3k$mUUE~Zk{Or@NZ>E^MyNwO~KQ2H=4nzs!mh*l-<8z$(6mM;hT3$tdY9Q%VEuD z*`NC_`a;?A{>)y_R)OGsbyhF(pC)4eXWgP7C`=)X^E~FO9X{D%;_)8N+{%1rHe9B^ z7#xCcCurT`-py2Y$q~36*77vEOrR;Zrb#xLfQwoI6Z3d@B~~3?|0T+`^^6LzcW&TN zL}m0x@i*j#aMSfuo-HKjX@d#hbue2%t_V>XuMST4k9YS@U;VN_`1#ersoGIU6aDZ1 z^?z^t@Bj7x^wkL}YS}nN9YTEQIMIZsI=A&nFZ4-RKctaobO>WP5<7U=7QL*hh}a(ed$dQ4JtOiuBw1_w)$s`{ zsp){qlm2CtI)_lYBbSH760A(-XD&008E}6_1d~0662qSf^dIoKfw3Xz z%TKe+?j^_W%*?aY8K=Ak=fV`xVj<2^V z{Ld`N0PkW58el1$DkjF}*@f`3ihj{`_3*&SK`mCOAs$L5Fe!8=OEr&1BXVjKCGjMM zU0%ju#kzDmsl?h0JXP8;mwzI*gK6YtW-7bp3mGi+olp?p4Ik6YCO-h!`AU80n(jn!riik}gSTE+=$C@6?^!VTZaE zFbbm4x@rw*?{EXXaT1kpfLHuMP08J0mp&1f(4J>^58)VpP-R>K03w*EC$36y!6-y&9##BH(HDiYzbNVIr`(5$vj5HDx;EgE_)mH_XbO(gCrDpNzdhUttu4;S>TU z%;Gf5TNiF2?VN>o_&blua8YM2?&*&l>HG+Y7;vbU4RulUigJ`K%3hw`6w(CX4S3I( zMYrvz-MXx5y+XZtdsz5eSL+5EIPJ08_N)lL*$g|I>tddzg~M~a+2kTHto79y2!!aR zD_}&mDsUkeW*Va;EEt2x0OqL>afpry-x^;TgU-Hc?a3q0sbgfy7%2-Af7IpWkQP;4 zc3vCtft}UO7D*Wspm@d7lQJrGpJLu?R%!S55g`ct-it2rzS9YfP0jLxQT!=rU$(Ip zldNy!vc2WI!?~-MsEc*9$0S+i;Bxk4@PfecO^85NVMsyugxj`x%RX?sHf&Y5O#_Dy zs22L4veo`mx0QNAIQ#$KUjMS~Rn-d@CMuc3Imff`rFxbt}(`OYM0%{M{COQz(<8l8q`g5M-)tMl`eyV1ws zMXc-J3D`!J3#*^k+!y9o6VJGjz6cX8X$9*!3a&LtdJ`9gWJZ8teyh{yOAO{^ZsG+p zO9xoeLo{G5eDuJb0GfP0hfAyQrN#5z)9DCBA8m4nG;DP+v;~y}88{&p;tP=rk~w-y zSvQ?%bviv}xLcb-)i+hx;iKf~{d~$^5`w<@;j4^U*9xO9Q?IYAq0~EL<0TDyzx-gX zIM3BPlwi{9-8+c5MzdIyqd0Bd^~i4w2pXd}Df;K<>6ah0u@Jj^=h>gh+mT@a?X0kj z4GHZUNad1NDWU;nT_LN$5(2BdNJz!(YP7yU?UwAYpPNF@t%b*3wPFxQRcZ_sWIj=w zWu-n#MWcns|H&#W^;eQ+HI_d@k>#JQ%A$m5sn?*_QWgETMWIjCc&SRf>lI+cRmPKi zyC;7mSNE6I{4%;;RzJB(oc&4aUy>y!6{YJGq@89o23K&J(W>ohGK0FaI=j-#ijK3o zW0mHPRA5ujPR*okb>V7s#unlsSYpGy#RJt408wQsn@3LpZZg{oY1L9|*6 z=xl&snTX|Ze>zM>4}Cis5;-9=zF=-VQ}F>8kDUYvUY_LO8?b)vPRbflj>T5t+=U{A zzI2%_XcW9A)J!<)#Y97JZRPjF=jpHN0FzYJo`hM?(|YUOj~|6=6AIR0C1Ztvfh|7p+>>351@U#~Td_1ACsLH1ZQAkx{ z;CH!JU|`R@6>1Rz*esXu%wWb2{C!6;8Pf_LY^8&YSfK z1gxuv)YZMu6H~`o>G@M&X#~8lTxtbw1aI=VEOqL%Xo-9Tq1w3U!B?7#8 zlaEy14rv)DY``j9^#}7Tm$Pl@0|98wVFGv~4i|f~kzly%^tzYMXM-dj>)~>k=z+dQ zrVE^&>HHSRarmvOk+*=(CM`@csJ}0=5@f=Y1@Y5JUmZiIg?)+g$+hSSyn0eeqUg7M zrC{JmMEL|SPfHdze=;w4$9FXMlx$dk(yO z^@LuquIsQ6FYV7U@~PmebG!g`c+{rs4VO{ce)r`En)N$;;5P2m`r=*ZPAGn;FFjsL zpyOjs`7Af{f%l&!yOKp$6OYJx)va2{`*wqw0ku;Elo^8=Mq5UAtituV+;a-C;DG>b*oqxyTEygz-b5 zHwywEe0Y_{WtiVrrL`6o4O*t?f4M%7s3aSfSx^R(To!#B7iHmPp(N=RH8tw_UOtiT zskrVYiFL^)^ZTE?A_v38@i5Gci7p#3i_Bh=IYqjsHj3)5?u-bgyP{}1)A>Z#)h|EX zqj-wWx9gQp7wJz)b{QpJUYqJ=#9a~Q!^9P;>7Qgq{D}(ZtM$z}`Bgg3nu@?TS>$+; zHucGUx>rxVR!tq<-$zN3-B_KY^gX+0#Bw^lj`J+Vh#=Qd9;4nD(lENfu&MJ!KFdTX?@-QB(^3PV)$aesGGnF=TK@Ez)U} zjuJg$zlrFiCNuYBs1(C@fQXY_W1GMO=s`L%Uff1(6Nd9qFsGc^n?#|(Hqa21U`)<7 zT?0@#ix`1+vt2qiJtEMzF>Mhoy|eS?%0 zcJda26d*G_;y4nZe!H=uPPFc;X<5#T{>BE>`M&67`D7!S#~Ws9r{yg90%?zR+d(^J z=c&n-rR`RA0IjZKyDg(~s6LX|$q6YO#g%K02VotG%%UX{)4{AF#(|U1+%__BP><3w zwi2`SI7OYWPP_4U4nmE8=sXLT6^+Il7X-8iga><6T>d3XI8c`=vn)Y?pTThfqy9^D z9i5>2dHje$Y~w4f(B1;Wxy@%iu3{RICN%UCoUjzjqSQ02j~&lL@d@ENt5-m32E#ZX zE@szA;-&K$gjK=1FbHNkDyY?W;KV*D)cWL|rFMtIMIMzp0sk=Lggge4O&~W)5*`}s zDMa!n14N{h3cpV9D-T;vk8GF@GdsmXfE;E3i z8qBCB$8{8H+XAGZhq%>EfYgE)WO#V8@j6Zyw>a%&+c?ZHhMvgxYn+Z)*G1kKc+$W6 zO0^$7lE~+m!`G*~gP#xI>;o|iC)*1;vG(1`52rNCnViC%ayEc!MvqMXq43on055{C zA(W!+PnPpAVd=4itf`pl1a(62dTpyAE}n8L+*5ze7KoZmAlF=9W?AV~25pg{1!630 z%x?f9FMxnlkH>IxZW$Gs;FiF%(N!%nzq3*yPY1_~XCocf3$DS8>07dUwxMzmASfCw zOtV{0n*V%}rZDyAYkaw0>!}wQUb|3p*z?zMgb(XUHoU^18GIzh)I4~Wrg(+eFqVOd zGv}j;)!k)QwG#u!h<;3*yrN>5j5yPwhKgav%PfDzgR(066@4AYyQKrl!y+ix2`oUs zx4lMd3Q(SkheoE~h0q!6@;AA_kPSee$?eppVs)V3~ z-E-JzkPU4=B38q!W!iB*Ji z!fd96bP0Gq!m6TcD>1emg0O$4KGp{n076LXISO`MFX95D!-3?aT9pj|k*=>U>XpJ^ zYXGbulu6=$;7%*EhWeQ)#FZYHGNam|?O210LK^CCk>C-M&o(bE`mT>Y0~UpmZTYd< zMvdYxNNWDY$T&K_Vpv`8TmPWsFXbKyjT+#|#a8mzci+ITpy*^V#Xb?5FB7iW58-P(V*>1}B$Www~QD2Jm6o zEV#j$4W|_m?n~QAE5((&9=E*P=r;7T(-tsHd%EmWj7M!(c8a_c3{JdYPmbYHfhTiJ zutl_NvAA4krBxB!LBJ5&l;EI-<-!qXw#qmqTTfAu6i~fFxx0m3 zE)7qzOSe<8m0%(at%q}qaWGxbKhwQYTmU1X76e$7K*oF|$w%@6y-HSTWPPiL3l=W~ zJK9YEe=scM3ja8mpeL$jinKrtriuuksVz^?V#5{GrxwWPHVbA|mE&9kdvS@USc|ks z1@#8f5~J<%&d&b}n!D<}A%SB^JmzKY|F*Y(bn1Ts&W-gDdgy}x_$9GQh!awo@wmjG z;$(4#_YKjN9@O3|am65jr|{t!7q3K@Yt=aLHYRUe2YYV^r~48};H4M9Q~;^U;5xI#owUx-SxKk@vLO9d#t3^_he-RhI+KSy) z6$lOXV-?Xo7asvJS{BBnkP{S>QMwRH*xxO8-=^Q;cP@mKCJLYDzw2Hu$%(JXzh+dBD|Kybs9#yaf0K5J>Edneu_2b#{_*kP=-}wh z;AnsUc<^HXXz=RfKEKSsF5{-Fh(9wPbAZ%!ShaQU z5|!}?yenr-m7Y*YaOm?WPIZT=|mC;&;{nqzeb%0&R*CJ*NfHr8l(5wuo{{!g% zVB~qppqqVC8mQdblio#`V*0Pxfjn2ZIm?WxlqANyn9Df@Q zBtm6gJyJ0$&B`f20LUjXXV*k}BNZO3FN{_Tw*ayp(|P_pi28+bF8q-PY9}-pjEEcD zSk)p;K!zrPh;~#?mq*J~eB=>NO_VMfAxKiR_pZv!UN9$HIKgxlny)ePI%5G`E(r0c+3mXRqN|^px#SC+_SMl6KBBeaxn=xjJ&F)f6qjCaRk(ciDHAv=8UfF@V&|9RqQpx4JHEU-uY~9){C41F*-TvsDp0 zp&}UYtrWs$Yo{S_63Sf6IYj=>7KIJnwGyhTzFiGgb=P69J`afwv-#3ELPMPaQlKoR z06IOy6=s3er7mwE_Q)t6kG1q}Mr5=ozDVb}9>qg)W^}2i(RC~%jeM!Bn)NEa$m}9vz$h)!JGYfnF6C`RXf=^=+y$tnc6g4%VAc zm}wQe6FjR@*Bhz8<&e8=qvoD2E_#PGe)a=5IH7$3Cz}SkHQw1*%&*K{L9^Wzo&BJ! zD!jQ--q|Gj>#H^yy1&w8ML(sH$T}{isu|0(V>re!qpGu3k zDB%>Z?Fh<>+Z&)Ku`%hY);Q zn;l?Hgp40wmEH;uJf8qkLwsXH%@)8YlVDBP5jYcIR!faxTY=`GdrO>7+;Qo;W%{w| z)FN4(b2MF-7i>PwR>B)<`#~zvJVr4gb;Wk>ZL%i3;)A!Pe=OFWp9Do&BM7-59rb5w z#T@|<;jc0LCd}>*wobwvO&)0tToNVulh-V3n=l zjfOT7D5M6ra^^aXB9^LT)aHg;^)LZ}OsOF7*mVS9Lf~eD^`H*7xhkW?nghr8Yz^Lp z6bkhc2r@s5(nXZ~*Y#HK>1OZgMy~PX9_nIaIMq>EOtmh*Af?82pf=NN-F>LBu&cSC zLZJuoZUd_h_9Vjm!R93njl^nP-#E(5UWeX*QWI%3VW>6Xtu=_n`vay*^0MaN3u7$6 zWvwt)65Qmjwbpz~DYcG%Qmj6NsR_}MtuACCJ!V>0HT^T1Y}OFN)C;AkhG#3i#O4cP zVdhrhxi9sicTD_tv`A2QeR_D_SL`_yu?U6ydAMtyx+`Ah>{l2vtUi6)b*^hVQlq`f zim7!fte#%7CSD|K2r`y89WXW??kSZlSt+9}Q} z(?779SncM<*~A3a zl~doqwg|mXaF+u269{G3!#qn8=x`okNAS7?AMzn)#trr5mz(lsYpuI>6OYO%bOH5T;6dl$?iBK6 zH!x1{hi!h>x@$97jZF9BA99PYJwxb{Ut79YNRNmyhLJ@pu{{v*a|tv+w$e9 zeEC|wd?R1}BwxP8muSS(zYk2&{TcWmpvg!OD)NNjYo6-@26SYW^HkrK_T?J>gYUMr z?iws#0jpI&7fX#GOOR>v4*@db5sV9cx+0wDaeRwQQs6opSf*$iwrGj3Yu&XMQK^rj zy!`Te@q!tyaOc3)8Mi==D}jDO5pdY|#Vc%nPKB;f2AMO z=R&UScla_vz`nJ(QO?qthC*l3*<^_Hn`@QBhEDU56Qte~u^9B%Vx5nkV~e zIv&+m1Lpv#8rljOKOW`>8DaqZilfx%YJyN;Mz97%BJMU&F44tJjz17rFzGu$Uk?ZMlD|-3!ba*ek*mG3F^s zgsl1eIJ)7Cd^pVau(e`wc|v*O7@^wMazc~+p7xhX#c@J4|BaNE(DHHVZ;;F2hC*3+ za`uRqx?7f-sUf)fj&Qxv%)zax#*q$hMno6xEs8Rm?cpmiQciB-ayX?Fu6|T$sgi(0 znCUXwn?`wYtS9@oFn(POqq!&)iWf?t*X#)6VsAD& znZ`4lLUxL-y}~}>svKnL3D|eKo&bvwv8x^x<`cfWXqXnX4|QY5YRxZpJq2*=My=rG2-9f~6u6q4wH`ZR!P|J&}O;F&hK%Me2K~vCj)n{Y=X|OAKI|S6YIYur2N@ z&c8_E05WfIBK{Z+uO>*-?T+a31F0{wT>GiHmLd{yfeYhd3JE{LbP_@& zzPi1L#sAQ+`L+xP2xM47xUM(_ihY5Xv_rhx|G57eE(`ldd}XBDg#g-uj+K`QPR*Ep z!0{H=jS9VGmLK6o2mg$-e5m*MI#}jWT7X~8*Suwm#T+R@lc6Yc9nG-pv9=f@o}`3> z_!cnH&d1@GkUl{Hl#hmVrv+gUj2@0V zx(Nc410ldnG+`sY0|S>gW8jYB^fJ4#y^F4+IAOK`V5bwql_0+RQJ&##3&0{ywL>e* zM89F^Du>gi(_q%Gty$m638uD5bhjUz6YjY|DyRkS)MQ_VxDJZ=9(;ya$OL5 zPjyl1k?mnQ)ky6!lGcA;fUFgXMC{p~P7>tc?3pt#EvX_rSKYQ5>IA`yZ(jz*U@r5r z_w(-Y;9&R7{t33ld(h-Eea9EECFs3wU%*9XVFKqM*vyWSMRAB*40h?kVn#<2nzQi` z=Fy1o$2=OH;5v+=ml1_d)9;rt(Al)Q3Tt>MJ*nqXF56XxE_ zl9PFqawm?n8}SA;A4Q`P(L{*y-K!{wDW93tN;f>A;~NrYxFy zn?iaN6D}cc?kFA`@Ub0=m>Pg!P&yYlZ;Fs{I6g)w8)YwYya}4|50e-lpAvJEJ++G$ zzX*z)iStrocc z{Dzr+GFR@)43B~twoGeU>YQ{zX;Txk4H;wF%xGb*q^RuxYHNrHZNw0GBb_M6L4@0A z;XZ(TnL2JcseV#w-%Lqxr*LGr6SieeaozdFk_2SX>-sVEaH_ zehWny6lmN051nTwrHKCl6L?ZaLs+>lP?m?m#jxI=bu`~YIDUQn7O8h+&6^h}L3P5< z3Dxz*C@$tC{xOfcVsopKM68B6`i#WV6(>BvF0dRZpNz!6y(3ZzVc!#ZE&NHx;+X?LOz z)1UC{7YRS3cn-~`O`UFOf|W@#9^YYlqKUIjPP|V~UlZW)%FLo$2o$vBSD4d1UOhg# zy_rk17aCZ&hbc~jRk-m!VehE>uD3NMDjyA-mfVheNfPY8aEdq3ZS=-S@GZWap1u|z z7%&{96uNv>>KRQ(!7OlGX_|5PxQk2j1#vGZC#u7Y4iKLs7ZDz!iJ3ZDehrWT6&UDe zd}dyw(MW#P*K~{zVx+K%%!a?Pg9q0HMab_p2kS(OYb2-)5I+fKpYFgc#VlmLEE$nx zfgbR584*E^5*OZLyz{0};fl_+zH)ETkS|kM8^XsSal~or3@V{jGz*CNUeac{1XQXo zJrLNnVo?J~&@l6$#(3K_O>sZ)I6v0oTo=S2p*`Vt9H#u@ddV;{%+i$3syWPf8sj!t zUh2POIBWR4pE84pB2D}p71Nh_G+~zE7>{$FWpMLAS06qC;n;}FGA<1Lzzp&-*P0*4 zuC8fROsNqRuxFQ?C0gLmG^4{aS|GWrCq@VYAIx{;PXR*&A>?rUBRU7=iziCi;oaUL zqP#rHikP=y5smfRoGw{;PO=>k)+FXWHzd*riTFL{hU3d_=?YN|lei>40CWcPQdeO9 z4C|3{0>@c26c<0m<ut}ED;lO;iwE# zkf|RgifY48)2J}bfu{07R_>=GU>F6dC`^O&7a77yp3UsU{LCxK6Q)Pr7aaHy=}As` zVE~NZnM~q0mQgU*=vdE*>NIE@fJg|GOgD$TH-Dl^U^|GOeIb_{0Be_oX?=pDWLUu%9XGRCA|VY`;#u|W#GNV1$Fjye5Hd| zoHtTKF?sDYm`9eOy((U4J%@SV*P|qgQ+|&Nk|t$_)*qM@)0%iBmN`fbsZG!fGgFq* z!#vM!m!?NAS#!*LA$^-H*MBKq5D*Q#H<;m*d}uoH#!NUgM*Z`W%xYs_+;o@ONw&xl zeGJ5N&e9u3n=l)&QT9mM53dIZB)l%-br8IhP#;g+fp`W2!WNM)1blnBroA=@-r>a9 zV+E*ze1T_sb`HF^pLb7w9=ttxvH$Yb!Tt-*OElWRJ;D=_nFIF-PbV%XJu+ToeIoeA zDM?(Edsb-}K2%cl-vNj~1rc8uQi^QIrxz3o-tmfUmJhh{LoM5~!?b6h*RcC+Qh4OS zyM5(32;S}MNl=>JUz=C>CSU00GcYXIxj#K&0`4j15DetYM3--(x!DKao2kPsekH{u z@W}mF^Ty-$U(MTzUy@$m@EZ@%JimNZM(A!} z9{3m#Cj#MCJj!tQxEHJ##1as-8W~FyUH+Iy>5vE@Jnk>edzr^GLpfl$GRi|Z;m0Jq zWQvs%PU!+s(oezVKmf)R!HFYYkr4a|oq-U=aI*jAm;K|x$T>A0;>;2u6{lV#9k5C?f zERrZ?)itJvQ4|6#yxu=O-9LV@d-C(a+c!UAqx5~EZ;#2g)Qkx@kSFFFimU-&pRyug zTv$2iBy}cH>^))6Ws`Yq=TH zPKn;*0guaL-swx6;urii$~eQR`K1&)cBWl$?%fm7SuQ!3>HPefTb8f#`;RP*<9XP1 zk3R;Pi1MQgctWf;c%x2mdTvuDc=@1bWCthooZ^9<@<}}*`oGkiPt#xO;Ec46>5==a ziJYQ`W8i=B{=fvxWVw+~&3%qgTaqu#jpgKM7qsGk-aX#kJKaYm_+u8e?%{}83vo{#cpckb_E|M)WFNvyFU$H9qhe+`(l4^biB{V!F+jrc!)2=_Cah$Qtzow zdH35HhxSi%Q1CJ7C5TApTbm$_up`B zMFL!`X*|CLr2y6A#tUj+>#qGHtPlY=A^95o0px}P?kHXUMmXmP>On&SxV*#98)QJi zA7nydLwL}aZ_u0q{-C^gPUoZLdm_Z_9{kl?CdB_7Syw*eJ}9$bLghPSr(0UkB~c8< zH!FYo#Vj9*QN7#-hk51U{g*J^5E=rvj5I_J1kEm_iOC~G`{@mj6(o9g{ z(qhfpn6!4q;*!{VxI8xpSqhj0bX(q@5&?j&;Z86cJ-^6EMkmuwOnn%Wc`@TT`W5+J z+Nq`ZpYcXSTWyL**iWZ4Av%A3%HX@_CQIk$E74#tvKeNhM=T2}f|yL_9KZg=uQX~m zk7<+~YpKNly=PM68`x}lZ=Nm4mS+sI&v#+mu;^v+g@LudVP87_2sE=Bjfeusn?gMUSFPieR=Bj<>_|< zqRn1&U%kG4O?^8tbeMTG#pd~f)h+Sr5s#NPYmj0}*kr;#c%Yh7%a#>ywj92_WqmNr zU_WJf`6C_CNn!;=WFvq$lkWtBAAxi~+C4r!5sAAmUhKc9q}II;UhKbq1!(*KW$#^^ z8%45q(f_h*f18Twsr&TVp8z45wj^XBmCGG*f~j736YH7@l3niZcV^yq z$N3`VjU>tI%5{0x$R9iF=_!oicU_4&>6CchCfVp(0ffH#; zc|0sw#zdbh>Tkd>wJcEDel$tb>%2(4q#`+>4 zj#ia~#&n!PxMPN`ageT-Ic4@>gMg5@Xppi~GoGwAENHBP#UZTf!v>R*1EQt8+^M#L zRK=He(J>qm!Cz46Ytt9}L89cRQK6*|d74(8_BB!Cbl;^@bR#xi4*gl=@@I37g7VS? z?{019K8k7}+qsZDo~xbZEuiDf;6C60EDYTSG(!?@^Y2son$Hw_;z5&Rr7YU)m{#U^ zpljv4J~&05=t8pM3IFG}h(+%%f57 zHRSr-x_u=?NZMP3k_yzqEY)nn-8WC8`4#7BSXn2>GXHkWRF{!#SB2u*C;eqK&DwUg zPs*_Iza80-goeCeTrM!?INghSMG8t_uM8N~e1)(c7*$L{*@W>UdN%X<3XuMIB5x&Y*-q?3 z^Pjwt{HCqQ(7*RwMl(}^EqW1V*l1igUKn&#a7+%F5FpcPdv_~~1XQvcLxZp&0D)`y zpI0B-7>Y%p{S{Inwc-hnZ`_z$ZVbrh7Yl#s5ld_uUYoG*jDq>DOgTZhN=1{aL=(lg8UV;wtQ)SLw5L`WdJ%r#lIA1wF$#e)F|4LFisS0 z+pHlqwWb%b3UnZI+C5r9lL-_k07j6s_)wKeO40y$2{AUsL_w|w=r@^4fpYJGX$*O@ z^_tJ7q$JJs4J6yYW@}LpcmdvpNd4MoYb@Dh3(HW?pv?^b3oY>)V+tW=8Yo3i% z&7Lf(aeJv)cn=}8BPCPas$`Y1sw9M>s)PnN6)c|zc;AmA->}M3Qv6=z8_$yBlJx>+ zlj*kbvvKyaREmqoU(Y1Kh=pv?UQV7@~)!kfi-I=(HA?Vc?fA%j=_ zt2TL%GOwf)UfG3T-R5NuF+1s46uktp#e@(8Bck1;DL^_x7g9>Bu}G)LWBevH+2Z&E zdqx=X()?C7E zs;Y{0s>C*eY9}kO)0|lQ3a&;j_V9=IY3#!+f1;x8hF+te$l~fYc!cF z!d&Van1Z0CE&ZK+uNk?8ABjYHWe^A50%&|uZO~NC(v=YIAkECy0GlB62?^VA%mkN3 z;)Kr9g6dPMA3;8~bv^+5Kq6y1Vjf87KjME`s$rv-8&>wjlDIb9aK?%>o9ciwjMny| zK~{hp&cI5tHucdPz%0I($H$5#YQXtGK`;j2@=auG?KuDfZWSZFY&=mvzCTU(viA5- zD|wWiP4=~wgM!@da%bXOZ5W}~Tpg2?2av#%M3TEoBVTi*fE-d8|^`G(E$_N}D%o*F!3 zo=Dnlo|`tAD7Na@-F~8HK3Ua$>ecqiR905y2(_byCf?vg7nKQ$NnTwX#lRfkyd<2I zGP0-*i5;lxg=%ovs&Mo1Jplu5lhVzo87Vg^m^ll($q?^18&a&EGd0t?ILfAh8CabF zlWexFR2{iqBw_43!&<`fIdSSY~sH8=Re;4Bld_2jQp!OS~*C~Ax5J_%tP^92*xogu8=Nejk@D( z0g48Ot9{<2ur&a~j`tMI7+1%p+xK%&zeSSNXu(S8?Ai#9$f|_lqo^-sJVw3V9rb!Q zNmCp|-o#3Va5^>)W#Hyvz6TUuXsx%XHFV^iQMwqH*!GTX(&VG|NsAX%GN#`^Lw(SP zgC5)PW1LP8l-a{Vp!$FVZ|{3;_Kl6mv+tcMZ?~ozAy9zN!uWelb}!pBg!L?Jvu|<^ z*SJvdd!}R~g?-{QwEkosiAJtkrP3YCgklB4SOJU$Fv{T^f^%+~cWijnD1F1{AClIs zU66;!Dwt+lLcq(V3_qVWippj0xs(%Fg?-d(UHQwoGmQ{1YYvyE*bPUZ#o|`!BdTb@ zP1Zq-CPqNTBWAsvMp@EqfdxfhoQsyI*r~${UAhJzYeng3vUX+aF?P$ObXy~@1)0pk%Mj1IdA%7#MtvW#lUeYuR3V~tUhKzLKo=%;)%Q{mlhl0*Xv8tByiN|wC?UVr%eg!t7 zLzSdx<*e2bZdnn*xC(<8;7B~e)L=&Dl6}z{uno>=Oa=k(!7@aNY6obcro{kR2wa}!bkg(sTd+*pFFXuKn$*Rx8D_6 zZe4cWQtp+ZOBJ#m_yrUEpV!X)KlByM!%c;okSIi0I-wGLDh6Z&pUPm zeOd6OSa;;gYz!#ajaCEGW*xRh~kJ7ju{)7+P{ly2t>%~Q<}j8OA2xNwlG1S%qt?=5{~`>a}8w08*H`W5c(lr6Ra0&6O&Z zmY$R(1p#6x(txB1T8V-U6)c7sy`A^9QVcg#rQUwVK32?gvZA%vB57NI!ZJlPYuT%~ zJ#J)qQHh!93Vp=X!W|_dq}C+Sa$tc>V|04$N;N)uER#dNU7q>IF(@1KGvfPR3$4$? z(OgU&&(~aGl{+OY3oAiLJSsM=cBDWc`t~GQKU57mMq(>qpcAaq;MnA1HO#X2qS~3w zn#c;_9QGb2e8eKD+brcr6UCn7P}Hd~HxigYjx{u?C;)&{T~0gfwyM>k(?Y?;H$}3& zV=Z?%(iwh(=b)CzQa+z`BBI_)qSA}9P6$?(teKaBMtmWbsn$CdJrt9Db;w9sCpN=` z6W;1^mNN>V5!*e0ixqB-U#6`iaIg!3=7qwn5CYm3EcS+=sOpf-EGOE{L>pv$H-tob zX`2pfN`tmNOg6!%GX+pnFGxo3dkNA;$gf1y8E#32-lEEo({*(3tFx^b2$N0#$C3wD zizSM_0j*n&DbOSzcTMsTQCC2Hy7s6-jvh-Dxv!T~`3!R=)AAJ%R;sPZc4O?TfFeX> z!C6nCebPE)liwzXjLoO%C~`F9iYc^qDl{^J8EamqQT|870T8IjxaOjG!!Jar0}k_F zW4!^YakQPWX)@^Isc2X?51^X(6?fSqhf)S`D$|@CW*NFp{7c(J4tiCNB08+a29%I8 z6d4k(S*|6v1Y_o=?W!uJm7b{@PNWagRIc5}i>fv)$i9<*j4{sOd&NE`l@DC01i%+I zfQCb|3aHrH#>l9GG(!-6625G!s!D?#psCo*@sN~Vz9m4_giy0_gSyC$4PZr~!0&J% zz}Hz`!cMoUrX-vtjUO**c9loH5W_N5b7`h@JWq@U;G<^-TPZ72ax-8$1EJ+xv$H9Q zIikL3vKB3~c*o^s4h`!_H>1u89UDccRjLZ`0GAQml5BCO02k`~wGqQZA#l_mB_O*g;Yr4tS=wq$PkX_Z1J`Cy3bAl2fICjo^kF;gn=P4tlUmcTz3b4{BtYxkmbcW1} zxh`52!eT%DZn=z`50{j4Uv6F9_eo3Ue8eKjOVc!bz$&`S^g-*pxy4qwA}M!A&;&VY z<&<+wD<$PzD<#jWQVLjEtw{NInLcsaJnI2CR1?_8Pun+>EYm>NeLU;FdE6*c23VJk}a>=-De8&e9ZK>t-W zd#q8D8;xe2=_FhRs|!@l6Ych1BB;)Pe6RPFXc?IBY8iN|*iQP4pqUaQ#35zPg4ARO zpQD$|R~b6(NHxbwQ=lV?eg+U|+Hzj?kc>dGSu&1oghc0_fm!ghp`)KKXu0*Cq}Wx0 zyUr*CnhkNw)_O1Wm{?i|h~>h(MuJ^)(iEYcqpVi3kA>Bvb>ZFi!cMV29!=l4{dK;* zbBY~Ifc-)A(`kqJkx7$V{Nh+px`BYAPC$YV`BwYv`zYTL*_~MRRo2j;GA>=uwFm(h z@`KPOhkLB44myIlvg7iT(M76WG(!p8-_FSt^P7g1I1YAiAcq_ubAg)S*ppfMXNvz}%8*Vq$yUgtN zL65MCsTG_Zo5K-tmfj&HLrNmhad%`AS+jP(D)~pnamQ3LV`c4Wl{3BoUOW3qhPn2c zSqD87b*$X{ux=lCTSdu!x(g$dZ}C=R6VRV3cX9eUu|?V zn+EesC>^ccQLyyARk*zAm08a(=i&9-r1?Q)s>XH&qu5zaVpe}J&DVn3`gypbr0t$u zfIv_4^`~H;A%{DhI}F$=RyM90k0x>3Y_(`9_9)P{m4Od!wj|GjdL7oq3dlL3 z@PN2B6jbBCDDIb28jwTs=$XNct%0Quz-7@Thq`sjbS6^FZj|9|#ORUAO&E`8OT!Rw zHlRW)i64KaW|O5jg=wVY25qFzIoyqMFv&7~7LX4%=n)PIuOVXRb7J1mN>$tuwSjy+ zwwaTXhzOYng=1ylN@pD^^vRJ(x&Uq&`C`mzm;# zi;XK0G;o+mp*HJ~1>R*z!{!|itPV(=1Sv=$+U)O!kMF{0)8ZG>XjA(rT$6%6CADTS z!$zspO9+u3x~aS5DO)< ztafnDGlJ7d+%?V&*b_lWlTS1tj%Vc8?${}LOGX)Bqbe!k<|`_xN`K!otE&=a##p>m z#-e2z-_kFtR|?%ltcsZSB9ApzW7t$kb0lUjRWVx13tfY%sX_p;SIoF78zz;iQszJ` znM|6*wsnsTf)Lx4*$Q-^gwV{99K;1a#4@D$rL zy%ct()9o8n1x6)SE!(eggG}A(9vP!n!AQJ-$u^@z6n_??E(P<+6eLZ~NZ`A++#9f= zvDld|H}IH647QfGrCvjn@oRq@IHgjb5Old|d9A5eA$bs{5o1tN$bGaYnoLZs8#giz z16IswhCUA4y;U12*vk|(;Pll!o2H6Y5Cd>Y3lZfW4+BD#?(eM+y^-<~WIy(YkEgTt z2#U%zx6W&Ts>T;|2V?Y9S!MWcE3={H{iuI?sE;a?Jz9*sqx7%+Qs_BJHY3cqZH=L{ ztsM~|aFYVmKT;9PNL={jnoeM4NscbO2)1ksCX0!9ogh!C53irhkJ zd22_+#UAF*g|1q?(L=0B5g2gPX$!?$n%|7>pH z(@x7;UEh#)1NTGz-7BDYt0lx$8hQh*O%wrHEH({wYe7ocJ5nL44PtRgL*FJ3qMUVP z7eY8ZYuNzj-Cpg@ZS?R}7pBNcpp5hcb>=(#fyH(<=mMyWx;fPp`P& z5I8tX=NvO0QeRo1hp&)_Bd#Q+LU@v&tC4|Q@N3$PnwXa^FDC41=>v?+1*A~gF2_EK zSg895Dx%a6r@ALJ*%AiB0Ly(C0FBVIbOdl=5y<2Le(f05Q~NNLrbr+?kMg zYu3Qq-Wqs2*l&^58xw!p%}XY_?NBTAZ$(S*btKTEw4*t z_l+YqRD-fiL9=EV6%*P!!Y4t_v@DSvk(1hu%dn9kI^b%!w}PlDew+5L8B$*cfYq9i~NlQbZU2(!!ZQH%DZE ztai&;=d|R?7P?Rj_s~}5RZ1Rd_*VEz*p9ErXYng?TKuZvVw>Vlyx`@I4M+G%l1l=ubytZXT6(OX;$MTKnp^?b!iy9K71#SB+3uc zux7HX^Oa?%{R+NXuMMwcGV{D6!vOB8 za`fMbJYe=9@=91-$1R)Rzk+R8CmqIQnER>y87N82jh1@lHOPwQ2s;Ym5>A?W{0WjY zgX;;n?gokOAkRtDdG;_&9TsL^itIQWmE=TiPG*HY^8NW@rk+kYnJ;FJ(KMy0DbvbK zMw#3)o2zn34fR~B)t(!aV{!Hl&Yp^}S8lseQtNk?&AviU09?p=Iwke^horbuzIam; zhlm#BR&jw~X0bnc9PMd7_P9lDRYi9cc&jUD{qH1tk6=Z_l~+Q-yy^*WUQuU3L-@ARf3fvm>uoha9@g&|#skYi3OcCXp_9RP zh&{)7s-|VC7y3DnbVhE%jZQA{WwkTyBImM+X}WB7t;K$C&B!9#=OSsZlj1H*^(5-( zbx3P1vn?s?<+sF(iH;j6&U{}SZL6~VurmIZwl}v3Ta-V&;eVXKFdef-zg4Je@*kf6nKrPi`j>Y80O)6GP~M{`&d)5yuSy8YC5QOg)ihny&}l~>m&-=0sW zB|l&{&(5Nyke4zcH$e0d5dR%Y+$dmqOc!?uLhqX_l0vaJ9UFU*I+#RNtpIT0m2POk z<(H}0!{a=^J#^EGRICo~=yoA;?>S zI4wnUf$dUx80o?AK5H5R*Vb~a?EPG~L}40OfwOr4aYqgFWAL@BiGJi96F(B*rst}#jL{@zGANV_O#SLAlZ_SViQE=48l zghV|V!{mwTv;tv#C3wX+CYwgS;Fq-HgFziuD0Yi2J5;>|A0L&-Tl)c4G6LU%ysM@} znUL>E3wsGo`p{VmF@%|E0LV@#;>5T9?>FKi3z^?I-KqmJL9#LpZ!JsF-gCk}$CREW z&S|p2AyIfl+^&y`##1#~h6KI%R=v;xsXOJpO~Q(QtV;bFVX#7MNQ~bWH+Bo*OWI)< zB#%6+YP;zRHO({r92+4&2K5L~Q^dA3WzO1(45ehkD3>GFR@n6IFfr>)5|!>H(X~}M zWYB$hTiXRCSZL8449X6m95S3&)WfRx-yV{t+sIT!2x;seQJjos&T>T%RU%Ii258@6yjgAG4DV%sO!tF$gyqAiN$aEHU_ zdu!5T!Uwjb!MZ=h-a1-^Gc>VOEQd2$1ntEc^DuTi&tI$@cj{xlR(|p}Fc@N{7Bvq| zmEp2KNRnf--5+mni%fL0pueNzhIXh#;otz=x?wX|<|Dc}>5x$wH5u+lS;<{Mh0_f^ zv`xwcPwB650XwdXla^OUiE`Ei!4H!Ix}k@${*Df_4)0hfQ$3JFR5ysn3;JKq_}{}z z2b}N3hUnh1hf;9wi)*EXqvd^2a5!WwM}V>67R?r z(p;p0o03lKP07IF_^OgWe^a`7XQpIXkEYQ+zwM+wqh&8@XX4{1JEw~3e$nW-k>KoD zba`FqM@HX{(hhhAupxuk=fd+e1dJI*tDT7is*G&n~0w#u3n2FY?&BJ@hri}94 zoE>4+t7@5~NZj)b8brx@42jd2j=XYJ!OLXzZAwH6r=cYeD4TMYa20OeJ>)G7VO7Vp z>ugGg|3j|%J6HXsYP4Wkby9ZDLHWn|!|1>qTQc9Q*yNNt$vS{GMfW@|rfnt5%{w9A zHzmkS++0=|^3)`%PeQL}Nh6VQk=Oc&lcqd)PD&pY9XE&~K|R4!nsRVHV{nw;6NS1d zm#EmpSV5eD=E`<*J?hm|kDN5sa}?aL&}=E^s4`K?lq${KT@bpJS!Ia)I$U~)<%)@J z(ll8|iYFK{^P3ZNiff!_Imh+g6gAVFW;E{6Z)mW*5jUesWujwH^f8L+p>9~6xu}br zW^Vwb%m(OrfPLk;J_E^mvrINP7tt?TQ|{uH&l3pO6`C?)kkD(eM3$v79q5?DJY&sb zrtmT0#9h|K8^SHCAzo$=_Ddw2z^c%J9NeCka$$6uEg%pq-x-$7#?QIlJ?G=}pf*>2$xHcN;%+RaHzdt2 zE)|wJE7F(^%q^gT7&Ekhh}iS`{=GScNktEdxUcPoB>M7BX8P~}{hqjcd=o(yO0Bv= zdK4#3od7ns!G%pyQ2$UmlgqKLPfD(_OQ$HnK$etSD9#~vP(iumMnL>?oGYQGc7h1O z->K(rlkJX0()5*r?mtSMi>ht&O0snksGSq|$1B#*4Akj;dTXbeFI#Ip)j zTx33_5a1%1bgIh}KtJGJ7J{*@L!jSbtXn)(R|)An4i zz88c?9dt7Ms|8KQ7OZ;u7(yeV8%rWg9yLL+9L6z?BaBQVA4A)Uy~XJTAC83PFSm4$D?gjng> zy}5N#=kn|W6I(vTGcXx~=_c%~>^lg2Sala5B!blTVF6=2Q#Np-F^(u!3eQ7+JMaMJ zS;iR9Rcmfp@Kvp0P~o=|V2PP+h8;vUy3;;S7)?H~9_GAtMXR4Ps09rpkz)joIB15- zr8r>oBr8eo@q=W-?#r5C1q_h_ZxaYGH3(gXUv6Ub#-bdJ#WGxlUWj0Ol<&_!$Cv(1 zFF{O(txw2XKuF;^pdsj2%j6NI{H1LKDb*l*kDZhrXwOCNn(&Ji&j;Vfl5=ERlJ=$% zIlK}UaI*AQ%bQqvj}#Th)3hGP47V0nwGrVE zjCW&DlIHA(`9Pk_X9FwYZ6b8*B_M(^>K7w}MJAb`zpQk$eo*VAdt&{-pW!aY2A?jr zpaA2(9W)v};Q}>Lm_BeGirtYT0hf$)sE7z(6!__yK*Y5riXF|+2~6(-xz~&bk;In#qN^8 z#g@V3!o)O-tcOh|#l3JggCSlm*XTgmnIg*Ed_A2q!~}Ap)sMO|Sy$nLAA7_9Z~6b7 z)bmCHZzZ5v@A(~X`2Q{czteZU(a<|>$Qup4)sV@3ub~eb0_y9tGsSPa^l#KF1YT#C z4&tLa-enw39dcF?F0a91q{JZ;omf-eEFSx)C^`49r@`DuM_tQh;7q`BYPDX1lI6;A z_5E?5-%r#6J9S*Z!O3h?;=hA7{suEFGEqq2&rEgeIXfG#wL#2frlm|&kgpp0h@*V&SAu%nGIE=-G>a?Qe zO0qEcP;L3Qmc(fR(Vq>AL3NpV3GG zTnKgzHae~leH9Bp&)Q+F9U9J`0+?|G`(0r>jwQGbX9ObDdh7P$>zYBFuRv)G_uEJC z?{&pGWb$86bLyUL&uli?qBjU0-mLuAD~=nHUn+tHm<#!eeFCwpVR&3(r@+;Su;)Ro zg@oFLycor09O06pPjtvz`ncPcl^t)}DThG$bVexIYADAfByv_bjCi4Fy{@|#s%n;^ z2L%7lR(0saGG0rY3SgETO9R$OITj!);L32palYF0sN(`neCiN8rO-fhig0v=g38J8swtXv56&;Bl6S=FIBf`|(UaH$dF%95Y%mD8u$ zX`}7-wmzR^Or;lavh#|lIb1PqEiqVOUl!BA3s&*cnNR#UTtsaA1+J*_PW+)lO0u8)v0T44eR zlN3!z%GpmNe z>u4Us9Gsstk?5VWK4=sVCh?$3z?Kf8I~#wbtU_f5LY%rI~&vpfoO4OFo@ zkJTtB-kRP%fHy!G3~?s^nY%9=rMrKc%_^0!Zo z72#qU7)SBq$t%#hcb%^S%+ifRRPnh-b5sC4>g1uEqnNm?50(ZSq%Vpru_3YHrL3(b zW@>swu)FKQ#&YT#8?NoA+aO{LqRUxw3U23t4isTQcge1|t7H$oH?|S(y^ndh>ut#h z@AE{S-PrSPyQOlnE)Y4$L+KuS>lWzfMOhzoI>JJ-<8sp3X$;t5CwcZxcqWJ5`S+)G z)W5TI2^3G~Pd#jU6k^uUVFh;N0RF}0mD1f1sTK10@b^}Z6q+<;ePH&ISOm650Zr=+ zP>``TCy1EK4D}MWk6U&Lhzb|^ea3jK42`0Ww1D$Wd*_WaoBCE95Fu%FL1|XZdLc|` zV~ksNNWNbr#jSD?Yruw}vT&D<%Dk?#&aS03)hJm9Z<<$}F|Q0yQXfYwckqL;TCM>T zD-IU`z8-PQ(Z@@C8O%o!BhlpG9rv-w5SSU40F$ho$>s{}cAp{KfN1CnMsaqZSI2e` zwV`g*iR+eBu0^RxDoir~JP}UCfv5_?6p-L;k{9gjlC58El0z{k%w`lQG{dN6!w)Mp6vDR$_nfs(gm6Ue)9#aIzV>}I= zmB1GCD;pOTqdi%VOJd{&6M}+LFcixGQA5Q znPXl!U-@5FFio%HU_MMCZAVFRr=fS*FGWdQ>>`{k&{fj@I!hGP3fy(WAf#PC)BoQ**Pq?=K9Y_dp&lF0MmJ4tN8 zp7eBC;zq8neO@a>ogRx}!zabbojhWi(i6L;AobC=Nd#Jn<~e|#cq zSdLmuV04u%!Q<3zvgD54jIlrFyecCms_-5U0%t+xgl~n@t(3DGpKqDrG^(~pFGiO8 zXu^%FiZsLb$-5Utl9$moDH8FNtJMNIm6v0DGai{@e3ET5;jADBpB-cIfbchRl&Ft= z(k_TAYK)L#w(f~-f(0Ymf|z|Z5$IRBD>CvNt?h-3QFYv$WbAQj6IHU$zAHlnf%Jx7 z1c`IftBMJWJxyy@{BG=XOaQyWz)69FoB!LnG}c3TzdzYz!%p6g(m znp)so=oT_dwKF<)%1um20GE()Ou8AmB7wXt?Y+}1U8&E`Hf;v?mRh;YHiid`@3Kdp z$r9Azy2~DE9w(jj2pt;mj#aX=4x+sMY^;;=7BtU^Fx{#9-A)~0z~c^%fmH)0vf~be zoKuQZJmVV*KLB8Cvpb1VcL0Ui_LFc_`e|lzv}{;M8s|s)(0TU2-2q%zp)nzRWp)gR zGR=y1<=Rr3vRk>&rLt7HM|IERj?g&YL6M;2q?S#Gq`H<(5C}UDa!64vH_p&ft0)w} zO%XB6-EbyBlAmA@Pc#;}V8W@i1(W4G!ifLHn>sV`wuDKea1FZtSL<-4#i9#mxsb@h zUwS0{#i%n|_{+$d>w6g;uUCN+t^8$x;1|?37Nuci7R=X?$(fqA_*ZN8(+9E1(sx$= zGG1LcS_0+3R_mc+f9VCzG3@8nLw!i z!(`bPGCID=Ulpmhc244vrU=_+nMxrg>i zYZET}qy_2%MnuRS)Hzx;$@WfOCl_=3!MY&iPJ(NcLo#pgGSm8AOKF3}!MqTuYT8t_ zj|9d%dr-6CA~kMtiIkOSelkwm0(IrU1XfOF?yF_S;?(JwYvm7wuNj=YhkkSe-?+@o z`w=dmNU@@Nu#RU;W0C>G!}D7oGKaQa-uR%=xQ2z<5BE9M&NJ1Usu3_8_Y-Xc~-yvS}< za~i^$fe?~YV{5QRF+9~C_giDdME9IuBQ-aW+>2<<@tLPled9tu3=I{iMtY!^3xhYU zQi4W}dojTOMAa69{y9y4JYJgt=ts+{B53p20PX@(lz7IC?Ff3oDYr}nT{kC#)kZ%(vJo(3(})S{(_WIm&>QCIFvB-kF45*7Zxo zCn;11-@aAzzh5Rb&!5W{ZF4Iq5O!etZE@>PQg}zPY%}B2q_$t6A5CC1{e<-L>W?0T2} zjqH4w=&hH_b16hB+vy8uxdKA|T-s@AXHe_X2TvV#%6J+qBmvgJm@!kyTf$&&ol9(K zzO8IFEuV}$mV<2T^W1PfJOY-PK=oj2Xmd!K9beNiCt?3Ww0SdzzuDYOL}maujyJo% z6|)M+;u|&t+;i(TYDbzZXxBOGWy{oxO^{EboLDp!JDARuDoC2fHi_3A%#=lMuNOc( zFOBh;`OYRYuRY0R|Id$`hIHNrAH45(5cncjR(wh%7)U$v+LRRqvUpH6AMD&+aX(72 zg`$PV!vy4Gif8F&>*~>8W)H4K2Y3QNg27EiWQ=*X$$EF8uE`Gv@U^FlK0ZxqvQ?8G z6hk;^F)j<6!*gfOgY714n1vA$SwJM*c7%o6WEpB@lAUK~6j&sv5ZmCteS+iKRfJ7p z1qmU#vJLu-y{x8_ArST_EHPS(olPWhvQFhAX$u*!EHEu>>Z%|r?v~xtho!(H-x`PY zWqknM6;$37mv`!dL`Bu&pfo~*EHeuDeR!4@@s6NCQsFqY)xHj<#aJL(YBAESbOF_lchCNz~X14ifqeW0>0sE0VPjz>! zuYlH%t7ZiHvW-#qV(hR~lHq0uMVZn|iejMc(j3bGC?DcTQpjM9;O(9W`{syd_ae(M zW#kp6Yg$v4Qxpc(6llf8-A?@im+~>xfjpOSt!Up`BURI*%8D*%Ntrs@F3rWC+id~H zCZ#yDyxwD2EF;ORwrNWCu8R&fKzb(g1(>;IN9Y_$*Zyy_B@J>;%Zkn}A}&Vpbub@= z*AZ}*R-h@5rUwoH*?>qsj{M2YpRZ!zAUfX4Uq(uNFTyDJ60hgPK^(=CY3PD)GdG+@ zF`|M?-OrU7g;6~6KL?%3jW(>;7x%z;er_KeGwiO8{_~tNJeZYl7_=aAR zcWjEsUJ7~=GgS2U4!vubgN-tQ7V~Ucub1;AG^w8Eq0aBo+c{GdY5#9@h^mPLZ6^$)FImx z-A`x@Y1VGl76v=WuwzTZngz5>15G52t;t3YYMf?*cn2|gGYct98%&zzK#ksPtq%2^ zB32gUX9gc3L6~e+cET#~E{!a!FSuDaOMkw)@FPF!OK8qy3?w~IigYw%9+E;yU&8jg zzYOE+V6?i3=V3n|V0IZ+Pe9F+^gbz#&kqz5Yzo$U*krc^IlB?>JQN^4XfRhxEC|m- zjl!8?$Z)y#IklW=>%Op4P9RAA{$3~m5px0`57XXCW_L?u{U+1=v zGF&hE8CaYDW#!K!SWCS;+|y(K%fjJIf8i`q9Z-m8)Q_Nja_MUy=3AQ>9X*!G!A{yI zwDfHMZQw~UFL#)36Q9F0T#`LZXdr`El~7ami-iI^p_*6KX;$8zRsq&@{eof_(J$I6 z4+CRC+2$$o!pPmP+IlML{t__avlK|QVHvBQ@Yp?3(Jn)r20Lk=v=g(WvNA<05~S3v z^t^R$Z?i4f-*x-&c1af&%eh<9idR9(5O($AjBO=gmh2Da8HSWJk8e z_r#EaeeGg8IWtSzPLKi~s)jso@N~!Y3-bMo;T4%@#m%u~<#$9rv@G0V8>KlQAa0~H zyURM+rH^F`Xs4jXME=v^&neX%^H!ndRIdSF?g&~#Dl5_G2EaAk+nCBRoI-`n{U(Jj zc?C^MK?}&+andE)-+AlQ0PB_-r$r=nvj*M1bTp#fwZs69NNnxtOUb`&BONI0c72#2 zBE^f6`DAwajvs5vEEteL4?uzy!=RNH+0|(cQNIFkqr$TVgL2H>qPrPwy-ClDOdQ`( zmcx6Z!B~hb-!!qIcFiWj*a_Gk77zOH{59mmvlMq21~rt@-V$Dx zJRuKLH*rLIn+M*0M8}ASj0$QlMZLvZ8S1bh&yq4B+2c5AJ8_VT*p`O6rP~hd4x)>q zK_X)0>|~~PkDoncR>#&S(uL6g1SVIW z02MGrWm7&*l#R3R!}5MO#to%bJ83R!feg3wsHIthS&gWo^A!VBt)@MMV$rJGv06Zr zV=q{GS)e7ISqfNO!kBtvmFb6902O_CW}GSD<&0L23oGl54M9yN1G~ zaDI{qro6pG^3$WOL1#K5mfmuitv(IYmquj_^UwZw2sAhU0~M-n&jW04g^5bFsWqy5wl z>%C&1dm#4qMFh3 z!dC^WTnu|@mvyYVG7Hcn%G%at8_|aRhrtNq-M-n1_Az{Pk#!;i z9RyV+GAojru5<+2$viOyCl}k|T}9YS{C?69%$qHMvjb`k8^0(|74u#pD<+;KvIH&GqQ}FgDp+S1^|`o$c$k2y@T&4f4Ct*$`*LQ$IAjw4wog&D>sVJd)2 zc0DGlfPyBeKx;Fm9R|9Eyod&=g-uel10qBqMy`YD$a9vXUft&j7PBx$J(?pWXRq7{ zR9{Z@^&0I%56`VKFRN~i3}>& zukh)nh;FFjLY>Sh+R5x@(zG7VPUx*(PZ|3DSgOQp&=CAOZY3RpPjqUTJxqr)9khQ> zKdd>H1adk&KYC-+XOHB}-tJ*IdI#vrHNu)AG?gg3AvTqXjIymnj%8+{!GQfGs>I@D zlbi<=YC+p<*?jj1>iQhc(PDUIV{vTv_{$DS**sim8no|ZhDZnOh{%qR1oM_y?xorb z&=0#|LskE>YwHsk8`F`wWk}yzYGhBp91B zbcsS>Gh8tufW(#AtK+gNeZ#WvZ+bP{WAt zSy7}aQt-fpo;c`2TW4Ef83Uw_vL)rHwD*z*%zxVaKAYZKyA|V6qboF1oJlz=q3s&O z9clV`=8HksCF;mas8yh>N-YzsISY(Cd*>;W;t@j+ZCo&?3WkCs`0;U9#rO#gbg#u zLObS`w`}~Mw?sq%N8ezK;06@u;HImLH(87vE;^m#V@Y1(%Itx^u*@FtB`kz~OMh{N z&bqdHKI>s>Fb*d?D&Vx{rI0G~Xbf`3uV0Ub>qM+a#q)03>>?}b+hdU_8BM5^&1#B431@iIyL13Y(tJi|v5^SyVr{HH zw)?8hO8y*ij;#P!-1Q4!3G7&UYGDn-H4`>?*699<-tXvyKRw#z^wpPI3G`xyOL*ve z*eJJ0IxfrY(J)WAPcO*)@mLb988a91N^g^8^|>O5!V`AGxN3|+OSpOv0*q79hb^|r+a7!@3zRK?Gh2iD zez!ASxH;~a*~k8F%p;sIYuRPg|N4LFx{h+=|L1?b|L8fs^Ez;xzVekgTCVg z@N)@OTtQ+8_1^nf>WWgYpge}~WuY^9!)7U*WP#avg&7_dZroAJoQA@TEMSe3uU^ zH*%ba)&;JvOZtTp9S$}n`#43c5`lRHg5Dci7Z;c?retW9|2lx0u;7exeO)DAaOs32 zDt@U6{QS)ErdXEaS6CmHbuOXicP{45oRbnXPq`lG9Z7}ePQKDcWHoDs`$7#HlHDsa z>zij#;)m5MGupiZ$8pBgZI`@Z`CoBRWXFOQ{0`OmxDR3TZ{T5Ab>0Wp(XR@**d^Cy z6?@~FkC<_>Ksv(|`!2;o$BDQY?t}eeze=&SDglbAN_`ep(Y6%txk^iG(6dsjPh?b8`0&&{?<@X@ZW%iS-C z=%jiUL|{T=18oBlWBg`Mw&L59mqAt{W5;LQ=_}!!g)#A**Fx|TWtzKR-PaTE-Q=r# z=TZVzDd$E2M6CxCR?lB&baF0XZ>$~vlgp&EEKi&lIMo8XXu#tH(*hovIHO+vrQ>a$ zD(_+Wr|JuTp8qQzk|RR_tW6_Ff2z`eE1kfx-2amG;7hKM1_#>Q-mc*WQDO2|dnv^^~`}%rt_;DTAj^D2%glk0T_#aMdo;iNM z=1Z6}m)1UQ*U&$wp867b-4Ez?Z}r-TJmy7LT-br*{Z5o3eBuHNz-7+cU@6ZmWZVH4&=@2JJ$!2N8)ZQ-pl0Fz5SaPP@eytp zlM7RRV&zX@jG@*MZ-4#QuI$}?;`pC&vmNXHGT~2vYNzH6SocI);MEq5^TZPq*{1zZ zuGzjO-qY0w=c_;eM4;8baXmWN@SAn_#CKlNi2x7G&9gH-u)cb-ojJ5ze)d4=nLGN6 z9_Tzg-d_H|{m&oJS1=D>pTAJs=p{` zq((1p?~k7k*YGLN@jiL@Zl9Jt9tv;?#HW_@)Fo^&+MwC_s&U8ZH|WhDZ;?1b4&X|^x;mI*arB2|5I&9bBQ^bbr4k;LdH?nnC(e&-ZRX7FK4tmT3@2N0ZVODAjC&T8WET@{|3Ce(;YPJ8!Pw@-Qj zMLlb9mv#OrloAlfco1B9bc}d7^>ltXlp122CromNbF)uL_5LT?zqd3}z+7OKpN}-F zPaTIx3gs8Hzu@t9HA42=i2BSNdb1vpi=8pMR0=cb@%$j)qq--*jO->uEm!muF%W#O zbcrK5(UX|xWB}&|PQ(B{!Gx}XI~nxL;XV{N-e;QRf#c1Uo?2MB2~ZgUEId<=SAh2g zzFb(e9+ZZh5smv5JY(^l*2w2ONr1L-!nHE)V489hXU{fq_Od4W#0SRv)-@1FK*M7@ zXCs`CBb-_jnpvaQ6Yp1-?oRY*00gS{+ZwA`0Tbx+4J}HgSK#~Kxw^gk)KcDpVTw+e zY^806*UyZ%B_w~o@x4 zFwX37LURqfzOm+$hTC_foVmYS`=dtdwBfX_Zp8V?t4%7 z{#lAIo0&a&-4e? z>7C7IQo5hvrLTSOR_@1G@)J=G|0H%DPOKfSyI+VXNpOX3p{M+Ju2~C-p%=#dC@lY9 zn7&c6;%^?6##TwwKKPs(2qBrEFu^ld$!k+Tj#7fZHzV2T{cdEPK-av~og0>=`i^%G zOQ3Kql92_ENO*Zmd>n%d3_e<64qoxe;jQe{OBS(u^VhlSICpp%1x}^!tsEnOz21-L zRQGtkIEA5tCkJF$Y<76EXFx%Dw^S0gt(vc$u=9E5p$cm(U8D)G=C9XIn8I@X=3YuK zdwUPJ=RL0_kp!cN%y+0)Z(xN_L@bGgE_qD~v*iltmAzq#&g?bILT8{YFNr(?!pJAW zh=z4W!ezrbgzJ!*EnTdq}^|E|?8 z*(GK`RDN)+M0nIucKa7SEnq->y8i|iD?4D)_N!}kI}zn(7xhJk$sLlxNeqPTM_gL? zIrlHAqEl7nFYBMEN?`NMv|CK6{UNw7eB~ z)Bd|7?vI&%#7wUiW=G=EJS!u^ZiKs@Rm}y zx~0nEeB;rz*B3tgFT9i80nuOhd}k-3ufUyQ`3>i2gxiO=!tLX?vsc>#mZ8g6f3%^Q8;kJKSuc6I0SK3gBd>&^RYwO|JA$0CwDKRYgertdF# z>aEW9?_g2Sc*%cxx;3UDx`}xDrqJ30CwS#P_Zf0>e0jQszUJx-u;@D9jLV?Kc%Je_ z+q*Ty)s=P9u1_}Ndynt!cptv*kGezWm$D}P&(=rLO!SD9k@xfVcL~?X-<%iq-Eh=Q zp@=V*SfdL0H%MeYiGbR_C&y;>UO{P$M5!EGC(}xlohB8F0qz`yz~BKGrSUo zuP1ux1+yNGbR*7L%6Vj!9&mG%fZ!z?uB&88+?TJW1CDIG7gZ196-4<;pS9?f94T+p#&=%{C6? zY^Ha>!SH?XjM)2`ehBUBfA?2A!+ih!;QM*e+_@UWg@;UUif6hP3f<@1*@OPb-RR4^Iu_J@aISO``CK3jLg^bZ>DQD();$!iRn5SV;l~X z0m>-;$;2Ey-D~jm*?R09mDc-*Nq_xcDBbm&Zl1qMT{2npV5of+fL&%*^KW`g71?_X zL&pi0KPj#A`(p4)$9fP2Eax5s%59|l%b8&z3H9g8A$BdS2A22D`on84Ib4@VcSv)p z7KMAyd4>#;Yw8bc{YedhJ+o~I7N~KJh=_SiXHM#Si`_~$7?wnTK$3O# zkuG@uM+A$PpGHPG^U<>y#|^Wf(ps7ys0yZ+cUxamV`Kdi5rjO47gxPsVY6!zyMq7?UNZ`FTH)r<#7M(ZSqnC zS$t}=o|&M-y!UQzkB;nHsBf;0)&=!SWgsvbA6;hbU*eUOl#eE)`zWM0-?nD*>VO%P zqJN0=m^a1L5ldQxq=|aL32Ky3MOL{hzSrH5WIzcbZ$HAlhsKTz-{1>X-CvR;?Ddo6 zE2P9gHfNNIV2jROAsKhH-%pS|eR53@(s#xe=u17lfrF^T+MmB>!;$pX+^~N?f1R~y za1cQlp8X6tg6HnM147{J+4o;~MSc1X`d@j8kz;UdT__{xs<2-*aTcq8&*jQ@CYzUc z_Vc}at>^!h>hcbW$Ws;#_Kj!lE*44kOpU(OXv*}mzk1I$c{C(h%nK|on%xQhSNE^H zaH=QGjrH)P&m0%x{C;${uBWbxuwFm9!k8P2Hf$s$Y6kS(V56{@#pmvAA&WFN zXUQw5p>g|c^mpE%K==|W3vPjZD$D;}FaIRSdh~2bmIn*OM{Q1$W0~gmhWSdbmQ3@CEg4{tzVt{==L&V@aa1Q@%LDVZ z#gk~uQcctpCaHaNKN6J-Z~22n_|$-FBY^9#UGIMM*nf5ntMej#r^S*wvMy<$Ik;uTQhyQk_Wp&^lR zRvcm7`d`PR=bMaAo@=e62kqYfd7FOH5@53U+>iacUpFQV-F#vc>%LIIYt~rn+%@H? zKEcfB+gw`L;DC&Myn4hn+^`+4s*8wL!6oc1cpDG(mJyQr;pNfQhZ7YnU#So-?C&`d ziFYoYOA)T|RSt9;J-JY^OdV2F(`O`;C5lvzTkprn5`p!1KWm|TNVoQH6P~$D{Bp^c zi7!79UH!xD$)rU3GhdZKw53!{jPTC`-~IZKfC z_23NKxvXvi$Es19nT5)}!p3Z@#sp{`!Vfu)C4;-{sZ#;9TX?MU4s9oJgnqjl&(=QI zHcwvK=3o8cHXim*1G$}icL#bpS2UZJsG9%PH5~Zsd!Gn+AhVAy5&w~;G1Ga^kt627 z8tla9%@AbrMr29Tg_e~uC%N z#yID%4~XGENHnxKw;n5aJ*~U;;)iD+4VcMlScj@;2i7&iF#bI}o6ZdSRCl(16TI(q z;jzyWA->+YefG=PauFFm>Cypt{G_GC!k+Cy4+e|bn*^SPgp4VL-n=~4ZI&p$P=T+MK3J!QCvDn-~I z>E>q#q}DF>noq3$Tb)ajq3#A4;0y;VGK2L)28&Gnd!yCbqxI>pFTm^m5qPcqPw9{S zGeh-99)4<^u#TTEz+cZUz-x;RwwnMdVgvH-%zE;kUr(QgN4R%ph-&x$(mj$X%%?|2 z%niPJgV%{+vSEOGV)=J=5Vue5qHo+2)u#~IP@#%-P0L0~VLvsj-W$)e9x~_SjctMB z-9J%^ULvImiw?gsv(!Dm1^YJ9eEgQK=?*7M@^_$Z%I@n|M=C6;DkuXa1y5G&k zGh_5e9)4T%7nJR1U`zycFh zp^{WFDrS6El*{?W_#Z5vYcCvc=}}!C^5PA4f^v<1aTUL8=2)x?Mlw)cVR*~EWsQFz ztH5%kHzKn54IESEBk{sMnKJKA%G?o@D{4ooN!Xwa874-1Q_rs_WoR^k{r2>`xMSX! zGM`T0ML;Th|CusJtS7l2>5fqo2+Ps8dm90Ux^LCK;D*`uWum9;HoQKKt5a!nhY*}KWp%GO5r>&sQieSkgh^StP8HeZ$W94qJGMHu}H1#sX!i*bqzxpLaPn=Jj$q`_prc90(CDxy0LvQoP^KOBsRE`|1w03eVWIJ!3O>PNmH> z1||ya$(IfsYRSFVqxHSL|8Eox55DYQY}a=CpR#N7f%i9ZUSf_mW3&3re^+y~0rfo? z!4tD1QM$D(!hSlHepaEHO8u-tCJOvBvMEzrhyW)OAtkP8M?00h7v9+grHZ`ol&*+Y(yNvZrnVn ze|_==v%X+r*fBof2>rJa=1>0B!u+9nX8$bvK!$aHBaOXZ2(Orx=djdnLNFL+*Xe0o zhMR#N0D&6#iYw+Ark0wQ(jG)x#)T!()w|M(mgm5J=SEP}pRnJdY5aq3Tl?|9(SGO0 z_|MtzJgJsIa?ad^+po8u3sda53pFUjeLkxX_tAR-@BtnXq+Ce;U4y~^g`aO|A69y# z-FgiJ^2KAk#fdN(NoAGs5-rG7oLXB(o6`N!28Wr58e(K)>*}N>eOI4U7M1OR)Xkl1 z-M_xMrT=GD{9cAa@if*1>zjTZ_t`W-AL|NuHBajf&iWt`_HE4Xn=jj!G2f^aLe`)m z7EjMtbJolL7ro?>z&!F-Zi=(Mp8Y9BaB{yj7%5MoVYKd9Gz>>gchf%whcU2Wwi)_f z>G$lC{Pe{*8CWfGU}m32mei|{`@<-S`Vzy}89XsDfJc8EU;}#Zmr)-FjPwWIvQv$oz~VrA*J8yzc6gQTlWww=I2`2g@0ZPyQkId%s#pnfp3qe zzb6&VkCR^aldi@zuoW{!Yirg+vjg6%PKZyo8ZngrII$=L?jC>jX>!(RkiXXd<=iYi z0l~ghUva(LRWUj<2QE(s?aW9%QSg)ZM`rUMgPD+9>;E{IiOIhmW}-KtqkiAq za!=nPYu-E6hPMu(z5kNy?SBZKBH-`v{zDiQGKilhL)H8Xx&u!D@=&h8oo`=(k)f~} zf<)lSzNzSX{KWuNeDI#Yq{wZsip%HmDS+RRy%{0?mABR#XY*f(JmE{~jTn>iq=Gs2 zI3}`9ua2e%?Tg8e@z4nI#_$yT^{#Dp<4~G6c^d2DpO&D{sxdH95Qdb}i!DtBDRpm^ z;?%^{h@cS8QUt%GRj)fTfFGk$(H&5f74M+YDqMZ@z zt2iUxI5k)t;0#YrG$Ma^Pm2yBBg7JF@ym-5KI9|s-RC~TFZ7w<;zccC@!Dkd$DluY zeb;%Xqv4y|{tH{eeKPs-!W+k#y6Chky%&Sa8F__qvLJ+&Z;ke~C*6rY0c;@j>-ry@c|K#vki+QE-(h!$q9XF^&**xp zt1AJV&b8)x_q+EwU*?pgyVKX|x<0i^k{urWG@keo(F#b$Scw+yfFZXxDq1*qW)%94 z%jf3xb%?VsHVICp z`yN!OK*xX{#%i^@tF-{m5qeIUpw^M`>pNae)ZD1AX(b<(#}VmP*3)Kg@q7sz@_b%3X7BHH2yK@*wv!%$!1peJsB*xkhnKJneL0vf7gZND~?=J zDJVM@^jV(qzm3ev4Bq!^MxD`n;}j;!r-@;Q-QtCdDU0lTj6>W}=TIx$1S_usKa_h9 zrCdu5xz8k(H=vjinIb8|!8?@D)v*;%8lv8N{}Y%|CDd1u*jGOd zwy1%-%ks4;jyV{fEIS#IGRro5e#eO#n zNKRnRm9> zmBig%49OPr^hM&iUUkz@L1F`|n)V7dL@y2Xl<$cBSZ&0v+~_4V(xQAH?-hs#?SCE>Vb}3aOO+{Za()tqe zNjU%$UP#EJc*QO_Ig_KuK@||d^I0>qmIi`vp(p-<&ybglb5`qAKH8DqRs3BMm03Ea z!&rhnlAmbig41eAGpE#%C#>3u8&P64C_+PG(UIl*50!0?3 z6Wj5cQTDj_P8aPV68~P7fSUc^kR^ckOqVvrZ)FK+E!;Z8L79#5^lTjtV}-d$OI0Q zRwzu9U@u*+!1GAWI32*fN)_jvGK+8wo1FX#LUREWPE-Y_aS-Yy8x)?~y3xb3AWU!`gNbkhZH;tE5>S#?}1A zj3N6$Nu}S+9cx$(9X_|^Mrl>m$Luf6x~c4p1?8U-hksW|#Ex#>L1^bU{bm@>ugZV? zP0tx=ZB+)BU+{UW`ek>d!it=UE7_~c00&7)JLn^^e%bEPB8cLmdr191?yT~+`cv@` zXG}58Y+tE26|_bFOfjI$RJ_5b!BX}G`p`#At^!6j{wr}oPw~pmJD-AO+jvFN?w+Wh0-2&Q^q9qVH*mI}1kL$BY6&5C4${i?jshBp<$ErV z*%7*WL&Y6#lg;5iW}}1bQNxuL>Yqv}@GC#0x&km+Hr;mfe~~BBBW;OV$UQt>wqePT zAS&87@w2I^l*3U~VRN|Bx5ql`VtDcxOFX0XE&gl1JoezY@s*{CpCmnWw@jByQI5(6+bm9EGoh@w>hf&#`J&2ob=MY7KQRm<$Dqu>20|IM(#P+@B)*@A9Xx|GGcLEvK*^CjX8< zMF6v4e*eG)^7w4BgY*~aWQm902Y232(?_|JiAt;%Rr zzympGv@*U2-YcFF$9_N9^XRvVpH$%<{_7H7QgXIKrEFp9o&c#=IfyC)_VLrxk^t~>GNy40;= zb6MR%xvuLV*ZpX6l%>08XA5<`M=-cHH;}7jC$N;o11Z8<>?w?m#e?nbn4wa9JdhN3 zp~FtQkvSd)#K7i5?vYimUgBXMq*>Ix)OC9+>psPyvOCu0E|S?;`uG?R64lCg!|8_M z5KvU9B=TcO*{jjAR&i9_KBag0aS33_!=;wKZ9|%5w^!;%4AUi06;HW>g(c{lk}%=Z zZvfmm_1>{p(K!3r1YL#s`Bl+=DC!i{YS8a-89s}xg&M}Vgeg}uA;;L2ha>c6siF=u zn~oWl(MS4~h=Fhxea_Hp$kt(Q=Z4Nh0Zsv7x||R(%$SX4Jho5b_0{M`=D%rlPMCNw zVRVz^N23dV`0$sSfp&j6L}cPW5!bM3B8jdj)=OP2J5QWT9=*K6?C@BFat_)F>te)5 zNyy)eGk>S%R?TP|rl+&tn)nyP`}Q?+5iShYtQC z_NXhyPXZsm5%wzgS6F)-lV~<0si^>|?UXJ!2Dzt!w2wUqGHV>8Hk+fM;qVg86z3Xg zBcwTV4%^aZ(+A1Jb}=#dZcjW(O~@U=^GLfZ3u&@D*gyDpNY(SlCfxRd`}QkTQ?ye$ z&?)7t^Jy{{gV-M0Cs2ny#z$BKk&BRyDv?CS?jIMhBL3YRE}eUMt*!E(G$b)2xu6>| zwpkNT__u;k*<~M|E1q*eY@p zm|2Y0)H7z{-*S%>7F~tmMIk6 z+$%($V(je3LLKqGNyu{Ma5Uj>GXpYtt0_EZcEGy4768yFa*CR;4a`=J&#OBSPe(W- z<0E<2-EM|WAMX9~e7J45b}0L9aB#4!8_Nclh<+sl8M9%llO?s?SLGT$$cr^@ge2!3>!a5hQznKlO2A_*Fa z2Crz#lhdCJD|8$p^I}xP$NZKMKbhV`y<1mGLXJ$Z=5_z+#<%v&=|cPuwn zJNoOsVWMHYedHsgtQgjE&SXn|#e($OTz5q+F%fML80Jt%AyLo^Jf@z&N{ku8kOyoA zo_(MjJHWlPqJm=Yk8rl090AXcsB9-vD2O3eA+!_tBVrh!A1u&Q$N3BkG14JAGJ7aO zgy^sk+c!IVajeEOPV%r&@lI@MG>j9lOZQsyNn^#j!sCWwz89GvK05`J1i_9UtUn|o zQzKnW@IiH&rv?cZ-REAdcr*0DKqw`p*Le*Iva4~(OeZ(zciYH>5APvDJJX-M3gHpv zNnw{-Nt)KdtR=#C4v<$RO1}~fLYop52`t7zr7J9QCMA)RdxoIZJgrU!XUPI4OXtwv zk_MzXJp_g*Z1^CY+L7TcLiO()h8V7nO0H(G8qZAU3 z=3a#Hbf@sCOAS0LTlK$D+wPk^8-lfzD=myeE8m!3ylOT)(XwY_8fdYHQ3DD27#k=h zq0K45EOd4>YdIn*5A_SoHmMv7dePzVB)5vgV_dvlGewYl_U4#R6JfA)+qG#I(Dmi; zrRD}$Tca_MxYKKfO>H{sUh?%g`_uz#}llC;ot%+heoQg_TC1*9v_R6gVzqVS;g zJ;$6+Vk*&eH@4v`!!xoe@dJVE(H#vamo9I7x6XC*CgC#X=sxymEpgpEcFf&;8x&1(AuG^W3)oBxG80GHNz~1{@bfrcQ@jm;?9cRa$*j z!XBkfOzD{|SnjRRq3k<7` z(mR-4om*>zA`IwH1RIcdI74Q8EnRLyPKFa+Q41H0%u89=GizM(%F)_&lp;S2CXu-( zk@mWm$Kwf-@T|89M*eDh&TnGp5#ii;P_p{RtyodU$N>uo-kd_ zSezbmroi3kBy87f1uqM=R&W-}p6I$SPGzZv zSgK2UfVAermO5NwRmL^^LGN$Q>W}FBwFpIOAmtxVo-$fNGHUft#9qvjA`9Y}B0HJCP^ zcmEP)7yyT9(5g!7;n>s3^r7FZh<>fj-j)v-|@pz{miiSsz9 z8eKkzZe2Nv?wQH~x`vIwtwh3i_Oesak<$PGJ>s3_{`0F(`*K-h24xVAo^PWz5Yn?>8(>3~a^`2A88MtG7 z&?nzw6D&Y*1Rf`9pEZ1H)5^q{7n7>=$9ZIKsKV`GwL&wp6M{949%I@U_iBoL4W1|}rp_M=efNPWh;myV5BPZKgiip+UA&Y84<2&|d%^?8h?u{JBln%*#) z9V+Q6i}>U>MJ_R%i=ySVz7H`j9(EbZDf-56%ZILmT{u9wDx}$Nyd_HR7G{)uw`Va7 zF9lg*GU@&_kqlt1*JNSheNY+&ywwce7ZaEy*p&SD$Z?%%{e4E0SLSs{84SA&Sb~T2;`kks9Y4 ze7$H_&~<9&afT;t=ZoMx!fI?2jJ#V|$0_$iK8IUkxt#|ix$(Bdd5p8WwX2kH3Jd4Q zVXy}^5DawK`IGgQsVIYAL9RF%txxl6i*nBI1ipps7ppuC zQtkDUGAHlO#xtD#-Eli{yCO{@iMdRTn3<8N4sr-sBJ-CeSox5f0197o*NoEg}H2V7N=g=fFUQo^xqJ zRTCwWQ&Z$j*vXqYt>Ki@O?1mi2HV1u8S+$-bIn@v!@pAV67$YN;DWzrzD`hNc7j`Y zgC@w?Uz{WOHfc8cHTR#rc_<`$z9oLqb>tj;jh*3^G@(p3OoeQ~0k?(lp^y%>D42|~ z@z~-P$oJqVu^-D;ZaqV}^TLllu|wn|1$fcYV=E>^byYz7H;+ zWGuA|apX+4)~H~AT@NK3Dsq-LU?nIUKz2bxxrY_>z)u5@G#wcvG{*%>Q_azQP0xa4 zQ)b1Cx*f<-%Xs&Y=3McWyYVq!(US9=ni*TwLT4CmxXwdTN@9`j8#O;)vBdDU=5aaO zg%p&$6d{tNNe8#p56%!I-#3ZMBj^Hx{In7J4rl$bDSd`I;`-UV6m_zNprd$@5MnK zw2<02a~lf9u{X`Rc(J7L!YtIkWppm3lH2!!{$3EYzIi~lYS*ac?la130q)_1R$H891pABcqctI%%6pWIe5^DknYIY)6 zphJ#qxcTn|7NsLpUSb88X=a7XC<3%lTdWNNm}VxTsx`;ZMYo+oujPrpS>8tKqszS? zRLl933czEV6pe97m%v-e1{;5rZ3e@|$Sfl{>7nv3yA?a8oyCy|Ff^hLBw)!6W}=Ms z`AFc^mgo_l?Y)%!%A5!S=75Zih+f)fvpTc-qT#z@@!c~^?NpfJLC7=d%SxtMHm~^% zmz3?0=YzN>n891?rRi|we}kB$_G=7ALcs0UB_X)kjNfn-y&JhVJ3{z2DdDv`%J+%i zdX+D}|HChGq?kh*3tyyYfh2?nTmyRc6r#md1bSy8Jhu3|A4SnQI5{ge5UBijsOHfd z!R@yu^y~DIwrYFsE_-_i&g2BiP2&|5t4t$SkT*|ICI(*Z(9fp``wK+29msTE&Yk2u zpbMFSf(H2zgQXfL!W#^ng?@=7(OF;B{ps&@pEaZpM}ZmpQUB8^`ajJAyAn#YKacdV zW=8?Y{UejXP3$xagM*6?^aqY}FlnmP2f2TGsGl|ow=9h;tVsN{h@zAd|Cw0~`@mWpKlaje|IL#&77<2L&-@qZm)b?-NWWl1?_?==O|y>7hb(8;qc%u5{M<{2`{vDuD8znP28NF;|B~1Py-aPT{VWt*C zT5HWufQReKGX17-xe16pca4YVI~qa=qrY3jrD*)NhCd{AU&9}Dr-wk^2I$~8DDEjX z^H@j4c>_zw?}eTZ+xFaT7JMz;a*jLVfqnPa+JUJF{&ij)8%zd_;KB;i!PkT+cD<~z^+yg35I8TFRCG3m&?|y7n zmu}J2tH&*;x&DpE?YS{!rM+3Dv|l%K*|nTE$w8QqSn#obMlz3sf62+E%Wa_dGYaGU zIKyJ&=QRGJ-&ccE@~3*BbJU+D-sVwIn&jh5Z!rnNC?Kh3QTDBUl zit>1zqLSLKAMEKO!AlHln_K0nmoXd`}GXEsk7iyFJAMTN{ZiU zBQBf>>RH$W2@E%;x(5=s2lhSF6hTQw6ygOcuU%?HNuZm-Y8G|=T627$ae);Kd8l~V zV*Okq)yKHg_AqlUlsUa{_AKBgHtXlXihwe?Co#p}nUryi3sF zGyYR{geC_Ti~n!?#80XTnE9>=)P;q_iflL0RpEbW8DKzi_oPs2WP-`);wdI*?g60) z=!@|7Q8w)gD869_p^O9GIw2dtXIC+sB0sm4zXfdE0r=Fs#Iq$|&clYEFL~V*KN|uc z@jViZ0&X(;W=H^lM9qf$gX1*qs+bTru8pqvEJW-#5UfC(ur46*JQM4ZnXm(#>sBwM zt$p`Vpz=6J&f5cKg2jJAjDF49p{#X=v`}A%%#DXoD=EEGDLwL~=ZA`p__(W#JI>*2 z0Gu>Y8WC%Et8h~N(cFQp`xf@A(yMj<%HyMjS3~pbTK67jE4*}fU7OS|V)!URRL(4z zH#l?D)j}?d(nDJ{RY(Ki?hwCE zf7yJ~-^bd%6IIWX~;tJq0q4ZW`A>A8ZR_~yMQH{9U zrFXs4{{?g8o-CWAlx#+@x5`XyoiCp$x-EE6G;ww_UNvc?DdMs;hv_t~&fgyV5NCA! znqL>*80A0)94(k%1kdEP^C~-0Q=DBE06xFSm+T7u19V1^Rz_8gvabtZxZn%AW|= zL+}^|G&m^|-`5A@dJONV%o}*^BpKttzS0PQ4cS>}(vA=t+&+|<-BbqOrUxdqjTbcc z+%564W)Q=5wdWVYM}*Y0vz=Sp^D>?Dyzjaq<)9qxK~JxC%irbc2=U#ZvJsnsv&dYJ zOljjeH7iy2Zoahp>=|uFr*Xn8JPPTy93ML6^V_ZNI^O7Zl&s@;e9&1w2^w5?L0UL~ zXQN)=E*^WB5iy>C^Tf-~t~aTfK{CSxf4cwD#;jX83SV8mfe?xx;L*Zg88eVa;GoF# z(q+{DOCRkCqSpd;se^NIf87AFlJXJX`e_CD9PysVs-0zD$$1nf)vu&5j+z8=K<1Qs z9Tf~@u6@dA{Jg2Yt^5pwz4iFFgE(-7oN^>rU|Mv87iELAb>nRDc|Vs-OS!n1TvjY| zFbz%swDeYpMs8}}3l0ZPMg4&H(7p5=IIlnYEU1)C)*y*n^s4h-cvFo*kXJOxRMg4J ziU`+xFG`wxQ%n9y?H5;@_h5=?*v9z;qR0GPWL*xBIW03zr?2M-VeBY#f(IGh!Objv zF((h*GBq}EJ)ZTdN|=ocna0-Ug&<<5irxR}aqOBaxOpMe>Slvp^Z*fo+ppLXtRTbL zl&_cpcV48>kHIYL!glOCMARO+yqz1QkR(mos2e)UYSE7<=p_H-&oeC z%-OeD7XVk#E|fQ5;eF)+_d6xCC;q=kBmHm=*&?=cDV%vr~zEbb^$%hGv9f^ROaXefc znXn%BUGMbDnGFn>JMDN9%Gmulh%uL|xAg$1O?C6LO=~7*8+fn?mr#4vRdl7eVr3n% zC~T*HGXa(<(IYm3IG8NnCgjC-s#g!%tJhfUF~}Z~trw!Cp|!k55K2qME0ncxxS#T; zZUE_cjlq5qTQz_trnYdjcTJe3I-S3Pxo;}htDsH0hhOly8bDq*iy(7$2KM8=S($PQbmEWrm%_ep8Af2-B_1%eH=I8Jl^# ztBmty=cgCp?aavR=V9)ip!EdPo9}eJwG?!LszMmZh8v_y$GyT<;5`edeQ2eyVY&=1$(%x zi;LArQe_h~H>$&>7niGRgs+!g+^I<^y|`8bDZLoPtE;U1`pO#1U)XCk!KE6bSh7l0 z-n_(M!5805VquIcZ^p5j{L-8E@9He?f!m9#=WONFtgh0Q)TX635X6=*=IU8nS!MP2 z3mxR#{Pi0rvxTWC)tTstF1?w?lIOAdYNl7T^yXdUl*M=R?`kdYL^}OHvvP*~4#@H4 zTlHIerzh$+hDBO~&U?3wORXo`mb-1kx!5eca!Y0qq(zCh11vt#=iXCN4zOqI$T$&B zr?sLSl91Ey?Zmv;WRKl`&iE;^sy%%1?+hpq9U&pxR(p2!27)n>i>o? zly&HXB9ClCIVuUpko7i@d~x)My(qkir>)LjJ>U($>XCSSV+RQ5mKTOh%7Vp@#c3ub@c*7s(zn&Wlps8bxyty z-@OkIhbh5Xd!#iF2hJJ`z1Yj8fxV!26`RrZr8Y^eEU=Tu_2JrOD6E!}$h6!hI~j zuHNSL0-sVj|K4d~U_#V^BSqfDbSD-~eDu}OQc<6yDVW=jILGf0 z1QEw|`#Ih}^D&m*KWKt`gZl8XeOce924rI>INkjV zbvgRK*mQc|t0)0OLZYOEn&RB11&CXk^gdLRcnyZowbSZ*d*2hb_&pxB5p~mGCWM@&*ZzW<7;_aug0K#&bo*ytehwo7TcVDE@!@8D$mnCOA&& z^nYN0DT5c8L_GSB{#g6w{MSV;@JY96jS6fDha<4^%@FVvq6r2G2Wbj6uea-q{FhbF zBjfriR3m4RE7(m+0|_fXy{_U5wUtN`tp#YzcKu2@tUVDrP-{%CrTODEB6Z};xqLHK z-)yn1K%LY5xXPNGZIS#Hk>h|ld`uUNyetBx9ui1?gLwACZz?oT1h%zYA!Cf4e)x@4 zV!lGNQlZH}js5VOWXJk%RVZ8qp-!u~ttO{YC!2ve6~B<^4fJawu~pzELcELSkIt^0)Fkl>+Kxr4|U+#59~NVvAMdVYk!2^RZHaffL(+ ze+q7VaIJ7pntY8G;Vy-W@e}FWX4gJ6BFVlG12=~a?DJY?@bvv!o5kE%UIBEt-x8Hx zI&C7g5GSl?(77fWI+1gXYpwVhheC*1#OOF`q{3@jo6h`Bzca;}{JC18mTTYo z2}XGw`!rB)mPUOQZWD0UB?-F608v2ePn3L;l#E7uK2x}BA2@-M!j5KD@Jwt#+7fMK z#{QvgAJf-iHC_=IU_l3I3p&WY38J-1Pm&<8!UVKO7^NHPqN8BOOHr_PRZzq@mJ6TN zkgolwD|ADEj=DHn6?0stVvgItlDXY6duU_-O1WjU*zAuv2fO}+R9oeOP4VILB7ZIE zGSH|t-1+;`j|xe#p08D+QxDu$yj&|tWuGvTfa*;Yaig4P)*c88W(+-ioG z5^?$hY2xb=VYNndlk--%ma¥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 =TI~@Dg;tOSHN3QQ$C=Fj)>FeP7SY+R-mDP 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

A|D7MF8?5dfhfMNL}clVkE0Ejz|pVEimowYWm8 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 N>kHN3>4@iM%hEQ=tz$ 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∓>y${MKbU+W8vwkxhg{|)`}(%JF={ldN~*OuWyG} zB7+C0YN!svHaT=T!l9c0`RA6D4&GE1RSFS z+sR8#$4MwDzsGPQ4sa=wg4Z>Vaw?jIdw;QtcnV)X$=<;*d!GFu$xfe|p6)c1jvw=y z{}d#roNp4~FP0k6O{OsmuPNu2F8IxPA0QJj@d$RHFiGB7gm2HNFqRI<%&zrb2KS!) znOuKy&KA1#eL07rN+2>CQd9gHy$GoGM@~VuLAUs^DQ_er**Gm-%$j%G8~mL5wEJWc z*NSm%Xzo(MkjaG#JJuc2r+N&gSoJrn4VuO_>s5a*Q8@j-u3vZkb>~!$#N}VOE?1N! zA%oo3|KyOx>}f~=Af#XEePUfKO69*$bnL_C7utjV{!VAz{dKG-d0rV^PwvsD<8+*k P|MqwV2kaA=0H{#_5MrVk literal 0 HcmV?d00001 diff --git a/tests/registry/npm/npm-check-updates/registry.json b/tests/registry/npm/npm-check-updates/registry.json new file mode 100644 index 0000000000..71d96fd83d --- /dev/null +++ b/tests/registry/npm/npm-check-updates/registry.json @@ -0,0 +1,201 @@ +{ + "name": "npm-check-updates", + "dist-tags": { + "latest": "17.1.13" + }, + "versions": { + "17.1.13": { + "name": "npm-check-updates", + "version": "17.1.13", + "author": { + "name": "Tomas Junnonen", + "email": "tomas1@gmail.com" + }, + "license": "Apache-2.0", + "description": "Find newer versions of dependencies than what your package.json allows", + "engines": { + "node": "^18.18.0 || >=20.0.0", + "npm": ">=8.12.1" + }, + "main": "build/index.js", + "types": "build/index.d.ts", + "scripts": { + "build": "rimraf build && npm run build:options && vite build", + "build:options": "vite-node src/scripts/build-options.ts", + "build:analyze": "rimraf build && npm run build:options && ANALYZER=true vite build", + "lint": "cross-env FORCE_COLOR=1 npm-run-all --parallel --aggregate-output lint:*", + "lint:lockfile": "lockfile-lint", + "lint:markdown": "markdownlint \"**/*.md\" --ignore \"**/node_modules/**/*.md\" --ignore build --config .markdownlint.js", + "lint:src": "eslint --cache --cache-location node_modules/.cache/.eslintcache --ignore-path .gitignore --report-unused-disable-directives .", + "prepare": "src/scripts/install-hooks", + "prepublishOnly": "npm run build", + "prettier": "prettier . --check", + "test": "npm run test:unit && npm run test:e2e", + "test:bun": "test/bun-install.sh && mocha test/bun", + "test:unit": "mocha test test/package-managers/*", + "test:e2e": "./test/e2e.sh", + "ncu": "node build/cli.js" + }, + "bin": { + "npm-check-updates": "build/cli.js", + "ncu": "build/cli.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/raineorshine/npm-check-updates.git" + }, + "bugs": { + "url": "https://github.com/raineorshine/npm-check-updates/issues" + }, + "overrides": { + "ip": "2.0.1", + "jsonparse": "https://github.com/ARitz-Cracker/jsonparse/tree/patch-1", + "@yarnpkg/parsers": "2.6.0" + }, + "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@types/chai": "^4.3.19", + "@types/chai-as-promised": "^8.0.0", + "@types/chai-string": "^1.4.5", + "@types/cli-table": "^0.3.4", + "@types/hosted-git-info": "^3.0.5", + "@types/ini": "^4.1.1", + "@types/js-yaml": "^4.0.9", + "@types/json-parse-helpfulerror": "^1.0.3", + "@types/jsonlines": "^0.1.5", + "@types/lodash": "^4.17.10", + "@types/mocha": "^10.0.9", + "@types/node": "^22.7.5", + "@types/npm-registry-fetch": "^8.0.7", + "@types/parse-github-url": "^1.0.3", + "@types/picomatch": "^3.0.1", + "@types/progress": "^2.0.7", + "@types/prompts": "^2.4.9", + "@types/remote-git-tags": "^4.0.2", + "@types/semver": "^7.5.8", + "@types/semver-utils": "^1.1.3", + "@types/sinon": "^17.0.3", + "@types/update-notifier": "^6.0.8", + "@typescript-eslint/eslint-plugin": "^8.9.0", + "@typescript-eslint/parser": "^8.9.0", + "camelcase": "^6.3.0", + "chai": "^4.3.10", + "chai-as-promised": "^7.1.2", + "chai-string": "^1.5.0", + "chalk": "^5.3.0", + "cli-table3": "^0.6.5", + "commander": "^12.1.0", + "cross-env": "^7.0.3", + "dequal": "^2.0.3", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-config-raine": "^0.5.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.4.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.6.0", + "fast-glob": "^3.3.2", + "fast-memoize": "^2.5.2", + "find-up": "5.0.0", + "fp-and-or": "^1.0.2", + "hosted-git-info": "^8.0.0", + "ini": "^5.0.0", + "js-yaml": "^4.1.0", + "json-parse-helpfulerror": "^1.0.3", + "jsonlines": "^0.1.1", + "lockfile-lint": "^4.14.0", + "lodash": "^4.17.21", + "markdownlint-cli": "^0.42.0", + "mocha": "^10.7.3", + "npm-registry-fetch": "^18.0.2", + "npm-run-all": "^4.1.5", + "p-map": "^4.0.0", + "parse-github-url": "^1.0.3", + "picomatch": "^4.0.2", + "prettier": "^3.3.3", + "progress": "^2.0.3", + "prompts-ncu": "^3.0.2", + "rc-config-loader": "^4.1.3", + "remote-git-tags": "^3.0.0", + "rfdc": "^1.4.1", + "rimraf": "^6.0.1", + "rollup-plugin-node-externals": "^7.1.3", + "semver": "^7.6.3", + "semver-utils": "^1.1.4", + "should": "^13.2.3", + "sinon": "^19.0.2", + "source-map-support": "^0.5.21", + "spawn-please": "^3.0.0", + "strip-ansi": "^7.1.0", + "strip-json-comments": "^5.0.1", + "ts-node": "^10.9.2", + "typescript": "^5.6.3", + "typescript-json-schema": "^0.65.1", + "untildify": "^4.0.0", + "update-notifier": "^7.3.1", + "verdaccio": "^6.0.1", + "vite": "^5.4.9", + "vite-bundle-analyzer": "^0.12.1", + "vite-node": "^2.1.3", + "vite-plugin-dts": "^4.2.4", + "yarn": "^1.22.22" + }, + "lockfile-lint": { + "allowed-schemes": [ + "https:", + "git+ssh:" + ], + "allowed-hosts": [ + "npm", + "github.com" + ], + "empty-hostname": false, + "type": "npm ", + "path": "package-lock.json" + }, + "mocha": { + "check-leaks": true, + "extension": [ + "test.ts" + ], + "require": [ + "source-map-support/register", + "ts-node/register" + ], + "timeout": 60000, + "trace-deprecation": true, + "trace-warnings": true, + "use_strict": true + }, + "_id": "npm-check-updates@17.1.13", + "gitHead": "f514093647f1833c83653765493c694372d14fea", + "_nodeVersion": "22.0.0", + "_npmVersion": "10.9.1", + "dist": { + "integrity": "sha512-m9Woo2J5XVab0VcQpYvrQ0hx3ySI1mGbiHR595mc6Lr1/FIaTWvv+oU+T1WKSfXRiluKC/V5P6Bdk5agaYpqqg==", + "shasum": "93e1c5fa5b8e11bca0bd143650b14ffcf9fc6b5a", + "tarball": "http://localhost:4260/npm-check-updates/npm-check-updates-17.1.13.tgz", + "fileCount": 19, + "unpackedSize": 5336239 + }, + "directories": {}, + "_hasShrinkwrap": false + } + }, + "bugs": { + "url": "https://github.com/raineorshine/npm-check-updates/issues" + }, + "author": { + "name": "Tomas Junnonen", + "email": "tomas1@gmail.com" + }, + "license": "Apache-2.0", + "homepage": "https://github.com/raineorshine/npm-check-updates", + "repository": { + "type": "git", + "url": "git+https://github.com/raineorshine/npm-check-updates.git" + }, + "description": "Find newer versions of dependencies than what your package.json allows", + "readmeFilename": "README.md" +} diff --git a/tests/specs/npm/npm_check_updates/__test__.jsonc b/tests/specs/npm/npm_check_updates/__test__.jsonc new file mode 100644 index 0000000000..27b84c5f05 --- /dev/null +++ b/tests/specs/npm/npm_check_updates/__test__.jsonc @@ -0,0 +1,7 @@ +{ + "tempDir": true, + "steps": [{ + "args": "run -A npm:npm-check-updates", + "output": "output.out" + }] +} diff --git a/tests/specs/npm/npm_check_updates/output.out b/tests/specs/npm/npm_check_updates/output.out new file mode 100644 index 0000000000..b8c1ae4957 --- /dev/null +++ b/tests/specs/npm/npm_check_updates/output.out @@ -0,0 +1,2 @@ +[WILDCARD] +All dependencies match the latest package versions[WILDCARD] \ No newline at end of file diff --git a/tests/specs/npm/npm_check_updates/package.json b/tests/specs/npm/npm_check_updates/package.json new file mode 100644 index 0000000000..82afc391cd --- /dev/null +++ b/tests/specs/npm/npm_check_updates/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "chalk": "^5.0.1" + } +} From f912aac2cb94d1770219bc2c25471e0a23bb2d64 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Mon, 13 Jan 2025 15:31:08 +0000 Subject: [PATCH 095/107] fix(lsp): handle pathless untitled URIs (#27637) --- cli/lsp/language_server.rs | 8 +++---- cli/lsp/urls.rs | 23 +++++++++---------- tests/integration/lsp_tests.rs | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 35f5374efe..03f63e5f25 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1419,18 +1419,16 @@ impl Inner { // the file path is only used to determine what formatter should // be used to format the file, so give the filepath an extension // that matches what the user selected as the language - let file_path = document + let ext = document .maybe_language_id() - .and_then(|id| id.as_extension()) - .map(|ext| file_path.with_extension(ext)) - .unwrap_or(file_path); + .and_then(|id| id.as_extension().map(|s| s.to_string())); // it's not a js/ts file, so attempt to format its contents format_file( &file_path, document.content(), &fmt_options, &unstable_options, - None, + ext, ) } }; diff --git a/cli/lsp/urls.rs b/cli/lsp/urls.rs index 91c04f11c0..2aadaf5352 100644 --- a/cli/lsp/urls.rs +++ b/cli/lsp/urls.rs @@ -282,24 +282,26 @@ impl LspUrlMap { } } -/// Convert a e.g. `deno-notebook-cell:` specifier to a `file:` specifier. +/// Convert a e.g. `vscode-notebook-cell:` specifier to a `file:` specifier. /// ```rust /// assert_eq!( /// file_like_to_file_specifier( -/// &Url::parse("deno-notebook-cell:/path/to/file.ipynb#abc").unwrap(), +/// &Url::parse("vscode-notebook-cell:/path/to/file.ipynb#abc").unwrap(), /// ), -/// Some(Url::parse("file:///path/to/file.ipynb.ts?scheme=deno-notebook-cell#abc").unwrap()), +/// Some(Url::parse("file:///path/to/file.ipynb?scheme=untitled#abc").unwrap()), /// ); fn file_like_to_file_specifier(specifier: &Url) -> Option { - if matches!(specifier.scheme(), "untitled" | "deno-notebook-cell") { + if matches!( + specifier.scheme(), + "untitled" | "vscode-notebook-cell" | "deno-notebook-cell" + ) { if let Ok(mut s) = ModuleSpecifier::parse(&format!( - "file://{}", + "file:///{}", &specifier.as_str()[deno_core::url::quirks::internal_components(specifier) - .host_end as usize..], + .host_end as usize..].trim_start_matches('/'), )) { s.query_pairs_mut() .append_pair("scheme", specifier.scheme()); - s.set_path(&format!("{}.ts", s.path())); return Some(s); } } @@ -432,11 +434,11 @@ mod tests { fn test_file_like_to_file_specifier() { assert_eq!( file_like_to_file_specifier( - &Url::parse("deno-notebook-cell:/path/to/file.ipynb#abc").unwrap(), + &Url::parse("vscode-notebook-cell:/path/to/file.ipynb#abc").unwrap(), ), Some( Url::parse( - "file:///path/to/file.ipynb.ts?scheme=deno-notebook-cell#abc" + "file:///path/to/file.ipynb?scheme=vscode-notebook-cell#abc" ) .unwrap() ), @@ -446,8 +448,7 @@ mod tests { &Url::parse("untitled:/path/to/file.ipynb#123").unwrap(), ), Some( - Url::parse("file:///path/to/file.ipynb.ts?scheme=untitled#123") - .unwrap() + Url::parse("file:///path/to/file.ipynb?scheme=untitled#123").unwrap() ), ); } diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index a25710b2b1..bbeb6e7c13 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -11690,6 +11690,46 @@ fn lsp_format_exclude_default_config() { client.shutdown(); } +#[test] +fn lsp_format_untitled() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": "untitled:Untitled-1", + "languageId": "typescript", + "version": 1, + "text": " console.log();\n", + }, + })); + let res = client.write_request( + "textDocument/formatting", + json!({ + "textDocument": { + "uri": "untitled:Untitled-1", + }, + "options": { + "tabSize": 2, + "insertSpaces": true, + }, + }), + ); + assert_eq!( + res, + json!([ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 2 }, + }, + "newText": "", + }, + ]) + ); + client.shutdown(); +} + #[test] fn lsp_format_json() { let context = TestContextBuilder::new().use_temp_cwd().build(); From 714b40262e0f9bc7e1a98e76f6e211012b161706 Mon Sep 17 00:00:00 2001 From: Benjamin Swerdlow Date: Mon, 13 Jan 2025 08:34:37 -0800 Subject: [PATCH 096/107] refactor(node_resolver): make conditions_from_resolution_mode configurable (#27596) --- cli/factory.rs | 1 + cli/lsp/resolver.rs | 1 + cli/standalone/mod.rs | 1 + resolvers/node/lib.rs | 2 + resolvers/node/resolution.rs | 75 +++++++++++++++++++++++++++++++----- 5 files changed, 70 insertions(+), 10 deletions(-) diff --git a/cli/factory.rs b/cli/factory.rs index 5cc99830bc..25a39cae8b 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -670,6 +670,7 @@ impl CliFactory { .into_npm_pkg_folder_resolver(), self.pkg_json_resolver().clone(), self.sys(), + node_resolver::ConditionsFromResolutionMode::default(), ))) } .boxed_local(), diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 57ef2e6a3c..d7f7e684a9 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -783,6 +783,7 @@ impl<'a> ResolverFactory<'a> { npm_resolver.clone().into_npm_pkg_folder_resolver(), self.pkg_json_resolver.clone(), self.sys.clone(), + node_resolver::ConditionsFromResolutionMode::default(), ))) }) .as_ref() diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 961e8d6b51..ff38912a89 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -833,6 +833,7 @@ pub async fn run( npm_resolver.clone().into_npm_pkg_folder_resolver(), pkg_json_resolver.clone(), sys.clone(), + node_resolver::ConditionsFromResolutionMode::default(), )); let cjs_tracker = Arc::new(CjsTracker::new( in_npm_pkg_checker.clone(), diff --git a/resolvers/node/lib.rs b/resolvers/node/lib.rs index 7d8c98eafc..faee4f1645 100644 --- a/resolvers/node/lib.rs +++ b/resolvers/node/lib.rs @@ -9,6 +9,7 @@ mod npm; mod package_json; mod path; mod resolution; + mod sync; pub use deno_package_json::PackageJson; @@ -22,6 +23,7 @@ pub use package_json::PackageJsonThreadLocalCache; pub use path::PathClean; pub use resolution::parse_npm_pkg_name; pub use resolution::resolve_specifier_into_node_modules; +pub use resolution::ConditionsFromResolutionMode; pub use resolution::IsBuiltInNodeModuleChecker; pub use resolution::NodeResolution; pub use resolution::NodeResolutionKind; diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs index d0b52213f3..a60a6bec9e 100644 --- a/resolvers/node/resolution.rs +++ b/resolvers/node/resolution.rs @@ -1,6 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; +use std::fmt::Debug; use std::path::Path; use std::path::PathBuf; @@ -54,12 +55,32 @@ pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"]; pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"]; static TYPES_ONLY_CONDITIONS: &[&str] = &["types"]; -fn conditions_from_resolution_mode( - resolution_mode: ResolutionMode, -) -> &'static [&'static str] { - match resolution_mode { - ResolutionMode::Import => DEFAULT_CONDITIONS, - ResolutionMode::Require => REQUIRE_CONDITIONS, +type ConditionsFromResolutionModeFn = Box< + dyn Fn(ResolutionMode) -> &'static [&'static str] + Send + Sync + 'static, +>; + +#[derive(Default)] +pub struct ConditionsFromResolutionMode(Option); + +impl Debug for ConditionsFromResolutionMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ConditionsFromResolutionMode").finish() + } +} + +impl ConditionsFromResolutionMode { + pub fn new(func: ConditionsFromResolutionModeFn) -> Self { + Self(Some(func)) + } + + fn resolve( + &self, + resolution_mode: ResolutionMode, + ) -> &'static [&'static str] { + match &self.0 { + Some(func) => func(ResolutionMode::Import), + None => resolution_mode.default_conditions(), + } } } @@ -69,6 +90,15 @@ pub enum ResolutionMode { Require, } +impl ResolutionMode { + pub fn default_conditions(&self) -> &'static [&'static str] { + match self { + ResolutionMode::Import => DEFAULT_CONDITIONS, + ResolutionMode::Require => REQUIRE_CONDITIONS, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum NodeResolutionKind { Execution, @@ -120,6 +150,7 @@ pub struct NodeResolver< npm_pkg_folder_resolver: NpmPackageFolderResolverRc, pkg_json_resolver: PackageJsonResolverRc, sys: TSys, + conditions_from_resolution_mode: ConditionsFromResolutionMode, } impl< @@ -133,6 +164,7 @@ impl< npm_pkg_folder_resolver: NpmPackageFolderResolverRc, pkg_json_resolver: PackageJsonResolverRc, sys: TSys, + conditions_from_resolution_mode: ConditionsFromResolutionMode, ) -> Self { Self { in_npm_pkg_checker, @@ -140,6 +172,7 @@ impl< npm_pkg_folder_resolver, pkg_json_resolver, sys, + conditions_from_resolution_mode, } } @@ -197,11 +230,14 @@ impl< } } + let conditions = self + .conditions_from_resolution_mode + .resolve(resolution_mode); let url = self.module_resolve( specifier, referrer, resolution_mode, - conditions_from_resolution_mode(resolution_mode), + conditions, resolution_kind, )?; @@ -211,6 +247,7 @@ impl< &file_path, Some(referrer), resolution_mode, + conditions, )? } else { url @@ -343,7 +380,9 @@ impl< &package_subpath, maybe_referrer, resolution_mode, - conditions_from_resolution_mode(resolution_mode), + self + .conditions_from_resolution_mode + .resolve(resolution_mode), resolution_kind, )?; // TODO(bartlomieju): skipped checking errors for commonJS resolution and @@ -411,6 +450,7 @@ impl< path: &Path, maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], ) -> Result { fn probe_extensions( sys: &TSys, @@ -474,7 +514,7 @@ impl< /* sub path */ ".", maybe_referrer, resolution_mode, - conditions_from_resolution_mode(resolution_mode), + conditions, NodeResolutionKind::Types, ); if let Ok(resolution) = resolution_result { @@ -855,6 +895,7 @@ impl< &path, maybe_referrer, resolution_mode, + conditions, )?)); } else { return Ok(Some(url)); @@ -1212,6 +1253,7 @@ impl< package_subpath, maybe_referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1249,6 +1291,7 @@ impl< package_json, referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1268,6 +1311,7 @@ impl< package_json, referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1281,6 +1325,7 @@ impl< package_subpath, referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1294,12 +1339,18 @@ impl< package_subpath: &str, referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], resolution_kind: NodeResolutionKind, ) -> Result { assert_ne!(package_subpath, "."); let file_path = directory.join(package_subpath); if resolution_kind.is_types() { - Ok(self.path_to_declaration_url(&file_path, referrer, resolution_mode)?) + Ok(self.path_to_declaration_url( + &file_path, + referrer, + resolution_mode, + conditions, + )?) } else { Ok(url_from_file_path(&file_path).unwrap()) } @@ -1311,6 +1362,7 @@ impl< package_subpath: &str, maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], resolution_kind: NodeResolutionKind, ) -> Result { if package_subpath == "." { @@ -1327,6 +1379,7 @@ impl< package_subpath, maybe_referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| err.into()) @@ -1338,6 +1391,7 @@ impl< package_json: &PackageJson, maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], resolution_kind: NodeResolutionKind, ) -> Result { let pkg_json_kind = match resolution_mode { @@ -1356,6 +1410,7 @@ impl< &main, maybe_referrer, resolution_mode, + conditions, ); // don't surface errors, fallback to checking the index now if let Ok(url) = decl_url_result { From 2a2b39eb2ecdc85c87fba594a51d7b3ed6d837fe Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 13 Jan 2025 12:02:37 -0500 Subject: [PATCH 097/107] fix(compile): store embedded fs case sensitivity (#27653) --- cli/standalone/binary.rs | 23 +++- cli/standalone/serialization.rs | 1 + cli/standalone/virtual_fs.rs | 128 ++++++++++++++---- .../case_insensitive_building/__test__.jsonc | 24 ++++ .../case_insensitive_building/compile.out | 10 ++ .../case_insensitive_building/file.txt | 1 + .../compile/case_insensitive_building/main.js | 1 + .../case_insensitive_building/main.out | 1 + 8 files changed, 161 insertions(+), 28 deletions(-) create mode 100644 tests/specs/compile/case_insensitive_building/__test__.jsonc create mode 100644 tests/specs/compile/case_insensitive_building/compile.out create mode 100644 tests/specs/compile/case_insensitive_building/file.txt create mode 100644 tests/specs/compile/case_insensitive_building/main.js create mode 100644 tests/specs/compile/case_insensitive_building/main.out diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index f07c645d89..296d6825a7 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -73,6 +73,7 @@ use super::serialization::SourceMapStore; use super::virtual_fs::output_vfs; use super::virtual_fs::BuiltVfs; use super::virtual_fs::FileBackedVfs; +use super::virtual_fs::FileSystemCaseSensitivity; use super::virtual_fs::VfsBuilder; use super::virtual_fs::VfsFileSubDataKind; use super::virtual_fs::VfsRoot; @@ -202,6 +203,7 @@ pub struct Metadata { pub node_modules: Option, pub unstable_config: UnstableConfig, pub otel_config: OtelConfig, + pub vfs_case_sensitivity: FileSystemCaseSensitivity, } #[allow(clippy::too_many_arguments)] @@ -379,7 +381,11 @@ pub fn extract_standalone( root_path: root_path.clone(), start_file_offset: 0, }; - Arc::new(FileBackedVfs::new(Cow::Borrowed(vfs_files_data), fs_root)) + Arc::new(FileBackedVfs::new( + Cow::Borrowed(vfs_files_data), + fs_root, + metadata.vfs_case_sensitivity, + )) }; Ok(Some(StandaloneData { metadata, @@ -851,6 +857,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { npm_lazy_caching: self.cli_options.unstable_npm_lazy_caching(), }, otel_config: self.cli_options.otel_config(), + vfs_case_sensitivity: vfs.case_sensitivity, }; write_binary_bytes( @@ -984,11 +991,15 @@ impl<'a> DenoCompileBinaryWriter<'a> { // it's better to not expose the user's cache directory, so take it out // of there + let case_sensitivity = vfs.case_sensitivity(); let parent = global_cache_root_path.parent().unwrap(); let parent_dir = vfs.get_dir_mut(parent).unwrap(); let index = parent_dir .entries - .binary_search(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME) + .binary_search( + DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME, + case_sensitivity, + ) .unwrap(); let npm_global_cache_dir_entry = parent_dir.entries.remove(index); @@ -998,7 +1009,9 @@ impl<'a> DenoCompileBinaryWriter<'a> { Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME); for ancestor in parent.ancestors() { let dir = vfs.get_dir_mut(ancestor).unwrap(); - if let Ok(index) = dir.entries.binary_search(&last_name) { + if let Ok(index) = + dir.entries.binary_search(&last_name, case_sensitivity) + { dir.entries.remove(index); } last_name = Cow::Owned(dir.name.clone()); @@ -1009,7 +1022,9 @@ impl<'a> DenoCompileBinaryWriter<'a> { // now build the vfs and add the global cache dir entry there let mut built_vfs = vfs.build(); - built_vfs.entries.insert(npm_global_cache_dir_entry); + built_vfs + .entries + .insert(npm_global_cache_dir_entry, case_sensitivity); built_vfs } InnerCliNpmResolverRef::Byonm(_) => vfs.build(), diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index 238ef44fd0..ae2e411b5f 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -27,6 +27,7 @@ use indexmap::IndexMap; use super::binary::Metadata; use super::virtual_fs::BuiltVfs; +use super::virtual_fs::FileSystemCaseSensitivity; use super::virtual_fs::VfsBuilder; use super::virtual_fs::VirtualDirectoryEntries; use crate::standalone::virtual_fs::VirtualDirectory; diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index e167b153a7..0b1f33259d 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::cell::RefCell; +use std::cmp::Ordering; use std::collections::HashMap; use std::collections::HashSet; use std::fs::File; @@ -74,6 +75,7 @@ impl WindowsSystemRootablePath { #[derive(Debug)] pub struct BuiltVfs { pub root_path: WindowsSystemRootablePath, + pub case_sensitivity: FileSystemCaseSensitivity, pub entries: VirtualDirectoryEntries, pub files: Vec>, } @@ -95,6 +97,7 @@ pub struct VfsBuilder { file_offsets: HashMap, /// The minimum root directory that should be included in the VFS. min_root_dir: Option, + case_sensitivity: FileSystemCaseSensitivity, } impl VfsBuilder { @@ -108,9 +111,23 @@ impl VfsBuilder { current_offset: 0, file_offsets: Default::default(), min_root_dir: Default::default(), + // This is not exactly correct because file systems on these OSes + // may be case-sensitive or not based on the directory, but this + // is a good enough approximation and limitation. In the future, + // we may want to store this information per directory instead + // depending on the feedback we get. + case_sensitivity: if cfg!(windows) || cfg!(target_os = "macos") { + FileSystemCaseSensitivity::Insensitive + } else { + FileSystemCaseSensitivity::Sensitive + }, } } + pub fn case_sensitivity(&self) -> FileSystemCaseSensitivity { + self.case_sensitivity + } + /// Add a directory that might be the minimum root directory /// of the VFS. /// @@ -215,7 +232,10 @@ impl VfsBuilder { continue; } let name = component.as_os_str().to_string_lossy(); - let index = match current_dir.entries.binary_search(&name) { + let index = match current_dir + .entries + .binary_search(&name, self.case_sensitivity) + { Ok(index) => index, Err(insert_index) => { current_dir.entries.0.insert( @@ -252,7 +272,9 @@ impl VfsBuilder { continue; } let name = component.as_os_str().to_string_lossy(); - let entry = current_dir.entries.get_mut_by_name(&name)?; + let entry = current_dir + .entries + .get_mut_by_name(&name, self.case_sensitivity)?; match entry { VfsEntry::Dir(dir) => { current_dir = dir; @@ -304,6 +326,7 @@ impl VfsBuilder { ) -> Result<(), AnyError> { log::debug!("Adding file '{}'", path.display()); let checksum = util::checksum::gen(&[&data]); + let case_sensitivity = self.case_sensitivity; let offset = if let Some(offset) = self.file_offsets.get(&checksum) { // duplicate file, reuse an old offset *offset @@ -318,7 +341,7 @@ impl VfsBuilder { offset, len: data.len() as u64, }; - match dir.entries.binary_search(&name) { + match dir.entries.binary_search(&name, case_sensitivity) { Ok(index) => { let entry = &mut dir.entries.0[index]; match entry { @@ -379,10 +402,11 @@ impl VfsBuilder { std::fs::read_link(path) .with_context(|| format!("Reading symlink '{}'", path.display()))?, ); + let case_sensitivity = self.case_sensitivity; let target = normalize_path(path.parent().unwrap().join(&target)); let dir = self.add_dir_raw(path.parent().unwrap()); let name = path.file_name().unwrap().to_string_lossy(); - match dir.entries.binary_search(&name) { + match dir.entries.binary_search(&name, case_sensitivity) { Ok(_) => {} // previously inserted Err(insert_index) => { dir.entries.0.insert( @@ -478,6 +502,7 @@ impl VfsBuilder { } BuiltVfs { root_path: current_path, + case_sensitivity: self.case_sensitivity, entries: current_dir.entries, files: self.files, } @@ -906,6 +931,14 @@ impl VfsEntry { } } +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum FileSystemCaseSensitivity { + #[serde(rename = "s")] + Sensitive, + #[serde(rename = "i")] + Insensitive, +} + #[derive(Debug, Default, Serialize, Deserialize)] pub struct VirtualDirectoryEntries(Vec); @@ -928,23 +961,54 @@ impl VirtualDirectoryEntries { self.0.len() } - pub fn get_by_name(&self, name: &str) -> Option<&VfsEntry> { - self.binary_search(name).ok().map(|index| &self.0[index]) + pub fn get_by_name( + &self, + name: &str, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Option<&VfsEntry> { + self + .binary_search(name, case_sensitivity) + .ok() + .map(|index| &self.0[index]) } - pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut VfsEntry> { + pub fn get_mut_by_name( + &mut self, + name: &str, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Option<&mut VfsEntry> { self - .binary_search(name) + .binary_search(name, case_sensitivity) .ok() .map(|index| &mut self.0[index]) } - pub fn binary_search(&self, name: &str) -> Result { - self.0.binary_search_by(|e| e.name().cmp(name)) + pub fn binary_search( + &self, + name: &str, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Result { + match case_sensitivity { + FileSystemCaseSensitivity::Sensitive => { + self.0.binary_search_by(|e| e.name().cmp(name)) + } + FileSystemCaseSensitivity::Insensitive => self.0.binary_search_by(|e| { + e.name() + .chars() + .zip(name.chars()) + .map(|(a, b)| a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase())) + .find(|&ord| ord != Ordering::Equal) + .unwrap_or_else(|| e.name().len().cmp(&name.len())) + }), + } } - pub fn insert(&mut self, entry: VfsEntry) { - match self.binary_search(entry.name()) { + pub fn insert( + &mut self, + entry: VfsEntry, + case_sensitivity: FileSystemCaseSensitivity, + ) { + match self.binary_search(entry.name(), case_sensitivity) { Ok(index) => { self.0[index] = entry; } @@ -1039,19 +1103,21 @@ impl VfsRoot { fn find_entry<'a>( &'a self, path: &Path, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> { - self.find_entry_inner(path, &mut HashSet::new()) + self.find_entry_inner(path, &mut HashSet::new(), case_sensitivity) } fn find_entry_inner<'a>( &'a self, path: &Path, seen: &mut HashSet, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> { let mut path = Cow::Borrowed(path); loop { let (resolved_path, entry) = - self.find_entry_no_follow_inner(&path, seen)?; + self.find_entry_no_follow_inner(&path, seen, case_sensitivity)?; match entry { VfsEntryRef::Symlink(symlink) => { if !seen.insert(path.to_path_buf()) { @@ -1072,14 +1138,16 @@ impl VfsRoot { fn find_entry_no_follow( &self, path: &Path, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef)> { - self.find_entry_no_follow_inner(path, &mut HashSet::new()) + self.find_entry_no_follow_inner(path, &mut HashSet::new(), case_sensitivity) } fn find_entry_no_follow_inner<'a>( &'a self, path: &Path, seen: &mut HashSet, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> { let relative_path = match path.strip_prefix(&self.root_path) { Ok(p) => p, @@ -1101,7 +1169,8 @@ impl VfsRoot { } VfsEntryRef::Symlink(symlink) => { let dest = symlink.resolve_dest_from_root(&self.root_path); - let (resolved_path, entry) = self.find_entry_inner(&dest, seen)?; + let (resolved_path, entry) = + self.find_entry_inner(&dest, seen, case_sensitivity)?; final_path = resolved_path; // overwrite with the new resolved path match entry { VfsEntryRef::Dir(dir) => { @@ -1126,7 +1195,7 @@ impl VfsRoot { let component = component.to_string_lossy(); current_entry = current_dir .entries - .get_by_name(&component) + .get_by_name(&component, case_sensitivity) .ok_or_else(|| { std::io::Error::new(std::io::ErrorKind::NotFound, "path not found") })? @@ -1392,13 +1461,19 @@ impl FileBackedVfsMetadata { pub struct FileBackedVfs { vfs_data: Cow<'static, [u8]>, fs_root: VfsRoot, + case_sensitivity: FileSystemCaseSensitivity, } impl FileBackedVfs { - pub fn new(data: Cow<'static, [u8]>, fs_root: VfsRoot) -> Self { + pub fn new( + data: Cow<'static, [u8]>, + fs_root: VfsRoot, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Self { Self { vfs_data: data, fs_root, + case_sensitivity, } } @@ -1451,7 +1526,9 @@ impl FileBackedVfs { } pub fn read_link(&self, path: &Path) -> std::io::Result { - let (_, entry) = self.fs_root.find_entry_no_follow(path)?; + let (_, entry) = self + .fs_root + .find_entry_no_follow(path, self.case_sensitivity)?; match entry { VfsEntryRef::Symlink(symlink) => { Ok(symlink.resolve_dest_from_root(&self.fs_root.root_path)) @@ -1464,17 +1541,19 @@ impl FileBackedVfs { } pub fn lstat(&self, path: &Path) -> std::io::Result { - let (_, entry) = self.fs_root.find_entry_no_follow(path)?; + let (_, entry) = self + .fs_root + .find_entry_no_follow(path, self.case_sensitivity)?; Ok(entry.as_metadata()) } pub fn stat(&self, path: &Path) -> std::io::Result { - let (_, entry) = self.fs_root.find_entry(path)?; + let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?; Ok(entry.as_metadata()) } pub fn canonicalize(&self, path: &Path) -> std::io::Result { - let (path, _) = self.fs_root.find_entry(path)?; + let (path, _) = self.fs_root.find_entry(path, self.case_sensitivity)?; Ok(path) } @@ -1536,7 +1615,7 @@ impl FileBackedVfs { } pub fn dir_entry(&self, path: &Path) -> std::io::Result<&VirtualDirectory> { - let (_, entry) = self.fs_root.find_entry(path)?; + let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?; match entry { VfsEntryRef::Dir(dir) => Ok(dir), VfsEntryRef::Symlink(_) => unreachable!(), @@ -1548,7 +1627,7 @@ impl FileBackedVfs { } pub fn file_entry(&self, path: &Path) -> std::io::Result<&VirtualFile> { - let (_, entry) = self.fs_root.find_entry(path)?; + let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?; match entry { VfsEntryRef::Dir(_) => Err(std::io::Error::new( std::io::ErrorKind::Other, @@ -1754,6 +1833,7 @@ mod test { root_path: dest_path.to_path_buf(), start_file_offset: 0, }, + FileSystemCaseSensitivity::Sensitive, ), ) } diff --git a/tests/specs/compile/case_insensitive_building/__test__.jsonc b/tests/specs/compile/case_insensitive_building/__test__.jsonc new file mode 100644 index 0000000000..38636dc273 --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/__test__.jsonc @@ -0,0 +1,24 @@ +{ + "tempDir": true, + "steps": [{ + "if": "mac", + "args": "compile --output main --include file.txt --include FILE.txt main.js", + "output": "compile.out" + }, { + "if": "mac", + "commandName": "./main", + "args": [], + "output": "main.out", + "exitCode": 0 + }, { + "if": "windows", + "args": "compile --output main.exe --include file.txt --include FILE.txt main.js", + "output": "compile.out" + }, { + "if": "windows", + "commandName": "./main.exe", + "args": [], + "output": "main.out", + "exitCode": 0 + }] +} diff --git a/tests/specs/compile/case_insensitive_building/compile.out b/tests/specs/compile/case_insensitive_building/compile.out new file mode 100644 index 0000000000..895c2f1d46 --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/compile.out @@ -0,0 +1,10 @@ +Compile file:///[WILDLINE]/main.js to main[WILDLINE] + +Embedded Files + +main[WILDLINE] +├── file.txt ([WILDLINE]) +└── main.js ([WILDLINE]) + +Size: [WILDLINE] + diff --git a/tests/specs/compile/case_insensitive_building/file.txt b/tests/specs/compile/case_insensitive_building/file.txt new file mode 100644 index 0000000000..40816a2b5a --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/file.txt @@ -0,0 +1 @@ +Hi \ No newline at end of file diff --git a/tests/specs/compile/case_insensitive_building/main.js b/tests/specs/compile/case_insensitive_building/main.js new file mode 100644 index 0000000000..0fafe8a4dd --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/main.js @@ -0,0 +1 @@ +console.log(Deno.readTextFileSync(import.meta.dirname + "/file.txt")); diff --git a/tests/specs/compile/case_insensitive_building/main.out b/tests/specs/compile/case_insensitive_building/main.out new file mode 100644 index 0000000000..b14df6442e --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/main.out @@ -0,0 +1 @@ +Hi From 5a39f2f09675f4f2ea0198bd8b8e59c9ade703dc Mon Sep 17 00:00:00 2001 From: TateKennington Date: Tue, 14 Jan 2025 10:46:56 +1300 Subject: [PATCH 098/107] fix(node): Prevent node:child_process from always inheriting the parent environment (#27343) (#27340) Fixes #27343 Currently the node:child_process polyfill is always passing the full parent environment to all spawned subprocesses. In the case where `options.env` is provided those keys are overridden but the rest of the parent environment is still passed through. On Node the behaviour is for child processes to only inherit the parent environment when `options.env` isn't specified. When `options.env` is specified the child process inherits only those keys. This PR updates the internal node child_process polyfill so that the `clearEnv` argument is set to true when spawning the subprocess to prevent the parent environment always being inherited by default. It also fixes an issue where `normalizeSpawnArguments` wasn't returning the `env` option if `options.env` was unset. --- ext/node/polyfills/internal/child_process.ts | 2 + tests/unit_node/child_process_test.ts | 67 ++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/ext/node/polyfills/internal/child_process.ts b/ext/node/polyfills/internal/child_process.ts index 17809cc559..569ee7d328 100644 --- a/ext/node/polyfills/internal/child_process.ts +++ b/ext/node/polyfills/internal/child_process.ts @@ -277,6 +277,7 @@ export class ChildProcess extends EventEmitter { try { this.#process = new Deno.Command(cmd, { args: cmdArgs, + clearEnv: true, cwd, env: stringEnv, stdin: toDenoStdio(stdin), @@ -839,6 +840,7 @@ export function normalizeSpawnArguments( args, cwd, detached: !!options.detached, + env, envPairs, file, windowsHide: !!options.windowsHide, diff --git a/tests/unit_node/child_process_test.ts b/tests/unit_node/child_process_test.ts index bd875ad419..1732b9d2bf 100644 --- a/tests/unit_node/child_process_test.ts +++ b/tests/unit_node/child_process_test.ts @@ -656,6 +656,73 @@ Deno.test({ }, }); +Deno.test({ + name: + "[node/child_process spawn] child inherits Deno.env when options.env is not provided", + async fn() { + const deferred = withTimeout(); + Deno.env.set("BAR", "BAR"); + const env = spawn( + `"${Deno.execPath()}" eval -p "Deno.env.toObject().BAR"`, + { + shell: true, + }, + ); + try { + let envOutput = ""; + + assert(env.stdout); + env.on("error", (err: Error) => deferred.reject(err)); + env.stdout.on("data", (data) => { + envOutput += data; + }); + env.on("close", () => { + deferred.resolve(envOutput.trim()); + }); + await deferred.promise; + } finally { + env.kill(); + Deno.env.delete("BAR"); + } + const value = await deferred.promise; + assertEquals(value, "BAR"); + }, +}); + +Deno.test({ + name: + "[node/child_process spawn] child doesn't inherit Deno.env when options.env is provided", + async fn() { + const deferred = withTimeout(); + Deno.env.set("BAZ", "BAZ"); + const env = spawn( + `"${Deno.execPath()}" eval -p "Deno.env.toObject().BAZ"`, + { + env: {}, + shell: true, + }, + ); + try { + let envOutput = ""; + + assert(env.stdout); + env.on("error", (err: Error) => deferred.reject(err)); + env.stdout.on("data", (data) => { + envOutput += data; + }); + env.on("close", () => { + deferred.resolve(envOutput.trim()); + }); + await deferred.promise; + } finally { + env.kill(); + Deno.env.delete("BAZ"); + } + const value = await deferred.promise; + assertEquals(value, "undefined"); + }, +}); + // Regression test for https://github.com/denoland/deno/issues/20373 Deno.test(async function undefinedValueInEnvVar() { const deferred = withTimeout(); From 9dbb99a83cb599028aef662b23e13faf2df15297 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 13 Jan 2025 17:35:18 -0500 Subject: [PATCH 099/107] refactor: create `NpmInstaller` (#27626) This separates npm resolution code from npm installation (more work towards moving resolution code out of the CLI and cleaning up this code). --- Cargo.lock | 4 +- Cargo.toml | 2 +- cli/factory.rs | 223 ++++- cli/graph_util.rs | 44 +- cli/lsp/documents.rs | 10 +- cli/lsp/resolver.rs | 177 +++- cli/main.rs | 12 +- .../common/bin_entries.rs | 3 +- .../common/lifecycle_scripts.rs | 0 .../installers => installer}/common/mod.rs | 4 +- .../installers => installer}/global.rs | 14 +- .../installers => installer}/local.rs | 14 +- cli/npm/installer/mod.rs | 283 +++++++ .../installer.rs => installer/resolution.rs} | 17 +- cli/npm/managed.rs | 496 +++++++++++ cli/npm/managed/installers/mod.rs | 55 -- cli/npm/managed/mod.rs | 768 ------------------ cli/npm/mod.rs | 22 +- cli/resolver.rs | 107 ++- cli/standalone/mod.rs | 46 +- cli/tools/check.rs | 9 +- cli/tools/info.rs | 7 +- cli/tools/installer.rs | 4 +- cli/tools/jupyter/mod.rs | 4 +- cli/tools/registry/pm.rs | 6 +- cli/tools/registry/pm/cache_deps.rs | 15 +- cli/tools/registry/pm/deps.rs | 10 +- cli/tools/registry/pm/outdated.rs | 1 + cli/tools/repl/mod.rs | 4 +- cli/tools/repl/session.rs | 16 +- cli/tools/run/mod.rs | 12 +- cli/tools/task.rs | 21 +- cli/tsc/mod.rs | 5 +- cli/worker.rs | 13 +- resolvers/deno/npm/managed/global.rs | 6 +- resolvers/deno/npm/managed/local.rs | 6 +- resolvers/deno/npm/managed/mod.rs | 57 +- resolvers/deno/npm/managed/resolution.rs | 9 +- runtime/ops/process.rs | 2 + 39 files changed, 1367 insertions(+), 1141 deletions(-) rename cli/npm/{managed/installers => installer}/common/bin_entries.rs (99%) rename cli/npm/{managed/installers => installer}/common/lifecycle_scripts.rs (100%) rename cli/npm/{managed/installers => installer}/common/mod.rs (79%) rename cli/npm/{managed/installers => installer}/global.rs (97%) rename cli/npm/{managed/installers => installer}/local.rs (99%) create mode 100644 cli/npm/installer/mod.rs rename cli/npm/{managed/installer.rs => installer/resolution.rs} (92%) create mode 100644 cli/npm/managed.rs delete mode 100644 cli/npm/managed/installers/mod.rs delete mode 100644 cli/npm/managed/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 1f13898fe0..b2a78300c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2074,9 +2074,9 @@ dependencies = [ [[package]] name = "deno_npm" -version = "0.27.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f818ad5dc4c206b50b5cfa6f10b4b94b127e15c8342c152768eba40c225ca23" +checksum = "4adceb4c34f10e837d0e3ae76e88dddefb13e83c05c1ef1699fa5519241c9d27" dependencies = [ "async-trait", "capacity_builder 0.5.0", diff --git a/Cargo.toml b/Cargo.toml index 48abe48305..2b52664a7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ deno_bench_util = { version = "0.179.0", path = "./bench_util" } deno_config = { version = "=0.43.0", features = ["workspace", "sync"] } deno_lockfile = "=0.24.0" deno_media_type = { version = "0.2.3", features = ["module_specifier"] } -deno_npm = "=0.27.0" +deno_npm = "=0.27.2" deno_path_util = "=0.3.0" deno_permissions = { version = "0.44.0", path = "./runtime/permissions" } deno_runtime = { version = "0.193.0", path = "./runtime" } diff --git a/cli/factory.rs b/cli/factory.rs index 25a39cae8b..e3873b5164 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -11,8 +11,10 @@ use deno_core::error::AnyError; use deno_core::futures::FutureExt; use deno_core::FeatureChecker; use deno_error::JsErrorBox; +use deno_npm_cache::NpmCacheSetting; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::sloppy_imports::SloppyImportsCachedFs; @@ -67,19 +69,27 @@ use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; use crate::npm::create_cli_npm_resolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::NpmResolutionInstaller; use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmCacheHttpClient; +use crate::npm::CliNpmRegistryInfoProvider; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; +use crate::npm::CliNpmTarballCache; use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionCheckerMode; +use crate::npm::NpmResolutionInitializer; use crate::resolver::CjsTracker; use crate::resolver::CliDenoResolver; +use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; -use crate::resolver::CliResolverOptions; use crate::resolver::CliSloppyImportsResolver; +use crate::resolver::FoundPackageJsonDepFlag; use crate::resolver::NpmModuleLoader; use crate::standalone::binary::DenoCompileBinaryWriter; use crate::sys::CliSys; @@ -188,6 +198,7 @@ struct CliFactoryServices { emitter: Deferred>, feature_checker: Deferred>, file_fetcher: Deferred>, + found_pkg_json_dep_flag: Arc, fs: Deferred>, global_http_cache: Deferred>, http_cache: Deferred>, @@ -202,9 +213,18 @@ struct CliFactoryServices { module_load_preparer: Deferred>, node_code_translator: Deferred>, node_resolver: Deferred>, + npm_cache: Deferred>, npm_cache_dir: Deferred>, + npm_cache_http_client: Deferred>, + npm_graph_resolver: Deferred>, + npm_installer: Deferred>, + npm_registry_info_provider: Deferred>, npm_req_resolver: Deferred>, + npm_resolution: Arc, + npm_resolution_initializer: Deferred>, + npm_resolution_installer: Deferred>, npm_resolver: Deferred>, + npm_tarball_cache: Deferred>, parsed_source_cache: Deferred>, permission_desc_parser: Deferred>>, @@ -398,6 +418,18 @@ impl CliFactory { }) } + pub fn npm_cache(&self) -> Result<&Arc, AnyError> { + self.services.npm_cache.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmCache::new( + self.npm_cache_dir()?.clone(), + self.sys(), + NpmCacheSetting::from_cache_setting(&cli_options.cache_setting()), + cli_options.npmrc().clone(), + ))) + }) + } + pub fn npm_cache_dir(&self) -> Result<&Arc, AnyError> { self.services.npm_cache_dir.get_or_try_init(|| { let global_path = self.deno_dir()?.npm_folder_path(); @@ -410,6 +442,123 @@ impl CliFactory { }) } + pub fn npm_cache_http_client(&self) -> &Arc { + self.services.npm_cache_http_client.get_or_init(|| { + Arc::new(CliNpmCacheHttpClient::new( + self.http_client_provider().clone(), + self.text_only_progress_bar().clone(), + )) + }) + } + + pub fn npm_graph_resolver( + &self, + ) -> Result<&Arc, AnyError> { + self.services.npm_graph_resolver.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmGraphResolver::new( + self.npm_installer_if_managed()?.cloned(), + self.services.found_pkg_json_dep_flag.clone(), + cli_options.unstable_bare_node_builtins(), + cli_options.default_npm_caching_strategy(), + ))) + }) + } + + pub fn npm_installer_if_managed( + &self, + ) -> Result>, AnyError> { + let options = self.cli_options()?; + if options.use_byonm() || options.no_npm() { + Ok(None) + } else { + Ok(Some(self.npm_installer()?)) + } + } + + pub fn npm_installer(&self) -> Result<&Arc, AnyError> { + self.services.npm_installer.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(NpmInstaller::new( + self.npm_cache()?.clone(), + Arc::new(NpmInstallDepsProvider::from_workspace( + cli_options.workspace(), + )), + self.npm_resolution().clone(), + self.npm_resolution_initializer()?.clone(), + self.npm_resolution_installer()?.clone(), + self.text_only_progress_bar(), + self.sys(), + self.npm_tarball_cache()?.clone(), + cli_options.maybe_lockfile().cloned(), + cli_options.node_modules_dir_path().cloned(), + cli_options.lifecycle_scripts_config(), + cli_options.npm_system_info(), + ))) + }) + } + + pub fn npm_registry_info_provider( + &self, + ) -> Result<&Arc, AnyError> { + self + .services + .npm_registry_info_provider + .get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmRegistryInfoProvider::new( + self.npm_cache()?.clone(), + self.npm_cache_http_client().clone(), + cli_options.npmrc().clone(), + ))) + }) + } + + pub fn npm_resolution(&self) -> &Arc { + &self.services.npm_resolution + } + + pub fn npm_resolution_initializer( + &self, + ) -> Result<&Arc, AnyError> { + self + .services + .npm_resolution_initializer + .get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(NpmResolutionInitializer::new( + self.npm_registry_info_provider()?.clone(), + self.npm_resolution().clone(), + match cli_options.resolve_npm_resolution_snapshot()? { + Some(snapshot) => { + CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot)) + } + None => match cli_options.maybe_lockfile() { + Some(lockfile) => { + CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( + lockfile.clone(), + ) + } + None => CliNpmResolverManagedSnapshotOption::Specified(None), + }, + }, + ))) + }) + } + + pub fn npm_resolution_installer( + &self, + ) -> Result<&Arc, AnyError> { + self.services.npm_resolution_installer.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(NpmResolutionInstaller::new( + self.npm_registry_info_provider()?.clone(), + self.npm_resolution().clone(), + cli_options.maybe_lockfile().cloned(), + ))) + }) + } + pub async fn npm_resolver( &self, ) -> Result<&Arc, AnyError> { @@ -419,7 +568,7 @@ impl CliFactory { .get_or_try_init_async( async { let cli_options = self.cli_options()?; - create_cli_npm_resolver(if cli_options.use_byonm() { + Ok(create_cli_npm_resolver(if cli_options.use_byonm() { CliNpmResolverCreateOptions::Byonm( CliByonmNpmResolverCreateOptions { sys: self.sys(), @@ -438,52 +587,43 @@ impl CliFactory { }, ) } else { + self + .npm_resolution_initializer()? + .ensure_initialized() + .await?; CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { - http_client_provider: self.http_client_provider().clone(), - npm_install_deps_provider: Arc::new( - NpmInstallDepsProvider::from_workspace( - cli_options.workspace(), - ), - ), sys: self.sys(), - snapshot: match cli_options.resolve_npm_resolution_snapshot()? { - Some(snapshot) => { - CliNpmResolverManagedSnapshotOption::Specified(Some( - snapshot, - )) - } - None => match cli_options.maybe_lockfile() { - Some(lockfile) => { - CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( - lockfile.clone(), - ) - } - None => { - CliNpmResolverManagedSnapshotOption::Specified(None) - } - }, - }, - maybe_lockfile: cli_options.maybe_lockfile().cloned(), + npm_resolution: self.npm_resolution().clone(), npm_cache_dir: self.npm_cache_dir()?.clone(), - cache_setting: cli_options.cache_setting(), - text_only_progress_bar: self.text_only_progress_bar().clone(), maybe_node_modules_path: cli_options .node_modules_dir_path() .cloned(), npm_system_info: cli_options.npm_system_info(), npmrc: cli_options.npmrc().clone(), - lifecycle_scripts: cli_options.lifecycle_scripts_config(), }, ) - }) - .await + })) } .boxed_local(), ) .await } + pub fn npm_tarball_cache( + &self, + ) -> Result<&Arc, AnyError> { + self.services.npm_tarball_cache.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmTarballCache::new( + self.npm_cache()?.clone(), + self.npm_cache_http_client().clone(), + self.sys(), + cli_options.npmrc().clone(), + ))) + }) + } + pub fn sloppy_imports_resolver( &self, ) -> Result>, AnyError> { @@ -571,17 +711,10 @@ impl CliFactory { .resolver .get_or_try_init_async( async { - let cli_options = self.cli_options()?; - Ok(Arc::new(CliResolver::new(CliResolverOptions { - npm_resolver: if cli_options.no_npm() { - None - } else { - Some(self.npm_resolver().await?.clone()) - }, - bare_node_builtins_enabled: cli_options - .unstable_bare_node_builtins(), - deno_resolver: self.deno_resolver().await?.clone(), - }))) + Ok(Arc::new(CliResolver::new( + self.deno_resolver().await?.clone(), + self.services.found_pkg_json_dep_flag.clone(), + ))) } .boxed_local(), ) @@ -752,6 +885,7 @@ impl CliFactory { cli_options.clone(), self.module_graph_builder().await?.clone(), self.node_resolver().await?.clone(), + self.npm_installer_if_managed()?.cloned(), self.npm_resolver().await?.clone(), self.sys(), ))) @@ -777,6 +911,8 @@ impl CliFactory { cli_options.maybe_lockfile().cloned(), self.maybe_file_watcher_reporter().clone(), self.module_info_cache()?.clone(), + self.npm_graph_resolver()?.clone(), + self.npm_installer_if_managed()?.cloned(), self.npm_resolver().await?.clone(), self.parsed_source_cache().clone(), self.resolver().await?.clone(), @@ -797,7 +933,7 @@ impl CliFactory { let cli_options = self.cli_options()?; Ok(Arc::new(ModuleGraphCreator::new( cli_options.clone(), - self.npm_resolver().await?.clone(), + self.npm_installer_if_managed()?.cloned(), self.module_graph_builder().await?.clone(), self.type_checker().await?.clone(), ))) @@ -997,6 +1133,7 @@ impl CliFactory { self.sys(), )), node_resolver.clone(), + self.npm_installer_if_managed()?.cloned(), npm_resolver.clone(), pkg_json_resolver, self.root_cert_store_provider().clone(), diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 8da44c76ab..17d4fef553 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use deno_config::deno_json; use deno_config::deno_json::JsxImportSourceConfig; +use deno_config::deno_json::NodeModulesDirMode; use deno_config::workspace::JsrPackageConfig; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; @@ -51,8 +52,11 @@ use crate::cache::ModuleInfoCache; use crate::cache::ParsedSourceCache; use crate::colors; use crate::file_fetcher::CliFileFetcher; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; use crate::resolver::CjsTracker; +use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliResolver; use crate::resolver::CliSloppyImportsResolver; use crate::sys::CliSys; @@ -263,7 +267,7 @@ pub struct CreateGraphOptions<'a> { pub struct ModuleGraphCreator { options: Arc, - npm_resolver: Arc, + npm_installer: Option>, module_graph_builder: Arc, type_checker: Arc, } @@ -271,13 +275,13 @@ pub struct ModuleGraphCreator { impl ModuleGraphCreator { pub fn new( options: Arc, - npm_resolver: Arc, + npm_installer: Option>, module_graph_builder: Arc, type_checker: Arc, ) -> Self { Self { options, - npm_resolver, + npm_installer, module_graph_builder, type_checker, } @@ -400,9 +404,9 @@ impl ModuleGraphCreator { .build_graph_with_npm_resolution(&mut graph, options) .await?; - if let Some(npm_resolver) = self.npm_resolver.as_managed() { + if let Some(npm_installer) = &self.npm_installer { if graph.has_node_specifier && self.options.type_check_mode().is_true() { - npm_resolver.inject_synthetic_types_node_package().await?; + npm_installer.inject_synthetic_types_node_package().await?; } } @@ -497,6 +501,8 @@ pub struct ModuleGraphBuilder { lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, + npm_graph_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, parsed_source_cache: Arc, resolver: Arc, @@ -516,6 +522,8 @@ impl ModuleGraphBuilder { lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, + npm_graph_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, parsed_source_cache: Arc, resolver: Arc, @@ -532,6 +540,8 @@ impl ModuleGraphBuilder { lockfile, maybe_file_watcher_reporter, module_info_cache, + npm_graph_resolver, + npm_installer, npm_resolver, parsed_source_cache, resolver, @@ -630,10 +640,7 @@ impl ModuleGraphBuilder { Some(loader) => MutLoaderRef::Borrowed(loader), None => MutLoaderRef::Owned(self.create_graph_loader()), }; - let cli_resolver = &self.resolver; let graph_resolver = self.create_graph_resolver()?; - let graph_npm_resolver = - cli_resolver.create_graph_npm_resolver(options.npm_caching); let maybe_file_watcher_reporter = self .maybe_file_watcher_reporter .as_ref() @@ -654,7 +661,7 @@ impl ModuleGraphBuilder { executor: Default::default(), file_system: &self.sys, jsr_url_provider: &CliJsrUrlProvider, - npm_resolver: Some(&graph_npm_resolver), + npm_resolver: Some(self.npm_graph_resolver.as_ref()), module_analyzer: &analyzer, reporter: maybe_file_watcher_reporter, resolver: Some(&graph_resolver), @@ -678,16 +685,15 @@ impl ModuleGraphBuilder { if self .cli_options .node_modules_dir()? - .map(|m| m.uses_node_modules_dir()) + .map(|m| m == NodeModulesDirMode::Auto) .unwrap_or(false) { - if let Some(npm_resolver) = self.npm_resolver.as_managed() { - let already_done = - npm_resolver.ensure_top_level_package_json_install().await?; + if let Some(npm_installer) = &self.npm_installer { + let already_done = npm_installer + .ensure_top_level_package_json_install() + .await?; if !already_done && matches!(npm_caching, NpmCachingStrategy::Eager) { - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) - .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } } } @@ -775,11 +781,7 @@ impl ModuleGraphBuilder { None }; let parser = self.parsed_source_cache.as_capturing_parser(); - let cli_resolver = &self.resolver; let graph_resolver = self.create_graph_resolver()?; - let graph_npm_resolver = cli_resolver.create_graph_npm_resolver( - self.cli_options.default_npm_caching_strategy(), - ); graph.build_fast_check_type_graph( deno_graph::BuildFastCheckTypeGraphOptions { @@ -788,7 +790,7 @@ impl ModuleGraphBuilder { fast_check_dts: false, jsr_url_provider: &CliJsrUrlProvider, resolver: Some(&graph_resolver), - npm_resolver: Some(&graph_npm_resolver), + npm_resolver: Some(self.npm_graph_resolver.as_ref()), workspace_fast_check: options.workspace_fast_check, }, ); diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index f31353d436..70f55ffd62 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -480,7 +480,7 @@ impl Document { let is_cjs_resolver = resolver.as_is_cjs_resolver(self.file_referrer.as_ref()); let npm_resolver = - resolver.create_graph_npm_resolver(self.file_referrer.as_ref()); + resolver.as_graph_npm_resolver(self.file_referrer.as_ref()); let config_data = resolver.as_config_data(self.file_referrer.as_ref()); let jsx_import_source_config = config_data.and_then(|d| d.maybe_jsx_import_source_config()); @@ -503,7 +503,7 @@ impl Document { s, &CliJsrUrlProvider, Some(&resolver), - Some(&npm_resolver), + Some(npm_resolver.as_ref()), ), ) }) @@ -513,7 +513,7 @@ impl Document { Arc::new(d.with_new_resolver( &CliJsrUrlProvider, Some(&resolver), - Some(&npm_resolver), + Some(npm_resolver.as_ref()), )) }); is_script = self.is_script; @@ -1702,7 +1702,7 @@ fn analyze_module( ) -> (ModuleResult, ResolutionMode) { match parsed_source_result { Ok(parsed_source) => { - let npm_resolver = resolver.create_graph_npm_resolver(file_referrer); + let npm_resolver = resolver.as_graph_npm_resolver(file_referrer); let cli_resolver = resolver.as_cli_resolver(file_referrer); let is_cjs_resolver = resolver.as_is_cjs_resolver(file_referrer); let config_data = resolver.as_config_data(file_referrer); @@ -1731,7 +1731,7 @@ fn analyze_module( file_system: &deno_graph::source::NullFileSystem, jsr_url_provider: &CliJsrUrlProvider, maybe_resolver: Some(&resolver), - maybe_npm_resolver: Some(&npm_resolver), + maybe_npm_resolver: Some(npm_resolver.as_ref()), }, )), module_resolution_mode, diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index d7f7e684a9..af4ab6571f 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -9,7 +9,6 @@ use std::sync::Arc; use dashmap::DashMap; use deno_ast::MediaType; -use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::npm::NpmCacheDir; use deno_cache_dir::HttpCache; use deno_config::deno_json::JsxImportSourceConfig; @@ -21,9 +20,11 @@ use deno_graph::GraphImport; use deno_graph::ModuleSpecifier; use deno_graph::Range; use deno_npm::NpmSystemInfo; +use deno_npm_cache::TarballCache; use deno_path_util::url_to_file_path; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; @@ -42,6 +43,8 @@ use super::cache::LspCache; use super::jsr::JsrCacheResolver; use crate::args::create_default_npmrc; use crate::args::CliLockfile; +use crate::args::LifecycleScriptsConfig; +use crate::args::NpmCachingStrategy; use crate::args::NpmInstallDepsProvider; use crate::factory::Deferred; use crate::graph_util::to_node_resolution_kind; @@ -53,19 +56,25 @@ use crate::lsp::config::ConfigData; use crate::lsp::logging::lsp_warn; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; -use crate::npm::create_cli_npm_resolver_for_lsp; +use crate::npm::create_cli_npm_resolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::NpmResolutionInstaller; use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmCacheHttpClient; +use crate::npm::CliNpmRegistryInfoProvider; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::ManagedCliNpmResolver; +use crate::npm::NpmResolutionInitializer; use crate::resolver::CliDenoResolver; +use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; -use crate::resolver::CliResolverOptions; +use crate::resolver::FoundPackageJsonDepFlag; use crate::resolver::IsCjsResolver; -use crate::resolver::WorkerCliNpmGraphResolver; use crate::sys::CliSys; use crate::tsc::into_specifier_and_media_type; use crate::util::progress_bar::ProgressBar; @@ -77,6 +86,8 @@ struct LspScopeResolver { in_npm_pkg_checker: Arc, is_cjs_resolver: Arc, jsr_resolver: Option>, + npm_graph_resolver: Arc, + npm_installer: Option>, npm_resolver: Option>, node_resolver: Option>, npm_pkg_req_resolver: Option>, @@ -96,6 +107,8 @@ impl Default for LspScopeResolver { in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), is_cjs_resolver: factory.is_cjs_resolver().clone(), jsr_resolver: None, + npm_graph_resolver: factory.npm_graph_resolver().clone(), + npm_installer: None, npm_resolver: None, node_resolver: None, npm_pkg_req_resolver: None, @@ -121,6 +134,7 @@ impl LspScopeResolver { } let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone(); let npm_resolver = factory.npm_resolver().cloned(); + let npm_installer = factory.npm_installer().cloned(); let node_resolver = factory.node_resolver().cloned(); let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned(); let cli_resolver = factory.cli_resolver().clone(); @@ -133,8 +147,7 @@ impl LspScopeResolver { cache.for_specifier(config_data.map(|d| d.scope.as_ref())), config_data.and_then(|d| d.lockfile.clone()), ))); - let npm_graph_resolver = cli_resolver - .create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager); + let npm_graph_resolver = factory.npm_graph_resolver(); let maybe_jsx_import_source_config = config_data.and_then(|d| d.maybe_jsx_import_source_config()); let graph_imports = config_data @@ -156,7 +169,7 @@ impl LspScopeResolver { imports, &CliJsrUrlProvider, Some(&resolver), - Some(&npm_graph_resolver), + Some(npm_graph_resolver.as_ref()), ); (referrer, graph_import) }) @@ -207,8 +220,10 @@ impl LspScopeResolver { in_npm_pkg_checker, is_cjs_resolver: factory.is_cjs_resolver().clone(), jsr_resolver, + npm_graph_resolver: factory.npm_graph_resolver().clone(), npm_pkg_req_resolver, npm_resolver, + npm_installer, node_resolver, pkg_json_resolver, redirect_resolver, @@ -231,6 +246,9 @@ impl LspScopeResolver { in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), is_cjs_resolver: factory.is_cjs_resolver().clone(), jsr_resolver: self.jsr_resolver.clone(), + npm_graph_resolver: factory.npm_graph_resolver().clone(), + // npm installer isn't necessary for a snapshot + npm_installer: None, npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(), npm_resolver: factory.npm_resolver().cloned(), node_resolver: factory.node_resolver().cloned(), @@ -318,14 +336,12 @@ impl LspResolver { if let Some(dep_info) = dep_info { *resolver.dep_info.lock() = dep_info.clone(); } - if let Some(npm_resolver) = resolver.npm_resolver.as_ref() { - if let Some(npm_resolver) = npm_resolver.as_managed() { - let reqs = dep_info - .map(|i| i.npm_reqs.iter().cloned().collect::>()) - .unwrap_or_default(); - if let Err(err) = npm_resolver.set_package_reqs(&reqs).await { - lsp_warn!("Could not set npm package requirements: {:#}", err); - } + if let Some(npm_installer) = resolver.npm_installer.as_ref() { + let reqs = dep_info + .map(|i| i.npm_reqs.iter().cloned().collect::>()) + .unwrap_or_default(); + if let Err(err) = npm_installer.set_package_reqs(&reqs).await { + lsp_warn!("Could not set npm package requirements: {:#}", err); } } } @@ -339,14 +355,12 @@ impl LspResolver { resolver.resolver.as_ref() } - pub fn create_graph_npm_resolver( + pub fn as_graph_npm_resolver( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> WorkerCliNpmGraphResolver { + ) -> &Arc { let resolver = self.get_scope_resolver(file_referrer); - resolver - .resolver - .create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager) + &resolver.npm_graph_resolver } pub fn as_is_cjs_resolver( @@ -590,9 +604,12 @@ pub struct ScopeDepInfo { #[derive(Default)] struct ResolverFactoryServices { cli_resolver: Deferred>, + found_pkg_json_dep_flag: Arc, in_npm_pkg_checker: Deferred>, is_cjs_resolver: Deferred>, node_resolver: Deferred>>, + npm_graph_resolver: Deferred>, + npm_installer: Option>, npm_pkg_req_resolver: Deferred>>, npm_resolver: Option>, } @@ -616,6 +633,10 @@ impl<'a> ResolverFactory<'a> { } } + // todo(dsherret): probably this method could be removed in the future + // and instead just `npm_resolution_initializer.ensure_initialized()` could + // be called. The reason this exists is because creating the npm resolvers + // used to be async. async fn init_npm_resolver( &mut self, http_client_provider: &Arc, @@ -645,11 +666,31 @@ impl<'a> ResolverFactory<'a> { cache.deno_dir().npm_folder_path(), npmrc.get_all_known_registries_urls(), )); - CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { - http_client_provider: http_client_provider.clone(), - // only used for top level install, so we can ignore this - npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), - snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) { + let npm_cache = Arc::new(CliNpmCache::new( + npm_cache_dir.clone(), + sys.clone(), + // Use an "only" cache setting in order to make the + // user do an explicit "cache" command and prevent + // the cache from being filled with lots of packages while + // the user is typing. + deno_npm_cache::NpmCacheSetting::Only, + npmrc.clone(), + )); + let pb = ProgressBar::new(ProgressBarStyle::TextOnly); + let npm_client = Arc::new(CliNpmCacheHttpClient::new( + http_client_provider.clone(), + pb.clone(), + )); + let registry_info_provider = Arc::new(CliNpmRegistryInfoProvider::new( + npm_cache.clone(), + npm_client.clone(), + npmrc.clone(), + )); + let npm_resolution = Arc::new(NpmResolutionCell::default()); + let npm_resolution_initializer = Arc::new(NpmResolutionInitializer::new( + registry_info_provider.clone(), + npm_resolution.clone(), + match self.config_data.and_then(|d| d.lockfile.as_ref()) { Some(lockfile) => { CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( lockfile.clone(), @@ -657,26 +698,62 @@ impl<'a> ResolverFactory<'a> { } None => CliNpmResolverManagedSnapshotOption::Specified(None), }, + )); + // Don't provide the lockfile. We don't want these resolvers + // updating it. Only the cache request should update the lockfile. + let maybe_lockfile: Option> = None; + let maybe_node_modules_path = + self.config_data.and_then(|d| d.node_modules_dir.clone()); + let tarball_cache = Arc::new(TarballCache::new( + npm_cache.clone(), + npm_client.clone(), + sys.clone(), + npmrc.clone(), + )); + let npm_resolution_installer = Arc::new(NpmResolutionInstaller::new( + registry_info_provider, + npm_resolution.clone(), + maybe_lockfile.clone(), + )); + let npm_installer = Arc::new(NpmInstaller::new( + npm_cache.clone(), + Arc::new(NpmInstallDepsProvider::empty()), + npm_resolution.clone(), + npm_resolution_initializer.clone(), + npm_resolution_installer, + &pb, + sys.clone(), + tarball_cache.clone(), + maybe_lockfile, + maybe_node_modules_path.clone(), + LifecycleScriptsConfig::default(), + NpmSystemInfo::default(), + )); + self.set_npm_installer(npm_installer); + // spawn due to the lsp's `Send` requirement + deno_core::unsync::spawn(async move { + if let Err(err) = npm_resolution_initializer.ensure_initialized().await + { + log::warn!("failed to initialize npm resolution: {}", err); + } + }) + .await + .unwrap(); + + CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { sys: CliSys::default(), npm_cache_dir, - // Use an "only" cache setting in order to make the - // user do an explicit "cache" command and prevent - // the cache from being filled with lots of packages while - // the user is typing. - cache_setting: CacheSetting::Only, - text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly), - // Don't provide the lockfile. We don't want these resolvers - // updating it. Only the cache request should update the lockfile. - maybe_lockfile: None, - maybe_node_modules_path: self - .config_data - .and_then(|d| d.node_modules_dir.clone()), + maybe_node_modules_path, npmrc, + npm_resolution, npm_system_info: NpmSystemInfo::default(), - lifecycle_scripts: Default::default(), }) }; - self.set_npm_resolver(create_cli_npm_resolver_for_lsp(options).await); + self.set_npm_resolver(create_cli_npm_resolver(options)); + } + + pub fn set_npm_installer(&mut self, npm_installer: Arc) { + self.services.npm_installer = Some(npm_installer); } pub fn set_npm_resolver(&mut self, npm_resolver: Arc) { @@ -720,13 +797,27 @@ impl<'a> ResolverFactory<'a> { is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false), maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()), })); - Arc::new(CliResolver::new(CliResolverOptions { + Arc::new(CliResolver::new( deno_resolver, - npm_resolver: self.npm_resolver().cloned(), - bare_node_builtins_enabled: self + self.services.found_pkg_json_dep_flag.clone(), + )) + }) + } + + pub fn npm_installer(&self) -> Option<&Arc> { + self.services.npm_installer.as_ref() + } + + pub fn npm_graph_resolver(&self) -> &Arc { + self.services.npm_graph_resolver.get_or_init(|| { + Arc::new(CliNpmGraphResolver::new( + None, + self.services.found_pkg_json_dep_flag.clone(), + self .config_data .is_some_and(|d| d.unstable.contains("bare-node-builtins")), - })) + NpmCachingStrategy::Eager, + )) }) } diff --git a/cli/main.rs b/cli/main.rs index 6bbefcf956..f97ea81e5d 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -40,7 +40,6 @@ use deno_core::error::AnyError; use deno_core::error::CoreError; use deno_core::futures::FutureExt; use deno_core::unsync::JoinHandle; -use deno_npm::resolution::SnapshotFromLockfileError; use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_runtime::fmt_errors::format_js_error; @@ -52,6 +51,7 @@ use factory::CliFactory; use standalone::MODULE_NOT_FOUND; use standalone::UNSUPPORTED_SCHEME; +use self::npm::ResolveSnapshotError; use crate::args::flags_from_vec; use crate::args::DenoSubcommand; use crate::args::Flags; @@ -376,13 +376,15 @@ fn exit_for_error(error: AnyError) -> ! { util::result::any_and_jserrorbox_downcast_ref::(&error) { error_string = format_js_error(e); - } else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) = - util::result::any_and_jserrorbox_downcast_ref::( + } else if let Some(e @ ResolveSnapshotError { .. }) = + util::result::any_and_jserrorbox_downcast_ref::( &error, ) { - error_string = e.to_string(); - error_code = 10; + if let Some(e) = e.maybe_integrity_check_error() { + error_string = e.to_string(); + error_code = 10; + } } exit_with_message(&error_string, error_code); diff --git a/cli/npm/managed/installers/common/bin_entries.rs b/cli/npm/installer/common/bin_entries.rs similarity index 99% rename from cli/npm/managed/installers/common/bin_entries.rs rename to cli/npm/installer/common/bin_entries.rs index bc69786b6c..2f7bed285a 100644 --- a/cli/npm/managed/installers/common/bin_entries.rs +++ b/cli/npm/installer/common/bin_entries.rs @@ -8,8 +8,7 @@ use std::path::PathBuf; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmPackageId; - -use crate::npm::managed::NpmResolutionPackage; +use deno_npm::NpmResolutionPackage; #[derive(Default)] pub struct BinEntries<'a> { diff --git a/cli/npm/managed/installers/common/lifecycle_scripts.rs b/cli/npm/installer/common/lifecycle_scripts.rs similarity index 100% rename from cli/npm/managed/installers/common/lifecycle_scripts.rs rename to cli/npm/installer/common/lifecycle_scripts.rs diff --git a/cli/npm/managed/installers/common/mod.rs b/cli/npm/installer/common/mod.rs similarity index 79% rename from cli/npm/managed/installers/common/mod.rs rename to cli/npm/installer/common/mod.rs index 9659649a2e..bd22a58f03 100644 --- a/cli/npm/managed/installers/common/mod.rs +++ b/cli/npm/installer/common/mod.rs @@ -3,14 +3,14 @@ use async_trait::async_trait; use deno_error::JsErrorBox; -use crate::npm::PackageCaching; +use super::PackageCaching; pub mod bin_entries; pub mod lifecycle_scripts; /// Part of the resolution that interacts with the file system. #[async_trait(?Send)] -pub trait NpmPackageFsInstaller: Send + Sync { +pub trait NpmPackageFsInstaller: std::fmt::Debug + Send + Sync { async fn cache_packages<'a>( &self, caching: PackageCaching<'a>, diff --git a/cli/npm/managed/installers/global.rs b/cli/npm/installer/global.rs similarity index 97% rename from cli/npm/managed/installers/global.rs rename to cli/npm/installer/global.rs index 477d73dbfb..a6b296c6d8 100644 --- a/cli/npm/managed/installers/global.rs +++ b/cli/npm/installer/global.rs @@ -11,14 +11,14 @@ use deno_core::futures::StreamExt; use deno_error::JsErrorBox; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; -use deno_resolver::npm::managed::NpmResolution; +use deno_resolver::npm::managed::NpmResolutionCell; use super::common::lifecycle_scripts::LifecycleScriptsStrategy; use super::common::NpmPackageFsInstaller; +use super::PackageCaching; use crate::args::LifecycleScriptsConfig; use crate::cache::FastInsecureHasher; use crate::colors; -use crate::npm::managed::PackageCaching; use crate::npm::CliNpmCache; use crate::npm::CliNpmTarballCache; @@ -27,25 +27,25 @@ use crate::npm::CliNpmTarballCache; pub struct GlobalNpmPackageInstaller { cache: Arc, tarball_cache: Arc, - resolution: Arc, - system_info: NpmSystemInfo, + resolution: Arc, lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, } impl GlobalNpmPackageInstaller { pub fn new( cache: Arc, tarball_cache: Arc, - resolution: Arc, - system_info: NpmSystemInfo, + resolution: Arc, lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, ) -> Self { Self { cache, tarball_cache, resolution, - system_info, lifecycle_scripts, + system_info, } } } diff --git a/cli/npm/managed/installers/local.rs b/cli/npm/installer/local.rs similarity index 99% rename from cli/npm/managed/installers/local.rs rename to cli/npm/installer/local.rs index a8efaaeb00..87288c6c8e 100644 --- a/cli/npm/managed/installers/local.rs +++ b/cli/npm/installer/local.rs @@ -25,7 +25,7 @@ use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; use deno_path_util::fs::atomic_write_file_with_retries; use deno_resolver::npm::get_package_folder_id_folder_name; -use deno_resolver::npm::managed::NpmResolution; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_semver::package::PackageNv; use deno_semver::StackString; use serde::Deserialize; @@ -33,11 +33,11 @@ use serde::Serialize; use super::common::bin_entries; use super::common::NpmPackageFsInstaller; +use super::PackageCaching; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; use crate::cache::CACHE_PERM; use crate::colors; -use crate::npm::managed::PackageCaching; use crate::npm::CliNpmCache; use crate::npm::CliNpmTarballCache; use crate::sys::CliSys; @@ -54,12 +54,12 @@ pub struct LocalNpmPackageInstaller { cache: Arc, npm_install_deps_provider: Arc, progress_bar: ProgressBar, - resolution: Arc, + resolution: Arc, sys: CliSys, tarball_cache: Arc, + lifecycle_scripts: LifecycleScriptsConfig, root_node_modules_path: PathBuf, system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, } impl LocalNpmPackageInstaller { @@ -68,12 +68,12 @@ impl LocalNpmPackageInstaller { cache: Arc, npm_install_deps_provider: Arc, progress_bar: ProgressBar, - resolution: Arc, + resolution: Arc, sys: CliSys, tarball_cache: Arc, node_modules_folder: PathBuf, - system_info: NpmSystemInfo, lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, ) -> Self { Self { cache, @@ -82,9 +82,9 @@ impl LocalNpmPackageInstaller { resolution, tarball_cache, sys, + lifecycle_scripts, root_node_modules_path: node_modules_folder, system_info, - lifecycle_scripts, } } } diff --git a/cli/npm/installer/mod.rs b/cli/npm/installer/mod.rs new file mode 100644 index 0000000000..58b9cb1bc7 --- /dev/null +++ b/cli/npm/installer/mod.rs @@ -0,0 +1,283 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_core::error::AnyError; +use deno_core::unsync::sync::AtomicFlag; +use deno_error::JsErrorBox; +use deno_npm::registry::NpmPackageInfo; +use deno_npm::registry::NpmRegistryPackageInfoLoadError; +use deno_npm::NpmSystemInfo; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_runtime::colors; +use deno_semver::package::PackageReq; + +pub use self::common::NpmPackageFsInstaller; +use self::global::GlobalNpmPackageInstaller; +use self::local::LocalNpmPackageInstaller; +pub use self::resolution::AddPkgReqsResult; +pub use self::resolution::NpmResolutionInstaller; +use super::NpmResolutionInitializer; +use crate::args::CliLockfile; +use crate::args::LifecycleScriptsConfig; +use crate::args::NpmInstallDepsProvider; +use crate::args::PackageJsonDepValueParseWithLocationError; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; +use crate::util::progress_bar::ProgressBar; + +mod common; +mod global; +mod local; +mod resolution; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PackageCaching<'a> { + Only(Cow<'a, [PackageReq]>), + All, +} + +#[derive(Debug)] +pub struct NpmInstaller { + fs_installer: Arc, + npm_install_deps_provider: Arc, + npm_resolution_initializer: Arc, + npm_resolution_installer: Arc, + maybe_lockfile: Option>, + npm_resolution: Arc, + top_level_install_flag: AtomicFlag, +} + +impl NpmInstaller { + #[allow(clippy::too_many_arguments)] + pub fn new( + npm_cache: Arc, + npm_install_deps_provider: Arc, + npm_resolution: Arc, + npm_resolution_initializer: Arc, + npm_resolution_installer: Arc, + progress_bar: &ProgressBar, + sys: CliSys, + tarball_cache: Arc, + maybe_lockfile: Option>, + maybe_node_modules_path: Option, + lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, + ) -> Self { + let fs_installer: Arc = + match maybe_node_modules_path { + Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new( + npm_cache, + npm_install_deps_provider.clone(), + progress_bar.clone(), + npm_resolution.clone(), + sys, + tarball_cache, + node_modules_folder, + lifecycle_scripts, + system_info, + )), + None => Arc::new(GlobalNpmPackageInstaller::new( + npm_cache, + tarball_cache, + npm_resolution.clone(), + lifecycle_scripts, + system_info, + )), + }; + Self { + fs_installer, + npm_install_deps_provider, + npm_resolution, + npm_resolution_initializer, + npm_resolution_installer, + maybe_lockfile, + top_level_install_flag: Default::default(), + } + } + + /// Adds package requirements to the resolver and ensures everything is setup. + /// This includes setting up the `node_modules` directory, if applicable. + pub async fn add_and_cache_package_reqs( + &self, + packages: &[PackageReq], + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + self + .add_package_reqs_raw( + packages, + Some(PackageCaching::Only(packages.into())), + ) + .await + .dependencies_result + } + + pub async fn add_package_reqs_no_cache( + &self, + packages: &[PackageReq], + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + self + .add_package_reqs_raw(packages, None) + .await + .dependencies_result + } + + pub async fn add_package_reqs( + &self, + packages: &[PackageReq], + caching: PackageCaching<'_>, + ) -> Result<(), JsErrorBox> { + self + .add_package_reqs_raw(packages, Some(caching)) + .await + .dependencies_result + } + + pub async fn add_package_reqs_raw<'a>( + &self, + packages: &[PackageReq], + caching: Option>, + ) -> AddPkgReqsResult { + if packages.is_empty() { + return AddPkgReqsResult { + dependencies_result: Ok(()), + results: vec![], + }; + } + + #[cfg(debug_assertions)] + self.npm_resolution_initializer.debug_assert_initialized(); + + let mut result = self + .npm_resolution_installer + .add_package_reqs(packages) + .await; + + if result.dependencies_result.is_ok() { + if let Some(lockfile) = self.maybe_lockfile.as_ref() { + result.dependencies_result = lockfile.error_if_changed(); + } + } + if result.dependencies_result.is_ok() { + if let Some(caching) = caching { + result.dependencies_result = self.cache_packages(caching).await; + } + } + + result + } + + /// Sets package requirements to the resolver, removing old requirements and adding new ones. + /// + /// This will retrieve and resolve package information, but not cache any package files. + pub async fn set_package_reqs( + &self, + packages: &[PackageReq], + ) -> Result<(), AnyError> { + self + .npm_resolution_installer + .set_package_reqs(packages) + .await + } + + pub async fn inject_synthetic_types_node_package( + &self, + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + let reqs = &[PackageReq::from_str("@types/node").unwrap()]; + // add and ensure this isn't added to the lockfile + self + .add_package_reqs(reqs, PackageCaching::Only(reqs.into())) + .await?; + + Ok(()) + } + + pub async fn cache_package_info( + &self, + package_name: &str, + ) -> Result, NpmRegistryPackageInfoLoadError> { + self + .npm_resolution_installer + .cache_package_info(package_name) + .await + } + + pub async fn cache_packages( + &self, + caching: PackageCaching<'_>, + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + self.fs_installer.cache_packages(caching).await + } + + pub fn ensure_no_pkg_json_dep_errors( + &self, + ) -> Result<(), Box> { + for err in self.npm_install_deps_provider.pkg_json_dep_errors() { + match err.source.as_kind() { + deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => { + return Err(Box::new(err.clone())); + } + deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported { + .. + } => { + // only warn for this one + log::warn!( + "{} {}\n at {}", + colors::yellow("Warning"), + err.source, + err.location, + ) + } + } + } + Ok(()) + } + + /// Ensures that the top level `package.json` dependencies are installed. + /// This may set up the `node_modules` directory. + /// + /// Returns `true` if the top level packages are already installed. A + /// return value of `false` means that new packages were added to the NPM resolution. + pub async fn ensure_top_level_package_json_install( + &self, + ) -> Result { + if !self.top_level_install_flag.raise() { + return Ok(true); // already did this + } + + self.npm_resolution_initializer.ensure_initialized().await?; + + let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); + if pkg_json_remote_pkgs.is_empty() { + return Ok(true); + } + + // check if something needs resolving before bothering to load all + // the package information (which is slow) + if pkg_json_remote_pkgs.iter().all(|pkg| { + self + .npm_resolution + .resolve_pkg_id_from_pkg_req(&pkg.req) + .is_ok() + }) { + log::debug!( + "All package.json deps resolvable. Skipping top level install." + ); + return Ok(true); // everything is already resolvable + } + + let pkg_reqs = pkg_json_remote_pkgs + .iter() + .map(|pkg| pkg.req.clone()) + .collect::>(); + self.add_package_reqs_no_cache(&pkg_reqs).await?; + + Ok(false) + } +} diff --git a/cli/npm/managed/installer.rs b/cli/npm/installer/resolution.rs similarity index 92% rename from cli/npm/managed/installer.rs rename to cli/npm/installer/resolution.rs index 9f1cc05968..06bbcd4f76 100644 --- a/cli/npm/managed/installer.rs +++ b/cli/npm/installer/resolution.rs @@ -8,12 +8,14 @@ use deno_core::error::AnyError; use deno_error::JsErrorBox; use deno_lockfile::NpmPackageDependencyLockfileInfo; use deno_lockfile::NpmPackageLockfileInfo; +use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmRegistryApi; +use deno_npm::registry::NpmRegistryPackageInfoLoadError; use deno_npm::resolution::AddPkgReqsOptions; use deno_npm::resolution::NpmResolutionError; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmResolutionPackage; -use deno_resolver::npm::managed::NpmResolution; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; @@ -35,9 +37,10 @@ pub struct AddPkgReqsResult { } /// Updates the npm resolution with the provided package requirements. +#[derive(Debug)] pub struct NpmResolutionInstaller { registry_info_provider: Arc, - resolution: Arc, + resolution: Arc, maybe_lockfile: Option>, update_queue: TaskQueue, } @@ -45,7 +48,7 @@ pub struct NpmResolutionInstaller { impl NpmResolutionInstaller { pub fn new( registry_info_provider: Arc, - resolution: Arc, + resolution: Arc, maybe_lockfile: Option>, ) -> Self { Self { @@ -56,6 +59,14 @@ impl NpmResolutionInstaller { } } + pub async fn cache_package_info( + &self, + package_name: &str, + ) -> Result, NpmRegistryPackageInfoLoadError> { + // this will internally cache the package information + self.registry_info_provider.package_info(package_name).await + } + pub async fn add_package_reqs( &self, package_reqs: &[PackageReq], diff --git a/cli/npm/managed.rs b/cli/npm/managed.rs new file mode 100644 index 0000000000..0d4c209acc --- /dev/null +++ b/cli/npm/managed.rs @@ -0,0 +1,496 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_ast::ModuleSpecifier; +use deno_cache_dir::npm::NpmCacheDir; +use deno_core::parking_lot::Mutex; +use deno_core::serde_json; +use deno_core::url::Url; +use deno_error::JsError; +use deno_error::JsErrorBox; +use deno_npm::npm_rc::ResolvedNpmRc; +use deno_npm::registry::NpmRegistryApi; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::resolution::PackageReqNotFoundError; +use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; +use deno_npm::NpmPackageId; +use deno_npm::NpmResolutionPackage; +use deno_npm::NpmSystemInfo; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError; +use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError; +use deno_resolver::npm::managed::ResolvePkgIdFromSpecifierError; +use deno_resolver::npm::ByonmOrManagedNpmResolver; +use deno_resolver::npm::ManagedNpmResolver; +use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; +use deno_runtime::ops::process::NpmProcessStateProvider; +use deno_semver::package::PackageNv; +use deno_semver::package::PackageReq; +use node_resolver::NpmPackageFolderResolver; +use sys_traits::FsMetadata; +use thiserror::Error; + +use super::CliNpmRegistryInfoProvider; +use super::CliNpmResolver; +use super::InnerCliNpmResolverRef; +use crate::args::CliLockfile; +use crate::args::NpmProcessState; +use crate::args::NpmProcessStateKind; +use crate::cache::FastInsecureHasher; +use crate::sys::CliSys; + +#[derive(Debug, Clone)] +pub enum CliNpmResolverManagedSnapshotOption { + ResolveFromLockfile(Arc), + Specified(Option), +} + +#[derive(Debug)] +enum SyncState { + Pending(Option), + Err(ResolveSnapshotError), + Success, +} + +#[derive(Debug)] +pub struct NpmResolutionInitializer { + npm_registry_info_provider: Arc, + npm_resolution: Arc, + queue: tokio::sync::Mutex<()>, + sync_state: Mutex, +} + +impl NpmResolutionInitializer { + pub fn new( + npm_registry_info_provider: Arc, + npm_resolution: Arc, + snapshot_option: CliNpmResolverManagedSnapshotOption, + ) -> Self { + Self { + npm_registry_info_provider, + npm_resolution, + queue: tokio::sync::Mutex::new(()), + sync_state: Mutex::new(SyncState::Pending(Some(snapshot_option))), + } + } + + #[cfg(debug_assertions)] + pub fn debug_assert_initialized(&self) { + if !matches!(*self.sync_state.lock(), SyncState::Success) { + panic!("debug assert: npm resolution must be initialized before calling this code"); + } + } + + pub async fn ensure_initialized(&self) -> Result<(), JsErrorBox> { + // fast exit if not pending + { + match &*self.sync_state.lock() { + SyncState::Pending(_) => {} + SyncState::Err(err) => return Err(JsErrorBox::from_err(err.clone())), + SyncState::Success => return Ok(()), + } + } + + // only allow one task in here at a time + let _guard = self.queue.lock().await; + + let snapshot_option = { + let mut sync_state = self.sync_state.lock(); + match &mut *sync_state { + SyncState::Pending(snapshot_option) => { + // this should never panic, but if it does it means that a + // previous future was dropped while initialization occurred... + // that should never happen because this is initialized during + // startup + snapshot_option.take().unwrap() + } + // another thread updated the state while we were waiting + SyncState::Err(resolve_snapshot_error) => { + return Err(JsErrorBox::from_err(resolve_snapshot_error.clone())); + } + SyncState::Success => { + return Ok(()); + } + } + }; + + match resolve_snapshot(&self.npm_registry_info_provider, snapshot_option) + .await + { + Ok(maybe_snapshot) => { + if let Some(snapshot) = maybe_snapshot { + self + .npm_resolution + .set_snapshot(NpmResolutionSnapshot::new(snapshot)); + } + let mut sync_state = self.sync_state.lock(); + *sync_state = SyncState::Success; + Ok(()) + } + Err(err) => { + let mut sync_state = self.sync_state.lock(); + *sync_state = SyncState::Err(err.clone()); + Err(JsErrorBox::from_err(err)) + } + } + } +} + +pub struct CliManagedNpmResolverCreateOptions { + pub npm_cache_dir: Arc, + pub sys: CliSys, + pub maybe_node_modules_path: Option, + pub npm_system_info: NpmSystemInfo, + pub npmrc: Arc, + pub npm_resolution: Arc, +} + +pub fn create_managed_npm_resolver( + options: CliManagedNpmResolverCreateOptions, +) -> Arc { + let managed_npm_resolver = + Arc::new(ManagedNpmResolver::::new::( + &options.npm_cache_dir, + &options.npmrc, + options.npm_resolution.clone(), + options.sys.clone(), + options.maybe_node_modules_path, + )); + Arc::new(ManagedCliNpmResolver::new( + managed_npm_resolver, + options.npm_cache_dir, + options.npmrc, + options.npm_resolution, + options.sys, + options.npm_system_info, + )) +} + +#[derive(Debug, Error, Clone, JsError)] +#[error("failed reading lockfile '{}'", lockfile_path.display())] +#[class(inherit)] +pub struct ResolveSnapshotError { + lockfile_path: PathBuf, + #[inherit] + #[source] + source: SnapshotFromLockfileError, +} + +impl ResolveSnapshotError { + pub fn maybe_integrity_check_error( + &self, + ) -> Option<&deno_npm::resolution::IntegrityCheckFailedError> { + match &self.source { + SnapshotFromLockfileError::SnapshotFromLockfile( + deno_npm::resolution::SnapshotFromLockfileError::IntegrityCheckFailed( + err, + ), + ) => Some(err), + _ => None, + } + } +} + +async fn resolve_snapshot( + registry_info_provider: &Arc, + snapshot: CliNpmResolverManagedSnapshotOption, +) -> Result, ResolveSnapshotError> +{ + match snapshot { + CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => { + if !lockfile.overwrite() { + let snapshot = snapshot_from_lockfile( + lockfile.clone(), + ®istry_info_provider.as_npm_registry_api(), + ) + .await + .map_err(|source| ResolveSnapshotError { + lockfile_path: lockfile.filename.clone(), + source, + })?; + Ok(Some(snapshot)) + } else { + Ok(None) + } + } + CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot), + } +} + +#[derive(Debug, Error, Clone, JsError)] +pub enum SnapshotFromLockfileError { + #[error(transparent)] + #[class(inherit)] + IncompleteError( + #[from] deno_npm::resolution::IncompleteSnapshotFromLockfileError, + ), + #[error(transparent)] + #[class(inherit)] + SnapshotFromLockfile(#[from] deno_npm::resolution::SnapshotFromLockfileError), +} + +async fn snapshot_from_lockfile( + lockfile: Arc, + api: &dyn NpmRegistryApi, +) -> Result { + let (incomplete_snapshot, skip_integrity_check) = { + let lock = lockfile.lock(); + ( + deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?, + lock.overwrite, + ) + }; + let snapshot = deno_npm::resolution::snapshot_from_lockfile( + deno_npm::resolution::SnapshotFromLockfileParams { + incomplete_snapshot, + api, + skip_integrity_check, + }, + ) + .await?; + Ok(snapshot) +} + +/// An npm resolver where the resolution is managed by Deno rather than +/// the user bringing their own node_modules (BYONM) on the file system. +pub struct ManagedCliNpmResolver { + managed_npm_resolver: Arc>, + npm_cache_dir: Arc, + npm_rc: Arc, + sys: CliSys, + resolution: Arc, + system_info: NpmSystemInfo, +} + +impl std::fmt::Debug for ManagedCliNpmResolver { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ManagedCliNpmResolver") + .field("", &"") + .finish() + } +} + +impl ManagedCliNpmResolver { + #[allow(clippy::too_many_arguments)] + pub fn new( + managed_npm_resolver: Arc>, + npm_cache_dir: Arc, + npm_rc: Arc, + resolution: Arc, + sys: CliSys, + system_info: NpmSystemInfo, + ) -> Self { + Self { + managed_npm_resolver, + npm_cache_dir, + npm_rc, + resolution, + sys, + system_info, + } + } + + pub fn resolve_pkg_folder_from_pkg_id( + &self, + pkg_id: &NpmPackageId, + ) -> Result { + self + .managed_npm_resolver + .resolve_pkg_folder_from_pkg_id(pkg_id) + } + + /// Resolves the package id from the provided specifier. + pub fn resolve_pkg_id_from_specifier( + &self, + specifier: &ModuleSpecifier, + ) -> Result, ResolvePkgIdFromSpecifierError> { + self + .managed_npm_resolver + .resolve_pkg_id_from_specifier(specifier) + } + + pub fn resolve_pkg_reqs_from_pkg_id( + &self, + id: &NpmPackageId, + ) -> Vec { + self.resolution.resolve_pkg_reqs_from_pkg_id(id) + } + + pub fn all_system_packages( + &self, + system_info: &NpmSystemInfo, + ) -> Vec { + self.resolution.all_system_packages(system_info) + } + + /// Checks if the provided package req's folder is cached. + pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool { + self + .resolve_pkg_id_from_pkg_req(req) + .ok() + .and_then(|id| { + self + .managed_npm_resolver + .resolve_pkg_folder_from_pkg_id(&id) + .ok() + }) + .map(|folder| self.sys.fs_exists_no_err(folder)) + .unwrap_or(false) + } + + pub fn snapshot(&self) -> NpmResolutionSnapshot { + self.resolution.snapshot() + } + + pub fn top_package_req_for_name(&self, name: &str) -> Option { + let package_reqs = self.resolution.package_reqs(); + let mut entries = package_reqs + .iter() + .filter(|(_, nv)| nv.name == name) + .collect::>(); + entries.sort_by_key(|(_, nv)| &nv.version); + Some(entries.last()?.0.clone()) + } + + pub fn serialized_valid_snapshot_for_system( + &self, + system_info: &NpmSystemInfo, + ) -> ValidSerializedNpmResolutionSnapshot { + self + .resolution + .serialized_valid_snapshot_for_system(system_info) + } + + pub fn resolve_pkg_folder_from_deno_module( + &self, + nv: &PackageNv, + ) -> Result { + self + .managed_npm_resolver + .resolve_pkg_folder_from_deno_module(nv) + } + + pub fn resolve_pkg_id_from_pkg_req( + &self, + req: &PackageReq, + ) -> Result { + self.resolution.resolve_pkg_id_from_pkg_req(req) + } + + pub fn maybe_node_modules_path(&self) -> Option<&Path> { + self.managed_npm_resolver.node_modules_path() + } + + pub fn global_cache_root_path(&self) -> &Path { + self.npm_cache_dir.root_dir() + } + + pub fn global_cache_root_url(&self) -> &Url { + self.npm_cache_dir.root_dir_url() + } +} + +pub fn npm_process_state( + snapshot: ValidSerializedNpmResolutionSnapshot, + node_modules_path: Option<&Path>, +) -> String { + serde_json::to_string(&NpmProcessState { + kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()), + local_node_modules_path: node_modules_path + .map(|p| p.to_string_lossy().to_string()), + }) + .unwrap() +} + +impl NpmProcessStateProvider for ManagedCliNpmResolver { + fn get_npm_process_state(&self) -> String { + npm_process_state( + self.resolution.serialized_valid_snapshot(), + self.managed_npm_resolver.node_modules_path(), + ) + } +} + +impl CliNpmResolver for ManagedCliNpmResolver { + fn into_npm_pkg_folder_resolver( + self: Arc, + ) -> Arc { + self.managed_npm_resolver.clone() + } + + fn into_process_state_provider( + self: Arc, + ) -> Arc { + self + } + + fn into_byonm_or_managed( + self: Arc, + ) -> ByonmOrManagedNpmResolver { + ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone()) + } + + fn clone_snapshotted(&self) -> Arc { + // create a new snapshotted npm resolution and resolver + let npm_resolution = + Arc::new(NpmResolutionCell::new(self.resolution.snapshot())); + + Arc::new(ManagedCliNpmResolver::new( + Arc::new(ManagedNpmResolver::::new::( + &self.npm_cache_dir, + &self.npm_rc, + npm_resolution.clone(), + self.sys.clone(), + self.root_node_modules_path().map(ToOwned::to_owned), + )), + self.npm_cache_dir.clone(), + self.npm_rc.clone(), + npm_resolution, + self.sys.clone(), + self.system_info.clone(), + )) + } + + fn as_inner(&self) -> InnerCliNpmResolverRef { + InnerCliNpmResolverRef::Managed(self) + } + + fn root_node_modules_path(&self) -> Option<&Path> { + self.managed_npm_resolver.node_modules_path() + } + + fn check_state_hash(&self) -> Option { + // We could go further and check all the individual + // npm packages, but that's probably overkill. + let mut package_reqs = self + .resolution + .package_reqs() + .into_iter() + .collect::>(); + package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism + let mut hasher = FastInsecureHasher::new_without_deno_version(); + // ensure the cache gets busted when turning nodeModulesDir on or off + // as this could cause changes in resolution + hasher + .write_hashable(self.managed_npm_resolver.node_modules_path().is_some()); + for (pkg_req, pkg_nv) in package_reqs { + hasher.write_hashable(&pkg_req); + hasher.write_hashable(&pkg_nv); + } + Some(hasher.finish()) + } + + fn resolve_pkg_folder_from_deno_module_req( + &self, + req: &PackageReq, + referrer: &Url, + ) -> Result { + self + .managed_npm_resolver + .resolve_pkg_folder_from_deno_module_req(req, referrer) + .map_err(ResolvePkgFolderFromDenoReqError::Managed) + } +} diff --git a/cli/npm/managed/installers/mod.rs b/cli/npm/managed/installers/mod.rs deleted file mode 100644 index 39e0d6f77c..0000000000 --- a/cli/npm/managed/installers/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -use std::path::PathBuf; -use std::sync::Arc; - -use deno_npm::NpmSystemInfo; -use deno_resolver::npm::managed::NpmResolution; - -pub use self::common::NpmPackageFsInstaller; -use self::global::GlobalNpmPackageInstaller; -use self::local::LocalNpmPackageInstaller; -use crate::args::LifecycleScriptsConfig; -use crate::args::NpmInstallDepsProvider; -use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; -use crate::sys::CliSys; -use crate::util::progress_bar::ProgressBar; - -mod common; -mod global; -mod local; - -#[allow(clippy::too_many_arguments)] -pub fn create_npm_fs_installer( - npm_cache: Arc, - npm_install_deps_provider: &Arc, - progress_bar: &ProgressBar, - resolution: Arc, - sys: CliSys, - tarball_cache: Arc, - maybe_node_modules_path: Option, - system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, -) -> Arc { - match maybe_node_modules_path { - Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new( - npm_cache, - npm_install_deps_provider.clone(), - progress_bar.clone(), - resolution, - sys, - tarball_cache, - node_modules_folder, - system_info, - lifecycle_scripts, - )), - None => Arc::new(GlobalNpmPackageInstaller::new( - npm_cache, - tarball_cache, - resolution, - system_info, - lifecycle_scripts, - )), - } -} diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs deleted file mode 100644 index 8d2ede7e67..0000000000 --- a/cli/npm/managed/mod.rs +++ /dev/null @@ -1,768 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; - -use deno_ast::ModuleSpecifier; -use deno_cache_dir::npm::NpmCacheDir; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::url::Url; -use deno_error::JsErrorBox; -use deno_npm::npm_rc::ResolvedNpmRc; -use deno_npm::registry::NpmPackageInfo; -use deno_npm::registry::NpmRegistryApi; -use deno_npm::resolution::NpmResolutionSnapshot; -use deno_npm::resolution::PackageReqNotFoundError; -use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; -use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_npm::NpmSystemInfo; -use deno_npm_cache::NpmCacheSetting; -use deno_resolver::npm::managed::NpmResolution; -use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError; -use deno_resolver::npm::ByonmOrManagedNpmResolver; -use deno_resolver::npm::ManagedNpmResolver; -use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::colors; -use deno_runtime::ops::process::NpmProcessStateProvider; -use deno_semver::package::PackageNv; -use deno_semver::package::PackageReq; -use installer::AddPkgReqsResult; -use installer::NpmResolutionInstaller; -use installers::create_npm_fs_installer; -use installers::NpmPackageFsInstaller; -use node_resolver::NpmPackageFolderResolver; - -use super::CliNpmCache; -use super::CliNpmCacheHttpClient; -use super::CliNpmRegistryInfoProvider; -use super::CliNpmResolver; -use super::CliNpmTarballCache; -use super::InnerCliNpmResolverRef; -use crate::args::CliLockfile; -use crate::args::LifecycleScriptsConfig; -use crate::args::NpmInstallDepsProvider; -use crate::args::NpmProcessState; -use crate::args::NpmProcessStateKind; -use crate::args::PackageJsonDepValueParseWithLocationError; -use crate::cache::FastInsecureHasher; -use crate::sys::CliSys; -use crate::util::progress_bar::ProgressBar; -use crate::util::sync::AtomicFlag; - -mod installer; -mod installers; - -pub enum CliNpmResolverManagedSnapshotOption { - ResolveFromLockfile(Arc), - Specified(Option), -} - -pub struct CliManagedNpmResolverCreateOptions { - pub snapshot: CliNpmResolverManagedSnapshotOption, - pub maybe_lockfile: Option>, - pub http_client_provider: Arc, - pub npm_cache_dir: Arc, - pub sys: CliSys, - pub cache_setting: deno_cache_dir::file_fetcher::CacheSetting, - pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, - pub maybe_node_modules_path: Option, - pub npm_system_info: NpmSystemInfo, - pub npm_install_deps_provider: Arc, - pub npmrc: Arc, - pub lifecycle_scripts: LifecycleScriptsConfig, -} - -pub async fn create_managed_npm_resolver_for_lsp( - options: CliManagedNpmResolverCreateOptions, -) -> Arc { - let npm_cache = create_cache(&options); - let http_client = Arc::new(CliNpmCacheHttpClient::new( - options.http_client_provider.clone(), - options.text_only_progress_bar.clone(), - )); - let npm_api = create_api(npm_cache.clone(), http_client.clone(), &options); - // spawn due to the lsp's `Send` requirement - deno_core::unsync::spawn(async move { - let snapshot = match resolve_snapshot(&npm_api, options.snapshot).await { - Ok(snapshot) => snapshot, - Err(err) => { - log::warn!("failed to resolve snapshot: {}", err); - None - } - }; - create_inner( - http_client, - npm_cache, - options.npm_cache_dir, - options.npm_install_deps_provider, - npm_api, - options.sys, - options.text_only_progress_bar, - options.maybe_lockfile, - options.npmrc, - options.maybe_node_modules_path, - options.npm_system_info, - snapshot, - options.lifecycle_scripts, - ) - }) - .await - .unwrap() -} - -pub async fn create_managed_npm_resolver( - options: CliManagedNpmResolverCreateOptions, -) -> Result, AnyError> { - let npm_cache = create_cache(&options); - let http_client = Arc::new(CliNpmCacheHttpClient::new( - options.http_client_provider.clone(), - options.text_only_progress_bar.clone(), - )); - let api = create_api(npm_cache.clone(), http_client.clone(), &options); - let snapshot = resolve_snapshot(&api, options.snapshot).await?; - Ok(create_inner( - http_client, - npm_cache, - options.npm_cache_dir, - options.npm_install_deps_provider, - api, - options.sys, - options.text_only_progress_bar, - options.maybe_lockfile, - options.npmrc, - options.maybe_node_modules_path, - options.npm_system_info, - snapshot, - options.lifecycle_scripts, - )) -} - -#[allow(clippy::too_many_arguments)] -fn create_inner( - http_client: Arc, - npm_cache: Arc, - npm_cache_dir: Arc, - npm_install_deps_provider: Arc, - registry_info_provider: Arc, - sys: CliSys, - text_only_progress_bar: crate::util::progress_bar::ProgressBar, - maybe_lockfile: Option>, - npm_rc: Arc, - node_modules_dir_path: Option, - npm_system_info: NpmSystemInfo, - snapshot: Option, - lifecycle_scripts: LifecycleScriptsConfig, -) -> Arc { - let resolution = Arc::new(NpmResolution::from_serialized(snapshot)); - let tarball_cache = Arc::new(CliNpmTarballCache::new( - npm_cache.clone(), - http_client, - sys.clone(), - npm_rc.clone(), - )); - - let fs_installer = create_npm_fs_installer( - npm_cache.clone(), - &npm_install_deps_provider, - &text_only_progress_bar, - resolution.clone(), - sys.clone(), - tarball_cache.clone(), - node_modules_dir_path.clone(), - npm_system_info.clone(), - lifecycle_scripts.clone(), - ); - let managed_npm_resolver = - Arc::new(ManagedNpmResolver::::new::( - &npm_cache_dir, - &npm_rc, - resolution.clone(), - sys.clone(), - node_modules_dir_path, - )); - Arc::new(ManagedCliNpmResolver::new( - fs_installer, - maybe_lockfile, - managed_npm_resolver, - npm_cache, - npm_cache_dir, - npm_install_deps_provider, - npm_rc, - registry_info_provider, - resolution, - sys, - tarball_cache, - text_only_progress_bar, - npm_system_info, - lifecycle_scripts, - )) -} - -fn create_cache( - options: &CliManagedNpmResolverCreateOptions, -) -> Arc { - Arc::new(CliNpmCache::new( - options.npm_cache_dir.clone(), - options.sys.clone(), - NpmCacheSetting::from_cache_setting(&options.cache_setting), - options.npmrc.clone(), - )) -} - -fn create_api( - cache: Arc, - http_client: Arc, - options: &CliManagedNpmResolverCreateOptions, -) -> Arc { - Arc::new(CliNpmRegistryInfoProvider::new( - cache, - http_client, - options.npmrc.clone(), - )) -} - -async fn resolve_snapshot( - registry_info_provider: &Arc, - snapshot: CliNpmResolverManagedSnapshotOption, -) -> Result, AnyError> { - match snapshot { - CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => { - if !lockfile.overwrite() { - let snapshot = snapshot_from_lockfile( - lockfile.clone(), - ®istry_info_provider.as_npm_registry_api(), - ) - .await - .with_context(|| { - format!("failed reading lockfile '{}'", lockfile.filename.display()) - })?; - Ok(Some(snapshot)) - } else { - Ok(None) - } - } - CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot), - } -} - -async fn snapshot_from_lockfile( - lockfile: Arc, - api: &dyn NpmRegistryApi, -) -> Result { - let (incomplete_snapshot, skip_integrity_check) = { - let lock = lockfile.lock(); - ( - deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?, - lock.overwrite, - ) - }; - let snapshot = deno_npm::resolution::snapshot_from_lockfile( - deno_npm::resolution::SnapshotFromLockfileParams { - incomplete_snapshot, - api, - skip_integrity_check, - }, - ) - .await?; - Ok(snapshot) -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum PackageCaching<'a> { - Only(Cow<'a, [PackageReq]>), - All, -} - -#[derive(Debug, thiserror::Error, deno_error::JsError)] -pub enum ResolvePkgFolderFromDenoModuleError { - #[class(inherit)] - #[error(transparent)] - PackageNvNotFound(#[from] deno_npm::resolution::PackageNvNotFoundError), - #[class(inherit)] - #[error(transparent)] - ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), -} - -/// An npm resolver where the resolution is managed by Deno rather than -/// the user bringing their own node_modules (BYONM) on the file system. -pub struct ManagedCliNpmResolver { - fs_installer: Arc, - maybe_lockfile: Option>, - registry_info_provider: Arc, - managed_npm_resolver: Arc>, - npm_cache: Arc, - npm_cache_dir: Arc, - npm_install_deps_provider: Arc, - npm_rc: Arc, - sys: CliSys, - resolution: Arc, - resolution_installer: NpmResolutionInstaller, - tarball_cache: Arc, - text_only_progress_bar: ProgressBar, - npm_system_info: NpmSystemInfo, - top_level_install_flag: AtomicFlag, - lifecycle_scripts: LifecycleScriptsConfig, -} - -impl std::fmt::Debug for ManagedCliNpmResolver { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ManagedCliNpmResolver") - .field("", &"") - .finish() - } -} - -impl ManagedCliNpmResolver { - #[allow(clippy::too_many_arguments)] - pub fn new( - fs_installer: Arc, - maybe_lockfile: Option>, - managed_npm_resolver: Arc>, - npm_cache: Arc, - npm_cache_dir: Arc, - npm_install_deps_provider: Arc, - npm_rc: Arc, - registry_info_provider: Arc, - resolution: Arc, - sys: CliSys, - tarball_cache: Arc, - text_only_progress_bar: ProgressBar, - npm_system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, - ) -> Self { - let resolution_installer = NpmResolutionInstaller::new( - registry_info_provider.clone(), - resolution.clone(), - maybe_lockfile.clone(), - ); - Self { - fs_installer, - maybe_lockfile, - managed_npm_resolver, - npm_cache, - npm_cache_dir, - npm_install_deps_provider, - npm_rc, - registry_info_provider, - text_only_progress_bar, - resolution, - resolution_installer, - sys, - tarball_cache, - npm_system_info, - top_level_install_flag: Default::default(), - lifecycle_scripts, - } - } - - pub fn resolve_pkg_folder_from_pkg_id( - &self, - pkg_id: &NpmPackageId, - ) -> Result { - self - .managed_npm_resolver - .resolve_pkg_folder_from_pkg_id(pkg_id) - } - - /// Resolves the package id from the provided specifier. - pub fn resolve_pkg_id_from_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result, AnyError> { - let Some(cache_folder_id) = self - .managed_npm_resolver - .resolve_package_cache_folder_id_from_specifier(specifier)? - else { - return Ok(None); - }; - Ok(Some( - self - .resolution - .resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?, - )) - } - - pub fn resolve_pkg_reqs_from_pkg_id( - &self, - id: &NpmPackageId, - ) -> Vec { - self.resolution.resolve_pkg_reqs_from_pkg_id(id) - } - - /// Attempts to get the package size in bytes. - pub fn package_size( - &self, - package_id: &NpmPackageId, - ) -> Result { - let package_folder = self - .managed_npm_resolver - .resolve_pkg_folder_from_pkg_id(package_id)?; - Ok(crate::util::fs::dir_size(&package_folder)?) - } - - pub fn all_system_packages( - &self, - system_info: &NpmSystemInfo, - ) -> Vec { - self.resolution.all_system_packages(system_info) - } - - /// Checks if the provided package req's folder is cached. - pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool { - self - .resolve_pkg_id_from_pkg_req(req) - .ok() - .and_then(|id| { - self - .managed_npm_resolver - .resolve_pkg_folder_from_pkg_id(&id) - .ok() - }) - .map(|folder| folder.exists()) - .unwrap_or(false) - } - - /// Adds package requirements to the resolver and ensures everything is setup. - /// This includes setting up the `node_modules` directory, if applicable. - pub async fn add_and_cache_package_reqs( - &self, - packages: &[PackageReq], - ) -> Result<(), JsErrorBox> { - self - .add_package_reqs_raw( - packages, - Some(PackageCaching::Only(packages.into())), - ) - .await - .dependencies_result - } - - pub async fn add_package_reqs_no_cache( - &self, - packages: &[PackageReq], - ) -> Result<(), JsErrorBox> { - self - .add_package_reqs_raw(packages, None) - .await - .dependencies_result - } - - pub async fn add_package_reqs( - &self, - packages: &[PackageReq], - caching: PackageCaching<'_>, - ) -> Result<(), JsErrorBox> { - self - .add_package_reqs_raw(packages, Some(caching)) - .await - .dependencies_result - } - - pub async fn add_package_reqs_raw<'a>( - &self, - packages: &[PackageReq], - caching: Option>, - ) -> AddPkgReqsResult { - if packages.is_empty() { - return AddPkgReqsResult { - dependencies_result: Ok(()), - results: vec![], - }; - } - - let mut result = self.resolution_installer.add_package_reqs(packages).await; - - if result.dependencies_result.is_ok() { - if let Some(lockfile) = self.maybe_lockfile.as_ref() { - result.dependencies_result = lockfile.error_if_changed(); - } - } - if result.dependencies_result.is_ok() { - if let Some(caching) = caching { - result.dependencies_result = self.cache_packages(caching).await; - } - } - - result - } - - /// Sets package requirements to the resolver, removing old requirements and adding new ones. - /// - /// This will retrieve and resolve package information, but not cache any package files. - pub async fn set_package_reqs( - &self, - packages: &[PackageReq], - ) -> Result<(), AnyError> { - self.resolution_installer.set_package_reqs(packages).await - } - - pub fn snapshot(&self) -> NpmResolutionSnapshot { - self.resolution.snapshot() - } - - pub fn top_package_req_for_name(&self, name: &str) -> Option { - let package_reqs = self.resolution.package_reqs(); - let mut entries = package_reqs - .iter() - .filter(|(_, nv)| nv.name == name) - .collect::>(); - entries.sort_by_key(|(_, nv)| &nv.version); - Some(entries.last()?.0.clone()) - } - - pub fn serialized_valid_snapshot_for_system( - &self, - system_info: &NpmSystemInfo, - ) -> ValidSerializedNpmResolutionSnapshot { - self - .resolution - .serialized_valid_snapshot_for_system(system_info) - } - - pub async fn inject_synthetic_types_node_package( - &self, - ) -> Result<(), JsErrorBox> { - let reqs = &[PackageReq::from_str("@types/node").unwrap()]; - // add and ensure this isn't added to the lockfile - self - .add_package_reqs(reqs, PackageCaching::Only(reqs.into())) - .await?; - - Ok(()) - } - - pub async fn cache_packages( - &self, - caching: PackageCaching<'_>, - ) -> Result<(), JsErrorBox> { - self.fs_installer.cache_packages(caching).await - } - - pub fn resolve_pkg_folder_from_deno_module( - &self, - nv: &PackageNv, - ) -> Result { - let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?; - Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?) - } - - pub fn resolve_pkg_id_from_pkg_req( - &self, - req: &PackageReq, - ) -> Result { - self.resolution.resolve_pkg_id_from_pkg_req(req) - } - - pub fn ensure_no_pkg_json_dep_errors( - &self, - ) -> Result<(), Box> { - for err in self.npm_install_deps_provider.pkg_json_dep_errors() { - match err.source.as_kind() { - deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => { - return Err(Box::new(err.clone())); - } - deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported { - .. - } => { - // only warn for this one - log::warn!( - "{} {}\n at {}", - colors::yellow("Warning"), - err.source, - err.location, - ) - } - } - } - Ok(()) - } - - /// Ensures that the top level `package.json` dependencies are installed. - /// This may set up the `node_modules` directory. - /// - /// Returns `true` if the top level packages are already installed. A - /// return value of `false` means that new packages were added to the NPM resolution. - pub async fn ensure_top_level_package_json_install( - &self, - ) -> Result { - if !self.top_level_install_flag.raise() { - return Ok(true); // already did this - } - - let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); - if pkg_json_remote_pkgs.is_empty() { - return Ok(true); - } - - // check if something needs resolving before bothering to load all - // the package information (which is slow) - if pkg_json_remote_pkgs.iter().all(|pkg| { - self - .resolution - .resolve_pkg_id_from_pkg_req(&pkg.req) - .is_ok() - }) { - log::debug!( - "All package.json deps resolvable. Skipping top level install." - ); - return Ok(true); // everything is already resolvable - } - - let pkg_reqs = pkg_json_remote_pkgs - .iter() - .map(|pkg| pkg.req.clone()) - .collect::>(); - self.add_package_reqs_no_cache(&pkg_reqs).await?; - - Ok(false) - } - - pub async fn cache_package_info( - &self, - package_name: &str, - ) -> Result, AnyError> { - // this will internally cache the package information - self - .registry_info_provider - .package_info(package_name) - .await - .map_err(|err| err.into()) - } - - pub fn maybe_node_modules_path(&self) -> Option<&Path> { - self.managed_npm_resolver.node_modules_path() - } - - pub fn global_cache_root_path(&self) -> &Path { - self.npm_cache_dir.root_dir() - } - - pub fn global_cache_root_url(&self) -> &Url { - self.npm_cache_dir.root_dir_url() - } -} - -fn npm_process_state( - snapshot: ValidSerializedNpmResolutionSnapshot, - node_modules_path: Option<&Path>, -) -> String { - serde_json::to_string(&NpmProcessState { - kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()), - local_node_modules_path: node_modules_path - .map(|p| p.to_string_lossy().to_string()), - }) - .unwrap() -} - -impl NpmProcessStateProvider for ManagedCliNpmResolver { - fn get_npm_process_state(&self) -> String { - npm_process_state( - self.resolution.serialized_valid_snapshot(), - self.managed_npm_resolver.node_modules_path(), - ) - } -} - -impl CliNpmResolver for ManagedCliNpmResolver { - fn into_npm_pkg_folder_resolver( - self: Arc, - ) -> Arc { - self.managed_npm_resolver.clone() - } - - fn into_process_state_provider( - self: Arc, - ) -> Arc { - self - } - - fn into_byonm_or_managed( - self: Arc, - ) -> ByonmOrManagedNpmResolver { - ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone()) - } - - fn clone_snapshotted(&self) -> Arc { - // create a new snapshotted npm resolution and resolver - let npm_resolution = - Arc::new(NpmResolution::new(self.resolution.snapshot())); - - Arc::new(ManagedCliNpmResolver::new( - create_npm_fs_installer( - self.npm_cache.clone(), - &self.npm_install_deps_provider, - &self.text_only_progress_bar, - npm_resolution.clone(), - self.sys.clone(), - self.tarball_cache.clone(), - self.root_node_modules_path().map(ToOwned::to_owned), - self.npm_system_info.clone(), - self.lifecycle_scripts.clone(), - ), - self.maybe_lockfile.clone(), - Arc::new(ManagedNpmResolver::::new::( - &self.npm_cache_dir, - &self.npm_rc, - npm_resolution.clone(), - self.sys.clone(), - self.root_node_modules_path().map(ToOwned::to_owned), - )), - self.npm_cache.clone(), - self.npm_cache_dir.clone(), - self.npm_install_deps_provider.clone(), - self.npm_rc.clone(), - self.registry_info_provider.clone(), - npm_resolution, - self.sys.clone(), - self.tarball_cache.clone(), - self.text_only_progress_bar.clone(), - self.npm_system_info.clone(), - self.lifecycle_scripts.clone(), - )) - } - - fn as_inner(&self) -> InnerCliNpmResolverRef { - InnerCliNpmResolverRef::Managed(self) - } - - fn root_node_modules_path(&self) -> Option<&Path> { - self.managed_npm_resolver.node_modules_path() - } - - fn check_state_hash(&self) -> Option { - // We could go further and check all the individual - // npm packages, but that's probably overkill. - let mut package_reqs = self - .resolution - .package_reqs() - .into_iter() - .collect::>(); - package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism - let mut hasher = FastInsecureHasher::new_without_deno_version(); - // ensure the cache gets busted when turning nodeModulesDir on or off - // as this could cause changes in resolution - hasher - .write_hashable(self.managed_npm_resolver.node_modules_path().is_some()); - for (pkg_req, pkg_nv) in package_reqs { - hasher.write_hashable(&pkg_req); - hasher.write_hashable(&pkg_nv); - } - Some(hasher.finish()) - } - - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - referrer: &Url, - ) -> Result { - self - .managed_npm_resolver - .resolve_pkg_folder_from_deno_module_req(req, referrer) - .map_err(ResolvePkgFolderFromDenoReqError::Managed) - } -} diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 052a98e6cf..6ad7ad610e 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -1,6 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. mod byonm; +pub mod installer; mod managed; mod permission_checker; @@ -9,7 +10,6 @@ use std::path::PathBuf; use std::sync::Arc; use dashmap::DashMap; -use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; use deno_error::JsErrorBox; @@ -30,8 +30,8 @@ pub use self::byonm::CliByonmNpmResolverCreateOptions; pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::ManagedCliNpmResolver; -pub use self::managed::PackageCaching; -pub use self::managed::ResolvePkgFolderFromDenoModuleError; +pub use self::managed::NpmResolutionInitializer; +pub use self::managed::ResolveSnapshotError; pub use self::permission_checker::NpmRegistryReadPermissionChecker; pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode; use crate::file_fetcher::CliFileFetcher; @@ -109,28 +109,16 @@ pub enum CliNpmResolverCreateOptions { Byonm(CliByonmNpmResolverCreateOptions), } -pub async fn create_cli_npm_resolver_for_lsp( +pub fn create_cli_npm_resolver( options: CliNpmResolverCreateOptions, ) -> Arc { use CliNpmResolverCreateOptions::*; match options { - Managed(options) => { - managed::create_managed_npm_resolver_for_lsp(options).await - } + Managed(options) => managed::create_managed_npm_resolver(options), Byonm(options) => Arc::new(ByonmNpmResolver::new(options)), } } -pub async fn create_cli_npm_resolver( - options: CliNpmResolverCreateOptions, -) -> Result, AnyError> { - use CliNpmResolverCreateOptions::*; - match options { - Managed(options) => managed::create_managed_npm_resolver(options).await, - Byonm(options) => Ok(Arc::new(ByonmNpmResolver::new(options))), - } -} - pub enum InnerCliNpmResolverRef<'a> { Managed(&'a ManagedCliNpmResolver), #[allow(dead_code)] diff --git a/cli/resolver.rs b/cli/resolver.rs index 1d12d5f8b7..2f3d42e9e1 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -33,8 +33,8 @@ use thiserror::Error; use crate::args::NpmCachingStrategy; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::node::CliNodeCodeTranslator; -use crate::npm::CliNpmResolver; -use crate::npm::InnerCliNpmResolverRef; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::sys::CliSys; use crate::util::sync::AtomicFlag; use crate::util::text_encoding::from_utf8_lossy_cow; @@ -164,48 +164,30 @@ impl NpmModuleLoader { } } -pub struct CliResolverOptions { - pub deno_resolver: Arc, - pub npm_resolver: Option>, - pub bare_node_builtins_enabled: bool, -} +#[derive(Debug, Default)] +pub struct FoundPackageJsonDepFlag(AtomicFlag); /// A resolver that takes care of resolution, taking into account loaded /// import map, JSX settings. #[derive(Debug)] pub struct CliResolver { deno_resolver: Arc, - npm_resolver: Option>, - found_package_json_dep_flag: AtomicFlag, - bare_node_builtins_enabled: bool, + found_package_json_dep_flag: Arc, warned_pkgs: DashSet, } impl CliResolver { - pub fn new(options: CliResolverOptions) -> Self { + pub fn new( + deno_resolver: Arc, + found_package_json_dep_flag: Arc, + ) -> Self { Self { - deno_resolver: options.deno_resolver, - npm_resolver: options.npm_resolver, - found_package_json_dep_flag: Default::default(), - bare_node_builtins_enabled: options.bare_node_builtins_enabled, + deno_resolver, + found_package_json_dep_flag, warned_pkgs: Default::default(), } } - // todo(dsherret): move this off CliResolver as CliResolver is acting - // like a factory by doing this (it's beyond its responsibility) - pub fn create_graph_npm_resolver( - &self, - npm_caching: NpmCachingStrategy, - ) -> WorkerCliNpmGraphResolver { - WorkerCliNpmGraphResolver { - npm_resolver: self.npm_resolver.as_ref(), - found_package_json_dep_flag: &self.found_package_json_dep_flag, - bare_node_builtins_enabled: self.bare_node_builtins_enabled, - npm_caching, - } - } - pub fn resolve( &self, raw_specifier: &str, @@ -233,7 +215,7 @@ impl CliResolver { if resolution.found_package_json_dep { // mark that we need to do an "npm install" later - self.found_package_json_dep_flag.raise(); + self.found_package_json_dep_flag.0.raise(); } if let Some(diagnostic) = resolution.maybe_diagnostic { @@ -260,15 +242,31 @@ impl CliResolver { } #[derive(Debug)] -pub struct WorkerCliNpmGraphResolver<'a> { - npm_resolver: Option<&'a Arc>, - found_package_json_dep_flag: &'a AtomicFlag, +pub struct CliNpmGraphResolver { + npm_installer: Option>, + found_package_json_dep_flag: Arc, bare_node_builtins_enabled: bool, npm_caching: NpmCachingStrategy, } +impl CliNpmGraphResolver { + pub fn new( + npm_installer: Option>, + found_package_json_dep_flag: Arc, + bare_node_builtins_enabled: bool, + npm_caching: NpmCachingStrategy, + ) -> Self { + Self { + npm_installer, + found_package_json_dep_flag, + bare_node_builtins_enabled, + npm_caching, + } + } +} + #[async_trait(?Send)] -impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { +impl deno_graph::source::NpmResolver for CliNpmGraphResolver { fn resolve_builtin_node_module( &self, specifier: &ModuleSpecifier, @@ -298,17 +296,12 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { } fn load_and_cache_npm_package_info(&self, package_name: &str) { - match self.npm_resolver { - Some(npm_resolver) if npm_resolver.as_managed().is_some() => { - let npm_resolver = npm_resolver.clone(); - let package_name = package_name.to_string(); - deno_core::unsync::spawn(async move { - if let Some(managed) = npm_resolver.as_managed() { - let _ignore = managed.cache_package_info(&package_name).await; - } - }); - } - _ => {} + if let Some(npm_installer) = &self.npm_installer { + let npm_installer = npm_installer.clone(); + let package_name = package_name.to_string(); + deno_core::unsync::spawn(async move { + let _ignore = npm_installer.cache_package_info(&package_name).await; + }); } } @@ -316,17 +309,11 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { &self, package_reqs: &[PackageReq], ) -> NpmResolvePkgReqsResult { - match &self.npm_resolver { - Some(npm_resolver) => { - let npm_resolver = match npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(npm_resolver) => npm_resolver, - // if we are using byonm, then this should never be called because - // we don't use deno_graph's npm resolution in this case - InnerCliNpmResolverRef::Byonm(_) => unreachable!(), - }; - - let top_level_result = if self.found_package_json_dep_flag.is_raised() { - npm_resolver + match &self.npm_installer { + Some(npm_installer) => { + let top_level_result = if self.found_package_json_dep_flag.0.is_raised() + { + npm_installer .ensure_top_level_package_json_install() .await .map(|_| ()) @@ -334,15 +321,13 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { Ok(()) }; - let result = npm_resolver + let result = npm_installer .add_package_reqs_raw( package_reqs, match self.npm_caching { - NpmCachingStrategy::Eager => { - Some(crate::npm::PackageCaching::All) - } + NpmCachingStrategy::Eager => Some(PackageCaching::All), NpmCachingStrategy::Lazy => { - Some(crate::npm::PackageCaching::Only(package_reqs.into())) + Some(PackageCaching::Only(package_reqs.into())) } NpmCachingStrategy::Manual => None, }, diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index ff38912a89..4ca82f6b7d 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -37,10 +37,12 @@ use deno_core::ResolutionKind; use deno_core::SourceCodeCacheInfo; use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; +use deno_npm::resolution::NpmResolutionSnapshot; use deno_package_json::PackageJsonDepValue; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::create_in_npm_pkg_checker; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; use deno_runtime::deno_fs; @@ -91,6 +93,7 @@ use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionCheckerMode; +use crate::npm::NpmResolutionInitializer; use crate::resolver::CjsTracker; use crate::resolver::CliNpmReqResolver; use crate::resolver::NpmModuleLoader; @@ -687,18 +690,12 @@ pub async fn run( ca_data: metadata.ca_data.map(CaData::Bytes), cell: Default::default(), }); - let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly); - let http_client_provider = Arc::new(HttpClientProvider::new( - Some(root_cert_store_provider.clone()), - metadata.unsafely_ignore_certificate_errors.clone(), - )); // use a dummy npm registry url let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap(); let root_dir_url = Arc::new(ModuleSpecifier::from_directory_path(&root_path).unwrap()); let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap(); let npm_global_cache_dir = root_path.join(".deno_compile_node_modules"); - let cache_setting = CacheSetting::Only; let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone())); let npm_registry_permission_checker = { let mode = match &metadata.node_modules { @@ -743,29 +740,19 @@ pub async fn run( maybe_node_modules_path: maybe_node_modules_path.as_deref(), }, )); + let npm_resolution = + Arc::new(NpmResolutionCell::new(NpmResolutionSnapshot::new(snapshot))); let npm_resolver = create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { - snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some( - snapshot, - )), - maybe_lockfile: None, - http_client_provider: http_client_provider.clone(), + npm_resolution, npm_cache_dir, - npm_install_deps_provider: Arc::new( - // this is only used for installing packages, which isn't necessary with deno compile - NpmInstallDepsProvider::empty(), - ), sys: sys.clone(), - text_only_progress_bar: progress_bar, - cache_setting, maybe_node_modules_path, npm_system_info: Default::default(), npmrc, - lifecycle_scripts: Default::default(), }, - )) - .await?; + )); (in_npm_pkg_checker, npm_resolver) } Some(binary::NodeModules::Byonm { @@ -781,8 +768,7 @@ pub async fn run( pkg_json_resolver: pkg_json_resolver.clone(), root_node_modules_dir, }), - ) - .await?; + ); (in_npm_pkg_checker, npm_resolver) } None => { @@ -801,27 +787,18 @@ pub async fn run( maybe_node_modules_path: None, }, )); + let npm_resolution = Arc::new(NpmResolutionCell::default()); let npm_resolver = create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { - snapshot: CliNpmResolverManagedSnapshotOption::Specified(None), - http_client_provider: http_client_provider.clone(), - npm_install_deps_provider: Arc::new( - // this is only used for installing packages, which isn't necessary with deno compile - NpmInstallDepsProvider::empty(), - ), + npm_resolution, sys: sys.clone(), - cache_setting, - text_only_progress_bar: progress_bar, npm_cache_dir, - maybe_lockfile: None, maybe_node_modules_path: None, npm_system_info: Default::default(), npmrc: create_default_npmrc(), - lifecycle_scripts: Default::default(), }, - )) - .await?; + )); (in_npm_pkg_checker, npm_resolver) } }; @@ -995,6 +972,7 @@ pub async fn run( None, Box::new(module_loader_factory), node_resolver, + None, npm_resolver, pkg_json_resolver, root_cert_store_provider, diff --git a/cli/tools/check.rs b/cli/tools/check.rs index 53fd5c5db9..c3a285a9b2 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -34,6 +34,7 @@ use crate::graph_util::maybe_additional_sloppy_imports_message; use crate::graph_util::BuildFastCheckGraphOptions; use crate::graph_util::ModuleGraphBuilder; use crate::node::CliNodeResolver; +use crate::npm::installer::NpmInstaller; use crate::npm::CliNpmResolver; use crate::sys::CliSys; use crate::tsc; @@ -109,6 +110,7 @@ pub struct TypeChecker { cjs_tracker: Arc, cli_options: Arc, module_graph_builder: Arc, + npm_installer: Option>, node_resolver: Arc, npm_resolver: Arc, sys: CliSys, @@ -136,12 +138,14 @@ pub enum CheckError { } impl TypeChecker { + #[allow(clippy::too_many_arguments)] pub fn new( caches: Arc, cjs_tracker: Arc, cli_options: Arc, module_graph_builder: Arc, node_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, sys: CliSys, ) -> Self { @@ -151,6 +155,7 @@ impl TypeChecker { cli_options, module_graph_builder, node_resolver, + npm_installer, npm_resolver, sys, } @@ -191,9 +196,9 @@ impl TypeChecker { // node built-in specifiers use the @types/node package to determine // types, so inject that now (the caller should do this after the lockfile // has been written) - if let Some(npm_resolver) = self.npm_resolver.as_managed() { + if let Some(npm_installer) = &self.npm_installer { if graph.has_node_specifier { - npm_resolver.inject_synthetic_types_node_package().await?; + npm_installer.inject_synthetic_types_node_package().await?; } } diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 81b0af0a66..50faeda7d3 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -386,8 +386,11 @@ impl NpmInfo { npm_snapshot: &'a NpmResolutionSnapshot, ) { self.packages.insert(package.id.clone(), package.clone()); - if let Ok(size) = npm_resolver.package_size(&package.id) { - self.package_sizes.insert(package.id.clone(), size); + if let Ok(folder) = npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id) + { + if let Ok(size) = crate::util::fs::dir_size(&folder) { + self.package_sizes.insert(package.id.clone(), size); + } } for id in package.dependencies.values() { if !self.packages.contains_key(id) { diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index ff5744c07a..596d087589 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -300,8 +300,8 @@ async fn install_local( InstallFlagsLocal::TopLevel => { let factory = CliFactory::from_flags(flags); // surface any errors in the package.json - if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() { - npm_resolver.ensure_no_pkg_json_dep_errors()?; + if let Some(npm_installer) = factory.npm_installer_if_managed()? { + npm_installer.ensure_no_pkg_json_dep_errors()?; } crate::tools::registry::cache_top_level_deps(&factory, None).await?; diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs index 78b7675420..67c604118c 100644 --- a/cli/tools/jupyter/mod.rs +++ b/cli/tools/jupyter/mod.rs @@ -67,7 +67,7 @@ pub async fn kernel( // TODO(bartlomieju): should we run with all permissions? let permissions = PermissionsContainer::allow_all(factory.permission_desc_parser()?.clone()); - let npm_resolver = factory.npm_resolver().await?.clone(); + let npm_installer = factory.npm_installer_if_managed()?.cloned(); let resolver = factory.resolver().await?.clone(); let worker_factory = factory.create_cli_main_worker_factory().await?; let (stdio_tx, stdio_rx) = mpsc::unbounded_channel(); @@ -115,7 +115,7 @@ pub async fn kernel( let worker = worker.into_main_worker(); let mut repl_session = repl::ReplSession::initialize( cli_options, - npm_resolver, + npm_installer, resolver, worker, main_module, diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 2b1266bafb..0b27b1a3e3 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -861,10 +861,8 @@ async fn npm_install_after_modification( // make a new CliFactory to pick up the updated config file let cli_factory = CliFactory::from_flags(flags); // surface any errors in the package.json - let npm_resolver = cli_factory.npm_resolver().await?; - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver.ensure_no_pkg_json_dep_errors()?; - } + let npm_installer = cli_factory.npm_installer()?; + npm_installer.ensure_no_pkg_json_dep_errors()?; // npm install cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).await?; diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index 3137939cea..5683a30cc8 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -12,16 +12,19 @@ use crate::factory::CliFactory; use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; use crate::graph_util::CreateGraphOptions; +use crate::npm::installer::PackageCaching; pub async fn cache_top_level_deps( // todo(dsherret): don't pass the factory into this function. Instead use ctor deps factory: &CliFactory, jsr_resolver: Option>, ) -> Result<(), AnyError> { - let npm_resolver = factory.npm_resolver().await?; + let npm_installer = factory.npm_installer_if_managed()?; let cli_options = factory.cli_options()?; - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; + if let Some(npm_installer) = &npm_installer { + npm_installer + .ensure_top_level_package_json_install() + .await?; if let Some(lockfile) = cli_options.maybe_lockfile() { lockfile.error_if_changed()?; } @@ -138,10 +141,8 @@ pub async fn cache_top_level_deps( maybe_graph_error = graph_builder.graph_roots_valid(graph, &roots); } - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) - .await?; + if let Some(npm_installer) = &npm_installer { + npm_installer.cache_packages(PackageCaching::All).await?; } maybe_graph_error?; diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index 708f95f987..1dbb464dbe 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -42,6 +42,7 @@ use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; use crate::jsr::JsrFetchResolver; use crate::module_loader::ModuleLoadPreparer; +use crate::npm::installer::NpmInstaller; use crate::npm::CliNpmResolver; use crate::npm::NpmFetchResolver; use crate::util::sync::AtomicFlag; @@ -451,6 +452,7 @@ pub struct DepManager { pub(crate) jsr_fetch_resolver: Arc, pub(crate) npm_fetch_resolver: Arc, npm_resolver: Arc, + npm_installer: Arc, permissions_container: PermissionsContainer, main_module_graph_container: Arc, lockfile: Option>, @@ -460,6 +462,7 @@ pub struct DepManagerArgs { pub module_load_preparer: Arc, pub jsr_fetch_resolver: Arc, pub npm_fetch_resolver: Arc, + pub npm_installer: Arc, pub npm_resolver: Arc, pub permissions_container: PermissionsContainer, pub main_module_graph_container: Arc, @@ -477,6 +480,7 @@ impl DepManager { module_load_preparer, jsr_fetch_resolver, npm_fetch_resolver, + npm_installer, npm_resolver, permissions_container, main_module_graph_container, @@ -490,6 +494,7 @@ impl DepManager { dependencies_resolved: AtomicFlag::lowered(), module_load_preparer, npm_fetch_resolver, + npm_installer, npm_resolver, permissions_container, main_module_graph_container, @@ -556,7 +561,10 @@ impl DepManager { return Ok(()); } - npm_resolver.ensure_top_level_package_json_install().await?; + self + .npm_installer + .ensure_top_level_package_json_install() + .await?; let mut roots = Vec::new(); let mut info_futures = FuturesUnordered::new(); for dep in &self.deps { diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index e7fc88c31f..939c30b5c1 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -445,6 +445,7 @@ async fn dep_manager_args( jsr_fetch_resolver, npm_fetch_resolver, npm_resolver: factory.npm_resolver().await?.clone(), + npm_installer: factory.npm_installer()?.clone(), permissions_container: factory.root_permissions_container()?.clone(), main_module_graph_container: factory .main_module_graph_container() diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index 1bc6a2f9ea..05e2f320eb 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -164,7 +164,7 @@ pub async fn run( let cli_options = factory.cli_options()?; let main_module = cli_options.resolve_main_module()?; let permissions = factory.root_permissions_container()?; - let npm_resolver = factory.npm_resolver().await?.clone(); + let npm_installer = factory.npm_installer_if_managed()?.cloned(); let resolver = factory.resolver().await?.clone(); let file_fetcher = factory.file_fetcher()?; let worker_factory = factory.create_cli_main_worker_factory().await?; @@ -187,7 +187,7 @@ pub async fn run( let worker = worker.into_main_worker(); let session = ReplSession::initialize( cli_options, - npm_resolver, + npm_installer, resolver, worker, main_module.clone(), diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs index ed9dd61e2d..5ea4523c48 100644 --- a/cli/tools/repl/session.rs +++ b/cli/tools/repl/session.rs @@ -45,7 +45,7 @@ use crate::args::CliOptions; use crate::cdp; use crate::colors; use crate::lsp::ReplLanguageServer; -use crate::npm::CliNpmResolver; +use crate::npm::installer::NpmInstaller; use crate::resolver::CliResolver; use crate::tools::test::report_tests; use crate::tools::test::reporters::PrettyTestReporter; @@ -181,7 +181,7 @@ struct ReplJsxState { } pub struct ReplSession { - npm_resolver: Arc, + npm_installer: Option>, resolver: Arc, pub worker: MainWorker, session: LocalInspectorSession, @@ -200,7 +200,7 @@ pub struct ReplSession { impl ReplSession { pub async fn initialize( cli_options: &CliOptions, - npm_resolver: Arc, + npm_installer: Option>, resolver: Arc, mut worker: MainWorker, main_module: ModuleSpecifier, @@ -265,7 +265,7 @@ impl ReplSession { )?; let experimental_decorators = transpile_options.use_ts_decorators; let mut repl_session = ReplSession { - npm_resolver, + npm_installer, resolver, worker, session, @@ -704,8 +704,8 @@ impl ReplSession { &mut self, program: &swc_ast::Program, ) -> Result<(), AnyError> { - let Some(npm_resolver) = self.npm_resolver.as_managed() else { - return Ok(()); // don't auto-install for byonm + let Some(npm_installer) = &self.npm_installer else { + return Ok(()); }; let mut collector = ImportCollector::new(); @@ -737,13 +737,13 @@ impl ReplSession { let has_node_specifier = resolved_imports.iter().any(|url| url.scheme() == "node"); if !npm_imports.is_empty() || has_node_specifier { - npm_resolver + npm_installer .add_and_cache_package_reqs(&npm_imports) .await?; // prevent messages in the repl about @types/node not being cached if has_node_specifier { - npm_resolver.inject_synthetic_types_node_package().await?; + npm_installer.inject_synthetic_types_node_package().await?; } } Ok(()) diff --git a/cli/tools/run/mod.rs b/cli/tools/run/mod.rs index a10ce32947..ecf1bd52f3 100644 --- a/cli/tools/run/mod.rs +++ b/cli/tools/run/mod.rs @@ -12,6 +12,7 @@ use crate::args::EvalFlags; use crate::args::Flags; use crate::args::WatchFlagsWithPaths; use crate::factory::CliFactory; +use crate::npm::installer::PackageCaching; use crate::util; use crate::util::file_watcher::WatcherRestartMode; @@ -202,18 +203,17 @@ pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> { // ensure an "npm install" is done if the user has explicitly // opted into using a managed node_modules directory if cli_options.node_modules_dir()? == Some(NodeModulesDirMode::Auto) { - if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() { - let already_done = - npm_resolver.ensure_top_level_package_json_install().await?; + if let Some(npm_installer) = factory.npm_installer_if_managed()? { + let already_done = npm_installer + .ensure_top_level_package_json_install() + .await?; if !already_done && matches!( cli_options.default_npm_caching_strategy(), crate::graph_util::NpmCachingStrategy::Eager ) { - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) - .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } } } diff --git a/cli/tools/task.rs b/cli/tools/task.rs index 85834a0bfc..d6d87dd45e 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -36,6 +36,8 @@ use crate::args::TaskFlags; use crate::colors; use crate::factory::CliFactory; use crate::node::CliNodeResolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; use crate::task_runner; use crate::task_runner::run_future_forwarding_signals; @@ -203,6 +205,7 @@ pub async fn execute_script( }] }; + let npm_installer = factory.npm_installer_if_managed()?; let npm_resolver = factory.npm_resolver().await?; let node_resolver = factory.node_resolver().await?; let env_vars = task_runner::real_env_vars(); @@ -216,6 +219,7 @@ pub async fn execute_script( let task_runner = TaskRunner { task_flags: &task_flags, + npm_installer: npm_installer.map(|n| n.as_ref()), npm_resolver: npm_resolver.as_ref(), node_resolver: node_resolver.as_ref(), env_vars, @@ -266,6 +270,7 @@ struct RunSingleOptions<'a> { struct TaskRunner<'a> { task_flags: &'a TaskFlags, + npm_installer: Option<&'a NpmInstaller>, npm_resolver: &'a dyn CliNpmResolver, node_resolver: &'a CliNodeResolver, env_vars: HashMap, @@ -458,11 +463,11 @@ impl<'a> TaskRunner<'a> { return Ok(0); }; - if let Some(npm_resolver) = self.npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) + if let Some(npm_installer) = self.npm_installer { + npm_installer + .ensure_top_level_package_json_install() .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } let cwd = match &self.task_flags.cwd { @@ -497,11 +502,11 @@ impl<'a> TaskRunner<'a> { argv: &[String], ) -> Result { // ensure the npm packages are installed if using a managed resolver - if let Some(npm_resolver) = self.npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) + if let Some(npm_installer) = self.npm_installer { + npm_installer + .ensure_top_level_package_json_install() .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } let cwd = match &self.task_flags.cwd { diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index f645a5f6b8..b6cf691c38 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -28,6 +28,7 @@ use deno_graph::GraphKind; use deno_graph::Module; use deno_graph::ModuleGraph; use deno_graph::ResolutionResolved; +use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_semver::npm::NpmPackageReqReference; use node_resolver::errors::NodeJsErrorCode; @@ -709,9 +710,7 @@ pub enum ResolveError { PackageSubpathResolve(PackageSubpathResolveError), #[class(inherit)] #[error("{0}")] - ResolvePkgFolderFromDenoModule( - #[from] crate::npm::ResolvePkgFolderFromDenoModuleError, - ), + ResolvePkgFolderFromDenoModule(#[from] ResolvePkgFolderFromDenoModuleError), #[class(inherit)] #[error("{0}")] ResolveNonGraphSpecifierTypes(#[from] ResolveNonGraphSpecifierTypesError), diff --git a/cli/worker.rs b/cli/worker.rs index eee89d663c..d93c6c5f40 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -54,6 +54,8 @@ use crate::args::NpmCachingStrategy; use crate::args::StorageKeyResolver; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; use crate::sys::CliSys; use crate::util::checksum; @@ -148,6 +150,7 @@ struct SharedWorkerState { maybe_lockfile: Option>, module_loader_factory: Box, node_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, pkg_json_resolver: Arc, root_cert_store_provider: Arc, @@ -423,6 +426,7 @@ impl CliMainWorkerFactory { maybe_lockfile: Option>, module_loader_factory: Box, node_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, pkg_json_resolver: Arc, root_cert_store_provider: Arc, @@ -447,6 +451,7 @@ impl CliMainWorkerFactory { maybe_lockfile, module_loader_factory, node_resolver, + npm_installer, npm_resolver, pkg_json_resolver, root_cert_store_provider, @@ -496,18 +501,18 @@ impl CliMainWorkerFactory { let main_module = if let Ok(package_ref) = NpmPackageReqReference::from_specifier(&main_module) { - if let Some(npm_resolver) = shared.npm_resolver.as_managed() { + if let Some(npm_installer) = &shared.npm_installer { let reqs = &[package_ref.req().clone()]; - npm_resolver + npm_installer .add_package_reqs( reqs, if matches!( shared.default_npm_caching_strategy, NpmCachingStrategy::Lazy ) { - crate::npm::PackageCaching::Only(reqs.into()) + PackageCaching::Only(reqs.into()) } else { - crate::npm::PackageCaching::All + PackageCaching::All }, ) .await?; diff --git a/resolvers/deno/npm/managed/global.rs b/resolvers/deno/npm/managed/global.rs index cffe68a30e..d16234d8f4 100644 --- a/resolvers/deno/npm/managed/global.rs +++ b/resolvers/deno/npm/managed/global.rs @@ -16,7 +16,7 @@ use node_resolver::errors::ReferrerNotFoundError; use node_resolver::NpmPackageFolderResolver; use url::Url; -use super::resolution::NpmResolutionRc; +use super::resolution::NpmResolutionCellRc; use super::NpmCacheDirRc; use super::NpmPackageFsResolver; use crate::ResolvedNpmRcRc; @@ -26,14 +26,14 @@ use crate::ResolvedNpmRcRc; pub struct GlobalNpmPackageResolver { cache: NpmCacheDirRc, npm_rc: ResolvedNpmRcRc, - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, } impl GlobalNpmPackageResolver { pub fn new( cache: NpmCacheDirRc, npm_rc: ResolvedNpmRcRc, - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, ) -> Self { Self { cache, diff --git a/resolvers/deno/npm/managed/local.rs b/resolvers/deno/npm/managed/local.rs index 3aa1275d66..72b0b0d451 100644 --- a/resolvers/deno/npm/managed/local.rs +++ b/resolvers/deno/npm/managed/local.rs @@ -19,7 +19,7 @@ use sys_traits::FsCanonicalize; use sys_traits::FsMetadata; use url::Url; -use super::resolution::NpmResolutionRc; +use super::resolution::NpmResolutionCellRc; use super::NpmPackageFsResolver; use crate::npm::local::get_package_folder_id_folder_name_from_parts; use crate::npm::local::get_package_folder_id_from_folder_name; @@ -32,7 +32,7 @@ use crate::sync::MaybeSync; pub struct LocalNpmPackageResolver< TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync, > { - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, sys: TSys, root_node_modules_path: PathBuf, root_node_modules_url: Url, @@ -43,7 +43,7 @@ impl { #[allow(clippy::too_many_arguments)] pub fn new( - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, sys: TSys, node_modules_folder: PathBuf, ) -> Self { diff --git a/resolvers/deno/npm/managed/mod.rs b/resolvers/deno/npm/managed/mod.rs index a9775ee374..62147b2ac3 100644 --- a/resolvers/deno/npm/managed/mod.rs +++ b/resolvers/deno/npm/managed/mod.rs @@ -8,10 +8,13 @@ mod resolution; use std::path::Path; use std::path::PathBuf; +use deno_npm::resolution::PackageCacheFolderIdNotFoundError; +use deno_npm::resolution::PackageNvNotFoundError; use deno_npm::resolution::PackageReqNotFoundError; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_path_util::fs::canonicalize_path_maybe_not_exists; +use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; @@ -23,14 +26,24 @@ use self::common::NpmPackageFsResolver; use self::common::NpmPackageFsResolverRc; use self::global::GlobalNpmPackageResolver; use self::local::LocalNpmPackageResolver; -pub use self::resolution::NpmResolution; -pub use self::resolution::NpmResolutionRc; +pub use self::resolution::NpmResolutionCell; +pub use self::resolution::NpmResolutionCellRc; use crate::sync::new_rc; use crate::sync::MaybeSend; use crate::sync::MaybeSync; use crate::NpmCacheDirRc; use crate::ResolvedNpmRcRc; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolvePkgFolderFromDenoModuleError { + #[class(inherit)] + #[error(transparent)] + PackageNvNotFound(#[from] PackageNvNotFoundError), + #[class(inherit)] + #[error(transparent)] + ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), +} + #[derive(Debug, thiserror::Error, deno_error::JsError)] #[error(transparent)] pub enum ResolvePkgFolderFromPkgIdError { @@ -66,6 +79,16 @@ pub enum ManagedResolvePkgFolderFromDenoReqError { ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolvePkgIdFromSpecifierError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + NotFound(#[from] PackageCacheFolderIdNotFoundError), +} + #[allow(clippy::disallowed_types)] pub type ManagedNpmResolverRc = crate::sync::MaybeArc>; @@ -73,7 +96,7 @@ pub type ManagedNpmResolverRc = #[derive(Debug)] pub struct ManagedNpmResolver { fs_resolver: NpmPackageFsResolverRc, - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, sys: TSys, } @@ -89,7 +112,7 @@ impl ManagedNpmResolver { >( npm_cache_dir: &NpmCacheDirRc, npm_rc: &ResolvedNpmRcRc, - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, sys: TCreateSys, maybe_node_modules_path: Option, ) -> ManagedNpmResolver { @@ -144,6 +167,14 @@ impl ManagedNpmResolver { Ok(path) } + pub fn resolve_pkg_folder_from_deno_module( + &self, + nv: &PackageNv, + ) -> Result { + let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?; + Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?) + } + pub fn resolve_pkg_folder_from_deno_module_req( &self, req: &PackageReq, @@ -162,6 +193,24 @@ impl ManagedNpmResolver { .fs_resolver .resolve_package_cache_folder_id_from_specifier(specifier) } + + /// Resolves the package id from the provided specifier. + pub fn resolve_pkg_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, ResolvePkgIdFromSpecifierError> { + let Some(cache_folder_id) = self + .fs_resolver + .resolve_package_cache_folder_id_from_specifier(specifier)? + else { + return Ok(None); + }; + Ok(Some( + self + .resolution + .resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?, + )) + } } impl diff --git a/resolvers/deno/npm/managed/resolution.rs b/resolvers/deno/npm/managed/resolution.rs index 8ddd1ac70f..08dabf5a76 100644 --- a/resolvers/deno/npm/managed/resolution.rs +++ b/resolvers/deno/npm/managed/resolution.rs @@ -18,16 +18,17 @@ use deno_semver::package::PackageReq; use parking_lot::RwLock; #[allow(clippy::disallowed_types)] -pub type NpmResolutionRc = crate::sync::MaybeArc; +pub type NpmResolutionCellRc = crate::sync::MaybeArc; /// Handles updating and storing npm resolution in memory. /// /// This does not interact with the file system. -pub struct NpmResolution { +#[derive(Default)] +pub struct NpmResolutionCell { snapshot: RwLock, } -impl std::fmt::Debug for NpmResolution { +impl std::fmt::Debug for NpmResolutionCell { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let snapshot = self.snapshot.read(); f.debug_struct("NpmResolution") @@ -36,7 +37,7 @@ impl std::fmt::Debug for NpmResolution { } } -impl NpmResolution { +impl NpmResolutionCell { pub fn from_serialized( initial_snapshot: Option, ) -> Self { diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index 4c737f1126..4682085aea 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -140,7 +140,9 @@ pub trait NpmProcessStateProvider: #[derive(Debug)] pub struct EmptyNpmProcessStateProvider; + impl NpmProcessStateProvider for EmptyNpmProcessStateProvider {} + deno_core::extension!( deno_process, ops = [ From 7616429436c8799bfc0c04287712814423485458 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 13 Jan 2025 22:29:21 -0500 Subject: [PATCH 100/107] fix(compile/windows): better handling of deno_dir on different drive letter than code (#27654) Closes https://github.com/denoland/deno/issues/27651 --- cli/standalone/binary.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 296d6825a7..ff0343e27f 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -1007,8 +1007,16 @@ impl<'a> DenoCompileBinaryWriter<'a> { // this is not as optimized as it could be let mut last_name = Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME); - for ancestor in parent.ancestors() { - let dir = vfs.get_dir_mut(ancestor).unwrap(); + for ancestor in + parent.ancestors().map(Some).chain(std::iter::once(None)) + { + let dir = if let Some(ancestor) = ancestor { + vfs.get_dir_mut(ancestor).unwrap() + } else if cfg!(windows) { + vfs.get_system_root_dir_mut() + } else { + break; + }; if let Ok(index) = dir.entries.binary_search(&last_name, case_sensitivity) { From 9cb089f6db5574f091e54e222e8082ddd9ea779f Mon Sep 17 00:00:00 2001 From: Aaron Ang <67321817+aaron-ang@users.noreply.github.com> Date: Tue, 14 Jan 2025 04:01:14 -0500 Subject: [PATCH 101/107] fix(ext/node): add `writev` method to `FileHandle` (#27563) Part of #25554 --- ext/node/lib.rs | 2 +- ext/node/polyfills/_fs/_fs_writev.d.ts | 65 ----------------- .../_fs/{_fs_writev.mjs => _fs_writev.ts} | 73 +++++++++++++++++-- ext/node/polyfills/fs.ts | 2 +- ext/node/polyfills/internal/fs/handle.ts | 13 +++- ext/node/polyfills/internal/fs/streams.mjs | 2 +- tests/unit_node/_fs/_fs_handle_test.ts | 39 ++++++++++ tools/core_import_map.json | 2 +- 8 files changed, 118 insertions(+), 80 deletions(-) delete mode 100644 ext/node/polyfills/_fs/_fs_writev.d.ts rename ext/node/polyfills/_fs/{_fs_writev.mjs => _fs_writev.ts} (52%) diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 64b6c006a1..731cd3a9c7 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -487,7 +487,7 @@ deno_core::extension!(deno_node, "_fs/_fs_watch.ts", "_fs/_fs_write.mjs", "_fs/_fs_writeFile.ts", - "_fs/_fs_writev.mjs", + "_fs/_fs_writev.ts", "_next_tick.ts", "_process/exiting.ts", "_process/process.ts", diff --git a/ext/node/polyfills/_fs/_fs_writev.d.ts b/ext/node/polyfills/_fs/_fs_writev.d.ts deleted file mode 100644 index 1ba5511ff1..0000000000 --- a/ext/node/polyfills/_fs/_fs_writev.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. -// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts - -import { ErrnoException } from "ext:deno_node/_global.d.ts"; - -/** - * Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`. - * - * `position` is the offset from the beginning of the file where this data - * should be written. If `typeof position !== 'number'`, the data will be written - * at the current position. - * - * The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`. - * - * If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties. - * - * It is unsafe to use `fs.writev()` multiple times on the same file without - * waiting for the callback. For this scenario, use {@link createWriteStream}. - * - * On Linux, positional writes don't work when the file is opened in append mode. - * The kernel ignores the position argument and always appends the data to - * the end of the file. - * @since v12.9.0 - */ -export function writev( - fd: number, - buffers: ReadonlyArray, - cb: ( - err: ErrnoException | null, - bytesWritten: number, - buffers: ArrayBufferView[], - ) => void, -): void; -export function writev( - fd: number, - buffers: ReadonlyArray, - position: number | null, - cb: ( - err: ErrnoException | null, - bytesWritten: number, - buffers: ArrayBufferView[], - ) => void, -): void; -export interface WriteVResult { - bytesWritten: number; - buffers: ArrayBufferView[]; -} -export namespace writev { - function __promisify__( - fd: number, - buffers: ReadonlyArray, - position?: number, - ): Promise; -} -/** - * For detailed information, see the documentation of the asynchronous version of - * this API: {@link writev}. - * @since v12.9.0 - * @return The number of bytes written. - */ -export function writevSync( - fd: number, - buffers: ReadonlyArray, - position?: number, -): number; diff --git a/ext/node/polyfills/_fs/_fs_writev.mjs b/ext/node/polyfills/_fs/_fs_writev.ts similarity index 52% rename from ext/node/polyfills/_fs/_fs_writev.mjs rename to ext/node/polyfills/_fs/_fs_writev.ts index 61807c7f68..e38b44b19a 100644 --- a/ext/node/polyfills/_fs/_fs_writev.mjs +++ b/ext/node/polyfills/_fs/_fs_writev.ts @@ -5,15 +5,53 @@ // deno-lint-ignore-file prefer-primordials import { Buffer } from "node:buffer"; +import process from "node:process"; +import { ErrnoException } from "ext:deno_node/_global.d.ts"; import { validateBufferArray } from "ext:deno_node/internal/fs/utils.mjs"; import { getValidatedFd } from "ext:deno_node/internal/fs/utils.mjs"; +import { WriteVResult } from "ext:deno_node/internal/fs/handle.ts"; import { maybeCallback } from "ext:deno_node/_fs/_fs_common.ts"; import * as io from "ext:deno_io/12_io.js"; import { op_fs_seek_async, op_fs_seek_sync } from "ext:core/ops"; -export function writev(fd, buffers, position, callback) { +export interface WriteVResult { + bytesWritten: number; + buffers: ReadonlyArray; +} + +type writeVCallback = ( + err: ErrnoException | null, + bytesWritten: number, + buffers: ReadonlyArray, +) => void; + +/** + * Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`. + * + * `position` is the offset from the beginning of the file where this data + * should be written. If `typeof position !== 'number'`, the data will be written + * at the current position. + * + * The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`. + * + * If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties. + * + * It is unsafe to use `fs.writev()` multiple times on the same file without + * waiting for the callback. For this scenario, use {@link createWriteStream}. + * + * On Linux, positional writes don't work when the file is opened in append mode. + * The kernel ignores the position argument and always appends the data to + * the end of the file. + * @since v12.9.0 + */ +export function writev( + fd: number, + buffers: ReadonlyArray, + position?: number | null, + callback?: writeVCallback, +): void { const innerWritev = async (fd, buffers, position) => { - const chunks = []; + const chunks: Buffer[] = []; const offset = 0; for (let i = 0; i < buffers.length; i++) { if (Buffer.isBuffer(buffers[i])) { @@ -45,16 +83,24 @@ export function writev(fd, buffers, position, callback) { if (typeof position !== "number") position = null; innerWritev(fd, buffers, position).then( - (nwritten) => { - callback(null, nwritten, buffers); - }, + (nwritten) => callback(null, nwritten, buffers), (err) => callback(err), ); } -export function writevSync(fd, buffers, position) { +/** + * For detailed information, see the documentation of the asynchronous version of + * this API: {@link writev}. + * @since v12.9.0 + * @return The number of bytes written. + */ +export function writevSync( + fd: number, + buffers: ArrayBufferView[], + position?: number | null, +): number { const innerWritev = (fd, buffers, position) => { - const chunks = []; + const chunks: Buffer[] = []; const offset = 0; for (let i = 0; i < buffers.length; i++) { if (Buffer.isBuffer(buffers[i])) { @@ -85,3 +131,16 @@ export function writevSync(fd, buffers, position) { return innerWritev(fd, buffers, position); } + +export function writevPromise( + fd: number, + buffers: ArrayBufferView[], + position?: number, +): Promise { + return new Promise((resolve, reject) => { + writev(fd, buffers, position, (err, bytesWritten, buffers) => { + if (err) reject(err); + else resolve({ bytesWritten, buffers }); + }); + }); +} diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts index 76ff9ebd12..a6e43b5baa 100644 --- a/ext/node/polyfills/fs.ts +++ b/ext/node/polyfills/fs.ts @@ -119,7 +119,7 @@ import { // @deno-types="./_fs/_fs_write.d.ts" import { write, writeSync } from "ext:deno_node/_fs/_fs_write.mjs"; // @deno-types="./_fs/_fs_writev.d.ts" -import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.mjs"; +import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.ts"; import { readv, readvSync } from "ext:deno_node/_fs/_fs_readv.ts"; import { writeFile, diff --git a/ext/node/polyfills/internal/fs/handle.ts b/ext/node/polyfills/internal/fs/handle.ts index 2d787a9f3b..4244d159b7 100644 --- a/ext/node/polyfills/internal/fs/handle.ts +++ b/ext/node/polyfills/internal/fs/handle.ts @@ -6,7 +6,7 @@ import { EventEmitter } from "node:events"; import { Buffer } from "node:buffer"; import { Mode, promises, read, write } from "node:fs"; -export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts"; +import { core } from "ext:core/mod.js"; import { BinaryOptionsArgument, FileOptionsArgument, @@ -14,7 +14,8 @@ import { TextOptionsArgument, } from "ext:deno_node/_fs/_fs_common.ts"; import { ftruncatePromise } from "ext:deno_node/_fs/_fs_ftruncate.ts"; -import { core } from "ext:core/mod.js"; +export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts"; +import { writevPromise, WriteVResult } from "ext:deno_node/_fs/_fs_writev.ts"; interface WriteResult { bytesWritten: number; @@ -64,7 +65,7 @@ export class FileHandle extends EventEmitter { position, (err, bytesRead, buffer) => { if (err) reject(err); - else resolve({ buffer: buffer, bytesRead: bytesRead }); + else resolve({ buffer, bytesRead }); }, ); }); @@ -72,7 +73,7 @@ export class FileHandle extends EventEmitter { return new Promise((resolve, reject) => { read(this.fd, bufferOrOpt, (err, bytesRead, buffer) => { if (err) reject(err); - else resolve({ buffer: buffer, bytesRead: bytesRead }); + else resolve({ buffer, bytesRead }); }); }); } @@ -137,6 +138,10 @@ export class FileHandle extends EventEmitter { return fsCall(promises.writeFile, this, data, options); } + writev(buffers: ArrayBufferView[], position?: number): Promise { + return fsCall(writevPromise, this, buffers, position); + } + close(): Promise { // Note that Deno.close is not async return Promise.resolve(core.close(this.fd)); diff --git a/ext/node/polyfills/internal/fs/streams.mjs b/ext/node/polyfills/internal/fs/streams.mjs index 5adb8da225..d2ebecbe9a 100644 --- a/ext/node/polyfills/internal/fs/streams.mjs +++ b/ext/node/polyfills/internal/fs/streams.mjs @@ -18,7 +18,7 @@ import { errorOrDestroy } from "ext:deno_node/internal/streams/destroy.mjs"; import { open as fsOpen } from "ext:deno_node/_fs/_fs_open.ts"; import { read as fsRead } from "ext:deno_node/_fs/_fs_read.ts"; import { write as fsWrite } from "ext:deno_node/_fs/_fs_write.mjs"; -import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.mjs"; +import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.ts"; import { close as fsClose } from "ext:deno_node/_fs/_fs_close.ts"; import { Buffer } from "node:buffer"; import { diff --git a/tests/unit_node/_fs/_fs_handle_test.ts b/tests/unit_node/_fs/_fs_handle_test.ts index e4a41f8ba7..a8ca826329 100644 --- a/tests/unit_node/_fs/_fs_handle_test.ts +++ b/tests/unit_node/_fs/_fs_handle_test.ts @@ -118,6 +118,45 @@ Deno.test("[node/fs filehandle.writeFile] Write to file", async function () { assertEquals(decoder.decode(data), "hello world"); }); +Deno.test( + "[node/fs filehandle.writev] Write array of buffers to file", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w"); + + const buffer1 = Buffer.from("hello "); + const buffer2 = Buffer.from("world"); + const res = await fileHandle.writev([buffer1, buffer2]); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(res.bytesWritten, 11); + assertEquals(decoder.decode(data), "hello world"); + }, +); + +Deno.test( + "[node/fs filehandle.writev] Write array of buffers to file with position", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w"); + + const buffer1 = Buffer.from("hello "); + const buffer2 = Buffer.from("world"); + await fileHandle.writev([buffer1, buffer2], 0); + const buffer3 = Buffer.from("lorem ipsum"); + await fileHandle.writev([buffer3], 6); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(decoder.decode(data), "hello lorem ipsum"); + }, +); + Deno.test( "[node/fs filehandle.truncate] Truncate file with length", async function () { diff --git a/tools/core_import_map.json b/tools/core_import_map.json index d38221eb4c..feeae1ac70 100644 --- a/tools/core_import_map.json +++ b/tools/core_import_map.json @@ -38,7 +38,7 @@ "ext:deno_node/_fs/_fs_stat.ts": "../ext/node/polyfills/_fs/_fs_stat.ts", "ext:deno_node/_fs/_fs_watch.ts": "../ext/node/polyfills/_fs/_fs_watch.ts", "ext:deno_node/_fs/_fs_write.mjs": "../ext/node/polyfills/_fs/_fs_write.mjs", - "ext:deno_node/_fs/_fs_writev.mjs": "../ext/node/polyfills/_fs/_fs_writev.mjs", + "ext:deno_node/_fs/_fs_writev.ts": "../ext/node/polyfills/_fs/_fs_writev.ts", "ext:deno_node/_global.d.ts": "../ext/node/polyfills/_global.d.ts", "node:_http_agent": "../ext/node/polyfills/_http_agent.mjs", "node:_http_common": "../ext/node/polyfills/_http_common.ts", From a1f50a742219f6da78234412fe11a1456bfd3a0d Mon Sep 17 00:00:00 2001 From: siaeyy <75006429+siaeyy@users.noreply.github.com> Date: Tue, 14 Jan 2025 12:08:22 +0300 Subject: [PATCH 102/107] fix(node/fs): add utimes method to the FileHandle class (#27582) --- ext/node/polyfills/internal/fs/handle.ts | 8 ++++++++ tests/unit_node/_fs/_fs_handle_test.ts | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/ext/node/polyfills/internal/fs/handle.ts b/ext/node/polyfills/internal/fs/handle.ts index 4244d159b7..774b36dfc2 100644 --- a/ext/node/polyfills/internal/fs/handle.ts +++ b/ext/node/polyfills/internal/fs/handle.ts @@ -157,6 +157,14 @@ export class FileHandle extends EventEmitter { assertNotClosed(this, promises.chmod.name); return promises.chmod(this.#path, mode); } + + utimes( + atime: number | string | Date, + mtime: number | string | Date, + ): Promise { + assertNotClosed(this, promises.utimes.name); + return promises.utimes(this.#path, atime, mtime); + } } function assertNotClosed(handle: FileHandle, syscall: string) { diff --git a/tests/unit_node/_fs/_fs_handle_test.ts b/tests/unit_node/_fs/_fs_handle_test.ts index a8ca826329..9134c2cb0a 100644 --- a/tests/unit_node/_fs/_fs_handle_test.ts +++ b/tests/unit_node/_fs/_fs_handle_test.ts @@ -256,3 +256,20 @@ Deno.test({ await fileHandle.close(); }, }); + +Deno.test({ + name: + "[node/fs filehandle.utimes] Change the file system timestamps of the file", + async fn() { + const fileHandle = await fs.open(testData); + + const atime = new Date(); + const mtime = new Date(0); + + await fileHandle.utimes(atime, mtime); + assertEquals(Deno.statSync(testData).atime!, atime); + assertEquals(Deno.statSync(testData).mtime!, mtime); + + await fileHandle.close(); + }, +}); From 1e95c2056169c9b5de165b58bfd9296d5e5ce98e Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 14 Jan 2025 07:00:31 -0500 Subject: [PATCH 103/107] refactor: deno_config 0.45 (#27660) --- Cargo.lock | 5 +++-- Cargo.toml | 2 +- cli/args/mod.rs | 20 +++---------------- cli/lsp/config.rs | 15 ++------------ cli/lsp/diagnostics.rs | 7 +------ cli/lsp/documents.rs | 3 --- cli/lsp/language_server.rs | 2 -- cli/lsp/tsc.rs | 1 - .../with_malformed_config.out | 2 +- .../with_malformed_config2.out | 2 +- 10 files changed, 12 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2a78300c0..aa13ae0ccd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1497,11 +1497,12 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.43.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c4c11bd51ef6738cabfc3c53f16c209a0b8615cb1e4e5bf3b14e3b5deebfe21" +checksum = "47a47412627aa0d08414eca0e8329128013ab70bdb2cdfdc5456c2214cf24c8f" dependencies = [ "boxed_error", + "capacity_builder 0.5.0", "deno_error", "deno_package_json", "deno_path_util", diff --git a/Cargo.toml b/Cargo.toml index 2b52664a7f..48aeccdaef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ deno_ast = { version = "=0.44.0", features = ["transpiling"] } deno_core = { version = "0.330.0" } deno_bench_util = { version = "0.179.0", path = "./bench_util" } -deno_config = { version = "=0.43.0", features = ["workspace", "sync"] } +deno_config = { version = "=0.45.0", features = ["workspace", "sync"] } deno_lockfile = "=0.24.0" deno_media_type = { version = "0.2.3", features = ["module_specifier"] } deno_npm = "=0.27.2" diff --git a/cli/args/mod.rs b/cli/args/mod.rs index ebd321a20a..4ba85f1706 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -56,7 +56,6 @@ use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::url::Url; -use deno_error::JsErrorBox; use deno_graph::GraphKind; pub use deno_json::check_warn_tsconfig; use deno_lint::linter::LintConfig as DenoLintConfig; @@ -842,8 +841,6 @@ impl CliOptions { } else { &[] }; - let config_parse_options = - deno_config::deno_json::ConfigParseOptions::default(); let discover_pkg_json = flags.config_flag != ConfigFlag::Disabled && !flags.no_npm && !has_flag_env_var("DENO_NO_PACKAGE_JSON"); @@ -854,7 +851,6 @@ impl CliOptions { deno_json_cache: None, pkg_json_cache: Some(&node_resolver::PackageJsonThreadLocalCache), workspace_cache: None, - config_parse_options, additional_config_file_names, discover_pkg_json, maybe_vendor_override, @@ -1103,11 +1099,11 @@ impl CliOptions { } }; Ok(self.workspace().create_resolver( + &CliSys::default(), CreateResolverOptions { pkg_json_dep_resolution, specified_import_map: cli_arg_specified_import_map, }, - |path| std::fs::read_to_string(path).map_err(JsErrorBox::from_err), )?) } @@ -2126,12 +2122,7 @@ mod test { let cwd = &std::env::current_dir().unwrap(); let config_specifier = ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = ConfigFile::new( - config_text, - config_specifier, - &deno_config::deno_json::ConfigParseOptions::default(), - ) - .unwrap(); + let config_file = ConfigFile::new(config_text, config_specifier).unwrap(); let actual = resolve_import_map_specifier( Some("import-map.json"), Some(&config_file), @@ -2150,12 +2141,7 @@ mod test { let config_text = r#"{}"#; let config_specifier = ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = ConfigFile::new( - config_text, - config_specifier, - &deno_config::deno_json::ConfigParseOptions::default(), - ) - .unwrap(); + let config_file = ConfigFile::new(config_text, config_specifier).unwrap(); let actual = resolve_import_map_specifier( None, Some(&config_file), diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 7841ee0783..ba57502298 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -41,7 +41,6 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::url::Url; use deno_core::ModuleSpecifier; -use deno_error::JsErrorBox; use deno_lint::linter::LintConfig as DenoLintConfig; use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonCache; @@ -1247,7 +1246,6 @@ impl ConfigData { pkg_json_cache: Some(pkg_json_cache), workspace_cache: Some(workspace_cache), discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"), - config_parse_options: Default::default(), maybe_vendor_override: None, }, ) @@ -1572,11 +1570,11 @@ impl ConfigData { let resolver = member_dir .workspace .create_resolver( + &CliSys::default(), CreateResolverOptions { pkg_json_dep_resolution, specified_import_map, }, - |path| std::fs::read_to_string(path).map_err(JsErrorBox::from_err), ) .inspect_err(|err| { lsp_warn!( @@ -2078,7 +2076,6 @@ impl deno_config::workspace::WorkspaceCache for WorkspaceMemCache { #[cfg(test)] mod tests { - use deno_config::deno_json::ConfigParseOptions; use deno_core::resolve_url; use deno_core::serde_json; use deno_core::serde_json::json; @@ -2352,12 +2349,7 @@ mod tests { config .tree .inject_config_file( - ConfigFile::new( - "{}", - root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), - ) - .unwrap(), + ConfigFile::new("{}", root_uri.join("deno.json").unwrap()).unwrap(), ) .await; assert!(config.specifier_enabled(&root_uri)); @@ -2413,7 +2405,6 @@ mod tests { }) .to_string(), root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) @@ -2439,7 +2430,6 @@ mod tests { }) .to_string(), root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) @@ -2457,7 +2447,6 @@ mod tests { }) .to_string(), root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 42a1a0c52a..fe8dc4c8d5 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -1695,12 +1695,7 @@ mod tests { let mut config = Config::new_with_roots([root_uri.clone()]); if let Some((relative_path, json_string)) = maybe_import_map { let base_url = root_uri.join(relative_path).unwrap(); - let config_file = ConfigFile::new( - json_string, - base_url, - &deno_config::deno_json::ConfigParseOptions::default(), - ) - .unwrap(); + let config_file = ConfigFile::new(json_string, base_url).unwrap(); config.tree.inject_config_file(config_file).await; } let resolver = diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 70f55ffd62..6a7a08b5a1 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -1767,7 +1767,6 @@ fn bytes_to_content( #[cfg(test)] mod tests { use deno_config::deno_json::ConfigFile; - use deno_config::deno_json::ConfigParseOptions; use deno_core::serde_json; use deno_core::serde_json::json; use pretty_assertions::assert_eq; @@ -1924,7 +1923,6 @@ console.log(b, "hello deno"); }) .to_string(), config.root_uri().unwrap().join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) @@ -1968,7 +1966,6 @@ console.log(b, "hello deno"); }) .to_string(), config.root_uri().unwrap().join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 03f63e5f25..0f3bfcdf59 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -3621,8 +3621,6 @@ impl Inner { deno_json_cache: None, pkg_json_cache: None, workspace_cache: None, - config_parse_options: - deno_config::deno_json::ConfigParseOptions::default(), additional_config_file_names: &[], discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"), maybe_vendor_override: if force_global_cache { diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 8d9a5a46a1..e2a0fc430d 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -5589,7 +5589,6 @@ mod tests { }) .to_string(), temp_dir.url().join("deno.json").unwrap(), - &Default::default(), ) .unwrap(), ) diff --git a/tests/specs/lint/with_malformed_config/with_malformed_config.out b/tests/specs/lint/with_malformed_config/with_malformed_config.out index 1c0f0fff6e..928ec02fb1 100644 --- a/tests/specs/lint/with_malformed_config/with_malformed_config.out +++ b/tests/specs/lint/with_malformed_config/with_malformed_config.out @@ -1,4 +1,4 @@ error: Failed to parse "lint" configuration Caused by: - unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report` + unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`, `plugins` diff --git a/tests/specs/lint/with_malformed_config2/with_malformed_config2.out b/tests/specs/lint/with_malformed_config2/with_malformed_config2.out index 1c0f0fff6e..928ec02fb1 100644 --- a/tests/specs/lint/with_malformed_config2/with_malformed_config2.out +++ b/tests/specs/lint/with_malformed_config2/with_malformed_config2.out @@ -1,4 +1,4 @@ error: Failed to parse "lint" configuration Caused by: - unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report` + unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`, `plugins` From 3fb8fc1ba78ae7ded63dd2c1bb3249338bf14cac Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Tue, 14 Jan 2025 13:31:02 +0100 Subject: [PATCH 104/107] feat(unstable): refactor js lint plugin AST (#27615) This PR changes the underlying buffer backed AST format we use for JavaScript-based linting plugins. It adds support for various new types, makes traversal code a lot easier and is more polished compared to previous iterations. Here is a quick summary (in no particular order): - Node prop data is separate from traversal, which makes traversal code so much easier to reason about. Previously, it was interleaved with node prop data - spans are in a separate table as well, as they are rarely needed. - schema is separate from SWC conversion logic, which makes - supports recursive plain objects - supports numbers - supports bigint - supports regex - adds all SWC nodes Apologies, this is kinda a big PR, but it's worth it imo. _Marking as draft because I need to update some tests tomorrow._ --- cli/js/40_lint.js | 873 +- cli/js/40_lint_selector.js | 36 +- cli/js/40_lint_types.d.ts | 9 +- cli/tools/lint/ast_buffer/buffer.rs | 591 +- cli/tools/lint/ast_buffer/swc.rs | 3540 +++---- cli/tools/lint/ast_buffer/ts_estree.rs | 2356 ++++- .../__snapshots__/lint_plugin_test.ts.snap | 8608 +++++++++++++++++ tests/unit/lint_plugin_test.ts | 989 +- 8 files changed, 13757 insertions(+), 3245 deletions(-) create mode 100644 tests/unit/__snapshots__/lint_plugin_test.ts.snap diff --git a/cli/js/40_lint.js b/cli/js/40_lint.js index 4adbc1baa1..9f85f0871d 100644 --- a/cli/js/40_lint.js +++ b/cli/js/40_lint.js @@ -8,10 +8,27 @@ import { splitSelectors, } from "ext:cli/40_lint_selector.js"; import { core, internals } from "ext:core/mod.js"; + const { op_lint_create_serialized_ast, } = core.ops; +// Keep these in sync with Rust +const AST_IDX_INVALID = 0; +const AST_GROUP_TYPE = 1; +/// +/// +/// +/// +/// +const NODE_SIZE = 1 + 4 + 4 + 4 + 4; +const PROP_OFFSET = 1; +const CHILD_OFFSET = 1 + 4; +const NEXT_OFFSET = 1 + 4 + 4; +const PARENT_OFFSET = 1 + 4 + 4 + 4; +// Span size in buffer: u32 + u32 +const SPAN_SIZE = 4 + 4; + // Keep in sync with Rust // These types are expected to be present on every node. Note that this // isn't set in stone. We could revise this at a future point. @@ -34,12 +51,21 @@ const PropFlags = { * the string table that was included in the message. */ String: 2, + /** + * A numnber field. Numbers are represented as strings internally. + */ + Number: 3, /** This value is either 0 = false, or 1 = true */ - Bool: 3, + Bool: 4, /** No value, it's null */ - Null: 4, + Null: 5, /** No value, it's undefined */ - Undefined: 5, + Undefined: 6, + /** An object */ + Obj: 7, + Regex: 8, + BigInt: 9, + Array: 10, }; /** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */ @@ -51,6 +77,7 @@ const PropFlags = { /** @typedef {import("./40_lint_types.d.ts").LintPlugin} LintPlugin */ /** @typedef {import("./40_lint_types.d.ts").TransformFn} TransformFn */ /** @typedef {import("./40_lint_types.d.ts").MatchContext} MatchContext */ +/** @typedef {import("./40_lint_types.d.ts").Node} Node */ /** @type {LintState} */ const state = { @@ -100,17 +127,17 @@ export function installPlugin(plugin) { /** * @param {AstContext} ctx - * @param {number} offset - * @returns + * @param {number} idx + * @returns {FacadeNode | null} */ -function getNode(ctx, offset) { - if (offset === 0) return null; - const cached = ctx.nodes.get(offset); - if (cached !== undefined) return cached; +function getNode(ctx, idx) { + if (idx === AST_IDX_INVALID) return null; + const cached = ctx.nodes.get(idx); + if (cached !== undefined) return /** @type {*} */ (cached); - const node = new Node(ctx, offset); - ctx.nodes.set(offset, /** @type {*} */ (cached)); - return node; + const node = new FacadeNode(ctx, idx); + ctx.nodes.set(idx, /** @type {*} */ (node)); + return /** @type {*} */ (node); } /** @@ -122,31 +149,22 @@ function getNode(ctx, offset) { * @returns {number} */ function findPropOffset(buf, offset, search) { - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; - - const propCount = buf[offset]; + const count = buf[offset]; offset += 1; - for (let i = 0; i < propCount; i++) { + for (let i = 0; i < count; i++) { const maybe = offset; const prop = buf[offset++]; const kind = buf[offset++]; if (prop === search) return maybe; - if (kind === PropFlags.Ref) { - offset += 4; - } else if (kind === PropFlags.RefArr) { + if (kind === PropFlags.Obj) { const len = readU32(buf, offset); - offset += 4 + (len * 4); - } else if (kind === PropFlags.String) { offset += 4; - } else if (kind === PropFlags.Bool) { - offset++; - } else if (kind === PropFlags.Null || kind === PropFlags.Undefined) { - // No value + // prop + kind + value + offset += len * (1 + 1 + 4); } else { - offset++; + offset += 4; } } @@ -154,23 +172,23 @@ function findPropOffset(buf, offset, search) { } const INTERNAL_CTX = Symbol("ctx"); -const INTERNAL_OFFSET = Symbol("offset"); +const INTERNAL_IDX = Symbol("offset"); // This class is a facade for all materialized nodes. Instead of creating a // unique class per AST node, we have one class with getters for every // possible node property. This allows us to lazily materialize child node // only when they are needed. -class Node { +class FacadeNode { [INTERNAL_CTX]; - [INTERNAL_OFFSET]; + [INTERNAL_IDX]; /** * @param {AstContext} ctx - * @param {number} offset + * @param {number} idx */ - constructor(ctx, offset) { + constructor(ctx, idx) { this[INTERNAL_CTX] = ctx; - this[INTERNAL_OFFSET] = offset; + this[INTERNAL_IDX] = idx; } /** @@ -186,12 +204,12 @@ class Node { * @returns {string} */ [Symbol.for("Deno.customInspect")](_, options) { - const json = toJsValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET]); + const json = nodeToJson(this[INTERNAL_CTX], this[INTERNAL_IDX]); return Deno.inspect(json, options); } [Symbol.for("Deno.lint.toJsValue")]() { - return toJsValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET]); + return nodeToJson(this[INTERNAL_CTX], this[INTERNAL_IDX]); } } @@ -212,125 +230,243 @@ function setNodeGetters(ctx) { const name = getString(ctx.strTable, id); - Object.defineProperty(Node.prototype, name, { + Object.defineProperty(FacadeNode.prototype, name, { get() { - return readValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET], i); + return readValue( + this[INTERNAL_CTX], + this[INTERNAL_IDX], + i, + getNode, + ); }, }); } } /** - * Serialize a node recursively to plain JSON * @param {AstContext} ctx - * @param {number} offset - * @returns {*} + * @param {number} idx */ -function toJsValue(ctx, offset) { - const { buf } = ctx; - +function nodeToJson(ctx, idx) { /** @type {Record} */ const node = { - type: readValue(ctx, offset, AST_PROP_TYPE), - range: readValue(ctx, offset, AST_PROP_RANGE), + type: readValue(ctx, idx, AST_PROP_TYPE, nodeToJson), + range: readValue(ctx, idx, AST_PROP_RANGE, nodeToJson), }; - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; + const { buf } = ctx; + let offset = readPropOffset(ctx, idx); const count = buf[offset++]; - for (let i = 0; i < count; i++) { - const prop = buf[offset++]; - const kind = buf[offset++]; - const name = getString(ctx.strTable, ctx.strByProp[prop]); - if (kind === PropFlags.Ref) { - const v = readU32(buf, offset); - offset += 4; - node[name] = v === 0 ? null : toJsValue(ctx, v); - } else if (kind === PropFlags.RefArr) { - const len = readU32(buf, offset); - offset += 4; - const nodes = new Array(len); - for (let i = 0; i < len; i++) { - const v = readU32(buf, offset); - if (v === 0) continue; - nodes[i] = toJsValue(ctx, v); - offset += 4; - } - node[name] = nodes; - } else if (kind === PropFlags.Bool) { - const v = buf[offset++]; - node[name] = v === 1; - } else if (kind === PropFlags.String) { - const v = readU32(buf, offset); - offset += 4; - node[name] = getString(ctx.strTable, v); - } else if (kind === PropFlags.Null) { - node[name] = null; - } else if (kind === PropFlags.Undefined) { - node[name] = undefined; - } + for (let i = 0; i < count; i++) { + const prop = buf[offset]; + const _kind = buf[offset + 1]; + + const name = getString(ctx.strTable, ctx.strByProp[prop]); + node[name] = readProperty(ctx, offset, nodeToJson); + + // prop + type + value + offset += 1 + 1 + 4; } return node; } /** - * Read a specific property from a node + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readType(buf, idx) { + return buf[idx * NODE_SIZE]; +} + +/** + * @param {AstContext} ctx + * @param {number} idx + * @returns {Node["range"]} + */ +function readSpan(ctx, idx) { + let offset = ctx.spansOffset + (idx * SPAN_SIZE); + const start = readU32(ctx.buf, offset); + offset += 4; + const end = readU32(ctx.buf, offset); + + return [start, end]; +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readRawPropOffset(buf, idx) { + const offset = (idx * NODE_SIZE) + PROP_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext} ctx + * @param {number} idx + * @returns {number} + */ +function readPropOffset(ctx, idx) { + return readRawPropOffset(ctx.buf, idx) + ctx.propsOffset; +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readChild(buf, idx) { + const offset = (idx * NODE_SIZE) + CHILD_OFFSET; + return readU32(buf, offset); +} +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readNext(buf, idx) { + const offset = (idx * NODE_SIZE) + NEXT_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readParent(buf, idx) { + const offset = (idx * NODE_SIZE) + PARENT_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext["strTable"]} strTable + * @param {number} strId + * @returns {RegExp} + */ +function readRegex(strTable, strId) { + const raw = getString(strTable, strId); + const idx = raw.lastIndexOf("/"); + const pattern = raw.slice(1, idx); + const flags = idx < raw.length - 1 ? raw.slice(idx + 1) : undefined; + + return new RegExp(pattern, flags); +} + +/** * @param {AstContext} ctx * @param {number} offset - * @param {number} search - * @returns {*} + * @param {(ctx: AstContext, idx: number) => any} parseNode + * @returns {Record} */ -function readValue(ctx, offset, search) { - const { buf } = ctx; - const type = buf[offset]; +function readObject(ctx, offset, parseNode) { + const { buf, strTable, strByProp } = ctx; - if (search === AST_PROP_TYPE) { - return getString(ctx.strTable, ctx.strByType[type]); - } else if (search === AST_PROP_RANGE) { - const start = readU32(buf, offset + 1 + 4); - const end = readU32(buf, offset + 1 + 4 + 4); - return [start, end]; - } else if (search === AST_PROP_PARENT) { - const pos = readU32(buf, offset + 1); - return getNode(ctx, pos); + /** @type {Record} */ + const obj = {}; + + const count = readU32(buf, offset); + offset += 4; + + for (let i = 0; i < count; i++) { + const prop = buf[offset]; + const name = getString(strTable, strByProp[prop]); + obj[name] = readProperty(ctx, offset, parseNode); + // name + kind + value + offset += 1 + 1 + 4; } - offset = findPropOffset(ctx.buf, offset, search); - if (offset === -1) return undefined; + return obj; +} - const kind = buf[offset + 1]; - offset += 2; +/** + * @param {AstContext} ctx + * @param {number} offset + * @param {(ctx: AstContext, idx: number) => any} parseNode + * @returns {any} + */ +function readProperty(ctx, offset, parseNode) { + const { buf } = ctx; + + // skip over name + const _name = buf[offset++]; + const kind = buf[offset++]; if (kind === PropFlags.Ref) { const value = readU32(buf, offset); - return getNode(ctx, value); + return parseNode(ctx, value); } else if (kind === PropFlags.RefArr) { - const len = readU32(buf, offset); - offset += 4; + const groupId = readU32(buf, offset); - const nodes = new Array(len); - for (let i = 0; i < len; i++) { - nodes[i] = getNode(ctx, readU32(buf, offset)); - offset += 4; + const nodes = []; + let next = readChild(buf, groupId); + while (next > AST_IDX_INVALID) { + nodes.push(parseNode(ctx, next)); + next = readNext(buf, next); } + return nodes; } else if (kind === PropFlags.Bool) { - return buf[offset] === 1; + const v = readU32(buf, offset); + return v === 1; } else if (kind === PropFlags.String) { const v = readU32(buf, offset); return getString(ctx.strTable, v); + } else if (kind === PropFlags.Number) { + const v = readU32(buf, offset); + return Number(getString(ctx.strTable, v)); + } else if (kind === PropFlags.BigInt) { + const v = readU32(buf, offset); + return BigInt(getString(ctx.strTable, v)); + } else if (kind === PropFlags.Regex) { + const v = readU32(buf, offset); + return readRegex(ctx.strTable, v); } else if (kind === PropFlags.Null) { return null; } else if (kind === PropFlags.Undefined) { return undefined; + } else if (kind === PropFlags.Obj) { + const objOffset = readU32(buf, offset) + ctx.propsOffset; + return readObject(ctx, objOffset, parseNode); } throw new Error(`Unknown prop kind: ${kind}`); } +/** + * Read a specific property from a node + * @param {AstContext} ctx + * @param {number} idx + * @param {number} search + * @param {(ctx: AstContext, idx: number) => any} parseNode + * @returns {*} + */ +function readValue(ctx, idx, search, parseNode) { + const { buf } = ctx; + + if (search === AST_PROP_TYPE) { + const type = readType(buf, idx); + return getString(ctx.strTable, ctx.strByType[type]); + } else if (search === AST_PROP_RANGE) { + return readSpan(ctx, idx); + } else if (search === AST_PROP_PARENT) { + const parent = readParent(buf, idx); + return getNode(ctx, parent); + } + + const propOffset = readPropOffset(ctx, idx); + + const offset = findPropOffset(ctx.buf, propOffset, search); + if (offset === -1) return undefined; + + return readProperty(ctx, offset, parseNode); +} + const DECODER = new TextDecoder(); /** @@ -359,322 +495,149 @@ function getString(strTable, id) { return name; } -/** - * @param {AstContext["buf"]} buf - * @param {number} child - * @returns {null | [number, number]} - */ -function findChildOffset(buf, child) { - let offset = readU32(buf, child + 1); - - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; - - const propCount = buf[offset++]; - for (let i = 0; i < propCount; i++) { - const _prop = buf[offset++]; - const kind = buf[offset++]; - - switch (kind) { - case PropFlags.Ref: { - const start = offset; - const value = readU32(buf, offset); - offset += 4; - if (value === child) { - return [start, -1]; - } - break; - } - case PropFlags.RefArr: { - const start = offset; - - const len = readU32(buf, offset); - offset += 4; - - for (let j = 0; j < len; j++) { - const value = readU32(buf, offset); - offset += 4; - if (value === child) { - return [start, j]; - } - } - - break; - } - case PropFlags.String: - offset += 4; - break; - case PropFlags.Bool: - offset++; - break; - case PropFlags.Null: - case PropFlags.Undefined: - break; - } - } - - return null; -} - /** @implements {MatchContext} */ class MatchCtx { /** - * @param {AstContext["buf"]} buf - * @param {AstContext["strTable"]} strTable - * @param {AstContext["strByType"]} strByType + * @param {AstContext} ctx */ - constructor(buf, strTable, strByType) { - this.buf = buf; - this.strTable = strTable; - this.strByType = strByType; + constructor(ctx) { + this.ctx = ctx; } /** - * @param {number} offset - * @returns {number} - */ - getParent(offset) { - return readU32(this.buf, offset + 1); - } - - /** - * @param {number} offset - * @returns {number} - */ - getType(offset) { - return this.buf[offset]; - } - - /** - * @param {number} offset - * @param {number[]} propIds * @param {number} idx + * @returns {number} + */ + getParent(idx) { + return readParent(this.ctx.buf, idx); + } + + /** + * @param {number} idx + * @returns {number} + */ + getType(idx) { + return readType(this.ctx.buf, idx); + } + + /** + * @param {number} idx - Node idx + * @param {number[]} propIds + * @param {number} propIdx * @returns {unknown} */ - getAttrPathValue(offset, propIds, idx) { - const { buf } = this; + getAttrPathValue(idx, propIds, propIdx) { + if (idx === 0) throw -1; - const propId = propIds[idx]; + const { buf, strTable, strByType } = this.ctx; + + const propId = propIds[propIdx]; switch (propId) { case AST_PROP_TYPE: { - const type = this.getType(offset); - return getString(this.strTable, this.strByType[type]); + const type = readType(buf, idx); + return getString(strTable, strByType[type]); } case AST_PROP_PARENT: case AST_PROP_RANGE: - throw new Error(`Not supported`); + throw -1; } + let offset = readPropOffset(this.ctx, idx); + offset = findPropOffset(buf, offset, propId); - if (offset === -1) return undefined; + if (offset === -1) throw -1; const _prop = buf[offset++]; const kind = buf[offset++]; if (kind === PropFlags.Ref) { const value = readU32(buf, offset); // Checks need to end with a value, not a node - if (idx === propIds.length - 1) return undefined; - return this.getAttrPathValue(value, propIds, idx + 1); + if (propIdx === propIds.length - 1) throw -1; + return this.getAttrPathValue(value, propIds, propIdx + 1); } else if (kind === PropFlags.RefArr) { - const count = readU32(buf, offset); + const arrIdx = readU32(buf, offset); offset += 4; - if (idx < propIds.length - 1 && propIds[idx + 1] === AST_PROP_LENGTH) { + let count = 0; + let child = readChild(buf, arrIdx); + while (child > AST_IDX_INVALID) { + count++; + child = readNext(buf, child); + } + + if ( + propIdx < propIds.length - 1 && propIds[propIdx + 1] === AST_PROP_LENGTH + ) { return count; } // TODO(@marvinhagemeister): Allow traversing into array children? + throw -1; + } else if (kind === PropFlags.Obj) { + // TODO(@marvinhagemeister) } // Cannot traverse into primitives further - if (idx < propIds.length - 1) return undefined; + if (propIdx < propIds.length - 1) throw -1; if (kind === PropFlags.String) { const s = readU32(buf, offset); - return getString(this.strTable, s); + return getString(strTable, s); + } else if (kind === PropFlags.Number) { + const s = readU32(buf, offset); + return Number(getString(strTable, s)); + } else if (kind === PropFlags.Regex) { + const v = readU32(buf, offset); + return readRegex(strTable, v); } else if (kind === PropFlags.Bool) { - return buf[offset] === 1; + return readU32(buf, offset) === 1; } else if (kind === PropFlags.Null) { return null; } else if (kind === PropFlags.Undefined) { return undefined; } - return undefined; + throw -1; } /** - * @param {number} offset - * @param {number[]} propIds * @param {number} idx - * @returns {boolean} - */ - hasAttrPath(offset, propIds, idx) { - const { buf } = this; - - const propId = propIds[idx]; - // If propId is 0 then the property doesn't exist in the AST - if (propId === 0) return false; - - switch (propId) { - case AST_PROP_TYPE: - case AST_PROP_PARENT: - case AST_PROP_RANGE: - return true; - } - - offset = findPropOffset(buf, offset, propId); - if (offset === -1) return false; - if (idx === propIds.length - 1) return true; - - const _prop = buf[offset++]; - const kind = buf[offset++]; - if (kind === PropFlags.Ref) { - const value = readU32(buf, offset); - return this.hasAttrPath(value, propIds, idx + 1); - } else if (kind === PropFlags.RefArr) { - const _count = readU32(buf, offset); - offset += 4; - - if (idx < propIds.length - 1 && propIds[idx + 1] === AST_PROP_LENGTH) { - return true; - } - - // TODO(@marvinhagemeister): Allow traversing into array children? - } - - // Primitives cannot be traversed further. This means we - // didn't found the attribute. - if (idx < propIds.length - 1) return false; - - return true; - } - - /** - * @param {number} offset * @returns {number} */ - getFirstChild(offset) { - const { buf } = this; - - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; - - const count = buf[offset++]; - for (let i = 0; i < count; i++) { - const _prop = buf[offset++]; - const kind = buf[offset++]; - - switch (kind) { - case PropFlags.Ref: { - const v = readU32(buf, offset); - offset += 4; - return v; - } - case PropFlags.RefArr: { - const len = readU32(buf, offset); - offset += 4; - for (let j = 0; j < len; j++) { - const v = readU32(buf, offset); - offset += 4; - return v; - } - - return len; - } - - case PropFlags.String: - offset += 4; - break; - case PropFlags.Bool: - offset++; - break; - case PropFlags.Null: - case PropFlags.Undefined: - break; - } - } - - return -1; + getFirstChild(idx) { + const siblings = this.getSiblings(idx); + return siblings[0] ?? -1; } /** - * @param {number} offset + * @param {number} idx * @returns {number} */ - getLastChild(offset) { - const { buf } = this; - - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; - - let last = -1; - - const count = buf[offset++]; - for (let i = 0; i < count; i++) { - const _prop = buf[offset++]; - const kind = buf[offset++]; - - switch (kind) { - case PropFlags.Ref: { - const v = readU32(buf, offset); - offset += 4; - last = v; - break; - } - case PropFlags.RefArr: { - const len = readU32(buf, offset); - offset += 4; - for (let j = 0; j < len; j++) { - const v = readU32(buf, offset); - last = v; - offset += 4; - } - - break; - } - - case PropFlags.String: - offset += 4; - break; - case PropFlags.Bool: - offset++; - break; - case PropFlags.Null: - case PropFlags.Undefined: - break; - } - } - - return last; + getLastChild(idx) { + const siblings = this.getSiblings(idx); + return siblings.at(-1) ?? -1; } /** - * @param {number} id + * @param {number} idx * @returns {number[]} */ - getSiblings(id) { - const { buf } = this; + getSiblings(idx) { + const { buf } = this.ctx; + const parent = readParent(buf, idx); - const result = findChildOffset(buf, id); - // Happens for program nodes - if (result === null) return []; - - if (result[1] === -1) { - return [id]; + // Only RefArrays have siblings + const parentType = readType(buf, parent); + if (parentType !== AST_GROUP_TYPE) { + return []; } - let offset = result[0]; - const count = readU32(buf, offset); - offset += 4; - - /** @type {number[]} */ const out = []; - for (let i = 0; i < count; i++) { - const v = readU32(buf, offset); - offset += 4; - out.push(v); + let child = readChild(buf, parent); + while (child > AST_IDX_INVALID) { + out.push(child); + child = readNext(buf, child); } return out; @@ -683,7 +646,7 @@ class MatchCtx { /** * @param {Uint8Array} buf - * @param {AstContext} buf + * @returns {AstContext} */ function createAstContext(buf) { /** @type {Map} */ @@ -691,6 +654,8 @@ function createAstContext(buf) { // The buffer has a few offsets at the end which allows us to easily // jump to the relevant sections of the message. + const propsOffset = readU32(buf, buf.length - 24); + const spansOffset = readU32(buf, buf.length - 20); const typeMapOffset = readU32(buf, buf.length - 16); const propMapOffset = readU32(buf, buf.length - 12); const strTableOffset = readU32(buf, buf.length - 8); @@ -702,9 +667,7 @@ function createAstContext(buf) { const stringCount = readU32(buf, offset); offset += 4; - // TODO(@marvinhagemeister): We could lazily decode the strings on an as needed basis. - // Not sure if this matters much in practice though. - let id = 0; + let strId = 0; for (let i = 0; i < stringCount; i++) { const len = readU32(buf, offset); offset += 4; @@ -712,8 +675,8 @@ function createAstContext(buf) { const strBytes = buf.slice(offset, offset + len); offset += len; const s = DECODER.decode(strBytes); - strTable.set(id, s); - id++; + strTable.set(strId, s); + strId++; } if (strTable.size !== stringCount) { @@ -755,14 +718,17 @@ function createAstContext(buf) { buf, strTable, rootOffset, + spansOffset, + propsOffset, nodes: new Map(), strTableOffset, strByProp, strByType, typeByStr, propByStr, - matcher: new MatchCtx(buf, strTable, strByType), + matcher: /** @type {*} */ (null), }; + ctx.matcher = new MatchCtx(ctx); setNodeGetters(ctx); @@ -903,76 +869,53 @@ export function runPluginsForFile(fileName, serializedAst) { /** * @param {AstContext} ctx * @param {CompiledVisitor[]} visitors - * @param {number} offset + * @param {number} idx */ -function traverse(ctx, visitors, offset) { - // The 0 offset is used to denote an empty/placeholder node - if (offset === 0) return; - - const originalOffset = offset; +function traverse(ctx, visitors, idx) { + if (idx === AST_IDX_INVALID) return; const { buf } = ctx; + const nodeType = readType(ctx.buf, idx); /** @type {VisitorFn[] | null} */ let exits = null; - for (let i = 0; i < visitors.length; i++) { - const v = visitors[i]; - - if (v.matcher(ctx.matcher, offset)) { - if (v.info.exit !== NOOP) { - if (exits === null) { - exits = [v.info.exit]; - } else { - exits.push(v.info.exit); + // Only visit if it's an actual node + if (nodeType !== AST_GROUP_TYPE) { + // Loop over visitors and check if any selector matches + for (let i = 0; i < visitors.length; i++) { + const v = visitors[i]; + if (v.matcher(ctx.matcher, idx)) { + if (v.info.exit !== NOOP) { + if (exits === null) { + exits = [v.info.exit]; + } else { + exits.push(v.info.exit); + } } - } - if (v.info.enter !== NOOP) { - const node = /** @type {*} */ (getNode(ctx, offset)); - v.info.enter(node); + if (v.info.enter !== NOOP) { + const node = /** @type {*} */ (getNode(ctx, idx)); + v.info.enter(node); + } } } } - // Search for node references in the properties of the current node. All - // other properties can be ignored. try { - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; + const childIdx = readChild(buf, idx); + if (childIdx > AST_IDX_INVALID) { + traverse(ctx, visitors, childIdx); + } - const propCount = buf[offset]; - offset += 1; - - for (let i = 0; i < propCount; i++) { - const kind = buf[offset + 1]; - offset += 2; // propId + propFlags - - if (kind === PropFlags.Ref) { - const next = readU32(buf, offset); - offset += 4; - traverse(ctx, visitors, next); - } else if (kind === PropFlags.RefArr) { - const len = readU32(buf, offset); - offset += 4; - - for (let j = 0; j < len; j++) { - const child = readU32(buf, offset); - offset += 4; - traverse(ctx, visitors, child); - } - } else if (kind === PropFlags.String) { - offset += 4; - } else if (kind === PropFlags.Bool) { - offset += 1; - } else if (kind === PropFlags.Null || kind === PropFlags.Undefined) { - // No value - } + const nextIdx = readNext(buf, idx); + if (nextIdx > AST_IDX_INVALID) { + traverse(ctx, visitors, nextIdx); } } finally { if (exits !== null) { for (let i = 0; i < exits.length; i++) { - const node = /** @type {*} */ (getNode(ctx, originalOffset)); + const node = /** @type {*} */ (getNode(ctx, idx)); exits[i](node); } } @@ -1009,38 +952,46 @@ function _dump(ctx) { // deno-lint-ignore no-console console.log(); - let offset = 0; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(); - while (offset < strTableOffset) { - const type = buf[offset]; - const name = getString(ctx.strTable, ctx.strByType[type]); + let idx = 0; + while (idx < (strTableOffset / NODE_SIZE)) { + const type = readType(buf, idx); + const child = readChild(buf, idx); + const next = readNext(buf, idx); + const parent = readParent(buf, idx); + const range = readSpan(ctx, idx); + + const name = type === AST_IDX_INVALID + ? "" + : type === AST_GROUP_TYPE + ? "" + : getString(ctx.strTable, ctx.strByType[type]); // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(`${name}, offset: ${offset}, type: ${type}`); - offset += 1; + console.log(`${name}, idx: ${idx}, type: ${type}`); - const parent = readU32(buf, offset); - offset += 4; // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(` parent: ${parent}`); - - const start = readU32(buf, offset); - offset += 4; - const end = readU32(buf, offset); - offset += 4; + console.log(` child: ${child}, next: ${next}, parent: ${parent}`); // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(` range: ${start} -> ${end}`); + console.log(` range: ${range[0]}, ${range[1]}`); - const count = buf[offset++]; + const rawOffset = readRawPropOffset(ctx.buf, idx); + let propOffset = readPropOffset(ctx, idx); + const count = buf[propOffset++]; // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(` prop count: ${count}`); + console.log( + ` prop count: ${count}, prop offset: ${propOffset} raw offset: ${rawOffset}`, + ); for (let i = 0; i < count; i++) { - const prop = buf[offset++]; - const kind = buf[offset++]; + const prop = buf[propOffset++]; + const kind = buf[propOffset++]; const name = getString(ctx.strTable, ctx.strByProp[prop]); let kindName = "unknown"; @@ -1051,40 +1002,36 @@ function _dump(ctx) { } } + const v = readU32(buf, propOffset); + propOffset += 4; + if (kind === PropFlags.Ref) { - const v = readU32(buf, offset); - offset += 4; // @ts-ignore dump fn // deno-lint-ignore no-console console.log(` ${name}: ${v} (${kindName}, ${prop})`); } else if (kind === PropFlags.RefArr) { - const len = readU32(buf, offset); - offset += 4; // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(` ${name}: Array(${len}) (${kindName}, ${prop})`); - - for (let j = 0; j < len; j++) { - const v = readU32(buf, offset); - offset += 4; - // @ts-ignore dump fn - // deno-lint-ignore no-console - console.log(` - ${v} (${prop})`); - } + console.log(` ${name}: RefArray: ${v}, (${kindName}, ${prop})`); } else if (kind === PropFlags.Bool) { - const v = buf[offset]; - offset += 1; // @ts-ignore dump fn // deno-lint-ignore no-console console.log(` ${name}: ${v} (${kindName}, ${prop})`); } else if (kind === PropFlags.String) { - const v = readU32(buf, offset); - offset += 4; + const raw = getString(ctx.strTable, v); // @ts-ignore dump fn // deno-lint-ignore no-console - console.log( - ` ${name}: ${getString(ctx.strTable, v)} (${kindName}, ${prop})`, - ); + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Number) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Regex) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); } else if (kind === PropFlags.Null) { // @ts-ignore dump fn // deno-lint-ignore no-console @@ -1093,8 +1040,27 @@ function _dump(ctx) { // @ts-ignore dump fn // deno-lint-ignore no-console console.log(` ${name}: undefined (${kindName}, ${prop})`); + } else if (kind === PropFlags.BigInt) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Obj) { + let offset = v + ctx.propsOffset; + const count = readU32(ctx.buf, offset); + offset += 4; + + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log( + ` ${name}: Object (${count}) (${kindName}, ${prop}), raw offset ${v}`, + ); + + // TODO(@marvinhagemeister): Show object } } + + idx++; } } @@ -1107,9 +1073,10 @@ function _dump(ctx) { */ function runLintPlugin(plugin, fileName, sourceText) { installPlugin(plugin); - const serializedAst = op_lint_create_serialized_ast(fileName, sourceText); try { + const serializedAst = op_lint_create_serialized_ast(fileName, sourceText); + runPluginsForFile(fileName, serializedAst); } finally { // During testing we don't want to keep plugins around diff --git a/cli/js/40_lint_selector.js b/cli/js/40_lint_selector.js index c4cd523f9f..362130076a 100644 --- a/cli/js/40_lint_selector.js +++ b/cli/js/40_lint_selector.js @@ -744,8 +744,7 @@ export function compileSelector(selector) { fn = matchNthChild(node, fn); break; case PSEUDO_HAS: - // FIXME - // fn = matchIs(part, fn); + // TODO(@marvinhagemeister) throw new Error("TODO: :has"); case PSEUDO_NOT: fn = matchNot(node.selectors, fn); @@ -767,8 +766,7 @@ export function compileSelector(selector) { */ function matchFirstChild(next) { return (ctx, id) => { - const parent = ctx.getParent(id); - const first = ctx.getFirstChild(parent); + const first = ctx.getFirstChild(id); return first === id && next(ctx, first); }; } @@ -779,8 +777,7 @@ function matchFirstChild(next) { */ function matchLastChild(next) { return (ctx, id) => { - const parent = ctx.getParent(id); - const last = ctx.getLastChild(parent); + const last = ctx.getLastChild(id); return last === id && next(ctx, id); }; } @@ -955,7 +952,9 @@ function matchElem(part, next) { else if (part.elem === 0) return false; const type = ctx.getType(id); - if (type > 0 && type === part.elem) return next(ctx, id); + if (type > 0 && type === part.elem) { + return next(ctx, id); + } return false; }; @@ -968,7 +967,16 @@ function matchElem(part, next) { */ function matchAttrExists(attr, next) { return (ctx, id) => { - return ctx.hasAttrPath(id, attr.prop, 0) ? next(ctx, id) : false; + try { + ctx.getAttrPathValue(id, attr.prop, 0); + return next(ctx, id); + } catch (err) { + if (err === -1) { + return false; + } + + throw err; + } }; } @@ -979,9 +987,15 @@ function matchAttrExists(attr, next) { */ function matchAttrBin(attr, next) { return (ctx, id) => { - if (!ctx.hasAttrPath(id, attr.prop, 0)) return false; - const value = ctx.getAttrPathValue(id, attr.prop, 0); - if (!matchAttrValue(attr, value)) return false; + try { + const value = ctx.getAttrPathValue(id, attr.prop, 0); + if (!matchAttrValue(attr, value)) return false; + } catch (err) { + if (err === -1) { + return false; + } + throw err; + } return next(ctx, id); }; } diff --git a/cli/js/40_lint_types.d.ts b/cli/js/40_lint_types.d.ts index db2202981a..f07d16581e 100644 --- a/cli/js/40_lint_types.d.ts +++ b/cli/js/40_lint_types.d.ts @@ -12,6 +12,8 @@ export interface AstContext { strTableOffset: number; rootOffset: number; nodes: Map; + spansOffset: number; + propsOffset: number; strByType: number[]; strByProp: number[]; typeByStr: Map; @@ -19,6 +21,12 @@ export interface AstContext { matcher: MatchContext; } +export interface Node { + range: Range; +} + +export type Range = [number, number]; + // TODO(@marvinhagemeister) Remove once we land "official" types export interface RuleContext { id: string; @@ -121,7 +129,6 @@ export interface MatchContext { getSiblings(id: number): number[]; getParent(id: number): number; getType(id: number): number; - hasAttrPath(id: number, propIds: number[], idx: number): boolean; getAttrPathValue(id: number, propIds: number[], idx: number): unknown; } diff --git a/cli/tools/lint/ast_buffer/buffer.rs b/cli/tools/lint/ast_buffer/buffer.rs index 517c3b14dc..a884ee24f9 100644 --- a/cli/tools/lint/ast_buffer/buffer.rs +++ b/cli/tools/lint/ast_buffer/buffer.rs @@ -14,9 +14,14 @@ pub enum PropFlags { Ref, RefArr, String, + Number, Bool, Null, Undefined, + Object, + Regex, + BigInt, + Array, } impl From for u8 { @@ -33,21 +38,29 @@ impl TryFrom for PropFlags { 0 => Ok(PropFlags::Ref), 1 => Ok(PropFlags::RefArr), 2 => Ok(PropFlags::String), - 3 => Ok(PropFlags::Bool), - 4 => Ok(PropFlags::Null), - 5 => Ok(PropFlags::Undefined), + 3 => Ok(PropFlags::Number), + 4 => Ok(PropFlags::Bool), + 5 => Ok(PropFlags::Null), + 6 => Ok(PropFlags::Undefined), + 7 => Ok(PropFlags::Object), + 8 => Ok(PropFlags::Regex), + 9 => Ok(PropFlags::BigInt), + 10 => Ok(PropFlags::Array), _ => Err("Unknown Prop flag"), } } } +pub type Index = u32; + +const GROUP_KIND: u8 = 1; const MASK_U32_1: u32 = 0b11111111_00000000_00000000_00000000; const MASK_U32_2: u32 = 0b00000000_11111111_00000000_00000000; const MASK_U32_3: u32 = 0b00000000_00000000_11111111_00000000; const MASK_U32_4: u32 = 0b00000000_00000000_00000000_11111111; -// TODO: There is probably a native Rust function to do this. -pub fn append_u32(result: &mut Vec, value: u32) { +#[inline] +fn append_u32(result: &mut Vec, value: u32) { let v1: u8 = ((value & MASK_U32_1) >> 24) as u8; let v2: u8 = ((value & MASK_U32_2) >> 16) as u8; let v3: u8 = ((value & MASK_U32_3) >> 8) as u8; @@ -59,25 +72,11 @@ pub fn append_u32(result: &mut Vec, value: u32) { result.push(v4); } -pub fn append_usize(result: &mut Vec, value: usize) { +fn append_usize(result: &mut Vec, value: usize) { let raw = u32::try_from(value).unwrap(); append_u32(result, raw); } -pub fn write_usize(result: &mut [u8], value: usize, idx: usize) { - let raw = u32::try_from(value).unwrap(); - - let v1: u8 = ((raw & MASK_U32_1) >> 24) as u8; - let v2: u8 = ((raw & MASK_U32_2) >> 16) as u8; - let v3: u8 = ((raw & MASK_U32_3) >> 8) as u8; - let v4: u8 = (raw & MASK_U32_4) as u8; - - result[idx] = v1; - result[idx + 1] = v2; - result[idx + 2] = v3; - result[idx + 3] = v4; -} - #[derive(Debug)] pub struct StringTable { id: usize, @@ -119,71 +118,47 @@ impl StringTable { } #[derive(Debug, Clone, Copy, PartialEq)] -pub struct NodeRef(pub usize); - -/// Represents an offset to a node whose schema hasn't been committed yet +pub struct NodeRef(pub Index); #[derive(Debug, Clone, Copy, PartialEq)] -pub struct PendingNodeRef(pub NodeRef); +pub struct PendingRef(pub Index); -#[derive(Debug)] -pub struct BoolPos(pub usize); -#[derive(Debug)] -pub struct FieldPos(pub usize); -#[derive(Debug)] -pub struct FieldArrPos(pub usize); -#[derive(Debug)] -pub struct StrPos(pub usize); -#[derive(Debug)] -pub struct UndefPos(pub usize); -#[derive(Debug)] -pub struct NullPos(pub usize); - -#[derive(Debug)] -pub enum NodePos { - Bool(BoolPos), - #[allow(dead_code)] - Field(FieldPos), - #[allow(dead_code)] - FieldArr(FieldArrPos), - Str(StrPos), - Undef(UndefPos), - #[allow(dead_code)] - Null(NullPos), +pub trait AstBufSerializer { + fn serialize(&mut self) -> Vec; } -pub trait AstBufSerializer -where - K: Into + Display, - P: Into + Display, -{ - fn header(&mut self, kind: K, parent: NodeRef, span: &Span) - -> PendingNodeRef; - fn ref_field(&mut self, prop: P) -> FieldPos; - fn ref_vec_field(&mut self, prop: P, len: usize) -> FieldArrPos; - fn str_field(&mut self, prop: P) -> StrPos; - fn bool_field(&mut self, prop: P) -> BoolPos; - fn undefined_field(&mut self, prop: P) -> UndefPos; - #[allow(dead_code)] - fn null_field(&mut self, prop: P) -> NullPos; - fn commit_schema(&mut self, offset: PendingNodeRef) -> NodeRef; - - fn write_ref(&mut self, pos: FieldPos, value: NodeRef); - fn write_maybe_ref(&mut self, pos: FieldPos, value: Option); - fn write_refs(&mut self, pos: FieldArrPos, value: Vec); - fn write_str(&mut self, pos: StrPos, value: &str); - fn write_bool(&mut self, pos: BoolPos, value: bool); - - fn serialize(&mut self) -> Vec; +/// +/// +/// +/// +/// +#[derive(Debug)] +struct Node { + kind: u8, + prop_offset: u32, + child: u32, + next: u32, + parent: u32, } #[derive(Debug)] pub struct SerializeCtx { - buf: Vec, - start_buf: NodeRef, + root_idx: Index, + + nodes: Vec, + prop_stack: Vec>, + field_count: Vec, + field_buf: Vec, + prev_sibling_stack: Vec, + + /// Vec of spans + spans: Vec, + + /// Maps string id to the actual string str_table: StringTable, - kind_map: Vec, - prop_map: Vec, - field_count: u8, + /// Maps kind id to string id + kind_name_map: Vec, + /// Maps prop id to string id + prop_name_map: Vec, } /// This is the internal context used to allocate and fill the buffer. The point @@ -198,20 +173,24 @@ impl SerializeCtx { let kind_size = kind_len as usize; let prop_size = prop_len as usize; let mut ctx = Self { - start_buf: NodeRef(0), - buf: vec![], + spans: Vec::with_capacity(512), + root_idx: 0, + nodes: Vec::with_capacity(512), + prop_stack: vec![vec![]], + prev_sibling_stack: vec![0], + field_count: vec![0], + field_buf: Vec::with_capacity(1024), str_table: StringTable::new(), - kind_map: vec![0; kind_size], - prop_map: vec![0; prop_size], - field_count: 0, + kind_name_map: vec![0; kind_size], + prop_name_map: vec![0; prop_size], }; let empty_str = ctx.str_table.insert(""); // Placeholder node is always 0 - ctx.append_node(0, NodeRef(0), &DUMMY_SP, 0); - ctx.kind_map[0] = empty_str; - ctx.start_buf = NodeRef(ctx.buf.len()); + ctx.append_node(0, &DUMMY_SP); + ctx.kind_name_map[0] = empty_str; + ctx.kind_name_map[1] = empty_str; // Insert default props that are always present let type_str = ctx.str_table.insert("type"); @@ -220,258 +199,306 @@ impl SerializeCtx { let length_str = ctx.str_table.insert("length"); // These values are expected to be in this order on the JS side - ctx.prop_map[0] = empty_str; - ctx.prop_map[1] = type_str; - ctx.prop_map[2] = parent_str; - ctx.prop_map[3] = range_str; - ctx.prop_map[4] = length_str; + ctx.prop_name_map[0] = empty_str; + ctx.prop_name_map[1] = type_str; + ctx.prop_name_map[2] = parent_str; + ctx.prop_name_map[3] = range_str; + ctx.prop_name_map[4] = length_str; ctx } + pub fn set_root_idx(&mut self, idx: Index) { + self.root_idx = idx; + } + /// Allocate a node's header - fn field_header

(&mut self, prop: P, prop_flags: PropFlags) -> usize + fn field_header

(&mut self, prop: P, prop_flags: PropFlags) where P: Into + Display + Clone, { - self.field_count += 1; - - let offset = self.buf.len(); - + let flags: u8 = prop_flags.into(); let n: u8 = prop.clone().into(); - self.buf.push(n); - if let Some(v) = self.prop_map.get::(n.into()) { + if let Some(v) = self.prop_name_map.get::(n.into()) { if *v == 0 { let id = self.str_table.insert(&format!("{prop}")); - self.prop_map[n as usize] = id; + self.prop_name_map[n as usize] = id; } } - let flags: u8 = prop_flags.into(); - self.buf.push(flags); + // Increment field counter + let idx = self.field_count.len() - 1; + let count = self.field_count[idx]; + self.field_count[idx] = count + 1; - offset + let buf = self.prop_stack.last_mut().unwrap(); + buf.push(n); + buf.push(flags); } - /// Allocate a property pointing to another node. - fn field

(&mut self, prop: P, prop_flags: PropFlags) -> usize + fn get_node(&mut self, id: Index) -> &mut Node { + self.nodes.get_mut(id as usize).unwrap() + } + + fn set_parent(&mut self, child_id: Index, parent_id: Index) { + let child = self.get_node(child_id); + child.parent = parent_id; + } + + fn set_child(&mut self, parent_id: Index, child_id: Index) { + let parent = self.get_node(parent_id); + parent.child = child_id; + } + + fn set_next(&mut self, node_id: Index, next_id: Index) { + let node = self.get_node(node_id); + node.next = next_id; + } + + fn update_ref_links(&mut self, parent_id: Index, child_id: Index) { + let last_idx = self.prev_sibling_stack.len() - 1; + let parent = self.get_node(parent_id); + if parent.child == 0 { + parent.child = child_id; + } else { + let prev_id = self.prev_sibling_stack[last_idx]; + self.set_next(prev_id, child_id); + } + + self.prev_sibling_stack[last_idx] = child_id; + self.set_parent(child_id, parent_id); + } + + pub fn append_node(&mut self, kind: K, span: &Span) -> PendingRef where - P: Into + Display + Clone, + K: Into + Display + Clone, { - let offset = self.field_header(prop, prop_flags); - - append_usize(&mut self.buf, 0); - - offset + self.append_inner(kind, span.lo.0, span.hi.0) } - fn append_node( + pub fn append_inner( &mut self, - kind: u8, - parent: NodeRef, - span: &Span, - prop_count: usize, - ) -> PendingNodeRef { - let offset = self.buf.len(); - - // Node type fits in a u8 - self.buf.push(kind); - - // Offset to the parent node. Will be 0 if none exists - append_usize(&mut self.buf, parent.0); - - // Span, the start and end location of this node - append_u32(&mut self.buf, span.lo.0); - append_u32(&mut self.buf, span.hi.0); - - // No node has more than <10 properties - debug_assert!(prop_count < 10); - self.buf.push(prop_count as u8); - - PendingNodeRef(NodeRef(offset)) - } - - pub fn commit_schema(&mut self, node_ref: PendingNodeRef) -> NodeRef { - let mut offset = node_ref.0 .0; - - // type + parentId + span lo + span hi - offset += 1 + 4 + 4 + 4; - - self.buf[offset] = self.field_count; - self.field_count = 0; - - node_ref.0 - } - - /// Allocate the node header. It's always the same for every node. - /// - /// - /// - /// - /// (There is no node with more than 10 properties) - pub fn header( - &mut self, - kind: N, - parent: NodeRef, - span: &Span, - ) -> PendingNodeRef + kind: K, + span_lo: u32, + span_hi: u32, + ) -> PendingRef where - N: Into + Display + Clone, + K: Into + Display + Clone, { - let n: u8 = kind.clone().into(); + let kind_u8: u8 = kind.clone().into(); - if let Some(v) = self.kind_map.get::(n.into()) { + let id: Index = self.nodes.len() as u32; + + self.nodes.push(Node { + kind: kind_u8, + prop_offset: 0, + child: 0, + next: 0, + parent: 0, + }); + + if let Some(v) = self.kind_name_map.get::(kind_u8.into()) { if *v == 0 { - let id = self.str_table.insert(&format!("{kind}")); - self.kind_map[n as usize] = id; + let s_id = self.str_table.insert(&format!("{kind}")); + self.kind_name_map[kind_u8 as usize] = s_id; } } - // Prop count will be filled with the actual value when the - // schema is committed. - self.append_node(n, parent, span, 0) + self.field_count.push(0); + self.prop_stack.push(vec![]); + self.prev_sibling_stack.push(0); + + // write spans + self.spans.push(span_lo); + self.spans.push(span_hi); + + PendingRef(id) } - /// Allocate a reference property that will hold the offset of - /// another node. - pub fn ref_field

(&mut self, prop: P) -> usize + pub fn commit_node(&mut self, id: PendingRef) -> NodeRef { + let mut buf = self.prop_stack.pop().unwrap(); + let count = self.field_count.pop().unwrap(); + let offset = self.field_buf.len(); + + // All nodes have <10 fields + self.field_buf.push(count as u8); + self.field_buf.append(&mut buf); + + let node = self.nodes.get_mut(id.0 as usize).unwrap(); + node.prop_offset = offset as u32; + + self.prev_sibling_stack.pop(); + + NodeRef(id.0) + } + + // Allocate an object field + pub fn open_obj(&mut self) { + self.field_count.push(0); + self.prop_stack.push(vec![]); + } + + pub fn commit_obj

(&mut self, prop: P) where P: Into + Display + Clone, { - self.field(prop, PropFlags::Ref) + let mut buf = self.prop_stack.pop().unwrap(); + let count = self.field_count.pop().unwrap(); + let offset = self.field_buf.len(); + append_usize(&mut self.field_buf, count); + self.field_buf.append(&mut buf); + + self.field_header(prop, PropFlags::Object); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, offset); } - /// Allocate a property that is a vec of node offsets pointing to other - /// nodes. - pub fn ref_vec_field

(&mut self, prop: P, len: usize) -> usize + /// Allocate an null field + pub fn write_null

(&mut self, prop: P) where P: Into + Display + Clone, { - let offset = self.field(prop, PropFlags::RefArr); + self.field_header(prop, PropFlags::Null); - for _ in 0..len { - append_u32(&mut self.buf, 0); - } - - offset + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, 0); } - // Allocate a property representing a string. Strings are deduplicated - // in the message and the property will only contain the string id. - pub fn str_field

(&mut self, prop: P) -> usize + /// Allocate an null field + pub fn write_undefined

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

(&mut self, prop: P) -> usize + /// Allocate a number field + pub fn write_num

(&mut self, prop: P, value: &str) where P: Into + Display + Clone, { - let offset = self.field_header(prop, PropFlags::Bool); - self.buf.push(0); - offset + self.field_header(prop, PropFlags::Number); + + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); } - /// Allocate an undefined field - pub fn undefined_field

(&mut self, prop: P) -> usize + /// Allocate a bigint field + pub fn write_bigint

(&mut self, prop: P, value: &str) where P: Into + Display + Clone, { - self.field_header(prop, PropFlags::Undefined) + self.field_header(prop, PropFlags::BigInt); + + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); } - /// Allocate an undefined field - #[allow(dead_code)] - pub fn null_field

(&mut self, prop: P) -> usize + /// Allocate a RegExp field + pub fn write_regex

(&mut self, prop: P, value: &str) where P: Into + Display + Clone, { - self.field_header(prop, PropFlags::Null) - } + self.field_header(prop, PropFlags::Regex); - /// Replace the placeholder of a reference field with the actual offset - /// to the node we want to point to. - pub fn write_ref(&mut self, field_offset: usize, value: NodeRef) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::Ref { - panic!("Trying to write a ref into a non-ref field") - } - } - - write_usize(&mut self.buf, value.0, field_offset + 2); - } - - /// Helper for writing optional node offsets - pub fn write_maybe_ref( - &mut self, - field_offset: usize, - value: Option, - ) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::Ref { - panic!("Trying to write a ref into a non-ref field") - } - } - - let ref_value = if let Some(v) = value { v } else { NodeRef(0) }; - write_usize(&mut self.buf, ref_value.0, field_offset + 2); - } - - /// Write a vec of node offsets into the property. The necessary space - /// has been reserved earlier. - pub fn write_refs(&mut self, field_offset: usize, value: Vec) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::RefArr { - panic!("Trying to write a ref into a non-ref array field") - } - } - - let mut offset = field_offset + 2; - write_usize(&mut self.buf, value.len(), offset); - offset += 4; - - for item in value { - write_usize(&mut self.buf, item.0, offset); - offset += 4; - } + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); } /// Store the string in our string table and save the id of the string /// in the current field. - pub fn write_str(&mut self, field_offset: usize, value: &str) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::String { - panic!("Trying to write a ref into a non-string field") - } - } + pub fn write_str

(&mut self, prop: P, value: &str) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::String); let id = self.str_table.insert(value); - write_usize(&mut self.buf, id, field_offset + 2); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); } /// Write a bool to a field. - pub fn write_bool(&mut self, field_offset: usize, value: bool) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::Bool { - panic!("Trying to write a ref into a non-bool field") - } - } + pub fn write_bool

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

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

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

( + &mut self, + prop: P, + parent_ref: &PendingRef, + value: Vec, + ) where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::RefArr); + let group_id = self.append_node(GROUP_KIND, &DUMMY_SP); + let group_id = self.commit_node(group_id).0; + + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, group_id); + + self.update_ref_links(parent_ref.0, group_id); + + let mut prev_id = 0; + for (i, item) in value.iter().enumerate() { + self.set_parent(item.0, group_id); + + if i == 0 { + self.set_child(group_id, item.0); + } else { + self.set_next(prev_id, item.0); + } + + prev_id = item.0; + } } /// Serialize all information we have into a buffer that can be sent to JS. @@ -481,6 +508,8 @@ impl SerializeCtx { /// /// <- node kind id maps to string id /// <- node property id maps to string id + /// <- List of spans, rarely needed + /// /// /// /// @@ -490,7 +519,13 @@ impl SerializeCtx { // The buffer starts with the serialized AST first, because that // contains absolute offsets. By butting this at the start of the // message we don't have to waste time updating any offsets. - buf.append(&mut self.buf); + for node in &self.nodes { + buf.push(node.kind); + append_u32(&mut buf, node.prop_offset); + append_u32(&mut buf, node.child); + append_u32(&mut buf, node.next); + append_u32(&mut buf, node.parent); + } // Next follows the string table. We'll keep track of the offset // in the message of where the string table begins @@ -507,8 +542,8 @@ impl SerializeCtx { // Write the total number of entries in the kind -> str mapping table // TODO: make this a u8 - append_usize(&mut buf, self.kind_map.len()); - for v in &self.kind_map { + append_usize(&mut buf, self.kind_name_map.len()); + for v in &self.kind_name_map { append_usize(&mut buf, *v); } @@ -517,19 +552,35 @@ impl SerializeCtx { // as u8. let offset_prop_map = buf.len(); // Write the total number of entries in the kind -> str mapping table - append_usize(&mut buf, self.prop_map.len()); - for v in &self.prop_map { + append_usize(&mut buf, self.prop_name_map.len()); + for v in &self.prop_name_map { append_usize(&mut buf, *v); } + // Spans are rarely needed, so they're stored in a separate array. + // They're indexed by the node id. + let offset_spans = buf.len(); + for v in &self.spans { + append_u32(&mut buf, *v); + } + + // The field value table. They're detached from nodes as they're not + // as frequently needed as the nodes themselves. The most common + // operation is traversal and we can traverse nodes without knowing + // about the fields. + let offset_props = buf.len(); + buf.append(&mut self.field_buf); + // Putting offsets of relevant parts of the buffer at the end. This // allows us to hop to the relevant part by merely looking at the last // for values in the message. Each value represents an offset into the // buffer. + append_usize(&mut buf, offset_props); + append_usize(&mut buf, offset_spans); append_usize(&mut buf, offset_kind_map); append_usize(&mut buf, offset_prop_map); append_usize(&mut buf, offset_str_table); - append_usize(&mut buf, self.start_buf.0); + append_u32(&mut buf, self.root_idx); buf } diff --git a/cli/tools/lint/ast_buffer/swc.rs b/cli/tools/lint/ast_buffer/swc.rs index 7652756c9b..925d1bcd17 100644 --- a/cli/tools/lint/ast_buffer/swc.rs +++ b/cli/tools/lint/ast_buffer/swc.rs @@ -2,10 +2,13 @@ use deno_ast::swc::ast::AssignTarget; use deno_ast::swc::ast::AssignTargetPat; +use deno_ast::swc::ast::BindingIdent; use deno_ast::swc::ast::BlockStmtOrExpr; use deno_ast::swc::ast::Callee; use deno_ast::swc::ast::ClassMember; use deno_ast::swc::ast::Decl; +use deno_ast::swc::ast::Decorator; +use deno_ast::swc::ast::DefaultDecl; use deno_ast::swc::ast::ExportSpecifier; use deno_ast::swc::ast::Expr; use deno_ast::swc::ast::ExprOrSpread; @@ -14,13 +17,13 @@ use deno_ast::swc::ast::ForHead; use deno_ast::swc::ast::Function; use deno_ast::swc::ast::Ident; use deno_ast::swc::ast::IdentName; +use deno_ast::swc::ast::ImportSpecifier; use deno_ast::swc::ast::JSXAttrName; use deno_ast::swc::ast::JSXAttrOrSpread; use deno_ast::swc::ast::JSXAttrValue; use deno_ast::swc::ast::JSXElement; use deno_ast::swc::ast::JSXElementChild; use deno_ast::swc::ast::JSXElementName; -use deno_ast::swc::ast::JSXEmptyExpr; use deno_ast::swc::ast::JSXExpr; use deno_ast::swc::ast::JSXExprContainer; use deno_ast::swc::ast::JSXFragment; @@ -28,12 +31,14 @@ use deno_ast::swc::ast::JSXMemberExpr; use deno_ast::swc::ast::JSXNamespacedName; use deno_ast::swc::ast::JSXObject; use deno_ast::swc::ast::JSXOpeningElement; +use deno_ast::swc::ast::Key; use deno_ast::swc::ast::Lit; use deno_ast::swc::ast::MemberExpr; use deno_ast::swc::ast::MemberProp; use deno_ast::swc::ast::ModuleDecl; use deno_ast::swc::ast::ModuleExportName; use deno_ast::swc::ast::ModuleItem; +use deno_ast::swc::ast::ObjectLit; use deno_ast::swc::ast::ObjectPatProp; use deno_ast::swc::ast::OptChainBase; use deno_ast::swc::ast::Param; @@ -47,14 +52,18 @@ use deno_ast::swc::ast::PropOrSpread; use deno_ast::swc::ast::SimpleAssignTarget; use deno_ast::swc::ast::Stmt; use deno_ast::swc::ast::SuperProp; -use deno_ast::swc::ast::Tpl; use deno_ast::swc::ast::TsEntityName; use deno_ast::swc::ast::TsEnumMemberId; +use deno_ast::swc::ast::TsExprWithTypeArgs; use deno_ast::swc::ast::TsFnOrConstructorType; use deno_ast::swc::ast::TsFnParam; use deno_ast::swc::ast::TsIndexSignature; use deno_ast::swc::ast::TsLit; use deno_ast::swc::ast::TsLitType; +use deno_ast::swc::ast::TsModuleName; +use deno_ast::swc::ast::TsModuleRef; +use deno_ast::swc::ast::TsNamespaceBody; +use deno_ast::swc::ast::TsParamPropParam; use deno_ast::swc::ast::TsThisTypeOrIdent; use deno_ast::swc::ast::TsType; use deno_ast::swc::ast::TsTypeAnn; @@ -71,7 +80,7 @@ use deno_ast::swc::common::SyntaxContext; use deno_ast::view::Accessibility; use deno_ast::view::AssignOp; use deno_ast::view::BinaryOp; -use deno_ast::view::TruePlusMinus; +use deno_ast::view::MethodKind; use deno_ast::view::TsKeywordTypeKind; use deno_ast::view::TsTypeOperatorOp; use deno_ast::view::UnaryOp; @@ -80,53 +89,39 @@ use deno_ast::view::VarDeclKind; use deno_ast::ParsedSource; use super::buffer::AstBufSerializer; -use super::buffer::BoolPos; -use super::buffer::NodePos; use super::buffer::NodeRef; -use super::buffer::StrPos; use super::ts_estree::AstNode; -use super::ts_estree::AstProp; use super::ts_estree::TsEsTreeBuilder; +use super::ts_estree::TsKeywordKind; pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec { let mut ctx = TsEsTreeBuilder::new(); let program = &parsed_source.program(); - let raw = ctx.header(AstNode::Program, NodeRef(0), &program.span()); - let source_type_pos = ctx.str_field(AstProp::SourceType); - match program.as_ref() { Program::Module(module) => { - let body_pos = ctx.ref_vec_field(AstProp::Body, module.body.len()); - let pos = ctx.commit_schema(raw); - let children = module .body .iter() .map(|item| match item { ModuleItem::ModuleDecl(module_decl) => { - serialize_module_decl(&mut ctx, module_decl, pos) + serialize_module_decl(&mut ctx, module_decl) } - ModuleItem::Stmt(stmt) => serialize_stmt(&mut ctx, stmt, pos), + ModuleItem::Stmt(stmt) => serialize_stmt(&mut ctx, stmt), }) .collect::>(); - ctx.write_str(source_type_pos, "module"); - ctx.write_refs(body_pos, children); + ctx.write_program(&module.span, "module", children); } Program::Script(script) => { - let body_pos = ctx.ref_vec_field(AstProp::Body, script.body.len()); - let pos = ctx.commit_schema(raw); - let children = script .body .iter() - .map(|stmt| serialize_stmt(&mut ctx, stmt, pos)) + .map(|stmt| serialize_stmt(&mut ctx, stmt)) .collect::>(); - ctx.write_str(source_type_pos, "script"); - ctx.write_refs(body_pos, children); + ctx.write_program(&script.span, "script", children); } } @@ -136,539 +131,521 @@ pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec { fn serialize_module_decl( ctx: &mut TsEsTreeBuilder, module_decl: &ModuleDecl, - parent: NodeRef, ) -> NodeRef { match module_decl { ModuleDecl::Import(node) => { - let raw = ctx.header(AstNode::ImportExpression, parent, &node.span); - ctx.commit_schema(raw) - } - ModuleDecl::ExportDecl(node) => { - let raw = ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span); - let decl_pos = ctx.ref_field(AstProp::Declarations); - let pos = ctx.commit_schema(raw); + let src = serialize_lit(ctx, &Lit::Str(node.src.as_ref().clone())); + let attrs = serialize_import_attrs(ctx, &node.with); - let decl = serialize_decl(ctx, &node.decl, pos); - - ctx.write_ref(decl_pos, decl); - - pos - } - ModuleDecl::ExportNamed(node) => { - let raw = ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span); - let src_pos = ctx.ref_field(AstProp::Source); - let spec_pos = - ctx.ref_vec_field(AstProp::Specifiers, node.specifiers.len()); - let id = ctx.commit_schema(raw); - - // FIXME: Flags - // let mut flags = FlagValue::new(); - // flags.set(Flag::ExportType); - - let src_id = node - .src - .as_ref() - .map(|src| serialize_lit(ctx, &Lit::Str(*src.clone()), id)); - - let spec_ids = node + let specifiers = node .specifiers .iter() - .map(|spec| { - match spec { - ExportSpecifier::Named(child) => { - let raw = ctx.header(AstNode::ExportSpecifier, id, &child.span); - let local_pos = ctx.ref_field(AstProp::Local); - let exp_pos = ctx.ref_field(AstProp::Exported); - let spec_pos = ctx.commit_schema(raw); - - // let mut flags = FlagValue::new(); - // flags.set(Flag::ExportType); - - let local = - serialize_module_exported_name(ctx, &child.orig, spec_pos); - - let exported = child.exported.as_ref().map(|exported| { - serialize_module_exported_name(ctx, exported, spec_pos) + .map(|spec| match spec { + ImportSpecifier::Named(spec) => { + let local = serialize_ident(ctx, &spec.local, None); + let imported = spec + .imported + .as_ref() + .map_or(serialize_ident(ctx, &spec.local, None), |v| { + serialize_module_export_name(ctx, v) }); - - // ctx.write_flags(&flags); - ctx.write_ref(local_pos, local); - ctx.write_maybe_ref(exp_pos, exported); - - spec_pos - } - - // These two aren't syntactically valid - ExportSpecifier::Namespace(_) => todo!(), - ExportSpecifier::Default(_) => todo!(), + ctx.write_import_spec( + &spec.span, + spec.is_type_only, + local, + imported, + ) + } + ImportSpecifier::Default(spec) => { + let local = serialize_ident(ctx, &spec.local, None); + ctx.write_import_default_spec(&spec.span, local) + } + ImportSpecifier::Namespace(spec) => { + let local = serialize_ident(ctx, &spec.local, None); + ctx.write_import_ns_spec(&spec.span, local) } }) .collect::>(); - // ctx.write_flags(&flags); - ctx.write_maybe_ref(src_pos, src_id); - ctx.write_refs(spec_pos, spec_ids); + ctx.write_import_decl(&node.span, node.type_only, src, specifiers, attrs) + } + ModuleDecl::ExportDecl(node) => { + let decl = serialize_decl(ctx, &node.decl); + ctx.write_export_decl(&node.span, decl) + } + ModuleDecl::ExportNamed(node) => { + let attrs = serialize_import_attrs(ctx, &node.with); + let source = node + .src + .as_ref() + .map(|src| serialize_lit(ctx, &Lit::Str(*src.clone()))); - id + if let Some(ExportSpecifier::Namespace(ns)) = node.specifiers.first() { + let exported = serialize_module_export_name(ctx, &ns.name); + ctx.write_export_all_decl( + &node.span, + node.type_only, + exported, + source, + attrs, + ) + } else { + let specifiers = node + .specifiers + .iter() + .map(|spec| { + match spec { + ExportSpecifier::Named(spec) => { + let local = serialize_module_export_name(ctx, &spec.orig); + + let exported = spec.exported.as_ref().map_or( + serialize_module_export_name(ctx, &spec.orig), + |exported| serialize_module_export_name(ctx, exported), + ); + + ctx.write_export_spec( + &spec.span, + spec.is_type_only, + local, + exported, + ) + } + + // Already handled earlier + ExportSpecifier::Namespace(_) => unreachable!(), + // this is not syntactically valid + ExportSpecifier::Default(_) => unreachable!(), + } + }) + .collect::>(); + + ctx.write_export_named_decl(&node.span, specifiers, source, attrs) + } } ModuleDecl::ExportDefaultDecl(node) => { - let raw = - ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span); - ctx.commit_schema(raw) + let (is_type_only, decl) = match &node.decl { + DefaultDecl::Class(node) => { + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let super_class = node + .class + .super_class + .as_ref() + .map(|expr| serialize_expr(ctx, expr.as_ref())); + + let implements = node + .class + .implements + .iter() + .map(|item| serialize_ts_expr_with_type_args(ctx, item)) + .collect::>(); + + let members = node + .class + .body + .iter() + .filter_map(|member| serialize_class_member(ctx, member)) + .collect::>(); + + let body = ctx.write_class_body(&node.class.span, members); + + let decl = ctx.write_class_decl( + &node.class.span, + false, + node.class.is_abstract, + ident, + super_class, + implements, + body, + ); + + (false, decl) + } + DefaultDecl::Fn(node) => { + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let fn_obj = node.function.as_ref(); + + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &fn_obj.type_params); + + let params = fn_obj + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat)) + .collect::>(); + + let return_type = + maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type); + let body = fn_obj + .body + .as_ref() + .map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone()))); + + let decl = ctx.write_fn_decl( + &fn_obj.span, + false, + fn_obj.is_async, + fn_obj.is_generator, + ident, + type_params, + return_type, + body, + params, + ); + + (false, decl) + } + DefaultDecl::TsInterfaceDecl(node) => { + let ident_id = serialize_ident(ctx, &node.id, None); + let type_param = + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); + + let extend_ids = node + .extends + .iter() + .map(|item| { + let expr = serialize_expr(ctx, &item.expr); + let type_args = item + .type_args + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); + + ctx.write_ts_interface_heritage(&item.span, expr, type_args) + }) + .collect::>(); + + let body_elem_ids = node + .body + .body + .iter() + .map(|item| serialize_ts_type_elem(ctx, item)) + .collect::>(); + + let body_pos = + ctx.write_ts_interface_body(&node.body.span, body_elem_ids); + + let decl = ctx.write_ts_interface_decl( + &node.span, + node.declare, + ident_id, + type_param, + extend_ids, + body_pos, + ); + + (true, decl) + } + }; + + ctx.write_export_default_decl(&node.span, is_type_only, decl) } ModuleDecl::ExportDefaultExpr(node) => { - let raw = - ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span); - ctx.commit_schema(raw) + let expr = serialize_expr(ctx, &node.expr); + ctx.write_export_default_decl(&node.span, false, expr) } ModuleDecl::ExportAll(node) => { - let raw = ctx.header(AstNode::ExportAllDeclaration, parent, &node.span); - ctx.commit_schema(raw) + let src = serialize_lit(ctx, &Lit::Str(node.src.as_ref().clone())); + let attrs = serialize_import_attrs(ctx, &node.with); + + ctx.write_export_all_decl(&node.span, node.type_only, src, None, attrs) } ModuleDecl::TsImportEquals(node) => { - let raw = ctx.header(AstNode::TsImportEquals, parent, &node.span); - ctx.commit_schema(raw) + let ident = serialize_ident(ctx, &node.id, None); + let module_ref = match &node.module_ref { + TsModuleRef::TsEntityName(entity) => { + serialize_ts_entity_name(ctx, entity) + } + TsModuleRef::TsExternalModuleRef(external) => { + let expr = serialize_lit(ctx, &Lit::Str(external.expr.clone())); + ctx.write_ts_external_mod_ref(&external.span, expr) + } + }; + + ctx.write_export_ts_import_equals( + &node.span, + node.is_type_only, + ident, + module_ref, + ) } ModuleDecl::TsExportAssignment(node) => { - let raw = ctx.header(AstNode::TsExportAssignment, parent, &node.span); - ctx.commit_schema(raw) + let expr = serialize_expr(ctx, &node.expr); + ctx.write_export_assign(&node.span, expr) } ModuleDecl::TsNamespaceExport(node) => { - let raw = ctx.header(AstNode::TsNamespaceExport, parent, &node.span); - ctx.commit_schema(raw) + let decl = serialize_ident(ctx, &node.id, None); + ctx.write_export_ts_namespace(&node.span, decl) } } } -fn serialize_stmt( +fn serialize_import_attrs( ctx: &mut TsEsTreeBuilder, - stmt: &Stmt, - parent: NodeRef, -) -> NodeRef { + raw_attrs: &Option>, +) -> Vec { + raw_attrs.as_ref().map_or(vec![], |obj| { + obj + .props + .iter() + .map(|prop| { + let (key, value) = match prop { + // Invalid syntax + PropOrSpread::Spread(_) => unreachable!(), + PropOrSpread::Prop(prop) => { + match prop.as_ref() { + Prop::Shorthand(ident) => ( + serialize_ident(ctx, ident, None), + serialize_ident(ctx, ident, None), + ), + Prop::KeyValue(kv) => ( + serialize_prop_name(ctx, &kv.key), + serialize_expr(ctx, kv.value.as_ref()), + ), + // Invalid syntax + Prop::Assign(_) + | Prop::Getter(_) + | Prop::Setter(_) + | Prop::Method(_) => unreachable!(), + } + } + }; + + ctx.write_import_attr(&prop.span(), key, value) + }) + .collect::>() + }) +} + +fn serialize_stmt(ctx: &mut TsEsTreeBuilder, stmt: &Stmt) -> NodeRef { match stmt { Stmt::Block(node) => { - let raw = ctx.header(AstNode::BlockStatement, parent, &node.span); - let body_pos = ctx.ref_vec_field(AstProp::Body, node.stmts.len()); - let pos = ctx.commit_schema(raw); - let children = node .stmts .iter() - .map(|stmt| serialize_stmt(ctx, stmt, pos)) + .map(|stmt| serialize_stmt(ctx, stmt)) .collect::>(); - ctx.write_refs(body_pos, children); - - pos + ctx.write_block_stmt(&node.span, children) } Stmt::Empty(_) => NodeRef(0), - Stmt::Debugger(node) => { - let raw = ctx.header(AstNode::DebuggerStatement, parent, &node.span); - ctx.commit_schema(raw) + Stmt::Debugger(node) => ctx.write_debugger_stmt(&node.span), + Stmt::With(node) => { + let obj = serialize_expr(ctx, &node.obj); + let body = serialize_stmt(ctx, &node.body); + + ctx.write_with_stmt(&node.span, obj, body) } - Stmt::With(_) => todo!(), Stmt::Return(node) => { - let raw = ctx.header(AstNode::ReturnStatement, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let arg = node.arg.as_ref().map(|arg| serialize_expr(ctx, arg, pos)); - ctx.write_maybe_ref(arg_pos, arg); - - pos + let arg = node.arg.as_ref().map(|arg| serialize_expr(ctx, arg)); + ctx.write_return_stmt(&node.span, arg) } Stmt::Labeled(node) => { - let raw = ctx.header(AstNode::LabeledStatement, parent, &node.span); - let label_pos = ctx.ref_field(AstProp::Label); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let ident = serialize_ident(ctx, &node.label, None); + let stmt = serialize_stmt(ctx, &node.body); - let ident = serialize_ident(ctx, &node.label, pos); - let stmt = serialize_stmt(ctx, &node.body, pos); - - ctx.write_ref(label_pos, ident); - ctx.write_ref(body_pos, stmt); - - pos + ctx.write_labeled_stmt(&node.span, ident, stmt) } Stmt::Break(node) => { - let raw = ctx.header(AstNode::BreakStatement, parent, &node.span); - let label_pos = ctx.ref_field(AstProp::Label); - let pos = ctx.commit_schema(raw); - let arg = node .label .as_ref() - .map(|label| serialize_ident(ctx, label, pos)); - - ctx.write_maybe_ref(label_pos, arg); - - pos + .map(|label| serialize_ident(ctx, label, None)); + ctx.write_break_stmt(&node.span, arg) } Stmt::Continue(node) => { - let raw = ctx.header(AstNode::ContinueStatement, parent, &node.span); - let label_pos = ctx.ref_field(AstProp::Label); - let pos = ctx.commit_schema(raw); - let arg = node .label .as_ref() - .map(|label| serialize_ident(ctx, label, pos)); + .map(|label| serialize_ident(ctx, label, None)); - ctx.write_maybe_ref(label_pos, arg); - - pos + ctx.write_continue_stmt(&node.span, arg) } Stmt::If(node) => { - let raw = ctx.header(AstNode::IfStatement, parent, &node.span); - let test_pos = ctx.ref_field(AstProp::Test); - let cons_pos = ctx.ref_field(AstProp::Consequent); - let alt_pos = ctx.ref_field(AstProp::Alternate); - let pos = ctx.commit_schema(raw); + let test = serialize_expr(ctx, node.test.as_ref()); + let cons = serialize_stmt(ctx, node.cons.as_ref()); + let alt = node.alt.as_ref().map(|alt| serialize_stmt(ctx, alt)); - let test = serialize_expr(ctx, node.test.as_ref(), pos); - let cons = serialize_stmt(ctx, node.cons.as_ref(), pos); - let alt = node.alt.as_ref().map(|alt| serialize_stmt(ctx, alt, pos)); - - ctx.write_ref(test_pos, test); - ctx.write_ref(cons_pos, cons); - ctx.write_maybe_ref(alt_pos, alt); - - pos + ctx.write_if_stmt(&node.span, test, cons, alt) } Stmt::Switch(node) => { - let raw = ctx.header(AstNode::SwitchStatement, parent, &node.span); - let disc_pos = ctx.ref_field(AstProp::Discriminant); - let cases_pos = ctx.ref_vec_field(AstProp::Cases, node.cases.len()); - let pos = ctx.commit_schema(raw); - - let disc = serialize_expr(ctx, &node.discriminant, pos); + let disc = serialize_expr(ctx, &node.discriminant); let cases = node .cases .iter() .map(|case| { - let raw = ctx.header(AstNode::SwitchCase, pos, &case.span); - let test_pos = ctx.ref_field(AstProp::Test); - let cons_pos = - ctx.ref_vec_field(AstProp::Consequent, case.cons.len()); - let case_pos = ctx.commit_schema(raw); - - let test = case - .test - .as_ref() - .map(|test| serialize_expr(ctx, test, case_pos)); + let test = case.test.as_ref().map(|test| serialize_expr(ctx, test)); let cons = case .cons .iter() - .map(|cons| serialize_stmt(ctx, cons, case_pos)) + .map(|cons| serialize_stmt(ctx, cons)) .collect::>(); - ctx.write_maybe_ref(test_pos, test); - ctx.write_refs(cons_pos, cons); - - case_pos + ctx.write_switch_case(&case.span, test, cons) }) .collect::>(); - ctx.write_ref(disc_pos, disc); - ctx.write_refs(cases_pos, cases); - - pos + ctx.write_switch_stmt(&node.span, disc, cases) } Stmt::Throw(node) => { - let raw = ctx.header(AstNode::ThrowStatement, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let arg = serialize_expr(ctx, &node.arg, pos); - ctx.write_ref(arg_pos, arg); - - pos + let arg = serialize_expr(ctx, &node.arg); + ctx.write_throw_stmt(&node.span, arg) } Stmt::Try(node) => { - let raw = ctx.header(AstNode::TryStatement, parent, &node.span); - let block_pos = ctx.ref_field(AstProp::Block); - let handler_pos = ctx.ref_field(AstProp::Handler); - let finalizer_pos = ctx.ref_field(AstProp::Finalizer); - let pos = ctx.commit_schema(raw); - - let block = serialize_stmt(ctx, &Stmt::Block(node.block.clone()), pos); + let block = serialize_stmt(ctx, &Stmt::Block(node.block.clone())); let handler = node.handler.as_ref().map(|catch| { - let raw = ctx.header(AstNode::CatchClause, pos, &catch.span); - let param_pos = ctx.ref_field(AstProp::Param); - let body_pos = ctx.ref_field(AstProp::Body); - let clause_pos = ctx.commit_schema(raw); + let param = catch.param.as_ref().map(|param| serialize_pat(ctx, param)); - let param = catch - .param - .as_ref() - .map(|param| serialize_pat(ctx, param, clause_pos)); + let body = serialize_stmt(ctx, &Stmt::Block(catch.body.clone())); - let body = - serialize_stmt(ctx, &Stmt::Block(catch.body.clone()), clause_pos); - - ctx.write_maybe_ref(param_pos, param); - ctx.write_ref(body_pos, body); - - clause_pos + ctx.write_catch_clause(&catch.span, param, body) }); - let finalizer = node.finalizer.as_ref().map(|finalizer| { - serialize_stmt(ctx, &Stmt::Block(finalizer.clone()), pos) - }); + let finalizer = node + .finalizer + .as_ref() + .map(|finalizer| serialize_stmt(ctx, &Stmt::Block(finalizer.clone()))); - ctx.write_ref(block_pos, block); - ctx.write_maybe_ref(handler_pos, handler); - ctx.write_maybe_ref(finalizer_pos, finalizer); - - pos + ctx.write_try_stmt(&node.span, block, handler, finalizer) } Stmt::While(node) => { - let raw = ctx.header(AstNode::WhileStatement, parent, &node.span); - let test_pos = ctx.ref_field(AstProp::Test); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let test = serialize_expr(ctx, node.test.as_ref()); + let stmt = serialize_stmt(ctx, node.body.as_ref()); - let test = serialize_expr(ctx, node.test.as_ref(), pos); - let stmt = serialize_stmt(ctx, node.body.as_ref(), pos); - - ctx.write_ref(test_pos, test); - ctx.write_ref(body_pos, stmt); - - pos + ctx.write_while_stmt(&node.span, test, stmt) } Stmt::DoWhile(node) => { - let raw = ctx.header(AstNode::DoWhileStatement, parent, &node.span); - let test_pos = ctx.ref_field(AstProp::Test); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, node.test.as_ref()); + let stmt = serialize_stmt(ctx, node.body.as_ref()); - let expr = serialize_expr(ctx, node.test.as_ref(), pos); - let stmt = serialize_stmt(ctx, node.body.as_ref(), pos); - - ctx.write_ref(test_pos, expr); - ctx.write_ref(body_pos, stmt); - - pos + ctx.write_do_while_stmt(&node.span, expr, stmt) } Stmt::For(node) => { - let raw = ctx.header(AstNode::ForStatement, parent, &node.span); - let init_pos = ctx.ref_field(AstProp::Init); - let test_pos = ctx.ref_field(AstProp::Test); - let update_pos = ctx.ref_field(AstProp::Update); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); - let init = node.init.as_ref().map(|init| match init { VarDeclOrExpr::VarDecl(var_decl) => { - serialize_stmt(ctx, &Stmt::Decl(Decl::Var(var_decl.clone())), pos) + serialize_stmt(ctx, &Stmt::Decl(Decl::Var(var_decl.clone()))) } - VarDeclOrExpr::Expr(expr) => serialize_expr(ctx, expr, pos), + VarDeclOrExpr::Expr(expr) => serialize_expr(ctx, expr), }); - let test = node - .test - .as_ref() - .map(|expr| serialize_expr(ctx, expr, pos)); - let update = node - .update - .as_ref() - .map(|expr| serialize_expr(ctx, expr, pos)); - let body = serialize_stmt(ctx, node.body.as_ref(), pos); + let test = node.test.as_ref().map(|expr| serialize_expr(ctx, expr)); + let update = node.update.as_ref().map(|expr| serialize_expr(ctx, expr)); + let body = serialize_stmt(ctx, node.body.as_ref()); - ctx.write_maybe_ref(init_pos, init); - ctx.write_maybe_ref(test_pos, test); - ctx.write_maybe_ref(update_pos, update); - ctx.write_ref(body_pos, body); - - pos + ctx.write_for_stmt(&node.span, init, test, update, body) } Stmt::ForIn(node) => { - let raw = ctx.header(AstNode::ForInStatement, parent, &node.span); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let left = serialize_for_head(ctx, &node.left); + let right = serialize_expr(ctx, node.right.as_ref()); + let body = serialize_stmt(ctx, node.body.as_ref()); - let left = serialize_for_head(ctx, &node.left, pos); - let right = serialize_expr(ctx, node.right.as_ref(), pos); - let body = serialize_stmt(ctx, node.body.as_ref(), pos); - - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); - ctx.write_ref(body_pos, body); - - pos + ctx.write_for_in_stmt(&node.span, left, right, body) } Stmt::ForOf(node) => { - let raw = ctx.header(AstNode::ForOfStatement, parent, &node.span); - let await_pos = ctx.bool_field(AstProp::Await); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let left = serialize_for_head(ctx, &node.left); + let right = serialize_expr(ctx, node.right.as_ref()); + let body = serialize_stmt(ctx, node.body.as_ref()); - let left = serialize_for_head(ctx, &node.left, pos); - let right = serialize_expr(ctx, node.right.as_ref(), pos); - let body = serialize_stmt(ctx, node.body.as_ref(), pos); - - ctx.write_bool(await_pos, node.is_await); - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); - ctx.write_ref(body_pos, body); - - pos + ctx.write_for_of_stmt(&node.span, node.is_await, left, right, body) } - Stmt::Decl(node) => serialize_decl(ctx, node, parent), + Stmt::Decl(node) => serialize_decl(ctx, node), Stmt::Expr(node) => { - let raw = ctx.header(AstNode::ExpressionStatement, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let pos = ctx.commit_schema(raw); - - let expr = serialize_expr(ctx, node.expr.as_ref(), pos); - ctx.write_ref(expr_pos, expr); - - pos + let expr = serialize_expr(ctx, node.expr.as_ref()); + ctx.write_expr_stmt(&node.span, expr) } } } -fn serialize_expr( - ctx: &mut TsEsTreeBuilder, - expr: &Expr, - parent: NodeRef, -) -> NodeRef { +fn serialize_expr(ctx: &mut TsEsTreeBuilder, expr: &Expr) -> NodeRef { match expr { - Expr::This(node) => { - let raw = ctx.header(AstNode::ThisExpression, parent, &node.span); - ctx.commit_schema(raw) - } + Expr::This(node) => ctx.write_this_expr(&node.span), Expr::Array(node) => { - let raw = ctx.header(AstNode::ArrayExpression, parent, &node.span); - let elems_pos = ctx.ref_vec_field(AstProp::Elements, node.elems.len()); - let pos = ctx.commit_schema(raw); - let elems = node .elems .iter() .map(|item| { item .as_ref() - .map_or(NodeRef(0), |item| serialize_expr_or_spread(ctx, item, pos)) + .map_or(NodeRef(0), |item| serialize_expr_or_spread(ctx, item)) }) .collect::>(); - ctx.write_refs(elems_pos, elems); - - pos + ctx.write_arr_expr(&node.span, elems) } Expr::Object(node) => { - let raw = ctx.header(AstNode::ObjectExpression, parent, &node.span); - let props_pos = ctx.ref_vec_field(AstProp::Properties, node.props.len()); - let pos = ctx.commit_schema(raw); - - let prop_ids = node + let props = node .props .iter() - .map(|prop| serialize_prop_or_spread(ctx, prop, pos)) + .map(|prop| serialize_prop_or_spread(ctx, prop)) .collect::>(); - ctx.write_refs(props_pos, prop_ids); - - pos + ctx.write_obj_expr(&node.span, props) } Expr::Fn(node) => { let fn_obj = node.function.as_ref(); - let raw = ctx.header(AstNode::FunctionExpression, parent, &fn_obj.span); - - let async_pos = ctx.bool_field(AstProp::Async); - let gen_pos = ctx.bool_field(AstProp::Generator); - let id_pos = ctx.ref_field(AstProp::Id); - let tparams_pos = ctx.ref_field(AstProp::TypeParameters); - let params_pos = ctx.ref_vec_field(AstProp::Params, fn_obj.params.len()); - let return_pos = ctx.ref_field(AstProp::ReturnType); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); - let ident = node .ident .as_ref() - .map(|ident| serialize_ident(ctx, ident, pos)); + .map(|ident| serialize_ident(ctx, ident, None)); let type_params = - maybe_serialize_ts_type_param(ctx, &fn_obj.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &fn_obj.type_params); let params = fn_obj .params .iter() - .map(|param| serialize_pat(ctx, ¶m.pat, pos)) + .map(|param| serialize_pat(ctx, ¶m.pat)) .collect::>(); - let return_id = - maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type, pos); + let return_id = maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type); let body = fn_obj .body .as_ref() - .map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone()), pos)); + .map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone()))); - ctx.write_bool(async_pos, fn_obj.is_async); - ctx.write_bool(gen_pos, fn_obj.is_generator); - ctx.write_maybe_ref(id_pos, ident); - ctx.write_maybe_ref(tparams_pos, type_params); - ctx.write_refs(params_pos, params); - ctx.write_maybe_ref(return_pos, return_id); - ctx.write_maybe_ref(body_pos, body); - - pos + ctx.write_fn_expr( + &fn_obj.span, + fn_obj.is_async, + fn_obj.is_generator, + ident, + type_params, + params, + return_id, + body, + ) } Expr::Unary(node) => { - let raw = ctx.header(AstNode::UnaryExpression, parent, &node.span); - let flag_pos = ctx.str_field(AstProp::Operator); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); + let arg = serialize_expr(ctx, &node.arg); + let op = match node.op { + UnaryOp::Minus => "-", + UnaryOp::Plus => "+", + UnaryOp::Bang => "!", + UnaryOp::Tilde => "~", + UnaryOp::TypeOf => "typeof", + UnaryOp::Void => "void", + UnaryOp::Delete => "delete", + }; - let arg = serialize_expr(ctx, &node.arg, pos); - - ctx.write_str( - flag_pos, - match node.op { - UnaryOp::Minus => "-", - UnaryOp::Plus => "+", - UnaryOp::Bang => "!", - UnaryOp::Tilde => "~", - UnaryOp::TypeOf => "typeof", - UnaryOp::Void => "void", - UnaryOp::Delete => "delete", - }, - ); - ctx.write_ref(arg_pos, arg); - - pos + ctx.write_unary_expr(&node.span, op, arg) } Expr::Update(node) => { - let raw = ctx.header(AstNode::UpdateExpression, parent, &node.span); - let prefix_pos = ctx.bool_field(AstProp::Prefix); - let arg_pos = ctx.ref_field(AstProp::Argument); - let op_ops = ctx.str_field(AstProp::Operator); - let pos = ctx.commit_schema(raw); + let arg = serialize_expr(ctx, node.arg.as_ref()); + let op = match node.op { + UpdateOp::PlusPlus => "++", + UpdateOp::MinusMinus => "--", + }; - let arg = serialize_expr(ctx, node.arg.as_ref(), pos); - - ctx.write_bool(prefix_pos, node.prefix); - ctx.write_ref(arg_pos, arg); - ctx.write_str( - op_ops, - match node.op { - UpdateOp::PlusPlus => "++", - UpdateOp::MinusMinus => "--", - }, - ); - - pos + ctx.write_update_expr(&node.span, node.prefix, op, arg) } Expr::Bin(node) => { let (node_type, flag_str) = match node.op { @@ -699,501 +676,361 @@ fn serialize_expr( BinaryOp::Exp => (AstNode::BinaryExpression, "**"), }; - let raw = ctx.header(node_type, parent, &node.span); - let op_pos = ctx.str_field(AstProp::Operator); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let pos = ctx.commit_schema(raw); + let left = serialize_expr(ctx, node.left.as_ref()); + let right = serialize_expr(ctx, node.right.as_ref()); - let left_id = serialize_expr(ctx, node.left.as_ref(), pos); - let right_id = serialize_expr(ctx, node.right.as_ref(), pos); - - ctx.write_str(op_pos, flag_str); - ctx.write_ref(left_pos, left_id); - ctx.write_ref(right_pos, right_id); - - pos + match node_type { + AstNode::LogicalExpression => { + ctx.write_logical_expr(&node.span, flag_str, left, right) + } + AstNode::BinaryExpression => { + ctx.write_bin_expr(&node.span, flag_str, left, right) + } + _ => unreachable!(), + } } Expr::Assign(node) => { - let raw = ctx.header(AstNode::AssignmentExpression, parent, &node.span); - let op_pos = ctx.str_field(AstProp::Operator); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let pos = ctx.commit_schema(raw); - let left = match &node.left { AssignTarget::Simple(simple_assign_target) => { match simple_assign_target { SimpleAssignTarget::Ident(target) => { - serialize_ident(ctx, &target.id, pos) + serialize_binding_ident(ctx, target) } SimpleAssignTarget::Member(target) => { - serialize_expr(ctx, &Expr::Member(target.clone()), pos) + serialize_expr(ctx, &Expr::Member(target.clone())) } SimpleAssignTarget::SuperProp(target) => { - serialize_expr(ctx, &Expr::SuperProp(target.clone()), pos) + serialize_expr(ctx, &Expr::SuperProp(target.clone())) } SimpleAssignTarget::Paren(target) => { - serialize_expr(ctx, &target.expr, pos) + serialize_expr(ctx, &target.expr) } SimpleAssignTarget::OptChain(target) => { - serialize_expr(ctx, &Expr::OptChain(target.clone()), pos) + serialize_expr(ctx, &Expr::OptChain(target.clone())) } SimpleAssignTarget::TsAs(target) => { - serialize_expr(ctx, &Expr::TsAs(target.clone()), pos) + serialize_expr(ctx, &Expr::TsAs(target.clone())) } SimpleAssignTarget::TsSatisfies(target) => { - serialize_expr(ctx, &Expr::TsSatisfies(target.clone()), pos) + serialize_expr(ctx, &Expr::TsSatisfies(target.clone())) } SimpleAssignTarget::TsNonNull(target) => { - serialize_expr(ctx, &Expr::TsNonNull(target.clone()), pos) + serialize_expr(ctx, &Expr::TsNonNull(target.clone())) } SimpleAssignTarget::TsTypeAssertion(target) => { - serialize_expr(ctx, &Expr::TsTypeAssertion(target.clone()), pos) + serialize_expr(ctx, &Expr::TsTypeAssertion(target.clone())) } SimpleAssignTarget::TsInstantiation(target) => { - serialize_expr(ctx, &Expr::TsInstantiation(target.clone()), pos) + serialize_expr(ctx, &Expr::TsInstantiation(target.clone())) } SimpleAssignTarget::Invalid(_) => unreachable!(), } } AssignTarget::Pat(target) => match target { AssignTargetPat::Array(array_pat) => { - serialize_pat(ctx, &Pat::Array(array_pat.clone()), pos) + serialize_pat(ctx, &Pat::Array(array_pat.clone())) } AssignTargetPat::Object(object_pat) => { - serialize_pat(ctx, &Pat::Object(object_pat.clone()), pos) + serialize_pat(ctx, &Pat::Object(object_pat.clone())) } AssignTargetPat::Invalid(_) => unreachable!(), }, }; - let right = serialize_expr(ctx, node.right.as_ref(), pos); + let right = serialize_expr(ctx, node.right.as_ref()); - ctx.write_str( - op_pos, - match node.op { - AssignOp::Assign => "=", - AssignOp::AddAssign => "+=", - AssignOp::SubAssign => "-=", - AssignOp::MulAssign => "*=", - AssignOp::DivAssign => "/=", - AssignOp::ModAssign => "%=", - AssignOp::LShiftAssign => "<<=", - AssignOp::RShiftAssign => ">>=", - AssignOp::ZeroFillRShiftAssign => ">>>=", - AssignOp::BitOrAssign => "|=", - AssignOp::BitXorAssign => "^=", - AssignOp::BitAndAssign => "&=", - AssignOp::ExpAssign => "**=", - AssignOp::AndAssign => "&&=", - AssignOp::OrAssign => "||=", - AssignOp::NullishAssign => "??=", - }, - ); - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); + let op = match node.op { + AssignOp::Assign => "=", + AssignOp::AddAssign => "+=", + AssignOp::SubAssign => "-=", + AssignOp::MulAssign => "*=", + AssignOp::DivAssign => "/=", + AssignOp::ModAssign => "%=", + AssignOp::LShiftAssign => "<<=", + AssignOp::RShiftAssign => ">>=", + AssignOp::ZeroFillRShiftAssign => ">>>=", + AssignOp::BitOrAssign => "|=", + AssignOp::BitXorAssign => "^=", + AssignOp::BitAndAssign => "&=", + AssignOp::ExpAssign => "**=", + AssignOp::AndAssign => "&&=", + AssignOp::OrAssign => "||=", + AssignOp::NullishAssign => "??=", + }; - pos + ctx.write_assignment_expr(&node.span, op, left, right) } - Expr::Member(node) => serialize_member_expr(ctx, node, parent, false), + Expr::Member(node) => serialize_member_expr(ctx, node, false), Expr::SuperProp(node) => { - let raw = ctx.header(AstNode::MemberExpression, parent, &node.span); - let computed_pos = ctx.bool_field(AstProp::Computed); - let obj_pos = ctx.ref_field(AstProp::Object); - let prop_pos = ctx.ref_field(AstProp::Property); - let pos = ctx.commit_schema(raw); - - let raw = ctx.header(AstNode::Super, pos, &node.obj.span); - let obj = ctx.commit_schema(raw); + let obj = ctx.write_super(&node.obj.span); let mut computed = false; let prop = match &node.prop { - SuperProp::Ident(ident_name) => { - serialize_ident_name(ctx, ident_name, pos) - } + SuperProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name), SuperProp::Computed(prop) => { computed = true; - serialize_expr(ctx, &prop.expr, pos) + serialize_expr(ctx, &prop.expr) } }; - ctx.write_bool(computed_pos, computed); - ctx.write_ref(obj_pos, obj); - ctx.write_ref(prop_pos, prop); - - pos + ctx.write_member_expr(&node.span, false, computed, obj, prop) } Expr::Cond(node) => { - let raw = ctx.header(AstNode::ConditionalExpression, parent, &node.span); - let test_pos = ctx.ref_field(AstProp::Test); - let cons_pos = ctx.ref_field(AstProp::Consequent); - let alt_pos = ctx.ref_field(AstProp::Alternate); - let pos = ctx.commit_schema(raw); + let test = serialize_expr(ctx, node.test.as_ref()); + let cons = serialize_expr(ctx, node.cons.as_ref()); + let alt = serialize_expr(ctx, node.alt.as_ref()); - let test = serialize_expr(ctx, node.test.as_ref(), pos); - let cons = serialize_expr(ctx, node.cons.as_ref(), pos); - let alt = serialize_expr(ctx, node.alt.as_ref(), pos); - - ctx.write_ref(test_pos, test); - ctx.write_ref(cons_pos, cons); - ctx.write_ref(alt_pos, alt); - - pos + ctx.write_conditional_expr(&node.span, test, cons, alt) } Expr::Call(node) => { - let raw = ctx.header(AstNode::CallExpression, parent, &node.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let callee_pos = ctx.ref_field(AstProp::Callee); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let args_pos = ctx.ref_vec_field(AstProp::Arguments, node.args.len()); - let pos = ctx.commit_schema(raw); + if let Callee::Import(_) = node.callee { + let source = node + .args + .first() + .map_or(NodeRef(0), |arg| serialize_expr_or_spread(ctx, arg)); - let callee = match &node.callee { - Callee::Super(super_node) => { - let raw = ctx.header(AstNode::Super, pos, &super_node.span); - ctx.commit_schema(raw) - } - Callee::Import(_) => todo!(), - Callee::Expr(expr) => serialize_expr(ctx, expr, pos), - }; + let options = node + .args + .get(1) + .map_or(NodeRef(0), |arg| serialize_expr_or_spread(ctx, arg)); - let type_arg = node.type_args.clone().map(|param_node| { - serialize_ts_param_inst(ctx, param_node.as_ref(), pos) - }); + ctx.write_import_expr(&node.span, source, options) + } else { + let callee = match &node.callee { + Callee::Super(super_node) => ctx.write_super(&super_node.span), + Callee::Import(_) => unreachable!("Already handled"), + Callee::Expr(expr) => serialize_expr(ctx, expr), + }; - let args = node - .args - .iter() - .map(|arg| serialize_expr_or_spread(ctx, arg, pos)) - .collect::>(); + let type_arg = node + .type_args + .clone() + .map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref())); - ctx.write_bool(opt_pos, false); - ctx.write_ref(callee_pos, callee); - ctx.write_maybe_ref(type_args_pos, type_arg); - ctx.write_refs(args_pos, args); + let args = node + .args + .iter() + .map(|arg| serialize_expr_or_spread(ctx, arg)) + .collect::>(); - pos + ctx.write_call_expr(&node.span, false, callee, type_arg, args) + } } Expr::New(node) => { - let raw = ctx.header(AstNode::NewExpression, parent, &node.span); - let callee_pos = ctx.ref_field(AstProp::Callee); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let args_pos = ctx.ref_vec_field( - AstProp::Arguments, - node.args.as_ref().map_or(0, |v| v.len()), - ); - let pos = ctx.commit_schema(raw); - - let callee = serialize_expr(ctx, node.callee.as_ref(), pos); + let callee = serialize_expr(ctx, node.callee.as_ref()); let args: Vec = node.args.as_ref().map_or(vec![], |args| { args .iter() - .map(|arg| serialize_expr_or_spread(ctx, arg, pos)) + .map(|arg| serialize_expr_or_spread(ctx, arg)) .collect::>() }); - let type_args = node.type_args.clone().map(|param_node| { - serialize_ts_param_inst(ctx, param_node.as_ref(), pos) - }); + let type_args = node + .type_args + .clone() + .map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref())); - ctx.write_ref(callee_pos, callee); - ctx.write_maybe_ref(type_args_pos, type_args); - ctx.write_refs(args_pos, args); - - pos + ctx.write_new_expr(&node.span, callee, type_args, args) } Expr::Seq(node) => { - let raw = ctx.header(AstNode::SequenceExpression, parent, &node.span); - let exprs_pos = ctx.ref_vec_field(AstProp::Expressions, node.exprs.len()); - let pos = ctx.commit_schema(raw); - let children = node .exprs .iter() - .map(|expr| serialize_expr(ctx, expr, pos)) + .map(|expr| serialize_expr(ctx, expr)) .collect::>(); - ctx.write_refs(exprs_pos, children); - - pos + ctx.write_sequence_expr(&node.span, children) } - Expr::Ident(node) => serialize_ident(ctx, node, parent), - Expr::Lit(node) => serialize_lit(ctx, node, parent), + Expr::Ident(node) => serialize_ident(ctx, node, None), + Expr::Lit(node) => serialize_lit(ctx, node), Expr::Tpl(node) => { - let raw = ctx.header(AstNode::TemplateLiteral, parent, &node.span); - let quasis_pos = ctx.ref_vec_field(AstProp::Quasis, node.quasis.len()); - let exprs_pos = ctx.ref_vec_field(AstProp::Expressions, node.exprs.len()); - let pos = ctx.commit_schema(raw); - let quasis = node .quasis .iter() .map(|quasi| { - let raw = ctx.header(AstNode::TemplateElement, pos, &quasi.span); - let tail_pos = ctx.bool_field(AstProp::Tail); - let raw_pos = ctx.str_field(AstProp::Raw); - let cooked_pos = ctx.str_field(AstProp::Cooked); - let tpl_pos = ctx.commit_schema(raw); - - ctx.write_bool(tail_pos, quasi.tail); - ctx.write_str(raw_pos, &quasi.raw); - ctx.write_str( - cooked_pos, + ctx.write_template_elem( + &quasi.span, + quasi.tail, + &quasi.raw, &quasi .cooked .as_ref() .map_or("".to_string(), |v| v.to_string()), - ); - - tpl_pos + ) }) .collect::>(); let exprs = node .exprs .iter() - .map(|expr| serialize_expr(ctx, expr, pos)) + .map(|expr| serialize_expr(ctx, expr)) .collect::>(); - ctx.write_refs(quasis_pos, quasis); - ctx.write_refs(exprs_pos, exprs); - - pos + ctx.write_template_lit(&node.span, quasis, exprs) } Expr::TaggedTpl(node) => { - let raw = - ctx.header(AstNode::TaggedTemplateExpression, parent, &node.span); - let tag_pos = ctx.ref_field(AstProp::Tag); - let type_arg_pos = ctx.ref_field(AstProp::TypeArguments); - let quasi_pos = ctx.ref_field(AstProp::Quasi); - let pos = ctx.commit_schema(raw); - - let tag = serialize_expr(ctx, &node.tag, pos); - - let type_param_id = node + let tag = serialize_expr(ctx, &node.tag); + let type_param = node .type_params .clone() - .map(|params| serialize_ts_param_inst(ctx, params.as_ref(), pos)); - let quasi = serialize_expr(ctx, &Expr::Tpl(*node.tpl.clone()), pos); + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); + let quasi = serialize_expr(ctx, &Expr::Tpl(*node.tpl.clone())); - ctx.write_ref(tag_pos, tag); - ctx.write_maybe_ref(type_arg_pos, type_param_id); - ctx.write_ref(quasi_pos, quasi); - - pos + ctx.write_tagged_template_expr(&node.span, tag, type_param, quasi) } Expr::Arrow(node) => { - let raw = - ctx.header(AstNode::ArrowFunctionExpression, parent, &node.span); - let async_pos = ctx.bool_field(AstProp::Async); - let gen_pos = ctx.bool_field(AstProp::Generator); - let type_param_pos = ctx.ref_field(AstProp::TypeParameters); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let body_pos = ctx.ref_field(AstProp::Body); - let return_type_pos = ctx.ref_field(AstProp::ReturnType); - let pos = ctx.commit_schema(raw); - let type_param = - maybe_serialize_ts_type_param(ctx, &node.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); let params = node .params .iter() - .map(|param| serialize_pat(ctx, param, pos)) + .map(|param| serialize_pat(ctx, param)) .collect::>(); let body = match node.body.as_ref() { BlockStmtOrExpr::BlockStmt(block_stmt) => { - serialize_stmt(ctx, &Stmt::Block(block_stmt.clone()), pos) + serialize_stmt(ctx, &Stmt::Block(block_stmt.clone())) } - BlockStmtOrExpr::Expr(expr) => serialize_expr(ctx, expr.as_ref(), pos), + BlockStmtOrExpr::Expr(expr) => serialize_expr(ctx, expr.as_ref()), }; - let return_type = - maybe_serialize_ts_type_ann(ctx, &node.return_type, pos); + let return_type = maybe_serialize_ts_type_ann(ctx, &node.return_type); - ctx.write_bool(async_pos, node.is_async); - ctx.write_bool(gen_pos, node.is_generator); - ctx.write_maybe_ref(type_param_pos, type_param); - ctx.write_refs(params_pos, params); - ctx.write_ref(body_pos, body); - ctx.write_maybe_ref(return_type_pos, return_type); - - pos + ctx.write_arrow_fn_expr( + &node.span, + node.is_async, + node.is_generator, + type_param, + params, + return_type, + body, + ) } Expr::Class(node) => { - // FIXME - let raw = ctx.header(AstNode::ClassExpression, parent, &node.class.span); - ctx.commit_schema(raw) + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let super_class = node + .class + .super_class + .as_ref() + .map(|expr| serialize_expr(ctx, expr.as_ref())); + + let implements = node + .class + .implements + .iter() + .map(|item| serialize_ts_expr_with_type_args(ctx, item)) + .collect::>(); + + let members = node + .class + .body + .iter() + .filter_map(|member| serialize_class_member(ctx, member)) + .collect::>(); + + let body = ctx.write_class_body(&node.class.span, members); + + ctx.write_class_expr( + &node.class.span, + false, + node.class.is_abstract, + ident, + super_class, + implements, + body, + ) } Expr::Yield(node) => { - let raw = ctx.header(AstNode::YieldExpression, parent, &node.span); - let delegate_pos = ctx.bool_field(AstProp::Delegate); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - let arg = node .arg .as_ref() - .map(|arg| serialize_expr(ctx, arg.as_ref(), pos)); + .map(|arg| serialize_expr(ctx, arg.as_ref())); - ctx.write_bool(delegate_pos, node.delegate); - ctx.write_maybe_ref(arg_pos, arg); - - pos + ctx.write_yield_expr(&node.span, node.delegate, arg) } Expr::MetaProp(node) => { - let raw = ctx.header(AstNode::MetaProp, parent, &node.span); - ctx.commit_schema(raw) + let prop = ctx.write_identifier(&node.span, "meta", false, None); + ctx.write_meta_prop(&node.span, prop) } Expr::Await(node) => { - let raw = ctx.header(AstNode::AwaitExpression, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let arg = serialize_expr(ctx, node.arg.as_ref(), pos); - - ctx.write_ref(arg_pos, arg); - - pos + let arg = serialize_expr(ctx, node.arg.as_ref()); + ctx.write_await_expr(&node.span, arg) } Expr::Paren(node) => { // Paren nodes are treated as a syntax only thing in TSEStree // and are never materialized to actual AST nodes. - serialize_expr(ctx, &node.expr, parent) + serialize_expr(ctx, &node.expr) } - Expr::JSXMember(node) => serialize_jsx_member_expr(ctx, node, parent), - Expr::JSXNamespacedName(node) => { - serialize_jsx_namespaced_name(ctx, node, parent) - } - Expr::JSXEmpty(node) => serialize_jsx_empty_expr(ctx, node, parent), - Expr::JSXElement(node) => serialize_jsx_element(ctx, node, parent), - Expr::JSXFragment(node) => serialize_jsx_fragment(ctx, node, parent), + Expr::JSXMember(node) => serialize_jsx_member_expr(ctx, node), + Expr::JSXNamespacedName(node) => serialize_jsx_namespaced_name(ctx, node), + Expr::JSXEmpty(node) => ctx.write_jsx_empty_expr(&node.span), + Expr::JSXElement(node) => serialize_jsx_element(ctx, node), + Expr::JSXFragment(node) => serialize_jsx_fragment(ctx, node), Expr::TsTypeAssertion(node) => { - let raw = ctx.header(AstNode::TSTypeAssertion, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, &node.expr); + let type_ann = serialize_ts_type(ctx, &node.type_ann); - let expr = serialize_expr(ctx, &node.expr, parent); - let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); - - ctx.write_ref(expr_pos, expr); - ctx.write_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_type_assertion(&node.span, expr, type_ann) } Expr::TsConstAssertion(node) => { - let raw = ctx.header(AstNode::TsConstAssertion, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, node.expr.as_ref()); - let arg = serialize_expr(ctx, node.expr.as_ref(), pos); + let type_name = ctx.write_identifier(&node.span, "const", false, None); + let type_ann = ctx.write_ts_type_ref(&node.span, type_name, None); - // FIXME - ctx.write_ref(arg_pos, arg); - - pos + ctx.write_ts_as_expr(&node.span, expr, type_ann) } Expr::TsNonNull(node) => { - let raw = ctx.header(AstNode::TSNonNullExpression, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let pos = ctx.commit_schema(raw); - - let expr_id = serialize_expr(ctx, node.expr.as_ref(), pos); - - ctx.write_ref(expr_pos, expr_id); - - pos + let expr = serialize_expr(ctx, node.expr.as_ref()); + ctx.write_ts_non_null(&node.span, expr) } Expr::TsAs(node) => { - let raw = ctx.header(AstNode::TSAsExpression, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, node.expr.as_ref()); + let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref()); - let expr = serialize_expr(ctx, node.expr.as_ref(), pos); - let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref(), pos); - - ctx.write_ref(expr_pos, expr); - ctx.write_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_as_expr(&node.span, expr, type_ann) } - Expr::TsInstantiation(node) => { - let raw = ctx.header(AstNode::TsInstantiation, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let pos = ctx.commit_schema(raw); - - let expr = serialize_expr(ctx, node.expr.as_ref(), pos); - - let type_arg = serialize_ts_param_inst(ctx, node.type_args.as_ref(), pos); - - ctx.write_ref(expr_pos, expr); - ctx.write_ref(type_args_pos, type_arg); - - pos + Expr::TsInstantiation(_) => { + // Invalid syntax + unreachable!() } Expr::TsSatisfies(node) => { - let raw = ctx.header(AstNode::TSSatisfiesExpression, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, node.expr.as_ref()); + let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref()); - let epxr = serialize_expr(ctx, node.expr.as_ref(), pos); - let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref(), pos); - - ctx.write_ref(expr_pos, epxr); - ctx.write_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_satisfies_expr(&node.span, expr, type_ann) } - Expr::PrivateName(node) => serialize_private_name(ctx, node, parent), + Expr::PrivateName(node) => serialize_private_name(ctx, node), Expr::OptChain(node) => { - let raw = ctx.header(AstNode::ChainExpression, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let arg = match node.base.as_ref() { + let expr = match node.base.as_ref() { OptChainBase::Member(member_expr) => { - serialize_member_expr(ctx, member_expr, pos, true) + serialize_member_expr(ctx, member_expr, true) } OptChainBase::Call(opt_call) => { - let raw = ctx.header(AstNode::CallExpression, pos, &opt_call.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let callee_pos = ctx.ref_field(AstProp::Callee); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let args_pos = - ctx.ref_vec_field(AstProp::Arguments, opt_call.args.len()); - let call_pos = ctx.commit_schema(raw); + let callee = serialize_expr(ctx, &opt_call.callee); - let callee = serialize_expr(ctx, &opt_call.callee, pos); - - let type_param_id = opt_call.type_args.clone().map(|params| { - serialize_ts_param_inst(ctx, params.as_ref(), call_pos) - }); + let type_param_id = opt_call + .type_args + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); let args = opt_call .args .iter() - .map(|arg| serialize_expr_or_spread(ctx, arg, pos)) + .map(|arg| serialize_expr_or_spread(ctx, arg)) .collect::>(); - ctx.write_bool(opt_pos, true); - ctx.write_ref(callee_pos, callee); - ctx.write_maybe_ref(type_args_pos, type_param_id); - ctx.write_refs(args_pos, args); - - call_pos + ctx.write_call_expr(&opt_call.span, true, callee, type_param_id, args) } }; - ctx.write_ref(arg_pos, arg); - - pos + ctx.write_chain_expr(&node.span, expr) } Expr::Invalid(_) => { unreachable!() @@ -1204,69 +1041,49 @@ fn serialize_expr( fn serialize_prop_or_spread( ctx: &mut TsEsTreeBuilder, prop: &PropOrSpread, - parent: NodeRef, ) -> NodeRef { match prop { PropOrSpread::Spread(spread_element) => serialize_spread( ctx, spread_element.expr.as_ref(), &spread_element.dot3_token, - parent, ), PropOrSpread::Prop(prop) => { - let raw = ctx.header(AstNode::Property, parent, &prop.span()); - - let shorthand_pos = ctx.bool_field(AstProp::Shorthand); - let computed_pos = ctx.bool_field(AstProp::Computed); - let method_pos = ctx.bool_field(AstProp::Method); - let kind_pos = ctx.str_field(AstProp::Kind); - let key_pos = ctx.ref_field(AstProp::Key); - let value_pos = ctx.ref_field(AstProp::Value); - let pos = ctx.commit_schema(raw); - let mut shorthand = false; let mut computed = false; let mut method = false; let mut kind = "init"; - // FIXME: optional - let (key_id, value_id) = match prop.as_ref() { + let (key, value) = match prop.as_ref() { Prop::Shorthand(ident) => { shorthand = true; - let value = serialize_ident(ctx, ident, pos); - (value, value) + let value = serialize_ident(ctx, ident, None); + let value2 = serialize_ident(ctx, ident, None); + (value, value2) } Prop::KeyValue(key_value_prop) => { if let PropName::Computed(_) = key_value_prop.key { computed = true; } - let key = serialize_prop_name(ctx, &key_value_prop.key, pos); - let value = serialize_expr(ctx, key_value_prop.value.as_ref(), pos); + let key = serialize_prop_name(ctx, &key_value_prop.key); + let value = serialize_expr(ctx, key_value_prop.value.as_ref()); (key, value) } Prop::Assign(assign_prop) => { - let raw = - ctx.header(AstNode::AssignmentPattern, pos, &assign_prop.span); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let child_pos = ctx.commit_schema(raw); + let left = serialize_ident(ctx, &assign_prop.key, None); + let right = serialize_expr(ctx, assign_prop.value.as_ref()); - let left = serialize_ident(ctx, &assign_prop.key, child_pos); - let right = - serialize_expr(ctx, assign_prop.value.as_ref(), child_pos); - - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); + let child_pos = ctx.write_assign_pat(&assign_prop.span, left, right); (left, child_pos) } Prop::Getter(getter_prop) => { kind = "get"; - let key = serialize_prop_name(ctx, &getter_prop.key, pos); + let key = serialize_prop_name(ctx, &getter_prop.key); let value = serialize_expr( ctx, @@ -1280,11 +1097,10 @@ fn serialize_prop_or_spread( body: getter_prop.body.clone(), is_generator: false, is_async: false, - type_params: None, // FIXME - return_type: None, + type_params: None, + return_type: getter_prop.type_ann.clone(), }), }), - pos, ); (key, value) @@ -1292,7 +1108,7 @@ fn serialize_prop_or_spread( Prop::Setter(setter_prop) => { kind = "set"; - let key_id = serialize_prop_name(ctx, &setter_prop.key, pos); + let key_id = serialize_prop_name(ctx, &setter_prop.key); let param = Param::from(*setter_prop.param.clone()); @@ -1312,7 +1128,6 @@ fn serialize_prop_or_spread( return_type: None, }), }), - pos, ); (key_id, value_id) @@ -1320,7 +1135,7 @@ fn serialize_prop_or_spread( Prop::Method(method_prop) => { method = true; - let key_id = serialize_prop_name(ctx, &method_prop.key, pos); + let key_id = serialize_prop_name(ctx, &method_prop.key); let value_id = serialize_expr( ctx, @@ -1328,21 +1143,21 @@ fn serialize_prop_or_spread( ident: None, function: method_prop.function.clone(), }), - pos, ); (key_id, value_id) } }; - ctx.write_bool(shorthand_pos, shorthand); - ctx.write_bool(computed_pos, computed); - ctx.write_bool(method_pos, method); - ctx.write_str(kind_pos, kind); - ctx.write_ref(key_pos, key_id); - ctx.write_ref(value_pos, value_id); - - pos + ctx.write_property( + &prop.span(), + shorthand, + computed, + method, + kind, + key, + value, + ) } } } @@ -1350,396 +1165,190 @@ fn serialize_prop_or_spread( fn serialize_member_expr( ctx: &mut TsEsTreeBuilder, node: &MemberExpr, - parent: NodeRef, optional: bool, ) -> NodeRef { - let raw = ctx.header(AstNode::MemberExpression, parent, &node.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let computed_pos = ctx.bool_field(AstProp::Computed); - let obj_pos = ctx.ref_field(AstProp::Object); - let prop_pos = ctx.ref_field(AstProp::Property); - let pos = ctx.commit_schema(raw); - - let obj = serialize_expr(ctx, node.obj.as_ref(), pos); - let mut computed = false; + let obj = serialize_expr(ctx, node.obj.as_ref()); let prop = match &node.prop { - MemberProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name, pos), + MemberProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name), MemberProp::PrivateName(private_name) => { - serialize_private_name(ctx, private_name, pos) + serialize_private_name(ctx, private_name) } MemberProp::Computed(computed_prop_name) => { computed = true; - serialize_expr(ctx, computed_prop_name.expr.as_ref(), pos) + serialize_expr(ctx, computed_prop_name.expr.as_ref()) } }; - ctx.write_bool(opt_pos, optional); - ctx.write_bool(computed_pos, computed); - ctx.write_ref(obj_pos, obj); - ctx.write_ref(prop_pos, prop); - - pos -} - -fn serialize_class_member( - ctx: &mut TsEsTreeBuilder, - member: &ClassMember, - parent: NodeRef, -) -> NodeRef { - match member { - ClassMember::Constructor(constructor) => { - let raw = - ctx.header(AstNode::MethodDefinition, parent, &constructor.span); - let key_pos = ctx.ref_field(AstProp::Key); - let body_pos = ctx.ref_field(AstProp::Body); - let args_pos = - ctx.ref_vec_field(AstProp::Arguments, constructor.params.len()); - let acc_pos = if constructor.accessibility.is_some() { - NodePos::Str(ctx.str_field(AstProp::Accessibility)) - } else { - NodePos::Undef(ctx.undefined_field(AstProp::Accessibility)) - }; - let member_id = ctx.commit_schema(raw); - - // FIXME flags - - let key = serialize_prop_name(ctx, &constructor.key, member_id); - let body = constructor - .body - .as_ref() - .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()), member_id)); - - let params = constructor - .params - .iter() - .map(|param| match param { - ParamOrTsParamProp::TsParamProp(_) => { - todo!() - } - ParamOrTsParamProp::Param(param) => { - serialize_pat(ctx, ¶m.pat, member_id) - } - }) - .collect::>(); - - if let Some(acc) = constructor.accessibility { - if let NodePos::Str(str_pos) = acc_pos { - ctx.write_str(str_pos, &accessibility_to_str(acc)); - } - } - - ctx.write_ref(key_pos, key); - ctx.write_maybe_ref(body_pos, body); - // FIXME - ctx.write_refs(args_pos, params); - - member_id - } - ClassMember::Method(method) => { - let raw = ctx.header(AstNode::MethodDefinition, parent, &method.span); - - let member_id = ctx.commit_schema(raw); - - // let mut flags = FlagValue::new(); - // flags.set(Flag::ClassMethod); - if method.function.is_async { - // FIXME - } - - // accessibility_to_flag(&mut flags, method.accessibility); - - let _key_id = serialize_prop_name(ctx, &method.key, member_id); - - let _body_id = - method.function.body.as_ref().map(|body| { - serialize_stmt(ctx, &Stmt::Block(body.clone()), member_id) - }); - - let _params = method - .function - .params - .iter() - .map(|param| serialize_pat(ctx, ¶m.pat, member_id)) - .collect::>(); - - // ctx.write_node(member_id, ); - // ctx.write_flags(&flags); - // ctx.write_id(key_id); - // ctx.write_id(body_id); - // ctx.write_ids(AstProp::Params, params); - - member_id - } - ClassMember::PrivateMethod(_) => todo!(), - ClassMember::ClassProp(_) => todo!(), - ClassMember::PrivateProp(_) => todo!(), - ClassMember::TsIndexSignature(member) => { - serialize_ts_index_sig(ctx, member, parent) - } - ClassMember::Empty(_) => unreachable!(), - ClassMember::StaticBlock(_) => todo!(), - ClassMember::AutoAccessor(_) => todo!(), - } + ctx.write_member_expr(&node.span, optional, computed, obj, prop) } fn serialize_expr_or_spread( ctx: &mut TsEsTreeBuilder, arg: &ExprOrSpread, - parent: NodeRef, ) -> NodeRef { if let Some(spread) = &arg.spread { - serialize_spread(ctx, &arg.expr, spread, parent) + serialize_spread(ctx, &arg.expr, spread) } else { - serialize_expr(ctx, arg.expr.as_ref(), parent) + serialize_expr(ctx, arg.expr.as_ref()) } } fn serialize_ident( ctx: &mut TsEsTreeBuilder, ident: &Ident, - parent: NodeRef, + type_ann: Option, ) -> NodeRef { - let raw = ctx.header(AstNode::Identifier, parent, &ident.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, ident.sym.as_str()); - - pos + ctx.write_identifier( + &ident.span, + ident.sym.as_str(), + ident.optional, + type_ann, + ) } -fn serialize_module_exported_name( +fn serialize_module_export_name( ctx: &mut TsEsTreeBuilder, name: &ModuleExportName, - parent: NodeRef, ) -> NodeRef { match &name { - ModuleExportName::Ident(ident) => serialize_ident(ctx, ident, parent), - ModuleExportName::Str(lit) => { - serialize_lit(ctx, &Lit::Str(lit.clone()), parent) - } + ModuleExportName::Ident(ident) => serialize_ident(ctx, ident, None), + ModuleExportName::Str(lit) => serialize_lit(ctx, &Lit::Str(lit.clone())), } } -fn serialize_decl( - ctx: &mut TsEsTreeBuilder, - decl: &Decl, - parent: NodeRef, -) -> NodeRef { +fn serialize_decl(ctx: &mut TsEsTreeBuilder, decl: &Decl) -> NodeRef { match decl { Decl::Class(node) => { - let raw = ctx.header(AstNode::ClassDeclaration, parent, &node.class.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let abstract_pos = ctx.bool_field(AstProp::Abstract); - let id_pos = ctx.ref_field(AstProp::Id); - let body_pos = ctx.ref_field(AstProp::Body); - let type_params_pos = ctx.ref_field(AstProp::TypeParameters); - let super_pos = ctx.ref_field(AstProp::SuperClass); - let super_type_pos = ctx.ref_field(AstProp::SuperTypeArguments); - let impl_pos = - ctx.ref_vec_field(AstProp::Implements, node.class.implements.len()); - let id = ctx.commit_schema(raw); - - let body_raw = ctx.header(AstNode::ClassBody, id, &node.class.span); - let body_body_pos = - ctx.ref_vec_field(AstProp::Body, node.class.body.len()); - let body_id = ctx.commit_schema(body_raw); - - let ident = serialize_ident(ctx, &node.ident, id); - let type_params = - maybe_serialize_ts_type_param(ctx, &node.class.type_params, id); + let ident = serialize_ident(ctx, &node.ident, None); let super_class = node .class .super_class .as_ref() - .map(|super_class| serialize_expr(ctx, super_class, id)); + .map(|expr| serialize_expr(ctx, expr.as_ref())); - let super_type_params = node - .class - .super_type_params - .as_ref() - .map(|super_params| serialize_ts_param_inst(ctx, super_params, id)); - - let implement_ids = node + let implements = node .class .implements .iter() - .map(|implements| { - let raw = - ctx.header(AstNode::TSClassImplements, id, &implements.span); - - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let child_pos = ctx.commit_schema(raw); - - let type_args = implements - .type_args - .clone() - .map(|args| serialize_ts_param_inst(ctx, &args, child_pos)); - - let expr = serialize_expr(ctx, &implements.expr, child_pos); - - ctx.write_ref(expr_pos, expr); - ctx.write_maybe_ref(type_args_pos, type_args); - - child_pos - }) + .map(|item| serialize_ts_expr_with_type_args(ctx, item)) .collect::>(); - let member_ids = node + let members = node .class .body .iter() - .map(|member| serialize_class_member(ctx, member, parent)) + .filter_map(|member| serialize_class_member(ctx, member)) .collect::>(); - ctx.write_ref(body_pos, body_id); + let body = ctx.write_class_body(&node.class.span, members); - ctx.write_bool(declare_pos, node.declare); - ctx.write_bool(abstract_pos, node.class.is_abstract); - ctx.write_ref(id_pos, ident); - ctx.write_maybe_ref(type_params_pos, type_params); - ctx.write_maybe_ref(super_pos, super_class); - ctx.write_maybe_ref(super_type_pos, super_type_params); - ctx.write_refs(impl_pos, implement_ids); - - // body - ctx.write_refs(body_body_pos, member_ids); - - id + ctx.write_class_decl( + &node.class.span, + false, + node.class.is_abstract, + Some(ident), + super_class, + implements, + body, + ) } Decl::Fn(node) => { - let raw = - ctx.header(AstNode::FunctionDeclaration, parent, &node.function.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let async_pos = ctx.bool_field(AstProp::Async); - let gen_pos = ctx.bool_field(AstProp::Generator); - let id_pos = ctx.ref_field(AstProp::Id); - let type_params_pos = ctx.ref_field(AstProp::TypeParameters); - let return_pos = ctx.ref_field(AstProp::ReturnType); - let body_pos = ctx.ref_field(AstProp::Body); - let params_pos = - ctx.ref_vec_field(AstProp::Params, node.function.params.len()); - let pos = ctx.commit_schema(raw); - - let ident_id = serialize_ident(ctx, &node.ident, parent); + let ident_id = serialize_ident(ctx, &node.ident, None); let type_param_id = - maybe_serialize_ts_type_param(ctx, &node.function.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &node.function.type_params); let return_type = - maybe_serialize_ts_type_ann(ctx, &node.function.return_type, pos); + maybe_serialize_ts_type_ann(ctx, &node.function.return_type); let body = node .function .body .as_ref() - .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()), pos)); + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()))); let params = node .function .params .iter() - .map(|param| serialize_pat(ctx, ¶m.pat, pos)) + .map(|param| serialize_pat(ctx, ¶m.pat)) .collect::>(); - ctx.write_bool(declare_pos, node.declare); - ctx.write_bool(async_pos, node.function.is_async); - ctx.write_bool(gen_pos, node.function.is_generator); - ctx.write_ref(id_pos, ident_id); - ctx.write_maybe_ref(type_params_pos, type_param_id); - ctx.write_maybe_ref(return_pos, return_type); - ctx.write_maybe_ref(body_pos, body); - ctx.write_refs(params_pos, params); - - pos + ctx.write_fn_decl( + &node.function.span, + node.declare, + node.function.is_async, + node.function.is_generator, + Some(ident_id), + type_param_id, + return_type, + body, + params, + ) } Decl::Var(node) => { - let raw = ctx.header(AstNode::VariableDeclaration, parent, &node.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let kind_pos = ctx.str_field(AstProp::Kind); - let decls_pos = - ctx.ref_vec_field(AstProp::Declarations, node.decls.len()); - let id = ctx.commit_schema(raw); + let children = node + .decls + .iter() + .map(|decl| { + let ident = serialize_pat(ctx, &decl.name); + let init = decl + .init + .as_ref() + .map(|init| serialize_expr(ctx, init.as_ref())); + + ctx.write_var_declarator(&decl.span, ident, init) + }) + .collect::>(); + + let kind = match node.kind { + VarDeclKind::Var => "var", + VarDeclKind::Let => "let", + VarDeclKind::Const => "const", + }; + + ctx.write_var_decl(&node.span, node.declare, kind, children) + } + Decl::Using(node) => { + let kind = if node.is_await { + "await using" + } else { + "using" + }; let children = node .decls .iter() .map(|decl| { - let raw = ctx.header(AstNode::VariableDeclarator, id, &decl.span); - let id_pos = ctx.ref_field(AstProp::Id); - let init_pos = ctx.ref_field(AstProp::Init); - let child_id = ctx.commit_schema(raw); - - // FIXME: Definite? - - let ident = serialize_pat(ctx, &decl.name, child_id); - + let ident = serialize_pat(ctx, &decl.name); let init = decl .init .as_ref() - .map(|init| serialize_expr(ctx, init.as_ref(), child_id)); + .map(|init| serialize_expr(ctx, init.as_ref())); - ctx.write_ref(id_pos, ident); - ctx.write_maybe_ref(init_pos, init); - - child_id + ctx.write_var_declarator(&decl.span, ident, init) }) .collect::>(); - ctx.write_bool(declare_pos, node.declare); - ctx.write_str( - kind_pos, - match node.kind { - VarDeclKind::Var => "var", - VarDeclKind::Let => "let", - VarDeclKind::Const => "const", - }, - ); - ctx.write_refs(decls_pos, children); - - id - } - Decl::Using(_) => { - todo!(); + ctx.write_var_decl(&node.span, false, kind, children) } Decl::TsInterface(node) => { - let raw = ctx.header(AstNode::TSInterface, parent, &node.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let id_pos = ctx.ref_field(AstProp::Id); - let extends_pos = ctx.ref_vec_field(AstProp::Extends, node.extends.len()); - let type_param_pos = ctx.ref_field(AstProp::TypeParameters); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); - - let body_raw = ctx.header(AstNode::TSInterfaceBody, pos, &node.body.span); - let body_body_pos = - ctx.ref_vec_field(AstProp::Body, node.body.body.len()); - let body_id = ctx.commit_schema(body_raw); - - let ident_id = serialize_ident(ctx, &node.id, pos); + let ident_id = serialize_ident(ctx, &node.id, None); let type_param = - maybe_serialize_ts_type_param(ctx, &node.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); let extend_ids = node .extends .iter() .map(|item| { - let raw = ctx.header(AstNode::TSInterfaceHeritage, pos, &item.span); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let expr_pos = ctx.ref_field(AstProp::Expression); - let child_pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, &item.expr); + let type_args = item + .type_args + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); - let expr = serialize_expr(ctx, &item.expr, child_pos); - let type_args = item.type_args.clone().map(|params| { - serialize_ts_param_inst(ctx, params.as_ref(), child_pos) - }); - - ctx.write_ref(expr_pos, expr); - ctx.write_maybe_ref(type_args_pos, type_args); - - child_pos + ctx.write_ts_interface_heritage(&item.span, expr, type_args) }) .collect::>(); @@ -1747,269 +1356,211 @@ fn serialize_decl( .body .body .iter() - .map(|item| match item { - TsTypeElement::TsCallSignatureDecl(ts_call) => { - let raw = ctx.header( - AstNode::TsCallSignatureDeclaration, - pos, - &ts_call.span, - ); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let params_pos = - ctx.ref_vec_field(AstProp::Params, ts_call.params.len()); - let return_pos = ctx.ref_field(AstProp::ReturnType); - let item_id = ctx.commit_schema(raw); - - let type_param = - maybe_serialize_ts_type_param(ctx, &ts_call.type_params, pos); - let return_type = - maybe_serialize_ts_type_ann(ctx, &ts_call.type_ann, pos); - let params = ts_call - .params - .iter() - .map(|param| serialize_ts_fn_param(ctx, param, pos)) - .collect::>(); - - ctx.write_maybe_ref(type_ann_pos, type_param); - ctx.write_refs(params_pos, params); - ctx.write_maybe_ref(return_pos, return_type); - - item_id - } - TsTypeElement::TsConstructSignatureDecl(_) => todo!(), - TsTypeElement::TsPropertySignature(sig) => { - let raw = ctx.header(AstNode::TSPropertySignature, pos, &sig.span); - - let computed_pos = ctx.bool_field(AstProp::Computed); - let optional_pos = ctx.bool_field(AstProp::Optional); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let _static_bos = ctx.bool_field(AstProp::Static); - let key_pos = ctx.ref_field(AstProp::Key); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let item_pos = ctx.commit_schema(raw); - - let key = serialize_expr(ctx, &sig.key, item_pos); - let type_ann = - maybe_serialize_ts_type_ann(ctx, &sig.type_ann, item_pos); - - ctx.write_bool(computed_pos, sig.computed); - ctx.write_bool(optional_pos, sig.optional); - ctx.write_bool(readonly_pos, sig.readonly); - ctx.write_ref(key_pos, key); - ctx.write_maybe_ref(type_ann_pos, type_ann); - - item_pos - } - TsTypeElement::TsGetterSignature(sig) => { - let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); - let computed_pos = ctx.bool_field(AstProp::Computed); - let optional_pos = ctx.bool_field(AstProp::Optional); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let _static_bos = ctx.bool_field(AstProp::Static); - let kind_pos = ctx.str_field(AstProp::Kind); - let key_pos = ctx.ref_field(AstProp::Key); - let return_type_pos = ctx.ref_field(AstProp::ReturnType); - let item_pos = ctx.commit_schema(raw); - - let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); - let return_type = - maybe_serialize_ts_type_ann(ctx, &sig.type_ann, item_pos); - - ctx.write_bool(computed_pos, false); - ctx.write_bool(optional_pos, false); - ctx.write_bool(readonly_pos, false); - ctx.write_str(kind_pos, "getter"); - ctx.write_maybe_ref(return_type_pos, return_type); - ctx.write_ref(key_pos, key); - - item_pos - } - TsTypeElement::TsSetterSignature(sig) => { - let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); - let computed_pos = ctx.bool_field(AstProp::Computed); - let optional_pos = ctx.bool_field(AstProp::Optional); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let _static_bos = ctx.bool_field(AstProp::Static); - let kind_pos = ctx.str_field(AstProp::Kind); - let key_pos = ctx.ref_field(AstProp::Key); - let params_pos = ctx.ref_vec_field(AstProp::Params, 1); - let item_pos = ctx.commit_schema(raw); - - let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); - let params = serialize_ts_fn_param(ctx, &sig.param, item_pos); - - ctx.write_bool(computed_pos, false); - ctx.write_bool(optional_pos, false); - ctx.write_bool(readonly_pos, false); - ctx.write_str(kind_pos, "setter"); - ctx.write_ref(key_pos, key); - ctx.write_refs(params_pos, vec![params]); - - item_pos - } - TsTypeElement::TsMethodSignature(sig) => { - let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); - let computed_pos = ctx.bool_field(AstProp::Computed); - let optional_pos = ctx.bool_field(AstProp::Optional); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let _static_bos = ctx.bool_field(AstProp::Static); - let kind_pos = ctx.str_field(AstProp::Kind); - let key_pos = ctx.ref_field(AstProp::Key); - let params_pos = - ctx.ref_vec_field(AstProp::Params, sig.params.len()); - let return_type_pos = ctx.ref_field(AstProp::ReturnType); - let item_pos = ctx.commit_schema(raw); - - let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); - let params = sig - .params - .iter() - .map(|param| serialize_ts_fn_param(ctx, param, item_pos)) - .collect::>(); - let return_type = - maybe_serialize_ts_type_ann(ctx, &sig.type_ann, item_pos); - - ctx.write_bool(computed_pos, false); - ctx.write_bool(optional_pos, false); - ctx.write_bool(readonly_pos, false); - ctx.write_str(kind_pos, "method"); - ctx.write_ref(key_pos, key); - ctx.write_refs(params_pos, params); - ctx.write_maybe_ref(return_type_pos, return_type); - - item_pos - } - TsTypeElement::TsIndexSignature(sig) => { - serialize_ts_index_sig(ctx, sig, pos) - } - }) + .map(|item| serialize_ts_type_elem(ctx, item)) .collect::>(); - ctx.write_bool(declare_pos, node.declare); - ctx.write_ref(id_pos, ident_id); - ctx.write_maybe_ref(type_param_pos, type_param); - ctx.write_refs(extends_pos, extend_ids); - ctx.write_ref(body_pos, body_id); - - // Body - ctx.write_refs(body_body_pos, body_elem_ids); - - pos + let body_pos = + ctx.write_ts_interface_body(&node.body.span, body_elem_ids); + ctx.write_ts_interface( + &node.span, + node.declare, + ident_id, + type_param, + extend_ids, + body_pos, + ) } Decl::TsTypeAlias(node) => { - let raw = ctx.header(AstNode::TsTypeAlias, parent, &node.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let id_pos = ctx.ref_field(AstProp::Id); - let type_params_pos = ctx.ref_field(AstProp::TypeParameters); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let ident = serialize_ident(ctx, &node.id, pos); - let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); + let ident = serialize_ident(ctx, &node.id, None); + let type_ann = serialize_ts_type(ctx, &node.type_ann); let type_param = - maybe_serialize_ts_type_param(ctx, &node.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); - ctx.write_bool(declare_pos, node.declare); - ctx.write_ref(id_pos, ident); - ctx.write_maybe_ref(type_params_pos, type_param); - ctx.write_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_type_alias( + &node.span, + node.declare, + ident, + type_param, + type_ann, + ) } Decl::TsEnum(node) => { - let raw = ctx.header(AstNode::TSEnumDeclaration, parent, &node.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let const_pos = ctx.bool_field(AstProp::Const); - let id_pos = ctx.ref_field(AstProp::Id); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); - - let body_raw = ctx.header(AstNode::TSEnumBody, pos, &node.span); - let members_pos = ctx.ref_vec_field(AstProp::Members, node.members.len()); - let body = ctx.commit_schema(body_raw); - - let ident_id = serialize_ident(ctx, &node.id, parent); + let id = serialize_ident(ctx, &node.id, None); let members = node .members .iter() .map(|member| { - let raw = ctx.header(AstNode::TSEnumMember, body, &member.span); - let id_pos = ctx.ref_field(AstProp::Id); - let init_pos = ctx.ref_field(AstProp::Initializer); - let member_id = ctx.commit_schema(raw); - let ident = match &member.id { - TsEnumMemberId::Ident(ident) => { - serialize_ident(ctx, ident, member_id) - } + TsEnumMemberId::Ident(ident) => serialize_ident(ctx, ident, None), TsEnumMemberId::Str(lit_str) => { - serialize_lit(ctx, &Lit::Str(lit_str.clone()), member_id) + serialize_lit(ctx, &Lit::Str(lit_str.clone())) } }; - let init = member - .init - .as_ref() - .map(|init| serialize_expr(ctx, init, member_id)); + let init = member.init.as_ref().map(|init| serialize_expr(ctx, init)); - ctx.write_ref(id_pos, ident); - ctx.write_maybe_ref(init_pos, init); - - member_id + ctx.write_ts_enum_member(&member.span, ident, init) }) .collect::>(); - ctx.write_refs(members_pos, members); - - ctx.write_bool(declare_pos, node.declare); - ctx.write_bool(const_pos, node.is_const); - ctx.write_ref(id_pos, ident_id); - ctx.write_ref(body_pos, body); - - pos + let body = ctx.write_ts_enum_body(&node.span, members); + ctx.write_ts_enum(&node.span, node.declare, node.is_const, id, body) } - Decl::TsModule(ts_module_decl) => { - let raw = ctx.header(AstNode::TsModule, parent, &ts_module_decl.span); - ctx.commit_schema(raw) + Decl::TsModule(node) => { + let ident = match &node.id { + TsModuleName::Ident(ident) => serialize_ident(ctx, ident, None), + TsModuleName::Str(str_lit) => { + serialize_lit(ctx, &Lit::Str(str_lit.clone())) + } + }; + + let body = node + .body + .as_ref() + .map(|body| serialize_ts_namespace_body(ctx, body)); + + ctx.write_ts_module_decl( + &node.span, + node.declare, + node.global, + ident, + body, + ) } } } +fn serialize_ts_namespace_body( + ctx: &mut TsEsTreeBuilder, + node: &TsNamespaceBody, +) -> NodeRef { + match node { + TsNamespaceBody::TsModuleBlock(mod_block) => { + let items = mod_block + .body + .iter() + .map(|item| match item { + ModuleItem::ModuleDecl(decl) => serialize_module_decl(ctx, decl), + ModuleItem::Stmt(stmt) => serialize_stmt(ctx, stmt), + }) + .collect::>(); + + ctx.write_ts_module_block(&mod_block.span, items) + } + TsNamespaceBody::TsNamespaceDecl(node) => { + let ident = serialize_ident(ctx, &node.id, None); + let body = serialize_ts_namespace_body(ctx, &node.body); + + ctx.write_ts_module_decl( + &node.span, + node.declare, + node.global, + ident, + Some(body), + ) + } + } +} + +fn serialize_ts_type_elem( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeElement, +) -> NodeRef { + match node { + TsTypeElement::TsCallSignatureDecl(ts_call) => { + let type_ann = + maybe_serialize_ts_type_param_decl(ctx, &ts_call.type_params); + let return_type = maybe_serialize_ts_type_ann(ctx, &ts_call.type_ann); + let params = ts_call + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + ctx.write_ts_call_sig_decl(&ts_call.span, type_ann, params, return_type) + } + TsTypeElement::TsConstructSignatureDecl(sig) => { + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &sig.type_params); + + let params = sig + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + // Must be present + let return_type = + maybe_serialize_ts_type_ann(ctx, &sig.type_ann).unwrap(); + + ctx.write_ts_construct_sig(&sig.span, type_params, params, return_type) + } + TsTypeElement::TsPropertySignature(sig) => { + let key = serialize_expr(ctx, &sig.key); + let type_ann = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); + + ctx.write_ts_property_sig( + &sig.span, + sig.computed, + sig.optional, + sig.readonly, + key, + type_ann, + ) + } + TsTypeElement::TsGetterSignature(sig) => { + let key = serialize_expr(ctx, sig.key.as_ref()); + let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); + + ctx.write_ts_getter_sig(&sig.span, key, return_type) + } + TsTypeElement::TsSetterSignature(sig) => { + let key = serialize_expr(ctx, sig.key.as_ref()); + let param = serialize_ts_fn_param(ctx, &sig.param); + + ctx.write_ts_setter_sig(&sig.span, key, param) + } + TsTypeElement::TsMethodSignature(sig) => { + let key = serialize_expr(ctx, &sig.key); + let type_parms = + maybe_serialize_ts_type_param_decl(ctx, &sig.type_params); + let params = sig + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); + + ctx.write_ts_method_sig( + &sig.span, + sig.computed, + sig.optional, + key, + type_parms, + params, + return_type, + ) + } + TsTypeElement::TsIndexSignature(sig) => serialize_ts_index_sig(ctx, sig), + } +} + fn serialize_ts_index_sig( ctx: &mut TsEsTreeBuilder, node: &TsIndexSignature, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::TSMethodSignature, parent, &node.span); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let static_pos = ctx.bool_field(AstProp::Static); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); - let params = node .params .iter() - .map(|param| serialize_ts_fn_param(ctx, param, pos)) + .map(|param| serialize_ts_fn_param(ctx, param)) .collect::>(); + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); - ctx.write_bool(readonly_pos, false); - ctx.write_bool(static_pos, node.is_static); - ctx.write_refs(params_pos, params); - ctx.write_maybe_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_index_sig(&node.span, node.readonly, params, type_ann) } -fn accessibility_to_str(accessibility: Accessibility) -> String { +fn accessibility_to_str(accessibility: &Accessibility) -> String { match accessibility { Accessibility::Public => "public".to_string(), Accessibility::Protected => "protected".to_string(), @@ -2020,106 +1571,53 @@ fn accessibility_to_str(accessibility: Accessibility) -> String { fn serialize_private_name( ctx: &mut TsEsTreeBuilder, node: &PrivateName, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::PrivateIdentifier, parent, &node.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, node.name.as_str()); - - pos + ctx.write_private_identifier(&node.span, node.name.as_str()) } fn serialize_jsx_element( ctx: &mut TsEsTreeBuilder, node: &JSXElement, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXElement, parent, &node.span); - let open_pos = ctx.ref_field(AstProp::OpeningElement); - let close_pos = ctx.ref_field(AstProp::ClosingElement); - let children_pos = ctx.ref_vec_field(AstProp::Children, node.children.len()); - let pos = ctx.commit_schema(raw); - - let open = serialize_jsx_opening_element(ctx, &node.opening, pos); + let open = serialize_jsx_opening_element(ctx, &node.opening); let close = node.closing.as_ref().map(|closing| { - let raw = ctx.header(AstNode::JSXClosingElement, pos, &closing.span); - let name_pos = ctx.ref_field(AstProp::Name); - let closing_pos = ctx.commit_schema(raw); - - let name = serialize_jsx_element_name(ctx, &closing.name, closing_pos); - ctx.write_ref(name_pos, name); - - closing_pos + let name = serialize_jsx_element_name(ctx, &closing.name); + ctx.write_jsx_closing_elem(&closing.span, name) }); - let children = serialize_jsx_children(ctx, &node.children, pos); + let children = serialize_jsx_children(ctx, &node.children); - ctx.write_ref(open_pos, open); - ctx.write_maybe_ref(close_pos, close); - ctx.write_refs(children_pos, children); - - pos + ctx.write_jsx_elem(&node.span, open, close, children) } fn serialize_jsx_fragment( ctx: &mut TsEsTreeBuilder, node: &JSXFragment, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXFragment, parent, &node.span); + let opening = ctx.write_jsx_opening_frag(&node.opening.span); + let closing = ctx.write_jsx_closing_frag(&node.closing.span); + let children = serialize_jsx_children(ctx, &node.children); - let opening_pos = ctx.ref_field(AstProp::OpeningFragment); - let closing_pos = ctx.ref_field(AstProp::ClosingFragment); - let children_pos = ctx.ref_vec_field(AstProp::Children, node.children.len()); - let pos = ctx.commit_schema(raw); - - let raw = ctx.header(AstNode::JSXOpeningFragment, pos, &node.opening.span); - let opening_id = ctx.commit_schema(raw); - - let raw = ctx.header(AstNode::JSXClosingFragment, pos, &node.closing.span); - let closing_id = ctx.commit_schema(raw); - - let children = serialize_jsx_children(ctx, &node.children, pos); - - ctx.write_ref(opening_pos, opening_id); - ctx.write_ref(closing_pos, closing_id); - ctx.write_refs(children_pos, children); - - pos + ctx.write_jsx_frag(&node.span, opening, closing, children) } fn serialize_jsx_children( ctx: &mut TsEsTreeBuilder, children: &[JSXElementChild], - parent: NodeRef, ) -> Vec { children .iter() .map(|child| { match child { JSXElementChild::JSXText(text) => { - let raw = ctx.header(AstNode::JSXText, parent, &text.span); - let raw_pos = ctx.str_field(AstProp::Raw); - let value_pos = ctx.str_field(AstProp::Value); - let pos = ctx.commit_schema(raw); - - ctx.write_str(raw_pos, &text.raw); - ctx.write_str(value_pos, &text.value); - - pos + ctx.write_jsx_text(&text.span, &text.raw, &text.value) } JSXElementChild::JSXExprContainer(container) => { - serialize_jsx_container_expr(ctx, container, parent) - } - JSXElementChild::JSXElement(el) => { - serialize_jsx_element(ctx, el, parent) - } - JSXElementChild::JSXFragment(frag) => { - serialize_jsx_fragment(ctx, frag, parent) + serialize_jsx_container_expr(ctx, container) } + JSXElementChild::JSXElement(el) => serialize_jsx_element(ctx, el), + JSXElementChild::JSXFragment(frag) => serialize_jsx_fragment(ctx, frag), // No parser supports this JSXElementChild::JSXSpreadChild(_) => unreachable!(), } @@ -2130,42 +1628,28 @@ fn serialize_jsx_children( fn serialize_jsx_member_expr( ctx: &mut TsEsTreeBuilder, node: &JSXMemberExpr, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXMemberExpression, parent, &node.span); - let obj_ref = ctx.ref_field(AstProp::Object); - let prop_ref = ctx.ref_field(AstProp::Property); - let pos = ctx.commit_schema(raw); - let obj = match &node.obj { - JSXObject::JSXMemberExpr(member) => { - serialize_jsx_member_expr(ctx, member, pos) - } - JSXObject::Ident(ident) => serialize_jsx_identifier(ctx, ident, parent), + JSXObject::JSXMemberExpr(member) => serialize_jsx_member_expr(ctx, member), + JSXObject::Ident(ident) => serialize_jsx_identifier(ctx, ident), }; - let prop = serialize_ident_name_as_jsx_identifier(ctx, &node.prop, pos); + let prop = serialize_ident_name_as_jsx_identifier(ctx, &node.prop); - ctx.write_ref(obj_ref, obj); - ctx.write_ref(prop_ref, prop); - - pos + ctx.write_jsx_member_expr(&node.span, obj, prop) } fn serialize_jsx_element_name( ctx: &mut TsEsTreeBuilder, node: &JSXElementName, - parent: NodeRef, ) -> NodeRef { match &node { - JSXElementName::Ident(ident) => { - serialize_jsx_identifier(ctx, ident, parent) - } + JSXElementName::Ident(ident) => serialize_jsx_identifier(ctx, ident), JSXElementName::JSXMemberExpr(member) => { - serialize_jsx_member_expr(ctx, member, parent) + serialize_jsx_member_expr(ctx, member) } JSXElementName::JSXNamespacedName(ns) => { - serialize_jsx_namespaced_name(ctx, ns, parent) + serialize_jsx_namespaced_name(ctx, ns) } } } @@ -2173,294 +1657,183 @@ fn serialize_jsx_element_name( fn serialize_jsx_opening_element( ctx: &mut TsEsTreeBuilder, node: &JSXOpeningElement, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXOpeningElement, parent, &node.span); - let sclose_pos = ctx.bool_field(AstProp::SelfClosing); - let name_pos = ctx.ref_field(AstProp::Name); - let attrs_pos = ctx.ref_vec_field(AstProp::Attributes, node.attrs.len()); - let pos = ctx.commit_schema(raw); + let name = serialize_jsx_element_name(ctx, &node.name); - let name = serialize_jsx_element_name(ctx, &node.name, pos); - - // FIXME: type args + let type_args = node + .type_args + .as_ref() + .map(|arg| serialize_ts_param_inst(ctx, arg)); let attrs = node .attrs .iter() .map(|attr| match attr { JSXAttrOrSpread::JSXAttr(attr) => { - let raw = ctx.header(AstNode::JSXAttribute, pos, &attr.span); - let name_pos = ctx.ref_field(AstProp::Name); - let value_pos = ctx.ref_field(AstProp::Value); - let attr_pos = ctx.commit_schema(raw); - let name = match &attr.name { JSXAttrName::Ident(name) => { - serialize_ident_name_as_jsx_identifier(ctx, name, attr_pos) + serialize_ident_name_as_jsx_identifier(ctx, name) } JSXAttrName::JSXNamespacedName(node) => { - serialize_jsx_namespaced_name(ctx, node, attr_pos) + serialize_jsx_namespaced_name(ctx, node) } }; let value = attr.value.as_ref().map(|value| match value { - JSXAttrValue::Lit(lit) => serialize_lit(ctx, lit, attr_pos), + JSXAttrValue::Lit(lit) => serialize_lit(ctx, lit), JSXAttrValue::JSXExprContainer(container) => { - serialize_jsx_container_expr(ctx, container, attr_pos) - } - JSXAttrValue::JSXElement(el) => { - serialize_jsx_element(ctx, el, attr_pos) - } - JSXAttrValue::JSXFragment(frag) => { - serialize_jsx_fragment(ctx, frag, attr_pos) + serialize_jsx_container_expr(ctx, container) } + JSXAttrValue::JSXElement(el) => serialize_jsx_element(ctx, el), + JSXAttrValue::JSXFragment(frag) => serialize_jsx_fragment(ctx, frag), }); - ctx.write_ref(name_pos, name); - ctx.write_maybe_ref(value_pos, value); - - attr_pos + ctx.write_jsx_attr(&attr.span, name, value) } JSXAttrOrSpread::SpreadElement(spread) => { - let raw = ctx.header(AstNode::JSXAttribute, pos, &spread.dot3_token); - let arg_pos = ctx.ref_field(AstProp::Argument); - let attr_pos = ctx.commit_schema(raw); - - let arg = serialize_expr(ctx, &spread.expr, attr_pos); - - ctx.write_ref(arg_pos, arg); - - attr_pos + let arg = serialize_expr(ctx, &spread.expr); + ctx.write_jsx_spread_attr(&spread.dot3_token, arg) } }) .collect::>(); - ctx.write_bool(sclose_pos, node.self_closing); - ctx.write_ref(name_pos, name); - ctx.write_refs(attrs_pos, attrs); - - pos + ctx.write_jsx_opening_elem( + &node.span, + node.self_closing, + name, + attrs, + type_args, + ) } fn serialize_jsx_container_expr( ctx: &mut TsEsTreeBuilder, node: &JSXExprContainer, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXExpressionContainer, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let pos = ctx.commit_schema(raw); - let expr = match &node.expr { - JSXExpr::JSXEmptyExpr(expr) => serialize_jsx_empty_expr(ctx, expr, pos), - JSXExpr::Expr(expr) => serialize_expr(ctx, expr, pos), + JSXExpr::JSXEmptyExpr(expr) => ctx.write_jsx_empty_expr(&expr.span), + JSXExpr::Expr(expr) => serialize_expr(ctx, expr), }; - ctx.write_ref(expr_pos, expr); - - pos -} - -fn serialize_jsx_empty_expr( - ctx: &mut TsEsTreeBuilder, - node: &JSXEmptyExpr, - parent: NodeRef, -) -> NodeRef { - let raw = ctx.header(AstNode::JSXEmptyExpression, parent, &node.span); - ctx.commit_schema(raw) + ctx.write_jsx_expr_container(&node.span, expr) } fn serialize_jsx_namespaced_name( ctx: &mut TsEsTreeBuilder, node: &JSXNamespacedName, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXNamespacedName, parent, &node.span); - let ns_pos = ctx.ref_field(AstProp::Namespace); - let name_pos = ctx.ref_field(AstProp::Name); - let pos = ctx.commit_schema(raw); + let ns = ctx.write_jsx_identifier(&node.ns.span, &node.ns.sym); + let name = ctx.write_jsx_identifier(&node.name.span, &node.name.sym); - let ns_id = serialize_ident_name_as_jsx_identifier(ctx, &node.ns, pos); - let name_id = serialize_ident_name_as_jsx_identifier(ctx, &node.name, pos); - - ctx.write_ref(ns_pos, ns_id); - ctx.write_ref(name_pos, name_id); - - pos + ctx.write_jsx_namespaced_name(&node.span, ns, name) } fn serialize_ident_name_as_jsx_identifier( ctx: &mut TsEsTreeBuilder, node: &IdentName, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXIdentifier, parent, &node.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, &node.sym); - - pos + ctx.write_jsx_identifier(&node.span, &node.sym) } fn serialize_jsx_identifier( ctx: &mut TsEsTreeBuilder, node: &Ident, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXIdentifier, parent, &node.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, &node.sym); - - pos + ctx.write_jsx_identifier(&node.span, &node.sym) } -fn serialize_pat( - ctx: &mut TsEsTreeBuilder, - pat: &Pat, - parent: NodeRef, -) -> NodeRef { +fn serialize_pat(ctx: &mut TsEsTreeBuilder, pat: &Pat) -> NodeRef { match pat { - Pat::Ident(node) => serialize_ident(ctx, &node.id, parent), + Pat::Ident(node) => serialize_binding_ident(ctx, node), Pat::Array(node) => { - let raw = ctx.header(AstNode::ArrayPattern, parent, &node.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let type_pos = ctx.ref_field(AstProp::TypeAnnotation); - let elems_pos = ctx.ref_vec_field(AstProp::Elements, node.elems.len()); - let pos = ctx.commit_schema(raw); - - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); let children = node .elems .iter() - .map(|pat| { - pat - .as_ref() - .map_or(NodeRef(0), |v| serialize_pat(ctx, v, pos)) - }) + .map(|pat| pat.as_ref().map_or(NodeRef(0), |v| serialize_pat(ctx, v))) .collect::>(); - ctx.write_bool(opt_pos, node.optional); - ctx.write_maybe_ref(type_pos, type_ann); - ctx.write_refs(elems_pos, children); - - pos + ctx.write_arr_pat(&node.span, node.optional, type_ann, children) } Pat::Rest(node) => { - let raw = ctx.header(AstNode::RestElement, parent, &node.span); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + let arg = serialize_pat(ctx, &node.arg); - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); - let arg = serialize_pat(ctx, &node.arg, parent); - - ctx.write_maybe_ref(type_ann_pos, type_ann); - ctx.write_ref(arg_pos, arg); - - pos + ctx.write_rest_elem(&node.span, type_ann, arg) } Pat::Object(node) => { - let raw = ctx.header(AstNode::ObjectPattern, parent, &node.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let props_pos = ctx.ref_vec_field(AstProp::Properties, node.props.len()); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); let children = node .props .iter() .map(|prop| match prop { ObjectPatProp::KeyValue(key_value_prop) => { - let raw = - ctx.header(AstNode::Property, pos, &key_value_prop.span()); - let computed_pos = ctx.bool_field(AstProp::Computed); - let key_pos = ctx.ref_field(AstProp::Key); - let value_pos = ctx.ref_field(AstProp::Value); - let child_pos = ctx.commit_schema(raw); - let computed = matches!(key_value_prop.key, PropName::Computed(_)); - let key = serialize_prop_name(ctx, &key_value_prop.key, child_pos); - let value = - serialize_pat(ctx, key_value_prop.value.as_ref(), child_pos); + let key = serialize_prop_name(ctx, &key_value_prop.key); + let value = serialize_pat(ctx, key_value_prop.value.as_ref()); - ctx.write_bool(computed_pos, computed); - ctx.write_ref(key_pos, key); - ctx.write_ref(value_pos, value); - - child_pos + ctx.write_property( + &key_value_prop.span(), + false, + computed, + false, + "init", + key, + value, + ) } ObjectPatProp::Assign(assign_pat_prop) => { - let raw = ctx.header(AstNode::Property, pos, &assign_pat_prop.span); - // TOOD: Doesn't seem to be present in SWC ast - let _computed_pos = ctx.bool_field(AstProp::Computed); - let key_pos = ctx.ref_field(AstProp::Key); - let value_pos = ctx.ref_field(AstProp::Value); - let child_pos = ctx.commit_schema(raw); - - let ident = serialize_ident(ctx, &assign_pat_prop.key.id, parent); + let ident = serialize_binding_ident(ctx, &assign_pat_prop.key); let value = assign_pat_prop .value .as_ref() - .map(|value| serialize_expr(ctx, value, child_pos)); + .map_or(NodeRef(0), |value| serialize_expr(ctx, value)); - ctx.write_ref(key_pos, ident); - ctx.write_maybe_ref(value_pos, value); - - child_pos + ctx.write_property( + &assign_pat_prop.span, + false, + false, + false, + "init", + ident, + value, + ) } ObjectPatProp::Rest(rest_pat) => { - serialize_pat(ctx, &Pat::Rest(rest_pat.clone()), parent) + serialize_pat(ctx, &Pat::Rest(rest_pat.clone())) } }) .collect::>(); - ctx.write_bool(opt_pos, node.optional); - ctx.write_maybe_ref(type_ann_pos, type_ann); - ctx.write_refs(props_pos, children); - - pos + ctx.write_obj_pat(&node.span, node.optional, type_ann, children) } Pat::Assign(node) => { - let raw = ctx.header(AstNode::AssignmentPattern, parent, &node.span); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let pos = ctx.commit_schema(raw); + let left = serialize_pat(ctx, &node.left); + let right = serialize_expr(ctx, &node.right); - let left = serialize_pat(ctx, &node.left, pos); - let right = serialize_expr(ctx, &node.right, pos); - - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); - - pos + ctx.write_assign_pat(&node.span, left, right) } Pat::Invalid(_) => unreachable!(), - Pat::Expr(node) => serialize_expr(ctx, node, parent), + Pat::Expr(node) => serialize_expr(ctx, node), } } fn serialize_for_head( ctx: &mut TsEsTreeBuilder, for_head: &ForHead, - parent: NodeRef, ) -> NodeRef { match for_head { ForHead::VarDecl(var_decl) => { - serialize_decl(ctx, &Decl::Var(var_decl.clone()), parent) + serialize_decl(ctx, &Decl::Var(var_decl.clone())) } ForHead::UsingDecl(using_decl) => { - serialize_decl(ctx, &Decl::Using(using_decl.clone()), parent) + serialize_decl(ctx, &Decl::Using(using_decl.clone())) } - ForHead::Pat(pat) => serialize_pat(ctx, pat, parent), + ForHead::Pat(pat) => serialize_pat(ctx, pat), } } @@ -2468,471 +1841,634 @@ fn serialize_spread( ctx: &mut TsEsTreeBuilder, expr: &Expr, span: &Span, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::SpreadElement, parent, span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let expr_pos = serialize_expr(ctx, expr, parent); - ctx.write_ref(arg_pos, expr_pos); - - pos + let expr = serialize_expr(ctx, expr); + ctx.write_spread(span, expr) } fn serialize_ident_name( ctx: &mut TsEsTreeBuilder, ident_name: &IdentName, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::Identifier, parent, &ident_name.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, ident_name.sym.as_str()); - - pos + ctx.write_identifier(&ident_name.span, ident_name.sym.as_str(), false, None) } fn serialize_prop_name( ctx: &mut TsEsTreeBuilder, prop_name: &PropName, - parent: NodeRef, ) -> NodeRef { match prop_name { - PropName::Ident(ident_name) => { - serialize_ident_name(ctx, ident_name, parent) - } - PropName::Str(str_prop) => { - let raw = ctx.header(AstNode::StringLiteral, parent, &str_prop.span); - let value_pos = ctx.str_field(AstProp::Value); - ctx.write_str(value_pos, &str_prop.value); - ctx.commit_schema(raw) - } - PropName::Num(number) => { - serialize_lit(ctx, &Lit::Num(number.clone()), parent) - } - PropName::Computed(node) => serialize_expr(ctx, &node.expr, parent), + PropName::Ident(ident_name) => serialize_ident_name(ctx, ident_name), + PropName::Str(str_prop) => serialize_lit(ctx, &Lit::Str(str_prop.clone())), + PropName::Num(number) => serialize_lit(ctx, &Lit::Num(number.clone())), + PropName::Computed(node) => serialize_expr(ctx, &node.expr), PropName::BigInt(big_int) => { - serialize_lit(ctx, &Lit::BigInt(big_int.clone()), parent) + serialize_lit(ctx, &Lit::BigInt(big_int.clone())) } } } -fn serialize_lit( - ctx: &mut TsEsTreeBuilder, - lit: &Lit, - parent: NodeRef, -) -> NodeRef { +fn serialize_lit(ctx: &mut TsEsTreeBuilder, lit: &Lit) -> NodeRef { match lit { Lit::Str(node) => { - let raw = ctx.header(AstNode::StringLiteral, parent, &node.span); - let value_pos = ctx.str_field(AstProp::Value); - let pos = ctx.commit_schema(raw); + let raw_value = if let Some(v) = &node.raw { + v.to_string() + } else { + format!("{}", node.value).to_string() + }; - ctx.write_str(value_pos, &node.value); - - pos - } - Lit::Bool(lit_bool) => { - let raw = ctx.header(AstNode::Bool, parent, &lit_bool.span); - let value_pos = ctx.bool_field(AstProp::Value); - let pos = ctx.commit_schema(raw); - - ctx.write_bool(value_pos, lit_bool.value); - - pos - } - Lit::Null(node) => { - let raw = ctx.header(AstNode::Null, parent, &node.span); - ctx.commit_schema(raw) + ctx.write_str_lit(&node.span, &node.value, &raw_value) } + Lit::Bool(node) => ctx.write_bool_lit(&node.span, node.value), + Lit::Null(node) => ctx.write_null_lit(&node.span), Lit::Num(node) => { - let raw = ctx.header(AstNode::NumericLiteral, parent, &node.span); - let value_pos = ctx.str_field(AstProp::Value); - let pos = ctx.commit_schema(raw); + let raw_value = if let Some(v) = &node.raw { + v.to_string() + } else { + format!("{}", node.value).to_string() + }; let value = node.raw.as_ref().unwrap(); - ctx.write_str(value_pos, value); - - pos + ctx.write_num_lit(&node.span, value, &raw_value) } Lit::BigInt(node) => { - let raw = ctx.header(AstNode::BigIntLiteral, parent, &node.span); - let value_pos = ctx.str_field(AstProp::Value); - let pos = ctx.commit_schema(raw); + let raw_bigint_value = if let Some(v) = &node.raw { + let mut s = v.to_string(); + s.pop(); + s.to_string() + } else { + format!("{}", node.value).to_string() + }; - ctx.write_str(value_pos, &node.value.to_string()); + let raw_value = if let Some(v) = &node.raw { + v.to_string() + } else { + format!("{}", node.value).to_string() + }; - pos + ctx.write_bigint_lit( + &node.span, + &node.value.to_string(), + &raw_value, + &raw_bigint_value, + ) } Lit::Regex(node) => { - let raw = ctx.header(AstNode::RegExpLiteral, parent, &node.span); - let pattern_pos = ctx.str_field(AstProp::Pattern); - let flags_pos = ctx.str_field(AstProp::Flags); - let pos = ctx.commit_schema(raw); + let raw = format!("/{}/{}", node.exp.as_str(), node.flags.as_str()); - ctx.write_str(pattern_pos, node.exp.as_str()); - ctx.write_str(flags_pos, node.flags.as_str()); - - pos + ctx.write_regex_lit( + &node.span, + node.exp.as_str(), + node.flags.as_str(), + &raw, + &raw, + ) } - Lit::JSXText(jsxtext) => { - let raw = ctx.header(AstNode::JSXText, parent, &jsxtext.span); - ctx.commit_schema(raw) + Lit::JSXText(node) => { + ctx.write_jsx_text(&node.span, &node.raw, &node.value) } } } +fn serialize_class_member( + ctx: &mut TsEsTreeBuilder, + member: &ClassMember, +) -> Option { + match member { + ClassMember::Constructor(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let key = serialize_prop_name(ctx, &node.key); + let params = node + .params + .iter() + .map(|param| match param { + ParamOrTsParamProp::TsParamProp(prop) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let decorators = prop + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + let paramter = match &prop.param { + TsParamPropParam::Ident(binding_ident) => { + serialize_binding_ident(ctx, binding_ident) + } + TsParamPropParam::Assign(assign_pat) => { + serialize_pat(ctx, &Pat::Assign(assign_pat.clone())) + } + }; + + ctx.write_ts_param_prop( + &prop.span, + prop.is_override, + prop.readonly, + a11y, + decorators, + paramter, + ) + } + ParamOrTsParamProp::Param(param) => serialize_pat(ctx, ¶m.pat), + }) + .collect::>(); + + let body = node + .body + .as_ref() + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()))); + + let value = ctx.write_fn_expr( + &node.span, false, false, None, None, params, None, body, + ); + + Some(ctx.write_class_method( + &node.span, + false, + false, + node.is_optional, + false, + false, + "constructor", + a11y, + key, + value, + )) + } + ClassMember::Method(node) => { + let key = serialize_prop_name(ctx, &node.key); + + Some(serialize_class_method( + ctx, + &node.span, + node.is_abstract, + node.is_override, + node.is_optional, + node.is_static, + node.accessibility, + &node.kind, + key, + &node.function, + )) + } + ClassMember::PrivateMethod(node) => { + let key = serialize_private_name(ctx, &node.key); + + Some(serialize_class_method( + ctx, + &node.span, + node.is_abstract, + node.is_override, + node.is_optional, + node.is_static, + node.accessibility, + &node.kind, + key, + &node.function, + )) + } + ClassMember::ClassProp(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let key = serialize_prop_name(ctx, &node.key); + let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr)); + + let decorators = node + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + Some(ctx.write_class_prop( + &node.span, + node.declare, + false, + node.is_optional, + node.is_override, + node.readonly, + node.is_static, + a11y, + decorators, + key, + value, + )) + } + ClassMember::PrivateProp(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let decorators = node + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + let key = serialize_private_name(ctx, &node.key); + + let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr)); + + Some(ctx.write_class_prop( + &node.span, + false, + false, + node.is_optional, + node.is_override, + node.readonly, + node.is_static, + a11y, + decorators, + key, + value, + )) + } + ClassMember::TsIndexSignature(node) => { + Some(serialize_ts_index_sig(ctx, node)) + } + ClassMember::Empty(_) => None, + ClassMember::StaticBlock(node) => { + let body = serialize_stmt(ctx, &Stmt::Block(node.body.clone())); + Some(ctx.write_static_block(&node.span, body)) + } + ClassMember::AutoAccessor(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + let decorators = node + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + let key = match &node.key { + Key::Private(private_name) => serialize_private_name(ctx, private_name), + Key::Public(prop_name) => serialize_prop_name(ctx, prop_name), + }; + + let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr)); + + Some(ctx.write_accessor_property( + &node.span, + false, + false, + false, + node.is_override, + false, + node.is_static, + a11y, + decorators, + key, + value, + )) + } + } +} + +#[allow(clippy::too_many_arguments)] +fn serialize_class_method( + ctx: &mut TsEsTreeBuilder, + span: &Span, + is_abstract: bool, + is_override: bool, + is_optional: bool, + is_static: bool, + accessibility: Option, + method_kind: &MethodKind, + key: NodeRef, + function: &Function, +) -> NodeRef { + let kind = match method_kind { + MethodKind::Method => "method", + MethodKind::Getter => "getter", + MethodKind::Setter => "setter", + }; + + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &function.type_params); + let params = function + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat)) + .collect::>(); + + let return_type = maybe_serialize_ts_type_ann(ctx, &function.return_type); + + let body = function + .body + .as_ref() + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()))); + + let value = if let Some(body) = body { + ctx.write_fn_expr( + &function.span, + function.is_async, + function.is_generator, + None, + type_params, + params, + return_type, + Some(body), + ) + } else { + ctx.write_ts_empty_body_fn_expr( + span, + false, + false, + function.is_async, + function.is_generator, + None, + type_params, + params, + return_type, + ) + }; + + let a11y = accessibility.as_ref().map(accessibility_to_str); + + if is_abstract { + ctx.write_ts_abstract_method_def( + span, + false, + is_optional, + is_override, + false, + a11y, + key, + value, + ) + } else { + ctx.write_class_method( + span, + false, + false, + is_optional, + is_override, + is_static, + kind, + a11y, + key, + value, + ) + } +} + +fn serialize_ts_expr_with_type_args( + ctx: &mut TsEsTreeBuilder, + node: &TsExprWithTypeArgs, +) -> NodeRef { + let expr = serialize_expr(ctx, &node.expr); + let type_args = node + .type_args + .as_ref() + .map(|arg| serialize_ts_param_inst(ctx, arg)); + + ctx.write_ts_class_implements(&node.span, expr, type_args) +} + +fn serialize_decorator(ctx: &mut TsEsTreeBuilder, node: &Decorator) -> NodeRef { + let expr = serialize_expr(ctx, &node.expr); + ctx.write_decorator(&node.span, expr) +} + +fn serialize_binding_ident( + ctx: &mut TsEsTreeBuilder, + node: &BindingIdent, +) -> NodeRef { + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + ctx.write_identifier(&node.span, &node.sym, node.optional, type_ann) +} + fn serialize_ts_param_inst( ctx: &mut TsEsTreeBuilder, node: &TsTypeParamInstantiation, - parent: NodeRef, ) -> NodeRef { - let raw = - ctx.header(AstNode::TSTypeParameterInstantiation, parent, &node.span); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let pos = ctx.commit_schema(raw); - let params = node .params .iter() - .map(|param| serialize_ts_type(ctx, param, pos)) + .map(|param| serialize_ts_type(ctx, param)) .collect::>(); - ctx.write_refs(params_pos, params); - - pos + ctx.write_ts_type_param_inst(&node.span, params) } -fn serialize_ts_type( - ctx: &mut TsEsTreeBuilder, - node: &TsType, - parent: NodeRef, -) -> NodeRef { +fn serialize_ts_type(ctx: &mut TsEsTreeBuilder, node: &TsType) -> NodeRef { match node { TsType::TsKeywordType(node) => { let kind = match node.kind { - TsKeywordTypeKind::TsAnyKeyword => AstNode::TSAnyKeyword, - TsKeywordTypeKind::TsUnknownKeyword => AstNode::TSUnknownKeyword, - TsKeywordTypeKind::TsNumberKeyword => AstNode::TSNumberKeyword, - TsKeywordTypeKind::TsObjectKeyword => AstNode::TSObjectKeyword, - TsKeywordTypeKind::TsBooleanKeyword => AstNode::TSBooleanKeyword, - TsKeywordTypeKind::TsBigIntKeyword => AstNode::TSBigIntKeyword, - TsKeywordTypeKind::TsStringKeyword => AstNode::TSStringKeyword, - TsKeywordTypeKind::TsSymbolKeyword => AstNode::TSSymbolKeyword, - TsKeywordTypeKind::TsVoidKeyword => AstNode::TSVoidKeyword, - TsKeywordTypeKind::TsUndefinedKeyword => AstNode::TSUndefinedKeyword, - TsKeywordTypeKind::TsNullKeyword => AstNode::TSNullKeyword, - TsKeywordTypeKind::TsNeverKeyword => AstNode::TSNeverKeyword, - TsKeywordTypeKind::TsIntrinsicKeyword => AstNode::TSIntrinsicKeyword, + TsKeywordTypeKind::TsAnyKeyword => TsKeywordKind::Any, + TsKeywordTypeKind::TsUnknownKeyword => TsKeywordKind::Unknown, + TsKeywordTypeKind::TsNumberKeyword => TsKeywordKind::Number, + TsKeywordTypeKind::TsObjectKeyword => TsKeywordKind::Object, + TsKeywordTypeKind::TsBooleanKeyword => TsKeywordKind::Boolean, + TsKeywordTypeKind::TsBigIntKeyword => TsKeywordKind::BigInt, + TsKeywordTypeKind::TsStringKeyword => TsKeywordKind::String, + TsKeywordTypeKind::TsSymbolKeyword => TsKeywordKind::Symbol, + TsKeywordTypeKind::TsVoidKeyword => TsKeywordKind::Void, + TsKeywordTypeKind::TsUndefinedKeyword => TsKeywordKind::Undefined, + TsKeywordTypeKind::TsNullKeyword => TsKeywordKind::Null, + TsKeywordTypeKind::TsNeverKeyword => TsKeywordKind::Never, + TsKeywordTypeKind::TsIntrinsicKeyword => TsKeywordKind::Intrinsic, }; - let raw = ctx.header(kind, parent, &node.span); - ctx.commit_schema(raw) - } - TsType::TsThisType(node) => { - let raw = ctx.header(AstNode::TSThisType, parent, &node.span); - ctx.commit_schema(raw) + ctx.write_ts_keyword(kind, &node.span) } + TsType::TsThisType(node) => ctx.write_ts_this_type(&node.span), TsType::TsFnOrConstructorType(node) => match node { TsFnOrConstructorType::TsFnType(node) => { - let raw = ctx.header(AstNode::TSFunctionType, parent, &node.span); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let pos = ctx.commit_schema(raw); - let param_ids = node .params .iter() - .map(|param| serialize_ts_fn_param(ctx, param, pos)) + .map(|param| serialize_ts_fn_param(ctx, param)) .collect::>(); - ctx.write_refs(params_pos, param_ids); - - pos + ctx.write_ts_fn_type(&node.span, param_ids) } - TsFnOrConstructorType::TsConstructorType(_) => { - todo!() + TsFnOrConstructorType::TsConstructorType(node) => { + // interface Foo { new(arg1: any): any } + let type_params = node + .type_params + .as_ref() + .map(|param| serialize_ts_type_param_decl(ctx, param)); + + let params = node + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + let return_type = serialize_ts_type_ann(ctx, node.type_ann.as_ref()); + + ctx.write_ts_construct_sig(&node.span, type_params, params, return_type) } }, TsType::TsTypeRef(node) => { - let raw = ctx.header(AstNode::TSTypeReference, parent, &node.span); - let name_pos = ctx.ref_field(AstProp::TypeName); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let pos = ctx.commit_schema(raw); - - let name = serialize_ts_entity_name(ctx, &node.type_name, pos); + let name = serialize_ts_entity_name(ctx, &node.type_name); let type_args = node .type_params .clone() - .map(|param| serialize_ts_param_inst(ctx, ¶m, pos)); + .map(|param| serialize_ts_param_inst(ctx, ¶m)); - ctx.write_ref(name_pos, name); - ctx.write_maybe_ref(type_args_pos, type_args); - - pos + ctx.write_ts_type_ref(&node.span, name, type_args) } TsType::TsTypeQuery(node) => { - let raw = ctx.header(AstNode::TSTypeQuery, parent, &node.span); - let name_pos = ctx.ref_field(AstProp::ExprName); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let pos = ctx.commit_schema(raw); - let expr_name = match &node.expr_name { TsTypeQueryExpr::TsEntityName(entity) => { - serialize_ts_entity_name(ctx, entity, pos) + serialize_ts_entity_name(ctx, entity) } TsTypeQueryExpr::Import(child) => { - serialize_ts_type(ctx, &TsType::TsImportType(child.clone()), pos) + serialize_ts_type(ctx, &TsType::TsImportType(child.clone())) } }; let type_args = node .type_args .clone() - .map(|param| serialize_ts_param_inst(ctx, ¶m, pos)); + .map(|param| serialize_ts_param_inst(ctx, ¶m)); - ctx.write_ref(name_pos, expr_name); - ctx.write_maybe_ref(type_args_pos, type_args); - - pos + ctx.write_ts_type_query(&node.span, expr_name, type_args) } - TsType::TsTypeLit(_) => { - // TODO: Not sure what this is - todo!() + TsType::TsTypeLit(node) => { + let members = node + .members + .iter() + .map(|member| serialize_ts_type_elem(ctx, member)) + .collect::>(); + + ctx.write_ts_type_lit(&node.span, members) } TsType::TsArrayType(node) => { - let raw = ctx.header(AstNode::TSArrayType, parent, &node.span); - let elem_pos = ctx.ref_field(AstProp::ElementType); - let pos = ctx.commit_schema(raw); - - let elem = serialize_ts_type(ctx, &node.elem_type, pos); - - ctx.write_ref(elem_pos, elem); - - pos + let elem = serialize_ts_type(ctx, &node.elem_type); + ctx.write_ts_array_type(&node.span, elem) } TsType::TsTupleType(node) => { - let raw = ctx.header(AstNode::TSTupleType, parent, &node.span); - let children_pos = - ctx.ref_vec_field(AstProp::ElementTypes, node.elem_types.len()); - let pos = ctx.commit_schema(raw); - let children = node .elem_types .iter() .map(|elem| { if let Some(label) = &elem.label { - let raw = ctx.header(AstNode::TSNamedTupleMember, pos, &elem.span); - let label_pos = ctx.ref_field(AstProp::Label); - let type_pos = ctx.ref_field(AstProp::ElementType); - let child_pos = ctx.commit_schema(raw); + let label = serialize_pat(ctx, label); + let type_id = serialize_ts_type(ctx, elem.ty.as_ref()); - let label_id = serialize_pat(ctx, label, child_pos); - let type_id = serialize_ts_type(ctx, elem.ty.as_ref(), child_pos); - - ctx.write_ref(label_pos, label_id); - ctx.write_ref(type_pos, type_id); - - child_pos + ctx.write_ts_named_tuple_member(&elem.span, label, type_id) } else { - serialize_ts_type(ctx, elem.ty.as_ref(), pos) + serialize_ts_type(ctx, elem.ty.as_ref()) } }) .collect::>(); - ctx.write_refs(children_pos, children); - - pos + ctx.write_ts_tuple_type(&node.span, children) + } + TsType::TsOptionalType(node) => { + let type_ann = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_optional_type(&node.span, type_ann) } - TsType::TsOptionalType(_) => todo!(), TsType::TsRestType(node) => { - let raw = ctx.header(AstNode::TSRestType, parent, &node.span); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); - - ctx.write_ref(type_ann_pos, type_ann); - - pos + let type_ann = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_rest_type(&node.span, type_ann) } TsType::TsUnionOrIntersectionType(node) => match node { TsUnionOrIntersectionType::TsUnionType(node) => { - let raw = ctx.header(AstNode::TSUnionType, parent, &node.span); - let types_pos = ctx.ref_vec_field(AstProp::Types, node.types.len()); - let pos = ctx.commit_schema(raw); - let children = node .types .iter() - .map(|item| serialize_ts_type(ctx, item, pos)) + .map(|item| serialize_ts_type(ctx, item)) .collect::>(); - ctx.write_refs(types_pos, children); - - pos + ctx.write_ts_union_type(&node.span, children) } TsUnionOrIntersectionType::TsIntersectionType(node) => { - let raw = ctx.header(AstNode::TSIntersectionType, parent, &node.span); - let types_pos = ctx.ref_vec_field(AstProp::Types, node.types.len()); - let pos = ctx.commit_schema(raw); - let children = node .types .iter() - .map(|item| serialize_ts_type(ctx, item, pos)) + .map(|item| serialize_ts_type(ctx, item)) .collect::>(); - ctx.write_refs(types_pos, children); - - pos + ctx.write_ts_intersection_type(&node.span, children) } }, TsType::TsConditionalType(node) => { - let raw = ctx.header(AstNode::TSConditionalType, parent, &node.span); - let check_pos = ctx.ref_field(AstProp::CheckType); - let extends_pos = ctx.ref_field(AstProp::ExtendsType); - let true_pos = ctx.ref_field(AstProp::TrueType); - let false_pos = ctx.ref_field(AstProp::FalseType); - let pos = ctx.commit_schema(raw); + let check = serialize_ts_type(ctx, &node.check_type); + let extends = serialize_ts_type(ctx, &node.extends_type); + let v_true = serialize_ts_type(ctx, &node.true_type); + let v_false = serialize_ts_type(ctx, &node.false_type); - let check = serialize_ts_type(ctx, &node.check_type, pos); - let extends = serialize_ts_type(ctx, &node.extends_type, pos); - let v_true = serialize_ts_type(ctx, &node.true_type, pos); - let v_false = serialize_ts_type(ctx, &node.false_type, pos); - - ctx.write_ref(check_pos, check); - ctx.write_ref(extends_pos, extends); - ctx.write_ref(true_pos, v_true); - ctx.write_ref(false_pos, v_false); - - pos + ctx.write_ts_conditional_type(&node.span, check, extends, v_true, v_false) } TsType::TsInferType(node) => { - let raw = ctx.header(AstNode::TSInferType, parent, &node.span); - let param_pos = ctx.ref_field(AstProp::TypeParameter); - let pos = ctx.commit_schema(raw); - - let param = serialize_ts_type_param(ctx, &node.type_param, parent); - - ctx.write_ref(param_pos, param); - - pos + let param = serialize_ts_type_param(ctx, &node.type_param); + ctx.write_ts_infer_type(&node.span, param) + } + TsType::TsParenthesizedType(node) => { + // Not materialized in TSEstree + serialize_ts_type(ctx, &node.type_ann) } - TsType::TsParenthesizedType(_) => todo!(), TsType::TsTypeOperator(node) => { - let raw = ctx.header(AstNode::TSTypeOperator, parent, &node.span); - let operator_pos = ctx.str_field(AstProp::Operator); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); + let type_ann = serialize_ts_type(ctx, &node.type_ann); - let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); - - ctx.write_str( - operator_pos, - match node.op { - TsTypeOperatorOp::KeyOf => "keyof", - TsTypeOperatorOp::Unique => "unique", - TsTypeOperatorOp::ReadOnly => "readonly", - }, - ); - ctx.write_ref(type_ann_pos, type_ann); - - pos - } - TsType::TsIndexedAccessType(node) => { - let raw = ctx.header(AstNode::TSIndexedAccessType, parent, &node.span); - let index_type_pos = ctx.ref_field(AstProp::IndexType); - let obj_type_pos = ctx.ref_field(AstProp::ObjectType); - let pos = ctx.commit_schema(raw); - - let index = serialize_ts_type(ctx, &node.index_type, pos); - let obj = serialize_ts_type(ctx, &node.obj_type, pos); - - ctx.write_ref(index_type_pos, index); - ctx.write_ref(obj_type_pos, obj); - - pos - } - TsType::TsMappedType(node) => { - let raw = ctx.header(AstNode::TSMappedType, parent, &node.span); - let name_pos = ctx.ref_field(AstProp::NameType); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let type_param_pos = ctx.ref_field(AstProp::TypeParameter); - let pos = ctx.commit_schema(raw); - - let opt_pos = - create_true_plus_minus_field(ctx, AstProp::Optional, node.optional); - let readonly_pos = - create_true_plus_minus_field(ctx, AstProp::Readonly, node.readonly); - - let name_id = maybe_serialize_ts_type(ctx, &node.name_type, pos); - let type_ann = maybe_serialize_ts_type(ctx, &node.type_ann, pos); - let type_param = serialize_ts_type_param(ctx, &node.type_param, pos); - - write_true_plus_minus(ctx, opt_pos, node.optional); - write_true_plus_minus(ctx, readonly_pos, node.readonly); - ctx.write_maybe_ref(name_pos, name_id); - ctx.write_maybe_ref(type_ann_pos, type_ann); - ctx.write_ref(type_param_pos, type_param); - - pos - } - TsType::TsLitType(node) => serialize_ts_lit_type(ctx, node, parent), - TsType::TsTypePredicate(node) => { - let raw = ctx.header(AstNode::TSTypePredicate, parent, &node.span); - let asserts_pos = ctx.bool_field(AstProp::Asserts); - let param_name_pos = ctx.ref_field(AstProp::ParameterName); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let param_name = match &node.param_name { - TsThisTypeOrIdent::TsThisType(ts_this_type) => { - let raw = ctx.header(AstNode::TSThisType, pos, &ts_this_type.span); - ctx.commit_schema(raw) - } - TsThisTypeOrIdent::Ident(ident) => serialize_ident(ctx, ident, pos), + let op = match node.op { + TsTypeOperatorOp::KeyOf => "keyof", + TsTypeOperatorOp::Unique => "unique", + TsTypeOperatorOp::ReadOnly => "readonly", }; - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + ctx.write_ts_type_op(&node.span, op, type_ann) + } + TsType::TsIndexedAccessType(node) => { + let index = serialize_ts_type(ctx, &node.index_type); + let obj = serialize_ts_type(ctx, &node.obj_type); - ctx.write_bool(asserts_pos, node.asserts); - ctx.write_ref(param_name_pos, param_name); - ctx.write_maybe_ref(type_ann_pos, type_ann); + ctx.write_ts_indexed_access_type(&node.span, index, obj) + } + TsType::TsMappedType(node) => { + let name = maybe_serialize_ts_type(ctx, &node.name_type); + let type_ann = maybe_serialize_ts_type(ctx, &node.type_ann); + let type_param = serialize_ts_type_param(ctx, &node.type_param); - pos + ctx.write_ts_mapped_type( + &node.span, + node.readonly, + node.optional, + name, + type_ann, + type_param, + ) + } + TsType::TsLitType(node) => serialize_ts_lit_type(ctx, node), + TsType::TsTypePredicate(node) => { + let param_name = match &node.param_name { + TsThisTypeOrIdent::TsThisType(node) => { + ctx.write_ts_this_type(&node.span) + } + TsThisTypeOrIdent::Ident(ident) => serialize_ident(ctx, ident, None), + }; + + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + + ctx.write_ts_type_predicate( + &node.span, + node.asserts, + param_name, + type_ann, + ) } TsType::TsImportType(node) => { - let raw = ctx.header(AstNode::TSTypePredicate, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let qualifier_pos = ctx.ref_field(AstProp::Qualifier); - let pos = ctx.commit_schema(raw); - let arg = serialize_ts_lit_type( ctx, &TsLitType { lit: TsLit::Str(node.arg.clone()), span: node.arg.span, }, - pos, ); - let type_arg = node.type_args.clone().map(|param_node| { - serialize_ts_param_inst(ctx, param_node.as_ref(), pos) - }); + let type_arg = node + .type_args + .clone() + .map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref())); - let qualifier = node.qualifier.clone().map_or(NodeRef(0), |quali| { - serialize_ts_entity_name(ctx, &quali, pos) - }); + let qualifier = node + .qualifier + .clone() + .map(|quali| serialize_ts_entity_name(ctx, &quali)); - ctx.write_ref(arg_pos, arg); - ctx.write_ref(qualifier_pos, qualifier); - ctx.write_maybe_ref(type_args_pos, type_arg); - - pos + ctx.write_ts_import_type(&node.span, arg, qualifier, type_arg) } } } @@ -2940,80 +2476,47 @@ fn serialize_ts_type( fn serialize_ts_lit_type( ctx: &mut TsEsTreeBuilder, node: &TsLitType, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::TSLiteralType, parent, &node.span); - let lit_pos = ctx.ref_field(AstProp::Literal); - let pos = ctx.commit_schema(raw); - - let lit = match &node.lit { - TsLit::Number(lit) => serialize_lit(ctx, &Lit::Num(lit.clone()), pos), - TsLit::Str(lit) => serialize_lit(ctx, &Lit::Str(lit.clone()), pos), - TsLit::Bool(lit) => serialize_lit(ctx, &Lit::Bool(*lit), pos), - TsLit::BigInt(lit) => serialize_lit(ctx, &Lit::BigInt(lit.clone()), pos), - TsLit::Tpl(lit) => serialize_expr( - ctx, - &Expr::Tpl(Tpl { - span: lit.span, - exprs: vec![], - quasis: lit.quasis.clone(), - }), - pos, - ), - }; - - ctx.write_ref(lit_pos, lit); - - pos -} - -fn create_true_plus_minus_field( - ctx: &mut TsEsTreeBuilder, - prop: AstProp, - value: Option, -) -> NodePos { - if let Some(v) = value { - match v { - TruePlusMinus::True => NodePos::Bool(ctx.bool_field(prop)), - TruePlusMinus::Plus | TruePlusMinus::Minus => { - NodePos::Str(ctx.str_field(prop)) - } + match &node.lit { + TsLit::Number(lit) => { + let lit = serialize_lit(ctx, &Lit::Num(lit.clone())); + ctx.write_ts_lit_type(&node.span, lit) } - } else { - NodePos::Undef(ctx.undefined_field(prop)) - } -} + TsLit::Str(lit) => { + let lit = serialize_lit(ctx, &Lit::Str(lit.clone())); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::Bool(lit) => { + let lit = serialize_lit(ctx, &Lit::Bool(*lit)); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::BigInt(lit) => { + let lit = serialize_lit(ctx, &Lit::BigInt(lit.clone())); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::Tpl(lit) => { + let quasis = lit + .quasis + .iter() + .map(|quasi| { + ctx.write_template_elem( + &quasi.span, + quasi.tail, + &quasi.raw, + &quasi + .cooked + .as_ref() + .map_or("".to_string(), |v| v.to_string()), + ) + }) + .collect::>(); + let types = lit + .types + .iter() + .map(|ts_type| serialize_ts_type(ctx, ts_type)) + .collect::>(); -fn extract_pos(pos: NodePos) -> usize { - match pos { - NodePos::Bool(bool_pos) => bool_pos.0, - NodePos::Field(field_pos) => field_pos.0, - NodePos::FieldArr(field_arr_pos) => field_arr_pos.0, - NodePos::Str(str_pos) => str_pos.0, - NodePos::Undef(undef_pos) => undef_pos.0, - NodePos::Null(null_pos) => null_pos.0, - } -} - -fn write_true_plus_minus( - ctx: &mut TsEsTreeBuilder, - pos: NodePos, - value: Option, -) { - if let Some(v) = value { - match v { - TruePlusMinus::True => { - let bool_pos = BoolPos(extract_pos(pos)); - ctx.write_bool(bool_pos, true); - } - TruePlusMinus::Plus => { - let str_pos = StrPos(extract_pos(pos)); - ctx.write_str(str_pos, "+") - } - TruePlusMinus::Minus => { - let str_pos = StrPos(extract_pos(pos)); - ctx.write_str(str_pos, "-") - } + ctx.write_ts_tpl_lit(&node.span, quasis, types) } } } @@ -3021,114 +2524,91 @@ fn write_true_plus_minus( fn serialize_ts_entity_name( ctx: &mut TsEsTreeBuilder, node: &TsEntityName, - parent: NodeRef, ) -> NodeRef { match &node { - TsEntityName::TsQualifiedName(_) => todo!(), - TsEntityName::Ident(ident) => serialize_ident(ctx, ident, parent), + TsEntityName::TsQualifiedName(node) => { + let left = serialize_ts_entity_name(ctx, &node.left); + let right = serialize_ident_name(ctx, &node.right); + + ctx.write_ts_qualified_name(&node.span, left, right) + } + TsEntityName::Ident(ident) => serialize_ident(ctx, ident, None), } } fn maybe_serialize_ts_type_ann( ctx: &mut TsEsTreeBuilder, node: &Option>, - parent: NodeRef, ) -> Option { node .as_ref() - .map(|type_ann| serialize_ts_type_ann(ctx, type_ann, parent)) + .map(|type_ann| serialize_ts_type_ann(ctx, type_ann)) } fn serialize_ts_type_ann( ctx: &mut TsEsTreeBuilder, node: &TsTypeAnn, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::TSTypeAnnotation, parent, &node.span); - let type_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let v_type = serialize_ts_type(ctx, &node.type_ann, pos); - - ctx.write_ref(type_pos, v_type); - - pos + let v_type = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_type_ann(&node.span, v_type) } fn maybe_serialize_ts_type( ctx: &mut TsEsTreeBuilder, node: &Option>, - parent: NodeRef, ) -> Option { - node - .as_ref() - .map(|item| serialize_ts_type(ctx, item, parent)) + node.as_ref().map(|item| serialize_ts_type(ctx, item)) } fn serialize_ts_type_param( ctx: &mut TsEsTreeBuilder, node: &TsTypeParam, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::TSTypeParameter, parent, &node.span); - let name_pos = ctx.ref_field(AstProp::Name); - let constraint_pos = ctx.ref_field(AstProp::Constraint); - let default_pos = ctx.ref_field(AstProp::Default); - let const_pos = ctx.bool_field(AstProp::Const); - let in_pos = ctx.bool_field(AstProp::In); - let out_pos = ctx.bool_field(AstProp::Out); - let pos = ctx.commit_schema(raw); + let name = serialize_ident(ctx, &node.name, None); + let constraint = maybe_serialize_ts_type(ctx, &node.constraint); + let default = maybe_serialize_ts_type(ctx, &node.default); - let name = serialize_ident(ctx, &node.name, pos); - let constraint = maybe_serialize_ts_type(ctx, &node.constraint, pos); - let default = maybe_serialize_ts_type(ctx, &node.default, pos); - - ctx.write_bool(const_pos, node.is_const); - ctx.write_bool(in_pos, node.is_in); - ctx.write_bool(out_pos, node.is_out); - ctx.write_ref(name_pos, name); - ctx.write_maybe_ref(constraint_pos, constraint); - ctx.write_maybe_ref(default_pos, default); - - pos + ctx.write_ts_type_param( + &node.span, + node.is_in, + node.is_out, + node.is_const, + name, + constraint, + default, + ) } -fn maybe_serialize_ts_type_param( +fn maybe_serialize_ts_type_param_decl( ctx: &mut TsEsTreeBuilder, node: &Option>, - parent: NodeRef, ) -> Option { - node.as_ref().map(|node| { - let raw = - ctx.header(AstNode::TSTypeParameterDeclaration, parent, &node.span); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let pos = ctx.commit_schema(raw); + node + .as_ref() + .map(|node| serialize_ts_type_param_decl(ctx, node)) +} - let params = node - .params - .iter() - .map(|param| serialize_ts_type_param(ctx, param, pos)) - .collect::>(); +fn serialize_ts_type_param_decl( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeParamDecl, +) -> NodeRef { + let params = node + .params + .iter() + .map(|param| serialize_ts_type_param(ctx, param)) + .collect::>(); - ctx.write_refs(params_pos, params); - - pos - }) + ctx.write_ts_type_param_decl(&node.span, params) } fn serialize_ts_fn_param( ctx: &mut TsEsTreeBuilder, node: &TsFnParam, - parent: NodeRef, ) -> NodeRef { match node { - TsFnParam::Ident(ident) => serialize_ident(ctx, ident, parent), - TsFnParam::Array(pat) => { - serialize_pat(ctx, &Pat::Array(pat.clone()), parent) - } - TsFnParam::Rest(pat) => serialize_pat(ctx, &Pat::Rest(pat.clone()), parent), - TsFnParam::Object(pat) => { - serialize_pat(ctx, &Pat::Object(pat.clone()), parent) - } + TsFnParam::Ident(ident) => serialize_binding_ident(ctx, ident), + TsFnParam::Array(pat) => serialize_pat(ctx, &Pat::Array(pat.clone())), + TsFnParam::Rest(pat) => serialize_pat(ctx, &Pat::Rest(pat.clone())), + TsFnParam::Object(pat) => serialize_pat(ctx, &Pat::Object(pat.clone())), } } diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs index 967bbef32a..340f9f3225 100644 --- a/cli/tools/lint/ast_buffer/ts_estree.rs +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -5,22 +5,17 @@ use std::fmt::Debug; use std::fmt::Display; use deno_ast::swc::common::Span; +use deno_ast::view::TruePlusMinus; use super::buffer::AstBufSerializer; -use super::buffer::BoolPos; -use super::buffer::FieldArrPos; -use super::buffer::FieldPos; use super::buffer::NodeRef; -use super::buffer::NullPos; -use super::buffer::PendingNodeRef; use super::buffer::SerializeCtx; -use super::buffer::StrPos; -use super::buffer::UndefPos; #[derive(Debug, Clone, PartialEq)] pub enum AstNode { // First node must always be the empty/invalid node Invalid, + RefArray, // Typically the Program, @@ -29,17 +24,27 @@ pub enum AstNode { ExportDefaultDeclaration, ExportNamedDeclaration, ImportDeclaration, - TsExportAssignment, - TsImportEquals, - TsNamespaceExport, + ImportSpecifier, + ImportAttribute, + ImportDefaultSpecifier, + ImportNamespaceSpecifier, + TSExportAssignment, + TSImportEqualss, + TSNamespaceExport, + TSNamespaceExportDeclaration, + TSImportEqualsDeclaration, + TSExternalModuleReference, + TSModuleDeclaration, + TSModuleBlock, // Decls ClassDeclaration, FunctionDeclaration, TSEnumDeclaration, TSInterface, - TsModule, - TsTypeAlias, + TSInterfaceDeclaration, + TSModule, + TSTypeAliasDeclaration, Using, VariableDeclaration, @@ -74,12 +79,13 @@ pub enum AstNode { ChainExpression, ClassExpression, ConditionalExpression, + EmptyExpr, FunctionExpression, Identifier, ImportExpression, LogicalExpression, MemberExpression, - MetaProp, + MetaProperty, NewExpression, ObjectExpression, PrivateIdentifier, @@ -89,8 +95,6 @@ pub enum AstNode { TemplateLiteral, ThisExpression, TSAsExpression, - TsConstAssertion, - TsInstantiation, TSNonNullExpression, TSSatisfiesExpression, TSTypeAssertion, @@ -98,16 +102,8 @@ pub enum AstNode { UpdateExpression, YieldExpression, - // TODO: TSEsTree uses a single literal node - // Literals - StringLiteral, - Bool, - Null, - NumericLiteral, - BigIntLiteral, - RegExpLiteral, - - EmptyExpr, + // Other + Literal, SpreadElement, Property, VariableDeclarator, @@ -117,6 +113,10 @@ pub enum AstNode { TemplateElement, MethodDefinition, ClassBody, + PropertyDefinition, + Decorator, + StaticBlock, + AccessorProperty, // Patterns ArrayPattern, @@ -150,6 +150,7 @@ pub enum AstNode { TSTypeReference, TSThisType, TSLiteralType, + TSTypeLiteral, TSInferType, TSConditionalType, TSUnionType, @@ -159,7 +160,7 @@ pub enum AstNode { TSTupleType, TSNamedTupleMember, TSFunctionType, - TsCallSignatureDeclaration, + TSCallSignatureDeclaration, TSPropertySignature, TSMethodSignature, TSIndexSignature, @@ -170,6 +171,13 @@ pub enum AstNode { TSRestType, TSArrayType, TSClassImplements, + TSAbstractMethodDefinition, + TSEmptyBodyFunctionExpression, + TSParameterProperty, + TSConstructSignatureDeclaration, + TSQualifiedName, + TSOptionalType, + TSTemplateLiteralType, TSAnyKeyword, TSBigIntKeyword, @@ -219,6 +227,7 @@ pub enum AstProp { Async, Attributes, Await, + BigInt, Block, Body, Callee, @@ -235,6 +244,7 @@ pub enum AstProp { Declaration, Declarations, Declare, + Decorators, Default, Definite, Delegate, @@ -246,12 +256,14 @@ pub enum AstProp { Expression, Expressions, Exported, + ExportKind, Extends, ExtendsType, FalseType, Finalizer, Flags, Generator, + Global, Handler, Id, In, @@ -259,6 +271,8 @@ pub enum AstProp { Init, Initializer, Implements, + Imported, + ImportKind, Key, Kind, Label, @@ -268,6 +282,7 @@ pub enum AstProp { Members, Meta, Method, + ModuleReference, Name, Namespace, NameType, @@ -277,8 +292,12 @@ pub enum AstProp { OpeningFragment, Operator, Optional, + Options, Out, + Override, Param, + Parameter, + Parameters, ParameterName, Params, Pattern, @@ -290,6 +309,7 @@ pub enum AstProp { Quasis, Raw, Readonly, + Regex, ReturnType, Right, SelfClosing, @@ -314,8 +334,6 @@ pub enum AstProp { Value, // Last value is used for max value } -// TODO: Feels like there should be an easier way to iterater over an -// enum in Rust and lowercase the first letter. impl Display for AstProp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match self { @@ -333,6 +351,7 @@ impl Display for AstProp { AstProp::Async => "async", AstProp::Attributes => "attributes", AstProp::Await => "await", + AstProp::BigInt => "bigint", AstProp::Block => "block", AstProp::Body => "body", AstProp::Callee => "callee", @@ -349,6 +368,7 @@ impl Display for AstProp { AstProp::Declaration => "declaration", AstProp::Declarations => "declarations", AstProp::Declare => "declare", + AstProp::Decorators => "decorators", AstProp::Default => "default", AstProp::Definite => "definite", AstProp::Delegate => "delegate", @@ -359,6 +379,7 @@ impl Display for AstProp { AstProp::ExprName => "exprName", AstProp::Expression => "expression", AstProp::Expressions => "expressions", + AstProp::ExportKind => "exportKind", AstProp::Exported => "exported", AstProp::Extends => "extends", AstProp::ExtendsType => "extendsType", @@ -366,6 +387,7 @@ impl Display for AstProp { AstProp::Finalizer => "finalizer", AstProp::Flags => "flags", AstProp::Generator => "generator", + AstProp::Global => "global", AstProp::Handler => "handler", AstProp::Id => "id", AstProp::In => "in", @@ -373,6 +395,8 @@ impl Display for AstProp { AstProp::Init => "init", AstProp::Initializer => "initializer", AstProp::Implements => "implements", + AstProp::Imported => "imported", + AstProp::ImportKind => "importKind", AstProp::Key => "key", AstProp::Kind => "kind", AstProp::Label => "label", @@ -382,6 +406,7 @@ impl Display for AstProp { AstProp::Members => "members", AstProp::Meta => "meta", AstProp::Method => "method", + AstProp::ModuleReference => "moduleReference", AstProp::Name => "name", AstProp::Namespace => "namespace", AstProp::NameType => "nameType", @@ -391,8 +416,12 @@ impl Display for AstProp { AstProp::OpeningFragment => "openingFragment", AstProp::Operator => "operator", AstProp::Optional => "optional", + AstProp::Options => "options", AstProp::Out => "out", + AstProp::Override => "override", AstProp::Param => "param", + AstProp::Parameter => "parameter", + AstProp::Parameters => "parameters", AstProp::ParameterName => "parameterName", AstProp::Params => "params", AstProp::Pattern => "pattern", @@ -404,6 +433,7 @@ impl Display for AstProp { AstProp::Quasis => "quasis", AstProp::Raw => "raw", AstProp::Readonly => "readonly", + AstProp::Regex => "regex", AstProp::ReturnType => "returnType", AstProp::Right => "right", AstProp::SelfClosing => "selfClosing", @@ -442,79 +472,2277 @@ pub struct TsEsTreeBuilder { ctx: SerializeCtx, } -// TODO: Add a builder API to make it easier to convert from different source -// ast formats. +impl AstBufSerializer for TsEsTreeBuilder { + fn serialize(&mut self) -> Vec { + self.ctx.serialize() + } +} + impl TsEsTreeBuilder { pub fn new() -> Self { // Max values - // TODO: Maybe there is a rust macro to grab the last enum value? let kind_max_count: u8 = u8::from(AstNode::TSEnumBody) + 1; let prop_max_count: u8 = u8::from(AstProp::Value) + 1; Self { ctx: SerializeCtx::new(kind_max_count, prop_max_count), } } -} -impl AstBufSerializer for TsEsTreeBuilder { - fn header( + pub fn write_program( &mut self, - kind: AstNode, - parent: NodeRef, span: &Span, - ) -> PendingNodeRef { - self.ctx.header(kind, parent, span) + source_kind: &str, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Program, span); + + self.ctx.write_str(AstProp::SourceType, source_kind); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + + self.ctx.set_root_idx(id.0); + + self.ctx.commit_node(id) } - fn commit_schema(&mut self, offset: PendingNodeRef) -> NodeRef { - self.ctx.commit_schema(offset) + pub fn write_import_decl( + &mut self, + span: &Span, + type_only: bool, + source: NodeRef, + specifiers: Vec, + attributes: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportDeclaration, span); + + let kind = if type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ImportKind, kind); + self.ctx.write_ref(AstProp::Source, &id, source); + self.ctx.write_ref_vec(AstProp::Specifiers, &id, specifiers); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attributes); + + self.ctx.commit_node(id) } - fn ref_field(&mut self, prop: AstProp) -> FieldPos { - FieldPos(self.ctx.ref_field(prop)) + pub fn write_import_spec( + &mut self, + span: &Span, + type_only: bool, + local: NodeRef, + imported: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportSpecifier, span); + + let kind = if type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ImportKind, kind); + + self.ctx.write_ref(AstProp::Imported, &id, imported); + self.ctx.write_ref(AstProp::Local, &id, local); + + self.ctx.commit_node(id) } - fn ref_vec_field(&mut self, prop: AstProp, len: usize) -> FieldArrPos { - FieldArrPos(self.ctx.ref_vec_field(prop, len)) + pub fn write_import_attr( + &mut self, + span: &Span, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportAttribute, span); + + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) } - fn str_field(&mut self, prop: AstProp) -> StrPos { - StrPos(self.ctx.str_field(prop)) + pub fn write_import_default_spec( + &mut self, + span: &Span, + local: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportDefaultSpecifier, span); + self.ctx.write_ref(AstProp::Local, &id, local); + self.ctx.commit_node(id) } - fn bool_field(&mut self, prop: AstProp) -> BoolPos { - BoolPos(self.ctx.bool_field(prop)) + pub fn write_import_ns_spec( + &mut self, + span: &Span, + local: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::ImportNamespaceSpecifier, span); + self.ctx.write_ref(AstProp::Local, &id, local); + self.ctx.commit_node(id) } - fn undefined_field(&mut self, prop: AstProp) -> UndefPos { - UndefPos(self.ctx.undefined_field(prop)) + pub fn write_export_decl(&mut self, span: &Span, decl: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportNamedDeclaration, span); + self.ctx.write_ref(AstProp::Declaration, &id, decl); + self.ctx.commit_node(id) } - fn null_field(&mut self, prop: AstProp) -> NullPos { - NullPos(self.ctx.null_field(prop)) + pub fn write_export_all_decl( + &mut self, + span: &Span, + is_type_only: bool, + source: NodeRef, + exported: Option, + attributes: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportAllDeclaration, span); + + let value = if is_type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ExportKind, value); + + self.ctx.write_maybe_ref(AstProp::Exported, &id, exported); + self.ctx.write_ref(AstProp::Source, &id, source); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attributes); + self.ctx.commit_node(id) } - fn write_ref(&mut self, pos: FieldPos, value: NodeRef) { - self.ctx.write_ref(pos.0, value); + pub fn write_export_default_decl( + &mut self, + span: &Span, + is_type_only: bool, + decl: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::ExportDefaultDeclaration, span); + + let value = if is_type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ExportKind, value); + self.ctx.write_ref(AstProp::Declaration, &id, decl); + self.ctx.commit_node(id) } - fn write_maybe_ref(&mut self, pos: FieldPos, value: Option) { - self.ctx.write_maybe_ref(pos.0, value); + pub fn write_export_named_decl( + &mut self, + span: &Span, + specifiers: Vec, + source: Option, + attributes: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportNamedDeclaration, span); + + self.ctx.write_ref_vec(AstProp::Specifiers, &id, specifiers); + self.ctx.write_maybe_ref(AstProp::Source, &id, source); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attributes); + + self.ctx.commit_node(id) } - fn write_refs(&mut self, pos: FieldArrPos, value: Vec) { - self.ctx.write_refs(pos.0, value); + pub fn write_export_ts_namespace( + &mut self, + span: &Span, + ident: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSNamespaceExportDeclaration, span); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.commit_node(id) } - fn write_str(&mut self, pos: StrPos, value: &str) { - self.ctx.write_str(pos.0, value); + pub fn write_export_ts_import_equals( + &mut self, + span: &Span, + is_type_only: bool, + ident: NodeRef, + reference: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSImportEqualsDeclaration, span); + + let value = if is_type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ImportKind, value); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_ref(AstProp::ModuleReference, &id, reference); + + self.ctx.commit_node(id) } - fn write_bool(&mut self, pos: BoolPos, value: bool) { - self.ctx.write_bool(pos.0, value); + pub fn write_ts_external_mod_ref( + &mut self, + span: &Span, + expr: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSExternalModuleReference, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) } - fn serialize(&mut self) -> Vec { - self.ctx.serialize() + pub fn write_export_spec( + &mut self, + span: &Span, + type_only: bool, + local: NodeRef, + exported: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportSpecifier, span); + + let kind = if type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ExportKind, kind); + + self.ctx.write_ref(AstProp::Exported, &id, exported); + self.ctx.write_ref(AstProp::Local, &id, local); + + self.ctx.commit_node(id) + } + + pub fn write_var_decl( + &mut self, + span: &Span, + declare: bool, + kind: &str, + decls: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::VariableDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_str(AstProp::Kind, kind); + self.ctx.write_ref_vec(AstProp::Declarations, &id, decls); + + self.ctx.commit_node(id) + } + + pub fn write_var_declarator( + &mut self, + span: &Span, + ident: NodeRef, + init: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::VariableDeclarator, span); + + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Init, &id, init); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_fn_decl( + &mut self, + span: &Span, + is_declare: bool, + is_async: bool, + is_generator: bool, + // Ident is required in most cases, but optional as default export + // declaration. TsEstree is weird... + ident: Option, + type_param: Option, + return_type: Option, + body: Option, + params: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::FunctionDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_param); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + + self.ctx.commit_node(id) + } + + pub fn write_decorator(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::Decorator, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_decl( + &mut self, + span: &Span, + is_declare: bool, + is_abstract: bool, + // Ident is required in most cases, but optional as default export + // declaration. TsEstree is weird... + ident: Option, + super_class: Option, + implements: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ClassDeclaration, span); + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Abstract, is_abstract); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::SuperClass, &id, super_class); + self.ctx.write_ref_vec(AstProp::Implements, &id, implements); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_expr( + &mut self, + span: &Span, + is_declare: bool, + is_abstract: bool, + ident: Option, + super_class: Option, + implements: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ClassExpression, span); + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Abstract, is_abstract); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::SuperClass, &id, super_class); + self.ctx.write_ref_vec(AstProp::Implements, &id, implements); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_class_body( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ClassBody, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_static_block(&mut self, span: &Span, body: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::StaticBlock, span); + self.ctx.write_ref(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_accessor_property( + &mut self, + span: &Span, + is_declare: bool, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_readonly: bool, + is_static: bool, + accessibility: Option, + decorators: Vec, + key: NodeRef, + value: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::AccessorProperty, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_bool(AstProp::Static, is_static); + self.write_accessibility(accessibility); + self.ctx.write_ref_vec(AstProp::Decorators, &id, decorators); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_maybe_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_prop( + &mut self, + span: &Span, + is_declare: bool, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_readonly: bool, + is_static: bool, + accessibility: Option, + decorators: Vec, + key: NodeRef, + value: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::PropertyDefinition, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_bool(AstProp::Static, is_static); + + self.write_accessibility(accessibility); + self.ctx.write_ref_vec(AstProp::Decorators, &id, decorators); + + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_maybe_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_method( + &mut self, + span: &Span, + is_declare: bool, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_static: bool, + kind: &str, + accessibility: Option, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::MethodDefinition, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Static, is_static); + self.ctx.write_str(AstProp::Kind, kind); + self.write_accessibility(accessibility); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_block_stmt( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::BlockStatement, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_debugger_stmt(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::DebuggerStatement, span); + self.ctx.commit_node(id) + } + + pub fn write_with_stmt( + &mut self, + span: &Span, + obj: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::WithStatement, span); + + self.ctx.write_ref(AstProp::Object, &id, obj); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_return_stmt( + &mut self, + span: &Span, + arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ReturnStatement, span); + self.ctx.write_maybe_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_labeled_stmt( + &mut self, + span: &Span, + label: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::LabeledStatement, span); + + self.ctx.write_ref(AstProp::Label, &id, label); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_break_stmt( + &mut self, + span: &Span, + label: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::BreakStatement, span); + self.ctx.write_maybe_ref(AstProp::Label, &id, label); + self.ctx.commit_node(id) + } + + pub fn write_continue_stmt( + &mut self, + span: &Span, + label: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ContinueStatement, span); + self.ctx.write_maybe_ref(AstProp::Label, &id, label); + self.ctx.commit_node(id) + } + + pub fn write_if_stmt( + &mut self, + span: &Span, + test: NodeRef, + consequent: NodeRef, + alternate: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::IfStatement, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Consequent, &id, consequent); + self.ctx.write_maybe_ref(AstProp::Alternate, &id, alternate); + + self.ctx.commit_node(id) + } + + pub fn write_switch_stmt( + &mut self, + span: &Span, + discriminant: NodeRef, + cases: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::SwitchStatement, span); + + self.ctx.write_ref(AstProp::Discriminant, &id, discriminant); + self.ctx.write_ref_vec(AstProp::Cases, &id, cases); + + self.ctx.commit_node(id) + } + + pub fn write_switch_case( + &mut self, + span: &Span, + test: Option, + consequent: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::SwitchCase, span); + + self.ctx.write_maybe_ref(AstProp::Test, &id, test); + self.ctx.write_ref_vec(AstProp::Consequent, &id, consequent); + + self.ctx.commit_node(id) + } + + pub fn write_throw_stmt(&mut self, span: &Span, arg: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ThrowStatement, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_while_stmt( + &mut self, + span: &Span, + test: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::WhileStatement, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_do_while_stmt( + &mut self, + span: &Span, + test: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::DoWhileStatement, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_for_stmt( + &mut self, + span: &Span, + init: Option, + test: Option, + update: Option, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ForStatement, span); + + self.ctx.write_maybe_ref(AstProp::Init, &id, init); + self.ctx.write_maybe_ref(AstProp::Test, &id, test); + self.ctx.write_maybe_ref(AstProp::Update, &id, update); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_for_in_stmt( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ForInStatement, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_for_of_stmt( + &mut self, + span: &Span, + is_await: bool, + left: NodeRef, + right: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ForOfStatement, span); + + self.ctx.write_bool(AstProp::Await, is_await); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_expr_stmt(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExpressionStatement, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_try_stmt( + &mut self, + span: &Span, + block: NodeRef, + handler: Option, + finalizer: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TryStatement, span); + + self.ctx.write_ref(AstProp::Block, &id, block); + self.ctx.write_maybe_ref(AstProp::Handler, &id, handler); + self.ctx.write_maybe_ref(AstProp::Finalizer, &id, finalizer); + + self.ctx.commit_node(id) + } + + pub fn write_catch_clause( + &mut self, + span: &Span, + param: Option, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::CatchClause, span); + + self.ctx.write_maybe_ref(AstProp::Param, &id, param); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_arr_expr( + &mut self, + span: &Span, + elems: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ArrayExpression, span); + self.ctx.write_ref_vec(AstProp::Elements, &id, elems); + self.ctx.commit_node(id) + } + + pub fn write_obj_expr( + &mut self, + span: &Span, + props: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ObjectExpression, span); + self.ctx.write_ref_vec(AstProp::Properties, &id, props); + self.ctx.commit_node(id) + } + + pub fn write_bin_expr( + &mut self, + span: &Span, + operator: &str, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::BinaryExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + pub fn write_logical_expr( + &mut self, + span: &Span, + operator: &str, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::LogicalExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_fn_expr( + &mut self, + span: &Span, + is_async: bool, + is_generator: bool, + ident: Option, + type_params: Option, + params: Vec, + return_type: Option, + body: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::FunctionExpression, span); + + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_arrow_fn_expr( + &mut self, + span: &Span, + is_async: bool, + is_generator: bool, + type_params: Option, + params: Vec, + return_type: Option, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ArrowFunctionExpression, span); + + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_this_expr(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::ThisExpression, span); + self.ctx.commit_node(id) + } + + pub fn write_super(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::Super, span); + self.ctx.commit_node(id) + } + + pub fn write_unary_expr( + &mut self, + span: &Span, + operator: &str, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::UnaryExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_new_expr( + &mut self, + span: &Span, + callee: NodeRef, + type_args: Option, + args: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::NewExpression, span); + + self.ctx.write_ref(AstProp::Callee, &id, callee); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + self.ctx.write_ref_vec(AstProp::Arguments, &id, args); + + self.ctx.commit_node(id) + } + + pub fn write_import_expr( + &mut self, + span: &Span, + source: NodeRef, + options: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportExpression, span); + + self.ctx.write_ref(AstProp::Source, &id, source); + self.ctx.write_ref(AstProp::Options, &id, options); + + self.ctx.commit_node(id) + } + + pub fn write_call_expr( + &mut self, + span: &Span, + optional: bool, + callee: NodeRef, + type_args: Option, + args: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::CallExpression, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self.ctx.write_ref(AstProp::Callee, &id, callee); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + self.ctx.write_ref_vec(AstProp::Arguments, &id, args); + + self.ctx.commit_node(id) + } + + pub fn write_update_expr( + &mut self, + span: &Span, + prefix: bool, + operator: &str, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::UpdateExpression, span); + + self.ctx.write_bool(AstProp::Prefix, prefix); + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_assignment_expr( + &mut self, + span: &Span, + operator: &str, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::AssignmentExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + pub fn write_conditional_expr( + &mut self, + span: &Span, + test: NodeRef, + consequent: NodeRef, + alternate: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ConditionalExpression, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Consequent, &id, consequent); + self.ctx.write_ref(AstProp::Alternate, &id, alternate); + + self.ctx.commit_node(id) + } + + pub fn write_member_expr( + &mut self, + span: &Span, + optional: bool, + computed: bool, + obj: NodeRef, + prop: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::MemberExpression, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self.ctx.write_bool(AstProp::Computed, computed); + self.ctx.write_ref(AstProp::Object, &id, obj); + self.ctx.write_ref(AstProp::Property, &id, prop); + + self.ctx.commit_node(id) + } + + pub fn write_chain_expr(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ChainExpression, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_sequence_expr( + &mut self, + span: &Span, + exprs: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::SequenceExpression, span); + self.ctx.write_ref_vec(AstProp::Expressions, &id, exprs); + self.ctx.commit_node(id) + } + + pub fn write_template_lit( + &mut self, + span: &Span, + quasis: Vec, + exprs: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TemplateLiteral, span); + + self.ctx.write_ref_vec(AstProp::Quasis, &id, quasis); + self.ctx.write_ref_vec(AstProp::Expressions, &id, exprs); + + self.ctx.commit_node(id) + } + + pub fn write_template_elem( + &mut self, + span: &Span, + tail: bool, + raw: &str, + cooked: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TemplateElement, span); + + self.ctx.write_bool(AstProp::Tail, tail); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_str(AstProp::Cooked, cooked); + + self.ctx.commit_node(id) + } + + pub fn write_tagged_template_expr( + &mut self, + span: &Span, + tag: NodeRef, + type_args: Option, + quasi: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TaggedTemplateExpression, span); + + self.ctx.write_ref(AstProp::Tag, &id, tag); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + self.ctx.write_ref(AstProp::Quasi, &id, quasi); + + self.ctx.commit_node(id) + } + + pub fn write_yield_expr( + &mut self, + span: &Span, + delegate: bool, + arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::YieldExpression, span); + + self.ctx.write_bool(AstProp::Delegate, delegate); + self.ctx.write_maybe_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_await_expr(&mut self, span: &Span, arg: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::AwaitExpression, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_meta_prop(&mut self, span: &Span, prop: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::MetaProperty, span); + self.ctx.write_ref(AstProp::Property, &id, prop); + self.ctx.commit_node(id) + } + + pub fn write_identifier( + &mut self, + span: &Span, + name: &str, + optional: bool, + type_annotation: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Identifier, span); + + self.ctx.write_str(AstProp::Name, name); + self.ctx.write_bool(AstProp::Optional, optional); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_annotation); + + self.ctx.commit_node(id) + } + + pub fn write_private_identifier( + &mut self, + span: &Span, + name: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::PrivateIdentifier, span); + self.ctx.write_str(AstProp::Name, name); + self.ctx.commit_node(id) + } + + pub fn write_assign_pat( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::AssignmentPattern, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + pub fn write_arr_pat( + &mut self, + span: &Span, + optional: bool, + type_ann: Option, + elems: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ArrayPattern, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref_vec(AstProp::Elements, &id, elems); + + self.ctx.commit_node(id) + } + + pub fn write_obj_pat( + &mut self, + span: &Span, + optional: bool, + type_ann: Option, + props: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ObjectPattern, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref_vec(AstProp::Properties, &id, props); + + self.ctx.commit_node(id) + } + + pub fn write_rest_elem( + &mut self, + span: &Span, + type_ann: Option, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::RestElement, span); + + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_spread(&mut self, span: &Span, arg: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::SpreadElement, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_property( + &mut self, + span: &Span, + shorthand: bool, + computed: bool, + method: bool, + kind: &str, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Property, span); + + self.ctx.write_bool(AstProp::Shorthand, shorthand); + self.ctx.write_bool(AstProp::Computed, computed); + self.ctx.write_bool(AstProp::Method, method); + self.ctx.write_str(AstProp::Kind, kind); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_str_lit( + &mut self, + span: &Span, + value: &str, + raw: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_str(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + + self.ctx.commit_node(id) + } + + pub fn write_bool_lit(&mut self, span: &Span, value: bool) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + let raw = &format!("{}", value); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_bool(AstProp::Value, value); + + self.ctx.commit_node(id) + } + + pub fn write_null_lit(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_null(AstProp::Value); + self.ctx.write_str(AstProp::Raw, "null"); + + self.ctx.commit_node(id) + } + + pub fn write_num_lit( + &mut self, + span: &Span, + value: &str, + raw: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_num(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + + self.ctx.commit_node(id) + } + + pub fn write_bigint_lit( + &mut self, + span: &Span, + value: &str, + raw: &str, + bigint_value: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_bigint(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_str(AstProp::BigInt, bigint_value); + + self.ctx.commit_node(id) + } + + pub fn write_regex_lit( + &mut self, + span: &Span, + pattern: &str, + flags: &str, + value: &str, + raw: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_regex(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.open_obj(); + self.ctx.write_str(AstProp::Flags, flags); + self.ctx.write_str(AstProp::Pattern, pattern); + self.ctx.commit_obj(AstProp::Regex); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_identifier(&mut self, span: &Span, name: &str) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXIdentifier, span); + self.ctx.write_str(AstProp::Name, name); + self.ctx.commit_node(id) + } + + pub fn write_jsx_namespaced_name( + &mut self, + span: &Span, + namespace: NodeRef, + name: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXNamespacedName, span); + + self.ctx.write_ref(AstProp::Namespace, &id, namespace); + self.ctx.write_ref(AstProp::Name, &id, name); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_empty_expr(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXEmptyExpression, span); + self.ctx.commit_node(id) + } + + pub fn write_jsx_elem( + &mut self, + span: &Span, + opening: NodeRef, + closing: Option, + children: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXElement, span); + + self.ctx.write_ref(AstProp::OpeningElement, &id, opening); + self + .ctx + .write_maybe_ref(AstProp::ClosingElement, &id, closing); + self.ctx.write_ref_vec(AstProp::Children, &id, children); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_opening_elem( + &mut self, + span: &Span, + self_closing: bool, + name: NodeRef, + attrs: Vec, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXOpeningElement, span); + + self.ctx.write_bool(AstProp::SelfClosing, self_closing); + self.ctx.write_ref(AstProp::Name, &id, name); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attrs); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_attr( + &mut self, + span: &Span, + name: NodeRef, + value: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXAttribute, span); + + self.ctx.write_ref(AstProp::Name, &id, name); + self.ctx.write_maybe_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_spread_attr( + &mut self, + span: &Span, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXSpreadAttribute, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_jsx_closing_elem( + &mut self, + span: &Span, + name: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXClosingElement, span); + self.ctx.write_ref(AstProp::Name, &id, name); + self.ctx.commit_node(id) + } + + pub fn write_jsx_frag( + &mut self, + span: &Span, + opening: NodeRef, + closing: NodeRef, + children: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXFragment, span); + + self.ctx.write_ref(AstProp::OpeningFragment, &id, opening); + self.ctx.write_ref(AstProp::ClosingFragment, &id, closing); + self.ctx.write_ref_vec(AstProp::Children, &id, children); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_opening_frag(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXOpeningFragment, span); + self.ctx.commit_node(id) + } + + pub fn write_jsx_closing_frag(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXClosingFragment, span); + self.ctx.commit_node(id) + } + + pub fn write_jsx_expr_container( + &mut self, + span: &Span, + expr: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXExpressionContainer, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_jsx_text( + &mut self, + span: &Span, + raw: &str, + value: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXText, span); + + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_str(AstProp::Value, value); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_member_expr( + &mut self, + span: &Span, + obj: NodeRef, + prop: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXMemberExpression, span); + + self.ctx.write_ref(AstProp::Object, &id, obj); + self.ctx.write_ref(AstProp::Property, &id, prop); + + self.ctx.commit_node(id) + } + + pub fn write_ts_module_decl( + &mut self, + span: &Span, + is_declare: bool, + is_global: bool, + ident: NodeRef, + body: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSModuleDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Global, is_global); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_module_block( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSModuleBlock, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_class_implements( + &mut self, + span: &Span, + expr: NodeRef, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSClassImplements, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_abstract_method_def( + &mut self, + span: &Span, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_static: bool, + accessibility: Option, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSAbstractMethodDefinition, span); + + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Static, is_static); + + self.write_accessibility(accessibility); + + self.ctx.write_str(AstProp::Kind, "method"); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Key, &id, value); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_empty_body_fn_expr( + &mut self, + span: &Span, + is_declare: bool, + is_expression: bool, + is_async: bool, + is_generator: bool, + ident: Option, + type_params: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSEmptyBodyFunctionExpression, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Expression, is_expression); + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_param_prop( + &mut self, + span: &Span, + is_override: bool, + is_readonly: bool, + accessibility: Option, + decorators: Vec, + param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSParameterProperty, span); + + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_bool(AstProp::Static, false); + self.write_accessibility(accessibility); + self.ctx.write_ref_vec(AstProp::Decorators, &id, decorators); + self.ctx.write_ref(AstProp::Parameter, &id, param); + + self.ctx.commit_node(id) + } + + pub fn write_ts_call_sig_decl( + &mut self, + span: &Span, + type_ann: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSCallSignatureDeclaration, span); + + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_property_sig( + &mut self, + span: &Span, + computed: bool, + optional: bool, + readonly: bool, + key: NodeRef, + type_ann: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSPropertySignature, span); + + self.ctx.write_bool(AstProp::Computed, computed); + self.ctx.write_bool(AstProp::Optional, optional); + self.ctx.write_bool(AstProp::Readonly, readonly); + // TODO(@marvinhagemeister) not sure where this is coming from + self.ctx.write_bool(AstProp::Static, false); + + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_enum( + &mut self, + span: &Span, + declare: bool, + is_const: bool, + ident: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSEnumDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_bool(AstProp::Const, is_const); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_ts_enum_body( + &mut self, + span: &Span, + members: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSEnumBody, span); + self.ctx.write_ref_vec(AstProp::Members, &id, members); + self.ctx.commit_node(id) + } + + pub fn write_ts_enum_member( + &mut self, + span: &Span, + ident: NodeRef, + init: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSEnumMember, span); + + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Initializer, &id, init); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_assertion( + &mut self, + span: &Span, + expr: NodeRef, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeAssertion, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_param_inst( + &mut self, + span: &Span, + params: Vec, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSTypeParameterInstantiation, span); + + self.ctx.write_ref_vec(AstProp::Params, &id, params); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_alias( + &mut self, + span: &Span, + declare: bool, + ident: NodeRef, + type_param: Option, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeAliasDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_param); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_satisfies_expr( + &mut self, + span: &Span, + expr: NodeRef, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSSatisfiesExpression, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_as_expr( + &mut self, + span: &Span, + expr: NodeRef, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSAsExpression, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_non_null(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSNonNullExpression, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_ts_this_type(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSThisType, span); + self.ctx.commit_node(id) + } + + pub fn write_ts_interface( + &mut self, + span: &Span, + declare: bool, + ident: NodeRef, + type_param: Option, + extends: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterface, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Extends, &id, type_param); + self + .ctx + .write_ref_vec(AstProp::TypeParameters, &id, extends); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_ts_interface_decl( + &mut self, + span: &Span, + declare: bool, + ident: NodeRef, + type_param: Option, + extends: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterfaceDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Extends, &id, type_param); + self + .ctx + .write_ref_vec(AstProp::TypeParameters, &id, extends); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_ts_interface_body( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterfaceBody, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_construct_sig( + &mut self, + span: &Span, + type_params: Option, + params: Vec, + return_type: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSConstructSignatureDeclaration, span); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self.ctx.write_ref(AstProp::ReturnType, &id, return_type); + self.ctx.commit_node(id) + } + + pub fn write_ts_getter_sig( + &mut self, + span: &Span, + key: NodeRef, + return_type: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, false); + self.ctx.write_bool(AstProp::Optional, false); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "getter"); + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_setter_sig( + &mut self, + span: &Span, + key: NodeRef, + param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, false); + self.ctx.write_bool(AstProp::Optional, false); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "setter"); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref_vec(AstProp::Params, &id, vec![param]); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_method_sig( + &mut self, + span: &Span, + is_computed: bool, + is_optional: bool, + key: NodeRef, + type_params: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "method"); + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_interface_heritage( + &mut self, + span: &Span, + expr: NodeRef, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterfaceHeritage, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + pub fn write_ts_index_sig( + &mut self, + span: &Span, + is_readonly: bool, + params: Vec, + type_ann: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSIndexSignature, span); + + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_ref_vec(AstProp::Parameters, &id, params); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_union_type( + &mut self, + span: &Span, + types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSUnionType, span); + self.ctx.write_ref_vec(AstProp::Types, &id, types); + self.ctx.commit_node(id) + } + + pub fn write_ts_intersection_type( + &mut self, + span: &Span, + types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSIntersectionType, span); + self.ctx.write_ref_vec(AstProp::Types, &id, types); + self.ctx.commit_node(id) + } + + pub fn write_ts_infer_type( + &mut self, + span: &Span, + type_param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInferType, span); + self.ctx.write_ref(AstProp::TypeParameter, &id, type_param); + self.ctx.commit_node(id) + } + + pub fn write_ts_type_op( + &mut self, + span: &Span, + op: &str, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeOperator, span); + + self.ctx.write_str(AstProp::Operator, op); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_indexed_access_type( + &mut self, + span: &Span, + index: NodeRef, + obj: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSIndexedAccessType, span); + + self.ctx.write_ref(AstProp::IndexType, &id, index); + self.ctx.write_ref(AstProp::ObjectType, &id, obj); + + self.ctx.commit_node(id) + } + + pub fn write_ts_keyword( + &mut self, + kind: TsKeywordKind, + span: &Span, + ) -> NodeRef { + let kind = match kind { + TsKeywordKind::Any => AstNode::TSAnyKeyword, + TsKeywordKind::Unknown => AstNode::TSUnknownKeyword, + TsKeywordKind::Number => AstNode::TSNumberKeyword, + TsKeywordKind::Object => AstNode::TSObjectKeyword, + TsKeywordKind::Boolean => AstNode::TSBooleanKeyword, + TsKeywordKind::BigInt => AstNode::TSBigIntKeyword, + TsKeywordKind::String => AstNode::TSStringKeyword, + TsKeywordKind::Symbol => AstNode::TSSymbolKeyword, + TsKeywordKind::Void => AstNode::TSVoidKeyword, + TsKeywordKind::Undefined => AstNode::TSUndefinedKeyword, + TsKeywordKind::Null => AstNode::TSNullKeyword, + TsKeywordKind::Never => AstNode::TSNeverKeyword, + TsKeywordKind::Intrinsic => AstNode::TSIntrinsicKeyword, + }; + + let id = self.ctx.append_node(kind, span); + self.ctx.commit_node(id) + } + + pub fn write_ts_rest_type( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSRestType, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + + pub fn write_ts_conditional_type( + &mut self, + span: &Span, + check: NodeRef, + extends: NodeRef, + true_type: NodeRef, + false_type: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSConditionalType, span); + + self.ctx.write_ref(AstProp::CheckType, &id, check); + self.ctx.write_ref(AstProp::ExtendsType, &id, extends); + self.ctx.write_ref(AstProp::TrueType, &id, true_type); + self.ctx.write_ref(AstProp::FalseType, &id, false_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_mapped_type( + &mut self, + span: &Span, + readonly: Option, + optional: Option, + name: Option, + type_ann: Option, + type_param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMappedType, span); + + self.write_plus_minus_true(AstProp::Readonly, readonly); + self.write_plus_minus_true(AstProp::Optional, optional); + self.ctx.write_maybe_ref(AstProp::NameType, &id, name); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref(AstProp::TypeParameter, &id, type_param); + + self.ctx.commit_node(id) + } + + pub fn write_ts_lit_type(&mut self, span: &Span, lit: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSLiteralType, span); + self.ctx.write_ref(AstProp::Literal, &id, lit); + self.ctx.commit_node(id) + } + + pub fn write_ts_tpl_lit( + &mut self, + span: &Span, + quasis: Vec, + types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTemplateLiteralType, span); + + self.ctx.write_ref_vec(AstProp::Quasis, &id, quasis); + self.ctx.write_ref_vec(AstProp::Types, &id, types); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_lit( + &mut self, + span: &Span, + members: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeLiteral, span); + self.ctx.write_ref_vec(AstProp::Members, &id, members); + self.ctx.commit_node(id) + } + + pub fn write_ts_optional_type( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSOptionalType, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + + pub fn write_ts_type_ann( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeAnnotation, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + + pub fn write_ts_array_type( + &mut self, + span: &Span, + elem_type: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSArrayType, span); + self.ctx.write_ref(AstProp::ElementType, &id, elem_type); + self.ctx.commit_node(id) + } + + pub fn write_ts_type_query( + &mut self, + span: &Span, + expr_name: NodeRef, + type_arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeQuery, span); + + self.ctx.write_ref(AstProp::ExprName, &id, expr_name); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_arg); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_ref( + &mut self, + span: &Span, + type_name: NodeRef, + type_arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeReference, span); + + self.ctx.write_ref(AstProp::TypeName, &id, type_name); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_arg); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_predicate( + &mut self, + span: &Span, + asserts: bool, + param_name: NodeRef, + type_ann: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypePredicate, span); + + self.ctx.write_bool(AstProp::Asserts, asserts); + self.ctx.write_ref(AstProp::ParameterName, &id, param_name); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_tuple_type( + &mut self, + span: &Span, + elem_types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTupleType, span); + + self + .ctx + .write_ref_vec(AstProp::ElementTypes, &id, elem_types); + + self.ctx.commit_node(id) + } + + pub fn write_ts_named_tuple_member( + &mut self, + span: &Span, + label: NodeRef, + elem_type: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSNamedTupleMember, span); + + self.ctx.write_ref(AstProp::Label, &id, label); + self.ctx.write_ref(AstProp::ElementType, &id, elem_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_param_decl( + &mut self, + span: &Span, + params: Vec, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSTypeParameterDeclaration, span); + + self.ctx.write_ref_vec(AstProp::Params, &id, params); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_type_param( + &mut self, + span: &Span, + is_in: bool, + is_out: bool, + is_const: bool, + name: NodeRef, + constraint: Option, + default: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeParameter, span); + + self.ctx.write_bool(AstProp::In, is_in); + self.ctx.write_bool(AstProp::Out, is_out); + self.ctx.write_bool(AstProp::Const, is_const); + self.ctx.write_ref(AstProp::Name, &id, name); + self + .ctx + .write_maybe_ref(AstProp::Constraint, &id, constraint); + self.ctx.write_maybe_ref(AstProp::Default, &id, default); + + self.ctx.commit_node(id) + } + + pub fn write_ts_import_type( + &mut self, + span: &Span, + arg: NodeRef, + qualifier: Option, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSImportType, span); + + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.write_maybe_ref(AstProp::Qualifier, &id, qualifier); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + pub fn write_export_assign(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSExportAssignment, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_ts_fn_type( + &mut self, + span: &Span, + params: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSFunctionType, span); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self.ctx.commit_node(id) + } + + pub fn write_ts_qualified_name( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSQualifiedName, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + fn write_accessibility(&mut self, accessibility: Option) { + if let Some(value) = accessibility { + self.ctx.write_str(AstProp::Accessibility, &value); + } else { + self.ctx.write_undefined(AstProp::Accessibility); + } + } + + fn write_plus_minus_true( + &mut self, + prop: AstProp, + value: Option, + ) { + match value { + Some(TruePlusMinus::Plus) => self.ctx.write_str(prop, "+"), + Some(TruePlusMinus::Minus) => self.ctx.write_str(prop, "-"), + Some(TruePlusMinus::True) => self.ctx.write_bool(prop, true), + _ => self.ctx.write_undefined(prop), + } } } + +#[derive(Debug)] +pub enum TsKeywordKind { + Any, + Unknown, + Number, + Object, + Boolean, + BigInt, + String, + Symbol, + Void, + Undefined, + Null, + Never, + Intrinsic, +} diff --git a/tests/unit/__snapshots__/lint_plugin_test.ts.snap b/tests/unit/__snapshots__/lint_plugin_test.ts.snap new file mode 100644 index 0000000000..337fcecc8f --- /dev/null +++ b/tests/unit/__snapshots__/lint_plugin_test.ts.snap @@ -0,0 +1,8608 @@ +export const snapshot = {}; + +snapshot[`Plugin - Program 1`] = ` +{ + body: [], + range: [ + 1, + 1, + ], + sourceType: "script", + type: "Program", +} +`; + +snapshot[`Plugin - ImportDeclaration 1`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 14, + ], + source: { + range: [ + 8, + 13, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 2`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 23, + ], + source: { + range: [ + 17, + 22, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + local: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 11, + ], + type: "ImportDefaultSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 3`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 28, + ], + source: { + range: [ + 22, + 27, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + local: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 16, + ], + type: "ImportNamespaceSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 4`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 39, + ], + source: { + range: [ + 33, + 38, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + importKind: "value", + imported: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + type: "ImportSpecifier", + }, + { + importKind: "value", + imported: { + name: "bar", + optional: false, + range: [ + 15, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "baz", + optional: false, + range: [ + 22, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 15, + 25, + ], + type: "ImportSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 5`] = ` +{ + attributes: [ + { + key: { + name: "type", + optional: false, + range: [ + 30, + 34, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 30, + 42, + ], + type: "ImportAttribute", + value: { + range: [ + 36, + 42, + ], + raw: '"json"', + type: "Literal", + value: "json", + }, + }, + ], + importKind: "value", + range: [ + 1, + 45, + ], + source: { + range: [ + 17, + 22, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + local: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 11, + ], + type: "ImportDefaultSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ExportNamedDeclaration 1`] = ` +{ + attributes: [], + range: [ + 1, + 27, + ], + source: { + range: [ + 21, + 26, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + exportKind: "value", + exported: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + type: "ExportSpecifier", + }, + ], + type: "ExportNamedDeclaration", +} +`; + +snapshot[`Plugin - ExportNamedDeclaration 2`] = ` +{ + attributes: [], + range: [ + 1, + 34, + ], + source: { + range: [ + 28, + 33, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + exportKind: "value", + exported: { + name: "baz", + optional: false, + range: [ + 17, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "bar", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 20, + ], + type: "ExportSpecifier", + }, + ], + type: "ExportNamedDeclaration", +} +`; + +snapshot[`Plugin - ExportNamedDeclaration 3`] = ` +{ + attributes: [ + { + key: { + name: "type", + optional: false, + range: [ + 34, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 34, + 46, + ], + type: "ImportAttribute", + value: { + range: [ + 40, + 46, + ], + raw: '"json"', + type: "Literal", + value: "json", + }, + }, + ], + range: [ + 1, + 49, + ], + source: { + range: [ + 21, + 26, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + exportKind: "value", + exported: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + type: "ExportSpecifier", + }, + ], + type: "ExportNamedDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 1`] = ` +{ + declaration: { + async: false, + body: { + body: [], + range: [ + 31, + 33, + ], + type: "BlockStatement", + }, + declare: false, + generator: false, + id: { + name: "foo", + optional: false, + range: [ + 25, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + params: [], + range: [ + 16, + 33, + ], + returnType: null, + type: "FunctionDeclaration", + typeParameters: null, + }, + exportKind: "value", + range: [ + 1, + 33, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 2`] = ` +{ + declaration: { + async: false, + body: { + body: [], + range: [ + 28, + 30, + ], + type: "BlockStatement", + }, + declare: false, + generator: false, + id: null, + params: [], + range: [ + 16, + 30, + ], + returnType: null, + type: "FunctionDeclaration", + typeParameters: null, + }, + exportKind: "value", + range: [ + 1, + 30, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 3`] = ` +{ + declaration: { + abstract: false, + body: { + body: [], + range: [ + 16, + 28, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 22, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 16, + 28, + ], + superClass: null, + type: "ClassDeclaration", + }, + exportKind: "value", + range: [ + 1, + 28, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 4`] = ` +{ + declaration: { + abstract: false, + body: { + body: [], + range: [ + 16, + 24, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 16, + 24, + ], + superClass: null, + type: "ClassDeclaration", + }, + exportKind: "value", + range: [ + 1, + 24, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 5`] = ` +{ + declaration: { + name: "bar", + optional: false, + range: [ + 16, + 19, + ], + type: "Identifier", + typeAnnotation: null, + }, + exportKind: "value", + range: [ + 1, + 20, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 6`] = ` +{ + declaration: { + body: { + body: [], + range: [ + 30, + 32, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "Foo", + optional: false, + range: [ + 26, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 16, + 32, + ], + type: "TSInterfaceDeclaration", + typeParameters: [], + }, + exportKind: "type", + range: [ + 1, + 32, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportAllDeclaration 1`] = ` +{ + attributes: [], + exportKind: "value", + exported: null, + range: [ + 1, + 21, + ], + source: { + range: [ + 15, + 20, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + type: "ExportAllDeclaration", +} +`; + +snapshot[`Plugin - ExportAllDeclaration 2`] = ` +{ + attributes: [], + exportKind: "value", + exported: { + range: [ + 22, + 27, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 1, + 28, + ], + source: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ExportAllDeclaration", +} +`; + +snapshot[`Plugin - ExportAllDeclaration 3`] = ` +{ + attributes: [ + { + key: { + name: "type", + optional: false, + range: [ + 28, + 32, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 28, + 40, + ], + type: "ImportAttribute", + value: { + range: [ + 34, + 40, + ], + raw: '"json"', + type: "Literal", + value: "json", + }, + }, + ], + exportKind: "value", + exported: null, + range: [ + 1, + 43, + ], + source: { + range: [ + 15, + 20, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + type: "ExportAllDeclaration", +} +`; + +snapshot[`Plugin - TSExportAssignment 1`] = ` +{ + expression: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 14, + ], + type: "TSExportAssignment", +} +`; + +snapshot[`Plugin - TSNamespaceExportDeclaration 1`] = ` +{ + id: { + name: "A", + optional: false, + range: [ + 21, + 22, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 23, + ], + type: "TSNamespaceExportDeclaration", +} +`; + +snapshot[`Plugin - TSImportEqualsDeclaration 1`] = ` +{ + id: { + name: "a", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + importKind: "value", + moduleReference: { + name: "b", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 13, + ], + type: "TSImportEqualsDeclaration", +} +`; + +snapshot[`Plugin - TSImportEqualsDeclaration 2`] = ` +{ + id: { + name: "a", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + importKind: "value", + moduleReference: { + expression: { + range: [ + 20, + 25, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 12, + 26, + ], + type: "TSExternalModuleReference", + }, + range: [ + 1, + 26, + ], + type: "TSImportEqualsDeclaration", +} +`; + +snapshot[`Plugin - BlockStatement 1`] = ` +{ + body: [ + { + expression: { + name: "foo", + optional: false, + range: [ + 3, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 3, + 7, + ], + type: "ExpressionStatement", + }, + ], + range: [ + 1, + 9, + ], + type: "BlockStatement", +} +`; + +snapshot[`Plugin - BreakStatement 1`] = ` +{ + label: null, + range: [ + 15, + 21, + ], + type: "BreakStatement", +} +`; + +snapshot[`Plugin - BreakStatement 2`] = ` +{ + label: { + name: "foo", + optional: false, + range: [ + 26, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 20, + 30, + ], + type: "BreakStatement", +} +`; + +snapshot[`Plugin - ContinueStatement 1`] = ` +{ + label: null, + range: [ + 1, + 10, + ], + type: "ContinueStatement", +} +`; + +snapshot[`Plugin - ContinueStatement 2`] = ` +{ + label: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 14, + ], + type: "ContinueStatement", +} +`; + +snapshot[`Plugin - DebuggerStatement 1`] = ` +{ + range: [ + 1, + 10, + ], + type: "DebuggerStatement", +} +`; + +snapshot[`Plugin - DoWhileStatement 1`] = ` +{ + body: { + body: [], + range: [ + 4, + 6, + ], + type: "BlockStatement", + }, + range: [ + 1, + 19, + ], + test: { + name: "foo", + optional: false, + range: [ + 14, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "DoWhileStatement", +} +`; + +snapshot[`Plugin - ExpressionStatement 1`] = ` +{ + expression: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 5, + ], + type: "ExpressionStatement", +} +`; + +snapshot[`Plugin - ForInStatement 1`] = ` +{ + body: { + body: [], + range: [ + 14, + 16, + ], + type: "BlockStatement", + }, + left: { + name: "a", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 16, + ], + right: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForInStatement", +} +`; + +snapshot[`Plugin - ForOfStatement 1`] = ` +{ + await: false, + body: { + body: [], + range: [ + 14, + 16, + ], + type: "BlockStatement", + }, + left: { + name: "a", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 16, + ], + right: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForOfStatement", +} +`; + +snapshot[`Plugin - ForOfStatement 2`] = ` +{ + await: true, + body: { + body: [], + range: [ + 20, + 22, + ], + type: "BlockStatement", + }, + left: { + name: "a", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 22, + ], + right: { + name: "b", + optional: false, + range: [ + 17, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForOfStatement", +} +`; + +snapshot[`Plugin - ForStatement 1`] = ` +{ + body: { + body: [], + range: [ + 10, + 12, + ], + type: "BlockStatement", + }, + init: null, + range: [ + 1, + 12, + ], + test: null, + type: "ForStatement", + update: null, +} +`; + +snapshot[`Plugin - ForStatement 2`] = ` +{ + body: { + body: [], + range: [ + 15, + 17, + ], + type: "BlockStatement", + }, + init: { + name: "a", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 17, + ], + test: { + name: "b", + optional: false, + range: [ + 9, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForStatement", + update: { + name: "c", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, +} +`; + +snapshot[`Plugin - IfStatement 1`] = ` +{ + alternate: null, + consequent: { + body: [], + range: [ + 10, + 12, + ], + type: "BlockStatement", + }, + range: [ + 1, + 12, + ], + test: { + name: "foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "IfStatement", +} +`; + +snapshot[`Plugin - IfStatement 2`] = ` +{ + alternate: { + body: [], + range: [ + 18, + 20, + ], + type: "BlockStatement", + }, + consequent: { + body: [], + range: [ + 10, + 12, + ], + type: "BlockStatement", + }, + range: [ + 1, + 20, + ], + test: { + name: "foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "IfStatement", +} +`; + +snapshot[`Plugin - LabeledStatement 1`] = ` +{ + body: { + body: [], + range: [ + 6, + 8, + ], + type: "BlockStatement", + }, + label: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 8, + ], + type: "LabeledStatement", +} +`; + +snapshot[`Plugin - ReturnStatement 1`] = ` +{ + argument: null, + range: [ + 1, + 7, + ], + type: "ReturnStatement", +} +`; + +snapshot[`Plugin - ReturnStatement 2`] = ` +{ + argument: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "ReturnStatement", +} +`; + +snapshot[`Plugin - SwitchStatement 1`] = ` +{ + cases: [ + { + consequent: [], + range: [ + 22, + 31, + ], + test: { + name: "foo", + optional: false, + range: [ + 27, + 30, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "SwitchCase", + }, + { + consequent: [ + { + label: null, + range: [ + 56, + 62, + ], + type: "BreakStatement", + }, + ], + range: [ + 38, + 62, + ], + test: { + name: "bar", + optional: false, + range: [ + 43, + 46, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "SwitchCase", + }, + { + consequent: [ + { + body: [], + range: [ + 86, + 88, + ], + type: "BlockStatement", + }, + ], + range: [ + 69, + 88, + ], + test: null, + type: "SwitchCase", + }, + ], + discriminant: { + name: "foo", + optional: false, + range: [ + 9, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 94, + ], + type: "SwitchStatement", +} +`; + +snapshot[`Plugin - ThrowStatement 1`] = ` +{ + argument: { + name: "foo", + optional: false, + range: [ + 7, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 11, + ], + type: "ThrowStatement", +} +`; + +snapshot[`Plugin - TryStatement 1`] = ` +{ + block: { + body: [], + range: [ + 5, + 7, + ], + type: "BlockStatement", + }, + finalizer: null, + handler: { + body: { + body: [], + range: [ + 14, + 16, + ], + type: "BlockStatement", + }, + param: null, + range: [ + 8, + 16, + ], + type: "CatchClause", + }, + range: [ + 1, + 16, + ], + type: "TryStatement", +} +`; + +snapshot[`Plugin - TryStatement 2`] = ` +{ + block: { + body: [], + range: [ + 5, + 7, + ], + type: "BlockStatement", + }, + finalizer: null, + handler: { + body: { + body: [], + range: [ + 18, + 20, + ], + type: "BlockStatement", + }, + param: { + name: "e", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 20, + ], + type: "CatchClause", + }, + range: [ + 1, + 20, + ], + type: "TryStatement", +} +`; + +snapshot[`Plugin - TryStatement 3`] = ` +{ + block: { + body: [], + range: [ + 5, + 7, + ], + type: "BlockStatement", + }, + finalizer: { + body: [], + range: [ + 16, + 18, + ], + type: "BlockStatement", + }, + handler: null, + range: [ + 1, + 18, + ], + type: "TryStatement", +} +`; + +snapshot[`Plugin - WhileStatement 1`] = ` +{ + body: { + body: [], + range: [ + 13, + 15, + ], + type: "BlockStatement", + }, + range: [ + 1, + 15, + ], + test: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "WhileStatement", +} +`; + +snapshot[`Plugin - WithStatement 1`] = ` +{ + body: { + body: [], + range: [ + 11, + 13, + ], + type: "BlockStatement", + }, + object: { + elements: [], + range: [ + 7, + 9, + ], + type: "ArrayExpression", + }, + range: [ + 1, + 13, + ], + type: "WithStatement", +} +`; + +snapshot[`Plugin - ArrayExpression 1`] = ` +{ + elements: [ + { + elements: [], + range: [ + 2, + 4, + ], + type: "ArrayExpression", + }, + ], + range: [ + 1, + 9, + ], + type: "ArrayExpression", +} +`; + +snapshot[`Plugin - ArrowFunctionExpression 1`] = ` +{ + async: false, + body: { + body: [], + range: [ + 7, + 9, + ], + type: "BlockStatement", + }, + generator: false, + params: [], + range: [ + 1, + 9, + ], + returnType: null, + type: "ArrowFunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - ArrowFunctionExpression 2`] = ` +{ + async: true, + body: { + body: [], + range: [ + 13, + 15, + ], + type: "BlockStatement", + }, + generator: false, + params: [], + range: [ + 1, + 15, + ], + returnType: null, + type: "ArrowFunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - ArrowFunctionExpression 3`] = ` +{ + async: false, + body: { + body: [], + range: [ + 34, + 36, + ], + type: "BlockStatement", + }, + generator: false, + params: [ + { + name: "a", + optional: false, + range: [ + 2, + 11, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 3, + 11, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 5, + 11, + ], + type: "TSNumberKeyword", + }, + }, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 13, + 24, + ], + type: "RestElement", + typeAnnotation: { + range: [ + 17, + 24, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + elementType: { + range: [ + 19, + 22, + ], + type: "TSAnyKeyword", + }, + range: [ + 19, + 24, + ], + type: "TSArrayType", + }, + }, + }, + ], + range: [ + 1, + 36, + ], + returnType: { + range: [ + 25, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 27, + 30, + ], + type: "TSAnyKeyword", + }, + }, + type: "ArrowFunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - AssignmentExpression 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "=", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "AssignmentExpression", +} +`; + +snapshot[`Plugin - AssignmentExpression 2`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "=", + range: [ + 1, + 12, + ], + right: { + left: { + name: "a", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "??=", + range: [ + 5, + 12, + ], + right: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "AssignmentExpression", + }, + type: "AssignmentExpression", +} +`; + +snapshot[`Plugin - AwaitExpression 1`] = ` +{ + argument: { + name: "foo", + optional: false, + range: [ + 7, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 10, + ], + type: "AwaitExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 2`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">=", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 3`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "<", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 4`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "<=", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 5`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "==", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 6`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "===", + range: [ + 1, + 8, + ], + right: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 7`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "!=", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 8`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "!=", + range: [ + 1, + 8, + ], + right: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 9`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "<<", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 10`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">>", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 11`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">>>", + range: [ + 1, + 8, + ], + right: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 12`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "+", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 13`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "-", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 14`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "*", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 15`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "/", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 16`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "%", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 17`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "|", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 18`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "^", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 19`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "&", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 20`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "in", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 21`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "**", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - CallExpression 1`] = ` +{ + arguments: [], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 1, + 6, + ], + type: "CallExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - CallExpression 2`] = ` +{ + arguments: [ + { + name: "a", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 11, + ], + type: "SpreadElement", + }, + ], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 1, + 13, + ], + type: "CallExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - CallExpression 3`] = ` +{ + arguments: [], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: true, + range: [ + 1, + 8, + ], + type: "CallExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - CallExpression 4`] = ` +{ + arguments: [], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 1, + 9, + ], + type: "CallExpression", + typeArguments: { + params: [ + { + range: [ + 5, + 6, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 4, + 7, + ], + type: "TSTypeParameterInstantiation", + }, +} +`; + +snapshot[`Plugin - ChainExpression 1`] = ` +{ + expression: { + computed: false, + object: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: true, + property: { + name: "b", + optional: false, + range: [ + 4, + 5, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 5, + ], + type: "MemberExpression", + }, + range: [ + 1, + 5, + ], + type: "ChainExpression", +} +`; + +snapshot[`Plugin - ClassExpression 1`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 13, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 13, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 2`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 17, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 5, + 17, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 3`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 29, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 5, + 29, + ], + superClass: { + name: "Bar", + optional: false, + range: [ + 23, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 4`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 50, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [ + { + expression: { + name: "Baz", + optional: false, + range: [ + 38, + 41, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 38, + 41, + ], + type: "TSClassImplements", + typeArguments: null, + }, + { + expression: { + name: "Baz2", + optional: false, + range: [ + 43, + 47, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 43, + 47, + ], + type: "TSClassImplements", + typeArguments: null, + }, + ], + range: [ + 5, + 50, + ], + superClass: { + name: "Bar", + optional: false, + range: [ + 23, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 5`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 20, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 5, + 20, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 6`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + key: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "method", + optional: false, + override: false, + range: [ + 13, + 21, + ], + static: false, + type: "MethodDefinition", + value: { + async: false, + body: { + body: [], + range: [ + 19, + 21, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [], + range: [ + 13, + 21, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, + }, + }, + ], + range: [ + 5, + 23, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 23, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 7`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + key: { + name: "foo", + range: [ + 13, + 17, + ], + type: "PrivateIdentifier", + }, + kind: "method", + optional: false, + override: false, + range: [ + 13, + 22, + ], + static: false, + type: "MethodDefinition", + value: { + async: false, + body: { + body: [], + range: [ + 20, + 22, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [], + range: [ + 13, + 22, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, + }, + }, + ], + range: [ + 5, + 24, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 24, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 8`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 24, + ], + readonly: false, + static: false, + type: "PropertyDefinition", + value: null, + }, + ], + range: [ + 5, + 26, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 26, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 9`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 22, + ], + readonly: false, + static: false, + type: "PropertyDefinition", + value: { + name: "bar", + optional: false, + range: [ + 19, + 22, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 24, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 24, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 10`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + key: { + name: "constructor", + optional: false, + range: [ + 13, + 24, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "constructor", + optional: false, + override: false, + range: [ + 13, + 47, + ], + static: false, + type: "MethodDefinition", + value: { + async: false, + body: { + body: [], + range: [ + 45, + 47, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [ + { + accessibility: undefined, + decorators: [], + override: false, + parameter: { + name: "foo", + optional: false, + range: [ + 32, + 35, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 35, + 43, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 37, + 43, + ], + type: "TSStringKeyword", + }, + }, + }, + range: [ + 25, + 43, + ], + readonly: false, + static: false, + type: "TSParameterProperty", + }, + ], + range: [ + 13, + 47, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, + }, + }, + ], + range: [ + 5, + 49, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 49, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 11`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + range: [ + 13, + 17, + ], + type: "PrivateIdentifier", + }, + optional: false, + override: false, + range: [ + 13, + 31, + ], + readonly: false, + static: false, + type: "PropertyDefinition", + value: { + name: "bar", + optional: false, + range: [ + 28, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 33, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 33, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 12`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 20, + 23, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 29, + ], + readonly: false, + static: true, + type: "PropertyDefinition", + value: { + name: "bar", + optional: false, + range: [ + 26, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 31, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 31, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 13`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 20, + 23, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 24, + ], + readonly: false, + static: true, + type: "PropertyDefinition", + value: null, + }, + { + body: { + body: [ + { + expression: { + left: { + name: "foo", + optional: false, + range: [ + 34, + 37, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "=", + range: [ + 34, + 43, + ], + right: { + name: "bar", + optional: false, + range: [ + 40, + 43, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "AssignmentExpression", + }, + range: [ + 34, + 43, + ], + type: "ExpressionStatement", + }, + ], + range: [ + 32, + 45, + ], + type: "BlockStatement", + }, + range: [ + 25, + 45, + ], + type: "StaticBlock", + }, + ], + range: [ + 5, + 47, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 47, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ConditionalExpression 1`] = ` +{ + alternate: { + name: "c", + optional: false, + range: [ + 9, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + consequent: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 10, + ], + test: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ConditionalExpression", +} +`; + +snapshot[`Plugin - FunctionExpression 1`] = ` +{ + async: false, + body: { + body: [], + range: [ + 17, + 19, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [], + range: [ + 5, + 19, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - FunctionExpression 2`] = ` +{ + async: false, + body: { + body: [], + range: [ + 20, + 22, + ], + type: "BlockStatement", + }, + generator: false, + id: { + name: "foo", + optional: false, + range: [ + 14, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + params: [], + range: [ + 5, + 22, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - FunctionExpression 3`] = ` +{ + async: false, + body: { + body: [], + range: [ + 45, + 47, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [ + { + name: "a", + optional: true, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 17, + 25, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 19, + 25, + ], + type: "TSNumberKeyword", + }, + }, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 30, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 27, + 38, + ], + type: "RestElement", + typeAnnotation: { + range: [ + 31, + 38, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + elementType: { + range: [ + 33, + 36, + ], + type: "TSAnyKeyword", + }, + range: [ + 33, + 38, + ], + type: "TSArrayType", + }, + }, + }, + ], + range: [ + 5, + 47, + ], + returnType: { + range: [ + 39, + 44, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 41, + 44, + ], + type: "TSAnyKeyword", + }, + }, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - FunctionExpression 4`] = ` +{ + async: true, + body: { + body: [], + range: [ + 24, + 26, + ], + type: "BlockStatement", + }, + generator: true, + id: null, + params: [], + range: [ + 5, + 26, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - Identifier 1`] = ` +{ + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, +} +`; + +snapshot[`Plugin - ImportExpression 1`] = ` +{ + options: { + properties: [ + { + computed: false, + key: { + name: "with", + optional: false, + range: [ + 17, + 21, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 17, + 39, + ], + shorthand: false, + type: "Property", + value: { + properties: [ + { + computed: false, + key: { + name: "type", + optional: false, + range: [ + 25, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 25, + 37, + ], + shorthand: false, + type: "Property", + value: { + range: [ + 31, + 37, + ], + raw: "'json'", + type: "Literal", + value: "json", + }, + }, + ], + range: [ + 23, + 39, + ], + type: "ObjectExpression", + }, + }, + ], + range: [ + 15, + 41, + ], + type: "ObjectExpression", + }, + range: [ + 1, + 42, + ], + source: { + range: [ + 8, + 13, + ], + raw: "'foo'", + type: "Literal", + value: "foo", + }, + type: "ImportExpression", +} +`; + +snapshot[`Plugin - LogicalExpression 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "&&", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "LogicalExpression", +} +`; + +snapshot[`Plugin - LogicalExpression 2`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "||", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "LogicalExpression", +} +`; + +snapshot[`Plugin - LogicalExpression 3`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "??", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "LogicalExpression", +} +`; + +snapshot[`Plugin - MemberExpression 1`] = ` +{ + computed: false, + object: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + property: { + name: "b", + optional: false, + range: [ + 3, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 4, + ], + type: "MemberExpression", +} +`; + +snapshot[`Plugin - MemberExpression 2`] = ` +{ + computed: true, + object: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + property: { + range: [ + 3, + 6, + ], + raw: "'b'", + type: "Literal", + value: "b", + }, + range: [ + 1, + 7, + ], + type: "MemberExpression", +} +`; + +snapshot[`Plugin - MetaProperty 1`] = ` +{ + property: { + name: "meta", + optional: false, + range: [ + 1, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "MetaProperty", +} +`; + +snapshot[`Plugin - NewExpression 1`] = ` +{ + arguments: [], + callee: { + name: "Foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 10, + ], + type: "NewExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - NewExpression 2`] = ` +{ + arguments: [ + { + name: "a", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 18, + 19, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 15, + 18, + ], + type: "SpreadElement", + }, + ], + callee: { + name: "Foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 20, + ], + type: "NewExpression", + typeArguments: { + params: [ + { + range: [ + 9, + 10, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 9, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 8, + 11, + ], + type: "TSTypeParameterInstantiation", + }, +} +`; + +snapshot[`Plugin - ObjectExpression 1`] = ` +{ + properties: [], + range: [ + 5, + 7, + ], + type: "ObjectExpression", +} +`; + +snapshot[`Plugin - ObjectExpression 2`] = ` +{ + properties: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 7, + 8, + ], + shorthand: true, + type: "Property", + value: { + name: "a", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 10, + ], + type: "ObjectExpression", +} +`; + +snapshot[`Plugin - ObjectExpression 3`] = ` +{ + properties: [ + { + computed: false, + key: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 7, + 11, + ], + shorthand: false, + type: "Property", + value: { + name: "c", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + { + computed: true, + key: { + name: "c", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 13, + 19, + ], + shorthand: false, + type: "Property", + value: { + name: "d", + optional: false, + range: [ + 18, + 19, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 21, + ], + type: "ObjectExpression", +} +`; + +snapshot[`Plugin - PrivateIdentifier 1`] = ` +{ + name: "foo", + range: [ + 13, + 17, + ], + type: "PrivateIdentifier", +} +`; + +snapshot[`Plugin - SequenceExpression 1`] = ` +{ + expressions: [ + { + name: "a", + optional: false, + range: [ + 2, + 3, + ], + type: "Identifier", + typeAnnotation: null, + }, + { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + ], + range: [ + 2, + 6, + ], + type: "SequenceExpression", +} +`; + +snapshot[`Plugin - Super 1`] = ` +{ + range: [ + 41, + 46, + ], + type: "Super", +} +`; + +snapshot[`Plugin - TaggedTemplateExpression 1`] = ` +{ + quasi: { + expressions: [ + { + name: "bar", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + ], + quasis: [ + { + cooked: "foo ", + range: [ + 5, + 9, + ], + raw: "foo ", + tail: false, + type: "TemplateElement", + }, + { + cooked: " baz", + range: [ + 15, + 19, + ], + raw: " baz", + tail: true, + type: "TemplateElement", + }, + ], + range: [ + 4, + 20, + ], + type: "TemplateLiteral", + }, + range: [ + 1, + 20, + ], + tag: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "TaggedTemplateExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - TemplateLiteral 1`] = ` +{ + expressions: [ + { + name: "bar", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + ], + quasis: [ + { + cooked: "foo ", + range: [ + 2, + 6, + ], + raw: "foo ", + tail: false, + type: "TemplateElement", + }, + { + cooked: " baz", + range: [ + 12, + 16, + ], + raw: " baz", + tail: true, + type: "TemplateElement", + }, + ], + range: [ + 1, + 17, + ], + type: "TemplateLiteral", +} +`; + +snapshot[`Plugin - ThisExpression 1`] = ` +{ + range: [ + 1, + 5, + ], + type: "ThisExpression", +} +`; + +snapshot[`Plugin - TSAsExpression 1`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 7, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 6, + 7, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSAsExpression 2`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 11, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 1, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "const", + optional: false, + range: [ + 1, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSNonNullExpression 1`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 3, + ], + type: "TSNonNullExpression", +} +`; + +snapshot[`Plugin - TSSatisfiesExpression 1`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 14, + ], + type: "TSSatisfiesExpression", + typeAnnotation: { + range: [ + 13, + 14, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "b", + optional: false, + range: [ + 13, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - UnaryExpression 1`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "typeof", + range: [ + 1, + 9, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UnaryExpression 2`] = ` +{ + argument: { + range: [ + 6, + 7, + ], + raw: "0", + type: "Literal", + value: 0, + }, + operator: "void", + range: [ + 1, + 7, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UnaryExpression 3`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 2, + 3, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "-", + range: [ + 1, + 3, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UnaryExpression 4`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 2, + 3, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "+", + range: [ + 1, + 3, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 1`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "++", + prefix: false, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 2`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 3, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "++", + prefix: true, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 3`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "--", + prefix: false, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 4`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 3, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "--", + prefix: true, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - YieldExpression 1`] = ` +{ + argument: { + name: "bar", + optional: false, + range: [ + 25, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + delegate: false, + range: [ + 19, + 28, + ], + type: "YieldExpression", +} +`; + +snapshot[`Plugin - Literal 1`] = ` +{ + range: [ + 1, + 2, + ], + raw: "1", + type: "Literal", + value: 1, +} +`; + +snapshot[`Plugin - Literal 2`] = ` +{ + range: [ + 1, + 6, + ], + raw: "'foo'", + type: "Literal", + value: "foo", +} +`; + +snapshot[`Plugin - Literal 3`] = ` +{ + range: [ + 1, + 6, + ], + raw: '"foo"', + type: "Literal", + value: "foo", +} +`; + +snapshot[`Plugin - Literal 4`] = ` +{ + range: [ + 1, + 5, + ], + raw: "true", + type: "Literal", + value: true, +} +`; + +snapshot[`Plugin - Literal 5`] = ` +{ + range: [ + 1, + 6, + ], + raw: "false", + type: "Literal", + value: false, +} +`; + +snapshot[`Plugin - Literal 6`] = ` +{ + range: [ + 1, + 5, + ], + raw: "null", + type: "Literal", + value: null, +} +`; + +snapshot[`Plugin - Literal 7`] = ` +{ + bigint: "1", + range: [ + 1, + 3, + ], + raw: "1n", + type: "Literal", + value: 1n, +} +`; + +snapshot[`Plugin - Literal 8`] = ` +{ + range: [ + 1, + 7, + ], + raw: "/foo/g", + regex: { + flags: "g", + pattern: "foo", + }, + type: "Literal", + value: /foo/g, +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 1`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 8, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 8, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 2`] = ` +{ + children: [], + closingElement: { + name: { + name: "div", + range: [ + 8, + 11, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 12, + ], + type: "JSXClosingElement", + }, + openingElement: { + attributes: [], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 6, + ], + selfClosing: false, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 12, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 3`] = ` +{ + children: [], + closingElement: { + name: { + name: "div", + range: [ + 10, + 13, + ], + type: "JSXIdentifier", + }, + range: [ + 8, + 14, + ], + type: "JSXClosingElement", + }, + openingElement: { + attributes: [ + { + name: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 7, + ], + type: "JSXAttribute", + value: null, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 8, + ], + selfClosing: false, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 14, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 4`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [ + { + name: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 11, + ], + type: "JSXAttribute", + value: { + range: [ + 8, + 11, + ], + raw: '"b"', + type: "Literal", + value: "b", + }, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 14, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 14, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 5`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [ + { + name: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 11, + ], + type: "JSXAttribute", + value: { + expression: { + range: [ + 9, + 10, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 8, + 11, + ], + type: "JSXExpressionContainer", + }, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 14, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 14, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 6`] = ` +{ + children: [ + { + range: [ + 6, + 9, + ], + raw: "foo", + type: "JSXText", + value: "foo", + }, + { + expression: { + range: [ + 10, + 11, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 9, + 12, + ], + type: "JSXExpressionContainer", + }, + ], + closingElement: { + name: { + name: "div", + range: [ + 14, + 17, + ], + type: "JSXIdentifier", + }, + range: [ + 12, + 18, + ], + type: "JSXClosingElement", + }, + openingElement: { + attributes: [], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 6, + ], + selfClosing: false, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 18, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 7`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + object: { + name: "a", + range: [ + 2, + 3, + ], + type: "JSXIdentifier", + }, + property: { + name: "b", + range: [ + 4, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 2, + 5, + ], + type: "JSXMemberExpression", + }, + range: [ + 1, + 8, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 8, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 8`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [ + { + name: { + name: { + name: "b", + range: [ + 8, + 9, + ], + type: "JSXIdentifier", + }, + namespace: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 9, + ], + type: "JSXNamespacedName", + }, + range: [ + 6, + 13, + ], + type: "JSXAttribute", + value: { + expression: { + range: [ + 11, + 12, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 10, + 13, + ], + type: "JSXExpressionContainer", + }, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 16, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 16, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 9`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + name: "Foo", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 8, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 8, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 10`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + name: "Foo", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 11, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: { + params: [ + { + range: [ + 6, + 7, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 8, + ], + type: "TSTypeParameterInstantiation", + }, + }, + range: [ + 1, + 11, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment 1`] = ` +{ + children: [], + closingFragment: { + range: [ + 3, + 6, + ], + type: "JSXClosingFragment", + }, + openingFragment: { + range: [ + 1, + 3, + ], + type: "JSXOpeningFragment", + }, + range: [ + 1, + 6, + ], + type: "JSXFragment", +} +`; + +snapshot[`Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment 2`] = ` +{ + children: [ + { + range: [ + 3, + 6, + ], + raw: "foo", + type: "JSXText", + value: "foo", + }, + { + expression: { + range: [ + 7, + 8, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 6, + 9, + ], + type: "JSXExpressionContainer", + }, + ], + closingFragment: { + range: [ + 9, + 12, + ], + type: "JSXClosingFragment", + }, + openingFragment: { + range: [ + 1, + 3, + ], + type: "JSXOpeningFragment", + }, + range: [ + 1, + 12, + ], + type: "JSXFragment", +} +`; + +snapshot[`Plugin - TSAsExpression 3`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 9, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 6, + 9, + ], + type: "TSAnyKeyword", + }, +} +`; + +snapshot[`Plugin - TSAsExpression 4`] = ` +{ + expression: { + range: [ + 1, + 6, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 1, + 15, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 1, + 15, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "const", + optional: false, + range: [ + 1, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSEnumDeclaration 1`] = ` +{ + body: { + members: [], + range: [ + 1, + 12, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 2`] = ` +{ + body: { + members: [], + range: [ + 1, + 18, + ], + type: "TSEnumBody", + }, + const: true, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 12, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 18, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 3`] = ` +{ + body: { + members: [ + { + id: { + name: "A", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: null, + range: [ + 12, + 13, + ], + type: "TSEnumMember", + }, + { + id: { + name: "B", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: null, + range: [ + 15, + 16, + ], + type: "TSEnumMember", + }, + ], + range: [ + 1, + 18, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 18, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 4`] = ` +{ + body: { + members: [ + { + id: { + range: [ + 12, + 17, + ], + raw: '"a-b"', + type: "Literal", + value: "a-b", + }, + initializer: null, + range: [ + 12, + 17, + ], + type: "TSEnumMember", + }, + ], + range: [ + 1, + 19, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 19, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 5`] = ` +{ + body: { + members: [ + { + id: { + name: "A", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: { + range: [ + 16, + 17, + ], + raw: "1", + type: "Literal", + value: 1, + }, + range: [ + 12, + 17, + ], + type: "TSEnumMember", + }, + { + id: { + name: "B", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: { + range: [ + 23, + 24, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 19, + 24, + ], + type: "TSEnumMember", + }, + { + id: { + name: "C", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: { + left: { + name: "A", + optional: false, + range: [ + 30, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "|", + range: [ + 30, + 35, + ], + right: { + name: "B", + optional: false, + range: [ + 34, + 35, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", + }, + range: [ + 26, + 35, + ], + type: "TSEnumMember", + }, + ], + range: [ + 1, + 37, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 37, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSInterface 1`] = ` +{ + body: { + body: [], + range: [ + 13, + 15, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 15, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 2`] = ` +{ + body: { + body: [], + range: [ + 16, + 18, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 13, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 13, + 14, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 12, + 15, + ], + type: "TSTypeParameterDeclaration", + }, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 18, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 3`] = ` +{ + body: { + body: [], + range: [ + 36, + 38, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 38, + ], + type: "TSInterface", + typeParameters: [ + { + expression: { + name: "Foo", + optional: false, + range: [ + 21, + 24, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 21, + 27, + ], + type: "TSInterfaceHeritage", + typeArguments: { + params: [ + { + range: [ + 25, + 26, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 25, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 24, + 27, + ], + type: "TSTypeParameterInstantiation", + }, + }, + { + expression: { + name: "Bar", + optional: false, + range: [ + 29, + 32, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 29, + 35, + ], + type: "TSInterfaceHeritage", + typeArguments: { + params: [ + { + range: [ + 33, + 34, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 33, + 34, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 32, + 35, + ], + type: "TSTypeParameterInstantiation", + }, + }, + ], +} +`; + +snapshot[`Plugin - TSInterface 4`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "foo", + optional: false, + range: [ + 15, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 15, + 24, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 18, + 23, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 20, + 23, + ], + type: "TSAnyKeyword", + }, + }, + }, + { + computed: false, + key: { + name: "bar", + optional: false, + range: [ + 25, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: true, + range: [ + 25, + 34, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 29, + 34, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 31, + 34, + ], + type: "TSAnyKeyword", + }, + }, + }, + ], + range: [ + 13, + 36, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 36, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 5`] = ` +{ + body: { + body: [ + { + parameters: [ + { + name: "key", + optional: false, + range: [ + 25, + 36, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 28, + 36, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 30, + 36, + ], + type: "TSStringKeyword", + }, + }, + }, + ], + range: [ + 15, + 42, + ], + readonly: true, + type: "TSIndexSignature", + typeAnnotation: { + range: [ + 37, + 42, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 39, + 42, + ], + type: "TSAnyKeyword", + }, + }, + }, + ], + range: [ + 13, + 44, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 44, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 6`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 24, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 15, + 30, + ], + readonly: true, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 25, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 27, + 30, + ], + type: "TSAnyKeyword", + }, + }, + }, + ], + range: [ + 13, + 32, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 32, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 7`] = ` +{ + body: { + body: [ + { + params: [ + { + name: "a", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 20, + 23, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 22, + 23, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 22, + 23, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + }, + ], + range: [ + 15, + 27, + ], + returnType: { + range: [ + 24, + 27, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 26, + 27, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + type: "TSCallSignatureDeclaration", + typeAnnotation: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 17, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 15, + 18, + ], + type: "TSTypeParameterDeclaration", + }, + }, + ], + range: [ + 13, + 29, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 29, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 8`] = ` +{ + body: { + body: [ + { + params: [ + { + name: "a", + optional: false, + range: [ + 23, + 24, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 24, + 27, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 26, + 27, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + }, + ], + range: [ + 15, + 31, + ], + returnType: { + range: [ + 28, + 31, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 30, + 31, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 30, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + type: "TSConstructSignatureDeclaration", + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 20, + 21, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 20, + 21, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 19, + 22, + ], + type: "TSTypeParameterDeclaration", + }, + }, + ], + range: [ + 13, + 33, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 33, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 9`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 15, + 36, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 16, + 36, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + params: [ + { + name: "a", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 27, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 29, + 30, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 29, + 30, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + }, + ], + range: [ + 18, + 36, + ], + returnType: { + range: [ + 32, + 36, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 35, + 36, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 35, + 36, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + type: "TSConstructSignatureDeclaration", + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 23, + 24, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 23, + 24, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 22, + 25, + ], + type: "TSTypeParameterDeclaration", + }, + }, + }, + }, + ], + range: [ + 13, + 38, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 38, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 10`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "getter", + optional: false, + range: [ + 15, + 30, + ], + readonly: false, + returnType: { + range: [ + 22, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 24, + 30, + ], + type: "TSStringKeyword", + }, + }, + static: false, + type: "TSMethodSignature", + }, + ], + range: [ + 13, + 32, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 32, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 11`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "setter", + optional: false, + params: [ + { + name: "v", + optional: false, + range: [ + 21, + 22, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 22, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 24, + 30, + ], + type: "TSStringKeyword", + }, + }, + }, + ], + range: [ + 15, + 31, + ], + readonly: false, + static: false, + type: "TSMethodSignature", + }, + ], + range: [ + 13, + 33, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 33, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 12`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "method", + optional: false, + params: [ + { + name: "arg", + optional: true, + range: [ + 20, + 23, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 24, + 29, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 26, + 29, + ], + type: "TSAnyKeyword", + }, + }, + }, + { + argument: { + name: "args", + optional: false, + range: [ + 34, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 31, + 45, + ], + type: "RestElement", + typeAnnotation: { + range: [ + 38, + 45, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + elementType: { + range: [ + 40, + 43, + ], + type: "TSAnyKeyword", + }, + range: [ + 40, + 45, + ], + type: "TSArrayType", + }, + }, + }, + ], + range: [ + 15, + 51, + ], + readonly: false, + returnType: { + range: [ + 46, + 51, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 48, + 51, + ], + type: "TSAnyKeyword", + }, + }, + static: false, + type: "TSMethodSignature", + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 17, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 17, + 18, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 16, + 19, + ], + type: "TSTypeParameterDeclaration", + }, + }, + ], + range: [ + 13, + 53, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 53, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSSatisfiesExpression 2`] = ` +{ + expression: { + properties: [], + range: [ + 11, + 13, + ], + type: "ObjectExpression", + }, + range: [ + 11, + 25, + ], + type: "TSSatisfiesExpression", + typeAnnotation: { + range: [ + 24, + 25, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "A", + optional: false, + range: [ + 24, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSTypeAliasDeclaration 1`] = ` +{ + declare: false, + id: { + name: "A", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 13, + ], + type: "TSTypeAliasDeclaration", + typeAnnotation: { + range: [ + 10, + 13, + ], + type: "TSAnyKeyword", + }, + typeParameters: null, +} +`; + +snapshot[`Plugin - TSTypeAliasDeclaration 2`] = ` +{ + declare: false, + id: { + name: "A", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 16, + ], + type: "TSTypeAliasDeclaration", + typeAnnotation: { + range: [ + 13, + 16, + ], + type: "TSAnyKeyword", + }, + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 8, + 9, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 7, + 10, + ], + type: "TSTypeParameterDeclaration", + }, +} +`; + +snapshot[`Plugin - TSTypeAliasDeclaration 3`] = ` +{ + declare: true, + id: { + name: "A", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 24, + ], + type: "TSTypeAliasDeclaration", + typeAnnotation: { + range: [ + 21, + 24, + ], + type: "TSAnyKeyword", + }, + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 17, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 15, + 18, + ], + type: "TSTypeParameterDeclaration", + }, +} +`; + +snapshot[`Plugin - TSNonNullExpression 2`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 3, + ], + type: "TSNonNullExpression", +} +`; + +snapshot[`Plugin - TSUnionType 1`] = ` +{ + range: [ + 10, + 15, + ], + type: "TSUnionType", + types: [ + { + range: [ + 10, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + { + range: [ + 14, + 15, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "C", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], +} +`; + +snapshot[`Plugin - TSIntersectionType 1`] = ` +{ + range: [ + 10, + 15, + ], + type: "TSIntersectionType", + types: [ + { + range: [ + 10, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + { + range: [ + 14, + 15, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "C", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], +} +`; + +snapshot[`Plugin - TSModuleDeclaration 1`] = ` +{ + body: { + body: [], + range: [ + 10, + 12, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "A", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSModuleDeclaration 2`] = ` +{ + body: { + body: [ + { + declaration: { + async: false, + body: null, + declare: false, + generator: false, + id: { + name: "A", + optional: false, + range: [ + 36, + 37, + ], + type: "Identifier", + typeAnnotation: null, + }, + params: [], + range: [ + 27, + 45, + ], + returnType: { + range: [ + 39, + 45, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 41, + 45, + ], + type: "TSVoidKeyword", + }, + }, + type: "FunctionDeclaration", + typeParameters: null, + }, + range: [ + 20, + 45, + ], + type: "ExportNamedDeclaration", + }, + ], + range: [ + 18, + 47, + ], + type: "TSModuleBlock", + }, + declare: true, + global: false, + id: { + name: "A", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 47, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSModuleDeclaration + TSModuleBlock 1`] = ` +{ + body: { + body: [], + range: [ + 10, + 12, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "A", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSModuleDeclaration + TSModuleBlock 2`] = ` +{ + body: { + body: [ + { + body: { + body: [], + range: [ + 27, + 29, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "B", + optional: false, + range: [ + 25, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 15, + 29, + ], + type: "TSModuleDeclaration", + }, + ], + range: [ + 13, + 31, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 31, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSQualifiedName 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + right: { + name: "b", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "TSQualifiedName", +} +`; + +snapshot[`Plugin - TSTypeLiteral 1`] = ` +{ + members: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 12, + 16, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 13, + 16, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + literal: { + range: [ + 15, + 16, + ], + raw: "1", + type: "Literal", + value: 1, + }, + range: [ + 15, + 16, + ], + type: "TSLiteralType", + }, + }, + }, + ], + range: [ + 10, + 18, + ], + type: "TSTypeLiteral", +} +`; + +snapshot[`Plugin - TSOptionalType 1`] = ` +{ + range: [ + 11, + 18, + ], + type: "TSOptionalType", + typeAnnotation: { + range: [ + 11, + 17, + ], + type: "TSNumberKeyword", + }, +} +`; + +snapshot[`Plugin - TSRestType 1`] = ` +{ + range: [ + 11, + 22, + ], + type: "TSRestType", + typeAnnotation: { + elementType: { + range: [ + 14, + 20, + ], + type: "TSNumberKeyword", + }, + range: [ + 14, + 22, + ], + type: "TSArrayType", + }, +} +`; + +snapshot[`Plugin - TSConditionalType 1`] = ` +{ + checkType: { + range: [ + 10, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + extendsType: { + range: [ + 20, + 21, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "C", + optional: false, + range: [ + 20, + 21, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + falseType: { + range: [ + 33, + 39, + ], + type: "TSStringKeyword", + }, + range: [ + 10, + 39, + ], + trueType: { + range: [ + 24, + 30, + ], + type: "TSNumberKeyword", + }, + type: "TSConditionalType", +} +`; + +snapshot[`Plugin - TSInferType 1`] = ` +{ + range: [ + 29, + 39, + ], + type: "TSInferType", + typeParameter: { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "Item", + optional: false, + range: [ + 35, + 39, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 35, + 39, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSTypeOperator 1`] = ` +{ + operator: "keyof", + range: [ + 10, + 17, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 16, + 17, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSTypeOperator 2`] = ` +{ + operator: "unique", + range: [ + 21, + 34, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 28, + 34, + ], + type: "TSSymbolKeyword", + }, +} +`; + +snapshot[`Plugin - TSTypeOperator 3`] = ` +{ + operator: "readonly", + range: [ + 10, + 21, + ], + type: "TSTypeOperator", + typeAnnotation: { + elementTypes: [], + range: [ + 19, + 21, + ], + type: "TSTupleType", + }, +} +`; + +snapshot[`Plugin - TSMappedType 1`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 41, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 31, + 38, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 2`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 45, + ], + readonly: true, + type: "TSMappedType", + typeAnnotation: { + elementTypes: [], + range: [ + 40, + 42, + ], + type: "TSTupleType", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 30, + 37, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 36, + 37, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 36, + 37, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 25, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 25, + 37, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 3`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 46, + ], + readonly: "-", + type: "TSMappedType", + typeAnnotation: { + elementTypes: [], + range: [ + 41, + 43, + ], + type: "TSTupleType", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 31, + 38, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 37, + 38, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 37, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 26, + 38, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 4`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 46, + ], + readonly: "+", + type: "TSMappedType", + typeAnnotation: { + elementTypes: [], + range: [ + 41, + 43, + ], + type: "TSTupleType", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 31, + 38, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 37, + 38, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 37, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 26, + 38, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 5`] = ` +{ + nameType: null, + optional: true, + range: [ + 13, + 42, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 32, + 39, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 6`] = ` +{ + nameType: null, + optional: "-", + range: [ + 13, + 43, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 33, + 40, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 7`] = ` +{ + nameType: null, + optional: "+", + range: [ + 13, + 43, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 33, + 40, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSLiteralType 1`] = ` +{ + literal: { + range: [ + 10, + 14, + ], + raw: "true", + type: "Literal", + value: true, + }, + range: [ + 10, + 14, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSLiteralType 2`] = ` +{ + literal: { + range: [ + 10, + 15, + ], + raw: "false", + type: "Literal", + value: false, + }, + range: [ + 10, + 15, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSLiteralType 3`] = ` +{ + literal: { + range: [ + 10, + 11, + ], + raw: "1", + type: "Literal", + value: 1, + }, + range: [ + 10, + 11, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSLiteralType 4`] = ` +{ + literal: { + range: [ + 10, + 15, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 10, + 15, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSTemplateLiteralType 1`] = ` +{ + quasis: [ + { + cooked: "a ", + range: [ + 11, + 13, + ], + raw: "a ", + tail: false, + type: "TemplateElement", + }, + { + cooked: "", + range: [ + 22, + 22, + ], + raw: "", + tail: true, + type: "TemplateElement", + }, + ], + range: [ + 10, + 23, + ], + type: "TSTemplateLiteralType", + types: [ + { + range: [ + 15, + 21, + ], + type: "TSStringKeyword", + }, + ], +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 1`] = ` +{ + elementTypes: [ + { + range: [ + 11, + 17, + ], + type: "TSNumberKeyword", + }, + ], + range: [ + 10, + 18, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 2`] = ` +{ + elementTypes: [ + { + elementType: { + range: [ + 14, + 20, + ], + type: "TSNumberKeyword", + }, + label: { + name: "x", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 11, + 20, + ], + type: "TSNamedTupleMember", + }, + ], + range: [ + 10, + 21, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 3`] = ` +{ + elementTypes: [ + { + elementType: { + range: [ + 14, + 20, + ], + type: "TSNumberKeyword", + }, + label: { + name: "x", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 11, + 20, + ], + type: "TSNamedTupleMember", + }, + ], + range: [ + 10, + 21, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 4`] = ` +{ + elementTypes: [ + { + elementType: { + elementType: { + range: [ + 17, + 23, + ], + type: "TSNumberKeyword", + }, + range: [ + 17, + 25, + ], + type: "TSArrayType", + }, + label: { + argument: { + name: "x", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 11, + 16, + ], + type: "RestElement", + typeAnnotation: null, + }, + range: [ + 11, + 25, + ], + type: "TSNamedTupleMember", + }, + ], + range: [ + 10, + 26, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSArrayType 1`] = ` +{ + elementType: { + range: [ + 10, + 16, + ], + type: "TSNumberKeyword", + }, + range: [ + 10, + 18, + ], + type: "TSArrayType", +} +`; + +snapshot[`Plugin - TSTypeQuery 1`] = ` +{ + exprName: { + name: "B", + optional: false, + range: [ + 17, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 18, + ], + type: "TSTypeQuery", + typeArguments: null, +} +`; + +snapshot[`Plugin - TS keywords 1`] = ` +{ + range: [ + 10, + 13, + ], + type: "TSAnyKeyword", +} +`; + +snapshot[`Plugin - TS keywords 2`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSBigIntKeyword", +} +`; + +snapshot[`Plugin - TS keywords 3`] = ` +{ + range: [ + 10, + 17, + ], + type: "TSBooleanKeyword", +} +`; + +snapshot[`Plugin - TS keywords 4`] = ` +{ + range: [ + 10, + 19, + ], + type: "TSIntrinsicKeyword", +} +`; + +snapshot[`Plugin - TS keywords 5`] = ` +{ + range: [ + 10, + 15, + ], + type: "TSNeverKeyword", +} +`; + +snapshot[`Plugin - TS keywords 6`] = ` +{ + range: [ + 10, + 14, + ], + type: "TSNullKeyword", +} +`; + +snapshot[`Plugin - TS keywords 7`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSNumberKeyword", +} +`; + +snapshot[`Plugin - TS keywords 8`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSObjectKeyword", +} +`; + +snapshot[`Plugin - TS keywords 9`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSStringKeyword", +} +`; + +snapshot[`Plugin - TS keywords 10`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSSymbolKeyword", +} +`; + +snapshot[`Plugin - TS keywords 11`] = ` +{ + range: [ + 10, + 19, + ], + type: "TSUndefinedKeyword", +} +`; + +snapshot[`Plugin - TS keywords 12`] = ` +{ + range: [ + 10, + 17, + ], + type: "TSUnknownKeyword", +} +`; + +snapshot[`Plugin - TS keywords 13`] = ` +{ + range: [ + 10, + 14, + ], + type: "TSVoidKeyword", +} +`; diff --git a/tests/unit/lint_plugin_test.ts b/tests/unit/lint_plugin_test.ts index 5b00f49d01..6c71501c46 100644 --- a/tests/unit/lint_plugin_test.ts +++ b/tests/unit/lint_plugin_test.ts @@ -1,6 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; +import { assertSnapshot } from "@std/testing/snapshot"; // TODO(@marvinhagemeister) Remove once we land "official" types export interface LintReportData { @@ -85,9 +86,12 @@ function testVisit( return result; } -function testLintNode(source: string, ...selectors: string[]) { - // deno-lint-ignore no-explicit-any - const log: any[] = []; +async function testSnapshot( + t: Deno.TestContext, + source: string, + ...selectors: string[] +) { + const log: unknown[] = []; testPlugin(source, { create() { @@ -103,7 +107,8 @@ function testLintNode(source: string, ...selectors: string[]) { }, }); - return log; + assertEquals(log.length > 0, true); + await assertSnapshot(t, log[0]); } Deno.test("Plugin - visitor enter/exit", () => { @@ -313,303 +318,157 @@ Deno.test("Plugin - visitor :nth-child", () => { assertEquals(result[1].node.name, "foobar"); }); -Deno.test("Plugin - Program", () => { - const node = testLintNode("", "Program"); - assertEquals(node[0], { - type: "Program", - sourceType: "script", - range: [1, 1], - body: [], - }); +Deno.test("Plugin - Program", async (t) => { + await testSnapshot(t, "", "Program"); }); -Deno.test("Plugin - BlockStatement", () => { - const node = testLintNode("{ foo; }", "BlockStatement"); - assertEquals(node[0], { - type: "BlockStatement", - range: [1, 9], - body: [{ - type: "ExpressionStatement", - range: [3, 7], - expression: { - type: "Identifier", - name: "foo", - range: [3, 6], - }, - }], - }); +Deno.test("Plugin - ImportDeclaration", async (t) => { + await testSnapshot(t, 'import "foo";', "ImportDeclaration"); + await testSnapshot(t, 'import foo from "foo";', "ImportDeclaration"); + await testSnapshot(t, 'import * as foo from "foo";', "ImportDeclaration"); + await testSnapshot( + t, + 'import { foo, bar as baz } from "foo";', + "ImportDeclaration", + ); + await testSnapshot( + t, + 'import foo from "foo" with { type: "json" };', + "ImportDeclaration", + ); }); -Deno.test("Plugin - BreakStatement", () => { - let node = testLintNode("break;", "BreakStatement"); - assertEquals(node[0], { - type: "BreakStatement", - range: [1, 7], - label: null, - }); - - node = testLintNode("break foo;", "BreakStatement"); - assertEquals(node[0], { - type: "BreakStatement", - range: [1, 11], - label: { - type: "Identifier", - range: [7, 10], - name: "foo", - }, - }); +Deno.test("Plugin - ExportNamedDeclaration", async (t) => { + await testSnapshot(t, 'export { foo } from "foo";', "ExportNamedDeclaration"); + await testSnapshot( + t, + 'export { bar as baz } from "foo";', + "ExportNamedDeclaration", + ); + await testSnapshot( + t, + 'export { foo } from "foo" with { type: "json" };', + "ExportNamedDeclaration", + ); }); -Deno.test("Plugin - ContinueStatement", () => { - let node = testLintNode("continue;", "ContinueStatement"); - assertEquals(node[0], { - type: "ContinueStatement", - range: [1, 10], - label: null, - }); - - node = testLintNode("continue foo;", "ContinueStatement"); - assertEquals(node[0], { - type: "ContinueStatement", - range: [1, 14], - label: { - type: "Identifier", - range: [10, 13], - name: "foo", - }, - }); +Deno.test("Plugin - ExportDefaultDeclaration", async (t) => { + await testSnapshot( + t, + "export default function foo() {}", + "ExportDefaultDeclaration", + ); + await testSnapshot( + t, + "export default function () {}", + "ExportDefaultDeclaration", + ); + await testSnapshot( + t, + "export default class Foo {}", + "ExportDefaultDeclaration", + ); + await testSnapshot( + t, + "export default class {}", + "ExportDefaultDeclaration", + ); + await testSnapshot(t, "export default bar;", "ExportDefaultDeclaration"); + await testSnapshot( + t, + "export default interface Foo {};", + "ExportDefaultDeclaration", + ); }); -Deno.test("Plugin - DebuggerStatement", () => { - const node = testLintNode("debugger;", "DebuggerStatement"); - assertEquals(node[0], { - type: "DebuggerStatement", - range: [1, 10], - }); +Deno.test("Plugin - ExportAllDeclaration", async (t) => { + await testSnapshot(t, 'export * from "foo";', "ExportAllDeclaration"); + await testSnapshot(t, 'export * as foo from "foo";', "ExportAllDeclaration"); + await testSnapshot( + t, + 'export * from "foo" with { type: "json" };', + "ExportAllDeclaration", + ); }); -Deno.test("Plugin - DoWhileStatement", () => { - const node = testLintNode("do {} while (foo);", "DoWhileStatement"); - assertEquals(node[0], { - type: "DoWhileStatement", - range: [1, 19], - test: { - type: "Identifier", - range: [14, 17], - name: "foo", - }, - body: { - type: "BlockStatement", - range: [4, 6], - body: [], - }, - }); +Deno.test("Plugin - TSExportAssignment", async (t) => { + await testSnapshot(t, "export = foo;", "TSExportAssignment"); }); -Deno.test("Plugin - ExpressionStatement", () => { - const node = testLintNode("foo;", "ExpressionStatement"); - assertEquals(node[0], { - type: "ExpressionStatement", - range: [1, 5], - expression: { - type: "Identifier", - range: [1, 4], - name: "foo", - }, - }); +Deno.test("Plugin - TSNamespaceExportDeclaration", async (t) => { + await testSnapshot( + t, + "export as namespace A;", + "TSNamespaceExportDeclaration", + ); }); -Deno.test("Plugin - ForInStatement", () => { - const node = testLintNode("for (a in b) {}", "ForInStatement"); - assertEquals(node[0], { - type: "ForInStatement", - range: [1, 16], - left: { - type: "Identifier", - range: [6, 7], - name: "a", - }, - right: { - type: "Identifier", - range: [11, 12], - name: "b", - }, - body: { - type: "BlockStatement", - range: [14, 16], - body: [], - }, - }); +Deno.test("Plugin - TSImportEqualsDeclaration", async (t) => { + await testSnapshot(t, "import a = b", "TSImportEqualsDeclaration"); + await testSnapshot( + t, + 'import a = require("foo")', + "TSImportEqualsDeclaration", + ); }); -Deno.test("Plugin - ForOfStatement", () => { - let node = testLintNode("for (a of b) {}", "ForOfStatement"); - assertEquals(node[0], { - type: "ForOfStatement", - range: [1, 16], - await: false, - left: { - type: "Identifier", - range: [6, 7], - name: "a", - }, - right: { - type: "Identifier", - range: [11, 12], - name: "b", - }, - body: { - type: "BlockStatement", - range: [14, 16], - body: [], - }, - }); - - node = testLintNode("for await (a of b) {}", "ForOfStatement"); - assertEquals(node[0], { - type: "ForOfStatement", - range: [1, 22], - await: true, - left: { - type: "Identifier", - range: [12, 13], - name: "a", - }, - right: { - type: "Identifier", - range: [17, 18], - name: "b", - }, - body: { - type: "BlockStatement", - range: [20, 22], - body: [], - }, - }); +Deno.test("Plugin - BlockStatement", async (t) => { + await testSnapshot(t, "{ foo; }", "BlockStatement"); }); -Deno.test("Plugin - ForStatement", () => { - let node = testLintNode("for (;;) {}", "ForStatement"); - assertEquals(node[0], { - type: "ForStatement", - range: [1, 12], - init: null, - test: null, - update: null, - body: { - type: "BlockStatement", - range: [10, 12], - body: [], - }, - }); - - node = testLintNode("for (a; b; c) {}", "ForStatement"); - assertEquals(node[0], { - type: "ForStatement", - range: [1, 17], - init: { - type: "Identifier", - range: [6, 7], - name: "a", - }, - test: { - type: "Identifier", - range: [9, 10], - name: "b", - }, - update: { - type: "Identifier", - range: [12, 13], - name: "c", - }, - body: { - type: "BlockStatement", - range: [15, 17], - body: [], - }, - }); +Deno.test("Plugin - BreakStatement", async (t) => { + await testSnapshot(t, "while (false) break;", "BreakStatement"); + await testSnapshot(t, "foo: while (false) break foo;", "BreakStatement"); }); -Deno.test("Plugin - IfStatement", () => { - let node = testLintNode("if (foo) {}", "IfStatement"); - assertEquals(node[0], { - type: "IfStatement", - range: [1, 12], - test: { - type: "Identifier", - name: "foo", - range: [5, 8], - }, - consequent: { - type: "BlockStatement", - range: [10, 12], - body: [], - }, - alternate: null, - }); - - node = testLintNode("if (foo) {} else {}", "IfStatement"); - assertEquals(node[0], { - type: "IfStatement", - range: [1, 20], - test: { - type: "Identifier", - name: "foo", - range: [5, 8], - }, - consequent: { - type: "BlockStatement", - range: [10, 12], - body: [], - }, - alternate: { - type: "BlockStatement", - range: [18, 20], - body: [], - }, - }); +Deno.test("Plugin - ContinueStatement", async (t) => { + await testSnapshot(t, "continue;", "ContinueStatement"); + await testSnapshot(t, "continue foo;", "ContinueStatement"); }); -Deno.test("Plugin - LabeledStatement", () => { - const node = testLintNode("foo: {};", "LabeledStatement"); - assertEquals(node[0], { - type: "LabeledStatement", - range: [1, 8], - label: { - type: "Identifier", - name: "foo", - range: [1, 4], - }, - body: { - type: "BlockStatement", - range: [6, 8], - body: [], - }, - }); +Deno.test("Plugin - DebuggerStatement", async (t) => { + await testSnapshot(t, "debugger;", "DebuggerStatement"); }); -Deno.test("Plugin - ReturnStatement", () => { - let node = testLintNode("return", "ReturnStatement"); - assertEquals(node[0], { - type: "ReturnStatement", - range: [1, 7], - argument: null, - }); - - node = testLintNode("return foo;", "ReturnStatement"); - assertEquals(node[0], { - type: "ReturnStatement", - range: [1, 12], - argument: { - type: "Identifier", - name: "foo", - range: [8, 11], - }, - }); +Deno.test("Plugin - DoWhileStatement", async (t) => { + await testSnapshot(t, "do {} while (foo);", "DoWhileStatement"); }); -Deno.test("Plugin - SwitchStatement", () => { - const node = testLintNode( +Deno.test("Plugin - ExpressionStatement", async (t) => { + await testSnapshot(t, "foo;", "ExpressionStatement"); +}); + +Deno.test("Plugin - ForInStatement", async (t) => { + await testSnapshot(t, "for (a in b) {}", "ForInStatement"); +}); + +Deno.test("Plugin - ForOfStatement", async (t) => { + await testSnapshot(t, "for (a of b) {}", "ForOfStatement"); + await testSnapshot(t, "for await (a of b) {}", "ForOfStatement"); +}); + +Deno.test("Plugin - ForStatement", async (t) => { + await testSnapshot(t, "for (;;) {}", "ForStatement"); + await testSnapshot(t, "for (a; b; c) {}", "ForStatement"); +}); + +Deno.test("Plugin - IfStatement", async (t) => { + await testSnapshot(t, "if (foo) {}", "IfStatement"); + await testSnapshot(t, "if (foo) {} else {}", "IfStatement"); +}); + +Deno.test("Plugin - LabeledStatement", async (t) => { + await testSnapshot(t, "foo: {};", "LabeledStatement"); +}); + +Deno.test("Plugin - ReturnStatement", async (t) => { + await testSnapshot(t, "return", "ReturnStatement"); + await testSnapshot(t, "return foo;", "ReturnStatement"); +}); + +Deno.test("Plugin - SwitchStatement", async (t) => { + await testSnapshot( + t, `switch (foo) { case foo: case bar: @@ -619,151 +478,449 @@ Deno.test("Plugin - SwitchStatement", () => { }`, "SwitchStatement", ); - assertEquals(node[0], { - type: "SwitchStatement", - range: [1, 94], - discriminant: { - type: "Identifier", - range: [9, 12], - name: "foo", - }, - cases: [ - { - type: "SwitchCase", - range: [22, 31], - test: { - type: "Identifier", - range: [27, 30], - name: "foo", - }, - consequent: [], - }, - { - type: "SwitchCase", - range: [38, 62], - test: { - type: "Identifier", - range: [43, 46], - name: "bar", - }, - consequent: [ - { - type: "BreakStatement", - label: null, - range: [56, 62], - }, - ], - }, - { - type: "SwitchCase", - range: [69, 88], - test: null, - consequent: [ - { - type: "BlockStatement", - range: [86, 88], - body: [], - }, - ], - }, - ], - }); }); -Deno.test("Plugin - ThrowStatement", () => { - const node = testLintNode("throw foo;", "ThrowStatement"); - assertEquals(node[0], { - type: "ThrowStatement", - range: [1, 11], - argument: { - type: "Identifier", - range: [7, 10], - name: "foo", - }, - }); +Deno.test("Plugin - ThrowStatement", async (t) => { + await testSnapshot(t, "throw foo;", "ThrowStatement"); }); -Deno.test("Plugin - TryStatement", () => { - let node = testLintNode("try {} catch {};", "TryStatement"); - assertEquals(node[0], { - type: "TryStatement", - range: [1, 16], - block: { - type: "BlockStatement", - range: [5, 7], - body: [], - }, - handler: { - type: "CatchClause", - range: [8, 16], - param: null, - body: { - type: "BlockStatement", - range: [14, 16], - body: [], - }, - }, - finalizer: null, - }); - - node = testLintNode("try {} catch (e) {};", "TryStatement"); - assertEquals(node[0], { - type: "TryStatement", - range: [1, 20], - block: { - type: "BlockStatement", - range: [5, 7], - body: [], - }, - handler: { - type: "CatchClause", - range: [8, 20], - param: { - type: "Identifier", - range: [15, 16], - name: "e", - }, - body: { - type: "BlockStatement", - range: [18, 20], - body: [], - }, - }, - finalizer: null, - }); - - node = testLintNode("try {} finally {};", "TryStatement"); - assertEquals(node[0], { - type: "TryStatement", - range: [1, 18], - block: { - type: "BlockStatement", - range: [5, 7], - body: [], - }, - handler: null, - finalizer: { - type: "BlockStatement", - range: [16, 18], - body: [], - }, - }); +Deno.test("Plugin - TryStatement", async (t) => { + await testSnapshot(t, "try {} catch {};", "TryStatement"); + await testSnapshot(t, "try {} catch (e) {};", "TryStatement"); + await testSnapshot(t, "try {} finally {};", "TryStatement"); }); -Deno.test("Plugin - WhileStatement", () => { - const node = testLintNode("while (foo) {}", "WhileStatement"); - assertEquals(node[0], { - type: "WhileStatement", - range: [1, 15], - test: { - type: "Identifier", - range: [8, 11], - name: "foo", - }, - body: { - type: "BlockStatement", - range: [13, 15], - body: [], - }, - }); +Deno.test("Plugin - WhileStatement", async (t) => { + await testSnapshot(t, "while (foo) {}", "WhileStatement"); +}); + +Deno.test("Plugin - WithStatement", async (t) => { + await testSnapshot(t, "with ([]) {}", "WithStatement"); +}); + +Deno.test("Plugin - ArrayExpression", async (t) => { + await testSnapshot(t, "[[],,[]]", "ArrayExpression"); +}); + +Deno.test("Plugin - ArrowFunctionExpression", async (t) => { + await testSnapshot(t, "() => {}", "ArrowFunctionExpression"); + await testSnapshot(t, "async () => {}", "ArrowFunctionExpression"); + await testSnapshot( + t, + "(a: number, ...b: any[]): any => {}", + "ArrowFunctionExpression", + ); +}); + +Deno.test("Plugin - AssignmentExpression", async (t) => { + await testSnapshot(t, "a = b", "AssignmentExpression"); + await testSnapshot(t, "a = a ??= b", "AssignmentExpression"); +}); + +Deno.test("Plugin - AwaitExpression", async (t) => { + await testSnapshot(t, "await foo;", "AwaitExpression"); +}); + +Deno.test("Plugin - BinaryExpression", async (t) => { + await testSnapshot(t, "a > b", "BinaryExpression"); + await testSnapshot(t, "a >= b", "BinaryExpression"); + await testSnapshot(t, "a < b", "BinaryExpression"); + await testSnapshot(t, "a <= b", "BinaryExpression"); + await testSnapshot(t, "a == b", "BinaryExpression"); + await testSnapshot(t, "a === b", "BinaryExpression"); + await testSnapshot(t, "a != b", "BinaryExpression"); + await testSnapshot(t, "a !== b", "BinaryExpression"); + await testSnapshot(t, "a << b", "BinaryExpression"); + await testSnapshot(t, "a >> b", "BinaryExpression"); + await testSnapshot(t, "a >>> b", "BinaryExpression"); + await testSnapshot(t, "a + b", "BinaryExpression"); + await testSnapshot(t, "a - b", "BinaryExpression"); + await testSnapshot(t, "a * b", "BinaryExpression"); + await testSnapshot(t, "a / b", "BinaryExpression"); + await testSnapshot(t, "a % b", "BinaryExpression"); + await testSnapshot(t, "a | b", "BinaryExpression"); + await testSnapshot(t, "a ^ b", "BinaryExpression"); + await testSnapshot(t, "a & b", "BinaryExpression"); + await testSnapshot(t, "a in b", "BinaryExpression"); + await testSnapshot(t, "a ** b", "BinaryExpression"); +}); + +Deno.test("Plugin - CallExpression", async (t) => { + await testSnapshot(t, "foo();", "CallExpression"); + await testSnapshot(t, "foo(a, ...b);", "CallExpression"); + await testSnapshot(t, "foo?.();", "CallExpression"); + await testSnapshot(t, "foo();", "CallExpression"); +}); + +Deno.test("Plugin - ChainExpression", async (t) => { + await testSnapshot(t, "a?.b", "ChainExpression"); +}); + +Deno.test("Plugin - ClassExpression", async (t) => { + await testSnapshot(t, "a = class {}", "ClassExpression"); + await testSnapshot(t, "a = class Foo {}", "ClassExpression"); + await testSnapshot(t, "a = class Foo extends Bar {}", "ClassExpression"); + await testSnapshot( + t, + "a = class Foo extends Bar implements Baz, Baz2 {}", + "ClassExpression", + ); + await testSnapshot(t, "a = class Foo {}", "ClassExpression"); + await testSnapshot(t, "a = class { foo() {} }", "ClassExpression"); + await testSnapshot(t, "a = class { #foo() {} }", "ClassExpression"); + await testSnapshot(t, "a = class { foo: number }", "ClassExpression"); + await testSnapshot(t, "a = class { foo = bar }", "ClassExpression"); + await testSnapshot( + t, + "a = class { constructor(public foo: string) {} }", + "ClassExpression", + ); + await testSnapshot(t, "a = class { #foo: number = bar }", "ClassExpression"); + await testSnapshot(t, "a = class { static foo = bar }", "ClassExpression"); + await testSnapshot( + t, + "a = class { static foo; static { foo = bar } }", + "ClassExpression", + ); +}); + +Deno.test("Plugin - ConditionalExpression", async (t) => { + await testSnapshot(t, "a ? b : c", "ConditionalExpression"); +}); + +Deno.test("Plugin - FunctionExpression", async (t) => { + await testSnapshot(t, "a = function () {}", "FunctionExpression"); + await testSnapshot(t, "a = function foo() {}", "FunctionExpression"); + await testSnapshot( + t, + "a = function (a?: number, ...b: any[]): any {}", + "FunctionExpression", + ); + await testSnapshot(t, "a = async function* () {}", "FunctionExpression"); +}); + +Deno.test("Plugin - Identifier", async (t) => { + await testSnapshot(t, "a", "Identifier"); +}); + +Deno.test("Plugin - ImportExpression", async (t) => { + await testSnapshot( + t, + "import('foo', { with: { type: 'json' } })", + "ImportExpression", + ); +}); + +Deno.test("Plugin - LogicalExpression", async (t) => { + await testSnapshot(t, "a && b", "LogicalExpression"); + await testSnapshot(t, "a || b", "LogicalExpression"); + await testSnapshot(t, "a ?? b", "LogicalExpression"); +}); + +Deno.test("Plugin - MemberExpression", async (t) => { + await testSnapshot(t, "a.b", "MemberExpression"); + await testSnapshot(t, "a['b']", "MemberExpression"); +}); + +Deno.test("Plugin - MetaProperty", async (t) => { + await testSnapshot(t, "import.meta", "MetaProperty"); +}); + +Deno.test("Plugin - NewExpression", async (t) => { + await testSnapshot(t, "new Foo()", "NewExpression"); + await testSnapshot(t, "new Foo(a, ...b)", "NewExpression"); +}); + +Deno.test("Plugin - ObjectExpression", async (t) => { + await testSnapshot(t, "a = {}", "ObjectExpression"); + await testSnapshot(t, "a = { a }", "ObjectExpression"); + await testSnapshot(t, "a = { b: c, [c]: d }", "ObjectExpression"); +}); + +Deno.test("Plugin - PrivateIdentifier", async (t) => { + await testSnapshot(t, "class Foo { #foo = foo }", "PrivateIdentifier"); +}); + +Deno.test("Plugin - SequenceExpression", async (t) => { + await testSnapshot(t, "(a, b)", "SequenceExpression"); +}); + +Deno.test("Plugin - Super", async (t) => { + await testSnapshot( + t, + "class Foo extends Bar { constructor() { super(); } }", + "Super", + ); +}); + +Deno.test("Plugin - TaggedTemplateExpression", async (t) => { + await testSnapshot(t, "foo`foo ${bar} baz`", "TaggedTemplateExpression"); +}); + +Deno.test("Plugin - TemplateLiteral", async (t) => { + await testSnapshot(t, "`foo ${bar} baz`", "TemplateLiteral"); +}); + +Deno.test("Plugin - ThisExpression", async (t) => { + await testSnapshot(t, "this", "ThisExpression"); +}); + +Deno.test("Plugin - TSAsExpression", async (t) => { + await testSnapshot(t, "a as b", "TSAsExpression"); + await testSnapshot(t, "a as const", "TSAsExpression"); +}); + +Deno.test("Plugin - TSNonNullExpression", async (t) => { + await testSnapshot(t, "a!", "TSNonNullExpression"); +}); + +Deno.test("Plugin - TSSatisfiesExpression", async (t) => { + await testSnapshot(t, "a satisfies b", "TSSatisfiesExpression"); +}); + +Deno.test("Plugin - UnaryExpression", async (t) => { + await testSnapshot(t, "typeof a", "UnaryExpression"); + await testSnapshot(t, "void 0", "UnaryExpression"); + await testSnapshot(t, "-a", "UnaryExpression"); + await testSnapshot(t, "+a", "UnaryExpression"); +}); + +Deno.test("Plugin - UpdateExpression", async (t) => { + await testSnapshot(t, "a++", "UpdateExpression"); + await testSnapshot(t, "++a", "UpdateExpression"); + await testSnapshot(t, "a--", "UpdateExpression"); + await testSnapshot(t, "--a", "UpdateExpression"); +}); + +Deno.test("Plugin - YieldExpression", async (t) => { + await testSnapshot(t, "function* foo() { yield bar; }", "YieldExpression"); +}); + +Deno.test("Plugin - Literal", async (t) => { + await testSnapshot(t, "1", "Literal"); + await testSnapshot(t, "'foo'", "Literal"); + await testSnapshot(t, '"foo"', "Literal"); + await testSnapshot(t, "true", "Literal"); + await testSnapshot(t, "false", "Literal"); + await testSnapshot(t, "null", "Literal"); + await testSnapshot(t, "1n", "Literal"); + await testSnapshot(t, "/foo/g", "Literal"); +}); + +Deno.test("Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr", async (t) => { + await testSnapshot(t, "

", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, '
', "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "
foo{2}
", "JSXElement"); + await testSnapshot(t, "", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "", "JSXElement"); + await testSnapshot(t, " />", "JSXElement"); +}); + +Deno.test("Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment", async (t) => { + await testSnapshot(t, "<>", "JSXFragment"); + await testSnapshot(t, "<>foo{2}", "JSXFragment"); +}); + +Deno.test("Plugin - TSAsExpression", async (t) => { + await testSnapshot(t, "a as any", "TSAsExpression"); + await testSnapshot(t, '"foo" as const', "TSAsExpression"); +}); + +Deno.test("Plugin - TSEnumDeclaration", async (t) => { + await testSnapshot(t, "enum Foo {}", "TSEnumDeclaration"); + await testSnapshot(t, "const enum Foo {}", "TSEnumDeclaration"); + await testSnapshot(t, "enum Foo { A, B }", "TSEnumDeclaration"); + await testSnapshot(t, 'enum Foo { "a-b" }', "TSEnumDeclaration"); + await testSnapshot( + t, + "enum Foo { A = 1, B = 2, C = A | B }", + "TSEnumDeclaration", + ); +}); + +Deno.test("Plugin - TSInterface", async (t) => { + await testSnapshot(t, "interface A {}", "TSInterface"); + await testSnapshot(t, "interface A {}", "TSInterface"); + await testSnapshot(t, "interface A extends Foo, Bar {}", "TSInterface"); + await testSnapshot(t, "interface A { foo: any, bar?: any }", "TSInterface"); + await testSnapshot( + t, + "interface A { readonly [key: string]: any }", + "TSInterface", + ); + + await testSnapshot(t, "interface A { readonly a: any }", "TSInterface"); + await testSnapshot(t, "interface A { (a: T): T }", "TSInterface"); + await testSnapshot(t, "interface A { new (a: T): T }", "TSInterface"); + await testSnapshot(t, "interface A { a: new (a: T) => T }", "TSInterface"); + await testSnapshot(t, "interface A { get a(): string }", "TSInterface"); + await testSnapshot(t, "interface A { set a(v: string) }", "TSInterface"); + + await testSnapshot( + t, + "interface A { a(arg?: any, ...args: any[]): any }", + "TSInterface", + ); +}); + +Deno.test("Plugin - TSSatisfiesExpression", async (t) => { + await testSnapshot(t, "const a = {} satisfies A", "TSSatisfiesExpression"); +}); + +Deno.test("Plugin - TSTypeAliasDeclaration", async (t) => { + await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); + await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); + await testSnapshot(t, "declare type A = any", "TSTypeAliasDeclaration"); +}); + +Deno.test("Plugin - TSNonNullExpression", async (t) => { + await testSnapshot(t, "a!", "TSNonNullExpression"); +}); + +Deno.test("Plugin - TSUnionType", async (t) => { + await testSnapshot(t, "type A = B | C", "TSUnionType"); +}); + +Deno.test("Plugin - TSIntersectionType", async (t) => { + await testSnapshot(t, "type A = B & C", "TSIntersectionType"); +}); + +Deno.test("Plugin - TSModuleDeclaration", async (t) => { + await testSnapshot(t, "module A {}", "TSModuleDeclaration"); + await testSnapshot( + t, + "declare module A { export function A(): void }", + "TSModuleDeclaration", + ); +}); + +Deno.test("Plugin - TSModuleDeclaration + TSModuleBlock", async (t) => { + await testSnapshot(t, "module A {}", "TSModuleDeclaration"); + await testSnapshot( + t, + "namespace A { namespace B {} }", + "TSModuleDeclaration", + ); +}); + +Deno.test("Plugin - TSQualifiedName", async (t) => { + await testSnapshot(t, "type A = a.b;", "TSQualifiedName"); +}); + +Deno.test("Plugin - TSTypeLiteral", async (t) => { + await testSnapshot(t, "type A = { a: 1 };", "TSTypeLiteral"); +}); + +Deno.test("Plugin - TSOptionalType", async (t) => { + await testSnapshot(t, "type A = [number?]", "TSOptionalType"); +}); + +Deno.test("Plugin - TSRestType", async (t) => { + await testSnapshot(t, "type A = [...number[]]", "TSRestType"); +}); + +Deno.test("Plugin - TSConditionalType", async (t) => { + await testSnapshot( + t, + "type A = B extends C ? number : string;", + "TSConditionalType", + ); +}); + +Deno.test("Plugin - TSInferType", async (t) => { + await testSnapshot( + t, + "type A = T extends Array ? Item : T;", + "TSInferType", + ); +}); + +Deno.test("Plugin - TSTypeOperator", async (t) => { + await testSnapshot(t, "type A = keyof B", "TSTypeOperator"); + await testSnapshot(t, "declare const sym1: unique symbol;", "TSTypeOperator"); + await testSnapshot(t, "type A = readonly []", "TSTypeOperator"); +}); + +Deno.test("Plugin - TSMappedType", async (t) => { + await testSnapshot( + t, + "type A = { [P in keyof T]: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { -readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { +readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]?: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]-?: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]+?: boolean; };", + "TSMappedType", + ); +}); + +Deno.test("Plugin - TSLiteralType", async (t) => { + await testSnapshot(t, "type A = true", "TSLiteralType"); + await testSnapshot(t, "type A = false", "TSLiteralType"); + await testSnapshot(t, "type A = 1", "TSLiteralType"); + await testSnapshot(t, 'type A = "foo"', "TSLiteralType"); +}); + +Deno.test("Plugin - TSTemplateLiteralType", async (t) => { + await testSnapshot(t, "type A = `a ${string}`", "TSTemplateLiteralType"); +}); + +Deno.test("Plugin - TSTupleType + TSArrayType", async (t) => { + await testSnapshot(t, "type A = [number]", "TSTupleType"); + await testSnapshot(t, "type A = [x: number]", "TSTupleType"); + await testSnapshot(t, "type A = [x: number]", "TSTupleType"); + await testSnapshot(t, "type A = [...x: number[]]", "TSTupleType"); +}); + +Deno.test("Plugin - TSArrayType", async (t) => { + await testSnapshot(t, "type A = number[]", "TSArrayType"); +}); + +Deno.test("Plugin - TSTypeQuery", async (t) => { + await testSnapshot(t, "type A = typeof B", "TSTypeQuery"); +}); + +Deno.test("Plugin - TS keywords", async (t) => { + await testSnapshot(t, "type A = any", "TSAnyKeyword"); + await testSnapshot(t, "type A = bigint", "TSBigIntKeyword"); + await testSnapshot(t, "type A = boolean", "TSBooleanKeyword"); + await testSnapshot(t, "type A = intrinsic", "TSIntrinsicKeyword"); + await testSnapshot(t, "type A = never", "TSNeverKeyword"); + await testSnapshot(t, "type A = null", "TSNullKeyword"); + await testSnapshot(t, "type A = number", "TSNumberKeyword"); + await testSnapshot(t, "type A = object", "TSObjectKeyword"); + await testSnapshot(t, "type A = string", "TSStringKeyword"); + await testSnapshot(t, "type A = symbol", "TSSymbolKeyword"); + await testSnapshot(t, "type A = undefined", "TSUndefinedKeyword"); + await testSnapshot(t, "type A = unknown", "TSUnknownKeyword"); + await testSnapshot(t, "type A = void", "TSVoidKeyword"); }); From 0b033140c07b5abee231711b0fbc3fa24f5f9eec Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 14 Jan 2025 10:01:05 -0500 Subject: [PATCH 105/107] refactor: move `CliNpmResolver` to `deno_resolver::npm::NpmResolver` (#27659) As title. After this PR all npm resolution will be out of the CLI crate. --- Cargo.lock | 1 + cli/cache/mod.rs | 5 +- cli/emit.rs | 6 +- cli/factory.rs | 41 ++-- cli/graph_util.rs | 21 +- cli/lsp/analysis.rs | 26 ++- cli/lsp/resolver.rs | 135 +++++++---- cli/module_loader.rs | 19 +- cli/node.rs | 16 +- cli/npm/byonm.rs | 64 +---- cli/npm/managed.rs | 285 +---------------------- cli/npm/mod.rs | 91 ++------ cli/resolver.rs | 22 +- cli/standalone/binary.rs | 45 ++-- cli/standalone/mod.rs | 30 +-- cli/task_runner.rs | 18 +- cli/tools/check.rs | 29 ++- cli/tools/coverage/mod.rs | 5 +- cli/tools/info.rs | 50 ++-- cli/tools/registry/pm/deps.rs | 18 +- cli/tools/task.rs | 4 +- cli/tsc/mod.rs | 8 +- cli/worker.rs | 16 +- ext/node/lib.rs | 42 ++-- ext/node/ops/require.rs | 51 +++- resolvers/deno/cjs.rs | 25 +- resolvers/deno/lib.rs | 60 ++++- resolvers/deno/npm/byonm.rs | 17 +- resolvers/deno/npm/managed/common.rs | 73 ++++-- resolvers/deno/npm/managed/global.rs | 48 ++-- resolvers/deno/npm/managed/local.rs | 119 +++++----- resolvers/deno/npm/managed/mod.rs | 97 +++++--- resolvers/deno/npm/managed/resolution.rs | 21 +- resolvers/deno/npm/mod.rs | 197 +++++++++++++--- resolvers/node/analyze.rs | 39 +++- resolvers/node/lib.rs | 2 - resolvers/node/npm.rs | 15 +- resolvers/node/resolution.rs | 50 +++- resolvers/node/sync.rs | 7 - runtime/Cargo.toml | 1 + runtime/examples/extension/main.rs | 8 +- runtime/snapshot.rs | 4 + runtime/web_worker.rs | 50 +++- runtime/worker.rs | 50 +++- 44 files changed, 1030 insertions(+), 901 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa13ae0ccd..95633e3ce9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2242,6 +2242,7 @@ dependencies = [ "deno_node", "deno_path_util", "deno_permissions", + "deno_resolver", "deno_telemetry", "deno_terminal 0.2.0", "deno_tls", diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index ff9f07fc4e..0d7808cba6 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -15,6 +15,7 @@ use deno_graph::source::CacheInfo; use deno_graph::source::LoadFuture; use deno_graph::source::LoadResponse; use deno_graph::source::Loader; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::deno_permissions::PermissionsContainer; use node_resolver::InNpmPackageChecker; @@ -76,7 +77,7 @@ pub struct FetchCacher { pub file_header_overrides: HashMap>, file_fetcher: Arc, global_http_cache: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, module_info_cache: Arc, permissions: PermissionsContainer, sys: CliSys, @@ -88,7 +89,7 @@ impl FetchCacher { pub fn new( file_fetcher: Arc, global_http_cache: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, module_info_cache: Arc, sys: CliSys, options: FetchCacherOptions, diff --git a/cli/emit.rs b/cli/emit.rs index e9b5a4e250..69ac8323bb 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -24,11 +24,11 @@ use deno_graph::ModuleGraph; use crate::cache::EmitCache; use crate::cache::FastInsecureHasher; use crate::cache::ParsedSourceCache; -use crate::resolver::CjsTracker; +use crate::resolver::CliCjsTracker; #[derive(Debug)] pub struct Emitter { - cjs_tracker: Arc, + cjs_tracker: Arc, emit_cache: Arc, parsed_source_cache: Arc, transpile_and_emit_options: @@ -39,7 +39,7 @@ pub struct Emitter { impl Emitter { pub fn new( - cjs_tracker: Arc, + cjs_tracker: Arc, emit_cache: Arc, parsed_source_cache: Arc, transpile_options: deno_ast::TranspileOptions, diff --git a/cli/factory.rs b/cli/factory.rs index e3873b5164..3280fd379b 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -16,6 +16,7 @@ use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::DenoResolverOptions; @@ -32,7 +33,6 @@ use deno_runtime::inspector_server::InspectorServer; use deno_runtime::permissions::RuntimePermissionDescriptorParser; use log::warn; use node_resolver::analyze::NodeCodeTranslator; -use node_resolver::InNpmPackageChecker; use once_cell::sync::OnceCell; use crate::args::check_warn_tsconfig; @@ -68,7 +68,6 @@ use crate::node::CliCjsCodeAnalyzer; use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; -use crate::npm::create_cli_npm_resolver; use crate::npm::installer::NpmInstaller; use crate::npm::installer::NpmResolutionInstaller; use crate::npm::CliByonmNpmResolverCreateOptions; @@ -83,7 +82,7 @@ use crate::npm::CliNpmTarballCache; use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionCheckerMode; use crate::npm::NpmResolutionInitializer; -use crate::resolver::CjsTracker; +use crate::resolver::CliCjsTracker; use crate::resolver::CliDenoResolver; use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliNpmReqResolver; @@ -190,7 +189,7 @@ impl Deferred { struct CliFactoryServices { blob_store: Deferred>, caches: Deferred>, - cjs_tracker: Deferred>, + cjs_tracker: Deferred>, cli_options: Deferred>, code_cache: Deferred>, deno_resolver: Deferred>, @@ -203,7 +202,7 @@ struct CliFactoryServices { global_http_cache: Deferred>, http_cache: Deferred>, http_client_provider: Deferred>, - in_npm_pkg_checker: Deferred>, + in_npm_pkg_checker: Deferred, main_graph_container: Deferred>, maybe_file_watcher_reporter: Deferred>, maybe_inspector_server: Deferred>>, @@ -223,7 +222,7 @@ struct CliFactoryServices { npm_resolution: Arc, npm_resolution_initializer: Deferred>, npm_resolution_installer: Deferred>, - npm_resolver: Deferred>, + npm_resolver: Deferred, npm_tarball_cache: Deferred>, parsed_source_cache: Deferred>, permission_desc_parser: @@ -399,7 +398,7 @@ impl CliFactory { pub fn in_npm_pkg_checker( &self, - ) -> Result<&Arc, AnyError> { + ) -> Result<&DenoInNpmPackageChecker, AnyError> { self.services.in_npm_pkg_checker.get_or_try_init(|| { let cli_options = self.cli_options()?; let options = if cli_options.use_byonm() { @@ -414,7 +413,7 @@ impl CliFactory { }, ) }; - Ok(deno_resolver::npm::create_in_npm_pkg_checker(options)) + Ok(DenoInNpmPackageChecker::new(options)) }) } @@ -559,16 +558,14 @@ impl CliFactory { }) } - pub async fn npm_resolver( - &self, - ) -> Result<&Arc, AnyError> { + pub async fn npm_resolver(&self) -> Result<&CliNpmResolver, AnyError> { self .services .npm_resolver .get_or_try_init_async( async { let cli_options = self.cli_options()?; - Ok(create_cli_npm_resolver(if cli_options.use_byonm() { + Ok(CliNpmResolver::new(if cli_options.use_byonm() { CliNpmResolverCreateOptions::Byonm( CliByonmNpmResolverCreateOptions { sys: self.sys(), @@ -796,11 +793,7 @@ impl CliFactory { Ok(Arc::new(CliNodeResolver::new( self.in_npm_pkg_checker()?.clone(), RealIsBuiltInNodeModuleChecker, - self - .npm_resolver() - .await? - .clone() - .into_npm_pkg_folder_resolver(), + self.npm_resolver().await?.clone(), self.pkg_json_resolver().clone(), self.sys(), node_resolver::ConditionsFromResolutionMode::default(), @@ -833,11 +826,7 @@ impl CliFactory { cjs_esm_analyzer, self.in_npm_pkg_checker()?.clone(), node_resolver, - self - .npm_resolver() - .await? - .clone() - .into_npm_pkg_folder_resolver(), + self.npm_resolver().await?.clone(), self.pkg_json_resolver().clone(), self.sys(), ))) @@ -857,7 +846,7 @@ impl CliFactory { sys: self.sys(), in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(), node_resolver: self.node_resolver().await?.clone(), - npm_resolver: npm_resolver.clone().into_byonm_or_managed(), + npm_resolver: npm_resolver.clone(), }))) }) .await @@ -988,10 +977,10 @@ impl CliFactory { .await } - pub fn cjs_tracker(&self) -> Result<&Arc, AnyError> { + pub fn cjs_tracker(&self) -> Result<&Arc, AnyError> { self.services.cjs_tracker.get_or_try_init(|| { let options = self.cli_options()?; - Ok(Arc::new(CjsTracker::new( + Ok(Arc::new(CliCjsTracker::new( self.in_npm_pkg_checker()?.clone(), self.pkg_json_resolver().clone(), if options.is_node_main() || options.unstable_detect_cjs() { @@ -1040,7 +1029,7 @@ impl CliFactory { self.emitter()?, self.file_fetcher()?, self.http_client_provider(), - self.npm_resolver().await?.as_ref(), + self.npm_resolver().await?, self.workspace_resolver().await?.as_ref(), cli_options.npm_system_info(), )) diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 17d4fef553..e57fcf8a94 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -30,6 +30,7 @@ use deno_graph::ResolutionError; use deno_graph::SpecifierError; use deno_graph::WorkspaceFastCheckOption; use deno_path_util::url_to_file_path; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::sloppy_imports::SloppyImportsResolutionKind; use deno_runtime::deno_node; @@ -37,7 +38,6 @@ use deno_runtime::deno_permissions::PermissionsContainer; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; use deno_semver::SmallStackString; -use node_resolver::InNpmPackageChecker; use crate::args::config_to_deno_graph_workspace_member; use crate::args::jsr_url; @@ -55,7 +55,7 @@ use crate::file_fetcher::CliFileFetcher; use crate::npm::installer::NpmInstaller; use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; -use crate::resolver::CjsTracker; +use crate::resolver::CliCjsTracker; use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliResolver; use crate::resolver::CliSloppyImportsResolver; @@ -493,17 +493,17 @@ pub enum BuildGraphWithNpmResolutionError { pub struct ModuleGraphBuilder { caches: Arc, - cjs_tracker: Arc, + cjs_tracker: Arc, cli_options: Arc, file_fetcher: Arc, global_http_cache: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, npm_graph_resolver: Arc, npm_installer: Option>, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, @@ -514,17 +514,17 @@ impl ModuleGraphBuilder { #[allow(clippy::too_many_arguments)] pub fn new( caches: Arc, - cjs_tracker: Arc, + cjs_tracker: Arc, cli_options: Arc, file_fetcher: Arc, global_http_cache: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, npm_graph_resolver: Arc, npm_installer: Option>, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, root_permissions_container: PermissionsContainer, @@ -712,8 +712,7 @@ impl ModuleGraphBuilder { let initial_package_deps_len = graph.packages.package_deps_sum(); let initial_package_mappings_len = graph.packages.mappings().len(); - if roots.iter().any(|r| r.scheme() == "npm") - && self.npm_resolver.as_byonm().is_some() + if roots.iter().any(|r| r.scheme() == "npm") && self.npm_resolver.is_byonm() { return Err(BuildGraphWithNpmResolutionError::UnsupportedNpmSpecifierEntrypointResolutionWay); } @@ -1226,7 +1225,7 @@ fn format_deno_graph_error(err: &dyn Error) -> String { #[derive(Debug)] struct CliGraphResolver<'a> { - cjs_tracker: &'a CjsTracker, + cjs_tracker: &'a CliCjsTracker, resolver: &'a CliResolver, jsx_import_source_config: Option, } diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index f7b487f055..f8f382f594 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -19,6 +19,7 @@ use deno_core::ModuleSpecifier; use deno_error::JsErrorBox; use deno_lint::diagnostic::LintDiagnosticRange; use deno_path_util::url_to_file_path; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_runtime::deno_node::PathClean; use deno_semver::jsr::JsrPackageNvReference; use deno_semver::jsr::JsrPackageReqReference; @@ -31,6 +32,7 @@ use deno_semver::SmallStackString; use deno_semver::StackString; use deno_semver::Version; use import_map::ImportMap; +use node_resolver::InNpmPackageChecker; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use once_cell::sync::Lazy; @@ -365,7 +367,9 @@ impl<'a> TsResponseImportMapper<'a> { if let Ok(Some(pkg_id)) = npm_resolver.resolve_pkg_id_from_specifier(specifier) { - let pkg_reqs = npm_resolver.resolve_pkg_reqs_from_pkg_id(&pkg_id); + let pkg_reqs = npm_resolver + .resolution() + .resolve_pkg_reqs_from_pkg_id(&pkg_id); // check if any pkg reqs match what is found in an import map if !pkg_reqs.is_empty() { let sub_path = npm_resolver @@ -1295,6 +1299,19 @@ impl CodeActionCollection { range: &lsp::Range, language_server: &language_server::Inner, ) -> Option { + fn top_package_req_for_name( + resolution: &NpmResolutionCell, + name: &str, + ) -> Option { + let package_reqs = resolution.package_reqs(); + let mut entries = package_reqs + .into_iter() + .filter(|(_, nv)| nv.name == name) + .collect::>(); + entries.sort_by(|a, b| a.1.version.cmp(&b.1.version)); + Some(entries.pop()?.0) + } + let (dep_key, dependency, _) = document.get_maybe_dependency(&range.end)?; if dependency.maybe_deno_types_specifier.is_some() { @@ -1382,9 +1399,10 @@ impl CodeActionCollection { .and_then(|versions| versions.first().cloned())?; let types_specifier_text = if let Some(npm_resolver) = managed_npm_resolver { - let mut specifier_text = if let Some(req) = - npm_resolver.top_package_req_for_name(&types_package_name) - { + let mut specifier_text = if let Some(req) = top_package_req_for_name( + npm_resolver.resolution(), + &types_package_name, + ) { format!("npm:{req}") } else { format!("npm:{}@^{}", &types_package_name, types_package_version) diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index af4ab6571f..1b393ad22b 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -26,6 +26,7 @@ use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; use deno_resolver::NodeAndNpmReqResolver; @@ -35,7 +36,6 @@ use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use indexmap::IndexMap; -use node_resolver::InNpmPackageChecker; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; @@ -56,10 +56,10 @@ use crate::lsp::config::ConfigData; use crate::lsp::logging::lsp_warn; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; -use crate::npm::create_cli_npm_resolver; use crate::npm::installer::NpmInstaller; use crate::npm::installer::NpmResolutionInstaller; use crate::npm::CliByonmNpmResolverCreateOptions; +use crate::npm::CliManagedNpmResolver; use crate::npm::CliManagedNpmResolverCreateOptions; use crate::npm::CliNpmCache; use crate::npm::CliNpmCacheHttpClient; @@ -67,14 +67,13 @@ use crate::npm::CliNpmRegistryInfoProvider; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; -use crate::npm::ManagedCliNpmResolver; use crate::npm::NpmResolutionInitializer; use crate::resolver::CliDenoResolver; +use crate::resolver::CliIsCjsResolver; use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; use crate::resolver::FoundPackageJsonDepFlag; -use crate::resolver::IsCjsResolver; use crate::sys::CliSys; use crate::tsc::into_specifier_and_media_type; use crate::util::progress_bar::ProgressBar; @@ -83,12 +82,13 @@ use crate::util::progress_bar::ProgressBarStyle; #[derive(Debug, Clone)] struct LspScopeResolver { resolver: Arc, - in_npm_pkg_checker: Arc, - is_cjs_resolver: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, + is_cjs_resolver: Arc, jsr_resolver: Option>, npm_graph_resolver: Arc, npm_installer: Option>, - npm_resolver: Option>, + npm_resolution: Arc, + npm_resolver: Option, node_resolver: Option>, npm_pkg_req_resolver: Option>, pkg_json_resolver: Arc, @@ -111,6 +111,7 @@ impl Default for LspScopeResolver { npm_installer: None, npm_resolver: None, node_resolver: None, + npm_resolution: factory.services.npm_resolution.clone(), npm_pkg_req_resolver: None, pkg_json_resolver: factory.pkg_json_resolver().clone(), redirect_resolver: None, @@ -224,6 +225,7 @@ impl LspScopeResolver { npm_pkg_req_resolver, npm_resolver, npm_installer, + npm_resolution: factory.services.npm_resolution.clone(), node_resolver, pkg_json_resolver, redirect_resolver, @@ -235,12 +237,58 @@ impl LspScopeResolver { } fn snapshot(&self) -> Arc { + // create a copy of the resolution and then re-initialize the npm resolver from that + // todo(dsherret): this is pretty terrible... we should improve this. It should + // be possible to just change the npm_resolution on the new factory then access + // another method to create a new npm resolver let mut factory = ResolverFactory::new(self.config_data.as_ref()); - let npm_resolver = - self.npm_resolver.as_ref().map(|r| r.clone_snapshotted()); + factory + .services + .npm_resolution + .set_snapshot(self.npm_resolution.snapshot()); + let npm_resolver = self.npm_resolver.as_ref(); if let Some(npm_resolver) = &npm_resolver { - factory.set_npm_resolver(npm_resolver.clone()); + factory.set_npm_resolver(CliNpmResolver::new::( + match npm_resolver { + CliNpmResolver::Byonm(byonm_npm_resolver) => { + CliNpmResolverCreateOptions::Byonm( + CliByonmNpmResolverCreateOptions { + root_node_modules_dir: byonm_npm_resolver + .root_node_modules_path() + .map(|p| p.to_path_buf()), + sys: CliSys::default(), + pkg_json_resolver: self.pkg_json_resolver.clone(), + }, + ) + } + CliNpmResolver::Managed(managed_npm_resolver) => { + CliNpmResolverCreateOptions::Managed({ + let npmrc = self + .config_data + .as_ref() + .and_then(|d| d.npmrc.clone()) + .unwrap_or_else(create_default_npmrc); + let npm_cache_dir = Arc::new(NpmCacheDir::new( + &CliSys::default(), + managed_npm_resolver.global_cache_root_path().to_path_buf(), + npmrc.get_all_known_registries_urls(), + )); + CliManagedNpmResolverCreateOptions { + sys: CliSys::default(), + npm_cache_dir, + maybe_node_modules_path: managed_npm_resolver + .root_node_modules_path() + .map(|p| p.to_path_buf()), + npmrc, + npm_resolution: factory.services.npm_resolution.clone(), + npm_system_info: NpmSystemInfo::default(), + } + }) + } + }, + )); } + Arc::new(Self { resolver: factory.cli_resolver().clone(), in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), @@ -250,6 +298,7 @@ impl LspScopeResolver { // npm installer isn't necessary for a snapshot npm_installer: None, npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(), + npm_resolution: factory.services.npm_resolution.clone(), npm_resolver: factory.npm_resolver().cloned(), node_resolver: factory.node_resolver().cloned(), redirect_resolver: self.redirect_resolver.clone(), @@ -366,7 +415,7 @@ impl LspResolver { pub fn as_is_cjs_resolver( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> &IsCjsResolver { + ) -> &CliIsCjsResolver { let resolver = self.get_scope_resolver(file_referrer); resolver.is_cjs_resolver.as_ref() } @@ -382,7 +431,7 @@ impl LspResolver { pub fn in_npm_pkg_checker( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> &Arc { + ) -> &DenoInNpmPackageChecker { let resolver = self.get_scope_resolver(file_referrer); &resolver.in_npm_pkg_checker } @@ -390,7 +439,7 @@ impl LspResolver { pub fn maybe_managed_npm_resolver( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> Option<&ManagedCliNpmResolver> { + ) -> Option<&CliManagedNpmResolver> { let resolver = self.get_scope_resolver(file_referrer); resolver.npm_resolver.as_ref().and_then(|r| r.as_managed()) } @@ -605,13 +654,14 @@ pub struct ScopeDepInfo { struct ResolverFactoryServices { cli_resolver: Deferred>, found_pkg_json_dep_flag: Arc, - in_npm_pkg_checker: Deferred>, - is_cjs_resolver: Deferred>, + in_npm_pkg_checker: Deferred, + is_cjs_resolver: Deferred>, node_resolver: Deferred>>, npm_graph_resolver: Deferred>, npm_installer: Option>, npm_pkg_req_resolver: Deferred>>, - npm_resolver: Option>, + npm_resolver: Option, + npm_resolution: Arc, } struct ResolverFactory<'a> { @@ -686,10 +736,9 @@ impl<'a> ResolverFactory<'a> { npm_client.clone(), npmrc.clone(), )); - let npm_resolution = Arc::new(NpmResolutionCell::default()); let npm_resolution_initializer = Arc::new(NpmResolutionInitializer::new( registry_info_provider.clone(), - npm_resolution.clone(), + self.services.npm_resolution.clone(), match self.config_data.and_then(|d| d.lockfile.as_ref()) { Some(lockfile) => { CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( @@ -712,13 +761,13 @@ impl<'a> ResolverFactory<'a> { )); let npm_resolution_installer = Arc::new(NpmResolutionInstaller::new( registry_info_provider, - npm_resolution.clone(), + self.services.npm_resolution.clone(), maybe_lockfile.clone(), )); let npm_installer = Arc::new(NpmInstaller::new( npm_cache.clone(), Arc::new(NpmInstallDepsProvider::empty()), - npm_resolution.clone(), + self.services.npm_resolution.clone(), npm_resolution_initializer.clone(), npm_resolution_installer, &pb, @@ -745,22 +794,22 @@ impl<'a> ResolverFactory<'a> { npm_cache_dir, maybe_node_modules_path, npmrc, - npm_resolution, + npm_resolution: self.services.npm_resolution.clone(), npm_system_info: NpmSystemInfo::default(), }) }; - self.set_npm_resolver(create_cli_npm_resolver(options)); + self.set_npm_resolver(CliNpmResolver::new(options)); } pub fn set_npm_installer(&mut self, npm_installer: Arc) { self.services.npm_installer = Some(npm_installer); } - pub fn set_npm_resolver(&mut self, npm_resolver: Arc) { + pub fn set_npm_resolver(&mut self, npm_resolver: CliNpmResolver) { self.services.npm_resolver = Some(npm_resolver); } - pub fn npm_resolver(&self) -> Option<&Arc> { + pub fn npm_resolver(&self) -> Option<&CliNpmResolver> { self.services.npm_resolver.as_ref() } @@ -825,29 +874,27 @@ impl<'a> ResolverFactory<'a> { &self.pkg_json_resolver } - pub fn in_npm_pkg_checker(&self) -> &Arc { + pub fn in_npm_pkg_checker(&self) -> &DenoInNpmPackageChecker { self.services.in_npm_pkg_checker.get_or_init(|| { - deno_resolver::npm::create_in_npm_pkg_checker( - match self.services.npm_resolver.as_ref().map(|r| r.as_inner()) { - Some(crate::npm::InnerCliNpmResolverRef::Byonm(_)) | None => { - CreateInNpmPkgCheckerOptions::Byonm - } - Some(crate::npm::InnerCliNpmResolverRef::Managed(m)) => { - CreateInNpmPkgCheckerOptions::Managed( - ManagedInNpmPkgCheckerCreateOptions { - root_cache_dir_url: m.global_cache_root_url(), - maybe_node_modules_path: m.maybe_node_modules_path(), - }, - ) - } - }, - ) + DenoInNpmPackageChecker::new(match &self.services.npm_resolver { + Some(CliNpmResolver::Byonm(_)) | None => { + CreateInNpmPkgCheckerOptions::Byonm + } + Some(CliNpmResolver::Managed(m)) => { + CreateInNpmPkgCheckerOptions::Managed( + ManagedInNpmPkgCheckerCreateOptions { + root_cache_dir_url: m.global_cache_root_url(), + maybe_node_modules_path: m.root_node_modules_path(), + }, + ) + } + }) }) } - pub fn is_cjs_resolver(&self) -> &Arc { + pub fn is_cjs_resolver(&self) -> &Arc { self.services.is_cjs_resolver.get_or_init(|| { - Arc::new(IsCjsResolver::new( + Arc::new(CliIsCjsResolver::new( self.in_npm_pkg_checker().clone(), self.pkg_json_resolver().clone(), if self @@ -871,7 +918,7 @@ impl<'a> ResolverFactory<'a> { Some(Arc::new(CliNodeResolver::new( self.in_npm_pkg_checker().clone(), RealIsBuiltInNodeModuleChecker, - npm_resolver.clone().into_npm_pkg_folder_resolver(), + npm_resolver.clone(), self.pkg_json_resolver.clone(), self.sys.clone(), node_resolver::ConditionsFromResolutionMode::default(), @@ -890,7 +937,7 @@ impl<'a> ResolverFactory<'a> { Some(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { in_npm_pkg_checker: self.in_npm_pkg_checker().clone(), node_resolver: node_resolver.clone(), - npm_resolver: npm_resolver.clone().into_byonm_or_managed(), + npm_resolver: npm_resolver.clone(), sys: self.sys.clone(), }))) }) diff --git a/cli/module_loader.rs b/cli/module_loader.rs index ba53077a3c..daeb4dda37 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -39,6 +39,7 @@ use deno_graph::ModuleGraph; use deno_graph::ModuleGraphError; use deno_graph::Resolution; use deno_graph::WasmModule; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::code_cache; use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::NodeRequireLoader; @@ -70,7 +71,7 @@ use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; use crate::npm::NpmRegistryReadPermissionChecker; -use crate::resolver::CjsTracker; +use crate::resolver::CliCjsTracker; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; use crate::resolver::ModuleCodeStringSource; @@ -233,10 +234,10 @@ struct SharedCliModuleLoaderState { initial_cwd: PathBuf, is_inspecting: bool, is_repl: bool, - cjs_tracker: Arc, + cjs_tracker: Arc, code_cache: Option>, emitter: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, main_module_graph_container: Arc, module_load_preparer: Arc, node_code_translator: Arc, @@ -244,7 +245,7 @@ struct SharedCliModuleLoaderState { npm_module_loader: NpmModuleLoader, npm_registry_permission_checker: Arc, npm_req_resolver: Arc, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, sys: CliSys, @@ -294,10 +295,10 @@ impl CliModuleLoaderFactory { #[allow(clippy::too_many_arguments)] pub fn new( options: &CliOptions, - cjs_tracker: Arc, + cjs_tracker: Arc, code_cache: Option>, emitter: Arc, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, main_module_graph_container: Arc, module_load_preparer: Arc, node_code_translator: Arc, @@ -305,7 +306,7 @@ impl CliModuleLoaderFactory { npm_module_loader: NpmModuleLoader, npm_registry_permission_checker: Arc, npm_req_resolver: Arc, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, parsed_source_cache: Arc, resolver: Arc, sys: CliSys, @@ -1139,11 +1140,11 @@ impl ModuleGraphUpdatePermit for WorkerModuleGraphUpdatePermit { #[derive(Debug)] struct CliNodeRequireLoader { - cjs_tracker: Arc, + cjs_tracker: Arc, emitter: Arc, sys: CliSys, graph_container: TGraphContainer, - in_npm_pkg_checker: Arc, + in_npm_pkg_checker: DenoInNpmPackageChecker, npm_registry_permission_checker: Arc, } diff --git a/cli/node.rs b/cli/node.rs index aa44dcab18..892e25914a 100644 --- a/cli/node.rs +++ b/cli/node.rs @@ -7,6 +7,7 @@ use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; use deno_graph::ParsedSourceStore; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::deno_fs; use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis; @@ -19,15 +20,22 @@ use serde::Serialize; use crate::cache::CacheDBHash; use crate::cache::NodeAnalysisCache; use crate::cache::ParsedSourceCache; -use crate::resolver::CjsTracker; +use crate::npm::CliNpmResolver; +use crate::resolver::CliCjsTracker; use crate::sys::CliSys; pub type CliNodeCodeTranslator = NodeCodeTranslator< CliCjsCodeAnalyzer, + DenoInNpmPackageChecker, RealIsBuiltInNodeModuleChecker, + CliNpmResolver, + CliSys, +>; +pub type CliNodeResolver = deno_runtime::deno_node::NodeResolver< + DenoInNpmPackageChecker, + CliNpmResolver, CliSys, >; -pub type CliNodeResolver = deno_runtime::deno_node::NodeResolver; pub type CliPackageJsonResolver = node_resolver::PackageJsonResolver; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -43,7 +51,7 @@ pub enum CliCjsAnalysis { pub struct CliCjsCodeAnalyzer { cache: NodeAnalysisCache, - cjs_tracker: Arc, + cjs_tracker: Arc, fs: deno_fs::FileSystemRc, parsed_source_cache: Option>, } @@ -51,7 +59,7 @@ pub struct CliCjsCodeAnalyzer { impl CliCjsCodeAnalyzer { pub fn new( cache: NodeAnalysisCache, - cjs_tracker: Arc, + cjs_tracker: Arc, fs: deno_fs::FileSystemRc, parsed_source_cache: Option>, ) -> Self { diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index bd29a6ec72..8dc498bb04 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -1,21 +1,12 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::path::Path; -use std::path::PathBuf; use std::sync::Arc; use deno_core::serde_json; -use deno_core::url::Url; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmNpmResolverCreateOptions; -use deno_resolver::npm::ByonmOrManagedNpmResolver; -use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_runtime::ops::process::NpmProcessStateProvider; -use deno_semver::package::PackageReq; -use node_resolver::NpmPackageFolderResolver; -use super::CliNpmResolver; -use super::InnerCliNpmResolverRef; use crate::args::NpmProcessState; use crate::args::NpmProcessStateKind; use crate::sys::CliSys; @@ -24,67 +15,18 @@ pub type CliByonmNpmResolverCreateOptions = ByonmNpmResolverCreateOptions; pub type CliByonmNpmResolver = ByonmNpmResolver; -// todo(dsherret): the services hanging off `CliNpmResolver` doesn't seem ideal. We should probably decouple. #[derive(Debug)] -struct CliByonmWrapper(Arc); +pub struct CliByonmNpmProcessStateProvider(pub Arc); -impl NpmProcessStateProvider for CliByonmWrapper { +impl NpmProcessStateProvider for CliByonmNpmProcessStateProvider { fn get_npm_process_state(&self) -> String { serde_json::to_string(&NpmProcessState { kind: NpmProcessStateKind::Byonm, local_node_modules_path: self .0 - .root_node_modules_dir() + .root_node_modules_path() .map(|p| p.to_string_lossy().to_string()), }) .unwrap() } } - -impl CliNpmResolver for CliByonmNpmResolver { - fn into_npm_pkg_folder_resolver( - self: Arc, - ) -> Arc { - self - } - - fn into_process_state_provider( - self: Arc, - ) -> Arc { - Arc::new(CliByonmWrapper(self)) - } - - fn into_byonm_or_managed( - self: Arc, - ) -> ByonmOrManagedNpmResolver { - ByonmOrManagedNpmResolver::Byonm(self) - } - - fn clone_snapshotted(&self) -> Arc { - Arc::new(self.clone()) - } - - fn as_inner(&self) -> InnerCliNpmResolverRef { - InnerCliNpmResolverRef::Byonm(self) - } - - fn root_node_modules_path(&self) -> Option<&Path> { - self.root_node_modules_dir() - } - - fn check_state_hash(&self) -> Option { - // it is very difficult to determine the check state hash for byonm - // so we just return None to signify check caching is not supported - None - } - - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - referrer: &Url, - ) -> Result { - self - .resolve_pkg_folder_from_deno_module_req(req, referrer) - .map_err(ResolvePkgFolderFromDenoReqError::Byonm) - } -} diff --git a/cli/npm/managed.rs b/cli/npm/managed.rs index 0d4c209acc..4122c881f1 100644 --- a/cli/npm/managed.rs +++ b/cli/npm/managed.rs @@ -4,44 +4,28 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use deno_ast::ModuleSpecifier; -use deno_cache_dir::npm::NpmCacheDir; use deno_core::parking_lot::Mutex; use deno_core::serde_json; -use deno_core::url::Url; use deno_error::JsError; use deno_error::JsErrorBox; -use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmRegistryApi; use deno_npm::resolution::NpmResolutionSnapshot; -use deno_npm::resolution::PackageReqNotFoundError; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; -use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_npm::NpmSystemInfo; +use deno_resolver::npm::managed::ManagedNpmResolverCreateOptions; use deno_resolver::npm::managed::NpmResolutionCell; -use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError; -use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError; -use deno_resolver::npm::managed::ResolvePkgIdFromSpecifierError; -use deno_resolver::npm::ByonmOrManagedNpmResolver; -use deno_resolver::npm::ManagedNpmResolver; -use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; +use deno_resolver::npm::ManagedNpmResolverRc; use deno_runtime::ops::process::NpmProcessStateProvider; -use deno_semver::package::PackageNv; -use deno_semver::package::PackageReq; -use node_resolver::NpmPackageFolderResolver; -use sys_traits::FsMetadata; use thiserror::Error; use super::CliNpmRegistryInfoProvider; -use super::CliNpmResolver; -use super::InnerCliNpmResolverRef; use crate::args::CliLockfile; use crate::args::NpmProcessState; use crate::args::NpmProcessStateKind; -use crate::cache::FastInsecureHasher; use crate::sys::CliSys; +pub type CliManagedNpmResolverCreateOptions = + ManagedNpmResolverCreateOptions; + #[derive(Debug, Clone)] pub enum CliNpmResolverManagedSnapshotOption { ResolveFromLockfile(Arc), @@ -139,36 +123,6 @@ impl NpmResolutionInitializer { } } -pub struct CliManagedNpmResolverCreateOptions { - pub npm_cache_dir: Arc, - pub sys: CliSys, - pub maybe_node_modules_path: Option, - pub npm_system_info: NpmSystemInfo, - pub npmrc: Arc, - pub npm_resolution: Arc, -} - -pub fn create_managed_npm_resolver( - options: CliManagedNpmResolverCreateOptions, -) -> Arc { - let managed_npm_resolver = - Arc::new(ManagedNpmResolver::::new::( - &options.npm_cache_dir, - &options.npmrc, - options.npm_resolution.clone(), - options.sys.clone(), - options.maybe_node_modules_path, - )); - Arc::new(ManagedCliNpmResolver::new( - managed_npm_resolver, - options.npm_cache_dir, - options.npmrc, - options.npm_resolution, - options.sys, - options.npm_system_info, - )) -} - #[derive(Debug, Error, Clone, JsError)] #[error("failed reading lockfile '{}'", lockfile_path.display())] #[class(inherit)] @@ -254,145 +208,6 @@ async fn snapshot_from_lockfile( Ok(snapshot) } -/// An npm resolver where the resolution is managed by Deno rather than -/// the user bringing their own node_modules (BYONM) on the file system. -pub struct ManagedCliNpmResolver { - managed_npm_resolver: Arc>, - npm_cache_dir: Arc, - npm_rc: Arc, - sys: CliSys, - resolution: Arc, - system_info: NpmSystemInfo, -} - -impl std::fmt::Debug for ManagedCliNpmResolver { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ManagedCliNpmResolver") - .field("", &"") - .finish() - } -} - -impl ManagedCliNpmResolver { - #[allow(clippy::too_many_arguments)] - pub fn new( - managed_npm_resolver: Arc>, - npm_cache_dir: Arc, - npm_rc: Arc, - resolution: Arc, - sys: CliSys, - system_info: NpmSystemInfo, - ) -> Self { - Self { - managed_npm_resolver, - npm_cache_dir, - npm_rc, - resolution, - sys, - system_info, - } - } - - pub fn resolve_pkg_folder_from_pkg_id( - &self, - pkg_id: &NpmPackageId, - ) -> Result { - self - .managed_npm_resolver - .resolve_pkg_folder_from_pkg_id(pkg_id) - } - - /// Resolves the package id from the provided specifier. - pub fn resolve_pkg_id_from_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result, ResolvePkgIdFromSpecifierError> { - self - .managed_npm_resolver - .resolve_pkg_id_from_specifier(specifier) - } - - pub fn resolve_pkg_reqs_from_pkg_id( - &self, - id: &NpmPackageId, - ) -> Vec { - self.resolution.resolve_pkg_reqs_from_pkg_id(id) - } - - pub fn all_system_packages( - &self, - system_info: &NpmSystemInfo, - ) -> Vec { - self.resolution.all_system_packages(system_info) - } - - /// Checks if the provided package req's folder is cached. - pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool { - self - .resolve_pkg_id_from_pkg_req(req) - .ok() - .and_then(|id| { - self - .managed_npm_resolver - .resolve_pkg_folder_from_pkg_id(&id) - .ok() - }) - .map(|folder| self.sys.fs_exists_no_err(folder)) - .unwrap_or(false) - } - - pub fn snapshot(&self) -> NpmResolutionSnapshot { - self.resolution.snapshot() - } - - pub fn top_package_req_for_name(&self, name: &str) -> Option { - let package_reqs = self.resolution.package_reqs(); - let mut entries = package_reqs - .iter() - .filter(|(_, nv)| nv.name == name) - .collect::>(); - entries.sort_by_key(|(_, nv)| &nv.version); - Some(entries.last()?.0.clone()) - } - - pub fn serialized_valid_snapshot_for_system( - &self, - system_info: &NpmSystemInfo, - ) -> ValidSerializedNpmResolutionSnapshot { - self - .resolution - .serialized_valid_snapshot_for_system(system_info) - } - - pub fn resolve_pkg_folder_from_deno_module( - &self, - nv: &PackageNv, - ) -> Result { - self - .managed_npm_resolver - .resolve_pkg_folder_from_deno_module(nv) - } - - pub fn resolve_pkg_id_from_pkg_req( - &self, - req: &PackageReq, - ) -> Result { - self.resolution.resolve_pkg_id_from_pkg_req(req) - } - - pub fn maybe_node_modules_path(&self) -> Option<&Path> { - self.managed_npm_resolver.node_modules_path() - } - - pub fn global_cache_root_path(&self) -> &Path { - self.npm_cache_dir.root_dir() - } - - pub fn global_cache_root_url(&self) -> &Url { - self.npm_cache_dir.root_dir_url() - } -} - pub fn npm_process_state( snapshot: ValidSerializedNpmResolutionSnapshot, node_modules_path: Option<&Path>, @@ -405,92 +220,14 @@ pub fn npm_process_state( .unwrap() } -impl NpmProcessStateProvider for ManagedCliNpmResolver { +#[derive(Debug)] +pub struct CliManagedNpmProcessStateProvider(pub ManagedNpmResolverRc); + +impl NpmProcessStateProvider for CliManagedNpmProcessStateProvider { fn get_npm_process_state(&self) -> String { npm_process_state( - self.resolution.serialized_valid_snapshot(), - self.managed_npm_resolver.node_modules_path(), + self.0.resolution().serialized_valid_snapshot(), + self.0.root_node_modules_path(), ) } } - -impl CliNpmResolver for ManagedCliNpmResolver { - fn into_npm_pkg_folder_resolver( - self: Arc, - ) -> Arc { - self.managed_npm_resolver.clone() - } - - fn into_process_state_provider( - self: Arc, - ) -> Arc { - self - } - - fn into_byonm_or_managed( - self: Arc, - ) -> ByonmOrManagedNpmResolver { - ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone()) - } - - fn clone_snapshotted(&self) -> Arc { - // create a new snapshotted npm resolution and resolver - let npm_resolution = - Arc::new(NpmResolutionCell::new(self.resolution.snapshot())); - - Arc::new(ManagedCliNpmResolver::new( - Arc::new(ManagedNpmResolver::::new::( - &self.npm_cache_dir, - &self.npm_rc, - npm_resolution.clone(), - self.sys.clone(), - self.root_node_modules_path().map(ToOwned::to_owned), - )), - self.npm_cache_dir.clone(), - self.npm_rc.clone(), - npm_resolution, - self.sys.clone(), - self.system_info.clone(), - )) - } - - fn as_inner(&self) -> InnerCliNpmResolverRef { - InnerCliNpmResolverRef::Managed(self) - } - - fn root_node_modules_path(&self) -> Option<&Path> { - self.managed_npm_resolver.node_modules_path() - } - - fn check_state_hash(&self) -> Option { - // We could go further and check all the individual - // npm packages, but that's probably overkill. - let mut package_reqs = self - .resolution - .package_reqs() - .into_iter() - .collect::>(); - package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism - let mut hasher = FastInsecureHasher::new_without_deno_version(); - // ensure the cache gets busted when turning nodeModulesDir on or off - // as this could cause changes in resolution - hasher - .write_hashable(self.managed_npm_resolver.node_modules_path().is_some()); - for (pkg_req, pkg_nv) in package_reqs { - hasher.write_hashable(&pkg_req); - hasher.write_hashable(&pkg_nv); - } - Some(hasher.finish()) - } - - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - referrer: &Url, - ) -> Result { - self - .managed_npm_resolver - .resolve_pkg_folder_from_deno_module_req(req, referrer) - .map_err(ResolvePkgFolderFromDenoReqError::Managed) - } -} diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 6ad7ad610e..1c12ce6c59 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -5,8 +5,6 @@ pub mod installer; mod managed; mod permission_checker; -use std::path::Path; -use std::path::PathBuf; use std::sync::Arc; use dashmap::DashMap; @@ -15,21 +13,15 @@ use deno_core::url::Url; use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; -use deno_resolver::npm::ByonmNpmResolver; -use deno_resolver::npm::ByonmOrManagedNpmResolver; -use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::ops::process::NpmProcessStateProvider; +use deno_runtime::ops::process::NpmProcessStateProviderRc; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use http::HeaderName; use http::HeaderValue; -use node_resolver::NpmPackageFolderResolver; -pub use self::byonm::CliByonmNpmResolver; pub use self::byonm::CliByonmNpmResolverCreateOptions; pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliNpmResolverManagedSnapshotOption; -pub use self::managed::ManagedCliNpmResolver; pub use self::managed::NpmResolutionInitializer; pub use self::managed::ResolveSnapshotError; pub use self::permission_checker::NpmRegistryReadPermissionChecker; @@ -44,6 +36,10 @@ pub type CliNpmTarballCache = pub type CliNpmCache = deno_npm_cache::NpmCache; pub type CliNpmRegistryInfoProvider = deno_npm_cache::RegistryInfoProvider; +pub type CliNpmResolver = deno_resolver::npm::NpmResolver; +pub type CliManagedNpmResolver = deno_resolver::npm::ManagedNpmResolver; +pub type CliNpmResolverCreateOptions = + deno_resolver::npm::NpmResolverCreateOptions; #[derive(Debug)] pub struct CliNpmCacheHttpClient { @@ -63,6 +59,19 @@ impl CliNpmCacheHttpClient { } } +pub fn create_npm_process_state_provider( + npm_resolver: &CliNpmResolver, +) -> NpmProcessStateProviderRc { + match npm_resolver { + CliNpmResolver::Byonm(byonm_npm_resolver) => Arc::new( + byonm::CliByonmNpmProcessStateProvider(byonm_npm_resolver.clone()), + ), + CliNpmResolver::Managed(managed_npm_resolver) => Arc::new( + managed::CliManagedNpmProcessStateProvider(managed_npm_resolver.clone()), + ), + } +} + #[async_trait::async_trait(?Send)] impl deno_npm_cache::NpmCacheHttpClient for CliNpmCacheHttpClient { async fn download_with_retries_on_any_tokio_runtime( @@ -104,70 +113,6 @@ impl deno_npm_cache::NpmCacheHttpClient for CliNpmCacheHttpClient { } } -pub enum CliNpmResolverCreateOptions { - Managed(CliManagedNpmResolverCreateOptions), - Byonm(CliByonmNpmResolverCreateOptions), -} - -pub fn create_cli_npm_resolver( - options: CliNpmResolverCreateOptions, -) -> Arc { - use CliNpmResolverCreateOptions::*; - match options { - Managed(options) => managed::create_managed_npm_resolver(options), - Byonm(options) => Arc::new(ByonmNpmResolver::new(options)), - } -} - -pub enum InnerCliNpmResolverRef<'a> { - Managed(&'a ManagedCliNpmResolver), - #[allow(dead_code)] - Byonm(&'a CliByonmNpmResolver), -} - -// todo(dsherret): replace with an enum -pub trait CliNpmResolver: Send + Sync + std::fmt::Debug { - fn into_npm_pkg_folder_resolver( - self: Arc, - ) -> Arc; - fn into_process_state_provider( - self: Arc, - ) -> Arc; - fn into_byonm_or_managed( - self: Arc, - ) -> ByonmOrManagedNpmResolver; - - fn clone_snapshotted(&self) -> Arc; - - fn as_inner(&self) -> InnerCliNpmResolverRef; - - fn as_managed(&self) -> Option<&ManagedCliNpmResolver> { - match self.as_inner() { - InnerCliNpmResolverRef::Managed(inner) => Some(inner), - InnerCliNpmResolverRef::Byonm(_) => None, - } - } - - fn as_byonm(&self) -> Option<&CliByonmNpmResolver> { - match self.as_inner() { - InnerCliNpmResolverRef::Managed(_) => None, - InnerCliNpmResolverRef::Byonm(inner) => Some(inner), - } - } - - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - referrer: &Url, - ) -> Result; - - fn root_node_modules_path(&self) -> Option<&Path>; - - /// Returns a hash returning the state of the npm resolver - /// or `None` if the state currently can't be determined. - fn check_state_hash(&self) -> Option; -} - #[derive(Debug)] pub struct NpmFetchResolver { nv_by_req: DashMap>, diff --git a/cli/resolver.rs b/cli/resolver.rs index 2f3d42e9e1..5677767fdd 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -19,6 +19,7 @@ use deno_graph::source::UnknownBuiltInNodeModuleError; use deno_graph::NpmLoadError; use deno_graph::NpmResolvePkgReqsResult; use deno_npm::resolution::NpmResolutionError; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::sloppy_imports::SloppyImportsResolver; use deno_runtime::colors; @@ -35,22 +36,31 @@ use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::node::CliNodeCodeTranslator; use crate::npm::installer::NpmInstaller; use crate::npm::installer::PackageCaching; +use crate::npm::CliNpmResolver; use crate::sys::CliSys; use crate::util::sync::AtomicFlag; use crate::util::text_encoding::from_utf8_lossy_cow; -pub type CjsTracker = deno_resolver::cjs::CjsTracker; -pub type IsCjsResolver = deno_resolver::cjs::IsCjsResolver; +pub type CliCjsTracker = + deno_resolver::cjs::CjsTracker; +pub type CliIsCjsResolver = + deno_resolver::cjs::IsCjsResolver; pub type CliSloppyImportsCachedFs = SloppyImportsCachedFs; pub type CliSloppyImportsResolver = SloppyImportsResolver; pub type CliDenoResolver = deno_resolver::DenoResolver< + DenoInNpmPackageChecker, RealIsBuiltInNodeModuleChecker, + CliNpmResolver, CliSloppyImportsCachedFs, CliSys, >; -pub type CliNpmReqResolver = - deno_resolver::npm::NpmReqResolver; +pub type CliNpmReqResolver = deno_resolver::npm::NpmReqResolver< + DenoInNpmPackageChecker, + RealIsBuiltInNodeModuleChecker, + CliNpmResolver, + CliSys, +>; pub struct ModuleCodeStringSource { pub code: ModuleSourceCode, @@ -69,14 +79,14 @@ pub struct NotSupportedKindInNpmError { // todo(dsherret): move to module_loader.rs (it seems to be here due to use in standalone) #[derive(Clone)] pub struct NpmModuleLoader { - cjs_tracker: Arc, + cjs_tracker: Arc, fs: Arc, node_code_translator: Arc, } impl NpmModuleLoader { pub fn new( - cjs_tracker: Arc, + cjs_tracker: Arc, fs: Arc, node_code_translator: Arc, ) -> Self { diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index ff0343e27f..c9b57f3d6b 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -92,8 +92,7 @@ use crate::emit::Emitter; use crate::file_fetcher::CliFileFetcher; use crate::http_util::HttpClientProvider; use crate::npm::CliNpmResolver; -use crate::npm::InnerCliNpmResolverRef; -use crate::resolver::CjsTracker; +use crate::resolver::CliCjsTracker; use crate::shared::ReleaseChannel; use crate::standalone::virtual_fs::VfsEntry; use crate::util::archive; @@ -410,13 +409,13 @@ pub struct WriteBinOptions<'a> { } pub struct DenoCompileBinaryWriter<'a> { - cjs_tracker: &'a CjsTracker, + cjs_tracker: &'a CliCjsTracker, cli_options: &'a CliOptions, deno_dir: &'a DenoDir, emitter: &'a Emitter, file_fetcher: &'a CliFileFetcher, http_client_provider: &'a HttpClientProvider, - npm_resolver: &'a dyn CliNpmResolver, + npm_resolver: &'a CliNpmResolver, workspace_resolver: &'a WorkspaceResolver, npm_system_info: NpmSystemInfo, } @@ -424,13 +423,13 @@ pub struct DenoCompileBinaryWriter<'a> { impl<'a> DenoCompileBinaryWriter<'a> { #[allow(clippy::too_many_arguments)] pub fn new( - cjs_tracker: &'a CjsTracker, + cjs_tracker: &'a CliCjsTracker, cli_options: &'a CliOptions, deno_dir: &'a DenoDir, emitter: &'a Emitter, file_fetcher: &'a CliFileFetcher, http_client_provider: &'a HttpClientProvider, - npm_resolver: &'a dyn CliNpmResolver, + npm_resolver: &'a CliNpmResolver, workspace_resolver: &'a WorkspaceResolver, npm_system_info: NpmSystemInfo, ) -> Self { @@ -599,10 +598,11 @@ impl<'a> DenoCompileBinaryWriter<'a> { None => None, }; let mut vfs = VfsBuilder::new(); - let npm_snapshot = match self.npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(managed) => { - let snapshot = - managed.serialized_valid_snapshot_for_system(&self.npm_system_info); + let npm_snapshot = match &self.npm_resolver { + CliNpmResolver::Managed(managed) => { + let snapshot = managed + .resolution() + .serialized_valid_snapshot_for_system(&self.npm_system_info); if !snapshot.as_serialized().packages.is_empty() { self.fill_npm_vfs(&mut vfs).context("Building npm vfs.")?; Some(snapshot) @@ -610,7 +610,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { None } } - InnerCliNpmResolverRef::Byonm(_) => { + CliNpmResolver::Byonm(_) => { self.fill_npm_vfs(&mut vfs)?; None } @@ -751,8 +751,8 @@ impl<'a> DenoCompileBinaryWriter<'a> { ); } - let node_modules = match self.npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(_) => { + let node_modules = match &self.npm_resolver { + CliNpmResolver::Managed(_) => { npm_snapshot.as_ref().map(|_| NodeModules::Managed { node_modules_dir: self.npm_resolver.root_node_modules_path().map( |path| { @@ -765,7 +765,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { ), }) } - InnerCliNpmResolverRef::Byonm(resolver) => Some(NodeModules::Byonm { + CliNpmResolver::Byonm(resolver) => Some(NodeModules::Byonm { root_node_modules_dir: resolver.root_node_modules_path().map( |node_modules_dir| { root_dir_url @@ -880,16 +880,17 @@ impl<'a> DenoCompileBinaryWriter<'a> { } } - match self.npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(npm_resolver) => { + match &self.npm_resolver { + CliNpmResolver::Managed(npm_resolver) => { if let Some(node_modules_path) = npm_resolver.root_node_modules_path() { maybe_warn_different_system(&self.npm_system_info); builder.add_dir_recursive(node_modules_path)?; Ok(()) } else { // we'll flatten to remove any custom registries later - let mut packages = - npm_resolver.all_system_packages(&self.npm_system_info); + let mut packages = npm_resolver + .resolution() + .all_system_packages(&self.npm_system_info); packages.sort_by(|a, b| a.id.cmp(&b.id)); // determinism for package in packages { let folder = @@ -899,7 +900,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { Ok(()) } } - InnerCliNpmResolverRef::Byonm(_) => { + CliNpmResolver::Byonm(_) => { maybe_warn_different_system(&self.npm_system_info); for pkg_json in self.cli_options.workspace().package_jsons() { builder.add_file_at_path(&pkg_json.path)?; @@ -942,8 +943,8 @@ impl<'a> DenoCompileBinaryWriter<'a> { &self, mut vfs: VfsBuilder, ) -> BuiltVfs { - match self.npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(npm_resolver) => { + match &self.npm_resolver { + CliNpmResolver::Managed(npm_resolver) => { if npm_resolver.root_node_modules_path().is_some() { return vfs.build(); } @@ -1035,7 +1036,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { .insert(npm_global_cache_dir_entry, case_sensitivity); built_vfs } - InnerCliNpmResolverRef::Byonm(_) => vfs.build(), + CliNpmResolver::Byonm(_) => vfs.build(), } } } diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 4ca82f6b7d..876c194ed1 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -40,10 +40,11 @@ use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::resolution::NpmResolutionSnapshot; use deno_package_json::PackageJsonDepValue; use deno_resolver::cjs::IsCjsResolutionMode; -use deno_resolver::npm::create_in_npm_pkg_checker; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; use deno_resolver::npm::managed::NpmResolutionCell; +use deno_resolver::npm::ByonmNpmResolverCreateOptions; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::npm::NpmReqResolverOptions; use deno_runtime::deno_fs; use deno_runtime::deno_fs::FileSystem; @@ -85,7 +86,6 @@ use crate::node::CliCjsCodeAnalyzer; use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; -use crate::npm::create_cli_npm_resolver; use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; use crate::npm::CliNpmResolver; @@ -94,7 +94,7 @@ use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionCheckerMode; use crate::npm::NpmResolutionInitializer; -use crate::resolver::CjsTracker; +use crate::resolver::CliCjsTracker; use crate::resolver::CliNpmReqResolver; use crate::resolver::NpmModuleLoader; use crate::sys::CliSys; @@ -122,7 +122,7 @@ use self::binary::Metadata; pub use self::file_system::DenoCompileFileSystem; struct SharedModuleLoaderState { - cjs_tracker: Arc, + cjs_tracker: Arc, code_cache: Option>, fs: Arc, modules: StandaloneModules, @@ -131,7 +131,7 @@ struct SharedModuleLoaderState { npm_module_loader: Arc, npm_registry_permission_checker: NpmRegistryReadPermissionChecker, npm_req_resolver: Arc, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, source_maps: SourceMapStore, vfs: Arc, workspace_resolver: WorkspaceResolver, @@ -734,7 +734,7 @@ pub async fn run( let maybe_node_modules_path = node_modules_dir .map(|node_modules_dir| root_path.join(node_modules_dir)); let in_npm_pkg_checker = - create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed( + DenoInNpmPackageChecker::new(CreateInNpmPkgCheckerOptions::Managed( ManagedInNpmPkgCheckerCreateOptions { root_cache_dir_url: npm_cache_dir.root_dir_url(), maybe_node_modules_path: maybe_node_modules_path.as_deref(), @@ -743,7 +743,7 @@ pub async fn run( let npm_resolution = Arc::new(NpmResolutionCell::new(NpmResolutionSnapshot::new(snapshot))); let npm_resolver = - create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( + CliNpmResolver::new(CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { npm_resolution, npm_cache_dir, @@ -761,8 +761,8 @@ pub async fn run( let root_node_modules_dir = root_node_modules_dir.map(|p| vfs.root().join(p)); let in_npm_pkg_checker = - create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Byonm); - let npm_resolver = create_cli_npm_resolver( + DenoInNpmPackageChecker::new(CreateInNpmPkgCheckerOptions::Byonm); + let npm_resolver = CliNpmResolver::new( CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { sys: sys.clone(), pkg_json_resolver: pkg_json_resolver.clone(), @@ -781,7 +781,7 @@ pub async fn run( npmrc.get_all_known_registries_urls(), )); let in_npm_pkg_checker = - create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed( + DenoInNpmPackageChecker::new(CreateInNpmPkgCheckerOptions::Managed( ManagedInNpmPkgCheckerCreateOptions { root_cache_dir_url: npm_cache_dir.root_dir_url(), maybe_node_modules_path: None, @@ -789,7 +789,7 @@ pub async fn run( )); let npm_resolution = Arc::new(NpmResolutionCell::default()); let npm_resolver = - create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( + CliNpmResolver::new(CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { npm_resolution, sys: sys.clone(), @@ -807,12 +807,12 @@ pub async fn run( let node_resolver = Arc::new(NodeResolver::new( in_npm_pkg_checker.clone(), RealIsBuiltInNodeModuleChecker, - npm_resolver.clone().into_npm_pkg_folder_resolver(), + npm_resolver.clone(), pkg_json_resolver.clone(), sys.clone(), node_resolver::ConditionsFromResolutionMode::default(), )); - let cjs_tracker = Arc::new(CjsTracker::new( + let cjs_tracker = Arc::new(CliCjsTracker::new( in_npm_pkg_checker.clone(), pkg_json_resolver.clone(), if metadata.unstable_config.detect_cjs { @@ -830,7 +830,7 @@ pub async fn run( sys: sys.clone(), in_npm_pkg_checker: in_npm_pkg_checker.clone(), node_resolver: node_resolver.clone(), - npm_resolver: npm_resolver.clone().into_byonm_or_managed(), + npm_resolver: npm_resolver.clone(), })); let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new( node_analysis_cache, @@ -842,7 +842,7 @@ pub async fn run( cjs_esm_code_analyzer, in_npm_pkg_checker, node_resolver.clone(), - npm_resolver.clone().into_npm_pkg_folder_resolver(), + npm_resolver.clone(), pkg_json_resolver.clone(), sys.clone(), )); diff --git a/cli/task_runner.rs b/cli/task_runner.rs index 8510a650e7..50dcd64dc7 100644 --- a/cli/task_runner.rs +++ b/cli/task_runner.rs @@ -25,9 +25,8 @@ use tokio::task::LocalSet; use tokio_util::sync::CancellationToken; use crate::node::CliNodeResolver; +use crate::npm::CliManagedNpmResolver; use crate::npm::CliNpmResolver; -use crate::npm::InnerCliNpmResolverRef; -use crate::npm::ManagedCliNpmResolver; pub fn get_script_with_args(script: &str, argv: &[String]) -> String { let additional_args = argv @@ -414,15 +413,15 @@ impl ShellCommand for NodeModulesFileRunCommand { } pub fn resolve_custom_commands( - npm_resolver: &dyn CliNpmResolver, + npm_resolver: &CliNpmResolver, node_resolver: &CliNodeResolver, ) -> Result>, AnyError> { - let mut commands = match npm_resolver.as_inner() { - InnerCliNpmResolverRef::Byonm(npm_resolver) => { + let mut commands = match npm_resolver { + CliNpmResolver::Byonm(npm_resolver) => { let node_modules_dir = npm_resolver.root_node_modules_path().unwrap(); resolve_npm_commands_from_bin_dir(node_modules_dir) } - InnerCliNpmResolverRef::Managed(npm_resolver) => { + CliNpmResolver::Managed(npm_resolver) => { resolve_managed_npm_commands(npm_resolver, node_resolver)? } }; @@ -521,13 +520,12 @@ fn resolve_execution_path_from_npx_shim( } fn resolve_managed_npm_commands( - npm_resolver: &ManagedCliNpmResolver, + npm_resolver: &CliManagedNpmResolver, node_resolver: &CliNodeResolver, ) -> Result>, AnyError> { let mut result = HashMap::new(); - let snapshot = npm_resolver.snapshot(); - for id in snapshot.top_level_packages() { - let package_folder = npm_resolver.resolve_pkg_folder_from_pkg_id(id)?; + for id in npm_resolver.resolution().top_level_packages() { + let package_folder = npm_resolver.resolve_pkg_folder_from_pkg_id(&id)?; let bin_commands = node_resolver.resolve_binary_commands(&package_folder)?; for bin_command in bin_commands { diff --git a/cli/tools/check.rs b/cli/tools/check.rs index c3a285a9b2..8c584113cb 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -112,7 +112,7 @@ pub struct TypeChecker { module_graph_builder: Arc, npm_installer: Option>, node_resolver: Arc, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, sys: CliSys, } @@ -146,7 +146,7 @@ impl TypeChecker { module_graph_builder: Arc, node_resolver: Arc, npm_installer: Option>, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, sys: CliSys, ) -> Self { Self { @@ -189,6 +189,29 @@ impl TypeChecker { mut graph: ModuleGraph, options: CheckOptions, ) -> Result<(Arc, Diagnostics), CheckError> { + fn check_state_hash(resolver: &CliNpmResolver) -> Option { + match resolver { + CliNpmResolver::Byonm(_) => { + // not feasible and probably slower to compute + None + } + CliNpmResolver::Managed(resolver) => { + // we should probably go further and check all the individual npm packages + let mut package_reqs = resolver.resolution().package_reqs(); + package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism + let mut hasher = FastInsecureHasher::new_without_deno_version(); + // ensure the cache gets busted when turning nodeModulesDir on or off + // as this could cause changes in resolution + hasher.write_hashable(resolver.root_node_modules_path().is_some()); + for (pkg_req, pkg_nv) in package_reqs { + hasher.write_hashable(&pkg_req); + hasher.write_hashable(&pkg_nv); + } + Some(hasher.finish()) + } + } + } + if !options.type_check_mode.is_true() || graph.roots.is_empty() { return Ok((graph.into(), Default::default())); } @@ -240,7 +263,7 @@ impl TypeChecker { &self.sys, &graph, check_js, - self.npm_resolver.check_state_hash(), + check_state_hash(&self.npm_resolver), type_check_mode, &ts_config, ); diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 06afa5fac2..9b6ef81ea3 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -22,6 +22,7 @@ use deno_core::serde_json; use deno_core::sourcemap::SourceMap; use deno_core::url::Url; use deno_core::LocalInspectorSession; +use deno_resolver::npm::DenoInNpmPackageChecker; use node_resolver::InNpmPackageChecker; use regex::Regex; use text_lines::TextLines; @@ -464,7 +465,7 @@ fn filter_coverages( coverages: Vec, include: Vec, exclude: Vec, - in_npm_pkg_checker: &dyn InNpmPackageChecker, + in_npm_pkg_checker: &DenoInNpmPackageChecker, ) -> Vec { let include: Vec = include.iter().map(|e| Regex::new(e).unwrap()).collect(); @@ -532,7 +533,7 @@ pub fn cover_files( script_coverages, coverage_flags.include, coverage_flags.exclude, - in_npm_pkg_checker.as_ref(), + in_npm_pkg_checker, ); if script_coverages.is_empty() { return Err(anyhow!("No covered files included in the report")); diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 50faeda7d3..8c3b2665c5 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -32,8 +32,7 @@ use crate::args::InfoFlags; use crate::display; use crate::factory::CliFactory; use crate::graph_util::graph_exit_integrity_errors; -use crate::npm::CliNpmResolver; -use crate::npm::ManagedCliNpmResolver; +use crate::npm::CliManagedNpmResolver; use crate::util::checksum; use crate::util::display::DisplayTreeNode; @@ -138,6 +137,10 @@ pub async fn info( lockfile.write_if_changed()?; } + let maybe_npm_info = npm_resolver + .as_managed() + .map(|r| (r, r.resolution().snapshot())); + if info_flags.json { let mut json_graph = serde_json::json!(graph); if let Some(output) = json_graph.as_object_mut() { @@ -148,11 +151,19 @@ pub async fn info( ); } - add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref(), npmrc); + add_npm_packages_to_json( + &mut json_graph, + maybe_npm_info.as_ref().map(|(_, s)| s), + npmrc, + ); display::write_json_to_stdout(&json_graph)?; } else { let mut output = String::new(); - GraphDisplayContext::write(&graph, npm_resolver.as_ref(), &mut output)?; + GraphDisplayContext::write( + &graph, + maybe_npm_info.as_ref().map(|(r, s)| (*r, s)), + &mut output, + )?; display::write_to_stdout_ignore_sigpipe(output.as_bytes())?; } } else { @@ -251,15 +262,14 @@ fn print_cache_info( fn add_npm_packages_to_json( json: &mut serde_json::Value, - npm_resolver: &dyn CliNpmResolver, + npm_snapshot: Option<&NpmResolutionSnapshot>, npmrc: &ResolvedNpmRc, ) { - let Some(npm_resolver) = npm_resolver.as_managed() else { + let Some(npm_snapshot) = npm_snapshot else { return; // does not include byonm to deno info's output }; // ideally deno_graph could handle this, but for now we just modify the json here - let snapshot = npm_resolver.snapshot(); let json = json.as_object_mut().unwrap(); let modules = json.get_mut("modules").and_then(|m| m.as_array_mut()); if let Some(modules) = modules { @@ -273,7 +283,7 @@ fn add_npm_packages_to_json( .and_then(|k| k.as_str()) .and_then(|specifier| NpmPackageNvReference::from_str(specifier).ok()) .and_then(|package_ref| { - snapshot + npm_snapshot .resolve_package_from_deno_module(package_ref.nv()) .ok() }); @@ -295,7 +305,8 @@ fn add_npm_packages_to_json( if let Some(specifier) = dep.get("specifier").and_then(|s| s.as_str()) { if let Ok(npm_ref) = NpmPackageReqReference::from_str(specifier) { - if let Ok(pkg) = snapshot.resolve_pkg_from_pkg_req(npm_ref.req()) + if let Ok(pkg) = + npm_snapshot.resolve_pkg_from_pkg_req(npm_ref.req()) { dep.insert( "npmPackage".to_string(), @@ -321,8 +332,9 @@ fn add_npm_packages_to_json( } } - let mut sorted_packages = - snapshot.all_packages_for_every_system().collect::>(); + let mut sorted_packages = npm_snapshot + .all_packages_for_every_system() + .collect::>(); sorted_packages.sort_by(|a, b| a.id.cmp(&b.id)); let mut json_packages = serde_json::Map::with_capacity(sorted_packages.len()); for pkg in sorted_packages { @@ -356,7 +368,7 @@ struct NpmInfo { impl NpmInfo { pub fn build<'a>( graph: &'a ModuleGraph, - npm_resolver: &'a ManagedCliNpmResolver, + npm_resolver: &'a CliManagedNpmResolver, npm_snapshot: &'a NpmResolutionSnapshot, ) -> Self { let mut info = NpmInfo::default(); @@ -382,7 +394,7 @@ impl NpmInfo { fn fill_package_info<'a>( &mut self, package: &NpmResolutionPackage, - npm_resolver: &'a ManagedCliNpmResolver, + npm_resolver: &'a CliManagedNpmResolver, npm_snapshot: &'a NpmResolutionSnapshot, ) { self.packages.insert(package.id.clone(), package.clone()); @@ -419,13 +431,15 @@ struct GraphDisplayContext<'a> { impl<'a> GraphDisplayContext<'a> { pub fn write( graph: &'a ModuleGraph, - npm_resolver: &'a dyn CliNpmResolver, + managed_npm_info: Option<( + &'a CliManagedNpmResolver, + &'a NpmResolutionSnapshot, + )>, writer: &mut TWrite, ) -> Result<(), AnyError> { - let npm_info = match npm_resolver.as_managed() { - Some(npm_resolver) => { - let npm_snapshot = npm_resolver.snapshot(); - NpmInfo::build(graph, npm_resolver, &npm_snapshot) + let npm_info = match managed_npm_info { + Some((npm_resolver, npm_snapshot)) => { + NpmInfo::build(graph, npm_resolver, npm_snapshot) } None => NpmInfo::default(), }; diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index 1dbb464dbe..3d152490e8 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -451,7 +451,7 @@ pub struct DepManager { // TODO(nathanwhit): probably shouldn't be pub pub(crate) jsr_fetch_resolver: Arc, pub(crate) npm_fetch_resolver: Arc, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, npm_installer: Arc, permissions_container: PermissionsContainer, main_module_graph_container: Arc, @@ -463,7 +463,7 @@ pub struct DepManagerArgs { pub jsr_fetch_resolver: Arc, pub npm_fetch_resolver: Arc, pub npm_installer: Arc, - pub npm_resolver: Arc, + pub npm_resolver: CliNpmResolver, pub permissions_container: PermissionsContainer, pub main_module_graph_container: Arc, pub lockfile: Option>, @@ -551,9 +551,10 @@ impl DepManager { let npm_resolver = self.npm_resolver.as_managed().unwrap(); if self.deps.iter().all(|dep| match dep.kind { - DepKind::Npm => { - npm_resolver.resolve_pkg_id_from_pkg_req(&dep.req).is_ok() - } + DepKind::Npm => npm_resolver + .resolution() + .resolve_pkg_id_from_pkg_req(&dep.req) + .is_ok(), DepKind::Jsr => graph.packages.mappings().contains_key(&dep.req), }) { self.dependencies_resolved.raise(); @@ -630,7 +631,12 @@ impl DepManager { let graph = self.main_module_graph_container.graph(); let mut resolved = Vec::with_capacity(self.deps.len()); - let snapshot = self.npm_resolver.as_managed().unwrap().snapshot(); + let snapshot = self + .npm_resolver + .as_managed() + .unwrap() + .resolution() + .snapshot(); let resolved_npm = snapshot.package_reqs(); let resolved_jsr = graph.packages.mappings(); for dep in &self.deps { diff --git a/cli/tools/task.rs b/cli/tools/task.rs index d6d87dd45e..329195ab46 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -220,7 +220,7 @@ pub async fn execute_script( let task_runner = TaskRunner { task_flags: &task_flags, npm_installer: npm_installer.map(|n| n.as_ref()), - npm_resolver: npm_resolver.as_ref(), + npm_resolver, node_resolver: node_resolver.as_ref(), env_vars, cli_options, @@ -271,7 +271,7 @@ struct RunSingleOptions<'a> { struct TaskRunner<'a> { task_flags: &'a TaskFlags, npm_installer: Option<&'a NpmInstaller>, - npm_resolver: &'a dyn CliNpmResolver, + npm_resolver: &'a CliNpmResolver, node_resolver: &'a CliNodeResolver, env_vars: HashMap, cli_options: &'a CliOptions, diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index b6cf691c38..37d52c6cf2 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -46,7 +46,7 @@ use crate::cache::FastInsecureHasher; use crate::cache::ModuleInfoCache; use crate::node::CliNodeResolver; use crate::npm::CliNpmResolver; -use crate::resolver::CjsTracker; +use crate::resolver::CliCjsTracker; use crate::sys::CliSys; use crate::util::checksum; use crate::util::path::mapped_specifier_for_tsc; @@ -300,13 +300,13 @@ pub fn into_specifier_and_media_type( #[derive(Debug)] pub struct TypeCheckingCjsTracker { - cjs_tracker: Arc, + cjs_tracker: Arc, module_info_cache: Arc, } impl TypeCheckingCjsTracker { pub fn new( - cjs_tracker: Arc, + cjs_tracker: Arc, module_info_cache: Arc, ) -> Self { Self { @@ -358,7 +358,7 @@ impl TypeCheckingCjsTracker { pub struct RequestNpmState { pub cjs_tracker: Arc, pub node_resolver: Arc, - pub npm_resolver: Arc, + pub npm_resolver: CliNpmResolver, } /// A structure representing a request to be sent to the tsc runtime. diff --git a/cli/worker.rs b/cli/worker.rs index d93c6c5f40..45d4b0af70 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -19,6 +19,7 @@ use deno_core::ModuleLoader; use deno_core::PollEventLoopOptions; use deno_core::SharedArrayBufferStore; use deno_error::JsErrorBox; +use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::code_cache; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_fs; @@ -151,7 +152,7 @@ struct SharedWorkerState { module_loader_factory: Box, node_resolver: Arc, npm_installer: Option>, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, pkg_json_resolver: Arc, root_cert_store_provider: Arc, root_permissions: PermissionsContainer, @@ -168,18 +169,17 @@ impl SharedWorkerState { pub fn create_node_init_services( &self, node_require_loader: NodeRequireLoaderRc, - ) -> NodeExtInitServices { + ) -> NodeExtInitServices { NodeExtInitServices { node_require_loader, node_resolver: self.node_resolver.clone(), - npm_resolver: self.npm_resolver.clone().into_npm_pkg_folder_resolver(), pkg_json_resolver: self.pkg_json_resolver.clone(), sys: self.sys.clone(), } } pub fn npm_process_state_provider(&self) -> NpmProcessStateProviderRc { - self.npm_resolver.clone().into_process_state_provider() + crate::npm::create_npm_process_state_provider(&self.npm_resolver) } } @@ -427,7 +427,7 @@ impl CliMainWorkerFactory { module_loader_factory: Box, node_resolver: Arc, npm_installer: Option>, - npm_resolver: Arc, + npm_resolver: CliNpmResolver, pkg_json_resolver: Arc, root_cert_store_provider: Arc, root_permissions: PermissionsContainer, @@ -886,7 +886,11 @@ mod tests { ..Default::default() }; - MainWorker::bootstrap_from_options::( + MainWorker::bootstrap_from_options::< + DenoInNpmPackageChecker, + CliNpmResolver, + CliSys, + >( main_module, WorkerServiceOptions { module_loader: Rc::new(FsModuleLoader), diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 731cd3a9c7..325cec6f5b 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -15,8 +15,9 @@ use deno_core::v8; use deno_core::v8::ExternalReference; use deno_error::JsErrorBox; use node_resolver::errors::ClosestPkgJsonError; +use node_resolver::InNpmPackageChecker; use node_resolver::IsBuiltInNodeModuleChecker; -use node_resolver::NpmPackageFolderResolverRc; +use node_resolver::NpmPackageFolderResolver; use node_resolver::PackageJsonResolverRc; use once_cell::sync::Lazy; @@ -185,17 +186,21 @@ fn op_node_build_os() -> String { } #[derive(Clone)] -pub struct NodeExtInitServices { +pub struct NodeExtInitServices< + TInNpmPackageChecker: InNpmPackageChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TSys: ExtNodeSys, +> { pub node_require_loader: NodeRequireLoaderRc, - pub node_resolver: NodeResolverRc, - pub npm_resolver: NpmPackageFolderResolverRc, + pub node_resolver: + NodeResolverRc, pub pkg_json_resolver: PackageJsonResolverRc, pub sys: TSys, } deno_core::extension!(deno_node, deps = [ deno_io, deno_fs ], - parameters = [P: NodePermissions, TSys: ExtNodeSys], + parameters = [P: NodePermissions, TInNpmPackageChecker: InNpmPackageChecker, TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: ExtNodeSys], ops = [ ops::blocklist::op_socket_address_parse, ops::blocklist::op_socket_address_get_serialization, @@ -395,13 +400,13 @@ deno_core::extension!(deno_node, ops::require::op_require_init_paths, ops::require::op_require_node_module_paths, ops::require::op_require_proxy_path, - ops::require::op_require_is_deno_dir_package, - ops::require::op_require_resolve_deno_dir, + ops::require::op_require_is_deno_dir_package, + ops::require::op_require_resolve_deno_dir, ops::require::op_require_is_maybe_cjs, ops::require::op_require_is_request_relative, ops::require::op_require_resolve_lookup_paths, ops::require::op_require_try_self_parent_path, - ops::require::op_require_try_self, + ops::require::op_require_try_self, ops::require::op_require_real_path, ops::require::op_require_path_is_absolute, ops::require::op_require_path_dirname, @@ -410,9 +415,9 @@ deno_core::extension!(deno_node, ops::require::op_require_path_basename, ops::require::op_require_read_file

, ops::require::op_require_as_file_path, - ops::require::op_require_resolve_exports, + ops::require::op_require_resolve_exports, ops::require::op_require_read_package_scope, - ops::require::op_require_package_imports_resolve, + ops::require::op_require_package_imports_resolve, ops::require::op_require_break_on_next_statement, ops::util::op_node_guess_handle_type, ops::worker_threads::op_worker_threads_filename, @@ -681,7 +686,7 @@ deno_core::extension!(deno_node, "node:zlib" = "zlib.ts", ], options = { - maybe_init: Option>, + maybe_init: Option>, fs: deno_fs::FileSystemRc, }, state = |state, options| { @@ -691,7 +696,6 @@ deno_core::extension!(deno_node, state.put(init.sys.clone()); state.put(init.node_require_loader.clone()); state.put(init.node_resolver.clone()); - state.put(init.npm_resolver.clone()); state.put(init.pkg_json_resolver.clone()); } }, @@ -833,10 +837,18 @@ pub trait ExtNodeSys: impl ExtNodeSys for sys_traits::impls::RealSys {} -pub type NodeResolver = - node_resolver::NodeResolver; +pub type NodeResolver = + node_resolver::NodeResolver< + TInNpmPackageChecker, + RealIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >; #[allow(clippy::disallowed_types)] -pub type NodeResolverRc = deno_fs::sync::MaybeArc>; +pub type NodeResolverRc = + deno_fs::sync::MaybeArc< + NodeResolver, + >; #[allow(clippy::disallowed_types)] pub fn create_host_defined_options<'s>( diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs index 3135ba1e86..bff0cd79ed 100644 --- a/ext/node/ops/require.rs +++ b/ext/node/ops/require.rs @@ -19,7 +19,9 @@ use deno_path_util::normalize_path; use deno_path_util::url_from_file_path; use deno_path_util::url_to_file_path; use node_resolver::errors::ClosestPkgJsonError; +use node_resolver::InNpmPackageChecker; use node_resolver::NodeResolutionKind; +use node_resolver::NpmPackageFolderResolver; use node_resolver::ResolutionMode; use node_resolver::REQUIRE_CONDITIONS; use sys_traits::FsCanonicalize; @@ -30,7 +32,6 @@ use crate::ExtNodeSys; use crate::NodePermissions; use crate::NodeRequireLoaderRc; use crate::NodeResolverRc; -use crate::NpmPackageFolderResolverRc; use crate::PackageJsonResolverRc; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] @@ -256,12 +257,20 @@ pub fn op_require_is_request_relative(#[string] request: String) -> bool { #[op2] #[string] -pub fn op_require_resolve_deno_dir( +pub fn op_require_resolve_deno_dir< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] request: String, #[string] parent_filename: String, ) -> Result, deno_path_util::PathToUrlError> { - let resolver = state.borrow::(); + let resolver = state.borrow::>(); Ok( resolver @@ -275,11 +284,19 @@ pub fn op_require_resolve_deno_dir( } #[op2(fast)] -pub fn op_require_is_deno_dir_package( +pub fn op_require_is_deno_dir_package< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TSys: ExtNodeSys + 'static, +>( state: &mut OpState, #[string] path: String, ) -> bool { - let resolver = state.borrow::>(); + let resolver = state.borrow::>(); match deno_path_util::url_from_file_path(&PathBuf::from(path)) { Ok(specifier) => resolver.in_npm_package(&specifier), Err(_) => false, @@ -451,6 +468,8 @@ pub fn op_require_try_self_parent_path< #[string] pub fn op_require_try_self< P: NodePermissions + 'static, + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, TSys: ExtNodeSys + 'static, >( state: &mut OpState, @@ -493,7 +512,11 @@ pub fn op_require_try_self< let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap(); if let Some(exports) = &pkg.exports { - let node_resolver = state.borrow::>(); + let node_resolver = state.borrow::>(); let r = node_resolver.package_exports_resolve( &pkg.path, &expansion, @@ -552,6 +575,8 @@ pub fn op_require_as_file_path(#[string] file_or_url: String) -> String { #[string] pub fn op_require_resolve_exports< P: NodePermissions + 'static, + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, TSys: ExtNodeSys + 'static, >( state: &mut OpState, @@ -563,7 +588,11 @@ pub fn op_require_resolve_exports< #[string] parent_path: String, ) -> Result, RequireError> { let sys = state.borrow::(); - let node_resolver = state.borrow::>(); + let node_resolver = state.borrow::>(); let pkg_json_resolver = state.borrow::>(); let modules_path = PathBuf::from(&modules_path_str); @@ -655,6 +684,8 @@ pub fn op_require_read_package_scope< #[string] pub fn op_require_package_imports_resolve< P: NodePermissions + 'static, + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, TSys: ExtNodeSys + 'static, >( state: &mut OpState, @@ -672,7 +703,11 @@ pub fn op_require_package_imports_resolve< }; if pkg.imports.is_some() { - let node_resolver = state.borrow::>(); + let node_resolver = state.borrow::>(); let referrer_url = Url::from_file_path(&referrer_filename).unwrap(); let url = node_resolver.package_imports_resolve( &request, diff --git a/resolvers/deno/cjs.rs b/resolvers/deno/cjs.rs index bae645b481..4358b0ced2 100644 --- a/resolvers/deno/cjs.rs +++ b/resolvers/deno/cjs.rs @@ -2,7 +2,7 @@ use deno_media_type::MediaType; use node_resolver::errors::ClosestPkgJsonError; -use node_resolver::InNpmPackageCheckerRc; +use node_resolver::InNpmPackageChecker; use node_resolver::PackageJsonResolverRc; use node_resolver::ResolutionMode; use sys_traits::FsRead; @@ -16,14 +16,16 @@ use crate::sync::MaybeDashMap; /// be CJS or ESM after they're loaded based on their contents. So these /// files will be "maybe CJS" until they're loaded. #[derive(Debug)] -pub struct CjsTracker { - is_cjs_resolver: IsCjsResolver, +pub struct CjsTracker { + is_cjs_resolver: IsCjsResolver, known: MaybeDashMap, } -impl CjsTracker { +impl + CjsTracker +{ pub fn new( - in_npm_pkg_checker: InNpmPackageCheckerRc, + in_npm_pkg_checker: TInNpmPackageChecker, pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, ) -> Self { @@ -125,15 +127,20 @@ pub enum IsCjsResolutionMode { /// Resolves whether a module is CJS or ESM. #[derive(Debug)] -pub struct IsCjsResolver { - in_npm_pkg_checker: InNpmPackageCheckerRc, +pub struct IsCjsResolver< + TInNpmPackageChecker: InNpmPackageChecker, + TSys: FsRead, +> { + in_npm_pkg_checker: TInNpmPackageChecker, pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, } -impl IsCjsResolver { +impl + IsCjsResolver +{ pub fn new( - in_npm_pkg_checker: InNpmPackageCheckerRc, + in_npm_pkg_checker: TInNpmPackageChecker, pkg_json_resolver: PackageJsonResolverRc, mode: IsCjsResolutionMode, ) -> Self { diff --git a/resolvers/deno/lib.rs b/resolvers/deno/lib.rs index 12d8effc46..454c7f12e2 100644 --- a/resolvers/deno/lib.rs +++ b/resolvers/deno/lib.rs @@ -19,11 +19,12 @@ use deno_package_json::PackageJsonDepValueParseError; use deno_semver::npm::NpmPackageReqReference; use node_resolver::errors::NodeResolveError; use node_resolver::errors::PackageSubpathResolveError; -use node_resolver::InNpmPackageCheckerRc; +use node_resolver::InNpmPackageChecker; use node_resolver::IsBuiltInNodeModuleChecker; use node_resolver::NodeResolution; use node_resolver::NodeResolutionKind; use node_resolver::NodeResolverRc; +use node_resolver::NpmPackageFolderResolver; use node_resolver::ResolutionMode; use npm::MissingPackageNodeModulesFolderError; use npm::NodeModulesOutOfDateError; @@ -101,22 +102,42 @@ pub enum DenoResolveErrorKind { #[derive(Debug)] pub struct NodeAndNpmReqResolver< + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - pub node_resolver: NodeResolverRc, - pub npm_req_resolver: NpmReqResolverRc, + pub node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + pub npm_req_resolver: NpmReqResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, } pub struct DenoResolverOptions< 'a, + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSloppyImportResolverFs: SloppyImportResolverFs, TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - pub in_npm_pkg_checker: InNpmPackageCheckerRc, - pub node_and_req_resolver: - Option>, + pub in_npm_pkg_checker: TInNpmPackageChecker, + pub node_and_req_resolver: Option< + NodeAndNpmReqResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + >, pub sloppy_imports_resolver: Option>, pub workspace_resolver: WorkspaceResolverRc, @@ -131,13 +152,21 @@ pub struct DenoResolverOptions< /// import map, JSX settings. #[derive(Debug)] pub struct DenoResolver< + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSloppyImportResolverFs: SloppyImportResolverFs, TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - in_npm_pkg_checker: InNpmPackageCheckerRc, - node_and_npm_resolver: - Option>, + in_npm_pkg_checker: TInNpmPackageChecker, + node_and_npm_resolver: Option< + NodeAndNpmReqResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + >, sloppy_imports_resolver: Option>, workspace_resolver: WorkspaceResolverRc, @@ -146,14 +175,25 @@ pub struct DenoResolver< } impl< + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSloppyImportResolverFs: SloppyImportResolverFs, TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, - > DenoResolver + > + DenoResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSloppyImportResolverFs, + TSys, + > { pub fn new( options: DenoResolverOptions< + TInNpmPackageChecker, TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, TSloppyImportResolverFs, TSys, >, diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs index bf25ba5646..4f72692f8b 100644 --- a/resolvers/deno/npm/byonm.rs +++ b/resolvers/deno/npm/byonm.rs @@ -27,8 +27,6 @@ use thiserror::Error; use url::Url; use super::local::normalize_pkg_name_for_node_modules_deno_folder; -use crate::sync::MaybeSend; -use crate::sync::MaybeSync; #[derive(Debug, Error, deno_error::JsError)] pub enum ByonmResolvePkgFolderFromDenoReqError { @@ -89,7 +87,7 @@ impl } } - pub fn root_node_modules_dir(&self) -> Option<&Path> { + pub fn root_node_modules_path(&self) -> Option<&Path> { self.root_node_modules_dir.as_deref() } @@ -377,15 +375,8 @@ impl } } -impl< - TSys: FsCanonicalize - + FsMetadata - + FsRead - + FsReadDir - + MaybeSend - + MaybeSync - + std::fmt::Debug, - > NpmPackageFolderResolver for ByonmNpmResolver +impl + NpmPackageFolderResolver for ByonmNpmResolver { fn resolve_package_folder_from_package( &self, @@ -438,7 +429,7 @@ impl< } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ByonmInNpmPackageChecker; impl InNpmPackageChecker for ByonmInNpmPackageChecker { diff --git a/resolvers/deno/npm/managed/common.rs b/resolvers/deno/npm/managed/common.rs index 6569a4594f..a92fe226dd 100644 --- a/resolvers/deno/npm/managed/common.rs +++ b/resolvers/deno/npm/managed/common.rs @@ -6,26 +6,69 @@ use std::path::PathBuf; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use node_resolver::NpmPackageFolderResolver; +use sys_traits::FsCanonicalize; +use sys_traits::FsMetadata; use url::Url; -use crate::sync::MaybeSend; -use crate::sync::MaybeSync; +#[derive(Debug)] +pub enum NpmPackageFsResolver { + Local(super::local::LocalNpmPackageResolver), + Global(super::global::GlobalNpmPackageResolver), +} -#[allow(clippy::disallowed_types)] -pub type NpmPackageFsResolverRc = - crate::sync::MaybeArc; +impl NpmPackageFsResolver { + /// The local node_modules folder (only for the local resolver). + pub fn node_modules_path(&self) -> Option<&Path> { + match self { + NpmPackageFsResolver::Local(resolver) => resolver.node_modules_path(), + NpmPackageFsResolver::Global(_) => None, + } + } -/// Part of the resolution that interacts with the file system. -pub trait NpmPackageFsResolver: - NpmPackageFolderResolver + MaybeSend + MaybeSync -{ - /// The local node_modules folder if it is applicable to the implementation. - fn node_modules_path(&self) -> Option<&Path>; + pub fn maybe_package_folder( + &self, + package_id: &NpmPackageId, + ) -> Option { + match self { + NpmPackageFsResolver::Local(resolver) => { + resolver.maybe_package_folder(package_id) + } + NpmPackageFsResolver::Global(resolver) => { + resolver.maybe_package_folder(package_id) + } + } + } - fn maybe_package_folder(&self, package_id: &NpmPackageId) -> Option; - - fn resolve_package_cache_folder_id_from_specifier( + pub fn resolve_package_cache_folder_id_from_specifier( &self, specifier: &Url, - ) -> Result, std::io::Error>; + ) -> Result, std::io::Error> { + match self { + NpmPackageFsResolver::Local(resolver) => { + resolver.resolve_package_cache_folder_id_from_specifier(specifier) + } + NpmPackageFsResolver::Global(resolver) => { + resolver.resolve_package_cache_folder_id_from_specifier(specifier) + } + } + } +} + +impl NpmPackageFolderResolver + for NpmPackageFsResolver +{ + fn resolve_package_folder_from_package( + &self, + specifier: &str, + referrer: &Url, + ) -> Result { + match self { + NpmPackageFsResolver::Local(r) => { + r.resolve_package_folder_from_package(specifier, referrer) + } + NpmPackageFsResolver::Global(r) => { + r.resolve_package_folder_from_package(specifier, referrer) + } + } + } } diff --git a/resolvers/deno/npm/managed/global.rs b/resolvers/deno/npm/managed/global.rs index d16234d8f4..271851c79c 100644 --- a/resolvers/deno/npm/managed/global.rs +++ b/resolvers/deno/npm/managed/global.rs @@ -2,7 +2,6 @@ //! Code for global npm cache resolution. -use std::path::Path; use std::path::PathBuf; use deno_npm::NpmPackageCacheFolderId; @@ -18,7 +17,6 @@ use url::Url; use super::resolution::NpmResolutionCellRc; use super::NpmCacheDirRc; -use super::NpmPackageFsResolver; use crate::ResolvedNpmRcRc; /// Resolves packages from the global npm cache. @@ -42,6 +40,26 @@ impl GlobalNpmPackageResolver { } } + pub fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { + let folder_copy_index = self + .resolution + .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; + let registry_url = self.npm_rc.get_registry_url(&id.nv.name); + Some(self.cache.package_folder_for_id( + &id.nv.name, + &id.nv.version.to_string(), + folder_copy_index, + registry_url, + )) + } + + pub fn resolve_package_cache_folder_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, std::io::Error> { + Ok(self.resolve_package_cache_folder_id_from_specifier_inner(specifier)) + } + fn resolve_package_cache_folder_id_from_specifier_inner( &self, specifier: &Url, @@ -121,29 +139,3 @@ impl NpmPackageFolderResolver for GlobalNpmPackageResolver { } } } - -impl NpmPackageFsResolver for GlobalNpmPackageResolver { - fn node_modules_path(&self) -> Option<&Path> { - None - } - - fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { - let folder_copy_index = self - .resolution - .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; - let registry_url = self.npm_rc.get_registry_url(&id.nv.name); - Some(self.cache.package_folder_for_id( - &id.nv.name, - &id.nv.version.to_string(), - folder_copy_index, - registry_url, - )) - } - - fn resolve_package_cache_folder_id_from_specifier( - &self, - specifier: &Url, - ) -> Result, std::io::Error> { - Ok(self.resolve_package_cache_folder_id_from_specifier_inner(specifier)) - } -} diff --git a/resolvers/deno/npm/managed/local.rs b/resolvers/deno/npm/managed/local.rs index 72b0b0d451..e453101df4 100644 --- a/resolvers/deno/npm/managed/local.rs +++ b/resolvers/deno/npm/managed/local.rs @@ -20,27 +20,20 @@ use sys_traits::FsMetadata; use url::Url; use super::resolution::NpmResolutionCellRc; -use super::NpmPackageFsResolver; use crate::npm::local::get_package_folder_id_folder_name_from_parts; use crate::npm::local::get_package_folder_id_from_folder_name; -use crate::sync::MaybeSend; -use crate::sync::MaybeSync; /// Resolver that creates a local node_modules directory /// and resolves packages from it. #[derive(Debug)] -pub struct LocalNpmPackageResolver< - TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync, -> { +pub struct LocalNpmPackageResolver { resolution: NpmResolutionCellRc, sys: TSys, root_node_modules_path: PathBuf, root_node_modules_url: Url, } -impl - LocalNpmPackageResolver -{ +impl LocalNpmPackageResolver { #[allow(clippy::too_many_arguments)] pub fn new( resolution: NpmResolutionCellRc, @@ -56,6 +49,55 @@ impl } } + pub fn node_modules_path(&self) -> Option<&Path> { + Some(self.root_node_modules_path.as_ref()) + } + + pub fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { + let folder_copy_index = self + .resolution + .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; + // package is stored at: + // node_modules/.deno//node_modules/ + Some( + self + .root_node_modules_path + .join(".deno") + .join(get_package_folder_id_folder_name_from_parts( + &id.nv, + folder_copy_index, + )) + .join("node_modules") + .join(&id.nv.name), + ) + } + + pub fn resolve_package_cache_folder_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, std::io::Error> { + let Some(folder_path) = + self.resolve_package_folder_from_specifier(specifier)? + else { + return Ok(None); + }; + // ex. project/node_modules/.deno/preact@10.24.3/node_modules/preact/ + let Some(node_modules_ancestor) = folder_path + .ancestors() + .find(|ancestor| ancestor.ends_with("node_modules")) + else { + return Ok(None); + }; + let Some(folder_name) = + node_modules_ancestor.parent().and_then(|p| p.file_name()) + else { + return Ok(None); + }; + Ok(get_package_folder_id_from_folder_name( + &folder_name.to_string_lossy(), + )) + } + fn resolve_package_root(&self, path: &Path) -> PathBuf { let mut last_found = path; loop { @@ -101,9 +143,8 @@ impl } } -impl< - TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync + std::fmt::Debug, - > NpmPackageFolderResolver for LocalNpmPackageResolver +impl NpmPackageFolderResolver + for LocalNpmPackageResolver { fn resolve_package_folder_from_package( &self, @@ -163,60 +204,6 @@ impl< } } -impl< - TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync + std::fmt::Debug, - > NpmPackageFsResolver for LocalNpmPackageResolver -{ - fn node_modules_path(&self) -> Option<&Path> { - Some(self.root_node_modules_path.as_ref()) - } - - fn maybe_package_folder(&self, id: &NpmPackageId) -> Option { - let folder_copy_index = self - .resolution - .resolve_pkg_cache_folder_copy_index_from_pkg_id(id)?; - // package is stored at: - // node_modules/.deno//node_modules/ - Some( - self - .root_node_modules_path - .join(".deno") - .join(get_package_folder_id_folder_name_from_parts( - &id.nv, - folder_copy_index, - )) - .join("node_modules") - .join(&id.nv.name), - ) - } - - fn resolve_package_cache_folder_id_from_specifier( - &self, - specifier: &Url, - ) -> Result, std::io::Error> { - let Some(folder_path) = - self.resolve_package_folder_from_specifier(specifier)? - else { - return Ok(None); - }; - // ex. project/node_modules/.deno/preact@10.24.3/node_modules/preact/ - let Some(node_modules_ancestor) = folder_path - .ancestors() - .find(|ancestor| ancestor.ends_with("node_modules")) - else { - return Ok(None); - }; - let Some(folder_name) = - node_modules_ancestor.parent().and_then(|p| p.file_name()) - else { - return Ok(None); - }; - Ok(get_package_folder_id_from_folder_name( - &folder_name.to_string_lossy(), - )) - } -} - fn join_package_name(path: &Path, package_name: &str) -> PathBuf { let mut path = path.to_path_buf(); // ensure backslashes are used on windows diff --git a/resolvers/deno/npm/managed/mod.rs b/resolvers/deno/npm/managed/mod.rs index 62147b2ac3..291e299bc8 100644 --- a/resolvers/deno/npm/managed/mod.rs +++ b/resolvers/deno/npm/managed/mod.rs @@ -13,6 +13,7 @@ use deno_npm::resolution::PackageNvNotFoundError; use deno_npm::resolution::PackageReqNotFoundError; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; +use deno_npm::NpmSystemInfo; use deno_path_util::fs::canonicalize_path_maybe_not_exists; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; @@ -23,14 +24,10 @@ use sys_traits::FsMetadata; use url::Url; use self::common::NpmPackageFsResolver; -use self::common::NpmPackageFsResolverRc; use self::global::GlobalNpmPackageResolver; use self::local::LocalNpmPackageResolver; pub use self::resolution::NpmResolutionCell; pub use self::resolution::NpmResolutionCellRc; -use crate::sync::new_rc; -use crate::sync::MaybeSend; -use crate::sync::MaybeSync; use crate::NpmCacheDirRc; use crate::ResolvedNpmRcRc; @@ -89,58 +86,84 @@ pub enum ResolvePkgIdFromSpecifierError { NotFound(#[from] PackageCacheFolderIdNotFoundError), } +pub struct ManagedNpmResolverCreateOptions< + TSys: FsCanonicalize + FsMetadata + Clone, +> { + pub npm_cache_dir: NpmCacheDirRc, + pub sys: TSys, + pub maybe_node_modules_path: Option, + pub npm_system_info: NpmSystemInfo, + pub npmrc: ResolvedNpmRcRc, + pub npm_resolution: NpmResolutionCellRc, +} + #[allow(clippy::disallowed_types)] pub type ManagedNpmResolverRc = crate::sync::MaybeArc>; #[derive(Debug)] -pub struct ManagedNpmResolver { - fs_resolver: NpmPackageFsResolverRc, +pub struct ManagedNpmResolver { + fs_resolver: NpmPackageFsResolver, + npm_cache_dir: NpmCacheDirRc, resolution: NpmResolutionCellRc, sys: TSys, } -impl ManagedNpmResolver { - pub fn new< - TCreateSys: FsCanonicalize - + FsMetadata - + std::fmt::Debug - + MaybeSend - + MaybeSync - + Clone - + 'static, - >( - npm_cache_dir: &NpmCacheDirRc, - npm_rc: &ResolvedNpmRcRc, - resolution: NpmResolutionCellRc, - sys: TCreateSys, - maybe_node_modules_path: Option, +impl ManagedNpmResolver { + pub fn new( + options: ManagedNpmResolverCreateOptions, ) -> ManagedNpmResolver { - let fs_resolver: NpmPackageFsResolverRc = match maybe_node_modules_path { - Some(node_modules_folder) => new_rc(LocalNpmPackageResolver::new( - resolution.clone(), - sys.clone(), - node_modules_folder, - )), - None => new_rc(GlobalNpmPackageResolver::new( - npm_cache_dir.clone(), - npm_rc.clone(), - resolution.clone(), + let fs_resolver = match options.maybe_node_modules_path { + Some(node_modules_folder) => { + NpmPackageFsResolver::Local(LocalNpmPackageResolver::new( + options.npm_resolution.clone(), + options.sys.clone(), + node_modules_folder, + )) + } + None => NpmPackageFsResolver::Global(GlobalNpmPackageResolver::new( + options.npm_cache_dir.clone(), + options.npmrc.clone(), + options.npm_resolution.clone(), )), }; ManagedNpmResolver { fs_resolver, - sys, - resolution, + npm_cache_dir: options.npm_cache_dir, + sys: options.sys, + resolution: options.npm_resolution, } } #[inline] - pub fn node_modules_path(&self) -> Option<&Path> { + pub fn root_node_modules_path(&self) -> Option<&Path> { self.fs_resolver.node_modules_path() } + pub fn global_cache_root_path(&self) -> &Path { + self.npm_cache_dir.root_dir() + } + + pub fn global_cache_root_url(&self) -> &Url { + self.npm_cache_dir.root_dir_url() + } + + pub fn resolution(&self) -> &NpmResolutionCell { + self.resolution.as_ref() + } + + /// Checks if the provided package req's folder is cached. + pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool { + self + .resolution + .resolve_pkg_id_from_pkg_req(req) + .ok() + .and_then(|id| self.resolve_pkg_folder_from_pkg_id(&id).ok()) + .map(|folder| self.sys.fs_exists_no_err(folder)) + .unwrap_or(false) + } + pub fn resolve_pkg_folder_from_pkg_id( &self, package_id: &NpmPackageId, @@ -213,8 +236,8 @@ impl ManagedNpmResolver { } } -impl - NpmPackageFolderResolver for ManagedNpmResolver +impl NpmPackageFolderResolver + for ManagedNpmResolver { fn resolve_package_folder_from_package( &self, @@ -234,7 +257,7 @@ impl } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ManagedInNpmPackageChecker { root_dir: Url, } diff --git a/resolvers/deno/npm/managed/resolution.rs b/resolvers/deno/npm/managed/resolution.rs index 08dabf5a76..faa0d43664 100644 --- a/resolvers/deno/npm/managed/resolution.rs +++ b/resolvers/deno/npm/managed/resolution.rs @@ -1,7 +1,5 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::collections::HashMap; - use deno_npm::resolution::NpmPackagesPartitioned; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::resolution::PackageCacheFolderIdNotFoundError; @@ -124,8 +122,23 @@ impl NpmResolutionCell { .map(|pkg| pkg.id.clone()) } - pub fn package_reqs(&self) -> HashMap { - self.snapshot.read().package_reqs().clone() + pub fn package_reqs(&self) -> Vec<(PackageReq, PackageNv)> { + self + .snapshot + .read() + .package_reqs() + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect() + } + + pub fn top_level_packages(&self) -> Vec { + self + .snapshot + .read() + .top_level_packages() + .cloned() + .collect::>() } pub fn all_system_packages( diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs index 26b156d29d..fed3d388bf 100644 --- a/resolvers/deno/npm/mod.rs +++ b/resolvers/deno/npm/mod.rs @@ -1,6 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::fmt::Debug; +use std::path::Path; use std::path::PathBuf; use boxed_error::Boxed; @@ -14,11 +15,12 @@ use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::PackageResolveErrorKind; use node_resolver::errors::PackageSubpathResolveError; -use node_resolver::InNpmPackageCheckerRc; +use node_resolver::InNpmPackageChecker; use node_resolver::IsBuiltInNodeModuleChecker; use node_resolver::NodeResolution; use node_resolver::NodeResolutionKind; use node_resolver::NodeResolverRc; +use node_resolver::NpmPackageFolderResolver; use node_resolver::ResolutionMode; use sys_traits::FsCanonicalize; use sys_traits::FsMetadata; @@ -35,10 +37,14 @@ pub use self::byonm::ByonmResolvePkgFolderFromDenoReqError; pub use self::local::get_package_folder_id_folder_name; pub use self::local::normalize_pkg_name_for_node_modules_deno_folder; use self::managed::create_managed_in_npm_pkg_checker; +use self::managed::ManagedInNpmPackageChecker; use self::managed::ManagedInNpmPkgCheckerCreateOptions; pub use self::managed::ManagedNpmResolver; +use self::managed::ManagedNpmResolverCreateOptions; pub use self::managed::ManagedNpmResolverRc; use crate::sync::new_rc; +use crate::sync::MaybeSend; +use crate::sync::MaybeSync; mod byonm; mod local; @@ -49,14 +55,33 @@ pub enum CreateInNpmPkgCheckerOptions<'a> { Byonm, } -pub fn create_in_npm_pkg_checker( - options: CreateInNpmPkgCheckerOptions, -) -> InNpmPackageCheckerRc { - match options { - CreateInNpmPkgCheckerOptions::Managed(options) => { - new_rc(create_managed_in_npm_pkg_checker(options)) +#[derive(Debug, Clone)] +pub enum DenoInNpmPackageChecker { + Managed(ManagedInNpmPackageChecker), + Byonm(ByonmInNpmPackageChecker), +} + +impl DenoInNpmPackageChecker { + pub fn new(options: CreateInNpmPkgCheckerOptions) -> Self { + match options { + CreateInNpmPkgCheckerOptions::Managed(options) => { + DenoInNpmPackageChecker::Managed(create_managed_in_npm_pkg_checker( + options, + )) + } + CreateInNpmPkgCheckerOptions::Byonm => { + DenoInNpmPackageChecker::Byonm(ByonmInNpmPackageChecker) + } + } + } +} + +impl InNpmPackageChecker for DenoInNpmPackageChecker { + fn in_npm_package(&self, specifier: &Url) -> bool { + match self { + DenoInNpmPackageChecker::Managed(c) => c.in_npm_package(specifier), + DenoInNpmPackageChecker::Byonm(c) => c.in_npm_package(specifier), } - CreateInNpmPkgCheckerOptions::Byonm => new_rc(ByonmInNpmPackageChecker), } } @@ -115,10 +140,22 @@ pub enum ResolvePkgFolderFromDenoReqError { Byonm(byonm::ByonmResolvePkgFolderFromDenoReqError), } -#[derive(Debug, Clone)] -pub enum ByonmOrManagedNpmResolver< - TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, +pub enum NpmResolverCreateOptions< + TSys: FsRead + + FsCanonicalize + + FsMetadata + + std::fmt::Debug + + MaybeSend + + MaybeSync + + Clone + + 'static, > { + Managed(ManagedNpmResolverCreateOptions), + Byonm(ByonmNpmResolverCreateOptions), +} + +#[derive(Debug, Clone)] +pub enum NpmResolver { /// The resolver when "bring your own node_modules" is enabled where Deno /// does not setup the node_modules directories automatically, but instead /// uses what already exists on the file system. @@ -126,57 +163,158 @@ pub enum ByonmOrManagedNpmResolver< Managed(ManagedNpmResolverRc), } -impl - ByonmOrManagedNpmResolver -{ +impl NpmResolver { + pub fn new< + TCreateSys: FsCanonicalize + + FsMetadata + + FsRead + + FsReadDir + + std::fmt::Debug + + MaybeSend + + MaybeSync + + Clone + + 'static, + >( + options: NpmResolverCreateOptions, + ) -> NpmResolver { + match options { + NpmResolverCreateOptions::Managed(options) => { + NpmResolver::Managed(new_rc(ManagedNpmResolver::::new::< + TCreateSys, + >(options))) + } + NpmResolverCreateOptions::Byonm(options) => { + NpmResolver::Byonm(new_rc(ByonmNpmResolver::new(options))) + } + } + } + + pub fn is_byonm(&self) -> bool { + matches!(self, NpmResolver::Byonm(_)) + } + + pub fn is_managed(&self) -> bool { + matches!(self, NpmResolver::Managed(_)) + } + + pub fn as_managed(&self) -> Option<&ManagedNpmResolver> { + match self { + NpmResolver::Managed(resolver) => Some(resolver), + NpmResolver::Byonm(_) => None, + } + } + + pub fn root_node_modules_path(&self) -> Option<&Path> { + match self { + NpmResolver::Byonm(resolver) => resolver.root_node_modules_path(), + NpmResolver::Managed(resolver) => resolver.root_node_modules_path(), + } + } + pub fn resolve_pkg_folder_from_deno_module_req( &self, req: &PackageReq, referrer: &Url, ) -> Result { match self { - ByonmOrManagedNpmResolver::Byonm(byonm_resolver) => byonm_resolver + NpmResolver::Byonm(byonm_resolver) => byonm_resolver .resolve_pkg_folder_from_deno_module_req(req, referrer) .map_err(ResolvePkgFolderFromDenoReqError::Byonm), - ByonmOrManagedNpmResolver::Managed(managed_resolver) => managed_resolver + NpmResolver::Managed(managed_resolver) => managed_resolver .resolve_pkg_folder_from_deno_module_req(req, referrer) .map_err(ResolvePkgFolderFromDenoReqError::Managed), } } } +impl + NpmPackageFolderResolver for NpmResolver +{ + fn resolve_package_folder_from_package( + &self, + specifier: &str, + referrer: &Url, + ) -> Result { + match self { + NpmResolver::Byonm(byonm_resolver) => { + byonm_resolver.resolve_package_folder_from_package(specifier, referrer) + } + NpmResolver::Managed(managed_resolver) => managed_resolver + .resolve_package_folder_from_package(specifier, referrer), + } + } +} + pub struct NpmReqResolverOptions< + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { - pub in_npm_pkg_checker: InNpmPackageCheckerRc, - pub node_resolver: NodeResolverRc, - pub npm_resolver: ByonmOrManagedNpmResolver, + pub in_npm_pkg_checker: TInNpmPackageChecker, + pub node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + pub npm_resolver: NpmResolver, pub sys: TSys, } #[allow(clippy::disallowed_types)] -pub type NpmReqResolverRc = - crate::sync::MaybeArc>; +pub type NpmReqResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, +> = crate::sync::MaybeArc< + NpmReqResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, +>; #[derive(Debug)] pub struct NpmReqResolver< + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, > { sys: TSys, - in_npm_pkg_checker: InNpmPackageCheckerRc, - node_resolver: NodeResolverRc, - npm_resolver: ByonmOrManagedNpmResolver, + in_npm_pkg_checker: TInNpmPackageChecker, + node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + npm_resolver: NpmResolver, } impl< + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir, - > NpmReqResolver + > + NpmReqResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + > { pub fn new( - options: NpmReqResolverOptions, + options: NpmReqResolverOptions< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, ) -> Self { Self { sys: options.sys, @@ -224,7 +362,7 @@ impl< match resolution_result { Ok(url) => Ok(url), Err(err) => { - if matches!(self.npm_resolver, ByonmOrManagedNpmResolver::Byonm(_)) { + if matches!(self.npm_resolver, NpmResolver::Byonm(_)) { let package_json_path = package_folder.join("package.json"); if !self.sys.fs_exists_no_err(&package_json_path) { return Err( @@ -292,9 +430,8 @@ impl< .into_box(), ); } - if let ByonmOrManagedNpmResolver::Byonm( - byonm_npm_resolver, - ) = &self.npm_resolver + if let NpmResolver::Byonm(byonm_npm_resolver) = + &self.npm_resolver { if byonm_npm_resolver .find_ancestor_package_json_with_dep( diff --git a/resolvers/node/analyze.rs b/resolvers/node/analyze.rs index a6ba3927aa..e144e2b8fb 100644 --- a/resolvers/node/analyze.rs +++ b/resolvers/node/analyze.rs @@ -20,11 +20,11 @@ use sys_traits::FsMetadata; use sys_traits::FsRead; use url::Url; -use crate::npm::InNpmPackageCheckerRc; use crate::resolution::NodeResolverRc; +use crate::InNpmPackageChecker; use crate::IsBuiltInNodeModuleChecker; use crate::NodeResolutionKind; -use crate::NpmPackageFolderResolverRc; +use crate::NpmPackageFolderResolver; use crate::PackageJsonResolverRc; use crate::PathClean; use crate::ResolutionMode; @@ -62,28 +62,49 @@ pub trait CjsCodeAnalyzer { pub struct NodeCodeTranslator< TCjsCodeAnalyzer: CjsCodeAnalyzer, + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: FsCanonicalize + FsMetadata + FsRead, > { cjs_code_analyzer: TCjsCodeAnalyzer, - in_npm_pkg_checker: InNpmPackageCheckerRc, - node_resolver: NodeResolverRc, - npm_resolver: NpmPackageFolderResolverRc, + in_npm_pkg_checker: TInNpmPackageChecker, + node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + npm_resolver: TNpmPackageFolderResolver, pkg_json_resolver: PackageJsonResolverRc, sys: TSys, } impl< TCjsCodeAnalyzer: CjsCodeAnalyzer, + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: FsCanonicalize + FsMetadata + FsRead, - > NodeCodeTranslator + > + NodeCodeTranslator< + TCjsCodeAnalyzer, + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + > { pub fn new( cjs_code_analyzer: TCjsCodeAnalyzer, - in_npm_pkg_checker: InNpmPackageCheckerRc, - node_resolver: NodeResolverRc, - npm_resolver: NpmPackageFolderResolverRc, + in_npm_pkg_checker: TInNpmPackageChecker, + node_resolver: NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, + npm_resolver: TNpmPackageFolderResolver, pkg_json_resolver: PackageJsonResolverRc, sys: TSys, ) -> Self { diff --git a/resolvers/node/lib.rs b/resolvers/node/lib.rs index faee4f1645..c0e6383237 100644 --- a/resolvers/node/lib.rs +++ b/resolvers/node/lib.rs @@ -14,9 +14,7 @@ mod sync; pub use deno_package_json::PackageJson; pub use npm::InNpmPackageChecker; -pub use npm::InNpmPackageCheckerRc; pub use npm::NpmPackageFolderResolver; -pub use npm::NpmPackageFolderResolverRc; pub use package_json::PackageJsonResolver; pub use package_json::PackageJsonResolverRc; pub use package_json::PackageJsonThreadLocalCache; diff --git a/resolvers/node/npm.rs b/resolvers/node/npm.rs index f799d6ddee..bb3de2a7f5 100644 --- a/resolvers/node/npm.rs +++ b/resolvers/node/npm.rs @@ -9,16 +9,8 @@ use url::Url; use crate::errors; use crate::path::PathClean; -use crate::sync::MaybeSend; -use crate::sync::MaybeSync; -#[allow(clippy::disallowed_types)] -pub type NpmPackageFolderResolverRc = - crate::sync::MaybeArc; - -pub trait NpmPackageFolderResolver: - std::fmt::Debug + MaybeSend + MaybeSync -{ +pub trait NpmPackageFolderResolver { /// Resolves an npm package folder path from the specified referrer. fn resolve_package_folder_from_package( &self, @@ -27,11 +19,8 @@ pub trait NpmPackageFolderResolver: ) -> Result; } -#[allow(clippy::disallowed_types)] -pub type InNpmPackageCheckerRc = crate::sync::MaybeArc; - /// Checks if a provided specifier is in an npm package. -pub trait InNpmPackageChecker: std::fmt::Debug + MaybeSend + MaybeSync { +pub trait InNpmPackageChecker { fn in_npm_package(&self, specifier: &Url) -> bool; fn in_npm_package_at_dir_path(&self, path: &Path) -> bool { diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs index a60a6bec9e..9ea5e17e41 100644 --- a/resolvers/node/resolution.rs +++ b/resolvers/node/resolution.rs @@ -46,8 +46,8 @@ use crate::errors::TypesNotFoundError; use crate::errors::TypesNotFoundErrorData; use crate::errors::UnsupportedDirImportError; use crate::errors::UnsupportedEsmUrlSchemeError; -use crate::npm::InNpmPackageCheckerRc; -use crate::NpmPackageFolderResolverRc; +use crate::InNpmPackageChecker; +use crate::NpmPackageFolderResolver; use crate::PackageJsonResolverRc; use crate::PathClean; @@ -137,31 +137,52 @@ pub trait IsBuiltInNodeModuleChecker: std::fmt::Debug { } #[allow(clippy::disallowed_types)] -pub type NodeResolverRc = - crate::sync::MaybeArc>; +pub type NodeResolverRc< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, +> = crate::sync::MaybeArc< + NodeResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + >, +>; #[derive(Debug)] pub struct NodeResolver< + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: FsCanonicalize + FsMetadata + FsRead, > { - in_npm_pkg_checker: InNpmPackageCheckerRc, + in_npm_pkg_checker: TInNpmPackageChecker, is_built_in_node_module_checker: TIsBuiltInNodeModuleChecker, - npm_pkg_folder_resolver: NpmPackageFolderResolverRc, + npm_pkg_folder_resolver: TNpmPackageFolderResolver, pkg_json_resolver: PackageJsonResolverRc, sys: TSys, conditions_from_resolution_mode: ConditionsFromResolutionMode, } impl< + TInNpmPackageChecker: InNpmPackageChecker, TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, TSys: FsCanonicalize + FsMetadata + FsRead, - > NodeResolver + > + NodeResolver< + TInNpmPackageChecker, + TIsBuiltInNodeModuleChecker, + TNpmPackageFolderResolver, + TSys, + > { pub fn new( - in_npm_pkg_checker: InNpmPackageCheckerRc, + in_npm_pkg_checker: TInNpmPackageChecker, is_built_in_node_module_checker: TIsBuiltInNodeModuleChecker, - npm_pkg_folder_resolver: NpmPackageFolderResolverRc, + npm_pkg_folder_resolver: TNpmPackageFolderResolver, pkg_json_resolver: PackageJsonResolverRc, sys: TSys, conditions_from_resolution_mode: ConditionsFromResolutionMode, @@ -444,6 +465,17 @@ impl< Ok(url) } + /// Resolves an npm package folder path from the specified referrer. + pub fn resolve_package_folder_from_package( + &self, + specifier: &str, + referrer: &Url, + ) -> Result { + self + .npm_pkg_folder_resolver + .resolve_package_folder_from_package(specifier, referrer) + } + /// Checks if the resolved file has a corresponding declaration file. fn path_to_declaration_url( &self, diff --git a/resolvers/node/sync.rs b/resolvers/node/sync.rs index 8cf06932ac..218253b453 100644 --- a/resolvers/node/sync.rs +++ b/resolvers/node/sync.rs @@ -6,17 +6,10 @@ pub use inner::*; mod inner { #![allow(clippy::disallowed_types)] - pub use core::marker::Send as MaybeSend; - pub use core::marker::Sync as MaybeSync; pub use std::sync::Arc as MaybeArc; } #[cfg(not(feature = "sync"))] mod inner { pub use std::rc::Rc as MaybeArc; - - pub trait MaybeSync {} - impl MaybeSync for T where T: ?Sized {} - pub trait MaybeSend {} - impl MaybeSend for T where T: ?Sized {} } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 5ecd911e8a..f59325962f 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -91,6 +91,7 @@ deno_net.workspace = true deno_node.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true +deno_resolver.workspace = true deno_telemetry.workspace = true deno_terminal.workspace = true deno_tls.workspace = true diff --git a/runtime/examples/extension/main.rs b/runtime/examples/extension/main.rs index 9f0eb63b01..e1538b8b75 100644 --- a/runtime/examples/extension/main.rs +++ b/runtime/examples/extension/main.rs @@ -12,6 +12,8 @@ use deno_core::op2; use deno_core::FsModuleLoader; use deno_core::ModuleSpecifier; use deno_fs::RealFs; +use deno_resolver::npm::DenoInNpmPackageChecker; +use deno_resolver::npm::NpmResolver; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::permissions::RuntimePermissionDescriptorParser; use deno_runtime::worker::MainWorker; @@ -42,7 +44,11 @@ async fn main() -> Result<(), AnyError> { ); let mut worker = MainWorker::bootstrap_from_options( main_module.clone(), - WorkerServiceOptions:: { + WorkerServiceOptions::< + DenoInNpmPackageChecker, + NpmResolver, + sys_traits::impls::RealSys, + > { module_loader: Rc::new(FsModuleLoader), permissions: PermissionsContainer::allow_all(permission_desc_parser), blob_store: Default::default(), diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index 8d008eeab0..331369551c 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -14,6 +14,8 @@ use deno_core::Extension; use deno_http::DefaultHttpPropertyExtractor; use deno_io::fs::FsError; use deno_permissions::PermissionCheckError; +use deno_resolver::npm::DenoInNpmPackageChecker; +use deno_resolver::npm::NpmResolver; use crate::ops; use crate::ops::bootstrap::SnapshotOptions; @@ -310,6 +312,8 @@ pub fn create_runtime_snapshot( deno_fs::deno_fs::init_ops_and_esm::(fs.clone()), deno_node::deno_node::init_ops_and_esm::< Permissions, + DenoInNpmPackageChecker, + NpmResolver, sys_traits::impls::RealSys, >(None, fs.clone()), runtime::init_ops_and_esm(), diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 64393bb64c..4389c7752c 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -54,6 +54,8 @@ use deno_web::JsMessageData; use deno_web::MessagePort; use deno_web::Transferable; use log::debug; +use node_resolver::InNpmPackageChecker; +use node_resolver::NpmPackageFolderResolver; use crate::inspector_server::InspectorServer; use crate::ops; @@ -334,7 +336,11 @@ fn create_handles( (internal_handle, external_handle) } -pub struct WebWorkerServiceOptions { +pub struct WebWorkerServiceOptions< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, +> { pub blob_store: Arc, pub broadcast_channel: InMemoryBroadcastChannel, pub compiled_wasm_module_store: Option, @@ -342,7 +348,13 @@ pub struct WebWorkerServiceOptions { pub fs: Arc, pub maybe_inspector_server: Option>, pub module_loader: Rc, - pub node_services: Option>, + pub node_services: Option< + NodeExtInitServices< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, + >, pub npm_process_state_provider: Option, pub permissions: PermissionsContainer, pub root_cert_store_provider: Option>, @@ -398,8 +410,16 @@ impl Drop for WebWorker { } impl WebWorker { - pub fn bootstrap_from_options( - services: WebWorkerServiceOptions, + pub fn bootstrap_from_options< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, + >( + services: WebWorkerServiceOptions< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, options: WebWorkerOptions, ) -> (Self, SendableWebWorkerHandle) { let (mut worker, handle, bootstrap_options) = @@ -408,8 +428,16 @@ impl WebWorker { (worker, handle) } - fn from_options( - services: WebWorkerServiceOptions, + fn from_options< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, + >( + services: WebWorkerServiceOptions< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, mut options: WebWorkerOptions, ) -> (Self, SendableWebWorkerHandle, BootstrapOptions) { deno_core::extension!(deno_permissions_web_worker, @@ -500,10 +528,12 @@ impl WebWorker { deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(), ), - deno_node::deno_node::init_ops_and_esm::( - services.node_services, - services.fs, - ), + deno_node::deno_node::init_ops_and_esm::< + PermissionsContainer, + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >(services.node_services, services.fs), // Runtime ops that are always initialized for WebWorkers ops::runtime::deno_runtime::init_ops_and_esm(options.main_module.clone()), ops::worker_host::deno_worker_host::init_ops_and_esm( diff --git a/runtime/worker.rs b/runtime/worker.rs index a649c83d47..7cd6468fc5 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -45,6 +45,8 @@ use deno_tls::RootCertStoreProvider; use deno_tls::TlsKeys; use deno_web::BlobStore; use log::debug; +use node_resolver::InNpmPackageChecker; +use node_resolver::NpmPackageFolderResolver; use crate::code_cache::CodeCache; use crate::code_cache::CodeCacheType; @@ -128,7 +130,11 @@ pub struct MainWorker { dispatch_process_exit_event_fn_global: v8::Global, } -pub struct WorkerServiceOptions { +pub struct WorkerServiceOptions< + TInNpmPackageChecker: InNpmPackageChecker, + TNpmPackageFolderResolver: NpmPackageFolderResolver, + TExtNodeSys: ExtNodeSys, +> { pub blob_store: Arc, pub broadcast_channel: InMemoryBroadcastChannel, pub feature_checker: Arc, @@ -139,7 +145,13 @@ pub struct WorkerServiceOptions { /// If not provided runtime will error if code being /// executed tries to load modules. pub module_loader: Rc, - pub node_services: Option>, + pub node_services: Option< + NodeExtInitServices< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, + >, pub npm_process_state_provider: Option, pub permissions: PermissionsContainer, pub root_cert_store_provider: Option>, @@ -300,9 +312,17 @@ pub fn create_op_metrics( } impl MainWorker { - pub fn bootstrap_from_options( + pub fn bootstrap_from_options< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, + >( main_module: ModuleSpecifier, - services: WorkerServiceOptions, + services: WorkerServiceOptions< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, options: WorkerOptions, ) -> Self { let (mut worker, bootstrap_options) = @@ -311,9 +331,17 @@ impl MainWorker { worker } - fn from_options( + fn from_options< + TInNpmPackageChecker: InNpmPackageChecker + 'static, + TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static, + TExtNodeSys: ExtNodeSys + 'static, + >( main_module: ModuleSpecifier, - services: WorkerServiceOptions, + services: WorkerServiceOptions< + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >, mut options: WorkerOptions, ) -> (Self, BootstrapOptions) { deno_core::extension!(deno_permissions_worker, @@ -413,10 +441,12 @@ impl MainWorker { deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(), ), - deno_node::deno_node::init_ops_and_esm::( - services.node_services, - services.fs, - ), + deno_node::deno_node::init_ops_and_esm::< + PermissionsContainer, + TInNpmPackageChecker, + TNpmPackageFolderResolver, + TExtNodeSys, + >(services.node_services, services.fs), // Ops from this crate ops::runtime::deno_runtime::init_ops_and_esm(main_module.clone()), ops::worker_host::deno_worker_host::init_ops_and_esm( From c943f56949d723ba891b26a0cc4aaaaf7b358d95 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 15 Jan 2025 01:00:55 +0900 Subject: [PATCH 106/107] fix(ext/node): fix playwright http client (#27662) --- ext/node/polyfills/_tls_wrap.ts | 2 +- ext/node/polyfills/net.ts | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/ext/node/polyfills/_tls_wrap.ts b/ext/node/polyfills/_tls_wrap.ts index 0edea1c053..dc7dac77ac 100644 --- a/ext/node/polyfills/_tls_wrap.ts +++ b/ext/node/polyfills/_tls_wrap.ts @@ -154,7 +154,7 @@ export class TLSSocket extends net.Socket { const afterConnect = handle.afterConnect; handle.afterConnect = async (req: any, status: number) => { options.hostname ??= undefined; // coerce to undefined if null, startTls expects hostname to be undefined - if (tlssock._isNpmAgent) { + if (tlssock._needsSockInitWorkaround) { // skips the TLS handshake for @npmcli/agent as it's handled by // onSocket handler of ClientRequest object. tlssock.emit("secure"); diff --git a/ext/node/polyfills/net.ts b/ext/node/polyfills/net.ts index bebdd8dade..2d57507c1b 100644 --- a/ext/node/polyfills/net.ts +++ b/ext/node/polyfills/net.ts @@ -1157,6 +1157,13 @@ function _emitCloseNT(s: Socket | Server) { s.emit("close"); } +// The packages that need socket initialization workaround +const pkgsNeedsSockInitWorkaround = [ + "@npmcli/agent", + "npm-check-updates", + "playwright-core", +]; + /** * This class is an abstraction of a TCP socket or a streaming `IPC` endpoint * (uses named pipes on Windows, and Unix domain sockets otherwise). It is also @@ -1201,9 +1208,11 @@ export class Socket extends Duplex { _host: string | null = null; // deno-lint-ignore no-explicit-any _parent: any = null; - // The flag for detecting if it's called in @npmcli/agent + // Skip some initialization (initial read and tls handshake if it's tls socket). + // If this flag is true, it's used as connection for http(s) request, and + // the reading and TLS handshake is done by the http client. // See discussions in https://github.com/denoland/deno/pull/25470 for more details. - _isNpmAgent = false; + _needsSockInitWorkaround = false; autoSelectFamilyAttemptedAddresses: AddressInfo[] | undefined = undefined; constructor(options: SocketOptions | number) { @@ -1224,21 +1233,20 @@ export class Socket extends Duplex { super(options); - // Note: If the socket is created from one of: - // - @npmcli/agent - // - npm-check-updates (bundles @npmcli/agent as a dependency) + // Note: If the socket is created from one of `pkgNeedsSockInitWorkaround`, // the 'socket' event on ClientRequest object happens after 'connect' event on Socket object. // That swaps the sequence of op_node_http_request_with_conn() call and // initial socket read. That causes op_node_http_request_with_conn() not // working. // To avoid the above situation, we detect the socket created from - // @npmcli/agent and pause the socket (and also skips the startTls call - // if it's TLSSocket) + // one of those packages using stack trace and pause the socket + // (and also skips the startTls call if it's TLSSocket) // TODO(kt3k): Remove this workaround const errorStack = new Error().stack; - this._isNpmAgent = errorStack?.includes("@npmcli/agent") || - errorStack?.includes("npm-check-updates") || false; - if (this._isNpmAgent) { + this._needsSockInitWorkaround = pkgsNeedsSockInitWorkaround.some((pkg) => + errorStack?.includes(pkg) + ); + if (this._needsSockInitWorkaround) { this.pause(); } From 974e2f44b2660a1cd13f8c901ff28a2f57ed391f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 14 Jan 2025 16:29:36 +0000 Subject: [PATCH 107/107] refactor: add 'deno_os' crate (#27655) This commit creates "deno_os" extension crate and moves numerous ops from "runtime/" crate to the new crate. --- Cargo.lock | 25 +++++++++-- Cargo.toml | 1 + cli/js/40_bench.js | 2 +- cli/js/40_test.js | 2 +- cli/task_runner.rs | 2 +- cli/worker.rs | 2 +- ext/node/polyfills/os.ts | 2 +- ext/node/polyfills/process.ts | 2 +- {runtime/js => ext/os}/30_os.js | 0 {runtime/js => ext/os}/40_signals.js | 0 ext/os/Cargo.toml | 33 ++++++++++++++ {runtime/ops => ext}/os/README.md | 4 +- runtime/ops/os/mod.rs => ext/os/lib.rs | 61 +++++++++++++++++++++++--- ext/os/ops/mod.rs | 3 ++ {runtime => ext/os}/ops/signal.rs | 19 ++------ {runtime => ext/os}/signal.rs | 0 {runtime => ext/os}/sys_info.rs | 0 runtime/Cargo.toml | 5 +-- runtime/js/90_deno_ns.js | 4 +- runtime/js/99_main.js | 2 +- runtime/lib.rs | 10 +---- runtime/ops/mod.rs | 2 - runtime/ops/process.rs | 9 ++-- runtime/shared.rs | 2 - runtime/snapshot.rs | 3 +- runtime/web_worker.rs | 3 +- runtime/worker.rs | 21 ++------- tests/integration/run_tests.rs | 2 +- tools/core_import_map.json | 4 +- 29 files changed, 145 insertions(+), 80 deletions(-) rename {runtime/js => ext/os}/30_os.js (100%) rename {runtime/js => ext/os}/40_signals.js (100%) create mode 100644 ext/os/Cargo.toml rename {runtime/ops => ext}/os/README.md (97%) rename runtime/ops/os/mod.rs => ext/os/lib.rs (90%) create mode 100644 ext/os/ops/mod.rs rename {runtime => ext/os}/ops/signal.rs (95%) rename {runtime => ext/os}/signal.rs (100%) rename {runtime => ext/os}/sys_info.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 95633e3ce9..aadd0c5f95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2140,6 +2140,27 @@ dependencies = [ "thiserror 2.0.3", ] +[[package]] +name = "deno_os" +version = "0.1.0" +dependencies = [ + "deno_core", + "deno_error", + "deno_path_util", + "deno_permissions", + "deno_telemetry", + "libc", + "netif", + "ntapi", + "once_cell", + "serde", + "signal-hook", + "signal-hook-registry", + "thiserror 2.0.3", + "tokio", + "winapi", +] + [[package]] name = "deno_package_json" version = "0.4.0" @@ -2240,6 +2261,7 @@ dependencies = [ "deno_napi", "deno_net", "deno_node", + "deno_os", "deno_path_util", "deno_permissions", "deno_resolver", @@ -2263,7 +2285,6 @@ dependencies = [ "hyper-util", "libc", "log", - "netif", "nix", "node_resolver", "notify", @@ -2274,8 +2295,6 @@ dependencies = [ "rustyline", "same-file", "serde", - "signal-hook", - "signal-hook-registry", "sys_traits", "tempfile", "test_server", diff --git a/Cargo.toml b/Cargo.toml index 48aeccdaef..d9f976d863 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,7 @@ deno_kv = { version = "0.93.0", path = "./ext/kv" } deno_napi = { version = "0.116.0", path = "./ext/napi" } deno_net = { version = "0.177.0", path = "./ext/net" } deno_node = { version = "0.123.0", path = "./ext/node" } +deno_os = { version = "0.1.0", path = "./ext/os" } deno_telemetry = { version = "0.7.0", path = "./ext/telemetry" } deno_tls = { version = "0.172.0", path = "./ext/tls" } deno_url = { version = "0.185.0", path = "./ext/url" } diff --git a/cli/js/40_bench.js b/cli/js/40_bench.js index 0ad05c5197..fb0e86463d 100644 --- a/cli/js/40_bench.js +++ b/cli/js/40_bench.js @@ -8,7 +8,7 @@ import { restorePermissions, } from "ext:cli/40_test_common.js"; import { Console } from "ext:deno_console/01_console.js"; -import { setExitHandler } from "ext:runtime/30_os.js"; +import { setExitHandler } from "ext:deno_os/30_os.js"; const { op_register_bench, op_bench_get_origin, diff --git a/cli/js/40_test.js b/cli/js/40_test.js index 34d9ec6550..3dbb7ec340 100644 --- a/cli/js/40_test.js +++ b/cli/js/40_test.js @@ -26,7 +26,7 @@ const { TypeError, } = primordials; -import { setExitHandler } from "ext:runtime/30_os.js"; +import { setExitHandler } from "ext:deno_os/30_os.js"; // Capture `Deno` global so that users deleting or mangling it, won't // have impact on our sanitizers. diff --git a/cli/task_runner.rs b/cli/task_runner.rs index 50dcd64dc7..14e850ee76 100644 --- a/cli/task_runner.rs +++ b/cli/task_runner.rs @@ -596,7 +596,7 @@ async fn listen_ctrl_c(kill_signal: KillSignal) { #[cfg(unix)] async fn listen_and_forward_all_signals(kill_signal: KillSignal) { use deno_core::futures::FutureExt; - use deno_runtime::signal::SIGNAL_NUMS; + use deno_runtime::deno_os::signal::SIGNAL_NUMS; // listen and forward every signal we support let mut futures = Vec::with_capacity(SIGNAL_NUMS.len()); diff --git a/cli/worker.rs b/cli/worker.rs index 45d4b0af70..d9cdbd3fb0 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -855,7 +855,7 @@ fn create_web_worker_callback( /// Instead probe for the total memory on the system and use it instead /// as a default. pub fn create_isolate_create_params() -> Option { - let maybe_mem_info = deno_runtime::sys_info::mem_info(); + let maybe_mem_info = deno_runtime::deno_os::sys_info::mem_info(); maybe_mem_info.map(|mem_info| { v8::CreateParams::default() .heap_limits_from_system_memory(mem_info.total, 0) diff --git a/ext/node/polyfills/os.ts b/ext/node/polyfills/os.ts index bd4b285ba3..4901b20fa8 100644 --- a/ext/node/polyfills/os.ts +++ b/ext/node/polyfills/os.ts @@ -35,7 +35,7 @@ import { validateIntegerRange } from "ext:deno_node/_utils.ts"; import process from "node:process"; import { isWindows } from "ext:deno_node/_util/os.ts"; import { os } from "ext:deno_node/internal_binding/constants.ts"; -import { osUptime } from "ext:runtime/30_os.js"; +import { osUptime } from "ext:deno_os/30_os.js"; import { Buffer } from "ext:deno_node/internal/buffer.mjs"; import { primordials } from "ext:core/mod.js"; const { StringPrototypeEndsWith, StringPrototypeSlice } = primordials; diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index e91a2ee005..b3fe0883dc 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -58,7 +58,7 @@ import { } from "ext:deno_node/_next_tick.ts"; import { isWindows } from "ext:deno_node/_util/os.ts"; import * as io from "ext:deno_io/12_io.js"; -import * as denoOs from "ext:runtime/30_os.js"; +import * as denoOs from "ext:deno_os/30_os.js"; export let argv0 = ""; diff --git a/runtime/js/30_os.js b/ext/os/30_os.js similarity index 100% rename from runtime/js/30_os.js rename to ext/os/30_os.js diff --git a/runtime/js/40_signals.js b/ext/os/40_signals.js similarity index 100% rename from runtime/js/40_signals.js rename to ext/os/40_signals.js diff --git a/ext/os/Cargo.toml b/ext/os/Cargo.toml new file mode 100644 index 0000000000..fb553d1532 --- /dev/null +++ b/ext/os/Cargo.toml @@ -0,0 +1,33 @@ +# Copyright 2018-2025 the Deno authors. MIT license. + +[package] +name = "deno_os" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +readme = "README.md" +repository.workspace = true +description = "OS specific APIs for Deno" + +[lib] +path = "lib.rs" + +[dependencies] +deno_core.workspace = true +deno_error.workspace = true +deno_path_util.workspace = true +deno_permissions.workspace = true +deno_telemetry.workspace = true +libc.workspace = true +netif = "0.1.6" +once_cell.workspace = true +serde.workspace = true +signal-hook = "0.3.17" +signal-hook-registry = "1.4.0" +thiserror.workspace = true +tokio.workspace = true + +[target.'cfg(windows)'.dependencies] +winapi = { workspace = true, features = ["commapi", "knownfolders", "mswsock", "objbase", "psapi", "shlobj", "tlhelp32", "winbase", "winerror", "winuser", "winsock2"] } +ntapi = "0.4.0" diff --git a/runtime/ops/os/README.md b/ext/os/README.md similarity index 97% rename from runtime/ops/os/README.md rename to ext/os/README.md index ae1a5958e4..f308ed3d2b 100644 --- a/runtime/ops/os/README.md +++ b/ext/os/README.md @@ -1,4 +1,6 @@ -## `os` ops +# deno_os + +This crate implements OS specific APIs for Deno `loadavg` diff --git a/runtime/ops/os/mod.rs b/ext/os/lib.rs similarity index 90% rename from runtime/ops/os/mod.rs rename to ext/os/lib.rs index ad3a292c3a..f084601678 100644 --- a/runtime/ops/os/mod.rs +++ b/ext/os/lib.rs @@ -1,19 +1,54 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::collections::HashMap; +use std::collections::HashSet; use std::env; +use std::sync::atomic::AtomicI32; +use std::sync::atomic::Ordering; +use std::sync::Arc; use deno_core::op2; use deno_core::v8; use deno_core::OpState; -use deno_node::NODE_ENV_VAR_ALLOWLIST; use deno_path_util::normalize_path; use deno_permissions::PermissionCheckError; use deno_permissions::PermissionsContainer; +use once_cell::sync::Lazy; use serde::Serialize; -use crate::sys_info; -use crate::worker::ExitCode; +mod ops; +pub mod signal; +pub mod sys_info; + +pub use ops::signal::SignalError; + +pub static NODE_ENV_VAR_ALLOWLIST: Lazy> = Lazy::new(|| { + // The full list of environment variables supported by Node.js is available + // at https://nodejs.org/api/cli.html#environment-variables + let mut set = HashSet::new(); + set.insert("NODE_DEBUG".to_string()); + set.insert("NODE_OPTIONS".to_string()); + set +}); + +#[derive(Clone, Default)] +pub struct ExitCode(Arc); + +impl ExitCode { + pub fn get(&self) -> i32 { + self.0.load(Ordering::Relaxed) + } + + pub fn set(&mut self, code: i32) { + self.0.store(code, Ordering::Relaxed); + } +} + +pub fn exit(code: i32) -> ! { + deno_telemetry::flush(); + #[allow(clippy::disallowed_methods)] + std::process::exit(code); +} deno_core::extension!( deno_os, @@ -35,13 +70,21 @@ deno_core::extension!( op_system_memory_info, op_uid, op_runtime_memory_usage, + ops::signal::op_signal_bind, + ops::signal::op_signal_unbind, + ops::signal::op_signal_poll, ], + esm = ["30_os.js", "40_signals.js"], options = { exit_code: ExitCode, }, state = |state, options| { state.put::(options.exit_code); - }, + #[cfg(unix)] + { + state.put(ops::signal::SignalState::default()); + } + } ); deno_core::extension!( @@ -64,12 +107,16 @@ deno_core::extension!( op_system_memory_info, op_uid, op_runtime_memory_usage, + ops::signal::op_signal_bind, + ops::signal::op_signal_unbind, + ops::signal::op_signal_poll, ], + esm = ["30_os.js", "40_signals.js"], middleware = |op| match op.name { "op_exit" | "op_set_exit_code" | "op_get_exit_code" => op.with_implementation_from(&deno_core::op_void_sync()), _ => op, - }, + } ); #[derive(Debug, thiserror::Error, deno_error::JsError)] @@ -196,7 +243,7 @@ fn op_get_exit_code(state: &mut OpState) -> i32 { #[op2(fast)] fn op_exit(state: &mut OpState) { let code = state.borrow::().get(); - crate::exit(code) + exit(code) } #[op2(stack_trace)] @@ -239,7 +286,7 @@ fn op_network_interfaces( Ok(netif::up()?.map(NetworkInterface::from).collect()) } -#[derive(serde::Serialize)] +#[derive(Serialize)] struct NetworkInterface { family: &'static str, name: String, diff --git a/ext/os/ops/mod.rs b/ext/os/ops/mod.rs new file mode 100644 index 0000000000..857d4688c7 --- /dev/null +++ b/ext/os/ops/mod.rs @@ -0,0 +1,3 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +pub mod signal; diff --git a/runtime/ops/signal.rs b/ext/os/ops/signal.rs similarity index 95% rename from runtime/ops/signal.rs rename to ext/os/ops/signal.rs index beefa1cd78..5b556e3a2c 100644 --- a/runtime/ops/signal.rs +++ b/ext/os/ops/signal.rs @@ -33,17 +33,6 @@ use tokio::signal::windows::CtrlBreak; #[cfg(windows)] use tokio::signal::windows::CtrlC; -deno_core::extension!( - deno_signal, - ops = [op_signal_bind, op_signal_unbind, op_signal_poll], - state = |state| { - #[cfg(unix)] - { - state.put(SignalState::default()); - } - } -); - #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SignalError { #[class(type)] @@ -62,7 +51,7 @@ pub enum SignalError { #[cfg(unix)] #[derive(Default)] -struct SignalState { +pub struct SignalState { enable_default_handlers: BTreeMap>, } @@ -164,7 +153,7 @@ impl Resource for SignalStreamResource { #[cfg(unix)] #[op2(fast)] #[smi] -fn op_signal_bind( +pub fn op_signal_bind( state: &mut OpState, #[string] sig: &str, ) -> Result { @@ -201,7 +190,7 @@ fn op_signal_bind( #[cfg(windows)] #[op2(fast)] #[smi] -fn op_signal_bind( +pub fn op_signal_bind( state: &mut OpState, #[string] sig: &str, ) -> Result { @@ -225,7 +214,7 @@ fn op_signal_bind( } #[op2(async)] -async fn op_signal_poll( +pub async fn op_signal_poll( state: Rc>, #[smi] rid: ResourceId, ) -> Result { diff --git a/runtime/signal.rs b/ext/os/signal.rs similarity index 100% rename from runtime/signal.rs rename to ext/os/signal.rs diff --git a/runtime/sys_info.rs b/ext/os/sys_info.rs similarity index 100% rename from runtime/sys_info.rs rename to ext/os/sys_info.rs diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index f59325962f..808f79ab7b 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -49,6 +49,7 @@ deno_core.workspace = true deno_cron.workspace = true deno_crypto.workspace = true deno_fetch.workspace = true +deno_os.workspace = true deno_ffi.workspace = true deno_fs = { workspace = true, features = ["sync_fs"] } deno_http.workspace = true @@ -89,6 +90,7 @@ deno_kv.workspace = true deno_napi.workspace = true deno_net.workspace = true deno_node.workspace = true +deno_os.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true deno_resolver.workspace = true @@ -114,7 +116,6 @@ hyper-util.workspace = true hyper_v014 = { workspace = true, features = ["server", "stream", "http1", "http2", "runtime"] } libc.workspace = true log.workspace = true -netif = "0.1.6" notify.workspace = true once_cell.workspace = true percent-encoding.workspace = true @@ -122,8 +123,6 @@ regex.workspace = true rustyline = { workspace = true, features = ["custom-bindings"] } same-file = "1.0.6" serde.workspace = true -signal-hook = "0.3.17" -signal-hook-registry = "1.4.0" sys_traits.workspace = true tempfile.workspace = true thiserror.workspace = true diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 8127ac9618..b241028077 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -21,10 +21,10 @@ import * as version from "ext:runtime/01_version.ts"; import * as permissions from "ext:runtime/10_permissions.js"; import * as io from "ext:deno_io/12_io.js"; import * as fs from "ext:deno_fs/30_fs.js"; -import * as os from "ext:runtime/30_os.js"; +import * as os from "ext:deno_os/30_os.js"; import * as fsEvents from "ext:runtime/40_fs_events.js"; import * as process from "ext:runtime/40_process.js"; -import * as signals from "ext:runtime/40_signals.js"; +import * as signals from "ext:deno_os/40_signals.js"; import * as tty from "ext:runtime/40_tty.js"; import * as kv from "ext:deno_kv/01_db.ts"; import * as cron from "ext:deno_cron/01_cron.ts"; diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 03c662bcab..2971fd2c00 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -55,7 +55,7 @@ import { registerDeclarativeServer } from "ext:deno_http/00_serve.ts"; import * as event from "ext:deno_web/02_event.js"; import * as location from "ext:deno_web/12_location.js"; import * as version from "ext:runtime/01_version.ts"; -import * as os from "ext:runtime/30_os.js"; +import * as os from "ext:deno_os/30_os.js"; import * as timers from "ext:deno_web/02_timers.js"; import { getDefaultInspectOptions, diff --git a/runtime/lib.rs b/runtime/lib.rs index 9afe91cd25..c104f5cd61 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -16,6 +16,7 @@ pub use deno_kv; pub use deno_napi; pub use deno_net; pub use deno_node; +pub use deno_os; pub use deno_permissions; pub use deno_terminal::colors; pub use deno_tls; @@ -33,9 +34,7 @@ pub mod inspector_server; pub mod js; pub mod ops; pub mod permissions; -pub mod signal; pub mod snapshot; -pub mod sys_info; pub mod tokio_util; pub mod web_worker; pub mod worker; @@ -46,6 +45,7 @@ pub use worker_bootstrap::WorkerExecutionMode; pub use worker_bootstrap::WorkerLogLevel; pub mod shared; +pub use deno_os::exit; pub use shared::runtime; pub struct UnstableGranularFlag { @@ -147,12 +147,6 @@ pub static UNSTABLE_GRANULAR_FLAGS: &[UnstableGranularFlag] = &[ }, ]; -pub fn exit(code: i32) -> ! { - deno_telemetry::flush(); - #[allow(clippy::disallowed_methods)] - std::process::exit(code); -} - #[cfg(test)] mod test { use super::*; diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs index 438c3896e6..d131e9aab5 100644 --- a/runtime/ops/mod.rs +++ b/runtime/ops/mod.rs @@ -3,11 +3,9 @@ pub mod bootstrap; pub mod fs_events; pub mod http; -pub mod os; pub mod permissions; pub mod process; pub mod runtime; -pub mod signal; pub mod tty; pub mod web_worker; pub mod worker_host; diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index 4682085aea..fc32f7e066 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -31,14 +31,13 @@ use deno_io::ChildStderrResource; use deno_io::ChildStdinResource; use deno_io::ChildStdoutResource; use deno_io::IntoRawIoHandle; +use deno_os::SignalError; use deno_permissions::PermissionsContainer; use deno_permissions::RunQueryDescriptor; use serde::Deserialize; use serde::Serialize; use tokio::process::Command; -use crate::ops::signal::SignalError; - pub const UNSTABLE_FEATURE_NAME: &str = "process"; #[derive(Copy, Clone, Eq, PartialEq, Deserialize)] @@ -296,7 +295,7 @@ impl TryFrom for ChildStatus { success: false, code: 128 + signal, #[cfg(unix)] - signal: Some(crate::signal::signal_int_to_str(signal)?.to_string()), + signal: Some(deno_os::signal::signal_int_to_str(signal)?.to_string()), #[cfg(not(unix))] signal: None, } @@ -1116,7 +1115,7 @@ mod deprecated { #[cfg(unix)] pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> { - let signo = crate::signal::signal_str_to_int(signal) + let signo = deno_os::signal::signal_str_to_int(signal) .map_err(SignalError::InvalidSignalStr)?; use nix::sys::signal::kill as unix_kill; use nix::sys::signal::Signal; @@ -1144,7 +1143,7 @@ mod deprecated { if !matches!(signal, "SIGKILL" | "SIGTERM") { Err( - SignalError::InvalidSignalStr(crate::signal::InvalidSignalStrError( + SignalError::InvalidSignalStr(deno_os::signal::InvalidSignalStrError( signal.to_string(), )) .into(), diff --git a/runtime/shared.rs b/runtime/shared.rs index f8588dd72c..0e747b0565 100644 --- a/runtime/shared.rs +++ b/runtime/shared.rs @@ -42,10 +42,8 @@ extension!(runtime, "06_util.js", "10_permissions.js", "11_workers.js", - "30_os.js", "40_fs_events.js", "40_process.js", - "40_signals.js", "40_tty.js", "41_prompt.js", "90_deno_ns.js", diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index 331369551c..08ea17a986 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -310,6 +310,7 @@ pub fn create_runtime_snapshot( ), deno_io::deno_io::init_ops_and_esm(Default::default()), deno_fs::deno_fs::init_ops_and_esm::(fs.clone()), + deno_os::deno_os::init_ops_and_esm(Default::default()), deno_node::deno_node::init_ops_and_esm::< Permissions, DenoInNpmPackageChecker, @@ -323,10 +324,8 @@ pub fn create_runtime_snapshot( None, ), ops::fs_events::deno_fs_events::init_ops(), - ops::os::deno_os::init_ops(Default::default()), ops::permissions::deno_permissions::init_ops(), ops::process::deno_process::init_ops(None), - ops::signal::deno_signal::init_ops(), ops::tty::deno_tty::init_ops(), ops::http::deno_http_runtime::init_ops(), ops::bootstrap::deno_bootstrap::init_ops(Some(snapshot_options)), diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 4389c7752c..e4ea42a2f7 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -528,6 +528,7 @@ impl WebWorker { deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(), ), + deno_os::deno_os_worker::init_ops_and_esm(), deno_node::deno_node::init_ops_and_esm::< PermissionsContainer, TInNpmPackageChecker, @@ -541,12 +542,10 @@ impl WebWorker { options.format_js_error_fn, ), ops::fs_events::deno_fs_events::init_ops_and_esm(), - ops::os::deno_os_worker::init_ops_and_esm(), ops::permissions::deno_permissions::init_ops_and_esm(), ops::process::deno_process::init_ops_and_esm( services.npm_process_state_provider, ), - ops::signal::deno_signal::init_ops_and_esm(), ops::tty::deno_tty::init_ops_and_esm(), ops::http::deno_http_runtime::init_ops_and_esm(), ops::bootstrap::deno_bootstrap::init_ops_and_esm( diff --git a/runtime/worker.rs b/runtime/worker.rs index 7cd6468fc5..cfcadccc46 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -3,8 +3,6 @@ use std::borrow::Cow; use std::collections::HashMap; use std::rc::Rc; use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicI32; -use std::sync::atomic::Ordering::Relaxed; use std::sync::Arc; use std::time::Duration; use std::time::Instant; @@ -40,6 +38,7 @@ use deno_io::Stdio; use deno_kv::dynamic::MultiBackendDbHandler; use deno_node::ExtNodeSys; use deno_node::NodeExtInitServices; +use deno_os::ExitCode; use deno_permissions::PermissionsContainer; use deno_tls::RootCertStoreProvider; use deno_tls::TlsKeys; @@ -97,19 +96,6 @@ pub fn validate_import_attributes_callback( } } -#[derive(Clone, Default)] -pub struct ExitCode(Arc); - -impl ExitCode { - pub fn get(&self) -> i32 { - self.0.load(Relaxed) - } - - pub fn set(&mut self, code: i32) { - self.0.store(code, Relaxed); - } -} - /// This worker is created and used by almost all /// subcommands in Deno executable. /// @@ -363,7 +349,7 @@ impl MainWorker { // Permissions: many ops depend on this let enable_testing_features = options.bootstrap.enable_testing_features; - let exit_code = ExitCode(Arc::new(AtomicI32::new(0))); + let exit_code = ExitCode::default(); let create_cache = options.cache_storage_dir.map(|storage_dir| { let create_cache_fn = move || SqliteBackedCache::new(storage_dir.clone()); CreateCache(Arc::new(create_cache_fn)) @@ -441,6 +427,7 @@ impl MainWorker { deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(), ), + deno_os::deno_os::init_ops_and_esm(exit_code.clone()), deno_node::deno_node::init_ops_and_esm::< PermissionsContainer, TInNpmPackageChecker, @@ -454,12 +441,10 @@ impl MainWorker { options.format_js_error_fn.clone(), ), ops::fs_events::deno_fs_events::init_ops_and_esm(), - ops::os::deno_os::init_ops_and_esm(exit_code.clone()), ops::permissions::deno_permissions::init_ops_and_esm(), ops::process::deno_process::init_ops_and_esm( services.npm_process_state_provider, ), - ops::signal::deno_signal::init_ops_and_esm(), ops::tty::deno_tty::init_ops_and_esm(), ops::http::deno_http_runtime::init_ops_and_esm(), ops::bootstrap::deno_bootstrap::init_ops_and_esm( diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index 5e4db3d3e4..abf1c200d9 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -445,7 +445,7 @@ fn permissions_trace() { test_util::assertions::assert_wildcard_match(&text, concat!( "┏ ⚠️ Deno requests sys access to \"hostname\".\r\n", "┠─ Requested by `Deno.hostname()` API.\r\n", - "┃ ├─ Object.hostname (ext:runtime/30_os.js:43:10)\r\n", + "┃ ├─ Object.hostname (ext:deno_os/30_os.js:43:10)\r\n", "┃ ├─ foo (file://[WILDCARD]/run/permissions_trace.ts:2:8)\r\n", "┃ ├─ bar (file://[WILDCARD]/run/permissions_trace.ts:6:3)\r\n", "┃ └─ file://[WILDCARD]/run/permissions_trace.ts:9:1\r\n", diff --git a/tools/core_import_map.json b/tools/core_import_map.json index feeae1ac70..4843884015 100644 --- a/tools/core_import_map.json +++ b/tools/core_import_map.json @@ -239,10 +239,10 @@ "ext:runtime/06_util.js": "../runtime/js/06_util.js", "ext:runtime/10_permissions.js": "../runtime/js/10_permissions.js", "ext:runtime/11_workers.js": "../runtime/js/11_workers.js", - "ext:runtime/30_os.js": "../runtime/js/30_os.js", + "ext:deno_os/30_os.js": "../ext/os/30_os.js", "ext:runtime/40_fs_events.js": "../runtime/js/40_fs_events.js", "ext:runtime/40_process.js": "../runtime/js/40_process.js", - "ext:runtime/40_signals.js": "../runtime/js/40_signals.js", + "ext:deno_os/40_signals.js": "../ext/os/40_signals.js", "ext:runtime/40_tty.js": "../runtime/js/40_tty.js", "ext:runtime/41_prompt.js": "../runtime/js/41_prompt.js", "ext:runtime/90_deno_ns.js": "../runtime/js/90_deno_ns.js",